/tests/utils/testapp/gen-ust-events-constructor/gen-ust-events-c-constructor-so
/tests/utils/testapp/gen-ust-events-constructor/uses_heap
/tests/utils/testapp/gen-ust-events-ns/gen-ust-events-ns
+/tests/regression/tools/config-directory/test_config.py
/tests/regression/tools/health/health_check
/tests/regression/kernel/select_poll_epoll
/tests/regression/tools/notification/base_client
])
# Inject variable into python test script.
+AC_CONFIG_FILES([tests/regression/tools/config-directory/test_config.py],[chmod +x tests/regression/tools/config-directory/test_config.py])
AC_CONFIG_FILES([tests/regression/ust/python-logging/test_python_logging],[chmod +x tests/regression/ust/python-logging/test_python_logging])
# Inject LTTNG_TOOLS_BUILD_WITH_LIBPFM variable in test script.
AC_CONFIG_FILES([tests/perf/test_perf_raw],[chmod +x tests/perf/test_perf_raw])
ust/ust-constructor/test_ust_constructor_c_static.py \
ust/ust-constructor/test_ust_constructor_cpp_dynamic.py \
ust/ust-constructor/test_ust_constructor_cpp_static.py \
+ tools/config-directory/test_config.py \
tools/metadata/test_ust \
tools/relayd-grouping/test_ust \
tools/trigger/rate-policy/test_ust_rate_policy
--- /dev/null
+#!/usr/bin/env python3
+#
+# SPDX-FileCopyrightText: 2024 Kienan Stewart <kstewart@efficios.com>
+# SPDX-License-Identifier: GPL-2.0-only
+#
+
+"""
+Tests the behaviour of the configuration and socket paths for lttng-sessiond, lttng-relayd, the consumer daemons, and liblttng-ctl.
+"""
+
+import copy
+import os
+import os.path
+import pathlib
+import platform
+import pwd
+import re
+import socket
+import stat
+import subprocess
+import sys
+import tempfile
+import time
+
+# Import in-tree test utils
+test_utils_import_path = pathlib.Path(__file__).absolute().parents[3] / "utils"
+sys.path.append(str(test_utils_import_path))
+
+import lttngtest
+import bt2
+
+user_info = pwd.getpwuid(os.getuid())
+user_is_root = user_info.pw_uid == 0
+
+DEFAULT_SUBSTITUTIONS = {
+ "USER_HOME": user_info.pw_dir,
+ "USER_ID": user_info.pw_uid,
+ "SYSTEM_HOME": "@LTTNG_SYSTEM_RUNDIR@",
+ "LTTNG_HOME": "@LTTNG_SYSTEM_RUNDIR@" if user_is_root else "{USER_HOME}",
+ "RUNDIR": "@LTTNG_SYSTEM_RUNDIR@" if user_is_root else "{LTTNG_HOME}/.lttng",
+ # If the values from the lttng-ust headers change, these will need to be updated.
+ "LTTNG_UST_SOCKET_NAME": "lttng-ust-sock-8",
+ "LTTNG_UST_WAIT_FILENAME": "lttng-ust-wait-8",
+}
+
+DEFAULT_EXPECTED_PATHS = {
+ "rundir": {
+ "type": "directory",
+ "path": "{RUNDIR}",
+ },
+ "apps_unix_sock_path": {
+ "type": "socket",
+ "path": "{RUNDIR}/{LTTNG_UST_SOCKET_NAME}",
+ },
+ "wait_shm_path": {
+ "type": "shm",
+ "path": (
+ "/{LTTNG_UST_WAIT_FILENAME}"
+ if user_is_root
+ else "/{LTTNG_UST_WAIT_FILENAME}-{USER_ID}"
+ ),
+ },
+ "client_unix_sock_path": {
+ "type": "socket",
+ "path": "{RUNDIR}/client-lttng-sessiond",
+ },
+ "sessiond_health_unix_sock_path": {
+ "type": "socket",
+ "path": "{RUNDIR}/sessiond-health",
+ },
+ "sessiond_notification_unix_sock_path": {
+ "type": "socket",
+ "path": "{RUNDIR}/sessiond-notification",
+ },
+ "relayd_health_unix_sock_path": {
+ "type": "socket",
+ "path": "{RUNDIR}/relayd/health-{RELAYD_PID}",
+ },
+}
+
+if platform.architecture()[0] == "64bit":
+ DEFAULT_EXPECTED_PATHS["ustconsumerd64_health_unix_sock_path"] = {
+ "type": "socket",
+ "path": "{RUNDIR}/ustconsumerd64/health",
+ }
+else:
+ DEFAULT_EXPECTED_PATHS["ustconsumerd32_healh_unix_sock_path"] = {
+ "type": "socket",
+ "path": "{RUNDIR}/ustconsumerd32/health",
+ }
+
+if lttngtest._Environment.run_kernel_tests():
+ DEFAULT_EXPECTED_PATHS["kconsumerd_health_unix_sock_path"] = {
+ "type": "socket",
+ "path": "{RUNDIR}/kconsumerd/health",
+ }
+
+# These are used for compatibility with python < 3.9. If the python
+# version is 3.9+, `dict_a | dict_b` can be used instead.
+_ust_ctl_path_expected_paths = copy.deepcopy(DEFAULT_EXPECTED_PATHS)
+_ust_ctl_path_expected_paths.update(
+ {
+ "apps_unix_sock_path": {
+ "type": "socket",
+ "path": "{LTTNG_UST_CTL_PATH}/{LTTNG_UST_SOCKET_NAME}",
+ },
+ }
+)
+
+tests = {
+ "default": {
+ "description": "LTTng with no specific environment settings",
+ "environment_variables": {},
+ "substitutions": DEFAULT_SUBSTITUTIONS,
+ "expected_paths": DEFAULT_EXPECTED_PATHS,
+ },
+ "lttng_home": {
+ "description": "LTTng with LTTNG_HOME set",
+ "environment_variables": {
+ # This value will come from the lttngtest environment
+ "LTTNG_HOME": True,
+ },
+ "substitutions": DEFAULT_SUBSTITUTIONS,
+ "expected_paths": DEFAULT_EXPECTED_PATHS,
+ },
+ "ust_ctl_path": {
+ "description": "LTTng with LTTNG_UST_CTL_PATH set",
+ "environment_variables": {
+ # This value will be generated in the lttngtest environment
+ "LTTNG_UST_CTL_PATH": True,
+ },
+ "substitutions": DEFAULT_SUBSTITUTIONS,
+ "expected_paths": _ust_ctl_path_expected_paths,
+ },
+ "lttng_home_and_ust_ctl_path": {
+ "description": "LTTng with LTTNG_HOME and LTTNG_UST_CTL_PATH set",
+ "environment_variables": {
+ "LTTNG_HOME": True,
+ "LTTNG_UST_CTL_PATH": True,
+ },
+ "substitutions": DEFAULT_SUBSTITUTIONS,
+ "expected_paths": _ust_ctl_path_expected_paths,
+ },
+}
+
+
+class MissingDict(dict):
+ def __missing__(self, key):
+ return key
+
+
+def test_config_and_socket_paths(
+ test_env,
+ tap,
+ test_identifier,
+ description,
+ environment_variables,
+ substitutions,
+ expected_paths,
+):
+ tap.diagnostic(
+ "[{}] Config and socket paths - {}".format(test_identifier, description)
+ )
+
+ if (
+ "LTTNG_HOME" in environment_variables
+ and test_env.lttng_home_location is not None
+ ):
+ environment_variables["LTTNG_HOME"] = str(test_env.lttng_home_location)
+ substitutions.update(environment_variables)
+
+ substitutions["RELAYD_PID"] = test_env._relayd.pid
+
+ client = lttngtest.LTTngClient(test_env, log=tap.diagnostic)
+ session_output_location = lttngtest.LocalSessionOutputLocation(
+ test_env.create_temporary_directory("trace")
+ )
+ session = client.create_session(output=session_output_location)
+ channel = session.add_channel(lttngtest.lttngctl.TracingDomain.User)
+ channel.add_recording_rule(lttngtest.lttngctl.UserTracepointEventRule("tp:tptest"))
+
+ kernel_channel = None
+ if test_env.run_kernel_tests():
+ kernel_channel = session.add_channel(lttngtest.lttngctl.TracingDomain.Kernel)
+ kernel_channel.add_recording_rule(
+ lttngtest.lttngctl.KernelTracepointEventRule("*")
+ )
+
+ session.start()
+ test_app = test_env.launch_wait_trace_test_application(100)
+ test_app.trace()
+ test_app.wait_for_tracing_done()
+ test_app.wait_for_exit()
+
+ errors = []
+ for path_id, path_conf in expected_paths.items():
+ path = path_conf["path"]
+ while re.search("\{\w+\}", path) is not None:
+ path = path.format_map(MissingDict(substitutions))
+ tap.diagnostic(
+ "Checking for file type `{}` at `{}`".format(path_conf["type"], path)
+ )
+ if path_conf["type"] == "file":
+ if not os.path.isfile(path):
+ tap.diagnostic("`{}` is not a file".format(path))
+ errors.append(path_id)
+ elif path_conf["type"] == "socket":
+ try:
+ if not stat.S_ISSOCK(os.stat(path).st_mode):
+ tap.diagnostic("`{}` is not a socket".format(path))
+ errors.append(path_id)
+ except Exception as e:
+ tap.diagnostic(
+ "Exception while checking socket `{}`: {}".format(path, str(e))
+ )
+ errors.append(path_id)
+ elif path_conf["type"] == "directory":
+ if not os.path.isdir(path):
+ tap.diagnostic("`{}` is not a directory".format(path))
+ errors.append(path_id)
+ elif path_conf["type"] == "shm":
+ # @FIXME: Portability on Windows and MacOSX
+ if platform.system() != "Linux":
+ tap.diagnostic("Skipping check of shm at `{}`".format(path))
+ continue
+ path = "/dev/shm/{}".format(path)
+ if not os.path.isfile(path):
+ tap.diagnostic("Shared memory at `{}` is not a file".format(path))
+ errors.append(path_id)
+ else:
+ tap.diagnostic(
+ "Unknown type `{}` for path `{}`".format(path_conf["type"], path)
+ )
+ errors.append(path_id)
+ tap.test(
+ len(errors) == 0,
+ "{} expected configuration paths exist. {} errors".format(
+ len(expected_paths), len(errors)
+ ),
+ )
+
+ session.stop()
+ session.destroy()
+
+
+if __name__ == "__main__":
+ tap = lttngtest.TapGenerator(len(tests))
+
+ for test_identifier, test_configuration in tests.items():
+ temp_conf = copy.deepcopy(test_configuration)
+ skip_lttng_home = (
+ "LTTNG_HOME" not in test_configuration["environment_variables"]
+ )
+ if skip_lttng_home and os.getenv("LTTNG_HOME", None) is not None:
+ tap.skip(
+ "LTTNG_HOME is already set, skipping test that should exercise the default value"
+ )
+ continue
+
+ # This will hold keep the temporary object file alive until the next
+ # iteration. When the object is destroy, the tempfile will be cleaned
+ # up.
+ tempfiles = []
+ if "LTTNG_UST_CTL_PATH" in test_configuration["environment_variables"]:
+ tempfiles.append(tempfile.TemporaryDirectory())
+ temp_conf["environment_variables"]["LTTNG_UST_CTL_PATH"] = tempfiles[
+ -1
+ ].name
+ temp_conf["substitutions"]["LTTNG_UST_CTL_PATH"] = tempfiles[-1].name
+ with lttngtest.test_environment(
+ with_sessiond=True,
+ log=tap.diagnostic,
+ with_relayd=True,
+ extra_env_vars=temp_conf["environment_variables"],
+ skip_temporary_lttng_home=skip_lttng_home,
+ ) as test_env:
+ test_config_and_socket_paths(test_env, tap, test_identifier, **temp_conf)
+ sys.exit(0 if tap.is_successful else 1)
self._environment = environment # type: Environment
self._iteration_count = event_count
# File that the application will wait to see before tracing its events.
- dir = self._compat_pathlike(environment.lttng_home_location)
+ dir = (
+ self._compat_pathlike(environment.lttng_home_location)
+ if environment.lttng_home_location
+ else None
+ )
if run_as is not None:
dir = os.path.join(dir, run_as)
self._app_start_tracing_file_path = pathlib.Path(
self._tracing_started = False
test_app_env = os.environ.copy()
- test_app_env["LTTNG_HOME"] = str(environment.lttng_home_location)
+ if environment.lttng_home_location is not None:
+ test_app_env["LTTNG_HOME"] = str(environment.lttng_home_location)
# Make sure the app is blocked until it is properly registered to
# the session daemon.
test_app_env["LTTNG_UST_REGISTER_TIMEOUT"] = "-1"
with_sessiond, # type: bool
log=None, # type: Optional[Callable[[str], None]]
with_relayd=False, # type: bool
+ extra_env_vars=dict(), # type: dict
+ skip_temporary_lttng_home=False, # type: bool
):
super().__init__(log)
signal.signal(signal.SIGTERM, self._handle_termination_signal)
pathlib.Path(__file__).absolute().parents[3]
) # type: pathlib.Path
- self._lttng_home = TemporaryDirectory(
- "lttng_test_env_home"
- ) # type: Optional[str]
- os.chmod(
- str(self._lttng_home.path),
- stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IROTH | stat.S_IXOTH,
- )
+ self._extra_env_vars = extra_env_vars
+
+ # There are times when we need to exercise default configurations
+ # that don't set LTTNG_HOME. When doing this, it makes it impossible
+ # to safely run parallel tests.
+ self._lttng_home = None
+ if not skip_temporary_lttng_home:
+ self._lttng_home = TemporaryDirectory(
+ "lttng_test_env_home"
+ ) # type: Optional[TemporaryDirectory]
+ os.chmod(
+ str(self._lttng_home.path),
+ stat.S_IRUSR
+ | stat.S_IWUSR
+ | stat.S_IXUSR
+ | stat.S_IROTH
+ | stat.S_IXOTH,
+ )
self._relayd = (
self._launch_lttng_relayd() if with_relayd else None
@property
def lttng_home_location(self):
# type: () -> pathlib.Path
- if self._lttng_home is None:
- raise RuntimeError("Attempt to access LTTng home after clean-up")
- return self._lttng_home.path
+ if self._lttng_home is not None:
+ return self._lttng_home.path
+ return None
@property
def lttng_client_path(self):
# type: (Optional[str]) -> pathlib.Path
# Simply return a path that is contained within LTTNG_HOME; it will
# be destroyed when the temporary home goes out of scope.
- assert self._lttng_home
return pathlib.Path(
tempfile.mkdtemp(
prefix="tmp" if prefix is None else prefix,
- dir=str(self._lttng_home.path),
+ dir=str(self.lttng_home_location) if self.lttng_home_location else None,
)
)
+ @staticmethod
+ def run_kernel_tests():
+ # type: () -> bool
+ return (
+ os.getenv("LTTNG_TOOLS_DISABLE_KERNEL_TESTS", "0") != "1"
+ and os.getuid() == 0
+ )
+
# Unpack a list of environment variables from a string
# such as "HELLO=is_it ME='/you/are/looking/for'"
@staticmethod
relayd_env_vars = os.environ.get("LTTNG_RELAYD_ENV_VARS")
relayd_env = os.environ.copy()
+ relayd_env.update(self._extra_env_vars)
if relayd_env_vars:
self._log("Additional lttng-relayd environment variables:")
for name, value in self._unpack_env_vars(relayd_env_vars):
self._log("{}={}".format(name, value))
relayd_env[name] = value
- assert self._lttng_home is not None
- relayd_env["LTTNG_HOME"] = str(self._lttng_home.path)
+ if self.lttng_home_location is not None:
+ relayd_env["LTTNG_HOME"] = str(self.lttng_home_location)
self._log(
- "Launching relayd with LTTNG_HOME='${}'".format(str(self._lttng_home.path))
+ "Launching relayd with LTTNG_HOME='${}'".format(
+ str(self.lttng_home_location)
+ )
)
verbose = []
if os.environ.get("LTTNG_TEST_VERBOSE_RELAYD") is not None:
# Setup the session daemon's environment
sessiond_env_vars = os.environ.get("LTTNG_SESSIOND_ENV_VARS")
sessiond_env = os.environ.copy()
+ sessiond_env.update(self._extra_env_vars)
if sessiond_env_vars:
self._log("Additional lttng-sessiond environment variables:")
additional_vars = self._unpack_env_vars(sessiond_env_vars)
self._project_root / "src" / "common"
)
- assert self._lttng_home is not None
- sessiond_env["LTTNG_HOME"] = str(self._lttng_home.path)
+ if self.lttng_home_location is not None:
+ sessiond_env["LTTNG_HOME"] = str(self.lttng_home_location)
wait_queue = _SignalWaitQueue()
with wait_queue.intercept_signal(signal.SIGUSR1):
self._log(
"Launching session daemon with LTTNG_HOME=`{home_dir}`".format(
- home_dir=str(self._lttng_home.path)
+ home_dir=str(self.lttng_home_location)
)
)
verbose = []
@contextlib.contextmanager
-def test_environment(with_sessiond, log=None, with_relayd=False):
+def test_environment(
+ with_sessiond,
+ log=None,
+ with_relayd=False,
+ extra_env_vars=dict(),
+ skip_temporary_lttng_home=False,
+):
# type: (bool, Optional[Callable[[str], None]], bool) -> Iterator[_Environment]
- env = _Environment(with_sessiond, log, with_relayd)
+ env = _Environment(
+ with_sessiond, log, with_relayd, extra_env_vars, skip_temporary_lttng_home
+ )
try:
yield env
finally:
if rule.filter_expression:
client_args = client_args + " " + rule.filter_expression
- if rule.log_level_rule:
+ if getattr(rule, "log_level_rule", None):
if isinstance(rule.log_level_rule, lttngctl.LogLevelRuleAsSevereAs):
client_args = client_args + " --loglevel {log_level}".format(
log_level=_get_log_level_argument_name(
)
)
- if rule.name_pattern_exclusions:
+ if getattr(rule, "name_pattern_exclusions", None):
client_args = client_args + " --exclude "
for idx, pattern in enumerate(rule.name_pattern_exclusions):
if idx != 0:
# type: (lttngctl.TracingDomain, Optional[str], lttngctl.BufferSharingPolicy) -> lttngctl.Channel
channel_name = lttngctl.Channel._generate_name()
domain_option_name = _get_domain_option_name(domain)
+ buffer_sharing_policy = (
+ "--buffers-uid"
+ if buffer_sharing_policy == lttngctl.BufferSharingPolicy.PerUID
+ else "--buffers-pid"
+ )
self._client._run_cmd(
"enable-channel --session '{session_name}' --{domain_name} '{channel_name}' {buffer_sharing_policy}".format(
session_name=self.name,
domain_name=domain_option_name,
channel_name=channel_name,
buffer_sharing_policy=(
- "--buffers-uid"
- if buffer_sharing_policy == lttngctl.BufferSharingPolicy.PerUID
- else "--buffers-pid"
+ buffer_sharing_policy
+ if domain != lttngctl.TracingDomain.Kernel
+ else ""
),
)
)
self._log("lttng {command_args}".format(command_args=command_args))
client_env = os.environ.copy() # type: dict[str, str]
- client_env["LTTNG_HOME"] = str(self._environment.lttng_home_location)
+ if self._environment.lttng_home_location is not None:
+ client_env["LTTNG_HOME"] = str(self._environment.lttng_home_location)
process = subprocess.Popen(
args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=client_env