From: Jérémie Galarneau Date: Mon, 13 May 2024 18:41:41 +0000 (-0400) Subject: .gitignore: ignore local vscode workspace settings file X-Git-Url: http://git.liburcu.org/?p=lttng-tools.git;a=commitdiff_plain;h=HEAD;hp=e793ddc8acc7f978bde7a991644cc6749ac1bcc9 .gitignore: ignore local vscode workspace settings file Signed-off-by: Jérémie Galarneau Change-Id: I518d85077566ab341acb4644d27132ade79b5749 --- diff --git a/.clang-format b/.clang-format index 28ea69873..8fcee64f8 100644 --- a/.clang-format +++ b/.clang-format @@ -35,7 +35,6 @@ BraceWrapping: SplitEmptyFunction: true BreakBeforeBinaryOperators: None BreakBeforeTernaryOperators: false -BreakConstructorInitializers: AfterColon BreakStringLiterals: false ColumnLimit: 100 ConstructorInitializerAllOnOneLineOrOnePerLine: true diff --git a/.gitignore b/.gitignore index 4c5a4e5e9..66e76696f 100644 --- a/.gitignore +++ b/.gitignore @@ -47,7 +47,7 @@ TAGS /.clangd/ compile_commands.json *_flymake* -/.vscode/* +/.vscode/settings.json # m4 macros not automatically generated /m4/libtool.m4 diff --git a/.vscode/build.sh b/.vscode/build.sh new file mode 100755 index 000000000..9a4e3625a --- /dev/null +++ b/.vscode/build.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# Copyright (C) 2024 Jérémie Galarneau +# +# SPDX-License-Identifier: LGPL-2.1-only +# + +source_dir="$1" + +# Run make quietly to check if a Makefile exists +make_output=$(make -C "$source_dir" -q 2>&1) +make_exit_status=$? + +# Check the return status of make -q +if [ $make_exit_status -eq 2 ]; then + # It seems the Makefiles don't exist. Most likely the user forgot to + # setup their tree. + echo "$make_output" + echo -e "\033[33mMake couldn't find a Makefile: did you run ./bootstrap and ./configure ?\033[0m" + exit 1 +fi + +# Check if compile_commands.json does not exist in the source directory and if bear is installed +if [ ! -f "$source_dir/compile_commands.json" ] && which bear >/dev/null 2>&1; then + # Bear is installed and compile_commands.json is not present + # Perform a make clean since compile_commands.json is missing and bear is installed + make -C "$source_dir" clean + + # Prefix bear to the make command + command_prefix="bear -- " +fi + +# Run make with or without bear prefix, depending on the condition above +eval "${command_prefix}"make -C "$source_dir" -j "$(nproc)" diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..4541842e1 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,76 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug LTTng Client", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/src/bin/lttng/.libs/lttng", + // Replace with your args + "args": [ + "help" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "${workspaceFolder}/.vscode/libtool_gdb_wrapper.sh", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "Build LTTng-tools" + }, + { + "name": "Debug LTTng Session Daemon", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/src/bin/lttng-sessiond/.libs/lttng-sessiond", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + // The session daemon fails to launch if it can't find the session schema description + "environment": [ + { + "name": "LTTNG_SESSION_CONFIG_XSD_PATH", + "value": "${workspaceFolder}/src/common/" + } + ], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "${workspaceFolder}/.vscode/libtool_gdb_wrapper.sh", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "Build LTTng-tools" + }, + { + "name": "Debug LTTng Relay Daemon", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/src/bin/lttng-relayd/lttng-relayd", + "args": [], + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "${workspaceFolder}/.vscode/libtool_gdb_wrapper.sh", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "Build LTTng-tools" + }, + ] +} \ No newline at end of file diff --git a/.vscode/libtool_gdb_wrapper.sh b/.vscode/libtool_gdb_wrapper.sh new file mode 100755 index 000000000..0f60c832d --- /dev/null +++ b/.vscode/libtool_gdb_wrapper.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env sh +# Copyright (C) 2024 Jérémie Galarneau +# +# SPDX-License-Identifier: LGPL-2.1-only +# +# Wrapper script to setup the environment before invoking gdb +# on the in-tree binaries (under `.libs`) + +libtool --mode=execute gdb "$@" diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..9f28bc573 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,21 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "shell", + "label": "Build LTTng-tools", + // Assumes you ran ./bootstrap and ./configure with your preferred options + "command": "${workspaceFolder}/.vscode/build.sh ${workspaceFolder}", + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} \ No newline at end of file diff --git a/src/bin/lttng-relayd/sessiond-trace-chunks.cpp b/src/bin/lttng-relayd/sessiond-trace-chunks.cpp index 9d793c9bc..2326878fb 100644 --- a/src/bin/lttng-relayd/sessiond-trace-chunks.cpp +++ b/src/bin/lttng-relayd/sessiond-trace-chunks.cpp @@ -78,8 +78,8 @@ struct trace_chunk_registry_ht_element { static unsigned long trace_chunk_registry_ht_key_hash(const struct trace_chunk_registry_ht_key *key) { - const uint64_t uuid_h1 = *reinterpret_cast(&key->sessiond_uuid[0]); - const uint64_t uuid_h2 = *reinterpret_cast(&key->sessiond_uuid[1]); + const uint64_t uuid_h1 = reinterpret_cast(key->sessiond_uuid.data())[0]; + const uint64_t uuid_h2 = reinterpret_cast(key->sessiond_uuid.data())[1]; return hash_key_u64(&uuid_h1, lttng_ht_seed) ^ hash_key_u64(&uuid_h2, lttng_ht_seed); } diff --git a/src/bin/lttng-sessiond/ust-app.cpp b/src/bin/lttng-sessiond/ust-app.cpp index 12250edfd..d993b7b44 100644 --- a/src/bin/lttng-sessiond/ust-app.cpp +++ b/src/bin/lttng-sessiond/ust-app.cpp @@ -6025,8 +6025,8 @@ static void ust_app_synchronize_event_notifier_rules(struct ust_app *app) } for (i = 0; i < count; i++) { - struct lttng_condition *condition; - struct lttng_event_rule *event_rule; + const struct lttng_condition *condition; + const struct lttng_event_rule *event_rule; struct lttng_trigger *trigger; const struct ust_app_event_notifier_rule *looked_up_event_notifier_rule; enum lttng_condition_status condition_status; @@ -6036,7 +6036,7 @@ static void ust_app_synchronize_event_notifier_rules(struct ust_app *app) LTTNG_ASSERT(trigger); token = lttng_trigger_get_tracer_token(trigger); - condition = lttng_trigger_get_condition(trigger); + condition = lttng_trigger_get_const_condition(trigger); if (lttng_condition_get_type(condition) != LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES) { @@ -6044,8 +6044,8 @@ static void ust_app_synchronize_event_notifier_rules(struct ust_app *app) continue; } - condition_status = lttng_condition_event_rule_matches_borrow_rule_mutable( - condition, &event_rule); + condition_status = + lttng_condition_event_rule_matches_get_rule(condition, &event_rule); LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); if (lttng_event_rule_get_domain_type(event_rule) == LTTNG_DOMAIN_KERNEL) { diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 34fd83452..8170e6e5a 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -115,6 +115,7 @@ libcommon_lgpl_la_SOURCES = \ time.cpp \ tracker.cpp tracker.hpp \ trigger.cpp \ + type-traits.hpp \ unix.cpp unix.hpp \ uri.cpp uri.hpp \ userspace-probe.cpp \ @@ -432,6 +433,7 @@ endif # libstring-utils noinst_LTLIBRARIES += libstring-utils.la libstring_utils_la_SOURCES = \ + string-utils/c-string-view.hpp \ string-utils/format.hpp \ string-utils/string-utils.cpp \ string-utils/string-utils.hpp diff --git a/src/common/consumer/consumer.hpp b/src/common/consumer/consumer.hpp index 310969172..851b3e2b8 100644 --- a/src/common/consumer/consumer.hpp +++ b/src/common/consumer/consumer.hpp @@ -922,7 +922,7 @@ void lttng_consumer_set_command_sock_path(struct lttng_consumer_local_data *ctx, * on error. */ int lttng_consumer_send_error(struct lttng_consumer_local_data *ctx, - enum lttcomm_return_code error_code); + enum lttcomm_return_code error_code); /* * Called from signal handler to ensure a clean exit. diff --git a/src/common/event-rule/kernel-syscall.cpp b/src/common/event-rule/kernel-syscall.cpp index 23a432aac..933c3543f 100644 --- a/src/common/event-rule/kernel-syscall.cpp +++ b/src/common/event-rule/kernel-syscall.cpp @@ -133,6 +133,10 @@ static bool lttng_event_rule_kernel_syscall_is_equal(const struct lttng_event_ru goto end; } + if (a->emission_site != b->emission_site) { + goto end; + } + is_equal = true; end: return is_equal; diff --git a/src/common/string-utils/c-string-view.hpp b/src/common/string-utils/c-string-view.hpp new file mode 100644 index 000000000..6b175ea33 --- /dev/null +++ b/src/common/string-utils/c-string-view.hpp @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2023 Philippe Proulx + * + * SPDX-License-Identifier: MIT + */ + +#ifndef LTTNG_C_STRING_VIEW_HPP +#define LTTNG_C_STRING_VIEW_HPP + +#include +#include + +#include +#include +#include +#include + +namespace lttng { + +/* + * A view on a constant null-terminated C string. + */ +class c_string_view final { +public: + /* + * Builds an empty view (data() returns `nullptr`). + * + * Intentionally not explicit. + */ + constexpr c_string_view() noexcept = default; + + /* + * Builds a view of the C string `str` (may be `nullptr`). + * + * Intentionally not explicit. + */ + /* NOLINTBEGIN(google-explicit-constructor) */ + constexpr c_string_view(const char *const str) noexcept : _str{ str } + { + } + /* NOLINTEND(google-explicit-constructor) */ + + /* + * Builds a view of the string `str`. + */ + /* NOLINTBEGIN(google-explicit-constructor) */ + c_string_view(const std::string& str) noexcept : _str{ str.c_str() } + { + } + /* NOLINTEND */ + + /* + * Makes this view view the C string `str` (may be `nullptr`). + */ + c_string_view& operator=(const char *const str) noexcept + { + _str = str; + return *this; + } + + /* + * Viewed null-terminated C string (may be `nullptr`). + */ + const char *data() const noexcept + { + return _str; + } + + /* + * Alias of data(). + */ + operator const char *() const noexcept /* NOLINT(google-explicit-constructor) */ + { + return this->data(); + } + + /* + * Evaluate as boolean (false means an empty string). + */ + operator bool() const noexcept /* NOLINT(google-explicit-constructor) */ + { + return *this->data(); + } + + /* + * Alias of data(). + */ + const char *operator*() const noexcept + { + return this->data(); + } + + /* + * Alias of data(). + * + * data() must not return `nullptr`. + */ + const char *begin() const noexcept + { + return this->data(); + } + + /* + * Pointer to the null character of the viewed C string. + * + * data() must not return `nullptr`. + */ + const char *end() const noexcept + { + return _str + this->len(); + } + + /* + * Length of the viewed C string, excluding the null character. + * + * data() must not return `nullptr`. + */ + std::size_t len() const noexcept + { + return std::strlen(_str); + } + + /* + * Returns an `std::string` instance containing a copy of the viewed + * C string. + * + * data() must not return `nullptr`. + */ + std::string str() const + { + return std::string{ _str }; + } + + /* + * Alias of str(). + */ + operator std::string() const /* NOLINT(google-explicit-constructor) */ + { + return this->str(); + } + + /* + * Returns the character at index `i`. + * + * `i` must be less than what len() returns. + * + * data() must not return `nullptr`. + */ + char operator[](const std::size_t i) const noexcept + { + return _str[i]; + } + + bool startsWith(const lttng::c_string_view prefix) const noexcept + { + return std::strncmp(_str, (const char *) prefix, prefix.len()) == 0; + } + +private: + const char *_str = nullptr; +}; + +inline const char *format_as(const c_string_view& str) +{ + return str ? *str : "(null)"; +} + +namespace internal { + +template +const char *as_const_char_ptr(StrT&& val) noexcept +{ + return val.data(); +} + +inline const char *as_const_char_ptr(const char *const val) noexcept +{ + return val; +} + +template +using comparable_with_c_string_view = lttng::traits:: + is_one_of::type, c_string_view, std::string, const char *>; + +} /* namespace internal */ + +/* + * Returns true if `lhs` is equal to `rhs`. + * + * `LhsT` and `RhsT` may be any of: + * + * • `const char *` + * • `std::string` + * • `c_string_view` + * + * Both `lhs` and `rhs` must not have an underlying `nullptr` raw data. + */ +template < + typename LhsT, + typename RhsT, + typename = + typename std::enable_if::value>::type, + typename = + typename std::enable_if::value>::type> +bool operator==(LhsT&& lhs, RhsT&& rhs) noexcept +{ + const auto raw_lhs = internal::as_const_char_ptr(lhs); + const auto raw_rhs = internal::as_const_char_ptr(rhs); + + return std::strcmp(raw_lhs, raw_rhs) == 0; +} + +/* + * Returns true if `lhs` is not equal to `rhs`. + * + * `LhsT` and `RhsT` may be any of: + * + * • `const char *` + * • `std::string` + * • `c_string_view` + * + * Both `lhs` and `rhs` must not have an underlying `nullptr` raw data. + */ +template < + typename LhsT, + typename RhsT, + typename = + typename std::enable_if::value>::type, + typename = + typename std::enable_if::value>::type> +bool operator!=(LhsT&& lhs, RhsT&& rhs) noexcept +{ + return !(std::forward(lhs) == std::forward(rhs)); +} + +} /* namespace lttng */ + +/* + * Appends `rhs` to `lhs`. + */ +inline void operator+=(std::string& lhs, lttng::c_string_view rhs) +{ + lhs += rhs.data(); +} + +namespace std { +template <> +struct hash { + std::size_t operator()(const lttng::c_string_view& str) const + { + auto hash_value = std::hash{}('\0'); + + for (auto character : str) { + hash_value ^= std::hash{}(character); + } + + return hash_value; + } +}; +} /* namespace std */ + +#endif /* LTTNG_C_STRING_VIEW_HPP */ diff --git a/src/common/type-traits.hpp b/src/common/type-traits.hpp new file mode 100644 index 000000000..3801bf13f --- /dev/null +++ b/src/common/type-traits.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 Philippe Proulx + * + * SPDX-License-Identifier: MIT + */ + +#ifndef LTTNG_TYPE_TRAITS_HPP +#define LTTNG_TYPE_TRAITS_HPP + +#include + +namespace lttng { +namespace traits { + +/* + * Provides the member constant `value` equal to: + * + * `T` is in the list of types `Ts`: + * `true` + * + * Otherwise: + * `false` + */ +template +struct is_one_of : std::false_type { +}; + +template +struct is_one_of : std::true_type { +}; + +template +struct is_one_of : is_one_of { +}; +} /* namespace traits */ +} /* namespace lttng */ + +#endif /* LTTNG_TYPE_TRAITS_HPP */ diff --git a/tests/regression/Makefile.am b/tests/regression/Makefile.am index ca3fb2d52..da5cb1b57 100644 --- a/tests/regression/Makefile.am +++ b/tests/regression/Makefile.am @@ -16,6 +16,7 @@ TESTS = tools/base-path/test_ust \ tools/health/test_thread_ok \ tools/live/test_kernel \ tools/live/test_lttng_kernel \ + tools/live/test_per_application_leaks.py \ tools/live/test_ust \ tools/live/test_ust_tracefile_count \ tools/live/test_lttng_ust \ diff --git a/tests/regression/tools/live/Makefile.am b/tests/regression/tools/live/Makefile.am index 494b982df..3ad9b7ba3 100644 --- a/tests/regression/tools/live/Makefile.am +++ b/tests/regression/tools/live/Makefile.am @@ -6,7 +6,7 @@ LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la LIBLTTNG_SESSIOND_COMMON=$(top_builddir)/src/bin/lttng-sessiond/liblttng-sessiond-common.la noinst_PROGRAMS = live_test -EXTRA_DIST = test_kernel test_lttng_kernel +EXTRA_DIST = test_kernel test_lttng_kernel test_per_application_leaks.py if HAVE_LIBLTTNG_UST_CTL EXTRA_DIST += test_ust test_ust_tracefile_count test_lttng_ust diff --git a/tests/regression/tools/live/test_per_application_leaks.py b/tests/regression/tools/live/test_per_application_leaks.py new file mode 100755 index 000000000..1d12049ed --- /dev/null +++ b/tests/regression/tools/live/test_per_application_leaks.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +# +# SPDX-FileCyoprightText: Kienan Stewart +# SPDX-License-Identifier: GPL-2.0-only + +""" +Test that the consumerd doesn't leak file descriptor allocations in /dev/shm +when the relayd exits before instrumented applications start. + +@see https://bugs.lttng.org/issues/1411 +""" + +import os +import pathlib +import subprocess +import sys + +test_utils_import_path = pathlib.Path(__file__).absolute().parents[3] / "utils" +sys.path.append(str(test_utils_import_path)) + +import lttngtest + + +def get_consumerd_pid(tap, parent, match_string): + pid = 0 + try: + process = subprocess.Popen( + ["pgrep", "-P", str(parent), "-f", match_string], + stdout=subprocess.PIPE, + ) + process.wait() + output = str(process.stdout.read(), encoding="UTF-8").splitlines() + if len(output) != 1: + raise Exception( + "Unexpected number of output lines (got {}): {}".format( + len(output), output + ) + ) + pid = int(output[0]) + except Exception as e: + tap.diagnostic( + "Failed to find child process of '{}' matching '{}': '{}'".format( + parent, match_string, str(e) + ) + ) + return pid + + +def count_process_dev_shm_fds(pid): + count = 0 + if pid == 0: + return count + dir = os.path.join("/proc", str(pid), "fd") + for root, dirs, files in os.walk(dir): + for f in files: + filename = pathlib.Path(os.path.join(root, f)) + try: + if filename.is_symlink() and str(filename.resolve()).startswith( + "/dev/shm/shm-ust-consumer" + ): + count += 1 + except FileNotFoundError: + # As we're walking /proc/XX/fd/, fds may be added or removed + continue + return count + + +def count_dev_shm_fds(tap, test_env): + consumer32_pid = get_consumerd_pid(tap, test_env._sessiond.pid, "ustconsumerd32") + fds_consumerd32 = count_process_dev_shm_fds(consumer32_pid) + consumer64_pid = get_consumerd_pid(tap, test_env._sessiond.pid, "ustconsumerd64") + fds_consumerd64 = count_process_dev_shm_fds(consumer64_pid) + return (fds_consumerd32, fds_consumerd64) + + +def test_fd_leak(tap, test_env, buffer_sharing_policy, kill_relayd=True): + tap.diagnostic( + "test_fd_leak with buffer sharing policy {}, kill relayd: {}".format( + buffer_sharing_policy, kill_relayd + ) + ) + client = lttngtest.LTTngClient(test_env, log=tap.diagnostic) + output = lttngtest.NetworkSessionOutputLocation( + "net://localhost:{}:{}/".format( + test_env.lttng_relayd_control_port, test_env.lttng_relayd_data_port + ) + ) + + session = client.create_session(output=output, live=True) + channel = session.add_channel( + lttngtest.lttngctl.TracingDomain.User, + buffer_sharing_policy=buffer_sharing_policy, + ) + channel.add_recording_rule(lttngtest.lttngctl.UserTracepointEventRule()) + session.start() + + count_post_start = count_dev_shm_fds(tap, test_env) + + # Kill the relayd + if kill_relayd: + test_env._terminate_relayd() + + test_env.launch_wait_trace_test_application(10) + count_post_app1 = count_dev_shm_fds(tap, test_env) + + test_env.launch_wait_trace_test_application(10) + count_post_app2 = count_dev_shm_fds(tap, test_env) + + test_env.launch_wait_trace_test_application(10) + count_post_app3 = count_dev_shm_fds(tap, test_env) + + session.stop() + session.destroy() + + count_post_destroy = count_dev_shm_fds(tap, test_env) + + tap.diagnostic( + "FD counts post-start: {}, post-destroy: {}".format( + count_post_start, count_post_destroy + ) + ) + tap.test( + count_post_start == count_post_destroy, + "Count of consumerd FDs in /dev/shm are equal after session start then after destroy", + ) + + tap.diagnostic( + "FD counts post-app-1: {}, post-app-2: {}, post-app-3: {}".format( + count_post_app1, count_post_app2, count_post_app3 + ) + ) + if buffer_sharing_policy == lttngtest.lttngctl.BufferSharingPolicy.PerUID: + tap.test( + (count_post_app1 == count_post_app2) + and (count_post_app2 == count_post_app3), + "Count of consumerd FDs in /dev/shm doesn't leak over several application invocations", + ) + else: + tap.skip( + "Count of consumerds FDs in /dev/shm doesn't leak over several application invocations - no mechanism is available to guarantee buffer reclamation within a given time frame" + ) + + +tap = lttngtest.TapGenerator(8) +for kill_relayd in [True, False]: + for buffer_sharing_policy in [ + lttngtest.lttngctl.BufferSharingPolicy.PerUID, + lttngtest.lttngctl.BufferSharingPolicy.PerPID, + ]: + with lttngtest.test_environment( + log=tap.diagnostic, with_relayd=True, with_sessiond=True + ) as test_env: + test_fd_leak(tap, test_env, buffer_sharing_policy, kill_relayd) + +sys.exit(0 if tap.is_successful else 1) diff --git a/tests/utils/lttngtest/environment.py b/tests/utils/lttngtest/environment.py index 74dcfb8d2..73d18874b 100644 --- a/tests/utils/lttngtest/environment.py +++ b/tests/utils/lttngtest/environment.py @@ -618,14 +618,20 @@ class _Environment(logger._Logger): Launch an application that will trace from within constructors. """ return _TraceTestApplication( - self._project_root - / "tests" - / "utils" - / "testapp" - / subpath, + self._project_root / "tests" / "utils" / "testapp" / subpath, self, ) + def _terminate_relayd(self): + if self._relayd and self._relayd.poll() is None: + self._relayd.terminate() + self._relayd.wait() + if self._relayd_output_consumer: + self._relayd_output_consumer.join() + self._relayd_output_consumer = None + self._log("Relayd killed") + self._relayd = None + # Clean-up managed processes def _cleanup(self): # type: () -> None @@ -646,14 +652,7 @@ class _Environment(logger._Logger): self._log("Session daemon killed") self._sessiond = None - if self._relayd and self._relayd.poll() is None: - self._relayd.terminate() - self._relayd.wait() - if self._relayd_output_consumer: - self._relayd_output_consumer.join() - self._relayd_output_consumer = None - self._log("Relayd killed") - self._relayd = None + self._terminate_relayd() self._lttng_home = None diff --git a/tests/utils/xml-utils/Makefile.am b/tests/utils/xml-utils/Makefile.am index 7997d94e1..5890eef27 100644 --- a/tests/utils/xml-utils/Makefile.am +++ b/tests/utils/xml-utils/Makefile.am @@ -1,5 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only +EXTRA_DIST = common.hpp + noinst_PROGRAMS = validate_xml extract_xml pretty_xml validate_xml_SOURCES = validate_xml.cpp validate_xml_CPPFLAGS = $(libxml2_CFLAGS) $(AM_CPPFLAGS) @@ -9,7 +11,7 @@ extract_xml_SOURCES = extract_xml.cpp extract_xml_CPPFLAGS = $(libxml2_CFLAGS) $(AM_CPPFLAGS) extract_xml_LDADD = $(libxml2_LIBS) -pretty_xml_SOURCES = pretty_xml.c +pretty_xml_SOURCES = pretty_xml.cpp pretty_xml_CPPFLAGS = $(libxml2_CFLAGS) $(AM_CPPFLAGS) pretty_xml_LDADD = $(libxml2_LIBS) diff --git a/tests/utils/xml-utils/common.hpp b/tests/utils/xml-utils/common.hpp new file mode 100644 index 000000000..01a9b5207 --- /dev/null +++ b/tests/utils/xml-utils/common.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 EfficiOS Inc. + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef TESTS_UTILS_XML_UTILS_COMMON_HPP +#define TESTS_UTILS_XML_UTILS_COMMON_HPP + +#include "common/make-unique-wrapper.hpp" + +#include +#include + +namespace lttng { +namespace libxml { + +using parser_ctx_uptr = std::unique_ptr< + xmlParserCtxt, + lttng::memory::create_deleter_class::deleter>; +using doc_uptr = + std::unique_ptr::deleter>; + +/* + * Manage the global parser context of libxml2. + * There should only be one instance of this class per process. + */ +class global_parser_context { +public: + global_parser_context() + { + xmlInitParser(); + } + + ~global_parser_context() + { + xmlCleanupParser(); + } + + /* Deactivate copy and assignment. */ + global_parser_context(const global_parser_context&) = delete; + global_parser_context(global_parser_context&&) = delete; + global_parser_context& operator=(const global_parser_context&) = delete; + global_parser_context& operator=(global_parser_context&&) = delete; +}; +} /* namespace libxml */ +} /* namespace lttng */ +#endif /* TESTS_UTILS_XML_UTILS_COMMON_HPP */ diff --git a/tests/utils/xml-utils/extract_xml.cpp b/tests/utils/xml-utils/extract_xml.cpp index 3995dde48..280f2ed02 100644 --- a/tests/utils/xml-utils/extract_xml.cpp +++ b/tests/utils/xml-utils/extract_xml.cpp @@ -24,6 +24,8 @@ * node;b; * node;c; */ +#include "common.hpp" + #include #include @@ -36,6 +38,8 @@ #include #include +namespace ll = lttng::libxml; + #if defined(LIBXML_XPATH_ENABLED) static int opt_verbose; @@ -176,8 +180,15 @@ static int extract_xpath(const char *xml_path, const xmlChar *xpath) LTTNG_ASSERT(xml_path); LTTNG_ASSERT(xpath); + ll::parser_ctx_uptr parserCtx{ xmlNewParserCtxt() }; + + if (!parserCtx) { + fprintf(stderr, "ERR: could not allocate an XML parser context\n"); + return -1; + } + /* Parse the xml file */ - doc = xmlParseFile(xml_path); + doc = xmlCtxtReadFile(parserCtx.get(), xml_path, nullptr, XML_PARSE_NOBLANKS); if (!doc) { fprintf(stderr, "ERR parsing: xml file invalid \"%s\"\n", xml_path); return -1; @@ -253,7 +264,6 @@ int main(int argc, char **argv) /* Init libxml */ xmlInitParser(); - xmlKeepBlanksDefault(0); if (access(argv[optind], F_OK)) { fprintf(stderr, "ERR:%s\n", "Xml path not valid"); return -1; diff --git a/tests/utils/xml-utils/pretty_xml.c b/tests/utils/xml-utils/pretty_xml.c deleted file mode 100644 index 3f296f023..000000000 --- a/tests/utils/xml-utils/pretty_xml.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2021 EfficiOS Inc. - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -/* - * Prettyfi a xml input from stdin to stddout. - * This allows a more human friendly format for xml testing when problems occur. - */ - -#include - -int main(void) -{ - xmlDocPtr doc = NULL; - - /* Init libxml. */ - xmlInitParser(); - xmlKeepBlanksDefault(0); - - /* Parse the XML document from stdin. */ - doc = xmlParseFile("-"); - if (!doc) { - fprintf(stderr, "ERR parsing: xml input invalid"); - return -1; - } - - xmlDocFormatDump(stdout, doc, 1); - - xmlFreeDoc(doc); - /* Shutdown libxml. */ - xmlCleanupParser(); - - return 0; -} diff --git a/tests/utils/xml-utils/pretty_xml.cpp b/tests/utils/xml-utils/pretty_xml.cpp new file mode 100644 index 000000000..8a6e967a7 --- /dev/null +++ b/tests/utils/xml-utils/pretty_xml.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 EfficiOS Inc. + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +/* + * Prettyfi a xml input from stdin to stddout. + * This allows a more human friendly format for xml testing when problems occur. + */ + +#include "common.hpp" + +#include + +#include +#include +#include + +namespace ll = lttng::libxml; + +int main() +{ + const ll::global_parser_context global_parser_context; + const ll::parser_ctx_uptr parserCtx{ xmlNewParserCtxt() }; + + /* Parse the XML document from stdin. */ + const ll::doc_uptr doc{ xmlCtxtReadFd( + parserCtx.get(), STDIN_FILENO, nullptr, nullptr, XML_PARSE_NOBLANKS) }; + if (!doc) { + std::cerr << "Error: invalid XML input on stdin\n"; + return -1; + } + + xmlDocFormatDump(stdout, doc.get(), 1); + + return 0; +}