/.clangd/
compile_commands.json
*_flymake*
-/.vscode/*
# m4 macros not automatically generated
/m4/libtool.m4
--- /dev/null
+#!/usr/bin/env bash
+# Copyright (C) 2024 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+#
+# 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)"
--- /dev/null
+{
+ "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
--- /dev/null
+#!/usr/bin/env sh
+# Copyright (C) 2024 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+#
+# 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 "$@"
--- /dev/null
+{
+ "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
link:doc/python-howto.txt[`doc/python-howto.txt`] to understand how to
use them.
+== Supported versions
+
+The LTTng project supports the last two released stable versions
+(e.g. stable-2.13 and stable-2.12).
+
+Fixes are backported from the master branch to the last stable version unless
+those fixes would break the ABI or API. Those fixes may be backported to the
+second-last stable version, depending on complexity and ABI/API compatibility.
+
+Security fixes are backported from the master branch to both of the last stable
+version and the second-last stable version.
+
+New features are integrated into the master branch and not backported to the
+last stable branch.
+
== Community
Mailing list::
static unsigned long trace_chunk_registry_ht_key_hash(const struct trace_chunk_registry_ht_key *key)
{
- const uint64_t uuid_h1 = *reinterpret_cast<const uint64_t *>(&key->sessiond_uuid[0]);
- const uint64_t uuid_h2 = *reinterpret_cast<const uint64_t *>(&key->sessiond_uuid[1]);
+ const uint64_t uuid_h1 = reinterpret_cast<const uint64_t *>(key->sessiond_uuid.data())[0];
+ const uint64_t uuid_h2 = reinterpret_cast<const uint64_t *>(key->sessiond_uuid.data())[1];
return hash_key_u64(&uuid_h1, lttng_ht_seed) ^ hash_key_u64(&uuid_h2, lttng_ht_seed);
}
/* Delete streams that might have been left in the stream list. */
cds_list_for_each_entry_safe (stream, stmp, &channel->streams.head, send_node) {
- /*
- * Once a stream is added to this list, the buffers were created so we
- * have a guarantee that this call will succeed. Setting the monitor
- * mode to 0 so we don't lock nor try to delete the stream from the
- * global hash table.
- */
- stream->monitor = 0;
consumer_stream_destroy(stream, nullptr);
}
}
* 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.
goto end;
}
+ if (a->emission_site != b->emission_site) {
+ goto end;
+ }
+
is_equal = true;
end:
return is_equal;
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 \
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
--- /dev/null
+#!/usr/bin/env python3
+#
+# SPDX-FileCyoprightText: Kienan Stewart <kstewart@efficios.com>
+# 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)
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
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