From f04a82006fc82e66b505b9428b0f54b533d6ddf0 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Thu, 10 Sep 2020 12:22:59 -0400 Subject: [PATCH 01/16] userspace-probe: log function name on invalid parameter error MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérémie Galarneau Change-Id: I31bb1e7af0cde85a7ddb4614796cae1da3e5b1b9 --- src/common/userspace-probe.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/common/userspace-probe.c b/src/common/userspace-probe.c index 79069513a..9e5429f73 100644 --- a/src/common/userspace-probe.c +++ b/src/common/userspace-probe.c @@ -430,7 +430,7 @@ lttng_userspace_probe_location_function_create(const char *binary_path, struct lttng_userspace_probe_location *ret = NULL; if (!binary_path || !function_name) { - ERR("Invalid argument(s)"); + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); goto end; } @@ -458,7 +458,7 @@ lttng_userspace_probe_location_tracepoint_create(const char *binary_path, struct lttng_userspace_probe_location *ret = NULL; if (!binary_path || !probe_name || !provider_name) { - ERR("Invalid argument(s)"); + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); goto end; } @@ -693,7 +693,7 @@ const char *lttng_userspace_probe_location_function_get_binary_path( if (!location || lttng_userspace_probe_location_get_type(location) != LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) { - ERR("Invalid argument(s)"); + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); goto end; } @@ -713,7 +713,7 @@ const char *lttng_userspace_probe_location_tracepoint_get_binary_path( if (!location || lttng_userspace_probe_location_get_type(location) != LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) { - ERR("Invalid argument(s)"); + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); goto end; } @@ -733,7 +733,7 @@ const char *lttng_userspace_probe_location_function_get_function_name( if (!location || lttng_userspace_probe_location_get_type(location) != LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) { - ERR("Invalid argument(s)"); + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); goto end; } @@ -752,7 +752,7 @@ const char *lttng_userspace_probe_location_tracepoint_get_probe_name( if (!location || lttng_userspace_probe_location_get_type(location) != LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) { - ERR("Invalid argument(s)"); + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); goto end; } @@ -771,7 +771,7 @@ const char *lttng_userspace_probe_location_tracepoint_get_provider_name( if (!location || lttng_userspace_probe_location_get_type(location) != LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) { - ERR("Invalid argument(s)"); + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); goto end; } @@ -790,7 +790,7 @@ int lttng_userspace_probe_location_function_get_binary_fd( if (!location || lttng_userspace_probe_location_get_type(location) != LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) { - ERR("Invalid argument(s)"); + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); goto end; } @@ -811,7 +811,7 @@ lttng_userspace_probe_location_function_get_instrumentation_type( if (!location || lttng_userspace_probe_location_get_type(location) != LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) { - ERR("Invalid argument(s)"); + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); type = LTTNG_USERSPACE_PROBE_LOCATION_FUNCTION_INSTRUMENTATION_TYPE_UNKNOWN; goto end; } @@ -836,7 +836,7 @@ lttng_userspace_probe_location_function_set_instrumentation_type( LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION || instrumentation_type != LTTNG_USERSPACE_PROBE_LOCATION_FUNCTION_INSTRUMENTATION_TYPE_ENTRY) { - ERR("Invalid argument(s)"); + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); status = LTTNG_USERSPACE_PROBE_LOCATION_STATUS_INVALID; goto end; } @@ -856,7 +856,7 @@ int lttng_userspace_probe_location_tracepoint_get_binary_fd( if (!location || lttng_userspace_probe_location_get_type(location) != LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) { - ERR("Invalid argument(s)"); + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); goto end; } @@ -876,7 +876,7 @@ lttng_userspace_probe_location_function_get_lookup_method( if (!location || lttng_userspace_probe_location_get_type(location) != LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) { - ERR("Invalid argument(s)"); + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); goto end; } @@ -893,7 +893,7 @@ lttng_userspace_probe_location_tracepoint_get_lookup_method( if (!location || lttng_userspace_probe_location_get_type(location) != LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) { - ERR("Invalid argument(s)"); + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); goto end; } @@ -1130,7 +1130,7 @@ int lttng_userspace_probe_location_serialize( struct lttng_userspace_probe_location_comm location_generic_comm; if (!location) { - ERR("Invalid argument(s)"); + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); ret = -LTTNG_ERR_INVALID; goto end; } -- 2.34.1 From b9e63e21bd01c0deeaec2195ba912e38460bc038 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Thu, 10 Sep 2020 12:23:28 -0400 Subject: [PATCH 02/16] userspace-probe: replace explicit null-termination check MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Replace explicit null-termination checks by uses of lttng_buffer_view_contains_string() which provides the same guarantees and ensures the string pointer is within the view. Signed-off-by: Jérémie Galarneau Change-Id: If0038a82ad7dfe1ed0e8cdef7870d5e25d62200d --- src/common/userspace-probe.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/common/userspace-probe.c b/src/common/userspace-probe.c index 9e5429f73..49cc83575 100644 --- a/src/common/userspace-probe.c +++ b/src/common/userspace-probe.c @@ -1212,11 +1212,14 @@ int lttng_userspace_probe_location_function_create_from_payload( binary_path_src = function_name_src + location_function_comm->function_name_len; - if (function_name_src[location_function_comm->function_name_len - 1] != '\0') { + if (lttng_buffer_view_contains_string(&view->buffer, function_name_src, + location_function_comm->function_name_len)) { ret = -LTTNG_ERR_INVALID; goto end; } - if (binary_path_src[location_function_comm->binary_path_len - 1] != '\0') { + + if (lttng_buffer_view_contains_string(&view->buffer, binary_path_src, + location_function_comm->binary_path_len)) { ret = -LTTNG_ERR_INVALID; goto end; } @@ -1300,17 +1303,20 @@ int lttng_userspace_probe_location_tracepoint_create_from_payload( binary_path_src = provider_name_src + location_tracepoint_comm->provider_name_len; - if (probe_name_src[location_tracepoint_comm->probe_name_len - 1] != '\0') { + if (lttng_buffer_view_contains_string(&view->buffer, probe_name_src, + location_tracepoint_comm->probe_name_len)) { ret = -LTTNG_ERR_INVALID; goto end; } - if (provider_name_src[location_tracepoint_comm->provider_name_len - 1] != '\0') { + if (lttng_buffer_view_contains_string(&view->buffer, provider_name_src, + location_tracepoint_comm->provider_name_len)) { ret = -LTTNG_ERR_INVALID; goto end; } - if (binary_path_src[location_tracepoint_comm->binary_path_len - 1] != '\0') { + if (lttng_buffer_view_contains_string(&view->buffer, binary_path_src, + location_tracepoint_comm->binary_path_len)) { ret = -LTTNG_ERR_INVALID; goto end; } -- 2.34.1 From ce83431da09a14449027ef7b2ef9d3f7f82ea674 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Thu, 10 Sep 2020 12:29:50 -0400 Subject: [PATCH 03/16] Tests: clean-up: remove trailing dot in snapshot test statements MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérémie Galarneau Change-Id: I95208d67b18ba806f009b9a6b53d5e2cc38b6c74 --- tests/regression/tools/snapshots/test_kernel | 10 +++++----- .../regression/tools/snapshots/test_kernel_streaming | 2 +- tests/regression/tools/snapshots/ust_test | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/regression/tools/snapshots/test_kernel b/tests/regression/tools/snapshots/test_kernel index 15b7ec176..24b0ad4a2 100755 --- a/tests/regression/tools/snapshots/test_kernel +++ b/tests/regression/tools/snapshots/test_kernel @@ -138,9 +138,9 @@ function test_kernel_local_snapshot_discard () rm -rf $TRACE_PATH if [ x"$FIRST_LINE" != x"$FIRST_LINE_2" ]; then - fail "First snapshot event do not match." + fail "First snapshot event do not match" else - pass "First snapshot event match." + pass "First snapshot event match" fi stop_lttng_tracing_ok $SESSION_NAME @@ -176,9 +176,9 @@ function test_kernel_local_snapshot_overwrite_small_buffers () rm -rf $TRACE_PATH if [ x"$FIRST_LINE" != x"$FIRST_LINE_2" ]; then - pass "First snapshot event do not match." + pass "First snapshot event do not match" else - fail "First snapshot event match." + fail "First snapshot event match" fi stop_lttng_tracing_ok $SESSION_NAME @@ -222,7 +222,7 @@ else isroot=0 fi -skip $isroot "Root access is needed. Skipping all kernel snapshot tests." $NUM_TESTS || +skip $isroot "Root access is needed. Skipping all kernel snapshot tests" $NUM_TESTS || { validate_lttng_modules_present diff --git a/tests/regression/tools/snapshots/test_kernel_streaming b/tests/regression/tools/snapshots/test_kernel_streaming index 0a9fcd5c3..544c2e4db 100755 --- a/tests/regression/tools/snapshots/test_kernel_streaming +++ b/tests/regression/tools/snapshots/test_kernel_streaming @@ -150,7 +150,7 @@ else isroot=0 fi -skip $isroot "Root access is needed. Skipping all kernel streaming tests." $NUM_TESTS || +skip $isroot "Root access is needed. Skipping all kernel streaming tests" $NUM_TESTS || { validate_lttng_modules_present diff --git a/tests/regression/tools/snapshots/ust_test b/tests/regression/tools/snapshots/ust_test index c2563afd8..37475d7fd 100755 --- a/tests/regression/tools/snapshots/ust_test +++ b/tests/regression/tools/snapshots/ust_test @@ -23,12 +23,12 @@ TRACE_PATH=$(mktemp -d) source $TESTDIR/utils/utils.sh if [ ! -x "$TESTAPP_BIN" ]; then - BAIL_OUT "No UST events binary detected." + BAIL_OUT "No UST events binary detected" fi # Need the number of snapshot to do. if [ -z $1 ]; then - BAIL_OUT "A number of snapshot is needed." + BAIL_OUT "A number of snapshot is needed" fi NR_SNAPSHOT=$1 @@ -237,9 +237,9 @@ function test_ust_local_snapshot_small_discard_buffers () rm -rf $TRACE_PATH/ if [ x"$FIRST_LINE" != x"$FIRST_LINE_2" ]; then - fail "First snapshot event do not match." + fail "First snapshot event do not match" else - pass "First snapshot event match." + pass "First snapshot event match" fi stop_lttng_tracing_ok $SESSION_NAME @@ -282,9 +282,9 @@ function test_ust_local_snapshot_small_overwrite_buffers () rm -rf $TRACE_PATH/ if [ x"$FIRST_LINE" != x"$FIRST_LINE_2" ]; then - pass "First snapshot event do not match." + pass "First snapshot event do not match" else - fail "First snapshot event match." + fail "First snapshot event match" fi stop_lttng_tracing_ok $SESSION_NAME -- 2.34.1 From 2f8d430ce8e8429d5278d77ce4627a49c82403bf Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Fri, 11 Sep 2020 11:10:44 -0400 Subject: [PATCH 04/16] Revert "userspace-probe: replace explicit null-termination check" MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This reverts commit b9e63e21bd01c0deeaec2195ba912e38460bc038. Signed-off-by: Jérémie Galarneau Change-Id: I909339dbdcf4a7931ad95c5b4b6af152aa52487c --- src/common/userspace-probe.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/common/userspace-probe.c b/src/common/userspace-probe.c index 49cc83575..9e5429f73 100644 --- a/src/common/userspace-probe.c +++ b/src/common/userspace-probe.c @@ -1212,14 +1212,11 @@ int lttng_userspace_probe_location_function_create_from_payload( binary_path_src = function_name_src + location_function_comm->function_name_len; - if (lttng_buffer_view_contains_string(&view->buffer, function_name_src, - location_function_comm->function_name_len)) { + if (function_name_src[location_function_comm->function_name_len - 1] != '\0') { ret = -LTTNG_ERR_INVALID; goto end; } - - if (lttng_buffer_view_contains_string(&view->buffer, binary_path_src, - location_function_comm->binary_path_len)) { + if (binary_path_src[location_function_comm->binary_path_len - 1] != '\0') { ret = -LTTNG_ERR_INVALID; goto end; } @@ -1303,20 +1300,17 @@ int lttng_userspace_probe_location_tracepoint_create_from_payload( binary_path_src = provider_name_src + location_tracepoint_comm->provider_name_len; - if (lttng_buffer_view_contains_string(&view->buffer, probe_name_src, - location_tracepoint_comm->probe_name_len)) { + if (probe_name_src[location_tracepoint_comm->probe_name_len - 1] != '\0') { ret = -LTTNG_ERR_INVALID; goto end; } - if (lttng_buffer_view_contains_string(&view->buffer, provider_name_src, - location_tracepoint_comm->provider_name_len)) { + if (provider_name_src[location_tracepoint_comm->provider_name_len - 1] != '\0') { ret = -LTTNG_ERR_INVALID; goto end; } - if (lttng_buffer_view_contains_string(&view->buffer, binary_path_src, - location_tracepoint_comm->binary_path_len)) { + if (binary_path_src[location_tracepoint_comm->binary_path_len - 1] != '\0') { ret = -LTTNG_ERR_INVALID; goto end; } -- 2.34.1 From 48c24aea283e0f749e844c2aecc1139b7560fff1 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Thu, 10 Sep 2020 12:23:28 -0400 Subject: [PATCH 05/16] userspace-probe: replace explicit null-termination check MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Replace explicit null-termination checks by uses of lttng_buffer_view_contains_string() which provides the same guarantees and ensures the string pointer is within the view. Signed-off-by: Jérémie Galarneau Change-Id: If0038a82ad7dfe1ed0e8cdef7870d5e25d62200d --- src/common/userspace-probe.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/common/userspace-probe.c b/src/common/userspace-probe.c index 9e5429f73..2ad8bfd91 100644 --- a/src/common/userspace-probe.c +++ b/src/common/userspace-probe.c @@ -1212,11 +1212,14 @@ int lttng_userspace_probe_location_function_create_from_payload( binary_path_src = function_name_src + location_function_comm->function_name_len; - if (function_name_src[location_function_comm->function_name_len - 1] != '\0') { + if (!lttng_buffer_view_contains_string(&view->buffer, function_name_src, + location_function_comm->function_name_len)) { ret = -LTTNG_ERR_INVALID; goto end; } - if (binary_path_src[location_function_comm->binary_path_len - 1] != '\0') { + + if (!lttng_buffer_view_contains_string(&view->buffer, binary_path_src, + location_function_comm->binary_path_len)) { ret = -LTTNG_ERR_INVALID; goto end; } @@ -1300,17 +1303,20 @@ int lttng_userspace_probe_location_tracepoint_create_from_payload( binary_path_src = provider_name_src + location_tracepoint_comm->provider_name_len; - if (probe_name_src[location_tracepoint_comm->probe_name_len - 1] != '\0') { + if (!lttng_buffer_view_contains_string(&view->buffer, probe_name_src, + location_tracepoint_comm->probe_name_len)) { ret = -LTTNG_ERR_INVALID; goto end; } - if (provider_name_src[location_tracepoint_comm->provider_name_len - 1] != '\0') { + if (!lttng_buffer_view_contains_string(&view->buffer, provider_name_src, + location_tracepoint_comm->provider_name_len)) { ret = -LTTNG_ERR_INVALID; goto end; } - if (binary_path_src[location_tracepoint_comm->binary_path_len - 1] != '\0') { + if (!lttng_buffer_view_contains_string(&view->buffer, binary_path_src, + location_tracepoint_comm->binary_path_len)) { ret = -LTTNG_ERR_INVALID; goto end; } -- 2.34.1 From 808cb744a46310d2e5fbbe9ab025cd427bcfc0a8 Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Mon, 24 Aug 2020 15:50:49 -0400 Subject: [PATCH 06/16] Introduce kernel-probe locations MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Kernel probe can be configured by two type of location. The first one is via address: lttng_kernel_probe_location_address_create. The second one is using a symbol name combined with an offset. Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: Icd280e7a8403c761987472f0a416ef5188a2068d --- .gitignore | 1 + include/Makefile.am | 4 +- include/lttng/kernel-probe-internal.h | 94 +++++ include/lttng/kernel-probe.h | 87 ++++ src/common/Makefile.am | 1 + src/common/kernel-probe.c | 564 ++++++++++++++++++++++++++ tests/unit/Makefile.am | 10 +- tests/unit/test_kernel_probe.c | 169 ++++++++ 8 files changed, 927 insertions(+), 3 deletions(-) create mode 100644 include/lttng/kernel-probe-internal.h create mode 100644 include/lttng/kernel-probe.h create mode 100644 src/common/kernel-probe.c create mode 100644 tests/unit/test_kernel_probe.c diff --git a/.gitignore b/.gitignore index 49aa0d1c1..f23a72b9c 100644 --- a/.gitignore +++ b/.gitignore @@ -142,6 +142,7 @@ health_check /tests/perf/test_perf_raw /tests/unit/test_string_utils /tests/unit/test_buffer_view +/tests/unit/test_kernel_probe /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/include/Makefile.am b/include/Makefile.am index baad620dd..cd2877193 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -117,7 +117,8 @@ lttnginclude_HEADERS = \ lttng/destruction-handle.h \ lttng/clear.h \ lttng/clear-handle.h \ - lttng/tracker.h + lttng/tracker.h \ + lttng/kernel-probe.h lttngactioninclude_HEADERS= \ lttng/action/action.h \ @@ -171,5 +172,6 @@ noinst_HEADERS = \ lttng/userspace-probe-internal.h \ lttng/session-internal.h \ lttng/session-descriptor-internal.h \ + lttng/kernel-probe-internal.h \ version.h \ version.i diff --git a/include/lttng/kernel-probe-internal.h b/include/lttng/kernel-probe-internal.h new file mode 100644 index 000000000..680f3afab --- /dev/null +++ b/include/lttng/kernel-probe-internal.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2020 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_KERNEL_PROBE_INTERNAL_H +#define LTTNG_KERNEL_PROBE_INTERNAL_H + +#include +#include +#include +#include +#include +#include + +struct lttng_payload; +struct lttng_payload_view; +struct lttng_dynamic_buffer; + +typedef bool (*kernel_probe_location_equal_cb)( + const struct lttng_kernel_probe_location *a, + const struct lttng_kernel_probe_location *b); +typedef int (*kernel_probe_location_serialize_cb)( + const struct lttng_kernel_probe_location *kernel_probe_location, + struct lttng_payload *payload); +typedef bool (*kernel_probe_location_equal_cb)( + const struct lttng_kernel_probe_location *a, + const struct lttng_kernel_probe_location *b); +typedef ssize_t (*kernel_probe_location_create_from_payload_cb)( + struct lttng_payload_view *view, + struct lttng_kernel_probe_location **kernel_probe_location); + +struct lttng_kernel_probe_location_comm { + /* enum lttng_kernel_probe_location_type */ + int8_t type; + /* + * Payload is composed of, in that order, + * - type-specific payload + */ + char payload[]; +} LTTNG_PACKED; + +struct lttng_kernel_probe_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_probe_location_address_comm { + uint64_t address; +} LTTNG_PACKED; + +/* Common ancestor of all kernel probe locations. */ +struct lttng_kernel_probe_location { + enum lttng_kernel_probe_location_type type; + kernel_probe_location_equal_cb equal; + kernel_probe_location_serialize_cb serialize; +}; + +struct lttng_kernel_probe_location_symbol { + struct lttng_kernel_probe_location parent; + char *symbol_name; + uint64_t offset; +}; + +struct lttng_kernel_probe_location_address { + struct lttng_kernel_probe_location parent; + uint64_t address; +}; + +LTTNG_HIDDEN +int lttng_kernel_probe_location_serialize( + const struct lttng_kernel_probe_location *location, + struct lttng_payload *payload); + +LTTNG_HIDDEN +ssize_t lttng_kernel_probe_location_create_from_payload( + struct lttng_payload_view *view, + struct lttng_kernel_probe_location **probe_location); + +LTTNG_HIDDEN +bool lttng_kernel_probe_location_is_equal( + const struct lttng_kernel_probe_location *a, + const struct lttng_kernel_probe_location *b); + +#endif /* LTTNG_KERNEL_PROBE_INTERNAL_H */ diff --git a/include/lttng/kernel-probe.h b/include/lttng/kernel-probe.h new file mode 100644 index 000000000..4536fd5ef --- /dev/null +++ b/include/lttng/kernel-probe.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2020 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_KERNEL_PROBE_H +#define LTTNG_KERNEL_PROBE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct lttng_kernel_probe_location; + +enum lttng_kernel_probe_location_status { + LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK = 0, + /* Invalid parameters provided. */ + LTTNG_KERNEL_PROBE_LOCATION_STATUS_INVALID = -1, +}; + +enum lttng_kernel_probe_location_type { + LTTNG_KERNEL_PROBE_LOCATION_TYPE_UNKNOWN = -1, + /* Location derived from a symbol and an offset. */ + LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET = 0, + /* Location derived from an address. */ + LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS = 1, +}; + +/* + * Get the type of the kernel probe location. + */ +extern enum lttng_kernel_probe_location_type +lttng_kernel_probe_location_get_type( + const struct lttng_kernel_probe_location *location); + +/* + * Destroy the kernel probe location. + */ +extern void lttng_kernel_probe_location_destroy( + struct lttng_kernel_probe_location *location); + +/* + * Create a symbol derived probe location. + * On failure, NULL is returned. + */ +extern struct lttng_kernel_probe_location * +lttng_kernel_probe_location_symbol_create(const char *symbol_name, + uint64_t offset); + +/* + * Get the symbol name of a symbol derived probe location. + */ +extern const char *lttng_kernel_probe_location_symbol_get_name( + const struct lttng_kernel_probe_location *location); + +/* + * Get the offset of a symbol derived location. + */ +extern enum lttng_kernel_probe_location_status +lttng_kernel_probe_location_symbol_get_offset( + const struct lttng_kernel_probe_location *location, + uint64_t *offset); + +/* + * Create an address derived probe location. + * On failure, NULL is returned. + */ +extern struct lttng_kernel_probe_location * +lttng_kernel_probe_location_address_create(uint64_t address); + +/* + * Get the address of an address derived probe location. + */ +extern enum lttng_kernel_probe_location_status +lttng_kernel_probe_location_address_get_address( + const struct lttng_kernel_probe_location *location, + uint64_t *offset); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_KERNEL_PROBE_H */ diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 6ee43e48f..0905e899f 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -52,6 +52,7 @@ libcommon_la_SOURCES = \ fd-handle.c fd-handle.h \ fs-handle.c fs-handle.h fs-handle-internal.h \ futex.c futex.h \ + kernel-probe.c \ location.c \ mi-lttng.c mi-lttng.h \ notification.c \ diff --git a/src/common/kernel-probe.c b/src/common/kernel-probe.c new file mode 100644 index 000000000..1934a76b1 --- /dev/null +++ b/src/common/kernel-probe.c @@ -0,0 +1,564 @@ +/* + * 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 + +static +int lttng_kernel_probe_location_address_serialize( + const struct lttng_kernel_probe_location *location, + struct lttng_payload *payload); + +static +int lttng_kernel_probe_location_symbol_serialize( + const struct lttng_kernel_probe_location *location, + struct lttng_payload *payload); + +static +bool lttng_kernel_probe_location_address_is_equal( + const struct lttng_kernel_probe_location *a, + const struct lttng_kernel_probe_location *b); + +static +bool lttng_kernel_probe_location_symbol_is_equal( + const struct lttng_kernel_probe_location *a, + const struct lttng_kernel_probe_location *b); + +enum lttng_kernel_probe_location_type lttng_kernel_probe_location_get_type( + const struct lttng_kernel_probe_location *location) +{ + return location ? location->type : + LTTNG_KERNEL_PROBE_LOCATION_TYPE_UNKNOWN; +} + +static +void lttng_kernel_probe_location_address_destroy( + struct lttng_kernel_probe_location *location) +{ + assert(location); + free(location); +} + +static +void lttng_kernel_probe_location_symbol_destroy( + struct lttng_kernel_probe_location *location) +{ + struct lttng_kernel_probe_location_symbol *location_symbol = NULL; + + assert(location); + + location_symbol = container_of(location, + struct lttng_kernel_probe_location_symbol, + parent); + + assert(location_symbol); + + free(location_symbol->symbol_name); + free(location); +} + +void lttng_kernel_probe_location_destroy( + struct lttng_kernel_probe_location *location) +{ + if (!location) { + return; + } + + switch (location->type) { + case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS: + lttng_kernel_probe_location_address_destroy(location); + break; + case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET: + lttng_kernel_probe_location_symbol_destroy(location); + break; + default: + abort(); + } +} + +struct lttng_kernel_probe_location * +lttng_kernel_probe_location_address_create(uint64_t address) +{ + struct lttng_kernel_probe_location *ret = NULL; + struct lttng_kernel_probe_location_address *location; + + location = zmalloc(sizeof(*location)); + if (!location) { + PERROR("Error allocating userspace probe location"); + goto end; + } + + location->address = address; + + ret = &location->parent; + ret->type = LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS; + ret->equal = lttng_kernel_probe_location_address_is_equal; + ret->serialize = lttng_kernel_probe_location_address_serialize; + +end: + return ret; +} + +struct lttng_kernel_probe_location * +lttng_kernel_probe_location_symbol_create(const char *symbol_name, + uint64_t offset) +{ + char *symbol_name_copy = NULL; + struct lttng_kernel_probe_location *ret = NULL; + struct lttng_kernel_probe_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 probe location"); + goto error; + } + + location->symbol_name = symbol_name_copy; + location->offset = offset; + + ret = &location->parent; + ret->type = LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET; + ret->equal = lttng_kernel_probe_location_symbol_is_equal; + ret->serialize = lttng_kernel_probe_location_symbol_serialize; + goto end; + +error: + free(symbol_name_copy); +end: + return ret; +} + +enum lttng_kernel_probe_location_status +lttng_kernel_probe_location_address_get_address( + const struct lttng_kernel_probe_location *location, + uint64_t *offset) +{ + enum lttng_kernel_probe_location_status ret = + LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK; + struct lttng_kernel_probe_location_address *address_location; + + assert(offset); + + if (!location || lttng_kernel_probe_location_get_type(location) != + LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + ret = LTTNG_KERNEL_PROBE_LOCATION_STATUS_INVALID; + goto end; + } + + address_location = container_of(location, + struct lttng_kernel_probe_location_address, parent); + *offset = address_location->address; +end: + return ret; +} + +const char *lttng_kernel_probe_location_symbol_get_name( + const struct lttng_kernel_probe_location *location) +{ + const char *ret = NULL; + struct lttng_kernel_probe_location_symbol *symbol_location; + + if (!location || lttng_kernel_probe_location_get_type(location) != + LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + goto end; + } + + symbol_location = container_of(location, + struct lttng_kernel_probe_location_symbol, parent); + ret = symbol_location->symbol_name; +end: + return ret; +} + +enum lttng_kernel_probe_location_status +lttng_kernel_probe_location_symbol_get_offset( + const struct lttng_kernel_probe_location *location, + uint64_t *offset) +{ + enum lttng_kernel_probe_location_status ret = + LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK; + struct lttng_kernel_probe_location_symbol *symbol_location; + + assert(offset); + + if (!location || lttng_kernel_probe_location_get_type(location) != + LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + ret = LTTNG_KERNEL_PROBE_LOCATION_STATUS_INVALID; + goto end; + } + + symbol_location = container_of(location, + struct lttng_kernel_probe_location_symbol, parent); + *offset = symbol_location->offset; +end: + return ret; +} + +static +int lttng_kernel_probe_location_symbol_serialize( + const struct lttng_kernel_probe_location *location, + struct lttng_payload *payload) +{ + int ret; + size_t symbol_name_len; + size_t original_payload_size; + struct lttng_kernel_probe_location_symbol *location_symbol; + struct lttng_kernel_probe_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_probe_location_get_type(location) == + LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET); + + original_payload_size = payload->buffer.size; + location_symbol = container_of(location, + struct lttng_kernel_probe_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_probe_location_address_serialize( + const struct lttng_kernel_probe_location *location, + struct lttng_payload *payload) +{ + int ret; + size_t original_payload_size; + struct lttng_kernel_probe_location_address *location_address; + struct lttng_kernel_probe_location_address_comm location_address_comm; + + assert(location); + assert(lttng_kernel_probe_location_get_type(location) == + LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS); + + original_payload_size = payload->buffer.size; + location_address = container_of(location, + struct lttng_kernel_probe_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_probe_location_serialize( + const struct lttng_kernel_probe_location *location, + struct lttng_payload *payload) +{ + int ret; + size_t original_payload_size; + struct lttng_kernel_probe_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_probe_location_symbol_create_from_payload( + struct lttng_payload_view *view, + struct lttng_kernel_probe_location **location) +{ + struct lttng_kernel_probe_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_probe_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_probe_location_address_create_from_payload( + struct lttng_payload_view *view, + struct lttng_kernel_probe_location **location) +{ + struct lttng_kernel_probe_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_probe_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_probe_location_create_from_payload( + struct lttng_payload_view *view, + struct lttng_kernel_probe_location **location) +{ + struct lttng_kernel_probe_location_comm *probe_location_comm; + enum lttng_kernel_probe_location_type type; + ssize_t consumed = 0; + ssize_t ret; + + assert(view); + assert(location); + + if (view->buffer.size <= sizeof(*probe_location_comm)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + probe_location_comm = (typeof(probe_location_comm)) view->buffer.data; + type = (enum lttng_kernel_probe_location_type) probe_location_comm->type; + consumed += sizeof(*probe_location_comm); + + switch (type) { + case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET: + { + struct lttng_payload_view location_view = + lttng_payload_view_from_view( + view, consumed, -1); + + ret = lttng_kernel_probe_location_symbol_create_from_payload( + &location_view, location); + break; + } + case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS: + { + struct lttng_payload_view location_view = + lttng_payload_view_from_view(view, consumed, -1); + + ret = lttng_kernel_probe_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 +bool lttng_kernel_probe_location_address_is_equal( + const struct lttng_kernel_probe_location *_a, + const struct lttng_kernel_probe_location *_b) +{ + bool is_equal = false; + struct lttng_kernel_probe_location_address *a, *b; + + a = container_of(_a, struct lttng_kernel_probe_location_address, + parent); + b = container_of(_b, struct lttng_kernel_probe_location_address, + parent); + + if (a->address != b->address) { + goto end; + } + + is_equal = true; + +end: + return is_equal; +} + +static +bool lttng_kernel_probe_location_symbol_is_equal( + const struct lttng_kernel_probe_location *_a, + const struct lttng_kernel_probe_location *_b) +{ + bool is_equal = false; + struct lttng_kernel_probe_location_symbol *a, *b; + + a = container_of(_a, struct lttng_kernel_probe_location_symbol, + parent); + b = container_of(_b, struct lttng_kernel_probe_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_probe_location_is_equal( + const struct lttng_kernel_probe_location *a, + const struct lttng_kernel_probe_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; +} diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 338772068..ae086ce90 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -24,7 +24,8 @@ TESTS = test_kernel_data \ test_uuid \ test_buffer_view \ test_payload \ - test_unix_socket + test_unix_socket \ + test_kernel_probe LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la @@ -45,7 +46,8 @@ noinst_PROGRAMS = test_uri test_session test_kernel_data \ test_fd_tracker test_uuid \ test_buffer_view \ test_payload \ - test_unix_socket + test_unix_socket \ + test_kernel_probe if HAVE_LIBLTTNG_UST_CTL noinst_PROGRAMS += test_ust_data @@ -214,3 +216,7 @@ test_payload_LDADD = $(LIBTAP) $(LIBSESSIOND_COMM) $(LIBCOMMON) # unix socket test test_unix_socket_SOURCES = test_unix_socket.c test_unix_socket_LDADD = $(LIBTAP) $(LIBSESSIOND_COMM) $(LIBCOMMON) + +# Kernel probe location api test +test_kernel_probe_SOURCES = test_kernel_probe.c +test_kernel_probe_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) diff --git a/tests/unit/test_kernel_probe.c b/tests/unit/test_kernel_probe.c new file mode 100644 index 000000000..33ac1daf4 --- /dev/null +++ b/tests/unit/test_kernel_probe.c @@ -0,0 +1,169 @@ +/* + * Unit tests for the kernel probe location 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 24 + +static void test_kernel_probe_location_address(void) +{ + struct lttng_kernel_probe_location *location = NULL; + struct lttng_kernel_probe_location *location_from_buffer = NULL; + enum lttng_kernel_probe_location_status status; + enum lttng_kernel_probe_location_type type; + uint64_t address = 50, _address; + struct lttng_payload payload; + + diag("Testing kernel probe location address"); + + lttng_payload_init(&payload); + + location = lttng_kernel_probe_location_address_create(address); + ok(location, "Location object"); + + type = lttng_kernel_probe_location_get_type(location); + ok(LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS == type, + "Location type got %d expected %d", type, + LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS); + + status = lttng_kernel_probe_location_address_get_address( + location, &_address); + ok(status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK, "Getting address"); + ok(address == _address, + "Address is equal. Got %" PRIu64 " expected %" PRIu64, + _address, address); + + ok(lttng_kernel_probe_location_serialize(location, &payload) > 0, + "Serializing"); + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + ok(lttng_kernel_probe_location_create_from_payload( + &view, &location_from_buffer) > 0, + "Deserializing"); + } + + type = lttng_kernel_probe_location_get_type(location_from_buffer); + ok(LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS == type, + "Location from buffer type got %d expected %d", type, + LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS); + + status = lttng_kernel_probe_location_address_get_address( + location_from_buffer, &_address); + ok(status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK, "Getting address"); + ok(address == _address, + "Address from buffer is equal. Got %" PRIu64 + " expected %" PRIu64, + _address, address); + + ok(lttng_kernel_probe_location_is_equal(location, location_from_buffer), + "serialized and from buffer are equal"); + + lttng_payload_reset(&payload); + lttng_kernel_probe_location_destroy(location); + lttng_kernel_probe_location_destroy(location_from_buffer); +} + +static void test_kernel_probe_location_symbol(void) +{ + struct lttng_kernel_probe_location *location = NULL; + struct lttng_kernel_probe_location *location_from_buffer = NULL; + enum lttng_kernel_probe_location_status status; + enum lttng_kernel_probe_location_type type; + uint64_t offset = 50, _offset; + const char *symbol = "Une_bonne", *_symbol; + struct lttng_payload payload; + + diag("Testing kernel probe location symbol"); + + lttng_payload_init(&payload); + + location = lttng_kernel_probe_location_symbol_create(symbol, offset); + ok(location, "Location object"); + + type = lttng_kernel_probe_location_get_type(location); + ok(LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET == type, + "Location type got %d expected %d", type, + LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET); + + _symbol = lttng_kernel_probe_location_symbol_get_name(location); + ok(_symbol, "Getting symbol name"); + ok(!strncmp(symbol, _symbol, strlen(symbol)), + "Symbol name is equal. Got %s, expected %s", _symbol, + symbol); + + status = lttng_kernel_probe_location_symbol_get_offset( + location, &_offset); + ok(status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK, "Getting offset"); + ok(offset == _offset, + "Offset is equal. Got %" PRIu64 " expected %" PRIu64, + _offset, offset); + + ok(lttng_kernel_probe_location_serialize(location, &payload) > 0, + "Serializing"); + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + ok(lttng_kernel_probe_location_create_from_payload( + &view, &location_from_buffer) > 0, + "Deserializing"); + } + + type = lttng_kernel_probe_location_get_type(location_from_buffer); + ok(LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET == type, + "Location from buffer type got %d expected %d", type, + LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET); + + _symbol = lttng_kernel_probe_location_symbol_get_name( + location_from_buffer); + ok(_symbol, "Getting symbol name"); + ok(!strncmp(symbol, _symbol, strlen(symbol)), + "Symbol name is equal. Got %s, expected %s", _symbol, + symbol); + + status = lttng_kernel_probe_location_symbol_get_offset( + location_from_buffer, &_offset); + ok(status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK, "Getting offset"); + ok(offset == _offset, + "Offset is equal. Got %" PRIu64 " expected %" PRIu64, + _offset, offset); + + ok(lttng_kernel_probe_location_is_equal(location, location_from_buffer), + "serialized and from buffer are equal"); + + lttng_payload_reset(&payload); + lttng_kernel_probe_location_destroy(location); + lttng_kernel_probe_location_destroy(location_from_buffer); +} + +int main(int argc, const char *argv[]) +{ + plan_tests(NUM_TESTS); + test_kernel_probe_location_address(); + test_kernel_probe_location_symbol(); + return exit_status(); +} -- 2.34.1 From 7a3dcaf60e26cbedf0363a6bd0da7116e013bc80 Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Wed, 11 Mar 2020 13:50:08 -0400 Subject: [PATCH 07/16] event-rule: lttng_event_rule base object MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit A lttng_event_rule object is the base object representing an event-rule. We plan on using the event-rule object for compositing with a new condition type. This also paves the way toward further improvement of the lttng_event related APIs. Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: I5bbfa3b75337f040cf7e565447d4a4af590ed043 --- configure.ac | 3 + include/Makefile.am | 4 + .../lttng/event-rule/event-rule-internal.h | 123 ++++++++ include/lttng/event-rule/event-rule.h | 58 ++++ include/lttng/lttng.h | 1 + src/common/Makefile.am | 1 + src/common/event-rule/event-rule.c | 276 ++++++++++++++++++ 7 files changed, 466 insertions(+) create mode 100644 include/lttng/event-rule/event-rule-internal.h create mode 100644 include/lttng/event-rule/event-rule.h create mode 100644 src/common/event-rule/event-rule.c diff --git a/configure.ac b/configure.ac index dd0fb21da..1b6f3fcb0 100644 --- a/configure.ac +++ b/configure.ac @@ -1102,6 +1102,9 @@ AC_SUBST(lttngnotificationincludedir) lttngtriggerincludedir="${includedir}/lttng/trigger" AC_SUBST(lttngtriggerincludedir) +lttngeventruleincludedir="${includedir}/lttng/event-rule" +AC_SUBST(lttngeventruleincludedir) + lttnglibexecdir="${libdir}/lttng/libexec" AC_SUBST(lttnglibexecdir) diff --git a/include/Makefile.am b/include/Makefile.am index cd2877193..fe83588ca 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -143,6 +143,9 @@ lttngnotificationinclude_HEADERS= \ lttngtriggerinclude_HEADERS= \ lttng/trigger/trigger.h +lttngeventruleinclude_HEADERS= \ + lttng/event-rule/event-rule.h + noinst_HEADERS = \ lttng/snapshot-internal.h \ lttng/health-internal.h \ @@ -173,5 +176,6 @@ noinst_HEADERS = \ lttng/session-internal.h \ lttng/session-descriptor-internal.h \ lttng/kernel-probe-internal.h \ + lttng/event-rule/event-rule-internal.h \ version.h \ version.i diff --git a/include/lttng/event-rule/event-rule-internal.h b/include/lttng/event-rule/event-rule-internal.h new file mode 100644 index 000000000..9fb115caf --- /dev/null +++ b/include/lttng/event-rule/event-rule-internal.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_RULE_INTERNAL_H +#define LTTNG_EVENT_RULE_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include + +struct lttng_payload; +struct lttng_payload_view; + +typedef void (*event_rule_destroy_cb)(struct lttng_event_rule *event_rule); +typedef bool (*event_rule_validate_cb)( + const struct lttng_event_rule *event_rule); +typedef int (*event_rule_serialize_cb)( + const struct lttng_event_rule *event_rule, + struct lttng_payload *payload); +typedef bool (*event_rule_equal_cb)(const struct lttng_event_rule *a, + const struct lttng_event_rule *b); +typedef ssize_t (*event_rule_create_from_payload_cb)( + struct lttng_payload_view *view, + struct lttng_event_rule **event_rule); +typedef enum lttng_error_code (*event_rule_generate_filter_bytecode_cb)( + struct lttng_event_rule *event_rule, uid_t uid, gid_t gid); +typedef const char *(*event_rule_get_filter_cb)( + const struct lttng_event_rule *event_rule); +typedef const struct lttng_filter_bytecode *( + *event_rule_get_filter_bytecode_cb)( + const struct lttng_event_rule *event_rule); +typedef struct lttng_event_exclusion *(*event_rule_generate_exclusions_cb)( + const struct lttng_event_rule *event_rule); + +struct lttng_event_rule { + struct urcu_ref ref; + enum lttng_event_rule_type type; + event_rule_validate_cb validate; + event_rule_serialize_cb serialize; + event_rule_equal_cb equal; + event_rule_destroy_cb destroy; + event_rule_generate_filter_bytecode_cb generate_filter_bytecode; + event_rule_get_filter_cb get_filter; + event_rule_get_filter_bytecode_cb get_filter_bytecode; + event_rule_generate_exclusions_cb generate_exclusions; +}; + +struct lttng_event_rule_comm { + /* enum lttng_event_rule_type */ + int8_t event_rule_type; + char payload[]; +}; + +LTTNG_HIDDEN +void lttng_event_rule_init(struct lttng_event_rule *event_rule, + enum lttng_event_rule_type type); + +LTTNG_HIDDEN +bool lttng_event_rule_validate(const struct lttng_event_rule *event_rule); + +LTTNG_HIDDEN +ssize_t lttng_event_rule_create_from_payload( + struct lttng_payload_view *payload, + struct lttng_event_rule **event_rule); + +LTTNG_HIDDEN +int lttng_event_rule_serialize(const struct lttng_event_rule *event_rule, + struct lttng_payload *payload); + +LTTNG_HIDDEN +bool lttng_event_rule_is_equal(const struct lttng_event_rule *a, + const struct lttng_event_rule *b); + +LTTNG_HIDDEN +bool lttng_event_rule_get(struct lttng_event_rule *rule); + +LTTNG_HIDDEN +void lttng_event_rule_put(struct lttng_event_rule *rule); + +LTTNG_HIDDEN +enum lttng_domain_type lttng_event_rule_get_domain_type( + const struct lttng_event_rule *rule); + +LTTNG_HIDDEN +enum lttng_error_code lttng_event_rule_generate_filter_bytecode( + struct lttng_event_rule *rule, uid_t uid, gid_t gid); + +/* + * If not present/implemented returns NULL. + * Caller DOES NOT own the returned object. + */ +LTTNG_HIDDEN +const char *lttng_event_rule_get_filter(const struct lttng_event_rule *rule); + +/* + * If not present/implemented returns NULL. + * Caller DOES NOT own the returned object. + */ +LTTNG_HIDDEN +const struct lttng_filter_bytecode *lttng_event_rule_get_filter_bytecode( + const struct lttng_event_rule *rule); + +/* + * If not present/implemented return NULL. + * Caller OWNS the returned object. + */ +LTTNG_HIDDEN +struct lttng_event_exclusion *lttng_event_rule_generate_exclusions( + const struct lttng_event_rule *rule); + +LTTNG_HIDDEN +const char *lttng_event_rule_type_str(enum lttng_event_rule_type type); + +#endif /* LTTNG_EVENT_RULE_INTERNAL_H */ diff --git a/include/lttng/event-rule/event-rule.h b/include/lttng/event-rule/event-rule.h new file mode 100644 index 000000000..e097dd488 --- /dev/null +++ b/include/lttng/event-rule/event-rule.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_RULE_H +#define LTTNG_EVENT_RULE_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct lttng_event_rule; + +enum lttng_event_rule_type { + LTTNG_EVENT_RULE_TYPE_UNKNOWN = -1, + LTTNG_EVENT_RULE_TYPE_TRACEPOINT = 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, +}; + +enum lttng_event_rule_status { + LTTNG_EVENT_RULE_STATUS_OK = 0, + LTTNG_EVENT_RULE_STATUS_ERROR = -1, + LTTNG_EVENT_RULE_STATUS_UNKNOWN = -2, + LTTNG_EVENT_RULE_STATUS_INVALID = -3, + LTTNG_EVENT_RULE_STATUS_UNSET = -4, + LTTNG_EVENT_RULE_STATUS_UNSUPPORTED = -5, +}; + +/** + * An event rule describes a set of criteria to be used as a discriminant in + * regards to a set of events. + */ + +/* + * Get the event rule type. + * + * Returns the type of an event rule on success, LTTNG_EVENT_RULE_UNKNOWN on + * error. + */ +extern enum lttng_event_rule_type lttng_event_rule_get_type( + const struct lttng_event_rule *event_rule); + +/* + * Destroy an event rule object. + */ +extern void lttng_event_rule_destroy(struct lttng_event_rule *rule); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_EVENT_RULE_H */ diff --git a/include/lttng/lttng.h b/include/lttng/lttng.h index 6746d7bbf..bfc3bbd7d 100644 --- a/include/lttng/lttng.h +++ b/include/lttng/lttng.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 0905e899f..c29ce68dd 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -48,6 +48,7 @@ libcommon_la_SOURCES = \ error.c error.h \ evaluation.c \ event.c \ + event-rule/event-rule.c \ filter.c filter.h \ fd-handle.c fd-handle.h \ fs-handle.c fs-handle.h fs-handle-internal.h \ diff --git a/src/common/event-rule/event-rule.c b/src/common/event-rule/event-rule.c new file mode 100644 index 000000000..efeb76539 --- /dev/null +++ b/src/common/event-rule/event-rule.c @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include + +enum lttng_event_rule_type lttng_event_rule_get_type( + const struct lttng_event_rule *event_rule) +{ + return event_rule ? event_rule->type : LTTNG_EVENT_RULE_TYPE_UNKNOWN; +} + +LTTNG_HIDDEN +enum lttng_domain_type lttng_event_rule_get_domain_type( + const struct lttng_event_rule *event_rule) +{ + enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; + + switch (lttng_event_rule_get_type(event_rule)) { + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + /* TODO */ + domain_type = LTTNG_DOMAIN_NONE; + break; + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + case LTTNG_EVENT_RULE_TYPE_KPROBE: + case LTTNG_EVENT_RULE_TYPE_KRETPROBE: + case LTTNG_EVENT_RULE_TYPE_UPROBE: + domain_type = LTTNG_DOMAIN_KERNEL; + break; + case LTTNG_EVENT_RULE_TYPE_UNKNOWN: + domain_type = LTTNG_DOMAIN_NONE; + break; + } + + return domain_type; +} + +static void lttng_event_rule_release(struct urcu_ref *ref) +{ + struct lttng_event_rule *event_rule = + container_of(ref, typeof(*event_rule), ref); + + assert(event_rule->destroy); + event_rule->destroy(event_rule); +} + +void lttng_event_rule_destroy(struct lttng_event_rule *event_rule) +{ + lttng_event_rule_put(event_rule); +} + +LTTNG_HIDDEN +bool lttng_event_rule_validate(const struct lttng_event_rule *event_rule) +{ + bool valid; + + if (!event_rule) { + valid = false; + goto end; + } + + if (!event_rule->validate) { + /* Sub-class guarantees that it can never be invalid. */ + valid = true; + goto end; + } + + valid = event_rule->validate(event_rule); +end: + return valid; +} + +LTTNG_HIDDEN +int lttng_event_rule_serialize(const struct lttng_event_rule *event_rule, + struct lttng_payload *payload) +{ + int ret; + struct lttng_event_rule_comm event_rule_comm = {}; + + if (!event_rule) { + ret = -1; + goto end; + } + + event_rule_comm.event_rule_type = (int8_t) event_rule->type; + + ret = lttng_dynamic_buffer_append( + &payload->buffer, &event_rule_comm, sizeof(event_rule_comm)); + if (ret) { + goto end; + } + + ret = event_rule->serialize(event_rule, payload); + if (ret) { + goto end; + } +end: + return ret; +} + +LTTNG_HIDDEN +bool lttng_event_rule_is_equal(const struct lttng_event_rule *a, + const struct lttng_event_rule *b) +{ + bool is_equal = false; + + if (!a || !b) { + goto end; + } + + if (a->type != b->type) { + goto end; + } + + if (a == b) { + is_equal = true; + goto end; + } + + is_equal = a->equal ? a->equal(a, b) : true; +end: + return is_equal; +} + +LTTNG_HIDDEN +ssize_t lttng_event_rule_create_from_payload( + struct lttng_payload_view *view, + struct lttng_event_rule **event_rule) +{ + ssize_t ret, consumed = 0; + const struct lttng_event_rule_comm *event_rule_comm; + event_rule_create_from_payload_cb create_from_payload = NULL; + + if (!view || !event_rule) { + ret = -1; + goto end; + } + + DBG("Deserializing event_rule from payload"); + event_rule_comm = (const struct lttng_event_rule_comm *) view->buffer.data; + consumed += sizeof(*event_rule_comm); + + switch ((enum lttng_event_rule_type) event_rule_comm->event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + /* TODO */ + break; + case LTTNG_EVENT_RULE_TYPE_KPROBE: + /* TODO */ + break; + case LTTNG_EVENT_RULE_TYPE_KRETPROBE: + /* TODO */ + break; + case LTTNG_EVENT_RULE_TYPE_UPROBE: + /* TODO */ + break; + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + /* TODO */ + break; + default: + ERR("Attempted to create event rule of unknown type (%i)", + (int) event_rule_comm->event_rule_type); + ret = -1; + goto end; + } + + assert(create_from_payload); + + { + struct lttng_payload_view child_view = + lttng_payload_view_from_view( + view, consumed, -1); + + ret = create_from_payload(&child_view, event_rule); + if (ret < 0) { + goto end; + } + + consumed += ret; + } + + if (!lttng_event_rule_validate(*event_rule)) { + ret = -1; + goto end; + } + + ret = consumed; +end: + return ret; +} + +LTTNG_HIDDEN +void lttng_event_rule_init(struct lttng_event_rule *event_rule, + enum lttng_event_rule_type type) +{ + urcu_ref_init(&event_rule->ref); + event_rule->type = type; +} + +LTTNG_HIDDEN +bool lttng_event_rule_get(struct lttng_event_rule *event_rule) +{ + return urcu_ref_get_unless_zero(&event_rule->ref); +} + +LTTNG_HIDDEN +void lttng_event_rule_put(struct lttng_event_rule *event_rule) +{ + if (!event_rule) { + return; + } + + assert(event_rule->ref.refcount); + urcu_ref_put(&event_rule->ref, lttng_event_rule_release); +} + +LTTNG_HIDDEN +enum lttng_error_code lttng_event_rule_generate_filter_bytecode( + struct lttng_event_rule *rule, uid_t uid, gid_t gid) +{ + assert(rule->generate_filter_bytecode); + return rule->generate_filter_bytecode(rule, uid, gid); +} + +LTTNG_HIDDEN +const char *lttng_event_rule_get_filter(const struct lttng_event_rule *rule) +{ + assert(rule->get_filter); + return rule->get_filter(rule); +} + +LTTNG_HIDDEN +const struct lttng_filter_bytecode *lttng_event_rule_get_filter_bytecode( + const struct lttng_event_rule *rule) +{ + assert(rule->get_filter_bytecode); + return rule->get_filter_bytecode(rule); +} + +LTTNG_HIDDEN +struct lttng_event_exclusion *lttng_event_rule_generate_exclusions( + const struct lttng_event_rule *rule) +{ + assert(rule->generate_exclusions); + return rule->generate_exclusions(rule); +} + +LTTNG_HIDDEN +const char *lttng_event_rule_type_str(enum lttng_event_rule_type type) +{ + switch (type) { + case LTTNG_EVENT_RULE_TYPE_UNKNOWN: + return "unknown"; + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + return "tracepoint"; + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + return "syscall"; + case LTTNG_EVENT_RULE_TYPE_KPROBE: + return "probe"; + case LTTNG_EVENT_RULE_TYPE_KRETPROBE: + return "function"; + case LTTNG_EVENT_RULE_TYPE_UPROBE: + return "userspace-probe"; + default: + abort(); + } +} -- 2.34.1 From 077192fd1880652a61400b493a0b6cf5bb199435 Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Wed, 11 Mar 2020 13:52:38 -0400 Subject: [PATCH 08/16] event-rule: introduce event-rule kprobe MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This is the "--probe" option of the enable-event command line. Change-Id: I6d763df53d5e838ea2266d49b1f22bc23a9addb1 Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau --- include/Makefile.am | 4 +- include/lttng/event-rule/kprobe-internal.h | 39 ++ include/lttng/event-rule/kprobe.h | 84 +++++ include/lttng/kernel-probe-internal.h | 4 + include/lttng/lttng.h | 1 + src/common/Makefile.am | 1 + src/common/event-rule/event-rule.c | 5 +- src/common/event-rule/kprobe.c | 413 +++++++++++++++++++++ src/common/kernel-probe.c | 109 +++++- 9 files changed, 656 insertions(+), 4 deletions(-) create mode 100644 include/lttng/event-rule/kprobe-internal.h create mode 100644 include/lttng/event-rule/kprobe.h create mode 100644 src/common/event-rule/kprobe.c diff --git a/include/Makefile.am b/include/Makefile.am index fe83588ca..3ba967e0a 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -144,7 +144,8 @@ lttngtriggerinclude_HEADERS= \ lttng/trigger/trigger.h lttngeventruleinclude_HEADERS= \ - lttng/event-rule/event-rule.h + lttng/event-rule/event-rule.h \ + lttng/event-rule/kprobe.h noinst_HEADERS = \ lttng/snapshot-internal.h \ @@ -177,5 +178,6 @@ noinst_HEADERS = \ lttng/session-descriptor-internal.h \ lttng/kernel-probe-internal.h \ lttng/event-rule/event-rule-internal.h \ + lttng/event-rule/kprobe-internal.h \ version.h \ version.i diff --git a/include/lttng/event-rule/kprobe-internal.h b/include/lttng/event-rule/kprobe-internal.h new file mode 100644 index 000000000..7d2ecc8cc --- /dev/null +++ b/include/lttng/event-rule/kprobe-internal.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_RULE_KPROBE_INTERNAL_H +#define LTTNG_EVENT_RULE_KPROBE_INTERNAL_H + +#include +#include +#include +#include + +struct lttng_event_rule_kprobe { + struct lttng_event_rule parent; + char *name; + struct lttng_kernel_probe_location *location; +}; + +struct lttng_event_rule_kprobe_comm { + /* Includes terminator `\0`. */ + uint32_t name_len; + uint32_t location_len; + /* + * Payload is composed of, in that order: + * - name (null terminated), + * - kernel probe location object. + */ + char payload[]; +} LTTNG_PACKED; + +LTTNG_HIDDEN +ssize_t lttng_event_rule_kprobe_create_from_payload( + struct lttng_payload_view *payload, + struct lttng_event_rule **rule); + +#endif /* LTTNG_EVENT_RULE_KPROBE_INTERNAL_H */ diff --git a/include/lttng/event-rule/kprobe.h b/include/lttng/event-rule/kprobe.h new file mode 100644 index 000000000..e3d7563a4 --- /dev/null +++ b/include/lttng/event-rule/kprobe.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_RULE_KPROBE_H +#define LTTNG_EVENT_RULE_KPROBE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +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. + */ +extern enum lttng_event_rule_status lttng_event_rule_kprobe_set_location( + struct lttng_event_rule *rule, + const struct lttng_kernel_probe_location *location); + +/* + * Get the kernel probe location of a kprobe 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_kprobe_get_location( + const struct lttng_event_rule *rule, + const struct lttng_kernel_probe_location **location); + +/* + * Set the name of a kprobe 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_kprobe_set_name( + struct lttng_event_rule *rule, const char *name); + +/* + * Get the name of a kprobe event rule. + * + * The caller does not assume the ownership of the returned name. + * The name shall only only be used for the duration of the event + * rule's lifetime, or before a different name is set. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's name on + * success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is passed, + * or LTTNG_EVENT_RULE_STATUS_UNSET if a name was not set prior to this call. + */ +extern enum lttng_event_rule_status lttng_event_rule_kprobe_get_name( + const struct lttng_event_rule *rule, const char **name); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_EVENT_RULE_KPROBE_H */ diff --git a/include/lttng/kernel-probe-internal.h b/include/lttng/kernel-probe-internal.h index 680f3afab..381c6a411 100644 --- a/include/lttng/kernel-probe-internal.h +++ b/include/lttng/kernel-probe-internal.h @@ -91,4 +91,8 @@ bool lttng_kernel_probe_location_is_equal( const struct lttng_kernel_probe_location *a, const struct lttng_kernel_probe_location *b); +LTTNG_HIDDEN +struct lttng_kernel_probe_location *lttng_kernel_probe_location_copy( + const struct lttng_kernel_probe_location *location); + #endif /* LTTNG_KERNEL_PROBE_INTERNAL_H */ diff --git a/include/lttng/lttng.h b/include/lttng/lttng.h index bfc3bbd7d..a7fef39c3 100644 --- a/include/lttng/lttng.h +++ b/include/lttng/lttng.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include diff --git a/src/common/Makefile.am b/src/common/Makefile.am index c29ce68dd..b31e6c546 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -49,6 +49,7 @@ libcommon_la_SOURCES = \ evaluation.c \ event.c \ event-rule/event-rule.c \ + event-rule/kprobe.c \ filter.c filter.h \ fd-handle.c fd-handle.h \ fs-handle.c fs-handle.h fs-handle-internal.h \ diff --git a/src/common/event-rule/event-rule.c b/src/common/event-rule/event-rule.c index efeb76539..e5736d6f9 100644 --- a/src/common/event-rule/event-rule.c +++ b/src/common/event-rule/event-rule.c @@ -12,6 +12,7 @@ #include #include #include +#include #include enum lttng_event_rule_type lttng_event_rule_get_type( @@ -146,7 +147,7 @@ ssize_t lttng_event_rule_create_from_payload( goto end; } - DBG("Deserializing event_rule from payload"); + DBG("Deserializing event_rule from payload."); event_rule_comm = (const struct lttng_event_rule_comm *) view->buffer.data; consumed += sizeof(*event_rule_comm); @@ -155,7 +156,7 @@ ssize_t lttng_event_rule_create_from_payload( /* TODO */ break; case LTTNG_EVENT_RULE_TYPE_KPROBE: - /* TODO */ + create_from_payload = lttng_event_rule_kprobe_create_from_payload; break; case LTTNG_EVENT_RULE_TYPE_KRETPROBE: /* TODO */ diff --git a/src/common/event-rule/kprobe.c b/src/common/event-rule/kprobe.c new file mode 100644 index 000000000..a5c93e653 --- /dev/null +++ b/src/common/event-rule/kprobe.c @@ -0,0 +1,413 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#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) + +#if (LTTNG_SYMBOL_NAME_LEN == 256) +#define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "255" +#endif + +static void lttng_event_rule_kprobe_destroy(struct lttng_event_rule *rule) +{ + struct lttng_event_rule_kprobe *kprobe; + + kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + + lttng_kernel_probe_location_destroy(kprobe->location); + free(kprobe->name); + free(kprobe); +} + +static bool lttng_event_rule_kprobe_validate( + const struct lttng_event_rule *rule) +{ + bool valid = false; + struct lttng_event_rule_kprobe *kprobe; + + if (!rule) { + goto end; + } + + kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + + /* Required field. */ + if (!kprobe->name) { + ERR("Invalid name event rule: a name must be set."); + goto end; + } + + /* Required field. */ + if(!kprobe->location) { + ERR("Invalid name event rule: a location must be set."); + goto end; + } + + valid = true; +end: + return valid; +} + +static int lttng_event_rule_kprobe_serialize( + const struct lttng_event_rule *rule, + struct lttng_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; + + if (!rule || !IS_KPROBE_EVENT_RULE(rule)) { + ret = -1; + goto end; + } + + header_offset = payload->buffer.size; + + DBG("Serializing kprobe event rule."); + kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + + name_len = strlen(kprobe->name) + 1; + kprobe_comm.name_len = name_len; + + ret = lttng_dynamic_buffer_append( + &payload->buffer, &kprobe_comm, sizeof(kprobe_comm)); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, kprobe->name, name_len); + if (ret) { + goto end; + } + + size_before_location = payload->buffer.size; + + ret = lttng_kernel_probe_location_serialize(kprobe->location, payload); + if (ret < 0) { + goto end; + } + + /* Update the header regarding the probe size. */ + header = (struct lttng_event_rule_kprobe_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_kprobe_is_equal(const struct lttng_event_rule *_a, + const struct lttng_event_rule *_b) +{ + bool is_equal = false; + struct lttng_event_rule_kprobe *a, *b; + + a = container_of(_a, struct lttng_event_rule_kprobe, parent); + b = container_of(_b, struct lttng_event_rule_kprobe, parent); + + /* Quick checks */ + if (!!a->name != !!b->name) { + goto end; + } + + /* Long check */ + assert(a->name); + assert(b->name); + if (strcmp(a->name, b->name)) { + goto end; + } + + is_equal = lttng_kernel_probe_location_is_equal( + a->location, b->location); +end: + return is_equal; +} + +static enum lttng_error_code lttng_event_rule_kprobe_generate_filter_bytecode( + struct lttng_event_rule *rule, uid_t uid, gid_t gid) +{ + /* Nothing to do. */ + return LTTNG_OK; +} + +static const char *lttng_event_rule_kprobe_get_filter( + const struct lttng_event_rule *rule) +{ + /* Not supported. */ + return NULL; +} + +static const struct lttng_filter_bytecode * +lttng_event_rule_kprobe_get_filter_bytecode(const struct lttng_event_rule *rule) +{ + /* Not supported. */ + return NULL; +} + +static struct lttng_event_exclusion * +lttng_event_rule_kprobe_generate_exclusions(const struct lttng_event_rule *rule) +{ + /* Not supported. */ + return NULL; +} + +struct lttng_event_rule *lttng_event_rule_kprobe_create() +{ + struct lttng_event_rule *rule = NULL; + struct lttng_event_rule_kprobe *krule; + + krule = zmalloc(sizeof(struct lttng_event_rule_kprobe)); + 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; + krule->parent.generate_filter_bytecode = + lttng_event_rule_kprobe_generate_filter_bytecode; + krule->parent.get_filter = lttng_event_rule_kprobe_get_filter; + krule->parent.get_filter_bytecode = + lttng_event_rule_kprobe_get_filter_bytecode; + krule->parent.generate_exclusions = + lttng_event_rule_kprobe_generate_exclusions; +end: + return rule; +} + +LTTNG_HIDDEN +ssize_t lttng_event_rule_kprobe_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 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; + + if (!_event_rule) { + ret = -1; + goto end; + } + + if (view->buffer.size < sizeof(*kprobe_comm)) { + ERR("Failed to initialize from malformed event rule kprobe: buffer too short to contain header."); + ret = -1; + goto end; + } + + current_buffer_view = lttng_buffer_view_from_view( + &view->buffer, offset, sizeof(*kprobe_comm)); + kprobe_comm = (typeof(kprobe_comm)) current_buffer_view.data; + if (!kprobe_comm) { + ret = -1; + goto end; + } + + rule = lttng_event_rule_kprobe_create(); + if (!rule) { + ERR("Failed to create event rule kprobe."); + ret = -1; + goto end; + } + + kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + + /* Skip to payload */ + offset += current_buffer_view.size; + + { + /* Map the name. */ + struct lttng_payload_view current_payload_view = + lttng_payload_view_from_view(view, offset, + kprobe_comm->name_len); + + name = current_payload_view.buffer.data; + if (!name) { + ret = -1; + goto end; + } + + if (!lttng_buffer_view_contains_string( + ¤t_payload_view.buffer, name, + kprobe_comm->name_len)) { + ret = -1; + goto end; + } + } + + /* Skip after the name. */ + offset += kprobe_comm->name_len; + + /* Map the kernel probe location. */ + { + struct lttng_payload_view current_payload_view = + lttng_payload_view_from_view(view, offset, + kprobe_comm->location_len); + + ret = lttng_kernel_probe_location_create_from_payload( + ¤t_payload_view, &location); + if (ret < 0) { + ret = -1; + goto end; + } + } + + if (ret != kprobe_comm->location_len) { + ret = -1; + goto end; + } + + kprobe->location = location; + + /* Skip after the location */ + offset += kprobe_comm->location_len; + + status = lttng_event_rule_kprobe_set_name(rule, name); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set event rule kprobe name."); + ret = -1; + goto end; + } + + *_event_rule = rule; + rule = NULL; + ret = offset; +end: + lttng_event_rule_destroy(rule); + return ret; +} + +enum lttng_event_rule_status lttng_event_rule_kprobe_set_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( + 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; + + 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 = kprobe->location; + + if (!*location) { + status = LTTNG_EVENT_RULE_STATUS_UNSET; + goto end; + } + +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_kprobe_set_name( + struct lttng_event_rule *rule, const char *name) +{ + char *name_copy = NULL; + struct lttng_event_rule_kprobe *kprobe; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_KPROBE_EVENT_RULE(rule) || !name || + strlen(name) == 0) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + name_copy = strdup(name); + if (!name_copy) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + + free(kprobe->name); + + kprobe->name = name_copy; + name_copy = NULL; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_kprobe_get_name( + const struct lttng_event_rule *rule, const char **name) +{ + struct lttng_event_rule_kprobe *kprobe; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_KPROBE_EVENT_RULE(rule) || !name) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + if (!kprobe->name) { + status = LTTNG_EVENT_RULE_STATUS_UNSET; + goto end; + } + + *name = kprobe->name; +end: + return status; +} diff --git a/src/common/kernel-probe.c b/src/common/kernel-probe.c index 1934a76b1..9a84727ca 100644 --- a/src/common/kernel-probe.c +++ b/src/common/kernel-probe.c @@ -99,7 +99,7 @@ lttng_kernel_probe_location_address_create(uint64_t address) location = zmalloc(sizeof(*location)); if (!location) { - PERROR("Error allocating userspace probe location"); + PERROR("Error allocating userspace probe location."); goto end; } @@ -562,3 +562,110 @@ bool lttng_kernel_probe_location_is_equal( end: return is_equal; } + +static struct lttng_kernel_probe_location * +lttng_kernel_probe_location_symbol_copy( + const struct lttng_kernel_probe_location *location) +{ + struct lttng_kernel_probe_location *new_location = NULL; + struct lttng_kernel_probe_location_symbol *symbol_location; + enum lttng_kernel_probe_location_status status; + const char *symbol_name = NULL; + uint64_t offset; + + assert(location); + assert(location->type == LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET); + symbol_location = container_of( + location, typeof(*symbol_location), parent); + + /* Get probe location offset */ + status = lttng_kernel_probe_location_symbol_get_offset(location, &offset); + if (status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) { + ERR("Get kernel probe location offset failed."); + goto error; + } + + symbol_name = lttng_kernel_probe_location_symbol_get_name(location); + if (!symbol_name) { + ERR("Kernel probe symbol name is NULL."); + goto error; + } + + /* Create the probe_location */ + new_location = lttng_kernel_probe_location_symbol_create( + symbol_name, offset); + + goto end; + +error: + new_location = NULL; +end: + return new_location; +} +static struct lttng_kernel_probe_location * +lttng_kernel_probe_location_address_copy( + const struct lttng_kernel_probe_location *location) +{ + struct lttng_kernel_probe_location *new_location = NULL; + struct lttng_kernel_probe_location_address *address_location; + enum lttng_kernel_probe_location_status status; + uint64_t address; + + assert(location); + assert(location->type == LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS); + address_location = container_of( + location, typeof(*address_location), parent); + + + /* Get probe location fields */ + status = lttng_kernel_probe_location_address_get_address(location, &address); + if (status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) { + ERR("Get kernel probe address failed."); + goto error; + } + + /* Create the probe_location */ + new_location = lttng_kernel_probe_location_address_create(address); + + goto end; + +error: + new_location = NULL; +end: + return new_location; +} + +LTTNG_HIDDEN +struct lttng_kernel_probe_location *lttng_kernel_probe_location_copy( + const struct lttng_kernel_probe_location *location) +{ + struct lttng_kernel_probe_location *new_location = NULL; + enum lttng_kernel_probe_location_type type; + + if (!location) { + goto err; + } + + type = lttng_kernel_probe_location_get_type(location); + switch (type) { + case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS: + new_location = + lttng_kernel_probe_location_address_copy(location); + if (!new_location) { + goto err; + } + break; + case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET: + new_location = + lttng_kernel_probe_location_symbol_copy(location); + if (!new_location) { + goto err; + } + break; + default: + new_location = NULL; + goto err; + } +err: + return new_location; +} -- 2.34.1 From e6a39346997db7e1d40026b2cc10be5893723eb3 Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Wed, 11 Mar 2020 13:55:16 -0400 Subject: [PATCH 09/16] event-rule: introduce event rule syscall MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This is the "--syscall" option of the enable-event command line. Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: I9c36cb0440e1f26a3b436782eb437a8ddffbe4fa --- include/Makefile.am | 4 +- include/lttng/event-rule/syscall-internal.h | 46 ++ include/lttng/event-rule/syscall.h | 83 ++++ include/lttng/lttng.h | 1 + src/common/Makefile.am | 1 + src/common/event-rule/event-rule.c | 4 +- src/common/event-rule/syscall.c | 468 ++++++++++++++++++++ 7 files changed, 605 insertions(+), 2 deletions(-) create mode 100644 include/lttng/event-rule/syscall-internal.h create mode 100644 include/lttng/event-rule/syscall.h create mode 100644 src/common/event-rule/syscall.c diff --git a/include/Makefile.am b/include/Makefile.am index 3ba967e0a..92b5c75bd 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -145,7 +145,8 @@ lttngtriggerinclude_HEADERS= \ lttngeventruleinclude_HEADERS= \ lttng/event-rule/event-rule.h \ - lttng/event-rule/kprobe.h + lttng/event-rule/kprobe.h \ + lttng/event-rule/syscall.h noinst_HEADERS = \ lttng/snapshot-internal.h \ @@ -179,5 +180,6 @@ noinst_HEADERS = \ lttng/kernel-probe-internal.h \ lttng/event-rule/event-rule-internal.h \ lttng/event-rule/kprobe-internal.h \ + lttng/event-rule/syscall-internal.h \ version.h \ version.i diff --git a/include/lttng/event-rule/syscall-internal.h b/include/lttng/event-rule/syscall-internal.h new file mode 100644 index 000000000..e99b69ae4 --- /dev/null +++ b/include/lttng/event-rule/syscall-internal.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_RULE_SYSCALL_INTERNAL_H +#define LTTNG_EVENT_RULE_SYSCALL_INTERNAL_H + +#include +#include +#include +#include + +struct lttng_event_rule_syscall { + struct lttng_event_rule parent; + char *pattern; + char *filter_expression; + + /* Internal use only. */ + struct { + char *filter; + struct lttng_filter_bytecode *bytecode; + } internal_filter; +}; + +struct lttng_event_rule_syscall_comm { + /* Includes terminator `\0`. */ + uint32_t pattern_len; + /* Includes terminator `\0`. */ + uint32_t filter_expression_len; + /* + * Payload is composed of, in that order: + * - Pattern (null terminated), + * - Filter expression (null terminated). + */ + char payload[]; +} LTTNG_PACKED; + +LTTNG_HIDDEN +ssize_t lttng_event_rule_syscall_create_from_payload( + struct lttng_payload_view *view, + struct lttng_event_rule **rule); + +#endif /* LTTNG_EVENT_RULE_SYSCALL_INTERNAL_H */ diff --git a/include/lttng/event-rule/syscall.h b/include/lttng/event-rule/syscall.h new file mode 100644 index 000000000..baf8432c1 --- /dev/null +++ b/include/lttng/event-rule/syscall.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_RULE_SYSCALL_H +#define LTTNG_EVENT_RULE_SYSCALL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Create a newly allocated syscall 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_syscall_create(void); + +/* + * Set the pattern of a syscall event rule. + * + * Pattern can contain wildcard '*'. See man lttng-enable-event. + * + * The pattern 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_syscall_set_pattern( + struct lttng_event_rule *rule, const char *pattern); + +/* + * Get the pattern of a syscall event rule. + * + * The caller does not assume the ownership of the returned pattern. The + * pattern shall only only be used for the duration of the event rule's + * lifetime, or before a different pattern is set. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's pattern + * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid + * parameter is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a pattern + * was not set prior to this call. + */ +extern enum lttng_event_rule_status lttng_event_rule_syscall_get_pattern( + const struct lttng_event_rule *rule, const char **pattern); + +/* + * Set the filter expression of a syscall event rule. + * + * The expression is copied 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_syscall_set_filter( + struct lttng_event_rule *rule, const char *expression); + +/* + * Get the filter expression of a syscall event rule. + * + * The caller does not assume the ownership of the returned filter expression. + * The filter expression shall only only be used for the duration of the event + * rule's lifetime, or before a different filter expression is set. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's filter + * expression on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid + * parameter is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a filter expression + * was not set prior to this call. + */ +extern enum lttng_event_rule_status lttng_event_rule_syscall_get_filter( + const struct lttng_event_rule *rule, const char **expression); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_EVENT_RULE_SYSCALL_H */ diff --git a/include/lttng/lttng.h b/include/lttng/lttng.h index a7fef39c3..8686643b8 100644 --- a/include/lttng/lttng.h +++ b/include/lttng/lttng.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include diff --git a/src/common/Makefile.am b/src/common/Makefile.am index b31e6c546..556c55777 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -50,6 +50,7 @@ libcommon_la_SOURCES = \ event.c \ event-rule/event-rule.c \ event-rule/kprobe.c \ + event-rule/syscall.c \ filter.c filter.h \ fd-handle.c fd-handle.h \ fs-handle.c fs-handle.h fs-handle-internal.h \ diff --git a/src/common/event-rule/event-rule.c b/src/common/event-rule/event-rule.c index e5736d6f9..707011586 100644 --- a/src/common/event-rule/event-rule.c +++ b/src/common/event-rule/event-rule.c @@ -13,6 +13,7 @@ #include #include #include +#include #include enum lttng_event_rule_type lttng_event_rule_get_type( @@ -165,7 +166,8 @@ ssize_t lttng_event_rule_create_from_payload( /* TODO */ break; case LTTNG_EVENT_RULE_TYPE_SYSCALL: - /* TODO */ + create_from_payload = + lttng_event_rule_syscall_create_from_payload; break; default: ERR("Attempted to create event rule of unknown type (%i)", diff --git a/src/common/event-rule/syscall.c b/src/common/event-rule/syscall.c new file mode 100644 index 000000000..ef7ccd0f9 --- /dev/null +++ b/src/common/event-rule/syscall.c @@ -0,0 +1,468 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define IS_SYSCALL_EVENT_RULE(rule) \ + (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_SYSCALL) + +static void lttng_event_rule_syscall_destroy(struct lttng_event_rule *rule) +{ + struct lttng_event_rule_syscall *syscall; + + if (rule == NULL) { + return; + } + + syscall = container_of(rule, struct lttng_event_rule_syscall, parent); + + free(syscall->pattern); + free(syscall->filter_expression); + free(syscall->internal_filter.filter); + free(syscall->internal_filter.bytecode); + free(syscall); +} + +static bool lttng_event_rule_syscall_validate( + const struct lttng_event_rule *rule) +{ + bool valid = false; + struct lttng_event_rule_syscall *syscall; + + if (!rule) { + goto end; + } + + syscall = container_of(rule, struct lttng_event_rule_syscall, parent); + + /* Required field. */ + if (!syscall->pattern) { + ERR("Invalid syscall event rule: a pattern must be set."); + goto end; + } + + valid = true; +end: + return valid; +} + +static int lttng_event_rule_syscall_serialize( + const struct lttng_event_rule *rule, + struct lttng_payload *payload) +{ + int ret; + size_t pattern_len, filter_expression_len; + struct lttng_event_rule_syscall *syscall; + struct lttng_event_rule_syscall_comm syscall_comm; + + if (!rule || !IS_SYSCALL_EVENT_RULE(rule)) { + ret = -1; + goto end; + } + + DBG("Serializing syscall event rule"); + syscall = container_of(rule, struct lttng_event_rule_syscall, parent); + + pattern_len = strlen(syscall->pattern) + 1; + + if (syscall->filter_expression != NULL) { + filter_expression_len = strlen(syscall->filter_expression) + 1; + } else { + filter_expression_len = 0; + } + + syscall_comm.pattern_len = pattern_len; + syscall_comm.filter_expression_len = filter_expression_len; + + ret = lttng_dynamic_buffer_append( + &payload->buffer, &syscall_comm, sizeof(syscall_comm)); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append( + &payload->buffer, syscall->pattern, pattern_len); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, + syscall->filter_expression, filter_expression_len); +end: + return ret; +} + +static bool lttng_event_rule_syscall_is_equal(const struct lttng_event_rule *_a, + const struct lttng_event_rule *_b) +{ + bool is_equal = false; + struct lttng_event_rule_syscall *a, *b; + + a = container_of(_a, struct lttng_event_rule_syscall, parent); + b = container_of(_b, struct lttng_event_rule_syscall, parent); + + if (!!a->filter_expression != !!b->filter_expression) { + goto end; + } + + assert(a->pattern); + assert(b->pattern); + if (strcmp(a->pattern, b->pattern)) { + goto end; + } + + if (a->filter_expression && b->filter_expression) { + if (strcmp(a->filter_expression, b->filter_expression)) { + goto end; + } + } else if (!!a->filter_expression != !!b->filter_expression) { + /* One is set and not the other. */ + goto end; + } + + is_equal = true; +end: + return is_equal; +} + +static enum lttng_error_code lttng_event_rule_syscall_generate_filter_bytecode( + struct lttng_event_rule *rule, uid_t uid, gid_t gid) +{ + int ret; + enum lttng_error_code ret_code = LTTNG_OK; + struct lttng_event_rule_syscall *syscall; + enum lttng_event_rule_status status; + const char *filter; + struct lttng_filter_bytecode *bytecode = NULL; + + assert(rule); + + syscall = container_of(rule, struct lttng_event_rule_syscall, parent); + + /* Generate the filter bytecode. */ + status = lttng_event_rule_syscall_get_filter(rule, &filter); + if (status == LTTNG_EVENT_RULE_STATUS_UNSET) { + filter = NULL; + } else if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ret_code = LTTNG_ERR_FILTER_INVAL; + goto end; + } + + if (filter && filter[0] == '\0') { + ret_code = LTTNG_ERR_FILTER_INVAL; + goto end; + } + + if (filter == NULL) { + /* Nothing to do. */ + ret = LTTNG_OK; + goto end; + } + + syscall->internal_filter.filter = strdup(filter); + if (syscall->internal_filter.filter == NULL) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + ret = run_as_generate_filter_bytecode( + syscall->internal_filter.filter, uid, gid, &bytecode); + if (ret) { + ret_code = LTTNG_ERR_FILTER_INVAL; + } + + syscall->internal_filter.bytecode = bytecode; + bytecode = NULL; + +end: + free(bytecode); + return ret_code; +} + +static const char *lttng_event_rule_syscall_get_internal_filter( + const struct lttng_event_rule *rule) +{ + struct lttng_event_rule_syscall *syscall; + + assert(rule); + syscall = container_of(rule, struct lttng_event_rule_syscall, parent); + + return syscall->internal_filter.filter; +} + +static const struct lttng_filter_bytecode * +lttng_event_rule_syscall_get_internal_filter_bytecode( + const struct lttng_event_rule *rule) +{ + struct lttng_event_rule_syscall *syscall; + + assert(rule); + syscall = container_of(rule, struct lttng_event_rule_syscall, parent); + + return syscall->internal_filter.bytecode; +} + +static struct lttng_event_exclusion * +lttng_event_rule_syscall_generate_exclusions( + const struct lttng_event_rule *rule) +{ + /* Not supported. */ + return NULL; +} + +struct lttng_event_rule *lttng_event_rule_syscall_create() +{ + struct lttng_event_rule *rule = NULL; + struct lttng_event_rule_syscall *syscall_rule; + + syscall_rule = zmalloc(sizeof(struct lttng_event_rule_syscall)); + if (!syscall_rule) { + goto end; + } + + rule = &syscall_rule->parent; + lttng_event_rule_init( + &syscall_rule->parent, LTTNG_EVENT_RULE_TYPE_SYSCALL); + syscall_rule->parent.validate = lttng_event_rule_syscall_validate; + syscall_rule->parent.serialize = lttng_event_rule_syscall_serialize; + syscall_rule->parent.equal = lttng_event_rule_syscall_is_equal; + syscall_rule->parent.destroy = lttng_event_rule_syscall_destroy; + syscall_rule->parent.generate_filter_bytecode = + lttng_event_rule_syscall_generate_filter_bytecode; + syscall_rule->parent.get_filter = + lttng_event_rule_syscall_get_internal_filter; + syscall_rule->parent.get_filter_bytecode = + lttng_event_rule_syscall_get_internal_filter_bytecode; + syscall_rule->parent.generate_exclusions = + lttng_event_rule_syscall_generate_exclusions; +end: + return rule; +} + +LTTNG_HIDDEN +ssize_t lttng_event_rule_syscall_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_syscall_comm *syscall_comm; + const char *pattern; + const char *filter_expression = NULL; + struct lttng_buffer_view current_buffer_view; + struct lttng_event_rule *rule = NULL; + + if (!_event_rule) { + ret = -1; + goto end; + } + + if (view->buffer.size < sizeof(*syscall_comm)) { + ERR("Failed to initialize from malformed event rule syscall: buffer too short to contain header"); + ret = -1; + goto end; + } + + current_buffer_view = lttng_buffer_view_from_view( + &view->buffer, offset, sizeof(*syscall_comm)); + syscall_comm = (typeof(syscall_comm)) current_buffer_view.data; + + if (!syscall_comm) { + ret = -1; + goto end; + } + + rule = lttng_event_rule_syscall_create(); + if (!rule) { + ERR("Failed to create event rule syscall"); + ret = -1; + goto end; + } + + /* Skip to payload. */ + offset += current_buffer_view.size; + + /* Map the pattern. */ + current_buffer_view = lttng_buffer_view_from_view( + &view->buffer, offset, syscall_comm->pattern_len); + pattern = current_buffer_view.data; + if (!pattern) { + ret = -1; + goto end; + } + + if (!lttng_buffer_view_contains_string(¤t_buffer_view, pattern, + syscall_comm->pattern_len)) { + ret = -1; + goto end; + } + + /* Skip after the pattern. */ + offset += syscall_comm->pattern_len; + + if (!syscall_comm->filter_expression_len) { + goto skip_filter_expression; + } + + /* Map the filter_expression. */ + current_buffer_view = lttng_buffer_view_from_view(&view->buffer, offset, + syscall_comm->filter_expression_len); + filter_expression = current_buffer_view.data; + if (!filter_expression) { + ret = -1; + goto end; + } + + if (!lttng_buffer_view_contains_string(¤t_buffer_view, + filter_expression, + syscall_comm->filter_expression_len)) { + ret = -1; + goto end; + } + + /* Skip after the pattern. */ + offset += syscall_comm->filter_expression_len; + +skip_filter_expression: + + status = lttng_event_rule_syscall_set_pattern(rule, pattern); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set event rule syscall pattern"); + ret = -1; + goto end; + } + + if (filter_expression) { + status = lttng_event_rule_syscall_set_filter( + rule, filter_expression); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set event rule syscall pattern"); + ret = -1; + goto end; + } + } + + *_event_rule = rule; + rule = NULL; + ret = offset; +end: + lttng_event_rule_destroy(rule); + return ret; +} + +enum lttng_event_rule_status lttng_event_rule_syscall_set_pattern( + struct lttng_event_rule *rule, const char *pattern) +{ + char *pattern_copy = NULL; + struct lttng_event_rule_syscall *syscall; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !pattern || + strlen(pattern) == 0) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + syscall = container_of(rule, struct lttng_event_rule_syscall, parent); + pattern_copy = strdup(pattern); + if (!pattern_copy) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + + if (syscall->pattern) { + free(syscall->pattern); + } + + syscall->pattern = pattern_copy; + pattern_copy = NULL; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_syscall_get_pattern( + const struct lttng_event_rule *rule, const char **pattern) +{ + struct lttng_event_rule_syscall *syscall; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !pattern) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + syscall = container_of(rule, struct lttng_event_rule_syscall, parent); + if (!syscall->pattern) { + status = LTTNG_EVENT_RULE_STATUS_UNSET; + goto end; + } + + *pattern = syscall->pattern; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_syscall_set_filter( + struct lttng_event_rule *rule, const char *expression) +{ + char *expression_copy = NULL; + struct lttng_event_rule_syscall *syscall; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + /* TODO: validate that the passed expression is valid. */ + + if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !expression || + strlen(expression) == 0) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + syscall = container_of(rule, struct lttng_event_rule_syscall, parent); + expression_copy = strdup(expression); + if (!expression_copy) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + + if (syscall->filter_expression) { + free(syscall->filter_expression); + } + + syscall->filter_expression = expression_copy; + expression_copy = NULL; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_syscall_get_filter( + const struct lttng_event_rule *rule, const char **expression) +{ + struct lttng_event_rule_syscall *syscall; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !expression) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + syscall = container_of(rule, struct lttng_event_rule_syscall, parent); + if (!syscall->filter_expression) { + status = LTTNG_EVENT_RULE_STATUS_UNSET; + goto end; + } + + *expression = syscall->filter_expression; +end: + return status; +} -- 2.34.1 From df08d338ff7f7ced42ba49530617b06a73678669 Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Wed, 11 Mar 2020 13:56:36 -0400 Subject: [PATCH 10/16] event-rule: introduce event rule uprobe MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This is the "--userspace-probe" option of the enable-event command line. Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: I43478cb75bd8fdc28e2927d53e0a9a09f2283dea --- include/Makefile.am | 4 +- include/lttng/event-rule/uprobe-internal.h | 45 +++ include/lttng/event-rule/uprobe.h | 83 +++++ include/lttng/lttng.h | 1 + src/common/Makefile.am | 1 + src/common/event-rule/event-rule.c | 3 +- src/common/event-rule/uprobe.c | 410 +++++++++++++++++++++ 7 files changed, 545 insertions(+), 2 deletions(-) create mode 100644 include/lttng/event-rule/uprobe-internal.h create mode 100644 include/lttng/event-rule/uprobe.h create mode 100644 src/common/event-rule/uprobe.c diff --git a/include/Makefile.am b/include/Makefile.am index 92b5c75bd..b5875cd66 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -146,7 +146,8 @@ lttngtriggerinclude_HEADERS= \ lttngeventruleinclude_HEADERS= \ lttng/event-rule/event-rule.h \ lttng/event-rule/kprobe.h \ - lttng/event-rule/syscall.h + lttng/event-rule/syscall.h \ + lttng/event-rule/uprobe.h noinst_HEADERS = \ lttng/snapshot-internal.h \ @@ -181,5 +182,6 @@ noinst_HEADERS = \ lttng/event-rule/event-rule-internal.h \ lttng/event-rule/kprobe-internal.h \ lttng/event-rule/syscall-internal.h \ + lttng/event-rule/uprobe-internal.h \ version.h \ version.i diff --git a/include/lttng/event-rule/uprobe-internal.h b/include/lttng/event-rule/uprobe-internal.h new file mode 100644 index 000000000..6d4b10661 --- /dev/null +++ b/include/lttng/event-rule/uprobe-internal.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_RULE_UPROBE_INTERNAL_H +#define LTTNG_EVENT_RULE_UPROBE_INTERNAL_H + +#include +#include +#include +#include + +struct lttng_event_rule_uprobe { + struct lttng_event_rule parent; + char *name; + struct lttng_userspace_probe_location *location; +}; + +struct lttng_event_rule_uprobe_comm { + /* Includes terminator `\0`. */ + uint32_t name_len; + /* Includes terminator `\0`. */ + uint32_t location_len; + /* + * Payload is composed of, in that order: + * - name (null terminated), + * - user space probe location object. + */ + char payload[]; +} LTTNG_PACKED; + +LTTNG_HIDDEN +ssize_t lttng_event_rule_uprobe_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( + const struct lttng_event_rule *rule); + +#endif /* LTTNG_EVENT_RULE_UPROBE_INTERNAL_H */ diff --git a/include/lttng/event-rule/uprobe.h b/include/lttng/event-rule/uprobe.h new file mode 100644 index 000000000..12aa01318 --- /dev/null +++ b/include/lttng/event-rule/uprobe.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_RULE_UPROBE_H +#define LTTNG_EVENT_RULE_UPROBE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * 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. + */ +extern enum lttng_event_rule_status lttng_event_rule_uprobe_set_location( + struct lttng_event_rule *rule, + const struct lttng_userspace_probe_location *location); + +/* + * Get the location of a uprobe event rule. + * + * The caller does not assume the ownership of the returned location. + * The location shall only be used for the duration of the event + * rule's lifetime, or before a different location is set. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's location + * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is + * passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a location was not set prior to + * this call. + */ +extern enum lttng_event_rule_status lttng_event_rule_uprobe_get_location( + const struct lttng_event_rule *rule, + const struct lttng_userspace_probe_location **location); + +/* + * Set the name of a uprobe event rule. + * + * The name is copied internally. + * + * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_event_rule_status lttng_event_rule_uprobe_set_name( + struct lttng_event_rule *rule, const char *name); + +/* + * Get the name of a uprobe event rule. + * + * The caller does not assume the ownership of the returned name. + * The name shall only only be used for the duration of the event + * rule's lifetime, or before a different name is set. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's name on + * success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is passed, + * or LTTNG_EVENT_RULE_STATUS_UNSET if a name was not set prior to this call. + */ +extern enum lttng_event_rule_status lttng_event_rule_uprobe_get_name( + const struct lttng_event_rule *rule, const char **name); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_EVENT_RULE_UPROBE_H */ diff --git a/include/lttng/lttng.h b/include/lttng/lttng.h index 8686643b8..c248eceb9 100644 --- a/include/lttng/lttng.h +++ b/include/lttng/lttng.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 556c55777..7c2ab3e3a 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -51,6 +51,7 @@ libcommon_la_SOURCES = \ event-rule/event-rule.c \ event-rule/kprobe.c \ event-rule/syscall.c \ + event-rule/uprobe.c \ filter.c filter.h \ fd-handle.c fd-handle.h \ fs-handle.c fs-handle.h fs-handle-internal.h \ diff --git a/src/common/event-rule/event-rule.c b/src/common/event-rule/event-rule.c index 707011586..8e9b003c8 100644 --- a/src/common/event-rule/event-rule.c +++ b/src/common/event-rule/event-rule.c @@ -14,6 +14,7 @@ #include #include #include +#include #include enum lttng_event_rule_type lttng_event_rule_get_type( @@ -163,7 +164,7 @@ ssize_t lttng_event_rule_create_from_payload( /* TODO */ break; case LTTNG_EVENT_RULE_TYPE_UPROBE: - /* TODO */ + create_from_payload = lttng_event_rule_uprobe_create_from_payload; break; case LTTNG_EVENT_RULE_TYPE_SYSCALL: create_from_payload = diff --git a/src/common/event-rule/uprobe.c b/src/common/event-rule/uprobe.c new file mode 100644 index 000000000..8d63975ab --- /dev/null +++ b/src/common/event-rule/uprobe.c @@ -0,0 +1,410 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IS_UPROBE_EVENT_RULE(rule) \ + (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_UPROBE) + +static void lttng_event_rule_uprobe_destroy(struct lttng_event_rule *rule) +{ + struct lttng_event_rule_uprobe *uprobe; + + uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); + + lttng_userspace_probe_location_destroy(uprobe->location); + free(uprobe->name); + free(uprobe); +} + +static bool lttng_event_rule_uprobe_validate( + const struct lttng_event_rule *rule) +{ + bool valid = false; + struct lttng_event_rule_uprobe *uprobe; + + if (!rule) { + goto end; + } + + uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); + + /* Required field. */ + if (!uprobe->name) { + ERR("Invalid uprobe event rule: a pattern must be set."); + goto end; + } + + if (!uprobe->location) { + ERR("Invalid uprobe event rule: a location must be set."); + goto end; + } + + valid = true; +end: + return valid; +} + +static int lttng_event_rule_uprobe_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; + + if (!rule || !IS_UPROBE_EVENT_RULE(rule)) { + ret = -1; + goto end; + } + + header_offset = payload->buffer.size; + + DBG("Serializing uprobe event rule."); + uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); + + name_len = strlen(uprobe->name) + 1; + + uprobe_comm.name_len = name_len; + + ret = lttng_dynamic_buffer_append( + &payload->buffer, &uprobe_comm, sizeof(uprobe_comm)); + if (ret) { + goto end; + } + ret = lttng_dynamic_buffer_append( + &payload->buffer, uprobe->name, name_len); + if (ret) { + goto end; + } + + size_before_probe = payload->buffer.size; + + /* This serialize return the size taken in the buffer. */ + ret = lttng_userspace_probe_location_serialize( + uprobe->location, payload); + if (ret < 0) { + goto end; + } + + /* Update the header regarding the probe size. */ + header = (struct lttng_event_rule_uprobe_comm + *) ((char *) payload->buffer.data + + header_offset); + header->location_len = payload->buffer.size - size_before_probe; + + ret = 0; + +end: + return ret; +} + +static bool lttng_event_rule_uprobe_is_equal(const struct lttng_event_rule *_a, + const struct lttng_event_rule *_b) +{ + bool is_equal = false; + struct lttng_event_rule_uprobe *a, *b; + + a = container_of(_a, struct lttng_event_rule_uprobe, parent); + b = container_of(_b, struct lttng_event_rule_uprobe, parent); + + /* uprobe is invalid if this is not true. */ + assert(a->name); + assert(b->name); + if (strcmp(a->name, b->name)) { + goto end; + } + + assert(a->location); + assert(b->location); + is_equal = lttng_userspace_probe_location_is_equal( + a->location, b->location); +end: + return is_equal; +} + +static enum lttng_error_code lttng_event_rule_uprobe_generate_filter_bytecode( + struct lttng_event_rule *rule, uid_t uid, gid_t gid) +{ + /* Nothing to do. */ + return LTTNG_OK; +} + +static const char *lttng_event_rule_uprobe_get_filter( + const struct lttng_event_rule *rule) +{ + /* Unsupported. */ + return NULL; +} + +static const struct lttng_filter_bytecode * +lttng_event_rule_uprobe_get_filter_bytecode(const struct lttng_event_rule *rule) +{ + /* Unsupported. */ + return NULL; +} + +static struct lttng_event_exclusion * +lttng_event_rule_uprobe_generate_exclusions(const struct lttng_event_rule *rule) +{ + /* Unsupported. */ + return NULL; +} + +struct lttng_event_rule *lttng_event_rule_uprobe_create() +{ + struct lttng_event_rule *rule = NULL; + struct lttng_event_rule_uprobe *urule; + + urule = zmalloc(sizeof(struct lttng_event_rule_uprobe)); + 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; + urule->parent.generate_filter_bytecode = + lttng_event_rule_uprobe_generate_filter_bytecode; + urule->parent.get_filter = lttng_event_rule_uprobe_get_filter; + urule->parent.get_filter_bytecode = + lttng_event_rule_uprobe_get_filter_bytecode; + urule->parent.generate_exclusions = + lttng_event_rule_uprobe_generate_exclusions; +end: + return rule; +} + +LTTNG_HIDDEN +ssize_t lttng_event_rule_uprobe_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 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; + enum lttng_event_rule_status status; + + if (!_event_rule) { + ret = -1; + goto end; + } + + if (view->buffer.size < sizeof(*uprobe_comm)) { + ERR("Failed to initialize from malformed event rule uprobe: buffer too short to contain header."); + ret = -1; + goto end; + } + + current_buffer_view = lttng_buffer_view_from_view( + &view->buffer, offset, sizeof(*uprobe_comm)); + uprobe_comm = (typeof(uprobe_comm)) current_buffer_view.data; + + if (!uprobe_comm) { + ret = -1; + goto end; + } + + rule = lttng_event_rule_uprobe_create(); + if (!rule) { + ERR("Failed to create event rule uprobe."); + ret = -1; + goto end; + } + + /* Skip to payload. */ + offset += current_buffer_view.size; + + /* Map the name. */ + current_buffer_view = lttng_buffer_view_from_view( + &view->buffer, offset, uprobe_comm->name_len); + name = current_buffer_view.data; + if (!name) { + ret = -1; + goto end; + } + + if (!lttng_buffer_view_contains_string(¤t_buffer_view, name, + uprobe_comm->name_len)) { + ret = -1; + goto end; + } + + /* Skip after the name. */ + offset += uprobe_comm->name_len; + + /* Map the location. */ + struct lttng_payload_view current_payload_view = + lttng_payload_view_from_view(view, offset, + uprobe_comm->location_len); + ret = lttng_userspace_probe_location_create_from_payload( + ¤t_payload_view, &location); + if (ret < 0) { + ret = -1; + goto end; + } + + assert(ret == uprobe_comm->location_len); + + /* Skip after the location. */ + offset += uprobe_comm->location_len; + + uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); + uprobe->location = location; + + status = lttng_event_rule_uprobe_set_name(rule, name); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ret = -1; + goto end; + } + + if (!lttng_event_rule_uprobe_validate(rule)) { + ret = -1; + goto end; + } + + *_event_rule = rule; + rule = NULL; + ret = offset; +end: + lttng_event_rule_destroy(rule); + return ret; +} + +enum lttng_event_rule_status lttng_event_rule_uprobe_set_location( + struct lttng_event_rule *rule, + const struct lttng_userspace_probe_location *location) +{ + struct lttng_userspace_probe_location *location_copy = NULL; + struct lttng_event_rule_uprobe *uprobe; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !location) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); + location_copy = lttng_userspace_probe_location_copy(location); + if (!location_copy) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + + if (uprobe->location) { + lttng_userspace_probe_location_destroy(uprobe->location); + } + + uprobe->location = location_copy; + location_copy = NULL; +end: + lttng_userspace_probe_location_destroy(location_copy); + return status; +} + +enum lttng_event_rule_status lttng_event_rule_uprobe_get_location( + const struct lttng_event_rule *rule, + const struct lttng_userspace_probe_location **location) +{ + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !location) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + *location = lttng_event_rule_uprobe_get_location_mutable(rule); + if (!*location) { + status = LTTNG_EVENT_RULE_STATUS_UNSET; + goto end; + } + +end: + return status; +} + +LTTNG_HIDDEN +struct lttng_userspace_probe_location * +lttng_event_rule_uprobe_get_location_mutable( + const struct lttng_event_rule *rule) +{ + struct lttng_event_rule_uprobe *uprobe; + + assert(rule); + uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); + + return uprobe->location; +} + +enum lttng_event_rule_status lttng_event_rule_uprobe_set_name( + struct lttng_event_rule *rule, const char *name) +{ + char *name_copy = NULL; + struct lttng_event_rule_uprobe *uprobe; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !name || + strlen(name) == 0) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); + name_copy = strdup(name); + if (!name_copy) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + + if (uprobe->name) { + free(uprobe->name); + } + + uprobe->name = name_copy; + name_copy = NULL; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_uprobe_get_name( + const struct lttng_event_rule *rule, const char **name) +{ + struct lttng_event_rule_uprobe *uprobe; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !name) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); + if (!uprobe->name) { + status = LTTNG_EVENT_RULE_STATUS_UNSET; + goto end; + } + + *name = uprobe->name; +end: + return status; +} -- 2.34.1 From 6d420eff669a3de55be3ee348f88a4e244b78ecf Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Wed, 11 Mar 2020 13:58:48 -0400 Subject: [PATCH 11/16] event-rule: introduce event rule tracepoint MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: I77495a7b0e359e6f513dfc862dec65b4946d08ba --- include/Makefile.am | 4 +- .../lttng/event-rule/tracepoint-internal.h | 72 + include/lttng/event-rule/tracepoint.h | 188 +++ include/lttng/lttng.h | 1 + src/common/Makefile.am | 1 + src/common/event-rule/event-rule.c | 11 +- src/common/event-rule/tracepoint.c | 1155 +++++++++++++++++ 7 files changed, 1428 insertions(+), 4 deletions(-) create mode 100644 include/lttng/event-rule/tracepoint-internal.h create mode 100644 include/lttng/event-rule/tracepoint.h create mode 100644 src/common/event-rule/tracepoint.c diff --git a/include/Makefile.am b/include/Makefile.am index b5875cd66..1cfcd8320 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -147,7 +147,8 @@ lttngeventruleinclude_HEADERS= \ lttng/event-rule/event-rule.h \ lttng/event-rule/kprobe.h \ lttng/event-rule/syscall.h \ - lttng/event-rule/uprobe.h + lttng/event-rule/uprobe.h \ + lttng/event-rule/tracepoint.h noinst_HEADERS = \ lttng/snapshot-internal.h \ @@ -183,5 +184,6 @@ noinst_HEADERS = \ lttng/event-rule/kprobe-internal.h \ lttng/event-rule/syscall-internal.h \ lttng/event-rule/uprobe-internal.h \ + lttng/event-rule/tracepoint-internal.h \ version.h \ version.i diff --git a/include/lttng/event-rule/tracepoint-internal.h b/include/lttng/event-rule/tracepoint-internal.h new file mode 100644 index 000000000..039b0a6cf --- /dev/null +++ b/include/lttng/event-rule/tracepoint-internal.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_RULE_TRACEPOINT_INTERNAL_H +#define LTTNG_EVENT_RULE_TRACEPOINT_INTERNAL_H + +#include +#include +#include +#include +#include +#include + +struct lttng_event_rule_tracepoint { + struct lttng_event_rule parent; + + /* Domain. */ + enum lttng_domain_type domain; + + /* Name pattern. */ + char *pattern; + + /* Filter. */ + char *filter_expression; + + /* Loglevel. */ + struct { + enum lttng_loglevel_type type; + int value; + } loglevel; + + /* Exclusions. */ + struct lttng_dynamic_pointer_array exclusions; + + /* internal use only. */ + struct { + char *filter; + struct lttng_filter_bytecode *bytecode; + } internal_filter; +}; + +struct lttng_event_rule_tracepoint_comm { + /* enum lttng_domain_type. */ + int8_t domain_type; + /* enum lttng_event_logleven_type. */ + int8_t loglevel_type; + int32_t loglevel_value; + /* Includes terminator `\0`. */ + uint32_t pattern_len; + /* Includes terminator `\0`. */ + uint32_t filter_expression_len; + uint32_t exclusions_count; + uint32_t exclusions_len; + /* + * Payload is composed of, in that order: + * - pattern (null terminated), + * - filter expression (null terminated), + * - exclusions (32 bit length + null terminated string). + */ + char payload[]; +} LTTNG_PACKED; + +LTTNG_HIDDEN +ssize_t lttng_event_rule_tracepoint_create_from_payload( + struct lttng_payload_view *view, + struct lttng_event_rule **rule); + +#endif /* LTTNG_EVENT_RULE_TRACEPOINT_INTERNAL_H */ diff --git a/include/lttng/event-rule/tracepoint.h b/include/lttng/event-rule/tracepoint.h new file mode 100644 index 000000000..e24b0fbe1 --- /dev/null +++ b/include/lttng/event-rule/tracepoint.h @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_RULE_TRACEPOINT_H +#define LTTNG_EVENT_RULE_TRACEPOINT_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Create a newly allocated tracepoint 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_tracepoint_create( + enum lttng_domain_type domain); + +/* + * Set the pattern of a tracepoint event rule. + * + * Pattern can contain wildcard '*'. See man lttng-enable-event. + * + * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_event_rule_status lttng_event_rule_tracepoint_set_pattern( + struct lttng_event_rule *rule, const char *pattern); + +/* + * Get the pattern of a tracepoint event rule. + * + * The caller does not assume the ownership of the returned pattern. The + * pattern shall only only be used for the duration of the event rule's + * lifetime, or before a different pattern is set. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's pattern + * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid + * parameter is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a pattern + * was not set prior to this call. + */ +extern enum lttng_event_rule_status lttng_event_rule_tracepoint_get_pattern( + const struct lttng_event_rule *rule, const char **pattern); + +/* + * Get the domain type of a tracepoint event rule. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the domain type output parameter + * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is + * passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a pattern was not set prior to + * this call. + */ +extern enum lttng_event_rule_status lttng_event_rule_tracepoint_get_domain_type( + const struct lttng_event_rule *rule, + enum lttng_domain_type *type); + +/* + * Set the filter expression of a tracepoint event rule. + * + * The expression is copied internally. + * + * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_event_rule_status lttng_event_rule_tracepoint_set_filter( + struct lttng_event_rule *rule, const char *expression); + +/* + * Get the filter expression of a tracepoint event rule. + * + * The caller does not assume the ownership of the returned filter expression. + * The filter expression shall only only be used for the duration of the event + * rule's lifetime, or before a different filter expression is set. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's filter + * expression on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid + * parameter is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a filter expression + * was not set prior to this call. + */ +extern enum lttng_event_rule_status lttng_event_rule_tracepoint_get_filter( + const struct lttng_event_rule *rule, const char **expression); + +/* + * Set the single 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. + * + * 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); + +/* + * Set the log level to all of a tracepoint event rule. + * + * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_event_rule_status +lttng_event_rule_tracepoint_set_log_level_all(struct lttng_event_rule *rule); + +/* + * Get the log level type of a tracepoint event rule. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the log level type 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_type( + 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); + +/* + * Add an exclusion to the set of exclusion of an event rule. + * + * The passed exclusion will be copied to the event_rule. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK on success, + * LTTNG_EVENT_RULE_STATUS_INVALID if invalid parameters are passed, or + * LTTNG_EVENT_RULE_STATUS_UNSUPPORTED if this property is not supported by the + * domain. + */ +extern enum lttng_event_rule_status lttng_event_rule_tracepoint_add_exclusion( + struct lttng_event_rule *rule, + const char *exclusion); + +/* + * Get the exclusions property count of an event rule. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the count output parameter + * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is + * passed. + */ +extern enum lttng_event_rule_status +lttng_event_rule_tracepoint_get_exclusions_count( + const struct lttng_event_rule *rule, unsigned int *count); + +/* + * Get the event rule exclusion at the given index. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the exclusion output parameter + * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is + * passed. + */ +extern enum lttng_event_rule_status +lttng_event_rule_tracepoint_get_exclusion_at_index( + const struct lttng_event_rule *rule, + unsigned int index, + const char **exclusion); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_EVENT_RULE_TRACEPOINT_H */ diff --git a/include/lttng/lttng.h b/include/lttng/lttng.h index c248eceb9..1049e0f0d 100644 --- a/include/lttng/lttng.h +++ b/include/lttng/lttng.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 7c2ab3e3a..f6918b54c 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -52,6 +52,7 @@ libcommon_la_SOURCES = \ event-rule/kprobe.c \ event-rule/syscall.c \ event-rule/uprobe.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 \ diff --git a/src/common/event-rule/event-rule.c b/src/common/event-rule/event-rule.c index 8e9b003c8..cdf3e0ded 100644 --- a/src/common/event-rule/event-rule.c +++ b/src/common/event-rule/event-rule.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -31,9 +32,12 @@ enum lttng_domain_type lttng_event_rule_get_domain_type( switch (lttng_event_rule_get_type(event_rule)) { case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: - /* TODO */ - domain_type = LTTNG_DOMAIN_NONE; + { + enum lttng_event_rule_status status; + status = lttng_event_rule_tracepoint_get_domain_type(event_rule, &domain_type); + assert(status == LTTNG_EVENT_RULE_STATUS_OK); break; + } case LTTNG_EVENT_RULE_TYPE_SYSCALL: case LTTNG_EVENT_RULE_TYPE_KPROBE: case LTTNG_EVENT_RULE_TYPE_KRETPROBE: @@ -155,7 +159,8 @@ ssize_t lttng_event_rule_create_from_payload( switch ((enum lttng_event_rule_type) event_rule_comm->event_rule_type) { case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: - /* TODO */ + 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; diff --git a/src/common/event-rule/tracepoint.c b/src/common/event-rule/tracepoint.c new file mode 100644 index 000000000..14e4c7b01 --- /dev/null +++ b/src/common/event-rule/tracepoint.c @@ -0,0 +1,1155 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define IS_TRACEPOINT_EVENT_RULE(rule) \ + (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_TRACEPOINT) + +static void lttng_event_rule_tracepoint_destroy(struct lttng_event_rule *rule) +{ + struct lttng_event_rule_tracepoint *tracepoint; + + if (rule == NULL) { + return; + } + + tracepoint = container_of( + rule, struct lttng_event_rule_tracepoint, parent); + + lttng_dynamic_pointer_array_reset(&tracepoint->exclusions); + free(tracepoint->pattern); + free(tracepoint->filter_expression); + free(tracepoint->internal_filter.filter); + free(tracepoint->internal_filter.bytecode); + free(tracepoint); +} + +static bool lttng_event_rule_tracepoint_validate( + const struct lttng_event_rule *rule) +{ + bool valid = false; + struct lttng_event_rule_tracepoint *tracepoint; + + if (!rule) { + goto end; + } + + tracepoint = container_of( + rule, struct lttng_event_rule_tracepoint, parent); + + /* Required field. */ + if (!tracepoint->pattern) { + ERR("Invalid tracepoint event rule: a pattern must be set."); + goto end; + } + + /* Required field. */ + if (tracepoint->domain == LTTNG_DOMAIN_NONE) { + ERR("Invalid tracepoint event rule: a domain must be set."); + goto end; + } + + valid = true; +end: + return valid; +} + +static int lttng_event_rule_tracepoint_serialize( + const struct lttng_event_rule *rule, + struct lttng_payload *payload) +{ + int ret, i; + size_t pattern_len, filter_expression_len, exclusions_len; + 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; + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule)) { + ret = -1; + goto end; + } + + DBG("Serializing tracepoint event rule."); + tracepoint = container_of( + rule, struct lttng_event_rule_tracepoint, parent); + + status = lttng_event_rule_tracepoint_get_exclusions_count(rule, &exclusion_count); + assert(status == LTTNG_EVENT_RULE_STATUS_OK); + + pattern_len = strlen(tracepoint->pattern) + 1; + + if (tracepoint->filter_expression != NULL) { + filter_expression_len = + strlen(tracepoint->filter_expression) + 1; + } else { + filter_expression_len = 0; + } + + exclusions_len = 0; + for (i = 0; i < exclusion_count; i++) { + const char *exclusion; + + status = lttng_event_rule_tracepoint_get_exclusion_at_index( + rule, i, &exclusion); + assert(status == LTTNG_EVENT_RULE_STATUS_OK); + + /* Length field. */ + exclusions_len += sizeof(uint32_t); + /* Payload (null terminated). */ + exclusions_len += strlen(exclusion) + 1; + } + + 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; + tracepoint_comm.exclusions_len = exclusions_len; + + ret = lttng_dynamic_buffer_append(&payload->buffer, &tracepoint_comm, + sizeof(tracepoint_comm)); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append( + &payload->buffer, tracepoint->pattern, pattern_len); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, tracepoint->filter_expression, + filter_expression_len); + if (ret) { + goto end; + } + + for (i = 0; i < exclusion_count; i++) { + size_t len; + const char *exclusion; + + status = lttng_event_rule_tracepoint_get_exclusion_at_index( + rule, i, &exclusion); + assert(status == LTTNG_EVENT_RULE_STATUS_OK); + + len = strlen(exclusion) + 1; + /* Append exclusion length, includes the null terminator. */ + ret = lttng_dynamic_buffer_append( + &payload->buffer, &len, sizeof(uint32_t)); + if (ret) { + goto end; + } + + exclusions_appended_len += sizeof(uint32_t); + + /* Include the '\0' in the payload. */ + ret = lttng_dynamic_buffer_append( + &payload->buffer, exclusion, len); + if (ret) { + goto end; + } + + exclusions_appended_len += len; + } + + assert(exclusions_len == exclusions_appended_len); + +end: + return ret; +} + +static bool lttng_event_rule_tracepoint_is_equal( + const struct lttng_event_rule *_a, + const struct lttng_event_rule *_b) +{ + int i; + bool is_equal = false; + struct lttng_event_rule_tracepoint *a, *b; + unsigned int count_a, count_b; + enum lttng_event_rule_status status; + + a = container_of(_a, struct lttng_event_rule_tracepoint, parent); + b = container_of(_b, struct lttng_event_rule_tracepoint, parent); + + status = lttng_event_rule_tracepoint_get_exclusions_count(_a, &count_a); + assert(status == LTTNG_EVENT_RULE_STATUS_OK); + status = lttng_event_rule_tracepoint_get_exclusions_count(_b, &count_b); + assert(status == LTTNG_EVENT_RULE_STATUS_OK); + + /* Quick checks. */ + if (a->domain != b->domain) { + goto end; + } + + if (count_a != count_b) { + goto end; + } + + if (!!a->filter_expression != !!b->filter_expression) { + goto end; + } + + /* Long check. */ + assert(a->pattern); + assert(b->pattern); + if (strcmp(a->pattern, b->pattern)) { + goto end; + } + + if (a->filter_expression && b->filter_expression) { + if (strcmp(a->filter_expression, b->filter_expression)) { + goto end; + } + } else if (!!a->filter_expression != !!b->filter_expression) { + /* One is set; not the other. */ + goto end; + } + + if (a->loglevel.type != b->loglevel.type) { + goto end; + } + + if (a->loglevel.value != b->loglevel.value) { + goto end; + } + + for (i = 0; i < count_a; i++) { + const char *exclusion_a, *exclusion_b; + + status = lttng_event_rule_tracepoint_get_exclusion_at_index( + _a, i, &exclusion_a); + assert(status == LTTNG_EVENT_RULE_STATUS_OK); + status = lttng_event_rule_tracepoint_get_exclusion_at_index( + _b, i, &exclusion_b); + assert(status == LTTNG_EVENT_RULE_STATUS_OK); + if (strcmp(exclusion_a, exclusion_b)) { + goto end; + } + } + + is_equal = true; +end: + return is_equal; +} + +/* + * On success ret is 0; + * + * On error ret is negative. + * + * An event with NO loglevel and the name is * will return NULL. + */ +static int generate_agent_filter( + const struct lttng_event_rule *rule, char **_agent_filter) +{ + int err; + int ret = 0; + char *agent_filter = NULL; + const char *pattern; + const char *filter; + enum lttng_loglevel_type loglevel_type; + enum lttng_event_rule_status status; + + assert(rule); + assert(_agent_filter); + + status = lttng_event_rule_tracepoint_get_pattern(rule, &pattern); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ret = -1; + goto end; + } + + status = lttng_event_rule_tracepoint_get_filter(rule, &filter); + if (status == LTTNG_EVENT_RULE_STATUS_UNSET) { + filter = NULL; + } else if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ret = -1; + goto end; + } + + status = lttng_event_rule_tracepoint_get_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) { + if (filter) { + err = asprintf(&agent_filter, + "(%s) && (logger_name == \"%s\")", + filter, pattern); + } else { + err = asprintf(&agent_filter, "logger_name == \"%s\"", + pattern); + } + + if (err < 0) { + PERROR("Failed to format agent filter string"); + ret = -1; + goto end; + } + } + + if (loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) { + const char *op; + int loglevel_value; + + status = lttng_event_rule_tracepoint_get_log_level( + rule, &loglevel_value); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ret = -1; + goto end; + } + + if (loglevel_type == LTTNG_EVENT_LOGLEVEL_RANGE) { + op = ">="; + } else { + op = "=="; + } + + if (filter || agent_filter) { + char *new_filter; + + err = asprintf(&new_filter, + "(%s) && (int_loglevel %s %d)", + agent_filter ? agent_filter : filter, + op, loglevel_value); + if (agent_filter) { + free(agent_filter); + } + agent_filter = new_filter; + } else { + err = asprintf(&agent_filter, "int_loglevel %s %d", op, + loglevel_value); + } + + if (err < 0) { + PERROR("Failed to format agent filter string"); + ret = -1; + goto end; + } + } + + *_agent_filter = agent_filter; + agent_filter = NULL; + +end: + free(agent_filter); + return ret; +} + +static enum lttng_error_code +lttng_event_rule_tracepoint_generate_filter_bytecode( + struct lttng_event_rule *rule, uid_t uid, gid_t gid) +{ + int ret; + enum lttng_error_code ret_code; + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_domain_type domain_type; + enum lttng_event_rule_status status; + const char *filter; + struct lttng_filter_bytecode *bytecode = NULL; + + assert(rule); + + tracepoint = container_of( + rule, struct lttng_event_rule_tracepoint, parent); + + status = lttng_event_rule_tracepoint_get_filter(rule, &filter); + if (status == LTTNG_EVENT_RULE_STATUS_UNSET) { + filter = NULL; + } else if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ret_code = LTTNG_ERR_FILTER_INVAL; + goto end; + } + + if (filter && filter[0] == '\0') { + ret_code = LTTNG_ERR_FILTER_INVAL; + goto error; + } + + status = lttng_event_rule_tracepoint_get_domain_type( + rule, &domain_type); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ret_code = LTTNG_ERR_UNK; + goto error; + } + + switch (domain_type) { + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_PYTHON: + { + char *agent_filter; + + ret = generate_agent_filter(rule, &agent_filter); + if (ret) { + ret_code = LTTNG_ERR_FILTER_INVAL; + goto error; + } + + tracepoint->internal_filter.filter = agent_filter; + break; + } + default: + { + if (filter) { + tracepoint->internal_filter.filter = strdup(filter); + if (tracepoint->internal_filter.filter == NULL) { + ret_code = LTTNG_ERR_NOMEM; + goto error; + } + } else { + tracepoint->internal_filter.filter = NULL; + } + break; + } + } + + if (tracepoint->internal_filter.filter == NULL) { + ret_code = LTTNG_OK; + goto end; + } + + ret = run_as_generate_filter_bytecode( + tracepoint->internal_filter.filter, uid, gid, + &bytecode); + if (ret) { + ret_code = LTTNG_ERR_FILTER_INVAL; + goto end; + } + + tracepoint->internal_filter.bytecode = bytecode; + bytecode = NULL; + ret_code = LTTNG_OK; + +error: +end: + free(bytecode); + return ret_code; +} + +static const char *lttng_event_rule_tracepoint_get_internal_filter( + const struct lttng_event_rule *rule) +{ + struct lttng_event_rule_tracepoint *tracepoint; + + assert(rule); + tracepoint = container_of( + rule, struct lttng_event_rule_tracepoint, parent); + return tracepoint->internal_filter.filter; +} + +static const struct lttng_filter_bytecode * +lttng_event_rule_tracepoint_get_internal_filter_bytecode( + const struct lttng_event_rule *rule) +{ + struct lttng_event_rule_tracepoint *tracepoint; + + assert(rule); + tracepoint = container_of( + rule, struct lttng_event_rule_tracepoint, parent); + return tracepoint->internal_filter.bytecode; +} + +static struct lttng_event_exclusion * +lttng_event_rule_tracepoint_generate_exclusions( + const struct lttng_event_rule *rule) +{ + enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; + enum lttng_event_rule_status status; + struct lttng_event_exclusion *local_exclusions = NULL; + struct lttng_event_exclusion *ret_exclusions = NULL; + unsigned int nb_exclusions = 0; + unsigned int i; + + status = lttng_event_rule_tracepoint_get_domain_type(rule, &domain_type); + assert(status == LTTNG_EVENT_RULE_STATUS_OK); + + switch (domain_type) { + case LTTNG_DOMAIN_KERNEL: + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: + /* Not supported. */ + ret_exclusions = NULL; + goto end; + case LTTNG_DOMAIN_UST: + /* Exclusions supported. */ + break; + default: + abort(); + } + + status = lttng_event_rule_tracepoint_get_exclusions_count( + rule, &nb_exclusions); + assert(status == LTTNG_EVENT_RULE_STATUS_OK); + if (nb_exclusions == 0) { + /* Nothing to do. */ + ret_exclusions = NULL; + goto end; + } + + local_exclusions = zmalloc(sizeof(struct lttng_event_exclusion) + + (LTTNG_SYMBOL_NAME_LEN * nb_exclusions)); + if (!local_exclusions) { + PERROR("Failed to allocate exclusions buffer"); + ret_exclusions = NULL; + goto end; + } + + local_exclusions->count = nb_exclusions; + for (i = 0; i < nb_exclusions; i++) { + /* Truncation is already checked at the setter level. */ + const char *tmp; + + status = lttng_event_rule_tracepoint_get_exclusion_at_index( + rule, i, &tmp); + assert(status == LTTNG_EVENT_RULE_STATUS_OK); + strncpy(local_exclusions->names[i], tmp, LTTNG_SYMBOL_NAME_LEN); + local_exclusions->names[i][LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + } + + /* Pass ownership. */ + ret_exclusions = local_exclusions; + local_exclusions = NULL; +end: + free(local_exclusions); + return ret_exclusions; +} + +static void destroy_lttng_exclusions_element(void *ptr) +{ + free(ptr); +} + +struct lttng_event_rule *lttng_event_rule_tracepoint_create( + enum lttng_domain_type domain_type) +{ + struct lttng_event_rule *rule = NULL; + struct lttng_event_rule_tracepoint *tp_rule; + + if (domain_type == LTTNG_DOMAIN_NONE) { + goto end; + } + + tp_rule = zmalloc(sizeof(struct lttng_event_rule_tracepoint)); + if (!tp_rule) { + goto end; + } + + rule = &tp_rule->parent; + lttng_event_rule_init(&tp_rule->parent, LTTNG_EVENT_RULE_TYPE_TRACEPOINT); + tp_rule->parent.validate = lttng_event_rule_tracepoint_validate; + tp_rule->parent.serialize = lttng_event_rule_tracepoint_serialize; + tp_rule->parent.equal = lttng_event_rule_tracepoint_is_equal; + tp_rule->parent.destroy = lttng_event_rule_tracepoint_destroy; + tp_rule->parent.generate_filter_bytecode = + lttng_event_rule_tracepoint_generate_filter_bytecode; + tp_rule->parent.get_filter = + lttng_event_rule_tracepoint_get_internal_filter; + tp_rule->parent.get_filter_bytecode = + lttng_event_rule_tracepoint_get_internal_filter_bytecode; + tp_rule->parent.generate_exclusions = + lttng_event_rule_tracepoint_generate_exclusions; + + tp_rule->domain = domain_type; + tp_rule->loglevel.type = LTTNG_EVENT_LOGLEVEL_ALL; + + lttng_dynamic_pointer_array_init(&tp_rule->exclusions, + destroy_lttng_exclusions_element); +end: + return rule; +} + +LTTNG_HIDDEN +ssize_t lttng_event_rule_tracepoint_create_from_payload( + struct lttng_payload_view *view, + struct lttng_event_rule **_event_rule) +{ + ssize_t ret, offset = 0; + 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; + const char **exclusions = NULL; + const uint32_t *exclusion_len; + const char *exclusion; + struct lttng_buffer_view current_buffer_view; + struct lttng_event_rule *rule = NULL; + + if (!_event_rule) { + ret = -1; + goto end; + } + + if (view->buffer.size < sizeof(*tracepoint_comm)) { + ERR("Failed to initialize from malformed event rule tracepoint: buffer too short to contain header."); + ret = -1; + goto end; + } + + current_buffer_view = lttng_buffer_view_from_view( + &view->buffer, offset, sizeof(*tracepoint_comm)); + tracepoint_comm = (typeof(tracepoint_comm)) current_buffer_view.data; + + if (!tracepoint_comm) { + ret = -1; + goto end; + } + + if (tracepoint_comm->domain_type <= LTTNG_DOMAIN_NONE || + tracepoint_comm->domain_type > LTTNG_DOMAIN_PYTHON) { + /* Invalid domain value. */ + ERR("Invalid domain type value (%i) found in tracepoint_comm buffer.", + (int) tracepoint_comm->domain_type); + ret = -1; + goto end; + } + + domain_type = (enum lttng_domain_type) tracepoint_comm->domain_type; + rule = lttng_event_rule_tracepoint_create(domain_type); + if (!rule) { + ERR("Failed to create event rule tracepoint."); + ret = -1; + goto end; + } + + loglevel_type = (enum lttng_loglevel_type) + tracepoint_comm->loglevel_type; + switch (loglevel_type) { + case LTTNG_EVENT_LOGLEVEL_ALL: + status = lttng_event_rule_tracepoint_set_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; + + /* Map the pattern. */ + current_buffer_view = lttng_buffer_view_from_view( + &view->buffer, offset, tracepoint_comm->pattern_len); + pattern = current_buffer_view.data; + if (!pattern) { + ret = -1; + goto end; + } + + if (!lttng_buffer_view_contains_string(¤t_buffer_view, pattern, + tracepoint_comm->pattern_len)) { + ret = -1; + goto end; + } + + /* Skip after the pattern. */ + offset += tracepoint_comm->pattern_len; + + if (!tracepoint_comm->filter_expression_len) { + goto skip_filter_expression; + } + + /* Map the filter_expression. */ + current_buffer_view = lttng_buffer_view_from_view(&view->buffer, offset, + tracepoint_comm->filter_expression_len); + filter_expression = current_buffer_view.data; + if (!filter_expression) { + ret = -1; + goto end; + } + + if (!lttng_buffer_view_contains_string(¤t_buffer_view, + filter_expression, + tracepoint_comm->filter_expression_len)) { + ret = -1; + goto end; + } + + /* Skip after the pattern. */ + offset += tracepoint_comm->filter_expression_len; + +skip_filter_expression: + for (i = 0; i < tracepoint_comm->exclusions_count; i++) { + current_buffer_view = lttng_buffer_view_from_view( + &view->buffer, offset, sizeof(*exclusion_len)); + exclusion_len = (typeof(exclusion_len)) current_buffer_view.data; + if (!exclusion_len) { + ret = -1; + goto end; + } + + offset += sizeof(*exclusion_len); + current_buffer_view = lttng_buffer_view_from_view( + &view->buffer, offset, *exclusion_len); + exclusion = current_buffer_view.data; + if (!lttng_buffer_view_contains_string(¤t_buffer_view, + exclusion, *exclusion_len)) { + ret = -1; + goto end; + } + + status = lttng_event_rule_tracepoint_add_exclusion(rule, exclusion); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to add event rule tracepoint exclusion \"%s\".", + exclusion); + ret = -1; + goto end; + } + + /* Skip to next exclusion. */ + offset += *exclusion_len; + } + + status = lttng_event_rule_tracepoint_set_pattern(rule, pattern); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set event rule tracepoint pattern."); + ret = -1; + goto end; + } + + if (filter_expression) { + status = lttng_event_rule_tracepoint_set_filter( + rule, filter_expression); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set event rule tracepoint pattern."); + ret = -1; + goto end; + } + } + + *_event_rule = rule; + rule = NULL; + ret = offset; +end: + free(exclusions); + lttng_event_rule_destroy(rule); + return ret; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_set_pattern( + struct lttng_event_rule *rule, const char *pattern) +{ + char *pattern_copy = NULL; + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !pattern || + strlen(pattern) == 0) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of( + rule, struct lttng_event_rule_tracepoint, parent); + pattern_copy = strdup(pattern); + if (!pattern_copy) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + + free(tracepoint->pattern); + + tracepoint->pattern = pattern_copy; + pattern_copy = NULL; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_get_pattern( + const struct lttng_event_rule *rule, const char **pattern) +{ + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !pattern) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of( + rule, struct lttng_event_rule_tracepoint, parent); + if (!tracepoint->pattern) { + status = LTTNG_EVENT_RULE_STATUS_UNSET; + goto end; + } + + *pattern = tracepoint->pattern; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_get_domain_type( + const struct lttng_event_rule *rule, + enum lttng_domain_type *type) +{ + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !type) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of( + rule, struct lttng_event_rule_tracepoint, parent); + *type = tracepoint->domain; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_set_filter( + struct lttng_event_rule *rule, const char *expression) +{ + char *expression_copy = NULL; + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !expression || + strlen(expression) == 0) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of( + rule, struct lttng_event_rule_tracepoint, parent); + expression_copy = strdup(expression); + if (!expression_copy) { + PERROR("Failed to copy filter expression"); + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + + if (tracepoint->filter_expression) { + free(tracepoint->filter_expression); + } + + tracepoint->filter_expression = expression_copy; + expression_copy = NULL; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_get_filter( + const struct lttng_event_rule *rule, const char **expression) +{ + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !expression) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of( + rule, struct lttng_event_rule_tracepoint, parent); + if (!tracepoint->filter_expression) { + status = LTTNG_EVENT_RULE_STATUS_UNSET; + goto end; + } + + *expression = tracepoint->filter_expression; +end: + return status; +} + +static bool log_level_value_valid( + int level, enum lttng_domain_type domain) +{ + bool valid = false; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + case LTTNG_DOMAIN_UST: + if (level < LTTNG_LOGLEVEL_EMERG) { + /* Invalid. */ + goto end; + } + if (level > LTTNG_LOGLEVEL_DEBUG) { + /* Invalid. */ + goto end; + } + + valid = true; + break; + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: + /* + * For both JUL and LOG4J custom log level are possible and can + * spawn the entire int32 range. + * For python, custom log level are possible, it is not clear if + * negative value are accepted (NOTSET == 0) but the source code + * validate against the int type implying that negative values + * are accepted. + */ + valid = true; + goto end; + + case LTTNG_DOMAIN_NONE: + default: + abort(); + } + +end: + return valid; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_set_log_level( + 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; + 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; + } + + 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; + 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; + } + + 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; + + 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( + 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) +{ + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !level) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of( + rule, struct lttng_event_rule_tracepoint, parent); + if (tracepoint->loglevel.type == LTTNG_EVENT_LOGLEVEL_ALL) { + status = LTTNG_EVENT_RULE_STATUS_UNSET; + goto end; + } + + *level = tracepoint->loglevel.value; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_add_exclusion( + struct lttng_event_rule *rule, + const char *exclusion) +{ + int ret; + char *exclusion_copy = NULL; + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + enum lttng_domain_type domain_type; + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || + !exclusion) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of( + rule, struct lttng_event_rule_tracepoint, parent); + + status = lttng_event_rule_tracepoint_get_domain_type( + rule, &domain_type); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + goto end; + } + + switch (domain_type) { + case LTTNG_DOMAIN_KERNEL: + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: + status = LTTNG_EVENT_RULE_STATUS_UNSUPPORTED; + goto end; + case LTTNG_DOMAIN_UST: + /* Exclusions supported. */ + break; + default: + abort(); + } + + if (strlen(exclusion) >= LTTNG_SYMBOL_NAME_LEN) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + exclusion_copy = strdup(exclusion); + if (!exclusion_copy) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + + ret = lttng_dynamic_pointer_array_add_pointer(&tracepoint->exclusions, + exclusion_copy); + if (ret < 0) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + + exclusion_copy = NULL; +end: + free(exclusion_copy); + return status; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_get_exclusions_count( + const struct lttng_event_rule *rule, unsigned int *count) +{ + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !count) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of( + rule, struct lttng_event_rule_tracepoint, parent); + *count = lttng_dynamic_pointer_array_get_count(&tracepoint->exclusions); +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_get_exclusion_at_index( + const struct lttng_event_rule *rule, + unsigned int index, + const char **exclusion) +{ + unsigned int count; + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !exclusion) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of( + rule, struct lttng_event_rule_tracepoint, parent); + if (lttng_event_rule_tracepoint_get_exclusions_count(rule, &count) != + LTTNG_EVENT_RULE_STATUS_OK) { + goto end; + } + + if (index >= count) { + goto end; + } + + *exclusion = lttng_dynamic_pointer_array_get_pointer( + &tracepoint->exclusions, index); +end: + return status; +} -- 2.34.1 From dbc20d3ca8514150ede944f364985c571f682c0e Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Thu, 28 Nov 2019 14:19:16 -0500 Subject: [PATCH 12/16] tests: unit: event-rule unit testing MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: Ic25fac00027b9b62c7d9dd060e75228aa7e86c57 --- .gitignore | 1 + tests/unit/Makefile.am | 8 +- tests/unit/test_event_rule.c | 389 +++++++++++++++++++++++++++++++++++ 3 files changed, 397 insertions(+), 1 deletion(-) create mode 100644 tests/unit/test_event_rule.c diff --git a/.gitignore b/.gitignore index f23a72b9c..9b2b60d04 100644 --- a/.gitignore +++ b/.gitignore @@ -89,6 +89,7 @@ compile_commands.json /tests/unit/test_relayd_backward_compat_group_by_session /tests/unit/test_fd_tracker /tests/unit/test_payload +/tests/unit/test_event_rule /tests/unit/test_unix_socket kernel_all_events_basic kernel_event_basic diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index ae086ce90..8dc4748a4 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -17,6 +17,7 @@ TESTS = test_kernel_data \ test_utils_compat_poll \ test_string_utils \ test_notification \ + test_event_rule \ test_directory_handle \ test_relayd_backward_compat_group_by_session \ ini_config/test_ini_config \ @@ -47,7 +48,8 @@ noinst_PROGRAMS = test_uri test_session test_kernel_data \ test_buffer_view \ test_payload \ test_unix_socket \ - test_kernel_probe + test_kernel_probe \ + test_event_rule if HAVE_LIBLTTNG_UST_CTL noinst_PROGRAMS += test_ust_data @@ -191,6 +193,10 @@ test_string_utils_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBSTRINGUTILS) $(DL_LIBS) # Notification api test_notification_SOURCES = test_notification.c test_notification_LDADD = $(LIBTAP) $(LIBLTTNG_CTL) $(DL_LIBS) +# +# Event rule api +test_event_rule_SOURCES = test_event_rule.c +test_event_rule_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) # relayd backward compat for groou-by-session utilities test_relayd_backward_compat_group_by_session_SOURCES = test_relayd_backward_compat_group_by_session.c diff --git a/tests/unit/test_event_rule.c b/tests/unit/test_event_rule.c new file mode 100644 index 000000000..53f16dbbd --- /dev/null +++ b/tests/unit/test_event_rule.c @@ -0,0 +1,389 @@ +/* + * Unit tests for the notification API. + * + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#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 187 + +struct tracepoint_test { + enum lttng_domain_type type; + bool support_exclusion; +}; + +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_payload payload; + + type = test->type; + diag("Testing domain %d.", type); + + lttng_payload_init(&payload); + + tracepoint = lttng_event_rule_tracepoint_create(type); + ok(tracepoint, "tracepoint object."); + + status = lttng_event_rule_tracepoint_get_domain_type(tracepoint, &domain_type); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get tracepoint domain."); + ok(domain_type == type, "domain type got %d expected %d.", domain_type, type); + + status = lttng_event_rule_tracepoint_set_pattern(tracepoint, pattern); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting pattern."); + status = lttng_event_rule_tracepoint_get_pattern(tracepoint, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting pattern."); + ok(!strncmp(pattern, tmp, strlen(pattern)), "pattern is equal."); + + status = lttng_event_rule_tracepoint_set_filter(tracepoint, filter); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting filter."); + status = lttng_event_rule_tracepoint_get_filter(tracepoint, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting filter."); + ok(!strncmp(filter, tmp, strlen(filter)), "filter is equal."); + + status = lttng_event_rule_tracepoint_set_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."); + + if (test->support_exclusion) { + int i; + + for (i = 0; i < 3; i++) { + status = lttng_event_rule_tracepoint_add_exclusion(tracepoint, exclusions[i]); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting exclusions \"%s\"", exclusions[i]); + } + + status = lttng_event_rule_tracepoint_get_exclusions_count(tracepoint, &count); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting exclusion count."); + ok(count == 3, "count is %d/3", count); + + for (i = 0; i < count; i++) { + status = lttng_event_rule_tracepoint_get_exclusion_at_index(tracepoint, i, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting exclusion at index %d.", i); + ok(!strncmp(exclusions[i], tmp, strlen(exclusions[i])), "%s == %s.", tmp, exclusions[i]); + } + } else { + int i; + + for (i = 0; i < 3; i++) { + status = lttng_event_rule_tracepoint_add_exclusion(tracepoint, exclusions[i]); + ok(status == LTTNG_EVENT_RULE_STATUS_UNSUPPORTED, "setting exclusions unsupported \"%s\".", exclusions[i]); + } + + status = lttng_event_rule_tracepoint_get_exclusions_count(tracepoint, &count); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting exclusion count."); + ok(count == 0, "count is %d/0", count); + } + + ok(lttng_event_rule_serialize(tracepoint, &payload) == 0, "Serializing."); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + + ok(lttng_event_rule_create_from_payload( + &view, &tracepoint_from_buffer) > 0, + "Deserializing."); + } + + ok(lttng_event_rule_is_equal(tracepoint, tracepoint_from_buffer), "serialized and from buffer are equal."); + + lttng_payload_reset(&payload); + lttng_event_rule_destroy(tracepoint); + lttng_event_rule_destroy(tracepoint_from_buffer); +} + +static +void test_event_rule_tracepoint(void) +{ + int i; + struct lttng_event_rule *tracepoint = NULL; + struct tracepoint_test tests[] = {{LTTNG_DOMAIN_JUL, false}, + {LTTNG_DOMAIN_KERNEL, false}, + {LTTNG_DOMAIN_LOG4J, false}, + {LTTNG_DOMAIN_PYTHON, false}, + {LTTNG_DOMAIN_UST, true}}; + + diag("Testing lttng_event_rule_tracepoint."); + tracepoint = lttng_event_rule_tracepoint_create(LTTNG_DOMAIN_NONE); + ok(!tracepoint, "Domain type restriction on create."); + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + test_event_rule_tracepoint_by_domain(&tests[i]); + } +} + +static void test_event_rule_syscall(void) +{ + struct lttng_event_rule *syscall = NULL; + struct lttng_event_rule *syscall_from_buffer = NULL; + enum lttng_event_rule_status status; + const char *pattern = "my_event_*"; + const char *filter = "msg_id == 23 && size >= 2048"; + const char *tmp; + struct lttng_payload payload; + + diag("Event rule syscall."); + + lttng_payload_init(&payload); + + syscall = lttng_event_rule_syscall_create(); + ok(syscall, "syscall object."); + + status = lttng_event_rule_syscall_set_pattern(syscall, pattern); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting pattern."); + status = lttng_event_rule_syscall_get_pattern(syscall, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting pattern."); + ok(!strncmp(pattern, tmp, strlen(pattern)), "pattern is equal."); + + status = lttng_event_rule_syscall_set_filter(syscall, filter); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting filter."); + status = lttng_event_rule_syscall_get_filter(syscall, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting filter."); + ok(!strncmp(filter, tmp, strlen(filter)), "filter is equal."); + + ok(lttng_event_rule_serialize(syscall, &payload) == 0, "Serializing."); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + + ok(lttng_event_rule_create_from_payload( + &view, &syscall_from_buffer) > 0, + "Deserializing."); + } + + ok(lttng_event_rule_is_equal(syscall, syscall_from_buffer), + "serialized and from buffer are equal."); + + lttng_payload_reset(&payload); + lttng_event_rule_destroy(syscall); + lttng_event_rule_destroy(syscall_from_buffer); +} + +static void test_event_rule_uprobe(void) +{ + struct lttng_event_rule *uprobe = NULL; + struct lttng_event_rule *uprobe_from_buffer = NULL; + struct lttng_userspace_probe_location_lookup_method *lookup_method = + NULL; + struct lttng_userspace_probe_location *probe_location = NULL; + const struct lttng_userspace_probe_location *probe_location_tmp = NULL; + enum lttng_event_rule_status status; + + const char *probe_name = "my_probe."; + const char *tmp; + struct lttng_payload payload; + + diag("Event rule uprobe."); + + lookup_method = lttng_userspace_probe_location_lookup_method_function_elf_create(); + if (!lookup_method) { + fail("Setup error on userspace probe lookup method creation."); + goto end; + } + + probe_location = lttng_userspace_probe_location_function_create( + "/proc/self/exe", + "lttng_userspace_probe_location_tracepoint_create", + lookup_method); + if (!probe_location) { + fail("Setup error on userspace probe location creation."); + goto end; + } + + /* Ownership transferred to the probe location function object. */ + lookup_method = NULL; + + lttng_payload_init(&payload); + + uprobe = lttng_event_rule_uprobe_create(); + 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( + uprobe, &probe_location_tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, + "Getting uprobe event rule location."); + ok(lttng_userspace_probe_location_is_equal( + probe_location, probe_location_tmp), + "Location is equal."); + + status = lttng_event_rule_uprobe_set_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); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Getting uprobe name."); + ok(!strcmp(probe_name, tmp), "Uprobe name are equal."); + + ok(lttng_event_rule_serialize(uprobe, &payload) == 0, "Serializing."); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + + ok(lttng_event_rule_create_from_payload( + &view, &uprobe_from_buffer) > 0, + "Deserializing."); + } + + ok(lttng_event_rule_is_equal(uprobe, uprobe_from_buffer), + "serialized and from buffer are equal."); + +end: + lttng_payload_reset(&payload); + lttng_event_rule_destroy(uprobe); + lttng_event_rule_destroy(uprobe_from_buffer); + lttng_userspace_probe_location_destroy(probe_location); + lttng_userspace_probe_location_lookup_method_destroy(lookup_method); +} + +static void test_event_rule_kprobe_by_location( + const struct lttng_kernel_probe_location *location) +{ + struct lttng_event_rule *kprobe = NULL; + struct lttng_event_rule *kprobe_from_buffer = NULL; + enum lttng_event_rule_status status; + const struct lttng_kernel_probe_location *_location; + + const char *probe_name = "my_probe"; + const char *tmp; + struct lttng_payload payload; + + diag("Event rule kprobe for location type %d.", + lttng_kernel_probe_location_get_type(location)); + + lttng_payload_init(&payload); + + kprobe = lttng_event_rule_kprobe_create(); + 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); + 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); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting kprobe event rule name: %s.", probe_name); + status = lttng_event_rule_kprobe_get_name(kprobe, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Getting kprobe name."); + ok(!strcmp(probe_name, tmp), "kprobe name are equal."); + + ok(lttng_event_rule_serialize(kprobe, &payload) == 0, "Serializing."); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + + ok(lttng_event_rule_create_from_payload( + &view, &kprobe_from_buffer) > 0, + "Deserializing."); + } + + ok(lttng_event_rule_is_equal(kprobe, kprobe_from_buffer), + "serialized and from buffer are equal."); + + lttng_payload_reset(&payload); + lttng_event_rule_destroy(kprobe); + lttng_event_rule_destroy(kprobe_from_buffer); +} + +static void test_event_rule_kprobe(void) +{ + struct lttng_kernel_probe_location *address_location = NULL; + struct lttng_kernel_probe_location *symbol_location = NULL; + + address_location = lttng_kernel_probe_location_address_create(50); + symbol_location = lttng_kernel_probe_location_symbol_create("une_bonne", 50); + assert(address_location); + assert(symbol_location); + + test_event_rule_kprobe_by_location(address_location); + test_event_rule_kprobe_by_location(symbol_location); + + lttng_kernel_probe_location_destroy(address_location); + lttng_kernel_probe_location_destroy(symbol_location); +} + +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(); + return exit_status(); +} -- 2.34.1 From dc11d4e3c15b2511cd390aa12c586f5594c80111 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Thu, 10 Sep 2020 23:57:54 -0400 Subject: [PATCH 13/16] Build fix: implicit declaration of function 'PERROR' on Solaris MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Solaris 10/11 CI builds complain that PERROR is not declared in memstream.h. This addresses the warning on both platforms. Signed-off-by: Jérémie Galarneau Change-Id: I09ce7cea216e075d0ef7256f738ea7400d193b6b --- src/common/filter/memstream.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/filter/memstream.h b/src/common/filter/memstream.h index cd246eed2..41c768146 100644 --- a/src/common/filter/memstream.h +++ b/src/common/filter/memstream.h @@ -21,6 +21,7 @@ FILE *lttng_fmemopen(void *buf, size_t size, const char *mode) #include #include +#include /* * Fallback for systems which don't have fmemopen. Copy buffer to a -- 2.34.1 From 9120e619240c698c899a9baea203a52c4bdfd565 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Tue, 15 Sep 2020 16:22:14 -0400 Subject: [PATCH 14/16] Fix: PERROR spam when `tracing` group does not exist MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The session daemon prints a PERROR on launch when the tracing group does not exist. This should not occur when the group simply does not exist as this is not an error. In that case (ESRCH), a DBG statement is sufficient. Signed-off-by: Jérémie Galarneau Change-Id: I3ade29071a8f4e9fe2eb56bf05ff4150b70fd463 --- src/common/utils.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/common/utils.c b/src/common/utils.c index 7006a2158..2a20e5a8c 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -1274,8 +1274,14 @@ int utils_get_group_id(const char *name, bool warn, gid_t *gid) } } if (ret) { - PERROR("Failed to get group file entry for group name \"%s\"", - name); + if (ret == ESRCH) { + DBG("Could not find group file entry for group name '%s'", + name); + } else { + PERROR("Failed to get group file entry for group name '%s'", + name); + } + ret = -1; goto error; } -- 2.34.1 From 3a4595c2469472dee1656cde5f8882c2123efd3c Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Mon, 21 Sep 2020 17:24:50 -0400 Subject: [PATCH 15/16] Tests: ns_contexts: discarded events result in test failure MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit A follow-up change makes all events emited by gen-ust-events a bit larger, which causes them to no longer fit in the default channel configuration's buffers. This causes the test to fail occasionnaly when the consumer daemon fails to consume the packets fast enough to leave room in the buffers for new events. The test doesn't need to produce 10,000 events; reducing to 1,000 produced events makes no material difference and works around the problem. Signed-off-by: Jérémie Galarneau Change-Id: Ie87583bb9bb9cdd813f80443231a65164ef67df1 --- tests/regression/ust/namespaces/test_ns_contexts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/ust/namespaces/test_ns_contexts b/tests/regression/ust/namespaces/test_ns_contexts index c1c6a2ae6..062146970 100755 --- a/tests/regression/ust/namespaces/test_ns_contexts +++ b/tests/regression/ust/namespaces/test_ns_contexts @@ -12,7 +12,7 @@ TESTDIR=$CURDIR/../../.. TESTAPP_PATH="$TESTDIR/utils/testapp" TESTAPP_NAME="gen-ust-events" TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME" -NUM_EVENT=10000 +NUM_EVENT=1000 EVENT_NAME="tp:tptest" TESTS_PER_NS=13 -- 2.34.1 From 2463b7879c00298daa79744cdaae82ac061a4ed8 Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Tue, 29 Sep 2020 11:46:24 -0400 Subject: [PATCH 16/16] SoW-2020-0002: Trace Hit Counters: trigger error reporting integration Revision 1 Signed-off-by: Jonathan Rajotte Change-Id: I841cc0a4a8c9cc41c6f14fa982c5967992511287 --- .gitignore | 6 + DO_NO_MERGE.txt | 2 + LICENSES/BSL-1.0 | 30 + Makefile.am | 1 + configure.ac | 7 + doc/examples/trigger-on-event/Makefile | 56 + doc/examples/trigger-on-event/README.md | 95 + doc/examples/trigger-on-event/demo.sh | 22 + .../trigger-on-event/instrumented-app.c | 36 + .../trigger-on-event/notification-client.c | 530 +++ .../trigger-on-event/performance/Makefile | 57 + .../trigger-on-event/performance/README.md | 83 + .../performance/bt_plugin_plot.py | 260 ++ .../trigger-on-event/performance/consumer.c | 185 ++ .../performance/generate-data.sh | 50 + .../performance/generate-graph.sh | 108 + .../performance/perform-experience.sh | 61 + .../performance/performance.c | 10 + .../performance/performance.h | 39 + .../trigger-on-event/performance/producer.c | 57 + .../tracepoint-trigger-example.c | 10 + .../tracepoint-trigger-example.h | 28 + doc/man/Makefile.am | 6 +- doc/man/lttng-add-trigger.1.txt | 207 ++ doc/man/lttng-list-triggers.1.txt | 37 + doc/man/lttng-remove-trigger.1.txt | 38 + include/Makefile.am | 7 + include/lttng/action/action-internal.h | 4 - include/lttng/action/action.h | 2 +- include/lttng/condition/condition-internal.h | 3 + include/lttng/condition/condition.h | 2 + include/lttng/condition/evaluation-internal.h | 5 +- include/lttng/condition/event-rule-internal.h | 107 + include/lttng/condition/event-rule.h | 171 + include/lttng/domain-internal.h | 25 + include/lttng/event-expr-internal.h | 66 + include/lttng/event-expr.h | 243 ++ include/lttng/event-field-value-internal.h | 192 ++ include/lttng/event-field-value.h | 229 ++ include/lttng/event-internal.h | 15 + .../lttng/event-rule/event-rule-internal.h | 22 +- include/lttng/event-rule/syscall-internal.h | 2 +- .../lttng/event-rule/tracepoint-internal.h | 2 +- include/lttng/lttng-error.h | 3 + include/lttng/lttng.h | 3 + include/lttng/snapshot.h | 10 +- include/lttng/trigger/trigger-internal.h | 167 +- include/lttng/trigger/trigger.h | 151 +- src/Makefile.am | 2 +- src/bin/lttng-sessiond/Makefile.am | 5 +- src/bin/lttng-sessiond/action-executor.c | 129 +- src/bin/lttng-sessiond/agent-thread.c | 10 + src/bin/lttng-sessiond/agent.c | 155 +- src/bin/lttng-sessiond/agent.h | 30 +- src/bin/lttng-sessiond/client.c | 102 +- src/bin/lttng-sessiond/cmd.c | 293 +- src/bin/lttng-sessiond/cmd.h | 11 +- src/bin/lttng-sessiond/condition-internal.c | 133 + src/bin/lttng-sessiond/condition-internal.h | 20 + src/bin/lttng-sessiond/consumer.c | 10 +- src/bin/lttng-sessiond/dispatch.c | 41 +- src/bin/lttng-sessiond/event.c | 238 +- src/bin/lttng-sessiond/event.h | 13 +- src/bin/lttng-sessiond/globals.c | 2 + src/bin/lttng-sessiond/kernel.c | 591 +++- src/bin/lttng-sessiond/kernel.h | 16 +- src/bin/lttng-sessiond/lttng-sessiond.h | 1 + src/bin/lttng-sessiond/main.c | 47 +- src/bin/lttng-sessiond/modprobe.c | 7 + .../notification-thread-commands.c | 155 + .../notification-thread-commands.h | 45 + .../notification-thread-events.c | 998 +++++- .../notification-thread-events.h | 4 + .../notification-thread-internal.h | 25 + src/bin/lttng-sessiond/notification-thread.c | 111 +- src/bin/lttng-sessiond/notification-thread.h | 43 +- src/bin/lttng-sessiond/register.c | 2 +- src/bin/lttng-sessiond/rotate.c | 4 +- src/bin/lttng-sessiond/session.c | 4 +- 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 | 245 +- src/bin/lttng-sessiond/trace-kernel.h | 32 +- src/bin/lttng-sessiond/trace-ust.c | 4 +- src/bin/lttng-sessiond/trace-ust.h | 12 +- .../lttng-sessiond/trigger-error-accounting.c | 668 ++++ .../lttng-sessiond/trigger-error-accounting.h | 52 + src/bin/lttng-sessiond/ust-abi-internal.h | 38 + src/bin/lttng-sessiond/ust-app.c | 840 ++++- src/bin/lttng-sessiond/ust-app.h | 52 +- src/bin/lttng-sessiond/ust-consumer.c | 2 +- src/bin/lttng-sessiond/ust-ctl-internal.h | 88 + src/bin/lttng/Makefile.am | 7 +- src/bin/lttng/command.h | 3 + src/bin/lttng/commands/add_trigger.c | 2161 ++++++++++++ src/bin/lttng/commands/enable_events.c | 428 +-- src/bin/lttng/commands/list_triggers.c | 692 ++++ src/bin/lttng/commands/remove_trigger.c | 191 ++ src/bin/lttng/lttng.c | 3 + src/bin/lttng/uprobe.c | 387 +++ src/bin/lttng/uprobe.h | 19 + src/bin/lttng/utils.c | 19 + src/bin/lttng/utils.h | 2 + src/common/Makefile.am | 30 +- src/common/actions/action.c | 9 +- src/common/actions/group.c | 2 +- src/common/actions/rotate-session.c | 2 +- src/common/actions/snapshot-session.c | 3 +- src/common/actions/start-session.c | 2 +- src/common/actions/stop-session.c | 2 +- src/common/argpar/Makefile.am | 3 + src/common/argpar/argpar.c | 746 +++++ src/common/argpar/argpar.h | 330 ++ src/common/bytecode/Makefile.am | 6 + src/common/bytecode/bytecode.c | 264 ++ src/common/bytecode/bytecode.h | 266 ++ src/common/compat/directory-handle.c | 19 +- src/common/{ => conditions}/buffer-usage.c | 0 src/common/{ => conditions}/condition.c | 34 + src/common/conditions/event-rule.c | 1567 +++++++++ .../{ => conditions}/session-consumed-size.c | 0 .../{ => conditions}/session-rotation.c | 0 src/common/credentials.c | 53 +- src/common/credentials.h | 15 +- src/common/domain.c | 30 + src/common/dynamic-array.h | 17 + src/common/error.c | 3 + src/common/evaluation.c | 16 + src/common/event-expr-to-bytecode.c | 201 ++ src/common/event-expr-to-bytecode.h | 20 + src/common/event-expr.c | 449 +++ src/common/event-field-value.c | 592 ++++ src/common/event-rule/event-rule.c | 32 +- src/common/event-rule/kprobe.c | 2 +- src/common/event-rule/syscall.c | 4 +- src/common/event-rule/tracepoint.c | 36 +- src/common/event-rule/uprobe.c | 2 +- src/common/filter.c | 2 +- src/common/filter.h | 2 +- src/common/filter/Makefile.am | 5 +- src/common/filter/filter-ast.h | 5 +- src/common/filter/filter-bytecode.h | 243 -- src/common/filter/filter-grammar-test.c | 2 +- src/common/filter/filter-ir.h | 65 + src/common/filter/filter-parser.y | 2 +- .../filter/filter-visitor-generate-bytecode.c | 272 +- src/common/index-allocator.c | 121 + src/common/index-allocator.h | 36 + src/common/kernel-consumer/kernel-consumer.c | 4 +- src/common/kernel-ctl/kernel-ctl.c | 46 +- src/common/kernel-ctl/kernel-ctl.h | 13 +- src/common/kernel-ctl/kernel-ioctl.h | 13 + src/common/lttng-kernel.h | 66 + src/common/notification.c | 2 +- src/common/optional.h | 9 +- src/common/runas.c | 10 +- src/common/runas.h | 2 +- src/common/sessiond-comm/sessiond-comm.h | 3 +- src/{bin/lttng-sessiond => common}/shm.c | 38 + src/{bin/lttng-sessiond => common}/shm.h | 2 + src/common/trace-chunk.c | 4 +- src/common/trigger.c | 762 ++++- src/common/ust-consumer/ust-consumer.c | 80 +- src/common/utils.c | 45 +- src/common/utils.h | 12 + src/lib/lttng-ctl/lttng-ctl.c | 177 +- src/lib/lttng-ctl/snapshot.c | 10 +- src/vendor/Makefile.am | 3 + src/vendor/msgpack/Makefile.am | 35 + src/vendor/msgpack/fbuffer.h | 38 + src/vendor/msgpack/gcc_atomic.h | 25 + src/vendor/msgpack/lttng-config.h | 19 + src/vendor/msgpack/msgpack.h | 24 + src/vendor/msgpack/object.h | 118 + src/vendor/msgpack/objectc.c | 482 +++ src/vendor/msgpack/pack.h | 174 + src/vendor/msgpack/pack_define.h | 18 + src/vendor/msgpack/pack_template.h | 945 ++++++ src/vendor/msgpack/predef.h | 24 + src/vendor/msgpack/sbuffer.h | 110 + src/vendor/msgpack/sysdep.h | 216 ++ src/vendor/msgpack/timestamp.h | 58 + src/vendor/msgpack/unpack.c | 702 ++++ src/vendor/msgpack/unpack.h | 281 ++ src/vendor/msgpack/unpack_define.h | 89 + src/vendor/msgpack/unpack_template.h | 471 +++ src/vendor/msgpack/util.h | 15 + src/vendor/msgpack/version.c | 22 + src/vendor/msgpack/version.h | 38 + src/vendor/msgpack/version_master.h | 3 + src/vendor/msgpack/vrefbuffer.c | 250 ++ src/vendor/msgpack/vrefbuffer.h | 140 + src/vendor/msgpack/zbuffer.h | 201 ++ src/vendor/msgpack/zone.c | 222 ++ src/vendor/msgpack/zone.h | 163 + tests/regression/Makefile.am | 20 +- tests/regression/kernel/test_callstack | 7 +- tests/regression/kernel/test_syscall | 2 +- tests/regression/tools/Makefile.am | 2 +- .../regression/tools/notification/Makefile.am | 61 +- .../tools/notification/notification.c | 2895 ++++++++++++++--- .../tools/notification/sessiond_testpoints.c | 113 + .../test_notification_kernel_buffer_usage | 88 + .../test_notification_kernel_capture | 55 + .../test_notification_kernel_error | 55 + .../test_notification_kernel_instrumentation | 51 + .../test_notification_kernel_syscall | 52 + .../test_notification_kernel_userspace_probe | 49 + .../notification/test_notification_multi_app | 67 +- .../test_notification_trigger_discarded_count | 262 ++ .../test_notification_ust_buffer_usage | 74 + .../test_notification_ust_capture | 40 + .../notification/test_notification_ust_error | 39 + ...ication_ust_event_rule_condition_exclusion | 41 + .../notification/util_event_generator.sh | 195 ++ tests/regression/tools/trigger/Makefile.am | 56 + tests/regression/tools/trigger/base_client.c | 252 ++ .../tools/trigger/consumer_testpoints.c | 147 + .../tools/trigger/start-stop/Makefile.am | 19 + .../tools/trigger/start-stop/test_start_stop | 198 ++ .../tools/trigger/test_add_trigger_cli | 413 +++ .../tools/trigger/test_list_triggers_cli | 399 +++ .../tools/trigger/test_remove_trigger_cli | 119 + .../test_trigger_kernel} | 20 +- .../test_trigger_ust} | 22 +- tests/regression/tools/trigger/trigger.c | 729 +++++ .../tools/trigger/utils/Makefile.am | 9 + .../tools/trigger/utils/notification-client.c | 242 ++ tests/unit/Makefile.am | 14 +- tests/unit/test_condition.c | 98 + tests/unit/test_event_expr_to_bytecode.c | 90 + .../gen-syscall-events/gen-syscall-events.c | 60 +- .../testapp/gen-ust-events/gen-ust-events.c | 8 +- tests/utils/testapp/gen-ust-events/tp.h | 27 + 235 files changed, 29361 insertions(+), 2074 deletions(-) create mode 100644 DO_NO_MERGE.txt create mode 100644 LICENSES/BSL-1.0 create mode 100644 doc/examples/trigger-on-event/Makefile create mode 100644 doc/examples/trigger-on-event/README.md create mode 100755 doc/examples/trigger-on-event/demo.sh create mode 100644 doc/examples/trigger-on-event/instrumented-app.c create mode 100644 doc/examples/trigger-on-event/notification-client.c create mode 100644 doc/examples/trigger-on-event/performance/Makefile create mode 100644 doc/examples/trigger-on-event/performance/README.md create mode 100644 doc/examples/trigger-on-event/performance/bt_plugin_plot.py create mode 100644 doc/examples/trigger-on-event/performance/consumer.c create mode 100755 doc/examples/trigger-on-event/performance/generate-data.sh create mode 100755 doc/examples/trigger-on-event/performance/generate-graph.sh create mode 100755 doc/examples/trigger-on-event/performance/perform-experience.sh create mode 100644 doc/examples/trigger-on-event/performance/performance.c create mode 100644 doc/examples/trigger-on-event/performance/performance.h create mode 100644 doc/examples/trigger-on-event/performance/producer.c create mode 100644 doc/examples/trigger-on-event/tracepoint-trigger-example.c create mode 100644 doc/examples/trigger-on-event/tracepoint-trigger-example.h create mode 100644 doc/man/lttng-add-trigger.1.txt create mode 100644 doc/man/lttng-list-triggers.1.txt create mode 100644 doc/man/lttng-remove-trigger.1.txt create mode 100644 include/lttng/condition/event-rule-internal.h create mode 100644 include/lttng/condition/event-rule.h create mode 100644 include/lttng/domain-internal.h create mode 100644 include/lttng/event-expr-internal.h create mode 100644 include/lttng/event-expr.h create mode 100644 include/lttng/event-field-value-internal.h create mode 100644 include/lttng/event-field-value.h create mode 100644 src/bin/lttng-sessiond/condition-internal.c create mode 100644 src/bin/lttng-sessiond/condition-internal.h create mode 100644 src/bin/lttng-sessiond/trigger-error-accounting.c create mode 100644 src/bin/lttng-sessiond/trigger-error-accounting.h create mode 100644 src/bin/lttng/commands/add_trigger.c create mode 100644 src/bin/lttng/commands/list_triggers.c create mode 100644 src/bin/lttng/commands/remove_trigger.c create mode 100644 src/bin/lttng/uprobe.c create mode 100644 src/bin/lttng/uprobe.h create mode 100644 src/common/argpar/Makefile.am create mode 100644 src/common/argpar/argpar.c create mode 100644 src/common/argpar/argpar.h create mode 100644 src/common/bytecode/Makefile.am create mode 100644 src/common/bytecode/bytecode.c create mode 100644 src/common/bytecode/bytecode.h rename src/common/{ => conditions}/buffer-usage.c (100%) rename src/common/{ => conditions}/condition.c (83%) create mode 100644 src/common/conditions/event-rule.c rename src/common/{ => conditions}/session-consumed-size.c (100%) rename src/common/{ => conditions}/session-rotation.c (100%) create mode 100644 src/common/domain.c create mode 100644 src/common/event-expr-to-bytecode.c create mode 100644 src/common/event-expr-to-bytecode.h create mode 100644 src/common/event-expr.c create mode 100644 src/common/event-field-value.c delete mode 100644 src/common/filter/filter-bytecode.h create mode 100644 src/common/index-allocator.c create mode 100644 src/common/index-allocator.h rename src/{bin/lttng-sessiond => common}/shm.c (86%) rename src/{bin/lttng-sessiond => common}/shm.h (85%) create mode 100644 src/vendor/Makefile.am create mode 100644 src/vendor/msgpack/Makefile.am create mode 100644 src/vendor/msgpack/fbuffer.h create mode 100644 src/vendor/msgpack/gcc_atomic.h create mode 100644 src/vendor/msgpack/lttng-config.h create mode 100644 src/vendor/msgpack/msgpack.h create mode 100644 src/vendor/msgpack/object.h create mode 100644 src/vendor/msgpack/objectc.c create mode 100644 src/vendor/msgpack/pack.h create mode 100644 src/vendor/msgpack/pack_define.h create mode 100644 src/vendor/msgpack/pack_template.h create mode 100644 src/vendor/msgpack/predef.h create mode 100644 src/vendor/msgpack/sbuffer.h create mode 100644 src/vendor/msgpack/sysdep.h create mode 100644 src/vendor/msgpack/timestamp.h create mode 100644 src/vendor/msgpack/unpack.c create mode 100644 src/vendor/msgpack/unpack.h create mode 100644 src/vendor/msgpack/unpack_define.h create mode 100644 src/vendor/msgpack/unpack_template.h create mode 100644 src/vendor/msgpack/util.h create mode 100644 src/vendor/msgpack/version.c create mode 100644 src/vendor/msgpack/version.h create mode 100644 src/vendor/msgpack/version_master.h create mode 100644 src/vendor/msgpack/vrefbuffer.c create mode 100644 src/vendor/msgpack/vrefbuffer.h create mode 100644 src/vendor/msgpack/zbuffer.h create mode 100644 src/vendor/msgpack/zone.c create mode 100644 src/vendor/msgpack/zone.h create mode 100644 tests/regression/tools/notification/sessiond_testpoints.c create mode 100755 tests/regression/tools/notification/test_notification_kernel_buffer_usage create mode 100755 tests/regression/tools/notification/test_notification_kernel_capture create mode 100755 tests/regression/tools/notification/test_notification_kernel_error create mode 100755 tests/regression/tools/notification/test_notification_kernel_instrumentation create mode 100755 tests/regression/tools/notification/test_notification_kernel_syscall create mode 100755 tests/regression/tools/notification/test_notification_kernel_userspace_probe create mode 100755 tests/regression/tools/notification/test_notification_trigger_discarded_count create mode 100755 tests/regression/tools/notification/test_notification_ust_buffer_usage create mode 100755 tests/regression/tools/notification/test_notification_ust_capture create mode 100755 tests/regression/tools/notification/test_notification_ust_error create mode 100755 tests/regression/tools/notification/test_notification_ust_event_rule_condition_exclusion create mode 100644 tests/regression/tools/notification/util_event_generator.sh create mode 100644 tests/regression/tools/trigger/Makefile.am create mode 100644 tests/regression/tools/trigger/base_client.c create mode 100644 tests/regression/tools/trigger/consumer_testpoints.c create mode 100644 tests/regression/tools/trigger/start-stop/Makefile.am create mode 100755 tests/regression/tools/trigger/start-stop/test_start_stop create mode 100755 tests/regression/tools/trigger/test_add_trigger_cli create mode 100755 tests/regression/tools/trigger/test_list_triggers_cli create mode 100755 tests/regression/tools/trigger/test_remove_trigger_cli rename tests/regression/tools/{notification/test_notification_kernel => trigger/test_trigger_kernel} (72%) rename tests/regression/tools/{notification/test_notification_ust => trigger/test_trigger_ust} (67%) create mode 100644 tests/regression/tools/trigger/trigger.c create mode 100644 tests/regression/tools/trigger/utils/Makefile.am create mode 100644 tests/regression/tools/trigger/utils/notification-client.c create mode 100644 tests/unit/test_condition.c create mode 100644 tests/unit/test_event_expr_to_bytecode.c diff --git a/.gitignore b/.gitignore index 9b2b60d04..f1b53075a 100644 --- a/.gitignore +++ b/.gitignore @@ -90,6 +90,7 @@ compile_commands.json /tests/unit/test_fd_tracker /tests/unit/test_payload /tests/unit/test_event_rule +/tests/unit/test_condition /tests/unit/test_unix_socket kernel_all_events_basic kernel_event_basic @@ -108,6 +109,8 @@ health_check /tests/regression/tools/notification/notification /tests/regression/tools/rotation/schedule_api /tests/regression/tools/notification/rotation +/tests/regression/tools/trigger/base_client +/tests/regression/tools/trigger/trigger /tests/regression/ust/overlap/demo/demo /tests/regression/ust/linking/demo_builtin /tests/regression/ust/linking/demo_static @@ -163,6 +166,9 @@ health_check # examples /doc/examples/rotation/rotate-client +/doc/examples/trigger-on-event/performance/consumer +/doc/examples/trigger-on-event/performance/producer +/doc/examples/trigger-on-event/performance/performance.a /benchmark/ diff --git a/DO_NO_MERGE.txt b/DO_NO_MERGE.txt new file mode 100644 index 000000000..0c57f8d93 --- /dev/null +++ b/DO_NO_MERGE.txt @@ -0,0 +1,2 @@ +captures +trigger error counter diff --git a/LICENSES/BSL-1.0 b/LICENSES/BSL-1.0 new file mode 100644 index 000000000..f1a01222e --- /dev/null +++ b/LICENSES/BSL-1.0 @@ -0,0 +1,30 @@ +Valid-License-Identifier: BSL-1.0 +SPDX-URL: https://spdx.org/licenses/BSL-1.0.html +Usage-Guide: + To use the Boost Software License put the following SPDX tag/value pair into a + comment according to the placement guidelines in the licensing rules + documentation: + SPDX-License-Identifier: BSL-1.0 +License-Text: + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by this +license (the "Software") to use, reproduce, display, distribute, execute, and +transmit the Software, and to prepare derivative works of the Software, and to +permit third-parties to whom the Software is furnished to do so, all subject to +the following: + +The copyright notices in the Software and this entire statement, including the +above license grant, this restriction and the following disclaimer, must be +included in all copies of the Software, in whole or in part, and all derivative +works of the Software, unless such copies or derivative works are solely in the +form of machine-executable object code generated by a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE +COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES +OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Makefile.am b/Makefile.am index 0240558c8..807d85b17 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,4 +22,5 @@ EXTRA_DIST = extras/lttng-bash_completion \ LICENSES/GPL-2.0 \ LICENSES/LGPL-2.1 \ LICENSES/MIT \ + LICENSES/BSL-1.0 \ version diff --git a/configure.ac b/configure.ac index 1b6f3fcb0..0c458e5d0 100644 --- a/configure.ac +++ b/configure.ac @@ -1121,6 +1121,8 @@ AC_CONFIG_FILES([ extras/core-handler/Makefile src/Makefile src/common/Makefile + src/common/argpar/Makefile + src/common/bytecode/Makefile src/common/kernel-ctl/Makefile src/common/kernel-consumer/Makefile src/common/consumer/Makefile @@ -1145,6 +1147,8 @@ AC_CONFIG_FILES([ src/bin/lttng-relayd/Makefile src/bin/lttng/Makefile src/bin/lttng-crash/Makefile + src/vendor/Makefile + src/vendor/msgpack/Makefile tests/Makefile tests/destructive/Makefile tests/regression/Makefile @@ -1172,6 +1176,9 @@ AC_CONFIG_FILES([ tests/regression/tools/working-directory/Makefile tests/regression/tools/relayd-grouping/Makefile tests/regression/tools/clear/Makefile + tests/regression/tools/trigger/Makefile + tests/regression/tools/trigger/start-stop/Makefile + tests/regression/tools/trigger/utils/Makefile tests/regression/ust/Makefile tests/regression/ust/nprocesses/Makefile tests/regression/ust/high-throughput/Makefile diff --git a/doc/examples/trigger-on-event/Makefile b/doc/examples/trigger-on-event/Makefile new file mode 100644 index 000000000..f758a05cf --- /dev/null +++ b/doc/examples/trigger-on-event/Makefile @@ -0,0 +1,56 @@ +# Copyright (C) 2020 Jérémie Galarneau +# +# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +# OR IMPLIED. ANY USE IS AT YOUR OWN RISK. +# +# Permission is hereby granted to use or copy this program for any +# purpose, provided the above notices are retained on all copies. +# Permission to modify the code and to distribute modified code is +# granted, provided the above notices are retained, and a notice that +# the code was modified is included with the above copyright notice. +# +# This Makefile is not using automake so that users may see how to build +# a program with tracepoint provider probes compiled as static libraries. +# +# This makefile is purposefully kept simple to support GNU and BSD make. + +LOCAL_CPPFLAGS += -I. +LIBS_INSTRUMENTED_APP = -ldl -llttng-ust +LIBS_NOTIFICATION_CLIENT = -ldl -llttng-ctl +AM_V_P := : +AR ?= ar + +all: instrumented-app notification-client + +tracepoint-trigger-example.o: tracepoint-trigger-example.c tracepoint-trigger-example.h + @if $(AM_V_P); then set -x; else echo " CC $@"; fi; \ + $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) \ + $(CFLAGS) -c -o $@ $< + +tracepoint-trigger-example.a: tracepoint-trigger-example.o + @if $(AM_V_P); then set -x; else echo " AR $@"; fi; \ + $(AR) -rc $@ tracepoint-trigger-example.o + +instrumented-app.o: instrumented-app.c + @if $(AM_V_P); then set -x; else echo " CC $@"; fi; \ + $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) \ + $(CFLAGS) -c -o $@ $< + +instrumented-app: instrumented-app.o tracepoint-trigger-example.a + @if $(AM_V_P); then set -x; else echo " CCLD $@"; fi; \ + $(CC) -o $@ $(LDFLAGS) $(CPPFLAGS) $(AM_LDFLAGS) $(AM_CFLAGS) \ + $(CFLAGS) instrumented-app.o tracepoint-trigger-example.a $(LIBS_INSTRUMENTED_APP) + +notification-client.o: notification-client.c + @if $(AM_V_P); then set -x; else echo " CC $@"; fi; \ + $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) \ + $(CFLAGS) -c -o $@ $< + +notification-client: notification-client.o + @if $(AM_V_P); then set -x; else echo " CCLD $@"; fi; \ + $(CC) -o $@ $(LDFLAGS) $(CPPFLAGS) $(AM_LDFLAGS) $(AM_CFLAGS) \ + $(CFLAGS) notification-client.o tracepoint-trigger-example.a $(LIBS_NOTIFICATION_CLIENT) + +.PHONY: clean +clean: + rm -f *.o *.a instrumented-app notification-client diff --git a/doc/examples/trigger-on-event/README.md b/doc/examples/trigger-on-event/README.md new file mode 100644 index 000000000..da2c7b937 --- /dev/null +++ b/doc/examples/trigger-on-event/README.md @@ -0,0 +1,95 @@ +# Trigger notification example + +## Description +This example is made-up of three executables. + +### `notification-client` + +``` +Usage: notification-client TRIGGER_NAME TRIGGER_NAME2 ... +``` + +A simple client that subscribes to the notifications emitted by the `TRIGGER_NAME` trigger. + +Multiple trigger names can be passed and subscribed to. + + +### `instrumented-app` + +An application that emits the `trigger_example:my_event` event every 2 seconds. + +### `demo.sh` + +This script adds a trigger named `demo_trigger` which emits a notification when +the user-space `trigger_example:my_event` event occurs. + +This script also adds a trigger named `demo_trigger_capture` which emits a +notification when the user-space `trigger_example:my_event` event occurs and +provides captured fields if present. + +Once the triggers have been setup, the notification-client is launched to print +all notifications emitted by the `demo_trigger` and `demo_trigger_capture` +trigger. + +## Building + +Simply run the included Makefile. + +## Running the example + +1) Launch a session daemon using: + ``` + $ lttng-sessiond + ``` +2) Launch the `demo.sh` script +3) Launch the `instrumented-app` + +The following output should be produced: + +``` +$ ./demo.sh +Registering a notification trigger named "demo_trigger" for the trigger_example:my_event user-space event +Trigger registered successfully. +Trigger registered successfully. +Subscribed to notifications of trigger "demo_trigger_capture" +Subscribed to notifications of trigger "demo_trigger" +[08-24-2020] 17:20:33.598221 - Received notification of event rule trigger "demo_trigger" +[08-24-2020] 17:20:33.598855 - Received notification of event rule trigger "demo_trigger_capture" +Captured field values: + Field: iteration Value: [Unsigned int] 0, + Field: does_not_exist Value: Capture unavailable, + Field: $ctx.vtid Value: [Unsigned int] 2302494, + Field: $ctx.procname Value: [String] instrumented-ap. +[08-24-2020] 17:20:35.598556 - Received notification of event rule trigger "demo_trigger" +[08-24-2020] 17:20:35.599293 - Received notification of event rule trigger "demo_trigger_capture" +Captured field values: + Field: iteration Value: [Unsigned int] 1, + Field: does_not_exist Value: Capture unavailable, + Field: $ctx.vtid Value: [Unsigned int] 2302494, + Field: $ctx.procname Value: [String] instrumented-ap. +[08-24-2020] 17:20:37.598977 - Received notification of event rule trigger "demo_trigger" +[08-24-2020] 17:20:37.599676 - Received notification of event rule trigger "demo_trigger_capture" +Captured field values: + Field: iteration Value: [Unsigned int] 2, + Field: does_not_exist Value: Capture unavailable, + Field: $ctx.vtid Value: [Unsigned int] 2302494, + Field: $ctx.procname Value: [String] instrumented-ap. +[08-24-2020] 17:20:39.599430 - Received notification of event rule trigger "demo_trigger" +[08-24-2020] 17:20:39.600178 - Received notification of event rule trigger "demo_trigger_capture" +Captured field values: + Field: iteration Value: [Unsigned int] 3, + Field: does_not_exist Value: Capture unavailable, + Field: $ctx.vtid Value: [Unsigned int] 2302494, + Field: $ctx.procname Value: [String] instrumented-ap. +... +``` + +``` +$ ./instrumented-app +[08-24-2020] 17:20:33.597441 - Tracing event "trigger_example:my_event" +[08-24-2020] 17:20:35.597703 - Tracing event "trigger_example:my_event" +[08-24-2020] 17:20:37.597997 - Tracing event "trigger_example:my_event" +... +``` + + diff --git a/doc/examples/trigger-on-event/demo.sh b/doc/examples/trigger-on-event/demo.sh new file mode 100755 index 000000000..9bf8a0083 --- /dev/null +++ b/doc/examples/trigger-on-event/demo.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# +# Copyright (C) 2020 Jérémie Galarneau +# +# SPDX-License-Identifier: MIT + +EVENT_NAME=trigger_example:my_event +TRIGGER_NAME=demo_trigger +TRIGGER_NAME_CAPTURE=demo_trigger_capture + +lttng list > /dev/null 2>&1 +if [ $? -ne 0 ]; then + echo "Could not connect to session daemon, are you sure it is running?" + exit 1 +fi + +echo "Registering a notification trigger named \"$TRIGGER_NAME\" for the $EVENT_NAME user-space event" +lttng add-trigger --id $TRIGGER_NAME --condition on-event --userspace $EVENT_NAME --action notify +lttng add-trigger --id $TRIGGER_NAME_CAPTURE --condition on-event --userspace $EVENT_NAME --capture 'iteration' --capture 'does_not_exist' --capture '$ctx.vtid' --capture '$ctx.procname' --action notify + +./notification-client $TRIGGER_NAME $TRIGGER_NAME_CAPTURE + diff --git a/doc/examples/trigger-on-event/instrumented-app.c b/doc/examples/trigger-on-event/instrumented-app.c new file mode 100644 index 000000000..f6fd67de3 --- /dev/null +++ b/doc/examples/trigger-on-event/instrumented-app.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: MIT + * + */ + +#include "tracepoint-trigger-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_event\"\n", time_str, tv.tv_usec); + + tracepoint(trigger_example, my_event, i); + sleep(2); + } + return 0; +} diff --git a/doc/examples/trigger-on-event/notification-client.c b/doc/examples/trigger-on-event/notification-client.c new file mode 100644 index 000000000..dc5044665 --- /dev/null +++ b/doc/examples/trigger-on-event/notification-client.c @@ -0,0 +1,530 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: MIT + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int print_capture(const struct lttng_condition *condition, + const struct lttng_event_field_value *capture, + unsigned int indent_level); +static int print_array(const struct lttng_condition *condition, + const struct lttng_event_field_value *array, + unsigned int indent_level); + +static void indent(unsigned int indentation_level) +{ + unsigned int i; + for (i = 0; i < indentation_level; i++) { + printf(" "); + } +} + +static +void print_one_event_expr(const struct lttng_event_expr *event_expr) +{ + enum lttng_event_expr_type type; + + type = lttng_event_expr_get_type(event_expr); + + switch (type) { + case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: { + const char *name; + + name = lttng_event_expr_event_payload_field_get_name(event_expr); + printf("%s", name); + + break; + } + + case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: { + const char *name; + + name = lttng_event_expr_channel_context_field_get_name(event_expr); + printf("$ctx.%s", name); + + break; + } + + case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: { + const char *provider_name; + const char *type_name; + + provider_name = + lttng_event_expr_app_specific_context_field_get_provider_name( + event_expr); + type_name = + lttng_event_expr_app_specific_context_field_get_type_name( + event_expr); + + printf("$app.%s:%s", provider_name, type_name); + + break; + } + + case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: { + unsigned int index; + const struct lttng_event_expr *parent_expr; + enum lttng_event_expr_status status; + + parent_expr = lttng_event_expr_array_field_element_get_parent_expr( + event_expr); + assert(parent_expr != NULL); + + print_one_event_expr(parent_expr); + + status = lttng_event_expr_array_field_element_get_index( + event_expr, &index); + assert(status == LTTNG_EVENT_EXPR_STATUS_OK); + + printf("[%u]", index); + + break; + } + + default: + abort(); + } +} + +static bool action_group_contains_notify( + const struct lttng_action *action_group) +{ + unsigned int i, count; + enum lttng_action_status status = + lttng_action_group_get_count(action_group, &count); + + if (status != LTTNG_ACTION_STATUS_OK) { + printf("Failed to get action count from action group\n"); + exit(1); + } + + for (i = 0; i < count; i++) { + const struct lttng_action *action = + lttng_action_group_get_at_index( + action_group, i); + const enum lttng_action_type action_type = + lttng_action_get_type(action); + + if (action_type == LTTNG_ACTION_TYPE_NOTIFY) { + return true; + } + } + return false; +} + +static int print_capture(const struct lttng_condition *condition, + const struct lttng_event_field_value *capture, + unsigned int indent_level) +{ + int ret = 0; + enum lttng_event_field_value_status event_field_status; + enum lttng_event_field_value_type type; + uint64_t u_val; + int64_t s_val; + double d_val; + const char *string_val = NULL; + + switch (lttng_event_field_value_get_type(capture)) { + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT: + { + event_field_status = + lttng_event_field_value_unsigned_int_get_value( + capture, &u_val); + if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + ret = 1; + goto end; + } + + printf("[Unsigned int] %" PRIu64, u_val); + break; + } + case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT: + { + event_field_status = + lttng_event_field_value_signed_int_get_value( + capture, &s_val); + if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + ret = 1; + goto end; + } + + printf("[Signed int] %" PRId64, s_val); + break; + } + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM: + { + event_field_status = + lttng_event_field_value_unsigned_int_get_value( + capture, &u_val); + if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + ret = 1; + goto end; + } + + printf("[Unsigned enum] %" PRIu64, u_val); + break; + } + case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM: + { + event_field_status = + lttng_event_field_value_signed_int_get_value( + capture, &s_val); + if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + ret = 1; + goto end; + } + + printf("[Signed enum] %" PRId64, s_val); + break; + } + case LTTNG_EVENT_FIELD_VALUE_TYPE_REAL: + { + event_field_status = lttng_event_field_value_real_get_value( + capture, &d_val); + if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + ret = 1; + goto end; + } + + printf("[Real] %lf", d_val); + break; + } + case LTTNG_EVENT_FIELD_VALUE_TYPE_STRING: + { + string_val = lttng_event_field_value_string_get_value(capture); + if (string_val == NULL) { + ret = 1; + goto end; + } + + printf("[String] %s", string_val); + break; + } + case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY: + printf("[Array] [\n"); + print_array(condition, capture, indent_level); + indent(indent_level); + printf("]\n"); + break; + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN: + case LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID: + default: + ret = 1; + break; + } + +end: + return ret; +} + +static void print_unavailabe(void) +{ + printf("Capture unavailable"); +} + +static int print_array(const struct lttng_condition *condition, + const struct lttng_event_field_value *array, + unsigned int indent_level) +{ + int ret = 0; + enum lttng_event_field_value_status event_field_status; + unsigned int captured_field_count; + + event_field_status = lttng_event_field_value_array_get_length( + array, &captured_field_count); + if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + ret = 1; + goto end; + } + + for (unsigned int i = 0; i < captured_field_count; i++) { + const struct lttng_event_field_value *captured_field = NULL; + const struct lttng_event_expr *expr = + lttng_condition_event_rule_get_capture_descriptor_at_index( + condition, i); + assert(expr); + + indent(indent_level + 1); + + printf("Field: "); + print_one_event_expr(expr); + printf(" Value: "); + + event_field_status = + lttng_event_field_value_array_get_element_at_index( + array, i, &captured_field); + if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + if (event_field_status == + LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE) { + print_unavailabe(); + } else { + ret = 1; + goto end; + } + } else { + print_capture(condition, captured_field, indent_level + 1); + } + + if (i + 1 < captured_field_count) { + printf(","); + } else { + printf("."); + } + printf("\n"); + } + +end: + return ret; +} + +static int print_captures(struct lttng_notification *notification) +{ + int ret = 0; + const struct lttng_evaluation *evaluation = + lttng_notification_get_evaluation(notification); + const struct lttng_condition *condition = + lttng_notification_get_condition(notification); + + /* Status */ + enum lttng_condition_status condition_status; + enum lttng_evaluation_status evaluation_status; + enum lttng_event_field_value_status event_field_status; + + const struct lttng_event_field_value *captured_field_array = NULL; + unsigned int expected_capture_field_count; + unsigned int captured_field_count; + + assert(lttng_evaluation_get_type(evaluation) == + LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + + condition_status = + lttng_condition_event_rule_get_capture_descriptor_count( + condition, + &expected_capture_field_count); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + ret = 1; + goto end; + } + + if (expected_capture_field_count == 0) { + ret = 0; + goto end; + } + + evaluation_status = lttng_evaluation_get_captured_values( + evaluation, &captured_field_array); + if (evaluation_status != LTTNG_EVALUATION_STATUS_OK) { + ret = 1; + goto end; + } + + printf("Captured field values:\n"); + print_array(condition, captured_field_array, 1); +end: + return ret; +} + +static int print_notification(struct lttng_notification *notification) +{ + int ret = 0; + const struct lttng_evaluation *evaluation = + lttng_notification_get_evaluation(notification); + const enum lttng_condition_type type = + lttng_evaluation_get_type(evaluation); + + switch (type) { + case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: + printf("Received consumed size notification\n"); + break; + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + printf("Received buffer usage notification\n"); + break; + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + printf("Received session rotation ongoing notification\n"); + break; + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + printf("Received session rotation completed notification\n"); + break; + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + { + const char *trigger_name; + enum lttng_evaluation_status evaluation_status; + char time_str[64]; + struct timeval tv; + time_t the_time; + + gettimeofday(&tv, NULL); + the_time = tv.tv_sec; + + strftime(time_str, sizeof(time_str), "[%m-%d-%Y] %T", + localtime(&the_time)); + printf("%s.%ld - ", time_str, tv.tv_usec); + + evaluation_status = + lttng_evaluation_event_rule_get_trigger_name( + evaluation, &trigger_name); + if (evaluation_status != LTTNG_EVALUATION_STATUS_OK) { + fprintf(stderr, "Failed to get trigger name of event rule notification\n"); + ret = -1; + break; + } + + printf("Received notification of event rule trigger \"%s\"\n", + trigger_name); + ret = print_captures(notification); + break; + } + default: + fprintf(stderr, "Unknown notification type (%d)\n", type); + } + + return ret; +} + +int main(int argc, char **argv) +{ + int ret; + struct lttng_triggers *triggers = NULL; + unsigned int count, i, j, subcription_count = 0, trigger_count; + enum lttng_trigger_status trigger_status; + struct lttng_notification_channel *notification_channel = NULL; + + if (argc < 2) { + fprintf(stderr, "Missing trigger name(s)\n"); + fprintf(stderr, "Usage: notification-client TRIGGER_NAME ..."); + ret = -1; + goto end; + } + + trigger_count = argc - 1; + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + if (!notification_channel) { + fprintf(stderr, "Failed to create notification channel\n"); + ret = -1; + goto end; + } + + ret = lttng_list_triggers(&triggers); + if (ret != LTTNG_OK) { + fprintf(stderr, "Failed to list triggers\n"); + goto end; + } + + trigger_status = lttng_triggers_get_count(triggers, &count); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + fprintf(stderr, "Failed to get trigger count\n"); + ret = -1; + goto end; + } + + for (i = 0; i < count; i++) { + const struct lttng_trigger *trigger = + lttng_triggers_get_at_index(triggers, i); + const struct lttng_condition *condition = + lttng_trigger_get_const_condition(trigger); + const struct lttng_action *action = + lttng_trigger_get_const_action(trigger); + const enum lttng_action_type action_type = + lttng_action_get_type(action); + enum lttng_notification_channel_status channel_status; + const char *trigger_name = NULL; + bool subscribe = false; + + lttng_trigger_get_name(trigger, &trigger_name); + for (j = 0; j < trigger_count; j++) { + if (!strcmp(trigger_name, argv[j + 1])) { + subscribe = true; + break; + } + } + + if (!subscribe) { + continue; + } + + if (!((action_type == LTTNG_ACTION_TYPE_GROUP && + action_group_contains_notify(action)) || + action_type == LTTNG_ACTION_TYPE_NOTIFY)) { + printf("The action of trigger \"%s\" is not \"notify\", skipping.\n", + trigger_name); + continue; + } + + channel_status = lttng_notification_channel_subscribe( + notification_channel, condition); + if (channel_status == + LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED) { + continue; + } + if (channel_status) { + fprintf(stderr, "Failed to subscribe to notifications of trigger \"%s\"\n", + trigger_name); + ret = -1; + goto end; + } + + printf("Subscribed to notifications of trigger \"%s\"\n", + trigger_name); + subcription_count++; + } + + if (subcription_count == 0) { + printf("No matching trigger with a notify action found.\n"); + ret = 0; + goto end; + } + + for (;;) { + struct lttng_notification *notification; + enum lttng_notification_channel_status channel_status; + + channel_status = + lttng_notification_channel_get_next_notification( + notification_channel, + ¬ification); + switch (channel_status) { + case LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED: + printf("Dropped notification\n"); + break; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED: + ret = 0; + goto end; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK: + break; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_CLOSED: + printf("Notification channel was closed by peer.\n"); + break; + default: + fprintf(stderr, "A communication error occurred on the notification channel.\n"); + ret = -1; + goto end; + } + + ret = print_notification(notification); + lttng_notification_destroy(notification); + if (ret) { + goto end; + } + } +end: + lttng_triggers_destroy(triggers); + lttng_notification_channel_destroy(notification_channel); + return !!ret; +} diff --git a/doc/examples/trigger-on-event/performance/Makefile b/doc/examples/trigger-on-event/performance/Makefile new file mode 100644 index 000000000..751548b4a --- /dev/null +++ b/doc/examples/trigger-on-event/performance/Makefile @@ -0,0 +1,57 @@ +# Copyright (C) 2020 Jérémie Galarneau +# +# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +# OR IMPLIED. ANY USE IS AT YOUR OWN RISK. +# +# Permission is hereby granted to use or copy this program for any +# purpose, provided the above notices are retained on all copies. +# Permission to modify the code and to distribute modified code is +# granted, provided the above notices are retained, and a notice that +# the code was modified is included with the above copyright notice. +# +# This Makefile is not using automake so that users may see how to build +# a program with tracepoint provider probes compiled as static libraries. +# +# This makefile is purposefully kept simple to support GNU and BSD make. + +LOCAL_CPPFLAGS += -I. +LIBS_INSTRUMENTED_APP = -ldl -llttng-ust +LIBS_NOTIFICATION_CLIENT = -ldl -llttng-ctl +LIBS_PERFORMANCE_CLIENT = -ldl -llttng-ust -llttng-ctl +AM_V_P := : +AR ?= ar + +all: producer consumer + +performance.o: performance.c performance.h + @if $(AM_V_P); then set -x; else echo " CC $@"; fi; \ + $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) \ + $(CFLAGS) -c -o $@ $< + +performance.a: performance.o + @if $(AM_V_P); then set -x; else echo " AR $@"; fi; \ + $(AR) -rc $@ performance.o + +producer.o: producer.c + @if $(AM_V_P); then set -x; else echo " CC $@"; fi; \ + $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) \ + $(CFLAGS) -c -o $@ $< + +consumer.o: consumer.c + @if $(AM_V_P); then set -x; else echo " CC $@"; fi; \ + $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) \ + $(CFLAGS) -c -o $@ $< + +producer: producer.o performance.a + @if $(AM_V_P); then set -x; else echo " CCLD $@"; fi; \ + $(CC) -o $@ $(LDFLAGS) $(CPPFLAGS) $(AM_LDFLAGS) $(AM_CFLAGS) \ + $(CFLAGS) producer.o performance.a $(LIBS_PERFORMANCE_CLIENT) + +consumer: consumer.o performance.a + @if $(AM_V_P); then set -x; else echo " CCLD $@"; fi; \ + $(CC) -o $@ $(LDFLAGS) $(CPPFLAGS) $(AM_LDFLAGS) $(AM_CFLAGS) \ + $(CFLAGS) consumer.o performance.a $(LIBS_PERFORMANCE_CLIENT) + +.PHONY: clean +clean: + rm -f *.o *.a producer consumer diff --git a/doc/examples/trigger-on-event/performance/README.md b/doc/examples/trigger-on-event/performance/README.md new file mode 100644 index 000000000..8592f4139 --- /dev/null +++ b/doc/examples/trigger-on-event/performance/README.md @@ -0,0 +1,83 @@ +# Trigger notification end-to-end latency analysis + +## Description +This analysis is made-up of five executables. + +### `producer` + +``` +Usage: producer UNIQUE_ID NB_EVENT DELAY_MS +``` + +An application that emits `NB_EVENT` times the `performance:hit` event every +`DELAY_MS` milliseconds. + + +### `consumer` +``` +Usage: consumer UNIQUE_ID NB_EVENT TRIGGER_NAME +``` + +A simple notification client that subscribes to the notifications emitted by the +`TRIGGER_NAME` trigger. The consumer expects `NB_EVENT` notification and on each +valid reception emits a `performance:receive` event. + + +### `perform-experience.sh` + +``` +Usage: perform-experience.sh SOURCE_ID TRACE_DIRECTORY_NAME DELAY_MS NB_EVENT` +``` + +This script performs a complete end-to-end trigger latency experience with +`DELAY_MS` between each trigger hit and `NB_EVENT` times. + +The resulting lttng-ust trace is stored inside `$(pwd)/trace/TRACE_DIRECTORY_NAME` + +### `generate-data.sh` + +``` +Usage: generate-data.sh +``` + +This script performs all configured experiences and apply the customs workload +as necessary. + +The resulting traces are stored inside `$(pwd)/trace/` + +This script in its current form will run for about 25 hours. + +This script depends on `perform-experience.sh`. + +### `generate-graph.sh` + +``` +Usage: generate-graph.sh +``` + +This script generate all histograms and saved them individually as pdf files. It +also generate a `summary.pdf` files that contains all pdfs in order of trigger frequency. + +This script does not have to run on the machine that produced the data. + +This script requires the presence of the "trace/" folder to work. + +This script depends on the `bt_plugin_plot.py` babeltrace 2 plugins. Hence this +script requires Babeltrace 2 with python bindings and python plugin support. + +The `bt_plugin_plot.py` requires `matplotlib`. + + +## Building + +Simply run the included Makefile. + +## Running the complete + +1) Launch a session daemon using: + ``` + $ lttng-sessiond + ``` +2) Launch `generate-data.sh` +3) Wait ~25 hours +3) Launch `generate-graph.sh` diff --git a/doc/examples/trigger-on-event/performance/bt_plugin_plot.py b/doc/examples/trigger-on-event/performance/bt_plugin_plot.py new file mode 100644 index 000000000..626a9a11a --- /dev/null +++ b/doc/examples/trigger-on-event/performance/bt_plugin_plot.py @@ -0,0 +1,260 @@ +import bt2 +import itertools +import matplotlib.pyplot as plt +import sys +import statistics +import csv +from collections import defaultdict + + +class DataLogger(object): + def __init__(self, name="Untitled"): + self._name = name + + def get_name(self): + return self._name + + def get_x_data(self): + raise NotImplementedError + + def get_y_data(self): + raise NotImplementedError + + def received_event(self, ts, event): + raise NotImplementedError + + +class DurationDataLogger(DataLogger): + """ + This class allow to create a duration histogram for the given pair of + event and unique tuple key generator. + + """ + def __init__(self, start_event, end_event, *args, **kwargs): + super(DurationDataLogger, self).__init__(*args, **kwargs) + + (self._event_start, self._start_fields) = start_event + (self._event_end, self._end_fields) = end_event + + self._durations = [] + self._pair = dict() + + def get_x_data(self): + return self._durations + + def received_event(self, ts, event): + if event.name == self._event_start: + key = () + for field in self._start_fields: + value = event.payload_field[str(field)] + key = key + (value,) + self._pair[key] = ts + return + + if event.name == self._event_end: + key = () + for field in self._end_fields: + value = event.payload_field[str(field)] + key = key + (value,) + + if key not in self._pair: + print("unmatched end event") + return + + start_ts = self._pair[key] + duration = (ts - start_ts) / 1000000.0 + self._durations.append(duration) + +class DurationCSVDataLogger(DataLogger): + """ + This class allow to create a duration histogram for the given csv. + """ + def __init__(self, filepath, *args, **kwargs): + super(DurationCSVDataLogger, self).__init__(*args, **kwargs) + + self._filepath = filepath + + + self._durations = [] + with open(filepath, newline='') as file: + reader = csv.reader(file, quoting=csv.QUOTE_NONE) + next(reader) + for row in reader: + self._durations.append(float(row[0])) + + def get_x_data(self): + return self._durations + + def received_event(self, ts, event): + return + + +class Plot(object): + def __init__( + self, loggers, title="Untitled", x_label="Untitled", y_label="Untitled" + ): + self._loggers = loggers + self._title = title + self._x_label = x_label + self._y_label = y_label + + def received_event(self, ts, event): + for logger in self._loggers: + logger.received_event(ts, event) + + def plot(self): + raise NotImplementedError + + def generate_csv(self): + raise NotImplementedError + + @staticmethod + def _format_filename(title, ext): + title = title.lower() + title = "".join("-" if not c.isalnum() else c for c in title) + title = "".join( + ["".join(j) if i != "-" else i for (i, j) in itertools.groupby(title)] + ) + return f"{title}.{ext}" + +class HistogramPlot(Plot): + def __init__(self, *args, **kwargs): + super(HistogramPlot, self).__init__(*args, **kwargs) + + @staticmethod + def get_statistics_header(): + return ["minimum", "maximum", "mean", "pstdev", "count"] + + @staticmethod + def get_statistics(samples): + stats = [] + stats.append('%f' % min(samples)) + stats.append('%f' % max(samples)) + stats.append('%f' % statistics.mean(samples)) + stats.append('%f' % statistics.pstdev(samples)) + stats.append('%d' % len(samples)) + return stats + + def plot(self): + sys.argv = [''] + complete_set = []; + logger_statistic = defaultdict(dict) + + figure = plt.figure() + plt.title(self._title) + plt.xlabel(self._x_label, figure=figure) + plt.ylabel(self._y_label, figure=figure) + plt.yscale('log', nonposy='clip') + + table_rows_label = [] + table_celltext = [] + for logger in self._loggers: + x = logger.get_x_data() + table_rows_label.append(logger.get_name()) + table_celltext.append(HistogramPlot.get_statistics(x)) + + complete_set +=x; + plt.hist(x, bins='auto', alpha=0.5, figure=figure, label=logger.get_name()) + + table_rows_label.append("all") + table_celltext.append(HistogramPlot.get_statistics(complete_set)) + the_table = plt.table(cellText=table_celltext, + rowLabels=table_rows_label, + colLabels=HistogramPlot.get_statistics_header(), + loc='bottom', + bbox=[0.0,-0.45,1,.28], + ) + + the_table.auto_set_font_size(False) + the_table.set_fontsize(8) + + plt.subplots_adjust(bottom=0.20) + plt.legend(loc='center left', bbox_to_anchor=(1, 0.5)) + plt.savefig(Plot._format_filename(self._title, "pdf"), bbox_inches="tight") + + def generate_csv(self): + for logger in self._loggers: + x_data = logger.get_x_data() + with open(Plot._format_filename(self._title, "%s.csv" % logger.get_name()), 'w', newline='') as export: + wr = csv.writer(export, quoting=csv.QUOTE_NONE) + wr.writerow([self._x_label]) + for x in x_data: + wr.writerow([x]) + + +@bt2.plugin_component_class +class PlotSink(bt2._UserSinkComponent): + def __init__(self, config, params, obj): + self._plots = [] + + if "histograms" in params: + for plot in params["histograms"]: + self._plots.append(PlotSink.create_histogram(plot)) + + self._add_input_port("in") + + def _user_consume(self): + msg = next(self._iter) + if type(msg) in [ + bt2._PacketBeginningMessageConst, + bt2._PacketEndMessageConst, + bt2._StreamBeginningMessageConst, + bt2._StreamEndMessageConst, + ]: + return + + ts = msg.default_clock_snapshot.value + for plot in self._plots: + plot.received_event(ts, msg.event) + + def _user_finalize(self): + {plot.plot() for plot in self._plots} + {plot.generate_csv () for plot in self._plots} + return + + def _user_graph_is_configured(self): + self._iter = self._create_message_iterator(self._input_ports["in"]) + + @staticmethod + def create_histogram(params): + loggers = [] + for logger in params[3]: + if logger[0] == "duration": + logger = PlotSink.create_duration_logger(logger) + elif logger[0] == "duration-csv": + logger = PlotSink.create_duration_logger_csv(logger) + else: + raise ValueError + + loggers.append(logger) + + title = str(params[0]) + x_label = str(params[1]) + y_label = str(params[2]) + + return HistogramPlot(loggers, title=title, x_label=x_label, + y_label=y_label) + + @staticmethod + def create_duration_logger(params): + return DurationDataLogger( + (str(params[2]), params[3]), + (str(params[4]), params[5]), + name=str(params[1]), + ) + + def create_duration_logger_csv(params): + return DurationCSVDataLogger( + str(params[2]), + name=str(params[1]), + ) + + +bt2.register_plugin( + module_name=__name__, + name="plot", + description="Plot Sink", + author="EfficiOS inc.", + license="GPL", + version=(1, 0, 0), +) diff --git a/doc/examples/trigger-on-event/performance/consumer.c b/doc/examples/trigger-on-event/performance/consumer.c new file mode 100644 index 000000000..31c4f1436 --- /dev/null +++ b/doc/examples/trigger-on-event/performance/consumer.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * Copyright (C) 2020 Jonathan Rajotte-Julien + * + * + * SPDX-License-Identifier: MIT + * + */ +#include "performance.h" + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +bool action_group_contains_notify(const struct lttng_action *action_group) +{ + unsigned int i, count; + enum lttng_action_status status = + lttng_action_group_get_count(action_group, &count); + + if (status != LTTNG_ACTION_STATUS_OK) { + printf("Failed to get action count from action group\n"); + exit(1); + } + + for (i = 0; i < count; i++) { + const struct lttng_action *action = + lttng_action_group_get_at_index( + action_group, i); + const enum lttng_action_type action_type = + lttng_action_get_type(action); + + if (action_type == LTTNG_ACTION_TYPE_NOTIFY) { + return true; + } + } + return false; +} + +int main(int argc, char **argv) +{ + int ret; + int id, nb_reception; + struct lttng_triggers *triggers = NULL; + unsigned int count, i, subcription_count = 0; + enum lttng_trigger_status trigger_status; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_notification *notification; + enum lttng_notification_channel_status channel_status; + const struct lttng_evaluation *evaluation; + enum lttng_condition_type type; + + if (argc != 4) { + fprintf(stderr, "Missing unique_id\n"); + fprintf(stderr, "Missing nb_event\n"); + fprintf(stderr, "Missing trigger name\n"); + fprintf(stderr, "Usage: notification-client TRIGGER_NAME\n"); + ret = 1; + goto end; + } + + id = atoi(argv[1]); + nb_reception = atoi(argv[2]); + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + if (!notification_channel) { + fprintf(stderr, "Failed to create notification channel\n"); + ret = -1; + goto end; + } + + ret = lttng_list_triggers(&triggers); + if (ret != LTTNG_OK) { + fprintf(stderr, "Failed to list triggers\n"); + goto end; + } + + trigger_status = lttng_triggers_get_count(triggers, &count); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + fprintf(stderr, "Failed to get trigger count\n"); + ret = -1; + goto end; + } + + for (i = 0; i < count; i++) { + const struct lttng_trigger *trigger = + lttng_triggers_get_at_index(triggers, i); + const struct lttng_condition *condition = + lttng_trigger_get_const_condition(trigger); + const struct lttng_action *action = + lttng_trigger_get_const_action(trigger); + const enum lttng_action_type action_type = + lttng_action_get_type(action); + enum lttng_notification_channel_status channel_status; + const char *trigger_name = NULL; + + lttng_trigger_get_name(trigger, &trigger_name); + if (strcmp(trigger_name, argv[3])) { + continue; + } + + if (!((action_type == LTTNG_ACTION_TYPE_GROUP && + action_group_contains_notify(action)) || + action_type == LTTNG_ACTION_TYPE_NOTIFY)) { + printf("The action of trigger \"%s\" is not \"notify\", skipping.\n", + trigger_name); + continue; + } + + channel_status = lttng_notification_channel_subscribe( + notification_channel, condition); + if (channel_status) { + fprintf(stderr, "Failed to subscribe to notifications of trigger \"%s\"\n", + trigger_name); + ret = -1; + goto end; + } + + printf("Subscribed to notifications of trigger \"%s\"\n", + trigger_name); + subcription_count++; + } + + if (subcription_count == 0) { + printf("No matching trigger with a notify action found.\n"); + ret = 0; + goto end; + } + + for (int i = 0; i < nb_reception; i++) { + channel_status = + lttng_notification_channel_get_next_notification( + notification_channel, + ¬ification); + switch (channel_status) { + case LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED: + printf("Dropped notification\n"); + sleep(1); + continue; + break; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED: + ret = 0; + goto end; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK: + break; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_CLOSED: + printf("Notification channel was closed by peer.\n"); + break; + default: + fprintf(stderr, "A communication error occurred on the notification channel.\n"); + ret = -1; + goto end; + } + + evaluation = lttng_notification_get_evaluation(notification); + type = lttng_evaluation_get_type(evaluation); + + if (type != LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) { + assert(0); + } + + tracepoint(performance, receive, id, i); + lttng_notification_destroy(notification); + if (ret) { + goto end; + } + } + +end: + lttng_triggers_destroy(triggers); + lttng_notification_channel_destroy(notification_channel); + return !!ret; +} diff --git a/doc/examples/trigger-on-event/performance/generate-data.sh b/doc/examples/trigger-on-event/performance/generate-data.sh new file mode 100755 index 000000000..58065a57d --- /dev/null +++ b/doc/examples/trigger-on-event/performance/generate-data.sh @@ -0,0 +1,50 @@ +#!/bin/bash -x +# +# Copyright (C) 2020 Jonathan Rajotte-Julien +# +# SPDX-License-Identifier: MIT + +cpu_stressor=$(nproc) + +while read -r load delay count; do + stress-ng --cpu-load "$load" --cpu "$cpu_stressor" & + stress_ng_id=$! + + # Let the workload stabilize + sleep 5 + + ./perform-experience.sh 0 "${load}_cpuload_${delay}ms" "$delay" "$count" + kill "$stress_ng_id" + wait +done << EOF +0 1 10000 +25 1 10000 +50 1 10000 +75 1 10000 +100 1 10000 +0 10 5000 +25 10 5000 +50 10 5000 +75 10 5000 +100 10 5000 +0 100 6000 +25 100 6000 +50 100 6000 +75 100 6000 +100 100 6000 +0 1000 6000 +25 1000 6000 +50 1000 6000 +75 1000 6000 +100 1000 6000 +0 10000 600 +25 10000 600 +50 10000 600 +75 10000 600 +100 10000 600 +0 60000 100 +25 60000 100 +50 60000 100 +75 60000 100 +100 60000 60 +EOF diff --git a/doc/examples/trigger-on-event/performance/generate-graph.sh b/doc/examples/trigger-on-event/performance/generate-graph.sh new file mode 100755 index 000000000..3140eb57c --- /dev/null +++ b/doc/examples/trigger-on-event/performance/generate-graph.sh @@ -0,0 +1,108 @@ +#!/bin/bash +# +# Copyright (C) 2020 Jonathan Rajotte-Julien +# +# SPDX-License-Identifier: MIT + +plugin_path=$(dirname "$0") + +DATA1="[\"duration\", \"D1\", \"performance:hit\", [\"source\", \"iteration\"], \"performance:receive\", [\"source\", \"iteration\"]]" + +while read -r load delay count; do + S=$(echo "scale=3; 1 / ( $delay / 1000 )" | bc | awk '{printf "%.3f", $0}' ); + hz=${S/.000/} + hz_title=${hz/./-} + + echo "Graphing Hz: ${hz} CPU: ${load}" + + PLOT1="[\"Trigger latency, Freq:${hz}Hz, CPU load: ${load}%\", \"T (ms)\", \"count\", [$DATA1]]" + + babeltrace2 --plugin-path="$plugin_path" --component sink.plot.PlotSink \ + --params="histograms=[$PLOT1]" \ + "./trace/${load}_cpuload_${delay}ms" +done << EOF +0 1 10000 +25 1 10000 +50 1 10000 +75 1 10000 +100 1 10000 +0 10 5000 +25 10 5000 +50 10 5000 +75 10 5000 +100 10 5000 +0 100 6000 +25 100 6000 +50 100 6000 +75 100 6000 +100 100 6000 +0 1000 1500 +25 1000 1500 +50 1000 1500 +75 1000 1500 +100 1000 1500 +0 10000 300 +25 10000 300 +50 10000 300 +75 10000 300 +100 10000 300 +0 60000 50 +25 60000 50 +50 60000 50 +75 60000 50 +100 60000 50 +EOF + +pdf_unite="" +csvs="" +# Generate united graph and base pdf list to unite +while read -r delay ; do + S=$(echo "scale=3; 1 / ( $delay / 1000 )" | bc | awk '{printf "%.3f", $0}' ); + hz=${S/.000/} + hz_title=${hz/./-} + local_pdf_unite="" + echo "Combining graphs for Hz: ${hz} hz_title: $hz_title" + + loggers="" + for load in 100 75 50 25 0; do + path=trigger-latency-freq-${hz_title}hz-cpu-load-${load}-.D1.csv + csvs="$csvs $path" + loggers="[\"duration-csv\", \"${load}% CPU\", \"${path}\"], $loggers" + local_pdf_unite="${local_pdf_unite} trigger-latency-freq-${hz_title}hz-cpu-load-${load}-.pdf" + done + pdf_unite="$pdf_unite $local_pdf_unite" + + PLOT1="[\"Trigger latency, Freq:${hz}Hz\", \"T (ms)\", \"count\", [$loggers]]" + babeltrace2 --plugin-path="$plugin_path" --component sink.plot.PlotSink \ + --params="histograms=[$PLOT1]" \ + "./trace/0_cpuload_${delay}ms" +done << EOF +1 +10 +100 +1000 +10000 +60000 +EOF + +# Add united graphs to the pdfunite cmd +while read -r delay ; do + S=$(echo "scale=3; 1 / ( $delay / 1000 )" | bc | awk '{printf "%.3f", $0}' ); + hz=${S/.000/} + hz_title=${hz/./-} + + pdf_unite="trigger-latency-freq-${hz_title}hz.pdf $pdf_unite" + +done << EOF +60000 +10000 +1000 +100 +10 +1 +EOF + +rm -rf $csvs + +pdfunite $pdf_unite summary.pdf + diff --git a/doc/examples/trigger-on-event/performance/perform-experience.sh b/doc/examples/trigger-on-event/performance/perform-experience.sh new file mode 100755 index 000000000..02e2cd448 --- /dev/null +++ b/doc/examples/trigger-on-event/performance/perform-experience.sh @@ -0,0 +1,61 @@ +#!/bin/bash +# +# Copyright (C) 2020 Jonathan Rajotte-Julien +# +# SPDX-License-Identifier: MIT + +EVENT_NAME_HIT=performance:hit +EVENT_NAME_RECEIVE=performance:receive +TRIGGER_NAME=performance_hit + +if [ -z "$1" ]; then + echo "missing source id int value" + exit 1 +fi + +if [ -z "$2" ]; then + echo "missing trace directory name" + exit 1 +fi + +if [ -z "$3" ]; then + echo "missing loop delay" + exit 1 +fi + +if [ -z "$4" ]; then + echo "missing loop count" + exit 1 +fi + +key_id="$1" +trace_directory="$(pwd)/trace/$2" +delay=$3 +count=$4 + +if ! lttng list > /dev/null 2>&1 ; then + echo "Could not connect to session daemon, are you sure it is running?" + exit 1 +fi + +lttng create performance --output="$trace_directory" +lttng enable-event -u $EVENT_NAME_HIT,$EVENT_NAME_RECEIVE -s performance +lttng start + +filter="source==$key_id" +lttng add-trigger --id ${TRIGGER_NAME} --condition on-event --userspace $EVENT_NAME_HIT --filter="$filter" --action notify + +./consumer "$key_id" "$count" $TRIGGER_NAME & + +# Cheap way to synchronize and ensure that the consumer is ready to consume +sleep 2 + +./producer "$key_id" "$count" "$delay" & + +wait + +lttng remove-trigger ${TRIGGER_NAME} + +lttng stop +lttng destroy performance + diff --git a/doc/examples/trigger-on-event/performance/performance.c b/doc/examples/trigger-on-event/performance/performance.c new file mode 100644 index 000000000..311b0349d --- /dev/null +++ b/doc/examples/trigger-on-event/performance/performance.c @@ -0,0 +1,10 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: MIT + * + */ + +#define TRACEPOINT_DEFINE +#define TRACEPOINT_CREATE_PROBES +#include "performance.h" diff --git a/doc/examples/trigger-on-event/performance/performance.h b/doc/examples/trigger-on-event/performance/performance.h new file mode 100644 index 000000000..79e0a36a8 --- /dev/null +++ b/doc/examples/trigger-on-event/performance/performance.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: MIT + * + */ + +#undef TRACEPOINT_PROVIDER +#define TRACEPOINT_PROVIDER performance + +#undef TRACEPOINT_INCLUDE +#define TRACEPOINT_INCLUDE "./performance.h" + +#if !defined(_TRACEPOINT_PERFORMANCE_H) || defined(TRACEPOINT_HEADER_MULTI_READ) +#define _TRACEPOINT_PERFORMANCE_H + +#include + +TRACEPOINT_EVENT(performance, hit, + TP_ARGS(int, source, + int, iteration), + TP_FIELDS( + ctf_integer(uint64_t, source, source) + ctf_integer(uint64_t, iteration, iteration) + ) +) + +TRACEPOINT_EVENT(performance, receive, + TP_ARGS(int, source, + int, iteration), + TP_FIELDS( + ctf_integer(uint64_t, source, source) + ctf_integer(uint64_t, iteration, iteration) + ) +) + +#endif /* _TRACEPOINT_PERFORMANCE_H */ + +#include diff --git a/doc/examples/trigger-on-event/performance/producer.c b/doc/examples/trigger-on-event/performance/producer.c new file mode 100644 index 000000000..3aefaae20 --- /dev/null +++ b/doc/examples/trigger-on-event/performance/producer.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * Copyright (C) 2020 Jonathan Rajotte-Julien + * + * SPDX-License-Identifier: MIT + * + */ +#include "performance.h" + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + int ret = 0; + int nb_hit; + int id; + long long sleep_ms; + struct timespec sleep_time; + struct timespec sleep_rm; + + if (argc != 4) { + fprintf(stderr, "Missing unique_id\n"); + fprintf(stderr, "Missing number of event \n"); + fprintf(stderr, "Missing delay between event in ms \n"); + fprintf(stderr, "Usage: producer id bn_event delay_ms\n"); + ret = 1; + goto end; + } + + id = atoi(argv[1]); + nb_hit = atoi(argv[2]); + sleep_ms = atoll(argv[3]); + + sleep_time.tv_sec = sleep_ms / 1000; + sleep_time.tv_nsec = (sleep_ms % 1000) * 1000000; + + for (int i = 0; i < nb_hit; i++) { + tracepoint(performance, hit, 0, i); + nanosleep(&sleep_time, &sleep_rm); + } + +end: + return !!ret; +} diff --git a/doc/examples/trigger-on-event/tracepoint-trigger-example.c b/doc/examples/trigger-on-event/tracepoint-trigger-example.c new file mode 100644 index 000000000..3f781e3b8 --- /dev/null +++ b/doc/examples/trigger-on-event/tracepoint-trigger-example.c @@ -0,0 +1,10 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: MIT + * + */ + +#define TRACEPOINT_DEFINE +#define TRACEPOINT_CREATE_PROBES +#include "tracepoint-trigger-example.h" diff --git a/doc/examples/trigger-on-event/tracepoint-trigger-example.h b/doc/examples/trigger-on-event/tracepoint-trigger-example.h new file mode 100644 index 000000000..e82e3c704 --- /dev/null +++ b/doc/examples/trigger-on-event/tracepoint-trigger-example.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: MIT + * + */ + +#undef TRACEPOINT_PROVIDER +#define TRACEPOINT_PROVIDER trigger_example + +#undef TRACEPOINT_INCLUDE +#define TRACEPOINT_INCLUDE "./tracepoint-trigger-example.h" + +#if !defined(_TRACEPOINT_TRIGGER_EXAMPLE_H) || defined(TRACEPOINT_HEADER_MULTI_READ) +#define _TRACEPOINT_TRIGGER_EXAMPLE_H + +#include + +TRACEPOINT_EVENT(trigger_example, my_event, + 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 80bedbadf..5ae6ffbe3 100644 --- a/doc/man/Makefile.am +++ b/doc/man/Makefile.am @@ -36,7 +36,11 @@ MAN1_NAMES = \ lttng-rotate \ lttng-enable-rotation \ lttng-disable-rotation \ - lttng-clear + lttng-clear \ + lttng-add-trigger \ + lttng-remove-trigger \ + lttng-list-triggers + MAN3_NAMES = MAN8_NAMES = lttng-sessiond lttng-relayd MAN1_NO_ASCIIDOC_NAMES = diff --git a/doc/man/lttng-add-trigger.1.txt b/doc/man/lttng-add-trigger.1.txt new file mode 100644 index 000000000..5e2e90e43 --- /dev/null +++ b/doc/man/lttng-add-trigger.1.txt @@ -0,0 +1,207 @@ +lttng-add-trigger(1) +===================== +:revdate: 27 May 2020 + + +NAME +---- +lttng-add-trigger - Create LTTng triggers + + +SYNOPSIS +-------- + +[verse] +*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *add-trigger* [--id ID] + [--fire-every N] [--fire-once-after N] + --condition CONDITION-NAME CONDITION-ARGS + --action ACTION-NAME ACTION-ARGS + [--action ACTION-NAME ACTION-ARGS]... + + +DESCRIPTION +----------- + +The `lttng add-trigger` command is used to create triggers. A +trigger is an association between a *condition* and one or more +*actions*. When the condition associated to a trigger is met, the +actions associated to that trigger are executed. The tracing does not +have to be active for the conditions to be met, and triggers are +independent from tracing sessions. + +By default, a trigger fires every time its condition is met. The +'--fire-every' and '--fire-once-after' options can be used to change +this mode. + +The syntax by which conditions and actions are specified is described +below. + +[[conditions]] +Conditions +~~~~~~~~~~ + +Conditions are specified with the `--condition` option, followed by a +condition name, and possibly some more arguments, depending on the +specific condition. There must be exactly one condition given in the +`lttng add-trigger` invocation. + +The available conditions are: + +Event rule: `on-event [event rule arguments]`:: + This type of condition is met when the tracer encounters an event + matching the given even rule. The arguments describing the event + rule are the same as those describing the event rules of the + man:lttng-enable-event(1) command, with these exceptions: + + - It is not possible to use filter operands that use values from + the context. + ++ +Fields to capture can be specified with the option:--capture option, followed by +a capture expression. Zero or more captures can be configured. See the +<> section below for more information. + +[[actions]] +Actions +~~~~~~~ + +Actions are specified with the `--action` option, followed by an action +name, and possibly some more arguments, depending on the specific +action. There must be at least one action given in the +`lttng add-trigger` invocation. + +The available actions are: + +Notify: *notify*:: + This action causes the LTTng session daemon to send a notification, + through its notification mechanism (see man:lttng-sessiond(8)). + Some details about the condition evaluation are sent along with the + notification. + +Start session: *start-session* session-name:: + This action causes the LTTng session daemon to start tracing for the + session with the given name. If no session with the given name exist + at the time the condition is met, nothing is done. + +Stop session: *stop-session* session-name:: + This action causes the LTTng session daemon to stop tracing for the + session with the given name. If no session with the given name exist + at the time the condition is met, nothing is done. + +Rotate session: *rotate-session* session-name:: + This action causes the LTTng session daemon to rotate the session + with the given name. See man:lttng-rotate(1) for more information + about the session rotation concept. If no session with the given + name exist at the time the condition is met, nothing is done. + +Snapshot session: *snapshot-session* session-name:: + This action causes the LTTng session daemon to take a snapshot of the + session with the given name. See man:lttng-snapshot(1) for more + information about the session snapshot concept. If no session with + the given name exist at the time the condition is met, nothing is + done. + + +[[capture-expr]] +Capture expression +~~~~~~~~~~~~~~~~~~ + +A capture expression can be specified with the option:--capture option when +creating a new on-event condition. If the capture expression corresponds with an +event's field when tracing, the runtime dynamic value corresponding to the +capture expression is captured. + +NOTE: Make sure to **single-quote** the capture expression when running +the command from a shell, as capture expressions typically include +characters having a special meaning for most shells. + +* Supported field types: + - integer, + - unsigned integer, + - floating point value, + - fixed-size array of integers, + - variable-size array of integers (sequence), + - enumeration, + - text string, + - element of any allowing previous type. + +* The dynamic value of an event field is captured by using its name as a C + identifier. ++ +The square bracket notation is available, like in the C +language, to access array/sequence field. +Only a constant, positive integer number can be used within square +brackets. If the index is out of bounds, the capture expression +evaluates to `unavailable`. ++ +An enumeration field's value is an integer. ++ +When the capture's field does not exist, the capture expression +evaluates to `unavailable`. ++ +Examples: `my_field`, `target_cpu`, `seq[7]` + +* The dynamic value of a statically-known context field is captured by + prefixing its name with `$ctx.`. See man:lttng-add-context(1) to get a list of + available contexts. ++ +When the expression's statically-known context field does not exist, +the capture expression evaluates to `unavailable`. ++ +Examples: `$ctx.prio`, `$ctx.preemptible`, +`$ctx.perf:cpu:stalled-cycles-frontend`. ++ +NOTE: The statically-known context field does NOT need to be added using the +man:lttng-add-context(1) command. The statically-known context fields are +always available in the context of triggers. + +* The dynamic value of an application-specific context field is captured by + prefixing its name with `$app.` (follows the format used to add such a context + field with the man:lttng-add-context(1) command). ++ +When the expression's application-specific context field does not exist, +the capture expression evaluates to `unavailable`. ++ +Example: `$app.server:cur_user`. ++ +NOTE: The application-specific context field does NOT need to be added using the +man:lttng-add-context(1) command. The application-specific context fields fields +are always available in the context of triggers. + + +OPTIONS +------- + +option:--condition:: + Define the condition for the trigger. See the + <> section for more details. + +option:--action:: + Define an action for the trigger. See the <> + section for more details. + +option:--id='ID':: + Set the id of the trigger to 'ID'. If omitted, an id will + automatically be assigned to the trigger by the session daemon. ++ +If a trigger with the specified 'ID' already exists, the trigger +creation will fail. + +option:--fire-every 'N':: + Execute the trigger's actions every 'N' times the condition is met. + +option:--fire-once-after 'N':: + Execute the trigger's actions once after 'N' times the condition is + met, then never after that. + +include::common-cmd-help-options.txt[] + + +include::common-cmd-footer.txt[] + + +SEE ALSO +-------- +man:lttng-list-triggers(1), +man:lttng-remove-trigger(1), +man:lttng(1) diff --git a/doc/man/lttng-list-triggers.1.txt b/doc/man/lttng-list-triggers.1.txt new file mode 100644 index 000000000..2ab3085cd --- /dev/null +++ b/doc/man/lttng-list-triggers.1.txt @@ -0,0 +1,37 @@ +lttng-list-triggers(1) +====================== +:revdate: 20 January 2020 + + +NAME +---- +lttng-list-triggers - List LTTng triggers + + +SYNOPSIS +-------- + +[verse] +*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *list-triggers* + + +DESCRIPTION +----------- + +The `lttng list-triggers` command list the existing triggers. + + +OPTIONS +------- + +include::common-cmd-help-options.txt[] + + +include::common-cmd-footer.txt[] + + +SEE ALSO +-------- +man:lttng-add-trigger(1), +man:lttng-remove-trigger(1), +man:lttng(1) diff --git a/doc/man/lttng-remove-trigger.1.txt b/doc/man/lttng-remove-trigger.1.txt new file mode 100644 index 000000000..645ef152f --- /dev/null +++ b/doc/man/lttng-remove-trigger.1.txt @@ -0,0 +1,38 @@ +lttng-remove-trigger(1) +======================== +:revdate: 20 January 2020 + + +NAME +---- +lttng-remove-trigger - Remove LTTng triggers + + +SYNOPSIS +-------- + +[verse] +*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *remove-trigger* 'ID' + + +DESCRIPTION +----------- + +The `lttng remove-trigger` command removes the trigger with the given +'ID'. + + +OPTIONS +------- + +include::common-cmd-help-options.txt[] + + +include::common-cmd-footer.txt[] + + +SEE ALSO +-------- +man:lttng-add-trigger(1), +man:lttng-list-trigger(1), +man:lttng(1) diff --git a/include/Makefile.am b/include/Makefile.am index 1cfcd8320..9a7da5f22 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -103,6 +103,8 @@ lttnginclude_HEADERS = \ lttng/channel.h \ lttng/domain.h \ lttng/event.h \ + lttng/event-expr.h \ + lttng/event-field-value.h \ lttng/handle.h \ lttng/session.h \ lttng/lttng-error.h \ @@ -132,6 +134,7 @@ lttngactioninclude_HEADERS= \ lttngconditioninclude_HEADERS= \ lttng/condition/condition.h \ lttng/condition/buffer-usage.h \ + lttng/condition/event-rule.h \ lttng/condition/session-consumed-size.h \ lttng/condition/session-rotation.h \ lttng/condition/evaluation.h @@ -164,6 +167,7 @@ noinst_HEADERS = \ lttng/action/stop-session-internal.h \ lttng/condition/condition-internal.h \ lttng/condition/buffer-usage-internal.h \ + lttng/condition/event-rule-internal.h \ lttng/condition/session-consumed-size-internal.h \ lttng/condition/evaluation-internal.h \ lttng/condition/session-rotation-internal.h \ @@ -172,7 +176,10 @@ noinst_HEADERS = \ lttng/endpoint-internal.h \ lttng/notification/channel-internal.h \ lttng/channel-internal.h \ + lttng/domain-internal.h \ lttng/event-internal.h \ + lttng/event-expr-internal.h \ + lttng/event-field-value-internal.h \ lttng/rotate-internal.h \ lttng/ref-internal.h \ lttng/location-internal.h \ diff --git a/include/lttng/action/action-internal.h b/include/lttng/action/action-internal.h index 20faea697..9e43a34d0 100644 --- a/include/lttng/action/action-internal.h +++ b/include/lttng/action/action-internal.h @@ -61,10 +61,6 @@ LTTNG_HIDDEN ssize_t lttng_action_create_from_payload(struct lttng_payload_view *view, struct lttng_action **action); -LTTNG_HIDDEN -enum lttng_action_type lttng_action_get_type_const( - const struct lttng_action *action); - LTTNG_HIDDEN bool lttng_action_is_equal(const struct lttng_action *a, const struct lttng_action *b); diff --git a/include/lttng/action/action.h b/include/lttng/action/action.h index 1309f85bd..be7e397d0 100644 --- a/include/lttng/action/action.h +++ b/include/lttng/action/action.h @@ -38,7 +38,7 @@ enum lttng_action_status { * Returns the type of an action on success, LTTNG_ACTION_TYPE_UNKNOWN on error. */ extern enum lttng_action_type lttng_action_get_type( - struct lttng_action *action); + const struct lttng_action *action); /* * Destroy (frees) an action object. diff --git a/include/lttng/condition/condition-internal.h b/include/lttng/condition/condition-internal.h index 405199b42..92bb4de71 100644 --- a/include/lttng/condition/condition-internal.h +++ b/include/lttng/condition/condition-internal.h @@ -71,4 +71,7 @@ LTTNG_HIDDEN bool lttng_condition_is_equal(const struct lttng_condition *a, const struct lttng_condition *b); +LTTNG_HIDDEN +const char *lttng_condition_type_str(enum lttng_condition_type type); + #endif /* LTTNG_CONDITION_INTERNAL_H */ diff --git a/include/lttng/condition/condition.h b/include/lttng/condition/condition.h index 877ccd1a3..e1b732398 100644 --- a/include/lttng/condition/condition.h +++ b/include/lttng/condition/condition.h @@ -21,6 +21,7 @@ enum lttng_condition_type { LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW = 102, LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING = 103, LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED = 104, + LTTNG_CONDITION_TYPE_EVENT_RULE_HIT = 105, }; enum lttng_condition_status { @@ -29,6 +30,7 @@ enum lttng_condition_status { LTTNG_CONDITION_STATUS_UNKNOWN = -2, LTTNG_CONDITION_STATUS_INVALID = -3, LTTNG_CONDITION_STATUS_UNSET = -4, + LTTNG_CONDITION_STATUS_UNSUPPORTED = -4, }; /* 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 new file mode 100644 index 000000000..7f71a7bee --- /dev/null +++ b/include/lttng/condition/event-rule-internal.h @@ -0,0 +1,107 @@ +/* + * 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 +#include + +struct lttng_capture_descriptor { + /* The index at which the capture for this descriptor in the received + * payload from the tracer. This is populated on sessiond side. + * -1 is uninitialized. + * This is necessary since a single trigger can have multiple notify + * action, only an ordered set of capture desciptor is passed to the tracer. + */ + int32_t capture_index; + struct lttng_event_expr *event_expression; +}; + +struct lttng_condition_event_rule { + struct lttng_condition parent; + struct lttng_event_rule *rule; + + /* Array of `struct lttng_capture_descriptor *` */ + struct lttng_dynamic_pointer_array capture_descriptors; +}; + +struct lttng_evaluation_event_rule { + struct lttng_evaluation parent; + char *name; + + /* MessagePack-encoded captured event field values */ + struct lttng_dynamic_buffer capture_payload; + + /* + * The content of this array event field value is the decoded + * version of `capture_payload` above. + * + * This is a cache: it's not serialized/deserialized in + * communications from/to the library and the session daemon. + */ + struct lttng_event_field_value *captured_values; +}; + +struct lttng_evaluation_event_rule_comm { + uint32_t trigger_name_length; + /* Trigger name */ + char payload[]; +} LTTNG_PACKED; + +struct lttng_condition_event_rule_capture_bytecode_element +{ + struct lttng_event_expr *expression; + struct lttng_bytecode *bytecode; +}; + +LTTNG_HIDDEN +ssize_t lttng_condition_event_rule_create_from_payload( + struct lttng_payload_view *view, + struct lttng_condition **condition); + +LTTNG_HIDDEN +enum lttng_condition_status +lttng_condition_event_rule_get_rule_mutable( + const struct lttng_condition *condition, + struct lttng_event_rule **rule); + +LTTNG_HIDDEN +struct lttng_evaluation *lttng_evaluation_event_rule_create( + const struct lttng_condition_event_rule *condition, + const char* trigger_name, + const char *capture_payload, size_t capture_payload_size, + bool decode_capture_payload); + +LTTNG_HIDDEN +ssize_t lttng_evaluation_event_rule_create_from_payload( + const struct lttng_condition_event_rule *condition, + struct lttng_payload_view *view, + struct lttng_evaluation **_evaluation); + +/* + * The returned `lttng_dynamic_pointer_array` contains a index ordered set of + * `lttng_condition_event_rule_capture_bytecode_element`. + * This ensure that minimal work will be done by the tracer for cases where + * multiple identical capture expression are present. + */ +LTTNG_HIDDEN +enum lttng_error_code +lttng_condition_event_rule_generate_capture_descriptor_bytecode_set( + struct lttng_condition *condition, + struct lttng_dynamic_pointer_array *bytecode_set); + +LTTNG_HIDDEN +struct lttng_capture_descriptor * +lttng_condition_event_rule_get_internal_capture_descriptor_at_index( + const struct lttng_condition *condition, unsigned int index); + +#endif /* LTTNG_CONDITION_event_rule_INTERNAL_H */ diff --git a/include/lttng/condition/event-rule.h b/include/lttng/condition/event-rule.h new file mode 100644 index 000000000..8047c6b1d --- /dev/null +++ b/include/lttng/condition/event-rule.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_CONDITION_EVENT_RULE_H +#define LTTNG_CONDITION_EVENT_RULE_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct lttng_event_expr; +struct lttng_event_field_value; + +/** + * Event rule conditions allows an action to be taken whenever an event matching + * the event rule is hit by the tracers. + * + * An event rule condition can also specify payload to be captured at runtime. + * This is done via the capture descriptor. + * + * Note: the dynamic runtime capture of payload is only available for the + * trigger notification subsystem. + */ + +/* + * Create a newly allocated event rule condition. + * + * Returns a new condition on success, NULL on failure. This condition must be + * destroyed using lttng_condition_destroy(). + */ +extern struct lttng_condition *lttng_condition_event_rule_create( + struct lttng_event_rule *rule); + +/* + * Get the rule property of a event rule condition. + * + * The caller does not assume the ownership of the returned rule. The + * rule shall only be used for the duration of the condition's + * lifetime. + * + * Returns LTTNG_CONDITION_STATUS_OK and a pointer to the condition's rule + * on success, LTTNG_CONDITION_STATUS_INVALID if an invalid + * parameter is passed. */ +extern enum lttng_condition_status lttng_condition_event_rule_get_rule( + const struct lttng_condition *condition, const struct lttng_event_rule **rule); + +/** + * lttng_evaluation_event_rule_hit are specialised lttng_evaluations which + * allow users to query a number of properties resulting from the evaluation + * of a condition which evaluated to true. + * + * The evaluation of a event rule hit yields two different results: + * TEMPORARY - The name of the triggers associated with the condition. + * TODO - The captured event payload if any + */ + +/* + * Get the trigger name property of a event rule hit evaluation. + * + * Returns LTTNG_EVALUATION_STATUS_OK on success and a trigger name + * or LTTNG_EVALUATION_STATUS_INVALID if + * an invalid parameter is passed. + */ +extern enum lttng_evaluation_status +lttng_evaluation_event_rule_get_trigger_name( + const struct lttng_evaluation *evaluation, + const char **name); + +/* + * Sets `*field_val` to the array event field value of the event rule + * condition evaluation `evaluation` which contains its captured values. + * + * Returns: + * + * `LTTNG_EVALUATION_STATUS_OK`: + * Success. + * + * `*field_val` is an array event field value with a length of at + * least one. + * + * `LTTNG_EVALUATION_STATUS_INVALID`: + * * `evaluation` is `NULL`. + * * The type of the condition of `evaluation` is not + * `LTTNG_CONDITION_TYPE_EVENT_RULE_HIT`. + * * The condition of `evaluation` has no capture descriptors. + * * `field_val` is `NULL`. + */ +extern enum lttng_evaluation_status +lttng_evaluation_get_captured_values( + const struct lttng_evaluation *evaluation, + const struct lttng_event_field_value **field_val); + +/* + * Appends (transfering the ownership) the capture descriptor `expr` to + * the event rule condition `condition`. + * + * Returns: + * + * `LTTNG_CONDITION_STATUS_OK`: + * Success. + * + * `LTTNG_CONDITION_STATUS_ERROR`: + * Memory error. + * + * `LTTNG_CONDITION_STATUS_INVALID`: + * * `condition` is `NULL`. + * * The type of `condition` is not + * `LTTNG_CONDITION_TYPE_EVENT_RULE_HIT`. + * * `expr` is `NULL`. + * * `expr` is not a locator expression, that is, its type is not + * one of: + * + * * `LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD` + * * `LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD` + * * `LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD` + * * `LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT` + * `LTTNG_CONDITION_STATUS_UNSUPPORTED`: + * * The associated event-rule does not support runtime capture. + */ +extern enum lttng_condition_status +lttng_condition_event_rule_append_capture_descriptor( + struct lttng_condition *condition, + struct lttng_event_expr *expr); + +/* + * Sets `*count` to the number of capture descriptors in the event rule + * condition `condition`. + * + * Returns: + * + * `LTTNG_CONDITION_STATUS_OK`: + * Success. + * + * `LTTNG_CONDITION_STATUS_INVALID`: + * * `condition` is `NULL`. + * * The type of `condition` is not + * `LTTNG_CONDITION_TYPE_EVENT_RULE_HIT`. + * * `count` is `NULL`. + */ +extern enum lttng_condition_status +lttng_condition_event_rule_get_capture_descriptor_count( + const struct lttng_condition *condition, unsigned int *count); + +/* + * Returns the capture descriptor (borrowed) of the event rule condition + * `condition` at the index `index`, or `NULL` if: + * + * * `condition` is `NULL`. + * * The type of `condition` is not + * `LTTNG_CONDITION_TYPE_EVENT_RULE_HIT`. + * * `index` is greater than or equal to the number of capture + * descriptors in `condition` (as returned by + * lttng_condition_event_rule_get_capture_descriptor_count()). + */ +extern const struct lttng_event_expr * +lttng_condition_event_rule_get_capture_descriptor_at_index( + const struct lttng_condition *condition, unsigned int index); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_CONDITION_EVENT_RULE_H */ diff --git a/include/lttng/domain-internal.h b/include/lttng/domain-internal.h new file mode 100644 index 000000000..877fa2892 --- /dev/null +++ b/include/lttng/domain-internal.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2020 Simon Marchi + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_DOMAIN_INTERNAL_H +#define LTTNG_DOMAIN_INTERNAL_H + +#include "lttng/domain.h" +#include "common/macros.h" + +#ifdef __cplusplus +extern "C" { +#endif + +LTTNG_HIDDEN +const char *lttng_domain_type_str(enum lttng_domain_type domain_type); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_DOMAIN_INTERNAL_H */ diff --git a/include/lttng/event-expr-internal.h b/include/lttng/event-expr-internal.h new file mode 100644 index 000000000..8c877818d --- /dev/null +++ b/include/lttng/event-expr-internal.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2020 Philippe Proulx + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_EXPR_INTERNAL_H +#define LTTNG_EVENT_EXPR_INTERNAL_H + +#include +#include +#include +#include + +struct lttng_event_expr { + struct urcu_ref ref; + enum lttng_event_expr_type type; +}; + +/* + * `LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD` and + * `LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD`. + */ +struct lttng_event_expr_field { + struct lttng_event_expr parent; + char *name; +}; + +/* `LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD` */ +struct lttng_event_expr_app_specific_context_field { + struct lttng_event_expr parent; + char *provider_name; + char *type_name; +}; + +/* `LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT` */ +struct lttng_event_expr_array_field_element { + struct lttng_event_expr parent; + + /* Owned by this */ + struct lttng_event_expr *array_field_expr; + + unsigned int index; +}; + +/* + * Returns whether or not `expr` is an l-value (locator value). + */ +static inline +bool lttng_event_expr_is_lvalue(const struct lttng_event_expr *expr) +{ + assert(expr); + return expr->type == LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD || + expr->type == LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD || + expr->type == LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD || + expr->type == LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT; +} + +LTTNG_HIDDEN +void lttng_event_expr_get(struct lttng_event_expr *expr); + +LTTNG_HIDDEN +void lttng_event_expr_put(struct lttng_event_expr *expr); + +#endif /* LTTNG_EVENT_EXPR_INTERNAL_H */ diff --git a/include/lttng/event-expr.h b/include/lttng/event-expr.h new file mode 100644 index 000000000..911648779 --- /dev/null +++ b/include/lttng/event-expr.h @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2020 Philippe Proulx + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_EXPR_H +#define LTTNG_EVENT_EXPR_H + +#include + +struct lttng_event_expr; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Types of an event expression. + */ +enum lttng_event_expr_type { + /* + * Returned by lttng_event_expr_get_type() with an invalid + * parameter. + */ + LTTNG_EVENT_EXPR_TYPE_INVALID = -1, + + /* + * The named payload field of an event. + * + * Command-line expression example: + * + * next_prio + */ + LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD = 0, + + /* + * The named per-channel context field of an event. + * + * Command-line expression example: + * + * $ctx.vpid + */ + LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD = 1, + + /* + * The named application-specific context field of an event. + * + * Command-line expression example: + * + * $app.iga:active-clients + */ + LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD = 2, + + /* + * The element of an array field. + * + * Command-line expression example: + * + * my_field[4] + * $ctx.some_context[5][1] + */ + LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT = 3, +}; + +/* + * Event expression API status codes. + */ +enum lttng_event_expr_status { + /* + * Invalid parameter. + */ + LTTNG_EVENT_EXPR_STATUS_INVALID = -1, + + /* + * Success. + */ + LTTNG_EVENT_EXPR_STATUS_OK = 0, +}; + +/* + * Returns the type of the event expression `expr`, or + * `LTTNG_EVENT_EXPR_TYPE_INVALID` if `expr` is `NULL`. + */ +extern enum lttng_event_expr_type lttng_event_expr_get_type( + const struct lttng_event_expr *expr); + +/* + * Creates an event payload field expression for the payload field named + * `field_name`. + * + * Returns `NULL` if: + * + * * There's a memory error. + * * `field_name` is `NULL`. + */ +extern struct lttng_event_expr *lttng_event_expr_event_payload_field_create( + const char *field_name); + +/* + * Returns the field name of the event payload field expression `expr`, + * or `NULL` if: + * + * * `expr` is `NULL`. + * * The type of `expr` is not + * `LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD`. + */ +extern const char *lttng_event_expr_event_payload_field_get_name( + const struct lttng_event_expr *expr); + +/* + * Creates a per-channel context field expression for the per-channel + * context field named `field_name`. + * + * Returns `NULL` if: + * + * * There's a memory error. + * * `field_name` is `NULL`. + */ +extern struct lttng_event_expr * +lttng_event_expr_channel_context_field_create(const char *field_name); + +/* + * Returns the field name of the per-channel context field + * expression `expr`, or `NULL` if: + * + * `expr` is `NULL`. + * * The type of `expr` is not + * `LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD`. + */ +extern const char *lttng_event_expr_channel_context_field_get_name( + const struct lttng_event_expr *expr); + +/* + * Creates an application-specific context field expression for the + * application-specific context field provided by the provider named + * `provider_name` and having the type named `type_name`. + * + * Returns `NULL` if: + * + * * There's a memory error. + * * `provider_name` is `NULL`. + * * `type_name` is `NULL`. + */ +extern struct lttng_event_expr * +lttng_event_expr_app_specific_context_field_create( + const char *provider_name, const char *type_name); + +/* + * Returns the provider name of the application-specific context field + * expression `expr`, or `NULL` if: + * + * * `expr` is `NULL`. + * * The type of `expr` is not + * `LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD`. + */ +extern const char * +lttng_event_expr_app_specific_context_field_get_provider_name( + const struct lttng_event_expr *expr); + +/* + * Returns the type name of the application-specific context field + * expression `expr`, or `NULL` if: + * + * * `expr` is `NULL`. + * * The type of `expr` is not + * `LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD`. + */ +extern const char * +lttng_event_expr_app_specific_context_field_get_type_name( + const struct lttng_event_expr *expr); + +/* + * Creates an array field element expression for the parent array field + * `array_field_expr` (transfering the ownership) and the index `index`. + * + * Returns `NULL` if: + * + * * There's a memory error. + * * `array_field_expr` is `NULL`. + * * `array_field_expr` is not a locator expression, that is, its type + * is not one of: + * + * * `LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD` + * * `LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD` + * * `LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD` + * * `LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT` + */ +extern struct lttng_event_expr *lttng_event_expr_array_field_element_create( + struct lttng_event_expr *array_field_expr, + unsigned int index); + +/* + * Returns the parent array field expression of the array field element + * expression `expr`, or `NULL` if: + * + * * `expr` is `NULL`. + * * The type of `expr` is not + * `LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT`. + */ +extern const struct lttng_event_expr * +lttng_event_expr_array_field_element_get_parent_expr( + const struct lttng_event_expr *expr); + +/* + * Sets `*index` to the index of the array field element expression + * `expr`. + * + * Returns: + * + * `LTTNG_EVENT_EXPR_STATUS_OK`: + * Success. + * + * `LTTNG_EVENT_EXPR_STATUS_INVALID`: + * * `expr` is `NULL`. + * * The type of `expr` is not + * `LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT`. + * * `index` is `NULL`. + */ +extern enum lttng_event_expr_status +lttng_event_expr_array_field_element_get_index( + const struct lttng_event_expr *expr, unsigned int *index); + +/* + * Returns whether or not the event expressions `expr_a` and `expr_b` + * are equal. + * + * `expr_a` and `expr_b` can be `NULL`. + */ +extern bool lttng_event_expr_is_equal(const struct lttng_event_expr *expr_a, + const struct lttng_event_expr *expr_b); + +/* + * Destroys the event expression `expr` if not `NULL`. + */ +extern void lttng_event_expr_destroy(struct lttng_event_expr *expr); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_EVENT_EXPR_H */ diff --git a/include/lttng/event-field-value-internal.h b/include/lttng/event-field-value-internal.h new file mode 100644 index 000000000..3ef6d2ccf --- /dev/null +++ b/include/lttng/event-field-value-internal.h @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2020 Philippe Proulx + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_FIELD_VALUE_INTERNAL_H +#define LTTNG_EVENT_FIELD_VALUE_INTERNAL_H + +#include +#include +#include +#include + +struct lttng_event_field_value { + enum lttng_event_field_value_type type; +}; + +/* + * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT`. + */ +struct lttng_event_field_value_uint { + struct lttng_event_field_value parent; + uint64_t val; +}; + +/* + * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT`. + */ +struct lttng_event_field_value_int { + struct lttng_event_field_value parent; + int64_t val; +}; + +/* + * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM` and + * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM` (base). + */ +struct lttng_event_field_value_enum { + struct lttng_event_field_value parent; + + /* + * Array of `char *` (owned by this). + */ + struct lttng_dynamic_pointer_array labels; +}; + +/* + * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM`. + */ +struct lttng_event_field_value_enum_uint { + struct lttng_event_field_value_enum parent; + uint64_t val; +}; + +/* + * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM`. + */ +struct lttng_event_field_value_enum_int { + struct lttng_event_field_value_enum parent; + int64_t val; +}; + +/* `LTTNG_EVENT_FIELD_VALUE_TYPE_REAL` */ +struct lttng_event_field_value_real { + struct lttng_event_field_value parent; + double val; +}; + +/* `LTTNG_EVENT_FIELD_VALUE_TYPE_STRING` */ +struct lttng_event_field_value_string { + struct lttng_event_field_value parent; + + /* Owned by this */ + char *val; +}; + +/* `LTTNG_EVENT_FIELD_VALUE_TYPE_STRING` */ +struct lttng_event_field_value_array { + struct lttng_event_field_value parent; + + /* + * Array of `struct lttng_event_field_value *` (owned by this). + * + * A `NULL` element means it's unavailable + * (`LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE` status). + */ + struct lttng_dynamic_pointer_array elems; +}; + +/* + * NOTE JORAJ: This was previously public. The only slight problem with that is + * that as of today (2020-05-26) there is no plan/sessiond for the tracer to + * actually provide this information. This was already known in [1]. For now + * this code have no value since it only confuse the end user. At upstreaming + * time we will need to decide if we want to remove all code pertaining to enum + * label, at least on the lttng-tools API. + * + * [1] https://support.efficios.com/issues/792 + * + * Sets `*count` to the number of labels of the enumeration event field + * value `field_val`. + * + * Returns: + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`: + * Success. + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`: + * * `field_val` is `NULL`. + * * The type of `field_val` is not + * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM` or + * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM`. + * * `count` is `NULL`. + */ +LTTNG_HIDDEN +enum lttng_event_field_value_status +lttng_event_field_value_enum_get_label_count( + const struct lttng_event_field_value *field_val, + unsigned int *count); + +/* + * NOTE JORAJ: see NOTE JORAJ off lttng_event_field_value_enum_get_label_count + * + * Returns the label at index `index` of the enumeration event field + * value `field_val`, or `NULL` if: + * + * * `field_val` is `NULL`. + * * The type of `field_val` is not + * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM` or + * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM`. + * * `index` is greater than or equal to the label count of `field_val`, + * as returned by lttng_event_field_value_enum_get_label_count(). + */ +LTTNG_HIDDEN +const char *lttng_event_field_value_enum_get_label_at_index( + const struct lttng_event_field_value *field_val, + unsigned int index); + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_uint_create( + uint64_t val); + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_int_create( + int64_t val); + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_enum_uint_create( + uint64_t val); + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_enum_int_create( + int64_t val); + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_real_create(double val); + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_string_create( + const char *val); + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_string_create_with_size( + const char *val, size_t size); + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_array_create(void); + +LTTNG_HIDDEN +int lttng_event_field_value_enum_append_label( + struct lttng_event_field_value *field_val, const char *label); + +LTTNG_HIDDEN +int lttng_event_field_value_enum_append_label_with_size( + struct lttng_event_field_value *field_val, const char *label, + size_t size); + +LTTNG_HIDDEN +int lttng_event_field_value_array_append( + struct lttng_event_field_value *array_field_val, + struct lttng_event_field_value *field_val); + +LTTNG_HIDDEN +int lttng_event_field_value_array_append_unavailable( + struct lttng_event_field_value *array_field_val); + +LTTNG_HIDDEN +void lttng_event_field_value_destroy(struct lttng_event_field_value *field_val); + +#endif /* LTTNG_EVENT_FIELD_VALUE_INTERNAL_H */ diff --git a/include/lttng/event-field-value.h b/include/lttng/event-field-value.h new file mode 100644 index 000000000..db4a4fb3b --- /dev/null +++ b/include/lttng/event-field-value.h @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2020 Philippe Proulx + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_FIELD_VALUE_H +#define LTTNG_EVENT_FIELD_VALUE_H + +#include + +struct lttng_event_field_value; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Types of a event field value expression. + */ +enum lttng_event_field_value_type { + /* + * Unknown. + */ + LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN = -2, + + /* + * Returned by lttng_event_field_value_get_type() with an + * invalid parameter. + */ + LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID = -1, + + /* + * Unsigned integer event field value. + */ + LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT = 0, + + /* + * Signed integer event field value. + */ + LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT = 1, + + /* + * Unsigned enumeration event field value. + * + * This type conceptually inherits + * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT`. + */ + LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM = 2, + + /* + * Signed enumeration event field value. + * + * This type conceptually inherits + * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT`. + */ + LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM = 3, + + /* + * Real event field value. + */ + LTTNG_EVENT_FIELD_VALUE_TYPE_REAL = 4, + + /* + * String event field value. + */ + LTTNG_EVENT_FIELD_VALUE_TYPE_STRING = 5, + + /* + * Array event field value. + */ + LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY = 6, +}; + +/* + * Event field value API status codes. + */ +enum lttng_event_field_value_status { + /* + * Event field value is not available. + */ + LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE = -2, + + /* + * Invalid parameter. + */ + LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID = -1, + + /* + * Success. + */ + LTTNG_EVENT_FIELD_VALUE_STATUS_OK = 0, +}; + +/* + * Returns the type of the event field value `field_val`, or: + * + * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN`: + * The type of `field_val` is unknown as of this version of the + * LTTng control library. + * + * `LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID`: + * `field_val` is `NULL`. + */ +extern enum lttng_event_field_value_type lttng_event_field_value_get_type( + const struct lttng_event_field_value *field_val); + +/* + * Sets `*val` to the raw value of the unsigned integer/enumeration + * event field value `field_val`. + * + * Returns: + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`: + * Success. + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`: + * * `field_val` is `NULL`. + * * The type of `field_val` is not + * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT` or + * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM`. + * * `val` is `NULL`. + */ +extern enum lttng_event_field_value_status +lttng_event_field_value_unsigned_int_get_value( + const struct lttng_event_field_value *field_val, uint64_t *val); + +/* + * Sets `*val` to the raw value of the signed integer/enumeration event + * field value `field_val`. + * + * Returns: + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`: + * Success. + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`: + * * `field_val` is `NULL`. + * * The type of `field_val` is not + * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT` or + * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM`. + * * `val` is `NULL`. + */ +extern enum lttng_event_field_value_status +lttng_event_field_value_signed_int_get_value( + const struct lttng_event_field_value *field_val, int64_t *val); + +/* + * Sets `*val` to the raw value of the real event field value + * `field_val`. + * + * Returns: + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`: + * Success. + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`: + * * `field_val` is `NULL`. + * * The type of `field_val` is not + * `LTTNG_EVENT_FIELD_VALUE_TYPE_REAL`. + * * `val` is `NULL`. + */ +extern enum lttng_event_field_value_status +lttng_event_field_value_real_get_value( + const struct lttng_event_field_value *field_val, double *val); + +/* + * Returns the raw value (an UTF-8 C string) of the string event field + * value `field_val`, or `NULL` if: + * + * * `field_val` is `NULL`. + * * The type of `field_val` is not + * `LTTNG_EVENT_FIELD_VALUE_TYPE_STRING`. + */ +extern const char *lttng_event_field_value_string_get_value( + const struct lttng_event_field_value *field_val); + +/* + * Sets `*length` to the length (the number of contained elements) of + * the array event field value `field_val`. + * + * Returns: + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`: + * Success. + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`: + * * `field_val` is `NULL`. + * * The type of `field_val` is not + * `LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY`. + * * `length` is `NULL`. + */ +extern enum lttng_event_field_value_status +lttng_event_field_value_array_get_length( + const struct lttng_event_field_value *field_val, + unsigned int *length); + +/* + * Sets `*elem_field_val` to the event field value at index `index` in + * the array event field value `field_val`. + * + * Returns: + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`: + * Success. + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`: + * * `field_val` is `NULL`. + * * The type of `field_val` is not + * `LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY`. + * * `index` is greater than or equal to the length of `field_val`, + * as returned by lttng_event_field_value_array_get_length(). + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE`: + * * No event field value exists at index `index` within + * `field_val`. + */ +extern enum lttng_event_field_value_status +lttng_event_field_value_array_get_element_at_index( + const struct lttng_event_field_value *field_val, + unsigned int index, + const struct lttng_event_field_value **elem_field_val); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_EVENT_FIELD_VALUE_H */ diff --git a/include/lttng/event-internal.h b/include/lttng/event-internal.h index b3df4c9c2..01e9b8088 100644 --- a/include/lttng/event-internal.h +++ b/include/lttng/event-internal.h @@ -36,4 +36,19 @@ struct lttng_event_extended { LTTNG_HIDDEN struct lttng_event *lttng_event_copy(const struct lttng_event *event); +// FIXME: the implementation of these should be moved to some common file, +// they should not be in the enable_events.c file. + +LTTNG_HIDDEN +int loglevel_str_to_value(const char *inputstr); + +LTTNG_HIDDEN +int loglevel_log4j_str_to_value(const char *inputstr); + +LTTNG_HIDDEN +int loglevel_jul_str_to_value(const char *inputstr); + +LTTNG_HIDDEN +int loglevel_python_str_to_value(const char *inputstr); + #endif /* LTTNG_EVENT_INTERNAL_H */ diff --git a/include/lttng/event-rule/event-rule-internal.h b/include/lttng/event-rule/event-rule-internal.h index 9fb115caf..b0f8a76fc 100644 --- a/include/lttng/event-rule/event-rule-internal.h +++ b/include/lttng/event-rule/event-rule-internal.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -35,11 +36,13 @@ typedef enum lttng_error_code (*event_rule_generate_filter_bytecode_cb)( struct lttng_event_rule *event_rule, uid_t uid, gid_t gid); typedef const char *(*event_rule_get_filter_cb)( const struct lttng_event_rule *event_rule); -typedef const struct lttng_filter_bytecode *( +typedef const struct lttng_bytecode *( *event_rule_get_filter_bytecode_cb)( const struct lttng_event_rule *event_rule); typedef struct lttng_event_exclusion *(*event_rule_generate_exclusions_cb)( const struct lttng_event_rule *event_rule); +typedef struct lttng_event *(*event_rule_generate_lttng_event_cb)( + const struct lttng_event_rule *event_rule); struct lttng_event_rule { struct urcu_ref ref; @@ -52,6 +55,7 @@ struct lttng_event_rule { event_rule_get_filter_cb get_filter; event_rule_get_filter_bytecode_cb get_filter_bytecode; event_rule_generate_exclusions_cb generate_exclusions; + event_rule_generate_lttng_event_cb generate_lttng_event; }; struct lttng_event_rule_comm { @@ -106,7 +110,7 @@ const char *lttng_event_rule_get_filter(const struct lttng_event_rule *rule); * Caller DOES NOT own the returned object. */ LTTNG_HIDDEN -const struct lttng_filter_bytecode *lttng_event_rule_get_filter_bytecode( +const struct lttng_bytecode *lttng_event_rule_get_filter_bytecode( const struct lttng_event_rule *rule); /* @@ -120,4 +124,18 @@ struct lttng_event_exclusion *lttng_event_rule_generate_exclusions( LTTNG_HIDDEN const char *lttng_event_rule_type_str(enum lttng_event_rule_type type); +/* + * This is compatibility helper, allowing us to generate a sessiond side (not + * communication) struct lttng_event object. This facilitate integration with + * current code. + * Caller OWN the returned object + */ +LTTNG_HIDDEN +struct lttng_event *lttng_event_rule_generate_lttng_event( + const struct lttng_event_rule *rule); + +/* Quick helper to test if the event rule apply to an agent domain */ +LTTNG_HIDDEN +bool lttng_event_rule_is_agent(const struct lttng_event_rule *rule); + #endif /* LTTNG_EVENT_RULE_INTERNAL_H */ diff --git a/include/lttng/event-rule/syscall-internal.h b/include/lttng/event-rule/syscall-internal.h index e99b69ae4..b72274f5b 100644 --- a/include/lttng/event-rule/syscall-internal.h +++ b/include/lttng/event-rule/syscall-internal.h @@ -21,7 +21,7 @@ struct lttng_event_rule_syscall { /* Internal use only. */ struct { char *filter; - struct lttng_filter_bytecode *bytecode; + struct lttng_bytecode *bytecode; } internal_filter; }; diff --git a/include/lttng/event-rule/tracepoint-internal.h b/include/lttng/event-rule/tracepoint-internal.h index 039b0a6cf..227fe6de9 100644 --- a/include/lttng/event-rule/tracepoint-internal.h +++ b/include/lttng/event-rule/tracepoint-internal.h @@ -39,7 +39,7 @@ struct lttng_event_rule_tracepoint { /* internal use only. */ struct { char *filter; - struct lttng_filter_bytecode *bytecode; + struct lttng_bytecode *bytecode; } internal_filter; }; diff --git a/include/lttng/lttng-error.h b/include/lttng/lttng-error.h index 9170948a1..97122ed39 100644 --- a/include/lttng/lttng-error.h +++ b/include/lttng/lttng-error.h @@ -174,6 +174,9 @@ enum lttng_error_code { LTTNG_ERR_GROUP_NOT_FOUND = 161, /* Group not found. */ LTTNG_ERR_UNSUPPORTED_DOMAIN = 162, /* Unsupported domain used. */ LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY = 163, /* Operation does not apply to the process attribute tracker's tracking policy */ + LTTNG_ERR_TRIGGER_GROUP_NOTIFICATION_FD = 164, /* Error initializing trigger group notification fd */ + LTTNG_ERR_TRIGGER_GROUP_ERROR_COUNTER = 165, /* Error initializing trigger group error counter */ + LTTNG_ERR_TRIGGER_GROUP_ERROR_COUNTER_FULL = 166, /* No bucket available in trigger group error counter */ /* MUST be last element of the manually-assigned section of the enum */ LTTNG_ERR_NR, diff --git a/include/lttng/lttng.h b/include/lttng/lttng.h index 1049e0f0d..107471afe 100644 --- a/include/lttng/lttng.h +++ b/include/lttng/lttng.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/include/lttng/snapshot.h b/include/lttng/snapshot.h index 24f165287..e674c0c7b 100644 --- a/include/lttng/snapshot.h +++ b/include/lttng/snapshot.h @@ -39,15 +39,15 @@ void lttng_snapshot_output_destroy(struct lttng_snapshot_output *output); */ /* Return snapshot ID. */ -uint32_t lttng_snapshot_output_get_id(struct lttng_snapshot_output *output); +uint32_t lttng_snapshot_output_get_id(const struct lttng_snapshot_output *output); /* Return maximum size of a snapshot. */ -uint64_t lttng_snapshot_output_get_maxsize(struct lttng_snapshot_output *output); +uint64_t lttng_snapshot_output_get_maxsize(const struct lttng_snapshot_output *output); /* Return snapshot name. */ -const char *lttng_snapshot_output_get_name(struct lttng_snapshot_output *output); +const char *lttng_snapshot_output_get_name(const struct lttng_snapshot_output *output); /* Return snapshot control URL in a text format. */ -const char *lttng_snapshot_output_get_ctrl_url(struct lttng_snapshot_output *output); +const char *lttng_snapshot_output_get_ctrl_url(const struct lttng_snapshot_output *output); /* Return snapshot data URL in a text format. */ -const char *lttng_snapshot_output_get_data_url(struct lttng_snapshot_output *output); +const char *lttng_snapshot_output_get_data_url(const struct lttng_snapshot_output *output); /* * Snapshot output setter family functions. diff --git a/include/lttng/trigger/trigger-internal.h b/include/lttng/trigger/trigger-internal.h index 4b031137c..2be0093c0 100644 --- a/include/lttng/trigger/trigger-internal.h +++ b/include/lttng/trigger/trigger-internal.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -26,34 +27,100 @@ struct lttng_trigger { struct lttng_condition *condition; struct lttng_action *action; - LTTNG_OPTIONAL(struct lttng_credentials) creds; + char *name; + /* For now only the uid portion of the credentials is used. */ + struct lttng_credentials creds; + LTTNG_OPTIONAL(uint64_t) error_count; + struct { + enum lttng_trigger_firing_policy_type type; + uint64_t threshold; + uint64_t current_count; + } firing_policy; + /* + * Internal use only. + * The unique token passed to the tracer to identify a event-rule + * notification. + */ + LTTNG_OPTIONAL(uint64_t) tracer_token; + /* + * Internal use only. + * Error accounting counter index. + */ + LTTNG_OPTIONAL(uint64_t) error_counter_index; + + /* + * This ordered set is used to hold the capture bytecodoes and their + * expression. lttng_action_capture_bytecode_element. + * We could only have bytecodes here... the expression are a left over + * from the generation process of the set. They are used for comparison + * during the gathering process. They are refcounted (TODO) and are the same + * object that are present un the underlying action object/s + */ + struct lttng_dynamic_pointer_array capture_bytecode_set; +}; + +struct lttng_triggers { + struct lttng_dynamic_pointer_array array; }; struct lttng_trigger_comm { /* length excludes its own length. */ + uint32_t name_length /* Includes '\0' */; uint32_t length; - /* A condition and action object follow. */ + /* Credentials, only the uid portion is used for now */ + int64_t uid; + /* Policy */ + /* Maps to enum lttng_trigger_firing_policy_type. */ + uint8_t policy_type; + uint64_t policy_threshold; + uint64_t error_count; + /* A name, condition and action object follow. */ char payload[]; } LTTNG_PACKED; +struct lttng_triggers_comm { + uint32_t count; + uint32_t length; + /* Count * lttng_trigger_comm structure */ + char payload[]; +}; + LTTNG_HIDDEN ssize_t lttng_trigger_create_from_payload(struct lttng_payload_view *view, struct lttng_trigger **trigger); LTTNG_HIDDEN -int lttng_trigger_serialize(struct lttng_trigger *trigger, +int lttng_trigger_serialize(const struct lttng_trigger *trigger, struct lttng_payload *payload); LTTNG_HIDDEN -const struct lttng_condition *lttng_trigger_get_const_condition( - const struct lttng_trigger *trigger); +bool lttng_trigger_validate(const struct lttng_trigger *trigger); + +LTTNG_HIDDEN +int lttng_trigger_assign_name( + struct lttng_trigger *dst, const struct lttng_trigger *src); + +LTTNG_HIDDEN +void lttng_trigger_set_tracer_token( + struct lttng_trigger *trigger, uint64_t token); + +LTTNG_HIDDEN +uint64_t lttng_trigger_get_tracer_token(const struct lttng_trigger *trigger); + +LTTNG_HIDDEN +void lttng_trigger_set_error_counter_index( + struct lttng_trigger *trigger, uint64_t error_counter_index); LTTNG_HIDDEN -const struct lttng_action *lttng_trigger_get_const_action( +uint64_t lttng_trigger_get_error_counter_index( const struct lttng_trigger *trigger); LTTNG_HIDDEN -bool lttng_trigger_validate(struct lttng_trigger *trigger); +int lttng_trigger_generate_name(struct lttng_trigger *trigger, uint64_t offset); + +LTTNG_HIDDEN +bool lttng_trigger_is_equal( + const struct lttng_trigger *a, const struct lttng_trigger *b); LTTNG_HIDDEN void lttng_trigger_get(struct lttng_trigger *trigger); @@ -61,13 +128,95 @@ void lttng_trigger_get(struct lttng_trigger *trigger); LTTNG_HIDDEN void lttng_trigger_put(struct lttng_trigger *trigger); +/* + * Allocate a new set of lttng_trigger. + * The returned object must be freed via lttng_triggers_destroy. + */ +LTTNG_HIDDEN +struct lttng_triggers *lttng_triggers_create(void); + +/* + * Return the non-const pointer of an element at index "index" of a + * lttng_triggers. + * + * The ownership of the lttng_triggers element is NOT transfered. + * The returned object can NOT be freed via lttng_trigger_destroy. + */ +LTTNG_HIDDEN +struct lttng_trigger *lttng_triggers_get_pointer_of_index( + const struct lttng_triggers *triggers, unsigned int index); + +/* + * Add a trigger to the triggers object. + */ +LTTNG_HIDDEN +int lttng_triggers_add( + struct lttng_triggers *triggers, struct lttng_trigger *trigger); + +/* + * Serialize a trigger set to a lttng_payload object. + * Return LTTNG_OK on success, negative lttng error code on error. + */ +LTTNG_HIDDEN +int lttng_triggers_serialize(const struct lttng_triggers *triggers, + struct lttng_payload *payload); + +LTTNG_HIDDEN +ssize_t lttng_triggers_create_from_payload(struct lttng_payload_view *view, + struct lttng_triggers **triggers); + LTTNG_HIDDEN const struct lttng_credentials *lttng_trigger_get_credentials( const struct lttng_trigger *trigger); LTTNG_HIDDEN -void lttng_trigger_set_credentials( - struct lttng_trigger *trigger, +void lttng_trigger_set_credentials(struct lttng_trigger *trigger, const struct lttng_credentials *creds); + +/* + * Fire the trigger. + * Increment the firing count. + */ +LTTNG_HIDDEN +void lttng_trigger_fire(struct lttng_trigger *trigger); + +/* + * Check if the trigger would fire. + */ +LTTNG_HIDDEN +bool lttng_trigger_should_fire(const struct lttng_trigger *trigger); + +LTTNG_HIDDEN +bool lttng_trigger_is_ready_to_fire( + struct lttng_trigger *trigger); + +LTTNG_HIDDEN +uint64_t lttng_trigger_get_error_count( + const struct lttng_trigger *trigger); + +LTTNG_HIDDEN +void lttng_trigger_set_error_count(struct lttng_trigger *trigger, + uint64_t error_count); + +/* + * Return the type of any uderlying domain requirement. If no particular + * requirement is needed return LTTNG_DOMAIN_NONE. + */ +LTTNG_HIDDEN +enum lttng_domain_type lttng_trigger_get_underlying_domain_type_restriction( + const struct lttng_trigger *trigger); + +LTTNG_HIDDEN +struct lttng_trigger *lttng_trigger_copy(const struct lttng_trigger *trigger); + +LTTNG_HIDDEN +unsigned int lttng_trigger_get_capture_bytecode_count( + const struct lttng_trigger *trigger); + +LTTNG_HIDDEN +const struct lttng_bytecode * +lttng_trigger_get_capture_bytecode_at_index( + const struct lttng_trigger *trigger, unsigned int index); + #endif /* LTTNG_TRIGGER_INTERNAL_H */ diff --git a/include/lttng/trigger/trigger.h b/include/lttng/trigger/trigger.h index feffc6a8f..e7ade8166 100644 --- a/include/lttng/trigger/trigger.h +++ b/include/lttng/trigger/trigger.h @@ -8,9 +8,14 @@ #ifndef LTTNG_TRIGGER_H #define LTTNG_TRIGGER_H +#include +#include + struct lttng_action; struct lttng_condition; struct lttng_trigger; +/* A set of triggers */ +struct lttng_triggers; #ifdef __cplusplus extern "C" { @@ -21,6 +26,21 @@ enum lttng_register_trigger_status { LTTNG_REGISTER_TRIGGER_STATUS_INVALID = -1, }; +enum lttng_trigger_status { + LTTNG_TRIGGER_STATUS_OK = 0, + LTTNG_TRIGGER_STATUS_ERROR = -1, + LTTNG_TRIGGER_STATUS_UNKNOWN = -2, + LTTNG_TRIGGER_STATUS_INVALID = -3, + LTTNG_TRIGGER_STATUS_UNSET = -4, + LTTNG_TRIGGER_STATUS_UNSUPPORTED = -5, + LTTNG_TRIGGER_STATUS_EPERM = -6, +}; + +enum lttng_trigger_firing_policy_type { + LTTNG_TRIGGER_FIRE_EVERY_N = 0, + LTTNG_TRIGGER_FIRE_ONCE_AFTER_N = 1, +}; + /* * Create a trigger object associating a condition and an action. * @@ -32,6 +52,9 @@ enum lttng_register_trigger_status { * The caller retains the ownership of both the condition and action * and both must be kept alive for the lifetime of the trigger object. * + * If the action is a notification action with capture descriptors, + * the condition must be an event rule condition. + * * A trigger must be registered in order to become activate and can * be destroyed after its registration. * @@ -42,6 +65,28 @@ enum lttng_register_trigger_status { extern struct lttng_trigger *lttng_trigger_create( struct lttng_condition *condition, struct lttng_action *action); +/* + * Set the user identity (uid) of a trigger. + * + * Only available for the root user (uid 0). + * + * Returns LTTNG_TRIGGER_STATUS_OK on success, + * LTTNG_TRIGGER_STATUS_EPERM if not authorized, + * LTTNG_TRIGGER_STATUS_INVALID if invalid parameters are passed. + */ +extern enum lttng_trigger_status lttng_trigger_set_user_identity( + struct lttng_trigger *trigger, uid_t uid); + +/* + * Get the user identity (uid) of a trigger. + * + * Returns LTTNG_TRIGGER_STATUS_OK on success, + * LTTNG_TRIGGER_STATUS_UNSET if unset, + * LTTNG_TRIGGER_STATUS_INVALID if invalid parameters are passed. + */ +extern enum lttng_trigger_status lttng_trigger_get_user_identity( + const struct lttng_trigger *trigger, uid_t *uid); + /* * Get the condition of a trigger. * @@ -52,6 +97,9 @@ extern struct lttng_trigger *lttng_trigger_create( extern struct lttng_condition *lttng_trigger_get_condition( struct lttng_trigger *trigger); +const struct lttng_condition *lttng_trigger_get_const_condition( + const struct lttng_trigger *trigger); + /* * Get the action of a trigger. * @@ -62,6 +110,68 @@ extern struct lttng_condition *lttng_trigger_get_condition( extern struct lttng_action *lttng_trigger_get_action( struct lttng_trigger *trigger); +const struct lttng_action *lttng_trigger_get_const_action( + const struct lttng_trigger *trigger); + +/* + * Get the name of a trigger. + * + * The caller does not assume the ownership of the returned name. + * The name shall only only be used for the duration of the trigger's + * lifetime, or until a different name is set. + * + * Returns LTTNG_TRIGGER_STATUS_OK and a pointer to the trigger's name on + * success, LTTNG_TRIGGER_STATUS_INVALID if an invalid parameter is passed, + * or LTTNG_TRIGGER_STATUS_UNSET if a name was not set prior to this call. + */ +extern enum lttng_trigger_status lttng_trigger_get_name( + const struct lttng_trigger *trigger, const char **name); + +/* + * Set the trigger name. + * + * A name is optional. + * A name will be assigned on trigger registration if no name is set. + * + * The name is copied. + * + * Return LTTNG_TRIGGER_STATUS_OK on success, LTTNG_TRIGGER_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_trigger_status lttng_trigger_set_name( + struct lttng_trigger *trigger, const char *name); + +/* + * Set the trigger firing policy. + * + * This is optional. By default a trigger is set to fire each time the + * associated condition occurs. + * + * Threshold is the number of time the condition must be hit before the policy is + * enacted. + * + * Return LTTNG_TRIGGER_STATUS_OK on success, LTTNG_TRIGGER_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_trigger_status lttng_trigger_set_firing_policy( + struct lttng_trigger *trigger, + enum lttng_trigger_firing_policy_type policy_type, + uint64_t threshold); + +/* + * Get the trigger firing policy. + * + * Threshold is the number of time the condition must be hit before the policy is + * enacted. + * + * Return LTTNG_TRIGGER_STATUS_OK on success, LTTNG_TRIGGER_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_trigger_status lttng_trigger_get_firing_policy( + const struct lttng_trigger *trigger, + enum lttng_trigger_firing_policy_type *policy_type, + uint64_t *threshold); + /* * Destroy (frees) a trigger object. */ @@ -83,7 +193,46 @@ extern int lttng_register_trigger(struct lttng_trigger *trigger); * * Return 0 on success, a negative LTTng error code on error. */ -extern int lttng_unregister_trigger(struct lttng_trigger *trigger); +extern int lttng_unregister_trigger(const struct lttng_trigger *trigger); + +/* + * List current triggers. + * + * On success, a newly-allocated trigger set is returned. + * The trigger set must be destroyed by the caller (see + * lttng_triggers_destroy()). + * + * Returns LTTNG_OK on success, else a negative LTTng error code. + */ +extern enum lttng_error_code lttng_list_triggers( + struct lttng_triggers **triggers); + +/* + * Get a trigger from the set at a given index. + * + * Note that the set maintains the ownership of the returned trigger. + * It must not be destroyed by the user, nor should it be held beyond the + * lifetime of the trigger set. + * + * Returns a trigger, or NULL on error. + */ +extern const struct lttng_trigger *lttng_triggers_get_at_index( + const struct lttng_triggers *triggers, unsigned int index); + +/* + * Get the number of trigger in a trigger set. + * + * Return LTTNG_TRIGGER_STATUS_OK on success, + * LTTNG_TRIGGER_STATUS_INVALID when passed invalid parameters. + */ +extern enum lttng_trigger_status lttng_triggers_get_count( + const struct lttng_triggers *triggers, unsigned int *count); + +/* + * Destroy a trigger set. + */ +extern void lttng_triggers_destroy(struct lttng_triggers *ids); + #ifdef __cplusplus } diff --git a/src/Makefile.am b/src/Makefile.am index fce97f3d1..a4f99905d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -SUBDIRS = common lib bin +SUBDIRS = vendor common lib bin diff --git a/src/bin/lttng-sessiond/Makefile.am b/src/bin/lttng-sessiond/Makefile.am index dd807125b..ef0ad8c21 100644 --- a/src/bin/lttng-sessiond/Makefile.am +++ b/src/bin/lttng-sessiond/Makefile.am @@ -16,10 +16,10 @@ lttng_sessiond_SOURCES = utils.c utils.h \ lttng-ust-ctl.h lttng-ust-abi.h lttng-ust-error.h \ ust-ctl-internal.h ust-abi-internal.h ust-error-internal.h \ ust-registry.h \ + condition-internal.c condition-internal.h \ context.c context.h \ channel.c channel.h \ event.c event.h \ - shm.c shm.h \ consumer.c consumer.h \ session.c session.h \ modprobe.c modprobe.h kern-modules.h \ @@ -55,8 +55,11 @@ lttng_sessiond_SOURCES = utils.c utils.h \ manage-consumer.c manage-consumer.h \ clear.c clear.h \ tracker.c tracker.h \ + trigger-error-accounting.c trigger-error-accounting.h \ action-executor.c action-executor.h +lttng_sessiond_LDFLAGS = -rdynamic + if HAVE_LIBLTTNG_UST_CTL lttng_sessiond_SOURCES += trace-ust.c ust-registry.c ust-app.c \ ust-consumer.c ust-consumer.h notify-apps.c \ diff --git a/src/bin/lttng-sessiond/action-executor.c b/src/bin/lttng-sessiond/action-executor.c index 2e6e27a8c..8df560e0f 100644 --- a/src/bin/lttng-sessiond/action-executor.c +++ b/src/bin/lttng-sessiond/action-executor.c @@ -16,12 +16,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -104,7 +106,7 @@ static const char *action_type_names[] = { static const char *get_action_name(const struct lttng_action *action) { - return action_type_names[lttng_action_get_type_const(action)]; + return action_type_names[lttng_action_get_type(action)]; } /* Check if this trigger allowed to interect with a given session. */ @@ -113,27 +115,37 @@ static bool is_trigger_allowed_for_session(const struct lttng_trigger *trigger, { bool is_allowed = false; const struct lttng_credentials session_creds = { - .uid = session->uid, - .gid = session->gid, + .uid = LTTNG_OPTIONAL_INIT_VALUE(session->uid), + .gid = LTTNG_OPTIONAL_INIT_VALUE(session->gid), }; /* Can never be NULL. */ const struct lttng_credentials *trigger_creds = lttng_trigger_get_credentials(trigger); - is_allowed = (trigger_creds->uid == session_creds.uid) || - (trigger_creds->uid == 0); + is_allowed = (lttng_credentials_is_equal_uid(trigger_creds, &session_creds)) || + (lttng_credentials_get_uid(trigger_creds) == 0); if (!is_allowed) { - WARN("Trigger is not allowed to interact with session `%s`: session uid = %ld, session gid = %ld, trigger uid = %ld, trigger gid = %ld", + WARN("Trigger is not allowed to interact with session `%s`: session uid = %ld, session gid = %ld, trigger uid = %ld", session->name, (long int) session->uid, (long int) session->gid, - (long int) trigger_creds->uid, - (long int) trigger_creds->gid); + (long int) lttng_credentials_get_uid(trigger_creds)); } return is_allowed; } +static const char *get_trigger_name(const struct lttng_trigger *trigger) +{ + const char *trigger_name; + enum lttng_trigger_status trigger_status; + + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + return trigger_name; +} + static int client_handle_transmission_status( struct notification_client *client, enum client_transmission_status status, @@ -184,9 +196,10 @@ static int action_executor_notify_handler(struct action_executor *executor, lttng_trigger_get_const_condition(work_item->trigger), work_item->evaluation, lttng_trigger_get_credentials(work_item->trigger), - LTTNG_OPTIONAL_GET_PTR(work_item->object_creds), - client_handle_transmission_status, - executor); + work_item->object_creds.is_set ? + &(work_item->object_creds.value) : + NULL, + client_handle_transmission_status, executor); } static int action_executor_start_session_handler(struct action_executor *executor, @@ -211,9 +224,9 @@ static int action_executor_start_session_handler(struct action_executor *executo session_lock_list(); session = session_find_by_name(session_name); if (!session) { - DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%p`", + DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%s`", session_name, get_action_name(action), - work_item->trigger); + get_trigger_name(work_item->trigger)); goto error_unlock_list; } @@ -225,16 +238,16 @@ static int action_executor_start_session_handler(struct action_executor *executo cmd_ret = cmd_start_trace(session); switch (cmd_ret) { case LTTNG_OK: - DBG("Successfully started session `%s` on behalf of trigger `%p`", - session_name, work_item->trigger); + DBG("Successfully started session `%s` on behalf of trigger `%s`", + session_name, get_trigger_name(work_item->trigger)); break; case LTTNG_ERR_TRACE_ALREADY_STARTED: - DBG("Attempted to start session `%s` on behalf of trigger `%p` but it was already started", - session_name, work_item->trigger); + DBG("Attempted to start session `%s` on behalf of trigger `%s` but it was already started", + session_name, get_trigger_name(work_item->trigger)); break; default: - WARN("Failed to start session `%s` on behalf of trigger `%p`: %s", - session_name, work_item->trigger, + WARN("Failed to start session `%s` on behalf of trigger `%s`: %s", + session_name, get_trigger_name(work_item->trigger), lttng_strerror(-cmd_ret)); break; } @@ -270,9 +283,9 @@ static int action_executor_stop_session_handler(struct action_executor *executor session_lock_list(); session = session_find_by_name(session_name); if (!session) { - DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%p`", + DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%s`", session_name, get_action_name(action), - work_item->trigger); + get_trigger_name(work_item->trigger)); goto error_unlock_list; } @@ -284,16 +297,16 @@ static int action_executor_stop_session_handler(struct action_executor *executor cmd_ret = cmd_stop_trace(session); switch (cmd_ret) { case LTTNG_OK: - DBG("Successfully stopped session `%s` on behalf of trigger `%p`", - session_name, work_item->trigger); + DBG("Successfully stopped session `%s` on behalf of trigger `%s`", + session_name, get_trigger_name(work_item->trigger)); break; case LTTNG_ERR_TRACE_ALREADY_STOPPED: - DBG("Attempted to stop session `%s` on behalf of trigger `%p` but it was already stopped", - session_name, work_item->trigger); + DBG("Attempted to stop session `%s` on behalf of trigger `%s` but it was already stopped", + session_name, get_trigger_name(work_item->trigger)); break; default: - WARN("Failed to stop session `%s` on behalf of trigger `%p`: %s", - session_name, work_item->trigger, + WARN("Failed to stop session `%s` on behalf of trigger `%s`: %s", + session_name, get_trigger_name(work_item->trigger), lttng_strerror(-cmd_ret)); break; } @@ -329,9 +342,9 @@ static int action_executor_rotate_session_handler(struct action_executor *execut session_lock_list(); session = session_find_by_name(session_name); if (!session) { - DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%p`", + DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%s`", session_name, get_action_name(action), - work_item->trigger); + get_trigger_name(work_item->trigger)); goto error_unlock_list; } @@ -344,21 +357,21 @@ static int action_executor_rotate_session_handler(struct action_executor *execut LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED); switch (cmd_ret) { case LTTNG_OK: - DBG("Successfully started rotation of session `%s` on behalf of trigger `%p`", - session_name, work_item->trigger); + DBG("Successfully started rotation of session `%s` on behalf of trigger `%s`", + session_name, get_trigger_name(work_item->trigger)); break; case LTTNG_ERR_ROTATION_PENDING: - DBG("Attempted to start a rotation of session `%s` on behalf of trigger `%p` but a rotation is already ongoing", - session_name, work_item->trigger); + DBG("Attempted to start a rotation of session `%s` on behalf of trigger `%s` but a rotation is already ongoing", + session_name, get_trigger_name(work_item->trigger)); break; case LTTNG_ERR_ROTATION_MULTIPLE_AFTER_STOP: case LTTNG_ERR_ROTATION_AFTER_STOP_CLEAR: - DBG("Attempted to start a rotation of session `%s` on behalf of trigger `%p` but a rotation has already been completed since the last stop or clear", - session_name, work_item->trigger); + DBG("Attempted to start a rotation of session `%s` on behalf of trigger `%s` but a rotation has already been completed since the last stop or clear", + session_name, get_trigger_name(work_item->trigger)); break; default: - WARN("Failed to start a rotation of session `%s` on behalf of trigger `%p`: %s", - session_name, work_item->trigger, + WARN("Failed to start a rotation of session `%s` on behalf of trigger `%s`: %s", + session_name, get_trigger_name(work_item->trigger), lttng_strerror(-cmd_ret)); break; } @@ -424,12 +437,12 @@ static int action_executor_snapshot_session_handler(struct action_executor *exec cmd_ret = cmd_snapshot_record(session, snapshot_output, 0); switch (cmd_ret) { case LTTNG_OK: - DBG("Successfully recorded snapshot of session `%s` on behalf of trigger `%p`", - session_name, work_item->trigger); + DBG("Successfully recorded snapshot of session `%s` on behalf of trigger `%s`", + session_name, get_trigger_name(work_item->trigger)); break; default: - WARN("Failed to record snapshot of session `%s` on behalf of trigger `%p`: %s", - session_name, work_item->trigger, + WARN("Failed to record snapshot of session `%s` on behalf of trigger `%s`: %s", + session_name, get_trigger_name(work_item->trigger), lttng_strerror(-cmd_ret)); break; } @@ -468,8 +481,8 @@ static int action_executor_group_handler(struct action_executor *executor, ret = action_executor_generic_handler( executor, work_item, action); if (ret) { - ERR("Stopping the execution of the action group of trigger `%p` following a fatal error", - work_item->trigger); + ERR("Stopping the execution of the action group of trigger `%s` following a fatal error", + get_trigger_name(work_item->trigger)); goto end; } } @@ -481,12 +494,12 @@ static int action_executor_generic_handler(struct action_executor *executor, const struct action_work_item *work_item, const struct lttng_action *action) { - DBG("Executing action `%s` of trigger `%p` action work item %" PRIu64, + DBG("Executing action `%s` of trigger `%s` action work item %" PRIu64, get_action_name(action), - work_item->trigger, + get_trigger_name(work_item->trigger), work_item->id); - return action_executors[lttng_action_get_type_const(action)]( + return action_executors[lttng_action_get_type(action)]( executor, work_item, action); } @@ -497,11 +510,11 @@ static int action_work_item_execute(struct action_executor *executor, const struct lttng_action *action = lttng_trigger_get_const_action(work_item->trigger); - DBG("Starting execution of action work item %" PRIu64 " of trigger `%p`", - work_item->id, work_item->trigger); + DBG("Starting execution of action work item %" PRIu64 " of trigger `%s`", + work_item->id, get_trigger_name(work_item->trigger)); ret = action_executor_generic_handler(executor, work_item, action); - DBG("Completed execution of action work item %" PRIu64 " of trigger `%p`", - work_item->id, work_item->trigger); + DBG("Completed execution of action work item %" PRIu64 " of trigger `%s`", + work_item->id, get_trigger_name(work_item->trigger)); return ret; } @@ -541,7 +554,7 @@ static void *action_executor_thread(void *_data) continue; } - /* Pop item from front of the listwith work lock held. */ + /* Pop item from front of the list with work lock held. */ work_item = cds_list_first_entry(&executor->work.list, struct action_work_item, list_node); cds_list_del(&work_item->list_node); @@ -636,8 +649,8 @@ void action_executor_destroy(struct action_executor *executor) cds_list_for_each_entry_safe ( work_item, tmp, &executor->work.list, list_node) { WARN("Discarding action work item %" PRIu64 - " associated to trigger `%p`", - work_item->id, work_item->trigger); + " associated to trigger `%s`", + work_item->id, get_trigger_name(work_item->trigger)); cds_list_del(&work_item->list_node); action_work_item_destroy(work_item); } @@ -662,9 +675,9 @@ enum action_executor_status action_executor_enqueue( /* Check for queue overflow. */ if (executor->work.pending_count >= MAX_QUEUED_WORK_COUNT) { /* Most likely spammy, remove if it is the case. */ - DBG("Refusing to enqueue action for trigger `%p` as work item %" PRIu64 - " (overflow)", - trigger, work_item_id); + DBG("Refusing to enqueue action for trigger `%s` as work item %" PRIu64 + " (overflow)", + get_trigger_name(trigger), work_item_id); executor_status = ACTION_EXECUTOR_STATUS_OVERFLOW; goto error_unlock; } @@ -672,7 +685,7 @@ enum action_executor_status action_executor_enqueue( work_item = zmalloc(sizeof(*work_item)); if (!work_item) { PERROR("Failed to allocate action executor work item on behalf of trigger `%p`", - trigger); + get_trigger_name(trigger)); executor_status = ACTION_EXECUTOR_STATUS_ERROR; goto error_unlock; } @@ -703,7 +716,7 @@ enum action_executor_status action_executor_enqueue( cds_list_add_tail(&work_item->list_node, &executor->work.list); executor->work.pending_count++; DBG("Enqueued action for trigger `%p` as work item %" PRIu64, - trigger, work_item_id); + get_trigger_name(trigger), work_item_id); signal = true; error_unlock: diff --git a/src/bin/lttng-sessiond/agent-thread.c b/src/bin/lttng-sessiond/agent-thread.c index 06ef377a3..44f41059b 100644 --- a/src/bin/lttng-sessiond/agent-thread.c +++ b/src/bin/lttng-sessiond/agent-thread.c @@ -59,6 +59,8 @@ static void update_agent_app(const struct agent_app *app) { struct ltt_session *session, *stmp; struct ltt_session_list *list; + struct agent *trigger_agent; + struct lttng_ht_iter iter; list = session_get_list(); assert(list); @@ -82,6 +84,14 @@ static void update_agent_app(const struct agent_app *app) session_unlock(session); session_put(session); } + + /* Do we need more locking here? maybe against trigger add? */ + rcu_read_lock(); + cds_lfht_for_each_entry (trigger_agents_ht_by_domain->ht, &iter.iter, + trigger_agent, node.node) { + agent_update(trigger_agent, app); + } + rcu_read_unlock(); } /* diff --git a/src/bin/lttng-sessiond/agent.c b/src/bin/lttng-sessiond/agent.c index e5978be5c..66c55fc46 100644 --- a/src/bin/lttng-sessiond/agent.c +++ b/src/bin/lttng-sessiond/agent.c @@ -11,6 +11,12 @@ #include #include +#include +#include +#include +#include +#include + #include #include @@ -101,7 +107,8 @@ no_match: } /* - * Match function for the events hash table lookup by name and loglevel. + * Match function for the events hash table lookup by name, loglevel and + * filter_expression. */ static int ht_match_event(struct cds_lfht_node *node, const void *_key) @@ -682,6 +689,7 @@ int agent_enable_event(struct agent_event *event, } event->enabled = 1; + event->user_refcount++; ret = LTTNG_OK; error: @@ -791,6 +799,17 @@ int agent_disable_event(struct agent_event *event, goto end; } + if (event->user_refcount - 1 != 0) { + /* + * Disable the agent event only when all users (trigger etc.) + * have disabled it. + */ + + event->user_refcount--; + ret = LTTNG_OK; + goto end; + } + rcu_read_lock(); cds_lfht_for_each_entry(agent_apps_ht_by_sock->ht, &iter.iter, app, @@ -806,6 +825,7 @@ int agent_disable_event(struct agent_event *event, } } + event->user_refcount = 0; event->enabled = 0; error: @@ -1109,7 +1129,7 @@ error: */ struct agent_event *agent_create_event(const char *name, enum lttng_loglevel_type loglevel_type, int loglevel_value, - struct lttng_filter_bytecode *filter, char *filter_expression) + struct lttng_bytecode *filter, char *filter_expression) { struct agent_event *event = NULL; @@ -1206,6 +1226,66 @@ void agent_find_events_by_name(const char *name, struct agent *agt, ht_match_event_by_name, &key, &iter->iter); } +/* + * Find the agent event matching the trigger. + * + * RCU read side lock MUST be acquired. It must be kept for as long as + * the returned agent_event is used. + * + * Return object if found else NULL. + */ +struct agent_event *agent_find_event_by_trigger( + const struct lttng_trigger *trigger, struct agent *agt) +{ + enum lttng_condition_status c_status; + enum lttng_event_rule_status er_status; + enum lttng_domain_type d_type; + const struct lttng_condition *condition; + const struct lttng_event_rule *rule; + const char *name; + const char *filter_expression; + /* TODO validate if this is the unset value or no */ + int loglevel_value = 0; + enum lttng_loglevel_type loglevel_type; + + assert(agt); + assert(agt->events); + + condition = lttng_trigger_get_const_condition(trigger); + + assert(lttng_condition_get_type(condition) == + LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + + c_status = lttng_condition_event_rule_get_rule(condition, &rule); + assert(c_status == LTTNG_CONDITION_STATUS_OK); + + assert(lttng_event_rule_get_type(rule) == + LTTNG_EVENT_RULE_TYPE_TRACEPOINT); + + d_type = lttng_event_rule_get_domain_type(rule); + assert(d_type == LTTNG_DOMAIN_JUL || d_type == LTTNG_DOMAIN_LOG4J || + d_type == LTTNG_DOMAIN_PYTHON); + + /* Get the name (aka pattern) */ + er_status = lttng_event_rule_tracepoint_get_pattern(rule, &name); + assert(er_status == LTTNG_EVENT_RULE_STATUS_OK); + + /* Get the internal filter_expression */ + filter_expression = lttng_event_rule_get_filter(rule); + + er_status = lttng_event_rule_tracepoint_get_log_level_type( + rule, &loglevel_type); + assert(er_status == LTTNG_EVENT_RULE_STATUS_OK); + if (loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) { + er_status = lttng_event_rule_tracepoint_get_log_level( + rule, &loglevel_value); + assert(er_status == LTTNG_EVENT_RULE_STATUS_OK); + } + + return agent_find_event(name, loglevel_type, loglevel_value, + filter_expression, agt); +} + /* * Get the next agent event duplicate by name. This should be called * after a call to agent_find_events_by_name() to iterate on events. @@ -1233,8 +1313,10 @@ void agent_event_next_duplicate(const char *name, * Return object if found else NULL. */ struct agent_event *agent_find_event(const char *name, - enum lttng_loglevel_type loglevel_type, int loglevel_value, - char *filter_expression, struct agent *agt) + enum lttng_loglevel_type loglevel_type, + int loglevel_value, + const char *filter_expression, + struct agent *agt) { struct lttng_ht_node_str *node; struct lttng_ht_iter iter; @@ -1347,6 +1429,21 @@ int agent_app_ht_alloc(void) return ret; } +/* + * Allocate agent_apps_ht_by_sock. + */ +int trigger_agent_ht_alloc(void) +{ + int ret = 0; + + trigger_agents_ht_by_domain = lttng_ht_new(0, LTTNG_HT_TYPE_U64); + if (!trigger_agents_ht_by_domain) { + ret = -1; + } + + return ret; +} + /* * Destroy a agent application by socket. */ @@ -1396,6 +1493,32 @@ void agent_app_ht_clean(void) lttng_ht_destroy(agent_apps_ht_by_sock); } +/* + * Clean-up the trigger agent hash table and destroy it. + */ +void trigger_agent_ht_clean(void) +{ + struct lttng_ht_node_u64 *node; + struct lttng_ht_iter iter; + + if (!trigger_agents_ht_by_domain) { + return; + } + rcu_read_lock(); + cds_lfht_for_each_entry (trigger_agents_ht_by_domain->ht, &iter.iter, + node, node) { + struct agent *agent; + + (void) lttng_ht_del(trigger_agents_ht_by_domain, &iter); + + agent = caa_container_of(node, struct agent, node); + agent_destroy(agent); + } + rcu_read_unlock(); + + lttng_ht_destroy(trigger_agents_ht_by_domain); +} + /* * Update a agent application (given socket) using the given agent. * @@ -1447,3 +1570,27 @@ void agent_update(const struct agent *agt, const struct agent_app *app) rcu_read_unlock(); } + +struct agent *trigger_find_agent(enum lttng_domain_type domain_type) +{ + struct agent *agt = NULL; + struct lttng_ht_node_u64 *node; + struct lttng_ht_iter iter; + uint64_t key; + + assert(trigger_agents_ht_by_domain); + + DBG3("Trigger agent lookup for domain %d", domain_type); + + key = domain_type; + + lttng_ht_lookup(trigger_agents_ht_by_domain, &key, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if (!node) { + goto end; + } + agt = caa_container_of(node, struct agent, node); + +end: + return agt; +} diff --git a/src/bin/lttng-sessiond/agent.h b/src/bin/lttng-sessiond/agent.h index f8e67efda..72aa48108 100644 --- a/src/bin/lttng-sessiond/agent.h +++ b/src/bin/lttng-sessiond/agent.h @@ -24,11 +24,15 @@ */ extern struct lttng_ht *agent_apps_ht_by_sock; +/* + * Hash table that contains the trigger agents by domain */ +extern struct lttng_ht *trigger_agents_ht_by_domain; + struct agent_ht_key { const char *name; int loglevel_value; enum lttng_loglevel_type loglevel_type; - char *filter_expression; + const char *filter_expression; }; /* @@ -84,9 +88,15 @@ struct agent_event { struct lttng_ht_node_str node; /* Filter associated with the event. NULL if none. */ - struct lttng_filter_bytecode *filter; + struct lttng_bytecode *filter; char *filter_expression; struct lttng_event_exclusion *exclusion; + + /* + * Multiple triggers and events can use this agent event. + * The event can only be disabled when the count is zero. + */ + unsigned int user_refcount; }; /* @@ -128,13 +138,15 @@ void agent_add(struct agent *agt, struct lttng_ht *ht); /* Agent event API. */ struct agent_event *agent_create_event(const char *name, enum lttng_loglevel_type loglevel_type, int loglevel_value, - struct lttng_filter_bytecode *filter, + struct lttng_bytecode *filter, char *filter_expression); void agent_add_event(struct agent_event *event, struct agent *agt); struct agent_event *agent_find_event(const char *name, - enum lttng_loglevel_type loglevel_type, int loglevel_value, - char *filter_expression, struct agent *agt); + enum lttng_loglevel_type loglevel_type, + int loglevel_value, + const char *filter_expression, + struct agent *agt); void agent_find_events_by_name(const char *name, struct agent *agt, struct lttng_ht_iter* iter); void agent_event_next_duplicate(const char *name, @@ -167,4 +179,12 @@ void agent_update(const struct agent *agt, const struct agent_app *app); int agent_list_events(struct lttng_event **events, enum lttng_domain_type domain); +struct agent_event *agent_find_event_by_trigger( + const struct lttng_trigger *trigger, struct agent *agt); + +/* todo: find a better place for this */ +struct agent *trigger_find_agent(enum lttng_domain_type domain_type); +void trigger_agent_ht_clean(void); +int trigger_agent_ht_alloc(void); + #endif /* LTTNG_SESSIOND_AGENT_H */ diff --git a/src/bin/lttng-sessiond/client.c b/src/bin/lttng-sessiond/client.c index 8a2ef85b7..b1c8cee9f 100644 --- a/src/bin/lttng-sessiond/client.c +++ b/src/bin/lttng-sessiond/client.c @@ -42,6 +42,7 @@ #include "utils.h" #include "manage-consumer.h" #include "clear.h" +#include "agent-thread.h" static bool is_root; @@ -770,6 +771,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock, int ret = LTTNG_OK; int need_tracing_session = 1; int need_domain; + int need_consumerd = 1; DBG("Processing client command %d", cmd_ctx->lsm.cmd_type); @@ -793,19 +795,29 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock, case LTTNG_SET_SESSION_SHM_PATH: case LTTNG_REGENERATE_METADATA: case LTTNG_REGENERATE_STATEDUMP: - case LTTNG_REGISTER_TRIGGER: - case LTTNG_UNREGISTER_TRIGGER: case LTTNG_ROTATE_SESSION: case LTTNG_ROTATION_GET_INFO: case LTTNG_ROTATION_SET_SCHEDULE: case LTTNG_SESSION_LIST_ROTATION_SCHEDULES: case LTTNG_CLEAR_SESSION: + case LTTNG_LIST_TRIGGERS: need_domain = 0; break; default: need_domain = 1; } + /* Needs a functioning consumerd */ + switch (cmd_ctx->lsm.cmd_type) { + case LTTNG_REGISTER_TRIGGER: + case LTTNG_UNREGISTER_TRIGGER: + need_consumerd = 0; + break; + default: + need_consumerd = 1; + break; + } + if (config.no_kernel && need_domain && cmd_ctx->lsm.domain.type == LTTNG_DOMAIN_KERNEL) { if (!is_root) { @@ -846,6 +858,8 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock, case LTTNG_DATA_PENDING: case LTTNG_ROTATE_SESSION: case LTTNG_ROTATION_GET_INFO: + case LTTNG_REGISTER_TRIGGER: + case LTTNG_LIST_TRIGGERS: break; default: /* Setup lttng message with no payload */ @@ -866,6 +880,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock, case LTTNG_SAVE_SESSION: case LTTNG_REGISTER_TRIGGER: case LTTNG_UNREGISTER_TRIGGER: + case LTTNG_LIST_TRIGGERS: need_tracing_session = 0; break; default: @@ -944,7 +959,8 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock, } /* Consumer is in an ERROR state. Report back to client */ - if (uatomic_read(&kernel_consumerd_state) == CONSUMER_ERROR) { + if (need_consumerd && uatomic_read(&kernel_consumerd_state) == + CONSUMER_ERROR) { ret = LTTNG_ERR_NO_KERNCONSUMERD; goto error; } @@ -989,14 +1005,21 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock, case LTTNG_DOMAIN_JUL: case LTTNG_DOMAIN_LOG4J: case LTTNG_DOMAIN_PYTHON: + if (!agent_tracing_is_enabled()) { + ret = LTTNG_ERR_AGENT_TRACING_DISABLED; + goto error; + } + /* Fallthrough */ case LTTNG_DOMAIN_UST: { if (!ust_app_supported()) { ret = LTTNG_ERR_NO_UST; goto error; } + /* Consumer is in an ERROR state. Report back to client */ - if (uatomic_read(&ust_consumerd_state) == CONSUMER_ERROR) { + if (need_consumerd && uatomic_read(&ust_consumerd_state) == + CONSUMER_ERROR) { ret = LTTNG_ERR_NO_USTCONSUMERD; goto error; } @@ -1445,7 +1468,7 @@ error_add_context: { struct lttng_event *ev = NULL; struct lttng_event_exclusion *exclusion = NULL; - struct lttng_filter_bytecode *bytecode = NULL; + struct lttng_bytecode *bytecode = NULL; char *filter_expression = NULL; /* Handle exclusion events and receive it from the client. */ @@ -2024,8 +2047,39 @@ error_add_context: } case LTTNG_REGISTER_TRIGGER: { + struct lttng_trigger *return_trigger; + size_t original_payload_size; + size_t payload_size; + + ret = setup_empty_lttng_msg(cmd_ctx); + if (ret) { + ret = LTTNG_ERR_NOMEM; + goto setup_error; + } + + original_payload_size = cmd_ctx->reply_payload.buffer.size; + ret = cmd_register_trigger(cmd_ctx, *sock, - notification_thread_handle); + notification_thread_handle, &return_trigger); + if (ret != LTTNG_OK) { + goto error; + } + + ret = lttng_trigger_serialize(return_trigger, &cmd_ctx->reply_payload); + if (ret) { + ERR("Failed to serialize trigger in reply to \"register trigger\" command"); + ret = LTTNG_ERR_NOMEM; + lttng_trigger_destroy(return_trigger); + goto error; + } + lttng_trigger_destroy(return_trigger); + + payload_size = cmd_ctx->reply_payload.buffer.size - + original_payload_size; + + update_lttng_msg(cmd_ctx, 0, payload_size); + + ret = LTTNG_OK; break; } case LTTNG_UNREGISTER_TRIGGER: @@ -2138,6 +2192,42 @@ error_add_context: ret = cmd_clear_session(cmd_ctx->session, sock); break; } + case LTTNG_LIST_TRIGGERS: + { + struct lttng_triggers *return_triggers; + size_t original_payload_size; + size_t payload_size; + + ret = setup_empty_lttng_msg(cmd_ctx); + if (ret) { + ret = LTTNG_ERR_NOMEM; + goto setup_error; + } + + original_payload_size = cmd_ctx->reply_payload.buffer.size; + + ret = cmd_list_triggers(cmd_ctx, + notification_thread_handle, &return_triggers); + if (ret != LTTNG_OK) { + goto error; + } + + ret = lttng_triggers_serialize( + return_triggers, &cmd_ctx->reply_payload); + lttng_triggers_destroy(return_triggers); + if (ret) { + ERR("Failed to serialize triggers in reply to \"list triggers\" command"); + ret = LTTNG_ERR_NOMEM; + goto error; + } + payload_size = cmd_ctx->reply_payload.buffer.size - + original_payload_size; + + update_lttng_msg(cmd_ctx, 0, payload_size); + + ret = LTTNG_OK; + break; + } default: ret = LTTNG_ERR_UND; break; diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index dc442035a..65553a538 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -29,6 +29,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -110,7 +117,7 @@ static int cmd_enable_event_internal(struct ltt_session *session, const struct lttng_domain *domain, char *channel_name, struct lttng_event *event, char *filter_expression, - struct lttng_filter_bytecode *filter, + struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, int wpipe); @@ -2058,7 +2065,7 @@ static int _cmd_enable_event(struct ltt_session *session, const struct lttng_domain *domain, char *channel_name, struct lttng_event *event, char *filter_expression, - struct lttng_filter_bytecode *filter, + struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, int wpipe, bool internal_event) { @@ -2141,7 +2148,7 @@ static int _cmd_enable_event(struct ltt_session *session, case LTTNG_EVENT_ALL: { char *filter_expression_a = NULL; - struct lttng_filter_bytecode *filter_a = NULL; + struct lttng_bytecode *filter_a = NULL; /* * We need to duplicate filter_expression and filter, @@ -2376,11 +2383,11 @@ static int _cmd_enable_event(struct ltt_session *session, { char *filter_expression_copy = NULL; - struct lttng_filter_bytecode *filter_copy = NULL; + struct lttng_bytecode *filter_copy = NULL; if (filter) { const size_t filter_size = sizeof( - struct lttng_filter_bytecode) + struct lttng_bytecode) + filter->len; filter_copy = zmalloc(filter_size); @@ -2456,7 +2463,7 @@ int cmd_enable_event(struct ltt_session *session, const struct lttng_domain *domain, char *channel_name, struct lttng_event *event, char *filter_expression, - struct lttng_filter_bytecode *filter, + struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, int wpipe) { @@ -2473,7 +2480,7 @@ static int cmd_enable_event_internal(struct ltt_session *session, const struct lttng_domain *domain, char *channel_name, struct lttng_event *event, char *filter_expression, - struct lttng_filter_bytecode *filter, + struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, int wpipe) { @@ -4256,8 +4263,59 @@ end: return ret; } +/* + * On success LTTNG_OK. On error, returns lttng_error code. + */ +static enum lttng_error_code prepare_trigger_object(struct lttng_trigger *trigger, const struct lttng_credentials *cmd_creds) +{ + enum lttng_error_code ret; + /* Internal object of the trigger might have to "generate" and + * "populate" internal field e.g filter bytecode + */ + struct lttng_condition *condition = NULL; + + condition = lttng_trigger_get_condition(trigger); + if (!condition) { + ret = LTTNG_ERR_INVALID_TRIGGER; + goto end; + } + + switch (lttng_condition_get_type(condition)) { + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + { + struct lttng_event_rule *event_rule; + lttng_condition_event_rule_get_rule_mutable( + condition, &event_rule); + ret = lttng_event_rule_generate_filter_bytecode( + event_rule, lttng_credentials_get_uid(cmd_creds), lttng_credentials_get_gid(cmd_creds)); + if (ret != LTTNG_OK) { + goto end; + } + + /* Generate the capture bytecode set */ + ret = lttng_condition_event_rule_generate_capture_descriptor_bytecode_set( + condition, &trigger->capture_bytecode_set); + if (ret != LTTNG_OK) { + goto end; + } + + ret = LTTNG_OK; + break; + } + default: + { + ret = LTTNG_OK; + break; + } + } + +end: + return ret; +} + int cmd_register_trigger(struct command_ctx *cmd_ctx, int sock, - struct notification_thread_handle *notification_thread) + struct notification_thread_handle *notification_thread, + struct lttng_trigger **return_trigger) { int ret; size_t trigger_len; @@ -4265,8 +4323,8 @@ int cmd_register_trigger(struct command_ctx *cmd_ctx, int sock, struct lttng_trigger *trigger = NULL; struct lttng_payload trigger_payload; struct lttng_credentials cmd_creds = { - .uid = cmd_ctx->creds.uid, - .gid = cmd_ctx->creds.gid, + .uid = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.uid), + .gid = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.gid), }; lttng_payload_init(&trigger_payload); @@ -4314,13 +4372,118 @@ int cmd_register_trigger(struct command_ctx *cmd_ctx, int sock, } } - /* Set the trigger credential */ - lttng_trigger_set_credentials(trigger, &cmd_creds); + /* + * Validate the trigger credentials against the command credentials. + * Only the root user can register a trigger with non-matching + * credentials. + */ + if (!lttng_credentials_is_equal_uid( + lttng_trigger_get_credentials(trigger), + &cmd_creds)) { + if (lttng_credentials_get_uid(&cmd_creds) != 0) { + ERR("Trigger credentials do not match the command credentials"); + ret = LTTNG_ERR_INVALID_TRIGGER; + goto end; + } + } + + /* + * Since we return the trigger object, take a reference to it + * Caller is responsible for calling lttng_destroy_trigger on it. + * This thread does not OWN the trigger. + */ + lttng_trigger_get(trigger); + + /* Prepare internal trigger object if needed on reception. + * Handles also special treatment for certain internal object of the + * trigger (e.g uprobe event rule binary fd. + */ + ret = prepare_trigger_object(trigger, &cmd_creds); + if (ret != LTTNG_OK) { + goto end; + } /* Inform the notification thread */ ret = notification_thread_command_register_trigger(notification_thread, trigger); - /* Ownership of trigger was transferred. */ + if (ret != LTTNG_OK) { + goto end; + } + + /* Synchronize tracers, only if needed */ + /* TODO: maybe extract somewhere else */ + { + struct lttng_condition *condition = NULL; + condition = lttng_trigger_get_condition(trigger); + if (!condition) { + ret = LTTNG_ERR_INVALID_TRIGGER; + goto end; + } + + if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) { + const struct lttng_event_rule *rule = NULL; + (void) lttng_condition_event_rule_get_rule(condition, &rule); + if (!rule) { + ret = LTTNG_ERR_INVALID_TRIGGER; + goto end; + } + if (lttng_event_rule_get_domain_type(rule) == LTTNG_DOMAIN_KERNEL) { + /* TODO: get the token value from the + * notification thread and only perform an + * enable and a disable.... This is NOT + * OPTIMIZED AT ALL + */ + ret = kernel_register_trigger(trigger, &cmd_creds); + if (ret != LTTNG_OK) { + enum lttng_error_code notif_thread_unregister_ret = + notification_thread_command_unregister_trigger( + notification_thread, + trigger); + if (notif_thread_unregister_ret != LTTNG_OK) { + ERR("Error unregistering notification thread trigger after kernel registration failure."); + } + goto end; + + } + } else { + /* TODO: get the token value from the + * notification thread and only perform an + * enable and a disable.... This is NOT + * OPTIMIZED AT ALL + */ + ust_app_global_update_all_tokens(); + /* Agent handling */ + if (lttng_event_rule_is_agent(rule)) { + struct agent *agt; + const char *pattern; + enum lttng_domain_type domain_type; + domain_type = lttng_event_rule_get_domain_type( + rule); + (void) lttng_event_rule_tracepoint_get_pattern( + rule, &pattern); + agt = trigger_find_agent(domain_type); + if (!agt) { + agt = agent_create(domain_type); + if (!agt) { + ret = LTTNG_ERR_NOMEM; + goto end; + } + agent_add(agt, trigger_agents_ht_by_domain); + } + + ret = trigger_agent_enable( + trigger, agt); + if (ret != LTTNG_OK) { + goto end; + } + } + } + } + } + + /* Return an image of the updated object to the client */ + *return_trigger = trigger; + /* Ownership of trigger was transferred to caller. */ trigger = NULL; end: lttng_trigger_destroy(trigger); @@ -4337,8 +4500,8 @@ int cmd_unregister_trigger(struct command_ctx *cmd_ctx, int sock, struct lttng_trigger *trigger = NULL; struct lttng_payload trigger_payload; struct lttng_credentials cmd_creds = { - .uid = cmd_ctx->creds.uid, - .gid = cmd_ctx->creds.gid, + .uid = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.uid), + .gid = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.gid), }; lttng_payload_init(&trigger_payload); @@ -4385,16 +4548,114 @@ int cmd_unregister_trigger(struct command_ctx *cmd_ctx, int sock, } } - lttng_trigger_set_credentials(trigger, &cmd_creds); + /* + * Validate the trigger credentials against the command credentials. + * Only the root user can unregister a trigger with non-matching + * credentials. + */ + if (!lttng_credentials_is_equal_uid( + lttng_trigger_get_credentials(trigger), + &cmd_creds)) { + if (lttng_credentials_get_uid(&cmd_creds) != 0) { + ERR("Trigger credentials do not match the command credentials"); + ret = LTTNG_ERR_INVALID_TRIGGER; + goto end; + } + } + + ret = prepare_trigger_object(trigger, &cmd_creds); + if (ret != LTTNG_OK) { + goto end; + } ret = notification_thread_command_unregister_trigger(notification_thread, trigger); + + /* Synchronize tracers, only if needed */ + /* TODO: maybe extract somewhere else */ + { + struct lttng_condition *condition = NULL; + condition = lttng_trigger_get_condition(trigger); + if (!condition) { + ret = LTTNG_ERR_INVALID_TRIGGER; + goto end; + } + + if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) { + const struct lttng_event_rule *rule = NULL; + (void) lttng_condition_event_rule_get_rule(condition, &rule); + if (!rule) { + ret = LTTNG_ERR_INVALID_TRIGGER; + goto end; + } + if (lttng_event_rule_get_domain_type(rule) == LTTNG_DOMAIN_KERNEL) { + /* TODO: get the token value from the + * notification thread and only perform an + * enable and a disable.... This is NOT + * OPTIMIZED AT ALL + */ + ret = kernel_unregister_trigger(trigger); + } else { + /* TODO: get the token value from the + * notification thread and only perform an + * enable and a disable.... This is NOT + * OPTIMIZED AT ALL + */ + ust_app_global_update_all_tokens(); + if (lttng_event_rule_is_agent(rule)) { + struct agent *agt; + const char *pattern; + enum lttng_domain_type domain_type; + + domain_type = lttng_event_rule_get_domain_type( + rule); + (void) lttng_event_rule_tracepoint_get_pattern( + rule, &pattern); + + agt = trigger_find_agent(domain_type); + if (!agt) { + ret = LTTNG_ERR_UST_EVENT_NOT_FOUND; + goto end; + } + ret = trigger_agent_disable( + trigger, agt); + if (ret != LTTNG_OK) { + goto end; + } + } + } + } + } + end: lttng_trigger_destroy(trigger); lttng_payload_reset(&trigger_payload); return ret; } +int cmd_list_triggers(struct command_ctx *cmd_ctx, + struct notification_thread_handle *notification_thread, + struct lttng_triggers **return_triggers) +{ + int ret = 0; + enum lttng_error_code ret_code; + struct lttng_triggers *triggers = NULL; + + /* Get list of token trigger from the notification thread here */ + ret_code = notification_thread_command_list_triggers(notification_thread, cmd_ctx->creds.uid, &triggers); + if (ret_code != LTTNG_OK) { + ret = ret_code; + goto end; + } + + /* Return a "view" of the current triggers */ + *return_triggers = triggers; + triggers = NULL; + ret = LTTNG_OK; +end: + lttng_triggers_destroy(triggers); + return ret; +} /* * Send relayd sockets from snapshot output to consumer. Ignore request if the * snapshot output is *not* set with a remote destination. diff --git a/src/bin/lttng-sessiond/cmd.h b/src/bin/lttng-sessiond/cmd.h index 76c53ea33..15763b597 100644 --- a/src/bin/lttng-sessiond/cmd.h +++ b/src/bin/lttng-sessiond/cmd.h @@ -86,11 +86,11 @@ int cmd_add_context(struct ltt_session *session, enum lttng_domain_type domain, char *channel_name, const struct lttng_event_context *ctx, int kwpipe); int cmd_set_filter(struct ltt_session *session, enum lttng_domain_type domain, char *channel_name, struct lttng_event *event, - struct lttng_filter_bytecode *bytecode); + struct lttng_bytecode *bytecode); int cmd_enable_event(struct ltt_session *session, const struct lttng_domain *domain, char *channel_name, struct lttng_event *event, char *filter_expression, - struct lttng_filter_bytecode *filter, + struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, int wpipe); @@ -142,10 +142,15 @@ int cmd_regenerate_metadata(struct ltt_session *session); int cmd_regenerate_statedump(struct ltt_session *session); int cmd_register_trigger(struct command_ctx *cmd_ctx, int sock, - struct notification_thread_handle *notification_thread_handle); + struct notification_thread_handle *notification_thread_handle, + struct lttng_trigger **return_trigger); int cmd_unregister_trigger(struct command_ctx *cmd_ctx, int sock, struct notification_thread_handle *notification_thread_handle); +int cmd_list_triggers(struct command_ctx *cmd_ctx, + struct notification_thread_handle *notification_thread_handle, + struct lttng_triggers **return_triggers); + int cmd_rotate_session(struct ltt_session *session, struct lttng_rotate_session_return *rotate_return, bool quiet_rotation, diff --git a/src/bin/lttng-sessiond/condition-internal.c b/src/bin/lttng-sessiond/condition-internal.c new file mode 100644 index 000000000..43c214c2c --- /dev/null +++ b/src/bin/lttng-sessiond/condition-internal.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include "condition-internal.h" + +static +unsigned long lttng_condition_buffer_usage_hash( + const struct lttng_condition *_condition) +{ + unsigned long hash; + unsigned long condition_type; + struct lttng_condition_buffer_usage *condition; + + condition = container_of(_condition, + struct lttng_condition_buffer_usage, parent); + + condition_type = (unsigned long) condition->parent.type; + hash = hash_key_ulong((void *) condition_type, lttng_ht_seed); + if (condition->session_name) { + hash ^= hash_key_str(condition->session_name, lttng_ht_seed); + } + if (condition->channel_name) { + hash ^= hash_key_str(condition->channel_name, lttng_ht_seed); + } + if (condition->domain.set) { + hash ^= hash_key_ulong( + (void *) condition->domain.type, + lttng_ht_seed); + } + if (condition->threshold_ratio.set) { + uint64_t val; + + val = condition->threshold_ratio.value * (double) UINT32_MAX; + hash ^= hash_key_u64(&val, lttng_ht_seed); + } else if (condition->threshold_bytes.set) { + uint64_t val; + + val = condition->threshold_bytes.value; + hash ^= hash_key_u64(&val, lttng_ht_seed); + } + return hash; +} + +static +unsigned long lttng_condition_session_consumed_size_hash( + const struct lttng_condition *_condition) +{ + unsigned long hash; + unsigned long condition_type = + (unsigned long) LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE; + struct lttng_condition_session_consumed_size *condition; + uint64_t val; + + condition = container_of(_condition, + struct lttng_condition_session_consumed_size, parent); + + hash = hash_key_ulong((void *) condition_type, lttng_ht_seed); + if (condition->session_name) { + hash ^= hash_key_str(condition->session_name, lttng_ht_seed); + } + val = condition->consumed_threshold_bytes.value; + hash ^= hash_key_u64(&val, lttng_ht_seed); + return hash; +} + +static +unsigned long lttng_condition_session_rotation_hash( + const struct lttng_condition *_condition) +{ + unsigned long hash, condition_type; + struct lttng_condition_session_rotation *condition; + + condition = container_of(_condition, + struct lttng_condition_session_rotation, parent); + condition_type = (unsigned long) condition->parent.type; + hash = hash_key_ulong((void *) condition_type, lttng_ht_seed); + assert(condition->session_name); + hash ^= hash_key_str(condition->session_name, lttng_ht_seed); + return hash; +} + +static +unsigned long lttng_condition_event_rule_hash( + const struct lttng_condition *_condition) +{ + unsigned long hash, condition_type; + struct lttng_condition_event_rule *condition; + + condition = container_of(_condition, + struct lttng_condition_event_rule, parent); + condition_type = (unsigned long) condition->parent.type; + hash = hash_key_ulong((void *) condition_type, lttng_ht_seed); + + /* TODO: further hasg using the event rule? on pattern maybe?*/ + return hash; +} + +/* + * The lttng_condition hashing code is kept in this file (rather than + * condition.c) since it makes use of GPLv2 code (hashtable utils), which we + * don't want to link in liblttng-ctl. + */ +unsigned long lttng_condition_hash(const struct lttng_condition *condition) +{ + switch (condition->type) { + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + return lttng_condition_buffer_usage_hash(condition); + case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: + return lttng_condition_session_consumed_size_hash(condition); + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + return lttng_condition_session_rotation_hash(condition); + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + return lttng_condition_event_rule_hash(condition); + default: + //ERR("[notification-thread] Unexpected condition type caught"); + abort(); + } +} diff --git a/src/bin/lttng-sessiond/condition-internal.h b/src/bin/lttng-sessiond/condition-internal.h new file mode 100644 index 000000000..2863f7bd8 --- /dev/null +++ b/src/bin/lttng-sessiond/condition-internal.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_SESSIOND_CONDITION_INTERNAL_H +#define LTTNG_SESSIOND_CONDITION_INTERNAL_H + +#include + +/* + * The lttng_condition hashing code is kept in this file (rather than + * condition.c) since it makes use of GPLv2 code (hashtable utils), which we + * don't want to link in liblttng-ctl. + */ +unsigned long lttng_condition_hash(const struct lttng_condition *condition); + +#endif /* LTTNG_SESSIOND_CONDITION_INTERNAL_H */ diff --git a/src/bin/lttng-sessiond/consumer.c b/src/bin/lttng-sessiond/consumer.c index 4bd9d80ab..115a46e2e 100644 --- a/src/bin/lttng-sessiond/consumer.c +++ b/src/bin/lttng-sessiond/consumer.c @@ -951,8 +951,10 @@ void consumer_init_ask_channel_comm_msg(struct lttcomm_consumer_msg *msg, assert(chunk_status == LTTNG_TRACE_CHUNK_STATUS_OK); LTTNG_OPTIONAL_SET(&msg->u.ask_channel.chunk_id, chunk_id); } - msg->u.ask_channel.buffer_credentials.uid = buffer_credentials->uid; - msg->u.ask_channel.buffer_credentials.gid = buffer_credentials->gid; + msg->u.ask_channel.buffer_credentials.uid = + lttng_credentials_get_uid(buffer_credentials); + msg->u.ask_channel.buffer_credentials.gid = + lttng_credentials_get_gid(buffer_credentials); msg->cmd_type = LTTNG_CONSUMER_ASK_CHANNEL_CREATION; msg->u.ask_channel.subbuf_size = subbuf_size; @@ -1930,9 +1932,9 @@ int consumer_create_trace_chunk(struct consumer_socket *socket, assert(domain_dirfd >= 0); msg.u.create_trace_chunk.credentials.value.uid = - chunk_credentials.uid; + lttng_credentials_get_uid(&chunk_credentials); msg.u.create_trace_chunk.credentials.value.gid = - chunk_credentials.gid; + lttng_credentials_get_gid(&chunk_credentials); msg.u.create_trace_chunk.credentials.is_set = 1; } diff --git a/src/bin/lttng-sessiond/dispatch.c b/src/bin/lttng-sessiond/dispatch.c index 4fe3dfce7..461c56d64 100644 --- a/src/bin/lttng-sessiond/dispatch.c +++ b/src/bin/lttng-sessiond/dispatch.c @@ -36,6 +36,7 @@ static void update_ust_app(int app_sock) { struct ltt_session *sess, *stmp; const struct ltt_session_list *session_list = session_get_list(); + struct ust_app *app; /* Consumer is in an ERROR state. Stop any application update. */ if (uatomic_read(&ust_consumerd_state) == CONSUMER_ERROR) { @@ -43,10 +44,25 @@ static void update_ust_app(int app_sock) return; } + rcu_read_lock(); + assert(app_sock >= 0); + app = ust_app_find_by_sock(app_sock); + if (app == NULL) { + /* + * Application can be unregistered before so + * this is possible hence simply stopping the + * update. + */ + DBG3("UST app update failed to find app sock %d", + app_sock); + goto unlock_rcu; + } + + /* Update all tokens for the app */ + ust_app_global_update_tokens(app); + /* For all tracing session(s) */ cds_list_for_each_entry_safe(sess, stmp, &session_list->head, list) { - struct ust_app *app; - if (!session_get(sess)) { continue; } @@ -55,26 +71,15 @@ static void update_ust_app(int app_sock) goto unlock_session; } - rcu_read_lock(); - assert(app_sock >= 0); - app = ust_app_find_by_sock(app_sock); - if (app == NULL) { - /* - * Application can be unregistered before so - * this is possible hence simply stopping the - * update. - */ - DBG3("UST app update failed to find app sock %d", - app_sock); - goto unlock_rcu; - } ust_app_global_update(sess->ust_session, app); - unlock_rcu: - rcu_read_unlock(); unlock_session: session_unlock(sess); session_put(sess); } + +unlock_rcu: + rcu_read_unlock(); + } /* @@ -386,6 +391,8 @@ static void *thread_dispatch_ust_registration(void *data) /* Set app version. This call will print an error if needed. */ (void) ust_app_version(app); + (void) ust_app_setup_trigger_group(app); + /* Send notify socket through the notify pipe. */ ret = send_socket_to_thread( notifiers->apps_cmd_notify_pipe_write_fd, diff --git a/src/bin/lttng-sessiond/event.c b/src/bin/lttng-sessiond/event.c index 189236beb..28f296a03 100644 --- a/src/bin/lttng-sessiond/event.c +++ b/src/bin/lttng-sessiond/event.c @@ -12,6 +12,11 @@ #include #include +#include +#include +#include +#include +#include #include #include #include @@ -27,6 +32,7 @@ #include "trace-kernel.h" #include "trace-ust.h" #include "agent.h" +#include "utils.h" /* * Add unique UST event based on the event name, filter bytecode and loglevel. @@ -42,7 +48,7 @@ static void add_unique_ust_event(struct lttng_ht *ht, assert(event); key.name = event->attr.name; - key.filter = (struct lttng_filter_bytecode *) event->filter; + key.filter = (struct lttng_bytecode *) event->filter; key.loglevel_type = event->attr.loglevel_type; key.loglevel_value = event->attr.loglevel; key.exclusion = event->exclusion; @@ -99,7 +105,7 @@ int event_kernel_disable_event(struct ltt_kernel_channel *kchan, */ int event_kernel_enable_event(struct ltt_kernel_channel *kchan, struct lttng_event *event, char *filter_expression, - struct lttng_filter_bytecode *filter) + struct lttng_bytecode *filter) { int ret; struct ltt_kernel_event *kevent; @@ -149,7 +155,7 @@ end: int event_ust_enable_tracepoint(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct lttng_event *event, char *filter_expression, - struct lttng_filter_bytecode *filter, + struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, bool internal_event) { @@ -366,6 +372,20 @@ error: return ret; } +static void agent_enable_all(struct agent *agt) +{ + struct agent_event *aevent; + struct lttng_ht_iter iter; + + /* Flag every event that they are now enabled. */ + rcu_read_lock(); + cds_lfht_for_each_entry ( + agt->events->ht, &iter.iter, aevent, node.node) { + aevent->enabled = 1; + } + rcu_read_unlock(); +} + /* * Enable all agent event for a given UST session. * @@ -373,11 +393,9 @@ error: */ int event_agent_enable_all(struct ltt_ust_session *usess, struct agent *agt, struct lttng_event *event, - struct lttng_filter_bytecode *filter ,char *filter_expression) + struct lttng_bytecode *filter ,char *filter_expression) { int ret; - struct agent_event *aevent; - struct lttng_ht_iter iter; assert(usess); @@ -389,13 +407,7 @@ int event_agent_enable_all(struct ltt_ust_session *usess, goto error; } - /* Flag every event that they are now enabled. */ - rcu_read_lock(); - cds_lfht_for_each_entry(agt->events->ht, &iter.iter, aevent, - node.node) { - aevent->enabled = 1; - } - rcu_read_unlock(); + agent_enable_all(agt); ret = LTTNG_OK; @@ -410,7 +422,7 @@ error: * contexts yet. Not an issue for now, since they are not generated by * the lttng-ctl library. */ -static int add_filter_app_ctx(struct lttng_filter_bytecode *bytecode, +static int add_filter_app_ctx(struct lttng_bytecode *bytecode, const char *filter_expression, struct agent *agt) { int ret = LTTNG_OK; @@ -467,28 +479,17 @@ end: return ret; } -/* - * Enable a single agent event for a given UST session. - * - * Return LTTNG_OK on success or else a LTTNG_ERR* code. - */ -int event_agent_enable(struct ltt_ust_session *usess, - struct agent *agt, struct lttng_event *event, - struct lttng_filter_bytecode *filter, +static int agent_enable(struct agent *agt, + struct lttng_event *event, + struct lttng_bytecode *filter, char *filter_expression) { int ret, created = 0; struct agent_event *aevent; - assert(usess); assert(event); assert(agt); - DBG("Event agent enabling %s for session %" PRIu64 " with loglevel type %d " - ", loglevel %d and filter \"%s\"", event->name, - usess->id, event->loglevel_type, event->loglevel, - filter_expression ? filter_expression : "NULL"); - aevent = agent_find_event(event->name, event->loglevel_type, event->loglevel, filter_expression, agt); if (!aevent) { @@ -542,6 +543,112 @@ end: return ret; } +/* + * Enable a single agent event for a given UST session. + * + * Return LTTNG_OK on success or else a LTTNG_ERR* code. + */ +int event_agent_enable(struct ltt_ust_session *usess, + struct agent *agt, + struct lttng_event *event, + struct lttng_bytecode *filter, + char *filter_expression) +{ + assert(usess); + assert(event); + assert(agt); + + DBG("Event agent enabling %s for session %" PRIu64 + " with loglevel type %d " + ", loglevel %d and filter \"%s\"", + event->name, usess->id, event->loglevel_type, + event->loglevel, + filter_expression ? filter_expression : "NULL"); + + return agent_enable(agt, event, filter, filter_expression); +} + +/* + * Enable a single agent event for a trigger. + * + * Return LTTNG_OK on success or else a LTTNG_ERR* code. + */ +int trigger_agent_enable(const struct lttng_trigger *trigger, struct agent *agt) +{ + int ret; + enum lttng_condition_status c_status; + enum lttng_domain_type d_type; + const struct lttng_condition *condition; + const struct lttng_event_rule *rule; + const char *filter_expression; + char *filter_expression_copy = NULL; + const struct lttng_bytecode *filter_bytecode; + struct lttng_bytecode *filter_bytecode_copy = NULL; + struct lttng_event *event = NULL; + + assert(trigger); + assert(agt); + + condition = lttng_trigger_get_const_condition(trigger); + + assert(lttng_condition_get_type(condition) == + LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + + c_status = lttng_condition_event_rule_get_rule(condition, &rule); + assert(c_status == LTTNG_CONDITION_STATUS_OK); + + assert(lttng_event_rule_get_type(rule) == + LTTNG_EVENT_RULE_TYPE_TRACEPOINT); + + d_type = lttng_event_rule_get_domain_type(rule); + assert(d_type == agt->domain); + + event = lttng_event_rule_generate_lttng_event(rule); + if (!event) { + ret = LTTNG_ERR_NOMEM; + goto end; + } + + /* Get the internal filter_expression and bytecode */ + filter_expression = lttng_event_rule_get_filter(rule); + if (filter_expression) { + filter_expression_copy = strdup(filter_expression); + if (!filter_expression_copy) { + ret = LTTNG_ERR_NOMEM; + goto end; + } + + /* Get the filter bytecode */ + filter_bytecode = lttng_event_rule_get_filter_bytecode(rule); + if (filter_bytecode) { + filter_bytecode_copy = bytecode_copy (filter_bytecode); + if (!filter_bytecode_copy) { + ret = LTTNG_ERR_NOMEM; + goto end; + } + } + } + + DBG("Event agent enabling %s for trigger %" PRIu64 + " with loglevel type %d " + ", loglevel %d and filter \"%s\"", + event->name, lttng_trigger_get_tracer_token(trigger), + event->loglevel_type, event->loglevel, + filter_expression ? filter_expression : "NULL"); + + ret = agent_enable(agt, event, filter_bytecode_copy, + filter_expression_copy); + /* Ownership was passed even in case of error */ + filter_expression_copy = NULL; + filter_bytecode_copy = NULL; + +end: + free(filter_expression_copy); + free(filter_bytecode_copy); + free(event); + return ret; +} + /* * Return the default event name associated with the provided UST domain. Return * NULL on error. @@ -567,6 +674,43 @@ const char *event_get_default_agent_ust_name(enum lttng_domain_type domain) return default_event_name; } +static int trigger_agent_disable_one(const struct lttng_trigger *trigger, + struct agent *agt, + struct agent_event *aevent) + +{ + int ret; + + assert(agt); + assert(trigger); + assert(aevent); + + /* + * Actual ust event un-registration happens on the trigger + * un-registration at that point. + */ + + DBG("Event agent disabling %s (loglevel type %d, loglevel value %d) for trigger %" PRIu64, + aevent->name, aevent->loglevel_type, + aevent->loglevel_value, lttng_trigger_get_tracer_token(trigger)); + + /* Already disabled? */ + if (!aevent->enabled) { + goto end; + } + + ret = agent_disable_event(aevent, agt->domain); + if (ret != LTTNG_OK) { + goto error; + } + +end: + return LTTNG_OK; + +error: + return ret; +} + /* * Disable a given agent event for a given UST session. * @@ -659,6 +803,44 @@ error: return ret; } +/* + * Disable agent event matching a given trigger. + * + * Return LTTNG_OK on success or else a LTTNG_ERR* code. + */ +int trigger_agent_disable( + const struct lttng_trigger *trigger, struct agent *agt) +{ + int ret = LTTNG_OK; + struct agent_event *aevent; + + assert(trigger); + assert(agt); + + DBG("Event agent disabling for trigger %" PRIu64, + lttng_trigger_get_tracer_token(trigger)); + + rcu_read_lock(); + aevent = agent_find_event_by_trigger(trigger, agt); + + if (aevent == NULL) { + DBG2("Event agent NOT found by trigger %" PRIu64, + lttng_trigger_get_tracer_token(trigger)); + ret = LTTNG_ERR_UST_EVENT_NOT_FOUND; + goto end; + } + + ret = trigger_agent_disable_one(trigger, agt, aevent); + + if (ret != LTTNG_OK) { + goto end; + } + +end: + rcu_read_unlock(); + return ret; +} + /* * Disable all agent events matching a given name for a given UST session. * diff --git a/src/bin/lttng-sessiond/event.h b/src/bin/lttng-sessiond/event.h index 1c646db37..8849ee7cc 100644 --- a/src/bin/lttng-sessiond/event.h +++ b/src/bin/lttng-sessiond/event.h @@ -17,12 +17,12 @@ int event_kernel_disable_event(struct ltt_kernel_channel *kchan, int event_kernel_enable_event(struct ltt_kernel_channel *kchan, struct lttng_event *event, char *filter_expression, - struct lttng_filter_bytecode *filter); + struct lttng_bytecode *filter); int event_ust_enable_tracepoint(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct lttng_event *event, char *filter_expression, - struct lttng_filter_bytecode *filter, + struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, bool internal_event); int event_ust_disable_tracepoint(struct ltt_ust_session *usess, @@ -32,16 +32,21 @@ int event_ust_disable_all_tracepoints(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan); int event_agent_enable(struct ltt_ust_session *usess, struct agent *agt, - struct lttng_event *event, struct lttng_filter_bytecode *filter, + struct lttng_event *event, struct lttng_bytecode *filter, char *filter_expression); int event_agent_enable_all(struct ltt_ust_session *usess, struct agent *agt, - struct lttng_event *event, struct lttng_filter_bytecode *filter, + struct lttng_event *event, struct lttng_bytecode *filter, char *filter_expression); int event_agent_disable(struct ltt_ust_session *usess, struct agent *agt, const char *event_name); int event_agent_disable_all(struct ltt_ust_session *usess, struct agent *agt); +int trigger_agent_enable( + const struct lttng_trigger *trigger, struct agent *agt); +int trigger_agent_disable( + const struct lttng_trigger *trigger, struct agent *agt); + const char *event_get_default_agent_ust_name(enum lttng_domain_type domain); #endif /* _LTT_EVENT_H */ diff --git a/src/bin/lttng-sessiond/globals.c b/src/bin/lttng-sessiond/globals.c index 20aa790a7..8681d5d90 100644 --- a/src/bin/lttng-sessiond/globals.c +++ b/src/bin/lttng-sessiond/globals.c @@ -20,8 +20,10 @@ long page_size; struct health_app *health_sessiond; struct notification_thread_handle *notification_thread_handle; +pthread_mutex_t notification_trigger_tokens_ht_lock = PTHREAD_MUTEX_INITIALIZER; struct lttng_ht *agent_apps_ht_by_sock = NULL; +struct lttng_ht *trigger_agents_ht_by_domain = NULL; struct lttng_kernel_tracer_version kernel_tracer_version; struct lttng_kernel_tracer_abi_version kernel_tracer_abi_version; diff --git a/src/bin/lttng-sessiond/kernel.c b/src/bin/lttng-sessiond/kernel.c index d7c7984d2..27b72a5af 100644 --- a/src/bin/lttng-sessiond/kernel.c +++ b/src/bin/lttng-sessiond/kernel.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -25,16 +26,28 @@ #include #include +#include +#include +#include +#include +#include +#include +#include + #include "lttng-sessiond.h" #include "lttng-syscall.h" +#include "condition-internal.h" #include "consumer.h" #include "kernel.h" #include "kernel-consumer.h" #include "kern-modules.h" +#include "sessiond-config.h" #include "utils.h" #include "rotate.h" #include "modprobe.h" #include "tracker.h" +#include "trigger-error-accounting.h" +#include "notification-thread-commands.h" /* * Key used to reference a channel between the sessiond and the consumer. This @@ -45,9 +58,10 @@ static uint64_t next_kernel_channel_key; static const char *module_proc_lttng = "/proc/lttng"; static int kernel_tracer_fd = -1; +static int kernel_tracer_trigger_group_fd = -1; +static int kernel_tracer_trigger_group_notification_fd = -1; +static struct cds_lfht *kernel_tracer_token_ht; -#include -#include /* * Add context on a kernel channel. * @@ -219,6 +233,44 @@ error: return -1; } +/* + * Create a kernel channel, register it to the kernel tracer and add it to the + * kernel session. + */ +static +int kernel_create_trigger_group(int *trigger_group_fd) +{ + int ret; + int local_fd = -1; + + assert(trigger_group_fd); + + /* Kernel tracer channel creation */ + ret = kernctl_create_trigger_group(kernel_tracer_fd); + if (ret < 0) { + PERROR("ioctl kernel create trigger group"); + ret = -1; + goto error; + } + + /* Store locally */ + local_fd = ret; + + /* Prevent fd duplication after execlp() */ + ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("fcntl session fd"); + } + + DBG("Kernel trigger group created (fd: %d)", + local_fd); + ret = 0; + +error: + *trigger_group_fd = local_fd; + return ret; +} + /* * Compute the offset of the instrumentation byte in the binary based on the * function probe location using the ELF lookup method. @@ -230,7 +282,7 @@ error: static int extract_userspace_probe_offset_function_elf( const struct lttng_userspace_probe_location *probe_location, - struct ltt_kernel_session *session, uint64_t *offset) + uid_t uid, gid_t gid, uint64_t *offset) { int fd; int ret = 0; @@ -267,8 +319,7 @@ int extract_userspace_probe_offset_function_elf( goto end; } - ret = run_as_extract_elf_symbol_offset(fd, symbol, session->uid, - session->gid, offset); + ret = run_as_extract_elf_symbol_offset(fd, symbol, uid, gid, offset); if (ret < 0) { DBG("userspace probe offset calculation failed for " "function %s", symbol); @@ -292,7 +343,7 @@ end: static int extract_userspace_probe_offset_tracepoint_sdt( const struct lttng_userspace_probe_location *probe_location, - struct ltt_kernel_session *session, uint64_t **offsets, + uid_t uid, gid_t gid, uint64_t **offsets, uint32_t *offsets_count) { enum lttng_userspace_probe_location_lookup_method_type lookup_method_type; @@ -338,7 +389,7 @@ int extract_userspace_probe_offset_tracepoint_sdt( } ret = run_as_extract_sdt_probe_offsets(fd, provider_name, probe_name, - session->uid, session->gid, offsets, offsets_count); + uid, gid, offsets, offsets_count); if (ret < 0) { DBG("userspace probe offset calculation failed for sdt " "probe %s:%s", provider_name, probe_name); @@ -359,29 +410,16 @@ end: return ret; } -/* - * Extract the offsets of the instrumentation point for the different lookup - * methods. - */ static -int userspace_probe_add_callsites(struct lttng_event *ev, - struct ltt_kernel_session *session, int fd) +int userspace_probe_add_callsite( + const struct lttng_userspace_probe_location *location, + uid_t uid, gid_t gid, int fd) { const struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL; enum lttng_userspace_probe_location_lookup_method_type type; - const struct lttng_userspace_probe_location *location = NULL; int ret; - assert(ev); - assert(ev->type == LTTNG_EVENT_USERSPACE_PROBE); - - location = lttng_event_get_userspace_probe_location(ev); - if (!location) { - ret = -1; - goto end; - } - lookup_method = - lttng_userspace_probe_location_get_lookup_method(location); + lookup_method = lttng_userspace_probe_location_get_lookup_method(location); if (!lookup_method) { ret = -1; goto end; @@ -394,7 +432,8 @@ int userspace_probe_add_callsites(struct lttng_event *ev, struct lttng_kernel_event_callsite callsite; uint64_t offset; - ret = extract_userspace_probe_offset_function_elf(location, session, &offset); + ret = extract_userspace_probe_offset_function_elf(location, + uid, gid, &offset); if (ret) { ret = LTTNG_ERR_PROBE_LOCATION_INVAL; goto end; @@ -403,8 +442,7 @@ int userspace_probe_add_callsites(struct lttng_event *ev, callsite.u.uprobe.offset = offset; ret = kernctl_add_callsite(fd, &callsite); if (ret) { - WARN("Adding callsite to userspace probe " - "event %s failed.", ev->name); + WARN("Adding callsite to ELF userspace probe failed."); ret = LTTNG_ERR_KERN_ENABLE_FAIL; goto end; } @@ -421,8 +459,8 @@ int userspace_probe_add_callsites(struct lttng_event *ev, * This call allocates the offsets buffer. This buffer must be freed * by the caller */ - ret = extract_userspace_probe_offset_tracepoint_sdt(location, session, - &offsets, &offsets_count); + ret = extract_userspace_probe_offset_tracepoint_sdt(location, + uid, gid, &offsets, &offsets_count); if (ret) { ret = LTTNG_ERR_PROBE_LOCATION_INVAL; goto end; @@ -431,8 +469,8 @@ int userspace_probe_add_callsites(struct lttng_event *ev, callsite.u.uprobe.offset = offsets[i]; ret = kernctl_add_callsite(fd, &callsite); if (ret) { - WARN("Adding callsite to userspace probe " - "event %s failed.", ev->name); + WARN("Adding callsite to SDT userspace probe " + "failed."); ret = LTTNG_ERR_KERN_ENABLE_FAIL; free(offsets); goto end; @@ -449,6 +487,71 @@ end: return ret; } +/* + * Extract the offsets of the instrumentation point for the different lookup + * methods. + */ +static +int userspace_probe_event_add_callsites(struct lttng_event *ev, + struct ltt_kernel_session *session, int fd) +{ + const struct lttng_userspace_probe_location *location = NULL; + int ret; + + assert(ev); + assert(ev->type == LTTNG_EVENT_USERSPACE_PROBE); + + location = lttng_event_get_userspace_probe_location(ev); + if (!location) { + ret = -1; + goto end; + } + + ret = userspace_probe_add_callsite(location, session->uid, session->gid, + fd); + if (ret) { + WARN("Adding callsite to userspace probe event \"%s\" " + "failed.", ev->name); + } + +end: + return ret; +} + +/* + * Extract the offsets of the instrumentation point for the different lookup + * methods. + */ +static int userspace_probe_event_rule_add_callsites( + const struct lttng_event_rule *rule, + const struct lttng_credentials *creds, + int fd) +{ + const struct lttng_userspace_probe_location *location = NULL; + enum lttng_event_rule_status status; + int ret; + + assert(rule); + assert(creds); + assert(lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_UPROBE); + + status = lttng_event_rule_uprobe_get_location(rule, &location); + if (status != LTTNG_EVENT_RULE_STATUS_OK || !location) { + ret = -1; + goto end; + } + + ret = userspace_probe_add_callsite( + location, lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds), fd); + if (ret) { + WARN("Adding callsite to userspace probe object %d" + "failed.", fd); + } + +end: + return ret; +} + /* * Create a kernel event, enable it to the kernel tracer and add it to the * channel event list of the kernel session. @@ -457,7 +560,7 @@ end: int kernel_create_event(struct lttng_event *ev, struct ltt_kernel_channel *channel, char *filter_expression, - struct lttng_filter_bytecode *filter) + struct lttng_bytecode *filter) { int err, fd; enum lttng_error_code ret; @@ -518,7 +621,8 @@ int kernel_create_event(struct lttng_event *ev, } if (ev->type == LTTNG_EVENT_USERSPACE_PROBE) { - ret = userspace_probe_add_callsites(ev, channel->session, event->fd); + ret = userspace_probe_event_add_callsites(ev, channel->session, + event->fd); if (ret) { goto add_callsite_error; } @@ -675,6 +779,42 @@ error: return ret; } +/* + * Disable a kernel trigger. + */ +static +int kernel_disable_token_event_rule(struct ltt_kernel_token_event_rule *event) +{ + int ret; + + assert(event); + + rcu_read_lock(); + cds_lfht_del(kernel_tracer_token_ht, &event->ht_node); + rcu_read_unlock(); + + ret = kernctl_disable(event->fd); + if (ret < 0) { + switch (-ret) { + case EEXIST: + ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + default: + PERROR("disable kernel event"); + break; + } + goto error; + } + + event->enabled = 0; + DBG("Kernel trigger token %" PRIu64" disabled (fd: %d)", event->token, event->fd); + + return 0; + +error: + return ret; +} + static struct process_attr_tracker *_kernel_get_process_attr_tracker( struct ltt_kernel_session *session, @@ -1796,6 +1936,7 @@ LTTNG_HIDDEN int init_kernel_tracer(void) { int ret; + enum lttng_error_code error_code_ret; bool is_root = !getuid(); /* Modprobe lttng kernel modules */ @@ -1827,20 +1968,48 @@ int init_kernel_tracer(void) if (ret < 0) { goto error_modules; } - if (ret < 1) { WARN("Kernel tracer does not support buffer monitoring. " "The monitoring timer of channels in the kernel domain " "will be set to 0 (disabled)."); } + ret = kernel_create_trigger_group(&kernel_tracer_trigger_group_fd); + if (ret < 0) { + /* TODO: error handling if it is not supported etc. */ + WARN("Failed trigger group creation"); + kernel_tracer_trigger_group_fd = -1; + /* This is not fatal */ + } else { + + error_code_ret = kernel_create_trigger_group_notification_fd( + &kernel_tracer_trigger_group_notification_fd); + if (error_code_ret != LTTNG_OK) { + goto error_modules; + } + + trigger_error_accounting_register_kernel(kernel_tracer_trigger_group_fd); + if (ret < 0) { + goto error_modules; + } + } + + kernel_tracer_token_ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0, + CDS_LFHT_AUTO_RESIZE|CDS_LFHT_ACCOUNTING, NULL); + if (!kernel_tracer_token_ht) { + goto error_modules; + } + DBG("Kernel tracer fd %d", kernel_tracer_fd); + DBG("Kernel tracer trigger group fd %d", kernel_tracer_trigger_group_fd); + DBG("Kernel tracer trigger group notification fd %d", kernel_tracer_trigger_group_notification_fd); ret = syscall_init_table(kernel_tracer_fd); if (ret < 0) { ERR("Unable to populate syscall table. Syscall tracing won't " "work for this session daemon."); } + return 0; error_version: @@ -1875,6 +2044,40 @@ LTTNG_HIDDEN void cleanup_kernel_tracer(void) { int ret; + struct cds_lfht_iter iter; + + struct ltt_kernel_token_event_rule *rule = NULL; + cds_lfht_for_each_entry(kernel_tracer_token_ht, &iter, rule, ht_node) { + kernel_disable_token_event_rule(rule); + trace_kernel_destroy_token_event_rule(rule); + cds_lfht_del(kernel_tracer_token_ht, &rule->ht_node); + } + + DBG2("Closing kernel trigger group notification fd"); + if (kernel_tracer_trigger_group_notification_fd >= 0) { + + ret = notification_thread_command_remove_application( + notification_thread_handle, + kernel_tracer_trigger_group_notification_fd); + if (ret != LTTNG_OK) { + ERR("Failed to remove kernel trigger notification from notification thread"); + } + ret = close(kernel_tracer_trigger_group_notification_fd); + if (ret) { + PERROR("close"); + } + kernel_tracer_trigger_group_notification_fd = -1; + } + + /* TODO: do we iterate over the list to remove all token? */ + DBG2("Closing kernel trigger group fd"); + if (kernel_tracer_trigger_group_fd >= 0) { + ret = close(kernel_tracer_trigger_group_fd); + if (ret) { + PERROR("close"); + } + kernel_tracer_trigger_group_fd = -1; + } DBG2("Closing kernel fd"); if (kernel_tracer_fd >= 0) { @@ -1884,6 +2087,7 @@ void cleanup_kernel_tracer(void) } kernel_tracer_fd = -1; } + DBG("Unloading kernel modules"); modprobe_remove_lttng_all(); free(syscall_table); @@ -1974,3 +2178,322 @@ end: rcu_read_unlock(); return status; } + +enum lttng_error_code kernel_create_trigger_group_notification_fd( + int *trigger_group_notification_fd) +{ + enum lttng_error_code error_code_ret; + int local_fd = -1, ret; + + assert(trigger_group_notification_fd); + + ret = kernctl_create_trigger_group_notification_fd(kernel_tracer_trigger_group_fd); + if (ret < 0) { + PERROR("ioctl kernel create trigger group"); + error_code_ret = LTTNG_ERR_TRIGGER_GROUP_NOTIFICATION_FD; + goto error; + } + + /* Store locally */ + local_fd = ret; + + /* Prevent fd duplication after execlp() */ + ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("fcntl session fd"); + error_code_ret = LTTNG_ERR_TRIGGER_GROUP_NOTIFICATION_FD; + goto error; + } + + DBG("Kernel trigger group notification created (fd: %d)", + local_fd); + error_code_ret = LTTNG_OK; + *trigger_group_notification_fd = local_fd; + +error: + return error_code_ret; +} + +enum lttng_error_code kernel_create_trigger_group_error_counter( + int *error_counter_fd, size_t nb_bucket) +{ + enum lttng_error_code error_code_ret; + int local_fd = -1, ret; + struct lttng_kernel_counter_conf error_counter_conf; + + assert(error_counter_fd); + + error_counter_conf.arithmetic = LTTNG_KERNEL_COUNTER_ARITHMETIC_MODULAR; + error_counter_conf.bitness = LTTNG_KERNEL_COUNTER_BITNESS_64BITS; + error_counter_conf.global_sum_step = 0; + error_counter_conf.number_dimensions = 1; + error_counter_conf.dimensions[0].size = nb_bucket; + error_counter_conf.dimensions[0].has_underflow = false; + error_counter_conf.dimensions[0].has_overflow = false; + + ret = kernctl_create_trigger_group_error_counter( + kernel_tracer_trigger_group_fd, &error_counter_conf); + if (ret < 0) { + PERROR("ioctl kernel create trigger group error counter"); + error_code_ret = LTTNG_ERR_TRIGGER_GROUP_ERROR_COUNTER; + goto error; + } + + /* Store locally */ + local_fd = ret; + + /* Prevent fd duplication after execlp() */ + ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + error_code_ret = LTTNG_ERR_TRIGGER_GROUP_ERROR_COUNTER; + PERROR("fcntl session fd"); + } + + DBG("Kernel trigger group error counter (fd: %d)", + local_fd); + error_code_ret = LTTNG_OK; + +error: + *error_counter_fd = local_fd; + return error_code_ret; +} + +enum lttng_error_code kernel_destroy_trigger_group_notification_fd( + int trigger_group_notification_fd) +{ + enum lttng_error_code ret = LTTNG_OK; + DBG("Closing trigger group notification fd %d", trigger_group_notification_fd); + if (trigger_group_notification_fd >= 0) { + ret = close(trigger_group_notification_fd); + if (ret) { + PERROR("close"); + } + } + return ret; +} + +static +unsigned long hash_trigger(struct lttng_trigger *trigger) +{ + const struct lttng_condition *condition = + lttng_trigger_get_const_condition(trigger); + return lttng_condition_hash(condition); +} + +static +int match_trigger(struct cds_lfht_node *node, const void *key) +{ + struct ltt_kernel_token_event_rule *token; + const struct lttng_trigger *trigger = key; + + token = caa_container_of(node, struct ltt_kernel_token_event_rule, ht_node); + + return lttng_trigger_is_equal(trigger, token->trigger); +} + +static enum lttng_error_code kernel_create_token_event_rule(struct lttng_trigger *trigger, + const struct lttng_credentials *creds, uint64_t token) +{ + int err, fd, ret = 0; + enum lttng_error_code error_code_ret; + struct ltt_kernel_token_event_rule *event; + struct lttng_kernel_trigger kernel_trigger = {}; + unsigned int capture_bytecode_count = 0; + struct lttng_condition *condition = NULL; + struct lttng_event_rule *event_rule = NULL; + + assert(trigger); + + condition = lttng_trigger_get_condition(trigger); + assert(condition); + assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + + lttng_condition_event_rule_get_rule_mutable(condition, &event_rule); + assert(event_rule); + assert(lttng_event_rule_get_type(event_rule) != LTTNG_EVENT_RULE_TYPE_UNKNOWN); + + error_code_ret = trace_kernel_create_token_event_rule(trigger, token, + lttng_trigger_get_error_counter_index(trigger), &event); + if (error_code_ret != LTTNG_OK) { + goto error; + } + + trace_kernel_init_trigger_from_event_rule(event_rule, &kernel_trigger); + kernel_trigger.id = event->token; + kernel_trigger.error_counter_idx = lttng_trigger_get_error_counter_index(trigger); + + fd = kernctl_create_trigger(kernel_tracer_trigger_group_fd, &kernel_trigger); + if (fd < 0) { + switch (-fd) { + case EEXIST: + error_code_ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + case ENOSYS: + WARN("Trigger type not implemented"); + error_code_ret = LTTNG_ERR_KERN_EVENT_ENOSYS; + break; + case ENOENT: + WARN("Event %s not found!", kernel_trigger.name); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + default: + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + PERROR("create trigger ioctl"); + } + goto free_event; + } + + event->fd = fd; + /* Prevent fd duplication after execlp() */ + err = fcntl(event->fd, F_SETFD, FD_CLOEXEC); + if (err < 0) { + PERROR("fcntl session fd"); + } + + if (event->filter) { + err = kernctl_filter(event->fd, event->filter); + if (err < 0) { + switch (-err) { + case ENOMEM: + error_code_ret = LTTNG_ERR_FILTER_NOMEM; + break; + default: + error_code_ret = LTTNG_ERR_FILTER_INVAL; + break; + } + goto filter_error; + } + } + + if (lttng_event_rule_get_type(event_rule) == + LTTNG_EVENT_RULE_TYPE_UPROBE) { + ret = userspace_probe_event_rule_add_callsites( + event_rule, creds, event->fd); + if (ret) { + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + goto add_callsite_error; + } + } + + /* Set the capture bytecode */ + capture_bytecode_count = lttng_trigger_get_capture_bytecode_count(trigger); + for (unsigned int i = 0; i < capture_bytecode_count; i++) { + const struct lttng_bytecode *capture_bytecode = lttng_trigger_get_capture_bytecode_at_index(trigger, i); + ret = kernctl_capture(event->fd, capture_bytecode); + if (ret < 0) { + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + goto error; + } + } + + err = kernctl_enable(event->fd); + if (err < 0) { + switch (-err) { + case EEXIST: + error_code_ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + default: + PERROR("enable kernel trigger"); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + } + goto enable_error; + } + + /* Add trigger to kernel token mapping in the hashtable. */ + cds_lfht_add(kernel_tracer_token_ht, hash_trigger(trigger), + &event->ht_node); + + DBG("Trigger %s created (fd: %d)", kernel_trigger.name, event->fd); + + return LTTNG_OK; + +add_callsite_error: +enable_error: +filter_error: + { + int closeret; + + closeret = close(event->fd); + if (closeret) { + PERROR("close event fd"); + } + } +free_event: + free(event); +error: + return error_code_ret; +} + +enum lttng_error_code kernel_register_trigger(struct lttng_trigger *trigger, const struct lttng_credentials *cmd_creds) +{ + enum lttng_error_code ret; + struct lttng_condition *condition; + struct lttng_event_rule *event_rule; + uint64_t token; + + /* TODO error handling */ + + rcu_read_lock(); + pthread_mutex_lock(¬ification_trigger_tokens_ht_lock); + + /* TODO: error checking and type checking */ + token = lttng_trigger_get_tracer_token(trigger); + condition = lttng_trigger_get_condition(trigger); + (void) lttng_condition_event_rule_get_rule_mutable(condition, &event_rule); + + assert(lttng_event_rule_get_domain_type(event_rule) == LTTNG_DOMAIN_KERNEL); + + ret = kernel_create_token_event_rule(trigger, cmd_creds, token); + if (ret != LTTNG_OK) { + ERR("Failed to create kernel trigger token."); + } + + rcu_read_unlock(); + pthread_mutex_unlock(¬ification_trigger_tokens_ht_lock); + return ret; +} + +enum lttng_error_code kernel_unregister_trigger(struct lttng_trigger *trigger) +{ + struct ltt_kernel_token_event_rule *token_event_rule_element; + struct cds_lfht_node *node; + struct cds_lfht_iter iter; + enum lttng_error_code error_code_ret; + int ret; + + rcu_read_lock(); + pthread_mutex_unlock(¬ification_trigger_tokens_ht_lock); + + cds_lfht_lookup(kernel_tracer_token_ht, hash_trigger(trigger), + match_trigger, trigger, &iter); + node = cds_lfht_iter_get_node(&iter); + if (!node) { + error_code_ret = LTTNG_ERR_TRIGGER_NOT_FOUND; + goto error; + } + + token_event_rule_element = caa_container_of(node, + struct ltt_kernel_token_event_rule, ht_node); + + ret = kernel_disable_token_event_rule(token_event_rule_element); + if (ret) { + error_code_ret = LTTNG_ERR_FATAL; + goto error; + } + + trace_kernel_destroy_token_event_rule(token_event_rule_element); + + rcu_read_unlock(); + pthread_mutex_unlock(¬ification_trigger_tokens_ht_lock); + + error_code_ret = LTTNG_OK; + +error: + return error_code_ret; +} + +int kernel_get_notification_fd(void) +{ + return kernel_tracer_trigger_group_notification_fd; +} diff --git a/src/bin/lttng-sessiond/kernel.h b/src/bin/lttng-sessiond/kernel.h index 53d480313..c9b17d260 100644 --- a/src/bin/lttng-sessiond/kernel.h +++ b/src/bin/lttng-sessiond/kernel.h @@ -29,7 +29,7 @@ int kernel_create_session(struct ltt_session *session); int kernel_create_channel(struct ltt_kernel_session *session, struct lttng_channel *chan); int kernel_create_event(struct lttng_event *ev, struct ltt_kernel_channel *channel, - char *filter_expression, struct lttng_filter_bytecode *filter); + char *filter_expression, struct lttng_bytecode *filter); int kernel_disable_channel(struct ltt_kernel_channel *chan); int kernel_disable_event(struct ltt_kernel_event *event); int kernel_enable_event(struct ltt_kernel_event *event); @@ -81,4 +81,18 @@ bool kernel_tracer_is_initialized(void); enum lttng_error_code kernel_create_channel_subdirectories( const struct ltt_kernel_session *ksess); +enum lttng_error_code kernel_create_trigger_group_notification_fd( + int *trigger_group_notification_fd); +enum lttng_error_code kernel_destroy_trigger_group_notification_fd( + int trigger_group_notification_fd); + +enum lttng_error_code kernel_register_trigger(struct lttng_trigger *trigger, const struct lttng_credentials *cmd_creds); +enum lttng_error_code kernel_unregister_trigger(struct lttng_trigger *trigger); +enum lttng_error_code kernel_create_trigger_group_error_counter( + int *trigger_group_error_counter_fd, size_t nb_bucket); +enum lttng_error_code kernel_trigger_update_error_count( + struct lttng_trigger *trigger); + +int kernel_get_notification_fd(void); + #endif /* _LTT_KERNEL_CTL_H */ diff --git a/src/bin/lttng-sessiond/lttng-sessiond.h b/src/bin/lttng-sessiond/lttng-sessiond.h index edc1bc115..9d0b9abd5 100644 --- a/src/bin/lttng-sessiond/lttng-sessiond.h +++ b/src/bin/lttng-sessiond/lttng-sessiond.h @@ -71,6 +71,7 @@ extern struct lttng_kernel_tracer_abi_version kernel_tracer_abi_version; /* Notification thread handle. */ extern struct notification_thread_handle *notification_thread_handle; +extern pthread_mutex_t notification_trigger_tokens_ht_lock; /* * This contains extra data needed for processing a command received by the diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index 4bdac5217..efb2117cb 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -51,7 +51,6 @@ #include "event.h" #include "kernel.h" #include "kernel-consumer.h" -#include "shm.h" #include "lttng-ust-ctl.h" #include "ust-consumer.h" #include "utils.h" @@ -74,6 +73,7 @@ #include "register.h" #include "manage-apps.h" #include "manage-kernel.h" +#include "trigger-error-accounting.h" static const char *help_msg = #ifdef LTTNG_EMBED_HELP @@ -83,6 +83,8 @@ NULL #endif ; +#define TRIGGER_ERROR_COUNTER_NUMBER_OF_BUCKET_MAX 65535 + const char *progname; static int lockfile_fd = -1; static int opt_print_version; @@ -120,6 +122,7 @@ static const struct option long_options[] = { { "load", required_argument, 0, 'l' }, { "kmod-probes", required_argument, 0, '\0' }, { "extra-kmod-probes", required_argument, 0, '\0' }, + { "trigger-error-number-of-bucket", required_argument, 0, '\0' }, { NULL, 0, 0, 0 } }; @@ -314,6 +317,9 @@ static void sessiond_cleanup(void) pthread_mutex_destroy(&session_list->lock); + DBG("Cleaning up all trigger agents"); + trigger_agent_ht_clean(); + DBG("Cleaning up all agent apps"); agent_app_ht_clean(); DBG("Closing all UST sockets"); @@ -694,6 +700,23 @@ static int set_option(int opt, const char *arg, const char *optname) ret = -ENOMEM; } } + } else if (string_match(optname, "trigger-error-number-of-bucket")) { + unsigned long v; + + errno = 0; + v = strtoul(arg, NULL, 0); + if (errno != 0 || !isdigit(arg[0])) { + ERR("Wrong value in --trigger-error-number-of-bucket parameter: %s", arg); + return -1; + } + if (v == 0 || v >= TRIGGER_ERROR_COUNTER_NUMBER_OF_BUCKET_MAX) { + ERR("Value out of range for --trigger-error-number-of-bucket parameter: %s", arg); + return -1; + } + config.trigger_error_counter_bucket = (int) v; + DBG3("Number of error counter set to non default: %i", + config.trigger_error_counter_bucket); + goto end; } else if (string_match(optname, "config") || opt == 'f') { /* This is handled in set_options() thus silent skip. */ goto end; @@ -1529,6 +1552,8 @@ int main(int argc, char **argv) goto stop_threads; } + trigger_error_accounting_init(config.trigger_error_counter_bucket); + /* * Initialize agent app hash table. We allocate the hash table here * since cleanup() can get called after this point. @@ -1539,6 +1564,11 @@ int main(int argc, char **argv) goto stop_threads; } + if (trigger_agent_ht_alloc()) { + ERR("Failed to allocate trigger agent hash table"); + retval = -1; + goto stop_threads; + } /* * These actions must be executed as root. We do that *after* setting up * the sockets path because we MUST make the check for another daemon using @@ -1636,7 +1666,8 @@ int main(int argc, char **argv) notification_thread_handle = notification_thread_handle_create( ust32_channel_monitor_pipe, ust64_channel_monitor_pipe, - kernel_channel_monitor_pipe); + kernel_channel_monitor_pipe, + kernel_get_notification_fd()); if (!notification_thread_handle) { retval = -1; ERR("Failed to create notification thread shared data"); @@ -1726,6 +1757,16 @@ int main(int argc, char **argv) retval = -1; goto stop_threads; } + + if (kernel_get_notification_fd() > -1) { + ret = notification_thread_command_add_application( + notification_thread_handle, kernel_get_notification_fd(), LTTNG_DOMAIN_KERNEL); + if (ret != LTTNG_OK) { + ERR("Failed to add kernel trigger event source to notification thread"); + retval = -1; + goto stop_threads; + } + } } /* Load sessions. */ @@ -1749,6 +1790,8 @@ int main(int argc, char **argv) sessiond_wait_for_quit_pipe(-1); stop_threads: + + trigger_error_accounting_fini(); /* * Ensure that the client thread is no longer accepting new commands, * which could cause new sessions to be created. diff --git a/src/bin/lttng-sessiond/modprobe.c b/src/bin/lttng-sessiond/modprobe.c index cf9c12b3c..a093f2412 100644 --- a/src/bin/lttng-sessiond/modprobe.c +++ b/src/bin/lttng-sessiond/modprobe.c @@ -30,6 +30,10 @@ #define LTTNG_MOD_OPTIONAL 0 /* LTTng kernel tracer mandatory core modules list */ +/* TODO: the new trigger client might not be present in previous lttng-modules + * should it be optional? + * Can we reuse this to also know of the trigger feature is supported? + */ struct kern_modules_param kern_modules_control_core[] = { { (char *) "lttng-ring-buffer-client-discard" }, { (char *) "lttng-ring-buffer-client-overwrite" }, @@ -37,6 +41,9 @@ struct kern_modules_param kern_modules_control_core[] = { { (char *) "lttng-ring-buffer-client-mmap-discard" }, { (char *) "lttng-ring-buffer-client-mmap-overwrite" }, { (char *) "lttng-ring-buffer-metadata-mmap-client" }, + { (char *) "lttng-ring-buffer-trigger-client" }, + { (char *) "lttng-counter-client-percpu-64-modular" }, + { (char *) "lttng-counter-client-percpu-32-modular" }, }; /* LTTng kernel tracer probe modules list */ diff --git a/src/bin/lttng-sessiond/notification-thread-commands.c b/src/bin/lttng-sessiond/notification-thread-commands.c index dc03e6a2d..3069c57d7 100644 --- a/src/bin/lttng-sessiond/notification-thread-commands.c +++ b/src/bin/lttng-sessiond/notification-thread-commands.c @@ -268,6 +268,112 @@ end: return ret_code; } +enum lttng_error_code notification_thread_command_add_application( + struct notification_thread_handle *handle, + int fd, + enum lttng_domain_type domain) +{ + int ret; + enum lttng_error_code ret_code; + struct notification_thread_command cmd = {}; + + assert(!(fd < 0)); + + init_notification_thread_command(&cmd); + + cmd.type = NOTIFICATION_COMMAND_TYPE_ADD_APPLICATION; + cmd.parameters.application.read_side_trigger_event_application_pipe = fd; + cmd.parameters.application.domain = domain; + + ret = run_command_wait(handle, &cmd); + if (ret) { + ret_code = LTTNG_ERR_UNK; + goto end; + } + ret_code = cmd.reply_code; +end: + return ret_code; +} + +enum lttng_error_code notification_thread_command_remove_application( + struct notification_thread_handle *handle, + int fd) +{ + int ret; + enum lttng_error_code ret_code; + struct notification_thread_command cmd = {}; + + init_notification_thread_command(&cmd); + + cmd.type = NOTIFICATION_COMMAND_TYPE_REMOVE_APPLICATION; + cmd.parameters.application.read_side_trigger_event_application_pipe = fd; + + ret = run_command_wait(handle, &cmd); + if (ret) { + ret_code = LTTNG_ERR_UNK; + goto end; + } + ret_code = cmd.reply_code; +end: + return ret_code; +} + +enum lttng_error_code notification_thread_command_get_tokens( + struct notification_thread_handle *handle, + struct lttng_triggers **tokens_triggers) +{ + int ret; + enum lttng_error_code ret_code; + struct notification_thread_command cmd = {}; + + assert(handle); + assert(tokens_triggers); + + init_notification_thread_command(&cmd); + + cmd.type = NOTIFICATION_COMMAND_TYPE_GET_TOKENS; + + ret = run_command_wait(handle, &cmd); + if (ret) { + ret_code = LTTNG_ERR_UNK; + goto end; + } + ret_code = cmd.reply_code; + *tokens_triggers = cmd.reply.get_tokens.triggers; + +end: + return ret_code; +} + +enum lttng_error_code notification_thread_command_list_triggers( + struct notification_thread_handle *handle, + uid_t uid, + struct lttng_triggers **triggers) +{ + int ret; + enum lttng_error_code ret_code; + struct notification_thread_command cmd = {}; + + assert(handle); + assert(triggers); + + init_notification_thread_command(&cmd); + + cmd.type = NOTIFICATION_COMMAND_TYPE_LIST_TRIGGERS; + cmd.parameters.list_triggers.uid = uid; + + ret = run_command_wait(handle, &cmd); + if (ret) { + ret_code = LTTNG_ERR_UNK; + goto end; + } + ret_code = cmd.reply_code; + *triggers = cmd.reply.list_triggers.triggers; + +end: + return ret_code; +} + void notification_thread_command_quit( struct notification_thread_handle *handle) { @@ -295,3 +401,52 @@ int notification_thread_client_communication_update( cmd.parameters.client_communication_update.status = transmission_status; return run_command_no_wait(handle, &cmd); } + +/* + * Takes ownership of the payload if present. + */ +LTTNG_HIDDEN +struct lttng_trigger_notification *lttng_trigger_notification_create( + uint64_t id, + enum lttng_domain_type domain, + char *payload, + size_t payload_size) +{ + struct lttng_trigger_notification *notification = NULL; + + assert(domain != LTTNG_DOMAIN_NONE); + + if (payload) { + assert(payload_size > 0); + } else { + assert(payload_size == 0); + } + + notification = zmalloc(sizeof(struct lttng_trigger_notification)); + if (notification == NULL) { + ERR("[notification-thread] Error allocating notification "); + goto end; + } + + notification->id = id; + notification->type = domain; + notification->capture_buffer = payload; + notification->capture_buf_size = payload_size; + +end: + return notification; +} + +LTTNG_HIDDEN +void lttng_trigger_notification_destroy( + struct lttng_trigger_notification *notification) +{ + if (!notification) { + return; + } + + if(notification->capture_buffer) { + free(notification->capture_buffer); + } + free(notification); +} diff --git a/src/bin/lttng-sessiond/notification-thread-commands.h b/src/bin/lttng-sessiond/notification-thread-commands.h index c09ebea46..6556bf7c5 100644 --- a/src/bin/lttng-sessiond/notification-thread-commands.h +++ b/src/bin/lttng-sessiond/notification-thread-commands.h @@ -27,6 +27,10 @@ enum notification_thread_command_type { NOTIFICATION_COMMAND_TYPE_REMOVE_CHANNEL, NOTIFICATION_COMMAND_TYPE_SESSION_ROTATION_ONGOING, NOTIFICATION_COMMAND_TYPE_SESSION_ROTATION_COMPLETED, + NOTIFICATION_COMMAND_TYPE_ADD_APPLICATION, + NOTIFICATION_COMMAND_TYPE_REMOVE_APPLICATION, + NOTIFICATION_COMMAND_TYPE_GET_TOKENS, + NOTIFICATION_COMMAND_TYPE_LIST_TRIGGERS, NOTIFICATION_COMMAND_TYPE_QUIT, NOTIFICATION_COMMAND_TYPE_CLIENT_COMMUNICATION_UPDATE, }; @@ -64,6 +68,16 @@ struct notification_thread_command { uint64_t trace_archive_chunk_id; struct lttng_trace_archive_location *location; } session_rotation; + /* Add/Remove application */ + struct { + int read_side_trigger_event_application_pipe; + enum lttng_domain_type domain; + } application; + /* List triggers */ + struct { + /* Credential */ + uid_t uid; + } list_triggers; /* Client communication update. */ struct { notification_client_id id; @@ -72,6 +86,15 @@ struct notification_thread_command { } parameters; + union { + struct { + struct lttng_triggers *triggers; + } get_tokens; + struct { + struct lttng_triggers *triggers; + } list_triggers; + } reply; + /* lttng_waiter on which to wait for command reply (optional). */ struct lttng_waiter reply_waiter; enum lttng_error_code reply_code; @@ -108,6 +131,28 @@ enum lttng_error_code notification_thread_command_session_rotation_completed( uint64_t trace_archive_chunk_id, struct lttng_trace_archive_location *location); +enum lttng_error_code notification_thread_command_add_application( + struct notification_thread_handle *handle, + int fd, + enum lttng_domain_type domain + ); + +enum lttng_error_code notification_thread_command_remove_application( + struct notification_thread_handle *handle, + int trigger_event_application_pipe); + +/* Must hold the notification_trigger_tokens_ht_lock to protect against + * insertion removal of triggers TODO: is it the case even with refcounting? */ +/* todo find a better way....*/ +enum lttng_error_code notification_thread_command_get_tokens( + struct notification_thread_handle *handle, + struct lttng_triggers **triggers); + +enum lttng_error_code notification_thread_command_list_triggers( + struct notification_thread_handle *handle, + uid_t uid, + struct lttng_triggers **triggers); + void notification_thread_command_quit( struct notification_thread_handle *handle); diff --git a/src/bin/lttng-sessiond/notification-thread-events.c b/src/bin/lttng-sessiond/notification-thread-events.c index 95101178f..b3c64d9c7 100644 --- a/src/bin/lttng-sessiond/notification-thread-events.c +++ b/src/bin/lttng-sessiond/notification-thread-events.c @@ -21,12 +21,17 @@ #include #include #include +#include +#include #include #include #include #include #include +#include +#include #include +#include #include #include @@ -34,11 +39,13 @@ #include #include +#include "condition-internal.h" #include "notification-thread.h" #include "notification-thread-events.h" #include "notification-thread-commands.h" #include "lttng-sessiond.h" #include "kernel.h" +#include "trigger-error-accounting.h" #define CLIENT_POLL_MASK_IN (LPOLLIN | LPOLLERR | LPOLLHUP | LPOLLRDHUP) #define CLIENT_POLL_MASK_IN_OUT (CLIENT_POLL_MASK_IN | LPOLLOUT) @@ -110,6 +117,7 @@ struct lttng_session_trigger_list { struct lttng_trigger_ht_element { struct lttng_trigger *trigger; struct cds_lfht_node node; + struct cds_lfht_node node_by_name_uid; /* call_rcu delayed reclaim. */ struct rcu_head rcu_node; }; @@ -187,6 +195,9 @@ int client_handle_transmission_status( enum client_transmission_status transmission_status, struct notification_thread_state *state); +static +void free_lttng_trigger_ht_element_rcu(struct rcu_head *node); + static int match_client_socket(struct cds_lfht_node *node, const void *key) { @@ -261,18 +272,26 @@ int match_channel_info(struct cds_lfht_node *node, const void *key) } static -int match_condition(struct cds_lfht_node *node, const void *key) +int match_trigger(struct cds_lfht_node *node, const void *key) { - struct lttng_condition *condition_key = (struct lttng_condition *) key; - struct lttng_trigger_ht_element *trigger; - struct lttng_condition *condition; + struct lttng_trigger *trigger_key = (struct lttng_trigger *) key; + struct lttng_trigger_ht_element *trigger_ht_element; - trigger = caa_container_of(node, struct lttng_trigger_ht_element, + trigger_ht_element = caa_container_of(node, struct lttng_trigger_ht_element, node); - condition = lttng_trigger_get_condition(trigger->trigger); - assert(condition); - return !!lttng_condition_is_equal(condition_key, condition); + return !!lttng_trigger_is_equal(trigger_key, trigger_ht_element->trigger); +} + +static +int match_trigger_token(struct cds_lfht_node *node, const void *key) +{ + const uint64_t *_key = key; + struct notification_trigger_tokens_ht_element *element; + + element = caa_container_of(node, struct notification_trigger_tokens_ht_element, + node); + return *_key == element->token ; } static @@ -301,103 +320,42 @@ int match_session(struct cds_lfht_node *node, const void *key) return !strcmp(session_info->name, name); } -static -unsigned long lttng_condition_buffer_usage_hash( - const struct lttng_condition *_condition) +/* + * Match trigger based on name and credentials only. + * Name duplication is NOT allowed for the same uid. + */ +static int match_name_uid(struct cds_lfht_node *node, const void *key) { - unsigned long hash; - unsigned long condition_type; - struct lttng_condition_buffer_usage *condition; + bool match = false; + struct lttng_trigger_ht_element *trigger_ht_element; + const char *name; + const char *key_name; + enum lttng_trigger_status status; + const struct lttng_credentials *key_creds; + const struct lttng_credentials *node_creds; + struct lttng_trigger *trigger_key = (struct lttng_trigger *) key; - condition = container_of(_condition, - struct lttng_condition_buffer_usage, parent); + trigger_ht_element = caa_container_of(node, struct lttng_trigger_ht_element, + node_by_name_uid); - condition_type = (unsigned long) condition->parent.type; - hash = hash_key_ulong((void *) condition_type, lttng_ht_seed); - if (condition->session_name) { - hash ^= hash_key_str(condition->session_name, lttng_ht_seed); - } - if (condition->channel_name) { - hash ^= hash_key_str(condition->channel_name, lttng_ht_seed); - } - if (condition->domain.set) { - hash ^= hash_key_ulong( - (void *) condition->domain.type, - lttng_ht_seed); - } - if (condition->threshold_ratio.set) { - uint64_t val; + status = lttng_trigger_get_name(trigger_ht_element->trigger, &name); + assert(status == LTTNG_TRIGGER_STATUS_OK); - val = condition->threshold_ratio.value * (double) UINT32_MAX; - hash ^= hash_key_u64(&val, lttng_ht_seed); - } else if (condition->threshold_bytes.set) { - uint64_t val; + status = lttng_trigger_get_name(trigger_key, &key_name); + assert(status == LTTNG_TRIGGER_STATUS_OK); - val = condition->threshold_bytes.value; - hash ^= hash_key_u64(&val, lttng_ht_seed); + if (strcmp(name, key_name) != 0) { + goto end; } - return hash; -} -static -unsigned long lttng_condition_session_consumed_size_hash( - const struct lttng_condition *_condition) -{ - unsigned long hash; - unsigned long condition_type = - (unsigned long) LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE; - struct lttng_condition_session_consumed_size *condition; - uint64_t val; + /* Check the uid */ + key_creds = lttng_trigger_get_credentials(trigger_key); + node_creds = lttng_trigger_get_credentials(trigger_ht_element->trigger); - condition = container_of(_condition, - struct lttng_condition_session_consumed_size, parent); + match = lttng_credentials_is_equal_uid(key_creds, node_creds); - hash = hash_key_ulong((void *) condition_type, lttng_ht_seed); - if (condition->session_name) { - hash ^= hash_key_str(condition->session_name, lttng_ht_seed); - } - val = condition->consumed_threshold_bytes.value; - hash ^= hash_key_u64(&val, lttng_ht_seed); - return hash; -} - -static -unsigned long lttng_condition_session_rotation_hash( - const struct lttng_condition *_condition) -{ - unsigned long hash, condition_type; - struct lttng_condition_session_rotation *condition; - - condition = container_of(_condition, - struct lttng_condition_session_rotation, parent); - condition_type = (unsigned long) condition->parent.type; - hash = hash_key_ulong((void *) condition_type, lttng_ht_seed); - assert(condition->session_name); - hash ^= hash_key_str(condition->session_name, lttng_ht_seed); - return hash; -} - -/* - * The lttng_condition hashing code is kept in this file (rather than - * condition.c) since it makes use of GPLv2 code (hashtable utils), which we - * don't want to link in liblttng-ctl. - */ -static -unsigned long lttng_condition_hash(const struct lttng_condition *condition) -{ - switch (condition->type) { - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: - return lttng_condition_buffer_usage_hash(condition); - case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: - return lttng_condition_session_consumed_size_hash(condition); - case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: - case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: - return lttng_condition_session_rotation_hash(condition); - default: - ERR("[notification-thread] Unexpected condition type caught"); - abort(); - } +end: + return match; } static @@ -442,6 +400,8 @@ enum lttng_object_type get_condition_binding_object( case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: return LTTNG_OBJECT_TYPE_SESSION; + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + return LTTNG_OBJECT_TYPE_NONE; default: return LTTNG_OBJECT_TYPE_UNKNOWN; } @@ -952,6 +912,7 @@ int evaluate_condition_for_client(const struct lttng_trigger *trigger, &evaluation, &object_uid, &object_gid); break; case LTTNG_OBJECT_TYPE_NONE: + DBG("[notification-thread] Newly subscribed-to condition not binded to object, nothing to evaluate"); ret = 0; goto end; case LTTNG_OBJECT_TYPE_UNKNOWN: @@ -1623,7 +1584,7 @@ int handle_notification_thread_command_add_channel( DBG("[notification-thread] Adding channel %s from session %s, channel key = %" PRIu64 " in %s domain", channel_name, session_name, channel_key_int, - channel_domain == LTTNG_DOMAIN_KERNEL ? "kernel" : "user space"); + lttng_domain_type_str(channel_domain)); CDS_INIT_LIST_HEAD(&trigger_list); @@ -1724,7 +1685,7 @@ int handle_notification_thread_command_remove_channel( struct channel_info *channel_info; DBG("[notification-thread] Removing channel key = %" PRIu64 " in %s domain", - channel_key, domain == LTTNG_DOMAIN_KERNEL ? "kernel" : "user space"); + channel_key, lttng_domain_type_str(domain)); rcu_read_lock(); @@ -1807,8 +1768,8 @@ int handle_notification_thread_command_session_rotation( struct lttng_trigger_list_element *trigger_list_element; struct session_info *session_info; const struct lttng_credentials session_creds = { - .uid = session_uid, - .gid = session_gid, + .uid = LTTNG_OPTIONAL_INIT_VALUE(session_uid), + .gid = LTTNG_OPTIONAL_INIT_VALUE(session_gid), }; rcu_read_lock(); @@ -1917,6 +1878,218 @@ end: return ret; } +static +int handle_notification_thread_command_add_application( + struct notification_thread_handle *handle, + struct notification_thread_state *state, + int read_side_trigger_event_application_pipe, + enum lttng_domain_type domain_type, + enum lttng_error_code *_cmd_result) +{ + int ret = 0; + enum lttng_error_code cmd_result = LTTNG_OK; + struct notification_event_trigger_source_element *element = NULL; + + element = zmalloc(sizeof(*element)); + if (!element) { + cmd_result = LTTNG_ERR_NOMEM; + ret = -1; + goto end; + } + + CDS_INIT_LIST_HEAD(&element->node); + element->fd = read_side_trigger_event_application_pipe; + element->domain = domain_type; + + pthread_mutex_lock(&handle->event_trigger_sources.lock); + cds_list_add(&element->node, &handle->event_trigger_sources.list); + pthread_mutex_unlock(&handle->event_trigger_sources.lock); + + /* TODO: remove on failure to add to list? */ + + /* Adding the read side pipe to the event poll */ + ret = lttng_poll_add(&state->events, + read_side_trigger_event_application_pipe, + LPOLLIN | LPOLLERR); + + DBG3("[notification-thread] Adding application event source from fd: %d", read_side_trigger_event_application_pipe); + if (ret < 0) { + /* TODO: what should be the value of cmd_result??? */ + ERR("[notification-thread] Failed to add event source pipe fd to pollset"); + goto end; + } + +end: + *_cmd_result = cmd_result; + return ret; +} + +static +int handle_notification_thread_command_remove_application( + struct notification_thread_handle *handle, + struct notification_thread_state *state, + int read_side_trigger_event_application_pipe, + enum lttng_error_code *_cmd_result) +{ + int ret = 0; + enum lttng_error_code cmd_result = LTTNG_OK; + /* Used for logging */ + enum lttng_domain_type domain = LTTNG_DOMAIN_NONE; + + /* TODO: missing a lock propably to revisit */ + struct notification_event_trigger_source_element *source_element = NULL, *tmp; + + cds_list_for_each_entry_safe(source_element, tmp, + &handle->event_trigger_sources.list, node) { + if (source_element->fd != read_side_trigger_event_application_pipe) { + continue; + } + + cds_list_del(&source_element->node); + break; + } + + /* It should always be found */ + assert(source_element); + + DBG3("[notification-thread] Removing application event source from fd: %d of domain: %s", + read_side_trigger_event_application_pipe, + lttng_domain_type_str(domain)); + free(source_element); + + /* Removing the read side pipe to the event poll */ + ret = lttng_poll_del(&state->events, + read_side_trigger_event_application_pipe); + if (ret < 0) { + /* TODO: what should be the value of cmd_result??? */ + ERR("[notification-thread] Failed to remove event source pipe fd from pollset"); + goto end; + } + +end: + *_cmd_result = cmd_result; + return ret; +} + +static int handle_notification_thread_command_get_tokens( + struct notification_thread_handle *handle, + struct notification_thread_state *state, + struct lttng_triggers **triggers, + enum lttng_error_code *_cmd_result) +{ + int ret = 0, i = 0; + enum lttng_error_code cmd_result = LTTNG_OK; + struct cds_lfht_iter iter; + struct notification_trigger_tokens_ht_element *element; + struct lttng_triggers *local_triggers = NULL; + + local_triggers = lttng_triggers_create(); + if (!local_triggers) { + cmd_result = LTTNG_ERR_NOMEM; + goto end; + } + + rcu_read_lock(); + cds_lfht_for_each_entry ( + state->trigger_tokens_ht, &iter, element, node) { + ret = lttng_triggers_add(local_triggers, element->trigger); + if (ret < 0) { + cmd_result = LTTNG_ERR_FATAL; + ret = -1; + goto end; + } + + i++; + } + + /* Passing ownership up */ + *triggers = local_triggers; + local_triggers = NULL; + +end: + rcu_read_unlock(); + lttng_triggers_destroy(local_triggers); + *_cmd_result = cmd_result; + return ret; +} + +static +int trigger_update_error_count(struct lttng_trigger *trigger) +{ + int ret = 0; + uint64_t error_count = 0; + enum trigger_error_accounting_status status; + + status = trigger_error_accounting_get_count(trigger, &error_count); + if (status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error getting trigger error count"); + } + + lttng_trigger_set_error_count(trigger, error_count); + return ret; +} + +static int handle_notification_thread_command_list_triggers( + struct notification_thread_handle *handle, + struct notification_thread_state *state, + uid_t uid, + struct lttng_triggers **triggers, + enum lttng_error_code *_cmd_result) +{ + int ret = 0, i = 0; + enum lttng_error_code cmd_result = LTTNG_OK; + struct cds_lfht_iter iter; + struct lttng_trigger_ht_element *trigger_ht_element; + struct lttng_triggers *local_triggers = NULL; + const struct lttng_credentials *creds; + + long scb, sca; + unsigned long count; + + rcu_read_lock(); + cds_lfht_count_nodes(state->triggers_ht, &scb, &count, &sca); + + local_triggers = lttng_triggers_create(); + if (!local_triggers) { + cmd_result = LTTNG_ERR_NOMEM; + goto end; + } + + cds_lfht_for_each_entry (state->triggers_ht, &iter, + trigger_ht_element, node) { + /* + * Only return the trigger for which the requestion client have + * access. + * Root user have visibility over all triggers. + */ + creds = lttng_trigger_get_credentials(trigger_ht_element->trigger); + if (uid != lttng_credentials_get_uid(creds) && uid != 0) { + continue; + } + + ret = trigger_update_error_count(trigger_ht_element->trigger); + assert(!ret); + + ret = lttng_triggers_add(local_triggers, trigger_ht_element->trigger); + if (ret < 0) { + ret = -1; + goto end; + } + + i++; + } + + /* Passing ownership up */ + *triggers = local_triggers; + local_triggers = NULL; + +end: + rcu_read_unlock(); + lttng_triggers_destroy(local_triggers); + *_cmd_result = cmd_result; + return ret; +} + static int condition_is_supported(struct lttng_condition *condition) { @@ -1948,6 +2121,15 @@ int condition_is_supported(struct lttng_condition *condition) ret = kernel_supports_ring_buffer_snapshot_sample_positions(); break; } + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + { + /* TODO: + * Check for kernel support. + * Check for ust support ?? + */ + ret = 1; + break; + } default: ret = 1; } @@ -1955,6 +2137,42 @@ end: return ret; } +static +int action_is_supported(struct lttng_action *action) +{ + int ret; + + switch (lttng_action_get_type(action)) { + case LTTNG_ACTION_TYPE_NOTIFY: + case LTTNG_ACTION_TYPE_START_SESSION: + case LTTNG_ACTION_TYPE_STOP_SESSION: + case LTTNG_ACTION_TYPE_ROTATE_SESSION: + case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION: + { + /* TODO validate that this is true for kernel in regards to + * rotation and snapshot. Start stop is not a problem notify + * either. + */ + /* For now all type of actions are supported */ + ret = 1; + break; + } + case LTTNG_ACTION_TYPE_GROUP: + { + /* TODO: Iterate over all internal actions and validate that + * they are supported + */ + ret = 1; + break; + + } + default: + ret = 1; + } + + return ret; +} + /* Must be called with RCU read lock held. */ static int bind_trigger_to_matching_session(struct lttng_trigger *trigger, @@ -2058,7 +2276,7 @@ bool is_trigger_action_notify(const struct lttng_trigger *trigger) enum lttng_action_type action_type; assert(action); - action_type = lttng_action_get_type_const(action); + action_type = lttng_action_get_type(action); if (action_type == LTTNG_ACTION_TYPE_NOTIFY) { is_notify = true; goto end; @@ -2074,7 +2292,7 @@ bool is_trigger_action_notify(const struct lttng_trigger *trigger) lttng_action_group_get_at_index( action, i); - action_type = lttng_action_get_type_const(inner_action); + action_type = lttng_action_get_type(inner_action); if (action_type == LTTNG_ACTION_TYPE_NOTIFY) { is_notify = true; goto end; @@ -2085,6 +2303,52 @@ end: return is_notify; } +static bool trigger_name_taken(struct notification_thread_state *state, + const struct lttng_trigger *trigger) +{ + struct cds_lfht_node *triggers_by_name_uid_ht_node; + struct cds_lfht_iter iter; + + /* + * No duplicata is allowed in the triggers_by_name_uid_ht. + * The match is done against the trigger name and uid. + */ + cds_lfht_lookup(state->triggers_by_name_uid_ht, + hash_key_str(trigger->name, lttng_ht_seed), + match_name_uid, + trigger, + &iter); + triggers_by_name_uid_ht_node = cds_lfht_iter_get_node(&iter); + if (triggers_by_name_uid_ht_node) { + return true; + } else { + return false; + } +} + +static +void generate_trigger_name(struct notification_thread_state *state, struct lttng_trigger *trigger, const char **name) +{ + /* + * Here the offset criteria guarantee an end. This will be a nice + * bikeshedding conversation. I would simply generate uuid and use them + * as trigger name. + */ + bool taken = false; + enum lttng_trigger_status status; + do { + lttng_trigger_generate_name(trigger, state->trigger_id.name_offset); + + status = lttng_trigger_get_name(trigger, name); + assert(status == LTTNG_TRIGGER_STATUS_OK); + + taken = trigger_name_taken(state, trigger); + if (taken) { + state->trigger_id.name_offset++; + } + } while (taken || state->trigger_id.name_offset == UINT32_MAX); +} + /* * FIXME A client's credentials are not checked when registering a trigger. * @@ -2107,32 +2371,60 @@ int handle_notification_thread_command_register_trigger( enum lttng_error_code *cmd_result) { int ret = 0; + int is_supported; struct lttng_condition *condition; + struct lttng_action *action; struct notification_client *client; struct notification_client_list *client_list = NULL; struct lttng_trigger_ht_element *trigger_ht_element = NULL; struct notification_client_list_element *client_list_element; + struct notification_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; struct lttng_credentials object_creds; + uid_t object_uid; + gid_t object_gid; enum action_executor_status executor_status; rcu_read_lock(); + /* Set the trigger's tracer token */ + lttng_trigger_set_tracer_token(trigger, state->trigger_id.token_generator); + + if (lttng_trigger_get_name(trigger, &trigger_name) == + LTTNG_TRIGGER_STATUS_UNSET) { + generate_trigger_name(state, trigger, &trigger_name); + } else if (trigger_name_taken(state, trigger)) { + /* Not a fatal error */ + *cmd_result = LTTNG_ERR_TRIGGER_EXISTS; + ret = 0; + goto error; + } + condition = lttng_trigger_get_condition(trigger); assert(condition); - ret = condition_is_supported(condition); - if (ret < 0) { + action = lttng_trigger_get_action(trigger); + assert(action); + + is_supported = condition_is_supported(condition); + if (is_supported < 0) { goto error; - } else if (ret == 0) { + } else if (is_supported == 0) { *cmd_result = LTTNG_ERR_NOT_SUPPORTED; goto error; - } else { - /* Feature is supported, continue. */ + } + + is_supported = action_is_supported(action); + if (is_supported < 0) { + goto error; + } else if (is_supported == 0) { ret = 0; + *cmd_result = LTTNG_ERR_NOT_SUPPORTED; + goto error; } trigger_ht_element = zmalloc(sizeof(*trigger_ht_element)); @@ -2143,12 +2435,13 @@ int handle_notification_thread_command_register_trigger( /* Add trigger to the trigger_ht. */ cds_lfht_node_init(&trigger_ht_element->node); + cds_lfht_node_init(&trigger_ht_element->node_by_name_uid); trigger_ht_element->trigger = trigger; node = cds_lfht_add_unique(state->triggers_ht, lttng_condition_hash(condition), - match_condition, - condition, + match_trigger, + trigger, &trigger_ht_element->node); if (node != &trigger_ht_element->node) { /* Not a fatal error, simply report it to the client. */ @@ -2156,10 +2449,66 @@ int handle_notification_thread_command_register_trigger( goto error_free_ht_element; } + node = cds_lfht_add_unique(state->triggers_by_name_uid_ht, + hash_key_str(trigger_name, lttng_ht_seed), match_name_uid, + trigger, &trigger_ht_element->node_by_name_uid); + if (node != &trigger_ht_element->node_by_name_uid) { + /* Not a fatal error, simply report it to the client. */ + cds_lfht_del(state->triggers_ht, &trigger_ht_element->node); + *cmd_result = LTTNG_ERR_TRIGGER_EXISTS; + goto error_free_ht_element; + } + + if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) { + uint64_t error_counter_index = 0; + enum trigger_error_accounting_status error_accounting_status; + + trigger_tokens_ht_element = zmalloc(sizeof(*trigger_tokens_ht_element)); + if (!trigger_tokens_ht_element) { + ret = -1; + cds_lfht_del(state->triggers_ht, &trigger_ht_element->node); + cds_lfht_del(state->triggers_by_name_uid_ht, &trigger_ht_element->node_by_name_uid); + goto error; + } + + error_accounting_status = trigger_error_accounting_register_trigger( + trigger, &error_counter_index); + if (error_accounting_status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error registering trigger for error accounting"); + cds_lfht_del(state->triggers_ht, &trigger_ht_element->node); + cds_lfht_del(state->triggers_by_name_uid_ht, &trigger_ht_element->node_by_name_uid); + *cmd_result = LTTNG_ERR_TRIGGER_GROUP_ERROR_COUNTER_FULL; + goto error_free_ht_element; + } + + lttng_trigger_set_error_counter_index(trigger, error_counter_index); + + /* Add trigger token to the trigger_tokens_ht. */ + cds_lfht_node_init(&trigger_tokens_ht_element->node); + trigger_tokens_ht_element->token = LTTNG_OPTIONAL_GET(trigger->tracer_token); + trigger_tokens_ht_element->trigger = trigger; + + node = cds_lfht_add_unique(state->trigger_tokens_ht, + hash_key_u64(&trigger_tokens_ht_element->token, lttng_ht_seed), + match_trigger_token, + &trigger_tokens_ht_element->token, + &trigger_tokens_ht_element->node); + if (node != &trigger_tokens_ht_element->node) { + /* TODO: THIS IS A FATAL ERROR... should never happen */ + /* Not a fatal error, simply report it to the client. */ + *cmd_result = LTTNG_ERR_TRIGGER_EXISTS; + cds_lfht_del(state->triggers_ht, &trigger_ht_element->node); + cds_lfht_del(state->triggers_by_name_uid_ht, &trigger_ht_element->node_by_name_uid); + trigger_error_accounting_unregister_trigger(trigger); + goto error_free_ht_element; + } + } + /* * Ownership of the trigger and of its wrapper was transfered to - * the triggers_ht. + * the triggers_ht. Same for token ht element if necessary. */ + trigger_tokens_ht_element = NULL; trigger_ht_element = NULL; free_trigger = false; @@ -2260,21 +2609,21 @@ int handle_notification_thread_command_register_trigger( switch (get_condition_binding_object(condition)) { case LTTNG_OBJECT_TYPE_SESSION: ret = evaluate_session_condition_for_client(condition, state, - &evaluation, &object_creds.uid, - &object_creds.gid); + &evaluation, &object_uid, + &object_gid); break; case LTTNG_OBJECT_TYPE_CHANNEL: ret = evaluate_channel_condition_for_client(condition, state, - &evaluation, &object_creds.uid, - &object_creds.gid); + &evaluation, &object_uid, + &object_gid); break; case LTTNG_OBJECT_TYPE_NONE: ret = 0; - goto error_put_client_list; + break; case LTTNG_OBJECT_TYPE_UNKNOWN: default: ret = -1; - goto error_put_client_list; + break; } if (ret) { @@ -2282,12 +2631,15 @@ int handle_notification_thread_command_register_trigger( goto error_put_client_list; } + LTTNG_OPTIONAL_SET(&object_creds.uid, object_uid); + LTTNG_OPTIONAL_SET(&object_creds.gid, object_gid); + DBG("Newly registered trigger's condition evaluated to %s", evaluation ? "true" : "false"); if (!evaluation) { /* Evaluation yielded nothing. Normal exit. */ ret = 0; - goto error_put_client_list; + goto end; } /* @@ -2318,18 +2670,26 @@ int handle_notification_thread_command_register_trigger( */ WARN("No space left when enqueuing action associated to newly registered trigger"); ret = 0; - goto error_put_client_list; + goto end; default: abort(); } +end: + /* Increment the trigger unique id generator */ + state->trigger_id.token_generator++; *cmd_result = LTTNG_OK; error_put_client_list: notification_client_list_put(client_list); error_free_ht_element: - free(trigger_ht_element); + if (trigger_ht_element) { + /* Delayed removal due to RCU constraint on delete. */ + call_rcu(&trigger_ht_element->rcu_node, free_lttng_trigger_ht_element_rcu); + } + + free(trigger_tokens_ht_element); error: if (free_trigger) { lttng_trigger_destroy(trigger); @@ -2345,6 +2705,13 @@ void free_lttng_trigger_ht_element_rcu(struct rcu_head *node) rcu_node)); } +static +void free_notification_trigger_tokens_ht_element_rcu(struct rcu_head *node) +{ + free(caa_container_of(node, struct notification_trigger_tokens_ht_element, + rcu_node)); +} + static int handle_notification_thread_command_unregister_trigger( struct notification_thread_state *state, @@ -2364,8 +2731,8 @@ int handle_notification_thread_command_unregister_trigger( cds_lfht_lookup(state->triggers_ht, lttng_condition_hash(condition), - match_condition, - condition, + match_trigger, + trigger, &iter); triggers_ht_node = cds_lfht_iter_get_node(&iter); if (!triggers_ht_node) { @@ -2382,13 +2749,7 @@ int handle_notification_thread_command_unregister_trigger( cds_list_for_each_entry_safe(trigger_element, tmp, &trigger_list->list, node) { - const struct lttng_condition *current_condition = - lttng_trigger_get_const_condition( - trigger_element->trigger); - - assert(current_condition); - if (!lttng_condition_is_equal(condition, - current_condition)) { + if (!lttng_trigger_is_equal(trigger, trigger_element->trigger)) { continue; } @@ -2399,21 +2760,44 @@ int handle_notification_thread_command_unregister_trigger( } } - /* - * Remove and release the client list from - * notification_trigger_clients_ht. - */ - client_list = get_client_list_from_condition(state, condition); - assert(client_list); + if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) { + struct notification_trigger_tokens_ht_element *trigger_tokens_ht_element; + cds_lfht_for_each_entry(state->trigger_tokens_ht, &iter, trigger_tokens_ht_element, + node) { + if (!lttng_trigger_is_equal(trigger, trigger_tokens_ht_element->trigger)) { + continue; + } - /* Put new reference and the hashtable's reference. */ - notification_client_list_put(client_list); - notification_client_list_put(client_list); - client_list = NULL; + trigger_error_accounting_unregister_trigger(trigger_tokens_ht_element->trigger); + + /* TODO talk to all app and remove it */ + DBG("[notification-thread] Removed trigger from tokens_ht"); + cds_lfht_del(state->trigger_tokens_ht, + &trigger_tokens_ht_element->node); + call_rcu(&trigger_tokens_ht_element->rcu_node, free_notification_trigger_tokens_ht_element_rcu); + + break; + } + } + + if (is_trigger_action_notify(trigger)) { + /* + * Remove and release the client list from + * notification_trigger_clients_ht. + */ + client_list = get_client_list_from_condition(state, condition); + assert(client_list); + + /* Put new reference and the hashtable's reference. */ + notification_client_list_put(client_list); + notification_client_list_put(client_list); + client_list = NULL; + } /* Remove trigger from triggers_ht. */ trigger_ht_element = caa_container_of(triggers_ht_node, struct lttng_trigger_ht_element, node); + cds_lfht_del(state->triggers_by_name_uid_ht, &trigger_ht_element->node_by_name_uid); cds_lfht_del(state->triggers_ht, triggers_ht_node); /* Release the ownership of the trigger. */ @@ -2496,6 +2880,43 @@ int handle_notification_thread_command( cmd->parameters.session_rotation.location, &cmd->reply_code); break; + case NOTIFICATION_COMMAND_TYPE_ADD_APPLICATION: + ret = handle_notification_thread_command_add_application( + handle, + state, + cmd->parameters.application.read_side_trigger_event_application_pipe, + cmd->parameters.application.domain, + &cmd->reply_code); + break; + case NOTIFICATION_COMMAND_TYPE_REMOVE_APPLICATION: + ret = handle_notification_thread_command_remove_application( + handle, + state, + cmd->parameters.application.read_side_trigger_event_application_pipe, + &cmd->reply_code); + break; + case NOTIFICATION_COMMAND_TYPE_GET_TOKENS: + { + struct lttng_triggers *triggers = NULL; + ret = handle_notification_thread_command_get_tokens( + handle, state, &triggers, &cmd->reply_code); + cmd->reply.get_tokens.triggers = triggers; + ret = 0; + break; + } + case NOTIFICATION_COMMAND_TYPE_LIST_TRIGGERS: + { + struct lttng_triggers *triggers = NULL; + ret = handle_notification_thread_command_list_triggers( + handle, + state, + cmd->parameters.list_triggers.uid, + &triggers, + &cmd->reply_code); + cmd->reply.list_triggers.triggers = triggers; + ret = 0; + break; + } case NOTIFICATION_COMMAND_TYPE_QUIT: DBG("[notification-thread] Received quit command"); cmd->reply_code = LTTNG_OK; @@ -3582,11 +4003,15 @@ int send_evaluation_to_clients(const struct lttng_trigger *trigger, struct notification_thread_state *state, uid_t object_uid, gid_t object_gid) { + const struct lttng_credentials creds = { + .uid = LTTNG_OPTIONAL_INIT_VALUE(object_uid), + .gid = LTTNG_OPTIONAL_INIT_VALUE(object_gid), + }; + return notification_client_list_send_evaluation(client_list, lttng_trigger_get_const_condition(trigger), evaluation, lttng_trigger_get_credentials(trigger), - &(struct lttng_credentials){ - .uid = object_uid, .gid = object_gid}, + &creds, client_handle_transmission_status_wrapper, state); } @@ -3693,8 +4118,8 @@ int notification_client_list_send_evaluation( } if (source_object_creds) { - if (client->uid != source_object_creds->uid && - client->gid != source_object_creds->gid && + if (client->uid != lttng_credentials_get_uid(source_object_creds) && + client->gid != lttng_credentials_get_gid(source_object_creds) && client->uid != 0) { /* * Client is not allowed to monitor this @@ -3705,7 +4130,7 @@ int notification_client_list_send_evaluation( } } - if (client->uid != trigger_creds->uid && client->gid != trigger_creds->gid) { + if (client->uid != lttng_credentials_get_uid(trigger_creds) && client->gid != lttng_credentials_get_gid(trigger_creds)) { DBG("[notification-thread] Skipping client at it does not have the permission to receive notification for this trigger"); goto skip_client; } @@ -3760,6 +4185,273 @@ end: return ret; } +static struct lttng_trigger_notification *receive_notification(int pipe, + enum lttng_domain_type domain) +{ + int ret; + uint64_t id; + struct lttng_trigger_notification *notification = NULL; + char *capture_buffer = NULL; + size_t capture_buffer_size; + void *reception_buffer; + size_t reception_size; + + struct lttng_ust_trigger_notification ust_notification; + struct lttng_kernel_trigger_notification kernel_notification; + + /* Init lttng_trigger_notification */ + + switch(domain) { + case LTTNG_DOMAIN_UST: + reception_buffer = (void *) &ust_notification; + reception_size = sizeof(ust_notification); + break; + case LTTNG_DOMAIN_KERNEL: + reception_buffer = (void *) &kernel_notification; + reception_size = sizeof(kernel_notification); + break; + default: + assert(0); + } + + /* + * The monitoring pipe only holds messages smaller than PIPE_BUF, + * ensuring that read/write of sampling messages are atomic. + */ + /* TODO: should we read as much as we can ? EWOULDBLOCK? */ + + ret = lttng_read(pipe, reception_buffer, reception_size); + if (ret != reception_size) { + PERROR("Failed to read from event source pipe (fd = %i, size to read=%zu, ret=%d)", + pipe, reception_size, ret); + /* TODO: Should this error out completly. + * This can happen when an app is killed as of today + * ret = -1 cause the whole thread to die and fuck up + * everything. + */ + goto end; + } + + switch(domain) { + case LTTNG_DOMAIN_UST: + id = ust_notification.id; + capture_buffer_size = + ust_notification.capture_buf_size; + break; + case LTTNG_DOMAIN_KERNEL: + id = kernel_notification.id; + capture_buffer_size = + kernel_notification.capture_buf_size; + break; + default: + assert(0); + } + + if (capture_buffer_size == 0) { + capture_buffer = NULL; + goto skip_capture; + } + + capture_buffer = zmalloc(capture_buffer_size); + if (!capture_buffer) { + ERR("[notification-thread] Failed to allocate capture buffer"); + goto end; + } + + /* + * Fetch additional payload (capture). + */ + ret = lttng_read(pipe, capture_buffer, capture_buffer_size); + if (ret != capture_buffer_size) { + ERR("[notification-thread] Failed to read from event source pipe (fd = %i)", + pipe); + /* TODO: Should this error out completly. + * This can happen when an app is killed as of today + * ret = -1 cause the whole thread to die and fuck up + * everything. + */ + goto end; + } + +skip_capture: + notification = lttng_trigger_notification_create( + id, domain, capture_buffer, capture_buffer_size); + if (notification == NULL) { + goto end; + } + + /* Ownership transfered to the lttng_trigger_notification object */ + capture_buffer = NULL; + +end: + free(capture_buffer); + return notification; +} + +int handle_notification_thread_event(struct notification_thread_state *state, + int pipe, + enum lttng_domain_type domain) +{ + int ret; + enum lttng_trigger_status trigger_status; + struct cds_lfht_node *node; + struct cds_lfht_iter iter; + struct notification_trigger_tokens_ht_element *element; + struct lttng_evaluation *evaluation = NULL; + struct lttng_trigger_notification *notification = NULL; + enum action_executor_status executor_status; + struct notification_client_list *client_list = NULL; + const char *trigger_name; + unsigned int capture_count = 0; + + notification = receive_notification(pipe, domain); + if (notification == NULL) { + ERR("[notification-thread] Error receiving notification from tracer (fd = %i, domain = %s)", + pipe, lttng_domain_type_str(domain)); + ret = -1; + goto end; + } + + /* Find triggers associated with this token. */ + rcu_read_lock(); + cds_lfht_lookup(state->trigger_tokens_ht, + hash_key_u64(¬ification->id, lttng_ht_seed), + match_trigger_token, ¬ification->id, &iter); + node = cds_lfht_iter_get_node(&iter); + if (caa_unlikely(!node)) { + /* TODO: is this an error? This might happen if the receive side + * is slow to process event from source and that the trigger was + * removed but the app still kicking. This yield another + * question on the trigger lifetime and when we can remove a + * trigger. How to guarantee that all event with the token idea + * have be processed? Do we want to provide this guarantee? + * + * Update: I have encountered this when using a trigger on + * sched_switch and then removing it. The frequency is quite + * high hence we en up exactly in the mentionned scenario. + * AFAIK this might be the best way to handle this. + */ + ret = 0; + goto end_unlock; + } + element = caa_container_of(node, + struct notification_trigger_tokens_ht_element, + node); + + if (!lttng_trigger_should_fire(element->trigger)) { + ret = 0; + goto end_unlock; + } + + lttng_trigger_fire(element->trigger); + + trigger_status = lttng_trigger_get_name(element->trigger, &trigger_name); + assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + if (LTTNG_CONDITION_STATUS_OK != + lttng_condition_event_rule_get_capture_descriptor_count( + lttng_trigger_get_const_condition( + element->trigger), + &capture_count)) { + ERR("Get capture count"); + ret = -1; + goto end; + } + + if (!notification->capture_buffer && capture_count != 0) { + ERR("Expected capture but capture buffer is null"); + ret = -1; + goto end; + } + + evaluation = lttng_evaluation_event_rule_create( + container_of(lttng_trigger_get_const_condition( + element->trigger), + struct lttng_condition_event_rule, + parent), + trigger_name, + notification->capture_buffer, + notification->capture_buf_size, false); + + if (evaluation == NULL) { + ERR("[notification-thread] Failed to create event rule hit evaluation"); + ret = -1; + goto end_unlock; + } + client_list = get_client_list_from_condition(state, + lttng_trigger_get_const_condition(element->trigger)); + executor_status = action_executor_enqueue(state->executor, + element->trigger, evaluation, NULL, client_list); + switch (executor_status) { + case ACTION_EXECUTOR_STATUS_OK: + ret = 0; + break; + case ACTION_EXECUTOR_STATUS_OVERFLOW: + { + struct notification_client_list_element *client_list_element, + *tmp; + + /* + * Not a fatal error; this is expected and simply means the + * executor has too much work queued already. + */ + ret = 0; + + if (!client_list) { + break; + } + + /* Warn clients that a notification (or more) was dropped. */ + pthread_mutex_lock(&client_list->lock); + cds_list_for_each_entry_safe(client_list_element, tmp, + &client_list->list, node) { + enum client_transmission_status transmission_status; + struct notification_client *client = + client_list_element->client; + + pthread_mutex_lock(&client->lock); + ret = client_notification_overflow(client); + if (ret) { + /* Fatal error. */ + goto next_client; + } + + transmission_status = + client_flush_outgoing_queue(client); + ret = client_handle_transmission_status( + client, transmission_status, state); + if (ret) { + /* Fatal error. */ + goto next_client; + } +next_client: + pthread_mutex_unlock(&client->lock); + if (ret) { + break; + } + } + pthread_mutex_unlock(&client_list->lock); + break; + } + case ACTION_EXECUTOR_STATUS_INVALID: + case ACTION_EXECUTOR_STATUS_ERROR: + /* Fatal error, shut down everything. */ + ERR("Fatal error encoutered while enqueuing action"); + ret = -1; + goto end_unlock; + default: + /* Unhandled error. */ + abort(); + } + +end_unlock: + lttng_trigger_notification_destroy(notification); + notification_client_list_put(client_list); + rcu_read_unlock(); +end: + return ret; +} + int handle_notification_thread_channel_sample( struct notification_thread_state *state, int pipe, enum lttng_domain_type domain) @@ -3813,8 +4505,7 @@ int handle_notification_thread_channel_sample( */ DBG("[notification-thread] Received a sample for an unknown channel from consumerd, key = %" PRIu64 " in %s domain", latest_sample.key.key, - domain == LTTNG_DOMAIN_KERNEL ? "kernel" : - "user space"); + lttng_domain_type_str(domain)); goto end_unlock; } channel_info = caa_container_of(node, struct channel_info, @@ -3894,8 +4585,8 @@ int handle_notification_thread_channel_sample( } channel_creds = (typeof(channel_creds)) { - .uid = channel_info->session_info->uid, - .gid = channel_info->session_info->gid, + .uid = LTTNG_OPTIONAL_INIT_VALUE(channel_info->session_info->uid), + .gid = LTTNG_OPTIONAL_INIT_VALUE(channel_info->session_info->gid), }; trigger_list = caa_container_of(node, struct lttng_channel_trigger_list, @@ -3903,6 +4594,7 @@ int handle_notification_thread_channel_sample( cds_list_for_each_entry(trigger_list_element, &trigger_list->list, node) { const struct lttng_condition *condition; + const struct lttng_action *action; struct lttng_trigger *trigger; struct notification_client_list *client_list = NULL; struct lttng_evaluation *evaluation = NULL; @@ -3912,6 +4604,18 @@ int handle_notification_thread_channel_sample( trigger = trigger_list_element->trigger; condition = lttng_trigger_get_const_condition(trigger); assert(condition); + action = lttng_trigger_get_const_action(trigger); + + if (!lttng_trigger_should_fire(trigger)) { + goto put_list; + } + + lttng_trigger_fire(trigger); + + /* Notify actions are the only type currently supported. */ + /* TODO support other type of action */ + assert(lttng_action_get_type(action) == + LTTNG_ACTION_TYPE_NOTIFY); /* * Check if any client is subscribed to the result of this diff --git a/src/bin/lttng-sessiond/notification-thread-events.h b/src/bin/lttng-sessiond/notification-thread-events.h index 2f699bf45..7affb752d 100644 --- a/src/bin/lttng-sessiond/notification-thread-events.h +++ b/src/bin/lttng-sessiond/notification-thread-events.h @@ -44,4 +44,8 @@ int handle_notification_thread_channel_sample( struct notification_thread_state *state, int pipe, enum lttng_domain_type domain); +int handle_notification_thread_event( + struct notification_thread_state *state, int pipe, + enum lttng_domain_type domain); + #endif /* NOTIFICATION_THREAD_EVENTS_H */ diff --git a/src/bin/lttng-sessiond/notification-thread-internal.h b/src/bin/lttng-sessiond/notification-thread-internal.h index eb23d1f78..53ee6064c 100644 --- a/src/bin/lttng-sessiond/notification-thread-internal.h +++ b/src/bin/lttng-sessiond/notification-thread-internal.h @@ -75,6 +75,17 @@ struct channel_info { struct rcu_head rcu_node; }; +/* + * Facilities to carry the different notifications type in the action + * processing code path. + */ +struct lttng_trigger_notification { + uint64_t id; + enum lttng_domain_type type; + size_t capture_buf_size; + char *capture_buffer; +}; + struct notification_client_list_element { struct notification_client *client; struct cds_list_head node; @@ -239,4 +250,18 @@ 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_trigger_notification *lttng_trigger_notification_create( + uint64_t id, + enum lttng_domain_type domain, + char *payload, + size_t payload_size); + +LTTNG_HIDDEN +void lttng_trigger_notification_destroy( + struct lttng_trigger_notification *trigger_notification); + #endif /* NOTIFICATION_THREAD_INTERNAL_H */ diff --git a/src/bin/lttng-sessiond/notification-thread.c b/src/bin/lttng-sessiond/notification-thread.c index 3ae8741d6..5a7c0075a 100644 --- a/src/bin/lttng-sessiond/notification-thread.c +++ b/src/bin/lttng-sessiond/notification-thread.c @@ -28,11 +28,17 @@ #include "lttng-sessiond.h" #include "health-sessiond.h" #include "thread.h" +#include "testpoint.h" + +#include "kernel.h" +#include #include #include #include + +int trigger_consumption_paused; /* * Destroy the thread data previously created by the init function. */ @@ -70,14 +76,21 @@ void notification_thread_handle_destroy( PERROR("close kernel consumer channel monitoring pipe"); } } + end: free(handle); } +/* + * TODO: refactor this if needed. Lifetime of the kernel notification event source. + * The kernel_notification_monitor_fd ownwership remain to the main thread. + * This is because we need to close this fd before removing the modules. + */ struct notification_thread_handle *notification_thread_handle_create( struct lttng_pipe *ust32_channel_monitor_pipe, struct lttng_pipe *ust64_channel_monitor_pipe, - struct lttng_pipe *kernel_channel_monitor_pipe) + struct lttng_pipe *kernel_channel_monitor_pipe, + int kernel_notification_monitor_fd) { int ret; struct notification_thread_handle *handle; @@ -135,6 +148,12 @@ struct notification_thread_handle *notification_thread_handle_create( } else { handle->channel_monitoring_pipes.kernel_consumer = -1; } + + CDS_INIT_LIST_HEAD(&handle->event_trigger_sources.list); + ret = pthread_mutex_init(&handle->event_trigger_sources.lock, NULL); + if (ret) { + goto error; + } end: return handle; error: @@ -305,7 +324,7 @@ int init_poll_set(struct lttng_poll_event *poll_set, goto error; } if (handle->channel_monitoring_pipes.kernel_consumer < 0) { - goto end; + goto skip_kernel_consumer; } ret = lttng_poll_add(poll_set, handle->channel_monitoring_pipes.kernel_consumer, @@ -314,6 +333,8 @@ int init_poll_set(struct lttng_poll_event *poll_set, ERR("[notification-thread] Failed to add kernel channel monitoring pipe fd to pollset"); goto error; } + +skip_kernel_consumer: end: return ret; error: @@ -363,6 +384,14 @@ void fini_thread_state(struct notification_thread_state *state) ret = cds_lfht_destroy(state->sessions_ht, NULL); assert(!ret); } + if (state->triggers_by_name_uid_ht) { + ret = cds_lfht_destroy(state->triggers_by_name_uid_ht, NULL); + assert(!ret); + } + if (state->trigger_tokens_ht) { + ret = cds_lfht_destroy(state->trigger_tokens_ht, NULL); + assert(!ret); + } /* * Must be destroyed after all channels have been destroyed. * See comment in struct lttng_session_trigger_list. @@ -404,6 +433,7 @@ int init_thread_state(struct notification_thread_handle *handle, memset(state, 0, sizeof(*state)); state->notification_channel_socket = -1; + state->trigger_id.token_generator = 1; lttng_poll_init(&state->events); ret = notification_channel_socket_create(); @@ -476,7 +506,17 @@ int init_thread_state(struct notification_thread_handle *handle, if (!state->triggers_ht) { goto error; } + state->triggers_by_name_uid_ht = cds_lfht_new(DEFAULT_HT_SIZE, + 1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL); + if (!state->triggers_by_name_uid_ht) { + goto error; + } + state->trigger_tokens_ht = cds_lfht_new(DEFAULT_HT_SIZE, + 1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL); + if (!state->trigger_tokens_ht) { + goto error; + } state->executor = action_executor_create(handle); if (!state->executor) { goto error; @@ -525,6 +565,63 @@ end: return ret; } +static int handle_trigger_event_pipe(int fd, + enum lttng_domain_type domain, + uint32_t revents, + struct notification_thread_state *state) +{ + int ret = 0; + + if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { + ret = lttng_poll_del(&state->events, fd); + if (ret) { + ERR("[notification-thread] Failed to remove event monitoring pipe from poll set"); + } + goto end; + } + + if (testpoint(sessiond_handle_trigger_event_pipe)) { + ret = 0; + goto end; + } + + if (caa_unlikely(trigger_consumption_paused)) { + DBG("Trigger consumption paused, sleeping..."); + sleep(1); + goto end; + } + + ret = handle_notification_thread_event(state, fd, domain); + if (ret) { + ERR("[notification-thread] Event sample handling error occurred for fd: %d", fd); + ret = -1; + goto end; + } + +end: + return ret; +} + +/* + * Return the event source domain type via parameter. + */ +static bool fd_is_event_source(struct notification_thread_handle *handle, int fd, enum lttng_domain_type *domain) +{ + struct notification_event_trigger_source_element *source_element, *tmp; + + assert(domain); + + cds_list_for_each_entry_safe(source_element, tmp, + &handle->event_trigger_sources.list, node) { + if (source_element->fd != fd) { + continue; + } + *domain = source_element->domain; + return true; + } + return false; +} + /* * This thread services notification channel clients and commands received * from various lttng-sessiond components over a command queue. @@ -535,6 +632,7 @@ void *thread_notification(void *data) int ret; struct notification_thread_handle *handle = data; struct notification_thread_state state; + enum lttng_domain_type domain; DBG("[notification-thread] Started notification thread"); @@ -554,6 +652,10 @@ void *thread_notification(void *data) goto end; } + if (testpoint(sessiond_thread_notification)) { + goto end; + } + while (true) { int fd_count, i; @@ -612,6 +714,11 @@ void *thread_notification(void *data) if (ret) { goto error; } + } else if (fd_is_event_source(handle, fd, &domain)) { + ret = handle_trigger_event_pipe(fd, domain, revents, &state); + if (ret) { + goto error; + } } else { /* Activity on a client's socket. */ if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { diff --git a/src/bin/lttng-sessiond/notification-thread.h b/src/bin/lttng-sessiond/notification-thread.h index 134804f9d..2afcfe15f 100644 --- a/src/bin/lttng-sessiond/notification-thread.h +++ b/src/bin/lttng-sessiond/notification-thread.h @@ -14,15 +14,29 @@ #include #include #include +#include #include #include #include #include #include - typedef uint64_t notification_client_id; +struct notification_event_trigger_source_element { + int fd; + enum lttng_domain_type domain; + struct cds_list_head node; +}; + +struct notification_trigger_tokens_ht_element { + uint64_t token; + struct lttng_trigger *trigger; + struct cds_lfht_node node; + /* call_rcu delayed reclaim. */ + struct rcu_head rcu_node; +}; + struct notification_thread_handle { /* * Queue of struct notification command. @@ -43,6 +57,15 @@ struct notification_thread_handle { int ust64_consumer; int kernel_consumer; } channel_monitoring_pipes; + /* + * Read side of pipes used to reveice event trigger generetated by + * registered applications + */ + struct { + /* List of notification_event_trigger_source_element */ + struct cds_list_head list; + pthread_mutex_t lock; + } event_trigger_sources; /* Used to wait for the launch of the notification thread. */ sem_t ready; }; @@ -108,9 +131,14 @@ struct notification_thread_handle { * channels through their struct channel_info (ref-counting is used). * * - triggers_ht: - * associates a condition to a struct lttng_trigger_ht_element. + * associates a trigger to a struct lttng_trigger_ht_element. * The hash table holds the ownership of the * lttng_trigger_ht_elements along with the triggers themselves. + * - triggers_by_name_uid_ht: + * associates a trigger (name, uid) tuple to + * a struct lttng_trigger_ht_element. + * The hash table does not hold any ownership and is used strictly + * for lookup on registration. * * The thread reacts to the following internal events: * 1) creation of a tracing channel, @@ -154,6 +182,7 @@ struct notification_thread_handle { * notification_trigger_clients_ht, * - add trigger to channel_triggers_ht (if applicable), * - add trigger to session_triggers_ht (if applicable), + * - add trigger to triggers_by_name_uid_ht * - add trigger to triggers_ht * - evaluate the trigger's condition right away to react if that condition * is true from the beginning. @@ -163,6 +192,7 @@ struct notification_thread_handle { * - remove the trigger from the notification_trigger_clients_ht, * - remove trigger from channel_triggers_ht (if applicable), * - remove trigger from session_triggers_ht (if applicable), + * - remove trigger from triggers_by_name_uid_ht * - remove trigger from triggers_ht * * 5) Reception of a channel monitor sample from the consumer daemon @@ -210,6 +240,12 @@ struct notification_thread_state { struct cds_lfht *channels_ht; struct cds_lfht *sessions_ht; struct cds_lfht *triggers_ht; + struct cds_lfht *triggers_by_name_uid_ht; + struct cds_lfht *trigger_tokens_ht; + struct { + uint64_t token_generator; + uint64_t name_offset; + } trigger_id; notification_client_id next_notification_client_id; struct action_executor *executor; }; @@ -218,7 +254,8 @@ struct notification_thread_state { struct notification_thread_handle *notification_thread_handle_create( struct lttng_pipe *ust32_channel_monitor_pipe, struct lttng_pipe *ust64_channel_monitor_pipe, - struct lttng_pipe *kernel_channel_monitor_pipe); + struct lttng_pipe *kernel_channel_monitor_pipe, + int kernel_notification_monitor_fd); void notification_thread_handle_destroy( struct notification_thread_handle *handle); struct lttng_thread *launch_notification_thread( diff --git a/src/bin/lttng-sessiond/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/rotate.c b/src/bin/lttng-sessiond/rotate.c index 30ec24124..77524ed8e 100644 --- a/src/bin/lttng-sessiond/rotate.c +++ b/src/bin/lttng-sessiond/rotate.c @@ -48,8 +48,8 @@ int subscribe_session_consumed_size_rotation(struct ltt_session *session, uint64 enum lttng_notification_channel_status nc_status; struct lttng_action *action; const struct lttng_credentials session_creds = { - .uid = session->uid, - .gid = session->gid, + .uid = LTTNG_OPTIONAL_INIT_VALUE(session->uid), + .gid = LTTNG_OPTIONAL_INIT_VALUE(session->gid), }; session->rotate_condition = lttng_condition_session_consumed_size_create(); diff --git a/src/bin/lttng-sessiond/session.c b/src/bin/lttng-sessiond/session.c index f8134f8ad..a4d116339 100644 --- a/src/bin/lttng-sessiond/session.c +++ b/src/bin/lttng-sessiond/session.c @@ -574,8 +574,8 @@ struct lttng_trace_chunk *session_create_new_trace_chunk( const char *base_path; struct lttng_directory_handle *session_output_directory = NULL; const struct lttng_credentials session_credentials = { - .uid = session->uid, - .gid = session->gid, + .uid = LTTNG_OPTIONAL_INIT_VALUE(session->uid), + .gid = LTTNG_OPTIONAL_INIT_VALUE(session->gid), }; uint64_t next_chunk_id; const struct consumer_output *output; diff --git a/src/bin/lttng-sessiond/sessiond-config.c b/src/bin/lttng-sessiond/sessiond-config.c index f0ab4ce04..3a9b7725d 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 }, + .trigger_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..7e0a789ef 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 trigger_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..37bb93b84 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_trigger_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 dfdc9fd14..198e1dd4f 100644 --- a/src/bin/lttng-sessiond/trace-kernel.c +++ b/src/bin/lttng-sessiond/trace-kernel.c @@ -13,9 +13,18 @@ #include #include +#include #include #include - +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -62,7 +71,7 @@ struct ltt_kernel_channel *trace_kernel_get_channel_by_name( struct ltt_kernel_event *trace_kernel_find_event( char *name, struct ltt_kernel_channel *channel, enum lttng_event_type type, - struct lttng_filter_bytecode *filter) + struct lttng_bytecode *filter) { struct ltt_kernel_event *ev; int found = 0; @@ -321,7 +330,7 @@ error: */ enum lttng_error_code trace_kernel_create_event( struct lttng_event *ev, char *filter_expression, - struct lttng_filter_bytecode *filter, + struct lttng_bytecode *filter, struct ltt_kernel_event **kernel_event) { enum lttng_error_code ret; @@ -468,6 +477,212 @@ error: return ret; } +/* + * Allocate and initialize a kernel token event rule. + * + * Return pointer to structure or NULL. + */ +enum lttng_error_code trace_kernel_create_token_event_rule( + struct lttng_trigger *trigger, + uint64_t token, + uint64_t trigger_error_counter_index, + struct ltt_kernel_token_event_rule **kernel_token_event_rule) +{ + enum lttng_error_code ret = LTTNG_OK; + struct ltt_kernel_token_event_rule *local_kernel_token_event_rule; + const struct lttng_condition *condition = NULL; + const struct lttng_event_rule *event_rule = NULL; + + assert(kernel_token_event_rule); + + condition = lttng_trigger_get_condition(trigger); + assert(condition); + assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + + assert(lttng_condition_event_rule_get_rule(condition, &event_rule) == LTTNG_CONDITION_STATUS_OK); + assert(event_rule); + assert(lttng_event_rule_get_type(event_rule) != LTTNG_EVENT_RULE_TYPE_UNKNOWN); + + local_kernel_token_event_rule = zmalloc(sizeof(struct ltt_kernel_token_event_rule)); + if (local_kernel_token_event_rule == NULL) { + PERROR("Failed to allocate ltt_kernel_token_event_rule structure"); + ret = LTTNG_ERR_NOMEM; + goto error; + } + + local_kernel_token_event_rule->fd = -1; + local_kernel_token_event_rule->enabled = 1; + local_kernel_token_event_rule->token = token; + local_kernel_token_event_rule->error_counter_index = trigger_error_counter_index; + + /* Get the reference of the event rule */ + lttng_trigger_get(trigger); + + local_kernel_token_event_rule->trigger = trigger; + /* The event rule still own the filter and bytecode */ + local_kernel_token_event_rule->filter = lttng_event_rule_get_filter_bytecode(event_rule); + + DBG3("[trace] Kernel token event rule %" PRIu64 " allocated", local_kernel_token_event_rule->token); +error: + *kernel_token_event_rule = local_kernel_token_event_rule; + return ret; +} + +/* + * Initialize a kernel trigger from an event rule. + */ +enum lttng_error_code trace_kernel_init_trigger_from_event_rule(const struct lttng_event_rule *rule, + struct lttng_kernel_trigger *kernel_trigger) +{ + enum lttng_error_code ret; + enum lttng_event_rule_status status; + const char *name = NULL; + + /* TODO: do this for now but have disucssion on if this could be the + * responsability of the event_rule itself ala + * "lttng_even_rule_generate_kernel_trigger" + */ + switch (lttng_event_rule_get_type(rule)) { + case LTTNG_EVENT_RULE_TYPE_KPROBE: + { + uint64_t address = 0, offset = 0; + const char *symbol_name = NULL; + const struct lttng_kernel_probe_location *location = NULL; + enum lttng_kernel_probe_location_status k_status; + + status = lttng_event_rule_kprobe_get_location(rule, &location); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + switch (lttng_kernel_probe_location_get_type(location)) { + case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS: + { + k_status = lttng_kernel_probe_location_address_get_address( + location, &address); + if (k_status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) { + ERR("Getting kernel probe address failed."); + } + + break; + } + case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET: + { + k_status = lttng_kernel_probe_location_symbol_get_offset( + location, &offset); + if (k_status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) { + ERR("Getting kernel probe symbol offset failed."); + } + + symbol_name = lttng_kernel_probe_location_symbol_get_name( + location); + break; + } + default: + ERR("Unknown kernel probe location type (%d)", + lttng_kernel_probe_location_get_type( + location)); + ret = LTTNG_ERR_INVALID; + goto error; + } + + kernel_trigger->instrumentation = LTTNG_KERNEL_KPROBE; + kernel_trigger->u.kprobe.addr = address; + kernel_trigger->u.kprobe.offset = offset; + if (symbol_name) { + strncpy(kernel_trigger->u.kprobe.symbol_name, + symbol_name, LTTNG_KERNEL_SYM_NAME_LEN); + } + kernel_trigger->u.kprobe + .symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = + '\0'; + (void) lttng_event_rule_kprobe_get_name(rule, &name); + ret = LTTNG_OK; + break; + } + case LTTNG_EVENT_RULE_TYPE_UPROBE: + { + const struct lttng_userspace_probe_location* location = NULL; + const struct lttng_userspace_probe_location_lookup_method *lookup = NULL; + + status = lttng_event_rule_uprobe_get_location(rule, &location); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + kernel_trigger->instrumentation = LTTNG_KERNEL_UPROBE; + + /* + * Only the elf lookup method is supported at the moment. + */ + lookup = lttng_userspace_probe_location_get_lookup_method( + location); + if (!lookup) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + /* + * From the kernel tracer's perspective, all userspace probe + * event types are all the same: a file and an offset. + */ + switch (lttng_userspace_probe_location_lookup_method_get_type(lookup)) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: + /* Get the file descriptor on the target binary. */ + kernel_trigger->u.uprobe.fd = + lttng_userspace_probe_location_function_get_binary_fd(location); + + break; + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: + /* Get the file descriptor on the target binary. */ + kernel_trigger->u.uprobe.fd = + lttng_userspace_probe_location_tracepoint_get_binary_fd(location); + break; + default: + DBG("Unsupported lookup method type"); + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + (void) lttng_event_rule_uprobe_get_name(rule, &name); + + ret = LTTNG_OK; + break; + } + case LTTNG_EVENT_RULE_TYPE_KRETPROBE: + assert("Not supported" && 0); + ret = LTTNG_OK; + break; + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + /* TODO: assert his is a kernel domain event-rule */ + kernel_trigger->instrumentation = LTTNG_KERNEL_TRACEPOINT; + (void) lttng_event_rule_tracepoint_get_pattern(rule, &name); + ret = LTTNG_OK; + break; + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + kernel_trigger->instrumentation = LTTNG_KERNEL_SYSCALL; + (void) lttng_event_rule_syscall_get_pattern(rule, &name); + ret = LTTNG_OK; + break; + default: + ERR("Unknown kernel event rule instrumentation type (%d)", lttng_event_rule_get_type(rule)); + ret = LTTNG_ERR_INVALID; + goto error; + } + + /* + * WTF is LTTNG_EVENT_ALL??? and LTTNG_EVENT_FUNTION_ENTRY????? + */ + + /* Copy event name */ + strncpy(kernel_trigger->name, name, LTTNG_KERNEL_SYM_NAME_LEN); + kernel_trigger->name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; + +error: + return ret; +} /* * Allocate and initialize a kernel metadata. * @@ -623,6 +838,30 @@ void trace_kernel_destroy_event(struct ltt_kernel_event *event) free(event); } +/* + * Cleanup kernel event structure. + */ +void trace_kernel_destroy_token_event_rule(struct ltt_kernel_token_event_rule *event) +{ + /* TODO: review in depth to ensure adequate disposing */ + assert(event); + + if (event->fd >= 0) { + int ret; + + DBG("[trace] Closing ,token event rule fd %d", event->fd); + /* Close kernel fd */ + ret = close(event->fd); + if (ret) { + PERROR("close"); + } + } else { + DBG("[trace] Tearing down token event rule (no associated fd)"); + } + + lttng_trigger_put(event->trigger); + free(event); +} /* * Cleanup kernel context structure. */ diff --git a/src/bin/lttng-sessiond/trace-kernel.h b/src/bin/lttng-sessiond/trace-kernel.h index dd6f21edf..0721a57d3 100644 --- a/src/bin/lttng-sessiond/trace-kernel.h +++ b/src/bin/lttng-sessiond/trace-kernel.h @@ -23,6 +23,11 @@ struct ltt_kernel_event_list { struct cds_list_head head; }; +/* Kernel event rule token list */ +struct ltt_kernel_token_event_rule_ht { + struct cds_lfht *ht; +}; + /* Channel stream list */ struct ltt_kernel_stream_list { struct cds_list_head head; @@ -48,8 +53,21 @@ struct ltt_kernel_event { struct lttng_kernel_event *event; struct cds_list_head list; char *filter_expression; - struct lttng_filter_bytecode *filter; + struct lttng_bytecode *filter; + struct lttng_userspace_probe_location *userspace_probe_location; +}; + +/* Kernel event */ +struct ltt_kernel_token_event_rule { + int fd; + uint64_t error_counter_index; + int enabled; + enum lttng_event_type type; + struct lttng_trigger *trigger; + uint64_t token; + const struct lttng_bytecode *filter; struct lttng_userspace_probe_location *userspace_probe_location; + struct cds_lfht_node ht_node; }; /* Kernel channel */ @@ -132,7 +150,7 @@ struct ltt_kernel_event *trace_kernel_get_event_by_name( struct ltt_kernel_event *trace_kernel_find_event( char *name, struct ltt_kernel_channel *channel, enum lttng_event_type type, - struct lttng_filter_bytecode *filter); + struct lttng_bytecode *filter); struct ltt_kernel_channel *trace_kernel_get_channel_by_name( const char *name, struct ltt_kernel_session *session); @@ -143,15 +161,22 @@ struct ltt_kernel_session *trace_kernel_create_session(void); struct ltt_kernel_channel *trace_kernel_create_channel( struct lttng_channel *chan); enum lttng_error_code trace_kernel_create_event(struct lttng_event *ev, - char *filter_expression, struct lttng_filter_bytecode *filter, + char *filter_expression, struct lttng_bytecode *filter, struct ltt_kernel_event **kernel_event); struct ltt_kernel_metadata *trace_kernel_create_metadata(void); struct ltt_kernel_stream *trace_kernel_create_stream(const char *name, unsigned int count); struct ltt_kernel_context *trace_kernel_create_context( struct lttng_kernel_context *ctx); +enum lttng_error_code trace_kernel_create_token_event_rule( + struct lttng_trigger *trigger, + uint64_t token, + uint64_t trigger_error_counter_index, + struct ltt_kernel_token_event_rule **kernel_token_event_rule); struct ltt_kernel_context *trace_kernel_copy_context( struct ltt_kernel_context *ctx); +enum lttng_error_code trace_kernel_init_trigger_from_event_rule(const struct lttng_event_rule *rule, + struct lttng_kernel_trigger *kernel_trigger); /* * Destroy functions free() the data structure and remove from linked list if @@ -163,6 +188,7 @@ void trace_kernel_destroy_channel(struct ltt_kernel_channel *channel); void trace_kernel_destroy_event(struct ltt_kernel_event *event); void trace_kernel_destroy_stream(struct ltt_kernel_stream *stream); void trace_kernel_destroy_context(struct ltt_kernel_context *ctx); +void trace_kernel_destroy_token_event_rule(struct ltt_kernel_token_event_rule *rule); void trace_kernel_free_session(struct ltt_kernel_session *session); #endif /* _LTT_TRACE_KERNEL_H */ diff --git a/src/bin/lttng-sessiond/trace-ust.c b/src/bin/lttng-sessiond/trace-ust.c index 7ee379ef8..527b354f1 100644 --- a/src/bin/lttng-sessiond/trace-ust.c +++ b/src/bin/lttng-sessiond/trace-ust.c @@ -194,7 +194,7 @@ error: * MUST be acquired before calling this. */ struct ltt_ust_event *trace_ust_find_event(struct lttng_ht *ht, - char *name, struct lttng_filter_bytecode *filter, + char *name, struct lttng_bytecode *filter, enum lttng_ust_loglevel_type loglevel_type, int loglevel_value, struct lttng_event_exclusion *exclusion) { @@ -446,7 +446,7 @@ end: */ enum lttng_error_code trace_ust_create_event(struct lttng_event *ev, char *filter_expression, - struct lttng_filter_bytecode *filter, + struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, bool internal_event, struct ltt_ust_event **ust_event) diff --git a/src/bin/lttng-sessiond/trace-ust.h b/src/bin/lttng-sessiond/trace-ust.h index ecd0b8771..53e1ab2b5 100644 --- a/src/bin/lttng-sessiond/trace-ust.h +++ b/src/bin/lttng-sessiond/trace-ust.h @@ -24,7 +24,7 @@ struct agent; struct ltt_ust_ht_key { const char *name; - const struct lttng_filter_bytecode *filter; + const struct lttng_bytecode *filter; enum lttng_ust_loglevel_type loglevel_type; int loglevel_value; const struct lttng_event_exclusion *exclusion; @@ -43,7 +43,7 @@ struct ltt_ust_event { struct lttng_ust_event attr; struct lttng_ht_node_str node; char *filter_expression; - struct lttng_filter_bytecode *filter; + struct lttng_bytecode *filter; struct lttng_event_exclusion *exclusion; /* * An internal event is an event which was created by the session daemon @@ -182,7 +182,7 @@ int trace_ust_ht_match_event_by_name(struct cds_lfht_node *node, * Lookup functions. NULL is returned if not found. */ struct ltt_ust_event *trace_ust_find_event(struct lttng_ht *ht, - char *name, struct lttng_filter_bytecode *filter, + char *name, struct lttng_bytecode *filter, enum lttng_ust_loglevel_type loglevel_type, int loglevel_value, struct lttng_event_exclusion *exclusion); struct ltt_ust_channel *trace_ust_find_channel_by_name(struct lttng_ht *ht, @@ -198,7 +198,7 @@ struct ltt_ust_channel *trace_ust_create_channel(struct lttng_channel *attr, enum lttng_domain_type domain); enum lttng_error_code trace_ust_create_event(struct lttng_event *ev, char *filter_expression, - struct lttng_filter_bytecode *filter, + struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, bool internal_event, struct ltt_ust_event **ust_event); struct ltt_ust_context *trace_ust_create_context( @@ -270,7 +270,7 @@ struct ltt_ust_channel *trace_ust_create_channel(struct lttng_channel *attr, static inline enum lttng_error_code trace_ust_create_event(struct lttng_event *ev, const char *filter_expression, - struct lttng_filter_bytecode *filter, + struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, bool internal_event, struct ltt_ust_event **ust_event) { @@ -310,7 +310,7 @@ int trace_ust_match_context(const struct ltt_ust_context *uctx, } static inline struct ltt_ust_event *trace_ust_find_event(struct lttng_ht *ht, - char *name, struct lttng_filter_bytecode *filter, + char *name, struct lttng_bytecode *filter, enum lttng_ust_loglevel_type loglevel_type, int loglevel_value, struct lttng_event_exclusion *exclusion) { diff --git a/src/bin/lttng-sessiond/trigger-error-accounting.c b/src/bin/lttng-sessiond/trigger-error-accounting.c new file mode 100644 index 000000000..9b50b6412 --- /dev/null +++ b/src/bin/lttng-sessiond/trigger-error-accounting.c @@ -0,0 +1,668 @@ +/* + * 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 "lttng-ust-error.h" +#include "trigger-error-accounting.h" +#include "ust-app.h" + +struct index_ht_entry { + struct lttng_ht_node_u64 node; + uint64_t error_counter_index; + struct rcu_head rcu_head; +}; + +struct error_account_entry { + struct lttng_ht_node_u64 node; + struct rcu_head rcu_head; + struct lttng_ust_object_data *counter; + struct lttng_ust_object_data **cpu_counters; + struct ustctl_daemon_counter *daemon_counter; +}; + +struct kernel_error_account_entry { + int kernel_trigger_error_counter_fd; +}; + +static struct kernel_error_account_entry kernel_error_accountant = { 0 }; + +/* Hashtable mapping trigger token to index_ht_entry */ +static struct lttng_ht *error_counter_indexes_ht; + +/* Hashtable mapping uid to error_account_entry */ +static struct lttng_ht *error_counter_uid_ht; + +static uint64_t error_counter_size = 0; +struct lttng_index_allocator *index_allocator; + + +void trigger_error_accounting_init(uint64_t nb_bucket) +{ + struct lttng_index_allocator *error_counter_index_allocator; + + error_counter_index_allocator = lttng_index_allocator_create(nb_bucket); + if (!error_counter_index_allocator) { + ERR("Failed to allocate trigger error counter index"); + goto error_index_allocator; + } + + index_allocator = error_counter_index_allocator; + + error_counter_indexes_ht = lttng_ht_new(16, LTTNG_HT_TYPE_U64); + error_counter_uid_ht = lttng_ht_new(16, LTTNG_HT_TYPE_U64); + error_counter_size = nb_bucket; + +error_index_allocator: + return; +} + +static +enum trigger_error_accounting_status get_error_counter_index_for_token( + uint64_t tracer_token, uint64_t *error_counter_index) +{ + struct lttng_ht_node_u64 *node; + struct lttng_ht_iter iter; + struct index_ht_entry *index_entry;; + enum trigger_error_accounting_status status; + + lttng_ht_lookup(error_counter_indexes_ht, &tracer_token, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if (node) { + index_entry = caa_container_of(node, struct index_ht_entry, node); + *error_counter_index = index_entry->error_counter_index; + status = TRIGGER_ERROR_ACCOUNTING_STATUS_OK; + } else { + status = TRIGGER_ERROR_ACCOUNTING_STATUS_NOT_FOUND; + } + + return status; +} + +#ifdef HAVE_LIBLTTNG_UST_CTL +static +struct error_account_entry *get_uid_accounting_entry(const struct ust_app *app) +{ + struct error_account_entry *entry; + struct lttng_ht_node_u64 *node; + struct lttng_ht_iter iter; + uint64_t key = app->uid; + + lttng_ht_lookup(error_counter_uid_ht, &key, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if(node == NULL) { + entry = NULL; + } else { + entry = caa_container_of(node, struct error_account_entry, node); + } + + return entry; +} + +static +struct error_account_entry *create_uid_accounting_entry( + const struct ust_app *app) +{ + int i, ret, nr_counter_cpu_fds; + struct ustctl_counter_dimension dimension[1] = {0}; + struct ustctl_daemon_counter *daemon_counter; + struct lttng_ust_object_data *counter, **counter_cpus; + int *counter_cpu_fds; + struct error_account_entry *entry = NULL; + + entry = zmalloc(sizeof(struct error_account_entry)); + if (!entry) { + PERROR("Allocating trigger error acounting entry") + goto error; + } + + nr_counter_cpu_fds = ustctl_get_nr_cpu_per_counter(); + counter_cpu_fds = zmalloc(nr_counter_cpu_fds * sizeof(*counter_cpu_fds)); + if (!counter_cpu_fds) { + ret = -1; + goto error_counter_cpu_fds_alloc; + } + + counter_cpus = zmalloc(nr_counter_cpu_fds * sizeof(*counter_cpus)); + if (!counter_cpus) { + ret = -1; + goto error_counter_cpus_alloc; + } + + for (i = 0; i < nr_counter_cpu_fds; i++) { + counter_cpu_fds[i] = shm_create_anonymous("trigger-error-accounting"); + //FIXME error handling + } + + + dimension[0].size = error_counter_size; + dimension[0].has_underflow = false; + dimension[0].has_overflow = false; + + daemon_counter = ustctl_create_counter(1, dimension, 0, -1, + nr_counter_cpu_fds, counter_cpu_fds, + USTCTL_COUNTER_BITNESS_32, + USTCTL_COUNTER_ARITHMETIC_MODULAR, + USTCTL_COUNTER_ALLOC_PER_CPU); + assert(daemon_counter); + + ret = ustctl_create_counter_data(daemon_counter, &counter); + assert(ret == 0); + + for (i = 0; i < nr_counter_cpu_fds; i++) { + ret = ustctl_create_counter_cpu_data(daemon_counter, i, + &counter_cpus[i]); + assert(ret == 0); + } + + entry->daemon_counter = daemon_counter; + entry->counter = counter; + entry->cpu_counters = counter_cpus; + + lttng_ht_node_init_u64(&entry->node, app->uid); + lttng_ht_add_unique_u64(error_counter_uid_ht, &entry->node); + + goto end; + +error_counter_cpus_alloc: + free(counter_cpu_fds); +error_counter_cpu_fds_alloc: + free(entry); +error: + entry = NULL; +end: + return entry; +} + +static +enum trigger_error_accounting_status send_counter_data_to_ust( + struct ust_app *app, + struct lttng_ust_object_data *new_counter) +{ + int ret; + enum trigger_error_accounting_status status; + + /* Attach counter to trigger group */ + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_send_counter_data_to_ust(app->sock, + app->token_communication.handle->handle, new_counter); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("Error ustctl send counter data to app pid: %d with ret %d", + app->pid, ret); + status = TRIGGER_ERROR_ACCOUNTING_STATUS_ERR; + } else { + DBG3("UST app send counter data to ust failed. Application is dead."); + status = TRIGGER_ERROR_ACCOUNTING_STATUS_APP_DEAD; + } + goto end; + } + + status = TRIGGER_ERROR_ACCOUNTING_STATUS_OK; +end: + return status; +} + +static +enum trigger_error_accounting_status send_counter_cpu_data_to_ust( + struct ust_app *app, + struct lttng_ust_object_data *new_counter, + struct lttng_ust_object_data *new_counter_cpu) +{ + int ret; + enum trigger_error_accounting_status status; + + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_send_counter_cpu_data_to_ust(app->sock, + new_counter, new_counter_cpu); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("Error ustctl send counter cpu data to app pid: %d with ret %d", + app->pid, ret); + status = TRIGGER_ERROR_ACCOUNTING_STATUS_ERR; + } else { + DBG3("UST app send counter cpu data to ust failed. Application is dead."); + status = TRIGGER_ERROR_ACCOUNTING_STATUS_APP_DEAD; + } + goto end; + } + + status = TRIGGER_ERROR_ACCOUNTING_STATUS_OK; +end: + return status; +} + +enum trigger_error_accounting_status trigger_error_accounting_register_app( + struct ust_app *app) +{ + int ret; + uint64_t i, nr_counter_cpu_fds; + struct lttng_ust_object_data *new_counter; + struct error_account_entry *entry; + enum trigger_error_accounting_status status; + + /* + * Check if we already have a error counter for the user id of this + * app. If not, create one. + */ + entry = get_uid_accounting_entry(app); + if (entry == NULL) { + entry = create_uid_accounting_entry(app); + } + + /* Duplicate counter object data*/ + ret = ustctl_duplicate_ust_object_data(&new_counter, + entry->counter); + assert(ret == 0); + + status = send_counter_data_to_ust(app, new_counter); + if (status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) { + goto end; + } + + nr_counter_cpu_fds = ustctl_get_nr_cpu_per_counter(); + for (i = 0; i < nr_counter_cpu_fds; i++) { + struct lttng_ust_object_data *new_counter_cpu = NULL; + + ret = ustctl_duplicate_ust_object_data(&new_counter_cpu, + entry->cpu_counters[i]); + assert(ret == 0); + + status = send_counter_cpu_data_to_ust(app, + new_counter, new_counter_cpu); + if (status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) { + goto end; + } + } + +end: + return status; +} + +static +enum trigger_error_accounting_status trigger_error_accounting_ust_get_count( + const struct lttng_trigger *trigger, uint64_t *count) +{ + struct lttng_ht_iter iter; + struct error_account_entry *uid_entry; + uint64_t error_counter_index, global_sum = 0; + enum trigger_error_accounting_status status; + size_t dimension_indexes[1]; + + /* + * Go over all error counters (ignoring uid) as a trigger (and trigger + * errors) can be generated from any applications that this session + * daemon is managing. + */ + + status = get_error_counter_index_for_token( + lttng_trigger_get_tracer_token(trigger), &error_counter_index); + if (status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) { + goto end; + } + + dimension_indexes[0] = error_counter_index; + + rcu_read_lock(); + + cds_lfht_for_each_entry(error_counter_uid_ht->ht, &iter.iter, + uid_entry, node.node) { + int ret; + int64_t local_value = 0;; + bool overflow = 0, underflow = 0; + ret = ustctl_counter_aggregate(uid_entry->daemon_counter, + dimension_indexes, &local_value, &overflow, + &underflow); + assert(ret == 0); + + /* should always be zero or above. */ + assert(local_value >= 0); + global_sum += (uint64_t) local_value; + + } + + rcu_read_unlock(); + + *count = global_sum; + status = TRIGGER_ERROR_ACCOUNTING_STATUS_OK; + +end: + return status; +} + +static +enum trigger_error_accounting_status trigger_error_accounting_ust_clear( + const struct lttng_trigger *trigger) +{ + struct lttng_ht_iter iter; + struct error_account_entry *uid_entry; + uint64_t error_counter_index; + enum trigger_error_accounting_status status; + size_t dimension_indexes[1]; + + /* + * Go over all error counters (ignoring uid) as a trigger (and trigger + * errors) can be generated from any applications that this session + * daemon is managing. + */ + + status = get_error_counter_index_for_token( + lttng_trigger_get_tracer_token(trigger), + &error_counter_index); + if (status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error getting trigger error counter index"); + goto end; + } + + dimension_indexes[0] = error_counter_index; + + rcu_read_lock(); + cds_lfht_for_each_entry(error_counter_uid_ht->ht, &iter.iter, + uid_entry, node.node) { + int ret; + ret = ustctl_counter_clear(uid_entry->daemon_counter, + dimension_indexes); + assert(ret == 0); + } + + rcu_read_unlock(); + status = TRIGGER_ERROR_ACCOUNTING_STATUS_OK; +end: + return status; +} + +#endif /* HAVE_LIBLTTNG_UST_CTL */ + +void trigger_error_accounting_register_kernel(int kernel_trigger_group_fd) +{ + int local_fd = -1, ret; + struct lttng_kernel_counter_conf error_counter_conf; + + + error_counter_conf.arithmetic = LTTNG_KERNEL_COUNTER_ARITHMETIC_MODULAR; + error_counter_conf.bitness = LTTNG_KERNEL_COUNTER_BITNESS_64BITS; + error_counter_conf.global_sum_step = 0; + error_counter_conf.number_dimensions = 1; + error_counter_conf.dimensions[0].size = error_counter_size; + error_counter_conf.dimensions[0].has_underflow = false; + error_counter_conf.dimensions[0].has_overflow = false; + + ret = kernctl_create_trigger_group_error_counter( + kernel_trigger_group_fd, &error_counter_conf); + if (ret < 0) { + PERROR("ioctl kernel create trigger group error counter"); + goto error; + } + + /* Store locally */ + local_fd = ret; + + /* Prevent fd duplication after execlp() */ + ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("fcntl trigger error counter fd"); + goto error; + } + + DBG("Kernel trigger group error counter (fd: %d)", local_fd); + + kernel_error_accountant.kernel_trigger_error_counter_fd = local_fd; + +error: + return; +} + +static +enum trigger_error_accounting_status create_error_counter_index_for_token( + uint64_t tracer_token, uint64_t *error_counter_index) +{ + struct index_ht_entry *index_entry;; + enum lttng_index_allocator_status index_alloc_status; + uint64_t local_error_counter_index; + enum trigger_error_accounting_status status; + + /* Allocate a new index for that counter. */ + index_alloc_status = lttng_index_allocator_alloc(index_allocator, + &local_error_counter_index); + switch (index_alloc_status) { + case LTTNG_INDEX_ALLOCATOR_STATUS_EMPTY: + ERR("No more index available in the configured trigger error counter: number-of-indices=%"PRIu64, + lttng_index_allocator_get_index_count( + index_allocator)); + status = TRIGGER_ERROR_ACCOUNTING_STATUS_NO_INDEX_AVAILABLE; + goto end; + case LTTNG_INDEX_ALLOCATOR_STATUS_OK: + break; + default: + status = TRIGGER_ERROR_ACCOUNTING_STATUS_ERR; + goto end; + } + + index_entry = zmalloc(sizeof(*index_entry)); + if (index_entry == NULL) { + PERROR("Trigger error counter hashtable entry zmalloc"); + status = TRIGGER_ERROR_ACCOUNTING_STATUS_NOMEM; + goto end; + } + + index_entry->error_counter_index = local_error_counter_index; + lttng_ht_node_init_u64(&index_entry->node, tracer_token); + + lttng_ht_add_unique_u64(error_counter_indexes_ht, &index_entry->node); + + *error_counter_index = local_error_counter_index; + status = TRIGGER_ERROR_ACCOUNTING_STATUS_OK; +end: + return status; +} + +enum trigger_error_accounting_status trigger_error_accounting_register_trigger( + const struct lttng_trigger *trigger, + uint64_t *error_counter_index) +{ + enum trigger_error_accounting_status status; + uint64_t local_error_counter_index; + + /* Check if this trigger already has a error counter index assigned. */ + status = get_error_counter_index_for_token( + lttng_trigger_get_tracer_token(trigger), + &local_error_counter_index); + switch (status) { + case TRIGGER_ERROR_ACCOUNTING_STATUS_NOT_FOUND: + DBG("Trigger error counter index for this tracer token not found. Allocating a new one."); + status = create_error_counter_index_for_token( + lttng_trigger_get_tracer_token(trigger), + &local_error_counter_index); + if (status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) { + goto end; + } + case TRIGGER_ERROR_ACCOUNTING_STATUS_OK: + *error_counter_index = local_error_counter_index; + status = TRIGGER_ERROR_ACCOUNTING_STATUS_OK; + break; + default: + break; + } + +end: + return status; +} + +static +enum trigger_error_accounting_status trigger_error_accounting_kernel_get_count( + const struct lttng_trigger *trigger, uint64_t *count) +{ + struct lttng_kernel_counter_value counter_value; + enum trigger_error_accounting_status status; + uint64_t error_counter_index; + int ret; + + status = get_error_counter_index_for_token( + lttng_trigger_get_tracer_token(trigger), &error_counter_index); + if (status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) { + goto end; + } + + counter_value.number_dimensions = 1; + counter_value.dimension_indexes[0] = error_counter_index; + + assert(kernel_error_accountant.kernel_trigger_error_counter_fd); + + ret = kernctl_counter_get_value( + kernel_error_accountant.kernel_trigger_error_counter_fd, + &counter_value); + if (ret) { + ERR("Error getting trigger error count."); + status = TRIGGER_ERROR_ACCOUNTING_STATUS_ERR; + goto end; + } + + if (counter_value.value < 0) { + ERR("Trigger error counter less than zero."); + status = TRIGGER_ERROR_ACCOUNTING_STATUS_ERR; + goto end; + } + + /* Error count can't be negative. */ + assert(counter_value.value >= 0); + *count = (uint64_t) counter_value.value; + + status = TRIGGER_ERROR_ACCOUNTING_STATUS_OK; + +end: + return status; +} + +enum trigger_error_accounting_status trigger_error_accounting_get_count( + const struct lttng_trigger *trigger, uint64_t *count) +{ + switch (lttng_trigger_get_underlying_domain_type_restriction(trigger)) { + case LTTNG_DOMAIN_KERNEL: + return trigger_error_accounting_kernel_get_count(trigger, count); +#ifdef HAVE_LIBLTTNG_UST_CTL + case LTTNG_DOMAIN_UST: + return trigger_error_accounting_ust_get_count(trigger, count); +#endif /* HAVE_LIBLTTNG_UST_CTL */ + default: + abort(); + } +} + +static +enum trigger_error_accounting_status trigger_error_accounting_clear( + const struct lttng_trigger *trigger) +{ + switch (lttng_trigger_get_underlying_domain_type_restriction(trigger)) { + case LTTNG_DOMAIN_KERNEL: + // FIXME: Should we clear it here? Right now I believe it's clear when + // we create a new trigger in the kernel. + return TRIGGER_ERROR_ACCOUNTING_STATUS_OK; +#ifdef HAVE_LIBLTTNG_UST_CTL + case LTTNG_DOMAIN_UST: + return trigger_error_accounting_ust_clear(trigger); +#endif /* HAVE_LIBLTTNG_UST_CTL */ + default: + abort(); + } +} + +void trigger_error_accounting_unregister_trigger( + const struct lttng_trigger *trigger) +{ + struct lttng_ht_iter iter; + struct lttng_ht_node_u64 *node; + struct index_ht_entry *index_entry; + enum trigger_error_accounting_status status; + enum lttng_index_allocator_status index_alloc_status; + uint64_t tracer_token = lttng_trigger_get_tracer_token(trigger); + + + status = trigger_error_accounting_clear(trigger); + if (status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error clearing trigger error counter index"); + } + + lttng_ht_lookup(error_counter_indexes_ht, &tracer_token, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if(node) { + index_entry = caa_container_of(node, struct index_ht_entry, node); + index_alloc_status = lttng_index_allocator_release( + index_allocator, + index_entry->error_counter_index); + if (index_alloc_status != LTTNG_INDEX_ALLOCATOR_STATUS_OK) { + ERR("Error releasing trigger error counter index"); + } + + lttng_ht_del(error_counter_indexes_ht, &iter); + free(index_entry); + } +} + +static void free_error_account_entry(struct rcu_head *head) +{ + struct error_account_entry *entry = caa_container_of(head, + struct error_account_entry, rcu_head); +#ifdef HAVE_LIBLTTNG_UST_CTL + ustctl_destroy_counter(entry->daemon_counter); +#endif /* HAVE_LIBLTTNG_UST_CTL */ + + free(entry); +} + +static void free_index_ht_entry(struct rcu_head *head) +{ + struct index_ht_entry *entry = caa_container_of(head, + struct index_ht_entry, rcu_head); + free(entry); +} + +void trigger_error_accounting_fini(void) +{ + struct lttng_ht_iter iter; + struct index_ht_entry *index_entry; + struct error_account_entry *uid_entry; + + if (kernel_error_accountant.kernel_trigger_error_counter_fd) { + int ret = close(kernel_error_accountant.kernel_trigger_error_counter_fd); + if (ret) { + PERROR("Closing kernel trigger error counter"); + } + } + + rcu_read_lock(); + + cds_lfht_for_each_entry(error_counter_uid_ht->ht, &iter.iter, + uid_entry, node.node) { + cds_lfht_del(error_counter_uid_ht->ht, &uid_entry->node.node); + call_rcu(&uid_entry->rcu_head, free_error_account_entry); + } + + cds_lfht_for_each_entry(error_counter_indexes_ht->ht, &iter.iter, + index_entry, node.node) { + cds_lfht_del(error_counter_indexes_ht->ht, &index_entry->node.node); + call_rcu(&index_entry->rcu_head, free_index_ht_entry); + } + + rcu_read_unlock(); + + lttng_ht_destroy(error_counter_uid_ht); + lttng_ht_destroy(error_counter_indexes_ht); +} diff --git a/src/bin/lttng-sessiond/trigger-error-accounting.h b/src/bin/lttng-sessiond/trigger-error-accounting.h new file mode 100644 index 000000000..442ddf23c --- /dev/null +++ b/src/bin/lttng-sessiond/trigger-error-accounting.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#ifndef _TRIGGER_ERROR_ACCOUNTING_H +#define _TRIGGER_ERROR_ACCOUNTING_H + +#include + +#include "ust-app.h" + +enum trigger_error_accounting_status { + TRIGGER_ERROR_ACCOUNTING_STATUS_OK, + TRIGGER_ERROR_ACCOUNTING_STATUS_ERR, + TRIGGER_ERROR_ACCOUNTING_STATUS_NOT_FOUND, + TRIGGER_ERROR_ACCOUNTING_STATUS_NOMEM, + TRIGGER_ERROR_ACCOUNTING_STATUS_NO_INDEX_AVAILABLE, + TRIGGER_ERROR_ACCOUNTING_STATUS_APP_DEAD, +}; + +void trigger_error_accounting_init(uint64_t nb_bucket); + +#ifdef HAVE_LIBLTTNG_UST_CTL +enum trigger_error_accounting_status trigger_error_accounting_register_app( + struct ust_app *app); +#else /* HAVE_LIBLTTNG_UST_CTL */ +static inline +enum trigger_error_accounting_status trigger_error_accounting_register_app( + struct ust_app *app) +{ + return TRIGGER_ERROR_ACCOUNTING_STATUS_OK; +} +#endif /* HAVE_LIBLTTNG_UST_CTL */ + +void trigger_error_accounting_register_kernel(int kernel_fd); + +enum trigger_error_accounting_status trigger_error_accounting_register_trigger( + const struct lttng_trigger *trigger, + uint64_t *error_counter_index); + +enum trigger_error_accounting_status trigger_error_accounting_get_count( + const struct lttng_trigger *trigger, uint64_t *count); + +void trigger_error_accounting_unregister_trigger( + const struct lttng_trigger *trigger); + +void trigger_error_accounting_fini(void); + +#endif /* _TRIGGER_ERROR_ACCOUNTING_H */ diff --git a/src/bin/lttng-sessiond/ust-abi-internal.h b/src/bin/lttng-sessiond/ust-abi-internal.h index 14f214f67..8f388e083 100644 --- a/src/bin/lttng-sessiond/ust-abi-internal.h +++ b/src/bin/lttng-sessiond/ust-abi-internal.h @@ -87,6 +87,31 @@ struct lttng_ust_stream { */ } LTTNG_PACKED; +#define LTTNG_UST_TRIGGER_PADDING1 16 +#define LTTNG_UST_TRIGGER_PADDING2 (LTTNG_UST_SYM_NAME_LEN + 32) +struct lttng_ust_trigger { + uint64_t id; + uint64_t error_counter_idx; + enum lttng_ust_instrumentation instrumentation; + char name[LTTNG_UST_SYM_NAME_LEN]; /* event name */ + + enum lttng_ust_loglevel_type loglevel_type; + int loglevel; /* value, -1: all */ + char padding[LTTNG_UST_TRIGGER_PADDING1]; + + /* Per instrumentation type configuration */ + union { + char padding[LTTNG_UST_TRIGGER_PADDING2]; + } u; +} LTTNG_PACKED; + +#define LTTNG_TRIGGER_NOTIFICATION_PADDING 32 +struct lttng_ust_trigger_notification { + uint64_t id; + uint16_t capture_buf_size; + char padding[LTTNG_TRIGGER_NOTIFICATION_PADDING]; +} LTTNG_PACKED; + #define LTTNG_UST_EVENT_PADDING1 16 #define LTTNG_UST_EVENT_PADDING2 (LTTNG_UST_SYM_NAME_LEN + 32) struct lttng_ust_event { @@ -251,6 +276,16 @@ struct lttng_ust_filter_bytecode { char data[0]; } LTTNG_PACKED; +#define CAPTURE_BYTECODE_MAX_LEN 65536 +#define LTTNG_UST_CAPTURE_PADDING 32 +struct lttng_ust_capture_bytecode { + uint32_t len; + uint32_t reloc_offset; + uint64_t seqnum; + char padding[LTTNG_UST_CAPTURE_PADDING]; + char data[0]; +} LTTNG_PACKED; + #define LTTNG_UST_EXCLUSION_PADDING 32 struct lttng_ust_event_exclusion { uint32_t count; @@ -306,6 +341,9 @@ struct lttng_ust_event_exclusion { #define LTTNG_UST_FILTER _UST_CMD(0xA0) #define LTTNG_UST_EXCLUSION _UST_CMD(0xA1) +#define LTTNG_UST_TRIGGER_SEND_FD _UST_CMD(0xB0) +#define LTTNG_UST_TRIGGER_CREATE _UST_CMDW(0xB1, struct lttng_ust_trigger) + #define LTTNG_UST_ROOT_HANDLE 0 struct lttng_ust_obj; diff --git a/src/bin/lttng-sessiond/ust-app.c b/src/bin/lttng-sessiond/ust-app.c index c332f5c75..a3601c424 100644 --- a/src/bin/lttng-sessiond/ust-app.c +++ b/src/bin/lttng-sessiond/ust-app.c @@ -8,21 +8,33 @@ #define _LGPL_SOURCE #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 "buffer-registry.h" +#include "condition-internal.h" #include "fd-limit.h" #include "health-sessiond.h" #include "ust-app.h" @@ -34,6 +46,9 @@ #include "lttng-sessiond.h" #include "notification-thread-commands.h" #include "rotate.h" +#include "event.h" +#include "trigger-error-accounting.h" + struct lttng_ht *ust_app_ht; struct lttng_ht *ust_app_ht_by_sock; @@ -245,7 +260,7 @@ static struct ust_registry_session *get_session_registry( { struct buffer_reg_uid *reg_uid = buffer_reg_uid_find( ua_sess->tracing_id, ua_sess->bits_per_long, - ua_sess->real_credentials.uid); + lttng_credentials_get_uid(&ua_sess->real_credentials)); if (!reg_uid) { goto error; } @@ -313,6 +328,34 @@ void delete_ust_app_event(int sock, struct ust_app_event *ua_event, free(ua_event); } +/* + * Delete ust app token event_rule safely. RCU read lock must be held before calling + * this function. TODO: or does it???? + */ +static +void delete_ust_app_token_event_rule(int sock, struct ust_app_token_event_rule *ua_token, + struct ust_app *app) +{ + int ret; + + assert(ua_token); + + if (ua_token->exclusion != NULL) + free(ua_token->exclusion); + if (ua_token->obj != NULL) { + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_release_object(sock, ua_token->obj); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0 && ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("UST app sock %d release event obj failed with ret %d", + sock, ret); + } + free(ua_token->obj); + } + lttng_trigger_put(ua_token->trigger); + free(ua_token); +} + /* * Release ust data object of the given stream. * @@ -898,6 +941,8 @@ void delete_ust_app(struct ust_app *app) { int ret, sock; struct ust_app_session *ua_sess, *tmp_ua_sess; + struct lttng_ht_iter iter; + struct ust_app_token_event_rule *token; /* * The session list lock must be held during this function to guarantee @@ -917,9 +962,26 @@ void delete_ust_app(struct ust_app *app) rcu_read_unlock(); } + /* Wipe token associated with the app */ + cds_lfht_for_each_entry(app->tokens_ht->ht, &iter.iter, token, + node.node) { + ret = lttng_ht_del(app->tokens_ht, &iter); + assert(!ret); + delete_ust_app_token_event_rule(app->sock, token, app); + } + ht_cleanup_push(app->sessions); ht_cleanup_push(app->ust_sessions_objd); ht_cleanup_push(app->ust_objd); + ht_cleanup_push(app->tokens_ht); + + /* This can happen if trigger setup failed. e.g killed app */ + if (app->token_communication.handle) { + ustctl_release_object(sock, app->token_communication.handle); + free(app->token_communication.handle); + } + + lttng_pipe_destroy(app->token_communication.trigger_event_pipe); /* * Wait until we have deleted the application from the sock hash table @@ -1125,6 +1187,52 @@ error: return NULL; } +/* + * Alloc new UST app token event rule. + */ +static struct ust_app_token_event_rule *alloc_ust_app_token_event_rule( + struct lttng_trigger *trigger) +{ + struct ust_app_token_event_rule *ua_token; + struct lttng_condition *condition = NULL; + struct lttng_event_rule *event_rule = NULL; + + ua_token = zmalloc(sizeof(struct ust_app_token_event_rule)); + if (ua_token == NULL) { + PERROR("Failed to allocate ust_app_token_event_rule structure"); + goto error; + } + + /* Get reference of the trigger */ + /* TODO should this be like lttng_event_rule_get with a returned bool? */ + lttng_trigger_get(trigger); + + ua_token->enabled = 1; + ua_token->token = lttng_trigger_get_tracer_token(trigger); + lttng_ht_node_init_u64(&ua_token->node, ua_token->token); + + condition = lttng_trigger_get_condition(trigger); + assert(condition); + assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + + assert(LTTNG_CONDITION_STATUS_OK == lttng_condition_event_rule_get_rule_mutable(condition, &event_rule)); + assert(event_rule); + + ua_token->trigger = trigger; + ua_token->filter = lttng_event_rule_get_filter_bytecode(event_rule); + ua_token->exclusion = lttng_event_rule_generate_exclusions(event_rule); + ua_token->error_counter_index = lttng_trigger_get_error_counter_index(trigger); + + /* TODO put capture here? or later*/ + + DBG3("UST app token event rule %" PRIu64 " allocated", ua_token->token); + + return ua_token; + +error: + return NULL; +} + /* * Alloc new UST app context. */ @@ -1166,50 +1274,51 @@ error: } /* - * Allocate a filter and copy the given original filter. + * Create a liblttng-ust filter bytecode from given bytecode. * * Return allocated filter or NULL on error. */ -static struct lttng_filter_bytecode *copy_filter_bytecode( - struct lttng_filter_bytecode *orig_f) +static struct lttng_ust_filter_bytecode * +create_ust_filter_bytecode_from_bytecode(const struct lttng_bytecode *orig_f) { - struct lttng_filter_bytecode *filter = NULL; + struct lttng_ust_filter_bytecode *filter = NULL; /* Copy filter bytecode */ filter = zmalloc(sizeof(*filter) + orig_f->len); if (!filter) { - PERROR("zmalloc alloc filter bytecode"); + PERROR("zmalloc alloc ust filter bytecode"); goto error; } + assert(sizeof(struct lttng_bytecode) == + sizeof(struct lttng_ust_filter_bytecode)); memcpy(filter, orig_f, sizeof(*filter) + orig_f->len); - error: return filter; } /* - * Create a liblttng-ust filter bytecode from given bytecode. + * Create a liblttng-ust capture bytecode from given bytecode. * * Return allocated filter or NULL on error. */ -static struct lttng_ust_filter_bytecode *create_ust_bytecode_from_bytecode( - struct lttng_filter_bytecode *orig_f) +static struct lttng_ust_capture_bytecode * +create_ust_capture_bytecode_from_bytecode(const struct lttng_bytecode *orig_f) { - struct lttng_ust_filter_bytecode *filter = NULL; + struct lttng_ust_capture_bytecode *capture = NULL; - /* Copy filter bytecode */ - filter = zmalloc(sizeof(*filter) + orig_f->len); - if (!filter) { - PERROR("zmalloc alloc ust filter bytecode"); + /* Copy capture bytecode */ + capture = zmalloc(sizeof(*capture) + orig_f->len); + if (!capture) { + PERROR("zmalloc alloc ust capture bytecode"); goto error; } - assert(sizeof(struct lttng_filter_bytecode) == - sizeof(struct lttng_ust_filter_bytecode)); - memcpy(filter, orig_f, sizeof(*filter) + orig_f->len); + assert(sizeof(struct lttng_bytecode) == + sizeof(struct lttng_ust_capture_bytecode)); + memcpy(capture, orig_f, sizeof(*capture) + orig_f->len); error: - return filter; + return capture; } /* @@ -1264,7 +1373,7 @@ error: * Return an ust_app_event object or NULL on error. */ static struct ust_app_event *find_ust_app_event(struct lttng_ht *ht, - const char *name, const struct lttng_filter_bytecode *filter, + const char *name, const struct lttng_bytecode *filter, int loglevel_value, const struct lttng_event_exclusion *exclusion) { @@ -1297,6 +1406,32 @@ end: return event; } +/* + * Lookup for an ust app tokens based on a token id. + * + * Return an ust_app_token_event_rule object or NULL on error. + */ +static struct ust_app_token_event_rule *find_ust_app_token_event_rule(struct lttng_ht *ht, + uint64_t token) +{ + struct lttng_ht_iter iter; + struct lttng_ht_node_u64 *node; + struct ust_app_token_event_rule *token_event_rule = NULL; + + assert(ht); + + lttng_ht_lookup(ht, &token, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if (node == NULL) { + DBG2("UST app token %" PRIu64 " not found", token); + goto end; + } + + token_event_rule = caa_container_of(node, struct ust_app_token_event_rule, node); +end: + return token_event_rule; +} + /* * Create the channel context on the tracer. * @@ -1343,33 +1478,79 @@ error: /* * Set the filter on the tracer. */ -static -int set_ust_event_filter(struct ust_app_event *ua_event, - struct ust_app *app) +static int set_ust_filter(struct ust_app *app, + const struct lttng_bytecode *bytecode, + struct lttng_ust_object_data *ust_object) { int ret; struct lttng_ust_filter_bytecode *ust_bytecode = NULL; health_code_update(); - if (!ua_event->filter) { - ret = 0; + ust_bytecode = create_ust_filter_bytecode_from_bytecode(bytecode); + if (!ust_bytecode) { + ret = -LTTNG_ERR_NOMEM; + goto error; + } + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_set_filter(app->sock, ust_bytecode, + ust_object); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("UST app set filter failed for object %p of app (pid: %d) " + "with ret %d", ust_object, app->pid, ret); + } else { + /* + * This is normal behavior, an application can die during the + * creation process. Don't report an error so the execution can + * continue normally. + */ + ret = 0; + DBG3("UST app set filter. Application is dead."); + } goto error; } - ust_bytecode = create_ust_bytecode_from_bytecode(ua_event->filter); + DBG2("UST filter set for object %p successfully", ust_object); + +error: + health_code_update(); + free(ust_bytecode); + return ret; +} + +/* + * Set a capture bytecode for the passed object. + * The seqnum enforce the ordering at runtime and on reception. + */ +static int set_ust_capture(struct ust_app *app, + const struct lttng_bytecode *bytecode, + unsigned int seqnum, + struct lttng_ust_object_data *ust_object) +{ + int ret; + struct lttng_ust_capture_bytecode *ust_bytecode = NULL; + + health_code_update(); + + ust_bytecode = create_ust_capture_bytecode_from_bytecode(bytecode); if (!ust_bytecode) { ret = -LTTNG_ERR_NOMEM; goto error; } + + /* Set the seqnum */ + ust_bytecode->seqnum = seqnum; + pthread_mutex_lock(&app->sock_lock); - ret = ustctl_set_filter(app->sock, ust_bytecode, - ua_event->obj); + ret = ustctl_set_capture(app->sock, ust_bytecode, + ust_object); pthread_mutex_unlock(&app->sock_lock); if (ret < 0) { if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { - ERR("UST app event %s filter failed for app (pid: %d) " - "with ret %d", ua_event->attr.name, app->pid, ret); + ERR("UST app set capture failed for object %p of app (pid: %d) " + "with ret %d", ust_object, app->pid, ret); } else { /* * This is normal behavior, an application can die during the @@ -1377,12 +1558,12 @@ int set_ust_event_filter(struct ust_app_event *ua_event, * continue normally. */ ret = 0; - DBG3("UST app filter event failed. Application is dead."); + DBG3("UST app set capture. Application is dead."); } goto error; } - DBG2("UST filter set successfully for event %s", ua_event->name); + DBG2("UST capture set for object %p successfully", ust_object); error: health_code_update(); @@ -1414,33 +1595,30 @@ end: /* * Set event exclusions on the tracer. */ -static -int set_ust_event_exclusion(struct ust_app_event *ua_event, - struct ust_app *app) +static int set_ust_exclusions(struct ust_app *app, + struct lttng_event_exclusion *exclusions, + struct lttng_ust_object_data *ust_object) { int ret; - struct lttng_ust_event_exclusion *ust_exclusion = NULL; + struct lttng_ust_event_exclusion *ust_exclusions = NULL; - health_code_update(); + assert(exclusions && exclusions->count > 0); - if (!ua_event->exclusion || !ua_event->exclusion->count) { - ret = 0; - goto error; - } + health_code_update(); - ust_exclusion = create_ust_exclusion_from_exclusion( - ua_event->exclusion); - if (!ust_exclusion) { + ust_exclusions = create_ust_exclusion_from_exclusion( + exclusions); + if (!ust_exclusions) { ret = -LTTNG_ERR_NOMEM; goto error; } pthread_mutex_lock(&app->sock_lock); - ret = ustctl_set_exclusion(app->sock, ust_exclusion, ua_event->obj); + ret = ustctl_set_exclusion(app->sock, ust_exclusions, ust_object); pthread_mutex_unlock(&app->sock_lock); if (ret < 0) { if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { - ERR("UST app event %s exclusions failed for app (pid: %d) " - "with ret %d", ua_event->attr.name, app->pid, ret); + ERR("UST app exclusions failed for object %p of app (pid: %d) " + "with ret %d", ust_object, app->pid, ret); } else { /* * This is normal behavior, an application can die during the @@ -1448,37 +1626,36 @@ int set_ust_event_exclusion(struct ust_app_event *ua_event, * continue normally. */ ret = 0; - DBG3("UST app event exclusion failed. Application is dead."); + DBG3("UST app set exclusions failed. Application is dead."); } goto error; } - DBG2("UST exclusion set successfully for event %s", ua_event->name); + DBG2("UST exclusions set successfully for object %p", ust_object); error: health_code_update(); - free(ust_exclusion); + free(ust_exclusions); return ret; } /* * Disable the specified event on to UST tracer for the UST session. */ -static int disable_ust_event(struct ust_app *app, - struct ust_app_session *ua_sess, struct ust_app_event *ua_event) +static int disable_ust_object(struct ust_app *app, + struct lttng_ust_object_data *object) { int ret; health_code_update(); pthread_mutex_lock(&app->sock_lock); - ret = ustctl_disable(app->sock, ua_event->obj); + ret = ustctl_disable(app->sock, object); pthread_mutex_unlock(&app->sock_lock); if (ret < 0) { if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { - ERR("UST app event %s disable failed for app (pid: %d) " - "and session handle %d with ret %d", - ua_event->attr.name, app->pid, ua_sess->handle, ret); + ERR("UST app disable failed for object %p app (pid: %d) with ret %d", + object, app->pid, ret); } else { /* * This is normal behavior, an application can die during the @@ -1491,8 +1668,8 @@ static int disable_ust_event(struct ust_app *app, goto error; } - DBG2("UST app event %s disabled successfully for app (pid: %d)", - ua_event->attr.name, app->pid); + DBG2("UST app object %p disabled successfully for app (pid: %d)", + object, app->pid); error: health_code_update(); @@ -1580,21 +1757,19 @@ error: /* * Enable the specified event on to UST tracer for the UST session. */ -static int enable_ust_event(struct ust_app *app, - struct ust_app_session *ua_sess, struct ust_app_event *ua_event) +static int enable_ust_object(struct ust_app *app, struct lttng_ust_object_data *ust_object) { int ret; health_code_update(); pthread_mutex_lock(&app->sock_lock); - ret = ustctl_enable(app->sock, ua_event->obj); + ret = ustctl_enable(app->sock, ust_object); pthread_mutex_unlock(&app->sock_lock); if (ret < 0) { if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { - ERR("UST app event %s enable failed for app (pid: %d) " - "and session handle %d with ret %d", - ua_event->attr.name, app->pid, ua_sess->handle, ret); + ERR("UST app enable failed for object %p app (pid: %d) with ret %d", + ust_object, app->pid, ret); } else { /* * This is normal behavior, an application can die during the @@ -1602,13 +1777,13 @@ static int enable_ust_event(struct ust_app *app, * continue normally. */ ret = 0; - DBG3("UST app enable event failed. Application is dead."); + DBG3("UST app enable failed. Application is dead."); } goto error; } - DBG2("UST app event %s enabled successfully for app (pid: %d)", - ua_event->attr.name, app->pid); + DBG2("UST app object %p enabled successfully for app (pid: %d)", + ust_object, app->pid); error: health_code_update(); @@ -1704,14 +1879,14 @@ int create_ust_event(struct ust_app *app, struct ust_app_session *ua_sess, ua_event->handle = ua_event->obj->handle; - DBG2("UST app event %s created successfully for pid:%d", - ua_event->attr.name, app->pid); + DBG2("UST app event %s created successfully for pid:%d object: %p", + ua_event->attr.name, app->pid, ua_event->obj); health_code_update(); /* Set filter if one is present. */ if (ua_event->filter) { - ret = set_ust_event_filter(ua_event, app); + ret = set_ust_filter(app, ua_event->filter, ua_event->obj); if (ret < 0) { goto error; } @@ -1719,7 +1894,7 @@ int create_ust_event(struct ust_app *app, struct ust_app_session *ua_sess, /* Set exclusions for the event */ if (ua_event->exclusion) { - ret = set_ust_event_exclusion(ua_event, app); + ret = set_ust_exclusions(app, ua_event->exclusion, ua_event->obj); if (ret < 0) { goto error; } @@ -1731,7 +1906,7 @@ int create_ust_event(struct ust_app *app, struct ust_app_session *ua_sess, * We now need to explicitly enable the event, since it * is now disabled at creation. */ - ret = enable_ust_event(app, ua_sess, ua_event); + ret = enable_ust_object(app, ua_event->obj); if (ret < 0) { /* * If we hit an EPERM, something is wrong with our enable call. If @@ -1758,6 +1933,188 @@ error: return ret; } +static +void init_ust_trigger_from_event_rule(const struct lttng_event_rule *rule, struct lttng_ust_trigger *trigger) +{ + enum lttng_event_rule_status status; + enum lttng_loglevel_type loglevel_type; + enum lttng_ust_loglevel_type ust_loglevel_type = LTTNG_UST_LOGLEVEL_ALL; + int loglevel = -1; + const char *pattern; + + /* For now only LTTNG_EVENT_RULE_TYPE_TRACEPOINT are supported */ + assert(lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_TRACEPOINT); + + memset(trigger, 0, sizeof(*trigger)); + + if (lttng_event_rule_is_agent(rule)) { + /* + * Special event for agents + * The actual meat of the event is in the filter that will be + * attached later on. + * Set the default values for the agent event. + */ + pattern = event_get_default_agent_ust_name(lttng_event_rule_get_domain_type(rule)); + loglevel = 0; + ust_loglevel_type = LTTNG_UST_LOGLEVEL_ALL; + } else { + status = lttng_event_rule_tracepoint_get_pattern(rule, &pattern); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + /* At this point this is a fatal error */ + assert(0); + } + + status = lttng_event_rule_tracepoint_get_log_level_type( + rule, &loglevel_type); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + /* At this point this is a fatal error */ + assert(0); + } + + switch (loglevel_type) { + case LTTNG_EVENT_LOGLEVEL_ALL: + ust_loglevel_type = LTTNG_UST_LOGLEVEL_ALL; + break; + case LTTNG_EVENT_LOGLEVEL_RANGE: + ust_loglevel_type = LTTNG_UST_LOGLEVEL_RANGE; + break; + case LTTNG_EVENT_LOGLEVEL_SINGLE: + ust_loglevel_type = LTTNG_UST_LOGLEVEL_SINGLE; + break; + } + + if (loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) { + status = lttng_event_rule_tracepoint_get_log_level( + rule, &loglevel); + assert(status == LTTNG_EVENT_RULE_STATUS_OK); + } + } + + trigger->instrumentation = LTTNG_UST_TRACEPOINT; + strncpy(trigger->name, pattern, LTTNG_UST_SYM_NAME_LEN - 1); + trigger->loglevel_type = ust_loglevel_type; + trigger->loglevel = loglevel; +} + +/* + * Create the specified event rule token onto the UST tracer for a UST app. + */ +static +int create_ust_token_event_rule(struct ust_app *app, struct ust_app_token_event_rule *ua_token) +{ + int ret = 0; + struct lttng_ust_trigger trigger; + struct lttng_condition *condition = NULL; + struct lttng_event_rule *event_rule = NULL; + unsigned int capture_bytecode_count = 0; + + health_code_update(); + assert(app->token_communication.handle); + + condition = lttng_trigger_get_condition(ua_token->trigger); + assert(condition); + assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + + lttng_condition_event_rule_get_rule_mutable(condition, &event_rule); + assert(event_rule); + assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_TRACEPOINT); + /* Should we also test for UST at this point, or do we trust all the + * upper level? */ + + init_ust_trigger_from_event_rule(event_rule, &trigger); + + trigger.id = ua_token->token; + trigger.error_counter_index = ua_token->error_counter_index; + + /* Create UST trigger on tracer */ + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_create_trigger(app->sock, &trigger, app->token_communication.handle, &ua_token->obj); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + abort(); + ERR("Error ustctl create trigger %s for app pid: %d with ret %d", + trigger.name, app->pid, ret); + } else { + /* + * This is normal behavior, an application can die during the + * creation process. Don't report an error so the execution can + * continue normally. + */ + ret = 0; + DBG3("UST app create event failed. Application is dead."); + } + goto error; + } + + ua_token->handle = ua_token->obj->handle; + + DBG2("UST app event %s created successfully for pid:%d object: %p", + trigger.name, app->pid, ua_token->obj); + + health_code_update(); + + /* Set filter if one is present. */ + if (ua_token->filter) { + ret = set_ust_filter(app, ua_token->filter, ua_token->obj); + if (ret < 0) { + goto error; + } + } + + /* Set exclusions for the event */ + if (ua_token->exclusion) { + ret = set_ust_exclusions(app, ua_token->exclusion, ua_token->obj); + if (ret < 0) { + goto error; + } + } + + /* Set the capture bytecode + * TODO: do we want to emulate what is done with exclusion and provide + * and object with a count of capture bytecode? instead of multiple + * call? + * */ + capture_bytecode_count = lttng_trigger_get_capture_bytecode_count(ua_token->trigger); + for (unsigned int i = 0; i < capture_bytecode_count; i++) { + const struct lttng_bytecode *capture_bytecode = lttng_trigger_get_capture_bytecode_at_index(ua_token->trigger, i); + ret = set_ust_capture(app, capture_bytecode, i, ua_token->obj); + if (ret < 0) { + goto error; + } + } + + /* + * We now need to explicitly enable the event, since it + * is disabled at creation. + */ + ret = enable_ust_object(app, ua_token->obj); + if (ret < 0) { + /* + * If we hit an EPERM, something is wrong with our enable call. If + * we get an EEXIST, there is a problem on the tracer side since we + * just created it. + */ + switch (ret) { + case -LTTNG_UST_ERR_PERM: + /* Code flow problem */ + assert(0); + case -LTTNG_UST_ERR_EXIST: + /* It's OK for our use case. */ + ret = 0; + break; + default: + break; + } + goto error; + } + ua_token->enabled = true; + +error: + health_code_update(); + return ret; +} + /* * Copy data between an UST app event and a LTT event. */ @@ -1776,7 +2133,7 @@ static void shadow_copy_event(struct ust_app_event *ua_event, /* Copy filter bytecode */ if (uevent->filter) { - ua_event->filter = copy_filter_bytecode(uevent->filter); + ua_event->filter = bytecode_copy(uevent->filter); /* Filter might be NULL here in case of ENONEM. */ } @@ -1847,10 +2204,10 @@ static void shadow_copy_session(struct ust_app_session *ua_sess, ua_sess->tracing_id = usess->id; ua_sess->id = get_next_session_id(); - ua_sess->real_credentials.uid = app->uid; - ua_sess->real_credentials.gid = app->gid; - ua_sess->effective_credentials.uid = usess->uid; - ua_sess->effective_credentials.gid = usess->gid; + LTTNG_OPTIONAL_SET(&ua_sess->real_credentials.uid, app->uid); + LTTNG_OPTIONAL_SET(&ua_sess->real_credentials.gid, app->gid); + LTTNG_OPTIONAL_SET(&ua_sess->effective_credentials.uid, usess->uid); + LTTNG_OPTIONAL_SET(&ua_sess->effective_credentials.gid, usess->gid); ua_sess->buffer_type = usess->buffer_type; ua_sess->bits_per_long = app->bits_per_long; @@ -1872,7 +2229,7 @@ static void shadow_copy_session(struct ust_app_session *ua_sess, case LTTNG_BUFFER_PER_UID: ret = snprintf(ua_sess->path, sizeof(ua_sess->path), DEFAULT_UST_TRACE_UID_PATH, - ua_sess->real_credentials.uid, + lttng_credentials_get_uid(&ua_sess->real_credentials), app->bits_per_long); break; default: @@ -1995,8 +2352,9 @@ static int setup_buffer_reg_pid(struct ust_app_session *ua_sess, app->uint64_t_alignment, app->long_alignment, app->byte_order, app->version.major, app->version.minor, reg_pid->root_shm_path, reg_pid->shm_path, - ua_sess->effective_credentials.uid, - ua_sess->effective_credentials.gid, ua_sess->tracing_id, + lttng_credentials_get_uid(&ua_sess->effective_credentials), + lttng_credentials_get_gid(&ua_sess->effective_credentials), + ua_sess->tracing_id, app->uid); if (ret < 0) { /* @@ -2341,7 +2699,7 @@ int enable_ust_app_event(struct ust_app_session *ua_sess, { int ret; - ret = enable_ust_event(app, ua_sess, ua_event); + ret = enable_ust_object(app, ua_event->obj); if (ret < 0) { goto error; } @@ -2360,7 +2718,7 @@ static int disable_ust_app_event(struct ust_app_session *ua_sess, { int ret; - ret = disable_ust_event(app, ua_sess, ua_event); + ret = disable_ust_object(app, ua_event->obj); if (ret < 0) { goto error; } @@ -2887,8 +3245,9 @@ static int create_channel_per_uid(struct ust_app *app, notification_ret = notification_thread_command_add_channel( notification_thread_handle, session->name, - ua_sess->effective_credentials.uid, - ua_sess->effective_credentials.gid, ua_chan->name, + lttng_credentials_get_uid(&ua_sess->effective_credentials), + lttng_credentials_get_gid(&ua_sess->effective_credentials), + ua_chan->name, ua_chan->key, LTTNG_DOMAIN_UST, ua_chan->attr.subbuf_size * ua_chan->attr.num_subbuf); if (notification_ret != LTTNG_OK) { @@ -2987,8 +3346,9 @@ static int create_channel_per_pid(struct ust_app *app, cmd_ret = notification_thread_command_add_channel( notification_thread_handle, session->name, - ua_sess->effective_credentials.uid, - ua_sess->effective_credentials.gid, ua_chan->name, + lttng_credentials_get_uid(&ua_sess->effective_credentials), + lttng_credentials_get_gid(&ua_sess->effective_credentials), + ua_chan->name, ua_chan->key, LTTNG_DOMAIN_UST, ua_chan->attr.subbuf_size * ua_chan->attr.num_subbuf); if (cmd_ret != LTTNG_OK) { @@ -3175,6 +3535,57 @@ error: return ret; } +/* + * Create UST app event and create it on the tracer side. + * + * Called with ust app session mutex held. + */ +static +int create_ust_app_token_event_rule(struct lttng_trigger *trigger, + struct ust_app *app) +{ + int ret = 0; + struct ust_app_token_event_rule *ua_token; + + ua_token = alloc_ust_app_token_event_rule(trigger); + if (ua_token == NULL) { + ret = -ENOMEM; + goto end; + } + + /* Create it on the tracer side */ + ret = create_ust_token_event_rule(app, ua_token); + if (ret < 0) { + /* + * Not found previously means that it does not exist on the + * tracer. If the application reports that the event existed, + * it means there is a bug in the sessiond or lttng-ust + * (or corruption, etc.) + */ + if (ret == -LTTNG_UST_ERR_EXIST) { + ERR("Tracer for application reported that a token event rule being created already existed: " + "token = \"%" PRIu64 "\", pid = %d, ppid = %d, uid = %d, gid = %d", + lttng_trigger_get_tracer_token(trigger), + app->pid, app->ppid, app->uid, + app->gid); + } + goto error; + } + + lttng_ht_add_unique_u64(app->tokens_ht, &ua_token->node); + + DBG2("UST app create token event rule %" PRIu64 " for PID %d completed", lttng_trigger_get_tracer_token(trigger), + app->pid); + + goto end; + +error: + /* Valid. Calling here is already in a read side lock */ + delete_ust_app_token_event_rule(-1, ua_token, app); +end: + return ret; +} + /* * Create UST metadata and open it on the tracer side. * @@ -3319,6 +3730,7 @@ error: struct ust_app *ust_app_create(struct ust_register_msg *msg, int sock) { struct ust_app *lta = NULL; + struct lttng_pipe *trigger_event_source_pipe = NULL; assert(msg); assert(sock >= 0); @@ -3335,12 +3747,20 @@ struct ust_app *ust_app_create(struct ust_register_msg *msg, int sock) goto error; } + trigger_event_source_pipe = lttng_pipe_open(FD_CLOEXEC); + if (!trigger_event_source_pipe) { + PERROR("Open trigger pipe"); + goto error; + } + lta = zmalloc(sizeof(struct ust_app)); if (lta == NULL) { PERROR("malloc"); goto error; } + lta->token_communication.trigger_event_pipe = trigger_event_source_pipe; + lta->ppid = msg->ppid; lta->uid = msg->uid; lta->gid = msg->gid; @@ -3359,6 +3779,7 @@ struct ust_app *ust_app_create(struct ust_register_msg *msg, int sock) lta->ust_objd = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); lta->ust_sessions_objd = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); lta->notify_sock = -1; + lta->tokens_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64); /* Copy name and make sure it's NULL terminated. */ strncpy(lta->name, msg->name, sizeof(lta->name)); @@ -3445,6 +3866,61 @@ int ust_app_version(struct ust_app *app) return ret; } +/* + * Setup the base trigger group. + * + * Return 0 on success else a negative value either an errno code or a + * LTTng-UST error code. + */ +int ust_app_setup_trigger_group(struct ust_app *app) +{ + int ret; + int writefd; + struct lttng_ust_object_data *group = NULL; + enum lttng_error_code lttng_ret; + enum trigger_error_accounting_status trigger_error_accounting_status; + + assert(app); + + /* Get the write side of the pipe */ + writefd = lttng_pipe_get_writefd(app->token_communication.trigger_event_pipe); + + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_create_trigger_group(app->sock, writefd, &group); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0) { + if (ret != -LTTNG_UST_ERR_EXITING && ret != -EPIPE) { + ERR("UST app %d create_trigger_group failed with ret %d, trigger pipe %d", app->sock, ret, writefd); + } else { + DBG("UST app %d create trigger group failed. Application is dead", app->sock); + } + goto end; + } + + lttng_ret = notification_thread_command_add_application( + notification_thread_handle, lttng_pipe_get_readfd(app->token_communication.trigger_event_pipe), LTTNG_DOMAIN_UST); + if (lttng_ret != LTTNG_OK) { + /* TODO: error */ + ret = - 1; + ERR("Failed to add channel to notification thread"); + goto end; + } + + /* Assign handle only when the complete setup is valid */ + app->token_communication.handle = group; + + trigger_error_accounting_status = trigger_error_accounting_register_app(app); + if (trigger_error_accounting_status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Failed to setup trigger error accouting for app"); + ret = -1; + goto end; + } + + +end: + return ret; +} + /* * Unregister app by removing it from the global traceable app list and freeing * the data struct. @@ -3453,6 +3929,7 @@ int ust_app_version(struct ust_app *app) */ void ust_app_unregister(int sock) { + enum lttng_error_code ret_code; struct ust_app *lta; struct lttng_ht_node_ulong *node; struct lttng_ht_iter ust_app_sock_iter; @@ -3558,6 +4035,19 @@ void ust_app_unregister(int sock) lta->pid); } + /* trigger handle can be null in certain scenario such as a dead app */ + if (lta->token_communication.handle) { + int fd = lttng_pipe_get_readfd( + lta->token_communication.trigger_event_pipe); + + ret_code = notification_thread_command_remove_application( + notification_thread_handle, + fd); + if (ret_code != LTTNG_OK) { + ERR("Failed to remove application from notification thread"); + } + } + /* Free memory */ call_rcu(<a->pid_n.head, delete_ust_app_rcu); @@ -4999,6 +5489,124 @@ end: return ret; } +static +void ust_app_synchronize_tokens(struct ust_app *app) +{ + int ret = 0; + enum lttng_error_code ret_code; + enum lttng_trigger_status t_status; + struct lttng_ht_iter app_trigger_iter; + struct lttng_triggers *triggers; + struct ust_app_token_event_rule *token_event_rule_element; + unsigned int count; + + rcu_read_lock(); + /* TODO: is this necessary to protect against new trigger being added ? + * notification_trigger_tokens_ht is still the backing data structure + * for this listing. Leave it there for now. + */ + pthread_mutex_lock(¬ification_trigger_tokens_ht_lock); + ret_code = notification_thread_command_get_tokens( + notification_thread_handle, &triggers); + if (ret_code != LTTNG_OK) { + ret = -1; + goto end; + } + + assert(triggers); + + t_status = lttng_triggers_get_count(triggers, &count); + if (t_status != LTTNG_TRIGGER_STATUS_OK) { + ret = -1; + goto end; + } + + for (unsigned int i = 0; i < count; i++) { + struct lttng_condition *condition; + struct lttng_event_rule *event_rule; + struct lttng_trigger *trigger; + struct ust_app_token_event_rule *ua_token; + uint64_t token; + + trigger = lttng_triggers_get_pointer_of_index(triggers, i); + assert(trigger); + + /* TODO: error checking and type checking */ + token = lttng_trigger_get_tracer_token(trigger); + condition = lttng_trigger_get_condition(trigger); + (void) lttng_condition_event_rule_get_rule_mutable(condition, &event_rule); + + if (lttng_event_rule_get_domain_type(event_rule) == LTTNG_DOMAIN_KERNEL) { + /* Skip kernel related trigger */ + continue; + } + + /* Iterate over all known token trigger */ + ua_token = find_ust_app_token_event_rule(app->tokens_ht, token); + if (!ua_token) { + ret = create_ust_app_token_event_rule(trigger, app); + if (ret < 0) { + goto end; + } + } + } + + /* Remove all unknown trigger from the app + * TODO find a way better way then this, do it on the unregister command + * and be specific on the token to remove instead of going over all + * trigger known to the app. This is sub optimal. + */ + cds_lfht_for_each_entry (app->tokens_ht->ht, &app_trigger_iter.iter, + token_event_rule_element, node.node) { + uint64_t token; + bool found = false; + + token = token_event_rule_element->token; + + /* + * Check if the app event trigger still exists on the + * notification side. + * TODO: might want to change the backing data struct of the + * lttng_triggers object to allow quick lookup? + * For kernel mostly all of this can be removed once we delete + * on a per trigger basis. + */ + + for (unsigned int i = 0; i < count; i++) { + struct lttng_trigger *trigger; + uint64_t inner_token; + + trigger = lttng_triggers_get_pointer_of_index( + triggers, i); + assert(trigger); + + inner_token = lttng_trigger_get_tracer_token(trigger); + + if (inner_token == token) { + found = true; + break; + } + } + + if (found) { + /* Still valid */ + continue; + } + + /* TODO: This is fucking ugly API for fuck sake */ + assert(!lttng_ht_del(app->tokens_ht, &app_trigger_iter)); + + (void) disable_ust_object(app, token_event_rule_element->obj); + + delete_ust_app_token_event_rule(app->sock, token_event_rule_element, app); + } +end: + lttng_triggers_destroy(triggers); + rcu_read_unlock(); + pthread_mutex_unlock(¬ification_trigger_tokens_ht_lock); + return; +} + /* * The caller must ensure that the application is compatible and is tracked * by the process attribute trackers. @@ -5140,6 +5748,21 @@ void ust_app_global_update(struct ltt_ust_session *usess, struct ust_app *app) } } +void ust_app_global_update_tokens(struct ust_app *app) +{ + DBG2("UST app global update token for app sock %d", app->sock); + + if (!app->compatible) { + return; + } + if (app->token_communication.handle == NULL) { + WARN("UST app global update token for app sock %d skipped since communcation handle is null", app->sock); + return; + } + + ust_app_synchronize_tokens(app); +} + /* * Called with session lock held. */ @@ -5155,6 +5778,31 @@ void ust_app_global_update_all(struct ltt_ust_session *usess) rcu_read_unlock(); } +void ust_app_global_update_all_tokens(void) +{ + struct lttng_ht_iter iter; + struct ust_app *app; + + rcu_read_lock(); + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) { + ust_app_global_update_tokens(app); + } + rcu_read_unlock(); +} + +void ust_app_update_trigger_error_count(struct lttng_trigger *trigger) +{ + uint64_t error_count = 0; + enum trigger_error_accounting_status status; + + status = trigger_error_accounting_get_count(trigger, &error_count); + if (status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error getting trigger error count"); + } + + lttng_trigger_set_error_count(trigger, error_count); +} + /* * Add context to a specific channel for global UST domain. */ @@ -5965,10 +6613,8 @@ enum lttng_error_code ust_app_snapshot_record( ua_chan, node.node) { status = consumer_snapshot_channel(socket, ua_chan->key, output, 0, - ua_sess->effective_credentials - .uid, - ua_sess->effective_credentials - .gid, + lttng_credentials_get_uid(&ua_sess->effective_credentials), + lttng_credentials_get_gid(&ua_sess->effective_credentials), &trace_path[consumer_path_offset], wait, nb_packets_per_stream); switch (status) { @@ -5988,8 +6634,8 @@ enum lttng_error_code ust_app_snapshot_record( } status = consumer_snapshot_channel(socket, registry->metadata_key, output, 1, - ua_sess->effective_credentials.uid, - ua_sess->effective_credentials.gid, + lttng_credentials_get_uid(&ua_sess->effective_credentials), + lttng_credentials_get_gid(&ua_sess->effective_credentials), &trace_path[consumer_path_offset], wait, 0); switch (status) { case LTTNG_OK: @@ -6345,10 +6991,8 @@ enum lttng_error_code ust_app_rotate_session(struct ltt_session *session) ua_chan, node.node) { ret = consumer_rotate_channel(socket, ua_chan->key, - ua_sess->effective_credentials - .uid, - ua_sess->effective_credentials - .gid, + lttng_credentials_get_uid(&ua_sess->effective_credentials), + lttng_credentials_get_gid(&ua_sess->effective_credentials), ua_sess->consumer, /* is_metadata_channel */ false); if (ret < 0) { @@ -6364,8 +7008,8 @@ enum lttng_error_code ust_app_rotate_session(struct ltt_session *session) (void) push_metadata(registry, usess->consumer); ret = consumer_rotate_channel(socket, registry->metadata_key, - ua_sess->effective_credentials.uid, - ua_sess->effective_credentials.gid, + lttng_credentials_get_uid(&ua_sess->effective_credentials), + lttng_credentials_get_gid(&ua_sess->effective_credentials), ua_sess->consumer, /* is_metadata_channel */ true); if (ret < 0) { diff --git a/src/bin/lttng-sessiond/ust-app.h b/src/bin/lttng-sessiond/ust-app.h index 164bc2c6f..3b65a67e4 100644 --- a/src/bin/lttng-sessiond/ust-app.h +++ b/src/bin/lttng-sessiond/ust-app.h @@ -11,6 +11,7 @@ #include +#include #include #include "trace-ust.h" @@ -22,7 +23,7 @@ /* Process name (short). */ #define UST_APP_PROCNAME_LEN 16 -struct lttng_filter_bytecode; +struct lttng_bytecode; struct lttng_ust_filter_bytecode; extern int ust_consumerd64_fd, ust_consumerd32_fd; @@ -39,7 +40,7 @@ struct ust_app_notify_sock_obj { struct ust_app_ht_key { const char *name; - const struct lttng_filter_bytecode *filter; + const struct lttng_bytecode *filter; enum lttng_ust_loglevel_type loglevel_type; const struct lttng_event_exclusion *exclusion; }; @@ -108,7 +109,21 @@ struct ust_app_event { struct lttng_ust_event attr; char name[LTTNG_UST_SYM_NAME_LEN]; struct lttng_ht_node_str node; - struct lttng_filter_bytecode *filter; + struct lttng_bytecode *filter; + struct lttng_event_exclusion *exclusion; +}; + +struct ust_app_token_event_rule { + int enabled; + uint64_t error_counter_index; + int handle; + struct lttng_ust_object_data *obj; + struct lttng_trigger *trigger; + uint64_t token; + struct lttng_ht_node_u64 node; + /* The event_rule object own this pointer */ + const struct lttng_bytecode *filter; + /* The event_rule object own this pointer */ struct lttng_event_exclusion *exclusion; }; @@ -292,6 +307,14 @@ struct ust_app { * Used for path creation */ time_t registration_time; + /* + * Trigger + */ + struct { + struct lttng_ust_object_data *handle; + struct lttng_pipe *trigger_event_pipe; + } token_communication; + struct lttng_ht *tokens_ht; }; #ifdef HAVE_LIBLTTNG_UST_CTL @@ -319,6 +342,10 @@ int ust_app_add_ctx_channel_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct ltt_ust_context *uctx); void ust_app_global_update(struct ltt_ust_session *usess, struct ust_app *app); void ust_app_global_update_all(struct ltt_ust_session *usess); +void ust_app_global_update_tokens(struct ust_app *app); +void ust_app_global_update_all_tokens(void); + +void ust_app_update_trigger_error_count(struct lttng_trigger *trigger); void ust_app_clean_list(void); int ust_app_ht_alloc(void); @@ -356,6 +383,8 @@ int ust_app_release_object(struct ust_app *app, enum lttng_error_code ust_app_clear_session(struct ltt_session *session); enum lttng_error_code ust_app_open_packets(struct ltt_session *session); +int ust_app_setup_trigger_group(struct ust_app *app); + static inline int ust_app_supported(void) { @@ -444,6 +473,17 @@ static inline void ust_app_global_update(struct ltt_ust_session *usess, struct ust_app *app) {} static inline +void ust_app_global_update_tokens(struct ust_app *app) +{} +static inline +void ust_app_global_update_all_tokens(void) +{} +static inline +int ust_app_setup_trigger_group(struct ust_app *app) +{ + return 0; +} +static inline int ust_app_disable_channel_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan) { @@ -531,7 +571,11 @@ unsigned int ust_app_get_nb_stream(struct ltt_ust_session *usess) { return 0; } - +static inline +void ust_app_update_trigger_error_count(struct lttng_trigger *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 25dc7ed1c..70e3d1658 100644 --- a/src/bin/lttng-sessiond/ust-consumer.c +++ b/src/bin/lttng-sessiond/ust-consumer.c @@ -151,7 +151,7 @@ static int ask_channel_creation(struct ust_app_session *ua_sess, ua_chan->tracefile_count, ua_sess->id, ua_sess->output_traces, - ua_sess->real_credentials.uid, + lttng_credentials_get_uid(&ua_sess->real_credentials), ua_chan->attr.blocking_timeout, root_shm_path, shm_path, trace_chunk, diff --git a/src/bin/lttng-sessiond/ust-ctl-internal.h b/src/bin/lttng-sessiond/ust-ctl-internal.h index 7c245bdce..51aca37a9 100644 --- a/src/bin/lttng-sessiond/ust-ctl-internal.h +++ b/src/bin/lttng-sessiond/ust-ctl-internal.h @@ -79,12 +79,22 @@ int ustctl_set_filter(int sock, struct lttng_ust_filter_bytecode *bytecode, struct lttng_ust_object_data *obj_data); int ustctl_set_exclusion(int sock, struct lttng_ust_event_exclusion *exclusion, struct lttng_ust_object_data *obj_data); +int ustctl_set_capture(int sock, struct lttng_ust_capture_bytecode *bytecode, + struct lttng_ust_object_data *obj_data); int ustctl_enable(int sock, struct lttng_ust_object_data *object); int ustctl_disable(int sock, struct lttng_ust_object_data *object); int ustctl_start_session(int sock, int handle); int ustctl_stop_session(int sock, int handle); +int ustctl_create_trigger_group(int sock, + int pipe_fd, + struct lttng_ust_object_data **trigger_group_handle); +int ustctl_create_trigger(int sock, + struct lttng_ust_trigger *trigger, + struct lttng_ust_object_data *trigger_group_handle, + struct lttng_ust_object_data **trigger_data); + /* * ustctl_tracepoint_list returns a tracepoint list handle, or negative * error value. @@ -533,4 +543,82 @@ int ustctl_reply_register_channel(int sock, enum ustctl_channel_header header_type, int ret_code); /* return code. 0 ok, negative error */ +/* + * Counter API. + */ + +enum ustctl_counter_bitness { + USTCTL_COUNTER_BITNESS_32 = 4, + USTCTL_COUNTER_BITNESS_64 = 8, +}; + +enum ustctl_counter_arithmetic { + USTCTL_COUNTER_ARITHMETIC_MODULAR = 0, + USTCTL_COUNTER_ARITHMETIC_SATURATION = 1, +}; + +/* Used as alloc flags. */ +enum ustctl_counter_alloc { + USTCTL_COUNTER_ALLOC_PER_CPU = (1 << 0), + USTCTL_COUNTER_ALLOC_GLOBAL = (1 << 1), +}; + +struct ustctl_daemon_counter; + +int ustctl_get_nr_cpu_per_counter(void); + +struct ustctl_counter_dimension { + uint64_t size; + uint64_t underflow_index; + uint64_t overflow_index; + uint8_t has_underflow; + uint8_t has_overflow; +}; + +struct ustctl_daemon_counter * + ustctl_create_counter(size_t nr_dimensions, + const struct ustctl_counter_dimension *dimensions, + int64_t global_sum_step, + int global_counter_fd, + int nr_counter_cpu_fds, + const int *counter_cpu_fds, + enum ustctl_counter_bitness bitness, + enum ustctl_counter_arithmetic arithmetic, + uint32_t alloc_flags); + +int ustctl_create_counter_data(struct ustctl_daemon_counter *counter, + struct lttng_ust_object_data **counter_data); + +int ustctl_create_counter_global_data(struct ustctl_daemon_counter *counter, + struct lttng_ust_object_data **counter_global_data); +int ustctl_create_counter_cpu_data(struct ustctl_daemon_counter *counter, int cpu, + struct lttng_ust_object_data **counter_cpu_data); + +/* + * Each counter data and counter cpu data created need to be destroyed + * before calling ustctl_destroy_counter(). + */ +void ustctl_destroy_counter(struct ustctl_daemon_counter *counter); + +int ustctl_send_counter_data_to_ust(int sock, int parent_handle, + struct lttng_ust_object_data *counter_data); +int ustctl_send_counter_global_data_to_ust(int sock, + struct lttng_ust_object_data *counter_data, + struct lttng_ust_object_data *counter_global_data); +int ustctl_send_counter_cpu_data_to_ust(int sock, + struct lttng_ust_object_data *counter_data, + struct lttng_ust_object_data *counter_cpu_data); + +int ustctl_counter_read(struct ustctl_daemon_counter *counter, + const size_t *dimension_indexes, + int cpu, int64_t *value, + bool *overflow, bool *underflow); +int ustctl_counter_aggregate(struct ustctl_daemon_counter *counter, + const size_t *dimension_indexes, + int64_t *value, + bool *overflow, bool *underflow); +int ustctl_counter_clear(struct ustctl_daemon_counter *counter, + const size_t *dimension_indexes); + + #endif /* LTTNG_UST_CTL_INTERNAL_H */ diff --git a/src/bin/lttng/Makefile.am b/src/bin/lttng/Makefile.am index a6bd7a9d7..96d7114a2 100644 --- a/src/bin/lttng/Makefile.am +++ b/src/bin/lttng/Makefile.am @@ -29,7 +29,11 @@ lttng_SOURCES = command.h conf.c conf.h commands/start.c \ commands/enable_rotation.c \ commands/disable_rotation.c \ commands/clear.c \ - utils.c utils.h lttng.c + commands/add_trigger.c \ + commands/list_triggers.c \ + commands/remove_trigger.c \ + utils.c utils.h lttng.c \ + uprobe.c uprobe.h lttng_CFLAGS = $(AM_CFLAGS) $(POPT_CFLAGS) @@ -38,4 +42,5 @@ lttng_LDADD = $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \ $(top_builddir)/src/common/config/libconfig.la \ $(top_builddir)/src/common/string-utils/libstring-utils.la \ $(top_builddir)/src/common/filter/libfilter.la \ + $(top_builddir)/src/common/argpar/libargpar.la \ $(POPT_LIBS) diff --git a/src/bin/lttng/command.h b/src/bin/lttng/command.h index 8f1c7be40..bf0045210 100644 --- a/src/bin/lttng/command.h +++ b/src/bin/lttng/command.h @@ -77,6 +77,9 @@ DECL_COMMAND(rotate); DECL_COMMAND(enable_rotation); DECL_COMMAND(disable_rotation); DECL_COMMAND(clear); +DECL_COMMAND(add_trigger); +DECL_COMMAND(list_triggers); +DECL_COMMAND(remove_trigger); extern int cmd_help(int argc, const char **argv, const struct cmd_struct commands[]); diff --git a/src/bin/lttng/commands/add_trigger.c b/src/bin/lttng/commands/add_trigger.c new file mode 100644 index 000000000..277a7e338 --- /dev/null +++ b/src/bin/lttng/commands/add_trigger.c @@ -0,0 +1,2161 @@ +#include +#include + +#include "../command.h" +#include "../uprobe.h" + +#include "common/argpar/argpar.h" +#include "common/dynamic-array.h" +#include "common/string-utils/string-utils.h" +#include "common/utils.h" +#include "lttng/condition/event-rule.h" +#include "lttng/event-internal.h" +#include "lttng/event-expr.h" +#include +#include "lttng/event-rule/kprobe.h" +#include "lttng/event-rule/syscall.h" +#include +#include "lttng/event-rule/uprobe.h" +#include "lttng/kernel-probe.h" +#include "common/filter/filter-ast.h" +#include "common/filter/filter-ir.h" +#include "common/dynamic-array.h" + + +#if (LTTNG_SYMBOL_NAME_LEN == 256) +#define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "255" +#endif + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP, + OPT_LIST_OPTIONS, + + OPT_CONDITION, + OPT_ACTION, + OPT_ID, + OPT_FIRE_ONCE_AFTER, + OPT_FIRE_EVERY, + OPT_USER_ID, + + OPT_ALL, + OPT_FILTER, + OPT_EXCLUDE, + OPT_LOGLEVEL, + OPT_LOGLEVEL_ONLY, + + OPT_USERSPACE, + OPT_KERNEL, + OPT_LOG4J, + OPT_JUL, + OPT_PYTHON, + + OPT_FUNCTION, + OPT_PROBE, + OPT_USERSPACE_PROBE, + OPT_SYSCALL, + OPT_TRACEPOINT, + + OPT_NAME, + OPT_MAX_SIZE, + OPT_DATA_URL, + OPT_CTRL_URL, + + OPT_CAPTURE, +}; + +static const struct argpar_opt_descr event_rule_opt_descrs[] = { + { OPT_ALL, 'a', "all", false }, + { OPT_FILTER, 'f', "filter", true }, + { OPT_EXCLUDE, 'x', "exclude", true }, + { OPT_LOGLEVEL, '\0', "loglevel", true }, + { OPT_LOGLEVEL_ONLY, '\0', "loglevel-only", true }, + + /* Domains */ + { OPT_USERSPACE, 'u', "userspace", false }, + { OPT_KERNEL, 'k', "kernel", false }, + { OPT_LOG4J, 'l', "log4j", false }, + { OPT_JUL, 'j', "jul", false }, + { OPT_PYTHON, 'p', "python", false }, + + /* Event rule types */ + { OPT_FUNCTION, '\0', "function", true }, + { OPT_PROBE, '\0', "probe", true }, + { OPT_USERSPACE_PROBE, '\0', "userspace-probe", true }, + { OPT_SYSCALL, '\0', "syscall" }, + { OPT_TRACEPOINT, '\0', "tracepoint" }, + + /* Capture descriptor */ + { OPT_CAPTURE, '\0', "capture", true }, + + ARGPAR_OPT_DESCR_SENTINEL +}; + +static +bool assign_domain_type(enum lttng_domain_type *dest, + enum lttng_domain_type src) +{ + bool ret; + + if (*dest == LTTNG_DOMAIN_NONE || *dest == src) { + *dest = src; + ret = true; + } else { + ERR("Multiple domains specified."); + ret = false; + } + + return ret; +} + +static +bool assign_event_rule_type(enum lttng_event_rule_type *dest, + enum lttng_event_rule_type src) +{ + bool ret; + + if (*dest == LTTNG_EVENT_RULE_TYPE_UNKNOWN || *dest == src) { + *dest = src; + ret = true; + } else { + ERR("Multiple event type not supported."); + ret = false; + } + + return ret; +} + +static +bool assign_string(char **dest, const char *src, const char *opt_name) +{ + bool ret; + + if (*dest) { + ERR( + "Duplicate %s given.", opt_name); + goto error; + } + + *dest = strdup(src); + if (!*dest) { + ERR("Failed to allocate %s string.", opt_name); + goto error; + } + + ret = true; + goto end; + +error: + ret = false; + +end: + return ret; +} + +/* This is defined in enable_events.c. */ +LTTNG_HIDDEN +int create_exclusion_list_and_validate(const char *event_name, + const char *exclusions_arg, + char ***exclusion_list); + +/* + * Parse `str` as a log level in domain `domain_type`. Return -1 if the string + * is not recognized as a valid log level. + */ +static +int parse_loglevel_string(const char *str, enum lttng_domain_type domain_type) +{ + + switch (domain_type) { + case LTTNG_DOMAIN_UST: + return loglevel_str_to_value(str); + + case LTTNG_DOMAIN_LOG4J: + return loglevel_log4j_str_to_value(str); + + case LTTNG_DOMAIN_JUL: + return loglevel_jul_str_to_value(str); + + case LTTNG_DOMAIN_PYTHON: + return loglevel_python_str_to_value(str); + + default: + /* Invalid domain type. */ + abort(); + } +} + +static int parse_kernel_probe_opts(const char *source, + struct lttng_kernel_probe_location **location) +{ + int ret = 0; + int match; + char s_hex[19]; + char name[LTTNG_SYMBOL_NAME_LEN]; + char *symbol_name = NULL; + uint64_t offset; + + /* Check for symbol+offset */ + match = sscanf(source, "%255[^'+']+%18s", name, s_hex); + if (match == 2) { + if (*s_hex == '\0') { + ERR("Kernel probe symbol offset is missing."); + goto error; + } + symbol_name = strndup(name, LTTNG_SYMBOL_NAME_LEN); + if (!symbol_name) { + ERR("Strndup kernel probe location symbol name."); + goto error; + } + offset = strtoul(s_hex, NULL, 0); + + *location = lttng_kernel_probe_location_symbol_create( + symbol_name, offset); + if (!location) { + ERR("Symbol kernel probe location creation failed."); + goto error; + } + + goto end; + } + + /* Check for symbol */ + if (isalpha(name[0]) || name[0] == '_') { + match = sscanf(source, + "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API + "s", + name); + if (match == 1) { + symbol_name = strndup(name, LTTNG_SYMBOL_NAME_LEN); + if (!symbol_name) { + ERR("Strndup kernel probe location symbol name."); + goto error; + } + + *location = lttng_kernel_probe_location_symbol_create( + symbol_name, 0); + if (!location) { + ERR("Symbol kernel probe location creation failed."); + goto error; + } + + goto end; + } + } + + /* Check for address */ + match = sscanf(source, "%18s", s_hex); + if (match > 0) { + uint64_t address; + if (*s_hex == '\0') { + ERR("Invalid kernel probe location address."); + goto error; + } + address = strtoul(s_hex, NULL, 0); + *location = lttng_kernel_probe_location_address_create(address); + if (!location) { + ERR("Symbol kernel probe location creation failed."); + goto error; + } + + goto end; + } + +error: + /* No match */ + ret = -1; + *location = NULL; + +end: + free(symbol_name); + return ret; +} + +static +struct lttng_event_expr *ir_op_load_expr_to_event_expr( + struct ir_load_expression *load_exp, const char *capture_str) +{ + struct ir_load_expression_op *load_expr_op = load_exp->child; + struct lttng_event_expr *event_expr = NULL; + char *provider_name = NULL; + + switch (load_expr_op->type) { + case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT: + { + const char *field_name; + + load_expr_op = load_expr_op->next; + assert(load_expr_op); + assert(load_expr_op->type == IR_LOAD_EXPRESSION_GET_SYMBOL); + field_name = load_expr_op->u.symbol; + assert(field_name); + + event_expr = lttng_event_expr_event_payload_field_create(field_name); + if (!event_expr) { + fprintf(stderr, "Failed to create payload field event expression.\n"); + goto error; + } + + break; + } + + case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT: + { + const char *field_name; + + load_expr_op = load_expr_op->next; + assert(load_expr_op); + assert(load_expr_op->type == IR_LOAD_EXPRESSION_GET_SYMBOL); + field_name = load_expr_op->u.symbol; + assert(field_name); + + event_expr = lttng_event_expr_channel_context_field_create(field_name); + if (!event_expr) { + fprintf(stderr, "Failed to create channel context field event expression.\n"); + goto error; + } + + break; + } + + case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT: + { + const char *field_name; + const char *colon; + const char *type_name; + + load_expr_op = load_expr_op->next; + assert(load_expr_op); + assert(load_expr_op->type == IR_LOAD_EXPRESSION_GET_SYMBOL); + field_name = load_expr_op->u.symbol; + assert(field_name); + + /* + * The field name needs to be of the form PROVIDER:TYPE. We + * split it here. + */ + colon = strchr(field_name, ':'); + if (!colon) { + fprintf(stderr, "Invalid app-specific context field name: missing colon in `%s`.\n", + field_name); + goto error; + } + + type_name = colon + 1; + if (*type_name == '\0') { + fprintf(stderr, + "Invalid app-specific context field name: missing type name after colon in `%s`.\n", + field_name); + goto error; + } + + provider_name = strndup(field_name, colon - field_name); + if (!provider_name) { + fprintf(stderr, "Failed to allocate string.\n"); + goto error; + } + + event_expr = lttng_event_expr_app_specific_context_field_create( + provider_name, type_name); + if (!event_expr) { + fprintf(stderr, + "Failed to create app-specific context field event expression.\n"); + goto error; + } + + break; + } + + default: + fprintf(stderr, "%s: unexpected load expr type %d.\n", + __func__, load_expr_op->type); + abort(); + } + + load_expr_op = load_expr_op->next; + + /* There may be a single array index after that. */ + if (load_expr_op->type == IR_LOAD_EXPRESSION_GET_INDEX) { + uint64_t index = load_expr_op->u.index; + struct lttng_event_expr *index_event_expr; + + index_event_expr = lttng_event_expr_array_field_element_create(event_expr, index); + if (!index_event_expr) { + fprintf(stderr, "Failed to create array field element event expression.\n"); + goto error; + } + + event_expr = index_event_expr; + load_expr_op = load_expr_op->next; + } + + switch (load_expr_op->type) { + case IR_LOAD_EXPRESSION_LOAD_FIELD: + /* + * This is what we expect, IR_LOAD_EXPRESSION_LOAD_FIELD is + * always found at the end of the chain. + */ + break; + case IR_LOAD_EXPRESSION_GET_SYMBOL: + fprintf(stderr, "Error: While parsing expression `%s`: Capturing subfields is not supported.\n", + capture_str); + goto error; + + default: + fprintf(stderr, "%s: unexpected load expression operator %s.\n", + __func__, ir_load_expression_type_str(load_expr_op->type)); + abort(); + } + + goto end; + +error: + lttng_event_expr_destroy(event_expr); + event_expr = NULL; + +end: + free(provider_name); + + return event_expr; +} + +static +struct lttng_event_expr *ir_op_load_to_event_expr(struct ir_op *ir, + const char *capture_str) +{ + struct lttng_event_expr *event_expr = NULL; + + assert(ir->op == IR_OP_LOAD); + + switch (ir->data_type) { + case IR_DATA_EXPRESSION: + { + struct ir_load_expression *ir_load_expr = ir->u.load.u.expression; + event_expr = ir_op_load_expr_to_event_expr(ir_load_expr, capture_str); + break; + } + + default: + fprintf(stderr, "%s: unexpected data type: %s.\n", __func__, + ir_data_type_str(ir->data_type)); + abort(); + } + + return event_expr; +} + +static +struct lttng_event_expr *ir_op_root_to_event_expr(struct ir_op *ir, + const char *capture_str) +{ + struct lttng_event_expr *event_expr = NULL; + + assert(ir->op == IR_OP_ROOT); + ir = ir->u.root.child; + + switch (ir->op) { + case IR_OP_LOAD: + event_expr = ir_op_load_to_event_expr(ir, capture_str); + break; + + case IR_OP_BINARY: + fprintf(stderr, "Error: While parsing expression `%s`: Binary operators are not allowed in capture expressions.\n", + capture_str); + break; + + case IR_OP_UNARY: + fprintf(stderr, "Error: While parsing expression `%s`: Unary operators are not allowed in capture expressions.\n", + capture_str); + break; + + case IR_OP_LOGICAL: + fprintf(stderr, "Error: While parsing expression `%s`: Logical operators are not allowed in capture expressions.\n", + capture_str); + break; + + default: + fprintf(stderr, "%s: unexpected IR op type: %s.\n", __func__, + ir_op_type_str(ir->op)); + abort(); + } + + return event_expr; +} + +static +void destroy_event_expr(void *ptr) +{ + lttng_event_expr_destroy(ptr); +} + +struct parse_event_rule_res { + /* Owned by this */ + struct lttng_event_rule *er; + + /* Array of `struct lttng_event_expr *` */ + struct lttng_dynamic_pointer_array capture_descriptors; +}; + +static +struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv) +{ + enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; + enum lttng_event_rule_type event_rule_type = LTTNG_EVENT_RULE_TYPE_UNKNOWN; + struct argpar_state *state; + struct argpar_item *item = NULL; + char *error = NULL; + int consumed_args = -1; + struct lttng_kernel_probe_location *kernel_probe_location = NULL; + struct lttng_userspace_probe_location *userspace_probe_location = NULL; + struct parse_event_rule_res res = { 0 }; + struct lttng_event_expr *event_expr = NULL; + struct filter_parser_ctx *parser_ctx = NULL; + + /* Was the -a/--all flag provided? */ + bool all_events = false; + + /* Tracepoint name (non-option argument) */ + const char *tracepoint_name = NULL; + + /* Holds the argument of --probe / --userspace-probe. */ + char *source = NULL; + + /* Filter */ + char *filter = NULL; + + /* Exclude */ + char *exclude = NULL; + char **exclusion_list = NULL; + + /* Log level */ + char *loglevel_str = NULL; + bool loglevel_only = false; + + lttng_dynamic_pointer_array_init(&res.capture_descriptors, + destroy_event_expr); + state = argpar_state_create(*argc, *argv, event_rule_opt_descrs); + if (!state) { + ERR("Failed to allocate an argpar state."); + goto error; + } + + while (true) { + enum argpar_state_parse_next_status status; + + ARGPAR_ITEM_DESTROY_AND_RESET(item); + status = argpar_state_parse_next(state, &item, &error); + if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { + ERR("%s", error); + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + /* Just stop parsing here. */ + break; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { + break; + } + + assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + /* Domains */ + case OPT_USERSPACE: + if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_UST)) { + goto error; + } + break; + + case OPT_KERNEL: + if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_KERNEL)) { + goto error; + } + break; + + case OPT_LOG4J: + if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_LOG4J)) { + goto error; + } + break; + + case OPT_JUL: + if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_JUL)) { + goto error; + } + break; + + case OPT_PYTHON: + if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_PYTHON)) { + goto error; + } + break; + + /* Event rule types */ + case OPT_FUNCTION: + if (!assign_event_rule_type(&event_rule_type, + LTTNG_EVENT_RULE_TYPE_KRETPROBE)) { + goto error; + } + break; + + case OPT_PROBE: + if (!assign_event_rule_type(&event_rule_type, + LTTNG_EVENT_RULE_TYPE_KPROBE)) { + goto error; + } + + if (!assign_string(&source, item_opt->arg, "source")) { + goto error; + } + + break; + + case OPT_USERSPACE_PROBE: + if (!assign_event_rule_type(&event_rule_type, + LTTNG_EVENT_RULE_TYPE_UPROBE)) { + goto error; + } + + if (!assign_string(&source, item_opt->arg, "source")) { + goto error; + } + break; + + case OPT_SYSCALL: + if (!assign_event_rule_type(&event_rule_type, + LTTNG_EVENT_RULE_TYPE_SYSCALL)) { + goto error; + } + break; + + case OPT_TRACEPOINT: + if (!assign_event_rule_type(&event_rule_type, + LTTNG_EVENT_RULE_TYPE_TRACEPOINT)) { + goto error; + } + break; + + case OPT_ALL: + all_events = true; + break; + + case OPT_FILTER: + if (!assign_string(&filter, item_opt->arg, "--filter/-f")) { + goto error; + } + break; + + case OPT_EXCLUDE: + if (!assign_string(&exclude, item_opt->arg, "--exclude/-x")) { + goto error; + } + break; + + case OPT_LOGLEVEL: + case OPT_LOGLEVEL_ONLY: + if (!assign_string(&loglevel_str, item_opt->arg, "--loglevel/--loglevel-only")) { + goto error; + } + + loglevel_only = item_opt->descr->id == OPT_LOGLEVEL_ONLY; + break; + + case OPT_CAPTURE: + { + const char *capture_str = item_opt->arg; + int ret; + + ret = filter_parser_ctx_create_from_filter_expression( + capture_str, &parser_ctx); + if (ret) { + fprintf(stderr, "Failed to parse capture expression `%s`.\n", capture_str); + goto error; + } + + event_expr = ir_op_root_to_event_expr(parser_ctx->ir_root, + capture_str); + if (!event_expr) { + /* ir_op_root_to_event_expr has printed an error message. */ + goto error; + } + + ret = lttng_dynamic_pointer_array_add_pointer( + &res.capture_descriptors, + event_expr); + if (ret) { + goto error; + } + + /* The ownership of event expression was transferred to the dynamic array. */ + event_expr = NULL; + + break; + } + + default: + abort(); + } + } else { + struct argpar_item_non_opt *item_non_opt = + (struct argpar_item_non_opt *) item; + + /* + * Don't accept two non-option arguments/tracepoint + * names. + */ + if (tracepoint_name) { + ERR( + "Unexpected argument: %s", + item_non_opt->arg); + goto error; + } + + tracepoint_name = item_non_opt->arg; + } + } + + if (event_rule_type == LTTNG_EVENT_RULE_TYPE_UNKNOWN) { + event_rule_type = LTTNG_EVENT_RULE_TYPE_TRACEPOINT; + } + + /* + * Option -a is applicable to event rules of type tracepoint and + * syscall, and it is equivalent to using "*" as the tracepoint name. + */ + if (all_events) { + switch (event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + break; + default: + ERR("Can't use -a/--all with event rule of type %s.", + lttng_event_rule_type_str(event_rule_type)); + goto error; + } + + if (tracepoint_name) { + ERR("Can't provide a tracepoint name with -a/--all."); + goto error; + } + + /* In which case, it's equivalent to tracepoint name "*". */ + tracepoint_name = "*"; + } + + /* + * A tracepoint name (or -a, for the event rule types that accept it) + * is required. + */ + if (!tracepoint_name) { + ERR("Need to provide either a tracepoint name or -a/--all."); + goto error; + } + + /* + * We don't support multiple tracepoint names for now. + */ + if (strchr(tracepoint_name, ',')) { + ERR("multiple tracepoint names are not supported at the moment."); + goto error; + } + + /* + * Update *argc and *argv so our caller can keep parsing what follows. + */ + consumed_args = argpar_state_get_ingested_orig_args(state); + assert(consumed_args >= 0); + *argc -= consumed_args; + *argv += consumed_args; + + /* Need to specify a domain. */ + if (domain_type == LTTNG_DOMAIN_NONE) { + ERR("Please specify a domain (-k/-u/-j)."); + goto error; + } + + /* Validate event rule type against domain. */ + switch (event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_KPROBE: + case LTTNG_EVENT_RULE_TYPE_KRETPROBE: + case LTTNG_EVENT_RULE_TYPE_UPROBE: + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + if (domain_type != LTTNG_DOMAIN_KERNEL) { + ERR("Event type not available for user-space tracing"); + goto error; + } + break; + + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + break; + + default: + abort(); + } + + /* + * Adding a filter to a probe, function or userspace-probe would be + * denied by the kernel tracer as it's not supported at the moment. We + * do an early check here to warn the user. + */ + if (filter && domain_type == LTTNG_DOMAIN_KERNEL) { + switch (event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + break; + default: + ERR("Filter expressions are not supported for %s events", + lttng_event_rule_type_str(event_rule_type)); + goto error; + } + } + + /* If --exclude/-x was passed, split it into an exclusion list. */ + if (exclude) { + if (domain_type != LTTNG_DOMAIN_UST) { + ERR("Event name exclusions are not yet implemented for %s events", + get_domain_str(domain_type)); + goto error; + } + + + if (create_exclusion_list_and_validate(tracepoint_name, exclude, + &exclusion_list) != 0) { + ERR("Failed to create exclusion list."); + goto error; + } + } + + if (loglevel_str && event_rule_type != LTTNG_EVENT_RULE_TYPE_TRACEPOINT) { + ERR("Log levels are only application to tracepoint event rules."); + goto error; + } + + /* Finally, create the event rule object. */ + switch (event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + { + enum lttng_event_rule_status event_rule_status; + + res.er = lttng_event_rule_tracepoint_create(domain_type); + if (!res.er) { + ERR("Failed to create tracepoint event rule."); + goto error; + } + + /* Set pattern. */ + event_rule_status = + lttng_event_rule_tracepoint_set_pattern(res.er, tracepoint_name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set tracepoint pattern."); + goto error; + } + + /* Set filter. */ + if (filter) { + event_rule_status = + lttng_event_rule_tracepoint_set_filter( + res.er, filter); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set tracepoint filter expression."); + goto error; + } + } + + /* Set exclusion list. */ + if (exclusion_list) { + int n; + + for (n = 0; exclusion_list[n]; n++) { + event_rule_status = lttng_event_rule_tracepoint_add_exclusion( + res.er, + exclusion_list[n]); + if (event_rule_status != + LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set tracepoint exclusion list."); + goto error; + } + } + } + + if (loglevel_str) { + int loglevel; + + if (domain_type == LTTNG_DOMAIN_KERNEL) { + ERR("Log levels are not supported by the kernel tracer."); + goto error; + } + + loglevel = parse_loglevel_string( + loglevel_str, domain_type); + if (loglevel < 0) { + ERR("Failed to parse `%s` as a log level.", loglevel_str); + goto error; + } + + if (loglevel_only) { + event_rule_status = + lttng_event_rule_tracepoint_set_log_level( + res.er, loglevel); + } else { + event_rule_status = + lttng_event_rule_tracepoint_set_log_level_range_lower_bound( + res.er, loglevel); + } + + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set log level."); + goto error; + } + } + + break; + } + + case LTTNG_EVENT_RULE_TYPE_KPROBE: + { + int ret; + enum lttng_event_rule_status event_rule_status; + + res.er = lttng_event_rule_kprobe_create(); + if (!res.er) { + ERR("Failed to create kprobe event rule."); + goto error; + } + + ret = parse_kernel_probe_opts(source, &kernel_probe_location); + if (ret) { + ERR("Failed to parse kernel probe location."); + goto error; + } + + event_rule_status = lttng_event_rule_kprobe_set_name(res.er, tracepoint_name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set kprobe event rule's name."); + goto error; + } + + assert(kernel_probe_location); + event_rule_status = lttng_event_rule_kprobe_set_location(res.er, kernel_probe_location); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set kprobe event rule's location."); + goto error; + } + + break; + } + + case LTTNG_EVENT_RULE_TYPE_UPROBE: + { + int ret; + enum lttng_event_rule_status event_rule_status; + + ret = parse_userspace_probe_opts(source, &userspace_probe_location); + if (ret) { + ERR("Failed to parse userspace probe location."); + goto error; + } + + res.er = lttng_event_rule_uprobe_create(); + if (!res.er) { + ERR("Failed to create userspace probe event rule."); + goto error; + } + + event_rule_status = lttng_event_rule_uprobe_set_location(res.er, userspace_probe_location); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set userspace probe event rule's location."); + goto error; + } + + event_rule_status = lttng_event_rule_uprobe_set_name(res.er, tracepoint_name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set userspace probe event rule's name."); + goto error; + } + + break; + } + + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + { + enum lttng_event_rule_status event_rule_status; + + res.er = lttng_event_rule_syscall_create(); + if (!res.er) { + ERR("Failed to create syscall event rule."); + goto error; + } + + event_rule_status = lttng_event_rule_syscall_set_pattern(res.er, tracepoint_name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set syscall event rule's pattern."); + goto error; + } + + if (filter) { + event_rule_status = lttng_event_rule_syscall_set_filter( + res.er, filter); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set syscall event rule's filter expression."); + goto error; + } + } + + break; + } + + default: + ERR("%s: I don't support event rules of type `%s` at the moment.", __func__, + lttng_event_rule_type_str(event_rule_type)); + goto error; + } + + goto end; + +error: + lttng_event_rule_destroy(res.er); + res.er = NULL; + lttng_dynamic_pointer_array_reset(&res.capture_descriptors); + +end: + if (parser_ctx) { + filter_parser_ctx_free(parser_ctx); + } + + lttng_event_expr_destroy(event_expr); + argpar_item_destroy(item); + free(error); + argpar_state_destroy(state); + free(filter); + free(exclude); + free(loglevel_str); + strutils_free_null_terminated_array_of_strings(exclusion_list); + lttng_kernel_probe_location_destroy(kernel_probe_location); + lttng_userspace_probe_location_destroy(userspace_probe_location); + return res; +} + +static +struct lttng_condition *handle_condition_event(int *argc, const char ***argv) +{ + struct parse_event_rule_res res; + struct lttng_condition *c; + size_t i; + + res = parse_event_rule(argc, argv); + if (!res.er) { + c = NULL; + goto error; + } + + c = lttng_condition_event_rule_create(res.er); + if (!c) { + goto error; + } + + for (i = 0; i < lttng_dynamic_pointer_array_get_count(&res.capture_descriptors); + i++) { + enum lttng_condition_status status; + struct lttng_event_expr **expr = + lttng_dynamic_array_get_element( + &res.capture_descriptors.array, i); + + assert(expr); + assert(*expr); + status = lttng_condition_event_rule_append_capture_descriptor( + c, *expr); + if (status != LTTNG_CONDITION_STATUS_OK) { + if (status == LTTNG_CONDITION_STATUS_UNSUPPORTED) { + ERR("The capture feature is unsupported by the event-rule type"); + } + goto error; + } + + /* Ownership of event expression moved to `c` */ + *expr = NULL; + } + + goto end; + +error: + lttng_condition_destroy(c); + c = NULL; + +end: + lttng_dynamic_pointer_array_reset(&res.capture_descriptors); + lttng_event_rule_destroy(res.er); + return c; +} + +static +struct lttng_condition *handle_condition_session_consumed_size(int *argc, const char ***argv) +{ + struct lttng_condition *cond = NULL; + struct argpar_state *state = NULL; + struct argpar_item *item = NULL; + const char *threshold_arg = NULL; + const char *session_name_arg = NULL; + uint64_t threshold; + char *error = NULL; + enum lttng_condition_status condition_status; + + state = argpar_state_create(*argc, *argv, event_rule_opt_descrs); + if (!state) { + ERR("Failed to allocate an argpar state."); + goto error; + } + + while (true) { + enum argpar_state_parse_next_status status; + + ARGPAR_ITEM_DESTROY_AND_RESET(item); + status = argpar_state_parse_next(state, &item, &error); + if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { + ERR("%s", error); + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + /* Just stop parsing here. */ + break; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { + break; + } + + assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + default: + abort(); + } + } else { + struct argpar_item_non_opt *item_non_opt; + + assert(item->type == ARGPAR_ITEM_TYPE_NON_OPT); + + item_non_opt = (struct argpar_item_non_opt *) item; + + switch (item_non_opt->non_opt_index) { + case 0: + session_name_arg = item_non_opt->arg; + break; + case 1: + threshold_arg = item_non_opt->arg; + break; + default: + ERR("Unexpected argument `%s`.", + item_non_opt->arg); + goto error; + } + } + } + + *argc -= argpar_state_get_ingested_orig_args(state); + *argv += argpar_state_get_ingested_orig_args(state); + + if (!session_name_arg) { + ERR("Missing session name argument."); + goto error; + } + + if (!threshold_arg) { + ERR("Missing threshold argument."); + goto error; + } + + if (utils_parse_size_suffix(threshold_arg, &threshold) != 0) { + ERR("Failed to parse `%s` as a size.", threshold_arg); + goto error; + } + + cond = lttng_condition_session_consumed_size_create(); + if (!cond) { + ERR("Failed to allocate a session consumed size condition."); + goto error; + } + + condition_status = lttng_condition_session_consumed_size_set_session_name( + cond, session_name_arg); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + ERR("Failed to set session consumed size condition session name."); + goto error; + } + + + condition_status = lttng_condition_session_consumed_size_set_threshold( + cond, threshold); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + ERR("Failed to set session consumed size condition threshold."); + goto error; + } + + goto end; + +error: + lttng_condition_destroy(cond); + cond = NULL; + +end: + argpar_state_destroy(state); + argpar_item_destroy(item); + free(error); + return cond; +} + +static +struct lttng_condition *handle_condition_buffer_usage_high(int *argc, const char ***argv) +{ + struct lttng_condition *cond = NULL; + struct argpar_state *state = NULL; + struct argpar_item *item = NULL; + const char *threshold_arg = NULL; + const char *session_name_arg = NULL; + uint64_t threshold; + char *error = NULL; + enum lttng_condition_status condition_status; + + state = argpar_state_create(*argc, *argv, event_rule_opt_descrs); + if (!state) { + ERR("Failed to allocate an argpar state."); + goto error; + } + + while (true) { + enum argpar_state_parse_next_status status; + + ARGPAR_ITEM_DESTROY_AND_RESET(item); + status = argpar_state_parse_next(state, &item, &error); + if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { + ERR("%s", error); + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + /* Just stop parsing here. */ + break; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { + break; + } + + assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + default: + abort(); + } + } else { + struct argpar_item_non_opt *item_non_opt; + + assert(item->type == ARGPAR_ITEM_TYPE_NON_OPT); + + item_non_opt = (struct argpar_item_non_opt *) item; + + switch (item_non_opt->non_opt_index) { + case 0: + session_name_arg = item_non_opt->arg; + break; + case 1: + threshold_arg = item_non_opt->arg; + break; + default: + ERR("Unexpected argument `%s`.", + item_non_opt->arg); + goto error; + } + } + } + + *argc -= argpar_state_get_ingested_orig_args(state); + *argv += argpar_state_get_ingested_orig_args(state); + + if (!session_name_arg) { + ERR("Missing session name argument."); + goto error; + } + + if (!threshold_arg) { + ERR("Missing threshold argument."); + goto error; + } + + if (utils_parse_size_suffix(threshold_arg, &threshold) != 0) { + ERR("Failed to parse `%s` as a size.", threshold_arg); + goto error; + } + + cond = lttng_condition_session_consumed_size_create(); + if (!cond) { + ERR("Failed to allocate a session consumed size condition."); + goto error; + } + + condition_status = lttng_condition_session_consumed_size_set_session_name( + cond, session_name_arg); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + ERR("Failed to set session consumed size condition session name."); + goto error; + } + + condition_status = lttng_condition_session_consumed_size_set_threshold( + cond, threshold); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + ERR("Failed to set session consumed size condition threshold."); + goto error; + } + + goto end; + +error: + lttng_condition_destroy(cond); + cond = NULL; + +end: + argpar_state_destroy(state); + argpar_item_destroy(item); + free(error); + return cond; +} + +static +struct lttng_condition *handle_condition_buffer_usage_low(int *argc, const char ***argv) +{ + return NULL; +} + +static +struct lttng_condition *handle_condition_session_rotation_ongoing(int *argc, const char ***argv) +{ + return NULL; +} + +static +struct lttng_condition *handle_condition_session_rotation_completed(int *argc, const char ***argv) +{ + return NULL; +} + +struct condition_descr { + const char *name; + struct lttng_condition *(*handler) (int *argc, const char ***argv); +}; + +static const +struct condition_descr condition_descrs[] = { + { "on-event", handle_condition_event }, + { "on-session-consumed-size", handle_condition_session_consumed_size }, + { "on-buffer-usage-high", handle_condition_buffer_usage_high }, + { "on-buffer-usage-low", handle_condition_buffer_usage_low }, + { "on-session-rotation-ongoing", handle_condition_session_rotation_ongoing }, + { "on-session-rotation-completed", handle_condition_session_rotation_completed }, +}; + +static +struct lttng_condition *parse_condition(int *argc, const char ***argv) +{ + int i; + struct lttng_condition *cond; + const char *condition_name; + const struct condition_descr *descr = NULL; + + if (*argc == 0) { + ERR("Missing condition name."); + goto error; + } + + condition_name = (*argv)[0]; + + (*argc)--; + (*argv)++; + + for (i = 0; i < ARRAY_SIZE(condition_descrs); i++) { + if (strcmp(condition_name, condition_descrs[i].name) == 0) { + descr = &condition_descrs[i]; + break; + } + } + + if (!descr) { + ERR("Unknown condition name: %s", condition_name); + goto error; + } + + cond = descr->handler(argc, argv); + if (!cond) { + /* The handler has already printed an error message. */ + goto error; + } + + goto end; +error: + cond = NULL; +end: + return cond; +} + + +static +struct lttng_action *handle_action_notify(int *argc, const char ***argv) +{ + return lttng_action_notify_create(); +} + +static const struct argpar_opt_descr no_opt_descrs[] = { + ARGPAR_OPT_DESCR_SENTINEL +}; + +/* + * Generic handler for a kind of action that takes a session name as its sole + * argument. + */ + +static +struct lttng_action *handle_action_simple_session( + int *argc, const char ***argv, + struct lttng_action *(*create_action_cb)(void), + enum lttng_action_status (*set_session_name_cb)(struct lttng_action *, const char *), + const char *action_name) +{ + struct lttng_action *action = NULL; + struct argpar_state *state = NULL; + struct argpar_item *item = NULL; + const char *session_name_arg = NULL; + char *error = NULL; + enum lttng_action_status action_status; + + state = argpar_state_create(*argc, *argv, no_opt_descrs); + if (!state) { + ERR("Failed to allocate an argpar state."); + goto error; + } + + while (true) { + enum argpar_state_parse_next_status status; + struct argpar_item_non_opt *item_non_opt; + + ARGPAR_ITEM_DESTROY_AND_RESET(item); + status = argpar_state_parse_next(state, &item, &error); + if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { + ERR("%s", error); + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + /* Just stop parsing here. */ + break; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { + break; + } + + assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); + assert(item->type == ARGPAR_ITEM_TYPE_NON_OPT); + + item_non_opt = (struct argpar_item_non_opt *) item; + + switch (item_non_opt->non_opt_index) { + case 0: + session_name_arg = item_non_opt->arg; + break; + default: + ERR("Unexpected argument `%s`.", + item_non_opt->arg); + goto error; + } + } + + *argc -= argpar_state_get_ingested_orig_args(state); + *argv += argpar_state_get_ingested_orig_args(state); + + if (!session_name_arg) { + ERR("Missing session name."); + goto error; + } + + action = create_action_cb(); + if (!action) { + ERR( + "Failed to allocate %s session action.", action_name); + goto error; + } + + action_status = set_session_name_cb(action, session_name_arg); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR( + "Failed to set action %s session's session name.", + action_name); + goto error; + } + + goto end; + +error: + lttng_action_destroy(action); + action = NULL; + +end: + return action; +} + +static +struct lttng_action *handle_action_start_session(int *argc, + const char ***argv) +{ + return handle_action_simple_session(argc, argv, + lttng_action_start_session_create, + lttng_action_start_session_set_session_name, + "start"); +} + +static +struct lttng_action *handle_action_stop_session(int *argc, + const char ***argv) +{ + return handle_action_simple_session(argc, argv, + lttng_action_stop_session_create, + lttng_action_stop_session_set_session_name, + "stop"); +} + +static +struct lttng_action *handle_action_rotate_session(int *argc, + const char ***argv) +{ + return handle_action_simple_session(argc, argv, + lttng_action_rotate_session_create, + lttng_action_rotate_session_set_session_name, + "rotate"); +} + +static const struct argpar_opt_descr snapshot_action_opt_descrs[] = { + { OPT_NAME, 'n', "name", true }, + { OPT_MAX_SIZE, 'm', "max-size", true }, + { OPT_CTRL_URL, '\0', "ctrl-url", true }, + { OPT_DATA_URL, '\0', "data-url", true }, + ARGPAR_OPT_DESCR_SENTINEL +}; + +static +struct lttng_action *handle_action_snapshot_session(int *argc, + const char ***argv) +{ + struct lttng_action *action = NULL; + struct argpar_state *state = NULL; + struct argpar_item *item = NULL; + const char *session_name_arg = NULL; + char *snapshot_name_arg = NULL; + char *ctrl_url_arg = NULL; + char *data_url_arg = NULL; + char *max_size_arg = NULL; + const char *url_arg = NULL; + char *error = NULL; + enum lttng_action_status action_status; + struct lttng_snapshot_output *snapshot_output = NULL; + int ret; + + state = argpar_state_create(*argc, *argv, snapshot_action_opt_descrs); + if (!state) { + ERR("Failed to allocate an argpar state."); + goto error; + } + + while (true) { + enum argpar_state_parse_next_status status; + + ARGPAR_ITEM_DESTROY_AND_RESET(item); + status = argpar_state_parse_next(state, &item, &error); + if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { + ERR("%s", error); + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + /* Just stop parsing here. */ + break; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { + break; + } + + assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_NAME: + if (!assign_string(&snapshot_name_arg, item_opt->arg, "--name/-n")) { + goto error; + } + break; + + case OPT_MAX_SIZE: + if (!assign_string(&max_size_arg, item_opt->arg, "--max-size/-m")) { + goto error; + } + break; + + case OPT_CTRL_URL: + if (!assign_string(&ctrl_url_arg, item_opt->arg, "--ctrl-url")) { + goto error; + } + break; + + case OPT_DATA_URL: + if (!assign_string(&data_url_arg, item_opt->arg, "--data-url")) { + goto error; + } + break; + + default: + abort(); + } + } else { + struct argpar_item_non_opt *item_non_opt; + + assert(item->type == ARGPAR_ITEM_TYPE_NON_OPT); + + item_non_opt = (struct argpar_item_non_opt *) item; + + switch (item_non_opt->non_opt_index) { + case 0: + session_name_arg = item_non_opt->arg; + break; + + // FIXME: the use of a non-option argument for this is to + // follow the syntax of `lttng snapshot record`. But otherwise, + // I think an option argument would be best. + case 1: + url_arg = item_non_opt->arg; + break; + + default: + ERR("Unexpected argument `%s`.", + item_non_opt->arg); + goto error; + } + } + } + + *argc -= argpar_state_get_ingested_orig_args(state); + *argv += argpar_state_get_ingested_orig_args(state); + + if (!session_name_arg) { + ERR("Missing session name."); + goto error; + } + + /* --ctrl-url and --data-url must come in pair. */ + if (ctrl_url_arg && !data_url_arg) { + ERR("--ctrl-url is specified, but --data-url is missing."); + goto error; + } + + if (!ctrl_url_arg && data_url_arg) { + ERR("--data-url is specified, but --ctrl-url is missing."); + goto error; + } + + /* --ctrl-url/--data-url and the non-option URL are mutually exclusive. */ + if (ctrl_url_arg && url_arg) { + ERR("Both --ctrl-url/--data-url and the non-option URL argument " + "can't be used together."); + goto error; + } + + /* + * Did the user specify an option that implies using a + * custom/unregistered output? + */ + if (url_arg || ctrl_url_arg) { + snapshot_output = lttng_snapshot_output_create(); + if (!snapshot_output) { + ERR("Failed to allocate a snapshot output."); + goto error; + } + } + + action = lttng_action_snapshot_session_create(); + if (!action) { + ERR( + "Failed to allocate snapshot session action."); + goto error; + } + + action_status = lttng_action_snapshot_session_set_session_name( + action, session_name_arg); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR( + "Failed to set action snapshot session's session name."); + goto error; + } + + if (snapshot_name_arg) { + if (!snapshot_output) { + ERR("Can't provide a snapshot output name without a snapshot output destination."); + goto error; + } + + ret = lttng_snapshot_output_set_name(snapshot_name_arg, snapshot_output); + if (ret != 0) { + ERR("Failed to set name of snapshot output."); + goto error; + } + } + + if (max_size_arg) { + uint64_t max_size; + + if (!snapshot_output) { + ERR("Can't provide a snapshot output max size without a snapshot output destination."); + goto error; + } + + ret = utils_parse_size_suffix(max_size_arg, &max_size); + if (ret != 0) { + ERR("Failed to parse `%s` as a size.", max_size_arg); + goto error; + } + + ret = lttng_snapshot_output_set_size(max_size, snapshot_output); + if (ret != 0) { + ERR("Failed to set snapshot output's max size."); + goto error; + } + } + + if (url_arg) { + /* One argument form, either net:// / net6:// or a local file path. */ + + if (strncmp(url_arg, "net://", strlen("net://")) == 0 || + strncmp(url_arg, "net6://", strlen("net6://")) == 0) { + ret = lttng_snapshot_output_set_network_url( + url_arg, snapshot_output); + if (ret != 0) { + ERR("Failed to parse %s as a network URL.", url_arg); + goto error; + } + } else { + ret = lttng_snapshot_output_set_local_path( + url_arg, snapshot_output); + if (ret != 0) { + ERR("Failed to parse %s as a local path.", url_arg); + goto error; + } + } + } + + if (ctrl_url_arg) { + /* + * Two argument form, network output with separate control and + * data URLs. + */ + ret = lttng_snapshot_output_set_network_urls( + ctrl_url_arg, data_url_arg, snapshot_output); + if (ret != 0) { + ERR("Failed to parse `%s` and `%s` as control and data URLs.", + ctrl_url_arg, data_url_arg); + goto error; + } + } + + if (snapshot_output) { + action_status = lttng_action_snapshot_session_set_output( + action, snapshot_output); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to set snapshot session action's output."); + goto error; + } + + /* Ownership of `snapshot_output` has been transferred to the action. */ + snapshot_output = NULL; + } + + goto end; + +error: + lttng_action_destroy(action); + action = NULL; + +end: + free(snapshot_name_arg); + free(ctrl_url_arg); + free(data_url_arg); + free(snapshot_output); + return action; +} + +struct action_descr { + const char *name; + struct lttng_action *(*handler) (int *argc, const char ***argv); +}; + +static const +struct action_descr action_descrs[] = { + { "notify", handle_action_notify }, + { "start-session", handle_action_start_session }, + { "stop-session", handle_action_stop_session }, + { "rotate-session", handle_action_rotate_session }, + { "snapshot-session", handle_action_snapshot_session }, +}; + +static +struct lttng_action *parse_action(int *argc, const char ***argv) +{ + int i; + struct lttng_action *action; + const char *action_name; + const struct action_descr *descr = NULL; + + if (*argc == 0) { + ERR("Missing action name."); + goto error; + } + + action_name = (*argv)[0]; + + (*argc)--; + (*argv)++; + + for (i = 0; i < ARRAY_SIZE(action_descrs); i++) { + if (strcmp(action_name, action_descrs[i].name) == 0) { + descr = &action_descrs[i]; + break; + } + } + + if (!descr) { + ERR("Unknown action name: %s", action_name); + goto error; + } + + action = descr->handler(argc, argv); + if (!action) { + /* The handler has already printed an error message. */ + goto error; + } + + goto end; +error: + action = NULL; +end: + return action; +} + +static const +struct argpar_opt_descr add_trigger_options[] = { + { OPT_HELP, 'h', "help", false }, + { OPT_LIST_OPTIONS, '\0', "list-options", false }, + { OPT_CONDITION, '\0', "condition", false }, + { OPT_ACTION, '\0', "action", false }, + { OPT_ID, '\0', "id", true }, + { OPT_FIRE_ONCE_AFTER, '\0', "fire-once-after", true }, + { OPT_FIRE_EVERY, '\0', "fire-every", true }, + { OPT_USER_ID, '\0', "user-id", true }, + ARGPAR_OPT_DESCR_SENTINEL, +}; + +static +void lttng_actions_destructor(void *p) +{ + struct lttng_action *action = p; + + lttng_action_destroy(action); +} + +int cmd_add_trigger(int argc, const char **argv) +{ + int ret; + int my_argc = argc - 1; + const char **my_argv = argv + 1; + struct lttng_condition *condition = NULL; + struct lttng_dynamic_pointer_array actions; + struct argpar_state *argpar_state = NULL; + struct argpar_item *argpar_item = NULL; + struct lttng_action *action_group = NULL; + struct lttng_action *action = NULL; + struct lttng_trigger *trigger = NULL; + char *error = NULL; + char *id = NULL; + int i; + char *fire_once_after_str = NULL; + char *fire_every_str = NULL; + char *user_id = NULL; + + lttng_dynamic_pointer_array_init(&actions, lttng_actions_destructor); + + while (true) { + enum argpar_state_parse_next_status status; + struct argpar_item_opt *item_opt; + int ingested_args; + + argpar_state_destroy(argpar_state); + argpar_state = argpar_state_create(my_argc, my_argv, + add_trigger_options); + if (!argpar_state) { + ERR("Failed to create argpar state."); + goto error; + } + + status = argpar_state_parse_next(argpar_state, &argpar_item, &error); + + if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { + ERR("%s", error); + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + ERR("%s", error); + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { + break; + } + + assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); + + if (argpar_item->type == ARGPAR_ITEM_TYPE_NON_OPT) { + struct argpar_item_non_opt *item_non_opt = + (struct argpar_item_non_opt *) argpar_item; + + ERR("Unexpected argument `%s`.", + item_non_opt->arg); + goto error; + } + + item_opt = (struct argpar_item_opt *) argpar_item; + + ingested_args = argpar_state_get_ingested_orig_args( + argpar_state); + + my_argc -= ingested_args; + my_argv += ingested_args; + + switch (item_opt->descr->id) { + case OPT_HELP: + SHOW_HELP(); + ret = 0; + goto end; + + case OPT_LIST_OPTIONS: + list_cmd_options_argpar(stdout, add_trigger_options); + ret = 0; + goto end; + + case OPT_CONDITION: + { + if (condition) { + ERR("A --condition was already given."); + goto error; + } + + condition = parse_condition(&my_argc, &my_argv); + if (!condition) { + /* + * An error message was already printed by + * parse_condition. + */ + goto error; + } + + break; + } + + case OPT_ACTION: + { + action = parse_action(&my_argc, &my_argv); + if (!action) { + /* + * An error message was already printed by + * parse_condition. + */ + goto error; + } + + ret = lttng_dynamic_pointer_array_add_pointer( + &actions, action); + if (ret) { + ERR("Failed to add pointer to pointer array."); + goto error; + } + + /* Ownership of the action was transferred to the group. */ + action = NULL; + + break; + } + + case OPT_ID: + { + if (!assign_string(&id, item_opt->arg, "--id")) { + goto error; + } + + break; + } + + case OPT_FIRE_ONCE_AFTER: + { + if (!assign_string(&fire_once_after_str, item_opt->arg, + "--fire-once-after")) { + goto error; + } + break; + } + + case OPT_FIRE_EVERY: + { + if (!assign_string(&fire_every_str, item_opt->arg, + "--fire-every")) { + goto error; + } + break; + } + + case OPT_USER_ID: + { + if (!assign_string(&user_id, item_opt->arg, + "--user-id")) { + goto error; + } + break; + } + + default: + abort(); + } + } + + if (!condition) { + ERR("Missing --condition."); + goto error; + } + + if (lttng_dynamic_pointer_array_get_count(&actions) == 0) { + ERR("Need at least one --action."); + goto error; + } + + if (fire_every_str && fire_once_after_str) { + ERR("Can't specify both --fire-once-after and --fire-every."); + goto error; + } + + action_group = lttng_action_group_create(); + if (!action_group) { + goto error; + } + + for (i = 0; i < lttng_dynamic_pointer_array_get_count(&actions); i++) { + enum lttng_action_status status; + + action = lttng_dynamic_pointer_array_steal_pointer(&actions, i); + + status = lttng_action_group_add_action( + action_group, action); + if (status != LTTNG_ACTION_STATUS_OK) { + goto error; + } + + /* Ownership of the action was transferred to the group. */ + action = NULL; + } + + + trigger = lttng_trigger_create(condition, action_group); + if (!trigger) { + goto error; + } + + if (id) { + enum lttng_trigger_status trigger_status = + lttng_trigger_set_name(trigger, id); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + ERR("Failed to set trigger id."); + goto error; + } + } + + if (fire_once_after_str) { + unsigned long long threshold; + enum lttng_trigger_status trigger_status; + + if (utils_parse_unsigned_long_long(fire_once_after_str, &threshold) != 0) { + ERR("Failed to parse `%s` as an integer.", fire_once_after_str); + goto error; + } + + trigger_status = lttng_trigger_set_firing_policy(trigger, + LTTNG_TRIGGER_FIRE_ONCE_AFTER_N, threshold); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + ERR("Failed to set trigger's firing policy."); + goto error; + } + } + + if (fire_every_str) { + unsigned long long threshold; + enum lttng_trigger_status trigger_status; + + if (utils_parse_unsigned_long_long(fire_every_str, &threshold) != 0) { + ERR("Failed to parse `%s` as an integer.", fire_every_str); + goto error; + } + + trigger_status = lttng_trigger_set_firing_policy(trigger, + LTTNG_TRIGGER_FIRE_EVERY_N, threshold); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + ERR("Failed to set trigger's firing policy."); + goto error; + } + } + + if (user_id) { + enum lttng_trigger_status trigger_status; + char *end; + long long uid; + + errno = 0; + uid = strtol(user_id, &end, 10); + if (end == user_id || *end != '\0' || errno != 0) { + ERR("Failed to parse `%s` as an integer.", user_id); + } + + trigger_status = lttng_trigger_set_user_identity(trigger, + uid); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + ERR("Failed to set trigger's user identity."); + goto error; + } + } + + ret = lttng_register_trigger(trigger); + if (ret) { + ERR("Failed to register trigger: %s.", + lttng_strerror(ret)); + goto error; + } + + MSG("Trigger registered successfully."); + + goto end; + +error: + ret = 1; + +end: + argpar_state_destroy(argpar_state); + lttng_dynamic_pointer_array_reset(&actions); + lttng_condition_destroy(condition); + lttng_action_destroy(action_group); + lttng_trigger_destroy(trigger); + free(id); + free(fire_once_after_str); + free(fire_every_str); + free(user_id); + // TODO: check what else to free + + return ret; +} diff --git a/src/bin/lttng/commands/enable_events.c b/src/bin/lttng/commands/enable_events.c index f41dc9eca..2187f7029 100644 --- a/src/bin/lttng/commands/enable_events.c +++ b/src/bin/lttng/commands/enable_events.c @@ -26,7 +26,10 @@ /* Mi dependancy */ #include +#include + #include "../command.h" +#include "../uprobe.h" #if (LTTNG_SYMBOL_NAME_LEN == 256) #define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "255" @@ -174,397 +177,11 @@ end: return ret; } -/* - * Walk the directories in the PATH environment variable to find the target - * binary passed as parameter. - * - * On success, the full path of the binary is copied in binary_full_path out - * parameter. This buffer is allocated by the caller and must be at least - * LTTNG_PATH_MAX bytes long. - * On failure, returns -1; - */ -static int walk_command_search_path(const char *binary, char *binary_full_path) -{ - char *tentative_binary_path = NULL; - char *command_search_path = NULL; - char *curr_search_dir_end = NULL; - char *curr_search_dir = NULL; - struct stat stat_output; - int ret = 0; - - command_search_path = lttng_secure_getenv("PATH"); - if (!command_search_path) { - ret = -1; - goto end; - } - - /* - * Duplicate the $PATH string as the char pointer returned by getenv() should - * not be modified. - */ - command_search_path = strdup(command_search_path); - if (!command_search_path) { - ret = -1; - goto end; - } - - /* - * This char array is used to concatenate path to binary to look for - * the binary. - */ - tentative_binary_path = zmalloc(LTTNG_PATH_MAX * sizeof(char)); - if (!tentative_binary_path) { - ret = -1; - goto alloc_error; - } - - curr_search_dir = command_search_path; - do { - /* - * Split on ':'. The return value of this call points to the - * matching character. - */ - curr_search_dir_end = strchr(curr_search_dir, ':'); - if (curr_search_dir_end != NULL) { - /* - * Add a NULL byte to the end of the first token so it - * can be used as a string. - */ - curr_search_dir_end[0] = '\0'; - } - - /* Empty the tentative path */ - memset(tentative_binary_path, 0, LTTNG_PATH_MAX * sizeof(char)); - - /* - * Build the tentative path to the binary using the current - * search directory and the name of the binary. - */ - ret = snprintf(tentative_binary_path, LTTNG_PATH_MAX, "%s/%s", - curr_search_dir, binary); - if (ret < 0) { - goto free_binary_path; - } - if (ret < LTTNG_PATH_MAX) { - /* - * Use STAT(2) to see if the file exists. - */ - ret = stat(tentative_binary_path, &stat_output); - if (ret == 0) { - /* - * Verify that it is a regular file or a - * symlink and not a special file (e.g. - * device). - */ - if (S_ISREG(stat_output.st_mode) - || S_ISLNK(stat_output.st_mode)) { - /* - * Found a match, set the out parameter - * and return success. - */ - ret = lttng_strncpy(binary_full_path, - tentative_binary_path, - LTTNG_PATH_MAX); - if (ret == -1) { - ERR("Source path does not fit " - "in destination buffer."); - } - goto free_binary_path; - } - } - } - /* Go to the next entry in the $PATH variable. */ - curr_search_dir = curr_search_dir_end + 1; - } while (curr_search_dir_end != NULL); - -free_binary_path: - free(tentative_binary_path); -alloc_error: - free(command_search_path); -end: - return ret; -} - -/* - * Check if the symbol field passed by the user is in fact an address or an - * offset from a symbol. Those two instrumentation types are not supported yet. - * It's expected to be a common mistake because of the existing --probe option - * that does support these formats. - * - * Here are examples of these unsupported formats for the --userspace-probe - * option: - * elf:/path/to/binary:0x400430 - * elf:/path/to/binary:4194364 - * elf:/path/to/binary:my_symbol+0x323 - * elf:/path/to/binary:my_symbol+43 - */ -static int warn_userspace_probe_syntax(const char *symbol) -{ - int ret; - - /* Check if the symbol field is an hex address. */ - ret = sscanf(symbol, "0x%*x"); - if (ret > 0) { - /* If there is a match, print a warning and return an error. */ - ERR("Userspace probe on address not supported yet."); - ret = CMD_UNSUPPORTED; - goto error; - } - - /* Check if the symbol field is an decimal address. */ - ret = sscanf(symbol, "%*u"); - if (ret > 0) { - /* If there is a match, print a warning and return an error. */ - ERR("Userspace probe on address not supported yet."); - ret = CMD_UNSUPPORTED; - goto error; - } - - /* Check if the symbol field is symbol+hex_offset. */ - ret = sscanf(symbol, "%*[^+]+0x%*x"); - if (ret > 0) { - /* If there is a match, print a warning and return an error. */ - ERR("Userspace probe on symbol+offset not supported yet."); - ret = CMD_UNSUPPORTED; - goto error; - } - - /* Check if the symbol field is symbol+decimal_offset. */ - ret = sscanf(symbol, "%*[^+]+%*u"); - if (ret > 0) { - /* If there is a match, print a warning and return an error. */ - ERR("Userspace probe on symbol+offset not supported yet."); - ret = CMD_UNSUPPORTED; - goto error; - } - - ret = 0; - -error: - return ret; -} - -/* - * Parse userspace probe options - * Set the userspace probe fields in the lttng_event struct and set the - * target_path to the path to the binary. - */ -static int parse_userspace_probe_opts(struct lttng_event *ev, char *opt) -{ - int ret = CMD_SUCCESS; - int num_token; - char **tokens; - char *target_path = NULL; - char *unescaped_target_path = NULL; - char *real_target_path = NULL; - char *symbol_name = NULL, *probe_name = NULL, *provider_name = NULL; - struct lttng_userspace_probe_location *probe_location = NULL; - struct lttng_userspace_probe_location_lookup_method *lookup_method = - NULL; - - if (opt == NULL) { - ret = CMD_ERROR; - goto end; - } - - switch (ev->type) { - case LTTNG_EVENT_USERSPACE_PROBE: - break; - default: - assert(0); - } - - /* - * userspace probe fields are separated by ':'. - */ - tokens = strutils_split(opt, ':', 1); - num_token = strutils_array_of_strings_len(tokens); - - /* - * Early sanity check that the number of parameter is between 2 and 4 - * inclusively. - * elf:PATH:SYMBOL - * std:PATH:PROVIDER_NAME:PROBE_NAME - * PATH:SYMBOL (same behavior as ELF) - */ - if (num_token < 2 || num_token > 4) { - ret = CMD_ERROR; - goto end_string; - } - - /* - * Looking up the first parameter will tell the technique to use to - * interpret the userspace probe/function description. - */ - switch (num_token) { - case 2: - /* When the probe type is omitted we assume ELF for now. */ - case 3: - if (num_token == 3 && strcmp(tokens[0], "elf") == 0) { - target_path = tokens[1]; - symbol_name = tokens[2]; - } else if (num_token == 2) { - target_path = tokens[0]; - symbol_name = tokens[1]; - } else { - ret = CMD_ERROR; - goto end_string; - } - lookup_method = - lttng_userspace_probe_location_lookup_method_function_elf_create(); - if (!lookup_method) { - WARN("Failed to create ELF lookup method"); - ret = CMD_ERROR; - goto end_string; - } - break; - case 4: - if (strcmp(tokens[0], "sdt") == 0) { - target_path = tokens[1]; - provider_name = tokens[2]; - probe_name = tokens[3]; - } else { - ret = CMD_ERROR; - goto end_string; - } - lookup_method = - lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create(); - if (!lookup_method) { - WARN("Failed to create SDT lookup method"); - ret = CMD_ERROR; - goto end_string; - } - break; - default: - ret = CMD_ERROR; - goto end_string; - } - - /* strutils_unescape_string allocates a new char *. */ - unescaped_target_path = strutils_unescape_string(target_path, 0); - if (!unescaped_target_path) { - ret = CMD_ERROR; - goto end_destroy_lookup_method; - } - - /* - * If there is not forward slash in the path. Walk the $PATH else - * expand. - */ - if (strchr(unescaped_target_path, '/') == NULL) { - /* Walk the $PATH variable to find the targeted binary. */ - real_target_path = zmalloc(LTTNG_PATH_MAX * sizeof(char)); - if (!real_target_path) { - PERROR("Error allocating path buffer"); - ret = CMD_ERROR; - goto end_destroy_lookup_method; - } - ret = walk_command_search_path(unescaped_target_path, real_target_path); - if (ret) { - ERR("Binary not found."); - ret = CMD_ERROR; - goto end_destroy_lookup_method; - } - } else { - /* - * Expand references to `/./` and `/../`. This function does not check - * if the file exists. This call returns an allocated buffer on - * success. - */ - real_target_path = utils_expand_path_keep_symlink(unescaped_target_path); - if (!real_target_path) { - ERR("Error expanding the path to binary."); - ret = CMD_ERROR; - goto end_destroy_lookup_method; - } - - /* - * Check if the file exists using access(2), If it does not, - * return an error. - */ - ret = access(real_target_path, F_OK); - if (ret) { - ERR("Cannot find binary at path: %s.", real_target_path); - ret = CMD_ERROR; - goto end_destroy_lookup_method; - } - } - - switch (lttng_userspace_probe_location_lookup_method_get_type(lookup_method)) { - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: - /* - * Check for common mistakes in userspace probe description syntax. - */ - ret = warn_userspace_probe_syntax(symbol_name); - if (ret) { - goto end_destroy_lookup_method; - } - - probe_location = lttng_userspace_probe_location_function_create( - real_target_path, symbol_name, lookup_method); - if (!probe_location) { - WARN("Failed to create function probe location"); - ret = CMD_ERROR; - goto end_destroy_lookup_method; - } - - /* Ownership transferred to probe_location. */ - lookup_method = NULL; - - ret = lttng_event_set_userspace_probe_location(ev, probe_location); - if (ret) { - WARN("Failed to set probe location on event"); - ret = CMD_ERROR; - goto end_destroy_location; - } - break; - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: - probe_location = lttng_userspace_probe_location_tracepoint_create( - real_target_path, provider_name, probe_name, lookup_method); - if (!probe_location) { - WARN("Failed to create function probe location"); - ret = CMD_ERROR; - goto end_destroy_lookup_method; - } - - /* Ownership transferred to probe_location. */ - lookup_method = NULL; - - ret = lttng_event_set_userspace_probe_location(ev, probe_location); - if (ret) { - WARN("Failed to set probe location on event"); - ret = CMD_ERROR; - goto end_destroy_location; - } - break; - default: - ret = CMD_ERROR; - goto end_destroy_lookup_method; - } - - /* Successful parsing, now clean up everything and return. */ - goto end_string; - -end_destroy_location: - lttng_userspace_probe_location_destroy(probe_location); -end_destroy_lookup_method: - lttng_userspace_probe_location_lookup_method_destroy(lookup_method); -end_string: - strutils_free_null_terminated_array_of_strings(tokens); - /* - * Freeing both char * here makes the error handling simplier. free() - * performs not action if the pointer is NULL. - */ - free(real_target_path); - free(unescaped_target_path); -end: - return ret; -} - /* * Maps LOG4j loglevel from string to value */ -static int loglevel_log4j_str_to_value(const char *inputstr) +LTTNG_HIDDEN +int loglevel_log4j_str_to_value(const char *inputstr) { int i = 0; char str[LTTNG_SYMBOL_NAME_LEN]; @@ -607,7 +224,8 @@ static int loglevel_log4j_str_to_value(const char *inputstr) /* * Maps JUL loglevel from string to value */ -static int loglevel_jul_str_to_value(const char *inputstr) +LTTNG_HIDDEN +int loglevel_jul_str_to_value(const char *inputstr) { int i = 0; char str[LTTNG_SYMBOL_NAME_LEN]; @@ -652,7 +270,8 @@ static int loglevel_jul_str_to_value(const char *inputstr) /* * Maps Python loglevel from string to value */ -static int loglevel_python_str_to_value(const char *inputstr) +LTTNG_HIDDEN +int loglevel_python_str_to_value(const char *inputstr) { int i = 0; char str[LTTNG_SYMBOL_NAME_LEN]; @@ -691,7 +310,7 @@ static int loglevel_python_str_to_value(const char *inputstr) /* * Maps loglevel from string to value */ -static +LTTNG_HIDDEN int loglevel_str_to_value(const char *inputstr) { int i = 0; @@ -891,7 +510,16 @@ end: return ret; } -static +/* + * FIXME: find a good place to declare this since add trigger also uses it + */ +LTTNG_HIDDEN +int create_exclusion_list_and_validate(const char *event_name, + const char *exclusions_arg, + char ***exclusion_list); + + +LTTNG_HIDDEN int create_exclusion_list_and_validate(const char *event_name, const char *exclusions_arg, char ***exclusion_list) @@ -969,6 +597,7 @@ static int enable_events(char *session_name) struct lttng_event *ev; struct lttng_domain dom; char **exclusion_list = NULL; + struct lttng_userspace_probe_location *uprobe_loc = NULL; memset(&dom, 0, sizeof(dom)); @@ -1356,7 +985,9 @@ static int enable_events(char *session_name) } break; case LTTNG_EVENT_USERSPACE_PROBE: - ret = parse_userspace_probe_opts(ev, opt_userspace_probe); + assert(ev->type == LTTNG_EVENT_USERSPACE_PROBE); + + ret = parse_userspace_probe_opts(opt_userspace_probe, &uprobe_loc); if (ret) { switch (ret) { case CMD_UNSUPPORTED: @@ -1373,6 +1004,16 @@ static int enable_events(char *session_name) } goto error; } + + ret = lttng_event_set_userspace_probe_location(ev, uprobe_loc); + if (ret) { + WARN("Failed to set probe location on event"); + ret = CMD_ERROR; + goto error; + } + + /* Ownership of the uprobe location was transferred to the event. */ + uprobe_loc = NULL; break; case LTTNG_EVENT_FUNCTION: ret = parse_probe_opts(ev, opt_function); @@ -1689,6 +1330,7 @@ error: } lttng_destroy_handle(handle); strutils_free_null_terminated_array_of_strings(exclusion_list); + lttng_userspace_probe_location_destroy(uprobe_loc); /* Overwrite ret with error_holder if there was an actual error with * enabling an event. diff --git a/src/bin/lttng/commands/list_triggers.c b/src/bin/lttng/commands/list_triggers.c new file mode 100644 index 000000000..2c6559be9 --- /dev/null +++ b/src/bin/lttng/commands/list_triggers.c @@ -0,0 +1,692 @@ +#include + +#include "../command.h" + +#include "common/argpar/argpar.h" +#include "common/mi-lttng.h" +#include "lttng/action/action.h" +#include "lttng/action/action-internal.h" +#include "lttng/condition/condition-internal.h" +#include "lttng/condition/event-rule.h" +#include "lttng/domain-internal.h" +#include "lttng/event-rule/event-rule-internal.h" +#include "lttng/event-rule/kprobe.h" +#include "lttng/event-rule/kprobe-internal.h" +#include "lttng/event-rule/syscall.h" +#include "lttng/event-rule/tracepoint.h" +#include "lttng/event-rule/uprobe.h" +#include "lttng/trigger/trigger-internal.h" +#include "lttng/kernel-probe.h" + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP, + OPT_LIST_OPTIONS, +}; + +static const +struct argpar_opt_descr list_trigger_options[] = { + { OPT_HELP, 'h', "help", false }, + { OPT_LIST_OPTIONS, '\0', "list-options", false }, + ARGPAR_OPT_DESCR_SENTINEL, +}; + +static +void print_event_rule_tracepoint(const struct lttng_event_rule *event_rule) +{ + enum lttng_event_rule_status event_rule_status; + enum lttng_domain_type domain_type; + const char *pattern; + const char *filter; + int log_level; + unsigned int exclusions_count; + int i; + + event_rule_status = lttng_event_rule_tracepoint_get_pattern( + event_rule, &pattern); + assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); + + event_rule_status = lttng_event_rule_tracepoint_get_domain_type( + event_rule, &domain_type); + assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); + + _MSG(" rule: %s (type: tracepoint, domain: %s", pattern, + lttng_domain_type_str(domain_type)); + + event_rule_status = lttng_event_rule_tracepoint_get_filter( + event_rule, &filter); + if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) { + _MSG(", filter: %s", filter); + } else { + assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET); + } + + event_rule_status = lttng_event_rule_tracepoint_get_log_level( + event_rule, &log_level); + if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) { + enum lttng_loglevel_type log_level_type; + const char *log_level_op; + + event_rule_status = lttng_event_rule_tracepoint_get_log_level_type( + event_rule, &log_level_type); + assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); + assert(log_level_type == LTTNG_EVENT_LOGLEVEL_RANGE || + log_level_type == LTTNG_EVENT_LOGLEVEL_SINGLE); + + log_level_op = (log_level_type == LTTNG_EVENT_LOGLEVEL_RANGE ? "<=" : "=="); + + _MSG(", log level %s %s", log_level_op, + mi_lttng_loglevel_string(log_level, domain_type)); + } else { + assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET); + } + + event_rule_status = lttng_event_rule_tracepoint_get_exclusions_count( + event_rule, &exclusions_count); + assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); + if (exclusions_count > 0) { + _MSG(", exclusions: "); + for (i = 0; i < exclusions_count; i++) { + const char *exclusion; + + event_rule_status = lttng_event_rule_tracepoint_get_exclusion_at_index( + event_rule, i, &exclusion); + assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); + + _MSG("%s%s", i > 0 ? "," : "", exclusion); + } + } + + MSG(")"); +} + +static void print_kernel_probe_location( + const struct lttng_kernel_probe_location *location) +{ + enum lttng_kernel_probe_location_status status; + switch (lttng_kernel_probe_location_get_type(location)) { + case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS: + { + uint64_t address; + + status = lttng_kernel_probe_location_address_get_address(location, &address); + if (status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) { + ERR("Getting kernel probe location address failed."); + goto end; + } + + _MSG("0x%" PRIx64, address); + + break; + } + case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET: + { + uint64_t offset; + const char *symbol_name; + + symbol_name = lttng_kernel_probe_location_symbol_get_name(location); + if (!symbol_name) { + ERR("Getting kernel probe location symbol name failed."); + goto end; + } + + status = lttng_kernel_probe_location_symbol_get_offset(location, &offset); + if (status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) { + ERR("Getting kernel probe location address failed."); + goto end; + } + + if (offset == 0) { + _MSG("%s", symbol_name); + } else { + _MSG("%s+0x%" PRIx64, symbol_name, offset); + } + + + break; + } + default: + abort(); + }; +end: + return; +} + +static +void print_event_rule_kprobe(const struct lttng_event_rule *event_rule) +{ + enum lttng_event_rule_status event_rule_status; + const char *name; + const struct lttng_kernel_probe_location *location; + + assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_KPROBE); + + event_rule_status = lttng_event_rule_kprobe_get_name(event_rule, &name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to get kprobe event rule's name."); + goto end; + } + + event_rule_status = lttng_event_rule_kprobe_get_location(event_rule, &location); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to get kprobe event rule's location."); + goto end; + } + + _MSG(" rule: %s (type: probe, location: ", name); + + print_kernel_probe_location(location); + + MSG(")"); + +end: + return; +} + +static +void print_event_rule_uprobe(const struct lttng_event_rule *event_rule) +{ + enum lttng_event_rule_status event_rule_status; + const char *name; + const struct lttng_userspace_probe_location *location; + enum lttng_userspace_probe_location_type userspace_probe_location_type; + + assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_UPROBE); + + event_rule_status = lttng_event_rule_uprobe_get_name(event_rule, &name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to get uprobe event rule's name."); + goto end; + } + + event_rule_status = lttng_event_rule_uprobe_get_location(event_rule, &location); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to get uprobe event rule's location."); + goto end; + } + + _MSG(" rule: %s (type: userspace probe, location: ", name); + + userspace_probe_location_type = + lttng_userspace_probe_location_get_type(location); + + switch (userspace_probe_location_type) { + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: + { + const char *binary_path, *function_name; + + binary_path = lttng_userspace_probe_location_function_get_binary_path(location); + function_name = lttng_userspace_probe_location_function_get_function_name(location); + + _MSG("%s:%s", binary_path, function_name); + break; + } + + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: + _MSG("SDT not implemented yet"); + break; + + default: + abort(); + } + + MSG(")"); + +end: + return; +} + +static +void print_event_rule_syscall(const struct lttng_event_rule *event_rule) +{ + const char *pattern, *filter; + enum lttng_event_rule_status event_rule_status; + + assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_SYSCALL); + + event_rule_status = lttng_event_rule_syscall_get_pattern(event_rule, &pattern); + assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); + + _MSG(" - rule: %s (type: syscall", pattern); + + event_rule_status = lttng_event_rule_syscall_get_filter( + event_rule, &filter); + if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) { + _MSG(", filter: %s", filter); + } else { + assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET); + } + + MSG(")"); +} + +static +void print_event_rule(const struct lttng_event_rule *event_rule) +{ + enum lttng_event_rule_type event_rule_type = + lttng_event_rule_get_type(event_rule); + + switch (event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + print_event_rule_tracepoint(event_rule); + break; + + case LTTNG_EVENT_RULE_TYPE_KPROBE: + print_event_rule_kprobe(event_rule); + break; + + case LTTNG_EVENT_RULE_TYPE_UPROBE: + print_event_rule_uprobe(event_rule); + break; + + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + print_event_rule_syscall(event_rule); + break; + + default: + abort(); + } +} + +static +void print_one_event_expr(const struct lttng_event_expr *event_expr) +{ + enum lttng_event_expr_type type; + + type = lttng_event_expr_get_type(event_expr); + + switch (type) { + case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: { + const char *name; + + name = lttng_event_expr_event_payload_field_get_name(event_expr); + _MSG("%s", name); + + break; + } + + case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: { + const char *name; + + name = lttng_event_expr_channel_context_field_get_name(event_expr); + _MSG("$ctx.%s", name); + + break; + } + + case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: { + const char *provider_name; + const char *type_name; + + provider_name = + lttng_event_expr_app_specific_context_field_get_provider_name( + event_expr); + type_name = + lttng_event_expr_app_specific_context_field_get_type_name( + event_expr); + + _MSG("$app.%s:%s", provider_name, type_name); + + break; + } + + case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: { + unsigned int index; + const struct lttng_event_expr *parent_expr; + enum lttng_event_expr_status status; + + parent_expr = lttng_event_expr_array_field_element_get_parent_expr( + event_expr); + assert(parent_expr != NULL); + + print_one_event_expr(parent_expr); + + status = lttng_event_expr_array_field_element_get_index( + event_expr, &index); + assert(status == LTTNG_EVENT_EXPR_STATUS_OK); + + _MSG("[%u]", index); + + break; + } + + default: + abort(); + } +} + +static +void print_condition_event_rule_hit(const struct lttng_condition *condition) +{ + const struct lttng_event_rule *event_rule; + enum lttng_condition_status condition_status; + unsigned int cap_desc_count, i; + + condition_status = + lttng_condition_event_rule_get_rule(condition, &event_rule); + assert(condition_status == LTTNG_CONDITION_STATUS_OK); + + print_event_rule(event_rule); + + condition_status + = lttng_condition_event_rule_get_capture_descriptor_count( + condition, &cap_desc_count); + assert(condition_status == LTTNG_CONDITION_STATUS_OK); + + if (cap_desc_count > 0) { + MSG(" captures:"); + + for (i = 0; i < cap_desc_count; i++) { + const struct lttng_event_expr *cap_desc = + lttng_condition_event_rule_get_capture_descriptor_at_index( + condition, i); + + _MSG(" - "); + print_one_event_expr(cap_desc); + MSG(""); + } + } +} + +static +void print_one_action(const struct lttng_action *action) +{ + enum lttng_action_type action_type; + enum lttng_action_status action_status; + const char *value; + + action_type = lttng_action_get_type(action); + assert(action_type != LTTNG_ACTION_TYPE_GROUP); + + switch (action_type) { + case LTTNG_ACTION_TYPE_NOTIFY: + MSG("notify"); + break; + + case LTTNG_ACTION_TYPE_START_SESSION: + action_status = lttng_action_start_session_get_session_name( + action, &value); + assert(action_status == LTTNG_ACTION_STATUS_OK); + MSG("start session `%s`", value); + break; + + case LTTNG_ACTION_TYPE_STOP_SESSION: + action_status = lttng_action_stop_session_get_session_name( + action, &value); + assert(action_status == LTTNG_ACTION_STATUS_OK); + MSG("stop session `%s`", value); + break; + + case LTTNG_ACTION_TYPE_ROTATE_SESSION: + action_status = lttng_action_rotate_session_get_session_name( + action, &value); + assert(action_status == LTTNG_ACTION_STATUS_OK); + MSG("rotate session `%s`", value); + break; + + case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION: + { + const struct lttng_snapshot_output *output; + + action_status = lttng_action_snapshot_session_get_session_name( + action, &value); + assert(action_status == LTTNG_ACTION_STATUS_OK); + _MSG("snapshot session `%s`", value); + + action_status = lttng_action_snapshot_session_get_output( + action, &output); + if (action_status == LTTNG_ACTION_STATUS_OK) { + const char *name; + uint64_t max_size; + const char *ctrl_url, *data_url; + bool starts_with_file, starts_with_net, starts_with_net6; + + ctrl_url = lttng_snapshot_output_get_ctrl_url(output); + assert(ctrl_url && strlen(ctrl_url) > 0); + + data_url = lttng_snapshot_output_get_data_url(output); + assert(data_url); + + starts_with_file = strncmp(ctrl_url, "file://", strlen("file://")) == 0; + starts_with_net = strncmp(ctrl_url, "net://", strlen("net://")) == 0; + starts_with_net6 = strncmp(ctrl_url, "net6://", strlen("net6://")) == 0; + + if (ctrl_url[0] == '/' || starts_with_file) { + if (starts_with_file) { + ctrl_url += strlen("file://"); + } + + _MSG(", path: %s", ctrl_url); + } else if (starts_with_net || starts_with_net6) { + _MSG(", url: %s", ctrl_url); + } else { + assert(strlen(data_url) > 0); + + _MSG(", control url: %s, data url: %s", ctrl_url, data_url); + } + + name = lttng_snapshot_output_get_name(output); + assert(name); + if (strlen(name) > 0) { + _MSG(", name: %s", name); + } + + max_size = lttng_snapshot_output_get_maxsize(output); + if (max_size != -1ULL) { + _MSG(", max size: %" PRIu64, max_size); + } + } + + MSG(""); + break; + } + + default: + abort(); + } +} + +static +void print_one_trigger(const struct lttng_trigger *trigger) +{ + const struct lttng_condition *condition; + enum lttng_condition_type condition_type; + const struct lttng_action *action; + enum lttng_action_type action_type; + enum lttng_trigger_status trigger_status; + const char *name; + enum lttng_trigger_firing_policy_type policy_type; + uint64_t threshold; + uid_t trigger_uid; + long error_counter; + + trigger_status = lttng_trigger_get_name(trigger, &name); + assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + trigger_status = lttng_trigger_get_user_identity(trigger, &trigger_uid); + assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + MSG("- id: %s", name); + MSG(" user id: %d", trigger_uid); + + trigger_status = lttng_trigger_get_firing_policy(trigger, + &policy_type, &threshold); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + ERR("Failed to get trigger's firing policy."); + goto end; + } + + switch (policy_type) { + case LTTNG_TRIGGER_FIRE_EVERY_N: + if (threshold > 1) { + MSG(" firing policy: after every %" PRIu64 " occurences", threshold); + } + break; + + case LTTNG_TRIGGER_FIRE_ONCE_AFTER_N: + MSG(" firing policy: once after %" PRIu64 " occurences", threshold); + break; + + default: + abort(); + } + + // FIXME: This should be printed in the condition printing function + error_counter = lttng_trigger_get_error_count(trigger); + MSG(" tracer notifications discarded: %ld", error_counter); + + condition = lttng_trigger_get_const_condition(trigger); + condition_type = lttng_condition_get_type(condition); + MSG(" condition: %s", + lttng_condition_type_str(condition_type)); + + switch (condition_type) { + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + print_condition_event_rule_hit(condition); + break; + + default: + MSG(" (condition type not handled in %s)", __func__); + break; + } + + action = lttng_trigger_get_const_action(trigger); + action_type = lttng_action_get_type(action); + if (action_type == LTTNG_ACTION_TYPE_GROUP) { + enum lttng_action_status action_status; + unsigned int count, i; + + MSG(" actions:"); + + action_status = lttng_action_group_get_count(action, &count); + assert(action_status == LTTNG_ACTION_STATUS_OK); + + for (i = 0; i < count; i++) { + const struct lttng_action *subaction = + lttng_action_group_get_at_index(action, i); + + _MSG(" "); + print_one_action(subaction); + } + } else { + _MSG(" action:"); + print_one_action(action); + } + +end: + return; +} + +static +int compare_triggers_by_name(const void *a, const void *b) +{ + const struct lttng_trigger *trigger_a = *((const struct lttng_trigger **) a); + const struct lttng_trigger *trigger_b = *((const struct lttng_trigger **) b); + const char *name_a, *name_b; + enum lttng_trigger_status trigger_status; + + trigger_status = lttng_trigger_get_name(trigger_a, &name_a); + assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + trigger_status = lttng_trigger_get_name(trigger_b, &name_b); + assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + return strcmp(name_a, name_b); +} + +int cmd_list_triggers(int argc, const char **argv) +{ + int ret; + struct argpar_parse_ret argpar_parse_ret = { 0 }; + struct lttng_triggers *triggers = NULL; + int i; + const struct lttng_trigger **sorted_triggers = NULL; + enum lttng_trigger_status trigger_status; + unsigned int num_triggers; + + argpar_parse_ret = argpar_parse(argc - 1, argv + 1, + list_trigger_options, true); + if (!argpar_parse_ret.items) { + ERR("%s", argpar_parse_ret.error); + goto error; + } + + for (i = 0; i < argpar_parse_ret.items->n_items; i++) { + struct argpar_item *item = argpar_parse_ret.items->items[i]; + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_HELP: + SHOW_HELP(); + ret = 0; + goto end; + + case OPT_LIST_OPTIONS: + list_cmd_options_argpar(stdout, + list_trigger_options); + ret = 0; + goto end; + + default: + abort(); + } + + } else { + struct argpar_item_non_opt *item_non_opt = + (struct argpar_item_non_opt *) item; + + ERR("Unexpected argument: %s", item_non_opt->arg); + } + } + + ret = lttng_list_triggers(&triggers); + if (ret != LTTNG_OK) { + ERR("Error listing triggers: %s.", + lttng_strerror(ret)); + goto error; + } + + trigger_status = lttng_triggers_get_count(triggers, &num_triggers); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + ERR("Failed to get trigger count."); + goto error; + } + + sorted_triggers = calloc(num_triggers, sizeof(struct lttng_trigger *)); + if (!sorted_triggers) { + ERR("Failed to allocate array of struct lttng_trigger *."); + goto error; + } + + for (i = 0; i < num_triggers; i++) { + sorted_triggers[i] = lttng_triggers_get_at_index(triggers, i); + } + + qsort(sorted_triggers, num_triggers, sizeof(struct lttng_trigger *), + compare_triggers_by_name); + + for (i = 0; i < num_triggers; i++) { + print_one_trigger(sorted_triggers[i]); + } + + ret = 0; + + goto end; + +error: + ret = 1; + +end: + argpar_parse_ret_fini(&argpar_parse_ret); + lttng_triggers_destroy(triggers); + + return ret; +} diff --git a/src/bin/lttng/commands/remove_trigger.c b/src/bin/lttng/commands/remove_trigger.c new file mode 100644 index 000000000..e7ef29318 --- /dev/null +++ b/src/bin/lttng/commands/remove_trigger.c @@ -0,0 +1,191 @@ +#include + +#include "../command.h" + +#include "common/argpar/argpar.h" + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP, + OPT_LIST_OPTIONS, + OPT_USER_ID, +}; + +static const +struct argpar_opt_descr remove_trigger_options[] = { + { OPT_HELP, 'h', "help", false }, + { OPT_LIST_OPTIONS, '\0', "list-options", false }, + { OPT_USER_ID, '\0', "user-id", true }, + ARGPAR_OPT_DESCR_SENTINEL, +}; + +static +bool assign_string(char **dest, const char *src, const char *opt_name) +{ + bool ret; + + if (*dest) { + ERR( + "Duplicate %s given.", opt_name); + goto error; + } + + *dest = strdup(src); + if (!*dest) { + ERR("Failed to allocate %s string.", opt_name); + goto error; + } + + ret = true; + goto end; + +error: + ret = false; + +end: + return ret; +} + +int cmd_remove_trigger(int argc, const char **argv) +{ + int ret; + struct argpar_parse_ret argpar_parse_ret = { 0 }; + const char *id = NULL; + int i; + struct lttng_triggers *triggers = NULL; + unsigned int triggers_count; + enum lttng_trigger_status trigger_status; + const struct lttng_trigger *trigger_to_remove = NULL; + char *user_id = NULL; + long long uid; + + argpar_parse_ret = argpar_parse(argc - 1, argv + 1, + remove_trigger_options, true); + if (!argpar_parse_ret.items) { + ERR("%s", argpar_parse_ret.error); + goto error; + } + + for (i = 0; i < argpar_parse_ret.items->n_items; i++) { + struct argpar_item *item = argpar_parse_ret.items->items[i]; + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_HELP: + SHOW_HELP(); + ret = 0; + goto end; + + case OPT_LIST_OPTIONS: + list_cmd_options_argpar(stdout, + remove_trigger_options); + ret = 0; + goto end; + + case OPT_USER_ID: + { + if (!assign_string(&user_id, item_opt->arg, + "--user-id")) { + goto error; + } + break; + } + + default: + abort(); + } + + } else { + struct argpar_item_non_opt *item_non_opt = + (struct argpar_item_non_opt *) item; + + if (id) { + ERR("Unexpected argument: %s", item_non_opt->arg); + goto error; + } + + id = item_non_opt->arg; + } + } + + if (!id) { + ERR("Missing `id` argument."); + goto error; + } + + if (user_id) { + char *end; + + errno = 0; + uid = strtol(user_id, &end, 10); + if (end == user_id || *end != '\0' || errno != 0) { + ERR("Failed to parse `%s` as an integer.", user_id); + } + + } else { + uid = geteuid(); + } + + ret = lttng_list_triggers(&triggers); + if (ret != LTTNG_OK) { + ERR("Failed to get the list of triggers."); + goto error; + } + + trigger_status = lttng_triggers_get_count(triggers, &triggers_count); + assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + for (i = 0; i < triggers_count; i++) { + const struct lttng_trigger *trigger; + const char *trigger_name; + uid_t trigger_uid; + + trigger = lttng_triggers_get_at_index(triggers, i); + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + trigger_status = lttng_trigger_get_user_identity( + trigger, &trigger_uid); + assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + if (trigger_uid == uid && + strcmp(trigger_name, id) == 0) { + trigger_to_remove = trigger; + break; + } + } + + if (!trigger_to_remove) { + ERR("Couldn't find trigger with id `%s`.", id); + goto error; + } + + ret = lttng_unregister_trigger(trigger_to_remove); + if (ret != 0) { + ERR("Failed to unregister trigger `%s`.", id); + goto error; + } + + MSG("Removed trigger `%s`.", id); + + ret = 0; + goto end; + +error: + ret = 1; + +end: + argpar_parse_ret_fini(&argpar_parse_ret); + lttng_triggers_destroy(triggers); + free(user_id); + + return ret; +} diff --git a/src/bin/lttng/lttng.c b/src/bin/lttng/lttng.c index 4907a43ba..437a9cc4c 100644 --- a/src/bin/lttng/lttng.c +++ b/src/bin/lttng/lttng.c @@ -65,6 +65,7 @@ static struct option long_options[] = { /* First level command */ static struct cmd_struct commands[] = { { "add-context", cmd_add_context}, + { "add-trigger", cmd_add_trigger}, { "create", cmd_create}, { "clear", cmd_clear}, { "destroy", cmd_destroy}, @@ -74,9 +75,11 @@ static struct cmd_struct commands[] = { { "enable-event", cmd_enable_events}, { "help", NULL}, { "list", cmd_list}, + { "list-triggers", cmd_list_triggers}, { "load", cmd_load}, { "metadata", cmd_metadata}, { "regenerate", cmd_regenerate}, + { "remove-trigger", cmd_remove_trigger}, { "rotate", cmd_rotate}, { "enable-rotation", cmd_enable_rotation}, { "disable-rotation", cmd_disable_rotation}, diff --git a/src/bin/lttng/uprobe.c b/src/bin/lttng/uprobe.c new file mode 100644 index 000000000..c51c44af0 --- /dev/null +++ b/src/bin/lttng/uprobe.c @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2020 EfficiOS, Inc. + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include "uprobe.h" + +#include +#include +#include + +#include "common/compat/getenv.h" +#include "common/string-utils/string-utils.h" +#include "common/utils.h" +#include "lttng/constant.h" + +#include "command.h" + +/* + * Walk the directories in the PATH environment variable to find the target + * binary passed as parameter. + * + * On success, the full path of the binary is copied in binary_full_path out + * parameter. This buffer is allocated by the caller and must be at least + * LTTNG_PATH_MAX bytes long. + * On failure, returns -1; + */ +static +int walk_command_search_path(const char *binary, char *binary_full_path) +{ + char *tentative_binary_path = NULL; + char *command_search_path = NULL; + char *curr_search_dir_end = NULL; + char *curr_search_dir = NULL; + struct stat stat_output; + int ret = 0; + + command_search_path = lttng_secure_getenv("PATH"); + if (!command_search_path) { + ret = -1; + goto end; + } + + /* + * Duplicate the $PATH string as the char pointer returned by getenv() should + * not be modified. + */ + command_search_path = strdup(command_search_path); + if (!command_search_path) { + ret = -1; + goto end; + } + + /* + * This char array is used to concatenate path to binary to look for + * the binary. + */ + tentative_binary_path = zmalloc(LTTNG_PATH_MAX * sizeof(char)); + if (!tentative_binary_path) { + ret = -1; + goto alloc_error; + } + + curr_search_dir = command_search_path; + do { + /* + * Split on ':'. The return value of this call points to the + * matching character. + */ + curr_search_dir_end = strchr(curr_search_dir, ':'); + if (curr_search_dir_end != NULL) { + /* + * Add a NULL byte to the end of the first token so it + * can be used as a string. + */ + curr_search_dir_end[0] = '\0'; + } + + /* Empty the tentative path */ + memset(tentative_binary_path, 0, LTTNG_PATH_MAX * sizeof(char)); + + /* + * Build the tentative path to the binary using the current + * search directory and the name of the binary. + */ + ret = snprintf(tentative_binary_path, LTTNG_PATH_MAX, "%s/%s", + curr_search_dir, binary); + if (ret < 0) { + goto free_binary_path; + } + if (ret < LTTNG_PATH_MAX) { + /* + * Use STAT(2) to see if the file exists. + */ + ret = stat(tentative_binary_path, &stat_output); + if (ret == 0) { + /* + * Verify that it is a regular file or a + * symlink and not a special file (e.g. + * device). + */ + if (S_ISREG(stat_output.st_mode) + || S_ISLNK(stat_output.st_mode)) { + /* + * Found a match, set the out parameter + * and return success. + */ + ret = lttng_strncpy(binary_full_path, + tentative_binary_path, + LTTNG_PATH_MAX); + if (ret == -1) { + ERR("Source path does not fit " + "in destination buffer."); + } + goto free_binary_path; + } + } + } + /* Go to the next entry in the $PATH variable. */ + curr_search_dir = curr_search_dir_end + 1; + } while (curr_search_dir_end != NULL); + +free_binary_path: + free(tentative_binary_path); +alloc_error: + free(command_search_path); +end: + return ret; +} + +/* + * Check if the symbol field passed by the user is in fact an address or an + * offset from a symbol. Those two instrumentation types are not supported yet. + * It's expected to be a common mistake because of the existing --probe option + * that does support these formats. + * + * Here are examples of these unsupported formats for the --userspace-probe + * option: + * elf:/path/to/binary:0x400430 + * elf:/path/to/binary:4194364 + * elf:/path/to/binary:my_symbol+0x323 + * elf:/path/to/binary:my_symbol+43 + */ +static +int warn_userspace_probe_syntax(const char *symbol) +{ + int ret; + + /* Check if the symbol field is an hex address. */ + ret = sscanf(symbol, "0x%*x"); + if (ret > 0) { + /* If there is a match, print a warning and return an error. */ + ERR("Userspace probe on address not supported yet."); + ret = CMD_UNSUPPORTED; + goto error; + } + + /* Check if the symbol field is an decimal address. */ + ret = sscanf(symbol, "%*u"); + if (ret > 0) { + /* If there is a match, print a warning and return an error. */ + ERR("Userspace probe on address not supported yet."); + ret = CMD_UNSUPPORTED; + goto error; + } + + /* Check if the symbol field is symbol+hex_offset. */ + ret = sscanf(symbol, "%*[^+]+0x%*x"); + if (ret > 0) { + /* If there is a match, print a warning and return an error. */ + ERR("Userspace probe on symbol+offset not supported yet."); + ret = CMD_UNSUPPORTED; + goto error; + } + + /* Check if the symbol field is symbol+decimal_offset. */ + ret = sscanf(symbol, "%*[^+]+%*u"); + if (ret > 0) { + /* If there is a match, print a warning and return an error. */ + ERR("Userspace probe on symbol+offset not supported yet."); + ret = CMD_UNSUPPORTED; + goto error; + } + + ret = 0; + +error: + return ret; +} + +/* + * Parse userspace probe options + * Set the userspace probe fields in the lttng_event struct and set the + * target_path to the path to the binary. + */ +LTTNG_HIDDEN +int parse_userspace_probe_opts(const char *opt, + struct lttng_userspace_probe_location **probe_location) +{ + int ret = CMD_SUCCESS; + int num_token; + char **tokens = NULL; + char *target_path = NULL; + char *unescaped_target_path = NULL; + char *real_target_path = NULL; + char *symbol_name = NULL, *probe_name = NULL, *provider_name = NULL; + struct lttng_userspace_probe_location *probe_location_local = NULL; + struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL; + + assert(opt); + + /* + * userspace probe fields are separated by ':'. + */ + tokens = strutils_split(opt, ':', 1); + num_token = strutils_array_of_strings_len(tokens); + + /* + * Early sanity check that the number of parameter is between 2 and 4 + * inclusively. + * elf:PATH:SYMBOL + * std:PATH:PROVIDER_NAME:PROBE_NAME + * PATH:SYMBOL (same behavior as ELF) + */ + if (num_token < 2 || num_token > 4) { + ret = CMD_ERROR; + goto end; + } + + /* + * Looking up the first parameter will tell the technique to use to + * interpret the userspace probe/function description. + */ + switch (num_token) { + case 2: + /* When the probe type is omitted we assume ELF for now. */ + case 3: + if (num_token == 3 && strcmp(tokens[0], "elf") == 0) { + target_path = tokens[1]; + symbol_name = tokens[2]; + } else if (num_token == 2) { + target_path = tokens[0]; + symbol_name = tokens[1]; + } else { + ret = CMD_ERROR; + goto end; + } + lookup_method = + lttng_userspace_probe_location_lookup_method_function_elf_create(); + if (!lookup_method) { + WARN("Failed to create ELF lookup method"); + ret = CMD_ERROR; + goto end; + } + break; + case 4: + if (strcmp(tokens[0], "sdt") == 0) { + target_path = tokens[1]; + provider_name = tokens[2]; + probe_name = tokens[3]; + } else { + ret = CMD_ERROR; + goto end; + } + lookup_method = + lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create(); + if (!lookup_method) { + WARN("Failed to create SDT lookup method"); + ret = CMD_ERROR; + goto end; + } + break; + default: + ret = CMD_ERROR; + goto end; + } + + /* strutils_unescape_string allocates a new char *. */ + unescaped_target_path = strutils_unescape_string(target_path, 0); + if (!unescaped_target_path) { + ret = CMD_ERROR; + goto end; + } + + /* + * If there is not forward slash in the path. Walk the $PATH else + * expand. + */ + if (strchr(unescaped_target_path, '/') == NULL) { + /* Walk the $PATH variable to find the targeted binary. */ + real_target_path = zmalloc(LTTNG_PATH_MAX * sizeof(char)); + if (!real_target_path) { + PERROR("Error allocating path buffer"); + ret = CMD_ERROR; + goto end; + } + ret = walk_command_search_path(unescaped_target_path, real_target_path); + if (ret) { + ERR("Binary not found."); + ret = CMD_ERROR; + goto end; + } + } else { + /* + * Expand references to `/./` and `/../`. This function does not check + * if the file exists. This call returns an allocated buffer on + * success. + */ + real_target_path = utils_expand_path_keep_symlink(unescaped_target_path); + if (!real_target_path) { + ERR("Error expanding the path to binary."); + ret = CMD_ERROR; + goto end; + } + + /* + * Check if the file exists using access(2), If it does not, + * return an error. + */ + ret = access(real_target_path, F_OK); + if (ret) { + ERR("Cannot find binary at path: %s.", real_target_path); + ret = CMD_ERROR; + goto end; + } + } + + switch (lttng_userspace_probe_location_lookup_method_get_type(lookup_method)) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: + /* + * Check for common mistakes in userspace probe description syntax. + */ + ret = warn_userspace_probe_syntax(symbol_name); + if (ret) { + goto end; + } + + probe_location_local = lttng_userspace_probe_location_function_create( + real_target_path, symbol_name, lookup_method); + if (!probe_location_local) { + WARN("Failed to create function probe location"); + ret = CMD_ERROR; + goto end; + } + + /* Ownership transferred to probe_location. */ + lookup_method = NULL; + break; + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: + probe_location_local = lttng_userspace_probe_location_tracepoint_create( + real_target_path, provider_name, probe_name, lookup_method); + if (!probe_location_local) { + WARN("Failed to create function probe location"); + ret = CMD_ERROR; + goto end; + } + + /* Ownership transferred to probe_location. */ + lookup_method = NULL; + break; + default: + ret = CMD_ERROR; + goto end; + } + + /* + * Everything went fine, transfer ownership of probe location to + * caller. + */ + *probe_location = probe_location_local; + probe_location_local = NULL; + +end: + lttng_userspace_probe_location_destroy(probe_location_local); + lttng_userspace_probe_location_lookup_method_destroy(lookup_method); + strutils_free_null_terminated_array_of_strings(tokens); + /* + * Freeing both char * here makes the error handling simplier. free() + * performs not action if the pointer is NULL. + */ + free(real_target_path); + free(unescaped_target_path); + + return ret; +} diff --git a/src/bin/lttng/uprobe.h b/src/bin/lttng/uprobe.h new file mode 100644 index 000000000..852f36642 --- /dev/null +++ b/src/bin/lttng/uprobe.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 EfficiOS, Inc. + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#ifndef SRC_BIN_LTTNG_UPROBE_H +#define SRC_BIN_LTTNG_UPROBE_H + +#include + +struct lttng_userspace_probe_location; + +LTTNG_HIDDEN +int parse_userspace_probe_opts(const char *opt, + struct lttng_userspace_probe_location **uprobe_loc); + +#endif /* SRC_BIN_LTTNG_UPROBE_H */ diff --git a/src/bin/lttng/utils.c b/src/bin/lttng/utils.c index 28b007b24..d9fd99ad5 100644 --- a/src/bin/lttng/utils.c +++ b/src/bin/lttng/utils.c @@ -128,6 +128,25 @@ void list_cmd_options(FILE *ofp, struct poptOption *options) } } +/* + * Same as list_cmd_options, but for options specified for argpar. + */ +void list_cmd_options_argpar(FILE *ofp, const struct argpar_opt_descr *options) +{ + int i; + const struct argpar_opt_descr *option = NULL; + + for (i = 0; options[i].long_name != NULL; i++) { + option = &options[i]; + + fprintf(ofp, "--%s\n", option->long_name); + + if (isprint(option->short_name)) { + fprintf(ofp, "-%c\n", option->short_name); + } + } +} + /* * fls: returns the position of the most significant bit. * Returns 0 if no bit is set, else returns the position of the most diff --git a/src/bin/lttng/utils.h b/src/bin/lttng/utils.h index 28ebc51a8..a51c8579a 100644 --- a/src/bin/lttng/utils.h +++ b/src/bin/lttng/utils.h @@ -9,6 +9,7 @@ #define _LTTNG_UTILS_H #include +#include "common/argpar/argpar.h" #include @@ -23,6 +24,7 @@ char *get_session_name(void); char *get_session_name_quiet(void); void list_commands(struct cmd_struct *commands, FILE *ofp); void list_cmd_options(FILE *ofp, struct poptOption *options); +void list_cmd_options_argpar(FILE *ofp, const struct argpar_opt_descr *options); /* * Return the minimum order for which x <= (1UL << order). diff --git a/src/common/Makefile.am b/src/common/Makefile.am index f6918b54c..31b92a962 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -2,7 +2,11 @@ AUTOMAKE_OPTIONS = subdir-objects -SUBDIRS = string-utils filter +SUBDIRS = \ + string-utils \ + bytecode \ + filter \ + argpar # Make sure to always distribute all folders # since SUBDIRS is decided at configure time. @@ -21,7 +25,10 @@ DIST_SUBDIRS = \ consumer \ string-utils \ fd-tracker \ - filter + bytecode \ + filter \ + argpar + # Common library noinst_LTLIBRARIES = libcommon.la EXTRA_DIST = mi-lttng-4.0.xsd @@ -34,20 +41,27 @@ libcommon_la_SOURCES = \ actions/snapshot-session.c \ actions/start-session.c \ actions/stop-session.c \ - buffer-usage.c \ buffer-view.h buffer-view.c \ common.h \ - condition.c \ + conditions/buffer-usage.c \ + conditions/condition.c \ + conditions/event-rule.c \ + conditions/session-consumed-size.c \ + conditions/session-rotation.c \ context.c context.h \ credentials.c credentials.h \ daemonize.c daemonize.h \ defaults.c \ + domain.c \ dynamic-array.c dynamic-array.h \ dynamic-buffer.c dynamic-buffer.h \ endpoint.c \ error.c error.h \ evaluation.c \ event.c \ + event-expr.c \ + event-expr-to-bytecode.c event-expr-to-bytecode.h \ + event-field-value.c \ event-rule/event-rule.c \ event-rule/kprobe.c \ event-rule/syscall.c \ @@ -58,6 +72,7 @@ libcommon_la_SOURCES = \ fs-handle.c fs-handle.h fs-handle-internal.h \ futex.c futex.h \ kernel-probe.c \ + index-allocator.c index-allocator.h \ location.c \ mi-lttng.c mi-lttng.h \ notification.c \ @@ -67,9 +82,8 @@ libcommon_la_SOURCES = \ pipe.c pipe.h \ readwrite.c readwrite.h \ runas.c runas.h \ - session-consumed-size.c \ + shm.c shm.h \ session-descriptor.c \ - session-rotation.c \ snapshot.c snapshot.h \ spawn-viewer.c spawn-viewer.h \ time.c \ @@ -91,11 +105,13 @@ libcommon_la_SOURCES += \ endif libcommon_la_LIBADD = \ + $(top_builddir)/src/common/bytecode/libbytecode.la \ $(top_builddir)/src/common/config/libconfig.la \ $(top_builddir)/src/common/compat/libcompat.la \ $(top_builddir)/src/common/hashtable/libhashtable.la \ $(top_builddir)/src/common/fd-tracker/libfd-tracker.la \ - $(top_builddir)/src/common/filter/libfilter.la + $(top_builddir)/src/common/filter/libfilter.la \ + $(top_builddir)/src/vendor/msgpack/libmsgpack.la if BUILD_LIB_COMPAT SUBDIRS += compat diff --git a/src/common/actions/action.c b/src/common/actions/action.c index 11b3c5501..86628542b 100644 --- a/src/common/actions/action.c +++ b/src/common/actions/action.c @@ -38,18 +38,11 @@ const char *lttng_action_type_string(enum lttng_action_type action_type) } } -enum lttng_action_type lttng_action_get_type(struct lttng_action *action) +enum lttng_action_type lttng_action_get_type(const struct lttng_action *action) { return action ? action->type : LTTNG_ACTION_TYPE_UNKNOWN; } -LTTNG_HIDDEN -enum lttng_action_type lttng_action_get_type_const( - const struct lttng_action *action) -{ - return action->type; -} - LTTNG_HIDDEN void lttng_action_init( struct lttng_action *action, diff --git a/src/common/actions/group.c b/src/common/actions/group.c index f57207415..31c6f456e 100644 --- a/src/common/actions/group.c +++ b/src/common/actions/group.c @@ -16,7 +16,7 @@ #include #define IS_GROUP_ACTION(action) \ - (lttng_action_get_type_const(action) == LTTNG_ACTION_TYPE_GROUP) + (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_GROUP) struct lttng_action_group { struct lttng_action parent; diff --git a/src/common/actions/rotate-session.c b/src/common/actions/rotate-session.c index 9255034da..a49287660 100644 --- a/src/common/actions/rotate-session.c +++ b/src/common/actions/rotate-session.c @@ -13,7 +13,7 @@ #include #define IS_ROTATE_SESSION_ACTION(action) \ - (lttng_action_get_type_const(action) == LTTNG_ACTION_TYPE_ROTATE_SESSION) + (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_ROTATE_SESSION) struct lttng_action_rotate_session { struct lttng_action parent; diff --git a/src/common/actions/snapshot-session.c b/src/common/actions/snapshot-session.c index 667166770..757683865 100644 --- a/src/common/actions/snapshot-session.c +++ b/src/common/actions/snapshot-session.c @@ -19,7 +19,7 @@ #include #define IS_SNAPSHOT_SESSION_ACTION(action) \ - (lttng_action_get_type_const(action) == LTTNG_ACTION_TYPE_SNAPSHOT_SESSION) + (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_SNAPSHOT_SESSION) struct lttng_action_snapshot_session { struct lttng_action parent; @@ -141,7 +141,6 @@ static int lttng_action_snapshot_session_serialize( assert(payload); size_before_comm = payload->buffer.size; - size_before_comm = size_before_comm + sizeof(comm); action_snapshot_session = action_snapshot_session_from_action(action); comm.session_name_len = diff --git a/src/common/actions/start-session.c b/src/common/actions/start-session.c index eeb00a5da..f21aaaae9 100644 --- a/src/common/actions/start-session.c +++ b/src/common/actions/start-session.c @@ -13,7 +13,7 @@ #include #define IS_START_SESSION_ACTION(action) \ - (lttng_action_get_type_const(action) == LTTNG_ACTION_TYPE_START_SESSION) + (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_START_SESSION) struct lttng_action_start_session { struct lttng_action parent; diff --git a/src/common/actions/stop-session.c b/src/common/actions/stop-session.c index 33077b676..5bd753f7c 100644 --- a/src/common/actions/stop-session.c +++ b/src/common/actions/stop-session.c @@ -13,7 +13,7 @@ #include #define IS_STOP_SESSION_ACTION(action) \ - (lttng_action_get_type_const(action) == LTTNG_ACTION_TYPE_STOP_SESSION) + (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_STOP_SESSION) struct lttng_action_stop_session { struct lttng_action parent; diff --git a/src/common/argpar/Makefile.am b/src/common/argpar/Makefile.am new file mode 100644 index 000000000..175526abe --- /dev/null +++ b/src/common/argpar/Makefile.am @@ -0,0 +1,3 @@ +noinst_LTLIBRARIES = libargpar.la + +libargpar_la_SOURCES = argpar.c argpar.h diff --git a/src/common/argpar/argpar.c b/src/common/argpar/argpar.c new file mode 100644 index 000000000..a8977ea3b --- /dev/null +++ b/src/common/argpar/argpar.c @@ -0,0 +1,746 @@ +/* + * Copyright 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include "argpar.h" + +#define argpar_realloc(_ptr, _type, _nmemb) ((_type *) realloc(_ptr, (_nmemb) * sizeof(_type))) +#define argpar_calloc(_type, _nmemb) ((_type *) calloc((_nmemb), sizeof(_type))) +#define argpar_zalloc(_type) argpar_calloc(_type, 1) + +#define ARGPAR_ASSERT(_cond) assert(_cond) + +/* + * Structure holding the argpar state between successive argpar_state_parse_next + * calls. + * + * Created with `argpar_state_create` and destroyed with `argpar_state_destroy`. + */ +struct argpar_state { + /* + * Data provided by the user in argpar_state_create, does not change + * afterwards. + */ + unsigned int argc; + const char * const *argv; + const struct argpar_opt_descr *descrs; + + /* + * Index of the argument to process in the next argpar_state_parse_next + * call. + */ + unsigned int i; + + /* Counter of non-option arguments. */ + int non_opt_index; + + /* + * Short option state: if set, we are in the middle of a short option + * group, so we should resume there at the next argpar_state_parse_next + * call. + */ + const char *short_opt_ch; +}; + +static +char *argpar_vasprintf(const char *fmt, va_list args) +{ + int len1, len2; + char *str; + va_list args2; + + va_copy(args2, args); + + len1 = vsnprintf(NULL, 0, fmt, args); + if (len1 < 0) { + str = NULL; + goto end; + } + + str = malloc(len1 + 1); + if (!str) { + goto end; + } + + len2 = vsnprintf(str, len1 + 1, fmt, args2); + + ARGPAR_ASSERT(len1 == len2); + +end: + va_end(args2); + return str; +} + + +static +char *argpar_asprintf(const char *fmt, ...) +{ + va_list args; + char *str; + + va_start(args, fmt); + str = argpar_vasprintf(fmt, args); + va_end(args); + + return str; +} + +static +bool argpar_string_append_printf(char **str, const char *fmt, ...) +{ + char *new_str = NULL; + char *addendum; + bool success; + va_list args; + + ARGPAR_ASSERT(str); + + va_start(args, fmt); + addendum = argpar_vasprintf(fmt, args); + va_end(args); + + if (!addendum) { + success = false; + goto end; + } + + new_str = argpar_asprintf("%s%s", *str ? *str : "", addendum); + if (!new_str) { + success = false; + goto end; + } + + free(*str); + *str = new_str; + + success = true; + +end: + free(addendum); + + return success; +} + +ARGPAR_HIDDEN +void argpar_item_destroy(struct argpar_item *item) +{ + if (!item) { + goto end; + } + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt * const opt_item = (void *) item; + + free((void *) opt_item->arg); + } + + free(item); + +end: + return; +} + +static +bool push_item(struct argpar_item_array * const array, + struct argpar_item * const item) +{ + bool success; + + ARGPAR_ASSERT(array); + ARGPAR_ASSERT(item); + + if (array->n_items == array->n_alloc) { + unsigned int new_n_alloc = array->n_alloc * 2; + struct argpar_item **new_items; + + new_items = argpar_realloc(array->items, + struct argpar_item *, new_n_alloc); + if (!new_items) { + success = false; + goto end; + } + + array->n_alloc = new_n_alloc; + array->items = new_items; + } + + array->items[array->n_items] = item; + array->n_items++; + + success = true; + +end: + return success; +} + +static +void destroy_item_array(struct argpar_item_array * const array) +{ + if (array) { + unsigned int i; + + for (i = 0; i < array->n_items; i++) { + argpar_item_destroy(array->items[i]); + } + + free(array->items); + free(array); + } +} + +static +struct argpar_item_array *new_item_array(void) +{ + struct argpar_item_array *ret; + const int initial_size = 10; + + ret = argpar_zalloc(struct argpar_item_array); + if (!ret) { + goto end; + } + + ret->items = argpar_calloc(struct argpar_item *, initial_size); + if (!ret->items) { + goto error; + } + + ret->n_alloc = initial_size; + + goto end; + +error: + destroy_item_array(ret); + ret = NULL; + +end: + return ret; +} + +static +struct argpar_item_opt *create_opt_item( + const struct argpar_opt_descr * const descr, + const char * const arg) +{ + struct argpar_item_opt *opt_item = + argpar_zalloc(struct argpar_item_opt); + + if (!opt_item) { + goto end; + } + + opt_item->base.type = ARGPAR_ITEM_TYPE_OPT; + opt_item->descr = descr; + + if (arg) { + opt_item->arg = strdup(arg); + if (!opt_item->arg) { + goto error; + } + } + + goto end; + +error: + argpar_item_destroy(&opt_item->base); + opt_item = NULL; + +end: + return opt_item; +} + +static +struct argpar_item_non_opt *create_non_opt_item(const char * const arg, + const unsigned int orig_index, + const unsigned int non_opt_index) +{ + struct argpar_item_non_opt * const non_opt_item = + argpar_zalloc(struct argpar_item_non_opt); + + if (!non_opt_item) { + goto end; + } + + non_opt_item->base.type = ARGPAR_ITEM_TYPE_NON_OPT; + non_opt_item->arg = arg; + non_opt_item->orig_index = orig_index; + non_opt_item->non_opt_index = non_opt_index; + +end: + return non_opt_item; +} + +static +const struct argpar_opt_descr *find_descr( + const struct argpar_opt_descr * const descrs, + const char short_name, const char * const long_name) +{ + const struct argpar_opt_descr *descr; + + for (descr = descrs; descr->short_name || descr->long_name; descr++) { + if (short_name && descr->short_name && + short_name == descr->short_name) { + goto end; + } + + if (long_name && descr->long_name && + strcmp(long_name, descr->long_name) == 0) { + goto end; + } + } + +end: + return !descr->short_name && !descr->long_name ? NULL : descr; +} + +enum parse_orig_arg_opt_ret { + PARSE_ORIG_ARG_OPT_RET_OK, + PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT = -2, + PARSE_ORIG_ARG_OPT_RET_ERROR = -1, +}; + +static +enum parse_orig_arg_opt_ret parse_short_opts(const char * const short_opts, + const char * const next_orig_arg, + const struct argpar_opt_descr * const descrs, + struct argpar_state *state, + char **error, + struct argpar_item **item) +{ + enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK; + bool used_next_orig_arg = false; + + if (strlen(short_opts) == 0) { + argpar_string_append_printf(error, "Invalid argument"); + goto error; + } + + if (!state->short_opt_ch) { + state->short_opt_ch = short_opts; + } + + const char *opt_arg = NULL; + const struct argpar_opt_descr *descr; + struct argpar_item_opt *opt_item; + + /* Find corresponding option descriptor */ + descr = find_descr(descrs, *state->short_opt_ch, NULL); + if (!descr) { + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT; + argpar_string_append_printf(error, + "Unknown option `-%c`", *state->short_opt_ch); + goto error; + } + + if (descr->with_arg) { + if (state->short_opt_ch[1]) { + /* `-oarg` form */ + opt_arg = &state->short_opt_ch[1]; + } else { + /* `-o arg` form */ + opt_arg = next_orig_arg; + used_next_orig_arg = true; + } + + /* + * We accept `-o ''` (empty option's argument), + * but not `-o` alone if an option's argument is + * expected. + */ + if (!opt_arg || (state->short_opt_ch[1] && strlen(opt_arg) == 0)) { + argpar_string_append_printf(error, + "Missing required argument for option `-%c`", + *state->short_opt_ch); + used_next_orig_arg = false; + goto error; + } + } + + /* Create and append option argument */ + opt_item = create_opt_item(descr, opt_arg); + if (!opt_item) { + goto error; + } + + *item = &opt_item->base; + + state->short_opt_ch++; + + if (descr->with_arg || !*state->short_opt_ch) { + /* Option has an argument: no more options */ + state->short_opt_ch = NULL; + + if (used_next_orig_arg) { + state->i += 2; + } else { + state->i += 1; + } + } + + goto end; + +error: + if (ret == PARSE_ORIG_ARG_OPT_RET_OK) { + ret = PARSE_ORIG_ARG_OPT_RET_ERROR; + } + +end: + return ret; +} + +static +enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg, + const char * const next_orig_arg, + const struct argpar_opt_descr * const descrs, + struct argpar_state *state, + char **error, + struct argpar_item **item) +{ + const size_t max_len = 127; + enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK; + const struct argpar_opt_descr *descr; + struct argpar_item_opt *opt_item; + bool used_next_orig_arg = false; + + /* Option's argument, if any */ + const char *opt_arg = NULL; + + /* Position of first `=`, if any */ + const char *eq_pos; + + /* Buffer holding option name when `long_opt_arg` contains `=` */ + char buf[max_len + 1]; + + /* Option name */ + const char *long_opt_name = long_opt_arg; + + if (strlen(long_opt_arg) == 0) { + argpar_string_append_printf(error, + "Invalid argument"); + goto error; + } + + /* Find the first `=` in original argument */ + eq_pos = strchr(long_opt_arg, '='); + if (eq_pos) { + const size_t long_opt_name_size = eq_pos - long_opt_arg; + + /* Isolate the option name */ + if (long_opt_name_size > max_len) { + argpar_string_append_printf(error, + "Invalid argument `--%s`", long_opt_arg); + goto error; + } + + memcpy(buf, long_opt_arg, long_opt_name_size); + buf[long_opt_name_size] = '\0'; + long_opt_name = buf; + } + + /* Find corresponding option descriptor */ + descr = find_descr(descrs, '\0', long_opt_name); + if (!descr) { + argpar_string_append_printf(error, + "Unknown option `--%s`", long_opt_name); + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT; + goto error; + } + + /* Find option's argument if any */ + if (descr->with_arg) { + if (eq_pos) { + /* `--long-opt=arg` style */ + opt_arg = eq_pos + 1; + } else { + /* `--long-opt arg` style */ + if (!next_orig_arg) { + argpar_string_append_printf(error, + "Missing required argument for option `--%s`", + long_opt_name); + goto error; + } + + opt_arg = next_orig_arg; + used_next_orig_arg = true; + } + } + + /* Create and append option argument */ + opt_item = create_opt_item(descr, opt_arg); + if (!opt_item) { + goto error; + } + + if (used_next_orig_arg) { + state->i += 2; + } else { + state->i += 1; + } + + *item = &opt_item->base; + goto end; + +error: + if (ret == PARSE_ORIG_ARG_OPT_RET_OK) { + ret = PARSE_ORIG_ARG_OPT_RET_ERROR; + } + +end: + return ret; +} + +static +enum parse_orig_arg_opt_ret parse_orig_arg_opt(const char * const orig_arg, + const char * const next_orig_arg, + const struct argpar_opt_descr * const descrs, + struct argpar_state *state, + char **error, + struct argpar_item **item) +{ + enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK; + + ARGPAR_ASSERT(orig_arg[0] == '-'); + + if (orig_arg[1] == '-') { + /* Long option */ + ret = parse_long_opt(&orig_arg[2], + next_orig_arg, descrs, state, error, item); + } else { + /* Short option */ + ret = parse_short_opts(&orig_arg[1], + next_orig_arg, descrs, state, error, item); + } + + return ret; +} + +static +bool prepend_while_parsing_arg_to_error(char **error, + const unsigned int i, const char * const arg) +{ + char *new_error; + bool success; + + ARGPAR_ASSERT(error); + ARGPAR_ASSERT(*error); + + new_error = argpar_asprintf("While parsing argument #%u (`%s`): %s", + i + 1, arg, *error); + if (!new_error) { + success = false; + goto end; + } + + free(*error); + *error = new_error; + success = true; + +end: + return success; +} + +ARGPAR_HIDDEN +struct argpar_state *argpar_state_create( + unsigned int argc, + const char * const *argv, + const struct argpar_opt_descr * const descrs) +{ + struct argpar_state *state; + + state = argpar_zalloc(struct argpar_state); + if (!state) { + goto end; + } + + state->argc = argc; + state->argv = argv; + state->descrs = descrs; + +end: + return state; +} + +ARGPAR_HIDDEN +void argpar_state_destroy(struct argpar_state *state) +{ + free(state); +} + +ARGPAR_HIDDEN +enum argpar_state_parse_next_status argpar_state_parse_next( + struct argpar_state *state, + struct argpar_item **item, + char **error) +{ + enum argpar_state_parse_next_status status; + + ARGPAR_ASSERT(state->i <= state->argc); + + *error = NULL; + + if (state->i == state->argc) { + status = ARGPAR_STATE_PARSE_NEXT_STATUS_END; + goto end; + } + + enum parse_orig_arg_opt_ret parse_orig_arg_opt_ret; + const char * const orig_arg = state->argv[state->i]; + const char * const next_orig_arg = + state->i < (state->argc - 1) ? state->argv[state->i + 1] : NULL; + + if (orig_arg[0] != '-') { + /* Non-option argument */ + struct argpar_item_non_opt *non_opt_item = + create_non_opt_item(orig_arg, state->i, state->non_opt_index); + + if (!non_opt_item) { + status = ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR; + goto end; + } + + state->non_opt_index++; + state->i++; + + *item = &non_opt_item->base; + status = ARGPAR_STATE_PARSE_NEXT_STATUS_OK; + goto end; + } + + /* Option argument */ + parse_orig_arg_opt_ret = parse_orig_arg_opt(orig_arg, + next_orig_arg, state->descrs, state, error, item); + switch (parse_orig_arg_opt_ret) { + case PARSE_ORIG_ARG_OPT_RET_OK: + status = ARGPAR_STATE_PARSE_NEXT_STATUS_OK; + break; + case PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT: + status = ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT; + break;; + case PARSE_ORIG_ARG_OPT_RET_ERROR: + prepend_while_parsing_arg_to_error( + error, state->i, orig_arg); + status = ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR; + break; + default: + abort(); + } + +end: + return status; +} + +ARGPAR_HIDDEN +int argpar_state_get_ingested_orig_args(struct argpar_state *state) +{ + return state->i; +} + +ARGPAR_HIDDEN +struct argpar_parse_ret argpar_parse(unsigned int argc, + const char * const *argv, + const struct argpar_opt_descr * const descrs, + bool fail_on_unknown_opt) +{ + struct argpar_parse_ret parse_ret = { 0 }; + struct argpar_item *item = NULL; + struct argpar_state *state = NULL; + + parse_ret.items = new_item_array(); + if (!parse_ret.items) { + parse_ret.error = strdup("Failed to create items array."); + ARGPAR_ASSERT(parse_ret.error); + goto error; + } + + state = argpar_state_create(argc, argv, descrs); + if (!state) { + parse_ret.error = strdup("Failed to create argpar state."); + ARGPAR_ASSERT(parse_ret.error); + goto error; + } + + while (true) { + enum argpar_state_parse_next_status status; + + status = argpar_state_parse_next(state, &item, &parse_ret.error); + if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { + break; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + if (fail_on_unknown_opt) { + parse_ret.ingested_orig_args = + argpar_state_get_ingested_orig_args(state); + prepend_while_parsing_arg_to_error( + &parse_ret.error, parse_ret.ingested_orig_args, + argv[parse_ret.ingested_orig_args]); + status = ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR; + goto error; + } + + free(parse_ret.error); + parse_ret.error = NULL; + break; + } + + ARGPAR_ASSERT(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); + + if (!push_item(parse_ret.items, item)) { + goto error; + } + item = NULL; + } + + ARGPAR_ASSERT(!parse_ret.error); + parse_ret.ingested_orig_args = + argpar_state_get_ingested_orig_args(state); + goto end; + +error: + ARGPAR_ASSERT(parse_ret.error); + + /* That's how we indicate that an error occured */ + destroy_item_array(parse_ret.items); + parse_ret.items = NULL; + +end: + argpar_state_destroy(state); + argpar_item_destroy(item); + return parse_ret; +} + +ARGPAR_HIDDEN +void argpar_parse_ret_fini(struct argpar_parse_ret *ret) +{ + ARGPAR_ASSERT(ret); + + destroy_item_array(ret->items); + ret->items = NULL; + + free(ret->error); + ret->error = NULL; +} diff --git a/src/common/argpar/argpar.h b/src/common/argpar/argpar.h new file mode 100644 index 000000000..1442f5183 --- /dev/null +++ b/src/common/argpar/argpar.h @@ -0,0 +1,330 @@ +#ifndef BABELTRACE_ARGPAR_H +#define BABELTRACE_ARGPAR_H + +/* + * Copyright 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * argpar is a library that provides facilities for argument parsing. + * + * Two APIs are available: + * + * - The iterator-style API, where you initialize a state object with + * `argpar_state_create`, then repeatedly call `argpar_state_parse_next` to + * get the arguments, until (1) there are no more arguments, (2) the parser + * encounters an error (e.g. unknown option) or (3) you get bored. This + * API gives you more control on when to stop parsing the arguments. + * + * - The parse-everything-in-one-shot-API, where you call `argpar_parse`, + * which parses the arguments until (1) there are not more arguments or + * (2) it encounters a parser error. It returns you a list of all the + * arguments it was able to parse, which you can consult at your leisure. + * + * The following describes how arguments are parsed, and applies to both APIs. + * + * argpar parses the arguments `argv` of which the count is `argc` using the + * sentinel-terminated (use `ARGPAR_OPT_DESCR_SENTINEL`) option + * descriptor array `descrs`. + * + * argpar considers ALL the elements of `argv`, including the* first one, so + * that you would typically pass `argc - 1` and `&argv[1]` from what main() + * receives. + * + * This argument parser supports: + * + * * Short options without an argument, possibly tied together: + * + * -f -auf -n + * + * * Short options with argument: + * + * -b 45 -f/mein/file -xyzhello + * + * * Long options without an argument: + * + * --five-guys --burger-king --pizza-hut --subway + * + * * Long options with arguments: + * + * --security enable --time=18.56 + * + * * Non-option arguments (anything else). + * + * This parser does not accept `-` or `--` as arguments. The latter + * means "end of options" for many command-line tools, but this function + * is all about keeping the order of the arguments, so it does not mean + * much to put them at the end. This has the side effect that a + * non-option argument cannot have the form of an option, for example if + * you need to pass the exact relative path `--component`. In that case, + * you would need to pass `./--component`. There's no generic way to + * escape `-` for the moment. + * + * This parser accepts duplicate options (it will output one item for each + * instance). + * + * The returned items are of the type `struct argpar_item *`. Each item + * is to be casted to the appropriate type (`struct argpar_item_opt *` or + * `struct argpar_item_non_opt *`) depending on its type. + * + * The items are returned in the same order that the arguments were parsed, + * including non-option arguments. This means, for example, that for + * + * --hello --meow=23 /path/to/file -b + * + * found items are returned in this order: option item (--hello), option item + * (--meow=23), non-option item (/path/to/file) and option item (-b). + */ + +#include + +/* Sentinel for an option descriptor array */ +#define ARGPAR_OPT_DESCR_SENTINEL { -1, '\0', NULL, false } + +/* + * ARGPAR_HIDDEN: if argpar is used in some shared library, we don't want them + * to be exported by that library, so mark them as "hidden". + * + * On Windows, symbols are local unless explicitly exported, + * see https://gcc.gnu.org/wiki/Visibility + */ +#if defined(_WIN32) || defined(__CYGWIN__) +#define ARGPAR_HIDDEN +#else +#define ARGPAR_HIDDEN __attribute__((visibility("hidden"))) +#endif + +/* Forward-declaration for the opaque type. */ +struct argpar_state; + +/* Option descriptor */ +struct argpar_opt_descr { + /* Numeric ID for this option */ + const int id; + + /* Short option character, or `\0` */ + const char short_name; + + /* Long option name (without `--`), or `NULL` */ + const char * const long_name; + + /* True if this option has an argument */ + const bool with_arg; +}; + +/* Item type */ +enum argpar_item_type { + /* Option */ + ARGPAR_ITEM_TYPE_OPT, + + /* Non-option */ + ARGPAR_ITEM_TYPE_NON_OPT, +}; + +/* Base item */ +struct argpar_item { + enum argpar_item_type type; +}; + +/* Option item */ +struct argpar_item_opt { + struct argpar_item base; + + /* Corresponding descriptor */ + const struct argpar_opt_descr *descr; + + /* Argument, or `NULL` if none */ + const char *arg; +}; + +/* Non-option item */ +struct argpar_item_non_opt { + struct argpar_item base; + + /* + * Complete argument, pointing to one of the entries of the + * original arguments (`argv`). + */ + const char *arg; + + /* Index of this argument amongst all original arguments (`argv`) */ + unsigned int orig_index; + + /* Index of this argument amongst other non-option arguments */ + unsigned int non_opt_index; +}; + +struct argpar_item_array { + /* Array of `struct argpar_item *`, or `NULL` on error */ + struct argpar_item **items; + + /* Number of used slots in `items`. */ + unsigned int n_items; + + /* Number of allocated slots in `items`. */ + unsigned int n_alloc; +}; + +/* What is returned by argpar_parse() */ +struct argpar_parse_ret { + /* Array of `struct argpar_item *`, or `NULL` on error */ + struct argpar_item_array *items; + + /* Error string, or `NULL` if none */ + char *error; + + /* Number of original arguments (`argv`) ingested */ + unsigned int ingested_orig_args; +}; + +/* + * Parses arguments in `argv` until the end is reached or an error is + * encountered. + * + * On success, this function returns an array of items + * (field `items` of `struct argpar_parse_ret`) corresponding to each parsed + * argument. + * + * In the returned structure, `ingested_orig_args` is the number of + * ingested arguments within `argv` to produce the resulting array of + * items. + * + * If `fail_on_unknown_opt` is true, then on success `ingested_orig_args` is + * equal to `argc`. Otherwise, `ingested_orig_args` contains the number of + * original arguments until an unknown _option_ occurs. For example, with + * + * --great --white contact nuance --shark nuclear + * + * if `--shark` is not described within `descrs` and + * `fail_on_unknown_opt` is false, then `ingested_orig_args` is 4 (two + * options, two non-options), whereas `argc` is 6. + * + * This makes it possible to know where a command name is, for example. + * With those arguments: + * + * --verbose --stuff=23 do-something --specific-opt -f -b + * + * and the descriptors for `--verbose` and `--stuff` only, the function + * returns the `--verbose` and `--stuff` option items, the + * `do-something` non-option item, and that three original arguments + * were ingested. This means you can start the next argument parsing + * stage, with option descriptors depending on the command name, at + * `&argv[3]`. + * + * Note that `ingested_orig_args` is not always equal to the number of + * returned items, as + * + * --hello -fdw + * + * for example contains two ingested original arguments, but four + * resulting items. + * + * On failure, the returned structure's `items` member is `NULL`, and + * the `error` string member contains details about the error. + * + * You can finalize the returned structure with + * argpar_parse_ret_fini(). + */ +ARGPAR_HIDDEN +struct argpar_parse_ret argpar_parse(unsigned int argc, + const char * const *argv, + const struct argpar_opt_descr *descrs, + bool fail_on_unknown_opt); + +/* + * Finalizes what is returned by argpar_parse(). + * + * It is safe to call argpar_parse() multiple times with the same + * structure. + */ +ARGPAR_HIDDEN +void argpar_parse_ret_fini(struct argpar_parse_ret *ret); + +/* + * Creates an instance of `struct argpar_state`. + * + * This sets up the argpar_state structure, but does not actually + * start parsing the arguments. + * + * When you are done with it, the state must be freed with + * `argpar_state_destroy`. + */ +ARGPAR_HIDDEN +struct argpar_state *argpar_state_create( + unsigned int argc, + const char * const *argv, + const struct argpar_opt_descr * const descrs); + +/* + * Destroys an instance of `struct argpar_state`. + */ +ARGPAR_HIDDEN +void argpar_state_destroy(struct argpar_state *state); + + +enum argpar_state_parse_next_status { + ARGPAR_STATE_PARSE_NEXT_STATUS_OK, + ARGPAR_STATE_PARSE_NEXT_STATUS_END, + ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT, + ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR, +}; + +/* + * Parses and returns the next argument from `state`. + * + * On success, an item describing the argument is returned in `*item` and + * ARGPAR_STATE_PARSE_NEXT_STATUS_OK is returned. The item must be freed with + * `argpar_item_destroy`. + * + * If there are no more arguments to parse, ARGPAR_STATE_PARSE_NEXT_STATUS_END + * is returned. + * + * On failure (status codes ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT and + * ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR), an error string is returned in `*error`. + * This string must be freed with `free`. + */ +enum argpar_state_parse_next_status argpar_state_parse_next( + struct argpar_state *state, + struct argpar_item **item, + char **error); + +/* + * Return the number of ingested elements from argv that were required to + * produce the previously returned items. + */ +ARGPAR_HIDDEN +int argpar_state_get_ingested_orig_args(struct argpar_state *state); + +/* + * Destroy an instance of `struct argpar_item`, as returned by + * argpar_state_parse_next. + */ +ARGPAR_HIDDEN +void argpar_item_destroy(struct argpar_item *item); + +#define ARGPAR_ITEM_DESTROY_AND_RESET(_item) \ + { \ + argpar_item_destroy(_item); \ + _item = NULL; \ + } + + +#endif /* BABELTRACE_ARGPAR_H */ diff --git a/src/common/bytecode/Makefile.am b/src/common/bytecode/Makefile.am new file mode 100644 index 000000000..9e1c6ddd1 --- /dev/null +++ b/src/common/bytecode/Makefile.am @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only + +noinst_LTLIBRARIES = libbytecode.la + +libbytecode_la_SOURCES = \ + bytecode.c bytecode.h diff --git a/src/common/bytecode/bytecode.c b/src/common/bytecode/bytecode.c new file mode 100644 index 000000000..d78cf5ade --- /dev/null +++ b/src/common/bytecode/bytecode.c @@ -0,0 +1,264 @@ +/* + * Copyright 2020 EfficiOS, Inc. + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include "bytecode.h" + +#include + +#include "common/align.h" + +#define INIT_ALLOC_SIZE 4 + +static inline int get_count_order(unsigned int count) +{ + int order; + + order = lttng_fls(count) - 1; + if (count & (count - 1)) + order++; + return order; +} + +LTTNG_HIDDEN +int bytecode_init(struct lttng_bytecode_alloc **fb) +{ + uint32_t alloc_len; + + alloc_len = sizeof(struct lttng_bytecode_alloc) + INIT_ALLOC_SIZE; + *fb = calloc(alloc_len, 1); + if (!*fb) { + return -ENOMEM; + } else { + (*fb)->alloc_len = alloc_len; + return 0; + } +} + +LTTNG_HIDDEN +int32_t bytecode_reserve(struct lttng_bytecode_alloc **fb, uint32_t align, uint32_t len) +{ + int32_t ret; + uint32_t padding = offset_align((*fb)->b.len, align); + uint32_t new_len = (*fb)->b.len + padding + len; + uint32_t new_alloc_len = sizeof(struct lttng_bytecode_alloc) + new_len; + uint32_t old_alloc_len = (*fb)->alloc_len; + + if (new_len > LTTNG_FILTER_MAX_LEN) + return -EINVAL; + + if (new_alloc_len > old_alloc_len) { + struct lttng_bytecode_alloc *newptr; + + new_alloc_len = + max_t(uint32_t, 1U << get_count_order(new_alloc_len), old_alloc_len << 1); + newptr = realloc(*fb, new_alloc_len); + if (!newptr) + return -ENOMEM; + *fb = newptr; + /* We zero directly the memory from start of allocation. */ + memset(&((char *) *fb)[old_alloc_len], 0, new_alloc_len - old_alloc_len); + (*fb)->alloc_len = new_alloc_len; + } + (*fb)->b.len += padding; + ret = (*fb)->b.len; + (*fb)->b.len += len; + return ret; +} + +LTTNG_HIDDEN +int bytecode_push(struct lttng_bytecode_alloc **fb, const void *data, + uint32_t align, uint32_t len) +{ + int32_t offset; + + offset = bytecode_reserve(fb, align, len); + if (offset < 0) + return offset; + memcpy(&(*fb)->b.data[offset], data, len); + return 0; +} + +LTTNG_HIDDEN +int bytecode_push_logical(struct lttng_bytecode_alloc **fb, + struct logical_op *data, + uint32_t align, uint32_t len, + uint16_t *skip_offset) +{ + int32_t offset; + + offset = bytecode_reserve(fb, align, len); + if (offset < 0) + return offset; + memcpy(&(*fb)->b.data[offset], data, len); + *skip_offset = + (void *) &((struct logical_op *) &(*fb)->b.data[offset])->skip_offset + - (void *) &(*fb)->b.data[0]; + return 0; +} + +LTTNG_HIDDEN +int bytecode_push_get_payload_root(struct lttng_bytecode_alloc **bytecode) +{ + struct load_op *insn; + uint32_t insn_len = sizeof(struct load_op); + int ret; + + insn = calloc(insn_len, 1); + if (!insn) + return -ENOMEM; + + insn->op = BYTECODE_OP_GET_PAYLOAD_ROOT; + ret = bytecode_push(bytecode, insn, 1, insn_len); + free(insn); + + return ret; +} + +LTTNG_HIDDEN +int bytecode_push_get_context_root(struct lttng_bytecode_alloc **bytecode) +{ + struct load_op *insn; + uint32_t insn_len = sizeof(struct load_op); + int ret; + + insn = calloc(insn_len, 1); + if (!insn) + return -ENOMEM; + + insn->op = BYTECODE_OP_GET_CONTEXT_ROOT; + ret = bytecode_push(bytecode, insn, 1, insn_len); + free(insn); + + return ret; +} + +LTTNG_HIDDEN +int bytecode_push_get_app_context_root(struct lttng_bytecode_alloc **bytecode) +{ + struct load_op *insn; + uint32_t insn_len = sizeof(struct load_op); + int ret; + + insn = calloc(insn_len, 1); + if (!insn) + return -ENOMEM; + + insn->op = BYTECODE_OP_GET_APP_CONTEXT_ROOT; + ret = bytecode_push(bytecode, insn, 1, insn_len); + free(insn); + + return ret; +} + +LTTNG_HIDDEN +int bytecode_push_get_index_u64(struct lttng_bytecode_alloc **bytecode, + uint64_t index) +{ + struct load_op *insn; + uint32_t insn_len = sizeof(struct load_op) + + sizeof(struct get_index_u64); + struct get_index_u64 index_op_data; + int ret; + + insn = calloc(insn_len, 1); + if (!insn) + return -ENOMEM; + + insn->op = BYTECODE_OP_GET_INDEX_U64; + index_op_data.index = index; + memcpy(insn->data, &index_op_data, sizeof(index)); + ret = bytecode_push(bytecode, insn, 1, insn_len); + + free(insn); + + return ret; +} + +LTTNG_HIDDEN +int bytecode_push_get_symbol(struct lttng_bytecode_alloc **bytecode, + struct lttng_bytecode_alloc **bytecode_reloc, + const char *symbol) +{ + struct load_op *insn; + uint32_t insn_len = sizeof(struct load_op) + + sizeof(struct get_symbol); + struct get_symbol symbol_offset; + uint32_t reloc_offset_u32; + uint16_t reloc_offset; + uint32_t bytecode_reloc_offset_u32; + int ret; + + insn = calloc(insn_len, 1); + if (!insn) { + ret = -ENOMEM; + goto end; + } + + insn->op = BYTECODE_OP_GET_SYMBOL; + + /* + * Get offset in the reloc portion at which the symbol name + * will end up at (GET_SYMBOL's operand points there). + */ + bytecode_reloc_offset_u32 = + bytecode_get_len(&(*bytecode_reloc)->b) + + sizeof(reloc_offset); + symbol_offset.offset = (uint16_t) bytecode_reloc_offset_u32; + memcpy(insn->data, &symbol_offset, sizeof(symbol_offset)); + + /* + * Get offset in the bytecode where the opcode will end up at, + * the reloc offset points to it. + */ + reloc_offset_u32 = bytecode_get_len(&(*bytecode)->b); + if (reloc_offset_u32 > LTTNG_FILTER_MAX_LEN - 1) { + ret = -EINVAL; + goto end; + } + reloc_offset = (uint16_t) reloc_offset_u32; + + /* Append op in bytecode. */ + ret = bytecode_push(bytecode, insn, 1, insn_len); + if (ret) { + goto end; + } + + /* Append reloc offset. */ + ret = bytecode_push(bytecode_reloc, &reloc_offset, + 1, sizeof(reloc_offset)); + if (ret) { + goto end; + } + + /* Append symbol name. */ + ret = bytecode_push(bytecode_reloc, symbol, 1, strlen(symbol) + 1); + +end: + free(insn); + return ret; +} + +/* + * Allocate an lttng_bytecode object and copy the given original bytecode. + * + * Return allocated bytecode or NULL on error. + */ +struct lttng_bytecode *bytecode_copy( + const struct lttng_bytecode *orig_f) +{ + struct lttng_bytecode *bytecode = NULL; + + bytecode = zmalloc(sizeof(*bytecode) + orig_f->len); + if (!bytecode) { + goto error; + } + + memcpy(bytecode, orig_f, sizeof(*bytecode) + orig_f->len); + +error: + return bytecode; +} diff --git a/src/common/bytecode/bytecode.h b/src/common/bytecode/bytecode.h new file mode 100644 index 000000000..b519fe19a --- /dev/null +++ b/src/common/bytecode/bytecode.h @@ -0,0 +1,266 @@ +/* + * Copyright 2020 EfficiOS, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_COMMON_BYTECODE_H +#define LTTNG_COMMON_BYTECODE_H + +#include + +#include "common/macros.h" +#include "common/sessiond-comm/sessiond-comm.h" + +/* + * offsets are absolute from start of bytecode. + */ + +struct field_ref { + /* Initially, symbol offset. After link, field offset. */ + uint16_t offset; +} LTTNG_PACKED; + +struct get_symbol { + /* Symbol offset. */ + uint16_t offset; +} LTTNG_PACKED; + +struct get_index_u16 { + uint16_t index; +} LTTNG_PACKED; + +struct get_index_u64 { + uint64_t index; +} LTTNG_PACKED; + +struct literal_numeric { + int64_t v; +} LTTNG_PACKED; + +struct literal_double { + double v; +} LTTNG_PACKED; + +struct literal_string { + char string[0]; +} LTTNG_PACKED; + +enum bytecode_op { + BYTECODE_OP_UNKNOWN = 0, + + BYTECODE_OP_RETURN = 1, + + /* binary */ + BYTECODE_OP_MUL = 2, + BYTECODE_OP_DIV = 3, + BYTECODE_OP_MOD = 4, + BYTECODE_OP_PLUS = 5, + BYTECODE_OP_MINUS = 6, + BYTECODE_OP_BIT_RSHIFT = 7, + BYTECODE_OP_BIT_LSHIFT = 8, + BYTECODE_OP_BIT_AND = 9, + BYTECODE_OP_BIT_OR = 10, + BYTECODE_OP_BIT_XOR = 11, + + /* binary comparators */ + BYTECODE_OP_EQ = 12, + BYTECODE_OP_NE = 13, + BYTECODE_OP_GT = 14, + BYTECODE_OP_LT = 15, + BYTECODE_OP_GE = 16, + BYTECODE_OP_LE = 17, + + /* string binary comparator: apply to */ + BYTECODE_OP_EQ_STRING = 18, + BYTECODE_OP_NE_STRING = 19, + BYTECODE_OP_GT_STRING = 20, + BYTECODE_OP_LT_STRING = 21, + BYTECODE_OP_GE_STRING = 22, + BYTECODE_OP_LE_STRING = 23, + + /* s64 binary comparator */ + BYTECODE_OP_EQ_S64 = 24, + BYTECODE_OP_NE_S64 = 25, + BYTECODE_OP_GT_S64 = 26, + BYTECODE_OP_LT_S64 = 27, + BYTECODE_OP_GE_S64 = 28, + BYTECODE_OP_LE_S64 = 29, + + /* double binary comparator */ + BYTECODE_OP_EQ_DOUBLE = 30, + BYTECODE_OP_NE_DOUBLE = 31, + BYTECODE_OP_GT_DOUBLE = 32, + BYTECODE_OP_LT_DOUBLE = 33, + BYTECODE_OP_GE_DOUBLE = 34, + BYTECODE_OP_LE_DOUBLE = 35, + + /* Mixed S64-double binary comparators */ + BYTECODE_OP_EQ_DOUBLE_S64 = 36, + BYTECODE_OP_NE_DOUBLE_S64 = 37, + BYTECODE_OP_GT_DOUBLE_S64 = 38, + BYTECODE_OP_LT_DOUBLE_S64 = 39, + BYTECODE_OP_GE_DOUBLE_S64 = 40, + BYTECODE_OP_LE_DOUBLE_S64 = 41, + + BYTECODE_OP_EQ_S64_DOUBLE = 42, + BYTECODE_OP_NE_S64_DOUBLE = 43, + BYTECODE_OP_GT_S64_DOUBLE = 44, + BYTECODE_OP_LT_S64_DOUBLE = 45, + BYTECODE_OP_GE_S64_DOUBLE = 46, + BYTECODE_OP_LE_S64_DOUBLE = 47, + + /* unary */ + BYTECODE_OP_UNARY_PLUS = 48, + BYTECODE_OP_UNARY_MINUS = 49, + BYTECODE_OP_UNARY_NOT = 50, + BYTECODE_OP_UNARY_PLUS_S64 = 51, + BYTECODE_OP_UNARY_MINUS_S64 = 52, + BYTECODE_OP_UNARY_NOT_S64 = 53, + BYTECODE_OP_UNARY_PLUS_DOUBLE = 54, + BYTECODE_OP_UNARY_MINUS_DOUBLE = 55, + BYTECODE_OP_UNARY_NOT_DOUBLE = 56, + + /* logical */ + BYTECODE_OP_AND = 57, + BYTECODE_OP_OR = 58, + + /* load field ref */ + BYTECODE_OP_LOAD_FIELD_REF = 59, + BYTECODE_OP_LOAD_FIELD_REF_STRING = 60, + BYTECODE_OP_LOAD_FIELD_REF_SEQUENCE = 61, + BYTECODE_OP_LOAD_FIELD_REF_S64 = 62, + BYTECODE_OP_LOAD_FIELD_REF_DOUBLE = 63, + + /* load immediate from operand */ + BYTECODE_OP_LOAD_STRING = 64, + BYTECODE_OP_LOAD_S64 = 65, + BYTECODE_OP_LOAD_DOUBLE = 66, + + /* cast */ + BYTECODE_OP_CAST_TO_S64 = 67, + BYTECODE_OP_CAST_DOUBLE_TO_S64 = 68, + BYTECODE_OP_CAST_NOP = 69, + + /* get context ref */ + BYTECODE_OP_GET_CONTEXT_REF = 70, + BYTECODE_OP_GET_CONTEXT_REF_STRING = 71, + BYTECODE_OP_GET_CONTEXT_REF_S64 = 72, + BYTECODE_OP_GET_CONTEXT_REF_DOUBLE = 73, + + /* load userspace field ref */ + BYTECODE_OP_LOAD_FIELD_REF_USER_STRING = 74, + BYTECODE_OP_LOAD_FIELD_REF_USER_SEQUENCE = 75, + + /* + * load immediate star globbing pattern (literal string) + * from immediate + */ + BYTECODE_OP_LOAD_STAR_GLOB_STRING = 76, + + /* globbing pattern binary operator: apply to */ + BYTECODE_OP_EQ_STAR_GLOB_STRING = 77, + BYTECODE_OP_NE_STAR_GLOB_STRING = 78, + + /* + * Instructions for recursive traversal through composed types. + */ + BYTECODE_OP_GET_CONTEXT_ROOT = 79, + BYTECODE_OP_GET_APP_CONTEXT_ROOT = 80, + BYTECODE_OP_GET_PAYLOAD_ROOT = 81, + + BYTECODE_OP_GET_SYMBOL = 82, + BYTECODE_OP_GET_SYMBOL_FIELD = 83, + BYTECODE_OP_GET_INDEX_U16 = 84, + BYTECODE_OP_GET_INDEX_U64 = 85, + + BYTECODE_OP_LOAD_FIELD = 86, + BYTECODE_OP_LOAD_FIELD_S8 = 87, + BYTECODE_OP_LOAD_FIELD_S16 = 88, + BYTECODE_OP_LOAD_FIELD_S32 = 89, + BYTECODE_OP_LOAD_FIELD_S64 = 90, + BYTECODE_OP_LOAD_FIELD_U8 = 91, + BYTECODE_OP_LOAD_FIELD_U16 = 92, + BYTECODE_OP_LOAD_FIELD_U32 = 93, + BYTECODE_OP_LOAD_FIELD_U64 = 94, + BYTECODE_OP_LOAD_FIELD_STRING = 95, + BYTECODE_OP_LOAD_FIELD_SEQUENCE = 96, + BYTECODE_OP_LOAD_FIELD_DOUBLE = 97, + + BYTECODE_OP_UNARY_BIT_NOT = 98, + + BYTECODE_OP_RETURN_S64 = 99, + + NR_BYTECODE_OPS, +}; + +typedef uint8_t bytecode_opcode_t; + +struct load_op { + bytecode_opcode_t op; + + /* + * data to load. Size known by enum bytecode_opcode_t and null-term + * char. + */ + char data[0]; +} LTTNG_PACKED; + +struct binary_op { + bytecode_opcode_t op; +} LTTNG_PACKED; + +struct unary_op { + bytecode_opcode_t op; +} LTTNG_PACKED; + +/* skip_offset is absolute from start of bytecode */ +struct logical_op { + bytecode_opcode_t op; + uint16_t skip_offset; /* bytecode insn, if skip second test */ +} LTTNG_PACKED; + +struct cast_op { + bytecode_opcode_t op; +} LTTNG_PACKED; + +struct return_op { + bytecode_opcode_t op; +} LTTNG_PACKED; + +struct lttng_bytecode_alloc { + uint32_t alloc_len; + struct lttng_bytecode b; +}; + +LTTNG_HIDDEN int bytecode_init(struct lttng_bytecode_alloc **fb); +LTTNG_HIDDEN int32_t bytecode_reserve(struct lttng_bytecode_alloc **fb, + uint32_t align, uint32_t len); +LTTNG_HIDDEN int bytecode_push(struct lttng_bytecode_alloc **fb, + const void *data, uint32_t align, uint32_t len); +LTTNG_HIDDEN int bytecode_push_logical(struct lttng_bytecode_alloc **fb, + struct logical_op *data, uint32_t align, uint32_t len, + uint16_t *skip_offset); +LTTNG_HIDDEN int bytecode_push_get_payload_root( + struct lttng_bytecode_alloc **bytecode); +LTTNG_HIDDEN int bytecode_push_get_context_root( + struct lttng_bytecode_alloc **bytecode); +LTTNG_HIDDEN int bytecode_push_get_app_context_root( + struct lttng_bytecode_alloc **bytecode); +LTTNG_HIDDEN int bytecode_push_get_index_u64( + struct lttng_bytecode_alloc **bytecode, uint64_t index); +LTTNG_HIDDEN int bytecode_push_get_symbol( + struct lttng_bytecode_alloc **bytecode, + struct lttng_bytecode_alloc **bytecode_reloc, + const char *symbol); +LTTNG_HIDDEN struct lttng_bytecode *bytecode_copy( + const struct lttng_bytecode *orig_f); + +static inline +unsigned int bytecode_get_len(struct lttng_bytecode *bytecode) +{ + return bytecode->len; +} + +#endif /* LTTNG_COMMON_BYTECODE_H */ diff --git a/src/common/compat/directory-handle.c b/src/common/compat/directory-handle.c index aea4be5fe..e29d6540b 100644 --- a/src/common/compat/directory-handle.c +++ b/src/common/compat/directory-handle.c @@ -1019,8 +1019,9 @@ int lttng_directory_handle_create_subdirectory_as_user( ret = create_directory_check_exists(handle, subdirectory, mode); } else { - ret = _run_as_mkdir(handle, subdirectory, - mode, creds->uid, creds->gid); + ret = _run_as_mkdir(handle, subdirectory, mode, + lttng_credentials_get_uid(creds), + lttng_credentials_get_gid(creds)); } return ret; @@ -1040,7 +1041,7 @@ int lttng_directory_handle_create_subdirectory_recursive_as_user( subdirectory_path, mode); } else { ret = _run_as_mkdir_recursive(handle, subdirectory_path, - mode, creds->uid, creds->gid); + mode, lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds)); } return ret; @@ -1081,7 +1082,7 @@ int lttng_directory_handle_open_file_as_user( mode); } else { ret = _run_as_open(handle, filename, flags, mode, - creds->uid, creds->gid); + lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds)); } return ret; } @@ -1108,7 +1109,7 @@ int lttng_directory_handle_unlink_file_as_user( /* Run as current user. */ ret = lttng_directory_handle_unlink(handle, filename); } else { - ret = _run_as_unlink(handle, filename, creds->uid, creds->gid); + ret = _run_as_unlink(handle, filename, lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds)); } return ret; } @@ -1149,7 +1150,7 @@ int lttng_directory_handle_rename_as_user( old_name, new_handle, new_name); } else { ret = _run_as_rename(old_handle, old_name, new_handle, - new_name, creds->uid, creds->gid); + new_name, lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds)); } return ret; } @@ -1175,7 +1176,7 @@ int lttng_directory_handle_remove_subdirectory_as_user( /* Run as current user. */ ret = lttng_directory_handle_rmdir(handle, name); } else { - ret = _run_as_rmdir(handle, name, creds->uid, creds->gid); + ret = _run_as_rmdir(handle, name, lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds)); } return ret; } @@ -1409,8 +1410,8 @@ int lttng_directory_handle_remove_subdirectory_recursive_as_user( /* Run as current user. */ ret = remove_directory_recursive(handle, name, flags); } else { - ret = _run_as_rmdir_recursive(handle, name, creds->uid, - creds->gid, flags); + ret = _run_as_rmdir_recursive(handle, name, lttng_credentials_get_uid(creds), + lttng_credentials_get_gid(creds), flags); } return ret; } diff --git a/src/common/buffer-usage.c b/src/common/conditions/buffer-usage.c similarity index 100% rename from src/common/buffer-usage.c rename to src/common/conditions/buffer-usage.c diff --git a/src/common/condition.c b/src/common/conditions/condition.c similarity index 83% rename from src/common/condition.c rename to src/common/conditions/condition.c index 427c49e09..a801bf4c9 100644 --- a/src/common/condition.c +++ b/src/common/conditions/condition.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -160,6 +161,9 @@ 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; + break; default: ERR("Attempted to create condition of unknown type (%i)", (int) condition_comm->condition_type); @@ -194,3 +198,33 @@ void lttng_condition_init(struct lttng_condition *condition, condition->type = type; urcu_ref_init(&condition->ref); } + +LTTNG_HIDDEN +const char *lttng_condition_type_str(enum lttng_condition_type type) +{ + switch (type) { + case LTTNG_CONDITION_TYPE_UNKNOWN: + return "unknown"; + + case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: + return "session consumed size"; + + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + return "buffer usage high"; + + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + return "buffer usage low"; + + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + return "session rotation ongoing"; + + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + return "session rotation completed"; + + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + return "event rule hit"; + + default: + return "???"; + } +} diff --git a/src/common/conditions/event-rule.c b/src/common/conditions/event-rule.c new file mode 100644 index 000000000..7333a4fa2 --- /dev/null +++ b/src/common/conditions/event-rule.c @@ -0,0 +1,1567 @@ +/* + * Copyright (C) 2020 Jonathan Rajotte + * + * 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_EVENT_RULE_CONDITION(condition) \ + (lttng_condition_get_type(condition) == \ + LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) + +static bool is_event_rule_evaluation(const struct lttng_evaluation *evaluation) +{ + enum lttng_condition_type type = lttng_evaluation_get_type(evaluation); + + return type == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT; +} + +static bool lttng_condition_event_rule_validate( + const struct lttng_condition *condition); +static int lttng_condition_event_rule_serialize( + const struct lttng_condition *condition, + struct lttng_payload *payload); +static bool lttng_condition_event_rule_is_equal( + const struct lttng_condition *_a, + const struct lttng_condition *_b); +static void lttng_condition_event_rule_destroy( + struct lttng_condition *condition); + +static bool lttng_condition_event_rule_validate( + const struct lttng_condition *condition) +{ + bool valid = false; + struct lttng_condition_event_rule *event_rule; + + if (!condition) { + goto end; + } + + event_rule = container_of( + condition, struct lttng_condition_event_rule, parent); + if (!event_rule->rule) { + ERR("Invalid session event_rule condition: a rule must be set."); + goto end; + } + + valid = lttng_event_rule_validate(event_rule->rule); +end: + return valid; +} + +/* + * Serializes the C string `str` into `buf`. + * + * Encoding is the length of `str` plus one (for the null character), + * and then the string, including its null character. + */ +static +int serialize_cstr(const char *str, struct lttng_dynamic_buffer *buf) +{ + int ret; + uint32_t len = strlen(str) + 1; + + /* Serialize the length, including the null character */ + DBG("Serializing C string's length (including null character): " + "%" PRIu32, len); + ret = lttng_dynamic_buffer_append(buf, &len, sizeof(len)); + if (ret) { + goto end; + } + + /* Serialize the string */ + DBG("Serializing C string: \"%s\"", str); + ret = lttng_dynamic_buffer_append(buf, str, len); + if (ret) { + goto end; + } + +end: + return ret; +} + +/* + * Serializes the event expression `expr` into `buf`. + */ +static +int serialize_event_expr(const struct lttng_event_expr *expr, + struct lttng_payload *payload) +{ + uint8_t type; + int ret; + + /* Serialize the expression's type */ + DBG("Serializing event expression's type: %d", expr->type); + type = expr->type; + ret = lttng_dynamic_buffer_append(&payload->buffer, &type, sizeof(type)); + if (ret) { + goto end; + } + + /* Serialize the expression */ + switch (expr->type) { + case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: + case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: + { + const struct lttng_event_expr_field *field_expr = + container_of(expr, + const struct lttng_event_expr_field, + parent); + + /* Serialize the field name */ + DBG("Serializing field event expression's field name: \"%s\"", + field_expr->name); + ret = serialize_cstr(field_expr->name, &payload->buffer); + if (ret) { + goto end; + } + + break; + } + case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: + { + const struct lttng_event_expr_app_specific_context_field *field_expr = + container_of(expr, + const struct lttng_event_expr_app_specific_context_field, + parent); + + /* Serialize the provider name */ + DBG("Serializing app-specific context field event expression's " + "provider name: \"%s\"", + field_expr->provider_name); + ret = serialize_cstr(field_expr->provider_name, &payload->buffer); + if (ret) { + goto end; + } + + /* Serialize the type name */ + DBG("Serializing app-specific context field event expression's " + "type name: \"%s\"", + field_expr->provider_name); + ret = serialize_cstr(field_expr->type_name, &payload->buffer); + if (ret) { + goto end; + } + + break; + } + case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: + { + const struct lttng_event_expr_array_field_element *elem_expr = + container_of(expr, + const struct lttng_event_expr_array_field_element, + parent); + uint32_t index = elem_expr->index; + + /* Serialize the index */ + DBG("Serializing array field element event expression's " + "index: %u", elem_expr->index); + ret = lttng_dynamic_buffer_append(&payload->buffer, &index, sizeof(index)); + if (ret) { + goto end; + } + + /* Serialize the parent array field expression */ + DBG("Serializing array field element event expression's " + "parent array field event expression."); + ret = serialize_event_expr(elem_expr->array_field_expr, payload); + if (ret) { + goto end; + } + + break; + } + default: + break; + } + +end: + return ret; +} + +LTTNG_HIDDEN +struct lttng_capture_descriptor * +lttng_condition_event_rule_get_internal_capture_descriptor_at_index( + const struct lttng_condition *condition, unsigned int index) +{ + const struct lttng_condition_event_rule *event_rule_cond = + container_of(condition, + const struct lttng_condition_event_rule, + parent); + struct lttng_capture_descriptor *desc = NULL; + unsigned int count; + enum lttng_condition_status status; + + if (!condition || !IS_EVENT_RULE_CONDITION(condition)) { + goto end; + } + + status = lttng_condition_event_rule_get_capture_descriptor_count( + condition, &count); + if (status != LTTNG_CONDITION_STATUS_OK) { + goto end; + } + + if (index >= count) { + goto end; + } + + desc = lttng_dynamic_pointer_array_get_pointer( + &event_rule_cond->capture_descriptors, index); +end: + return desc; +} + +static int lttng_condition_event_rule_serialize( + const struct lttng_condition *condition, + struct lttng_payload *payload) +{ + int ret; + struct lttng_condition_event_rule *event_rule; + enum lttng_condition_status status; + uint32_t capture_descr_count; + uint32_t i; + + if (!condition || !IS_EVENT_RULE_CONDITION(condition)) { + ret = -1; + goto end; + } + + DBG("Serializing event rule condition"); + event_rule = container_of( + condition, struct lttng_condition_event_rule, parent); + + DBG("Serializing event rule condition's event rule"); + ret = lttng_event_rule_serialize(event_rule->rule, payload); + if (ret) { + goto end; + } + + status = lttng_condition_event_rule_get_capture_descriptor_count( + condition, &capture_descr_count); + if (status != LTTNG_CONDITION_STATUS_OK) { + ret = -1; + goto end; + }; + + DBG("Serializing event rule condition's capture descriptor count: %" PRIu32, + capture_descr_count); + ret = lttng_dynamic_buffer_append(&payload->buffer, &capture_descr_count, + sizeof(capture_descr_count)); + if (ret) { + goto end; + } + + for (i = 0; i < capture_descr_count; i++) { + const struct lttng_capture_descriptor *desc = + lttng_condition_event_rule_get_internal_capture_descriptor_at_index( + condition, i); + + DBG("Serializing event rule condition's capture descriptor %" PRIu32, + i); + ret = serialize_event_expr(desc->event_expression, payload); + if (ret) { + goto end; + } + + /* + * Appending the internal index payload linked with the + * descriptor. + * TODO: might want to move to an englobing object to describe a + * capture descriptor publicly. + */ + ret = lttng_dynamic_buffer_append(&payload->buffer, &desc->capture_index, + sizeof(desc->capture_index)); + if (ret) { + goto end; + } + } + +end: + return ret; +} + +static +bool capture_descriptors_are_equal( + const struct lttng_condition *condition_a, + const struct lttng_condition *condition_b) +{ + bool is_equal = true; + unsigned int capture_descr_count_a; + unsigned int capture_descr_count_b; + size_t i; + enum lttng_condition_status status; + + status = lttng_condition_event_rule_get_capture_descriptor_count( + condition_a, &capture_descr_count_a); + if (status != LTTNG_CONDITION_STATUS_OK) { + goto not_equal; + } + + status = lttng_condition_event_rule_get_capture_descriptor_count( + condition_b, &capture_descr_count_b); + if (status != LTTNG_CONDITION_STATUS_OK) { + goto not_equal; + } + + if (capture_descr_count_a != capture_descr_count_b) { + goto not_equal; + } + + for (i = 0; i < capture_descr_count_a; i++) { + const struct lttng_event_expr *expr_a = + lttng_condition_event_rule_get_capture_descriptor_at_index( + condition_a, + i); + const struct lttng_event_expr *expr_b = + lttng_condition_event_rule_get_capture_descriptor_at_index( + condition_b, + i); + + if (!lttng_event_expr_is_equal(expr_a, expr_b)) { + goto not_equal; + } + } + + goto end; + +not_equal: + is_equal = false; + +end: + return is_equal; +} + +static bool lttng_condition_event_rule_is_equal( + const struct lttng_condition *_a, + const struct lttng_condition *_b) +{ + bool is_equal = false; + struct lttng_condition_event_rule *a, *b; + + a = container_of(_a, struct lttng_condition_event_rule, parent); + b = container_of(_b, struct lttng_condition_event_rule, parent); + + /* Both session names must be set or both must be unset. */ + if ((a->rule && !b->rule) || (!a->rule && b->rule)) { + WARN("Comparing session event_rule conditions with uninitialized rule."); + goto end; + } + + is_equal = lttng_event_rule_is_equal(a->rule, b->rule); + if (!is_equal) { + goto end; + } + + is_equal = capture_descriptors_are_equal(_a, _b); + +end: + return is_equal; +} + +static void lttng_condition_event_rule_destroy( + struct lttng_condition *condition) +{ + struct lttng_condition_event_rule *event_rule; + + event_rule = container_of( + condition, struct lttng_condition_event_rule, parent); + + lttng_event_rule_destroy(event_rule->rule); + lttng_dynamic_pointer_array_reset(&event_rule->capture_descriptors); + free(event_rule); +} + +static +void destroy_capture_descriptor(void *ptr) +{ + struct lttng_capture_descriptor *desc = + (struct lttng_capture_descriptor *) ptr; + lttng_event_expr_destroy(desc->event_expression); + free(desc); +} + +struct lttng_condition *lttng_condition_event_rule_create( + struct lttng_event_rule *rule) +{ + struct lttng_condition *parent = NULL; + struct lttng_condition_event_rule *condition = NULL; + + if (!rule) { + goto end; + } + + condition = zmalloc(sizeof(struct lttng_condition_event_rule)); + if (!condition) { + return NULL; + } + + lttng_condition_init(&condition->parent, + LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + condition->parent.validate = lttng_condition_event_rule_validate, + condition->parent.serialize = lttng_condition_event_rule_serialize, + condition->parent.equal = lttng_condition_event_rule_is_equal, + condition->parent.destroy = lttng_condition_event_rule_destroy, + + lttng_event_rule_get(rule); + + condition->rule = rule; + lttng_dynamic_pointer_array_init(&condition->capture_descriptors, + destroy_capture_descriptor); + + parent = &condition->parent; +end: + return parent; +} + +static +int64_t int_from_buffer(const struct lttng_buffer_view *view, size_t size, + size_t *offset) +{ + int64_t ret; + + if (*offset + size > view->size) { + ret = -1; + goto end; + } + + switch (size) { + case 1: + ret = (int64_t) view->data[*offset]; + break; + case sizeof(int32_t): + { + int32_t s32; + + memcpy(&s32, &view->data[*offset], sizeof(s32)); + ret = (int64_t) s32; + break; + } + case sizeof(ret): + memcpy(&ret, &view->data[*offset], sizeof(ret)); + break; + default: + abort(); + } + + *offset += size; + +end: + return ret; +} +static +uint64_t uint_from_buffer(const struct lttng_buffer_view *view, size_t size, + size_t *offset) +{ + uint64_t ret; + + if (*offset + size > view->size) { + ret = UINT64_C(-1); + goto end; + } + + switch (size) { + case 1: + ret = (uint64_t) view->data[*offset]; + break; + case sizeof(uint32_t): + { + uint32_t u32; + + memcpy(&u32, &view->data[*offset], sizeof(u32)); + ret = (uint64_t) u32; + break; + } + case sizeof(ret): + memcpy(&ret, &view->data[*offset], sizeof(ret)); + break; + default: + abort(); + } + + *offset += size; + +end: + return ret; +} + +static +const char *str_from_buffer(const struct lttng_buffer_view *view, + size_t *offset) +{ + uint64_t len; + const char *ret; + + len = uint_from_buffer(view, sizeof(uint32_t), offset); + if (len == UINT64_C(-1)) { + goto error; + } + + ret = &view->data[*offset]; + + if (!lttng_buffer_view_contains_string(view, ret, len)) { + goto error; + } + + *offset += len; + goto end; + +error: + ret = NULL; + +end: + return ret; +} + +static +struct lttng_event_expr *event_expr_from_payload( + struct lttng_payload_view *view, size_t *offset) +{ + struct lttng_event_expr *expr = NULL; + const char *str; + uint64_t type; + + type = uint_from_buffer(&view->buffer, sizeof(uint8_t), offset); + if (type == UINT64_C(-1)) { + goto error; + } + + switch (type) { + case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: + str = str_from_buffer(&view->buffer, offset); + if (!str) { + goto error; + } + + expr = lttng_event_expr_event_payload_field_create(str); + break; + case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: + str = str_from_buffer(&view->buffer, offset); + if (!str) { + goto error; + } + + expr = lttng_event_expr_channel_context_field_create(str); + break; + case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: + { + const char *provider_name; + const char *type_name; + + provider_name = str_from_buffer(&view->buffer, offset); + if (!provider_name) { + goto error; + } + + type_name = str_from_buffer(&view->buffer, offset); + if (!type_name) { + goto error; + } + + expr = lttng_event_expr_app_specific_context_field_create( + provider_name, type_name); + break; + } + case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: + { + struct lttng_event_expr *array_field_expr; + uint64_t index; + + index = uint_from_buffer(&view->buffer, sizeof(uint32_t), offset); + if (index == UINT64_C(-1)) { + goto error; + } + + /* Array field expression is the encoded after this */ + array_field_expr = event_expr_from_payload(view, offset); + if (!array_field_expr) { + goto error; + } + + /* Move ownership of `array_field_expr` to new expression */ + expr = lttng_event_expr_array_field_element_create( + array_field_expr, (unsigned int) index); + if (!expr) { + /* `array_field_expr` not moved: destroy it */ + lttng_event_expr_destroy(array_field_expr); + } + + break; + } + default: + abort(); + } + + goto end; + +error: + lttng_event_expr_destroy(expr); + expr = NULL; + +end: + return expr; +} + +LTTNG_HIDDEN +ssize_t lttng_condition_event_rule_create_from_payload( + struct lttng_payload_view *view, + struct lttng_condition **_condition) +{ + ssize_t consumed_length; + size_t offset = 0; + ssize_t size; + uint64_t capture_descr_count; + uint64_t i; + struct lttng_condition *condition = NULL; + struct lttng_event_rule *event_rule = NULL; + + if (!view || !_condition) { + goto error; + } + + /* Struct lttng_event_rule */ + { + struct lttng_payload_view event_rule_view = + lttng_payload_view_from_view(view, offset, -1); + size = lttng_event_rule_create_from_payload( + &event_rule_view, &event_rule); + } + + if (size < 0 || !event_rule) { + goto error; + } + + /* Create condition (no capture descriptors yet) at this point */ + condition = lttng_condition_event_rule_create(event_rule); + if (!condition) { + goto error; + } + + + /* Capture descriptor count */ + assert(size >= 0); + offset += (size_t) size; + capture_descr_count = uint_from_buffer(&view->buffer, sizeof(uint32_t), &offset); + if (capture_descr_count == UINT64_C(-1)) { + goto error; + } + + /* Capture descriptors */ + for (i = 0; i < capture_descr_count; i++) { + enum lttng_condition_status status; + struct lttng_capture_descriptor *desc; + struct lttng_event_expr *expr = event_expr_from_payload( + view, &offset); + int32_t payload_index = int_from_buffer(&view->buffer, sizeof(int32_t), + &offset); + + if (!expr) { + goto error; + } + + /* Move ownership of `expr` to `condition` */ + status = lttng_condition_event_rule_append_capture_descriptor( + condition, expr); + if (status != LTTNG_CONDITION_STATUS_OK) { + /* `expr` not moved: destroy it */ + lttng_event_expr_destroy(expr); + goto error; + } + + /* + * Set the internal payload object for the descriptor. This can + * be used by liblttng-ctl to access capture msgpack payload on + * the client side. + */ + desc = lttng_condition_event_rule_get_internal_capture_descriptor_at_index(condition, i); + if (desc == NULL) { + goto error; + } + desc->capture_index = payload_index; + } + + consumed_length = (ssize_t) offset; + *_condition = condition; + condition = NULL; + goto end; + +error: + consumed_length = -1; + +end: + lttng_event_rule_destroy(event_rule); + lttng_condition_destroy(condition); + return consumed_length; +} + +LTTNG_HIDDEN +enum lttng_condition_status lttng_condition_event_rule_get_rule_mutable( + const struct lttng_condition *condition, + struct lttng_event_rule **rule) +{ + struct lttng_condition_event_rule *event_rule; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_EVENT_RULE_CONDITION(condition) || !rule) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + event_rule = container_of( + condition, struct lttng_condition_event_rule, parent); + if (!event_rule->rule) { + status = LTTNG_CONDITION_STATUS_UNSET; + goto end; + } + *rule = event_rule->rule; +end: + return status; +} + +enum lttng_condition_status lttng_condition_event_rule_get_rule( + const struct lttng_condition *condition, + const struct lttng_event_rule **rule) +{ + struct lttng_event_rule *mutable_rule = NULL; + enum lttng_condition_status status; + + status = lttng_condition_event_rule_get_rule_mutable( + condition, &mutable_rule); + *rule = mutable_rule; + return status; +} + +enum lttng_condition_status +lttng_condition_event_rule_append_capture_descriptor( + struct lttng_condition *condition, + struct lttng_event_expr *expr) +{ + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + struct lttng_condition_event_rule *event_rule_cond = + container_of(condition, + struct lttng_condition_event_rule, parent); + int ret; + struct lttng_capture_descriptor *descriptor = NULL; + const struct lttng_event_rule *rule = NULL; + + /* Only accept l-values */ + if (!condition || !IS_EVENT_RULE_CONDITION(condition) || !expr || + !lttng_event_expr_is_lvalue(expr)) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + status = lttng_condition_event_rule_get_rule(condition, &rule); + if (status != LTTNG_CONDITION_STATUS_OK) { + goto end; + } + + switch(lttng_event_rule_get_type(rule)) { + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + /* Supported */ + status = LTTNG_CONDITION_STATUS_OK; + break; + case LTTNG_EVENT_RULE_TYPE_UNKNOWN: + status = LTTNG_CONDITION_STATUS_INVALID; + break; + default: + status = LTTNG_CONDITION_STATUS_UNSUPPORTED; + break; + } + + if (status != LTTNG_CONDITION_STATUS_OK) { + goto end; + } + + descriptor = malloc(sizeof(*descriptor)); + if (descriptor == NULL) { + status = LTTNG_CONDITION_STATUS_ERROR; + goto end; + } + + descriptor->capture_index = -1; + descriptor->event_expression = expr; + + ret = lttng_dynamic_pointer_array_add_pointer( + &event_rule_cond->capture_descriptors, descriptor); + if (ret) { + status = LTTNG_CONDITION_STATUS_ERROR; + goto end; + } + + /* Ownership is transfered to the internal capture_descriptors array */ + descriptor = NULL; +end: + free(descriptor); + return status; +} + +enum lttng_condition_status +lttng_condition_event_rule_get_capture_descriptor_count( + const struct lttng_condition *condition, unsigned int *count) +{ + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + const struct lttng_condition_event_rule *event_rule_cond = + container_of(condition, + const struct lttng_condition_event_rule, + parent); + + if (!condition || !IS_EVENT_RULE_CONDITION(condition) || !count) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + *count = lttng_dynamic_pointer_array_get_count( + &event_rule_cond->capture_descriptors); + +end: + return status; +} + +const struct lttng_event_expr * +lttng_condition_event_rule_get_capture_descriptor_at_index( + const struct lttng_condition *condition, unsigned int index) +{ + const struct lttng_event_expr *expr = NULL; + const struct lttng_capture_descriptor *desc = NULL; + + desc = lttng_condition_event_rule_get_internal_capture_descriptor_at_index( + condition, index); + if (desc == NULL) { + goto end; + } + expr = desc->event_expression; + +end: + return expr; +} + +LTTNG_HIDDEN +ssize_t lttng_evaluation_event_rule_create_from_payload( + const struct lttng_condition_event_rule *condition, + struct lttng_payload_view *view, + struct lttng_evaluation **_evaluation) +{ + ssize_t ret, offset = 0; + const char *name; + struct lttng_evaluation *evaluation = NULL; + const struct lttng_evaluation_event_rule_comm *comm = + (const struct lttng_evaluation_event_rule_comm *) + view->buffer.data; + uint32_t capture_payload_size; + const char *capture_payload = NULL; + + if (!_evaluation) { + ret = -1; + goto error; + } + + if (view->buffer.size < sizeof(*comm)) { + ret = -1; + goto error; + } + + /* Map the name, view of the payload */ + offset += sizeof(*comm); + { + struct lttng_payload_view current_view = + lttng_payload_view_from_view(view, offset, + comm->trigger_name_length); + name = current_view.buffer.data; + if (!name) { + ret = -1; + goto error; + } + + if (!lttng_buffer_view_contains_string(¤t_view.buffer, + name, comm->trigger_name_length)) { + ret = -1; + goto error; + } + } + + offset += comm->trigger_name_length; + { + struct lttng_payload_view current_view = lttng_payload_view_from_view(view, offset, -1); + + if (current_view.buffer.size < sizeof(capture_payload_size)) { + ret = -1; + goto error; + } + + memcpy(&capture_payload_size, current_view.buffer.data, + sizeof(capture_payload_size)); + } + offset += sizeof(capture_payload_size); + + if (capture_payload_size > 0) { + struct lttng_payload_view current_view = lttng_payload_view_from_view(view, offset, -1); + + if (current_view.buffer.size < capture_payload_size) { + ret = -1; + goto error; + } + + capture_payload = current_view.buffer.data; + } + + evaluation = lttng_evaluation_event_rule_create(condition, name, + capture_payload, capture_payload_size, true); + if (!evaluation) { + ret = -1; + goto error; + } + + offset += capture_payload_size; + *_evaluation = evaluation; + evaluation = NULL; + ret = offset; + +error: + lttng_evaluation_destroy(evaluation); + return ret; +} + +static int lttng_evaluation_event_rule_serialize( + const struct lttng_evaluation *evaluation, + struct lttng_payload *payload) +{ + int ret = 0; + struct lttng_evaluation_event_rule *hit; + struct lttng_evaluation_event_rule_comm comm; + uint32_t capture_payload_size; + + hit = container_of( + evaluation, struct lttng_evaluation_event_rule, parent); + comm.trigger_name_length = strlen(hit->name) + 1; + ret = lttng_dynamic_buffer_append( + &payload->buffer, &comm, sizeof(comm)); + if (ret) { + goto end; + } + ret = lttng_dynamic_buffer_append( + &payload->buffer, hit->name, comm.trigger_name_length); + if (ret) { + goto end; + } + + capture_payload_size = (uint32_t) hit->capture_payload.size; + ret = lttng_dynamic_buffer_append(&payload->buffer, &capture_payload_size, + sizeof(capture_payload_size)); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, hit->capture_payload.data, + hit->capture_payload.size); + if (ret) { + goto end; + } + +end: + return ret; +} + +static +bool msgpack_str_is_equal(const struct msgpack_object *obj, const char *str) +{ + bool is_equal = true; + + assert(obj->type == MSGPACK_OBJECT_STR); + + if (obj->via.str.size != strlen(str)) { + is_equal = false; + goto end; + } + + if (strncmp(obj->via.str.ptr, str, obj->via.str.size) != 0) { + is_equal = false; + goto end; + } + +end: + return is_equal; +} + +static +const msgpack_object *get_msgpack_map_obj(const struct msgpack_object *map_obj, + const char *name) +{ + const msgpack_object *ret = NULL; + size_t i; + + assert(map_obj->type == MSGPACK_OBJECT_MAP); + + for (i = 0; i < map_obj->via.map.size; i++) { + const struct msgpack_object_kv *kv = &map_obj->via.map.ptr[i]; + + assert(kv->key.type == MSGPACK_OBJECT_STR); + + if (msgpack_str_is_equal(&kv->key, name)) { + ret = &kv->val; + goto end; + } + } + +end: + return ret; +} + +static void lttng_evaluation_event_rule_destroy( + struct lttng_evaluation *evaluation) +{ + struct lttng_evaluation_event_rule *hit; + + hit = container_of( + evaluation, struct lttng_evaluation_event_rule, parent); + free(hit->name); + lttng_dynamic_buffer_reset(&hit->capture_payload); + if (hit->captured_values) { + lttng_event_field_value_destroy(hit->captured_values); + } + free(hit); +} + +static +int event_field_value_from_obj(const msgpack_object *obj, + struct lttng_event_field_value **field_val) +{ + assert(obj); + assert(field_val); + int ret = 0; + + switch (obj->type) { + case MSGPACK_OBJECT_NIL: + /* Unavailable */ + *field_val = NULL; + goto end; + case MSGPACK_OBJECT_POSITIVE_INTEGER: + *field_val = lttng_event_field_value_uint_create( + obj->via.u64); + break; + case MSGPACK_OBJECT_NEGATIVE_INTEGER: + *field_val = lttng_event_field_value_int_create( + obj->via.i64); + break; + case MSGPACK_OBJECT_FLOAT32: + case MSGPACK_OBJECT_FLOAT64: + *field_val = lttng_event_field_value_real_create( + obj->via.f64); + break; + case MSGPACK_OBJECT_STR: + *field_val = lttng_event_field_value_string_create_with_size( + obj->via.str.ptr, obj->via.str.size); + break; + case MSGPACK_OBJECT_ARRAY: + { + size_t i; + + *field_val = lttng_event_field_value_array_create(); + if (!*field_val) { + goto error; + } + + for (i = 0; i < obj->via.array.size; i++) { + const msgpack_object *elem_obj = &obj->via.array.ptr[i]; + struct lttng_event_field_value *elem_field_val; + + ret = event_field_value_from_obj(elem_obj, + &elem_field_val); + + if (ret) { + goto error; + } + + if (elem_field_val) { + ret = lttng_event_field_value_array_append( + *field_val, elem_field_val); + } else { + ret = lttng_event_field_value_array_append_unavailable( + *field_val); + } + + if (ret) { + lttng_event_field_value_destroy(elem_field_val); + goto error; + } + } + + break; + } + case MSGPACK_OBJECT_MAP: + { + /* + * As of this version, the only valid map object is + * for an enumeration value, for example: + * + * type: enum + * value: 177 + * labels: + * - Labatt 50 + * - Molson Dry + * - Carling Black Label + */ + const msgpack_object *inner_obj; + size_t label_i; + + inner_obj = get_msgpack_map_obj(obj, "type"); + if (!inner_obj) { + ERR("Missing `type` entry in map object."); + goto error; + } + + if (inner_obj->type != MSGPACK_OBJECT_STR) { + ERR("Map object's `type` entry is not a string (it's a %d).", + inner_obj->type); + goto error; + } + + if (!msgpack_str_is_equal(inner_obj, "enum")) { + ERR("Map object's `type` entry: expecting `enum`."); + goto error; + } + + inner_obj = get_msgpack_map_obj(obj, "value"); + if (!inner_obj) { + ERR("Missing `value` entry in map object."); + goto error; + } + + if (inner_obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER) { + *field_val = lttng_event_field_value_enum_uint_create( + inner_obj->via.u64); + } else if (inner_obj->type == MSGPACK_OBJECT_NEGATIVE_INTEGER) { + *field_val = lttng_event_field_value_enum_int_create( + inner_obj->via.i64); + } else { + ERR("Map object's `value` entry is not an integer (it's a %d).", + inner_obj->type); + goto error; + } + + if (!*field_val) { + goto error; + } + + inner_obj = get_msgpack_map_obj(obj, "labels"); + if (!inner_obj) { + /* No labels */ + goto end; + } + + if (inner_obj->type != MSGPACK_OBJECT_ARRAY) { + ERR("Map object's `labels` entry is not an array (it's a %d).", + inner_obj->type); + goto error; + } + + for (label_i = 0; label_i < inner_obj->via.array.size; + label_i++) { + int iret; + const msgpack_object *elem_obj = + &inner_obj->via.array.ptr[label_i]; + + if (elem_obj->type != MSGPACK_OBJECT_STR) { + ERR("Map object's `labels` entry's type is not a string (it's a %d).", + elem_obj->type); + goto error; + } + + iret = lttng_event_field_value_enum_append_label_with_size( + *field_val, elem_obj->via.str.ptr, + elem_obj->via.str.size); + if (iret) { + goto error; + } + } + + break; + } + default: + ERR("Unexpected object type %d.", obj->type); + goto error; + } + + if (!*field_val) { + goto error; + } + + goto end; + +error: + lttng_event_field_value_destroy(*field_val); + *field_val = NULL; + ret = -1; + +end: + return ret; +} + +static +struct lttng_event_field_value *event_field_value_from_capture_payload( + const struct lttng_condition_event_rule *condition, + const char *capture_payload, size_t capture_payload_size) +{ + struct lttng_event_field_value *ret = NULL; + msgpack_unpacked unpacked; + msgpack_unpack_return unpack_return; + const msgpack_object *root_obj; + const msgpack_object_array *root_array_obj; + size_t i; + size_t count; + + assert(condition); + assert(capture_payload); + + /* Initialize value */ + msgpack_unpacked_init(&unpacked); + + /* Decode */ + unpack_return = msgpack_unpack_next(&unpacked, capture_payload, + capture_payload_size, NULL); + if (unpack_return != MSGPACK_UNPACK_SUCCESS) { + ERR("msgpack_unpack_next() failed to decode the " + "MessagePack-encoded capture payload " + "(size %zu); returned %d.", + capture_payload_size, unpack_return); + goto error; + } + + /* Get root array */ + root_obj = &unpacked.data; + + if (root_obj->type != MSGPACK_OBJECT_ARRAY) { + ERR("Expecting an array as the root object; got type %d.", + root_obj->type); + goto error; + } + + root_array_obj = &root_obj->via.array; + + /* Create an empty root array event field value */ + ret = lttng_event_field_value_array_create(); + if (!ret) { + goto error; + } + + /* + * For each capture descriptor in the condition object: + * + * 1. Get its corresponding captured field value MessagePack + * object. + * + * 2. Create a corresponding event field value. + * + * 3. Append it to `ret` (the root array event field value). + */ + count = lttng_dynamic_pointer_array_get_count( + &condition->capture_descriptors); + assert(count > 0); + + for (i = 0; i < count; i++) { + const struct lttng_capture_descriptor *capture_descriptor = + lttng_condition_event_rule_get_internal_capture_descriptor_at_index( + &condition->parent, i); + const msgpack_object *elem_obj; + struct lttng_event_field_value *elem_field_val; + int iret; + + assert(capture_descriptor); + assert(capture_descriptor->capture_index >= 0); + + if (capture_descriptor->capture_index >= root_array_obj->size) { + ERR("Root array object of size %u does not have enough " + "elements for the capture index %u " + "(for capture descriptor #%zu).", + (unsigned int) root_array_obj->size, + (unsigned int) capture_descriptor->capture_index, + i); + goto error; + } + + elem_obj = &root_array_obj->ptr[(size_t) capture_descriptor->capture_index]; + iret = event_field_value_from_obj(elem_obj, + &elem_field_val); + if (iret) { + goto error; + } + + if (elem_field_val) { + iret = lttng_event_field_value_array_append(ret, + elem_field_val); + } else { + iret = lttng_event_field_value_array_append_unavailable( + ret); + } + + if (iret) { + lttng_event_field_value_destroy(elem_field_val); + goto error; + } + } + + goto end; + +error: + lttng_event_field_value_destroy(ret); + ret = NULL; + +end: + msgpack_unpacked_destroy(&unpacked); + return ret; +} + +LTTNG_HIDDEN +struct lttng_evaluation *lttng_evaluation_event_rule_create( + const struct lttng_condition_event_rule *condition, + const char *trigger_name, + const char *capture_payload, size_t capture_payload_size, + bool decode_capture_payload) +{ + struct lttng_evaluation_event_rule *hit; + struct lttng_evaluation *evaluation = NULL; + + hit = zmalloc(sizeof(struct lttng_evaluation_event_rule)); + if (!hit) { + goto error; + } + + hit->name = strdup(trigger_name); + if (!hit->name) { + goto error; + } + + lttng_dynamic_buffer_init(&hit->capture_payload); + + if (capture_payload) { + lttng_dynamic_buffer_append(&hit->capture_payload, + capture_payload, capture_payload_size); + + if (decode_capture_payload) { + hit->captured_values = + event_field_value_from_capture_payload( + condition, + capture_payload, + capture_payload_size); + if (!hit->captured_values) { + ERR("Failed to decode the capture payload (size %zu).", + capture_payload_size); + goto error; + } + } + } + + hit->parent.type = LTTNG_CONDITION_TYPE_EVENT_RULE_HIT; + hit->parent.serialize = lttng_evaluation_event_rule_serialize; + hit->parent.destroy = lttng_evaluation_event_rule_destroy; + + evaluation = &hit->parent; + hit = NULL; + +error: + if (hit) { + lttng_evaluation_event_rule_destroy(&hit->parent); + } + + return evaluation; +} + +enum lttng_evaluation_status lttng_evaluation_get_captured_values( + const struct lttng_evaluation *evaluation, + const struct lttng_event_field_value **field_val) +{ + struct lttng_evaluation_event_rule *hit; + enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK; + + if (!evaluation || !is_event_rule_evaluation(evaluation) || + !field_val) { + status = LTTNG_EVALUATION_STATUS_INVALID; + goto end; + } + + hit = container_of(evaluation, struct lttng_evaluation_event_rule, + parent); + if (!hit->captured_values) { + status = LTTNG_EVALUATION_STATUS_INVALID; + goto end; + } + + *field_val = hit->captured_values; + +end: + return status; +} + +enum lttng_evaluation_status lttng_evaluation_event_rule_get_trigger_name( + const struct lttng_evaluation *evaluation, const char **name) +{ + struct lttng_evaluation_event_rule *hit; + enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK; + + if (!evaluation || !is_event_rule_evaluation(evaluation) || !name) { + status = LTTNG_EVALUATION_STATUS_INVALID; + goto end; + } + + hit = container_of( + evaluation, struct lttng_evaluation_event_rule, parent); + *name = hit->name; +end: + return status; +} + +LTTNG_HIDDEN +enum lttng_error_code +lttng_condition_event_rule_generate_capture_descriptor_bytecode_set( + struct lttng_condition *condition, + struct lttng_dynamic_pointer_array *bytecode_set) +{ + enum lttng_error_code ret; + enum lttng_condition_status status; + unsigned int capture_count; + const struct lttng_condition_event_rule_capture_bytecode_element *set_element; + struct lttng_capture_descriptor *local_capture_desc; + ssize_t set_count; + struct lttng_condition_event_rule_capture_bytecode_element *set_element_to_append = + NULL; + struct lttng_bytecode *bytecode = NULL; + + if (!condition || !IS_EVENT_RULE_CONDITION(condition) || + !bytecode_set) { + ret = LTTNG_ERR_FATAL; + goto end; + } + + status = lttng_condition_event_rule_get_capture_descriptor_count( + condition, &capture_count); + if (status != LTTNG_CONDITION_STATUS_OK) { + ret = LTTNG_ERR_FATAL; + goto end; + } + + /* + * O(n^2), don't care. This code path is not hot. + * Before inserting into the set, validate that the expression is not + * already present in it. + */ + for (unsigned int i = 0; i < capture_count; i++) { + int found_in_set = false; + + set_count = lttng_dynamic_pointer_array_get_count(bytecode_set); + + local_capture_desc = + lttng_condition_event_rule_get_internal_capture_descriptor_at_index( + condition, i); + if (local_capture_desc == NULL) { + ret = LTTNG_ERR_FATAL; + goto end; + } + + /* + * Iterate over the set to check if already present in the set. + */ + for (ssize_t j = 0; j < set_count; j++) { + set_element = (struct lttng_condition_event_rule_capture_bytecode_element + *) + lttng_dynamic_pointer_array_get_pointer( + bytecode_set, j); + if (set_element == NULL) { + ret = LTTNG_ERR_FATAL; + goto end; + } + + if (!lttng_event_expr_is_equal( + local_capture_desc->event_expression, + set_element->expression)) { + /* Check against next set element */ + continue; + } + + /* + * Already present in the set, assign the + * capture index of the capture descriptor for + * future use. + */ + found_in_set = true; + local_capture_desc->capture_index = j; + /* Exit inner loop */ + break; + } + + if (found_in_set) { + /* Process next local capture descriptor */ + continue; + } + + /* + * Not found in the set. + * Insert the capture descriptor in the set. + */ + set_element_to_append = malloc(sizeof(*set_element_to_append)); + if (set_element_to_append == NULL) { + ret = LTTNG_ERR_NOMEM; + goto end; + } + + /* Generate the bytecode */ + status = lttng_event_expr_to_bytecode( + local_capture_desc->event_expression, + &bytecode); + if (status < 0 || bytecode == NULL) { + /* TODO: return pertinent capture related error code */ + ret = LTTNG_ERR_FILTER_INVAL; + goto end; + } + + set_element_to_append->bytecode = bytecode; + + /* + * Ensure the lifetime of the event expression. + * Our reference will be put on condition destroy. + */ + lttng_event_expr_get(local_capture_desc->event_expression); + set_element_to_append->expression = + local_capture_desc->event_expression; + + ret = lttng_dynamic_pointer_array_add_pointer( + bytecode_set, set_element_to_append); + if (ret < 0) { + ret = LTTNG_ERR_NOMEM; + goto end; + } + + /* Ownership tranfered to the bytecode set */ + set_element_to_append = NULL; + bytecode = NULL; + + /* Assign the capture descriptor for future use */ + local_capture_desc->capture_index = set_count; + } + + /* Everything went better than expected */ + ret = LTTNG_OK; + +end: + free(set_element_to_append); + free(bytecode); + return ret; +} diff --git a/src/common/session-consumed-size.c b/src/common/conditions/session-consumed-size.c similarity index 100% rename from src/common/session-consumed-size.c rename to src/common/conditions/session-consumed-size.c diff --git a/src/common/session-rotation.c b/src/common/conditions/session-rotation.c similarity index 100% rename from src/common/session-rotation.c rename to src/common/conditions/session-rotation.c diff --git a/src/common/credentials.c b/src/common/credentials.c index dc1bdd89f..589ffabc2 100644 --- a/src/common/credentials.c +++ b/src/common/credentials.c @@ -9,15 +9,60 @@ #include #include "credentials.h" -bool lttng_credentials_is_equal(const struct lttng_credentials *a, +uid_t lttng_credentials_get_uid(const struct lttng_credentials *creds) +{ + return LTTNG_OPTIONAL_GET(creds->uid); +} + +gid_t lttng_credentials_get_gid(const struct lttng_credentials *creds) +{ + return LTTNG_OPTIONAL_GET(creds->gid); +} + +bool lttng_credentials_is_equal_uid(const struct lttng_credentials *a, const struct lttng_credentials *b) { assert(a); assert(b); - if ((a->uid != b->uid) || (a->gid != b->gid)) { + /* XOR on the is_set value */ + if (!!a->uid.is_set != !!b->uid.is_set) { return false; } - return true; -}; + if (!a->uid.is_set && !b->uid.is_set) { + return true; + } + + /* Both a and b are set. */ + return a->uid.value == b->uid.value; +} + +bool lttng_credentials_is_equal_gid(const struct lttng_credentials *a, + const struct lttng_credentials *b) +{ + assert(a); + assert(b); + + /* XOR on the is_set value */ + if (!!a->gid.is_set != !!b->gid.is_set) { + return false; + } + + if (!a->gid.is_set && !b->gid.is_set) { + return true; + } + + /* Both a and b are set. */ + return a->gid.value == b->gid.value; +} + +bool lttng_credentials_is_equal(const struct lttng_credentials *a, + const struct lttng_credentials *b) +{ + assert(a); + assert(b); + + return lttng_credentials_is_equal_uid(a, b) && + lttng_credentials_is_equal_gid(a, b); +} diff --git a/src/common/credentials.h b/src/common/credentials.h index 9e5fc6260..c0dc6d7f1 100644 --- a/src/common/credentials.h +++ b/src/common/credentials.h @@ -11,11 +11,22 @@ #include #include +#include "optional.h" + struct lttng_credentials { - uid_t uid; - gid_t gid; + LTTNG_OPTIONAL(uid_t) uid; + LTTNG_OPTIONAL(gid_t) gid; }; +uid_t lttng_credentials_get_uid(const struct lttng_credentials *creds); +gid_t lttng_credentials_get_gid(const struct lttng_credentials *creds); + +bool lttng_credentials_is_equal_uid(const struct lttng_credentials *a, + const struct lttng_credentials *b); + +bool lttng_credentials_is_equal_gid(const struct lttng_credentials *a, + const struct lttng_credentials *b); + bool lttng_credentials_is_equal(const struct lttng_credentials *a, const struct lttng_credentials *b); diff --git a/src/common/domain.c b/src/common/domain.c new file mode 100644 index 000000000..277fc802a --- /dev/null +++ b/src/common/domain.c @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020 Simon Marchi + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include "lttng/domain-internal.h" +#include "common/macros.h" + +LTTNG_HIDDEN +const char *lttng_domain_type_str(enum lttng_domain_type domain_type) +{ + switch (domain_type) { + case LTTNG_DOMAIN_NONE: + return "none"; + case LTTNG_DOMAIN_KERNEL: + return "kernel"; + case LTTNG_DOMAIN_UST: + return "ust"; + case LTTNG_DOMAIN_JUL: + return "jul"; + case LTTNG_DOMAIN_LOG4J: + return "log4j"; + case LTTNG_DOMAIN_PYTHON: + return "python"; + default: + return "???"; + } +} diff --git a/src/common/dynamic-array.h b/src/common/dynamic-array.h index 0b488a39c..71cf9af18 100644 --- a/src/common/dynamic-array.h +++ b/src/common/dynamic-array.h @@ -140,6 +140,23 @@ void *lttng_dynamic_pointer_array_get_pointer( return *element; } +/* + * Returns the pointer at index `index`, sets the array slot to NULL. Does not + * run the destructor. + */ + +static inline +void *lttng_dynamic_pointer_array_steal_pointer( + struct lttng_dynamic_pointer_array *array, size_t index) +{ + void **p_element = lttng_dynamic_array_get_element(&array->array, index); + void *element = *p_element; + + *p_element = NULL; + + return element; +} + /* * Add a pointer to the end of a dynamic pointer array. The array's element * count is increased by one and its underlying capacity is adjusted diff --git a/src/common/error.c b/src/common/error.c index 11dbc9028..c64bfd710 100644 --- a/src/common/error.c +++ b/src/common/error.c @@ -239,6 +239,9 @@ static const char *error_string_array[] = { [ ERROR_INDEX(LTTNG_ERR_GROUP_NOT_FOUND) ] = "Group not found", [ ERROR_INDEX(LTTNG_ERR_UNSUPPORTED_DOMAIN) ] = "Unsupported domain used", [ ERROR_INDEX(LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY) ] = "Operation does not apply to the process attribute tracker's tracking policy", + [ ERROR_INDEX(LTTNG_ERR_TRIGGER_GROUP_NOTIFICATION_FD) ] = "Failed to create a trigger group notification file descriptor", + [ ERROR_INDEX(LTTNG_ERR_TRIGGER_GROUP_ERROR_COUNTER) ] = "Failed to create a trigger group error counter", + [ ERROR_INDEX(LTTNG_ERR_TRIGGER_GROUP_ERROR_COUNTER_FULL) ] = "Trigger group error counter full", /* Last element */ [ ERROR_INDEX(LTTNG_ERR_NR) ] = "Unknown error code" diff --git a/src/common/evaluation.c b/src/common/evaluation.c index e936bdd91..479b8b3e5 100644 --- a/src/common/evaluation.c +++ b/src/common/evaluation.c @@ -5,10 +5,12 @@ * */ +#include #include #include #include #include +#include #include #include #include @@ -48,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) { @@ -107,6 +110,19 @@ ssize_t lttng_evaluation_create_from_payload( } evaluation_size += ret; break; + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + assert(condition); + assert(condition->type == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + ret = lttng_evaluation_event_rule_create_from_payload( + container_of(condition, + struct lttng_condition_event_rule, + parent), + &evaluation_view, evaluation); + if (ret < 0) { + goto end; + } + evaluation_size += ret; + break; default: ERR("Attempted to create evaluation of unknown type (%i)", (int) evaluation_comm->type); diff --git a/src/common/event-expr-to-bytecode.c b/src/common/event-expr-to-bytecode.c new file mode 100644 index 000000000..05eccb7ea --- /dev/null +++ b/src/common/event-expr-to-bytecode.c @@ -0,0 +1,201 @@ +/* + * Copyright 2020 EfficiOS, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include "event-expr-to-bytecode.h" + +#include +#include +#include + + +static +int event_expr_to_bytecode_recursive(const struct lttng_event_expr *expr, + struct lttng_bytecode_alloc **bytecode, + struct lttng_bytecode_alloc **bytecode_reloc) +{ + enum lttng_event_expr_status event_expr_status; + int status; + + switch (lttng_event_expr_get_type(expr)) { + case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: + { + const char *name; + + status = bytecode_push_get_payload_root(bytecode); + if (status) { + goto end; + } + + name = lttng_event_expr_event_payload_field_get_name(expr); + if (!name) { + status = -1; + goto end; + } + + status = bytecode_push_get_symbol(bytecode, bytecode_reloc, name); + if (status) { + goto end; + } + + break; + } + + case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: + { + const char *name; + + status = bytecode_push_get_context_root(bytecode); + if (status) { + goto end; + } + + name = lttng_event_expr_channel_context_field_get_name(expr); + if (!name) { + status = -1; + goto end; + } + + status = bytecode_push_get_symbol(bytecode, bytecode_reloc, name); + if (status) { + goto end; + } + + break; + } + + case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: + { + const char *provider_name, *type_name; + char *name = NULL; + int ret; + + status = bytecode_push_get_app_context_root(bytecode); + if (status) { + goto end; + } + + provider_name = lttng_event_expr_app_specific_context_field_get_provider_name(expr); + if (!provider_name) { + status = -1; + goto end; + } + + type_name = lttng_event_expr_app_specific_context_field_get_type_name(expr); + if (!type_name) { + status = -1; + goto end; + } + + /* Reconstitute the app context field name from its two parts. */ + ret = asprintf(&name, "%s:%s", provider_name, type_name); + if (ret < 0) { + status = -1; + goto end; + } + + status = bytecode_push_get_symbol(bytecode, bytecode_reloc, name); + free(name); + if (status) { + goto end; + } + + break; + } + + case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: + { + const struct lttng_event_expr *parent; + unsigned int index; + + parent = lttng_event_expr_array_field_element_get_parent_expr(expr); + if (!parent) { + status = -1; + goto end; + } + + status = event_expr_to_bytecode_recursive(parent, bytecode, bytecode_reloc); + if (status) { + goto end; + } + + event_expr_status = lttng_event_expr_array_field_element_get_index( + expr, &index); + if (event_expr_status != LTTNG_EVENT_EXPR_STATUS_OK) { + status = -1; + goto end; + } + + status = bytecode_push_get_index_u64(bytecode, index); + if (status) { + goto end; + } + + break; + } + + default: + abort(); + } + + status = 0; +end: + return status; +} + +LTTNG_HIDDEN +int lttng_event_expr_to_bytecode(const struct lttng_event_expr *expr, + struct lttng_bytecode **bytecode_out) +{ + struct lttng_bytecode_alloc *bytecode = NULL; + struct lttng_bytecode_alloc *bytecode_reloc = NULL; + struct return_op ret_insn; + int status; + + status = bytecode_init(&bytecode); + if (status) { + goto end; + } + + status = bytecode_init(&bytecode_reloc); + if (status) { + goto end; + } + + status = event_expr_to_bytecode_recursive (expr, &bytecode, &bytecode_reloc); + if (status) { + goto end; + } + + ret_insn.op = BYTECODE_OP_RETURN; + bytecode_push(&bytecode, &ret_insn, 1, sizeof(ret_insn)); + + /* Append symbol table to bytecode. */ + bytecode->b.reloc_table_offset = bytecode_get_len(&bytecode->b); + status = bytecode_push(&bytecode, bytecode_reloc->b.data, + 1, bytecode_get_len(&bytecode_reloc->b)); + if (status) { + goto end; + } + + /* Copy the `lttng_bytecode` out of the `lttng_bytecode_alloc`. */ + *bytecode_out = bytecode_copy(&bytecode->b); + if (!*bytecode_out) { + status = -1; + goto end; + } + +end: + if (bytecode) { + free(bytecode); + } + + if (bytecode_reloc) { + free(bytecode_reloc); + } + + return status; +} diff --git a/src/common/event-expr-to-bytecode.h b/src/common/event-expr-to-bytecode.h new file mode 100644 index 000000000..9d7d57aea --- /dev/null +++ b/src/common/event-expr-to-bytecode.h @@ -0,0 +1,20 @@ +#ifndef SRC_COMMON_EVENT_EXPR_TO_BYTECODE_H +#define SRC_COMMON_EVENT_EXPR_TO_BYTECODE_H + +/* + * Copyright 2020 EfficiOS, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include + +struct lttng_bytecode; +struct lttng_event_expr; + +LTTNG_HIDDEN +int lttng_event_expr_to_bytecode (const struct lttng_event_expr *expr, + struct lttng_bytecode **bytecode_out); + +#endif /* SRC_COMMON_EVENT_EXPR_TO_BYTECODE_H */ diff --git a/src/common/event-expr.c b/src/common/event-expr.c new file mode 100644 index 000000000..10f109c46 --- /dev/null +++ b/src/common/event-expr.c @@ -0,0 +1,449 @@ +/* + * event-expr.c + * + * Linux Trace Toolkit Control Library + * + * Copyright (C) 2020 Philippe Proulx + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#define _LGPL_SOURCE +#include +#include + +#include +#include +#include + +enum lttng_event_expr_type lttng_event_expr_get_type( + const struct lttng_event_expr *expr) +{ + enum lttng_event_expr_type type; + + if (!expr) { + type = LTTNG_EVENT_EXPR_TYPE_INVALID; + goto end; + } + + type = expr->type; + +end: + return type; +} + +static +struct lttng_event_expr *create_empty_expr(enum lttng_event_expr_type type, + size_t size) +{ + struct lttng_event_expr *expr; + + expr = zmalloc(size); + if (!expr) { + goto end; + } + + urcu_ref_init(&expr->ref); + expr->type = type; + +end: + return expr; +} + +static +struct lttng_event_expr_field *create_field_event_expr( + enum lttng_event_expr_type type, + const char *name) +{ + struct lttng_event_expr_field *expr = + container_of( + create_empty_expr(type, sizeof(*expr)), + struct lttng_event_expr_field, parent); + + if (!expr) { + goto error; + } + + assert(name); + expr->name = strdup(name); + if (!expr->name) { + goto error; + } + + goto end; + +error: + lttng_event_expr_destroy(&expr->parent); + +end: + return expr; +} + +struct lttng_event_expr *lttng_event_expr_event_payload_field_create( + const char *field_name) +{ + struct lttng_event_expr *expr = NULL; + + if (!field_name) { + goto end; + } + + expr = &create_field_event_expr( + LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD, + field_name)->parent; + +end: + return expr; +} + +struct lttng_event_expr *lttng_event_expr_channel_context_field_create( + const char *field_name) +{ + struct lttng_event_expr *expr = NULL; + + if (!field_name) { + goto end; + } + + expr = &create_field_event_expr( + LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD, + field_name)->parent; + +end: + return expr; +} + +struct lttng_event_expr *lttng_event_expr_app_specific_context_field_create( + const char *provider_name, const char *type_name) +{ + struct lttng_event_expr_app_specific_context_field *expr = NULL; + + if (!type_name || !provider_name) { + goto error; + } + + expr = container_of(create_empty_expr( + LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD, + sizeof(*expr)), + struct lttng_event_expr_app_specific_context_field, + parent); + if (!expr) { + goto error; + } + + expr->provider_name = strdup(provider_name); + if (!expr->provider_name) { + goto error; + } + + expr->type_name = strdup(type_name); + if (!expr->type_name) { + goto error; + } + + goto end; + +error: + lttng_event_expr_destroy(&expr->parent); + +end: + return &expr->parent; +} + +struct lttng_event_expr *lttng_event_expr_array_field_element_create( + struct lttng_event_expr *array_field_expr, + unsigned int index) +{ + struct lttng_event_expr_array_field_element *expr = NULL; + + /* The parent array field expression must be an l-value */ + if (!array_field_expr || + !lttng_event_expr_is_lvalue(array_field_expr)) { + goto error; + } + + expr = container_of(create_empty_expr( + LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT, + sizeof(*expr)), + struct lttng_event_expr_array_field_element, + parent); + if (!expr) { + goto error; + } + + expr->array_field_expr = array_field_expr; + expr->index = index; + goto end; + +error: + lttng_event_expr_destroy(&expr->parent); + +end: + return &expr->parent; +} + +const char *lttng_event_expr_event_payload_field_get_name( + const struct lttng_event_expr *expr) +{ + const char *ret = NULL; + + if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD) { + goto end; + } + + ret = container_of(expr, + const struct lttng_event_expr_field, parent)->name; + +end: + return ret; +} + +const char *lttng_event_expr_channel_context_field_get_name( + const struct lttng_event_expr *expr) +{ + const char *ret = NULL; + + if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD) { + goto end; + } + + ret = container_of(expr, + const struct lttng_event_expr_field, parent)->name; + +end: + return ret; +} + +const char *lttng_event_expr_app_specific_context_field_get_provider_name( + const struct lttng_event_expr *expr) +{ + const char *ret = NULL; + + if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD) { + goto end; + } + + ret = container_of(expr, + const struct lttng_event_expr_app_specific_context_field, + parent)->provider_name; + +end: + return ret; +} + +const char *lttng_event_expr_app_specific_context_field_get_type_name( + const struct lttng_event_expr *expr) +{ + const char *ret = NULL; + + if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD) { + goto end; + } + + ret = container_of(expr, + const struct lttng_event_expr_app_specific_context_field, + parent)->type_name; + +end: + return ret; +} + +const struct lttng_event_expr * +lttng_event_expr_array_field_element_get_parent_expr( + const struct lttng_event_expr *expr) +{ + const struct lttng_event_expr *ret = NULL; + + if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT) { + goto end; + } + + ret = container_of(expr, + const struct lttng_event_expr_array_field_element, + parent)->array_field_expr; + +end: + return ret; +} + +enum lttng_event_expr_status lttng_event_expr_array_field_element_get_index( + const struct lttng_event_expr *expr, unsigned int *index) +{ + enum lttng_event_expr_status ret = LTTNG_EVENT_EXPR_STATUS_OK; + + if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT || + !index) { + ret = LTTNG_EVENT_EXPR_STATUS_INVALID; + goto end; + } + + *index = container_of(expr, + const struct lttng_event_expr_array_field_element, + parent)->index; + +end: + return ret; +} + +bool lttng_event_expr_is_equal(const struct lttng_event_expr *expr_a, + const struct lttng_event_expr *expr_b) +{ + bool is_equal = true; + + if (!expr_a && !expr_b) { + /* Both `NULL`: equal */ + goto end; + } + + if (!expr_a || !expr_b) { + /* Only one `NULL`: not equal */ + goto not_equal; + } + + if (expr_a->type != expr_b->type) { + /* Different types: not equal */ + goto not_equal; + } + + switch (expr_a->type) { + case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: + case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: + { + const struct lttng_event_expr_field *field_expr_a = + container_of(expr_a, + const struct lttng_event_expr_field, + parent); + const struct lttng_event_expr_field *field_expr_b = + container_of(expr_b, + const struct lttng_event_expr_field, + parent); + + if (strcmp(field_expr_a->name, field_expr_b->name) != 0) { + goto not_equal; + } + + break; + } + case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: + { + const struct lttng_event_expr_app_specific_context_field *field_expr_a = + container_of(expr_a, + const struct lttng_event_expr_app_specific_context_field, + parent); + const struct lttng_event_expr_app_specific_context_field *field_expr_b = + container_of(expr_b, + const struct lttng_event_expr_app_specific_context_field, + parent); + + if (strcmp(field_expr_a->provider_name, + field_expr_b->provider_name) != 0) { + goto not_equal; + } + + if (strcmp(field_expr_a->type_name, + field_expr_b->type_name) != 0) { + goto not_equal; + } + + break; + } + case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: + { + const struct lttng_event_expr_array_field_element *elem_expr_a = + container_of(expr_a, + const struct lttng_event_expr_array_field_element, + parent); + const struct lttng_event_expr_array_field_element *elem_expr_b = + container_of(expr_b, + const struct lttng_event_expr_array_field_element, + parent); + + if (!lttng_event_expr_is_equal(elem_expr_a->array_field_expr, + elem_expr_b->array_field_expr)) { + goto not_equal; + } + + if (elem_expr_a->index != elem_expr_b->index) { + goto not_equal; + } + + break; + } + default: + break; + } + + goto end; + +not_equal: + is_equal = false; + +end: + return is_equal; +} + +static +void event_expr_destroy_ref(struct urcu_ref *ref) +{ + struct lttng_event_expr *expr = + container_of(ref, struct lttng_event_expr, ref); + + switch (expr->type) { + case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: + case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: + { + struct lttng_event_expr_field *field_expr = + container_of(expr, + struct lttng_event_expr_field, parent); + + free(field_expr->name); + break; + } + case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: + { + struct lttng_event_expr_app_specific_context_field *field_expr = + container_of(expr, + struct lttng_event_expr_app_specific_context_field, + parent); + + free(field_expr->provider_name); + free(field_expr->type_name); + break; + } + case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: + { + struct lttng_event_expr_array_field_element *elem_expr = + container_of(expr, + struct lttng_event_expr_array_field_element, + parent); + + lttng_event_expr_destroy(elem_expr->array_field_expr); + break; + } + default: + break; + } + + free(expr); +} + +LTTNG_HIDDEN +void lttng_event_expr_get(struct lttng_event_expr *expr) +{ + urcu_ref_get(&expr->ref); +} + +LTTNG_HIDDEN +void lttng_event_expr_put(struct lttng_event_expr *expr) { + if(!expr) { + return; + } + urcu_ref_put(&expr->ref, event_expr_destroy_ref); +} + +void lttng_event_expr_destroy(struct lttng_event_expr *expr) +{ + lttng_event_expr_put(expr); + return; +} diff --git a/src/common/event-field-value.c b/src/common/event-field-value.c new file mode 100644 index 000000000..de524cc8c --- /dev/null +++ b/src/common/event-field-value.c @@ -0,0 +1,592 @@ +/* + * event-field-value.c + * + * Linux Trace Toolkit Control Library + * + * Copyright (C) 2020 Philippe Proulx + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include + +#include +#include +#include + +static +struct lttng_event_field_value *create_empty_field_val( + enum lttng_event_field_value_type type, size_t size) +{ + struct lttng_event_field_value *field_val; + + field_val = zmalloc(size); + if (!field_val) { + goto end; + } + + field_val->type = type; + +end: + return field_val; +} + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_uint_create( + uint64_t val) +{ + struct lttng_event_field_value_uint *field_val; + + field_val = container_of(create_empty_field_val( + LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT, + sizeof(*field_val)), + struct lttng_event_field_value_uint, parent); + if (!field_val) { + goto error; + } + + field_val->val = val; + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent); + +end: + return &field_val->parent; +} + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_int_create( + int64_t val) +{ + struct lttng_event_field_value_int *field_val; + + field_val = container_of(create_empty_field_val( + LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT, + sizeof(*field_val)), + struct lttng_event_field_value_int, parent); + if (!field_val) { + goto error; + } + + field_val->val = val; + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent); + +end: + return &field_val->parent; +} + +static +struct lttng_event_field_value_enum *create_enum_field_val( + enum lttng_event_field_value_type type, size_t size) +{ + struct lttng_event_field_value_enum *field_val; + + field_val = container_of(create_empty_field_val(type, size), + struct lttng_event_field_value_enum, parent); + if (!field_val) { + goto error; + } + + lttng_dynamic_pointer_array_init(&field_val->labels, free); + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent); + +end: + return field_val; +} + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_enum_uint_create( + uint64_t val) +{ + struct lttng_event_field_value_enum_uint *field_val; + + field_val = container_of(create_enum_field_val( + LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM, + sizeof(*field_val)), + struct lttng_event_field_value_enum_uint, parent); + if (!field_val) { + goto error; + } + + field_val->val = val; + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent.parent); + +end: + return &field_val->parent.parent; +} + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_enum_int_create( + int64_t val) +{ + struct lttng_event_field_value_enum_int *field_val; + + field_val = container_of(create_enum_field_val( + LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM, + sizeof(*field_val)), + struct lttng_event_field_value_enum_int, parent); + if (!field_val) { + goto error; + } + + field_val->val = val; + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent.parent); + +end: + return &field_val->parent.parent; +} + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_real_create(double val) +{ + struct lttng_event_field_value_real *field_val = container_of( + create_empty_field_val( + LTTNG_EVENT_FIELD_VALUE_TYPE_REAL, + sizeof(*field_val)), + struct lttng_event_field_value_real, parent); + + if (!field_val) { + goto error; + } + + field_val->val = val; + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent); + +end: + return &field_val->parent; +} + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_string_create_with_size( + const char *val, size_t size) +{ + struct lttng_event_field_value_string *field_val = container_of( + create_empty_field_val( + LTTNG_EVENT_FIELD_VALUE_TYPE_STRING, + sizeof(*field_val)), + struct lttng_event_field_value_string, parent); + + if (!field_val) { + goto error; + } + + assert(val); + field_val->val = strndup(val, size); + if (!field_val->val) { + goto error; + } + + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent); + +end: + return &field_val->parent; +} + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_string_create( + const char *val) +{ + assert(val); + return lttng_event_field_value_string_create_with_size(val, + strlen(val)); +} + +static +void destroy_field_val(void *field_val) +{ + lttng_event_field_value_destroy(field_val); +} + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_array_create(void) +{ + struct lttng_event_field_value_array *field_val = container_of( + create_empty_field_val( + LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY, + sizeof(*field_val)), + struct lttng_event_field_value_array, parent); + + if (!field_val) { + goto error; + } + + lttng_dynamic_pointer_array_init(&field_val->elems, destroy_field_val); + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent); + +end: + return &field_val->parent; +} + +LTTNG_HIDDEN +void lttng_event_field_value_destroy(struct lttng_event_field_value *field_val) +{ + if (!field_val) { + goto end; + } + + switch (field_val->type) { + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM: + case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM: + { + struct lttng_event_field_value_enum *enum_field_val = + container_of(field_val, + struct lttng_event_field_value_enum, parent); + + lttng_dynamic_pointer_array_reset(&enum_field_val->labels); + break; + } + case LTTNG_EVENT_FIELD_VALUE_TYPE_STRING: + { + struct lttng_event_field_value_string *str_field_val = + container_of(field_val, + struct lttng_event_field_value_string, parent); + + free(str_field_val->val); + break; + } + case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY: + { + struct lttng_event_field_value_array *array_field_expr = + container_of(field_val, + struct lttng_event_field_value_array, + parent); + + lttng_dynamic_pointer_array_reset(&array_field_expr->elems); + break; + } + default: + break; + } + + free(field_val); + +end: + return; +} + +LTTNG_HIDDEN +int lttng_event_field_value_enum_append_label_with_size( + struct lttng_event_field_value *field_val, + const char *label, size_t size) +{ + int ret; + char *mein_label; + + assert(field_val); + assert(label); + mein_label = strndup(label, size); + if (!mein_label) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_pointer_array_add_pointer( + &container_of(field_val, + struct lttng_event_field_value_enum, parent)->labels, + mein_label); + if (ret == 0) { + mein_label = NULL; + } + +end: + free(mein_label); + return ret; +} + +LTTNG_HIDDEN +int lttng_event_field_value_enum_append_label( + struct lttng_event_field_value *field_val, + const char *label) +{ + assert(label); + return lttng_event_field_value_enum_append_label_with_size(field_val, + label, strlen(label)); +} + +LTTNG_HIDDEN +int lttng_event_field_value_array_append( + struct lttng_event_field_value *array_field_val, + struct lttng_event_field_value *field_val) +{ + assert(array_field_val); + assert(field_val); + return lttng_dynamic_pointer_array_add_pointer( + &container_of(array_field_val, + struct lttng_event_field_value_array, parent)->elems, + field_val); +} + +LTTNG_HIDDEN +int lttng_event_field_value_array_append_unavailable( + struct lttng_event_field_value *array_field_val) +{ + assert(array_field_val); + return lttng_dynamic_pointer_array_add_pointer( + &container_of(array_field_val, + struct lttng_event_field_value_array, parent)->elems, + NULL); +} + +enum lttng_event_field_value_type lttng_event_field_value_get_type( + const struct lttng_event_field_value *field_val) +{ + enum lttng_event_field_value_type type; + + if (!field_val) { + type = LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID; + goto end; + } + + type = field_val->type; + +end: + return type; +} + +enum lttng_event_field_value_status +lttng_event_field_value_unsigned_int_get_value( + const struct lttng_event_field_value *field_val, uint64_t *val) +{ + enum lttng_event_field_value_status status; + + if (!field_val || !val) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + switch (field_val->type) { + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT: + *val = container_of(field_val, + const struct lttng_event_field_value_uint, + parent)->val; + break; + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM: + *val = container_of( + container_of(field_val, + const struct lttng_event_field_value_enum, + parent), + const struct lttng_event_field_value_enum_uint, + parent)->val; + break; + default: + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; + +end: + return status; +} + +enum lttng_event_field_value_status +lttng_event_field_value_signed_int_get_value( + const struct lttng_event_field_value *field_val, int64_t *val) +{ + enum lttng_event_field_value_status status; + + if (!field_val || !val) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + switch (field_val->type) { + case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT: + *val = container_of(field_val, + const struct lttng_event_field_value_int, + parent)->val; + break; + case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM: + *val = container_of( + container_of(field_val, + const struct lttng_event_field_value_enum, + parent), + const struct lttng_event_field_value_enum_int, + parent)->val; + break; + default: + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; + +end: + return status; +} + +enum lttng_event_field_value_status +lttng_event_field_value_real_get_value( + const struct lttng_event_field_value *field_val, double *val) +{ + enum lttng_event_field_value_status status; + + if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_REAL || + !val) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + *val = container_of(field_val, + const struct lttng_event_field_value_real, parent)->val; + status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; + +end: + return status; +} + +static +bool ist_enum_field_val(const struct lttng_event_field_value *field_val) +{ + return field_val->type == LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM || + field_val->type == LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM; +} + +enum lttng_event_field_value_status +lttng_event_field_value_enum_get_label_count( + const struct lttng_event_field_value *field_val, + unsigned int *count) +{ + enum lttng_event_field_value_status status; + + if (!field_val || !ist_enum_field_val(field_val) || !count) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + *count = (unsigned int) lttng_dynamic_pointer_array_get_count( + &container_of(field_val, + const struct lttng_event_field_value_enum, + parent)->labels); + status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; + +end: + return status; +} + +const char *lttng_event_field_value_enum_get_label_at_index( + const struct lttng_event_field_value *field_val, + unsigned int index) +{ + const char *ret; + const struct lttng_event_field_value_enum *enum_field_val; + + if (!field_val || !ist_enum_field_val(field_val)) { + ret = NULL; + goto end; + } + + enum_field_val = container_of(field_val, + const struct lttng_event_field_value_enum, parent); + + if (index >= lttng_dynamic_pointer_array_get_count(&enum_field_val->labels)) { + ret = NULL; + goto end; + } + + ret = lttng_dynamic_pointer_array_get_pointer(&enum_field_val->labels, + index); + +end: + return ret; +} + +const char *lttng_event_field_value_string_get_value( + const struct lttng_event_field_value *field_val) +{ + const char *ret; + + if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_STRING) { + ret = NULL; + goto end; + } + + ret = container_of(field_val, + const struct lttng_event_field_value_string, parent)->val; + +end: + return ret; +} + +enum lttng_event_field_value_status lttng_event_field_value_array_get_length( + const struct lttng_event_field_value *field_val, + unsigned int *length) +{ + enum lttng_event_field_value_status status; + + if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY || + !length) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + *length = (unsigned int) lttng_dynamic_pointer_array_get_count( + &container_of(field_val, + const struct lttng_event_field_value_array, + parent)->elems); + status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; + +end: + return status; +} + +enum lttng_event_field_value_status +lttng_event_field_value_array_get_element_at_index( + const struct lttng_event_field_value *field_val, + unsigned int index, + const struct lttng_event_field_value **elem_field_val) +{ + enum lttng_event_field_value_status status; + const struct lttng_event_field_value_array *array_field_val; + + if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY || + !elem_field_val) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + array_field_val = container_of(field_val, + const struct lttng_event_field_value_array, parent); + + if (index >= lttng_dynamic_pointer_array_get_count(&array_field_val->elems)) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + *elem_field_val = lttng_dynamic_pointer_array_get_pointer( + &array_field_val->elems, index); + if (*elem_field_val) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; + } else { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE; + } + +end: + return status; +} diff --git a/src/common/event-rule/event-rule.c b/src/common/event-rule/event-rule.c index cdf3e0ded..20731ec65 100644 --- a/src/common/event-rule/event-rule.c +++ b/src/common/event-rule/event-rule.c @@ -248,7 +248,7 @@ const char *lttng_event_rule_get_filter(const struct lttng_event_rule *rule) } LTTNG_HIDDEN -const struct lttng_filter_bytecode *lttng_event_rule_get_filter_bytecode( +const struct lttng_bytecode *lttng_event_rule_get_filter_bytecode( const struct lttng_event_rule *rule) { assert(rule->get_filter_bytecode); @@ -264,6 +264,36 @@ struct lttng_event_exclusion *lttng_event_rule_generate_exclusions( } LTTNG_HIDDEN +struct lttng_event *lttng_event_rule_generate_lttng_event( + const struct lttng_event_rule *rule) +{ + assert(rule->generate_lttng_event); + return rule->generate_lttng_event(rule); +} + +LTTNG_HIDDEN +bool lttng_event_rule_is_agent(const struct lttng_event_rule *rule) +{ + bool ret = false; + enum lttng_domain_type type = lttng_event_rule_get_domain_type(rule); + + switch (type) { + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: + ret = true; + break; + case LTTNG_DOMAIN_UST: + case LTTNG_DOMAIN_KERNEL: + ret = false; + break; + default: + assert(0); + }; + + return ret; +} + const char *lttng_event_rule_type_str(enum lttng_event_rule_type type) { switch (type) { diff --git a/src/common/event-rule/kprobe.c b/src/common/event-rule/kprobe.c index a5c93e653..415d3b77d 100644 --- a/src/common/event-rule/kprobe.c +++ b/src/common/event-rule/kprobe.c @@ -159,7 +159,7 @@ static const char *lttng_event_rule_kprobe_get_filter( return NULL; } -static const struct lttng_filter_bytecode * +static const struct lttng_bytecode * lttng_event_rule_kprobe_get_filter_bytecode(const struct lttng_event_rule *rule) { /* Not supported. */ diff --git a/src/common/event-rule/syscall.c b/src/common/event-rule/syscall.c index ef7ccd0f9..1097fca8f 100644 --- a/src/common/event-rule/syscall.c +++ b/src/common/event-rule/syscall.c @@ -144,7 +144,7 @@ static enum lttng_error_code lttng_event_rule_syscall_generate_filter_bytecode( struct lttng_event_rule_syscall *syscall; enum lttng_event_rule_status status; const char *filter; - struct lttng_filter_bytecode *bytecode = NULL; + struct lttng_bytecode *bytecode = NULL; assert(rule); @@ -201,7 +201,7 @@ static const char *lttng_event_rule_syscall_get_internal_filter( return syscall->internal_filter.filter; } -static const struct lttng_filter_bytecode * +static const struct lttng_bytecode * lttng_event_rule_syscall_get_internal_filter_bytecode( const struct lttng_event_rule *rule) { diff --git a/src/common/event-rule/tracepoint.c b/src/common/event-rule/tracepoint.c index 14e4c7b01..c3b9d0783 100644 --- a/src/common/event-rule/tracepoint.c +++ b/src/common/event-rule/tracepoint.c @@ -13,6 +13,7 @@ #include #include #include +#include #define IS_TRACEPOINT_EVENT_RULE(rule) \ (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_TRACEPOINT) @@ -365,7 +366,7 @@ lttng_event_rule_tracepoint_generate_filter_bytecode( enum lttng_domain_type domain_type; enum lttng_event_rule_status status; const char *filter; - struct lttng_filter_bytecode *bytecode = NULL; + struct lttng_bytecode *bytecode = NULL; assert(rule); @@ -457,7 +458,7 @@ static const char *lttng_event_rule_tracepoint_get_internal_filter( return tracepoint->internal_filter.filter; } -static const struct lttng_filter_bytecode * +static const struct lttng_bytecode * lttng_event_rule_tracepoint_get_internal_filter_bytecode( const struct lttng_event_rule *rule) { @@ -540,6 +541,35 @@ static void destroy_lttng_exclusions_element(void *ptr) free(ptr); } +static struct lttng_event *lttng_event_rule_tracepoint_generate_lttng_event( + const struct lttng_event_rule *rule) +{ + const struct lttng_event_rule_tracepoint *tracepoint; + struct lttng_event *local_event = NULL; + struct lttng_event *event = NULL; + + tracepoint = container_of( + rule, const struct lttng_event_rule_tracepoint, parent); + + local_event = zmalloc(sizeof(*local_event)); + if (!local_event) { + goto error; + } + + local_event->type = LTTNG_EVENT_TRACEPOINT; + (void) strncpy(local_event->name, tracepoint->pattern, + sizeof(local_event->name) - 1); + local_event->name[sizeof(local_event->name) - 1] = '\0'; + local_event->loglevel_type = tracepoint->loglevel.type; + local_event->loglevel = tracepoint->loglevel.value; + + event = local_event; + local_event = NULL; +error: + free(local_event); + return event; +} + struct lttng_event_rule *lttng_event_rule_tracepoint_create( enum lttng_domain_type domain_type) { @@ -569,6 +599,8 @@ struct lttng_event_rule *lttng_event_rule_tracepoint_create( lttng_event_rule_tracepoint_get_internal_filter_bytecode; tp_rule->parent.generate_exclusions = lttng_event_rule_tracepoint_generate_exclusions; + tp_rule->parent.generate_lttng_event = + lttng_event_rule_tracepoint_generate_lttng_event; tp_rule->domain = domain_type; tp_rule->loglevel.type = LTTNG_EVENT_LOGLEVEL_ALL; diff --git a/src/common/event-rule/uprobe.c b/src/common/event-rule/uprobe.c index 8d63975ab..a994b0e28 100644 --- a/src/common/event-rule/uprobe.c +++ b/src/common/event-rule/uprobe.c @@ -151,7 +151,7 @@ static const char *lttng_event_rule_uprobe_get_filter( return NULL; } -static const struct lttng_filter_bytecode * +static const struct lttng_bytecode * lttng_event_rule_uprobe_get_filter_bytecode(const struct lttng_event_rule *rule) { /* Unsupported. */ diff --git a/src/common/filter.c b/src/common/filter.c index fc16f1d52..5d333375d 100644 --- a/src/common/filter.c +++ b/src/common/filter.c @@ -16,7 +16,7 @@ struct bytecode_symbol_iterator { LTTNG_HIDDEN struct bytecode_symbol_iterator *bytecode_symbol_iterator_create( - struct lttng_filter_bytecode *bytecode) + struct lttng_bytecode *bytecode) { struct bytecode_symbol_iterator *it = NULL; diff --git a/src/common/filter.h b/src/common/filter.h index 2cbdd14a9..a03b1d9fe 100644 --- a/src/common/filter.h +++ b/src/common/filter.h @@ -18,7 +18,7 @@ struct bytecode_symbol_iterator; */ LTTNG_HIDDEN struct bytecode_symbol_iterator *bytecode_symbol_iterator_create( - struct lttng_filter_bytecode *bytecode); + struct lttng_bytecode *bytecode); /* * Advance iterator of one element. diff --git a/src/common/filter/Makefile.am b/src/common/filter/Makefile.am index cac4cb175..261acc086 100644 --- a/src/common/filter/Makefile.am +++ b/src/common/filter/Makefile.am @@ -19,7 +19,6 @@ libfilter_la_SOURCES = \ filter-visitor-ir-normalize-glob-patterns.c \ filter-visitor-generate-bytecode.c \ filter-ast.h \ - filter-bytecode.h \ filter-ir.h \ memstream.h libfilter_la_CFLAGS = -include filter-symbols.h $(AM_CFLAGS) @@ -63,4 +62,6 @@ all-local: filter-lexer.c endif # HAVE_FLEX filter_grammar_test_SOURCES = filter-grammar-test.c -filter_grammar_test_LDADD = libfilter.la +filter_grammar_test_LDADD = \ + libfilter.la \ + ../bytecode/libbytecode.la diff --git a/src/common/filter/filter-ast.h b/src/common/filter/filter-ast.h index 29fde10f8..93f9b9b25 100644 --- a/src/common/filter/filter-ast.h +++ b/src/common/filter/filter-ast.h @@ -17,6 +17,7 @@ */ #include +#include #include #define printf_debug(fmt, args...) \ @@ -157,8 +158,8 @@ struct filter_parser_ctx { struct filter_ast *ast; struct cds_list_head allocated_strings; struct ir_op *ir_root; - struct lttng_filter_bytecode_alloc *bytecode; - struct lttng_filter_bytecode_alloc *bytecode_reloc; + struct lttng_bytecode_alloc *bytecode; + struct lttng_bytecode_alloc *bytecode_reloc; }; struct filter_parser_ctx *filter_parser_ctx_alloc(FILE *input); diff --git a/src/common/filter/filter-bytecode.h b/src/common/filter/filter-bytecode.h deleted file mode 100644 index 053bb08bb..000000000 --- a/src/common/filter/filter-bytecode.h +++ /dev/null @@ -1,243 +0,0 @@ -#ifndef _FILTER_BYTECODE_H -#define _FILTER_BYTECODE_H - -/* - * filter-bytecode.h - * - * LTTng filter bytecode - * - * Copyright 2012 Mathieu Desnoyers - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include - -#include "filter-ast.h" - -/* - * offsets are absolute from start of bytecode. - */ - -struct field_ref { - /* Initially, symbol offset. After link, field offset. */ - uint16_t offset; -} LTTNG_PACKED; - -struct get_symbol { - /* Symbol offset. */ - uint16_t offset; -} LTTNG_PACKED; - -struct get_index_u16 { - uint16_t index; -} LTTNG_PACKED; - -struct get_index_u64 { - uint64_t index; -} LTTNG_PACKED; - -struct literal_numeric { - int64_t v; -} LTTNG_PACKED; - -struct literal_double { - double v; -} LTTNG_PACKED; - -struct literal_string { - char string[0]; -} LTTNG_PACKED; - -enum filter_op { - FILTER_OP_UNKNOWN = 0, - - FILTER_OP_RETURN = 1, - - /* binary */ - FILTER_OP_MUL = 2, - FILTER_OP_DIV = 3, - FILTER_OP_MOD = 4, - FILTER_OP_PLUS = 5, - FILTER_OP_MINUS = 6, - FILTER_OP_BIT_RSHIFT = 7, - FILTER_OP_BIT_LSHIFT = 8, - FILTER_OP_BIT_AND = 9, - FILTER_OP_BIT_OR = 10, - FILTER_OP_BIT_XOR = 11, - - /* binary comparators */ - FILTER_OP_EQ = 12, - FILTER_OP_NE = 13, - FILTER_OP_GT = 14, - FILTER_OP_LT = 15, - FILTER_OP_GE = 16, - FILTER_OP_LE = 17, - - /* string binary comparator: apply to */ - FILTER_OP_EQ_STRING = 18, - FILTER_OP_NE_STRING = 19, - FILTER_OP_GT_STRING = 20, - FILTER_OP_LT_STRING = 21, - FILTER_OP_GE_STRING = 22, - FILTER_OP_LE_STRING = 23, - - /* s64 binary comparator */ - FILTER_OP_EQ_S64 = 24, - FILTER_OP_NE_S64 = 25, - FILTER_OP_GT_S64 = 26, - FILTER_OP_LT_S64 = 27, - FILTER_OP_GE_S64 = 28, - FILTER_OP_LE_S64 = 29, - - /* double binary comparator */ - FILTER_OP_EQ_DOUBLE = 30, - FILTER_OP_NE_DOUBLE = 31, - FILTER_OP_GT_DOUBLE = 32, - FILTER_OP_LT_DOUBLE = 33, - FILTER_OP_GE_DOUBLE = 34, - FILTER_OP_LE_DOUBLE = 35, - - /* Mixed S64-double binary comparators */ - FILTER_OP_EQ_DOUBLE_S64 = 36, - FILTER_OP_NE_DOUBLE_S64 = 37, - FILTER_OP_GT_DOUBLE_S64 = 38, - FILTER_OP_LT_DOUBLE_S64 = 39, - FILTER_OP_GE_DOUBLE_S64 = 40, - FILTER_OP_LE_DOUBLE_S64 = 41, - - FILTER_OP_EQ_S64_DOUBLE = 42, - FILTER_OP_NE_S64_DOUBLE = 43, - FILTER_OP_GT_S64_DOUBLE = 44, - FILTER_OP_LT_S64_DOUBLE = 45, - FILTER_OP_GE_S64_DOUBLE = 46, - FILTER_OP_LE_S64_DOUBLE = 47, - - /* unary */ - FILTER_OP_UNARY_PLUS = 48, - FILTER_OP_UNARY_MINUS = 49, - FILTER_OP_UNARY_NOT = 50, - FILTER_OP_UNARY_PLUS_S64 = 51, - FILTER_OP_UNARY_MINUS_S64 = 52, - FILTER_OP_UNARY_NOT_S64 = 53, - FILTER_OP_UNARY_PLUS_DOUBLE = 54, - FILTER_OP_UNARY_MINUS_DOUBLE = 55, - FILTER_OP_UNARY_NOT_DOUBLE = 56, - - /* logical */ - FILTER_OP_AND = 57, - FILTER_OP_OR = 58, - - /* load field ref */ - FILTER_OP_LOAD_FIELD_REF = 59, - FILTER_OP_LOAD_FIELD_REF_STRING = 60, - FILTER_OP_LOAD_FIELD_REF_SEQUENCE = 61, - FILTER_OP_LOAD_FIELD_REF_S64 = 62, - FILTER_OP_LOAD_FIELD_REF_DOUBLE = 63, - - /* load immediate from operand */ - FILTER_OP_LOAD_STRING = 64, - FILTER_OP_LOAD_S64 = 65, - FILTER_OP_LOAD_DOUBLE = 66, - - /* cast */ - FILTER_OP_CAST_TO_S64 = 67, - FILTER_OP_CAST_DOUBLE_TO_S64 = 68, - FILTER_OP_CAST_NOP = 69, - - /* get context ref */ - FILTER_OP_GET_CONTEXT_REF = 70, - FILTER_OP_GET_CONTEXT_REF_STRING = 71, - FILTER_OP_GET_CONTEXT_REF_S64 = 72, - FILTER_OP_GET_CONTEXT_REF_DOUBLE = 73, - - /* load userspace field ref */ - FILTER_OP_LOAD_FIELD_REF_USER_STRING = 74, - FILTER_OP_LOAD_FIELD_REF_USER_SEQUENCE = 75, - - /* - * load immediate star globbing pattern (literal string) - * from immediate - */ - FILTER_OP_LOAD_STAR_GLOB_STRING = 76, - - /* globbing pattern binary operator: apply to */ - FILTER_OP_EQ_STAR_GLOB_STRING = 77, - FILTER_OP_NE_STAR_GLOB_STRING = 78, - - /* - * Instructions for recursive traversal through composed types. - */ - FILTER_OP_GET_CONTEXT_ROOT = 79, - FILTER_OP_GET_APP_CONTEXT_ROOT = 80, - FILTER_OP_GET_PAYLOAD_ROOT = 81, - - FILTER_OP_GET_SYMBOL = 82, - FILTER_OP_GET_SYMBOL_FIELD = 83, - FILTER_OP_GET_INDEX_U16 = 84, - FILTER_OP_GET_INDEX_U64 = 85, - - FILTER_OP_LOAD_FIELD = 86, - FILTER_OP_LOAD_FIELD_S8 = 87, - FILTER_OP_LOAD_FIELD_S16 = 88, - FILTER_OP_LOAD_FIELD_S32 = 89, - FILTER_OP_LOAD_FIELD_S64 = 90, - FILTER_OP_LOAD_FIELD_U8 = 91, - FILTER_OP_LOAD_FIELD_U16 = 92, - FILTER_OP_LOAD_FIELD_U32 = 93, - FILTER_OP_LOAD_FIELD_U64 = 94, - FILTER_OP_LOAD_FIELD_STRING = 95, - FILTER_OP_LOAD_FIELD_SEQUENCE = 96, - FILTER_OP_LOAD_FIELD_DOUBLE = 97, - - FILTER_OP_UNARY_BIT_NOT = 98, - - FILTER_OP_RETURN_S64 = 99, - - NR_FILTER_OPS, -}; - -typedef uint8_t filter_opcode_t; - -struct load_op { - filter_opcode_t op; - char data[0]; - /* data to load. Size known by enum filter_opcode and null-term char. */ -} LTTNG_PACKED; - -struct binary_op { - filter_opcode_t op; -} LTTNG_PACKED; - -struct unary_op { - filter_opcode_t op; -} LTTNG_PACKED; - -/* skip_offset is absolute from start of bytecode */ -struct logical_op { - filter_opcode_t op; - uint16_t skip_offset; /* bytecode insn, if skip second test */ -} LTTNG_PACKED; - -struct cast_op { - filter_opcode_t op; -} LTTNG_PACKED; - -struct return_op { - filter_opcode_t op; -} LTTNG_PACKED; - -struct lttng_filter_bytecode_alloc { - uint32_t alloc_len; - struct lttng_filter_bytecode b; -}; - -static inline -unsigned int bytecode_get_len(struct lttng_filter_bytecode *bytecode) -{ - return bytecode->len; -} - -#endif /* _FILTER_BYTECODE_H */ diff --git a/src/common/filter/filter-grammar-test.c b/src/common/filter/filter-grammar-test.c index 70656e1a8..9029fa65e 100644 --- a/src/common/filter/filter-grammar-test.c +++ b/src/common/filter/filter-grammar-test.c @@ -16,9 +16,9 @@ #include #include #include +#include "common/bytecode/bytecode.h" #include "filter-ast.h" #include "filter-parser.h" -#include "filter-bytecode.h" int main(int argc, char **argv) { diff --git a/src/common/filter/filter-ir.h b/src/common/filter/filter-ir.h index d62c0ee0c..5775e8004 100644 --- a/src/common/filter/filter-ir.h +++ b/src/common/filter/filter-ir.h @@ -31,6 +31,29 @@ enum ir_data_type { IR_DATA_EXPRESSION, }; +static inline +const char *ir_data_type_str(enum ir_data_type type) +{ + switch (type) { + case IR_DATA_UNKNOWN: + return "IR_DATA_UNKNOWN"; + case IR_DATA_STRING: + return "IR_DATA_STRING"; + case IR_DATA_NUMERIC: + return "IR_DATA_NUMERIC"; + case IR_DATA_FLOAT: + return "IR_DATA_FLOAT"; + case IR_DATA_FIELD_REF: + return "IR_DATA_FIELD_REF"; + case IR_DATA_GET_CONTEXT_REF: + return "IR_DATA_GET_CONTEXT_REF"; + case IR_DATA_EXPRESSION: + return "IR_DATA_EXPRESSION"; + default: + abort(); + } +} + enum ir_op_type { IR_OP_UNKNOWN = 0, IR_OP_ROOT, @@ -40,6 +63,27 @@ enum ir_op_type { IR_OP_LOGICAL, }; +static inline +const char *ir_op_type_str(enum ir_op_type type) +{ + switch (type) { + case IR_OP_UNKNOWN: + return "IR_OP_UNKNOWN"; + case IR_OP_ROOT: + return "IR_OP_ROOT"; + case IR_OP_LOAD: + return "IR_OP_LOAD"; + case IR_OP_UNARY: + return "IR_OP_UNARY"; + case IR_OP_BINARY: + return "IR_OP_BINARY"; + case IR_OP_LOGICAL: + return "IR_OP_LOGICAL"; + default: + abort(); + } +} + /* left or right child */ enum ir_side { IR_SIDE_UNKNOWN = 0, @@ -71,6 +115,27 @@ enum ir_load_expression_type { IR_LOAD_EXPRESSION_LOAD_FIELD, }; +static inline +const char *ir_load_expression_type_str(enum ir_load_expression_type type) +{ + switch (type) { + case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT: + return "IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT"; + case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT: + return "IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT"; + case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT: + return "IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT"; + case IR_LOAD_EXPRESSION_GET_SYMBOL: + return "IR_LOAD_EXPRESSION_GET_SYMBOL"; + case IR_LOAD_EXPRESSION_GET_INDEX: + return "IR_LOAD_EXPRESSION_GET_INDEX"; + case IR_LOAD_EXPRESSION_LOAD_FIELD: + return "IR_LOAD_EXPRESSION_LOAD_FIELD"; + default: + abort(); + } +} + struct ir_load_expression_op { struct ir_load_expression_op *next; enum ir_load_expression_type type; diff --git a/src/common/filter/filter-parser.y b/src/common/filter/filter-parser.y index 245d83c26..dc94a9813 100644 --- a/src/common/filter/filter-parser.y +++ b/src/common/filter/filter-parser.y @@ -18,9 +18,9 @@ #include #include #include +#include "common/bytecode/bytecode.h" #include "filter-ast.h" #include "filter-parser.h" -#include "filter-bytecode.h" #include "memstream.h" #include diff --git a/src/common/filter/filter-visitor-generate-bytecode.c b/src/common/filter/filter-visitor-generate-bytecode.c index 699273c3d..458b9d042 100644 --- a/src/common/filter/filter-visitor-generate-bytecode.c +++ b/src/common/filter/filter-visitor-generate-bytecode.c @@ -12,114 +12,24 @@ #include #include #include -#include -#include -#include "filter-bytecode.h" -#include "filter-ir.h" +#include "common/align.h" +#include "common/bytecode/bytecode.h" +#include "common/compat/string.h" +#include "common/macros.h" #include "filter-ast.h" - -#include +#include "filter-ir.h" #ifndef max_t #define max_t(type, a, b) ((type) ((a) > (b) ? (a) : (b))) #endif -#define INIT_ALLOC_SIZE 4 - static int recursive_visit_gen_bytecode(struct filter_parser_ctx *ctx, struct ir_op *node); -static inline int get_count_order(unsigned int count) -{ - int order; - - order = lttng_fls(count) - 1; - if (count & (count - 1)) - order++; - return order; -} - -static -int bytecode_init(struct lttng_filter_bytecode_alloc **fb) -{ - uint32_t alloc_len; - - alloc_len = sizeof(struct lttng_filter_bytecode_alloc) + INIT_ALLOC_SIZE; - *fb = calloc(alloc_len, 1); - if (!*fb) { - return -ENOMEM; - } else { - (*fb)->alloc_len = alloc_len; - return 0; - } -} - static -int32_t bytecode_reserve(struct lttng_filter_bytecode_alloc **fb, uint32_t align, uint32_t len) -{ - int32_t ret; - uint32_t padding = offset_align((*fb)->b.len, align); - uint32_t new_len = (*fb)->b.len + padding + len; - uint32_t new_alloc_len = sizeof(struct lttng_filter_bytecode_alloc) + new_len; - uint32_t old_alloc_len = (*fb)->alloc_len; - - if (new_len > LTTNG_FILTER_MAX_LEN) - return -EINVAL; - - if (new_alloc_len > old_alloc_len) { - struct lttng_filter_bytecode_alloc *newptr; - - new_alloc_len = - max_t(uint32_t, 1U << get_count_order(new_alloc_len), old_alloc_len << 1); - newptr = realloc(*fb, new_alloc_len); - if (!newptr) - return -ENOMEM; - *fb = newptr; - /* We zero directly the memory from start of allocation. */ - memset(&((char *) *fb)[old_alloc_len], 0, new_alloc_len - old_alloc_len); - (*fb)->alloc_len = new_alloc_len; - } - (*fb)->b.len += padding; - ret = (*fb)->b.len; - (*fb)->b.len += len; - return ret; -} - -static -int bytecode_push(struct lttng_filter_bytecode_alloc **fb, const void *data, - uint32_t align, uint32_t len) -{ - int32_t offset; - - offset = bytecode_reserve(fb, align, len); - if (offset < 0) - return offset; - memcpy(&(*fb)->b.data[offset], data, len); - return 0; -} - -static -int bytecode_push_logical(struct lttng_filter_bytecode_alloc **fb, - struct logical_op *data, - uint32_t align, uint32_t len, - uint16_t *skip_offset) -{ - int32_t offset; - - offset = bytecode_reserve(fb, align, len); - if (offset < 0) - return offset; - memcpy(&(*fb)->b.data[offset], data, len); - *skip_offset = - (void *) &((struct logical_op *) &(*fb)->b.data[offset])->skip_offset - - (void *) &(*fb)->b.data[0]; - return 0; -} - -static -int bytecode_patch(struct lttng_filter_bytecode_alloc **fb, +int bytecode_patch(struct lttng_bytecode_alloc **fb, const void *data, uint16_t offset, uint32_t len) @@ -143,7 +53,7 @@ int visit_node_root(struct filter_parser_ctx *ctx, struct ir_op *node) return ret; /* Generate end of bytecode instruction */ - insn.op = FILTER_OP_RETURN; + insn.op = BYTECODE_OP_RETURN; return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn)); } @@ -175,7 +85,7 @@ int append_str(char **s, const char *append) */ static int load_expression_legacy_match(const struct ir_load_expression *exp, - enum filter_op *op_type, + enum bytecode_op *op_type, char **symbol) { const struct ir_load_expression_op *op; @@ -184,21 +94,21 @@ int load_expression_legacy_match(const struct ir_load_expression *exp, op = exp->child; switch (op->type) { case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT: - *op_type = FILTER_OP_GET_CONTEXT_REF; + *op_type = BYTECODE_OP_GET_CONTEXT_REF; if (append_str(symbol, "$ctx.")) { return -ENOMEM; } need_dot = false; break; case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT: - *op_type = FILTER_OP_GET_CONTEXT_REF; + *op_type = BYTECODE_OP_GET_CONTEXT_REF; if (append_str(symbol, "$app.")) { return -ENOMEM; } need_dot = false; break; case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT: - *op_type = FILTER_OP_LOAD_FIELD_REF; + *op_type = BYTECODE_OP_LOAD_FIELD_REF; need_dot = false; break; @@ -250,7 +160,7 @@ int visit_node_load_expression_legacy(struct filter_parser_ctx *ctx, struct field_ref ref_offset; uint32_t reloc_offset_u32; uint16_t reloc_offset; - enum filter_op op_type; + enum bytecode_op op_type; char *symbol = NULL; int ret; @@ -328,100 +238,37 @@ int visit_node_load_expression(struct filter_parser_ctx *ctx, switch (op->type) { case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT: { - struct load_op *insn; - uint32_t insn_len = sizeof(struct load_op); - int ret; - - insn = calloc(insn_len, 1); - if (!insn) - return -ENOMEM; - insn->op = FILTER_OP_GET_CONTEXT_ROOT; - ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len); - free(insn); + int ret = bytecode_push_get_context_root(&ctx->bytecode); if (ret) { return ret; } + break; } case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT: { - struct load_op *insn; - uint32_t insn_len = sizeof(struct load_op); - int ret; - - insn = calloc(insn_len, 1); - if (!insn) - return -ENOMEM; - insn->op = FILTER_OP_GET_APP_CONTEXT_ROOT; - ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len); - free(insn); + int ret = bytecode_push_get_app_context_root(&ctx->bytecode); if (ret) { return ret; } + break; } case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT: { - struct load_op *insn; - uint32_t insn_len = sizeof(struct load_op); - int ret; - - insn = calloc(insn_len, 1); - if (!insn) - return -ENOMEM; - insn->op = FILTER_OP_GET_PAYLOAD_ROOT; - ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len); - free(insn); + int ret = bytecode_push_get_payload_root(&ctx->bytecode); if (ret) { return ret; } + break; } case IR_LOAD_EXPRESSION_GET_SYMBOL: { - struct load_op *insn; - uint32_t insn_len = sizeof(struct load_op) - + sizeof(struct get_symbol); - struct get_symbol symbol_offset; - uint32_t reloc_offset_u32; - uint16_t reloc_offset; - uint32_t bytecode_reloc_offset_u32; - int ret; - - insn = calloc(insn_len, 1); - if (!insn) - return -ENOMEM; - insn->op = FILTER_OP_GET_SYMBOL; - bytecode_reloc_offset_u32 = - bytecode_get_len(&ctx->bytecode_reloc->b) - + sizeof(reloc_offset); - symbol_offset.offset = - (uint16_t) bytecode_reloc_offset_u32; - memcpy(insn->data, &symbol_offset, - sizeof(symbol_offset)); - /* reloc_offset points to struct load_op */ - reloc_offset_u32 = bytecode_get_len(&ctx->bytecode->b); - if (reloc_offset_u32 > LTTNG_FILTER_MAX_LEN - 1) { - free(insn); - return -EINVAL; - } - reloc_offset = (uint16_t) reloc_offset_u32; - ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len); - if (ret) { - free(insn); - return ret; - } - /* append reloc */ - ret = bytecode_push(&ctx->bytecode_reloc, &reloc_offset, - 1, sizeof(reloc_offset)); - if (ret) { - free(insn); - return ret; - } - ret = bytecode_push(&ctx->bytecode_reloc, - op->u.symbol, - 1, strlen(op->u.symbol) + 1); - free(insn); + int ret = bytecode_push_get_symbol( + &ctx->bytecode, + &ctx->bytecode_reloc, + op->u.symbol); if (ret) { return ret; } @@ -429,20 +276,7 @@ int visit_node_load_expression(struct filter_parser_ctx *ctx, } case IR_LOAD_EXPRESSION_GET_INDEX: { - struct load_op *insn; - uint32_t insn_len = sizeof(struct load_op) - + sizeof(struct get_index_u64); - struct get_index_u64 index; - int ret; - - insn = calloc(insn_len, 1); - if (!insn) - return -ENOMEM; - insn->op = FILTER_OP_GET_INDEX_U64; - index.index = op->u.index; - memcpy(insn->data, &index, sizeof(index)); - ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len); - free(insn); + int ret = bytecode_push_get_index_u64(&ctx->bytecode, op->u.index); if (ret) { return ret; } @@ -457,7 +291,7 @@ int visit_node_load_expression(struct filter_parser_ctx *ctx, insn = calloc(insn_len, 1); if (!insn) return -ENOMEM; - insn->op = FILTER_OP_LOAD_FIELD; + insn->op = BYTECODE_OP_LOAD_FIELD; ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len); free(insn); if (ret) { @@ -500,7 +334,7 @@ int visit_node_load(struct filter_parser_ctx *ctx, struct ir_op *node) * that the appropriate matching function can be * called. Also, see comment below. */ - insn->op = FILTER_OP_LOAD_STAR_GLOB_STRING; + insn->op = BYTECODE_OP_LOAD_STAR_GLOB_STRING; break; default: /* @@ -513,7 +347,7 @@ int visit_node_load(struct filter_parser_ctx *ctx, struct ir_op *node) * can be anywhere in the string) is a special * case. */ - insn->op = FILTER_OP_LOAD_STRING; + insn->op = BYTECODE_OP_LOAD_STRING; break; } @@ -531,7 +365,7 @@ int visit_node_load(struct filter_parser_ctx *ctx, struct ir_op *node) insn = calloc(insn_len, 1); if (!insn) return -ENOMEM; - insn->op = FILTER_OP_LOAD_S64; + insn->op = BYTECODE_OP_LOAD_S64; memcpy(insn->data, &node->u.load.u.num, sizeof(int64_t)); ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len); free(insn); @@ -546,7 +380,7 @@ int visit_node_load(struct filter_parser_ctx *ctx, struct ir_op *node) insn = calloc(insn_len, 1); if (!insn) return -ENOMEM; - insn->op = FILTER_OP_LOAD_DOUBLE; + insn->op = BYTECODE_OP_LOAD_DOUBLE; memcpy(insn->data, &node->u.load.u.flt, sizeof(double)); ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len); free(insn); @@ -579,13 +413,13 @@ int visit_node_unary(struct filter_parser_ctx *ctx, struct ir_op *node) /* Nothing to do. */ return 0; case AST_UNARY_MINUS: - insn.op = FILTER_OP_UNARY_MINUS; + insn.op = BYTECODE_OP_UNARY_MINUS; return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn)); case AST_UNARY_NOT: - insn.op = FILTER_OP_UNARY_NOT; + insn.op = BYTECODE_OP_UNARY_NOT; return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn)); case AST_UNARY_BIT_NOT: - insn.op = FILTER_OP_UNARY_BIT_NOT; + insn.op = BYTECODE_OP_UNARY_BIT_NOT; return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn)); } } @@ -622,53 +456,53 @@ int visit_node_binary(struct filter_parser_ctx *ctx, struct ir_op *node) return -EINVAL; case AST_OP_MUL: - insn.op = FILTER_OP_MUL; + insn.op = BYTECODE_OP_MUL; break; case AST_OP_DIV: - insn.op = FILTER_OP_DIV; + insn.op = BYTECODE_OP_DIV; break; case AST_OP_MOD: - insn.op = FILTER_OP_MOD; + insn.op = BYTECODE_OP_MOD; break; case AST_OP_PLUS: - insn.op = FILTER_OP_PLUS; + insn.op = BYTECODE_OP_PLUS; break; case AST_OP_MINUS: - insn.op = FILTER_OP_MINUS; + insn.op = BYTECODE_OP_MINUS; break; case AST_OP_BIT_RSHIFT: - insn.op = FILTER_OP_BIT_RSHIFT; + insn.op = BYTECODE_OP_BIT_RSHIFT; break; case AST_OP_BIT_LSHIFT: - insn.op = FILTER_OP_BIT_LSHIFT; + insn.op = BYTECODE_OP_BIT_LSHIFT; break; case AST_OP_BIT_AND: - insn.op = FILTER_OP_BIT_AND; + insn.op = BYTECODE_OP_BIT_AND; break; case AST_OP_BIT_OR: - insn.op = FILTER_OP_BIT_OR; + insn.op = BYTECODE_OP_BIT_OR; break; case AST_OP_BIT_XOR: - insn.op = FILTER_OP_BIT_XOR; + insn.op = BYTECODE_OP_BIT_XOR; break; case AST_OP_EQ: - insn.op = FILTER_OP_EQ; + insn.op = BYTECODE_OP_EQ; break; case AST_OP_NE: - insn.op = FILTER_OP_NE; + insn.op = BYTECODE_OP_NE; break; case AST_OP_GT: - insn.op = FILTER_OP_GT; + insn.op = BYTECODE_OP_GT; break; case AST_OP_LT: - insn.op = FILTER_OP_LT; + insn.op = BYTECODE_OP_LT; break; case AST_OP_GE: - insn.op = FILTER_OP_GE; + insn.op = BYTECODE_OP_GE; break; case AST_OP_LE: - insn.op = FILTER_OP_LE; + insn.op = BYTECODE_OP_LE; break; } return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn)); @@ -699,9 +533,9 @@ int visit_node_logical(struct filter_parser_ctx *ctx, struct ir_op *node) if (node->u.binary.left->data_type == IR_DATA_FIELD_REF || node->u.binary.left->data_type == IR_DATA_GET_CONTEXT_REF || node->u.binary.left->data_type == IR_DATA_EXPRESSION) { - cast_insn.op = FILTER_OP_CAST_TO_S64; + cast_insn.op = BYTECODE_OP_CAST_TO_S64; } else { - cast_insn.op = FILTER_OP_CAST_DOUBLE_TO_S64; + cast_insn.op = BYTECODE_OP_CAST_DOUBLE_TO_S64; } ret = bytecode_push(&ctx->bytecode, &cast_insn, 1, sizeof(cast_insn)); @@ -715,10 +549,10 @@ int visit_node_logical(struct filter_parser_ctx *ctx, struct ir_op *node) return -EINVAL; case AST_OP_AND: - insn.op = FILTER_OP_AND; + insn.op = BYTECODE_OP_AND; break; case AST_OP_OR: - insn.op = FILTER_OP_OR; + insn.op = BYTECODE_OP_OR; break; } insn.skip_offset = (uint16_t) -1UL; /* Temporary */ @@ -740,9 +574,9 @@ int visit_node_logical(struct filter_parser_ctx *ctx, struct ir_op *node) if (node->u.binary.right->data_type == IR_DATA_FIELD_REF || node->u.binary.right->data_type == IR_DATA_GET_CONTEXT_REF || node->u.binary.right->data_type == IR_DATA_EXPRESSION) { - cast_insn.op = FILTER_OP_CAST_TO_S64; + cast_insn.op = BYTECODE_OP_CAST_TO_S64; } else { - cast_insn.op = FILTER_OP_CAST_DOUBLE_TO_S64; + cast_insn.op = BYTECODE_OP_CAST_DOUBLE_TO_S64; } ret = bytecode_push(&ctx->bytecode, &cast_insn, 1, sizeof(cast_insn)); 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-consumer/kernel-consumer.c b/src/common/kernel-consumer/kernel-consumer.c index de1a14c21..b9d843714 100644 --- a/src/common/kernel-consumer/kernel-consumer.c +++ b/src/common/kernel-consumer/kernel-consumer.c @@ -1207,8 +1207,8 @@ error_rotate_channel: case LTTNG_CONSUMER_CREATE_TRACE_CHUNK: { const struct lttng_credentials credentials = { - .uid = msg.u.create_trace_chunk.credentials.value.uid, - .gid = msg.u.create_trace_chunk.credentials.value.gid, + .uid = LTTNG_OPTIONAL_INIT_VALUE(msg.u.create_trace_chunk.credentials.value.uid), + .gid = LTTNG_OPTIONAL_INIT_VALUE(msg.u.create_trace_chunk.credentials.value.gid), }; const bool is_local_trace = !msg.u.create_trace_chunk.relayd_id.is_set; diff --git a/src/common/kernel-ctl/kernel-ctl.c b/src/common/kernel-ctl/kernel-ctl.c index 10e281d69..386685c2a 100644 --- a/src/common/kernel-ctl/kernel-ctl.c +++ b/src/common/kernel-ctl/kernel-ctl.c @@ -418,7 +418,51 @@ int kernctl_stop_session(int fd) LTTNG_KERNEL_SESSION_STOP); } -int kernctl_filter(int fd, struct lttng_filter_bytecode *filter) +int kernctl_create_trigger_group(int fd) +{ + return LTTNG_IOCTL_NO_CHECK(fd, LTTNG_KERNEL_TRIGGER_GROUP_CREATE); +} + +int kernctl_create_trigger_group_notification_fd(int group_fd) +{ + return LTTNG_IOCTL_NO_CHECK(group_fd, LTTNG_KERNEL_TRIGGER_GROUP_NOTIFICATION_FD); +} + +int kernctl_create_trigger_group_error_counter(int group_fd, struct lttng_kernel_counter_conf *error_counter_conf) +{ + return LTTNG_IOCTL_NO_CHECK(group_fd, LTTNG_KERNEL_COUNTER, error_counter_conf); +} + +int kernctl_counter_get_value(int counter_fd, struct lttng_kernel_counter_value *value) +{ + return LTTNG_IOCTL_NO_CHECK(counter_fd, LTTNG_KERNEL_COUNTER_VALUE, value); +} + +int kernctl_create_trigger(int group_fd, struct lttng_kernel_trigger *trigger) +{ + return LTTNG_IOCTL_NO_CHECK(group_fd, LTTNG_KERNEL_TRIGGER_CREATE, trigger); +} + +int kernctl_capture(int fd, const struct lttng_bytecode *capture) +{ + struct lttng_kernel_capture_bytecode *kb; + uint32_t len; + int ret; + + /* Translate bytecode to kernel bytecode */ + kb = zmalloc(sizeof(*kb) + capture->len); + if (!kb) + return -ENOMEM; + kb->len = len = capture->len; + kb->reloc_offset = capture->reloc_table_offset; + kb->seqnum = capture->seqnum; + memcpy(kb->data, capture->data, len); + ret = LTTNG_IOCTL_CHECK(fd, LTTNG_KERNEL_CAPTURE, kb); + free(kb); + return ret; +} + +int kernctl_filter(int fd, const struct lttng_bytecode *filter) { struct lttng_kernel_filter_bytecode *kb; uint32_t len; diff --git a/src/common/kernel-ctl/kernel-ctl.h b/src/common/kernel-ctl/kernel-ctl.h index 49925ea6c..eab697730 100644 --- a/src/common/kernel-ctl/kernel-ctl.h +++ b/src/common/kernel-ctl/kernel-ctl.h @@ -28,9 +28,20 @@ int kernctl_disable(int fd); int kernctl_start_session(int fd); int kernctl_stop_session(int fd); +int kernctl_create_trigger_group(int fd); + +/* Apply on trigger_group FD*/ +int kernctl_create_trigger_group_notification_fd(int fd); +int kernctl_create_trigger_group_error_counter(int fd, + struct lttng_kernel_counter_conf *error_counter_conf); +int kernctl_create_trigger(int fd, struct lttng_kernel_trigger *trigger); + +int kernctl_counter_get_value(int counter_fd, struct lttng_kernel_counter_value *value); + /* Apply on event FD */ -int kernctl_filter(int fd, struct lttng_filter_bytecode *filter); +int kernctl_filter(int fd, const struct lttng_bytecode *filter); int kernctl_add_callsite(int fd, struct lttng_kernel_event_callsite *callsite); +int kernctl_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 ed6555e10..4cffd9179 100644 --- a/src/common/kernel-ctl/kernel-ioctl.h +++ b/src/common/kernel-ctl/kernel-ioctl.h @@ -118,6 +118,19 @@ #define LTTNG_KERNEL_TRACER_ABI_VERSION \ _IOR(0xF6, 0x4B, struct lttng_kernel_tracer_abi_version) +#define LTTNG_KERNEL_TRIGGER_GROUP_CREATE _IO(0xF6, 0x4C) + +/* Trigger group file descriptor ioctl */ +#define LTTNG_KERNEL_TRIGGER_GROUP_NOTIFICATION_FD _IO(0xF6, 0x30) +#define LTTNG_KERNEL_TRIGGER_CREATE \ + _IOW(0xF6, 0x31, struct lttng_kernel_trigger) +#define LTTNG_KERNEL_CAPTURE _IO(0xF6, 0x32) + +#define LTTNG_KERNEL_COUNTER \ + _IOW(0xF6, 0x33, struct lttng_kernel_counter_conf) +#define LTTNG_KERNEL_COUNTER_VALUE \ + _IOWR(0xF6, 0x34, struct lttng_kernel_counter_value) + /* Session FD ioctl */ #define LTTNG_KERNEL_METADATA \ _IOW(0xF6, 0x54, struct lttng_kernel_channel) diff --git a/src/common/lttng-kernel.h b/src/common/lttng-kernel.h index fc2a1e259..cdf6f041b 100644 --- a/src/common/lttng-kernel.h +++ b/src/common/lttng-kernel.h @@ -151,6 +151,72 @@ struct lttng_kernel_event { } u; } LTTNG_PACKED; +#define LTTNG_KERNEL_TRIGGER_PADDING1 16 +#define LTTNG_KERNEL_TRIGGER_PADDING2 LTTNG_KERNEL_SYM_NAME_LEN + 32 +struct lttng_kernel_trigger { + uint64_t id; + uint64_t error_counter_idx; + char name[LTTNG_KERNEL_SYM_NAME_LEN]; /* event name */ + enum lttng_kernel_instrumentation instrumentation; + char padding[LTTNG_KERNEL_TRIGGER_PADDING1]; + + /* Per instrumentation type configuration */ + union { + struct lttng_kernel_kretprobe kretprobe; + struct lttng_kernel_kprobe kprobe; + struct lttng_kernel_uprobe uprobe; + struct lttng_kernel_function ftrace; + char padding[LTTNG_KERNEL_TRIGGER_PADDING2]; + } u; +} LTTNG_PACKED; + +enum lttng_kernel_counter_arithmetic { + LTTNG_KERNEL_COUNTER_ARITHMETIC_MODULAR = 1, +}; + +enum lttng_kernel_counter_bitness { + LTTNG_KERNEL_COUNTER_BITNESS_32BITS = 1, + LTTNG_KERNEL_COUNTER_BITNESS_64BITS = 2, +}; + +struct lttng_kernel_counter_dimension { + uint64_t size; + uint64_t underflow_index; + uint64_t overflow_index; + uint8_t has_underflow; + uint8_t has_overflow; +} LTTNG_PACKED; + +#define LTTNG_KERNEL_COUNTER_DIMENSION_MAX 8 +struct lttng_kernel_counter_conf { + uint32_t arithmetic; /* enum lttng_kernel_counter_arithmetic */ + uint32_t bitness; /* enum lttng_kernel_counter_bitness */ + uint32_t number_dimensions; + int64_t global_sum_step; + struct lttng_kernel_counter_dimension dimensions[LTTNG_KERNEL_COUNTER_DIMENSION_MAX]; +} LTTNG_PACKED; + +struct lttng_kernel_counter_value { + uint32_t number_dimensions; + uint64_t dimension_indexes[LTTNG_KERNEL_COUNTER_DIMENSION_MAX]; + int64_t value; +} LTTNG_PACKED; + +#define LTTNG_KERNEL_TRIGGER_NOTIFICATION_PADDING 32 +struct lttng_kernel_trigger_notification { + uint64_t id; + uint16_t capture_buf_size; + char padding[LTTNG_KERNEL_TRIGGER_NOTIFICATION_PADDING]; +} LTTNG_PACKED; + +#define LTTNG_KERNEL_CAPTURE_BYTECODE_MAX_LEN 65536 +struct lttng_kernel_capture_bytecode { + uint32_t len; + uint32_t reloc_offset; + uint64_t seqnum; + char data[0]; +} LTTNG_PACKED; + struct lttng_kernel_tracer_version { uint32_t major; uint32_t minor; diff --git a/src/common/notification.c b/src/common/notification.c index 2544d042f..27ba0e541 100644 --- a/src/common/notification.c +++ b/src/common/notification.c @@ -113,7 +113,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/optional.h b/src/common/optional.h index faa64bf60..ca7d1f043 100644 --- a/src/common/optional.h +++ b/src/common/optional.h @@ -79,12 +79,17 @@ }) /* - * Initialize an optional field. + * Initialize an optional field as unset. * * The wrapped field is set to the value it would gave if it had static storage * duration. */ -#define LTTNG_OPTIONAL_INIT { .is_set = 0 } +#define LTTNG_OPTIONAL_INIT_UNSET { .is_set = 0 } + +/* + * Initialize an optional field as 'set' with a given value. + */ +#define LTTNG_OPTIONAL_INIT_VALUE(val) { .value = val, .is_set = 1 } /* Set the value of an optional field. */ #define LTTNG_OPTIONAL_SET(field_ptr, val) \ diff --git a/src/common/runas.c b/src/common/runas.c index 3c4c2ae0b..731b45008 100644 --- a/src/common/runas.c +++ b/src/common/runas.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -37,7 +38,6 @@ #include #include -#include #include "runas.h" @@ -1801,13 +1801,13 @@ LTTNG_HIDDEN int run_as_generate_filter_bytecode(const char *filter_expression, uid_t uid, gid_t gid, - struct lttng_filter_bytecode **bytecode) + struct lttng_bytecode **bytecode) { int ret; struct run_as_data data = {}; struct run_as_ret run_as_ret = {}; - const struct lttng_filter_bytecode *view_bytecode = NULL; - struct lttng_filter_bytecode *local_bytecode = NULL; + const struct lttng_bytecode *view_bytecode = NULL; + struct lttng_bytecode *local_bytecode = NULL; DBG3("generate_filter_bytecode() from expression=\"%s\" for uid %d and gid %d", filter_expression, (int) uid, (int) gid); @@ -1825,7 +1825,7 @@ int run_as_generate_filter_bytecode(const char *filter_expression, goto error; } - view_bytecode = (const struct lttng_filter_bytecode *) run_as_ret.u.generate_filter_bytecode.bytecode; + view_bytecode = (const struct lttng_bytecode *) run_as_ret.u.generate_filter_bytecode.bytecode; local_bytecode = zmalloc(sizeof(*local_bytecode) + view_bytecode->len); if (!local_bytecode) { diff --git a/src/common/runas.h b/src/common/runas.h index 398221708..06e41cd01 100644 --- a/src/common/runas.h +++ b/src/common/runas.h @@ -74,7 +74,7 @@ LTTNG_HIDDEN int run_as_generate_filter_bytecode(const char *filter_expression, uid_t uid, gid_t gid, - struct lttng_filter_bytecode **bytecode); + struct lttng_bytecode **bytecode); LTTNG_HIDDEN int run_as_create_worker(const char *procname, post_fork_cleanup_cb clean_up_func, void *clean_up_user_data); diff --git a/src/common/sessiond-comm/sessiond-comm.h b/src/common/sessiond-comm/sessiond-comm.h index c15a26f88..e403bfe3e 100644 --- a/src/common/sessiond-comm/sessiond-comm.h +++ b/src/common/sessiond-comm/sessiond-comm.h @@ -106,6 +106,7 @@ enum lttcomm_sessiond_command { LTTNG_SESSION_LIST_ROTATION_SCHEDULES = 48, LTTNG_CREATE_SESSION_EXT = 49, LTTNG_CLEAR_SESSION = 50, + LTTNG_LIST_TRIGGERS = 51, }; enum lttcomm_relayd_command { @@ -429,7 +430,7 @@ struct lttcomm_session_msg { * starts at reloc_table_offset. */ #define LTTNG_FILTER_PADDING 32 -struct lttng_filter_bytecode { +struct lttng_bytecode { uint32_t len; /* len of data */ uint32_t reloc_table_offset; uint64_t seqnum; 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 1fc07dbd3..74b240ccc 100644 --- a/src/bin/lttng-sessiond/shm.c +++ b/src/common/shm.c @@ -193,3 +193,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/trace-chunk.c b/src/common/trace-chunk.c index 75d1738d9..242d0f71e 100644 --- a/src/common/trace-chunk.c +++ b/src/common/trace-chunk.c @@ -966,8 +966,8 @@ enum lttng_trace_chunk_status lttng_trace_chunk_get_credentials( pthread_mutex_lock(&chunk->lock); if (chunk->credentials.is_set) { if (chunk->credentials.value.use_current_user) { - credentials->uid = geteuid(); - credentials->gid = getegid(); + LTTNG_OPTIONAL_SET(&credentials->uid, geteuid()); + LTTNG_OPTIONAL_SET(&credentials->gid, getegid()); } else { *credentials = chunk->credentials.value.user; } diff --git a/src/common/trigger.c b/src/common/trigger.c index 2ef77b236..77f40af19 100644 --- a/src/common/trigger.c +++ b/src/common/trigger.c @@ -7,16 +7,32 @@ #include #include +#include +#include +#include +#include +#include #include #include #include #include +#include #include +#include #include #include +#include +static void destroy_lttng_condition_event_rule_capture_bytecode_element(void *ptr) +{ + struct lttng_condition_event_rule_capture_bytecode_element *element = + ptr; + lttng_event_expr_destroy(element->expression); + free(element->bytecode); + free(element); +} LTTNG_HIDDEN -bool lttng_trigger_validate(struct lttng_trigger *trigger) +bool lttng_trigger_validate(const struct lttng_trigger *trigger) { bool valid; @@ -25,6 +41,11 @@ bool lttng_trigger_validate(struct lttng_trigger *trigger) goto end; } + if (!trigger->creds.uid.is_set) { + valid = false; + goto end; + } + valid = lttng_condition_validate(trigger->condition) && lttng_action_validate(trigger->action); end: @@ -48,12 +69,19 @@ struct lttng_trigger *lttng_trigger_create( urcu_ref_init(&trigger->ref); + trigger->firing_policy.type = LTTNG_TRIGGER_FIRE_EVERY_N; + trigger->firing_policy.threshold = 1; + lttng_trigger_set_error_count(trigger, 0); + lttng_condition_get(condition); trigger->condition = condition; lttng_action_get(action); trigger->action = action; + lttng_dynamic_pointer_array_init(&trigger->capture_bytecode_set, + destroy_lttng_condition_event_rule_capture_bytecode_element); + end: return trigger; } @@ -69,11 +97,10 @@ struct lttng_condition *lttng_trigger_get_condition( return trigger ? trigger->condition : NULL; } -LTTNG_HIDDEN const struct lttng_condition *lttng_trigger_get_const_condition( const struct lttng_trigger *trigger) { - return trigger->condition; + return trigger ? trigger->condition : NULL; } @@ -88,11 +115,10 @@ struct lttng_action *lttng_trigger_get_action( return trigger ? trigger->action : NULL; } -LTTNG_HIDDEN const struct lttng_action *lttng_trigger_get_const_action( const struct lttng_trigger *trigger) { - return trigger->action; + return trigger ? trigger->action : NULL; } static void trigger_destroy_ref(struct urcu_ref *ref) @@ -103,6 +129,8 @@ static void trigger_destroy_ref(struct urcu_ref *ref) struct lttng_condition *condition = lttng_trigger_get_condition(trigger); + lttng_dynamic_pointer_array_reset(&trigger->capture_bytecode_set); + assert(action); assert(condition); @@ -110,6 +138,7 @@ static void trigger_destroy_ref(struct urcu_ref *ref) lttng_action_put(action); lttng_condition_put(condition); + free(trigger->name); free(trigger); } @@ -118,15 +147,41 @@ void lttng_trigger_destroy(struct lttng_trigger *trigger) lttng_trigger_put(trigger); } +static bool is_firing_policy_valid(enum lttng_trigger_firing_policy_type policy) +{ + bool valid = false; + + switch (policy) { + case LTTNG_TRIGGER_FIRE_EVERY_N: + case LTTNG_TRIGGER_FIRE_ONCE_AFTER_N: + valid = true; + break; + default: + valid = false; + break; + } + + return valid; +} + LTTNG_HIDDEN ssize_t lttng_trigger_create_from_payload( struct lttng_payload_view *src_view, struct lttng_trigger **trigger) { - ssize_t ret, offset = 0, condition_size, action_size; + ssize_t ret, offset = 0, condition_size, action_size, name_size = 0; + enum lttng_trigger_status status; struct lttng_condition *condition = NULL; struct lttng_action *action = NULL; const struct lttng_trigger_comm *trigger_comm; + const char *name = NULL; + uint64_t firing_threshold; + int64_t error_count; + enum lttng_trigger_firing_policy_type firing_policy; + struct lttng_credentials creds = { + .uid = LTTNG_OPTIONAL_INIT_UNSET, + .gid = LTTNG_OPTIONAL_INIT_UNSET, + }; if (!src_view || !trigger) { ret = -1; @@ -135,7 +190,35 @@ ssize_t lttng_trigger_create_from_payload( /* lttng_trigger_comm header */ trigger_comm = (typeof(trigger_comm)) src_view->buffer.data; + + /* Get the trigger creds */ + LTTNG_OPTIONAL_SET(&creds.uid, trigger_comm->uid); + offset += sizeof(*trigger_comm); + + firing_policy = trigger_comm->policy_type; + if (!is_firing_policy_valid(firing_policy)) { + ret =-1; + goto end; + } + + firing_threshold = trigger_comm->policy_threshold; + error_count = trigger_comm->error_count; + if (trigger_comm->name_length != 0) { + /* Name */ + struct lttng_payload_view name_view = + lttng_payload_view_from_view( + src_view, offset, trigger_comm->name_length); + + name = name_view.buffer.data; + if (!lttng_buffer_view_contains_string(&name_view.buffer, name, trigger_comm->name_length)){ + ret = -1; + goto end; + } + offset += trigger_comm->name_length; + name_size = trigger_comm->name_length; + } + { /* struct lttng_condition */ struct lttng_payload_view condition_view = @@ -168,7 +251,7 @@ ssize_t lttng_trigger_create_from_payload( offset += action_size; /* Unexpected size of inner-elements; the buffer is corrupted. */ - if ((ssize_t) trigger_comm->length != condition_size + action_size) { + if ((ssize_t) trigger_comm->length != condition_size + action_size + name_size) { ret = -1; goto error; } @@ -179,6 +262,8 @@ ssize_t lttng_trigger_create_from_payload( goto error; } + lttng_trigger_set_credentials(*trigger, &creds); + /* * The trigger object owns references to the action and condition * objects. @@ -189,6 +274,22 @@ ssize_t lttng_trigger_create_from_payload( lttng_action_put(action); action = NULL; + if (name) { + status = lttng_trigger_set_name(*trigger, name); + if (status != LTTNG_TRIGGER_STATUS_OK) { + ret = -1; + goto end; + } + } + + status = lttng_trigger_set_firing_policy(*trigger, firing_policy, firing_threshold); + if (status != LTTNG_TRIGGER_STATUS_OK) { + ret = -1; + goto end; + } + + lttng_trigger_set_error_count(*trigger, error_count); + ret = offset; error: @@ -203,13 +304,30 @@ end: * for the detailed format. */ LTTNG_HIDDEN -int lttng_trigger_serialize(struct lttng_trigger *trigger, +int lttng_trigger_serialize(const struct lttng_trigger *trigger, struct lttng_payload *payload) { int ret; - size_t header_offset, size_before_payload; + size_t header_offset, size_before_payload, size_name; struct lttng_trigger_comm trigger_comm = {}; struct lttng_trigger_comm *header; + const struct lttng_credentials *creds = NULL; + + creds = lttng_trigger_get_credentials(trigger); + assert(creds); + + trigger_comm.uid = LTTNG_OPTIONAL_GET(creds->uid); + + if (trigger->name != NULL) { + size_name = strlen(trigger->name) + 1; + } else { + size_name = 0; + } + + trigger_comm.name_length = size_name; + trigger_comm.policy_type = (uint8_t) trigger->firing_policy.type; + trigger_comm.policy_threshold = (uint64_t) trigger->firing_policy.threshold; + trigger_comm.error_count = (int64_t) lttng_trigger_get_error_count(trigger); header_offset = payload->buffer.size; ret = lttng_dynamic_buffer_append(&payload->buffer, &trigger_comm, @@ -219,6 +337,14 @@ int lttng_trigger_serialize(struct lttng_trigger *trigger, } size_before_payload = payload->buffer.size; + + /* Trigger name */ + ret = lttng_dynamic_buffer_append( + &payload->buffer, trigger->name, size_name); + if (ret) { + goto end; + } + ret = lttng_condition_serialize(trigger->condition, payload); if (ret) { goto end; @@ -236,6 +362,118 @@ end: return ret; } +enum lttng_trigger_status lttng_trigger_set_name(struct lttng_trigger *trigger, const char* name) +{ + char *name_copy = NULL; + enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK; + + if (!trigger || !name || + strlen(name) == 0) { + status = LTTNG_TRIGGER_STATUS_INVALID; + goto end; + } + + name_copy = strdup(name); + if (!name_copy) { + status = LTTNG_TRIGGER_STATUS_ERROR; + goto end; + } + + free(trigger->name); + + trigger->name = name_copy; + name_copy = NULL; +end: + return status; +} + +enum lttng_trigger_status lttng_trigger_get_name( + const struct lttng_trigger *trigger, const char **name) +{ + enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK; + + if (!trigger || !name) { + status = LTTNG_TRIGGER_STATUS_INVALID; + goto end; + } + + if (!trigger->name) { + status = LTTNG_TRIGGER_STATUS_UNSET; + } + + *name = trigger->name; +end: + return status; +} + +LTTNG_HIDDEN +int lttng_trigger_assign_name(struct lttng_trigger *dst, + const struct lttng_trigger *src) +{ + int ret = 0; + enum lttng_trigger_status status; + + status = lttng_trigger_set_name(dst, src->name); + if (status != LTTNG_TRIGGER_STATUS_OK) { + ret = -1; + ERR("Failed to set name for trigger"); + goto end; + } +end: + return ret; +} + +LTTNG_HIDDEN +void lttng_trigger_set_tracer_token(struct lttng_trigger *trigger, uint64_t token) +{ + assert(trigger); + LTTNG_OPTIONAL_SET(&trigger->tracer_token, token); +} + +LTTNG_HIDDEN +uint64_t lttng_trigger_get_tracer_token(const struct lttng_trigger *trigger) +{ + assert(trigger); + + return LTTNG_OPTIONAL_GET(trigger->tracer_token); +} + +LTTNG_HIDDEN +void lttng_trigger_set_error_counter_index(struct lttng_trigger *trigger, + uint64_t error_counter_index) +{ + assert(trigger); + LTTNG_OPTIONAL_SET(&trigger->error_counter_index, error_counter_index); +} + +LTTNG_HIDDEN +uint64_t lttng_trigger_get_error_counter_index( + const struct lttng_trigger *trigger) +{ + assert(trigger); + + return LTTNG_OPTIONAL_GET(trigger->error_counter_index); +} + +LTTNG_HIDDEN +int lttng_trigger_generate_name(struct lttng_trigger *trigger, uint64_t offset) +{ + int ret = 0; + char *generated_name = NULL; + + ret = asprintf(&generated_name, "T%" PRIu64 "", offset); + if (ret < 0) { + ERR("Failed to generate trigger name"); + ret = -1; + goto end; + } + + free(trigger->name); + trigger->name = generated_name; +end: + return ret; +} + LTTNG_HIDDEN void lttng_trigger_get(struct lttng_trigger *trigger) { @@ -252,18 +490,516 @@ void lttng_trigger_put(struct lttng_trigger *trigger) urcu_ref_put(&trigger->ref , trigger_destroy_ref); } +static void delete_trigger_array_element(void *ptr) +{ + struct lttng_trigger *trigger = ptr; + lttng_trigger_put(trigger); +} + +LTTNG_HIDDEN +bool lttng_trigger_is_equal( + const struct lttng_trigger *a, const struct lttng_trigger *b) +{ + if (a->firing_policy.type != b->firing_policy.type) { + return false; + } + + if (a->firing_policy.threshold != b->firing_policy.threshold) { + return false; + } + + /* + * Name is not taken into account since it is cosmetic only. + */ + if (!lttng_condition_is_equal(a->condition, b->condition)) { + return false; + } + + if (!lttng_action_is_equal(a->action, b->action)) { + return false; + } + + if (!lttng_credentials_is_equal(lttng_trigger_get_credentials(a), + lttng_trigger_get_credentials(b))) { + return false; + } + + return true; +} + +LTTNG_HIDDEN +struct lttng_triggers *lttng_triggers_create(void) +{ + struct lttng_triggers *triggers = NULL; + + triggers = zmalloc(sizeof(*triggers)); + if (!triggers) { + goto end; + } + + lttng_dynamic_pointer_array_init(&triggers->array, delete_trigger_array_element); + +end: + return triggers; +} + +LTTNG_HIDDEN +struct lttng_trigger *lttng_triggers_get_pointer_of_index( + const struct lttng_triggers *triggers, unsigned int index) +{ + struct lttng_trigger *trigger = NULL; + assert(triggers); + if (index >= lttng_dynamic_pointer_array_get_count(&triggers->array)) { + goto end; + } + + trigger = (struct lttng_trigger *) + lttng_dynamic_pointer_array_get_pointer( + &triggers->array, index); +end: + return trigger; +} + +LTTNG_HIDDEN +int lttng_triggers_add( + struct lttng_triggers *triggers, struct lttng_trigger *trigger) +{ + int ret; + + assert(triggers); + assert(trigger); + + lttng_trigger_get(trigger); + + ret = lttng_dynamic_pointer_array_add_pointer(&triggers->array, trigger); + if (ret) { + lttng_trigger_put(trigger); + } + + return ret; +} + +const struct lttng_trigger *lttng_triggers_get_at_index( + const struct lttng_triggers *triggers, unsigned int index) +{ + assert(triggers); + return lttng_triggers_get_pointer_of_index(triggers, index); +} + +enum lttng_trigger_status lttng_triggers_get_count(const struct lttng_triggers *triggers, unsigned int *count) +{ + enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK; + + if (!triggers || !count) { + status = LTTNG_TRIGGER_STATUS_INVALID; + goto end; + } + + *count = lttng_dynamic_pointer_array_get_count(&triggers->array); +end: + return status; +} + +void lttng_triggers_destroy(struct lttng_triggers *triggers) +{ + if (!triggers) { + return; + } + + lttng_dynamic_pointer_array_reset(&triggers->array); + free(triggers); +} + +int lttng_triggers_serialize(const struct lttng_triggers *triggers, + struct lttng_payload *payload) +{ + int ret; + unsigned int count; + size_t header_offset, size_before_payload; + struct lttng_triggers_comm triggers_comm = {}; + struct lttng_triggers_comm *header; + enum lttng_trigger_status status; + + header_offset = payload->buffer.size; + + status = lttng_triggers_get_count(triggers, &count); + if (status != LTTNG_TRIGGER_STATUS_OK) { + ret = LTTNG_ERR_INVALID; + goto end; + } + + triggers_comm.count = count; + + ret = lttng_dynamic_buffer_append(&payload->buffer, &triggers_comm, + sizeof(triggers_comm)); + if (ret) { + goto end; + } + + size_before_payload = payload->buffer.size; + + for (int i = 0; i < count; i++) { + const struct lttng_trigger *trigger = + lttng_triggers_get_at_index(triggers, i); + assert(trigger); + + ret = lttng_trigger_serialize(trigger, payload); + if (ret) { + goto end; + } + } + + /* Update payload size. */ + header = (struct lttng_triggers_comm *) ((char *) payload->buffer.data + header_offset); + header->length = payload->buffer.size - size_before_payload; +end: + return ret; +} + +LTTNG_HIDDEN +ssize_t lttng_triggers_create_from_payload( + struct lttng_payload_view *src_view, + struct lttng_triggers **triggers) +{ + ssize_t ret, offset = 0, trigger_size, triggers_size = 0; + const struct lttng_triggers_comm *triggers_comm; + struct lttng_triggers *local_triggers = NULL; + + if (!src_view || !triggers) { + ret = -1; + goto error; + } + + /* lttng_trigger_comms header */ + triggers_comm = (const struct lttng_triggers_comm *) src_view->buffer.data; + offset += sizeof(*triggers_comm); + + local_triggers = lttng_triggers_create(); + if (!local_triggers) { + ret = -1; + goto error; + } + + for (int i = 0; i < triggers_comm->count; i++) { + struct lttng_trigger *trigger = NULL; + struct lttng_payload_view trigger_view = + lttng_payload_view_from_view(src_view, offset, -1); + trigger_size = lttng_trigger_create_from_payload(&trigger_view, + &trigger); + if (trigger_size < 0) { + ret = trigger_size; + goto error; + } + + /* Pass ownership of the trigger to the collection */ + ret = lttng_triggers_add(local_triggers, trigger); + lttng_trigger_put(trigger); + if (ret < 0) { + ret = -1; + goto error; + } + + offset += trigger_size; + triggers_size += trigger_size; + } + + /* Unexpected size of inner-elements; the buffer is corrupted. */ + if ((ssize_t) triggers_comm->length != triggers_size) { + ret = -1; + goto error; + } + + /* Pass ownership to caller */ + *triggers = local_triggers; + local_triggers = NULL; + + ret = offset; +error: + + lttng_triggers_destroy(local_triggers); + return ret; +} + LTTNG_HIDDEN const struct lttng_credentials *lttng_trigger_get_credentials( const struct lttng_trigger *trigger) { - return LTTNG_OPTIONAL_GET_PTR(trigger->creds); + return &trigger->creds; } LTTNG_HIDDEN -void lttng_trigger_set_credentials( - struct lttng_trigger *trigger, +void lttng_trigger_set_credentials(struct lttng_trigger *trigger, const struct lttng_credentials *creds) { assert(creds); - LTTNG_OPTIONAL_SET(&trigger->creds, *creds); + trigger->creds = *creds; +} + +enum lttng_trigger_status lttng_trigger_set_user_identity( + struct lttng_trigger *trigger, uid_t uid) +{ + enum lttng_trigger_status ret = LTTNG_TRIGGER_STATUS_OK; + struct lttng_credentials creds = { + .uid = LTTNG_OPTIONAL_INIT_UNSET, + .gid = LTTNG_OPTIONAL_INIT_UNSET, + }; + + if (!trigger) { + ret = LTTNG_TRIGGER_STATUS_INVALID; + goto end; + } + + if (geteuid() != 0) { + ret = LTTNG_TRIGGER_STATUS_EPERM; + goto end; + } + + LTTNG_OPTIONAL_SET(&creds.uid, uid); + + lttng_trigger_set_credentials(trigger, &creds); + +end: + return ret; +} + +enum lttng_trigger_status lttng_trigger_get_user_identity( + const struct lttng_trigger *trigger, uid_t *uid) +{ + enum lttng_trigger_status ret = LTTNG_TRIGGER_STATUS_OK; + const struct lttng_credentials *creds = NULL; + + if (!trigger || !uid ) { + ret = LTTNG_TRIGGER_STATUS_INVALID; + goto end; + } + + if (!trigger->creds.uid.is_set ) { + ret = LTTNG_TRIGGER_STATUS_UNSET; + goto end; + } + + creds = lttng_trigger_get_credentials(trigger); + *uid = lttng_credentials_get_uid(creds); + +end: + return ret; +} + +LTTNG_HIDDEN +uint64_t lttng_trigger_get_error_count( + const struct lttng_trigger *trigger) +{ + return LTTNG_OPTIONAL_GET(trigger->error_count); +} + +LTTNG_HIDDEN +void lttng_trigger_set_error_count( + struct lttng_trigger *trigger, + uint64_t error_count) +{ + LTTNG_OPTIONAL_SET(&trigger->error_count, error_count); +} + +enum lttng_trigger_status lttng_trigger_set_firing_policy( + struct lttng_trigger *trigger, + enum lttng_trigger_firing_policy_type policy_type, + uint64_t threshold) +{ + enum lttng_trigger_status ret = LTTNG_TRIGGER_STATUS_OK; + assert(trigger); + + if (threshold < 1) { + ret = LTTNG_TRIGGER_STATUS_INVALID; + goto end; + } + + trigger->firing_policy.type = policy_type; + trigger->firing_policy.threshold = threshold; + +end: + return ret; +} + +enum lttng_trigger_status lttng_trigger_get_firing_policy( + const struct lttng_trigger *trigger, + enum lttng_trigger_firing_policy_type *policy_type, + uint64_t *threshold) +{ + enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK; + + if (!trigger || !policy_type || !threshold) { + status = LTTNG_TRIGGER_STATUS_INVALID; + goto end; + } + + *policy_type = trigger->firing_policy.type; + *threshold = trigger->firing_policy.threshold; + +end: + return status; +} + +LTTNG_HIDDEN +bool lttng_trigger_should_fire(const struct lttng_trigger *trigger) +{ + assert(trigger); + bool ready_to_fire = false; + + switch (trigger->firing_policy.type) { + case LTTNG_TRIGGER_FIRE_EVERY_N: + if (trigger->firing_policy.current_count < trigger->firing_policy.threshold) { + ready_to_fire = true; + } + break; + case LTTNG_TRIGGER_FIRE_ONCE_AFTER_N: + if (trigger->firing_policy.current_count < trigger->firing_policy.threshold) { + ready_to_fire = true; + } + break; + default: + abort(); + }; + + return ready_to_fire; +} + +LTTNG_HIDDEN +void lttng_trigger_fire(struct lttng_trigger *trigger) +{ + assert(trigger); + + trigger->firing_policy.current_count++; + + switch (trigger->firing_policy.type) { + case LTTNG_TRIGGER_FIRE_EVERY_N: + if (trigger->firing_policy.current_count == trigger->firing_policy.threshold) { + trigger->firing_policy.current_count = 0; + } + break; + case LTTNG_TRIGGER_FIRE_ONCE_AFTER_N: + /* + * TODO: deactivate the trigger condition on + * remove any work overhead on the + * traced application or kernel since the trigger will + * never fire again. + */ + break; + default: + abort(); + }; +} + +LTTNG_HIDDEN +enum lttng_domain_type lttng_trigger_get_underlying_domain_type_restriction( + const struct lttng_trigger *trigger) +{ + enum lttng_domain_type type = LTTNG_DOMAIN_NONE; + const struct lttng_event_rule *event_rule; + enum lttng_condition_status c_status; + enum lttng_condition_type c_type; + + assert(trigger); + assert(trigger->condition); + c_type = lttng_condition_get_type(trigger->condition); + if (c_type == LTTNG_CONDITION_TYPE_UNKNOWN) { + assert(0); + } + + switch (c_type) { + case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + type = LTTNG_DOMAIN_NONE; + break; + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + c_status = lttng_condition_event_rule_get_rule( + trigger->condition, &event_rule); + if (c_status != LTTNG_CONDITION_STATUS_OK) { + /* The condition object is invalid */ + assert(0); + } + + type = lttng_event_rule_get_domain_type(event_rule); + break; + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + c_status = lttng_condition_buffer_usage_get_domain_type( + trigger->condition, &type); + if (c_status != LTTNG_CONDITION_STATUS_OK) { + /* The condition object is invalid */ + assert(0); + } + break; + default: + type = LTTNG_DOMAIN_NONE; + break; + } + + return type; +} + +LTTNG_HIDDEN +struct lttng_trigger *lttng_trigger_copy(const struct lttng_trigger *trigger) +{ + int ret; + struct lttng_payload copy_buffer; + struct lttng_trigger *copy = NULL; + + lttng_payload_init(©_buffer); + + ret = lttng_trigger_serialize(trigger, ©_buffer); + if (ret < 0) { + goto end; + } + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + ©_buffer, 0, -1); + ret = lttng_trigger_create_from_payload( + &view, ©); + if (ret < 0) { + copy = NULL; + goto end; + } + } + +end: + lttng_payload_reset(©_buffer); + return copy; +} + +LTTNG_HIDDEN +unsigned int lttng_trigger_get_capture_bytecode_count( + const struct lttng_trigger *trigger) +{ + unsigned int count = 0; + if (!trigger) { + goto end; + } + + count = lttng_dynamic_pointer_array_get_count( + &trigger->capture_bytecode_set); + +end: + return count; +} + +LTTNG_HIDDEN +const struct lttng_bytecode * +lttng_trigger_get_capture_bytecode_at_index( + const struct lttng_trigger *trigger, unsigned int index) +{ + struct lttng_condition_event_rule_capture_bytecode_element *element = NULL; + struct lttng_bytecode *bytecode = NULL; + + element = lttng_dynamic_pointer_array_get_pointer( + &trigger->capture_bytecode_set, index); + + if (element == NULL) { + goto end; + } + bytecode = element->bytecode; +end: + return bytecode; } diff --git a/src/common/ust-consumer/ust-consumer.c b/src/common/ust-consumer/ust-consumer.c index 209931f56..cd9b273c2 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) { @@ -409,7 +368,8 @@ static int open_ust_stream_fd(struct lttng_consumer_channel *channel, int cpu, } return run_as_open(shm_path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, - session_credentials->uid, session_credentials->gid); + lttng_credentials_get_uid(session_credentials), + lttng_credentials_get_gid(session_credentials)); error_shm_path: return -1; @@ -487,8 +447,10 @@ error_open: ERR("Cannot get stream shm path"); } closeret = run_as_unlink(shm_path, - channel->buffer_credentials.value.uid, - channel->buffer_credentials.value.gid); + lttng_credentials_get_uid(LTTNG_OPTIONAL_GET_PTR( + channel->buffer_credentials)), + lttng_credentials_get_gid(LTTNG_OPTIONAL_GET_PTR( + channel->buffer_credentials))); if (closeret) { PERROR("unlink %s", shm_path); } @@ -497,8 +459,10 @@ error_open: /* Try to rmdir all directories under shm_path root. */ if (channel->root_shm_path[0]) { (void) run_as_rmdir_recursive(channel->root_shm_path, - channel->buffer_credentials.value.uid, - channel->buffer_credentials.value.gid, + lttng_credentials_get_uid(LTTNG_OPTIONAL_GET_PTR( + channel->buffer_credentials)), + lttng_credentials_get_gid(LTTNG_OPTIONAL_GET_PTR( + channel->buffer_credentials)), LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG); } free(stream_fds); @@ -1461,8 +1425,8 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, struct ustctl_consumer_channel_attr attr; const uint64_t chunk_id = msg.u.ask_channel.chunk_id.value; const struct lttng_credentials buffer_credentials = { - .uid = msg.u.ask_channel.buffer_credentials.uid, - .gid = msg.u.ask_channel.buffer_credentials.gid, + .uid = LTTNG_OPTIONAL_INIT_VALUE(msg.u.ask_channel.buffer_credentials.uid), + .gid = LTTNG_OPTIONAL_INIT_VALUE(msg.u.ask_channel.buffer_credentials.gid), }; /* Create a plain object and reserve a channel key. */ @@ -2072,8 +2036,8 @@ end_rotate_channel_nosignal: case LTTNG_CONSUMER_CREATE_TRACE_CHUNK: { const struct lttng_credentials credentials = { - .uid = msg.u.create_trace_chunk.credentials.value.uid, - .gid = msg.u.create_trace_chunk.credentials.value.gid, + .uid = LTTNG_OPTIONAL_INIT_VALUE(msg.u.create_trace_chunk.credentials.value.uid), + .gid = LTTNG_OPTIONAL_INIT_VALUE(msg.u.create_trace_chunk.credentials.value.gid), }; const bool is_local_trace = !msg.u.create_trace_chunk.relayd_id.is_set; @@ -2402,8 +2366,10 @@ void lttng_ustconsumer_del_channel(struct lttng_consumer_channel *chan) ERR("Cannot get stream shm path"); } ret = run_as_unlink(shm_path, - chan->buffer_credentials.value.uid, - chan->buffer_credentials.value.gid); + lttng_credentials_get_uid(LTTNG_OPTIONAL_GET_PTR( + chan->buffer_credentials)), + lttng_credentials_get_gid(LTTNG_OPTIONAL_GET_PTR( + chan->buffer_credentials))); if (ret) { PERROR("unlink %s", shm_path); } @@ -2422,8 +2388,10 @@ void lttng_ustconsumer_free_channel(struct lttng_consumer_channel *chan) /* Try to rmdir all directories under shm_path root. */ if (chan->root_shm_path[0]) { (void) run_as_rmdir_recursive(chan->root_shm_path, - chan->buffer_credentials.value.uid, - chan->buffer_credentials.value.gid, + lttng_credentials_get_uid(LTTNG_OPTIONAL_GET_PTR( + chan->buffer_credentials)), + lttng_credentials_get_gid(LTTNG_OPTIONAL_GET_PTR( + chan->buffer_credentials)), LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG); } free(chan->stream_fds); diff --git a/src/common/utils.c b/src/common/utils.c index 2a20e5a8c..3497f92db 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -679,8 +679,8 @@ int utils_mkdir(const char *path, mode_t mode, int uid, int gid) int ret; struct lttng_directory_handle *handle; const struct lttng_credentials creds = { - .uid = (uid_t) uid, - .gid = (gid_t) gid, + .uid = LTTNG_OPTIONAL_INIT_VALUE(uid), + .gid = LTTNG_OPTIONAL_INIT_VALUE(gid), }; handle = lttng_directory_handle_create(NULL); @@ -708,8 +708,8 @@ int utils_mkdir_recursive(const char *path, mode_t mode, int uid, int gid) int ret; struct lttng_directory_handle *handle; const struct lttng_credentials creds = { - .uid = (uid_t) uid, - .gid = (gid_t) gid, + .uid = LTTNG_OPTIONAL_INIT_VALUE(uid), + .gid = LTTNG_OPTIONAL_INIT_VALUE(gid), }; handle = lttng_directory_handle_create(NULL); @@ -1669,3 +1669,40 @@ end: free(buf); return ret_val; } + +LTTNG_HIDDEN +int utils_parse_unsigned_long_long(const char *str, + unsigned long long *value) +{ + int ret; + char *endptr; + + assert(str); + assert(value); + + errno = 0; + *value = strtoull(str, &endptr, 10); + + /* Conversion failed. Out of range? */ + if (errno != 0) { + ret = -1; + goto end; + } + + /* Not the end of the string? */ + if (*endptr) { + ret = -1; + goto end; + } + + /* Empty string? */ + if (endptr == str) { + ret = -1; + goto end; + } + + ret = 0; + +end: + return ret; +} diff --git a/src/common/utils.h b/src/common/utils.h index dec4cd8aa..570216d53 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -59,4 +59,16 @@ enum lttng_error_code utils_user_id_from_name( enum lttng_error_code utils_group_id_from_name( const char *group_name, gid_t *group_id); +/* + * Parse `str` as an unsigned long long value. + * + * Return 0 on success. Return -1 on failure which can be because: + * + * - `str` is zero length + * - `str` contains invalid + */ +LTTNG_HIDDEN +int utils_parse_unsigned_long_long(const char *str, + unsigned long long *value); + #endif /* _COMMON_UTILS_H */ diff --git a/src/lib/lttng-ctl/lttng-ctl.c b/src/lib/lttng-ctl/lttng-ctl.c index 3f1ab9fdc..3216e5002 100644 --- a/src/lib/lttng-ctl/lttng-ctl.c +++ b/src/lib/lttng-ctl/lttng-ctl.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -45,7 +46,6 @@ #include #include -#include #include #include "lttng-ctl-helper.h" @@ -2956,6 +2956,13 @@ int lttng_register_trigger(struct lttng_trigger *trigger) struct lttcomm_session_msg *message_lsm; struct lttng_payload message; struct lttng_payload reply; + struct lttng_trigger *reply_trigger = NULL; + enum lttng_domain_type domain_type; + struct lttng_credentials creds = { + .uid = LTTNG_OPTIONAL_INIT_VALUE(geteuid()), + .gid = LTTNG_OPTIONAL_INIT_UNSET, + }; + lttng_payload_init(&message); lttng_payload_init(&reply); @@ -2965,11 +2972,39 @@ int lttng_register_trigger(struct lttng_trigger *trigger) goto end; } + if (!trigger->creds.uid.is_set) { + /* Use the client credentials as the trigger credentials */ + lttng_trigger_set_credentials(trigger, &creds); + } else { + /* + * Validate that either the current trigger credentials and the + * client credentials are identical or that the current user is + * root. The root user can register, unregister triggers for + * himself and other users. + * This check is also present on the sessiond side, using the + * credentials passed on the socket. These check are all + * "safety" checks. + */ + const struct lttng_credentials *trigger_creds = + lttng_trigger_get_credentials(trigger); + if (!lttng_credentials_is_equal_uid(trigger_creds, &creds)) { + if (lttng_credentials_get_uid(&creds) != 0) { + ret = -LTTNG_ERR_EPERM; + goto end; + } + } + } + if (!lttng_trigger_validate(trigger)) { ret = -LTTNG_ERR_INVALID_TRIGGER; goto end; } + domain_type = lttng_trigger_get_underlying_domain_type_restriction( + trigger); + + lsm.domain.type = domain_type; + ret = lttng_dynamic_buffer_append(&message.buffer, &lsm, sizeof(lsm)); if (ret) { ret = -LTTNG_ERR_NOMEM; @@ -2990,6 +3025,39 @@ int lttng_register_trigger(struct lttng_trigger *trigger) message_lsm->u.trigger.length = (uint32_t) message.buffer.size - sizeof(lsm); + if (getenv("LTTNG_REGISTER_TRIGGER_DRY_RUN")) { + /* + * Don't really send the request, just deserialize, validate + * that it is equal to the original trigger (to test + * serialization and deserialization), and return. + */ + ssize_t sz; + + struct lttng_payload_view pv = lttng_payload_view_from_payload( + &message, sizeof(lsm), -1); + sz = lttng_trigger_create_from_payload(&pv, &reply_trigger); + if (sz != pv.buffer.size - sizeof(lsm)) { + ret = -LTTNG_ERR_UNK; + goto end; + } + + if (!reply_trigger) { + ret = -LTTNG_ERR_UNK; + goto end; + } + + if (!lttng_trigger_is_equal(trigger, reply_trigger)) { + ret = -LTTNG_ERR_UNK; + goto end; + } + + /* Give it a dummy name. */ + lttng_trigger_set_name(trigger, "yop"); + + ret = 0; + goto end; + } + { struct lttng_payload_view message_view = lttng_payload_view_from_payload( @@ -3003,20 +3071,45 @@ int lttng_register_trigger(struct lttng_trigger *trigger) } } + { + struct lttng_payload_view reply_view = + lttng_payload_view_from_payload( + &reply, 0, reply.buffer.size); + + ret = lttng_trigger_create_from_payload( + &reply_view, &reply_trigger); + if (ret < 0) { + ret = -LTTNG_ERR_FATAL; + goto end; + } + } + + ret = lttng_trigger_assign_name(trigger, reply_trigger); + if (ret < 0) { + ret = -LTTNG_ERR_FATAL; + goto end; + } + ret = 0; end: lttng_payload_reset(&message); lttng_payload_reset(&reply); + lttng_trigger_destroy(reply_trigger); return ret; } -int lttng_unregister_trigger(struct lttng_trigger *trigger) +int lttng_unregister_trigger(const struct lttng_trigger *trigger) { int ret; struct lttcomm_session_msg lsm; struct lttcomm_session_msg *message_lsm; struct lttng_payload message; struct lttng_payload reply; + struct lttng_trigger *copy = NULL; + struct lttng_credentials creds = { + .uid = LTTNG_OPTIONAL_INIT_VALUE(geteuid()), + .gid = LTTNG_OPTIONAL_INIT_UNSET, + }; lttng_payload_init(&message); lttng_payload_init(&reply); @@ -3026,7 +3119,36 @@ int lttng_unregister_trigger(struct lttng_trigger *trigger) goto end; } - if (!lttng_trigger_validate(trigger)) { + copy = lttng_trigger_copy(trigger); + if (!copy) { + ret = -LTTNG_ERR_UNK; + goto end; + } + + if (!copy->creds.uid.is_set) { + /* Use the client credentials as the trigger credentials */ + lttng_trigger_set_credentials(copy, &creds); + } else { + /* + * Validate that either the current trigger credentials and the + * client credentials are identical or that the current user is + * root. The root user can register, unregister triggers for + * himself and other users. + * This check is also present on the sessiond side, using the + * credentials passed on the socket. These check are all + * "safety" checks. + */ + const struct lttng_credentials *trigger_creds = + lttng_trigger_get_credentials(copy); + if (!lttng_credentials_is_equal_uid(trigger_creds, &creds)) { + if (lttng_credentials_get_uid(&creds) != 0) { + ret = -LTTNG_ERR_EPERM; + goto end; + } + } + } + + if (!lttng_trigger_validate(copy)) { ret = -LTTNG_ERR_INVALID_TRIGGER; goto end; } @@ -3046,7 +3168,7 @@ int lttng_unregister_trigger(struct lttng_trigger *trigger) */ message_lsm = (struct lttcomm_session_msg *) message.buffer.data; - ret = lttng_trigger_serialize(trigger, &message); + ret = lttng_trigger_serialize(copy, &message); if (ret < 0) { ret = -LTTNG_ERR_UNK; goto end; @@ -3074,11 +3196,58 @@ int lttng_unregister_trigger(struct lttng_trigger *trigger) ret = 0; end: + lttng_trigger_destroy(copy); lttng_payload_reset(&message); lttng_payload_reset(&reply); return ret; } +/* + * Ask the session daemon for all registered triggers. + * Allocate a lttng_triggers collection. + * On error, returns a negative value. + */ +enum lttng_error_code lttng_list_triggers(struct lttng_triggers **triggers) +{ + int ret; + struct lttcomm_session_msg lsm; + struct lttng_payload_view lsm_view = + lttng_payload_view_init_from_buffer( + (const char *) &lsm, 0, sizeof(lsm)); + struct lttng_triggers *local_triggers = NULL; + struct lttng_payload reply; + + lttng_payload_init(&reply); + + memset(&lsm, 0, sizeof(lsm)); + lsm.cmd_type = LTTNG_LIST_TRIGGERS; + + ret = lttng_ctl_ask_sessiond_payload(&lsm_view, &reply); + if (ret < 0) { + goto end; + } + + { + struct lttng_payload_view reply_view = + lttng_payload_view_from_payload( + &reply, 0, reply.buffer.size); + ret = lttng_triggers_create_from_payload( + &reply_view, &local_triggers); + if (ret < 0) { + ret = -LTTNG_ERR_FATAL; + goto end; + } + } + + *triggers = local_triggers; + local_triggers = NULL; + ret = LTTNG_OK; +end: + lttng_payload_reset(&reply); + lttng_triggers_destroy(local_triggers); + return ret; +} + /* * lib constructor. */ diff --git a/src/lib/lttng-ctl/snapshot.c b/src/lib/lttng-ctl/snapshot.c index 2d7725c96..8d3ec2013 100644 --- a/src/lib/lttng-ctl/snapshot.c +++ b/src/lib/lttng-ctl/snapshot.c @@ -238,29 +238,29 @@ void lttng_snapshot_output_destroy(struct lttng_snapshot_output *obj) * Getter family functions of snapshot output. */ -uint32_t lttng_snapshot_output_get_id(struct lttng_snapshot_output *output) +uint32_t lttng_snapshot_output_get_id(const struct lttng_snapshot_output *output) { return output->id; } const char *lttng_snapshot_output_get_name( - struct lttng_snapshot_output *output) + const struct lttng_snapshot_output *output) { return output->name; } -const char *lttng_snapshot_output_get_data_url(struct lttng_snapshot_output *output) +const char *lttng_snapshot_output_get_data_url(const struct lttng_snapshot_output *output) { return output->data_url; } -const char *lttng_snapshot_output_get_ctrl_url(struct lttng_snapshot_output *output) +const char *lttng_snapshot_output_get_ctrl_url(const struct lttng_snapshot_output *output) { return output->ctrl_url; } uint64_t lttng_snapshot_output_get_maxsize( - struct lttng_snapshot_output *output) + const struct lttng_snapshot_output *output) { return output->max_size; } diff --git a/src/vendor/Makefile.am b/src/vendor/Makefile.am new file mode 100644 index 000000000..e9c2fcfe7 --- /dev/null +++ b/src/vendor/Makefile.am @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +SUBDIRS = msgpack diff --git a/src/vendor/msgpack/Makefile.am b/src/vendor/msgpack/Makefile.am new file mode 100644 index 000000000..6666b6108 --- /dev/null +++ b/src/vendor/msgpack/Makefile.am @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: GPL-2.0-only + +AM_CPPFLAGS = -I$(top_srcdir)/src + +noinst_LTLIBRARIES = libmsgpack.la + +AM_CFLAGS += -fvisibility=hidden + +libmsgpack_la_SOURCES = \ + fbuffer.h \ + gcc_atomic.h \ + lttng-config.h \ + msgpack.h \ + objectc.c \ + object.h \ + pack_define.h \ + pack.h \ + pack_template.h \ + predef.h \ + sbuffer.h \ + sysdep.h \ + timestamp.h \ + unpack.c \ + unpack_define.h \ + unpack.h \ + unpack_template.h \ + util.h \ + version.c \ + version.h \ + version_master.h \ + vrefbuffer.c \ + vrefbuffer.h \ + zbuffer.h \ + zone.c \ + zone.h diff --git a/src/vendor/msgpack/fbuffer.h b/src/vendor/msgpack/fbuffer.h new file mode 100644 index 000000000..d478008c8 --- /dev/null +++ b/src/vendor/msgpack/fbuffer.h @@ -0,0 +1,38 @@ +/* + * MessagePack for C FILE* buffer adaptor + * + * Copyright (C) 2013 Vladimir Volodko + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef MSGPACK_FBUFFER_H +#define MSGPACK_FBUFFER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup msgpack_fbuffer FILE* buffer + * @ingroup msgpack_buffer + * @{ + */ + +static inline int msgpack_fbuffer_write(void* data, const char* buf, size_t len) +{ + return (1 == fwrite(buf, len, 1, (FILE *)data)) ? 0 : -1; +} + +/** @} */ + + +#ifdef __cplusplus +} +#endif + +#endif /* msgpack/fbuffer.h */ diff --git a/src/vendor/msgpack/gcc_atomic.h b/src/vendor/msgpack/gcc_atomic.h new file mode 100644 index 000000000..6b1b1a799 --- /dev/null +++ b/src/vendor/msgpack/gcc_atomic.h @@ -0,0 +1,25 @@ +/* + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ + +#ifndef MSGPACK_GCC_ATOMIC_H +#define MSGPACK_GCC_ATOMIC_H + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef int _msgpack_atomic_counter_t; + +int _msgpack_sync_decr_and_fetch(volatile _msgpack_atomic_counter_t* ptr); +int _msgpack_sync_incr_and_fetch(volatile _msgpack_atomic_counter_t* ptr); + + +#if defined(__cplusplus) +} +#endif + + +#endif // MSGPACK_GCC_ATOMIC_H diff --git a/src/vendor/msgpack/lttng-config.h b/src/vendor/msgpack/lttng-config.h new file mode 100644 index 000000000..073bf8da5 --- /dev/null +++ b/src/vendor/msgpack/lttng-config.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 Michael Jeanson + * + * SPDX-License-Identifier: BSL-1.0 + * + */ + +#ifndef MSGPACK_LTTNG_CONFIG_H +#define MSGPACK_LTTNG_CONFIG_H + +#include + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define MSGPACK_ENDIAN_LITTLE_BYTE 1 +#elif __BYTE_ORDER == __BIG_ENDIAN +#define MSGPACK_ENDIAN_BIG_BYTE 1 +#endif + +#endif diff --git a/src/vendor/msgpack/msgpack.h b/src/vendor/msgpack/msgpack.h new file mode 100644 index 000000000..cf962c9e2 --- /dev/null +++ b/src/vendor/msgpack/msgpack.h @@ -0,0 +1,24 @@ +/* + * MessagePack for C + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +/** + * @defgroup msgpack MessagePack C + * @{ + * @} + */ + +#include "vendor/msgpack/util.h" +#include "vendor/msgpack/object.h" +#include "vendor/msgpack/zone.h" +#include "vendor/msgpack/pack.h" +#include "vendor/msgpack/unpack.h" +#include "vendor/msgpack/sbuffer.h" +#include "vendor/msgpack/vrefbuffer.h" +#include "vendor/msgpack/version.h" + diff --git a/src/vendor/msgpack/object.h b/src/vendor/msgpack/object.h new file mode 100644 index 000000000..e9431744b --- /dev/null +++ b/src/vendor/msgpack/object.h @@ -0,0 +1,118 @@ +/* + * MessagePack for C dynamic typing routine + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef MSGPACK_OBJECT_H +#define MSGPACK_OBJECT_H + +#include "zone.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup msgpack_object Dynamically typed object + * @ingroup msgpack + * @{ + */ + +typedef enum { + MSGPACK_OBJECT_NIL = 0x00, + MSGPACK_OBJECT_BOOLEAN = 0x01, + MSGPACK_OBJECT_POSITIVE_INTEGER = 0x02, + MSGPACK_OBJECT_NEGATIVE_INTEGER = 0x03, + MSGPACK_OBJECT_FLOAT32 = 0x0a, + MSGPACK_OBJECT_FLOAT64 = 0x04, + MSGPACK_OBJECT_FLOAT = 0x04, +#if defined(MSGPACK_USE_LEGACY_NAME_AS_FLOAT) + MSGPACK_OBJECT_DOUBLE = MSGPACK_OBJECT_FLOAT, /* obsolete */ +#endif /* MSGPACK_USE_LEGACY_NAME_AS_FLOAT */ + MSGPACK_OBJECT_STR = 0x05, + MSGPACK_OBJECT_ARRAY = 0x06, + MSGPACK_OBJECT_MAP = 0x07, + MSGPACK_OBJECT_BIN = 0x08, + MSGPACK_OBJECT_EXT = 0x09 +} msgpack_object_type; + + +struct msgpack_object; +struct msgpack_object_kv; + +typedef struct { + uint32_t size; + struct msgpack_object* ptr; +} msgpack_object_array; + +typedef struct { + uint32_t size; + struct msgpack_object_kv* ptr; +} msgpack_object_map; + +typedef struct { + uint32_t size; + const char* ptr; +} msgpack_object_str; + +typedef struct { + uint32_t size; + const char* ptr; +} msgpack_object_bin; + +typedef struct { + int8_t type; + uint32_t size; + const char* ptr; +} msgpack_object_ext; + +typedef union { + bool boolean; + uint64_t u64; + int64_t i64; +#if defined(MSGPACK_USE_LEGACY_NAME_AS_FLOAT) + double dec; /* obsolete*/ +#endif /* MSGPACK_USE_LEGACY_NAME_AS_FLOAT */ + double f64; + msgpack_object_array array; + msgpack_object_map map; + msgpack_object_str str; + msgpack_object_bin bin; + msgpack_object_ext ext; +} msgpack_object_union; + +typedef struct msgpack_object { + msgpack_object_type type; + msgpack_object_union via; +} msgpack_object; + +typedef struct msgpack_object_kv { + msgpack_object key; + msgpack_object val; +} msgpack_object_kv; + +#if !defined(_KERNEL_MODE) +MSGPACK_DLLEXPORT +void msgpack_object_print(FILE* out, msgpack_object o); +#endif + +MSGPACK_DLLEXPORT +int msgpack_object_print_buffer(char *buffer, size_t buffer_size, msgpack_object o); + +MSGPACK_DLLEXPORT +bool msgpack_object_equal(const msgpack_object x, const msgpack_object y); + +/** @} */ + + +#ifdef __cplusplus +} +#endif + +#endif /* msgpack/object.h */ diff --git a/src/vendor/msgpack/objectc.c b/src/vendor/msgpack/objectc.c new file mode 100644 index 000000000..6086bd618 --- /dev/null +++ b/src/vendor/msgpack/objectc.c @@ -0,0 +1,482 @@ +/* + * MessagePack for C dynamic typing routine + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +#if defined(_KERNEL_MODE) +# undef _NO_CRT_STDIO_INLINE +# define _NO_CRT_STDIO_INLINE +#endif + +#include "vendor/msgpack/object.h" +#include "vendor/msgpack/pack.h" +#include + +#include +#include + +#if defined(_MSC_VER) +#if _MSC_VER >= 1800 +#include +#else +#define PRIu64 "I64u" +#define PRIi64 "I64i" +#define PRIi8 "i" +#endif +#else +#include +#endif + +#if defined(_KERNEL_MODE) +# undef snprintf +# define snprintf _snprintf +#endif + +int msgpack_pack_object(msgpack_packer* pk, msgpack_object d) +{ + switch(d.type) { + case MSGPACK_OBJECT_NIL: + return msgpack_pack_nil(pk); + + case MSGPACK_OBJECT_BOOLEAN: + if(d.via.boolean) { + return msgpack_pack_true(pk); + } else { + return msgpack_pack_false(pk); + } + + case MSGPACK_OBJECT_POSITIVE_INTEGER: + return msgpack_pack_uint64(pk, d.via.u64); + + case MSGPACK_OBJECT_NEGATIVE_INTEGER: + return msgpack_pack_int64(pk, d.via.i64); + + case MSGPACK_OBJECT_FLOAT32: + return msgpack_pack_float(pk, (float)d.via.f64); + + case MSGPACK_OBJECT_FLOAT64: + return msgpack_pack_double(pk, d.via.f64); + + case MSGPACK_OBJECT_STR: + { + int ret = msgpack_pack_str(pk, d.via.str.size); + if(ret < 0) { return ret; } + return msgpack_pack_str_body(pk, d.via.str.ptr, d.via.str.size); + } + + case MSGPACK_OBJECT_BIN: + { + int ret = msgpack_pack_bin(pk, d.via.bin.size); + if(ret < 0) { return ret; } + return msgpack_pack_bin_body(pk, d.via.bin.ptr, d.via.bin.size); + } + + case MSGPACK_OBJECT_EXT: + { + int ret = msgpack_pack_ext(pk, d.via.ext.size, d.via.ext.type); + if(ret < 0) { return ret; } + return msgpack_pack_ext_body(pk, d.via.ext.ptr, d.via.ext.size); + } + + case MSGPACK_OBJECT_ARRAY: + { + int ret = msgpack_pack_array(pk, d.via.array.size); + if(ret < 0) { + return ret; + } + else { + msgpack_object* o = d.via.array.ptr; + msgpack_object* const oend = d.via.array.ptr + d.via.array.size; + for(; o != oend; ++o) { + ret = msgpack_pack_object(pk, *o); + if(ret < 0) { return ret; } + } + + return 0; + } + } + + case MSGPACK_OBJECT_MAP: + { + int ret = msgpack_pack_map(pk, d.via.map.size); + if(ret < 0) { + return ret; + } + else { + msgpack_object_kv* kv = d.via.map.ptr; + msgpack_object_kv* const kvend = d.via.map.ptr + d.via.map.size; + for(; kv != kvend; ++kv) { + ret = msgpack_pack_object(pk, kv->key); + if(ret < 0) { return ret; } + ret = msgpack_pack_object(pk, kv->val); + if(ret < 0) { return ret; } + } + + return 0; + } + } + + default: + return -1; + } +} + +#if !defined(_KERNEL_MODE) + +static void msgpack_object_bin_print(FILE* out, const char *ptr, size_t size) +{ + size_t i; + for (i = 0; i < size; ++i) { + if (ptr[i] == '"') { + fputs("\\\"", out); + } else if (isprint((unsigned char)ptr[i])) { + fputc(ptr[i], out); + } else { + fprintf(out, "\\x%02x", (unsigned char)ptr[i]); + } + } +} + +void msgpack_object_print(FILE* out, msgpack_object o) +{ + switch(o.type) { + case MSGPACK_OBJECT_NIL: + fprintf(out, "nil"); + break; + + case MSGPACK_OBJECT_BOOLEAN: + fprintf(out, (o.via.boolean ? "true" : "false")); + break; + + case MSGPACK_OBJECT_POSITIVE_INTEGER: +#if defined(PRIu64) + fprintf(out, "%" PRIu64, o.via.u64); +#else + if (o.via.u64 > ULONG_MAX) + fprintf(out, "over 4294967295"); + else + fprintf(out, "%lu", (unsigned long)o.via.u64); +#endif + break; + + case MSGPACK_OBJECT_NEGATIVE_INTEGER: +#if defined(PRIi64) + fprintf(out, "%" PRIi64, o.via.i64); +#else + if (o.via.i64 > LONG_MAX) + fprintf(out, "over +2147483647"); + else if (o.via.i64 < LONG_MIN) + fprintf(out, "under -2147483648"); + else + fprintf(out, "%ld", (signed long)o.via.i64); +#endif + break; + + case MSGPACK_OBJECT_FLOAT32: + case MSGPACK_OBJECT_FLOAT64: + fprintf(out, "%f", o.via.f64); + break; + + case MSGPACK_OBJECT_STR: + fprintf(out, "\""); + fwrite(o.via.str.ptr, o.via.str.size, 1, out); + fprintf(out, "\""); + break; + + case MSGPACK_OBJECT_BIN: + fprintf(out, "\""); + msgpack_object_bin_print(out, o.via.bin.ptr, o.via.bin.size); + fprintf(out, "\""); + break; + + case MSGPACK_OBJECT_EXT: +#if defined(PRIi8) + fprintf(out, "(ext: %" PRIi8 ")", o.via.ext.type); +#else + fprintf(out, "(ext: %d)", (int)o.via.ext.type); +#endif + fprintf(out, "\""); + msgpack_object_bin_print(out, o.via.ext.ptr, o.via.ext.size); + fprintf(out, "\""); + break; + + case MSGPACK_OBJECT_ARRAY: + fprintf(out, "["); + if(o.via.array.size != 0) { + msgpack_object* p = o.via.array.ptr; + msgpack_object* const pend = o.via.array.ptr + o.via.array.size; + msgpack_object_print(out, *p); + ++p; + for(; p < pend; ++p) { + fprintf(out, ", "); + msgpack_object_print(out, *p); + } + } + fprintf(out, "]"); + break; + + case MSGPACK_OBJECT_MAP: + fprintf(out, "{"); + if(o.via.map.size != 0) { + msgpack_object_kv* p = o.via.map.ptr; + msgpack_object_kv* const pend = o.via.map.ptr + o.via.map.size; + msgpack_object_print(out, p->key); + fprintf(out, "=>"); + msgpack_object_print(out, p->val); + ++p; + for(; p < pend; ++p) { + fprintf(out, ", "); + msgpack_object_print(out, p->key); + fprintf(out, "=>"); + msgpack_object_print(out, p->val); + } + } + fprintf(out, "}"); + break; + + default: + // FIXME +#if defined(PRIu64) + fprintf(out, "#", o.type, o.via.u64); +#else + if (o.via.u64 > ULONG_MAX) + fprintf(out, "#", o.type); + else + fprintf(out, "#", o.type, (unsigned long)o.via.u64); +#endif + + } +} + +#endif + +#define MSGPACK_CHECKED_CALL(ret, func, aux_buffer, aux_buffer_size, ...) \ + ret = func(aux_buffer, aux_buffer_size, __VA_ARGS__); \ + if (ret <= 0 || ret >= (int)aux_buffer_size) return 0; \ + aux_buffer = aux_buffer + ret; \ + aux_buffer_size = aux_buffer_size - ret \ + +static int msgpack_object_bin_print_buffer(char *buffer, size_t buffer_size, const char *ptr, size_t size) +{ + size_t i; + char *aux_buffer = buffer; + size_t aux_buffer_size = buffer_size; + int ret; + + for (i = 0; i < size; ++i) { + if (ptr[i] == '"') { + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "\\\""); + } else if (isprint((unsigned char)ptr[i])) { + if (aux_buffer_size > 0) { + memcpy(aux_buffer, ptr + i, 1); + aux_buffer = aux_buffer + 1; + aux_buffer_size = aux_buffer_size - 1; + } + } else { + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "\\x%02x", (unsigned char)ptr[i]); + } + } + + return (int)(buffer_size - aux_buffer_size); +} + +int msgpack_object_print_buffer(char *buffer, size_t buffer_size, msgpack_object o) +{ + char *aux_buffer = buffer; + size_t aux_buffer_size = buffer_size; + int ret; + switch(o.type) { + case MSGPACK_OBJECT_NIL: + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "nil"); + break; + + case MSGPACK_OBJECT_BOOLEAN: + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, (o.via.boolean ? "true" : "false")); + break; + + case MSGPACK_OBJECT_POSITIVE_INTEGER: +#if defined(PRIu64) + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "%" PRIu64, o.via.u64); +#else + if (o.via.u64 > ULONG_MAX) { + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "over 4294967295"); + } else { + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "%lu", (unsigned long)o.via.u64); + } +#endif + break; + + case MSGPACK_OBJECT_NEGATIVE_INTEGER: +#if defined(PRIi64) + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "%" PRIi64, o.via.i64); +#else + if (o.via.i64 > LONG_MAX) { + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "over +2147483647"); + } else if (o.via.i64 < LONG_MIN) { + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "under -2147483648"); + } else { + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "%ld", (signed long)o.via.i64); + } +#endif + break; + + case MSGPACK_OBJECT_FLOAT32: + case MSGPACK_OBJECT_FLOAT64: + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "%f", o.via.f64); + break; + + case MSGPACK_OBJECT_STR: + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "\""); + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "%.*s", (int)o.via.str.size, o.via.str.ptr); + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "\""); + break; + + case MSGPACK_OBJECT_BIN: + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "\""); + MSGPACK_CHECKED_CALL(ret, msgpack_object_bin_print_buffer, aux_buffer, aux_buffer_size, o.via.bin.ptr, o.via.bin.size); + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "\""); + break; + + case MSGPACK_OBJECT_EXT: +#if defined(PRIi8) + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "(ext: %" PRIi8 ")", o.via.ext.type); +#else + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "(ext: %d)", (int)o.via.ext.type); +#endif + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "\""); + MSGPACK_CHECKED_CALL(ret, msgpack_object_bin_print_buffer, aux_buffer, aux_buffer_size, o.via.ext.ptr, o.via.ext.size); + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "\""); + break; + + case MSGPACK_OBJECT_ARRAY: + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "["); + if(o.via.array.size != 0) { + msgpack_object* p = o.via.array.ptr; + msgpack_object* const pend = o.via.array.ptr + o.via.array.size; + MSGPACK_CHECKED_CALL(ret, msgpack_object_print_buffer, aux_buffer, aux_buffer_size, *p); + ++p; + for(; p < pend; ++p) { + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, ", "); + MSGPACK_CHECKED_CALL(ret, msgpack_object_print_buffer, aux_buffer, aux_buffer_size, *p); + } + } + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "]"); + break; + + case MSGPACK_OBJECT_MAP: + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "{"); + if(o.via.map.size != 0) { + msgpack_object_kv* p = o.via.map.ptr; + msgpack_object_kv* const pend = o.via.map.ptr + o.via.map.size; + MSGPACK_CHECKED_CALL(ret, msgpack_object_print_buffer, aux_buffer, aux_buffer_size, p->key); + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "=>"); + MSGPACK_CHECKED_CALL(ret, msgpack_object_print_buffer, aux_buffer, aux_buffer_size, p->val); + ++p; + for(; p < pend; ++p) { + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, ", "); + MSGPACK_CHECKED_CALL(ret, msgpack_object_print_buffer, aux_buffer, aux_buffer_size, p->key); + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "=>"); + MSGPACK_CHECKED_CALL(ret, msgpack_object_print_buffer, aux_buffer, aux_buffer_size, p->val); + } + } + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "}"); + break; + + default: + // FIXME +#if defined(PRIu64) + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "#", o.type, o.via.u64); +#else + if (o.via.u64 > ULONG_MAX) { + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "#", o.type); + } else { + MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "#", o.type, (unsigned long)o.via.u64); + } +#endif + } + + return (int)(buffer_size - aux_buffer_size); +} + +#undef MSGPACK_CHECKED_CALL + +bool msgpack_object_equal(const msgpack_object x, const msgpack_object y) +{ + if(x.type != y.type) { return false; } + + switch(x.type) { + case MSGPACK_OBJECT_NIL: + return true; + + case MSGPACK_OBJECT_BOOLEAN: + return x.via.boolean == y.via.boolean; + + case MSGPACK_OBJECT_POSITIVE_INTEGER: + return x.via.u64 == y.via.u64; + + case MSGPACK_OBJECT_NEGATIVE_INTEGER: + return x.via.i64 == y.via.i64; + + case MSGPACK_OBJECT_FLOAT32: + case MSGPACK_OBJECT_FLOAT64: + return x.via.f64 == y.via.f64; + + case MSGPACK_OBJECT_STR: + return x.via.str.size == y.via.str.size && + memcmp(x.via.str.ptr, y.via.str.ptr, x.via.str.size) == 0; + + case MSGPACK_OBJECT_BIN: + return x.via.bin.size == y.via.bin.size && + memcmp(x.via.bin.ptr, y.via.bin.ptr, x.via.bin.size) == 0; + + case MSGPACK_OBJECT_EXT: + return x.via.ext.size == y.via.ext.size && + x.via.ext.type == y.via.ext.type && + memcmp(x.via.ext.ptr, y.via.ext.ptr, x.via.ext.size) == 0; + + case MSGPACK_OBJECT_ARRAY: + if(x.via.array.size != y.via.array.size) { + return false; + } else if(x.via.array.size == 0) { + return true; + } else { + msgpack_object* px = x.via.array.ptr; + msgpack_object* const pxend = x.via.array.ptr + x.via.array.size; + msgpack_object* py = y.via.array.ptr; + do { + if(!msgpack_object_equal(*px, *py)) { + return false; + } + ++px; + ++py; + } while(px < pxend); + return true; + } + + case MSGPACK_OBJECT_MAP: + if(x.via.map.size != y.via.map.size) { + return false; + } else if(x.via.map.size == 0) { + return true; + } else { + msgpack_object_kv* px = x.via.map.ptr; + msgpack_object_kv* const pxend = x.via.map.ptr + x.via.map.size; + msgpack_object_kv* py = y.via.map.ptr; + do { + if(!msgpack_object_equal(px->key, py->key) || !msgpack_object_equal(px->val, py->val)) { + return false; + } + ++px; + ++py; + } while(px < pxend); + return true; + } + + default: + return false; + } +} diff --git a/src/vendor/msgpack/pack.h b/src/vendor/msgpack/pack.h new file mode 100644 index 000000000..08ab84b5e --- /dev/null +++ b/src/vendor/msgpack/pack.h @@ -0,0 +1,174 @@ +/* + * MessagePack for C packing routine + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef MSGPACK_PACK_H +#define MSGPACK_PACK_H + +#include "pack_define.h" +#include "object.h" +#include "timestamp.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup msgpack_buffer Buffers + * @ingroup msgpack + * @{ + * @} + */ + +/** + * @defgroup msgpack_pack Serializer + * @ingroup msgpack + * @{ + */ + +typedef int (*msgpack_packer_write)(void* data, const char* buf, size_t len); + +typedef struct msgpack_packer { + void* data; + msgpack_packer_write callback; +} msgpack_packer; + +static void msgpack_packer_init(msgpack_packer* pk, void* data, msgpack_packer_write callback); + +static msgpack_packer* msgpack_packer_new(void* data, msgpack_packer_write callback); +static void msgpack_packer_free(msgpack_packer* pk); + +static int msgpack_pack_char(msgpack_packer* pk, char d); + +static int msgpack_pack_signed_char(msgpack_packer* pk, signed char d); +static int msgpack_pack_short(msgpack_packer* pk, short d); +static int msgpack_pack_int(msgpack_packer* pk, int d); +static int msgpack_pack_long(msgpack_packer* pk, long d); +static int msgpack_pack_long_long(msgpack_packer* pk, long long d); +static int msgpack_pack_unsigned_char(msgpack_packer* pk, unsigned char d); +static int msgpack_pack_unsigned_short(msgpack_packer* pk, unsigned short d); +static int msgpack_pack_unsigned_int(msgpack_packer* pk, unsigned int d); +static int msgpack_pack_unsigned_long(msgpack_packer* pk, unsigned long d); +static int msgpack_pack_unsigned_long_long(msgpack_packer* pk, unsigned long long d); + +static int msgpack_pack_uint8(msgpack_packer* pk, uint8_t d); +static int msgpack_pack_uint16(msgpack_packer* pk, uint16_t d); +static int msgpack_pack_uint32(msgpack_packer* pk, uint32_t d); +static int msgpack_pack_uint64(msgpack_packer* pk, uint64_t d); +static int msgpack_pack_int8(msgpack_packer* pk, int8_t d); +static int msgpack_pack_int16(msgpack_packer* pk, int16_t d); +static int msgpack_pack_int32(msgpack_packer* pk, int32_t d); +static int msgpack_pack_int64(msgpack_packer* pk, int64_t d); + +static int msgpack_pack_fix_uint8(msgpack_packer* pk, uint8_t d); +static int msgpack_pack_fix_uint16(msgpack_packer* pk, uint16_t d); +static int msgpack_pack_fix_uint32(msgpack_packer* pk, uint32_t d); +static int msgpack_pack_fix_uint64(msgpack_packer* pk, uint64_t d); +static int msgpack_pack_fix_int8(msgpack_packer* pk, int8_t d); +static int msgpack_pack_fix_int16(msgpack_packer* pk, int16_t d); +static int msgpack_pack_fix_int32(msgpack_packer* pk, int32_t d); +static int msgpack_pack_fix_int64(msgpack_packer* pk, int64_t d); + +static int msgpack_pack_float(msgpack_packer* pk, float d); +static int msgpack_pack_double(msgpack_packer* pk, double d); + +static int msgpack_pack_nil(msgpack_packer* pk); +static int msgpack_pack_true(msgpack_packer* pk); +static int msgpack_pack_false(msgpack_packer* pk); + +static int msgpack_pack_array(msgpack_packer* pk, size_t n); + +static int msgpack_pack_map(msgpack_packer* pk, size_t n); + +static int msgpack_pack_str(msgpack_packer* pk, size_t l); +static int msgpack_pack_str_body(msgpack_packer* pk, const void* b, size_t l); +static int msgpack_pack_str_with_body(msgpack_packer* pk, const void* b, size_t l); + +static int msgpack_pack_v4raw(msgpack_packer* pk, size_t l); +static int msgpack_pack_v4raw_body(msgpack_packer* pk, const void* b, size_t l); + +static int msgpack_pack_bin(msgpack_packer* pk, size_t l); +static int msgpack_pack_bin_body(msgpack_packer* pk, const void* b, size_t l); +static int msgpack_pack_bin_with_body(msgpack_packer* pk, const void* b, size_t l); + +static int msgpack_pack_ext(msgpack_packer* pk, size_t l, int8_t type); +static int msgpack_pack_ext_body(msgpack_packer* pk, const void* b, size_t l); +static int msgpack_pack_ext_with_body(msgpack_packer* pk, const void* b, size_t l, int8_t type); + +static int msgpack_pack_timestamp(msgpack_packer* pk, const msgpack_timestamp* d); + +MSGPACK_DLLEXPORT +int msgpack_pack_object(msgpack_packer* pk, msgpack_object d); + + +/** @} */ + + +#define msgpack_pack_inline_func(name) \ + inline int msgpack_pack ## name + +#define msgpack_pack_inline_func_cint(name) \ + inline int msgpack_pack ## name + +#define msgpack_pack_inline_func_fixint(name) \ + inline int msgpack_pack_fix ## name + +#define msgpack_pack_user msgpack_packer* + +#define msgpack_pack_append_buffer(user, buf, len) \ + return (*(user)->callback)((user)->data, (const char*)buf, len) + +#include "pack_template.h" + +inline void msgpack_packer_init(msgpack_packer* pk, void* data, msgpack_packer_write callback) +{ + pk->data = data; + pk->callback = callback; +} + +inline msgpack_packer* msgpack_packer_new(void* data, msgpack_packer_write callback) +{ + msgpack_packer* pk = (msgpack_packer*)calloc(1, sizeof(msgpack_packer)); + if(!pk) { return NULL; } + msgpack_packer_init(pk, data, callback); + return pk; +} + +inline void msgpack_packer_free(msgpack_packer* pk) +{ + free(pk); +} + +inline int msgpack_pack_str_with_body(msgpack_packer* pk, const void* b, size_t l) + { + int ret = msgpack_pack_str(pk, l); + if (ret != 0) { return ret; } + return msgpack_pack_str_body(pk, b, l); + } + + inline int msgpack_pack_bin_with_body(msgpack_packer* pk, const void* b, size_t l) + { + int ret = msgpack_pack_bin(pk, l); + if (ret != 0) { return ret; } + return msgpack_pack_bin_body(pk, b, l); + } + + inline int msgpack_pack_ext_with_body(msgpack_packer* pk, const void* b, size_t l, int8_t type) + { + int ret = msgpack_pack_ext(pk, l, type); + if (ret != 0) { return ret; } + return msgpack_pack_ext_body(pk, b, l); + } + +#ifdef __cplusplus +} +#endif + +#endif /* msgpack/pack.h */ diff --git a/src/vendor/msgpack/pack_define.h b/src/vendor/msgpack/pack_define.h new file mode 100644 index 000000000..5033bb42a --- /dev/null +++ b/src/vendor/msgpack/pack_define.h @@ -0,0 +1,18 @@ +/* + * MessagePack unpacking routine template + * + * Copyright (C) 2008-2010 FURUHASHI Sadayuki + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef MSGPACK_PACK_DEFINE_H +#define MSGPACK_PACK_DEFINE_H + +#include "vendor/msgpack/sysdep.h" +#include +#include + +#endif /* msgpack/pack_define.h */ + diff --git a/src/vendor/msgpack/pack_template.h b/src/vendor/msgpack/pack_template.h new file mode 100644 index 000000000..ac112e1d9 --- /dev/null +++ b/src/vendor/msgpack/pack_template.h @@ -0,0 +1,945 @@ +/* + * MessagePack packing routine template + * + * Copyright (C) 2008-2010 FURUHASHI Sadayuki + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ + +#if MSGPACK_ENDIAN_LITTLE_BYTE +#define TAKE8_8(d) ((uint8_t*)&d)[0] +#define TAKE8_16(d) ((uint8_t*)&d)[0] +#define TAKE8_32(d) ((uint8_t*)&d)[0] +#define TAKE8_64(d) ((uint8_t*)&d)[0] +#elif MSGPACK_ENDIAN_BIG_BYTE +#define TAKE8_8(d) ((uint8_t*)&d)[0] +#define TAKE8_16(d) ((uint8_t*)&d)[1] +#define TAKE8_32(d) ((uint8_t*)&d)[3] +#define TAKE8_64(d) ((uint8_t*)&d)[7] +#else +#error msgpack-c supports only big endian and little endian +#endif + +#ifndef msgpack_pack_inline_func +#error msgpack_pack_inline_func template is not defined +#endif + +#ifndef msgpack_pack_user +#error msgpack_pack_user type is not defined +#endif + +#ifndef msgpack_pack_append_buffer +#error msgpack_pack_append_buffer callback is not defined +#endif + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4204) /* nonstandard extension used: non-constant aggregate initializer */ +#endif + +/* + * Integer + */ + +#define msgpack_pack_real_uint8(x, d) \ +do { \ + if(d < (1<<7)) { \ + /* fixnum */ \ + msgpack_pack_append_buffer(x, &TAKE8_8(d), 1); \ + } else { \ + /* unsigned 8 */ \ + unsigned char buf[2] = {0xcc, TAKE8_8(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } \ +} while(0) + +#define msgpack_pack_real_uint16(x, d) \ +do { \ + if(d < (1<<7)) { \ + /* fixnum */ \ + msgpack_pack_append_buffer(x, &TAKE8_16(d), 1); \ + } else if(d < (1<<8)) { \ + /* unsigned 8 */ \ + unsigned char buf[2] = {0xcc, TAKE8_16(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } else { \ + /* unsigned 16 */ \ + unsigned char buf[3]; \ + buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ + msgpack_pack_append_buffer(x, buf, 3); \ + } \ +} while(0) + +#define msgpack_pack_real_uint32(x, d) \ +do { \ + if(d < (1<<8)) { \ + if(d < (1<<7)) { \ + /* fixnum */ \ + msgpack_pack_append_buffer(x, &TAKE8_32(d), 1); \ + } else { \ + /* unsigned 8 */ \ + unsigned char buf[2] = {0xcc, TAKE8_32(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } \ + } else { \ + if(d < (1<<16)) { \ + /* unsigned 16 */ \ + unsigned char buf[3]; \ + buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ + msgpack_pack_append_buffer(x, buf, 3); \ + } else { \ + /* unsigned 32 */ \ + unsigned char buf[5]; \ + buf[0] = 0xce; _msgpack_store32(&buf[1], (uint32_t)d); \ + msgpack_pack_append_buffer(x, buf, 5); \ + } \ + } \ +} while(0) + +#define msgpack_pack_real_uint64(x, d) \ +do { \ + if(d < (1ULL<<8)) { \ + if(d < (1ULL<<7)) { \ + /* fixnum */ \ + msgpack_pack_append_buffer(x, &TAKE8_64(d), 1); \ + } else { \ + /* unsigned 8 */ \ + unsigned char buf[2] = {0xcc, TAKE8_64(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } \ + } else { \ + if(d < (1ULL<<16)) { \ + /* unsigned 16 */ \ + unsigned char buf[3]; \ + buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ + msgpack_pack_append_buffer(x, buf, 3); \ + } else if(d < (1ULL<<32)) { \ + /* unsigned 32 */ \ + unsigned char buf[5]; \ + buf[0] = 0xce; _msgpack_store32(&buf[1], (uint32_t)d); \ + msgpack_pack_append_buffer(x, buf, 5); \ + } else { \ + /* unsigned 64 */ \ + unsigned char buf[9]; \ + buf[0] = 0xcf; _msgpack_store64(&buf[1], d); \ + msgpack_pack_append_buffer(x, buf, 9); \ + } \ + } \ +} while(0) + +#define msgpack_pack_real_int8(x, d) \ +do { \ + if(d < -(1<<5)) { \ + /* signed 8 */ \ + unsigned char buf[2] = {0xd0, TAKE8_8(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } else { \ + /* fixnum */ \ + msgpack_pack_append_buffer(x, &TAKE8_8(d), 1); \ + } \ +} while(0) + +#define msgpack_pack_real_int16(x, d) \ +do { \ + if(d < -(1<<5)) { \ + if(d < -(1<<7)) { \ + /* signed 16 */ \ + unsigned char buf[3]; \ + buf[0] = 0xd1; _msgpack_store16(&buf[1], (int16_t)d); \ + msgpack_pack_append_buffer(x, buf, 3); \ + } else { \ + /* signed 8 */ \ + unsigned char buf[2] = {0xd0, TAKE8_16(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } \ + } else if(d < (1<<7)) { \ + /* fixnum */ \ + msgpack_pack_append_buffer(x, &TAKE8_16(d), 1); \ + } else { \ + if(d < (1<<8)) { \ + /* unsigned 8 */ \ + unsigned char buf[2] = {0xcc, TAKE8_16(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } else { \ + /* unsigned 16 */ \ + unsigned char buf[3]; \ + buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ + msgpack_pack_append_buffer(x, buf, 3); \ + } \ + } \ +} while(0) + +#define msgpack_pack_real_int32(x, d) \ +do { \ + if(d < -(1<<5)) { \ + if(d < -(1<<15)) { \ + /* signed 32 */ \ + unsigned char buf[5]; \ + buf[0] = 0xd2; _msgpack_store32(&buf[1], (int32_t)d); \ + msgpack_pack_append_buffer(x, buf, 5); \ + } else if(d < -(1<<7)) { \ + /* signed 16 */ \ + unsigned char buf[3]; \ + buf[0] = 0xd1; _msgpack_store16(&buf[1], (int16_t)d); \ + msgpack_pack_append_buffer(x, buf, 3); \ + } else { \ + /* signed 8 */ \ + unsigned char buf[2] = {0xd0, TAKE8_32(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } \ + } else if(d < (1<<7)) { \ + /* fixnum */ \ + msgpack_pack_append_buffer(x, &TAKE8_32(d), 1); \ + } else { \ + if(d < (1<<8)) { \ + /* unsigned 8 */ \ + unsigned char buf[2] = {0xcc, TAKE8_32(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } else if(d < (1<<16)) { \ + /* unsigned 16 */ \ + unsigned char buf[3]; \ + buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ + msgpack_pack_append_buffer(x, buf, 3); \ + } else { \ + /* unsigned 32 */ \ + unsigned char buf[5]; \ + buf[0] = 0xce; _msgpack_store32(&buf[1], (uint32_t)d); \ + msgpack_pack_append_buffer(x, buf, 5); \ + } \ + } \ +} while(0) + +#define msgpack_pack_real_int64(x, d) \ +do { \ + if(d < -(1LL<<5)) { \ + if(d < -(1LL<<15)) { \ + if(d < -(1LL<<31)) { \ + /* signed 64 */ \ + unsigned char buf[9]; \ + buf[0] = 0xd3; _msgpack_store64(&buf[1], d); \ + msgpack_pack_append_buffer(x, buf, 9); \ + } else { \ + /* signed 32 */ \ + unsigned char buf[5]; \ + buf[0] = 0xd2; _msgpack_store32(&buf[1], (int32_t)d); \ + msgpack_pack_append_buffer(x, buf, 5); \ + } \ + } else { \ + if(d < -(1<<7)) { \ + /* signed 16 */ \ + unsigned char buf[3]; \ + buf[0] = 0xd1; _msgpack_store16(&buf[1], (int16_t)d); \ + msgpack_pack_append_buffer(x, buf, 3); \ + } else { \ + /* signed 8 */ \ + unsigned char buf[2] = {0xd0, TAKE8_64(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } \ + } \ + } else if(d < (1<<7)) { \ + /* fixnum */ \ + msgpack_pack_append_buffer(x, &TAKE8_64(d), 1); \ + } else { \ + if(d < (1LL<<16)) { \ + if(d < (1<<8)) { \ + /* unsigned 8 */ \ + unsigned char buf[2] = {0xcc, TAKE8_64(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } else { \ + /* unsigned 16 */ \ + unsigned char buf[3]; \ + buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ + msgpack_pack_append_buffer(x, buf, 3); \ + } \ + } else { \ + if(d < (1LL<<32)) { \ + /* unsigned 32 */ \ + unsigned char buf[5]; \ + buf[0] = 0xce; _msgpack_store32(&buf[1], (uint32_t)d); \ + msgpack_pack_append_buffer(x, buf, 5); \ + } else { \ + /* unsigned 64 */ \ + unsigned char buf[9]; \ + buf[0] = 0xcf; _msgpack_store64(&buf[1], d); \ + msgpack_pack_append_buffer(x, buf, 9); \ + } \ + } \ + } \ +} while(0) + + +#ifdef msgpack_pack_inline_func_fixint + +msgpack_pack_inline_func_fixint(_uint8)(msgpack_pack_user x, uint8_t d) +{ + unsigned char buf[2] = {0xcc, TAKE8_8(d)}; + msgpack_pack_append_buffer(x, buf, 2); +} + +msgpack_pack_inline_func_fixint(_uint16)(msgpack_pack_user x, uint16_t d) +{ + unsigned char buf[3]; + buf[0] = 0xcd; _msgpack_store16(&buf[1], d); + msgpack_pack_append_buffer(x, buf, 3); +} + +msgpack_pack_inline_func_fixint(_uint32)(msgpack_pack_user x, uint32_t d) +{ + unsigned char buf[5]; + buf[0] = 0xce; _msgpack_store32(&buf[1], d); + msgpack_pack_append_buffer(x, buf, 5); +} + +msgpack_pack_inline_func_fixint(_uint64)(msgpack_pack_user x, uint64_t d) +{ + unsigned char buf[9]; + buf[0] = 0xcf; _msgpack_store64(&buf[1], d); + msgpack_pack_append_buffer(x, buf, 9); +} + +msgpack_pack_inline_func_fixint(_int8)(msgpack_pack_user x, int8_t d) +{ + unsigned char buf[2] = {0xd0, TAKE8_8(d)}; + msgpack_pack_append_buffer(x, buf, 2); +} + +msgpack_pack_inline_func_fixint(_int16)(msgpack_pack_user x, int16_t d) +{ + unsigned char buf[3]; + buf[0] = 0xd1; _msgpack_store16(&buf[1], d); + msgpack_pack_append_buffer(x, buf, 3); +} + +msgpack_pack_inline_func_fixint(_int32)(msgpack_pack_user x, int32_t d) +{ + unsigned char buf[5]; + buf[0] = 0xd2; _msgpack_store32(&buf[1], d); + msgpack_pack_append_buffer(x, buf, 5); +} + +msgpack_pack_inline_func_fixint(_int64)(msgpack_pack_user x, int64_t d) +{ + unsigned char buf[9]; + buf[0] = 0xd3; _msgpack_store64(&buf[1], d); + msgpack_pack_append_buffer(x, buf, 9); +} + +#undef msgpack_pack_inline_func_fixint +#endif + + +msgpack_pack_inline_func(_uint8)(msgpack_pack_user x, uint8_t d) +{ + msgpack_pack_real_uint8(x, d); +} + +msgpack_pack_inline_func(_uint16)(msgpack_pack_user x, uint16_t d) +{ + msgpack_pack_real_uint16(x, d); +} + +msgpack_pack_inline_func(_uint32)(msgpack_pack_user x, uint32_t d) +{ + msgpack_pack_real_uint32(x, d); +} + +msgpack_pack_inline_func(_uint64)(msgpack_pack_user x, uint64_t d) +{ + msgpack_pack_real_uint64(x, d); +} + +msgpack_pack_inline_func(_int8)(msgpack_pack_user x, int8_t d) +{ + msgpack_pack_real_int8(x, d); +} + +msgpack_pack_inline_func(_int16)(msgpack_pack_user x, int16_t d) +{ + msgpack_pack_real_int16(x, d); +} + +msgpack_pack_inline_func(_int32)(msgpack_pack_user x, int32_t d) +{ + msgpack_pack_real_int32(x, d); +} + +msgpack_pack_inline_func(_int64)(msgpack_pack_user x, int64_t d) +{ + msgpack_pack_real_int64(x, d); +} + +msgpack_pack_inline_func(_char)(msgpack_pack_user x, char d) +{ +#if defined(CHAR_MIN) +#if CHAR_MIN < 0 + msgpack_pack_real_int8(x, d); +#else + msgpack_pack_real_uint8(x, d); +#endif +#else +#error CHAR_MIN is not defined +#endif +} + +msgpack_pack_inline_func(_signed_char)(msgpack_pack_user x, signed char d) +{ + msgpack_pack_real_int8(x, d); +} + +msgpack_pack_inline_func(_unsigned_char)(msgpack_pack_user x, unsigned char d) +{ + msgpack_pack_real_uint8(x, d); +} + +#ifdef msgpack_pack_inline_func_cint + +msgpack_pack_inline_func_cint(_short)(msgpack_pack_user x, short d) +{ +#if defined(SIZEOF_SHORT) +#if SIZEOF_SHORT == 2 + msgpack_pack_real_int16(x, d); +#elif SIZEOF_SHORT == 4 + msgpack_pack_real_int32(x, d); +#else + msgpack_pack_real_int64(x, d); +#endif + +#elif defined(SHRT_MAX) +#if SHRT_MAX == 0x7fff + msgpack_pack_real_int16(x, d); +#elif SHRT_MAX == 0x7fffffff + msgpack_pack_real_int32(x, d); +#else + msgpack_pack_real_int64(x, d); +#endif + +#else +if(sizeof(short) == 2) { + msgpack_pack_real_int16(x, d); +} else if(sizeof(short) == 4) { + msgpack_pack_real_int32(x, d); +} else { + msgpack_pack_real_int64(x, d); +} +#endif +} + +msgpack_pack_inline_func_cint(_int)(msgpack_pack_user x, int d) +{ +#if defined(SIZEOF_INT) +#if SIZEOF_INT == 2 + msgpack_pack_real_int16(x, d); +#elif SIZEOF_INT == 4 + msgpack_pack_real_int32(x, d); +#else + msgpack_pack_real_int64(x, d); +#endif + +#elif defined(INT_MAX) +#if INT_MAX == 0x7fff + msgpack_pack_real_int16(x, d); +#elif INT_MAX == 0x7fffffff + msgpack_pack_real_int32(x, d); +#else + msgpack_pack_real_int64(x, d); +#endif + +#else +if(sizeof(int) == 2) { + msgpack_pack_real_int16(x, d); +} else if(sizeof(int) == 4) { + msgpack_pack_real_int32(x, d); +} else { + msgpack_pack_real_int64(x, d); +} +#endif +} + +msgpack_pack_inline_func_cint(_long)(msgpack_pack_user x, long d) +{ +#if defined(SIZEOF_LONG) +#if SIZEOF_LONG == 2 + msgpack_pack_real_int16(x, d); +#elif SIZEOF_LONG == 4 + msgpack_pack_real_int32(x, d); +#else + msgpack_pack_real_int64(x, d); +#endif + +#elif defined(LONG_MAX) +#if LONG_MAX == 0x7fffL + msgpack_pack_real_int16(x, d); +#elif LONG_MAX == 0x7fffffffL + msgpack_pack_real_int32(x, d); +#else + msgpack_pack_real_int64(x, d); +#endif + +#else +if(sizeof(long) == 2) { + msgpack_pack_real_int16(x, d); +} else if(sizeof(long) == 4) { + msgpack_pack_real_int32(x, d); +} else { + msgpack_pack_real_int64(x, d); +} +#endif +} + +msgpack_pack_inline_func_cint(_long_long)(msgpack_pack_user x, long long d) +{ +#if defined(SIZEOF_LONG_LONG) +#if SIZEOF_LONG_LONG == 2 + msgpack_pack_real_int16(x, d); +#elif SIZEOF_LONG_LONG == 4 + msgpack_pack_real_int32(x, d); +#else + msgpack_pack_real_int64(x, d); +#endif + +#elif defined(LLONG_MAX) +#if LLONG_MAX == 0x7fffL + msgpack_pack_real_int16(x, d); +#elif LLONG_MAX == 0x7fffffffL + msgpack_pack_real_int32(x, d); +#else + msgpack_pack_real_int64(x, d); +#endif + +#else +if(sizeof(long long) == 2) { + msgpack_pack_real_int16(x, d); +} else if(sizeof(long long) == 4) { + msgpack_pack_real_int32(x, d); +} else { + msgpack_pack_real_int64(x, d); +} +#endif +} + +msgpack_pack_inline_func_cint(_unsigned_short)(msgpack_pack_user x, unsigned short d) +{ +#if defined(SIZEOF_SHORT) +#if SIZEOF_SHORT == 2 + msgpack_pack_real_uint16(x, d); +#elif SIZEOF_SHORT == 4 + msgpack_pack_real_uint32(x, d); +#else + msgpack_pack_real_uint64(x, d); +#endif + +#elif defined(USHRT_MAX) +#if USHRT_MAX == 0xffffU + msgpack_pack_real_uint16(x, d); +#elif USHRT_MAX == 0xffffffffU + msgpack_pack_real_uint32(x, d); +#else + msgpack_pack_real_uint64(x, d); +#endif + +#else +if(sizeof(unsigned short) == 2) { + msgpack_pack_real_uint16(x, d); +} else if(sizeof(unsigned short) == 4) { + msgpack_pack_real_uint32(x, d); +} else { + msgpack_pack_real_uint64(x, d); +} +#endif +} + +msgpack_pack_inline_func_cint(_unsigned_int)(msgpack_pack_user x, unsigned int d) +{ +#if defined(SIZEOF_INT) +#if SIZEOF_INT == 2 + msgpack_pack_real_uint16(x, d); +#elif SIZEOF_INT == 4 + msgpack_pack_real_uint32(x, d); +#else + msgpack_pack_real_uint64(x, d); +#endif + +#elif defined(UINT_MAX) +#if UINT_MAX == 0xffffU + msgpack_pack_real_uint16(x, d); +#elif UINT_MAX == 0xffffffffU + msgpack_pack_real_uint32(x, d); +#else + msgpack_pack_real_uint64(x, d); +#endif + +#else +if(sizeof(unsigned int) == 2) { + msgpack_pack_real_uint16(x, d); +} else if(sizeof(unsigned int) == 4) { + msgpack_pack_real_uint32(x, d); +} else { + msgpack_pack_real_uint64(x, d); +} +#endif +} + +msgpack_pack_inline_func_cint(_unsigned_long)(msgpack_pack_user x, unsigned long d) +{ +#if defined(SIZEOF_LONG) +#if SIZEOF_LONG == 2 + msgpack_pack_real_uint16(x, d); +#elif SIZEOF_LONG == 4 + msgpack_pack_real_uint32(x, d); +#else + msgpack_pack_real_uint64(x, d); +#endif + +#elif defined(ULONG_MAX) +#if ULONG_MAX == 0xffffUL + msgpack_pack_real_uint16(x, d); +#elif ULONG_MAX == 0xffffffffUL + msgpack_pack_real_uint32(x, d); +#else + msgpack_pack_real_uint64(x, d); +#endif + +#else +if(sizeof(unsigned long) == 2) { + msgpack_pack_real_uint16(x, d); +} else if(sizeof(unsigned long) == 4) { + msgpack_pack_real_uint32(x, d); +} else { + msgpack_pack_real_uint64(x, d); +} +#endif +} + +msgpack_pack_inline_func_cint(_unsigned_long_long)(msgpack_pack_user x, unsigned long long d) +{ +#if defined(SIZEOF_LONG_LONG) +#if SIZEOF_LONG_LONG == 2 + msgpack_pack_real_uint16(x, d); +#elif SIZEOF_LONG_LONG == 4 + msgpack_pack_real_uint32(x, d); +#else + msgpack_pack_real_uint64(x, d); +#endif + +#elif defined(ULLONG_MAX) +#if ULLONG_MAX == 0xffffUL + msgpack_pack_real_uint16(x, d); +#elif ULLONG_MAX == 0xffffffffUL + msgpack_pack_real_uint32(x, d); +#else + msgpack_pack_real_uint64(x, d); +#endif + +#else +if(sizeof(unsigned long long) == 2) { + msgpack_pack_real_uint16(x, d); +} else if(sizeof(unsigned long long) == 4) { + msgpack_pack_real_uint32(x, d); +} else { + msgpack_pack_real_uint64(x, d); +} +#endif +} + +#undef msgpack_pack_inline_func_cint +#endif + + + +/* + * Float + */ + +msgpack_pack_inline_func(_float)(msgpack_pack_user x, float d) +{ + unsigned char buf[5]; + union { float f; uint32_t i; } mem; + mem.f = d; + buf[0] = 0xca; _msgpack_store32(&buf[1], mem.i); + msgpack_pack_append_buffer(x, buf, 5); +} + +msgpack_pack_inline_func(_double)(msgpack_pack_user x, double d) +{ + unsigned char buf[9]; + union { double f; uint64_t i; } mem; + mem.f = d; + buf[0] = 0xcb; +#if defined(TARGET_OS_IPHONE) + // ok +#elif defined(__arm__) && !(__ARM_EABI__) // arm-oabi + // https://github.com/msgpack/msgpack-perl/pull/1 + mem.i = (mem.i & 0xFFFFFFFFUL) << 32UL | (mem.i >> 32UL); +#endif + _msgpack_store64(&buf[1], mem.i); + msgpack_pack_append_buffer(x, buf, 9); +} + + +/* + * Nil + */ + +msgpack_pack_inline_func(_nil)(msgpack_pack_user x) +{ + static const unsigned char d = 0xc0; + msgpack_pack_append_buffer(x, &d, 1); +} + + +/* + * Boolean + */ + +msgpack_pack_inline_func(_true)(msgpack_pack_user x) +{ + static const unsigned char d = 0xc3; + msgpack_pack_append_buffer(x, &d, 1); +} + +msgpack_pack_inline_func(_false)(msgpack_pack_user x) +{ + static const unsigned char d = 0xc2; + msgpack_pack_append_buffer(x, &d, 1); +} + + +/* + * Array + */ + +msgpack_pack_inline_func(_array)(msgpack_pack_user x, size_t n) +{ + if(n < 16) { + unsigned char d = 0x90 | (uint8_t)n; + msgpack_pack_append_buffer(x, &d, 1); + } else if(n < 65536) { + unsigned char buf[3]; + buf[0] = 0xdc; _msgpack_store16(&buf[1], (uint16_t)n); + msgpack_pack_append_buffer(x, buf, 3); + } else { + unsigned char buf[5]; + buf[0] = 0xdd; _msgpack_store32(&buf[1], (uint32_t)n); + msgpack_pack_append_buffer(x, buf, 5); + } +} + + +/* + * Map + */ + +msgpack_pack_inline_func(_map)(msgpack_pack_user x, size_t n) +{ + if(n < 16) { + unsigned char d = 0x80 | (uint8_t)n; + msgpack_pack_append_buffer(x, &TAKE8_8(d), 1); + } else if(n < 65536) { + unsigned char buf[3]; + buf[0] = 0xde; _msgpack_store16(&buf[1], (uint16_t)n); + msgpack_pack_append_buffer(x, buf, 3); + } else { + unsigned char buf[5]; + buf[0] = 0xdf; _msgpack_store32(&buf[1], (uint32_t)n); + msgpack_pack_append_buffer(x, buf, 5); + } +} + + +/* + * Str + */ + +msgpack_pack_inline_func(_str)(msgpack_pack_user x, size_t l) +{ + if(l < 32) { + unsigned char d = 0xa0 | (uint8_t)l; + msgpack_pack_append_buffer(x, &TAKE8_8(d), 1); + } else if(l < 256) { + unsigned char buf[2]; + buf[0] = 0xd9; buf[1] = (uint8_t)l; + msgpack_pack_append_buffer(x, buf, 2); + } else if(l < 65536) { + unsigned char buf[3]; + buf[0] = 0xda; _msgpack_store16(&buf[1], (uint16_t)l); + msgpack_pack_append_buffer(x, buf, 3); + } else { + unsigned char buf[5]; + buf[0] = 0xdb; _msgpack_store32(&buf[1], (uint32_t)l); + msgpack_pack_append_buffer(x, buf, 5); + } +} + +msgpack_pack_inline_func(_str_body)(msgpack_pack_user x, const void* b, size_t l) +{ + msgpack_pack_append_buffer(x, (const unsigned char*)b, l); +} + +/* + * Raw (V4) + */ + +msgpack_pack_inline_func(_v4raw)(msgpack_pack_user x, size_t l) +{ + if(l < 32) { + unsigned char d = 0xa0 | (uint8_t)l; + msgpack_pack_append_buffer(x, &TAKE8_8(d), 1); + } else if(l < 65536) { + unsigned char buf[3]; + buf[0] = 0xda; _msgpack_store16(&buf[1], (uint16_t)l); + msgpack_pack_append_buffer(x, buf, 3); + } else { + unsigned char buf[5]; + buf[0] = 0xdb; _msgpack_store32(&buf[1], (uint32_t)l); + msgpack_pack_append_buffer(x, buf, 5); + } +} + +msgpack_pack_inline_func(_v4raw_body)(msgpack_pack_user x, const void* b, size_t l) +{ + msgpack_pack_append_buffer(x, (const unsigned char*)b, l); +} + +/* + * Bin + */ + +msgpack_pack_inline_func(_bin)(msgpack_pack_user x, size_t l) +{ + if(l < 256) { + unsigned char buf[2]; + buf[0] = 0xc4; buf[1] = (uint8_t)l; + msgpack_pack_append_buffer(x, buf, 2); + } else if(l < 65536) { + unsigned char buf[3]; + buf[0] = 0xc5; _msgpack_store16(&buf[1], (uint16_t)l); + msgpack_pack_append_buffer(x, buf, 3); + } else { + unsigned char buf[5]; + buf[0] = 0xc6; _msgpack_store32(&buf[1], (uint32_t)l); + msgpack_pack_append_buffer(x, buf, 5); + } +} + +msgpack_pack_inline_func(_bin_body)(msgpack_pack_user x, const void* b, size_t l) +{ + msgpack_pack_append_buffer(x, (const unsigned char*)b, l); +} + +/* + * Ext + */ + +msgpack_pack_inline_func(_ext)(msgpack_pack_user x, size_t l, int8_t type) +{ + switch(l) { + case 1: { + unsigned char buf[2]; + buf[0] = 0xd4; + buf[1] = (unsigned char)type; + msgpack_pack_append_buffer(x, buf, 2); + } break; + case 2: { + unsigned char buf[2]; + buf[0] = 0xd5; + buf[1] = (unsigned char)type; + msgpack_pack_append_buffer(x, buf, 2); + } break; + case 4: { + unsigned char buf[2]; + buf[0] = 0xd6; + buf[1] = (unsigned char)type; + msgpack_pack_append_buffer(x, buf, 2); + } break; + case 8: { + unsigned char buf[2]; + buf[0] = 0xd7; + buf[1] = (unsigned char)type; + msgpack_pack_append_buffer(x, buf, 2); + } break; + case 16: { + unsigned char buf[2]; + buf[0] = 0xd8; + buf[1] = (unsigned char)type; + msgpack_pack_append_buffer(x, buf, 2); + } break; + default: + if(l < 256) { + unsigned char buf[3]; + buf[0] = 0xc7; + buf[1] = (unsigned char)l; + buf[2] = (unsigned char)type; + msgpack_pack_append_buffer(x, buf, 3); + } else if(l < 65536) { + unsigned char buf[4]; + buf[0] = 0xc8; + _msgpack_store16(&buf[1], l); + buf[3] = (unsigned char)type; + msgpack_pack_append_buffer(x, buf, 4); + } else { + unsigned char buf[6]; + buf[0] = 0xc9; + _msgpack_store32(&buf[1], l); + buf[5] = (unsigned char)type; + msgpack_pack_append_buffer(x, buf, 6); + } + break; + } +} + +msgpack_pack_inline_func(_ext_body)(msgpack_pack_user x, const void* b, size_t l) +{ + msgpack_pack_append_buffer(x, (const unsigned char*)b, l); +} + +msgpack_pack_inline_func(_timestamp)(msgpack_pack_user x, const msgpack_timestamp* d) +{ + if ((((int64_t)d->tv_sec) >> 34) == 0) { + uint64_t data64 = ((uint64_t) d->tv_nsec << 34) | (uint64_t)d->tv_sec; + if ((data64 & 0xffffffff00000000L) == 0) { + // timestamp 32 + char buf[4]; + uint32_t data32 = (uint32_t)data64; + msgpack_pack_ext(x, 4, -1); + _msgpack_store32(buf, data32); + msgpack_pack_append_buffer(x, buf, 4); + } else { + // timestamp 64 + char buf[8]; + msgpack_pack_ext(x, 8, -1); + _msgpack_store64(buf, data64); + msgpack_pack_append_buffer(x, buf, 8); + } + } else { + // timestamp 96 + char buf[12]; + _msgpack_store32(&buf[0], d->tv_nsec); + _msgpack_store64(&buf[4], d->tv_sec); + msgpack_pack_ext(x, 12, -1); + msgpack_pack_append_buffer(x, buf, 12); + } +} + +#undef msgpack_pack_inline_func +#undef msgpack_pack_user +#undef msgpack_pack_append_buffer + +#undef TAKE8_8 +#undef TAKE8_16 +#undef TAKE8_32 +#undef TAKE8_64 + +#undef msgpack_pack_real_uint8 +#undef msgpack_pack_real_uint16 +#undef msgpack_pack_real_uint32 +#undef msgpack_pack_real_uint64 +#undef msgpack_pack_real_int8 +#undef msgpack_pack_real_int16 +#undef msgpack_pack_real_int32 +#undef msgpack_pack_real_int64 + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif diff --git a/src/vendor/msgpack/predef.h b/src/vendor/msgpack/predef.h new file mode 100644 index 000000000..b2309eef7 --- /dev/null +++ b/src/vendor/msgpack/predef.h @@ -0,0 +1,24 @@ +/* +Copyright Rene Rivera 2008-2015 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE_1_0.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +*/ + +#if !defined(MSGPACK_PREDEF_H) || defined(MSGPACK_PREDEF_INTERNAL_GENERATE_TESTS) +#ifndef MSGPACK_PREDEF_H +#define MSGPACK_PREDEF_H +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#endif diff --git a/src/vendor/msgpack/sbuffer.h b/src/vendor/msgpack/sbuffer.h new file mode 100644 index 000000000..c494bae77 --- /dev/null +++ b/src/vendor/msgpack/sbuffer.h @@ -0,0 +1,110 @@ +/* + * MessagePack for C simple buffer implementation + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef MSGPACK_SBUFFER_H +#define MSGPACK_SBUFFER_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup msgpack_sbuffer Simple buffer + * @ingroup msgpack_buffer + * @{ + */ + +typedef struct msgpack_sbuffer { + size_t size; + char* data; + size_t alloc; +} msgpack_sbuffer; + +static inline void msgpack_sbuffer_init(msgpack_sbuffer* sbuf) +{ + memset(sbuf, 0, sizeof(msgpack_sbuffer)); +} + +static inline void msgpack_sbuffer_destroy(msgpack_sbuffer* sbuf) +{ + free(sbuf->data); +} + +static inline msgpack_sbuffer* msgpack_sbuffer_new(void) +{ + return (msgpack_sbuffer*)calloc(1, sizeof(msgpack_sbuffer)); +} + +static inline void msgpack_sbuffer_free(msgpack_sbuffer* sbuf) +{ + if(sbuf == NULL) { return; } + msgpack_sbuffer_destroy(sbuf); + free(sbuf); +} + +#ifndef MSGPACK_SBUFFER_INIT_SIZE +#define MSGPACK_SBUFFER_INIT_SIZE 8192 +#endif + +static inline int msgpack_sbuffer_write(void* data, const char* buf, size_t len) +{ + msgpack_sbuffer* sbuf = (msgpack_sbuffer*)data; + + if(sbuf->alloc - sbuf->size < len) { + void* tmp; + size_t nsize = (sbuf->alloc) ? + sbuf->alloc * 2 : MSGPACK_SBUFFER_INIT_SIZE; + + while(nsize < sbuf->size + len) { + size_t tmp_nsize = nsize * 2; + if (tmp_nsize <= nsize) { + nsize = sbuf->size + len; + break; + } + nsize = tmp_nsize; + } + + tmp = realloc(sbuf->data, nsize); + if(!tmp) { return -1; } + + sbuf->data = (char*)tmp; + sbuf->alloc = nsize; + } + + memcpy(sbuf->data + sbuf->size, buf, len); + sbuf->size += len; + return 0; +} + +static inline char* msgpack_sbuffer_release(msgpack_sbuffer* sbuf) +{ + char* tmp = sbuf->data; + sbuf->size = 0; + sbuf->data = NULL; + sbuf->alloc = 0; + return tmp; +} + +static inline void msgpack_sbuffer_clear(msgpack_sbuffer* sbuf) +{ + sbuf->size = 0; +} + +/** @} */ + + +#ifdef __cplusplus +} +#endif + +#endif /* msgpack/sbuffer.h */ diff --git a/src/vendor/msgpack/sysdep.h b/src/vendor/msgpack/sysdep.h new file mode 100644 index 000000000..ad5f615aa --- /dev/null +++ b/src/vendor/msgpack/sysdep.h @@ -0,0 +1,216 @@ +/* + * MessagePack system dependencies + * + * Copyright (C) 2008-2010 FURUHASHI Sadayuki + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef MSGPACK_SYSDEP_H +#define MSGPACK_SYSDEP_H + +#include +#include + +#include "vendor/msgpack/lttng-config.h" + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +# define snprintf(buf, len, format,...) _snprintf_s(buf, len, _TRUNCATE, format, __VA_ARGS__) +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1600 + typedef signed __int8 int8_t; + typedef unsigned __int8 uint8_t; + typedef signed __int16 int16_t; + typedef unsigned __int16 uint16_t; + typedef signed __int32 int32_t; + typedef unsigned __int32 uint32_t; + typedef signed __int64 int64_t; + typedef unsigned __int64 uint64_t; +#elif defined(_MSC_VER) // && _MSC_VER >= 1600 +# include +#else +# include +# include +#endif + +#if !defined(MSGPACK_DLLEXPORT) +#if defined(_MSC_VER) +# define MSGPACK_DLLEXPORT __declspec(dllexport) +#else /* _MSC_VER */ +# define MSGPACK_DLLEXPORT +#endif /* _MSC_VER */ +#endif + +#ifdef _WIN32 +# if defined(_KERNEL_MODE) +# define _msgpack_atomic_counter_header +# else +# define _msgpack_atomic_counter_header +# if !defined(WIN32_LEAN_AND_MEAN) +# define WIN32_LEAN_AND_MEAN +# endif /* WIN32_LEAN_AND_MEAN */ +# endif + typedef long _msgpack_atomic_counter_t; +#if defined(_AMD64_) || defined(_M_X64) || defined(_M_ARM64) +# define _msgpack_sync_decr_and_fetch(ptr) _InterlockedDecrement(ptr) +# define _msgpack_sync_incr_and_fetch(ptr) _InterlockedIncrement(ptr) +#else +# define _msgpack_sync_decr_and_fetch(ptr) InterlockedDecrement(ptr) +# define _msgpack_sync_incr_and_fetch(ptr) InterlockedIncrement(ptr) +#endif +#elif defined(__GNUC__) && ((__GNUC__*10 + __GNUC_MINOR__) < 41) + +# if defined(__cplusplus) +# define _msgpack_atomic_counter_header "vendor/msgpack/gcc_atomic.hpp" +# else +# define _msgpack_atomic_counter_header "vendor/msgpack/gcc_atomic.h" +# endif + +#else + typedef unsigned int _msgpack_atomic_counter_t; +# define _msgpack_sync_decr_and_fetch(ptr) __sync_sub_and_fetch(ptr, 1) +# define _msgpack_sync_incr_and_fetch(ptr) __sync_add_and_fetch(ptr, 1) +#endif + +#ifdef _WIN32 + +# ifdef __cplusplus + /* numeric_limits::min,max */ +# ifdef max +# undef max +# endif +# ifdef min +# undef min +# endif +# endif + +#elif defined(unix) || defined(__unix) || defined(__APPLE__) || defined(__OpenBSD__) + +#include /* __BYTE_ORDER */ +# if defined(linux) +# include +# endif + +#endif + +#if !defined(MSGPACK_ENDIAN_LITTLE_BYTE) && !defined(MSGPACK_ENDIAN_BIG_BYTE) +#include +#endif // !defined(MSGPACK_ENDIAN_LITTLE_BYTE) && !defined(MSGPACK_ENDIAN_BIG_BYTE) + +#if MSGPACK_ENDIAN_LITTLE_BYTE + +# if defined(unix) || defined(__unix) || defined(__APPLE__) || defined(__OpenBSD__) +# define _msgpack_be16(x) ntohs((uint16_t)x) +# else +# if defined(ntohs) +# define _msgpack_be16(x) ntohs(x) +# elif defined(_byteswap_ushort) || (defined(_MSC_VER) && _MSC_VER >= 1400) +# define _msgpack_be16(x) ((uint16_t)_byteswap_ushort((unsigned short)x)) +# else +# define _msgpack_be16(x) ( \ + ((((uint16_t)x) << 8) ) | \ + ((((uint16_t)x) >> 8) ) ) +# endif +# endif + +# if defined(unix) || defined(__unix) || defined(__APPLE__) || defined(__OpenBSD__) +# define _msgpack_be32(x) ntohl((uint32_t)x) +# else +# if defined(ntohl) +# define _msgpack_be32(x) ntohl(x) +# elif defined(_byteswap_ulong) || (defined(_MSC_VER) && _MSC_VER >= 1400) +# define _msgpack_be32(x) ((uint32_t)_byteswap_ulong((unsigned long)x)) +# else +# define _msgpack_be32(x) \ + ( ((((uint32_t)x) << 24) ) | \ + ((((uint32_t)x) << 8) & 0x00ff0000U ) | \ + ((((uint32_t)x) >> 8) & 0x0000ff00U ) | \ + ((((uint32_t)x) >> 24) ) ) +# endif +# endif + +# if defined(_byteswap_uint64) || (defined(_MSC_VER) && _MSC_VER >= 1400) +# define _msgpack_be64(x) (_byteswap_uint64(x)) +# elif defined(bswap_64) +# define _msgpack_be64(x) bswap_64(x) +# elif defined(__DARWIN_OSSwapInt64) +# define _msgpack_be64(x) __DARWIN_OSSwapInt64(x) +# else +# define _msgpack_be64(x) \ + ( ((((uint64_t)x) << 56) ) | \ + ((((uint64_t)x) << 40) & 0x00ff000000000000ULL ) | \ + ((((uint64_t)x) << 24) & 0x0000ff0000000000ULL ) | \ + ((((uint64_t)x) << 8) & 0x000000ff00000000ULL ) | \ + ((((uint64_t)x) >> 8) & 0x00000000ff000000ULL ) | \ + ((((uint64_t)x) >> 24) & 0x0000000000ff0000ULL ) | \ + ((((uint64_t)x) >> 40) & 0x000000000000ff00ULL ) | \ + ((((uint64_t)x) >> 56) ) ) +# endif + +#elif MSGPACK_ENDIAN_BIG_BYTE + +# define _msgpack_be16(x) (x) +# define _msgpack_be32(x) (x) +# define _msgpack_be64(x) (x) + +#else +# error msgpack-c supports only big endian and little endian +#endif /* MSGPACK_ENDIAN_LITTLE_BYTE */ + +#define _msgpack_load16(cast, from, to) do { \ + memcpy((cast*)(to), (from), sizeof(cast)); \ + *(to) = (cast)_msgpack_be16(*(to)); \ + } while (0); + +#define _msgpack_load32(cast, from, to) do { \ + memcpy((cast*)(to), (from), sizeof(cast)); \ + *(to) = (cast)_msgpack_be32(*(to)); \ + } while (0); +#define _msgpack_load64(cast, from, to) do { \ + memcpy((cast*)(to), (from), sizeof(cast)); \ + *(to) = (cast)_msgpack_be64(*(to)); \ + } while (0); + +#define _msgpack_store16(to, num) \ + do { uint16_t val = _msgpack_be16(num); memcpy(to, &val, 2); } while(0) +#define _msgpack_store32(to, num) \ + do { uint32_t val = _msgpack_be32(num); memcpy(to, &val, 4); } while(0) +#define _msgpack_store64(to, num) \ + do { uint64_t val = _msgpack_be64(num); memcpy(to, &val, 8); } while(0) + +/* +#define _msgpack_load16(cast, from) \ + ({ cast val; memcpy(&val, (char*)from, 2); _msgpack_be16(val); }) +#define _msgpack_load32(cast, from) \ + ({ cast val; memcpy(&val, (char*)from, 4); _msgpack_be32(val); }) +#define _msgpack_load64(cast, from) \ + ({ cast val; memcpy(&val, (char*)from, 8); _msgpack_be64(val); }) +*/ + + +#if !defined(__cplusplus) && defined(_MSC_VER) +# if !defined(_KERNEL_MODE) +# if !defined(FALSE) +# define FALSE (0) +# endif +# if !defined(TRUE) +# define TRUE (!FALSE) +# endif +# endif +# if _MSC_VER >= 1800 +# include +# else +# define bool int +# define true TRUE +# define false FALSE +# endif +# define inline __inline +#endif + +#ifdef __APPLE__ +# include +#endif + +#endif /* msgpack/sysdep.h */ diff --git a/src/vendor/msgpack/timestamp.h b/src/vendor/msgpack/timestamp.h new file mode 100644 index 000000000..df94253cb --- /dev/null +++ b/src/vendor/msgpack/timestamp.h @@ -0,0 +1,58 @@ +/* + * MessagePack for C TimeStamp + * + * Copyright (C) 2018 KONDO Takatoshi + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef MSGPACK_TIMESTAMP_H +#define MSGPACK_TIMESTAMP_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct msgpack_timestamp { + int64_t tv_sec; + uint32_t tv_nsec; +} msgpack_timestamp; + +static inline bool msgpack_object_to_timestamp(const msgpack_object* obj, msgpack_timestamp* ts) { + if (obj->type != MSGPACK_OBJECT_EXT) return false; + if (obj->via.ext.type != -1) return false; + switch (obj->via.ext.size) { + case 4: + ts->tv_nsec = 0; + { + uint32_t v; + _msgpack_load32(uint32_t, obj->via.ext.ptr, &v); + ts->tv_sec = v; + } + return true; + case 8: { + uint64_t value; + _msgpack_load64(uint64_t, obj->via.ext.ptr, &value); + ts->tv_nsec = (uint32_t)(value >> 34); + ts->tv_sec = value & 0x00000003ffffffffLL; + return true; + } + case 12: + _msgpack_load32(uint32_t, obj->via.ext.ptr, &ts->tv_nsec); + _msgpack_load64(int64_t, obj->via.ext.ptr + 4, &ts->tv_sec); + return true; + default: + return false; + } +} + + +#ifdef __cplusplus +} +#endif + +#endif /* msgpack/timestamp.h */ diff --git a/src/vendor/msgpack/unpack.c b/src/vendor/msgpack/unpack.c new file mode 100644 index 000000000..b390f5868 --- /dev/null +++ b/src/vendor/msgpack/unpack.c @@ -0,0 +1,702 @@ +/* + * MessagePack for C unpacking routine + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +#include "vendor/msgpack/unpack.h" +#include "vendor/msgpack/unpack_define.h" +#include "vendor/msgpack/util.h" +#include + +#ifdef _msgpack_atomic_counter_header +#include _msgpack_atomic_counter_header +#endif + + +typedef struct { + msgpack_zone** z; + bool referenced; +} unpack_user; + + +#define msgpack_unpack_struct(name) \ + struct template ## name + +#define msgpack_unpack_func(ret, name) \ + ret template ## name + +#define msgpack_unpack_callback(name) \ + template_callback ## name + +#define msgpack_unpack_object msgpack_object + +#define msgpack_unpack_user unpack_user + + +struct template_context; +typedef struct template_context template_context; + +static void template_init(template_context* ctx); + +static msgpack_object template_data(template_context* ctx); + +static int template_execute( + template_context* ctx, const char* data, size_t len, size_t* off); + + +static inline msgpack_object template_callback_root(unpack_user* u) +{ + msgpack_object o; + MSGPACK_UNUSED(u); + o.type = MSGPACK_OBJECT_NIL; + return o; +} + +static inline int template_callback_uint8(unpack_user* u, uint8_t d, msgpack_object* o) +{ + MSGPACK_UNUSED(u); + o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; + o->via.u64 = d; + return 0; +} + +static inline int template_callback_uint16(unpack_user* u, uint16_t d, msgpack_object* o) +{ + MSGPACK_UNUSED(u); + o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; + o->via.u64 = d; + return 0; +} + +static inline int template_callback_uint32(unpack_user* u, uint32_t d, msgpack_object* o) +{ + MSGPACK_UNUSED(u); + o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; + o->via.u64 = d; + return 0; +} + +static inline int template_callback_uint64(unpack_user* u, uint64_t d, msgpack_object* o) +{ + MSGPACK_UNUSED(u); + o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; + o->via.u64 = d; + return 0; +} + +static inline int template_callback_int8(unpack_user* u, int8_t d, msgpack_object* o) +{ + MSGPACK_UNUSED(u); + if(d >= 0) { + o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; + o->via.u64 = (uint64_t)d; + return 0; + } + else { + o->type = MSGPACK_OBJECT_NEGATIVE_INTEGER; + o->via.i64 = d; + return 0; + } +} + +static inline int template_callback_int16(unpack_user* u, int16_t d, msgpack_object* o) +{ + MSGPACK_UNUSED(u); + if(d >= 0) { + o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; + o->via.u64 = (uint64_t)d; + return 0; + } + else { + o->type = MSGPACK_OBJECT_NEGATIVE_INTEGER; + o->via.i64 = d; + return 0; + } +} + +static inline int template_callback_int32(unpack_user* u, int32_t d, msgpack_object* o) +{ + MSGPACK_UNUSED(u); + if(d >= 0) { + o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; + o->via.u64 = (uint64_t)d; + return 0; + } + else { + o->type = MSGPACK_OBJECT_NEGATIVE_INTEGER; + o->via.i64 = d; + return 0; + } +} + +static inline int template_callback_int64(unpack_user* u, int64_t d, msgpack_object* o) +{ + MSGPACK_UNUSED(u); + if(d >= 0) { + o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; + o->via.u64 = (uint64_t)d; + return 0; + } + else { + o->type = MSGPACK_OBJECT_NEGATIVE_INTEGER; + o->via.i64 = d; + return 0; + } +} + +static inline int template_callback_float(unpack_user* u, float d, msgpack_object* o) +{ + MSGPACK_UNUSED(u); + o->type = MSGPACK_OBJECT_FLOAT32; + o->via.f64 = d; + return 0; +} + +static inline int template_callback_double(unpack_user* u, double d, msgpack_object* o) +{ + MSGPACK_UNUSED(u); + o->type = MSGPACK_OBJECT_FLOAT64; + o->via.f64 = d; + return 0; +} + +static inline int template_callback_nil(unpack_user* u, msgpack_object* o) +{ + MSGPACK_UNUSED(u); + o->type = MSGPACK_OBJECT_NIL; + return 0; +} + +static inline int template_callback_true(unpack_user* u, msgpack_object* o) +{ + MSGPACK_UNUSED(u); + o->type = MSGPACK_OBJECT_BOOLEAN; + o->via.boolean = true; + return 0; +} + +static inline int template_callback_false(unpack_user* u, msgpack_object* o) +{ + MSGPACK_UNUSED(u); + o->type = MSGPACK_OBJECT_BOOLEAN; + o->via.boolean = false; + return 0; +} + +static inline int template_callback_array(unpack_user* u, unsigned int n, msgpack_object* o) +{ + size_t size; + // Let's leverage the fact that sizeof(msgpack_object) is a compile time constant + // to check for int overflows. + // Note - while n is constrained to 32-bit, the product of n * sizeof(msgpack_object) + // might not be constrained to 4GB on 64-bit systems +#if SIZE_MAX == UINT_MAX + if (n > SIZE_MAX/sizeof(msgpack_object)) + return MSGPACK_UNPACK_NOMEM_ERROR; +#endif + + o->type = MSGPACK_OBJECT_ARRAY; + o->via.array.size = 0; + + size = n * sizeof(msgpack_object); + + if (*u->z == NULL) { + *u->z = msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE); + if(*u->z == NULL) { + return MSGPACK_UNPACK_NOMEM_ERROR; + } + } + + // Unsure whether size = 0 should be an error, and if so, what to return + o->via.array.ptr = (msgpack_object*)msgpack_zone_malloc(*u->z, size); + if(o->via.array.ptr == NULL) { return MSGPACK_UNPACK_NOMEM_ERROR; } + return 0; +} + +static inline int template_callback_array_item(unpack_user* u, msgpack_object* c, msgpack_object o) +{ + MSGPACK_UNUSED(u); +#if defined(__GNUC__) && !defined(__clang__) + memcpy(&c->via.array.ptr[c->via.array.size], &o, sizeof(msgpack_object)); +#else /* __GNUC__ && !__clang__ */ + c->via.array.ptr[c->via.array.size] = o; +#endif /* __GNUC__ && !__clang__ */ + ++c->via.array.size; + return 0; +} + +static inline int template_callback_map(unpack_user* u, unsigned int n, msgpack_object* o) +{ + size_t size; + // Let's leverage the fact that sizeof(msgpack_object_kv) is a compile time constant + // to check for int overflows + // Note - while n is constrained to 32-bit, the product of n * sizeof(msgpack_object) + // might not be constrained to 4GB on 64-bit systems + + // Note - this will always be false on 64-bit systems +#if SIZE_MAX == UINT_MAX + if (n > SIZE_MAX/sizeof(msgpack_object_kv)) + return MSGPACK_UNPACK_NOMEM_ERROR; +#endif + + o->type = MSGPACK_OBJECT_MAP; + o->via.map.size = 0; + + size = n * sizeof(msgpack_object_kv); + + if (*u->z == NULL) { + *u->z = msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE); + if(*u->z == NULL) { + return MSGPACK_UNPACK_NOMEM_ERROR; + } + } + + // Should size = 0 be an error? If so, what error to return? + o->via.map.ptr = (msgpack_object_kv*)msgpack_zone_malloc(*u->z, size); + if(o->via.map.ptr == NULL) { return MSGPACK_UNPACK_NOMEM_ERROR; } + return 0; +} + +static inline int template_callback_map_item(unpack_user* u, msgpack_object* c, msgpack_object k, msgpack_object v) +{ + MSGPACK_UNUSED(u); +#if defined(__GNUC__) && !defined(__clang__) + memcpy(&c->via.map.ptr[c->via.map.size].key, &k, sizeof(msgpack_object)); + memcpy(&c->via.map.ptr[c->via.map.size].val, &v, sizeof(msgpack_object)); +#else /* __GNUC__ && !__clang__ */ + c->via.map.ptr[c->via.map.size].key = k; + c->via.map.ptr[c->via.map.size].val = v; +#endif /* __GNUC__ && !__clang__ */ + ++c->via.map.size; + return 0; +} + +static inline int template_callback_str(unpack_user* u, const char* b, const char* p, unsigned int l, msgpack_object* o) +{ + MSGPACK_UNUSED(b); + if (*u->z == NULL) { + *u->z = msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE); + if(*u->z == NULL) { + return MSGPACK_UNPACK_NOMEM_ERROR; + } + } + o->type = MSGPACK_OBJECT_STR; + o->via.str.ptr = p; + o->via.str.size = l; + u->referenced = true; + return 0; +} + +static inline int template_callback_bin(unpack_user* u, const char* b, const char* p, unsigned int l, msgpack_object* o) +{ + MSGPACK_UNUSED(b); + if (*u->z == NULL) { + *u->z = msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE); + if(*u->z == NULL) { + return MSGPACK_UNPACK_NOMEM_ERROR; + } + } + o->type = MSGPACK_OBJECT_BIN; + o->via.bin.ptr = p; + o->via.bin.size = l; + u->referenced = true; + return 0; +} + +static inline int template_callback_ext(unpack_user* u, const char* b, const char* p, unsigned int l, msgpack_object* o) +{ + MSGPACK_UNUSED(b); + if (l == 0) { + return MSGPACK_UNPACK_PARSE_ERROR; + } + if (*u->z == NULL) { + *u->z = msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE); + if(*u->z == NULL) { + return MSGPACK_UNPACK_NOMEM_ERROR; + } + } + o->type = MSGPACK_OBJECT_EXT; + o->via.ext.type = *p; + o->via.ext.ptr = p + 1; + o->via.ext.size = l - 1; + u->referenced = true; + return 0; +} + +#include "vendor/msgpack/unpack_template.h" + + +#define CTX_CAST(m) ((template_context*)(m)) +#define CTX_REFERENCED(mpac) CTX_CAST((mpac)->ctx)->user.referenced + +#define COUNTER_SIZE (sizeof(_msgpack_atomic_counter_t)) + + +static inline void init_count(void* buffer) +{ + *(volatile _msgpack_atomic_counter_t*)buffer = 1; +} + +static inline void decr_count(void* buffer) +{ + // atomic if(--*(_msgpack_atomic_counter_t*)buffer == 0) { free(buffer); } + if(_msgpack_sync_decr_and_fetch((volatile _msgpack_atomic_counter_t*)buffer) == 0) { + free(buffer); + } +} + +static inline void incr_count(void* buffer) +{ + // atomic ++*(_msgpack_atomic_counter_t*)buffer; + _msgpack_sync_incr_and_fetch((volatile _msgpack_atomic_counter_t*)buffer); +} + +static inline _msgpack_atomic_counter_t get_count(void* buffer) +{ + return *(volatile _msgpack_atomic_counter_t*)buffer; +} + +bool msgpack_unpacker_init(msgpack_unpacker* mpac, size_t initial_buffer_size) +{ + char* buffer; + void* ctx; + + if(initial_buffer_size < COUNTER_SIZE) { + initial_buffer_size = COUNTER_SIZE; + } + + buffer = (char*)malloc(initial_buffer_size); + if(buffer == NULL) { + return false; + } + + ctx = malloc(sizeof(template_context)); + if(ctx == NULL) { + free(buffer); + return false; + } + + mpac->buffer = buffer; + mpac->used = COUNTER_SIZE; + mpac->free = initial_buffer_size - mpac->used; + mpac->off = COUNTER_SIZE; + mpac->parsed = 0; + mpac->initial_buffer_size = initial_buffer_size; + mpac->z = NULL; + mpac->ctx = ctx; + + init_count(mpac->buffer); + + template_init(CTX_CAST(mpac->ctx)); + CTX_CAST(mpac->ctx)->user.z = &mpac->z; + CTX_CAST(mpac->ctx)->user.referenced = false; + + return true; +} + +void msgpack_unpacker_destroy(msgpack_unpacker* mpac) +{ + msgpack_zone_free(mpac->z); + free(mpac->ctx); + decr_count(mpac->buffer); +} + +msgpack_unpacker* msgpack_unpacker_new(size_t initial_buffer_size) +{ + msgpack_unpacker* mpac = (msgpack_unpacker*)malloc(sizeof(msgpack_unpacker)); + if(mpac == NULL) { + return NULL; + } + + if(!msgpack_unpacker_init(mpac, initial_buffer_size)) { + free(mpac); + return NULL; + } + + return mpac; +} + +void msgpack_unpacker_free(msgpack_unpacker* mpac) +{ + msgpack_unpacker_destroy(mpac); + free(mpac); +} + +bool msgpack_unpacker_expand_buffer(msgpack_unpacker* mpac, size_t size) +{ + if(mpac->used == mpac->off && get_count(mpac->buffer) == 1 + && !CTX_REFERENCED(mpac)) { + // rewind buffer + mpac->free += mpac->used - COUNTER_SIZE; + mpac->used = COUNTER_SIZE; + mpac->off = COUNTER_SIZE; + + if(mpac->free >= size) { + return true; + } + } + + if(mpac->off == COUNTER_SIZE) { + char* tmp; + size_t next_size = (mpac->used + mpac->free) * 2; // include COUNTER_SIZE + while(next_size < size + mpac->used) { + size_t tmp_next_size = next_size * 2; + if (tmp_next_size <= next_size) { + next_size = size + mpac->used; + break; + } + next_size = tmp_next_size; + } + + tmp = (char*)realloc(mpac->buffer, next_size); + if(tmp == NULL) { + return false; + } + + mpac->buffer = tmp; + mpac->free = next_size - mpac->used; + + } else { + char* tmp; + size_t next_size = mpac->initial_buffer_size; // include COUNTER_SIZE + size_t not_parsed = mpac->used - mpac->off; + while(next_size < size + not_parsed + COUNTER_SIZE) { + size_t tmp_next_size = next_size * 2; + if (tmp_next_size <= next_size) { + next_size = size + not_parsed + COUNTER_SIZE; + break; + } + next_size = tmp_next_size; + } + + tmp = (char*)malloc(next_size); + if(tmp == NULL) { + return false; + } + + init_count(tmp); + + memcpy(tmp+COUNTER_SIZE, mpac->buffer+mpac->off, not_parsed); + + if(CTX_REFERENCED(mpac)) { + if(!msgpack_zone_push_finalizer(mpac->z, decr_count, mpac->buffer)) { + free(tmp); + return false; + } + CTX_REFERENCED(mpac) = false; + } else { + decr_count(mpac->buffer); + } + + mpac->buffer = tmp; + mpac->used = not_parsed + COUNTER_SIZE; + mpac->free = next_size - mpac->used; + mpac->off = COUNTER_SIZE; + } + + return true; +} + +int msgpack_unpacker_execute(msgpack_unpacker* mpac) +{ + size_t off = mpac->off; + int ret = template_execute(CTX_CAST(mpac->ctx), + mpac->buffer, mpac->used, &mpac->off); + if(mpac->off > off) { + mpac->parsed += mpac->off - off; + } + return ret; +} + +msgpack_object msgpack_unpacker_data(msgpack_unpacker* mpac) +{ + return template_data(CTX_CAST(mpac->ctx)); +} + +msgpack_zone* msgpack_unpacker_release_zone(msgpack_unpacker* mpac) +{ + msgpack_zone* old = mpac->z; + + if (old == NULL) return NULL; + if(!msgpack_unpacker_flush_zone(mpac)) { + return NULL; + } + + mpac->z = NULL; + CTX_CAST(mpac->ctx)->user.z = &mpac->z; + + return old; +} + +void msgpack_unpacker_reset_zone(msgpack_unpacker* mpac) +{ + msgpack_zone_clear(mpac->z); +} + +bool msgpack_unpacker_flush_zone(msgpack_unpacker* mpac) +{ + if(CTX_REFERENCED(mpac)) { + if(!msgpack_zone_push_finalizer(mpac->z, decr_count, mpac->buffer)) { + return false; + } + CTX_REFERENCED(mpac) = false; + + incr_count(mpac->buffer); + } + + return true; +} + +void msgpack_unpacker_reset(msgpack_unpacker* mpac) +{ + template_init(CTX_CAST(mpac->ctx)); + // don't reset referenced flag + mpac->parsed = 0; +} + +static inline msgpack_unpack_return unpacker_next(msgpack_unpacker* mpac, + msgpack_unpacked* result) +{ + int ret; + + msgpack_unpacked_destroy(result); + + ret = msgpack_unpacker_execute(mpac); + + if(ret < 0) { + result->zone = NULL; + memset(&result->data, 0, sizeof(msgpack_object)); + return (msgpack_unpack_return)ret; + } + + if(ret == 0) { + return MSGPACK_UNPACK_CONTINUE; + } + result->zone = msgpack_unpacker_release_zone(mpac); + result->data = msgpack_unpacker_data(mpac); + + return MSGPACK_UNPACK_SUCCESS; +} + +msgpack_unpack_return msgpack_unpacker_next(msgpack_unpacker* mpac, + msgpack_unpacked* result) +{ + msgpack_unpack_return ret; + + ret = unpacker_next(mpac, result); + if (ret == MSGPACK_UNPACK_SUCCESS) { + msgpack_unpacker_reset(mpac); + } + + return ret; +} + +msgpack_unpack_return +msgpack_unpacker_next_with_size(msgpack_unpacker* mpac, + msgpack_unpacked* result, size_t *p_bytes) +{ + msgpack_unpack_return ret; + + ret = unpacker_next(mpac, result); + if (ret == MSGPACK_UNPACK_SUCCESS || ret == MSGPACK_UNPACK_CONTINUE) { + *p_bytes = mpac->parsed; + } + + if (ret == MSGPACK_UNPACK_SUCCESS) { + msgpack_unpacker_reset(mpac); + } + + return ret; +} + +msgpack_unpack_return +msgpack_unpack(const char* data, size_t len, size_t* off, + msgpack_zone* result_zone, msgpack_object* result) +{ + size_t noff = 0; + if(off != NULL) { noff = *off; } + + if(len <= noff) { + // FIXME + return MSGPACK_UNPACK_CONTINUE; + } + else { + int e; + template_context ctx; + template_init(&ctx); + + ctx.user.z = &result_zone; + ctx.user.referenced = false; + + e = template_execute(&ctx, data, len, &noff); + if(e < 0) { + return (msgpack_unpack_return)e; + } + + if(off != NULL) { *off = noff; } + + if(e == 0) { + return MSGPACK_UNPACK_CONTINUE; + } + + *result = template_data(&ctx); + + if(noff < len) { + return MSGPACK_UNPACK_EXTRA_BYTES; + } + + return MSGPACK_UNPACK_SUCCESS; + } +} + +msgpack_unpack_return +msgpack_unpack_next(msgpack_unpacked* result, + const char* data, size_t len, size_t* off) +{ + size_t noff = 0; + msgpack_unpacked_destroy(result); + + if(off != NULL) { noff = *off; } + + if(len <= noff) { + return MSGPACK_UNPACK_CONTINUE; + } + + { + int e; + template_context ctx; + template_init(&ctx); + + ctx.user.z = &result->zone; + ctx.user.referenced = false; + + e = template_execute(&ctx, data, len, &noff); + + if(off != NULL) { *off = noff; } + + if(e < 0) { + msgpack_zone_free(result->zone); + result->zone = NULL; + return (msgpack_unpack_return)e; + } + + if(e == 0) { + return MSGPACK_UNPACK_CONTINUE; + } + + result->data = template_data(&ctx); + + return MSGPACK_UNPACK_SUCCESS; + } +} + +#if defined(MSGPACK_OLD_COMPILER_BUS_ERROR_WORKAROUND) +// FIXME: Dirty hack to avoid a bus error caused by OS X's old gcc. +static void dummy_function_to_avoid_bus_error() +{ +} +#endif diff --git a/src/vendor/msgpack/unpack.h b/src/vendor/msgpack/unpack.h new file mode 100644 index 000000000..036d575eb --- /dev/null +++ b/src/vendor/msgpack/unpack.h @@ -0,0 +1,281 @@ +/* + * MessagePack for C unpacking routine + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef MSGPACK_UNPACKER_H +#define MSGPACK_UNPACKER_H + +#include "zone.h" +#include "object.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup msgpack_unpack Deserializer + * @ingroup msgpack + * @{ + */ + +typedef struct msgpack_unpacked { + msgpack_zone* zone; + msgpack_object data; +} msgpack_unpacked; + +typedef enum { + MSGPACK_UNPACK_SUCCESS = 2, + MSGPACK_UNPACK_EXTRA_BYTES = 1, + MSGPACK_UNPACK_CONTINUE = 0, + MSGPACK_UNPACK_PARSE_ERROR = -1, + MSGPACK_UNPACK_NOMEM_ERROR = -2 +} msgpack_unpack_return; + + +MSGPACK_DLLEXPORT +msgpack_unpack_return +msgpack_unpack_next(msgpack_unpacked* result, + const char* data, size_t len, size_t* off); + +/** @} */ + + +/** + * @defgroup msgpack_unpacker Streaming deserializer + * @ingroup msgpack + * @{ + */ + +typedef struct msgpack_unpacker { + char* buffer; + size_t used; + size_t free; + size_t off; + size_t parsed; + msgpack_zone* z; + size_t initial_buffer_size; + void* ctx; +} msgpack_unpacker; + + +#ifndef MSGPACK_UNPACKER_INIT_BUFFER_SIZE +#define MSGPACK_UNPACKER_INIT_BUFFER_SIZE (64*1024) +#endif + +/** + * Initializes a streaming deserializer. + * The initialized deserializer must be destroyed by msgpack_unpacker_destroy(msgpack_unpacker*). + */ +MSGPACK_DLLEXPORT +bool msgpack_unpacker_init(msgpack_unpacker* mpac, size_t initial_buffer_size); + +/** + * Destroys a streaming deserializer initialized by msgpack_unpacker_init(msgpack_unpacker*, size_t). + */ +MSGPACK_DLLEXPORT +void msgpack_unpacker_destroy(msgpack_unpacker* mpac); + + +/** + * Creates a streaming deserializer. + * The created deserializer must be destroyed by msgpack_unpacker_free(msgpack_unpacker*). + */ +MSGPACK_DLLEXPORT +msgpack_unpacker* msgpack_unpacker_new(size_t initial_buffer_size); + +/** + * Frees a streaming deserializer created by msgpack_unpacker_new(size_t). + */ +MSGPACK_DLLEXPORT +void msgpack_unpacker_free(msgpack_unpacker* mpac); + + +#ifndef MSGPACK_UNPACKER_RESERVE_SIZE +#define MSGPACK_UNPACKER_RESERVE_SIZE (32*1024) +#endif + +/** + * Reserves free space of the internal buffer. + * Use this function to fill the internal buffer with + * msgpack_unpacker_buffer(msgpack_unpacker*), + * msgpack_unpacker_buffer_capacity(const msgpack_unpacker*) and + * msgpack_unpacker_buffer_consumed(msgpack_unpacker*). + */ +static inline bool msgpack_unpacker_reserve_buffer(msgpack_unpacker* mpac, size_t size); + +/** + * Gets pointer to the free space of the internal buffer. + * Use this function to fill the internal buffer with + * msgpack_unpacker_reserve_buffer(msgpack_unpacker*, size_t), + * msgpack_unpacker_buffer_capacity(const msgpack_unpacker*) and + * msgpack_unpacker_buffer_consumed(msgpack_unpacker*). + */ +static inline char* msgpack_unpacker_buffer(msgpack_unpacker* mpac); + +/** + * Gets size of the free space of the internal buffer. + * Use this function to fill the internal buffer with + * msgpack_unpacker_reserve_buffer(msgpack_unpacker*, size_t), + * msgpack_unpacker_buffer(const msgpack_unpacker*) and + * msgpack_unpacker_buffer_consumed(msgpack_unpacker*). + */ +static inline size_t msgpack_unpacker_buffer_capacity(const msgpack_unpacker* mpac); + +/** + * Notifies the deserializer that the internal buffer filled. + * Use this function to fill the internal buffer with + * msgpack_unpacker_reserve_buffer(msgpack_unpacker*, size_t), + * msgpack_unpacker_buffer(msgpack_unpacker*) and + * msgpack_unpacker_buffer_capacity(const msgpack_unpacker*). + */ +static inline void msgpack_unpacker_buffer_consumed(msgpack_unpacker* mpac, size_t size); + + +/** + * Deserializes one object. + * Returns true if it successes. Otherwise false is returned. + * @param pac pointer to an initialized msgpack_unpacked object. + */ +MSGPACK_DLLEXPORT +msgpack_unpack_return msgpack_unpacker_next(msgpack_unpacker* mpac, msgpack_unpacked* pac); + +/** + * Deserializes one object and set the number of parsed bytes involved. + * Returns true if it successes. Otherwise false is returned. + * @param mpac pointer to an initialized msgpack_unpacker object. + * @param result pointer to an initialized msgpack_unpacked object. + * @param p_bytes pointer to variable that will be set with the number of parsed bytes. + */ +MSGPACK_DLLEXPORT +msgpack_unpack_return msgpack_unpacker_next_with_size(msgpack_unpacker* mpac, + msgpack_unpacked* result, + size_t *p_bytes); + +/** + * Initializes a msgpack_unpacked object. + * The initialized object must be destroyed by msgpack_unpacked_destroy(msgpack_unpacker*). + * Use the object with msgpack_unpacker_next(msgpack_unpacker*, msgpack_unpacked*) or + * msgpack_unpack_next(msgpack_unpacked*, const char*, size_t, size_t*). + */ +static inline void msgpack_unpacked_init(msgpack_unpacked* result); + +/** + * Destroys a streaming deserializer initialized by msgpack_unpacked(). + */ +static inline void msgpack_unpacked_destroy(msgpack_unpacked* result); + +/** + * Releases the memory zone from msgpack_unpacked object. + * The released zone must be freed by msgpack_zone_free(msgpack_zone*). + */ +static inline msgpack_zone* msgpack_unpacked_release_zone(msgpack_unpacked* result); + + +MSGPACK_DLLEXPORT +int msgpack_unpacker_execute(msgpack_unpacker* mpac); + +MSGPACK_DLLEXPORT +msgpack_object msgpack_unpacker_data(msgpack_unpacker* mpac); + +MSGPACK_DLLEXPORT +msgpack_zone* msgpack_unpacker_release_zone(msgpack_unpacker* mpac); + +MSGPACK_DLLEXPORT +void msgpack_unpacker_reset_zone(msgpack_unpacker* mpac); + +MSGPACK_DLLEXPORT +void msgpack_unpacker_reset(msgpack_unpacker* mpac); + +static inline size_t msgpack_unpacker_message_size(const msgpack_unpacker* mpac); + + +/** @} */ + + +// obsolete +MSGPACK_DLLEXPORT +msgpack_unpack_return +msgpack_unpack(const char* data, size_t len, size_t* off, + msgpack_zone* result_zone, msgpack_object* result); + + + + +static inline size_t msgpack_unpacker_parsed_size(const msgpack_unpacker* mpac); + +MSGPACK_DLLEXPORT +bool msgpack_unpacker_flush_zone(msgpack_unpacker* mpac); + +MSGPACK_DLLEXPORT +bool msgpack_unpacker_expand_buffer(msgpack_unpacker* mpac, size_t size); + +static inline bool msgpack_unpacker_reserve_buffer(msgpack_unpacker* mpac, size_t size) +{ + if(mpac->free >= size) { return true; } + return msgpack_unpacker_expand_buffer(mpac, size); +} + +static inline char* msgpack_unpacker_buffer(msgpack_unpacker* mpac) +{ + return mpac->buffer + mpac->used; +} + +static inline size_t msgpack_unpacker_buffer_capacity(const msgpack_unpacker* mpac) +{ + return mpac->free; +} + +static inline void msgpack_unpacker_buffer_consumed(msgpack_unpacker* mpac, size_t size) +{ + mpac->used += size; + mpac->free -= size; +} + +static inline size_t msgpack_unpacker_message_size(const msgpack_unpacker* mpac) +{ + return mpac->parsed - mpac->off + mpac->used; +} + +static inline size_t msgpack_unpacker_parsed_size(const msgpack_unpacker* mpac) +{ + return mpac->parsed; +} + + +static inline void msgpack_unpacked_init(msgpack_unpacked* result) +{ + memset(result, 0, sizeof(msgpack_unpacked)); +} + +static inline void msgpack_unpacked_destroy(msgpack_unpacked* result) +{ + if(result->zone != NULL) { + msgpack_zone_free(result->zone); + result->zone = NULL; + memset(&result->data, 0, sizeof(msgpack_object)); + } +} + +static inline msgpack_zone* msgpack_unpacked_release_zone(msgpack_unpacked* result) +{ + if(result->zone != NULL) { + msgpack_zone* z = result->zone; + result->zone = NULL; + return z; + } + return NULL; +} + + +#ifdef __cplusplus +} +#endif + +#endif /* msgpack/unpack.h */ diff --git a/src/vendor/msgpack/unpack_define.h b/src/vendor/msgpack/unpack_define.h new file mode 100644 index 000000000..194833958 --- /dev/null +++ b/src/vendor/msgpack/unpack_define.h @@ -0,0 +1,89 @@ +/* + * MessagePack unpacking routine template + * + * Copyright (C) 2008-2010 FURUHASHI Sadayuki + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef MSGPACK_UNPACK_DEFINE_H +#define MSGPACK_UNPACK_DEFINE_H + +#include "vendor/msgpack/sysdep.h" +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifndef MSGPACK_EMBED_STACK_SIZE +#define MSGPACK_EMBED_STACK_SIZE 32 +#endif + + +typedef enum { + MSGPACK_CS_HEADER = 0x00, // nil + + //MSGPACK_CS_ = 0x01, + //MSGPACK_CS_ = 0x02, // false + //MSGPACK_CS_ = 0x03, // true + + MSGPACK_CS_BIN_8 = 0x04, + MSGPACK_CS_BIN_16 = 0x05, + MSGPACK_CS_BIN_32 = 0x06, + + MSGPACK_CS_EXT_8 = 0x07, + MSGPACK_CS_EXT_16 = 0x08, + MSGPACK_CS_EXT_32 = 0x09, + + MSGPACK_CS_FLOAT = 0x0a, + MSGPACK_CS_DOUBLE = 0x0b, + MSGPACK_CS_UINT_8 = 0x0c, + MSGPACK_CS_UINT_16 = 0x0d, + MSGPACK_CS_UINT_32 = 0x0e, + MSGPACK_CS_UINT_64 = 0x0f, + MSGPACK_CS_INT_8 = 0x10, + MSGPACK_CS_INT_16 = 0x11, + MSGPACK_CS_INT_32 = 0x12, + MSGPACK_CS_INT_64 = 0x13, + + MSGPACK_CS_FIXEXT_1 = 0x14, + MSGPACK_CS_FIXEXT_2 = 0x15, + MSGPACK_CS_FIXEXT_4 = 0x16, + MSGPACK_CS_FIXEXT_8 = 0x17, + MSGPACK_CS_FIXEXT_16 = 0x18, + + MSGPACK_CS_STR_8 = 0x19, // str8 + MSGPACK_CS_STR_16 = 0x1a, // str16 + MSGPACK_CS_STR_32 = 0x1b, // str32 + MSGPACK_CS_ARRAY_16 = 0x1c, + MSGPACK_CS_ARRAY_32 = 0x1d, + MSGPACK_CS_MAP_16 = 0x1e, + MSGPACK_CS_MAP_32 = 0x1f, + + //MSGPACK_ACS_BIG_INT_VALUE, + //MSGPACK_ACS_BIG_FLOAT_VALUE, + MSGPACK_ACS_STR_VALUE, + MSGPACK_ACS_BIN_VALUE, + MSGPACK_ACS_EXT_VALUE +} msgpack_unpack_state; + + +typedef enum { + MSGPACK_CT_ARRAY_ITEM, + MSGPACK_CT_MAP_KEY, + MSGPACK_CT_MAP_VALUE +} msgpack_container_type; + + +#ifdef __cplusplus +} +#endif + +#endif /* msgpack/unpack_define.h */ + diff --git a/src/vendor/msgpack/unpack_template.h b/src/vendor/msgpack/unpack_template.h new file mode 100644 index 000000000..de30f3cf0 --- /dev/null +++ b/src/vendor/msgpack/unpack_template.h @@ -0,0 +1,471 @@ +/* + * MessagePack unpacking routine template + * + * Copyright (C) 2008-2010 FURUHASHI Sadayuki + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ + +#ifndef msgpack_unpack_func +#error msgpack_unpack_func template is not defined +#endif + +#ifndef msgpack_unpack_callback +#error msgpack_unpack_callback template is not defined +#endif + +#ifndef msgpack_unpack_struct +#error msgpack_unpack_struct template is not defined +#endif + +#ifndef msgpack_unpack_struct_decl +#define msgpack_unpack_struct_decl(name) msgpack_unpack_struct(name) +#endif + +#ifndef msgpack_unpack_object +#error msgpack_unpack_object type is not defined +#endif + +#ifndef msgpack_unpack_user +#error msgpack_unpack_user type is not defined +#endif + +#ifndef USE_CASE_RANGE +#if !defined(_MSC_VER) +#define USE_CASE_RANGE +#endif +#endif + +#if defined(_KERNEL_MODE) +#undef assert +#define assert NT_ASSERT +#endif + +msgpack_unpack_struct_decl(_stack) { + msgpack_unpack_object obj; + size_t count; + unsigned int ct; + msgpack_unpack_object map_key; +}; + +msgpack_unpack_struct_decl(_context) { + msgpack_unpack_user user; + unsigned int cs; + unsigned int trail; + unsigned int top; + /* + msgpack_unpack_struct(_stack)* stack; + unsigned int stack_size; + msgpack_unpack_struct(_stack) embed_stack[MSGPACK_EMBED_STACK_SIZE]; + */ + msgpack_unpack_struct(_stack) stack[MSGPACK_EMBED_STACK_SIZE]; +}; + + +msgpack_unpack_func(void, _init)(msgpack_unpack_struct(_context)* ctx) +{ + ctx->cs = MSGPACK_CS_HEADER; + ctx->trail = 0; + ctx->top = 0; + /* + ctx->stack = ctx->embed_stack; + ctx->stack_size = MSGPACK_EMBED_STACK_SIZE; + */ + ctx->stack[0].obj = msgpack_unpack_callback(_root)(&ctx->user); +} + +/* +msgpack_unpack_func(void, _destroy)(msgpack_unpack_struct(_context)* ctx) +{ + if(ctx->stack_size != MSGPACK_EMBED_STACK_SIZE) { + free(ctx->stack); + } +} +*/ + +msgpack_unpack_func(msgpack_unpack_object, _data)(msgpack_unpack_struct(_context)* ctx) +{ + return (ctx)->stack[0].obj; +} + + +msgpack_unpack_func(int, _execute)(msgpack_unpack_struct(_context)* ctx, const char* data, size_t len, size_t* off) +{ + assert(len >= *off); + { + const unsigned char* p = (unsigned char*)data + *off; + const unsigned char* const pe = (unsigned char*)data + len; + const void* n = NULL; + + unsigned int trail = ctx->trail; + unsigned int cs = ctx->cs; + unsigned int top = ctx->top; + msgpack_unpack_struct(_stack)* stack = ctx->stack; + /* + unsigned int stack_size = ctx->stack_size; + */ + msgpack_unpack_user* user = &ctx->user; + + msgpack_unpack_object obj; + msgpack_unpack_struct(_stack)* c = NULL; + + int ret; + +#define push_simple_value(func) \ + ret = msgpack_unpack_callback(func)(user, &obj); \ + if(ret < 0) { goto _failed; } \ + goto _push +#define push_fixed_value(func, arg) \ + ret = msgpack_unpack_callback(func)(user, arg, &obj); \ + if(ret < 0) { goto _failed; } \ + goto _push +#define push_variable_value(func, base, pos, len) \ + ret = msgpack_unpack_callback(func)(user, \ + (const char*)base, (const char*)pos, len, &obj); \ + if(ret < 0) { goto _failed; } \ + goto _push + +#define again_fixed_trail(_cs, trail_len) \ + trail = trail_len; \ + cs = _cs; \ + goto _fixed_trail_again +#define again_fixed_trail_if_zero(_cs, trail_len, ifzero) \ + trail = trail_len; \ + if(trail == 0) { goto ifzero; } \ + cs = _cs; \ + goto _fixed_trail_again + +#define start_container(func, count_, ct_) \ + if(top >= MSGPACK_EMBED_STACK_SIZE) { \ + ret = MSGPACK_UNPACK_NOMEM_ERROR; \ + goto _failed; \ + } /* FIXME */ \ + ret = msgpack_unpack_callback(func)(user, count_, &stack[top].obj); \ + if(ret < 0) { goto _failed; } \ + if((count_) == 0) { obj = stack[top].obj; goto _push; } \ + stack[top].ct = ct_; \ + stack[top].count = count_; \ + ++top; \ + goto _header_again + +#define NEXT_CS(p) \ + ((unsigned int)*p & 0x1f) + +#ifdef USE_CASE_RANGE +#define SWITCH_RANGE_BEGIN switch(*p) { +#define SWITCH_RANGE(FROM, TO) case FROM ... TO: +#define SWITCH_RANGE_DEFAULT default: +#define SWITCH_RANGE_END } +#else +#define SWITCH_RANGE_BEGIN { if(0) { +#define SWITCH_RANGE(FROM, TO) } else if(FROM <= *p && *p <= TO) { +#define SWITCH_RANGE_DEFAULT } else { +#define SWITCH_RANGE_END } } +#endif + + if(p == pe) { goto _out; } + do { + switch(cs) { + case MSGPACK_CS_HEADER: + SWITCH_RANGE_BEGIN + SWITCH_RANGE(0x00, 0x7f) // Positive Fixnum + push_fixed_value(_uint8, *(uint8_t*)p); + SWITCH_RANGE(0xe0, 0xff) // Negative Fixnum + push_fixed_value(_int8, *(int8_t*)p); + SWITCH_RANGE(0xc0, 0xdf) // Variable + switch(*p) { + case 0xc0: // nil + push_simple_value(_nil); + //case 0xc1: // string + // again_terminal_trail(NEXT_CS(p), p+1); + case 0xc2: // false + push_simple_value(_false); + case 0xc3: // true + push_simple_value(_true); + case 0xc4: // bin 8 + case 0xc5: // bin 16 + case 0xc6: // bin 32 + again_fixed_trail(NEXT_CS(p), 1 << (((unsigned int)*p) & 0x03)); + case 0xc7: // ext 8 + case 0xc8: // ext 16 + case 0xc9: // ext 32 + again_fixed_trail(NEXT_CS(p), 1 << ((((unsigned int)*p) + 1) & 0x03)); + case 0xca: // float + case 0xcb: // double + case 0xcc: // unsigned int 8 + case 0xcd: // unsigned int 16 + case 0xce: // unsigned int 32 + case 0xcf: // unsigned int 64 + case 0xd0: // signed int 8 + case 0xd1: // signed int 16 + case 0xd2: // signed int 32 + case 0xd3: // signed int 64 + again_fixed_trail(NEXT_CS(p), 1 << (((unsigned int)*p) & 0x03)); + case 0xd4: // fixext 1 + case 0xd5: // fixext 2 + case 0xd6: // fixext 4 + case 0xd7: // fixext 8 + again_fixed_trail_if_zero(MSGPACK_ACS_EXT_VALUE, + (1 << (((unsigned int)*p) & 0x03)) + 1, _ext_zero); + case 0xd8: // fixext 16 + again_fixed_trail_if_zero(MSGPACK_ACS_EXT_VALUE, 16+1, _ext_zero); + + case 0xd9: // str 8 + case 0xda: // str 16 + case 0xdb: // str 32 + again_fixed_trail(NEXT_CS(p), 1 << ((((unsigned int)*p) & 0x03) - 1)); + case 0xdc: // array 16 + case 0xdd: // array 32 + case 0xde: // map 16 + case 0xdf: // map 32 + again_fixed_trail(NEXT_CS(p), 2u << (((unsigned int)*p) & 0x01)); + default: + ret = MSGPACK_UNPACK_PARSE_ERROR; + goto _failed; + } + SWITCH_RANGE(0xa0, 0xbf) // FixStr + again_fixed_trail_if_zero(MSGPACK_ACS_STR_VALUE, ((unsigned int)*p & 0x1f), _str_zero); + SWITCH_RANGE(0x90, 0x9f) // FixArray + start_container(_array, ((unsigned int)*p) & 0x0f, MSGPACK_CT_ARRAY_ITEM); + SWITCH_RANGE(0x80, 0x8f) // FixMap + start_container(_map, ((unsigned int)*p) & 0x0f, MSGPACK_CT_MAP_KEY); + + SWITCH_RANGE_DEFAULT + ret = MSGPACK_UNPACK_PARSE_ERROR; + goto _failed; + SWITCH_RANGE_END + // end MSGPACK_CS_HEADER + + + _fixed_trail_again: + ++p; + // fallthrough + + default: + if((size_t)(pe - p) < trail) { goto _out; } + n = p; p += trail - 1; + switch(cs) { + //case MSGPACK_CS_ + //case MSGPACK_CS_ + case MSGPACK_CS_FLOAT: { + union { uint32_t i; float f; } mem; + _msgpack_load32(uint32_t, n, &mem.i); + push_fixed_value(_float, mem.f); } + case MSGPACK_CS_DOUBLE: { + union { uint64_t i; double f; } mem; + _msgpack_load64(uint64_t, n, &mem.i); +#if defined(TARGET_OS_IPHONE) + // ok +#elif defined(__arm__) && !(__ARM_EABI__) // arm-oabi + // https://github.com/msgpack/msgpack-perl/pull/1 + mem.i = (mem.i & 0xFFFFFFFFUL) << 32UL | (mem.i >> 32UL); +#endif + push_fixed_value(_double, mem.f); } + case MSGPACK_CS_UINT_8: + push_fixed_value(_uint8, *(uint8_t*)n); + case MSGPACK_CS_UINT_16:{ + uint16_t tmp; + _msgpack_load16(uint16_t,n,&tmp); + push_fixed_value(_uint16, tmp); + } + case MSGPACK_CS_UINT_32:{ + uint32_t tmp; + _msgpack_load32(uint32_t,n,&tmp); + push_fixed_value(_uint32, tmp); + } + case MSGPACK_CS_UINT_64:{ + uint64_t tmp; + _msgpack_load64(uint64_t,n,&tmp); + push_fixed_value(_uint64, tmp); + } + case MSGPACK_CS_INT_8: + push_fixed_value(_int8, *(int8_t*)n); + case MSGPACK_CS_INT_16:{ + int16_t tmp; + _msgpack_load16(int16_t,n,&tmp); + push_fixed_value(_int16, tmp); + } + case MSGPACK_CS_INT_32:{ + int32_t tmp; + _msgpack_load32(int32_t,n,&tmp); + push_fixed_value(_int32, tmp); + } + case MSGPACK_CS_INT_64:{ + int64_t tmp; + _msgpack_load64(int64_t,n,&tmp); + push_fixed_value(_int64, tmp); + } + case MSGPACK_CS_FIXEXT_1: + again_fixed_trail_if_zero(MSGPACK_ACS_EXT_VALUE, 1+1, _ext_zero); + case MSGPACK_CS_FIXEXT_2: + again_fixed_trail_if_zero(MSGPACK_ACS_EXT_VALUE, 2+1, _ext_zero); + case MSGPACK_CS_FIXEXT_4: + again_fixed_trail_if_zero(MSGPACK_ACS_EXT_VALUE, 4+1, _ext_zero); + case MSGPACK_CS_FIXEXT_8: + again_fixed_trail_if_zero(MSGPACK_ACS_EXT_VALUE, 8+1, _ext_zero); + case MSGPACK_CS_FIXEXT_16: + again_fixed_trail_if_zero(MSGPACK_ACS_EXT_VALUE, 16+1, _ext_zero); + case MSGPACK_CS_STR_8: + again_fixed_trail_if_zero(MSGPACK_ACS_STR_VALUE, *(uint8_t*)n, _str_zero); + case MSGPACK_CS_BIN_8: + again_fixed_trail_if_zero(MSGPACK_ACS_BIN_VALUE, *(uint8_t*)n, _bin_zero); + case MSGPACK_CS_EXT_8: + again_fixed_trail_if_zero(MSGPACK_ACS_EXT_VALUE, (*(uint8_t*)n) + 1, _ext_zero); + case MSGPACK_CS_STR_16:{ + uint16_t tmp; + _msgpack_load16(uint16_t,n,&tmp); + again_fixed_trail_if_zero(MSGPACK_ACS_STR_VALUE, tmp, _str_zero); + } + case MSGPACK_CS_BIN_16:{ + uint16_t tmp; + _msgpack_load16(uint16_t,n,&tmp); + again_fixed_trail_if_zero(MSGPACK_ACS_BIN_VALUE, tmp, _bin_zero); + } + case MSGPACK_CS_EXT_16:{ + uint16_t tmp; + _msgpack_load16(uint16_t,n,&tmp); + again_fixed_trail_if_zero(MSGPACK_ACS_EXT_VALUE, tmp + 1, _ext_zero); + } + case MSGPACK_CS_STR_32:{ + uint32_t tmp; + _msgpack_load32(uint32_t,n,&tmp); + again_fixed_trail_if_zero(MSGPACK_ACS_STR_VALUE, tmp, _str_zero); + } + case MSGPACK_CS_BIN_32:{ + uint32_t tmp; + _msgpack_load32(uint32_t,n,&tmp); + again_fixed_trail_if_zero(MSGPACK_ACS_BIN_VALUE, tmp, _bin_zero); + } + case MSGPACK_CS_EXT_32:{ + uint32_t tmp; + _msgpack_load32(uint32_t,n,&tmp); + again_fixed_trail_if_zero(MSGPACK_ACS_EXT_VALUE, tmp + 1, _ext_zero); + } + case MSGPACK_ACS_STR_VALUE: + _str_zero: + push_variable_value(_str, data, n, trail); + case MSGPACK_ACS_BIN_VALUE: + _bin_zero: + push_variable_value(_bin, data, n, trail); + case MSGPACK_ACS_EXT_VALUE: + _ext_zero: + push_variable_value(_ext, data, n, trail); + + case MSGPACK_CS_ARRAY_16:{ + uint16_t tmp; + _msgpack_load16(uint16_t,n,&tmp); + start_container(_array, tmp, MSGPACK_CT_ARRAY_ITEM); + } + case MSGPACK_CS_ARRAY_32:{ + /* FIXME security guard */ + uint32_t tmp; + _msgpack_load32(uint32_t,n,&tmp); + start_container(_array, tmp, MSGPACK_CT_ARRAY_ITEM); + } + + case MSGPACK_CS_MAP_16:{ + uint16_t tmp; + _msgpack_load16(uint16_t,n,&tmp); + start_container(_map, tmp, MSGPACK_CT_MAP_KEY); + } + case MSGPACK_CS_MAP_32:{ + /* FIXME security guard */ + uint32_t tmp; + _msgpack_load32(uint32_t,n,&tmp); + start_container(_map, tmp, MSGPACK_CT_MAP_KEY); + } + + default: + ret = MSGPACK_UNPACK_PARSE_ERROR; + goto _failed; + } + } + + _push: + if(top == 0) { goto _finish; } + c = &stack[top-1]; + switch(c->ct) { + case MSGPACK_CT_ARRAY_ITEM: + ret = msgpack_unpack_callback(_array_item)(user, &c->obj, obj); \ + if(ret < 0) { goto _failed; } + if(--c->count == 0) { + obj = c->obj; + --top; + /*printf("stack pop %d\n", top);*/ + goto _push; + } + goto _header_again; + case MSGPACK_CT_MAP_KEY: + c->map_key = obj; + c->ct = MSGPACK_CT_MAP_VALUE; + goto _header_again; + case MSGPACK_CT_MAP_VALUE: + ret = msgpack_unpack_callback(_map_item)(user, &c->obj, c->map_key, obj); \ + if(ret < 0) { goto _failed; } + if(--c->count == 0) { + obj = c->obj; + --top; + /*printf("stack pop %d\n", top);*/ + goto _push; + } + c->ct = MSGPACK_CT_MAP_KEY; + goto _header_again; + + default: + ret = MSGPACK_UNPACK_PARSE_ERROR; + goto _failed; + } + + _header_again: + cs = MSGPACK_CS_HEADER; + ++p; + } while(p != pe); + goto _out; + + + _finish: + stack[0].obj = obj; + ++p; + ret = 1; + /*printf("-- finish --\n"); */ + goto _end; + + _failed: + /*printf("** FAILED **\n"); */ + goto _end; + + _out: + ret = 0; + goto _end; + + _end: + ctx->cs = cs; + ctx->trail = trail; + ctx->top = top; + *off = (size_t)(p - (const unsigned char*)data); + + return ret; + } +} + +#undef msgpack_unpack_func +#undef msgpack_unpack_callback +#undef msgpack_unpack_struct +#undef msgpack_unpack_object +#undef msgpack_unpack_user + +#undef push_simple_value +#undef push_fixed_value +#undef push_variable_value +#undef again_fixed_trail +#undef again_fixed_trail_if_zero +#undef start_container + +#undef NEXT_CS + +#undef SWITCH_RANGE_BEGIN +#undef SWITCH_RANGE +#undef SWITCH_RANGE_DEFAULT +#undef SWITCH_RANGE_END diff --git a/src/vendor/msgpack/util.h b/src/vendor/msgpack/util.h new file mode 100644 index 000000000..959b56bec --- /dev/null +++ b/src/vendor/msgpack/util.h @@ -0,0 +1,15 @@ +/* + * MessagePack for C utilities + * + * Copyright (C) 2014 FURUHASHI Sadayuki + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef MSGPACK_UTIL_H +#define MSGPACK_UTIL_H + +#define MSGPACK_UNUSED(a) (void)(a) + +#endif /* MSGPACK_UTIL_H */ diff --git a/src/vendor/msgpack/version.c b/src/vendor/msgpack/version.c new file mode 100644 index 000000000..50016f650 --- /dev/null +++ b/src/vendor/msgpack/version.c @@ -0,0 +1,22 @@ +#include "vendor/msgpack/msgpack.h" + +const char* msgpack_version(void) +{ + return MSGPACK_VERSION; +} + +int msgpack_version_major(void) +{ + return MSGPACK_VERSION_MAJOR; +} + +int msgpack_version_minor(void) +{ + return MSGPACK_VERSION_MINOR; +} + +int msgpack_version_revision(void) +{ + return MSGPACK_VERSION_REVISION; +} + diff --git a/src/vendor/msgpack/version.h b/src/vendor/msgpack/version.h new file mode 100644 index 000000000..bd6605b8c --- /dev/null +++ b/src/vendor/msgpack/version.h @@ -0,0 +1,38 @@ +/* + * MessagePack for C version information + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef MSGPACK_VERSION_H +#define MSGPACK_VERSION_H + +#ifdef __cplusplus +extern "C" { +#endif + +MSGPACK_DLLEXPORT +const char* msgpack_version(void); +MSGPACK_DLLEXPORT +int msgpack_version_major(void); +MSGPACK_DLLEXPORT +int msgpack_version_minor(void); +MSGPACK_DLLEXPORT +int msgpack_version_revision(void); + +#include "version_master.h" + +#define MSGPACK_STR(v) #v +#define MSGPACK_VERSION_I(maj, min, rev) MSGPACK_STR(maj) "." MSGPACK_STR(min) "." MSGPACK_STR(rev) + +#define MSGPACK_VERSION MSGPACK_VERSION_I(MSGPACK_VERSION_MAJOR, MSGPACK_VERSION_MINOR, MSGPACK_VERSION_REVISION) + +#ifdef __cplusplus +} +#endif + +#endif /* msgpack/version.h */ + diff --git a/src/vendor/msgpack/version_master.h b/src/vendor/msgpack/version_master.h new file mode 100644 index 000000000..9db6023e7 --- /dev/null +++ b/src/vendor/msgpack/version_master.h @@ -0,0 +1,3 @@ +#define MSGPACK_VERSION_MAJOR 3 +#define MSGPACK_VERSION_MINOR 3 +#define MSGPACK_VERSION_REVISION 0 diff --git a/src/vendor/msgpack/vrefbuffer.c b/src/vendor/msgpack/vrefbuffer.c new file mode 100644 index 000000000..9c7b3b549 --- /dev/null +++ b/src/vendor/msgpack/vrefbuffer.c @@ -0,0 +1,250 @@ +/* + * MessagePack for C zero-copy buffer implementation + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +#include "vendor/msgpack/vrefbuffer.h" +#include +#include + +#define MSGPACK_PACKER_MAX_BUFFER_SIZE 9 + +struct msgpack_vrefbuffer_chunk { + struct msgpack_vrefbuffer_chunk* next; + /* data ... */ +}; + +bool msgpack_vrefbuffer_init(msgpack_vrefbuffer* vbuf, + size_t ref_size, size_t chunk_size) +{ + size_t nfirst; + struct iovec* array; + msgpack_vrefbuffer_chunk* chunk; + + if (ref_size == 0) { + ref_size = MSGPACK_VREFBUFFER_REF_SIZE; + } + if(chunk_size == 0) { + chunk_size = MSGPACK_VREFBUFFER_CHUNK_SIZE; + } + vbuf->chunk_size = chunk_size; + vbuf->ref_size = + ref_size > MSGPACK_PACKER_MAX_BUFFER_SIZE + 1 ? + ref_size : MSGPACK_PACKER_MAX_BUFFER_SIZE + 1 ; + + if((sizeof(msgpack_vrefbuffer_chunk) + chunk_size) < chunk_size) { + return false; + } + + nfirst = (sizeof(struct iovec) < 72/2) ? + 72 / sizeof(struct iovec) : 8; + + array = (struct iovec*)malloc( + sizeof(struct iovec) * nfirst); + if(array == NULL) { + return false; + } + + vbuf->tail = array; + vbuf->end = array + nfirst; + vbuf->array = array; + + chunk = (msgpack_vrefbuffer_chunk*)malloc( + sizeof(msgpack_vrefbuffer_chunk) + chunk_size); + if(chunk == NULL) { + free(array); + return false; + } + else { + msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer; + + ib->free = chunk_size; + ib->ptr = ((char*)chunk) + sizeof(msgpack_vrefbuffer_chunk); + ib->head = chunk; + chunk->next = NULL; + + return true; + } +} + +void msgpack_vrefbuffer_destroy(msgpack_vrefbuffer* vbuf) +{ + msgpack_vrefbuffer_chunk* c = vbuf->inner_buffer.head; + while(true) { + msgpack_vrefbuffer_chunk* n = c->next; + free(c); + if(n != NULL) { + c = n; + } else { + break; + } + } + free(vbuf->array); +} + +void msgpack_vrefbuffer_clear(msgpack_vrefbuffer* vbuf) +{ + msgpack_vrefbuffer_chunk* c = vbuf->inner_buffer.head->next; + msgpack_vrefbuffer_chunk* n; + while(c != NULL) { + n = c->next; + free(c); + c = n; + } + + { + msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer; + msgpack_vrefbuffer_chunk* chunk = ib->head; + chunk->next = NULL; + ib->free = vbuf->chunk_size; + ib->ptr = ((char*)chunk) + sizeof(msgpack_vrefbuffer_chunk); + + vbuf->tail = vbuf->array; + } +} + +int msgpack_vrefbuffer_append_ref(msgpack_vrefbuffer* vbuf, + const char* buf, size_t len) +{ + if(vbuf->tail == vbuf->end) { + const size_t nused = (size_t)(vbuf->tail - vbuf->array); + const size_t nnext = nused * 2; + + struct iovec* nvec = (struct iovec*)realloc( + vbuf->array, sizeof(struct iovec)*nnext); + if(nvec == NULL) { + return -1; + } + + vbuf->array = nvec; + vbuf->end = nvec + nnext; + vbuf->tail = nvec + nused; + } + + vbuf->tail->iov_base = (char*)buf; + vbuf->tail->iov_len = len; + ++vbuf->tail; + + return 0; +} + +int msgpack_vrefbuffer_append_copy(msgpack_vrefbuffer* vbuf, + const char* buf, size_t len) +{ + msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer; + char* m; + + if(ib->free < len) { + msgpack_vrefbuffer_chunk* chunk; + size_t sz = vbuf->chunk_size; + if(sz < len) { + sz = len; + } + + if((sizeof(msgpack_vrefbuffer_chunk) + sz) < sz){ + return -1; + } + chunk = (msgpack_vrefbuffer_chunk*)malloc( + sizeof(msgpack_vrefbuffer_chunk) + sz); + if(chunk == NULL) { + return -1; + } + + chunk->next = ib->head; + ib->head = chunk; + ib->free = sz; + ib->ptr = ((char*)chunk) + sizeof(msgpack_vrefbuffer_chunk); + } + + m = ib->ptr; + memcpy(m, buf, len); + ib->free -= len; + ib->ptr += len; + + if(vbuf->tail != vbuf->array && m == + (const char*)((vbuf->tail-1)->iov_base) + (vbuf->tail-1)->iov_len) { + (vbuf->tail-1)->iov_len += len; + return 0; + } else { + return msgpack_vrefbuffer_append_ref(vbuf, m, len); + } +} + +int msgpack_vrefbuffer_migrate(msgpack_vrefbuffer* vbuf, msgpack_vrefbuffer* to) +{ + size_t sz = vbuf->chunk_size; + msgpack_vrefbuffer_chunk* empty; + + if((sizeof(msgpack_vrefbuffer_chunk) + sz) < sz){ + return -1; + } + + empty = (msgpack_vrefbuffer_chunk*)malloc( + sizeof(msgpack_vrefbuffer_chunk) + sz); + if(empty == NULL) { + return -1; + } + + empty->next = NULL; + + { + const size_t nused = (size_t)(vbuf->tail - vbuf->array); + if(to->tail + nused < vbuf->end) { + struct iovec* nvec; + const size_t tosize = (size_t)(to->tail - to->array); + const size_t reqsize = nused + tosize; + size_t nnext = (size_t)(to->end - to->array) * 2; + while(nnext < reqsize) { + size_t tmp_nnext = nnext * 2; + if (tmp_nnext <= nnext) { + nnext = reqsize; + break; + } + nnext = tmp_nnext; + } + + nvec = (struct iovec*)realloc( + to->array, sizeof(struct iovec)*nnext); + if(nvec == NULL) { + free(empty); + return -1; + } + + to->array = nvec; + to->end = nvec + nnext; + to->tail = nvec + tosize; + } + + memcpy(to->tail, vbuf->array, sizeof(struct iovec)*nused); + + to->tail += nused; + vbuf->tail = vbuf->array; + + { + msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer; + msgpack_vrefbuffer_inner_buffer* const toib = &to->inner_buffer; + + msgpack_vrefbuffer_chunk* last = ib->head; + while(last->next != NULL) { + last = last->next; + } + last->next = toib->head; + toib->head = ib->head; + + if(toib->free < ib->free) { + toib->free = ib->free; + toib->ptr = ib->ptr; + } + + ib->head = empty; + ib->free = sz; + ib->ptr = ((char*)empty) + sizeof(msgpack_vrefbuffer_chunk); + } + } + + return 0; +} diff --git a/src/vendor/msgpack/vrefbuffer.h b/src/vendor/msgpack/vrefbuffer.h new file mode 100644 index 000000000..ab51aefa3 --- /dev/null +++ b/src/vendor/msgpack/vrefbuffer.h @@ -0,0 +1,140 @@ +/* + * MessagePack for C zero-copy buffer implementation + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef MSGPACK_VREFBUFFER_H +#define MSGPACK_VREFBUFFER_H + +#include "zone.h" +#include + +#if defined(unix) || defined(__unix) || defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__QNX__) || defined(__QNXTO__) || defined(__HAIKU__) +#include +#else +struct iovec { + void *iov_base; + size_t iov_len; +}; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup msgpack_vrefbuffer Vectored Referencing buffer + * @ingroup msgpack_buffer + * @{ + */ + +struct msgpack_vrefbuffer_chunk; +typedef struct msgpack_vrefbuffer_chunk msgpack_vrefbuffer_chunk; + +typedef struct msgpack_vrefbuffer_inner_buffer { + size_t free; + char* ptr; + msgpack_vrefbuffer_chunk* head; +} msgpack_vrefbuffer_inner_buffer; + +typedef struct msgpack_vrefbuffer { + struct iovec* tail; + struct iovec* end; + struct iovec* array; + + size_t chunk_size; + size_t ref_size; + + msgpack_vrefbuffer_inner_buffer inner_buffer; +} msgpack_vrefbuffer; + + +#ifndef MSGPACK_VREFBUFFER_REF_SIZE +#define MSGPACK_VREFBUFFER_REF_SIZE 32 +#endif + +#ifndef MSGPACK_VREFBUFFER_CHUNK_SIZE +#define MSGPACK_VREFBUFFER_CHUNK_SIZE 8192 +#endif + +MSGPACK_DLLEXPORT +bool msgpack_vrefbuffer_init(msgpack_vrefbuffer* vbuf, + size_t ref_size, size_t chunk_size); +MSGPACK_DLLEXPORT +void msgpack_vrefbuffer_destroy(msgpack_vrefbuffer* vbuf); + +static inline msgpack_vrefbuffer* msgpack_vrefbuffer_new(size_t ref_size, size_t chunk_size); +static inline void msgpack_vrefbuffer_free(msgpack_vrefbuffer* vbuf); + +static inline int msgpack_vrefbuffer_write(void* data, const char* buf, size_t len); + +static inline const struct iovec* msgpack_vrefbuffer_vec(const msgpack_vrefbuffer* vref); +static inline size_t msgpack_vrefbuffer_veclen(const msgpack_vrefbuffer* vref); + +MSGPACK_DLLEXPORT +int msgpack_vrefbuffer_append_copy(msgpack_vrefbuffer* vbuf, + const char* buf, size_t len); + +MSGPACK_DLLEXPORT +int msgpack_vrefbuffer_append_ref(msgpack_vrefbuffer* vbuf, + const char* buf, size_t len); + +MSGPACK_DLLEXPORT +int msgpack_vrefbuffer_migrate(msgpack_vrefbuffer* vbuf, msgpack_vrefbuffer* to); + +MSGPACK_DLLEXPORT +void msgpack_vrefbuffer_clear(msgpack_vrefbuffer* vref); + +/** @} */ + + +static inline msgpack_vrefbuffer* msgpack_vrefbuffer_new(size_t ref_size, size_t chunk_size) +{ + msgpack_vrefbuffer* vbuf = (msgpack_vrefbuffer*)malloc(sizeof(msgpack_vrefbuffer)); + if (vbuf == NULL) return NULL; + if(!msgpack_vrefbuffer_init(vbuf, ref_size, chunk_size)) { + free(vbuf); + return NULL; + } + return vbuf; +} + +static inline void msgpack_vrefbuffer_free(msgpack_vrefbuffer* vbuf) +{ + if(vbuf == NULL) { return; } + msgpack_vrefbuffer_destroy(vbuf); + free(vbuf); +} + +static inline int msgpack_vrefbuffer_write(void* data, const char* buf, size_t len) +{ + msgpack_vrefbuffer* vbuf = (msgpack_vrefbuffer*)data; + + if(len < vbuf->ref_size) { + return msgpack_vrefbuffer_append_copy(vbuf, buf, len); + } else { + return msgpack_vrefbuffer_append_ref(vbuf, buf, len); + } +} + +static inline const struct iovec* msgpack_vrefbuffer_vec(const msgpack_vrefbuffer* vref) +{ + return vref->array; +} + +static inline size_t msgpack_vrefbuffer_veclen(const msgpack_vrefbuffer* vref) +{ + return (size_t)(vref->tail - vref->array); +} + + +#ifdef __cplusplus +} +#endif + +#endif /* msgpack/vrefbuffer.h */ diff --git a/src/vendor/msgpack/zbuffer.h b/src/vendor/msgpack/zbuffer.h new file mode 100644 index 000000000..524906fab --- /dev/null +++ b/src/vendor/msgpack/zbuffer.h @@ -0,0 +1,201 @@ +/* + * MessagePack for C deflate buffer implementation + * + * Copyright (C) 2010 FURUHASHI Sadayuki + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef MSGPACK_ZBUFFER_H +#define MSGPACK_ZBUFFER_H + +#include "sysdep.h" +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup msgpack_zbuffer Compressed buffer + * @ingroup msgpack_buffer + * @{ + */ + +typedef struct msgpack_zbuffer { + z_stream stream; + char* data; + size_t init_size; +} msgpack_zbuffer; + +#ifndef MSGPACK_ZBUFFER_INIT_SIZE +#define MSGPACK_ZBUFFER_INIT_SIZE 8192 +#endif + +static inline bool msgpack_zbuffer_init( + msgpack_zbuffer* zbuf, int level, size_t init_size); +static inline void msgpack_zbuffer_destroy(msgpack_zbuffer* zbuf); + +static inline msgpack_zbuffer* msgpack_zbuffer_new(int level, size_t init_size); +static inline void msgpack_zbuffer_free(msgpack_zbuffer* zbuf); + +static inline char* msgpack_zbuffer_flush(msgpack_zbuffer* zbuf); + +static inline const char* msgpack_zbuffer_data(const msgpack_zbuffer* zbuf); +static inline size_t msgpack_zbuffer_size(const msgpack_zbuffer* zbuf); + +static inline bool msgpack_zbuffer_reset(msgpack_zbuffer* zbuf); +static inline void msgpack_zbuffer_reset_buffer(msgpack_zbuffer* zbuf); +static inline char* msgpack_zbuffer_release_buffer(msgpack_zbuffer* zbuf); + + +#ifndef MSGPACK_ZBUFFER_RESERVE_SIZE +#define MSGPACK_ZBUFFER_RESERVE_SIZE 512 +#endif + +static inline int msgpack_zbuffer_write(void* data, const char* buf, size_t len); + +static inline bool msgpack_zbuffer_expand(msgpack_zbuffer* zbuf); + + +static inline bool msgpack_zbuffer_init(msgpack_zbuffer* zbuf, + int level, size_t init_size) +{ + memset(zbuf, 0, sizeof(msgpack_zbuffer)); + zbuf->init_size = init_size; + if(deflateInit(&zbuf->stream, level) != Z_OK) { + free(zbuf->data); + return false; + } + return true; +} + +static inline void msgpack_zbuffer_destroy(msgpack_zbuffer* zbuf) +{ + deflateEnd(&zbuf->stream); + free(zbuf->data); +} + +static inline msgpack_zbuffer* msgpack_zbuffer_new(int level, size_t init_size) +{ + msgpack_zbuffer* zbuf = (msgpack_zbuffer*)malloc(sizeof(msgpack_zbuffer)); + if (zbuf == NULL) return NULL; + if(!msgpack_zbuffer_init(zbuf, level, init_size)) { + free(zbuf); + return NULL; + } + return zbuf; +} + +static inline void msgpack_zbuffer_free(msgpack_zbuffer* zbuf) +{ + if(zbuf == NULL) { return; } + msgpack_zbuffer_destroy(zbuf); + free(zbuf); +} + +static inline bool msgpack_zbuffer_expand(msgpack_zbuffer* zbuf) +{ + size_t used = (size_t)((char *)(zbuf->stream.next_out) - zbuf->data); + size_t csize = used + zbuf->stream.avail_out; + + size_t nsize = (csize == 0) ? zbuf->init_size : csize * 2; + + char* tmp = (char*)realloc(zbuf->data, nsize); + if(tmp == NULL) { + return false; + } + + zbuf->data = tmp; + zbuf->stream.next_out = (Bytef*)(tmp + used); + zbuf->stream.avail_out = (uInt)(nsize - used); + + return true; +} + +static inline int msgpack_zbuffer_write(void* data, const char* buf, size_t len) +{ + msgpack_zbuffer* zbuf = (msgpack_zbuffer*)data; + + zbuf->stream.next_in = (Bytef*)buf; + zbuf->stream.avail_in = (uInt)len; + + while(zbuf->stream.avail_in > 0) { + if(zbuf->stream.avail_out < MSGPACK_ZBUFFER_RESERVE_SIZE) { + if(!msgpack_zbuffer_expand(zbuf)) { + return -1; + } + } + + if(deflate(&zbuf->stream, Z_NO_FLUSH) != Z_OK) { + return -1; + } + } + + return 0; +} + +static inline char* msgpack_zbuffer_flush(msgpack_zbuffer* zbuf) +{ + while(true) { + switch(deflate(&zbuf->stream, Z_FINISH)) { + case Z_STREAM_END: + return zbuf->data; + case Z_OK: + case Z_BUF_ERROR: + if(!msgpack_zbuffer_expand(zbuf)) { + return NULL; + } + break; + default: + return NULL; + } + } +} + +static inline const char* msgpack_zbuffer_data(const msgpack_zbuffer* zbuf) +{ + return zbuf->data; +} + +static inline size_t msgpack_zbuffer_size(const msgpack_zbuffer* zbuf) +{ + return (size_t)((char *)(zbuf->stream.next_out) - zbuf->data); +} + +static inline void msgpack_zbuffer_reset_buffer(msgpack_zbuffer* zbuf) +{ + zbuf->stream.avail_out += (uInt)((char*)zbuf->stream.next_out - zbuf->data); + zbuf->stream.next_out = (Bytef*)zbuf->data; +} + +static inline bool msgpack_zbuffer_reset(msgpack_zbuffer* zbuf) +{ + if(deflateReset(&zbuf->stream) != Z_OK) { + return false; + } + msgpack_zbuffer_reset_buffer(zbuf); + return true; +} + +static inline char* msgpack_zbuffer_release_buffer(msgpack_zbuffer* zbuf) +{ + char* tmp = zbuf->data; + zbuf->data = NULL; + zbuf->stream.next_out = NULL; + zbuf->stream.avail_out = 0; + return tmp; +} + +/** @} */ + + +#ifdef __cplusplus +} +#endif + +#endif /* msgpack/zbuffer.h */ diff --git a/src/vendor/msgpack/zone.c b/src/vendor/msgpack/zone.c new file mode 100644 index 000000000..d63b9d63d --- /dev/null +++ b/src/vendor/msgpack/zone.c @@ -0,0 +1,222 @@ +/* + * MessagePack for C memory pool implementation + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +#include "vendor/msgpack/zone.h" +#include +#include + +struct msgpack_zone_chunk { + struct msgpack_zone_chunk* next; + /* data ... */ +}; + +static inline bool init_chunk_list(msgpack_zone_chunk_list* cl, size_t chunk_size) +{ + msgpack_zone_chunk* chunk = (msgpack_zone_chunk*)malloc( + sizeof(msgpack_zone_chunk) + chunk_size); + if(chunk == NULL) { + return false; + } + + cl->head = chunk; + cl->free = chunk_size; + cl->ptr = ((char*)chunk) + sizeof(msgpack_zone_chunk); + chunk->next = NULL; + + return true; +} + +static inline void destroy_chunk_list(msgpack_zone_chunk_list* cl) +{ + msgpack_zone_chunk* c = cl->head; + while(true) { + msgpack_zone_chunk* n = c->next; + free(c); + if(n != NULL) { + c = n; + } else { + break; + } + } +} + +static inline void clear_chunk_list(msgpack_zone_chunk_list* cl, size_t chunk_size) +{ + msgpack_zone_chunk* c = cl->head; + while(true) { + msgpack_zone_chunk* n = c->next; + if(n != NULL) { + free(c); + c = n; + } else { + cl->head = c; + break; + } + } + cl->head->next = NULL; + cl->free = chunk_size; + cl->ptr = ((char*)cl->head) + sizeof(msgpack_zone_chunk); +} + +void* msgpack_zone_malloc_expand(msgpack_zone* zone, size_t size) +{ + msgpack_zone_chunk_list* const cl = &zone->chunk_list; + msgpack_zone_chunk* chunk; + + size_t sz = zone->chunk_size; + + while(sz < size) { + size_t tmp_sz = sz * 2; + if (tmp_sz <= sz) { + sz = size; + break; + } + sz = tmp_sz; + } + + chunk = (msgpack_zone_chunk*)malloc( + sizeof(msgpack_zone_chunk) + sz); + if (chunk == NULL) { + return NULL; + } + else { + char* ptr = ((char*)chunk) + sizeof(msgpack_zone_chunk); + chunk->next = cl->head; + cl->head = chunk; + cl->free = sz - size; + cl->ptr = ptr + size; + + return ptr; + } +} + + +static inline void init_finalizer_array(msgpack_zone_finalizer_array* fa) +{ + fa->tail = NULL; + fa->end = NULL; + fa->array = NULL; +} + +static inline void call_finalizer_array(msgpack_zone_finalizer_array* fa) +{ + msgpack_zone_finalizer* fin = fa->tail; + for(; fin != fa->array; --fin) { + (*(fin-1)->func)((fin-1)->data); + } +} + +static inline void destroy_finalizer_array(msgpack_zone_finalizer_array* fa) +{ + call_finalizer_array(fa); + free(fa->array); +} + +static inline void clear_finalizer_array(msgpack_zone_finalizer_array* fa) +{ + call_finalizer_array(fa); + fa->tail = fa->array; +} + +bool msgpack_zone_push_finalizer_expand(msgpack_zone* zone, + void (*func)(void* data), void* data) +{ + msgpack_zone_finalizer_array* const fa = &zone->finalizer_array; + msgpack_zone_finalizer* tmp; + + const size_t nused = (size_t)(fa->end - fa->array); + + size_t nnext; + if(nused == 0) { + nnext = (sizeof(msgpack_zone_finalizer) < 72/2) ? + 72 / sizeof(msgpack_zone_finalizer) : 8; + + } else { + nnext = nused * 2; + } + + tmp = (msgpack_zone_finalizer*)realloc(fa->array, + sizeof(msgpack_zone_finalizer) * nnext); + if(tmp == NULL) { + return false; + } + + fa->array = tmp; + fa->end = tmp + nnext; + fa->tail = tmp + nused; + + fa->tail->func = func; + fa->tail->data = data; + + ++fa->tail; + + return true; +} + + +bool msgpack_zone_is_empty(msgpack_zone* zone) +{ + msgpack_zone_chunk_list* const cl = &zone->chunk_list; + msgpack_zone_finalizer_array* const fa = &zone->finalizer_array; + return cl->free == zone->chunk_size && cl->head->next == NULL && + fa->tail == fa->array; +} + + +void msgpack_zone_destroy(msgpack_zone* zone) +{ + destroy_finalizer_array(&zone->finalizer_array); + destroy_chunk_list(&zone->chunk_list); +} + +void msgpack_zone_clear(msgpack_zone* zone) +{ + clear_finalizer_array(&zone->finalizer_array); + clear_chunk_list(&zone->chunk_list, zone->chunk_size); +} + +bool msgpack_zone_init(msgpack_zone* zone, size_t chunk_size) +{ + zone->chunk_size = chunk_size; + + if(!init_chunk_list(&zone->chunk_list, chunk_size)) { + return false; + } + + init_finalizer_array(&zone->finalizer_array); + + return true; +} + +msgpack_zone* msgpack_zone_new(size_t chunk_size) +{ + msgpack_zone* zone = (msgpack_zone*)malloc( + sizeof(msgpack_zone)); + if(zone == NULL) { + return NULL; + } + + zone->chunk_size = chunk_size; + + if(!init_chunk_list(&zone->chunk_list, chunk_size)) { + free(zone); + return NULL; + } + + init_finalizer_array(&zone->finalizer_array); + + return zone; +} + +void msgpack_zone_free(msgpack_zone* zone) +{ + if(zone == NULL) { return; } + msgpack_zone_destroy(zone); + free(zone); +} diff --git a/src/vendor/msgpack/zone.h b/src/vendor/msgpack/zone.h new file mode 100644 index 000000000..9005be793 --- /dev/null +++ b/src/vendor/msgpack/zone.h @@ -0,0 +1,163 @@ +/* + * MessagePack for C memory pool implementation + * + * Copyright (C) 2008-2010 FURUHASHI Sadayuki + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef MSGPACK_ZONE_H +#define MSGPACK_ZONE_H + +#include "sysdep.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup msgpack_zone Memory zone + * @ingroup msgpack + * @{ + */ + +typedef struct msgpack_zone_finalizer { + void (*func)(void* data); + void* data; +} msgpack_zone_finalizer; + +typedef struct msgpack_zone_finalizer_array { + msgpack_zone_finalizer* tail; + msgpack_zone_finalizer* end; + msgpack_zone_finalizer* array; +} msgpack_zone_finalizer_array; + +struct msgpack_zone_chunk; +typedef struct msgpack_zone_chunk msgpack_zone_chunk; + +typedef struct msgpack_zone_chunk_list { + size_t free; + char* ptr; + msgpack_zone_chunk* head; +} msgpack_zone_chunk_list; + +typedef struct msgpack_zone { + msgpack_zone_chunk_list chunk_list; + msgpack_zone_finalizer_array finalizer_array; + size_t chunk_size; +} msgpack_zone; + +#ifndef MSGPACK_ZONE_CHUNK_SIZE +#define MSGPACK_ZONE_CHUNK_SIZE 8192 +#endif + +MSGPACK_DLLEXPORT +bool msgpack_zone_init(msgpack_zone* zone, size_t chunk_size); +MSGPACK_DLLEXPORT +void msgpack_zone_destroy(msgpack_zone* zone); + +MSGPACK_DLLEXPORT +msgpack_zone* msgpack_zone_new(size_t chunk_size); +MSGPACK_DLLEXPORT +void msgpack_zone_free(msgpack_zone* zone); + +static inline void* msgpack_zone_malloc(msgpack_zone* zone, size_t size); +static inline void* msgpack_zone_malloc_no_align(msgpack_zone* zone, size_t size); + +static inline bool msgpack_zone_push_finalizer(msgpack_zone* zone, + void (*func)(void* data), void* data); + +static inline void msgpack_zone_swap(msgpack_zone* a, msgpack_zone* b); + +MSGPACK_DLLEXPORT +bool msgpack_zone_is_empty(msgpack_zone* zone); + +MSGPACK_DLLEXPORT +void msgpack_zone_clear(msgpack_zone* zone); + +/** @} */ + + +#ifndef MSGPACK_ZONE_ALIGN +#define MSGPACK_ZONE_ALIGN sizeof(void*) +#endif + +MSGPACK_DLLEXPORT +void* msgpack_zone_malloc_expand(msgpack_zone* zone, size_t size); + +static inline void* msgpack_zone_malloc_no_align(msgpack_zone* zone, size_t size) +{ + char* ptr; + msgpack_zone_chunk_list* cl = &zone->chunk_list; + + if(zone->chunk_list.free < size) { + return msgpack_zone_malloc_expand(zone, size); + } + + ptr = cl->ptr; + cl->free -= size; + cl->ptr += size; + + return ptr; +} + +static inline void* msgpack_zone_malloc(msgpack_zone* zone, size_t size) +{ + char* aligned = + (char*)( + (size_t)( + zone->chunk_list.ptr + (MSGPACK_ZONE_ALIGN - 1) + ) / MSGPACK_ZONE_ALIGN * MSGPACK_ZONE_ALIGN + ); + size_t adjusted_size = size + (size_t)(aligned - zone->chunk_list.ptr); + if(zone->chunk_list.free >= adjusted_size) { + zone->chunk_list.free -= adjusted_size; + zone->chunk_list.ptr += adjusted_size; + return aligned; + } + { + void* ptr = msgpack_zone_malloc_expand(zone, size + (MSGPACK_ZONE_ALIGN - 1)); + if (ptr) { + return (char*)((size_t)(ptr) / MSGPACK_ZONE_ALIGN * MSGPACK_ZONE_ALIGN); + } + } + return NULL; +} + + +bool msgpack_zone_push_finalizer_expand(msgpack_zone* zone, + void (*func)(void* data), void* data); + +static inline bool msgpack_zone_push_finalizer(msgpack_zone* zone, + void (*func)(void* data), void* data) +{ + msgpack_zone_finalizer_array* const fa = &zone->finalizer_array; + msgpack_zone_finalizer* fin = fa->tail; + + if(fin == fa->end) { + return msgpack_zone_push_finalizer_expand(zone, func, data); + } + + fin->func = func; + fin->data = data; + + ++fa->tail; + + return true; +} + +static inline void msgpack_zone_swap(msgpack_zone* a, msgpack_zone* b) +{ + msgpack_zone tmp = *a; + *a = *b; + *b = tmp; +} + + +#ifdef __cplusplus +} +#endif + +#endif /* msgpack/zone.h */ diff --git a/tests/regression/Makefile.am b/tests/regression/Makefile.am index cbac90da7..b42248044 100644 --- a/tests/regression/Makefile.am +++ b/tests/regression/Makefile.am @@ -27,8 +27,17 @@ TESTS = tools/filtering/test_invalid_filter \ tools/crash/test_crash \ tools/regen-metadata/test_ust \ tools/regen-statedump/test_ust \ - tools/notification/test_notification_ust \ - tools/notification/test_notification_kernel \ + tools/notification/test_notification_ust_error \ + tools/notification/test_notification_ust_buffer_usage \ + tools/notification/test_notification_ust_capture \ + tools/notification/test_notification_ust_event_rule_condition_exclusion \ + tools/notification/test_notification_kernel_error \ + tools/notification/test_notification_kernel_buffer_usage \ + tools/notification/test_notification_kernel_capture \ + tools/notification/test_notification_kernel_instrumentation \ + tools/notification/test_notification_kernel_syscall \ + tools/notification/test_notification_trigger_discarded_count \ + tools/notification/test_notification_kernel_userspace_probe \ tools/notification/test_notification_multi_app \ tools/rotation/test_ust \ tools/rotation/test_kernel \ @@ -36,10 +45,13 @@ TESTS = tools/filtering/test_invalid_filter \ tools/rotation/test_schedule_api \ tools/metadata/test_kernel \ tools/working-directory/test_relayd_working_directory \ - tools/notification/test_notification_multi_app \ tools/clear/test_ust \ tools/clear/test_kernel \ - tools/tracker/test_event_tracker + tools/tracker/test_event_tracker \ + tools/trigger/start-stop/test_start_stop \ + tools/trigger/test_add_trigger_cli \ + tools/trigger/test_list_triggers_cli \ + tools/trigger/test_remove_trigger_cli if HAVE_LIBLTTNG_UST_CTL SUBDIRS += ust diff --git a/tests/regression/kernel/test_callstack b/tests/regression/kernel/test_callstack index c4a6200da..42c3fcce4 100755 --- a/tests/regression/kernel/test_callstack +++ b/tests/regression/kernel/test_callstack @@ -33,12 +33,15 @@ function lttng_track_pid() function run_workload() { local TEST_APP=$1 + # shift the first argument, passing along the other args if any to the + # test app. + shift local start_file_sync start_file_sync=$(mktemp -u) lttng_untrack_all - ./"$TEST_APP" "$start_file_sync" & + ./"$TEST_APP" "$start_file_sync" "$@" & PID=$! lttng_track_pid $PID @@ -108,7 +111,7 @@ function test_kernel_callstack() lttng_enable_kernel_syscall_ok "$SESSION_NAME" "$EVENT_NAME" "$CHANNEL_NAME" add_context_kernel_ok "$SESSION_NAME" "$CHANNEL_NAME" "callstack-kernel" - run_workload $TEST_APP_KERNELSPACE + run_workload "$TEST_APP_KERNELSPACE" "/proc/cpuinfo" "/proc/cmdline" destroy_lttng_session_ok "$SESSION_NAME" diff --git a/tests/regression/kernel/test_syscall b/tests/regression/kernel/test_syscall index 487ee668e..d09d61a95 100755 --- a/tests/regression/kernel/test_syscall +++ b/tests/regression/kernel/test_syscall @@ -27,7 +27,7 @@ function trace_testapp() lttng_untrack_kernel_all_ok # Launch the testapp and save its Process ID - ./"$TESTCMD" "$start_file_sync" & + ./"$TESTCMD" "$start_file_sync" "/proc/cpuinfo" "/proc/cmdline" & PID=$! # Set LTTng to track this PID and start the tracing diff --git a/tests/regression/tools/Makefile.am b/tests/regression/tools/Makefile.am index 22691e5e2..d561a6479 100644 --- a/tests/regression/tools/Makefile.am +++ b/tests/regression/tools/Makefile.am @@ -2,4 +2,4 @@ SUBDIRS = streaming filtering health tracefile-limits snapshots live exclusion save-load mi \ wildcard crash regen-metadata regen-statedump notification rotation \ - base-path metadata working-directory relayd-grouping clear tracker + base-path metadata working-directory relayd-grouping clear tracker trigger diff --git a/tests/regression/tools/notification/Makefile.am b/tests/regression/tools/notification/Makefile.am index f1f2a1667..c086bcf38 100644 --- a/tests/regression/tools/notification/Makefile.am +++ b/tests/regression/tools/notification/Makefile.am @@ -9,9 +9,25 @@ noinst_PROGRAMS = base_client notification rotation if NO_SHARED -CLEANFILES = libpause_consumer.so libpause_consumer.so.debug -EXTRA_DIST = test_notification_ust test_notification_kernel test_notification_multi_app base_client.c notification.c consumer_testpoints.c - +CLEANFILES = libpause_consumer.so libpause_consumer.so.debug libpause_sessiond.so libpause_sessiond.so.debug +EXTRA_DIST = \ + base_client.c \ + consumer_testpoints.c \ + sessiond_testpoints.c \ + notification.c \ + test_notification_kernel_buffer_usage \ + test_notification_kernel_capture \ + test_notification_kernel_error \ + test_notification_kernel_instrumentation \ + test_notification_kernel_syscall \ + test_notification_kernel_userspace_probe \ + test_notification_multi_app \ + test_notification_trigger_discarded_count \ + test_notification_ust_buffer_usage \ + test_notification_ust_capture \ + test_notification_ust_error \ + test_notification_ust_event_rule_condition_exclusion \ + util_event_generator.sh else # In order to test the health check feature, the helper library @@ -26,7 +42,14 @@ libpause_consumer_la_LIBADD = \ $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \ $(DL_LIBS) libpause_consumer_la_LDFLAGS = $(FORCE_SHARED_LIB_OPTIONS) -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) @@ -37,9 +60,35 @@ notification_LDADD = $(LIB_LTTNG_CTL) $(LIBTAP) -lm rotation_SOURCES = rotation.c rotation_LDADD = $(LIB_LTTNG_CTL) $(LIBTAP) -lm -noinst_SCRIPTS = test_notification_ust test_notification_kernel test_notification_multi_app test_rotation -EXTRA_DIST = test_notification_ust test_notification_kernel test_notification_multi_app test_rotation +noinst_SCRIPTS = \ + test_notification_kernel_buffer_usage \ + test_notification_kernel_error \ + test_notification_kernel_instrumentation \ + test_notification_kernel_syscall \ + test_notification_kernel_userspace_probe \ + test_notification_multi_app \ + test_notification_trigger_discarded_count \ + test_notification_ust_buffer_usage \ + test_notification_ust_error \ + test_notification_ust_event_rule_condition_exclusion \ + test_rotation \ + util_event_generator.sh +EXTRA_DIST = \ + test_notification_kernel_buffer_usage \ + test_notification_kernel_capture \ + test_notification_kernel_error \ + test_notification_kernel_instrumentation \ + test_notification_kernel_syscall \ + test_notification_kernel_userspace_probe \ + test_notification_multi_app \ + test_notification_trigger_discarded_count \ + test_notification_ust_buffer_usage \ + test_notification_ust_capture \ + test_notification_ust_error \ + test_notification_ust_event_rule_condition_exclusion \ + test_rotation \ + util_event_generator.sh all-local: @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ diff --git a/tests/regression/tools/notification/notification.c b/tests/regression/tools/notification/notification.c index 364d41895..303d8bc8c 100644 --- a/tests/regression/tools/notification/notification.c +++ b/tests/regression/tools/notification/notification.c @@ -29,701 +29,2463 @@ #include #include #include +#include #include #include +#include +#include +#include +#include +#include +#include #include +#include #include #include +#include #include -#include +#include #include -#define NUM_TESTS 104 +/* 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 = -1; +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 -void wait_on_file(const char *path, bool file_exist) +const char *field_value_type_to_str(enum lttng_event_field_value_type type) { - if (!path) { - return; + switch (type) { + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN: + return "UNKNOWN"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID: + return "INVALID"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT: + return "UNSIGNED INT"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT: + return "SIGNED INT"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM: + return "UNSIGNED ENUM"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM: + return "SIGNED ENUM"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_REAL: + return "REAL"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_STRING: + return "STRING"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY: + return "ARRAY"; + default: + abort(); } - for (;;) { - int ret; - struct stat buf; +} - ret = stat(path, &buf); - if (ret == -1 && errno == ENOENT) { - if (file_exist) { - /* - * The file does not exist. wait a bit and - * continue looping until it does. - */ - (void) poll(NULL, 0, 10); - continue; - } +static int validate_type( + const struct lttng_event_field_value *event_field, + enum lttng_event_field_value_type expect) +{ + int ret; + enum lttng_event_field_value_type value; - /* - * File does not exist and the exit condition we want. - * Break from the loop and return. - */ - break; - } - if (ret) { - perror("stat"); - exit(EXIT_FAILURE); - } - /* - * stat() returned 0, so the file exists. break now only if - * that's the exit condition we want. - */ - if (file_exist) { - break; - } + value = lttng_event_field_value_get_type(event_field); + if (value == LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID) { + ret = 1; + goto end; } + + ret = (expect == value); + ok(ret, "Expected field type: %s got %s", + field_value_type_to_str(expect), + field_value_type_to_str(value)); + + ret = !ret; + +end: + return ret; } -static -int write_pipe(const char *path, uint8_t data) +/* + * Validate unsigned captured field against the iteration number. + * The iteration number is always unsigned and will always be compared to value + * under MAX_UINT. + */ +static int validate_unsigned_int_field( + const struct lttng_event_field_value *event_field, + unsigned int iteration) { - int ret = 0; - int fd = 0; + int ret; + uint64_t value; + enum lttng_event_field_value_status status; - fd = open(path, O_WRONLY | O_NONBLOCK); - if (fd < 0) { - perror("Could not open consumer control named pipe"); + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT); + if (ret) { goto end; } - ret = write(fd, &data , sizeof(data)); - if (ret < 1) { - perror("Named pipe write failed"); - if (close(fd)) { - perror("Named pipe close failed"); - } - ret = -1; + status = lttng_event_field_value_unsigned_int_get_value( + event_field , &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_unsigned_int_get_value"); + ret = 1; goto end; } - ret = close(fd); - if (ret < 0) { - perror("Name pipe closing failed"); - ret = -1; - goto end; - } + ret = (value == (uint64_t) iteration); + ok (ret, "Expected unsigned int of value: %u got %" PRIu64, iteration, value); + + ret = !ret; + end: + return ret; } -static -int stop_consumer(const char **argv) +/* + * Validate signed captured field. + * Value should be -1. + */ +static int validate_signed_int_field( + const struct lttng_event_field_value *event_field, + unsigned int iteration) { - int ret = 0, i; + int ret; + int64_t expected = -1; + int64_t value; + enum lttng_event_field_value_status status; - for (i = named_pipe_args_start; i < nb_args; i++) { - ret = write_pipe(argv[i], 49); + /* Unused */ + (void) iteration; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT); + if (ret) { + goto end; + } + + status = lttng_event_field_value_signed_int_get_value( + event_field , &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_signed_int_get_value"); + ret = 1; + goto end; } + + ret = (value == expected); + ok(ret, "Expected signed int of value: %" PRId64 " got %" PRId64, expected, value); + + ret = !ret; + +end: + return ret; } -static -int resume_consumer(const char **argv) +/* + * Validate array of unsigned int. + */ +static int validate_array_unsigned_int_field( + const struct lttng_event_field_value *event_field, + unsigned int iteration) { - int ret = 0, i; + int ret; + enum lttng_event_field_value_status status; + unsigned int expected = 3; + unsigned int count; - for (i = named_pipe_args_start; i < nb_args; i++) { - ret = write_pipe(argv[i], 0); + /* Unused */ + (void) iteration; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY); + if (ret) { + goto end; + } + + status = lttng_event_field_value_array_get_length(event_field, &count); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_array_get_length"); + ret = 1; + goto end; + } + + ret = (count == expected); + ok(ret, "Expected %d subelements got %d", expected, count); + if (!ret) { + ret = 1; + goto end; + } + + for (unsigned int i = 1; i < count + 1; i++) { + const struct lttng_event_field_value *value; + status = lttng_event_field_value_array_get_element_at_index( + event_field, i - 1, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_array_get_element_at_index"); + ret = 1; + goto end; + } + ret = validate_unsigned_int_field(value, i); + if (ret) { + goto end; + } } + + ret = 0; +end: + return ret; } - -static -int suspend_application(void) +static int validate_array_unsigned_int_field_at_index( + const struct lttng_event_field_value *event_field, + unsigned int iteration) { int ret; - struct stat buf; + uint64_t expected_value = 2; + enum lttng_event_field_value_status status; + uint64_t value; - if (!stat(app_state_file, &buf)) { - fail("App is already in a suspended state."); - ret = -1; - goto error; - } + /* Unused */ + (void) iteration; - /* - * Send SIGUSR1 to application instructing it to bypass tracepoint. - */ - ret = kill(app_pid, SIGUSR1); + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT); if (ret) { - fail("SIGUSR1 failed. errno %d", errno); - ret = -1; - goto error; + goto end; } - wait_on_file(app_state_file, true); + status = lttng_event_field_value_unsigned_int_get_value( + event_field , &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_unsigned_int_get_value"); + ret = 1; + goto end; + } -error: - return ret; + ret = (value == expected_value); + ok (ret, "Expected unsigned int of value: %u got %" PRIu64, + expected_value, value); + ret = 0; +end: + return ret; } -static -int resume_application() +/* + * Validate sequence for a string (seqfield1): + * + * Value: "test" in utf8 [116, 101, 115, 116] + */ +static int validate_seqfield1( + const struct lttng_event_field_value *event_field, + unsigned int iteration) { int ret; - struct stat buf; + enum lttng_event_field_value_status status; + unsigned int count; + unsigned int expect[4] = {116, 101, 115, 116}; - ret = stat(app_state_file, &buf); - if (ret == -1 && errno == ENOENT) { - fail("State file does not exist"); - goto error; - } + /* Unused */ + (void) iteration; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY); if (ret) { - perror("stat"); - goto error; + goto end; } - ret = kill(app_pid, SIGUSR1); - if (ret) { - fail("SIGUSR1 failed. errno %d", errno); - ret = -1; - goto error; + status = lttng_event_field_value_array_get_length(event_field, &count); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_array_get_length"); + ret = 1; + goto end; } - wait_on_file(app_state_file, false); + ret = (count == 4); + ok(ret, "Expected 4 subelement got %d", count); + if (!ret) { + ret = 1; + goto end; + } -error: - return ret; + for (unsigned int i = 0; i < count ; i++) { + const struct lttng_event_field_value *value; + status = lttng_event_field_value_array_get_element_at_index( + event_field, i, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_array_get_element_at_index"); + ret = 1; + goto end; + } + ret = validate_unsigned_int_field(value, expect[i]); + if (ret) { + goto end; + } + } -} + ret = 0; +end: + return ret; +} -static -void test_triggers_buffer_usage_condition(const char *session_name, - const char *channel_name, - enum lttng_domain_type domain_type, - enum lttng_condition_type condition_type) +static int validate_string( + const struct lttng_event_field_value *event_field, + const char *expect) { - unsigned int test_vector_size = 5, i; - enum lttng_condition_status condition_status; - struct lttng_action *action; + int ret; + const char *value = NULL; - /* Set-up */ - action = lttng_action_notify_create(); - if (!action) { - fail("Setup error on action creation"); + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_STRING); + if (ret) { goto end; } - /* Test lttng_register_trigger with null value */ - ok(lttng_register_trigger(NULL) == -LTTNG_ERR_INVALID, "Registering a NULL trigger fails as expected"); + value = lttng_event_field_value_string_get_value(event_field); + if (!value) { + fail("lttng_event_field_value_array_get_length"); + ret = 1; + goto end; + } - /* Test: register a trigger */ + ok(!strcmp(value, expect), "Expected string: \"%s\" got \"%s\"", expect, value); - for (i = 0; i < pow(2,test_vector_size); i++) { - int loop_ret = 0; - char *test_tuple_string = NULL; - unsigned int mask_position = 0; - bool session_name_set = false; - bool channel_name_set = false; - bool threshold_ratio_set = false; - bool threshold_byte_set = false; - bool domain_type_set = false; + ret = 0; +end: - struct lttng_trigger *trigger = NULL; - struct lttng_condition *condition = NULL; + return ret; +} - /* Create base condition */ - switch (condition_type) { - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: - condition = lttng_condition_buffer_usage_low_create(); - break; - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: - condition = lttng_condition_buffer_usage_high_create(); - break; - default: - loop_ret = 1; - goto loop_end; - } +/* + * Validate string. Expected value is "test". + */ +static int validate_string_test( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + const char *expect = "test"; - if (!condition) { - loop_ret = 1; - goto loop_end; + /* Unused */ + (void) iteration; - } + ret = validate_string(event_field, expect); + return ret; +} - /* Prepare the condition for trigger registration test */ +/* + * Validate escaped string. Expected value is "\*". + */ +static int validate_string_escaped( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + const char *expect = "\\*"; - /* Set session name */ - if ((1 << mask_position) & i) { - condition_status = lttng_condition_buffer_usage_set_session_name( - condition, session_name); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - loop_ret = 1; - goto loop_end; - } - session_name_set = true; - } - mask_position++; + /* Unused */ + (void) iteration; - /* Set channel name */ - if ((1 << mask_position) & i) { - condition_status = lttng_condition_buffer_usage_set_channel_name( - condition, channel_name); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - loop_ret = 1; - goto loop_end; - } - channel_name_set = true; + ret = validate_string(event_field, expect); + return ret; +} + +/* + * Validate real field. + */ +static int validate_real( + const struct lttng_event_field_value *event_field, + double expect) +{ + int ret; + double value; + enum lttng_event_field_value_status status; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_REAL); + if (ret) { + goto end; + } + + status = lttng_event_field_value_real_get_value(event_field, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_real_get_value"); + ret = 1; + goto end; + } + + ret = (value == expect); + ok(ret, "Real expected: %f got: %f", expect, value); + + ret = !ret; +end: + return ret; +} + +/* + * Validate floatfield. + */ +static int validate_floatfield( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + double expect = 2222.0; + + /* Unused */ + (void) iteration; + + ret = validate_real(event_field, expect); + return ret; +} + +/* + * Validate doublefield. + */ +static int validate_doublefield( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + double expect = 2.0; + + /* Unused */ + (void) iteration; + + ret = validate_real(event_field, expect); + return ret; +} + +/* + * Validate enum0: enum0 = ( "AUTO: EXPECT 0" : container = 0 ) + */ +static int validate_enum0(const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + enum lttng_event_field_value_status status; + uint64_t value; + uint64_t expected_value = 0; + + /* Unused */ + (void) iteration; + + ret = validate_type(event_field, + LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM); + if (ret) { + goto end; + } + + status = lttng_event_field_value_unsigned_int_get_value( + event_field, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_unsigned_int_get_value"); + ret = 1; + goto end; + } + + ok(value == expected_value, + "Enum value expected: %" PRIu64 " got %" PRIu64, + expected_value, value); + +end: + return ret; +} + +/* + * Validate enumnegative: enumnegative = ( "AUTO: EXPECT 0" : container = 0 ) + * + * We expect 2 labels here. + */ +static int validate_enumnegative( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + enum lttng_event_field_value_status status; + int64_t value; + int64_t expected_value = -1; + + /* Unused */ + (void) iteration; + + ret = validate_type( + event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM); + if (ret) { + goto end; + } + + status = lttng_event_field_value_signed_int_get_value( + event_field, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_unsigned_int_get_value"); + ret = 1; + goto end; + } + + ok(value == expected_value, + "Enum value expected: %" PRId64 " got %" PRId64, + expected_value, value); + +end: + return ret; +} + +static int validate_context_procname_ust( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + + /* Unused */ + (void) iteration; + + ret = validate_string(event_field, "gen-ust-events"); + return ret; +} + +static int validate_context_procname_kernel( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + + /* Unused */ + (void) iteration; + + ret = validate_string(event_field, "echo"); + return ret; +} + +struct capture_base_field_tuple test_capture_base_fields[] = { + {"DOESNOTEXIST", FIELD_TYPE_PAYLOAD, false, false, NULL, NULL}, + {"intfield", FIELD_TYPE_PAYLOAD, true, true, validate_unsigned_int_field, validate_unsigned_int_field}, + {"longfield", FIELD_TYPE_PAYLOAD, true, true, validate_unsigned_int_field, validate_unsigned_int_field}, + {"signedfield", FIELD_TYPE_PAYLOAD, true, true, validate_signed_int_field, validate_signed_int_field}, + {"arrfield1", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field}, + {"arrfield2", FIELD_TYPE_PAYLOAD, true, true, validate_string_test, validate_string_test}, + {"arrfield3", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field}, + {"seqfield1", FIELD_TYPE_PAYLOAD, true, true, validate_seqfield1, validate_seqfield1}, + {"seqfield2", FIELD_TYPE_PAYLOAD, true, true, validate_string_test, validate_string_test}, + {"seqfield3", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field}, + {"seqfield4", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field}, + {"arrfield1[1]", FIELD_TYPE_ARRAY_FIELD, true, true, validate_array_unsigned_int_field_at_index, validate_array_unsigned_int_field_at_index}, + {"stringfield", FIELD_TYPE_PAYLOAD, true, true, validate_string_test, validate_string_test}, + {"stringfield2", FIELD_TYPE_PAYLOAD, true, true, validate_string_escaped, validate_string_escaped}, + {"floatfield", FIELD_TYPE_PAYLOAD, true, false, validate_floatfield, validate_floatfield}, + {"doublefield", FIELD_TYPE_PAYLOAD, true, false, validate_doublefield, validate_doublefield}, + {"enum0", FIELD_TYPE_PAYLOAD, true, true, validate_enum0, validate_enum0}, + {"enumnegative", FIELD_TYPE_PAYLOAD, true, true, validate_enumnegative, validate_enumnegative}, + {"$ctx.procname", FIELD_TYPE_CONTEXT, true, true, validate_context_procname_ust, validate_context_procname_kernel}, +}; + +static const char *get_notification_trigger_name( + struct lttng_notification *notification) +{ + const char *name = NULL; + enum lttng_evaluation_status status; + const struct lttng_evaluation *evaluation; + evaluation = lttng_notification_get_evaluation(notification); + if (evaluation == NULL) { + fail("lttng_notification_get_evaluation"); + goto end; + } + + switch (lttng_evaluation_get_type(evaluation)) { + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + { + status = lttng_evaluation_event_rule_get_trigger_name( + evaluation, &name); + if (status != LTTNG_EVALUATION_STATUS_OK) { + fail("lttng_evaluation_event_rule_get_trigger_name"); + name = NULL; + goto end; } - mask_position++; + break; + } + default: + fail("Wrong notification evaluation type \n"); + goto end; + } +end: + return name; +} - /* Set threshold ratio */ - if ((1 << mask_position) & i) { - condition_status = lttng_condition_buffer_usage_set_threshold_ratio( - condition, 0.0); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - loop_ret = 1; - goto loop_end; +static int validator_notification_trigger_name( + struct lttng_notification *notification, + const char *trigger_name) +{ + int ret; + bool name_is_equal; + const char *name; + + assert(notification); + assert(trigger_name); + + name = get_notification_trigger_name(notification); + if (name == NULL) { + ret = 1; + goto end; + } + + name_is_equal = (strcmp(trigger_name, name) == 0); + ok(name_is_equal, "Expected trigger name: %s got %s", trigger_name, + name); + + ret = !name_is_equal; + +end: + return ret; +} + +static +void wait_on_file(const char *path, bool file_exist) +{ + if (!path) { + return; + } + for (;;) { + int ret; + struct stat buf; + + ret = stat(path, &buf); + if (ret == -1 && errno == ENOENT) { + if (file_exist) { + /* + * The file does not exist. wait a bit and + * continue looping until it does. + */ + (void) poll(NULL, 0, 10); + continue; } - threshold_ratio_set = true; + + /* + * File does not exist and the exit condition we want. + * Break from the loop and return. + */ + break; } - mask_position++; + if (ret) { + perror("stat"); + exit(EXIT_FAILURE); + } + /* + * stat() returned 0, so the file exists. break now only if + * that's the exit condition we want. + */ + if (file_exist) { + break; + } + } +} + +static +int write_pipe(const char *path, uint8_t data) +{ + int ret = 0; + int fd = 0; + + fd = open(path, O_WRONLY | O_NONBLOCK); + if (fd < 0) { + perror("Could not open consumer control named pipe"); + goto end; + } + + ret = write(fd, &data , sizeof(data)); + if (ret < 1) { + perror("Named pipe write failed"); + if (close(fd)) { + perror("Named pipe close failed"); + } + ret = -1; + goto end; + } + + ret = close(fd); + if (ret < 0) { + perror("Name pipe closing failed"); + ret = -1; + goto end; + } +end: + return ret; +} + +static +int stop_consumer(const char **argv) +{ + int ret = 0, i; + + for (i = named_pipe_args_start; i < nb_args; i++) { + ret = write_pipe(argv[i], 49); + } + return ret; +} + +static +int resume_consumer(const char **argv) +{ + int ret = 0, i; + + for (i = named_pipe_args_start; i < nb_args; i++) { + ret = write_pipe(argv[i], 0); + } + return ret; +} + +static +int suspend_application(void) +{ + int ret; + struct stat buf; + + if (!stat(app_state_file, &buf)) { + fail("App is already in a suspended state."); + ret = -1; + goto error; + } + + /* + * Send SIGUSR1 to application instructing it to bypass tracepoint. + */ + assert(app_pid > 1); + + ret = kill(app_pid, SIGUSR1); + if (ret) { + fail("SIGUSR1 failed. errno %d", errno); + ret = -1; + goto error; + } + + wait_on_file(app_state_file, true); + +error: + return ret; + +} + +static +int resume_application() +{ + int ret; + struct stat buf; + + ret = stat(app_state_file, &buf); + if (ret == -1 && errno == ENOENT) { + fail("State file does not exist"); + goto error; + } + if (ret) { + perror("stat"); + goto error; + } + + assert(app_pid > 1); + + ret = kill(app_pid, SIGUSR1); + if (ret) { + fail("SIGUSR1 failed. errno %d", errno); + ret = -1; + goto error; + } + + wait_on_file(app_state_file, false); + +error: + return ret; + +} + + +static +void test_triggers_buffer_usage_condition(const char *session_name, + const char *channel_name, + enum lttng_domain_type domain_type, + enum lttng_condition_type condition_type) +{ + unsigned int test_vector_size = 5, i; + enum lttng_condition_status condition_status; + struct lttng_action *action; + + /* Set-up */ + action = lttng_action_notify_create(); + if (!action) { + fail("Setup error on action creation"); + goto end; + } + + /* Test lttng_register_trigger with null value */ + ok(lttng_register_trigger(NULL) == -LTTNG_ERR_INVALID, "Registering a NULL trigger fails as expected"); + + /* Test: register a trigger */ + + for (i = 0; i < pow(2,test_vector_size); i++) { + int loop_ret = 0; + char *test_tuple_string = NULL; + unsigned int mask_position = 0; + bool session_name_set = false; + bool channel_name_set = false; + bool threshold_ratio_set = false; + bool threshold_byte_set = false; + bool domain_type_set = false; + + struct lttng_trigger *trigger = NULL; + struct lttng_condition *condition = NULL; + + /* Create base condition */ + switch (condition_type) { + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + condition = lttng_condition_buffer_usage_low_create(); + break; + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + condition = lttng_condition_buffer_usage_high_create(); + break; + default: + loop_ret = 1; + goto loop_end; + } + + if (!condition) { + loop_ret = 1; + goto loop_end; + + } + + /* Prepare the condition for trigger registration test */ + + /* Set session name */ + if ((1 << mask_position) & i) { + condition_status = lttng_condition_buffer_usage_set_session_name( + condition, session_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + loop_ret = 1; + goto loop_end; + } + session_name_set = true; + } + mask_position++; + + /* Set channel name */ + if ((1 << mask_position) & i) { + condition_status = lttng_condition_buffer_usage_set_channel_name( + condition, channel_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + loop_ret = 1; + goto loop_end; + } + channel_name_set = true; + } + mask_position++; + + /* Set threshold ratio */ + if ((1 << mask_position) & i) { + condition_status = lttng_condition_buffer_usage_set_threshold_ratio( + condition, 0.0); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + loop_ret = 1; + goto loop_end; + } + threshold_ratio_set = true; + } + mask_position++; + + /* Set threshold byte */ + if ((1 << mask_position) & i) { + condition_status = lttng_condition_buffer_usage_set_threshold( + condition, 0); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + loop_ret = 1; + goto loop_end; + } + threshold_byte_set = true; + } + mask_position++; + + /* Set domain type */ + if ((1 << mask_position) & i) { + condition_status = lttng_condition_buffer_usage_set_domain_type( + condition, LTTNG_DOMAIN_UST); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + loop_ret = 1; + goto loop_end; + } + domain_type_set = true; + } + + /* Safety check */ + if (mask_position != test_vector_size -1) { + assert("Logic error for test vector generation"); + } + + loop_ret = asprintf(&test_tuple_string, "session name %s, channel name %s, threshold ratio %s, threshold byte %s, domain type %s", + session_name_set ? "set" : "unset", + channel_name_set ? "set" : "unset", + threshold_ratio_set ? "set" : "unset", + threshold_byte_set ? "set" : "unset", + domain_type_set? "set" : "unset"); + if (!test_tuple_string || loop_ret < 0) { + loop_ret = 1; + goto loop_end; + } + + /* Create trigger */ + trigger = lttng_trigger_create(condition, action); + if (!trigger) { + loop_ret = 1; + goto loop_end; + } + + loop_ret = lttng_register_trigger(trigger); + +loop_end: + if (loop_ret == 1) { + fail("Setup error occurred for tuple: %s", test_tuple_string); + goto loop_cleanup; + } + + /* This combination happens three times */ + if (session_name_set && channel_name_set + && (threshold_ratio_set || threshold_byte_set) + && domain_type_set) { + ok(loop_ret == 0, "Trigger is registered: %s", test_tuple_string); + + /* + * Test that a trigger cannot be registered + * multiple time. + */ + loop_ret = lttng_register_trigger(trigger); + ok(loop_ret == -LTTNG_ERR_TRIGGER_EXISTS, "Re-register trigger fails as expected: %s", test_tuple_string); + + /* Test that a trigger can be unregistered */ + loop_ret = lttng_unregister_trigger(trigger); + ok(loop_ret == 0, "Unregister trigger: %s", test_tuple_string); + + /* + * Test that unregistration of a non-previously + * registered trigger fail. + */ + loop_ret = lttng_unregister_trigger(trigger); + ok(loop_ret == -LTTNG_ERR_TRIGGER_NOT_FOUND, "Unregister of a non-registered trigger fails as expected: %s", test_tuple_string); + } else { + ok(loop_ret == -LTTNG_ERR_INVALID_TRIGGER, "Trigger is invalid as expected and cannot be registered: %s", test_tuple_string); + } + +loop_cleanup: + free(test_tuple_string); + lttng_trigger_destroy(trigger); + lttng_condition_destroy(condition); + } + +end: + lttng_action_destroy(action); +} + +static +void wait_data_pending(const char *session_name) +{ + int ret; + + do { + ret = lttng_data_pending(session_name); + assert(ret >= 0); + } while (ret != 0); +} + +static +int setup_buffer_usage_condition(struct lttng_condition *condition, + const char *condition_name, + const char *session_name, + const char *channel_name, + const enum lttng_domain_type domain_type) +{ + enum lttng_condition_status condition_status; + int ret = 0; + + condition_status = lttng_condition_buffer_usage_set_session_name( + condition, session_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Error setting session name on %s creation", condition_name); + ret = -1; + goto end; + } + + condition_status = lttng_condition_buffer_usage_set_channel_name( + condition, channel_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Error setting channel name on %s creation", condition_name); + ret = -1; + goto end; + } + + condition_status = lttng_condition_buffer_usage_set_domain_type( + condition, domain_type); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Error setting domain type on %s creation", condition_name); + ret = -1; + goto end; + } + +end: + return ret; +} + +static +void test_invalid_channel_subscription( + const enum lttng_domain_type domain_type) +{ + enum lttng_condition_status condition_status; + enum lttng_notification_channel_status nc_status; + struct lttng_condition *dummy_condition = NULL; + struct lttng_condition *dummy_invalid_condition = NULL; + struct lttng_notification_channel *notification_channel = NULL; + int ret = 0; + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + if (!notification_channel) { + goto end; + } + + /* + * Create a dummy, empty (thus invalid) condition to test error paths. + */ + dummy_invalid_condition = lttng_condition_buffer_usage_low_create(); + if (!dummy_invalid_condition) { + fail("Setup error on condition creation"); + goto end; + } + + /* + * Test subscription and unsubscription of an invalid condition to/from + * a channel. + */ + nc_status = lttng_notification_channel_subscribe( + notification_channel, dummy_invalid_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, + "Subscribing to an invalid condition"); + + nc_status = lttng_notification_channel_unsubscribe( + notification_channel, dummy_invalid_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, + "Unsubscribing from an invalid condition"); + + /* Create a valid dummy condition with a ratio of 0.5 */ + dummy_condition = lttng_condition_buffer_usage_low_create(); + if (!dummy_condition) { + fail("Setup error on dummy_condition creation"); + goto end; + } + + condition_status = lttng_condition_buffer_usage_set_threshold_ratio( + dummy_condition, 0.5); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on condition creation"); + goto end; + } + + ret = setup_buffer_usage_condition(dummy_condition, "dummy_condition", + "dummy_session", "dummy_channel", domain_type); + if (ret) { + fail("Setup error on dummy condition creation"); + goto end; + } + + /* + * Test subscription and unsubscription to/from a channel with invalid + * parameters. + */ + nc_status = lttng_notification_channel_subscribe(NULL, NULL); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, + "Notification channel subscription is invalid: NULL, NULL"); + + nc_status = lttng_notification_channel_subscribe( + notification_channel, NULL); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, + "Notification channel subscription is invalid: NON-NULL, NULL"); + + nc_status = lttng_notification_channel_subscribe(NULL, dummy_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, + "Notification channel subscription is invalid: NULL, NON-NULL"); + + nc_status = lttng_notification_channel_unsubscribe( + notification_channel, dummy_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_UNKNOWN_CONDITION, + "Unsubscribing from a valid unknown condition"); + +end: + lttng_notification_channel_destroy(notification_channel); + lttng_condition_destroy(dummy_invalid_condition); + lttng_condition_destroy(dummy_condition); + return; +} + +enum buffer_usage_type { + BUFFER_USAGE_TYPE_LOW, + BUFFER_USAGE_TYPE_HIGH, +}; + +static int register_buffer_usage_notify_trigger(const char *session_name, + const char *channel_name, + const enum lttng_domain_type domain_type, + enum buffer_usage_type buffer_usage_type, + double ratio, + struct lttng_condition **condition, + struct lttng_action **action, + struct lttng_trigger **trigger) +{ + enum lttng_condition_status condition_status; + struct lttng_action *tmp_action = NULL; + struct lttng_condition *tmp_condition = NULL; + struct lttng_trigger *tmp_trigger = NULL; + int ret = 0; + + /* Set-up */ + tmp_action = lttng_action_notify_create(); + if (!action) { + fail("Setup error on action creation"); + ret = -1; + goto error; + } + + if (buffer_usage_type == BUFFER_USAGE_TYPE_LOW) { + tmp_condition = lttng_condition_buffer_usage_low_create(); + } else { + tmp_condition = lttng_condition_buffer_usage_high_create(); + } + + if (!tmp_condition) { + fail("Setup error on condition creation"); + ret = -1; + goto error; + } + + /* Set the buffer usage threashold */ + condition_status = lttng_condition_buffer_usage_set_threshold_ratio( + tmp_condition, ratio); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on condition creation"); + ret = -1; + goto error; + } + + ret = setup_buffer_usage_condition(tmp_condition, "condition_name", + session_name, channel_name, domain_type); + if (ret) { + fail("Setup error on condition creation"); + ret = -1; + goto error; + } + + /* Register the triggers for condition */ + tmp_trigger = lttng_trigger_create(tmp_condition, tmp_action); + if (!tmp_trigger) { + fail("Setup error on trigger creation"); + ret = -1; + goto error; + } + + ret = lttng_register_trigger(tmp_trigger); + if (ret) { + fail("Setup error on trigger registration"); + ret = -1; + goto error; + } + + *condition = tmp_condition; + *trigger = tmp_trigger; + *action = tmp_action; + goto end; + +error: + lttng_action_destroy(tmp_action); + lttng_condition_destroy(tmp_condition); + lttng_trigger_destroy(tmp_trigger); + +end: + return ret; +} + +static void test_subscription_twice(const char *session_name, + const char *channel_name, + const enum lttng_domain_type domain_type) +{ + int ret = 0; + enum lttng_notification_channel_status nc_status; + + struct lttng_action *action = NULL; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_trigger *trigger = NULL; + + struct lttng_condition *condition = NULL; + + ret = register_buffer_usage_notify_trigger(session_name, channel_name, + domain_type, BUFFER_USAGE_TYPE_LOW, 0.99, &condition, + &action, &trigger); + if (ret) { + fail("Setup error on trigger registration"); + goto end; + } + + /* Begin testing. */ + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + if (!notification_channel) { + goto end; + } + + /* Subscribe a valid condition. */ + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to condition"); + + /* Subscribing again should fail. */ + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED, + "Subscribe to a condition for which subscription was already done"); + +end: + lttng_unregister_trigger(trigger); + lttng_trigger_destroy(trigger); + lttng_notification_channel_destroy(notification_channel); + lttng_action_destroy(action); + lttng_condition_destroy(condition); +} + +static void test_buffer_usage_notification_channel(const char *session_name, + const char *channel_name, + const enum lttng_domain_type domain_type, + const char **argv) +{ + int ret = 0; + enum lttng_notification_channel_status nc_status; + + struct lttng_action *low_action = NULL; + struct lttng_action *high_action = NULL; + struct lttng_notification *notification = NULL; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_trigger *low_trigger = NULL; + struct lttng_trigger *high_trigger = NULL; + + struct lttng_condition *low_condition = NULL; + struct lttng_condition *high_condition = NULL; + + double low_ratio = 0.0; + /* This is not 99 since we can end up in scenario where an event is + * bigger than 1% of the buffer and hence the buffer ratio will never + * trigger since the event will always be discarder by the tracer. + */ + + double high_ratio = 0.90; + + ret = register_buffer_usage_notify_trigger(session_name, channel_name, + domain_type, BUFFER_USAGE_TYPE_LOW, low_ratio, + &low_condition, &low_action, &low_trigger); + if (ret) { + fail("Setup error on low trigger registration"); + goto end; + } + + ret = register_buffer_usage_notify_trigger(session_name, channel_name, + domain_type, BUFFER_USAGE_TYPE_HIGH, high_ratio, + &high_condition, &high_action, &high_trigger); + if (ret) { + fail("Setup error on high trigger registration"); + goto end; + } + + /* Begin testing */ + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + if (!notification_channel) { + goto end; + } + + /* Subscribe a valid low condition */ + nc_status = lttng_notification_channel_subscribe( + notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to low condition"); + + /* Subscribe a valid high condition */ + nc_status = lttng_notification_channel_subscribe( + notification_channel, high_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to high condition"); + + resume_application(); + + /* Wait for notification to happen */ + stop_consumer(argv); + lttng_start_tracing(session_name); + + /* Wait for high notification */ + do { + nc_status = lttng_notification_channel_get_next_notification( + notification_channel, ¬ification); + } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && + lttng_condition_get_type(lttng_notification_get_condition( + notification)) == + LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, + "High notification received after intermediary communication"); + lttng_notification_destroy(notification); + notification = NULL; + + suspend_application(); + lttng_stop_tracing_no_wait(session_name); + resume_consumer(argv); + wait_data_pending(session_name); + + /* + * Test that communication still work even if there is notification + * waiting for consumption. + */ + + nc_status = lttng_notification_channel_unsubscribe( + notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Unsubscribe with pending notification"); + + nc_status = lttng_notification_channel_subscribe( + notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe with pending notification"); + + do { + nc_status = lttng_notification_channel_get_next_notification( + notification_channel, ¬ification); + } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && + lttng_condition_get_type(lttng_notification_get_condition( + notification)) == + LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, + "Low notification received after intermediary communication"); + lttng_notification_destroy(notification); + notification = NULL; + + /* Stop consumer to force a high notification */ + stop_consumer(argv); + resume_application(); + lttng_start_tracing(session_name); + + do { + nc_status = lttng_notification_channel_get_next_notification( + notification_channel, ¬ification); + } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && + lttng_condition_get_type(lttng_notification_get_condition( + notification)) == + LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, + "High notification received after intermediary communication"); + lttng_notification_destroy(notification); + notification = NULL; + + suspend_application(); + lttng_stop_tracing_no_wait(session_name); + resume_consumer(argv); + wait_data_pending(session_name); + + do { + nc_status = lttng_notification_channel_get_next_notification( + notification_channel, ¬ification); + } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && + lttng_condition_get_type(lttng_notification_get_condition( + notification)) == + LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, + "Low notification received after re-subscription"); + lttng_notification_destroy(notification); + notification = NULL; + + stop_consumer(argv); + resume_application(); + /* Stop consumer to force a high notification */ + lttng_start_tracing(session_name); + + do { + nc_status = lttng_notification_channel_get_next_notification( + notification_channel, ¬ification); + } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && + lttng_condition_get_type(lttng_notification_get_condition( + notification)) == + LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, + "High notification"); + lttng_notification_destroy(notification); + notification = NULL; + + suspend_application(); + + /* Resume consumer to allow event consumption */ + lttng_stop_tracing_no_wait(session_name); + resume_consumer(argv); + wait_data_pending(session_name); + + nc_status = lttng_notification_channel_unsubscribe( + notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Unsubscribe low condition with pending notification"); + + nc_status = lttng_notification_channel_unsubscribe( + notification_channel, high_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Unsubscribe high condition with pending notification"); + +end: + lttng_notification_channel_destroy(notification_channel); + lttng_trigger_destroy(low_trigger); + lttng_trigger_destroy(high_trigger); + lttng_action_destroy(low_action); + lttng_action_destroy(high_action); + lttng_condition_destroy(low_condition); + lttng_condition_destroy(high_condition); +} + +static void create_tracepoint_event_rule_trigger(const char *event_pattern, + const char *trigger_name, + const char *filter, + unsigned int exclusion_count, + const char **exclusions, + enum lttng_domain_type domain_type, + condition_capture_desc_cb capture_desc_cb, + struct lttng_condition **condition, + struct lttng_action **action, + struct lttng_trigger **trigger) +{ + enum lttng_event_rule_status event_rule_status; + enum lttng_trigger_status trigger_status; + + struct lttng_action *tmp_action = NULL; + struct lttng_event_rule *event_rule = NULL; + struct lttng_condition *tmp_condition = NULL; + struct lttng_trigger *tmp_trigger = NULL; + int ret; + + assert(event_pattern); + assert(trigger_name); + assert(condition); + assert(trigger); + + event_rule = lttng_event_rule_tracepoint_create(domain_type); + ok(event_rule, "Tracepoint event rule object creation"); + + event_rule_status = lttng_event_rule_tracepoint_set_pattern( + event_rule, event_pattern); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting tracepoint event rule pattern: %s", + event_pattern); + + if (filter) { + event_rule_status = lttng_event_rule_tracepoint_set_filter( + event_rule, filter); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting tracepoint event rule filter: %s", + filter); + } + + if (exclusions) { + int i; + bool success = true; + assert(domain_type == LTTNG_DOMAIN_UST); + assert(exclusion_count > 0); + + for (i = 0; i < exclusion_count; i++) { + event_rule_status = + lttng_event_rule_tracepoint_add_exclusion( + event_rule, + exclusions[i]); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + fail("Setting tracepoint event rule exclusion \"%s\".", + exclusions[i]); + success = false; + } + } + ok(success, "Setting tracepoint event rule exclusions"); + } + + tmp_condition = lttng_condition_event_rule_create(event_rule); + ok(tmp_condition, "Condition event rule object creation"); + + if (capture_desc_cb) { + ret = capture_desc_cb(tmp_condition); + if (ret) { + assert("Generating the condition capture descriptor"); + } + } + + tmp_action = lttng_action_notify_create(); + ok(tmp_action, "Action event rule object creation"); + + tmp_trigger = lttng_trigger_create(tmp_condition, tmp_action); + ok(tmp_trigger, "Trigger object creation %s", trigger_name); + + trigger_status = lttng_trigger_set_name(tmp_trigger, trigger_name); + ok(trigger_status == LTTNG_TRIGGER_STATUS_OK, + "Setting name to trigger %s", trigger_name); + + ret = lttng_register_trigger(tmp_trigger); + ok(ret == 0, "Trigger registration %s", trigger_name); + + lttng_event_rule_destroy(event_rule); + + *condition = tmp_condition; + *action = tmp_action; + *trigger = tmp_trigger; + + return; +} + +static struct lttng_notification *get_next_notification( + struct lttng_notification_channel *notification_channel) +{ + struct lttng_notification *local_notification = NULL; + enum lttng_notification_channel_status status; + + /* Receive the next notification. */ + status = lttng_notification_channel_get_next_notification( + notification_channel, &local_notification); + + switch (status) { + case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK: + break; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED: + fail("Notifications have been dropped"); + local_notification = NULL; + break; + default: + /* Unhandled conditions / errors. */ + fail("error: Unknown notification channel status\n"); + local_notification = NULL; + break; + } + + return local_notification; +} + +static void test_tracepoint_event_rule_notification( + enum lttng_domain_type domain_type) +{ + int i; + int ret; + enum lttng_notification_channel_status nc_status; + + struct lttng_action *action = NULL; + struct lttng_condition *condition = NULL; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_trigger *trigger = NULL; + const char *trigger_name = "my_precious"; + const char *pattern; - /* Set threshold byte */ - if ((1 << mask_position) & i) { - condition_status = lttng_condition_buffer_usage_set_threshold( - condition, 0); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - loop_ret = 1; - goto loop_end; - } - threshold_byte_set = true; - } - mask_position++; + if (domain_type == LTTNG_DOMAIN_UST) { + pattern = "tp:tptest"; + } else { + pattern = "lttng_test_filter_event"; + } - /* Set domain type */ - if ((1 << mask_position) & i) { - condition_status = lttng_condition_buffer_usage_set_domain_type( - condition, LTTNG_DOMAIN_UST); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - loop_ret = 1; - goto loop_end; - } - domain_type_set = true; - } + create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 0, + NULL, domain_type, NULL, &condition, &action, &trigger); - /* Safety check */ - if (mask_position != test_vector_size -1) { - assert("Logic error for test vector generation"); - } + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); - loop_ret = asprintf(&test_tuple_string, "session name %s, channel name %s, threshold ratio %s, threshold byte %s, domain type %s", - session_name_set ? "set" : "unset", - channel_name_set ? "set" : "unset", - threshold_ratio_set ? "set" : "unset", - threshold_byte_set ? "set" : "unset", - domain_type_set? "set" : "unset"); - if (!test_tuple_string || loop_ret < 0) { - loop_ret = 1; - goto loop_end; + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + resume_application(); + + /* Get 3 notifications */ + for (i = 0; i < 3; i++) { + struct lttng_notification *notification = get_next_notification( + notification_channel); + ok(notification, "Received notification"); + + /* Error */ + if (notification == NULL) { + goto end; } - /* Create trigger */ - trigger = lttng_trigger_create(condition, action); - if (!trigger) { - loop_ret = 1; - goto loop_end; + ret = validator_notification_trigger_name(notification, trigger_name); + lttng_notification_destroy(notification); + if (ret) { + goto end; } + } - loop_ret = lttng_register_trigger(trigger); +end: + suspend_application(); + lttng_notification_channel_destroy(notification_channel); + lttng_unregister_trigger(trigger); + lttng_trigger_destroy(trigger); + lttng_action_destroy(action); + lttng_condition_destroy(condition); + return; +} -loop_end: - if (loop_ret == 1) { - fail("Setup error occurred for tuple: %s", test_tuple_string); - goto loop_cleanup; - } +static void test_tracepoint_event_rule_notification_filter( + enum lttng_domain_type domain_type) +{ + int i; + enum lttng_notification_channel_status nc_status; - /* This combination happens three times */ - if (session_name_set && channel_name_set - && (threshold_ratio_set || threshold_byte_set) - && domain_type_set) { - ok(loop_ret == 0, "Trigger is registered: %s", test_tuple_string); + struct lttng_condition *ctrl_condition = NULL, *condition = NULL; + struct lttng_action *ctrl_action = NULL, *action = NULL; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_trigger *ctrl_trigger = NULL, *trigger = NULL; + const char *ctrl_trigger_name = "control_trigger"; + const char *trigger_name = "trigger"; + const char *pattern; + int ctrl_count = 0, count = 0; + + if (domain_type == LTTNG_DOMAIN_UST) { + pattern = "tp:tptest"; + } else { + pattern = "lttng_test_filter_event"; + } - /* - * Test that a trigger cannot be registered - * multiple time. - */ - loop_ret = lttng_register_trigger(trigger); - ok(loop_ret == -LTTNG_ERR_TRIGGER_EXISTS, "Re-register trigger fails as expected: %s", test_tuple_string); + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); - /* Test that a trigger can be unregistered */ - loop_ret = lttng_unregister_trigger(trigger); - ok(loop_ret == 0, "Unregister trigger: %s", test_tuple_string); + create_tracepoint_event_rule_trigger(pattern, ctrl_trigger_name, NULL, + 0, NULL, domain_type, NULL, &ctrl_condition, + &ctrl_action, &ctrl_trigger); - /* - * Test that unregistration of a non-previously - * registered trigger fail. - */ - loop_ret = lttng_unregister_trigger(trigger); - ok(loop_ret == -LTTNG_ERR_TRIGGER_NOT_FOUND, "Unregister of a non-registered trigger fails as expected: %s", test_tuple_string); - } else { - ok(loop_ret == -LTTNG_ERR_INVALID_TRIGGER, "Trigger is invalid as expected and cannot be registered: %s", test_tuple_string); + nc_status = lttng_notification_channel_subscribe( + notification_channel, ctrl_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + /* + * Attach a filter expression to get notification only if the + * `intfield` is even. + */ + create_tracepoint_event_rule_trigger(pattern, trigger_name, + "(intfield & 1) == 0", 0, NULL, domain_type, NULL, + &condition, &action, &trigger); + + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + /* + * We registered 2 notifications triggers, one with a filter and one + * without (control). The one with a filter will only fired when the + * `intfield` is a multiple of 2. We should get two times as many + * control notifications as filter notifications. + */ + resume_application(); + + /* + * Get 3 notifications. We should get 1 for the regular trigger (with + * the filter) and 2 from the control trigger. This works whatever + * the order we receive the notifications. + */ + for (i = 0; i < 3; i++) { + const char *name; + struct lttng_notification *notification = get_next_notification( + notification_channel); + ok(notification, "Received notification"); + + /* Error */ + if (notification == NULL) { + goto end; } -loop_cleanup: - free(test_tuple_string); - lttng_trigger_destroy(trigger); - lttng_condition_destroy(condition); + name = get_notification_trigger_name(notification); + if (name == NULL) { + lttng_notification_destroy(notification); + goto end; + } + + if (strcmp(ctrl_trigger_name, name) == 0) { + ctrl_count++; + } else if (strcmp(trigger_name, name) == 0) { + count++; + } + lttng_notification_destroy(notification); } + ok(ctrl_count / 2 == count, + "Get twice as many control notif as of regular notif"); end: + suspend_application(); + lttng_unregister_trigger(trigger); + lttng_unregister_trigger(ctrl_trigger); + lttng_notification_channel_destroy(notification_channel); + lttng_trigger_destroy(trigger); + lttng_trigger_destroy(ctrl_trigger); + lttng_condition_destroy(condition); + lttng_condition_destroy(ctrl_condition); lttng_action_destroy(action); + lttng_action_destroy(ctrl_action); + return; } -static -void wait_data_pending(const char *session_name) +static void test_tracepoint_event_rule_notification_exclusion( + enum lttng_domain_type domain_type) { - int ret; + enum lttng_notification_channel_status nc_status; + struct lttng_condition *ctrl_condition = NULL, *condition = NULL; + struct lttng_action *ctrl_action = NULL, *action = NULL; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_trigger *ctrl_trigger = NULL, *trigger = NULL; + const char *ctrl_trigger_name = "control_exclusion_trigger"; + const char *trigger_name = "exclusion_trigger"; + const char *pattern = "tp:tptest*"; + const char *exclusions[4] = { + "tp:tptest2", "tp:tptest3", "tp:tptest4", "tp:tptest5"}; + int ctrl_count = 0, count = 0; + int i; + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); - do { - ret = lttng_data_pending(session_name); - assert(ret >= 0); - } while (ret != 0); + create_tracepoint_event_rule_trigger(pattern, ctrl_trigger_name, NULL, + 0, NULL, domain_type, NULL, &ctrl_condition, + &ctrl_action, &ctrl_trigger); + + nc_status = lttng_notification_channel_subscribe( + notification_channel, ctrl_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 4, + exclusions, domain_type, NULL, &condition, &action, + &trigger); + + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + /* + * We registered 2 notifications triggers, one with an exclusion and + * one without (control). + * - The trigger with an exclusion will fire once every iteration. + * - The trigger without an exclusion will fire 5 times every + * iteration. + * + * We should get 5 times as many notifications from the control + * trigger. + */ + resume_application(); + + /* + * Get 6 notifications. We should get 1 for the regular trigger (with + * the exclusion) and 5 from the control trigger. This works whatever + * the order we receive the notifications. + */ + for (i = 0; i < 6; i++) { + const char *name; + struct lttng_notification *notification = get_next_notification( + notification_channel); + ok(notification, "Received notification"); + + /* Error */ + if (notification == NULL) { + goto end; + } + + name = get_notification_trigger_name(notification); + if (name == NULL) { + lttng_notification_destroy(notification); + goto end; + } + + if (strcmp(ctrl_trigger_name, name) == 0) { + ctrl_count++; + } else if (strcmp(trigger_name, name) == 0) { + count++; + } + lttng_notification_destroy(notification); + } + ok(ctrl_count / 5 == count, + "Got 5 times as many control notif as of regular notif"); + +end: + suspend_application(); + lttng_unregister_trigger(trigger); + lttng_unregister_trigger(ctrl_trigger); + lttng_notification_channel_destroy(notification_channel); + lttng_trigger_destroy(trigger); + lttng_trigger_destroy(ctrl_trigger); + lttng_condition_destroy(condition); + lttng_condition_destroy(ctrl_condition); + lttng_action_destroy(action); + lttng_action_destroy(ctrl_action); + return; } -static -void test_notification_channel(const char *session_name, const char *channel_name, const enum lttng_domain_type domain_type, const char **argv) +static void test_kprobe_event_rule_notification( + enum lttng_domain_type domain_type) { - int ret = 0; - enum lttng_condition_status condition_status; enum lttng_notification_channel_status nc_status; + enum lttng_event_rule_status event_rule_status; + enum lttng_trigger_status trigger_status; - struct lttng_action *action = NULL; - struct lttng_notification *notification = NULL; struct lttng_notification_channel *notification_channel = NULL; + struct lttng_condition *condition = NULL; + struct lttng_kernel_probe_location *location = NULL; + struct lttng_event_rule *event_rule = NULL; + struct lttng_action *action = NULL; struct lttng_trigger *trigger = NULL; + const char *trigger_name = "kprobe_trigger"; + const char *symbol_name = "_do_fork"; + int i, ret; - struct lttng_condition *low_condition = NULL; - struct lttng_condition *high_condition = NULL; - struct lttng_condition *dummy_invalid_condition = NULL; - struct lttng_condition *dummy_condition = NULL; - - double low_ratio = 0.0; - double high_ratio = 0.99; - - /* Set-up */ action = lttng_action_notify_create(); if (!action) { fail("Setup error on action creation"); goto end; } - /* Create a dummy, empty condition for later test */ - dummy_invalid_condition = lttng_condition_buffer_usage_low_create(); - if (!dummy_invalid_condition) { - fail("Setup error on condition creation"); + location = lttng_kernel_probe_location_symbol_create(symbol_name, 0); + if (!location) { + fail("Could not create kernel probe location."); goto end; } - /* Create a valid dummy condition with a ratio of 0.5 */ - dummy_condition = lttng_condition_buffer_usage_low_create(); - if (!dummy_condition) { - fail("Setup error on dummy_condition creation"); - goto end; + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); - } - condition_status = lttng_condition_buffer_usage_set_threshold_ratio( - dummy_condition, 0.5); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on condition creation"); + event_rule = lttng_event_rule_kprobe_create(); + ok(event_rule, "kprobe event rule object creation"); + + event_rule_status = lttng_event_rule_kprobe_set_location( + event_rule, location); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting kprobe event rule location: %s", symbol_name); + + event_rule_status = lttng_event_rule_kprobe_set_name( + event_rule, trigger_name); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting kprobe event rule name: %s", trigger_name); + + condition = lttng_condition_event_rule_create(event_rule); + ok(condition, "Condition event rule object creation"); + + /* Register the triggers for condition */ + trigger = lttng_trigger_create(condition, action); + if (!trigger) { + fail("Setup error on trigger creation"); goto end; } - condition_status = lttng_condition_buffer_usage_set_session_name( - dummy_condition, session_name); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on dummy_condition creation"); + trigger_status = lttng_trigger_set_name(trigger, trigger_name); + ok(trigger_status == LTTNG_TRIGGER_STATUS_OK, + "Setting name to trigger %s", trigger_name); + + ret = lttng_register_trigger(trigger); + if (ret) { + fail("Setup error on trigger registration"); goto end; } - condition_status = lttng_condition_buffer_usage_set_channel_name( - dummy_condition, channel_name); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on dummy_condition creation"); + + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + resume_application(); + + for (i = 0; i < 3; i++) { + struct lttng_notification *notification = get_next_notification( + notification_channel); + ok(notification, "Received notification"); + + /* Error */ + if (notification == NULL) { + goto end; + } + + ret = validator_notification_trigger_name(notification, trigger_name); + lttng_notification_destroy(notification); + if (ret) { + goto end; + } + } + +end: + suspend_application(); + lttng_notification_channel_destroy(notification_channel); + lttng_unregister_trigger(trigger); + lttng_trigger_destroy(trigger); + lttng_action_destroy(action); + lttng_event_rule_destroy(event_rule); + lttng_condition_destroy(condition); + lttng_kernel_probe_location_destroy(location); + return; +} + +static void test_uprobe_event_rule_notification( + enum lttng_domain_type domain_type, + const char *testapp_path, + const char *test_symbol_name) +{ + enum lttng_notification_channel_status nc_status; + enum lttng_event_rule_status event_rule_status; + enum lttng_trigger_status trigger_status; + + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_userspace_probe_location *probe_location = NULL; + struct lttng_userspace_probe_location_lookup_method *lookup_method = + NULL; + struct lttng_condition *condition = NULL; + struct lttng_event_rule *event_rule = NULL; + struct lttng_action *action = NULL; + struct lttng_trigger *trigger = NULL; + const char *trigger_name = "uprobe_trigger"; + int i, ret; + + action = lttng_action_notify_create(); + if (!action) { + fail("Setup error on action creation"); goto end; } - condition_status = lttng_condition_buffer_usage_set_domain_type( - dummy_condition, domain_type); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on dummy_condition creation"); + + lookup_method = lttng_userspace_probe_location_lookup_method_function_elf_create(); + if (!lookup_method) { + fail("Setup error on userspace probe lookup method creation"); goto end; } - /* Register a low condition with a ratio */ - low_condition = lttng_condition_buffer_usage_low_create(); - if (!low_condition) { - fail("Setup error on low_condition creation"); + probe_location = lttng_userspace_probe_location_function_create( + testapp_path, test_symbol_name, lookup_method); + if (!probe_location) { + fail("Setup error on userspace probe location creation"); goto end; } - condition_status = lttng_condition_buffer_usage_set_threshold_ratio( - low_condition, low_ratio); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on low_condition creation"); + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + + event_rule = lttng_event_rule_uprobe_create(); + ok(event_rule, "kprobe event rule object creation"); + + event_rule_status = lttng_event_rule_uprobe_set_location( + event_rule, probe_location); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting uprobe event rule location"); + + event_rule_status = lttng_event_rule_uprobe_set_name( + event_rule, trigger_name); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting uprobe event rule name: %s", trigger_name); + + condition = lttng_condition_event_rule_create(event_rule); + ok(condition, "Condition event rule object creation"); + + /* Register the triggers for condition */ + trigger = lttng_trigger_create(condition, action); + if (!trigger) { + fail("Setup error on trigger creation"); goto end; } - condition_status = lttng_condition_buffer_usage_set_session_name( - low_condition, session_name); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on low_condition creation"); + trigger_status = lttng_trigger_set_name(trigger, trigger_name); + ok(trigger_status == LTTNG_TRIGGER_STATUS_OK, + "Setting name to trigger %s", trigger_name); + + ret = lttng_register_trigger(trigger); + if (ret) { + fail("Setup error on trigger registration"); goto end; } - condition_status = lttng_condition_buffer_usage_set_channel_name( - low_condition, channel_name); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on low_condition creation"); + + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + resume_application(); + + for (i = 0; i < 3; i++) { + struct lttng_notification *notification = get_next_notification( + notification_channel); + ok(notification, "Received notification"); + + /* Error */ + if (notification == NULL) { + goto end; + } + + ret = validator_notification_trigger_name(notification, trigger_name); + lttng_notification_destroy(notification); + if (ret) { + goto end; + } + } +end: + suspend_application(); + lttng_notification_channel_destroy(notification_channel); + lttng_unregister_trigger(trigger); + lttng_trigger_destroy(trigger); + lttng_action_destroy(action); + lttng_event_rule_destroy(event_rule); + lttng_condition_destroy(condition); + return; +} + +static void test_syscall_event_rule_notification( + enum lttng_domain_type domain_type) +{ + enum lttng_notification_channel_status nc_status; + enum lttng_event_rule_status event_rule_status; + enum lttng_trigger_status trigger_status; + + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_condition *condition = NULL; + struct lttng_event_rule *event_rule = NULL; + struct lttng_action *action = NULL; + struct lttng_trigger *trigger = NULL; + const char *trigger_name = "syscall_trigger"; + const char *syscall_name = "openat"; + int i, ret; + + action = lttng_action_notify_create(); + if (!action) { + fail("Setup error on action creation"); goto end; } - condition_status = lttng_condition_buffer_usage_set_domain_type( - low_condition, domain_type); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on low_condition creation"); - goto end; - } + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); - /* Register a high condition with a ratio */ - high_condition = lttng_condition_buffer_usage_high_create(); - if (!high_condition) { - fail("Setup error on high_condition creation"); - goto end; - } + event_rule = lttng_event_rule_syscall_create(); + ok(event_rule, "syscall event rule object creation"); - condition_status = lttng_condition_buffer_usage_set_threshold_ratio( - high_condition, high_ratio); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on high_condition creation"); + event_rule_status = lttng_event_rule_syscall_set_pattern( + event_rule, syscall_name); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting syscall event rule pattern: %s", syscall_name); + + condition = lttng_condition_event_rule_create(event_rule); + ok(condition, "Condition event rule object creation"); + + /* Register the triggers for condition */ + trigger = lttng_trigger_create(condition, action); + if (!trigger) { + fail("Setup error on trigger creation"); goto end; } - condition_status = lttng_condition_buffer_usage_set_session_name( - high_condition, session_name); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on high_condition creation"); + trigger_status = lttng_trigger_set_name(trigger, trigger_name); + ok(trigger_status == LTTNG_TRIGGER_STATUS_OK, + "Setting name to trigger %s", trigger_name); + + ret = lttng_register_trigger(trigger); + if (ret) { + fail("Setup error on trigger registration"); goto end; } - condition_status = lttng_condition_buffer_usage_set_channel_name( - high_condition, channel_name); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on high_condition creation"); - goto end; + + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + resume_application(); + + for (i = 0; i < 3; i++) { + struct lttng_notification *notification = get_next_notification( + notification_channel); + ok(notification, "Received notification"); + + /* Error */ + if (notification == NULL) { + goto end; + } + + ret = validator_notification_trigger_name(notification, trigger_name); + lttng_notification_destroy(notification); + if (ret) { + goto end; + } } - condition_status = lttng_condition_buffer_usage_set_domain_type( - high_condition, domain_type); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on high_condition creation"); +end: + suspend_application(); + lttng_notification_channel_destroy(notification_channel); + lttng_unregister_trigger(trigger); + lttng_trigger_destroy(trigger); + lttng_action_destroy(action); + lttng_condition_destroy(condition); + return; +} + +static void test_syscall_event_rule_notification_filter( + enum lttng_domain_type domain_type) +{ + enum lttng_notification_channel_status nc_status; + enum lttng_event_rule_status event_rule_status; + enum lttng_trigger_status trigger_status; + + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_condition *condition = NULL; + struct lttng_event_rule *event_rule = NULL; + struct lttng_action *action = NULL; + struct lttng_trigger *trigger = NULL; + const char *trigger_name = "syscall_trigger"; + const char *syscall_name = "openat"; + int i, ret; + + action = lttng_action_notify_create(); + if (!action) { + fail("Setup error on action creation"); goto end; } - /* Register the triggers for low and high condition */ - trigger = lttng_trigger_create(low_condition, action); + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + + event_rule = lttng_event_rule_syscall_create(); + ok(event_rule, "syscall event rule object creation"); + + event_rule_status = lttng_event_rule_syscall_set_pattern( + event_rule, syscall_name); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting syscall event rule pattern: %s", syscall_name); + + event_rule_status = lttng_event_rule_syscall_set_filter( + event_rule, "filename==\"/proc/cpuinfo\""); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting syscall event rule pattern: %s", syscall_name); + + condition = lttng_condition_event_rule_create(event_rule); + ok(condition, "Condition event rule object creation"); + + /* Register the triggers for condition */ + trigger = lttng_trigger_create(condition, action); if (!trigger) { - fail("Setup error on low trigger creation"); + fail("Setup error on trigger creation"); goto end; } + trigger_status = lttng_trigger_set_name(trigger, trigger_name); + ok(trigger_status == LTTNG_TRIGGER_STATUS_OK, + "Setting name to trigger %s", trigger_name); + ret = lttng_register_trigger(trigger); if (ret) { - fail("Setup error on low trigger registration"); + fail("Setup error on trigger registration"); goto end; } + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + resume_application(); + + for (i = 0; i < 3; i++) { + struct lttng_notification *notification = get_next_notification( + notification_channel); + ok(notification, "Received notification"); + + /* Error */ + if (notification == NULL) { + goto end; + } + + ret = validator_notification_trigger_name(notification, trigger_name); + lttng_notification_destroy(notification); + if (ret) { + goto end; + } + } + +end: + suspend_application(); + lttng_unregister_trigger(trigger); + lttng_notification_channel_destroy(notification_channel); lttng_trigger_destroy(trigger); - trigger = NULL; + lttng_event_rule_destroy(event_rule); + lttng_condition_destroy(condition); + return; +} - trigger = lttng_trigger_create(high_condition, action); - if (!trigger) { - fail("Setup error on high trigger creation"); - goto end; +static int generate_capture_descr(struct lttng_condition *condition) +{ + int ret; + struct lttng_event_expr *expr = NULL; + unsigned int basic_field_size; + enum lttng_condition_status cond_status; + + basic_field_size = sizeof(test_capture_base_fields) / sizeof(*test_capture_base_fields); + for (int i = 0; i < basic_field_size; i++) { + + diag("Adding capture descriptor \"%s\"", test_capture_base_fields[i].field_name); + + switch (test_capture_base_fields[i].field_type) { + case FIELD_TYPE_PAYLOAD: + expr = lttng_event_expr_event_payload_field_create( + test_capture_base_fields[i].field_name); + break; + case FIELD_TYPE_CONTEXT: + expr = lttng_event_expr_channel_context_field_create( + test_capture_base_fields[i].field_name); + break; + case FIELD_TYPE_ARRAY_FIELD: + { + int nb_matches; + unsigned int index; + char field_name[256]; + struct lttng_event_expr *array_expr = NULL; + nb_matches = sscanf(test_capture_base_fields[i].field_name, + "%[^[][%u]", field_name, &index); + if (nb_matches != 2) { + ret = 1; + goto end; + } + array_expr = lttng_event_expr_event_payload_field_create( + field_name); + + expr = lttng_event_expr_array_field_element_create( + array_expr, index); + break; + } + case FIELD_TYPE_APP_CONTEXT: + fail("Application context not tested yet."); + default: + ret = 1; + goto end; + } + if (expr == NULL) { + fail("Creating capture expression"); + ret = -1; + goto end; + } + cond_status = lttng_condition_event_rule_append_capture_descriptor( + condition, expr); + if (cond_status != LTTNG_CONDITION_STATUS_OK) { + fail("Appending capture_descriptor"); + ret = -1; + lttng_event_expr_destroy(expr); + goto end; + } } - ret = lttng_register_trigger(trigger); - if (ret) { - fail("Setup error on high trigger registration"); + ret = 0; + +end: + return ret; +} + +static int validator_notification_trigger_capture( + enum lttng_domain_type domain, + struct lttng_notification *notification, + const int iteration) +{ + int ret; + unsigned int capture_count, i; + enum lttng_evaluation_status evaluation_status; + enum lttng_event_field_value_status event_field_value_status; + const struct lttng_evaluation *evaluation; + const struct lttng_event_field_value *captured_fields; + bool at_least_one_error = false; + + evaluation = lttng_notification_get_evaluation(notification); + if (evaluation == NULL) { + fail("lttng_notification_get_evaluation"); + ret = 1; goto end; } - /* Begin testing */ - notification_channel = lttng_notification_channel_create(lttng_session_daemon_notification_endpoint); - ok(notification_channel, "Notification channel object creation"); - if (!notification_channel) { + /* TODO: it seems weird that lttng_evaluation_get_captured_values return + * INVALID if no capture were present. might be better to return + * something with more meaning. Another question is how we link the + * notion of capture and the descriptor from the perspective of a + * client. Is it really invalid to ask for captured value when there might + * not be any? + */ + evaluation_status = lttng_evaluation_get_captured_values(evaluation, &captured_fields); + if (evaluation_status != LTTNG_EVALUATION_STATUS_OK) { + diag("lttng_evaluation_get_captured_values"); + ret = 1; goto end; } - /* Basic error path check */ - nc_status = lttng_notification_channel_subscribe(NULL, NULL); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Notification channel subscription is invalid: NULL, NULL"); - - nc_status = lttng_notification_channel_subscribe(notification_channel, NULL); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Notification channel subscription is invalid: NON-NULL, NULL"); + event_field_value_status = + lttng_event_field_value_array_get_length(captured_fields, + &capture_count); + if (event_field_value_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + assert(0 && "get count of captured field"); + } - nc_status = lttng_notification_channel_subscribe(NULL, low_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Notification channel subscription is invalid: NULL, NON-NULL"); + for (i = 0; i < capture_count; i++) { + const struct lttng_event_field_value *captured_field = NULL; + validate_cb validate; + bool expected; - nc_status = lttng_notification_channel_subscribe(notification_channel, dummy_invalid_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Subscribing to an invalid condition"); + diag("Validating capture \"%s\"", test_capture_base_fields[i].field_name); + event_field_value_status = lttng_event_field_value_array_get_element_at_index(captured_fields, i, &captured_field); - nc_status = lttng_notification_channel_unsubscribe(notification_channel, dummy_invalid_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Unsubscribing from an invalid condition"); + switch(domain) { + case LTTNG_DOMAIN_UST: + expected = test_capture_base_fields[i].expected_ust; + break; + case LTTNG_DOMAIN_KERNEL: + expected = test_capture_base_fields[i].expected_kernel; + break; + default: + assert(0 && "Domain invalid for this test"); + } - nc_status = lttng_notification_channel_unsubscribe(notification_channel, dummy_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_UNKNOWN_CONDITION, "Unsubscribing from a valid unknown condition"); + if (!expected) { + ok(event_field_value_status == LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE, "No payload captured"); + continue; + } - /* Subscribe a valid low condition */ - nc_status = lttng_notification_channel_subscribe(notification_channel, low_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Subscribe to condition"); + if (domain == LTTNG_DOMAIN_UST) { + validate = test_capture_base_fields[i].validate_ust; + } else { + validate = test_capture_base_fields[i].validate_kernel; + } - /* Subscribe a valid high condition */ - nc_status = lttng_notification_channel_subscribe(notification_channel, high_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Subscribe to condition"); + if (event_field_value_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + const char *reason; + if (event_field_value_status == + LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE) { + reason = "Expected a capture but it is unavailable"; + } else { + reason = "lttng_event_field_value_array_get_element_at_index"; + } + fail(reason); + ret = 1; + goto end; + } + diag("Captured field of type %s", + field_value_type_to_str( + lttng_event_field_value_get_type(captured_field))); - nc_status = lttng_notification_channel_subscribe(notification_channel, low_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED, "Subscribe to a condition for which subscription was already done"); + assert(validate); + ret = validate(captured_field, iteration); + if (ret) { + at_least_one_error = true; + } + } - nc_status = lttng_notification_channel_subscribe(notification_channel, high_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED, "Subscribe to a condition for which subscription was already done"); + ret = at_least_one_error; - /* Wait for notification to happen */ - stop_consumer(argv); - lttng_start_tracing(session_name); +end: + return ret; +} - /* Wait for high notification */ - do { - nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); - } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK - && notification - && lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, - "High notification received after intermediary communication"); - lttng_notification_destroy(notification); - notification = NULL; +static void test_tracepoint_event_rule_notification_capture( + enum lttng_domain_type domain_type) +{ + enum lttng_notification_channel_status nc_status; - suspend_application(); - lttng_stop_tracing_no_wait(session_name); - resume_consumer(argv); - wait_data_pending(session_name); + int i, ret; + struct lttng_action *action = NULL; + struct lttng_condition *condition = NULL; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_trigger *trigger = NULL; + const char *trigger_name = "my_precious"; + const char *pattern; - /* - * Test that communication still work even if there is notification - * waiting for consumption. - */ + if (domain_type == LTTNG_DOMAIN_UST) { + pattern = "tp:tptest"; + } else { + pattern = "lttng_test_filter_event"; + } - nc_status = lttng_notification_channel_unsubscribe(notification_channel, low_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Unsubscribe with pending notification"); + create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 0, + NULL, domain_type, generate_capture_descr, &condition, + &action, &trigger); - nc_status = lttng_notification_channel_subscribe(notification_channel, low_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "subscribe with pending notification"); + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); - do { - nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); - } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK - && notification - && lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, - "Low notification received after intermediary communication"); - lttng_notification_destroy(notification); - notification = NULL; + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); - /* Stop consumer to force a high notification */ - stop_consumer(argv); resume_application(); - lttng_start_tracing(session_name); - - do { - nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); - } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && - lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, - "High notification received after intermediary communication"); - lttng_notification_destroy(notification); - notification = NULL; - - suspend_application(); - lttng_stop_tracing_no_wait(session_name); - resume_consumer(argv); - wait_data_pending(session_name); - do { - nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); - } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && - lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, - "Low notification received after re-subscription"); - lttng_notification_destroy(notification); - notification = NULL; + /* Get 3 notifications */ + for (i = 0; i < 3; i++) { + struct lttng_notification *notification = get_next_notification( + notification_channel); + ok(notification, "Received notification"); - stop_consumer(argv); - resume_application(); - /* Stop consumer to force a high notification */ - lttng_start_tracing(session_name); + /* Error */ + if (notification == NULL) { + goto end; + } - do { - nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); - } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && - lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, - "High notification"); - lttng_notification_destroy(notification); - notification = NULL; + ret = validator_notification_trigger_name(notification, trigger_name); + if (ret) { + lttng_notification_destroy(notification); + goto end; + } - /* Resume consumer to allow event consumption */ - suspend_application(); - lttng_stop_tracing_no_wait(session_name); - resume_consumer(argv); - wait_data_pending(session_name); + ret = validator_notification_trigger_capture(domain_type, notification, i); + if (ret) { + lttng_notification_destroy(notification); + goto end; + } - nc_status = lttng_notification_channel_unsubscribe(notification_channel, low_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Unsubscribe low condition with pending notification"); - nc_status = lttng_notification_channel_unsubscribe(notification_channel, high_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Unsubscribe high condition with pending notification"); + lttng_notification_destroy(notification); + } end: + suspend_application(); lttng_notification_channel_destroy(notification_channel); + lttng_unregister_trigger(trigger); lttng_trigger_destroy(trigger); lttng_action_destroy(action); - lttng_condition_destroy(low_condition); - lttng_condition_destroy(high_condition); - lttng_condition_destroy(dummy_invalid_condition); - lttng_condition_destroy(dummy_condition); + lttng_condition_destroy(condition); + return; } int main(int argc, const char *argv[]) { - const char *session_name = NULL; - const char *channel_name = NULL; + int test_scenario; const char *domain_type_string = NULL; enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; - plan_tests(NUM_TESTS); - - /* Argument 6 and upward are named pipe location for consumerd control */ - named_pipe_args_start = 6; - - if (argc < 7) { - fail("Missing parameter for tests to run %d", argc); + if (argc < 5) { + fail("Missing test scenario, domain type, pid, or application state file argument(s)"); goto error; } - nb_args = argc; - - domain_type_string = argv[1]; - session_name = argv[2]; - channel_name = argv[3]; - app_pid = (pid_t) atoi(argv[4]); - app_state_file = argv[5]; + test_scenario = atoi(argv[1]); + domain_type_string = argv[2]; + app_pid = (pid_t) atoi(argv[3]); + app_state_file = argv[4]; if (!strcmp("LTTNG_DOMAIN_UST", domain_type_string)) { domain_type = LTTNG_DOMAIN_UST; @@ -736,13 +2498,172 @@ int main(int argc, const char *argv[]) goto error; } - diag("Test trigger for domain %s with buffer_usage_low condition", domain_type_string); - test_triggers_buffer_usage_condition(session_name, channel_name, domain_type, LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW); - diag("Test trigger for domain %s with buffer_usage_high condition", domain_type_string); - test_triggers_buffer_usage_condition(session_name, channel_name, domain_type, LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH); + /* + * Test cases are responsible for resuming the app when needed + * and making sure it's suspended when returning. + */ + suspend_application(); + + switch (test_scenario) { + case 1: + { + plan_tests(44); + + /* Test cases that need gen-ust-event testapp. */ + diag("Test basic notification error paths for domain %s", + domain_type_string); + test_invalid_channel_subscription(domain_type); + + diag("Test tracepoint event rule notifications for domain %s", + domain_type_string); + test_tracepoint_event_rule_notification(domain_type); + + diag("Test tracepoint event rule notifications with filter for domain %s", + domain_type_string); + test_tracepoint_event_rule_notification_filter(domain_type); + break; + } + case 2: + { + const char *session_name, *channel_name; + /* Test cases that need a tracing session enabled. */ + plan_tests(99); + + /* + * Argument 7 and upward are named pipe location for consumerd + * control. + */ + named_pipe_args_start = 7; + + if (argc < 8) { + fail("Missing parameter for tests to run %d", argc); + goto error; + } + + nb_args = argc; + + session_name = argv[5]; + channel_name = argv[6]; + + test_subscription_twice(session_name, channel_name, + domain_type); + + diag("Test trigger for domain %s with buffer_usage_low condition", + domain_type_string); + test_triggers_buffer_usage_condition(session_name, channel_name, + domain_type, + LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW); + + diag("Test trigger for domain %s with buffer_usage_high condition", + domain_type_string); + test_triggers_buffer_usage_condition(session_name, channel_name, + domain_type, + LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH); + + diag("Test buffer usage notification channel api for domain %s", + domain_type_string); + test_buffer_usage_notification_channel(session_name, channel_name, + domain_type, argv); + break; + } + case 3: + { + /* + * Test cases that need a test app with more than one event + * type. + */ + plan_tests(25); + + /* + * At the moment, the only test case of this scenario is + * exclusion which is only supported by UST + */ + assert(domain_type == LTTNG_DOMAIN_UST); + diag("Test tracepoint event rule notifications with exclusion for domain %s", + domain_type_string); + test_tracepoint_event_rule_notification_exclusion(domain_type); + + break; + } + case 4: + { + plan_tests(13); + /* Test cases that need the kernel tracer. */ + assert(domain_type == LTTNG_DOMAIN_KERNEL); + + diag("Test kprobe event rule notifications for domain %s", + domain_type_string); + + test_kprobe_event_rule_notification(domain_type); + + break; + } + case 5: + { + plan_tests(25); + /* Test cases that need the kernel tracer. */ + assert(domain_type == LTTNG_DOMAIN_KERNEL); + + diag("Test syscall event rule notifications for domain %s", + domain_type_string); + + test_syscall_event_rule_notification(domain_type); + + diag("Test syscall filtering event rule notifications for domain %s", + domain_type_string); + + test_syscall_event_rule_notification_filter(domain_type); + + break; + } + case 6: + { + const char *testapp_path, *test_symbol_name; + + plan_tests(13); + + if (argc < 7) { + fail("Missing parameter for tests to run %d", argc); + goto error; + } + + testapp_path = argv[5]; + test_symbol_name = argv[6]; + /* Test cases that need the kernel tracer. */ + assert(domain_type == LTTNG_DOMAIN_KERNEL); + + diag("Test userspace-probe event rule notifications for domain %s", + domain_type_string); + + test_uprobe_event_rule_notification( + domain_type, testapp_path, test_symbol_name); + + break; + } + case 7: + { + switch(domain_type) { + case LTTNG_DOMAIN_UST: + plan_tests(222); + break; + case LTTNG_DOMAIN_KERNEL: + plan_tests(216); + break; + default: + assert(0); + } + + diag("Test tracepoint event rule notification captures for domain %s", + domain_type_string); + test_tracepoint_event_rule_notification_capture(domain_type); + + break; + } + + default: + abort(); + } - diag("Test notification channel api for domain %s", domain_type_string); - test_notification_channel(session_name, channel_name, domain_type, argv); error: return exit_status(); } diff --git a/tests/regression/tools/notification/sessiond_testpoints.c b/tests/regression/tools/notification/sessiond_testpoints.c new file mode 100644 index 000000000..90b35272b --- /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 *trigger_notif_consumption_state;; + +int lttng_opt_verbose; +int lttng_opt_mi; +int lttng_opt_quiet; + +static +void __attribute__((destructor)) pause_pipe_fini(void) +{ + int ret; + + if (pause_pipe_path) { + ret = unlink(pause_pipe_path); + if (ret) { + PERROR("unlink pause pipe"); + } + } + + free(pause_pipe_path); + lttng_pipe_destroy(pause_pipe); +} + +/* + */ +int __testpoint_sessiond_thread_notification(void); +int __testpoint_sessiond_thread_notification(void) +{ + int ret = 0; + const char *pause_pipe_path_prefix; + + pause_pipe_path_prefix = lttng_secure_getenv( + "TRIGGER_PAUSE_PIPE_PATH"); + if (!pause_pipe_path_prefix) { + ret = -1; + goto end; + } + + trigger_notif_consumption_state = dlsym(NULL, "trigger_consumption_paused"); + assert(trigger_notif_consumption_state); + + ret = asprintf(&pause_pipe_path, "%s", pause_pipe_path_prefix); + if (ret < 1) { + ERR("Failed to allocate pause pipe path"); + goto end; + } + + DBG("Creating pause pipe at %s", pause_pipe_path); + pause_pipe = lttng_pipe_named_open(pause_pipe_path, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, O_NONBLOCK); + if (!pause_pipe) { + ERR("Failed to create pause pipe at %s", pause_pipe_path); + ret = -1; + goto end; + } + + /* Only the read end of the pipe is useful to us. */ + ret = lttng_pipe_write_close(pause_pipe); +end: + return ret; +} + +int __testpoint_sessiond_handle_trigger_event_pipe(void); +int __testpoint_sessiond_handle_trigger_event_pipe(void) +{ + int ret = 0; + uint8_t value; + bool value_read = false; + + if (!pause_pipe) { + ret = -1; + goto end; + } + + /* Purge pipe and only consider the freshest value. */ + do { + ret = lttng_pipe_read(pause_pipe, &value, sizeof(value)); + if (ret == sizeof(value)) { + value_read = true; + } + } while (ret == sizeof(value)); + + ret = (errno == EAGAIN) ? 0 : -errno; + + if (value_read) { + *trigger_notif_consumption_state = !!value; + DBG("Message received on pause pipe: %s data consumption", + *trigger_notif_consumption_state ? "paused" : "resumed"); + } +end: + return ret; +} diff --git a/tests/regression/tools/notification/test_notification_kernel_buffer_usage b/tests/regression/tools/notification/test_notification_kernel_buffer_usage new file mode 100755 index 000000000..3214f90f9 --- /dev/null +++ b/tests/regression/tools/notification/test_notification_kernel_buffer_usage @@ -0,0 +1,88 @@ +#!/bin/bash +# +# Copyright (C) 2017 Jonathan Rajotte +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname "$0")/ +TESTDIR=$CURDIR/../../../ + +TMPDIR=$(mktemp -d) + +#This is needed since the testpoint create a pipe with the consumerd type suffixed +TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n") +TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX") +TESTPOINT=$(readlink -f "${CURDIR}/.libs/libpause_consumer.so") +TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX") + + +SESSION_NAME="my_session" +CHANNEL_NAME="my_channel" + +NUM_TESTS=104 + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" +# shellcheck source=./util_event_generator.sh +source "$CURDIR/util_event_generator.sh" + +function test_buffer_usage_notification +{ + local event_name="lttng_test_filter_event" + local trace_path + local page_size + local consumerd_pipe=() + + trace_path=$(mktemp -d) + page_size=$(getconf PAGE_SIZE) + + create_lttng_session_notap $SESSION_NAME "$trace_path" + + lttng_enable_kernel_channel_notap $SESSION_NAME $CHANNEL_NAME \ + --subbuf-size="$page_size" + enable_kernel_lttng_event_notap $SESSION_NAME $event_name $CHANNEL_NAME + + kernel_event_generator generate_filter_events "$TESTAPP_STATE_PATH" & + APP_PID=$! + + # This is needed since the testpoint create a pipe with the consumer + # type suffixed. + for f in "$TESTPOINT_BASE_PATH"*; do + consumerd_pipe+=("$f") + done + + "$CURDIR/notification" 2 LTTNG_DOMAIN_KERNEL $APP_PID "$TESTAPP_STATE_PATH" \ + $SESSION_NAME $CHANNEL_NAME "${consumerd_pipe[@]}" + + destroy_lttng_session_notap $SESSION_NAME + + kill -SIGUSR2 $APP_PID + wait $APP_PID 2> /dev/null +} + +if [ "$(id -u)" == "0" ]; then + + validate_lttng_modules_present + + + modprobe lttng-test + + # Used on sessiond launch. + LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 \ + CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} \ + LD_PRELOAD=${TESTPOINT}" + start_lttng_sessiond_notap + + test_buffer_usage_notification + + stop_lttng_sessiond_notap + rmmod lttng-test + + rm -rf "${consumerd_pipe[@]}" 2> /dev/null +else + # Kernel tests are skipped. + plan_tests $NUM_TESTS + skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS +fi + +rm -rf "$TMPDIR" diff --git a/tests/regression/tools/notification/test_notification_kernel_capture b/tests/regression/tools/notification/test_notification_kernel_capture new file mode 100755 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_kernel_error b/tests/regression/tools/notification/test_notification_kernel_error new file mode 100755 index 000000000..ae39e9e3e --- /dev/null +++ b/tests/regression/tools/notification/test_notification_kernel_error @@ -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" 1 LTTNG_DOMAIN_KERNEL $APP_PID \ + "$TESTAPP_STATE_PATH" + + kill -SIGUSR2 $APP_PID + wait $APP_PID 2> /dev/null +} + + +if [ "$(id -u)" == "0" ]; then + validate_lttng_modules_present + + modprobe lttng-test + + start_lttng_sessiond_notap + + test_basic_error_path + + stop_lttng_sessiond_notap + rmmod lttng-test + +else + # Kernel tests are skipped. + plan_tests $NUM_TESTS + skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS +fi + +# Just in case cleanup +rm -rf "$TMPDIR" diff --git a/tests/regression/tools/notification/test_notification_kernel_instrumentation b/tests/regression/tools/notification/test_notification_kernel_instrumentation new file mode 100755 index 000000000..1bc702b70 --- /dev/null +++ b/tests/regression/tools/notification/test_notification_kernel_instrumentation @@ -0,0 +1,51 @@ +#!/bin/bash +# +# Copyright (C) 2017 Jonathan Rajotte-Julien +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname "$0")/ +TESTDIR=$CURDIR/../../../ + +TMPDIR=$(mktemp -d) + +TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX") + +NUM_TESTS=104 + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" +# shellcheck source=./util_event_generator.sh +source "$CURDIR/util_event_generator.sh" + +function test_kernel_instrumentation_notification +{ + kernel_event_generator generate_filter_events "$TESTAPP_STATE_PATH" & + APP_PID=$! + + "$CURDIR/notification" 4 LTTNG_DOMAIN_KERNEL $APP_PID \ + "$TESTAPP_STATE_PATH" + + kill -SIGUSR2 $APP_PID + wait $APP_PID 2> /dev/null +} + +if [ "$(id -u)" == "0" ]; then + validate_lttng_modules_present + + modprobe lttng-test + + start_lttng_sessiond_notap + + test_kernel_instrumentation_notification + + stop_lttng_sessiond_notap + rmmod lttng-test + +else + # Kernel tests are skipped. + plan_tests $NUM_TESTS + skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS +fi + +rm -rf "$TMPDIR" diff --git a/tests/regression/tools/notification/test_notification_kernel_syscall b/tests/regression/tools/notification/test_notification_kernel_syscall new file mode 100755 index 000000000..cf6a0e097 --- /dev/null +++ b/tests/regression/tools/notification/test_notification_kernel_syscall @@ -0,0 +1,52 @@ +#!/bin/bash +# +# Copyright (C) 2017 Jonathan Rajotte-Julien +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname "$0")/ +TESTDIR=$CURDIR/../../../ + +TMPDIR=$(mktemp -d) + +TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX") + +NUM_TESTS=104 + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" +# shellcheck source=./util_event_generator.sh +source "$CURDIR/util_event_generator.sh" + +function test_kernel_syscall_notification +{ + kernel_event_generator_run_once_per_transition generate_syscalls \ + "$TESTAPP_STATE_PATH" 10 /proc/cpuinfo /proc/cmdline & + APP_PID=$! + + # Pass the syscall_generator_file for filtering + "$CURDIR/notification" 5 LTTNG_DOMAIN_KERNEL $APP_PID \ + "$TESTAPP_STATE_PATH" + + + kill -SIGUSR2 $APP_PID + wait $APP_PID 2> /dev/null +} + +if [ "$(id -u)" == "0" ]; then + validate_lttng_modules_present + + start_lttng_sessiond_notap + + test_kernel_syscall_notification + + stop_lttng_sessiond_notap + +else + # Kernel tests are skipped. + plan_tests $NUM_TESTS + skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS +fi + +# Just in case cleanup +rm -rf "$TMPDIR" diff --git a/tests/regression/tools/notification/test_notification_kernel_userspace_probe b/tests/regression/tools/notification/test_notification_kernel_userspace_probe new file mode 100755 index 000000000..6a50869f0 --- /dev/null +++ b/tests/regression/tools/notification/test_notification_kernel_userspace_probe @@ -0,0 +1,49 @@ +#!/bin/bash +# +# Copyright (C) 2017 Jonathan Rajotte-Julien +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname "$0")/ +TESTDIR=$CURDIR/../../../ + +TMPDIR=$(mktemp -d) + +TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX") + +NUM_TESTS=104 + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" +# shellcheck source=./util_event_generator.sh +source "$CURDIR/util_event_generator.sh" + +function test_kernel_userspace_probe_notification +{ + kernel_event_generator_run_once_per_transition userspace_probe_testapp "$TESTAPP_STATE_PATH" 10 & + APP_PID=$! + + "$CURDIR/notification" 6 LTTNG_DOMAIN_KERNEL \ + $APP_PID "$TESTAPP_STATE_PATH" \ + "$USERSPACE_PROBE_ELF_TESTAPP_BIN" "test_function" + + kill -SIGUSR2 $APP_PID + wait $APP_PID 2> /dev/null +} + +if [ "$(id -u)" == "0" ]; then + validate_lttng_modules_present + + start_lttng_sessiond_notap + + test_kernel_userspace_probe_notification + + stop_lttng_sessiond_notap +else + # Kernel tests are skipped. + plan_tests $NUM_TESTS + skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS +fi + +# Just in case cleanup +rm -rf "$TMPDIR" diff --git a/tests/regression/tools/notification/test_notification_multi_app b/tests/regression/tools/notification/test_notification_multi_app index dae482ce3..f2cbf43a8 100755 --- a/tests/regression/tools/notification/test_notification_multi_app +++ b/tests/regression/tools/notification/test_notification_multi_app @@ -32,6 +32,7 @@ NUM_TEST_KERNEL=50 NUM_TESTS=$(($NUM_TEST_UST + $NUM_TEST_KERNEL)) source $TESTDIR/utils/utils.sh +source $CURDIR/util_event_generator.sh consumerd_pipe=() file_sync_after_first_event=$(mktemp -u) @@ -43,54 +44,6 @@ print_test_banner "$TEST_DESC" app_pids=() -function kernel_event_generator_toggle_state -{ - kernel_event_generator_suspended=$((kernel_event_generator_suspended==0)) - -} -function kernel_event_generator -{ - state_file=$1 - kernel_event_generator_suspended=0 - trap kernel_event_generator_toggle_state SIGUSR1 - - while (true); do - if [[ $kernel_event_generator_suspended -eq "1" ]]; then - touch $state_file - sleep 0.5 - else - if [[ -f $state_file ]]; then - rm $state_file 2> /dev/null - fi - echo -n "1000" > /proc/lttng-test-filter-event - fi - done -} - -function ust_event_generator_toggle_state -{ - ust_event_generator_suspended=$((ust_event_generator_suspended==0)) - -} -function ust_event_generator -{ - state_file=$1 - ust_event_generator_suspended=0 - trap ust_event_generator_toggle_state SIGUSR1 - trap "exit" SIGTERM SIGINT - while (true); do - if [[ $ust_event_generator_suspended -eq "1" ]]; then - touch $state_file - sleep 0.5 - else - if [[ -f $state_file ]]; then - rm $state_file 2> /dev/null - fi - taskset -c 0 $TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT > /dev/null 2>&1 - fi - done -} - function start_client { local pid=-1 local output_file=$1 @@ -300,12 +253,12 @@ function test_multi_app () function test_multi_app_ust () { diag "Multi client app UST notification" - ust_event_generator $TESTAPP_STATE_FILE & + ust_event_generator "$TESTAPP_BIN" "$TESTAPP_STATE_FILE" & local generator_pid=$! test_multi_app ust $generator_pid - kill -s SIGTERM $generator_pid 2> /dev/null + kill -s SIGUSR2 $generator_pid 2> /dev/null wait $generator_pid 2> /dev/null rm -rf ${TESTAPP_STATE_FILE} 2> /dev/null } @@ -315,13 +268,13 @@ function test_multi_app_kernel () diag "Multi client app kernel notification" modprobe lttng-test - kernel_event_generator $TESTAPP_STATE_FILE & + kernel_event_generator generate_filter_events $TESTAPP_STATE_FILE & local generator_pid=$! test_multi_app kernel $generator_pid - kill -s SIGTERM $generator_pid 2> /dev/null + kill -s SIGUSR2 $generator_pid 2> /dev/null wait $generator_pid 2> /dev/null rm -rf ${TESTAPP_STATE_FILE} 2> /dev/null @@ -333,12 +286,12 @@ function test_on_register_evaluation_ust () diag "On register notification UST" # Start app in infinite loop - ust_event_generator $TESTAPP_STATE_FILE & + ust_event_generator "$TESTAPP_BIN" "$TESTAPP_STATE_FILE" & local generator_pid=$! test_on_register_evaluation ust $generator_pid - kill -s SIGTERM $generator_pid 2> /dev/null + kill -s SIGUSR2 $generator_pid 2> /dev/null wait $generator_pid 2> /dev/null rm -rf ${TESTAPP_STATE_FILE} 2> /dev/null @@ -350,13 +303,13 @@ function test_on_register_evaluation_kernel() modprobe lttng-test - kernel_event_generator $TESTAPP_STATE_FILE & + kernel_event_generator generate_filter_events $TESTAPP_STATE_FILE & local generator_pid=$! test_on_register_evaluation kernel $generator_pid - kill -s SIGTERM $generator_pid 2> /dev/null + kill -s SIGUSR2 $generator_pid 2> /dev/null wait $generator_pid 2> /dev/null rm -rf ${TESTAPP_STATE_FILE} 2> /dev/null @@ -447,7 +400,7 @@ function test_on_register_evaluation () destroy_lttng_session_ok $SESSION_NAME stop_lttng_sessiond - kill -s SIGTERM $generator_pid 2> /dev/null + kill -s SIGUSR2 $generator_pid 2> /dev/null wait $generator_pid 2> /dev/null for pipe in "${consumerd_pipe[@]}"; do diff --git a/tests/regression/tools/notification/test_notification_trigger_discarded_count b/tests/regression/tools/notification/test_notification_trigger_discarded_count new file mode 100755 index 000000000..241e6fc64 --- /dev/null +++ b/tests/regression/tools/notification/test_notification_trigger_discarded_count @@ -0,0 +1,262 @@ +#!/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=15 +KERNEL_NUM_TESTS=14 +NUM_TESTS=$(($UST_NUM_TESTS + $KERNEL_NUM_TESTS)) + +plan_tests $NUM_TESTS + +function test_kernel_trigger_discarded_count +{ + local sessiond_pipe=() + local trigger_name="my_trigger" + local list_triggers_stdout=$(mktemp -t list_triggers_stdout.XXXXXX) + + # Used on sessiond launch. + LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 \ + TRIGGER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} \ + LD_PRELOAD=${TESTPOINT}" + + start_lttng_sessiond_notap + + # This is needed since the testpoint create a pipe with the sessiond + # type suffixed. + for f in "$TESTPOINT_BASE_PATH"*; do + sessiond_pipe+=("$f") + done + + "$FULL_LTTNG_BIN" add-trigger --id "$trigger_name" \ + --condition on-event --kernel lttng_test_filter_event \ + --action notify > /dev/null > /dev/null + ok $? "Adding \`on-event\` kernel trigger successful" + + "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout" + + # Confirm that the discarded notification line is present. + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0" + ok $? "No discarded tracer notification" + + # Stop consumption of trigger tracer notifications. + echo -n 1 > $sessiond_pipe + + # The trigger ring buffer configuration is currently made of 16 4096 + # bytes subbuffers. Each kernel notification is at least 42 bytes long. + # To fill it, we need to generate (16 * 4096)/42 = 1561 notifications. + # That number is a bit larger than what we need since some of the space + # is lost in subbuffer boundaries. + echo -n "200000" > /proc/lttng-test-filter-event + + "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout" + + # Confirm that the discarded notification line is present. To avoid + # false positive. + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded" + ok $? "Tracer notification discarded line printed" + + # Confirm that the number of tracer notifications discarded is not zero. + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0" + isnt $? 0 "Discarded tracer notification number non-zero as expected" + + "$FULL_LTTNG_BIN" remove-trigger "$trigger_name" > /dev/null + ok $? "Removing \`on-event\` kernel trigger successful" + + # Confirm that no trigger is enabled. + list_triggers_line_count=$("$FULL_LTTNG_BIN" list-triggers | wc -l) + is "$list_triggers_line_count" "0" "No \`on-event\` kernel trigger enabled as expected" + + # Enable another trigger and list it to confirm the counter was cleared. + "$FULL_LTTNG_BIN" add-trigger --id "$trigger_name" \ + --condition on-event --kernel lttng_test_filter_event \ + --action notify > /dev/null + ok $? "Adding another \`on-event\` kernel trigger successful" + + # Confirm that the discarded notification line is present. + "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout" + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0" + ok $? "No discarded tracer notification" + + "$FULL_LTTNG_BIN" remove-trigger "$trigger_name" > /dev/null + ok $? "Removing \`on-event\` kernel trigger successful" + + + stop_lttng_sessiond_notap + + unset LTTNG_SESSIOND_ENV_VARS + + rm -f "$list_triggers_stdout" +} + +function test_kernel_trigger_discarded_count_max_bucket +{ + "$FULL_LTTNG_SESSIOND_BIN" -d --trigger-error-number-of-bucket=3 + + for i in $(seq 3); do + "$FULL_LTTNG_BIN" add-trigger --id "$i" \ + --condition on-event --kernel my_event_that_doesnt_need_to_really_exist_$i \ + --action notify > /dev/null + ok $? "Enabling \`on-event\` kernel trigger $i succeeds" + done + + for i in $(seq 4 5); do + "$FULL_LTTNG_BIN" add-trigger --id "$i" \ + --condition on-event --kernel my_event_that_doesnt_need_to_really_exist_$i \ + --action notify > /dev/null 2>&1 + isnt $? 0 "Enabling \`on-event\` kernel trigger $i fails as expected" + done + + stop_lttng_sessiond_notap +} + +function test_ust_trigger_discarded_count +{ + local sessiond_pipe=() + local trigger_name="my_trigger" + local list_triggers_stdout=$(mktemp -t list_triggers_stdout.XXXXXX) + local NR_ITER=2000 + local NR_USEC_WAIT=0 + + + # Used on sessiond launch. + LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 \ + TRIGGER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} \ + LD_PRELOAD=${TESTPOINT}" + + start_lttng_sessiond_notap + + # This is needed since the testpoint create a pipe with the sessiond + # type suffixed. + for f in "$TESTPOINT_BASE_PATH"*; do + sessiond_pipe+=("$f") + done + + "$FULL_LTTNG_BIN" add-trigger --id "$trigger_name" \ + --condition on-event --userspace tp:tptest \ + --action notify > /dev/null + ok $? "Adding \`on-event\` userspace trigger successful" + + "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout" + + # Confirm that the discarded notification line is present. + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0" + ok $? "No discarded tracer notification" + + # Stop consumption of trigger tracer notifications. + echo -n 1 > $sessiond_pipe + + # The trigger ring buffer configuration is currently made of 16 4096 + # bytes subbuffers. Each userspace notification is at least 42 bytes long. + # To fill it, we need to generate (16 * 4096)/42 = 1561 notifications. + # That number is a bit larger than what we need since some of the space + # is lost in subbuffer boundaries. + $TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT + ok $? "Generating $NR_ITER tracer notifications" + + "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout" + + # Confirm that the discarded notification line is present. To avoid + # false positive. + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded" + ok $? "Tracer notification discarded line printed" + + # Confirm that the number of tracer notifications discarded is not zero. + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0" + isnt $? 0 "Discarded tracer notification number non-zero as expected" + + # Remove the trigger. + "$FULL_LTTNG_BIN" remove-trigger "$trigger_name" > /dev/null + ok $? "Removing \`on-event\` userspace trigger successful" + + # Confirm that no trigger is enabled. + list_triggers_line_count=$("$FULL_LTTNG_BIN" list-triggers | wc -l) + is "$list_triggers_line_count" "0" "No \`on-event\` userspace trigger enabled as expected" + + # Enable another trigger and list it to confirm the counter was cleared. + "$FULL_LTTNG_BIN" add-trigger --id "$trigger_name" \ + --condition on-event --userspace tp:tptest \ + --action notify > /dev/null + ok $? "Adding another \`on-event\` userspace trigger successful" + + # Confirm that the discarded notification line is present. + "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout" + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0" + ok $? "No discarded tracer notification" + + "$FULL_LTTNG_BIN" remove-trigger "$trigger_name" > /dev/null + ok $? "Removing \`on-event\` userspace trigger successful" + + stop_lttng_sessiond_notap + + unset LTTNG_SESSIOND_ENV_VARS + + rm -f "$list_triggers_stdout" +} +function test_ust_trigger_discarded_count_max_bucket +{ + "$FULL_LTTNG_SESSIOND_BIN" -d --trigger-error-number-of-bucket=3 + + for i in $(seq 3); do + "$FULL_LTTNG_BIN" add-trigger --id "$i" \ + --condition on-event --userspace my_event_that_doesnt_need_to_really_exist_$i \ + --action notify > /dev/null + ok $? "Enabling \`on-event\` userspace trigger $i succeeds" + done + + for i in $(seq 4 5); do + "$FULL_LTTNG_BIN" add-trigger --id "$i" \ + --condition on-event --userspace my_event_that_doesnt_need_to_really_exist_$i \ + --action notify > /dev/null 2>&1 + isnt $? 0 "Enabling \`on-event\` userspace trigger $i fails as expected" + done + + stop_lttng_sessiond_notap +} + +test_ust_trigger_discarded_count +test_ust_trigger_discarded_count_max_bucket + +if [ "$(id -u)" == "0" ]; then + + validate_lttng_modules_present + + modprobe lttng-test + + test_kernel_trigger_discarded_count + + test_kernel_trigger_discarded_count_max_bucket + + modprobe --remove lttng-test + + rm -rf "${sessiond_pipe[@]}" 2> /dev/null +else + # Kernel tests are skipped. + skip 0 "Root access is needed. Skipping all kernel notification tests." $KERNEL_NUM_TESTS +fi + +rm -rf "$TMPDIR" diff --git a/tests/regression/tools/notification/test_notification_ust_buffer_usage b/tests/regression/tools/notification/test_notification_ust_buffer_usage new file mode 100755 index 000000000..a8b96bb33 --- /dev/null +++ b/tests/regression/tools/notification/test_notification_ust_buffer_usage @@ -0,0 +1,74 @@ +#!/bin/bash +# +# Copyright (C) 2017 Jonathan Rajotte +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname "$0")/ +TESTDIR=$CURDIR/../../../ + +TMPDIR=$(mktemp -d) + +#This is needed since the testpoint create a pipe with the consumerd type suffixed +TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n") +TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX") +TESTPOINT=$(readlink -f "${CURDIR}/.libs/libpause_consumer.so") + +TESTAPP_PATH="$TESTDIR/utils/testapp" + +GEN_UST_EVENTS_TESTAPP_NAME="gen-ust-events" +GEN_UST_EVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_EVENTS_TESTAPP_NAME/$GEN_UST_EVENTS_TESTAPP_NAME" + +TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX") + +SESSION_NAME="my_session" +CHANNEL_NAME="my_channel" + +TRACE_PATH=$(mktemp -d) +PAGE_SIZE=$(getconf PAGE_SIZE) + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" +# shellcheck source=./util_event_generator.sh +source "$CURDIR/util_event_generator.sh" + +function test_buffer_usage_notification +{ + consumerd_pipe=() + event_name="tp:tptest" + + create_lttng_session_notap $SESSION_NAME "$TRACE_PATH" + + enable_ust_lttng_channel_notap $SESSION_NAME $CHANNEL_NAME --subbuf-size="$PAGE_SIZE" + enable_ust_lttng_event_notap $SESSION_NAME $event_name $CHANNEL_NAME + + # This is needed since the testpoint create a pipe with the consumer type suffixed + for f in "$TESTPOINT_BASE_PATH"*; do + consumerd_pipe+=("$f") + done + + ust_event_generator "$GEN_UST_EVENTS_TESTAPP_BIN" "$TESTAPP_STATE_PATH" & + APP_PID=$! + + "$CURDIR/notification" 2 LTTNG_DOMAIN_UST $APP_PID "$TESTAPP_STATE_PATH" \ + $SESSION_NAME $CHANNEL_NAME "${consumerd_pipe[@]}" + + destroy_lttng_session_notap $SESSION_NAME + + # On ungraceful kill the app is cleaned up via the full_cleanup call + # Suppress kill message + kill -SIGUSR2 $APP_PID + wait $APP_PID 2> /dev/null + + # Just in case cleanup + rm -rf "$TRACE_PATH" +} + +LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LD_PRELOAD=${TESTPOINT}" +start_lttng_sessiond_notap + +test_buffer_usage_notification + +stop_lttng_sessiond_notap + +rm -rf "$TMPDIR" diff --git a/tests/regression/tools/notification/test_notification_ust_capture b/tests/regression/tools/notification/test_notification_ust_capture new file mode 100755 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/notification/test_notification_ust_error b/tests/regression/tools/notification/test_notification_ust_error new file mode 100755 index 000000000..57de30df8 --- /dev/null +++ b/tests/regression/tools/notification/test_notification_ust_error @@ -0,0 +1,39 @@ +#!/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" 5 5 & + APP_PID=$! + + "$CURDIR/notification" 1 LTTNG_DOMAIN_UST $APP_PID "$TESTAPP_STATE_PATH" + + kill -SIGUSR2 $APP_PID + wait $APP_PID 2> /dev/null +} + +start_lttng_sessiond_notap + +test_basic_error_path + +stop_lttng_sessiond_notap diff --git a/tests/regression/tools/notification/test_notification_ust_event_rule_condition_exclusion b/tests/regression/tools/notification/test_notification_ust_event_rule_condition_exclusion new file mode 100755 index 000000000..12ae728bb --- /dev/null +++ b/tests/regression/tools/notification/test_notification_ust_event_rule_condition_exclusion @@ -0,0 +1,41 @@ +#!/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_NEVENTS_TESTAPP_NAME="gen-ust-nevents" +GEN_UST_NEVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_NEVENTS_TESTAPP_NAME/$GEN_UST_NEVENTS_TESTAPP_NAME" + +TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX") + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" +# shellcheck source=./util_event_generator.sh +source "$CURDIR/util_event_generator.sh" + +function test_event_rule_condition_exclusion_notification +{ + ust_event_generator_run_once_per_transition "$GEN_UST_NEVENTS_TESTAPP_BIN" "$TESTAPP_STATE_PATH" 5 5 & + APP_PID=$! + + "$CURDIR/notification" 3 LTTNG_DOMAIN_UST $APP_PID "$TESTAPP_STATE_PATH" + + kill -SIGUSR2 $APP_PID + wait $APP_PID 2> /dev/null +} + +start_lttng_sessiond_notap + +test_event_rule_condition_exclusion_notification + +stop_lttng_sessiond_notap + +rm -rf "$TMPDIR" diff --git a/tests/regression/tools/notification/util_event_generator.sh b/tests/regression/tools/notification/util_event_generator.sh new file mode 100644 index 000000000..cafd0643e --- /dev/null +++ b/tests/regression/tools/notification/util_event_generator.sh @@ -0,0 +1,195 @@ +#!/bin/bash +# +# Copyright (C) 2020 Jonathan Rajotte-Julien +# +# SPDX-License-Identifier: LGPL-2.1-only + +GENERATOR_CURDIR=$(dirname "$0")/ +GENERATOR_TESTDIR=$GENERATOR_CURDIR/../../../ +TESTAPP_PATH=${TESTAPP_PATH:-"$GENERATOR_TESTDIR/utils/testapp"} + +SYSCALL_TESTAPP_NAME=${SYSCALL_TESTAPP_NAME:-"gen-syscall-events"} +SYSCALL_TESTAPP_BIN=${SYSCALL_TESTAPP_BIN:-"$TESTAPP_PATH/$SYSCALL_TESTAPP_NAME/$SYSCALL_TESTAPP_NAME"} + +USERSPACE_PROBE_ELF_TESTAPP_NAME=${USERSPACE_PROBE_ELF_TESTAPP_NAME:-"userspace-probe-elf-binary"} +USERSPACE_PROBE_ELF_TESTAPP_BIN=${USERSPACE_PROBE_ELF_TESTAPP_BIN:-"$TESTAPP_PATH/$USERSPACE_PROBE_ELF_TESTAPP_NAME/.libs/$USERSPACE_PROBE_ELF_TESTAPP_NAME"} + +function generate_filter_events +{ + local nr=$1 + /bin/echo -n "$nr" > /proc/lttng-test-filter-event 2> /dev/null +} + +function generate_syscalls +{ + local nr=$1 + shift + + for i in $(seq 1 "$nr"); do + # Pass /dev/null so to generate the syscall right away. + $SYSCALL_TESTAPP_BIN /dev/null "$@" + done +} + +function userspace_probe_testapp +{ + local nr=$1 + shift + + for i in $(seq 1 "$nr"); do + $USERSPACE_PROBE_ELF_TESTAPP_BIN "$@" + done +} + +function ust_event_generator_toggle_state +{ + ust_event_generator_suspended=$((ust_event_generator_suspended==0)) +} + +function generator_quit +{ + generator_quit=0 +} + +# Note: Only one generator can be used at a time per domain type +function ust_event_generator_run_once_per_transition +{ + # Used by the signal trap + ust_event_generator_suspended=0 + # Used by the signal trap for SIGUSR2 to end the generator + generator_quit=1 + + local test_app=$1 + local state_file=$2 + local nr_iter=$3 + local nr_usec_wait=$4 + local run=false + + # Pass any of the remaining arguments to the generator. + shift 4 + + trap ust_event_generator_toggle_state SIGUSR1 + trap generator_quit SIGUSR2 + + while [ $generator_quit -ne 0 ]; do + if [[ $ust_event_generator_suspended -eq "1" ]]; then + touch "$state_file" + # Reset the "run" state + run=true + sleep 0.5 + elif [ "$run" = true ]; then + taskset -c 0 "$test_app" -i "$nr_iter" -w "$nr_usec_wait" "$@"> /dev/null 2>&1 + run=false; + if [[ -f $state_file ]]; then + rm -rf "$state_file" 2> /dev/null + fi + else + # Wait for a "suspend" to reset the run state + sleep 0.1 + fi + done +} + +# Note: Only one generator can be used at a time per domain type +function ust_event_generator +{ + # Used by the signal trap + ust_event_generator_suspended=0 + # Used by the signal trap for SIGUSR2 to end the generator + generator_quit=1 + + local test_app=$1 + local state_file=$2 + local nr_iter=1000 + local nr_usec_wait=5 + + # Pass any of the remaining arguments to the generator. + shift 2 + + trap ust_event_generator_toggle_state SIGUSR1 + trap generator_quit SIGUSR2 + + while [ $generator_quit -ne 0 ]; do + if [[ $ust_event_generator_suspended -eq "1" ]]; then + touch "$state_file" + # Reset the "run" state + sleep 0.5 + else + taskset -c 0 "$test_app" -i $nr_iter -w $nr_usec_wait "$@" > /dev/null 2>&1 + if [[ -f $state_file ]]; then + rm -rf "$state_file" 2> /dev/null + fi + fi + done +} + +function kernel_event_generator_toggle_state +{ + kernel_event_generator_suspended=$((kernel_event_generator_suspended==0)) + +} + +function kernel_event_generator_run_once_per_transition +{ + # Used by the signal trap + kernel_event_generator_suspended=0 + # Used by the signal trap for SIGUSR2 to end the generator + generator_quit=1 + + local generator=$1 + local state_file=$2 + local nr_iter=$3 + + # Pass any of the remaining arguments to the generator. + shift 3 + + local run=false + trap kernel_event_generator_toggle_state SIGUSR1 + trap generator_quit SIGUSR2 + + while [ $generator_quit -ne 0 ]; do + if [[ $kernel_event_generator_suspended -eq "1" ]]; then + touch "$state_file" + run=true + sleep 0.5 + elif [ "$run" = true ]; then + $generator "$nr_iter" "$@" + run=false + if [[ -f $state_file ]]; then + rm "$state_file" 2> /dev/null + fi + else + # Wait for a "suspend" to reset the run state + sleep 0.1 + fi + done +} + +function kernel_event_generator +{ + # Used by the signal trap + kernel_event_generator_suspended=0 + # Used by the signal trap for SIGUSR2 to end the generator + generator_quit=1 + + local generator=$1 + local state_file=$2 + + # Pass any of the remaining arguments to the generator. + shift 2 + + trap kernel_event_generator_toggle_state SIGUSR1 + trap generator_quit SIGUSR2 + + while [ $generator_quit -ne 0 ]; do + if [[ $kernel_event_generator_suspended -eq "1" ]]; then + touch "$state_file" + sleep 0.5 + else + $generator "10" "$@" + if [[ -f $state_file ]]; then + rm "$state_file" 2> /dev/null + fi + fi + done +} diff --git a/tests/regression/tools/trigger/Makefile.am b/tests/regression/tools/trigger/Makefile.am new file mode 100644 index 000000000..11c546e1e --- /dev/null +++ b/tests/regression/tools/trigger/Makefile.am @@ -0,0 +1,56 @@ +AM_CFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src -I$(top_srcdir)/tests -I$(top_srcdir)/tests/utils/ -I$(srcdir) +AM_LDFLAGS = + +SUBDIRS=utils start-stop + +LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la +LIB_LTTNG_CTL = $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la + +noinst_PROGRAMS = base_client trigger + +if NO_SHARED + +CLEANFILES = libpause_consumer.so libpause_consumer.so.debug +EXTRA_DIST = test_trigger_ust test_trigger_kernel base_client.c trigger.c consumer_testpoints.c + +else + +# In order to use testpoint, the helper library +# must be built as .so to be able to LD_PRELOAD it. +FORCE_SHARED_LIB_OPTIONS = -module -shared -avoid-version \ + -rpath $(abs_builddir) + +libpause_consumer_la_SOURCES = consumer_testpoints.c +libpause_consumer_la_LIBADD = \ + $(top_builddir)/src/common/libcommon.la \ + $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \ + $(DL_LIBS) +libpause_consumer_la_LDFLAGS = $(FORCE_SHARED_LIB_OPTIONS) +noinst_LTLIBRARIES = libpause_consumer.la + +base_client_SOURCES = base_client.c +base_client_LDADD = $(LIB_LTTNG_CTL) + +trigger_SOURCES = trigger.c +trigger_LDADD = $(LIB_LTTNG_CTL) $(LIBTAP) -lm + +noinst_SCRIPTS = test_trigger_ust test_trigger_kernel +EXTRA_DIST = test_trigger_ust test_trigger_kernel \ + test_add_trigger_cli \ + test_list_triggers_cli \ + test_remove_trigger_cli + +all-local: + @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ + for script in $(EXTRA_DIST); do \ + cp -f $(srcdir)/$$script $(builddir); \ + done; \ + fi + +clean-local: + @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ + for script in $(EXTRA_DIST); do \ + rm -f $(builddir)/$$script; \ + done; \ + fi +endif diff --git a/tests/regression/tools/trigger/base_client.c b/tests/regression/tools/trigger/base_client.c new file mode 100644 index 000000000..df540d27d --- /dev/null +++ b/tests/regression/tools/trigger/base_client.c @@ -0,0 +1,252 @@ +/* + * base_client.c + * + * Base client application for testing of LTTng trigger API + * + * Copyright 2019 Jonathan Rajotte + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const char *session_name = NULL; +enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; +const char *pattern = NULL; + +int parse_arguments(char **argv) { + const char *domain_type_string = NULL; + + session_name = argv[1]; + domain_type_string = argv[2]; + pattern = argv[3]; + + /* Parse arguments */ + /* Domain type */ + if (!strcasecmp("LTTNG_DOMAIN_UST", domain_type_string)) { + domain_type = LTTNG_DOMAIN_UST; + } + if (!strcasecmp("LTTNG_DOMAIN_KERNEL", domain_type_string)) { + domain_type = LTTNG_DOMAIN_KERNEL; + } + if (!strcasecmp("LTTNG_DOMAIN_JUL", domain_type_string)) { + domain_type = LTTNG_DOMAIN_JUL; + } + if (!strcasecmp("LTTNG_DOMAIN_PYTHON", domain_type_string)) { + domain_type = LTTNG_DOMAIN_PYTHON; + } + if (!strcasecmp("LTTNG_DOMAIN_LOG4J", domain_type_string)) { + domain_type = LTTNG_DOMAIN_LOG4J; + } + if (domain_type == LTTNG_DOMAIN_NONE) { + printf("error: Unknown domain type\n"); + goto error; + } + + return 0; +error: + return 1; +} + +int main(int argc, char **argv) +{ + int ret = 0; + struct lttng_condition *condition = NULL; + + enum lttng_event_rule_status event_rule_status; + struct lttng_event_rule *event_rule = NULL; + + enum lttng_action_status action_status; + struct lttng_action *action = NULL; + + enum lttng_notification_channel_status nc_status; + struct lttng_notification_channel *notification_channel = NULL; + + const char* exclusions[] = { "sample_component:message2"}; + + struct lttng_trigger *trigger = NULL; + + if (argc < 4) { + printf("error: Missing arguments for tests\n"); + ret = 1; + goto end; + } + + ret = parse_arguments(argv); + if (ret) { + printf("error: Could not parse arguments\n"); + goto end; + } + + event_rule = lttng_event_rule_tracepoint_create(domain_type); + if (!event_rule) { + printf("error: Could not create condition object\n"); + ret = 1; + goto end; + } + + event_rule_status = lttng_event_rule_tracepoint_set_pattern(event_rule, pattern); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + printf("error: Could not set pattern\n"); + ret = 1; + goto end; + } + + event_rule_status = lttng_event_rule_tracepoint_set_filter(event_rule, "message=='Hello World'"); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + printf("error: Could not set pattern\n"); + ret = 1; + goto end; + } + + event_rule_status = lttng_event_rule_tracepoint_add_exclusion(event_rule, exclusions[0]); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + printf("error: Could not set exclusions\n"); + ret = 1; + goto end; + } + + condition = lttng_condition_event_rule_create(event_rule); + if (!condition) { + printf("error: Could not create condition\n"); + ret = 1; + goto end; + } + /* Ownership was passed to condition */ + event_rule = NULL; + + action = lttng_action_notify_create(); + if (!action) { + printf("error: Could not create action notify\n"); + ret = 1; + goto end; + } + + trigger = lttng_trigger_create(condition, action); + if (!trigger) { + printf("error: Could not create trigger\n"); + ret = 1; + goto end; + } + + ret = lttng_register_trigger(trigger); + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + if (!notification_channel) { + printf("error: Could not create notification channel\n"); + ret = 1; + goto end; + } + /* + * An equivalent trigger might already be registered if an other app + * registered an equivalent trigger. + */ + if (ret < 0 && ret != -LTTNG_ERR_TRIGGER_EXISTS) { + printf("error: %s\n", lttng_strerror(ret)); + ret = 1; + goto end; + } + + nc_status = lttng_notification_channel_subscribe(notification_channel, condition); + if (nc_status != LTTNG_NOTIFICATION_CHANNEL_STATUS_OK) { + printf("error: Could not subscribe\n"); + ret = 1; + goto end; + } + + for (;;) { + struct lttng_notification *notification; + enum lttng_notification_channel_status status; + const struct lttng_evaluation *notification_evaluation; + const struct lttng_condition *notification_condition; + const char *name; + + /* Receive the next notification. */ + status = lttng_notification_channel_get_next_notification( + notification_channel, + ¬ification); + + switch (status) { + case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK: + break; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED: + ret = 1; + printf("error: No drop should be observed during this test app\n"); + goto end; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_CLOSED: + /* + * The notification channel has been closed by the + * session daemon. This is typically caused by a session + * daemon shutting down (cleanly or because of a crash). + */ + printf("error: Notification channel was closed\n"); + ret = 1; + goto end; + default: + /* Unhandled conditions / errors. */ + printf("error: Unknown notification channel status\n"); + ret = 1; + goto end; + } + + notification_condition = lttng_notification_get_condition(notification); + notification_evaluation = lttng_notification_get_evaluation(notification); + switch (lttng_evaluation_get_type(notification_evaluation)) { + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + lttng_evaluation_event_rule_get_trigger_name(notification_evaluation, &name); + printf("Received nootification from trigger \"%s\"\n", name); + break; + default: + printf("error: Wrong notification evaluation type \n"); + break; + + } + + lttng_notification_destroy(notification); + } +end: + if (trigger) { + lttng_unregister_trigger(trigger); + } + lttng_event_rule_destroy(event_rule); + lttng_trigger_destroy(trigger); + lttng_condition_destroy(condition); + lttng_action_destroy(action); + printf("exit: %d\n", ret); + return ret; +} diff --git a/tests/regression/tools/trigger/consumer_testpoints.c b/tests/regression/tools/trigger/consumer_testpoints.c new file mode 100644 index 000000000..1f5d83ecb --- /dev/null +++ b/tests/regression/tools/trigger/consumer_testpoints.c @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License, version 2 only, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *pause_pipe_path; +static struct lttng_pipe *pause_pipe; +static int *data_consumption_state; +static enum lttng_consumer_type (*lttng_consumer_get_type)(void); + +int lttng_opt_verbose; +int lttng_opt_mi; +int lttng_opt_quiet; + +static +void __attribute__((destructor)) pause_pipe_fini(void) +{ + int ret; + + if (pause_pipe_path) { + ret = unlink(pause_pipe_path); + if (ret) { + PERROR("unlink pause pipe"); + } + } + + free(pause_pipe_path); + lttng_pipe_destroy(pause_pipe); +} + +/* + * We use this testpoint, invoked at the start of the consumerd's data handling + * thread to create a named pipe/FIFO which a test application can use to either + * pause or resume the consumption of data. + */ +int __testpoint_consumerd_thread_data(void) +{ + int ret = 0; + const char *pause_pipe_path_prefix, *domain; + + pause_pipe_path_prefix = lttng_secure_getenv( + "CONSUMER_PAUSE_PIPE_PATH"); + if (!pause_pipe_path_prefix) { + ret = -1; + goto end; + } + + /* + * These symbols are exclusive to the consumerd process, hence we can't + * rely on their presence in the sessiond. Not looking-up these symbols + * dynamically would not allow this shared object to be LD_PRELOAD-ed + * when launching the session daemon. + */ + data_consumption_state = dlsym(NULL, "data_consumption_paused"); + assert(data_consumption_state); + lttng_consumer_get_type = dlsym(NULL, "lttng_consumer_get_type"); + assert(lttng_consumer_get_type); + + switch (lttng_consumer_get_type()) { + case LTTNG_CONSUMER_KERNEL: + domain = "kernel"; + break; + case LTTNG_CONSUMER32_UST: + domain = "ust32"; + break; + case LTTNG_CONSUMER64_UST: + domain = "ust64"; + break; + default: + abort(); + } + + ret = asprintf(&pause_pipe_path, "%s-%s", pause_pipe_path_prefix, + domain); + if (ret < 1) { + ERR("Failed to allocate pause pipe path"); + goto end; + } + + DBG("Creating pause pipe at %s", pause_pipe_path); + pause_pipe = lttng_pipe_named_open(pause_pipe_path, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, O_NONBLOCK); + if (!pause_pipe) { + ERR("Failed to create pause pipe at %s", pause_pipe_path); + ret = -1; + goto end; + } + + /* Only the read end of the pipe is useful to us. */ + ret = lttng_pipe_write_close(pause_pipe); +end: + return ret; +} + +int __testpoint_consumerd_thread_data_poll(void) +{ + int ret = 0; + uint8_t value; + bool value_read = false; + + if (!pause_pipe) { + ret = -1; + goto end; + } + + /* Purge pipe and only consider the freshest value. */ + do { + ret = lttng_pipe_read(pause_pipe, &value, sizeof(value)); + if (ret == sizeof(value)) { + value_read = true; + } + } while (ret == sizeof(value)); + + ret = (errno == EAGAIN) ? 0 : -errno; + + if (value_read) { + *data_consumption_state = !!value; + DBG("Message received on pause pipe: %s data consumption", + *data_consumption_state ? "paused" : "resumed"); + } +end: + return ret; +} diff --git a/tests/regression/tools/trigger/start-stop/Makefile.am b/tests/regression/tools/trigger/start-stop/Makefile.am new file mode 100644 index 000000000..c28007d59 --- /dev/null +++ b/tests/regression/tools/trigger/start-stop/Makefile.am @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-only + +noinst_SCRIPTS = test_start_stop + +EXTRA_DIST = test_start_stop + +all-local: + @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ + for script in $(EXTRA_DIST); do \ + cp -f $(srcdir)/$$script $(builddir); \ + done; \ + fi + +clean-local: + @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ + for script in $(EXTRA_DIST); do \ + rm -f $(builddir)/$$script; \ + done; \ + fi diff --git a/tests/regression/tools/trigger/start-stop/test_start_stop b/tests/regression/tools/trigger/start-stop/test_start_stop new file mode 100755 index 000000000..935d630cb --- /dev/null +++ b/tests/regression/tools/trigger/start-stop/test_start_stop @@ -0,0 +1,198 @@ +#!/bin/bash +# +# Copyright (C) 2020 Francis Deslauriers +# +# SPDX-License-Identifier: LGPL-2.1-only + +TEST_DESC="Triggers - Start and stop actions" + +CURDIR=$(dirname "$0")/ +TESTDIR=${CURDIR}/../../../.. + +# shellcheck source=../../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" + +TESTAPP_PATH="$TESTDIR/utils/testapp" +GEN_UST_EVENTS_TESTAPP_NAME="gen-ust-events" +GEN_UST_EVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_EVENTS_TESTAPP_NAME/$GEN_UST_EVENTS_TESTAPP_NAME" +FULL_LTTNG_BIN="$TESTDIR/../src/bin/lttng/$LTTNG_BIN" +NOTIFICATION_CLIENT_BIN="$CURDIR/../utils/notification-client" +NUM_TESTS=18 + +NR_ITER=5 +NR_USEC_WAIT=5 + +function lttng_add_trigger_ust() +{ + local expected_to_fail="$1" + local trigger_name="$2" + shift 2 + + "$FULL_LTTNG_BIN" add-trigger --id "$trigger_name" "$@" 1> /dev/null 2> /dev/null + ret=$? + if [[ $expected_to_fail -eq "1" ]]; then + test "$ret" -ne "0" + ok $? "Add trigger $trigger_name failed as expected" + else + ok $ret "Add trigger $trigger_name" + fi +} + +function lttng_remove_trigger_ust() +{ + local expected_to_fail="$1" + local trigger_name="$2" + + "$FULL_LTTNG_BIN" remove-trigger "$trigger_name" 1> /dev/null 2> /dev/null + ret=$? + if [[ $expected_to_fail -eq "1" ]]; then + test "$ret" -ne "0" + ok $? "Remove trigger $trigger_name failed as expected" + else + ok $ret "Remove trigger $trigger_name" + fi +} + +function lttng_add_trigger_ust_ok() +{ + lttng_add_trigger_ust 0 "$@" +} + +function lttng_remove_trigger_ust_ok() +{ + lttng_remove_trigger_ust 0 "$@" +} + +function lttng_session_is_active() +{ + local SESSION_NAME="$1" + "$FULL_LTTNG_BIN" list "$SESSION_NAME" | grep "Tracing session" | grep -q "\[active\]" + + ok $ret "Session \"$SESSION_NAME\" is active" +} + +function lttng_session_is_inactive() +{ + local SESSION_NAME="$1" + "$FULL_LTTNG_BIN" list "$SESSION_NAME" | grep "Tracing session" | grep -q "\[inactive\]" + + ok $ret "Session \"$SESSION_NAME\" is inactive" +} + +function test_start_session_action() +{ + local SESSION_NAME="my_triggered_session" + local TRIGGER_NAME="trigger1" + local TRACE_PATH=$(mktemp -d test-start-action-trace.XXXXXX) + local SYNC_AFTER_NOTIF_REGISTER_PATH=$(mktemp test-notif-register.XXXXXX) + + diag "Start session action" + + create_lttng_session_ok $SESSION_NAME "$TRACE_PATH" + + enable_ust_lttng_event_ok $SESSION_NAME "tp:tptest" + + lttng_session_is_inactive $SESSION_NAME + + # Add `start-session` action to an event-rule condition _followed_ by + # a `notify` action. + lttng_add_trigger_ust_ok \ + $TRIGGER_NAME \ + --condition on-event -u "tp:tptest" \ + --action start-session $SESSION_NAME \ + --action notify + + # Launch notification listener. + $NOTIFICATION_CLIENT_BIN \ + --trigger $TRIGGER_NAME \ + --sync-after-notif-register "$SYNC_AFTER_NOTIF_REGISTER_PATH" + notif_client_pid=$! + + while [ ! -f "${SYNC_AFTER_NOTIF_REGISTER_PATH}" ]; do + sleep 0.5 + done + + # Artificially produce the desired event-rule condition. + $GEN_UST_EVENTS_TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT > /dev/null 2>&1 + + # notification-client will exit once it receives a notification. + wait $notif_client_pid + test "$?" -eq "0" + ok $? "notification client exited successfully" + + # Test that the session as started. + lttng_session_is_active $SESSION_NAME + + # Tearing down. + lttng_remove_trigger_ust_ok $TRIGGER_NAME + stop_lttng_tracing_ok $SESSION_NAME + destroy_lttng_session_ok $SESSION_NAME + + rm -f "$SYNC_AFTER_NOTIF_REGISTER_PATH" + rm -rf "$TRACE_PATH" +} + +function test_stop_session_action() +{ + local SESSION_NAME="my_triggered_session" + local TRIGGER_NAME="trigger1" + local TRACE_PATH=$(mktemp -d test-stop-action-trace.XXXXXX) + local SYNC_AFTER_NOTIF_REGISTER_PATH=$(mktemp test-notif-register.XXXXXX) + + diag "Stop session action" + create_lttng_session_ok $SESSION_NAME "$TRACE_PATH" + + enable_ust_lttng_event_ok $SESSION_NAME "tp:tptest" + + start_lttng_tracing_ok $SESSION_NAME + + lttng_session_is_active $SESSION_NAME + + # Add `stop-session` action to an event-rule condition _followed_ by + # a `notify` action. + lttng_add_trigger_ust_ok \ + $TRIGGER_NAME \ + --condition on-event -u "tp:tptest" \ + --action stop-session $SESSION_NAME \ + --action notify + + # Launch notification listener. + $NOTIFICATION_CLIENT_BIN \ + --trigger $TRIGGER_NAME \ + --sync-after-notif-register "$SYNC_AFTER_NOTIF_REGISTER_PATH" + notif_client_pid=$! + + while [ ! -f "${SYNC_AFTER_NOTIF_REGISTER_PATH}" ]; do + sleep 0.5 + done + + # Artificially produce the desired event-rule condition. + $GEN_UST_EVENTS_TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT > /dev/null 2>&1 + + # notification-client will exit once it receives a notification. + wait $notif_client_pid + test "$?" -eq "0" + ok $? "notification client exited successfully" + + # Test that the session as started. + lttng_session_is_inactive $SESSION_NAME + + # Tearing down. + lttng_remove_trigger_ust_ok $TRIGGER_NAME + destroy_lttng_session_ok $SESSION_NAME + + rm -f "$SYNC_AFTER_NOTIF_REGISTER_PATH" + rm -rf "$TRACE_PATH" +} + + # MUST set TESTDIR before calling those functions +plan_tests $NUM_TESTS + +print_test_banner "$TEST_DESC" + +start_lttng_sessiond_notap + +test_start_session_action +test_stop_session_action + +stop_lttng_sessiond_notap diff --git a/tests/regression/tools/trigger/test_add_trigger_cli b/tests/regression/tools/trigger/test_add_trigger_cli new file mode 100755 index 000000000..467d942fd --- /dev/null +++ b/tests/regression/tools/trigger/test_add_trigger_cli @@ -0,0 +1,413 @@ +#!/bin/bash +# +# Copyright (C) - 2020 EfficiOS, inc +# +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# Test the `lttng add-trigger` command line interface. + +CURDIR="$(dirname "$0")" +TESTDIR="$CURDIR/../../.." + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" + +plan_tests 216 + +FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}" + +# shellcheck disable=SC2119 +start_lttng_sessiond_notap + +tmp_stdout=$(mktemp -t test_parse_cli_trigger_stdout.XXXXXX) +tmp_stderr=$(mktemp -t test_parse_cli_trigger_stderr.XXXXXX) +uprobe_elf_binary="${TESTDIR}/utils/testapp/userspace-probe-elf-binary/.libs/userspace-probe-elf-binary" + +if [ "$(id -u)" == "0" ]; then + ist_root=1 +else + ist_root=0 +fi + +function test_success () +{ + local test_name="$1" + shift + + diag "${FULL_LTTNG_BIN} add-trigger $*" + "${FULL_LTTNG_BIN}" add-trigger "$@" > "${tmp_stdout}" 2> "${tmp_stderr}" + ok $? "${test_name}: exit code is 0" + + diff -u "${tmp_stdout}" <(echo "Trigger registered successfully.") + ok $? "${test_name}: expected stdout" + + diff -u "${tmp_stderr}" /dev/null + ok $? "${test_name}: expected stderr" +} + +function test_failure () +{ + local test_name="$1" + local error_msg="$2" + + shift 2 + + diag "${FULL_LTTNG_BIN} add-trigger $*" + "${FULL_LTTNG_BIN}" add-trigger "$@" > "${tmp_stdout}" 2> "${tmp_stderr}" + isnt $? 0 "${test_name}: exit code is not 0" + + diff -u "${tmp_stdout}" /dev/null + ok $? "${test_name}: expected stdout" + + diff -u "${tmp_stderr}" <(echo "${error_msg}") + ok $? "${test_name}: expected stderr" +} + +# top-level options +test_success "explicit id" \ + --id hohoho \ + --condition on-event some-event-id -u \ + --action notify + +# `--condition on-event` successes +test_success "--condition on-event some-event -u" \ + --condition on-event some-event -u \ + --action notify + +test_success "--condition on-event -a -u" \ + --condition on-event -a -u \ + --action notify + +test_success "--fire-once-after" \ + --condition on-event -u test-fire-once-after \ + --action notify \ + --fire-once-after=55 + +test_success "--fire-every" \ + --condition on-event -u test-fire-every \ + --action notify \ + --fire-every=55 + +skip $ist_root "non-root user: skipping kprobe tests" 9 || { + test_success "--condition on-event probe by symbol" \ + --condition on-event -k --probe=lttng_channel_enable my_channel_enable \ + --action notify + + channel_enable_addr=$(grep ' t lttng_channel_enable\s\[lttng_tracer\]$' /proc/kallsyms | cut -f 1 -d ' ') + channel_disable_addr=$(grep ' t lttng_channel_disable\s\[lttng_tracer\]$' /proc/kallsyms | cut -f 1 -d ' ') + + # We need to find a valid offset. + base_symbol="" + offset=0 + if [[ 0x$channel_enable_addr -lt 0x$channel_disable_addr ]]; then + base_symbol="lttng_channel_enable" + offset=$(( 0x$channel_disable_addr - 0x$channel_enable_addr )) + else + base_symbol="lttng_channel_disable" + offset=$(( 0x$channel_enable_addr - 0x$channel_disable_addr )) + fi + + offset_hex="0x$(printf '%x' $offset)" + + test_success "--condition on-event probe by symbol with offset" \ + --condition on-event -k --probe="${base_symbol}+${offset_hex}" my_$base_symbol \ + --action notify + + test_success "--condition on-event probe by address" \ + --condition on-event -k "--probe=0x${channel_enable_addr}" my_channel_enable \ + --action notify +} + +skip $ist_root "non-root user: skipping uprobe tests" 6 || { + test_success "--condition on-event uprobe" \ + --condition on-event -k --userspace-probe=${uprobe_elf_binary}:test_function ma-probe \ + --action notify + + test_success "--condition on-event uprobe with elf prefix" \ + --condition on-event -k --userspace-probe=elf:${uprobe_elf_binary}:test_function ma-probe-2 \ + --action notify +} + +skip $ist_root "non-root user: skipping syscall tests" 9 || { + test_success "--condition on-event syscall" \ + --condition on-event -k --syscall open \ + --action notify + + test_success "--condition on-event syscall -a" \ + --condition on-event -k --syscall -a \ + --action notify + + test_success "--condition on-event syscall with filter" \ + --condition on-event -k --syscall --filter 'a > 2' open \ + --action notify +} + +# `--action notify` successes +test_success "--action notify" \ + --condition on-event some-event-notify -u \ + --action notify + +test_success "--action notify --capture foo" \ + --condition on-event some-event-notify-foo -u \ + --capture foo --action notify + +test_success "--action notify --capture foo[2]" \ + --condition on-event some-event-notify-foo2 -u \ + --capture 'foo[2]' --action notify + +test_success '--action notify --capture $ctx.foo' \ + --condition on-event some-event-notify-ctx-foo -u \ + --capture '$ctx.foo' --action notify + +test_success '--action notify --capture $ctx.foo[2]' \ + --condition on-event some-event-notify-ctx-foo2 -u \ + --capture '$ctx.foo[2]' --action notify + +test_success '--action notify --capture $app.prov:type' \ + --condition on-event some-event-notify-app-prov-type -u \ + --capture '$app.prov:type' --action notify + +test_success '--action notify --capture $app.prov:type[2]' \ + --condition on-event some-event-notify-app-prov-type-2 -u \ + --capture '$app.prov:type[2]' --action notify + +test_success '--action notify multiple captures' \ + --condition on-event some-event-notify-multiple-captures -u \ + --capture foo --capture '$app.hello:world' --action notify + +# `--action start-session` successes +test_success "--action start-session" \ + --condition on-event some-event-start-session -u \ + --action start-session ze-session + +# `--action stop-session` successes +test_success "--action stop-session foo" \ + --condition on-event some-event-stop-session -u \ + --action stop-session ze-session + +# `--action rotate-session` successes +test_success "--action rotate-session foo" \ + --condition on-event some-event-rotate-session -u \ + --action rotate-session ze-session + +# `--action snapshot-session` successes +test_success "--action snapshot-session foo" \ + --condition on-event some-event-snapshot-session -u \ + --action snapshot-session ze-session + +test_success "--action snapshot-session with file URI" \ + --condition on-event some-event-snapshot-session2 -u \ + --action snapshot-session ze-session /hello + +test_success "--action snapshot-session with net URI" \ + --condition on-event some-event-snapshot-session3 -u \ + --action snapshot-session ze-session net://1.2.3.4 + +test_success "--action snapshot-session with ctrl/data URIs" \ + --condition on-event some-event-snapshot-session4 -u \ + --action snapshot-session ze-session --ctrl-url=tcp://1.2.3.4:1234 --data-url=tcp://1.2.3.4:1235 + +# top-level failures +test_failure "no args" "Error: Missing --condition." + +test_failure "unknown option" \ + "Error: Unknown option \`--hello\`" \ + --hello + +test_failure "missing --action" \ + "Error: Need at least one --action." \ + --condition on-event hello -u + +test_failure "two --condition" \ + "Error: A --condition was already given." \ + --condition on-event aaa -u \ + --condition on-event bbb -u \ + --action notify + +test_failure "missing argument to --id" \ + "Error: While parsing argument #1 (\`--id\`): Missing required argument for option \`--id\`" \ + --id + +for cmd in fire-once-after fire-every; do + test_failure "missing argument to --${cmd}" \ + "Error: While parsing argument #1 (\`--${cmd}\`): Missing required argument for option \`--${cmd}\`" \ + --condition on-event -u -a --action notify \ + --${cmd} + + test_failure "invalid argument to --${cmd}: non-digit character" \ + "Error: Failed to parse \`123bob\` as an integer." \ + --condition on-event -u -a --action notify \ + --${cmd} 123bob + + test_failure "invalid argument to --${cmd}: empty string" \ + "Error: Failed to parse \`\` as an integer." \ + --condition on-event -u -a --action notify \ + --${cmd} "" +done + +# `--condition` failures +test_failure "missing args after --condition" \ + "Error: Missing condition name." \ + --condition +test_failure "unknown --condition" \ + "Error: Unknown condition name: zoofest" \ + --condition zoofest + +# `--condition on-event` failures +test_failure "missing args after --condition on-event" \ + "Error: Need to provide either a tracepoint name or -a/--all." \ + --condition on-event +test_failure "missing domain in --condition on-event" \ + "Error: Please specify a domain (-k/-u/-j)." \ + --condition on-event -a +test_failure "extra args after --condition on-event" \ + "Error: Unexpected argument: bozo" \ + --condition on-event foo -u bozo +test_failure "--condition on-event: --all with --probe" \ + "Error: Can't use -a/--all with event rule of type probe." \ + --condition on-event --probe=do_sys_open --all +test_failure "--condition on-event: missing tracepoint name with --probe" \ + "Error: Need to provide either a tracepoint name or -a/--all." \ + --condition on-event -k --probe do_sys_open + +test_failure "--condition on-event: missing tracepoint name with --userspace-probe" \ + "Error: Need to provide either a tracepoint name or -a/--all." \ + --condition on-event -k --userspace-probe=${uprobe_elf_binary}:test_function + +test_failure "--condition on-event: extra argument with --userspace-probe" \ + "Error: Unexpected argument: world" \ + --condition on-event -k --userspace-probe=${uprobe_elf_binary}:test_failure hello world + +test_failure "--condition on-event: missing tracepoint name with --syscall" \ + "Error: Need to provide either a tracepoint name or -a/--all." \ + --condition on-event -k --syscall + +test_failure "--condition on-event: extra argument with --syscall" \ + "Error: Unexpected argument: open" \ + --condition on-event -k --syscall open open + +test_failure "--condition on-event: both -a and a tracepoint name with --syscall" \ + "Error: Can't provide a tracepoint name with -a/--all." \ + --condition on-event -k --syscall -a open + +test_failure "--condition on-event --capture: missing argument (end of arg list)" \ + 'Error: While parsing argument #3 (`--capture`): Missing required argument for option `--capture`' \ + --action notify \ + --condition on-event -u -a --capture + +test_failure "--condition on-event --capture: missing argument (before another option)" \ + 'Error: While parsing expression `--action`: Unary operators are not allowed in capture expressions.' \ + --condition on-event -u -a --capture \ + --action notify \ + +test_failure "--condition on-event --capture: binary operator" \ + 'Error: While parsing expression `foo == 2`: Binary operators are not allowed in capture expressions.' \ + --condition on-event -u -a \ + --capture 'foo == 2' --action notify + +test_failure "--condition on-event --capture: unary operator" \ + 'Error: While parsing expression `!foo`: Unary operators are not allowed in capture expressions.' \ + --condition on-event -u -a \ + --capture '!foo' --action notify + +test_failure "--condition on-event --capture: logical operator" \ + 'Error: While parsing expression `foo || bar`: Logical operators are not allowed in capture expressions.' \ + --condition on-event -u -a \ + --capture 'foo || bar' --action notify + +test_failure "--condition on-event --capture: accessing a sub-field" \ + 'Error: While parsing expression `foo.bar`: Capturing subfields is not supported.' \ + --condition on-event -u -a \ + --capture 'foo.bar' --action notify + +test_failure "--condition on-event --capture: accessing the sub-field of an array element" \ + 'Error: While parsing expression `foo[3].bar`: Capturing subfields is not supported.' \ + --condition on-event -u -a \ + --capture 'foo[3].bar' --action notify + +test_failure "--condition on-event --capture: missing colon in app-specific context field" \ + 'Invalid app-specific context field name: missing colon in `foo`.' \ + --condition on-event -u -a \ + --capture '$app.foo' --action notify + +test_failure "--condition on-event --capture: missing colon in app-specific context field" \ + 'Invalid app-specific context field name: missing type name after colon in `foo:`.' \ + --condition on-event -u -a \ + --capture '$app.foo:' --action notify + +# `--action` failures +test_failure "missing args after --action" \ + "Error: Missing action name." \ + --condition on-event -u -a \ + --action + +# `--action notify` failures +test_failure "extra arg after --action notify" \ + "Error: Unexpected argument \`bob\`." \ + --condition on-event -u -a \ + --action notify bob + +# `--action start-session` failures +test_failure "missing arg after --action start-session" \ + "Error: Missing session name." \ + --condition on-event some-event-start-session -u \ + --action start-session +test_failure "extra arg after --action start-session" \ + "Error: Unexpected argument \`bob\`." \ + --condition on-event some-event-start-session -u \ + --action start-session ze-session bob + +# `--action stop-session` failures +test_failure "missing arg after --action stop-session" \ + "Error: Missing session name." \ + --condition on-event some-event-stop-session -u \ + --action stop-session +test_failure "extra arg after --action stop-session" \ + "Error: Unexpected argument \`bob\`." \ + --condition on-event some-event-stop-session -u \ + --action stop-session ze-session bob + +# `--action rotate-session` failures +test_failure "missing arg after --action rotate-session" \ + "Error: Missing session name." \ + --condition on-event some-event-rotate-session -u \ + --action rotate-session +test_failure "extra arg after --action rotate-session" \ + "Error: Unexpected argument \`bob\`." \ + --condition on-event some-event-rotate-session -u \ + --action rotate-session ze-session bob + +# `--action snapshot-session` failures +test_failure "missing arg after --action snapshot-session" \ + "Error: Missing session name." \ + --condition on-event some-event-snapshot-session -u \ + --action snapshot-session +test_failure "extra arg after --action snapshot-session" \ + "Error: Unexpected argument \`bob\`." \ + --condition on-event some-event-snapshot-session -u \ + --action snapshot-session ze-session /dest bob +test_failure "snapshot-session action, --max-size without destination" \ + "Error: Can't provide a snapshot output max size without a snapshot output destination." \ + --condition on-event some-event-snapshot-session -u \ + --action snapshot-session ze-session --max-size 10M +test_failure "snapshot-session action, --name without destination" \ + "Error: Can't provide a snapshot output name without a snapshot output destination." \ + --condition on-event some-event-snapshot-session -u \ + --action snapshot-session ze-session --name hallo + + +# Cleanup +stop_lttng_sessiond_notap +rm -f "${tmp_stdout}" +rm -f "${tmp_stderr}" diff --git a/tests/regression/tools/trigger/test_list_triggers_cli b/tests/regression/tools/trigger/test_list_triggers_cli new file mode 100755 index 000000000..78b89a18a --- /dev/null +++ b/tests/regression/tools/trigger/test_list_triggers_cli @@ -0,0 +1,399 @@ +#!/bin/bash +# +# Copyright (C) - 2020 EfficiOS, inc +# +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# Test the `lttng list-trigger` command line interface. + +CURDIR="$(dirname "$0")" +TESTDIR="$CURDIR/../../.." + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" + +plan_tests 44 + +FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}" + +tmp_stdout=$(mktemp -t test_list_triggers_cli_stdout.XXXXXX) +tmp_stderr=$(mktemp -t test_list_triggers_cli_stderr.XXXXXX) +tmp_expected_stdout=$(mktemp -t test_list_triggers_cli_expected_stdout.XXXXXX) +uprobe_elf_binary=$(realpath "${TESTDIR}/utils/testapp/userspace-probe-elf-binary/.libs/userspace-probe-elf-binary") + +uid=$(id --user) +gid=$(id --group) + +if [ "$uid" == "0" ]; then + ist_root=1 +else + ist_root=0 +fi + +function add_trigger () +{ + "${FULL_LTTNG_BIN}" add-trigger "$@" + ok $? "add trigger \`$*\`: exit code is 0" +} + +function list_triggers () +{ + local test_name="$1" + local expected_stdout_file="$2" + + "${FULL_LTTNG_BIN}" list-triggers > "${tmp_stdout}" 2> "${tmp_stderr}" + ok $? "${test_name}: exit code is 0" + + diff -u "${expected_stdout_file}" "${tmp_stdout}" + ok $? "${test_name}: expected stdout" + + diff -u /dev/null "${tmp_stderr}" + ok $? "${test_name}: expected stderr" +} + +test_top_level_options () +{ + # shellcheck disable=SC2119 + start_lttng_sessiond_notap + + + add_trigger --id hello --condition on-event -u test-id --action notify + add_trigger --fire-once-after 123 --condition on-event -u test-fire-once-after --action notify + add_trigger --fire-every 124 --condition on-event -u test-fire-every --action notify + + cat > "${tmp_expected_stdout}" <<- EOF + - id: T0 + user id: ${uid} + firing policy: once after 123 occurences + tracer notifications discarded: 0 + condition: event rule hit + rule: test-fire-once-after (type: tracepoint, domain: ust) + actions: + notify + - id: T1 + user id: ${uid} + firing policy: after every 124 occurences + tracer notifications discarded: 0 + condition: event rule hit + rule: test-fire-every (type: tracepoint, domain: ust) + actions: + notify + - id: hello + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: test-id (type: tracepoint, domain: ust) + actions: + notify + EOF + + list_triggers "top level options" "${tmp_expected_stdout}" + + stop_lttng_sessiond_notap +} + +test_on_event_tracepoint () +{ + # shellcheck disable=SC2119 + start_lttng_sessiond_notap + + add_trigger --condition on-event -u -a --action notify + add_trigger --id ABC --condition on-event aaa -u --filter 'p == 2' --action notify + add_trigger --condition on-event 'hello*' -u -x 'hello2,hello3,hello4' --action notify + add_trigger --id BCD --condition on-event -u gerboise --loglevel INFO --action notify + add_trigger --condition on-event -u lemming --loglevel-only WARNING --action notify + add_trigger --condition on-event -u capture-payload-field --capture a --action notify + add_trigger --condition on-event -u capture-array --capture 'a[2]' --capture '$ctx.tourlou[18]' --action notify + add_trigger --condition on-event -u capture-chan-ctx --capture '$ctx.vpid' --action notify + add_trigger --condition on-event -u capture-app-ctx --capture '$app.iga:active_clients' --action notify + + + cat > "${tmp_expected_stdout}" <<- EOF + - id: ABC + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: aaa (type: tracepoint, domain: ust, filter: p == 2) + actions: + notify + - id: BCD + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: gerboise (type: tracepoint, domain: ust, log level <= TRACE_INFO) + actions: + notify + - id: T0 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: * (type: tracepoint, domain: ust) + actions: + notify + - id: T1 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: hello* (type: tracepoint, domain: ust, exclusions: hello2,hello3,hello4) + actions: + notify + - id: T2 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: lemming (type: tracepoint, domain: ust, log level == TRACE_WARNING) + actions: + notify + - id: T3 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: capture-payload-field (type: tracepoint, domain: ust) + captures: + - a + actions: + notify + - id: T4 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: capture-array (type: tracepoint, domain: ust) + captures: + - a[2] + - \$ctx.tourlou[18] + actions: + notify + - id: T5 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: capture-chan-ctx (type: tracepoint, domain: ust) + captures: + - \$ctx.vpid + actions: + notify + - id: T6 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: capture-app-ctx (type: tracepoint, domain: ust) + captures: + - \$app.iga:active_clients + actions: + notify + EOF + + list_triggers "on-event, tracepoint event rule" "${tmp_expected_stdout}" + + stop_lttng_sessiond_notap +} + +test_on_event_probe () +{ + local channel_enable_addr + local channel_disable_addr + + # shellcheck disable=SC2119 + start_lttng_sessiond_notap + + channel_enable_addr=$(grep ' t lttng_channel_enable\s\[lttng_tracer\]$' /proc/kallsyms | cut -f 1 -d ' ') + channel_disable_addr=$(grep ' t lttng_channel_disable\s\[lttng_tracer\]$' /proc/kallsyms | cut -f 1 -d ' ') + + # We need to find a valid offset. + base_symbol="" + offset=0 + if [[ 0x$channel_enable_addr -lt 0x$channel_disable_addr ]]; then + base_symbol="lttng_channel_enable" + offset=$(( 0x$channel_disable_addr - 0x$channel_enable_addr )) + else + base_symbol="lttng_channel_disable" + offset=$(( 0x$channel_enable_addr - 0x$channel_disable_addr )) + fi + + offset_hex="0x$(printf '%x' $offset)" + + add_trigger --condition on-event -k --probe=lttng_channel_enable my_channel_enable --action notify + add_trigger --condition on-event -k --probe="${base_symbol}+${offset_hex}" my_channel_enable --action notify + add_trigger --condition on-event -k --probe="0x${channel_enable_addr}" my_channel_enable --action notify + + cat > "${tmp_expected_stdout}" <<- EOF + - id: T0 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: my_channel_enable (type: probe, location: lttng_channel_enable) + actions: + notify + - id: T1 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: my_channel_enable (type: probe, location: ${base_symbol}+${offset_hex}) + actions: + notify + - id: T2 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: my_channel_enable (type: probe, location: 0x${channel_enable_addr}) + actions: + notify + EOF + + list_triggers "on-event, probe event rule" "${tmp_expected_stdout}" + + stop_lttng_sessiond_notap +} + +test_on_event_userspace_probe () +{ + # shellcheck disable=SC2119 + start_lttng_sessiond_notap + + add_trigger --condition on-event -k --userspace-probe=${uprobe_elf_binary}:test_function ma-probe --action notify + + cat > "${tmp_expected_stdout}" <<- EOF + - id: T0 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: ma-probe (type: userspace probe, location: ${uprobe_elf_binary}:test_function) + actions: + notify + EOF + + list_triggers "on-event, userspace-probe event rule" "${tmp_expected_stdout}" + + stop_lttng_sessiond_notap +} + +test_on_event_syscall () +{ + # shellcheck disable=SC2119 + start_lttng_sessiond_notap + + add_trigger --condition on-event -k --syscall open --action notify + add_trigger --condition on-event -k --syscall ptrace --filter 'a > 2' --action notify + + cat > "${tmp_expected_stdout}" <<- EOF + - id: T0 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + - rule: open (type: syscall) + actions: + notify + - id: T1 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + - rule: ptrace (type: syscall, filter: a > 2) + actions: + notify + EOF + + list_triggers "on-event, syscall event rule" "${tmp_expected_stdout}" + + stop_lttng_sessiond_notap +} + +test_snapshot_action () +{ + start_lttng_sessiond_notap + + add_trigger --condition on-event -u some-event --action snapshot-session ze-session + add_trigger --condition on-event -u some-event --action snapshot-session ze-session /some/path + add_trigger --condition on-event -u some-event --action snapshot-session ze-session file:///some/other/path + add_trigger --condition on-event -u some-event --action snapshot-session ze-session net://1.2.3.4 + add_trigger --condition on-event -u some-event --action snapshot-session ze-session net://1.2.3.4:1234:1235 + add_trigger --condition on-event -u some-event --action snapshot-session ze-session --ctrl-url=tcp://1.2.3.4:1111 --data-url=tcp://1.2.3.4:1112 + add_trigger --condition on-event -u some-event --action snapshot-session ze-session /some/path --max-size=1234 + add_trigger --condition on-event -u some-event --action snapshot-session ze-session /some/path --name=meh + + + cat > "${tmp_expected_stdout}" <<- EOF + - id: T0 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: some-event (type: tracepoint, domain: ust) + actions: + snapshot session \`ze-session\` + - id: T1 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: some-event (type: tracepoint, domain: ust) + actions: + snapshot session \`ze-session\`, path: /some/path + - id: T2 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: some-event (type: tracepoint, domain: ust) + actions: + snapshot session \`ze-session\`, path: /some/other/path + - id: T3 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: some-event (type: tracepoint, domain: ust) + actions: + snapshot session \`ze-session\`, url: net://1.2.3.4 + - id: T4 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: some-event (type: tracepoint, domain: ust) + actions: + snapshot session \`ze-session\`, url: net://1.2.3.4:1234:1235 + - id: T5 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: some-event (type: tracepoint, domain: ust) + actions: + snapshot session \`ze-session\`, control url: tcp://1.2.3.4:1111, data url: tcp://1.2.3.4:1112 + - id: T6 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: some-event (type: tracepoint, domain: ust) + actions: + snapshot session \`ze-session\`, path: /some/path, max size: 1234 + - id: T7 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: some-event (type: tracepoint, domain: ust) + actions: + snapshot session \`ze-session\`, path: /some/path, name: meh + EOF + + list_triggers "snapshot action" "${tmp_expected_stdout}" + + stop_lttng_sessiond_notap +} + +test_top_level_options +test_on_event_tracepoint +skip $ist_root "non-root user: skipping kprobe tests" 6 || test_on_event_probe +skip $ist_root "non-root user: skipping uprobe tests" 4 || test_on_event_userspace_probe +skip $ist_root "non-root user: skipping syscall tests" 5 || test_on_event_syscall +test_snapshot_action + +# Cleanup +rm -f "${tmp_stdout}" +rm -f "${tmp_stderr}" +rm -f "${tmp_expected_stdout}" diff --git a/tests/regression/tools/trigger/test_remove_trigger_cli b/tests/regression/tools/trigger/test_remove_trigger_cli new file mode 100755 index 000000000..2c15561a3 --- /dev/null +++ b/tests/regression/tools/trigger/test_remove_trigger_cli @@ -0,0 +1,119 @@ +#!/bin/bash +# +# Copyright (C) - 2020 EfficiOS, inc +# +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# Test the `lttng remove-trigger` command line interface. + +CURDIR="$(dirname "$0")" +TESTDIR="$CURDIR/../../.." + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" + +plan_tests 17 + +FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}" + +tmp_stdout=$(mktemp -t test_list_triggers_cli_stdout.XXXXXX) +tmp_stderr=$(mktemp -t test_list_triggers_cli_stderr.XXXXXX) +tmp_expected_stdout=$(mktemp -t test_list_triggers_cli_expected_stdout.XXXXXX) + +uid=$(id --user) +gid=$(id --group) + +function add_trigger () +{ + "${FULL_LTTNG_BIN}" add-trigger "$@" + ok $? "add trigger \`$*\`: exit code is 0" +} + +function list_triggers () +{ + local test_name="$1" + local expected_stdout_file="$2" + + "${FULL_LTTNG_BIN}" list-triggers > "${tmp_stdout}" 2> "${tmp_stderr}" + ok $? "${test_name}: exit code is 0" + + diff -u "${expected_stdout_file}" "${tmp_stdout}" + ok $? "${test_name}: expected stdout" + + diff -u /dev/null "${tmp_stderr}" + ok $? "${test_name}: expected stderr" +} + +function remove_trigger () +{ + local id="$1" + local test_name="remove trigger ${id}" + + "${FULL_LTTNG_BIN}" remove-trigger "${id}" > "${tmp_stdout}" 2> "${tmp_stderr}" + ok $? "${test_name}: exit code is 0" + + diff -u <(echo "Removed trigger \`${id}\`.") "${tmp_stdout}" + ok $? "${test_name}: expected stdout" + + diff -u /dev/null "${tmp_stderr}" + ok $? "${test_name}: expected stderr" +} + +# shellcheck disable=SC2119 +start_lttng_sessiond_notap + +# Add a few triggers +add_trigger --condition on-event -u -a --action notify +add_trigger --id ABC --condition on-event aaa -u --filter 'p == 2' --action notify + +cat > "${tmp_expected_stdout}" <<- EOF +- id: ABC + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: aaa (type: tracepoint, domain: ust, filter: p == 2) + actions: + notify +- id: T0 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: * (type: tracepoint, domain: ust) + actions: + notify +EOF +list_triggers "two triggers left" "${tmp_expected_stdout}" + +remove_trigger "ABC" + +cat > "${tmp_expected_stdout}" <<- EOF +- id: T0 + user id: ${uid} + tracer notifications discarded: 0 + condition: event rule hit + rule: * (type: tracepoint, domain: ust) + actions: + notify +EOF +list_triggers "one trigger left" "${tmp_expected_stdout}" + +remove_trigger "T0" + +list_triggers "no triggers left" "/dev/null" + +# Cleanup +stop_lttng_sessiond_notap +rm -f "${tmp_stdout}" +rm -f "${tmp_stderr}" +rm -f "${tmp_expected_stdout}" diff --git a/tests/regression/tools/notification/test_notification_kernel b/tests/regression/tools/trigger/test_trigger_kernel similarity index 72% rename from tests/regression/tools/notification/test_notification_kernel rename to tests/regression/tools/trigger/test_trigger_kernel index 1eb0d1067..cc6fc5816 100755 --- a/tests/regression/tools/notification/test_notification_kernel +++ b/tests/regression/tools/trigger/test_trigger_kernel @@ -1,8 +1,19 @@ #!/bin/bash # -# Copyright (C) 2017 Jonathan Rajotte +# Copyright (C) - 2017 Jonathan Rajotte-Julien # -# SPDX-License-Identifier: LGPL-2.1-only +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA CURDIR=$(dirname $0)/ TESTDIR=$CURDIR/../../../ @@ -12,6 +23,7 @@ TMPDIR=$(mktemp -d) #This is needed since the testpoint create a pipe with the consumerd type suffixed TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n") TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX") +TESTPOIT_ARGS="CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LTTNG_TESTPOINT_ENABLE=1" TESTPOINT=$(readlink -f ${CURDIR}/.libs/libpause_consumer.so) @@ -34,7 +46,7 @@ NUM_TESTS=104 source $TESTDIR/utils/utils.sh -function kernel_event_generator_toggle_state +function kernel_event_generator_toogle_state { kernel_event_generator_suspended=$((kernel_event_generator_suspended==0)) @@ -43,7 +55,7 @@ function kernel_event_generator { state_file=$1 kernel_event_generator_suspended=0 - trap kernel_event_generator_toggle_state SIGUSR1 + trap kernel_event_generator_toogle_state SIGUSR1 while (true); do if [[ $kernel_event_generator_suspended -eq "1" ]]; then diff --git a/tests/regression/tools/notification/test_notification_ust b/tests/regression/tools/trigger/test_trigger_ust similarity index 67% rename from tests/regression/tools/notification/test_notification_ust rename to tests/regression/tools/trigger/test_trigger_ust index 57ae52cbd..82f79a8e6 100755 --- a/tests/regression/tools/notification/test_notification_ust +++ b/tests/regression/tools/trigger/test_trigger_ust @@ -1,8 +1,19 @@ #!/bin/bash # -# Copyright (C) 2017 Jonathan Rajotte +# Copyright (C) - 2017 Jonathan Rajotte-Julien # -# SPDX-License-Identifier: LGPL-2.1-only +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA CURDIR=$(dirname $0)/ TESTDIR=$CURDIR/../../../ @@ -12,6 +23,7 @@ TMPDIR=$(mktemp -d) #This is needed since the testpoint create a pipe with the consumerd type suffixed TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n") TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX") +TESTPOIT_ARGS="CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LTTNG_TESTPOINT_ENABLE=1" TESTPOINT=$(readlink -f ${CURDIR}/.libs/libpause_consumer.so) @@ -34,7 +46,7 @@ DIR=$(readlink -f $TESTDIR) source $TESTDIR/utils/utils.sh -function ust_event_generator_toggle_state +function ust_event_generator_toogle_state { ust_event_generator_suspended=$((ust_event_generator_suspended==0)) @@ -43,7 +55,7 @@ function ust_event_generator { state_file=$1 ust_event_generator_suspended=0 - trap ust_event_generator_toggle_state SIGUSR1 + trap ust_event_generator_toogle_state SIGUSR1 while (true); do if [[ $ust_event_generator_suspended -eq "1" ]]; then @@ -53,7 +65,7 @@ function ust_event_generator if [[ -f $state_file ]]; then rm -rf $state_file 2> /dev/null fi - taskset -c 0 $TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT > /dev/null 2>&1 + taskset -c 0 $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT > /dev/null 2>&1 fi done } diff --git a/tests/regression/tools/trigger/trigger.c b/tests/regression/tools/trigger/trigger.c new file mode 100644 index 000000000..88ef99d48 --- /dev/null +++ b/tests/regression/tools/trigger/trigger.c @@ -0,0 +1,729 @@ +/* + * notification.c + * + * Tests suite for LTTng notification API + * + * Copyright (C) 2017 Jonathan Rajotte + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define NUM_TESTS 104 + +int nb_args = 0; +int named_pipe_args_start = 0; +pid_t app_pid = -1; +const char *app_state_file = NULL; + +static +void wait_on_file(const char *path, bool file_exist) +{ + if (!path) { + return; + } + for (;;) { + int ret; + struct stat buf; + + ret = stat(path, &buf); + if (ret == -1 && errno == ENOENT) { + if (file_exist) { + (void) poll(NULL, 0, 10); /* 10 ms delay */ + continue; /* retry */ + } + break; /* File does not exist */ + } + if (ret) { + perror("stat"); + exit(EXIT_FAILURE); + } + break; /* found */ + } +} + +int write_pipe(const char *path, uint8_t data) +{ + int ret = 0; + int fd = 0; + + fd = open(path, O_WRONLY | O_NONBLOCK); + if (fd < 0) { + perror("Could not open consumer control named pipe"); + goto end; + } + + ret = write(fd, &data , sizeof(data)); + if (ret < 1) { + perror("Named pipe write failed"); + if (close(fd)) { + perror("Named pipe close failed"); + } + ret = -1; + goto end; + } + + ret = close(fd); + if (ret < 0) { + perror("Name pipe closing failed"); + ret = -1; + goto end; + } +end: + return ret; +} + +int stop_consumer(const char **argv) +{ + int ret = 0; + for (int i = named_pipe_args_start; i < nb_args; i++) { + ret = write_pipe(argv[i], 49); + } + return ret; +} + +int resume_consumer(const char **argv) +{ + int ret = 0; + for (int i = named_pipe_args_start; i < nb_args; i++) { + ret = write_pipe(argv[i], 0); + } + return ret; +} + +int suspend_application() +{ + int ret; + struct stat buf; + + if (!stat(app_state_file, &buf)) { + fail("App is already in a suspended state."); + ret = -1; + goto error; + } + + /* + * Send SIGUSR1 to application instructing it to bypass tracepoint. + */ + ret = kill(app_pid, SIGUSR1); + if (ret) { + fail("SIGUSR1 failed. errno %d", errno); + ret = -1; + goto error; + } + + wait_on_file(app_state_file, true); + +error: + return ret; + +} + +int resume_application() +{ + int ret; + struct stat buf; + + ret = stat(app_state_file, &buf); + if (ret == -1 && errno == ENOENT) { + fail("State file does not exist"); + goto error; + } + if (ret) { + perror("stat"); + goto error; + } + + ret = kill(app_pid, SIGUSR1); + if (ret) { + fail("SIGUSR1 failed. errno %d", errno); + ret = -1; + goto error; + } + + wait_on_file(app_state_file, false); + +error: + return ret; + +} + + +void test_triggers_buffer_usage_condition(const char *session_name, + const char *channel_name, + enum lttng_domain_type domain_type, + enum lttng_condition_type condition_type) +{ + enum lttng_condition_status condition_status; + struct lttng_action *action; + + /* Set-up */ + action = lttng_action_notify_create(); + if (!action) { + fail("Setup error on action creation"); + goto end; + } + + /* Test lttng_register_trigger with null value */ + ok(lttng_register_trigger(NULL) == -LTTNG_ERR_INVALID, "Registering a NULL trigger fails as expected"); + + /* Test: register a trigger */ + unsigned int test_vector_size = 5; + for (unsigned int i = 0; i < pow(2,test_vector_size); i++) { + int loop_ret = 0; + char *test_tuple_string = NULL; + unsigned int mask_position = 0; + bool session_name_set = false; + bool channel_name_set = false; + bool threshold_ratio_set = false; + bool threshold_byte_set = false; + bool domain_type_set = false; + + struct lttng_trigger *trigger = NULL; + struct lttng_condition *condition = NULL; + + /* Create base condition */ + switch (condition_type) { + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + condition = lttng_condition_buffer_usage_low_create(); + break; + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + condition = lttng_condition_buffer_usage_high_create(); + break; + default: + loop_ret = 1; + goto loop_end; + } + + if (!condition) { + loop_ret = 1; + goto loop_end; + + } + + /* Prepare the condition for trigger registration test */ + + /* Set session name */ + if ((1 << mask_position) & i) { + condition_status = lttng_condition_buffer_usage_set_session_name( + condition, session_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + loop_ret = 1; + goto loop_end; + } + session_name_set = true; + } + mask_position++; + + /* Set channel name */ + if ((1 << mask_position) & i) { + condition_status = lttng_condition_buffer_usage_set_channel_name( + condition, channel_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + loop_ret = 1; + goto loop_end; + } + channel_name_set = true; + } + mask_position++; + + /* Set threshold ratio */ + if ((1 << mask_position) & i) { + condition_status = lttng_condition_buffer_usage_set_threshold_ratio( + condition, 0.0); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + loop_ret = 1; + goto loop_end; + } + threshold_ratio_set = true; + } + mask_position++; + + /* Set threshold byte */ + if ((1 << mask_position) & i) { + condition_status = lttng_condition_buffer_usage_set_threshold( + condition, 0); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + loop_ret = 1; + goto loop_end; + } + threshold_byte_set = true; + } + mask_position++; + + /* Set domain type */ + if ((1 << mask_position) & i) { + condition_status = lttng_condition_buffer_usage_set_domain_type( + condition, LTTNG_DOMAIN_UST); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + loop_ret = 1; + goto loop_end; + } + domain_type_set = true; + } + + /* Safety check */ + if (mask_position != test_vector_size -1) { + assert("Logic error for test vector generation"); + } + + loop_ret = asprintf(&test_tuple_string, "session name %s, channel name %s, threshold ratio %s, threshold byte %s, domain type %s", + session_name_set ? "set" : "unset", + channel_name_set ? "set" : "unset", + threshold_ratio_set ? "set" : "unset", + threshold_byte_set ? "set" : "unset", + domain_type_set? "set" : "unset"); + if (!test_tuple_string || loop_ret < 0) { + loop_ret = 1; + goto loop_end; + } + + /* Create trigger */ + trigger = lttng_trigger_create(condition, action); + if (!trigger) { + loop_ret = 1; + goto loop_end; + } + + loop_ret = lttng_register_trigger(trigger); + +loop_end: + if (loop_ret == 1) { + fail("Setup error occurred for tuple: %s", test_tuple_string); + goto loop_cleanup; + } + + /* This combination happens three times */ + if (session_name_set && channel_name_set + && (threshold_ratio_set || threshold_byte_set) + && domain_type_set) { + ok(loop_ret == 0, "Trigger is registered: %s", test_tuple_string); + + /* + * Test that a trigger cannot be registered + * multiple time. + */ + loop_ret = lttng_register_trigger(trigger); + ok(loop_ret == -LTTNG_ERR_TRIGGER_EXISTS, "Re-register trigger fails as expected: %s", test_tuple_string); + + /* Test that a trigger can be unregistered */ + loop_ret = lttng_unregister_trigger(trigger); + ok(loop_ret == 0, "Unregister trigger: %s", test_tuple_string); + + /* + * Test that unregistration of a non-previously + * registered trigger fail. + */ + loop_ret = lttng_unregister_trigger(trigger); + ok(loop_ret == -LTTNG_ERR_TRIGGER_NOT_FOUND, "Unregister of a non-registerd trigger fails as expected: %s", test_tuple_string); + } else { + ok(loop_ret == -LTTNG_ERR_INVALID_TRIGGER, "Trigger is invalid as expected and cannot be registered: %s", test_tuple_string); + } + +loop_cleanup: + free(test_tuple_string); + lttng_trigger_destroy(trigger); + lttng_condition_destroy(condition); + } + +end: + lttng_action_destroy(action); +} + +static +void wait_data_pending(const char *session_name) +{ + int ret; + + do { + ret = lttng_data_pending(session_name); + assert(ret >= 0); + } while (ret != 0); +} + +void test_notification_channel(const char *session_name, const char *channel_name, const enum lttng_domain_type domain_type, const char **argv) +{ + int ret = 0; + enum lttng_condition_status condition_status; + enum lttng_notification_channel_status nc_status; + + struct lttng_action *action = NULL; + struct lttng_notification *notification = NULL; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_trigger *trigger = NULL; + + struct lttng_condition *low_condition = NULL; + struct lttng_condition *high_condition = NULL; + struct lttng_condition *dummy_invalid_condition = NULL; + struct lttng_condition *dummy_condition = NULL; + + double low_ratio = 0.0; + double high_ratio = 0.99; + + /* Set-up */ + action = lttng_action_notify_create(); + if (!action) { + fail("Setup error on action creation"); + goto end; + } + + /* Create a dummy, empty condition for later test */ + dummy_invalid_condition = lttng_condition_buffer_usage_low_create(); + if (!dummy_invalid_condition) { + fail("Setup error on condition creation"); + goto end; + } + + /* Create a valid dummy condition with a ratio of 0.5 */ + dummy_condition = lttng_condition_buffer_usage_low_create(); + if (!dummy_condition) { + fail("Setup error on dummy_condition creation"); + goto end; + + } + condition_status = lttng_condition_buffer_usage_set_threshold_ratio( + dummy_condition, 0.5); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on condition creation"); + goto end; + } + + condition_status = lttng_condition_buffer_usage_set_session_name( + dummy_condition, session_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on dummy_condition creation"); + goto end; + } + condition_status = lttng_condition_buffer_usage_set_channel_name( + dummy_condition, channel_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on dummy_condition creation"); + goto end; + } + condition_status = lttng_condition_buffer_usage_set_domain_type( + dummy_condition, domain_type); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on dummy_condition creation"); + goto end; + } + + /* Register a low condition with a ratio */ + low_condition = lttng_condition_buffer_usage_low_create(); + if (!low_condition) { + fail("Setup error on low_condition creation"); + goto end; + } + condition_status = lttng_condition_buffer_usage_set_threshold_ratio( + low_condition, low_ratio); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on low_condition creation"); + goto end; + } + + condition_status = lttng_condition_buffer_usage_set_session_name( + low_condition, session_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on low_condition creation"); + goto end; + } + condition_status = lttng_condition_buffer_usage_set_channel_name( + low_condition, channel_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on low_condition creation"); + goto end; + } + condition_status = lttng_condition_buffer_usage_set_domain_type( + low_condition, domain_type); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on low_condition creation"); + goto end; + + } + + /* Register a high condition with a ratio */ + high_condition = lttng_condition_buffer_usage_high_create(); + if (!high_condition) { + fail("Setup error on high_condition creation"); + goto end; + } + + condition_status = lttng_condition_buffer_usage_set_threshold_ratio( + high_condition, high_ratio); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on high_condition creation"); + goto end; + } + + condition_status = lttng_condition_buffer_usage_set_session_name( + high_condition, session_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on high_condition creation"); + goto end; + } + condition_status = lttng_condition_buffer_usage_set_channel_name( + high_condition, channel_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on high_condition creation"); + goto end; + } + condition_status = lttng_condition_buffer_usage_set_domain_type( + high_condition, domain_type); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on high_condition creation"); + goto end; + } + + /* Register the triggers for low and high condition */ + trigger = lttng_trigger_create(low_condition, action); + if (!trigger) { + fail("Setup error on low trigger creation"); + goto end; + } + + ret = lttng_register_trigger(trigger); + if (ret) { + fail("Setup error on low trigger registration"); + goto end; + } + + lttng_trigger_destroy(trigger); + trigger = NULL; + + trigger = lttng_trigger_create(high_condition, action); + if (!trigger) { + fail("Setup error on high trigger creation"); + goto end; + } + + ret = lttng_register_trigger(trigger); + if (ret) { + fail("Setup error on high trigger registration"); + goto end; + } + + /* Begin testing */ + notification_channel = lttng_notification_channel_create(lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + if (!notification_channel) { + goto end; + } + + /* Basic error path check */ + nc_status = lttng_notification_channel_subscribe(NULL, NULL); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Notification channel subscription is invalid: NULL, NULL"); + + nc_status = lttng_notification_channel_subscribe(notification_channel, NULL); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Notification channel subscription is invalid: NON-NULL, NULL"); + + nc_status = lttng_notification_channel_subscribe(NULL, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Notification channel subscription is invalid: NULL, NON-NULL"); + + nc_status = lttng_notification_channel_subscribe(notification_channel, dummy_invalid_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Subscribing to an invalid condition"); + + nc_status = lttng_notification_channel_unsubscribe(notification_channel, dummy_invalid_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Unsubscribing from an invalid condition"); + + nc_status = lttng_notification_channel_unsubscribe(notification_channel, dummy_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_UNKNOWN_CONDITION, "Unsubscribing from a valid unknown condition"); + + /* Subscribe a valid low condition */ + nc_status = lttng_notification_channel_subscribe(notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Subscribe to condition"); + + /* Subscribe a valid high condition */ + nc_status = lttng_notification_channel_subscribe(notification_channel, high_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Subscribe to condition"); + + nc_status = lttng_notification_channel_subscribe(notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED, "Subscribe to a condition for which subscription was already done"); + + nc_status = lttng_notification_channel_subscribe(notification_channel, high_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED, "Subscribe to a condition for which subscription was already done"); + + /* Wait for notification to happen */ + stop_consumer(argv); + lttng_start_tracing(session_name); + + /* Wait for high notification */ + nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK + && notification + && lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, + "High notification received after intermediary communication"); + lttng_notification_destroy(notification); + notification = NULL; + + suspend_application(); + lttng_stop_tracing_no_wait(session_name); + resume_consumer(argv); + wait_data_pending(session_name); + + /* + * Test that communication still work even if there is notification + * waiting for consumption. + */ + + nc_status = lttng_notification_channel_unsubscribe(notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Unsubscribe with pending notification"); + + nc_status = lttng_notification_channel_subscribe(notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "subscribe with pending notification"); + + nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK + && notification + && lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, + "Low notification received after intermediary communication"); + lttng_notification_destroy(notification); + notification = NULL; + + /* Stop consumer to force a high notification */ + stop_consumer(argv); + resume_application(); + lttng_start_tracing(session_name); + + nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && + lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, + "High notification received after intermediary communication"); + lttng_notification_destroy(notification); + notification = NULL; + + suspend_application(); + lttng_stop_tracing_no_wait(session_name); + resume_consumer(argv); + wait_data_pending(session_name); + + nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && + lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, + "Low notification received after re-subscription"); + lttng_notification_destroy(notification); + notification = NULL; + + stop_consumer(argv); + resume_application(); + /* Stop consumer to force a high notification */ + lttng_start_tracing(session_name); + + nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && + lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, + "High notification"); + lttng_notification_destroy(notification); + notification = NULL; + + /* Resume consumer to allow event consumption */ + suspend_application(); + lttng_stop_tracing_no_wait(session_name); + resume_consumer(argv); + wait_data_pending(session_name); + + nc_status = lttng_notification_channel_unsubscribe(notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Unsubscribe low condition with pending notification"); + nc_status = lttng_notification_channel_unsubscribe(notification_channel, high_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Unsubscribe high condition with pending notification"); + +end: + lttng_notification_channel_destroy(notification_channel); + lttng_trigger_destroy(trigger); + lttng_action_destroy(action); + lttng_condition_destroy(low_condition); + lttng_condition_destroy(high_condition); + lttng_condition_destroy(dummy_invalid_condition); + lttng_condition_destroy(dummy_condition); +} + +int main(int argc, const char *argv[]) +{ + const char *session_name = NULL; + const char *channel_name = NULL; + const char *domain_type_string = NULL; + enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; + + plan_tests(NUM_TESTS); + + /* Argument 6 and upward are named pipe location for consumerd control */ + named_pipe_args_start = 6; + + if (argc < 7) { + fail("Missing parameter for tests to run %d", argc); + goto error; + } + + nb_args = argc; + + domain_type_string = argv[1]; + session_name = argv[2]; + channel_name = argv[3]; + app_pid = (pid_t) atoi(argv[4]); + app_state_file = argv[5]; + + if (!strcmp("LTTNG_DOMAIN_UST", domain_type_string)) { + domain_type = LTTNG_DOMAIN_UST; + } + if (!strcmp("LTTNG_DOMAIN_KERNEL", domain_type_string)) { + domain_type = LTTNG_DOMAIN_KERNEL; + } + if (domain_type == LTTNG_DOMAIN_NONE) { + fail("Unknown domain type"); + goto error; + } + + diag("Test trigger for domain %s with buffer_usage_low condition", domain_type_string); + test_triggers_buffer_usage_condition(session_name, channel_name, domain_type, LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW); + diag("Test trigger for domain %s with buffer_usage_high condition", domain_type_string); + test_triggers_buffer_usage_condition(session_name, channel_name, domain_type, LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH); + + diag("Test notification channel api for domain %s", domain_type_string); + test_notification_channel(session_name, channel_name, domain_type, argv); +error: + return exit_status(); +} + diff --git a/tests/regression/tools/trigger/utils/Makefile.am b/tests/regression/tools/trigger/utils/Makefile.am new file mode 100644 index 000000000..4b716cadb --- /dev/null +++ b/tests/regression/tools/trigger/utils/Makefile.am @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only + +AM_CFLAGS += -I$(srcdir) -I$(top_srcdir)/tests/utils +LIBLTTNG_CTL=$(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la + +noinst_PROGRAMS = notification-client +notification_client_SOURCES = notification-client.c +notification_client_LDADD = $(LIBLTTNG_CTL) \ + $(top_builddir)/tests/utils/libtestutils.la diff --git a/tests/regression/tools/trigger/utils/notification-client.c b/tests/regression/tools/trigger/utils/notification-client.c new file mode 100644 index 000000000..aecc478bd --- /dev/null +++ b/tests/regression/tools/trigger/utils/notification-client.c @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: MIT + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "utils.h" + +static struct option long_options[] = +{ + /* These options set a flag. */ + {"trigger", required_argument, 0, 'i'}, + {"sync-after-notif-register", required_argument, 0, 'a'}, + {0, 0, 0, 0} +}; + +static bool action_group_contains_notify( + const struct lttng_action *action_group) +{ + unsigned int i, count; + enum lttng_action_status status = + lttng_action_group_get_count(action_group, &count); + + if (status != LTTNG_ACTION_STATUS_OK) { + printf("Failed to get action count from action group\n"); + exit(1); + } + + for (i = 0; i < count; i++) { + const struct lttng_action *action = + lttng_action_group_get_at_index( + action_group, i); + const enum lttng_action_type action_type = + lttng_action_get_type(action); + + if (action_type == LTTNG_ACTION_TYPE_NOTIFY) { + return true; + } + } + return false; +} + +static bool is_expected_trigger_name(const char *expected_trigger_name, + struct lttng_notification *notification) +{ + int ret = false; + const struct lttng_evaluation *evaluation = + lttng_notification_get_evaluation(notification); + const enum lttng_condition_type type = + lttng_evaluation_get_type(evaluation); + + switch (type) { + case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + break; + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + { + const char *trigger_name; + enum lttng_evaluation_status evaluation_status; + + evaluation_status = + lttng_evaluation_event_rule_get_trigger_name( + evaluation, &trigger_name); + if (evaluation_status != LTTNG_EVALUATION_STATUS_OK) { + fprintf(stderr, "Failed to get trigger name of event rule notification\n"); + ret = -1; + break; + } + + ret = true; + break; + } + default: + fprintf(stderr, "Unknown notification type (%d)\n", type); + } + + return ret; +} + +int main(int argc, char **argv) +{ + int ret; + int option; + int option_index; + const char *expected_trigger_name = NULL; + struct lttng_triggers *triggers = NULL; + unsigned int count, i, subcription_count = 0; + enum lttng_trigger_status trigger_status; + char *after_notif_register_file_path = NULL; + struct lttng_notification_channel *notification_channel = NULL; + + while ((option = getopt_long(argc, argv, "a:t:", + long_options, &option_index)) != -1) { + switch (option) { + case 'a': + after_notif_register_file_path = strdup(optarg); + break; + case 't': + expected_trigger_name = strdup(optarg); + break; + case '?': + /* getopt_long already printed an error message. */ + default: + ret = -1; + goto end; + } + } + + if (optind != argc) { + ret = -1; + goto end; + } + + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + if (!notification_channel) { + fprintf(stderr, "Failed to create notification channel\n"); + ret = -1; + goto end; + } + + ret = lttng_list_triggers(&triggers); + if (ret != LTTNG_OK) { + fprintf(stderr, "Failed to list triggers\n"); + goto end; + } + + trigger_status = lttng_triggers_get_count(triggers, &count); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + fprintf(stderr, "Failed to get trigger count\n"); + ret = -1; + goto end; + } + + for (i = 0; i < count; i++) { + const struct lttng_trigger *trigger = + lttng_triggers_get_at_index(triggers, i); + const struct lttng_condition *condition = + lttng_trigger_get_const_condition(trigger); + const struct lttng_action *action = + lttng_trigger_get_const_action(trigger); + const enum lttng_action_type action_type = + lttng_action_get_type(action); + enum lttng_notification_channel_status channel_status; + const char *trigger_name = NULL; + + lttng_trigger_get_name(trigger, &trigger_name); + if (strcmp(trigger_name, expected_trigger_name)) { + continue; + } + + if (!((action_type == LTTNG_ACTION_TYPE_GROUP && + action_group_contains_notify(action)) || + action_type == LTTNG_ACTION_TYPE_NOTIFY)) { + /* "The action of trigger is not notify, skipping. */ + continue; + } + + channel_status = lttng_notification_channel_subscribe( + notification_channel, condition); + if (channel_status) { + fprintf(stderr, "Failed to subscribe to notifications of trigger \"%s\"\n", + trigger_name); + ret = -1; + goto end; + } + + subcription_count++; + } + + if (subcription_count == 0) { + printf("No matching trigger with a notify action found.\n"); + ret = 0; + goto end; + } + + + /* + * We registered to the notification of our target trigger. We can now + * create the sync file to signify that we are ready. + */ + ret = create_file(after_notif_register_file_path); + if (ret != 0) { + goto end; + } + + for (;;) { + struct lttng_notification *notification; + enum lttng_notification_channel_status channel_status; + + channel_status = + lttng_notification_channel_get_next_notification( + notification_channel, + ¬ification); + switch (channel_status) { + case LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED: + printf("Dropped notification\n"); + break; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED: + ret = 0; + goto end; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK: + break; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_CLOSED: + printf("Notification channel was closed by peer.\n"); + break; + default: + fprintf(stderr, "A communication error occurred on the notification channel.\n"); + ret = -1; + goto end; + } + + ret = is_expected_trigger_name(expected_trigger_name, + notification); + lttng_notification_destroy(notification); + if (ret) { + ret = 0; + goto end; + } + } +end: + lttng_triggers_destroy(triggers); + lttng_notification_channel_destroy(notification_channel); + return !!ret; +} diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 8dc4748a4..ca41a42f4 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -24,6 +24,7 @@ TESTS = test_kernel_data \ test_fd_tracker \ test_uuid \ test_buffer_view \ + test_event_expr_to_bytecode \ test_payload \ test_unix_socket \ test_kernel_probe @@ -49,6 +50,8 @@ noinst_PROGRAMS = test_uri test_session test_kernel_data \ test_payload \ test_unix_socket \ test_kernel_probe \ + test_condition \ + test_event_expr_to_bytecode \ test_event_rule if HAVE_LIBLTTNG_UST_CTL @@ -63,9 +66,9 @@ test_uri_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBHASHTABLE) $(DL_LIBS) # Sessiond objects SESSIOND_OBJS = $(top_builddir)/src/bin/lttng-sessiond/buffer-registry.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/cmd.$(OBJEXT) \ + $(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) \ @@ -83,6 +86,7 @@ SESSIOND_OBJS = $(top_builddir)/src/bin/lttng-sessiond/buffer-registry.$(OBJEXT) $(top_builddir)/src/bin/lttng-sessiond/notification-thread-events.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/event.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/timer.$(OBJEXT) \ + $(top_builddir)/src/bin/lttng-sessiond/trigger-error-accounting.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/snapshot.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/sessiond-config.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/rotate.$(OBJEXT) \ @@ -198,6 +202,10 @@ test_notification_LDADD = $(LIBTAP) $(LIBLTTNG_CTL) $(DL_LIBS) test_event_rule_SOURCES = test_event_rule.c test_event_rule_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) +# Condition api +test_condition_SOURCES = test_condition.c +test_condition_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) + # relayd backward compat for groou-by-session utilities test_relayd_backward_compat_group_by_session_SOURCES = test_relayd_backward_compat_group_by_session.c test_relayd_backward_compat_group_by_session_LDADD = $(LIBTAP) $(LIBCOMMON) $(RELAYD_OBJS) @@ -226,3 +234,7 @@ test_unix_socket_LDADD = $(LIBTAP) $(LIBSESSIOND_COMM) $(LIBCOMMON) # Kernel probe location api test test_kernel_probe_SOURCES = test_kernel_probe.c test_kernel_probe_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) +# +# Event expression to bytecode test +test_event_expr_to_bytecode_SOURCES = test_event_expr_to_bytecode.c +test_event_expr_to_bytecode_LDADD = $(LIBTAP) $(LIBLTTNG_CTL) $(LIBCOMMON) diff --git a/tests/unit/test_condition.c b/tests/unit/test_condition.c new file mode 100644 index 000000000..a5fcf78c1 --- /dev/null +++ b/tests/unit/test_condition.c @@ -0,0 +1,98 @@ +/* + * test_condition.c + * + * Unit tests for the condition API. + * + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#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 11 + +static +void test_condition_event_rule(void) +{ + int ret, i; + struct lttng_event_rule *tracepoint = NULL; + const struct lttng_event_rule *tracepoint_tmp = NULL; + enum lttng_event_rule_status status; + struct lttng_condition *condition = NULL; + struct lttng_condition *condition_from_buffer = NULL; + enum lttng_condition_status condition_status; + const char *pattern="my_event_*"; + const char *filter="msg_id == 23 && size >= 2048"; + const char *exclusions[] = {"my_event_test1", "my_event_test2" ,"my_event_test3"}; + struct lttng_payload buffer; + + lttng_payload_init(&buffer); + + tracepoint = lttng_event_rule_tracepoint_create(LTTNG_DOMAIN_UST); + ok(tracepoint, "tracepoint UST_DOMAIN"); + + status = lttng_event_rule_tracepoint_set_pattern(tracepoint, pattern); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting pattern"); + + status = lttng_event_rule_tracepoint_set_filter(tracepoint, filter); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting filter"); + + status = lttng_event_rule_tracepoint_set_log_level_range_lower_bound(tracepoint, LTTNG_LOGLEVEL_WARNING); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting range loglevel"); + + for (i=0; i < 3; i++) { + status = lttng_event_rule_tracepoint_add_exclusion(tracepoint, exclusions[i]); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting exclusions"); + } + + condition = lttng_condition_event_rule_create(tracepoint); + ok(condition, "created condition"); + + condition_status = lttng_condition_event_rule_get_rule(condition, &tracepoint_tmp); + ok(condition_status == LTTNG_CONDITION_STATUS_OK, "getting event rule"); + ok(tracepoint == tracepoint_tmp, "Ownership transfer is good"); + + ret = lttng_condition_serialize(condition, &buffer); + ok(ret == 0, "Condition serialized"); + + { + struct lttng_payload_view view = lttng_payload_view_from_payload(&buffer, 0, -1); + (void) lttng_condition_create_from_payload(&view, &condition_from_buffer); + } + ok(condition_from_buffer, "condition from buffer is non null"); + + ok(lttng_condition_is_equal(condition, condition_from_buffer), "serialized and from buffer are equal"); + + lttng_payload_reset(&buffer); + lttng_event_rule_destroy(tracepoint); + lttng_condition_destroy(condition); + lttng_condition_destroy(condition_from_buffer); +} + +int main(int argc, const char *argv[]) +{ + plan_tests(NUM_TESTS); + test_condition_event_rule(); + return exit_status(); +} diff --git a/tests/unit/test_event_expr_to_bytecode.c b/tests/unit/test_event_expr_to_bytecode.c new file mode 100644 index 000000000..dd26e66e6 --- /dev/null +++ b/tests/unit/test_event_expr_to_bytecode.c @@ -0,0 +1,90 @@ +/* + * Copyright 2020 EfficiOS, Inc. + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include + +#define NR_TESTS 4 + +static +void test_event_payload_field(void) +{ + struct lttng_event_expr *event_expr; + struct lttng_bytecode *bytecode = NULL; + int ret; + + event_expr = lttng_event_expr_event_payload_field_create("tourlou"); + ret = lttng_event_expr_to_bytecode(event_expr, &bytecode); + + ok(ret == 0, "event payload field"); + + lttng_event_expr_destroy(event_expr); + free(bytecode); +} + +static +void test_channel_context_field(void) +{ + struct lttng_event_expr *event_expr; + struct lttng_bytecode *bytecode = NULL; + int ret; + + event_expr = lttng_event_expr_channel_context_field_create("tourlou"); + ret = lttng_event_expr_to_bytecode(event_expr, &bytecode); + + ok(ret == 0, "channel context field"); + + lttng_event_expr_destroy(event_expr); + free(bytecode); +} + +static +void test_app_specific_context_field(void) +{ + struct lttng_event_expr *event_expr; + struct lttng_bytecode *bytecode = NULL; + int ret; + + event_expr = lttng_event_expr_app_specific_context_field_create("Bob", "Leponge"); + ret = lttng_event_expr_to_bytecode(event_expr, &bytecode); + + ok(ret == 0, "app-specific context field"); + + lttng_event_expr_destroy(event_expr); + free(bytecode); +} + +static +void test_array_field_element(void) +{ + struct lttng_event_expr *event_expr; + struct lttng_bytecode *bytecode = NULL; + int ret; + + event_expr = lttng_event_expr_event_payload_field_create("allo"); + event_expr = lttng_event_expr_array_field_element_create(event_expr, 168); + ret = lttng_event_expr_to_bytecode(event_expr, &bytecode); + + ok(ret == 0, "array field element"); + + lttng_event_expr_destroy(event_expr); + free(bytecode); +} + +int main(void) +{ + plan_tests(NR_TESTS); + + test_event_payload_field(); + test_channel_context_field(); + test_app_specific_context_field(); + test_array_field_element(); + + return exit_status(); +} diff --git a/tests/utils/testapp/gen-syscall-events/gen-syscall-events.c b/tests/utils/testapp/gen-syscall-events/gen-syscall-events.c index ead6fe387..45390c1cc 100644 --- a/tests/utils/testapp/gen-syscall-events/gen-syscall-events.c +++ b/tests/utils/testapp/gen-syscall-events/gen-syscall-events.c @@ -13,6 +13,41 @@ #include "utils.h" #define MAX_LEN 16 + +static +int open_read_close(const char *path) +{ + int fd, ret; + char buf[MAX_LEN]; + /* + * Start generating syscalls. We use syscall(2) to prevent libc to change + * the underlying syscall. e.g. calling openat(2) instead of open(2). + */ + fd = syscall(SYS_openat, AT_FDCWD, path, O_RDONLY); + if (fd < 0) { + perror("open"); + ret = -1; + goto error; + } + + ret = syscall(SYS_read, fd, buf, MAX_LEN); + if (ret < 0) { + perror("read"); + ret = -1; + goto error; + } + + ret = syscall(SYS_close, fd); + if (ret == -1) { + perror("close"); + ret = -1; + goto error; + } + +error: + return ret; +} + /* * The process waits for the creation of a file passed as argument from an * external processes to execute a syscall and exiting. This is useful for tests @@ -21,18 +56,20 @@ */ int main(int argc, char **argv) { - int fd, ret; - char buf[MAX_LEN]; - char *start_file; + int ret; + const char *start_file, *path1, *path2; - if (argc != 2) { + if (argc != 4) { fprintf(stderr, "Error: Missing argument\n"); + fprintf(stderr, "USAGE: %s PATH_WAIT_FILE PATH1_TO_OPEN PATH2_TO_OPEN\n", argv[0]); fprintf(stderr, "USAGE: %s PATH_WAIT_FILE\n", argv[0]); ret = -1; goto error; } start_file = argv[1]; + path1 = argv[2]; + path2 = argv[3]; /* * Wait for the start_file to be created by an external process @@ -47,23 +84,14 @@ int main(int argc, char **argv) * Start generating syscalls. We use syscall(2) to prevent libc to change * the underlying syscall. e.g. calling openat(2) instead of open(2). */ - fd = syscall(SYS_openat, AT_FDCWD, "/proc/cpuinfo", O_RDONLY); - if (fd < 0) { - perror("open"); - ret = -1; - goto error; - } - - ret = syscall(SYS_read, fd, buf, MAX_LEN); - if (ret < 0) { - perror("read"); + ret = open_read_close(path1); + if (ret == -1) { ret = -1; goto error; } - ret = syscall(SYS_close, fd); + ret = open_read_close(path2); if (ret == -1) { - perror("close"); ret = -1; goto error; } diff --git a/tests/utils/testapp/gen-ust-events/gen-ust-events.c b/tests/utils/testapp/gen-ust-events/gen-ust-events.c index 7ce86bcae..df1e58e41 100644 --- a/tests/utils/testapp/gen-ust-events/gen-ust-events.c +++ b/tests/utils/testapp/gen-ust-events/gen-ust-events.c @@ -48,8 +48,10 @@ int main(int argc, char **argv) int option; long values[] = { 1, 2, 3 }; char text[10] = "test"; + char escape[10] = "\\*"; double dbl = 2.0; float flt = 2222.0; + uint32_t net_values[] = { 1, 2, 3 }; int nr_iter = 100, ret = 0, first_event_file_created = 0; useconds_t nr_usec = 0; char *after_first_event_file_path = NULL; @@ -64,6 +66,10 @@ int main(int argc, char **argv) /* Wait on file before exiting */ char *before_exit_file_path = NULL; + for (i = 0; i < 3; i++) { + net_values[i] = htonl(net_values[i]); + } + while ((option = getopt_long(argc, argv, "i:w:a:b:c:d:", long_options, &option_index)) != -1) { switch (option) { @@ -141,7 +147,7 @@ int main(int argc, char **argv) } netint = htonl(i); tracepoint(tp, tptest, i, netint, values, text, - strlen(text), dbl, flt); + strlen(text), escape, net_values, dbl, flt); /* * First loop we create the file if asked to indicate diff --git a/tests/utils/testapp/gen-ust-events/tp.h b/tests/utils/testapp/gen-ust-events/tp.h index bc1949417..132f9ba4e 100644 --- a/tests/utils/testapp/gen-ust-events/tp.h +++ b/tests/utils/testapp/gen-ust-events/tp.h @@ -12,24 +12,51 @@ #define _TRACEPOINT_TP_H #include +#include + +TRACEPOINT_ENUM( + tp, tptest_enum, + TP_ENUM_VALUES( + ctf_enum_auto("AUTO: EXPECT 0") + ctf_enum_value("VALUE: 23", 23) + ctf_enum_value("VALUE: 27", 27) + ctf_enum_auto("AUTO: EXPECT 28") + ctf_enum_range("RANGE: 101 TO 303", 101, 303) + ctf_enum_auto("AUTO: EXPECT 304") + ctf_enum_value("VALUE: -1", -1) + ) +) TRACEPOINT_EVENT(tp, tptest, TP_ARGS(int, anint, int, netint, long *, values, char *, text, size_t, textlen, + char *, etext, uint32_t * , net_values, double, doublearg, float, floatarg), TP_FIELDS( ctf_integer(int, intfield, anint) ctf_integer_hex(int, intfield2, anint) ctf_integer(long, longfield, anint) + ctf_integer(int, signedfield, -1) ctf_integer_network(int, netintfield, netint) ctf_integer_network_hex(int, netintfieldhex, netint) ctf_array(long, arrfield1, values, 3) ctf_array_text(char, arrfield2, text, 10) + ctf_array_network(uint32_t, arrfield3, net_values, 3) ctf_sequence(char, seqfield1, text, size_t, textlen) ctf_sequence_text(char, seqfield2, text, size_t, textlen) + ctf_sequence_network(uint32_t, seqfield3, net_values, size_t, 3) + ctf_sequence(long, seqfield4, values, size_t, 3) ctf_string(stringfield, text) + ctf_string(stringfield2, etext) ctf_float(float, floatfield, floatarg) ctf_float(double, doublefield, doublearg) + ctf_enum(tp, tptest_enum, int, enum0, 0) + ctf_enum(tp, tptest_enum, int, enum23, 23) + ctf_enum(tp, tptest_enum, int, enum27, 27) + ctf_enum(tp, tptest_enum, int, enum28, 28) + ctf_enum(tp, tptest_enum, int, enum202, 202) + ctf_enum(tp, tptest_enum, int, enum304, 304) + ctf_enum(tp, tptest_enum, int, enumnegative, -1) ) ) -- 2.34.1