From ed60bacaca267eb334b56468b84c73351ac575c5 Mon Sep 17 00:00:00 2001 From: Maciej Kaszynski Date: Mon, 4 May 2026 13:07:59 +0100 Subject: [PATCH 01/10] Adding id hash < -#include #include +#include #include #include #include -namespace score -{ - -namespace lcm +namespace score::lcm { /// @file identifier_hash.hpp @@ -145,7 +142,7 @@ class IdentifierHash final /// @param os The output stream. /// @param id The IdentifierHash object to output. /// @return A reference to the output stream. -inline std::ostream& operator<<(std::ostream& os, const IdentifierHash& id) +inline std::ostream& operator<<(std::ostream& os, const IdentifierHash& id) noexcept { const auto& reg = IdentifierHash::get_registry(); const auto it = reg.find(id.data()); @@ -160,8 +157,31 @@ inline std::ostream& operator<<(std::ostream& os, const IdentifierHash& id) return os; } -} // namespace lcm +} // namespace score::lcm + +#if __has_include("score/mw/log/logger.h") +#include "score/mw/log/logger.h" + +namespace score::lcm +{ + +inline score::mw::log::LogStream& operator<<(score::mw::log::LogStream& log_stream, const IdentifierHash& id) noexcept +{ + const auto& reg = IdentifierHash::get_registry(); + const auto it = reg.find(id.data()); + if (it != reg.end()) + { + log_stream << it->second; + } + else + { + log_stream << ""; + } + return log_stream; +} + +} // namespace score::lcm -} // namespace score +#endif #endif // IDENTIFIER_HASH_H_ From e28ceabc5d707410af74314b788bc784e5ab4205 Mon Sep 17 00:00:00 2001 From: Maciej Kaszynski Date: Mon, 4 May 2026 13:22:51 +0100 Subject: [PATCH 02/10] Adding bazel cout flag --- MODULE.bazel | 1 + config/BUILD | 16 ++++++++++++++++ src/control_client_lib/BUILD | 1 - src/launch_manager_daemon/common/BUILD | 8 ++++++++ .../health_monitor_lib/BUILD | 4 ++++ 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/MODULE.bazel b/MODULE.bazel index 754f0308..b6cd1023 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -16,6 +16,7 @@ module( ) # Bazel global rules +bazel_dep(name = "bazel_skylib", version = "1.9.0") bazel_dep(name = "rules_pkg", version = "1.1.0") bazel_dep(name = "rules_python", version = "1.8.5") bazel_dep(name = "rules_rust", version = "0.68.1-score") diff --git a/config/BUILD b/config/BUILD index 2057a774..ae6bb3ec 100644 --- a/config/BUILD +++ b/config/BUILD @@ -10,6 +10,8 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@bazel_skylib//rules:common_settings.bzl", "bool_flag") + config_setting( name = "x86_64-linux", define_values = { @@ -52,3 +54,17 @@ config_setting( name = "ub_sanitizer_enabled", define_values = {"sanitize": "undefined"}, ) + +bool_flag( + name = "use_cout_log", + build_setting_default = False, + visibility = ["//visibility:public"], +) + +config_setting( + name = "lm_use_cout_log", + flag_values = { + ":use_cout_log": "True", + }, + visibility = ["//visibility:public"], +) diff --git a/src/control_client_lib/BUILD b/src/control_client_lib/BUILD index d119de27..d4e304e0 100644 --- a/src/control_client_lib/BUILD +++ b/src/control_client_lib/BUILD @@ -32,6 +32,5 @@ cc_library( "//src/launch_manager_daemon/common:log", "//src/launch_manager_daemon/common:osal", "@score_baselibs//score/concurrency/future", - "@score_baselibs//score/mw/log", ], ) diff --git a/src/launch_manager_daemon/common/BUILD b/src/launch_manager_daemon/common/BUILD index 6a39acb4..42e59106 100644 --- a/src/launch_manager_daemon/common/BUILD +++ b/src/launch_manager_daemon/common/BUILD @@ -52,6 +52,14 @@ cc_library( name = "log", hdrs = ["include/score/lcm/internal/log.hpp"], includes = ["include"], + defines = select({ + "//config:lm_use_cout_log": [], + "//conditions:default": ["LC_LOG_SCORE_MW_LOG"], + }), + deps = [] + select({ + "//config:lm_use_cout_log": [], + "//conditions:default": ["@score_baselibs//score/mw/log"], + }), visibility = ["//src:__subpackages__"], ) diff --git a/src/launch_manager_daemon/health_monitor_lib/BUILD b/src/launch_manager_daemon/health_monitor_lib/BUILD index ecafe216..e5c930a2 100644 --- a/src/launch_manager_daemon/health_monitor_lib/BUILD +++ b/src/launch_manager_daemon/health_monitor_lib/BUILD @@ -46,6 +46,10 @@ cc_library_with_common_opts( "src", "src/score/lcm/saf/logging", ], + defines = select({ + "//config:lm_use_cout_log": [], + "//conditions:default": ["LC_LOG_SCORE_MW_LOG"], + }), visibility = ["//src:__pkg__"], deps = [ "@score_baselibs//score/mw/log", From 5c16b9425ccadb319fc85a9e9b0d2251952e0733 Mon Sep 17 00:00:00 2001 From: Maciej Kaszynski Date: Tue, 5 May 2026 11:41:04 +0100 Subject: [PATCH 03/10] Fixing test --- tests/README.md | 23 +++++++++++++++++++ .../integration/smoke/control_daemon_mock.cpp | 4 +++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 tests/README.md diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 00000000..63f48a11 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,23 @@ +# Test Options + +## Bazel Args + +| Arg | What It Does | +|:-----------------------------|:--------| +|`--sandbox_add_mount_pair=/tmp` | Uses the users real `/tmp` dir for testing (only for `--config=host` builds| +|`--test_output=streamed` | logs apear while running | +|`--test_output=all`| outputs all logs after test | +|`--nocache_test_results` | Reruns tests even if they have previously passed| +|`--runs_per_test=N` | Reruns the thests N times| +|`--compilation_mode=dbg` | Builds the binaries in debug mode (debug symbols & `NDEBUG` **not** defined ) | + + +## Pytest Args + +Note the arguments are written so that you can add them to the end of the bazel +test command. + +| Arg | What It Does | +|:-----------------------------|:--------| +|`--test_arg=--no-local-cleanup` | Integration tests don't cleanup after running | +|`--test_arg=-s` | More logging from the python test framework | diff --git a/tests/integration/smoke/control_daemon_mock.cpp b/tests/integration/smoke/control_daemon_mock.cpp index 7b70bb49..6045049d 100644 --- a/tests/integration/smoke/control_daemon_mock.cpp +++ b/tests/integration/smoke/control_daemon_mock.cpp @@ -20,9 +20,11 @@ #include #include "tests/utils/test_helper/test_helper.hpp" -score::lcm::ControlClient client; TEST(Smoke, Daemon) { + + score::lcm::ControlClient client {}; + TEST_STEP("Control daemon report kRunning") { // report kRunning auto result = score::lcm::LifecycleClient{}.ReportExecutionState(score::lcm::ExecutionState::kRunning); From 84f1325b126ef1bb416ac2f2896bf28fd31b8648 Mon Sep 17 00:00:00 2001 From: Maciej Kaszynski Date: Tue, 5 May 2026 11:49:40 +0100 Subject: [PATCH 04/10] Fixing formatting --- src/launch_manager_daemon/common/BUILD | 4 +- .../common/include/score/lcm/internal/log.hpp | 151 ++++++++++-------- .../health_monitor_lib/BUILD | 8 +- tests/README.md | 24 +-- 4 files changed, 102 insertions(+), 85 deletions(-) diff --git a/src/launch_manager_daemon/common/BUILD b/src/launch_manager_daemon/common/BUILD index 42e59106..c378f4ad 100644 --- a/src/launch_manager_daemon/common/BUILD +++ b/src/launch_manager_daemon/common/BUILD @@ -51,16 +51,16 @@ cc_library( cc_library( name = "log", hdrs = ["include/score/lcm/internal/log.hpp"], - includes = ["include"], defines = select({ "//config:lm_use_cout_log": [], "//conditions:default": ["LC_LOG_SCORE_MW_LOG"], }), + includes = ["include"], + visibility = ["//src:__subpackages__"], deps = [] + select({ "//config:lm_use_cout_log": [], "//conditions:default": ["@score_baselibs//score/mw/log"], }), - visibility = ["//src:__subpackages__"], ) cc_library( diff --git a/src/launch_manager_daemon/common/include/score/lcm/internal/log.hpp b/src/launch_manager_daemon/common/include/score/lcm/internal/log.hpp index de02e145..bb20f2f1 100644 --- a/src/launch_manager_daemon/common/include/score/lcm/internal/log.hpp +++ b/src/launch_manager_daemon/common/include/score/lcm/internal/log.hpp @@ -11,7 +11,6 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ - #ifndef LCM_LOG_HPP_INCLUDED #define LCM_LOG_HPP_INCLUDED @@ -23,43 +22,44 @@ #include "score/mw/log/logger.h" -namespace score { +namespace score +{ -namespace lcm { +namespace lcm +{ -namespace internal { +namespace internal +{ /// @brief Function to access global logging context, for Launch Manager. /// Launch Manager (LM) daemon, uses a single global logging context. /// This context is stored as a static variable inside this function and used all over LM daemon implementation. /// Please note that code should not call this function directly, but should use a set of wrapper macros. /// More information can be found in docs/architecture/concepts/logging/logging.rst file. -inline score::mw::log::Logger& _getLmLogger() noexcept { - // RULECHECKER_comment(1, 1, check_static_object_dynamic_initialization, "This is safe because the static is a function local.", true); +inline score::mw::log::Logger& _getLmLogger() noexcept +{ + // RULECHECKER_comment(1, 1, check_static_object_dynamic_initialization, "This is safe because the static is a + // function local.", true); static score::mw::log::Logger& log{score::mw::log::CreateLogger("LM", "Launch Manager logging context")}; return log; } -} // namespace lcm - } // namespace internal +} // namespace lcm + } // namespace score #else // LC_LOG_SCORE_MW_LOG // The only other solution supported is console logging. -#include -#include -#include #include +#include +#include #include -namespace score { - -namespace lcm { - -namespace internal { +namespace score::lcm::internal +{ enum class LogLevel { @@ -71,53 +71,62 @@ enum class LogLevel kVerbose = 5, }; -inline LogLevel GetLevelFromEnv() { - if (const char* levelStr = std::getenv("LC_STDOUT_LOG_LEVEL")) { +inline LogLevel GetLevelFromEnv() +{ + if (const char* levelStr = std::getenv("LC_STDOUT_LOG_LEVEL")) + { std::string_view levelSv{levelStr}; int logLevelTmp; - try { + try + { logLevelTmp = std::stoi(levelSv.data()); - }catch(...) { + } + catch (...) + { return LogLevel::kInfo; - } + } - if(logLevelTmp >= static_cast(LogLevel::kFatal) && logLevelTmp <= static_cast(LogLevel::kVerbose)) { + if (logLevelTmp >= static_cast(LogLevel::kFatal) && logLevelTmp <= static_cast(LogLevel::kVerbose)) + { return LogLevel(logLevelTmp); - } else { + } + else + { return LogLevel::kInfo; - } - - } else { + } + } + else + { return LogLevel::kInfo; } } -static LogLevel GetLevel() { +static LogLevel GetLevel() +{ const static LogLevel logLevel = GetLevelFromEnv(); return logLevel; } -inline std::ostream& operator<<(std::ostream& os, const std::tm* now ) { - std::cout << (now->tm_year + 1900) << '/' - << (now->tm_mon + 1) << '/' - << now->tm_mday << " " - << now->tm_hour << ":" - << now->tm_min << ":" - << now->tm_sec; +inline std::ostream& operator<<(std::ostream& os, const std::tm* now) +{ + std::cout << (now->tm_year + 1900) << '/' << (now->tm_mon + 1) << '/' << now->tm_mday << " " << now->tm_hour << ":" + << now->tm_min << ":" << now->tm_sec; return os; } class Stream { -public: + public: Stream() noexcept = default; - Stream(const Stream &) = delete; - Stream(Stream && other) noexcept { + Stream(const Stream&) = delete; + Stream(Stream&& other) noexcept + { print_ = other.print_; moved_ = true; }; - void SetPrint() { + void SetPrint() + { print_ = true; } @@ -125,7 +134,7 @@ class Stream Stream& operator<<(const T* value) noexcept { - if(print_) + if (print_) std::cout << " " << value; return *this; } @@ -133,17 +142,18 @@ class Stream template Stream& operator<<(const T& value) noexcept { - if(print_) + if (print_) std::cout << " " << value; return *this; } ~Stream() { - if(print_ && moved_) + if (print_ && moved_) std::cout << " ]" << reset_color_ << std::endl; } -private: + + private: bool print_{false}; bool moved_{false}; std::string_view reset_color_{"\033[0m"}; @@ -151,21 +161,22 @@ class Stream class Logger { -public: - Logger(std::string_view f_context, std::string_view f_description) : - ctxId_(f_context), ctxDescription_{f_description} { - + public: + Logger(std::string_view f_context, std::string_view f_description) + : ctxId_(f_context), ctxDescription_{f_description} + { } Stream LogFatal() noexcept { Stream stream; - if(GetLevel() >= LogLevel::kFatal) { + if (GetLevel() >= LogLevel::kFatal) + { stream.SetPrint(); std::time_t t = std::time(0); std::tm now; localtime_r(&t, &now); - stream << check_it_ << text_color_ << &now << appId_ << ctxId_ << "FATAL: ["; + stream << check_it_ << text_color_ << &now << appId_ << ctxId_ << "FATAL: ["; } return std::move(stream); } @@ -173,12 +184,13 @@ class Logger Stream LogError() noexcept { Stream stream; - if(GetLevel() >= LogLevel::kError) { + if (GetLevel() >= LogLevel::kError) + { stream.SetPrint(); std::time_t t = std::time(0); std::tm now; localtime_r(&t, &now); - stream << check_it_ << text_color_ << &now << appId_ << ctxId_ << "ERROR: ["; + stream << check_it_ << text_color_ << &now << appId_ << ctxId_ << "ERROR: ["; } return std::move(stream); @@ -187,12 +199,13 @@ class Logger Stream LogWarn() noexcept { Stream stream; - if(GetLevel() >= LogLevel::kWarn) { + if (GetLevel() >= LogLevel::kWarn) + { stream.SetPrint(); std::time_t t = std::time(0); std::tm now; localtime_r(&t, &now); - stream << check_it_ << text_color_ << &now << appId_ << ctxId_ << "WARNING: ["; + stream << check_it_ << text_color_ << &now << appId_ << ctxId_ << "WARNING: ["; } return std::move(stream); } @@ -200,12 +213,13 @@ class Logger Stream LogInfo() noexcept { Stream stream; - if(GetLevel() >= LogLevel::kInfo) { + if (GetLevel() >= LogLevel::kInfo) + { stream.SetPrint(); std::time_t t = std::time(0); std::tm now; localtime_r(&t, &now); - stream << text_color_ << &now << appId_ << ctxId_ << "INFO: ["; + stream << text_color_ << &now << appId_ << ctxId_ << "INFO: ["; } return std::move(stream); } @@ -213,12 +227,13 @@ class Logger Stream LogDebug() noexcept { Stream stream; - if(GetLevel() >= LogLevel::kDebug) { + if (GetLevel() >= LogLevel::kDebug) + { stream.SetPrint(); std::time_t t = std::time(0); std::tm now; localtime_r(&t, &now); - stream << text_color_ << &now << appId_ << ctxId_ << "DEBUG: ["; + stream << text_color_ << &now << appId_ << ctxId_ << "DEBUG: ["; } return std::move(stream); } @@ -226,16 +241,18 @@ class Logger Stream LogVerbose() noexcept { Stream stream; - if(GetLevel() >= LogLevel::kVerbose) { + if (GetLevel() >= LogLevel::kVerbose) + { stream.SetPrint(); std::time_t t = std::time(0); std::tm now; localtime_r(&t, &now); - stream << text_color_ << &now << appId_ << ctxId_ << "VERBOSE: ["; + stream << text_color_ << &now << appId_ << ctxId_ << "VERBOSE: ["; } return std::move(stream); } -private: + + private: const std::string_view appId_{"LCLM"}; const std::string_view ctxId_{"####"}; const std::string_view ctxDescription_{"####"}; @@ -243,17 +260,17 @@ class Logger const std::string_view check_it_{"\033[101;30m !!! -> \033[0m"}; }; -inline Logger& _getLmLogger() noexcept { - // RULECHECKER_comment(1, 1, check_static_object_dynamic_initialization, "This is safe because the static is a function local.", true); +inline Logger& _getLmLogger() noexcept +{ + // RULECHECKER_comment(1, 1, check_static_object_dynamic_initialization, "This is safe because the static is a + // function local.", true); static Logger log{"LCLM", "Launch Manager logging context"}; return log; } -} // namespace lcm - -} // namespace internal +} // namespace score::lcm::internal -} // namespace score +// namespace internal #endif // LC_LOG_SCORE_MW_LOG @@ -261,7 +278,7 @@ inline Logger& _getLmLogger() noexcept { #define LM_LOG_FATAL() (score::lcm::internal::_getLmLogger().LogFatal()) #define LM_LOG_ERROR() (score::lcm::internal::_getLmLogger().LogError()) #define LM_LOG_WARN() (score::lcm::internal::_getLmLogger().LogWarn()) -#define LM_LOG_INFO() (score::lcm::internal::_getLmLogger().LogInfo()) -#define LM_LOG_DEBUG() (score::lcm::internal::_getLmLogger().LogDebug()) +#define LM_LOG_INFO() (score::lcm::internal::_getLmLogger().LogWarn()) +#define LM_LOG_DEBUG() (score::lcm::internal::_getLmLogger().LogWarn()) #endif // LCM_LOG_HPP_INCLUDED diff --git a/src/launch_manager_daemon/health_monitor_lib/BUILD b/src/launch_manager_daemon/health_monitor_lib/BUILD index e5c930a2..e5ac4dda 100644 --- a/src/launch_manager_daemon/health_monitor_lib/BUILD +++ b/src/launch_manager_daemon/health_monitor_lib/BUILD @@ -42,14 +42,14 @@ cc_library_with_common_opts( name = "phm_logging", srcs = ["src/score/lcm/saf/logging/PhmLogger.cpp"], hdrs = ["src/score/lcm/saf/logging/PhmLogger.hpp"], - includes = [ - "src", - "src/score/lcm/saf/logging", - ], defines = select({ "//config:lm_use_cout_log": [], "//conditions:default": ["LC_LOG_SCORE_MW_LOG"], }), + includes = [ + "src", + "src/score/lcm/saf/logging", + ], visibility = ["//src:__pkg__"], deps = [ "@score_baselibs//score/mw/log", diff --git a/tests/README.md b/tests/README.md index 63f48a11..fd330fef 100644 --- a/tests/README.md +++ b/tests/README.md @@ -2,14 +2,14 @@ ## Bazel Args -| Arg | What It Does | -|:-----------------------------|:--------| -|`--sandbox_add_mount_pair=/tmp` | Uses the users real `/tmp` dir for testing (only for `--config=host` builds| -|`--test_output=streamed` | logs apear while running | -|`--test_output=all`| outputs all logs after test | -|`--nocache_test_results` | Reruns tests even if they have previously passed| -|`--runs_per_test=N` | Reruns the thests N times| -|`--compilation_mode=dbg` | Builds the binaries in debug mode (debug symbols & `NDEBUG` **not** defined ) | +| Arg | What It Does | +|:-------------------------------:-----------------------------------------------------------------------------| +|`--sandbox_add_mount_pair=/tmp` | Uses the users real `/tmp` dir for testing (only for `--config=host` builds | +|`--test_output=streamed` | logs apear while running | +|`--test_output=all` | outputs all logs after test | +|`--nocache_test_results` | Reruns tests even if they have previously passed | +|`--runs_per_test=N` | Reruns the thests N times | +|`--compilation_mode=dbg` | Builds the binaries in debug mode (debug symbols & `NDEBUG` **not** defined)| ## Pytest Args @@ -17,7 +17,7 @@ Note the arguments are written so that you can add them to the end of the bazel test command. -| Arg | What It Does | -|:-----------------------------|:--------| -|`--test_arg=--no-local-cleanup` | Integration tests don't cleanup after running | -|`--test_arg=-s` | More logging from the python test framework | +| Arg | What It Does | +|:-------------------------------|:----------------------------------------------| +|`--test_arg=--no-local-cleanup` | Integration tests don't cleanup after running | +|`--test_arg=-s` | More logging from the python test framework | From 96e8aa125adb4ef954f9b6ae76b9c9bc6aebd834 Mon Sep 17 00:00:00 2001 From: Maciej Kaszynski Date: Tue, 5 May 2026 13:59:40 +0100 Subject: [PATCH 05/10] Fixing build error --- .../src/score/lcm/saf/daemon/SwClusterHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/launch_manager_daemon/health_monitor_lib/src/score/lcm/saf/daemon/SwClusterHandler.cpp b/src/launch_manager_daemon/health_monitor_lib/src/score/lcm/saf/daemon/SwClusterHandler.cpp index 8ed7de8e..d1282643 100644 --- a/src/launch_manager_daemon/health_monitor_lib/src/score/lcm/saf/daemon/SwClusterHandler.cpp +++ b/src/launch_manager_daemon/health_monitor_lib/src/score/lcm/saf/daemon/SwClusterHandler.cpp @@ -61,7 +61,7 @@ bool SwClusterHandler::constructWorkers(std::shared_ptr Date: Tue, 5 May 2026 16:32:24 +0100 Subject: [PATCH 06/10] Enabling core dumps for started processes --- .../include/score/lcm/internal/config.hpp | 3 +++ .../src/internal/controlclientchannel.cpp | 2 -- .../process_group_manager/processlauncher.cpp | 21 +++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/launch_manager_daemon/common/include/score/lcm/internal/config.hpp b/src/launch_manager_daemon/common/include/score/lcm/internal/config.hpp index 95243a7c..0401a157 100644 --- a/src/launch_manager_daemon/common/include/score/lcm/internal/config.hpp +++ b/src/launch_manager_daemon/common/include/score/lcm/internal/config.hpp @@ -70,6 +70,9 @@ enum class ProcessLimits : std::uint32_t { maxLocalBuffSize = 32U ///< Maximum size for local buffer }; +// coverity[autosar_cpp14_a0_1_1_violation:INTENTIONAL] These are constants that are used globally. +constexpr std::uint32_t kCoreDumps = 1U; ///< Enable core dumps for managed processes (1 = enabled, 0 = disabled) + } // namespace lcm } // namespace internal diff --git a/src/launch_manager_daemon/common/src/internal/controlclientchannel.cpp b/src/launch_manager_daemon/common/src/internal/controlclientchannel.cpp index 3536aa81..d9536f3e 100644 --- a/src/launch_manager_daemon/common/src/internal/controlclientchannel.cpp +++ b/src/launch_manager_daemon/common/src/internal/controlclientchannel.cpp @@ -171,8 +171,6 @@ ControlClientChannelP ControlClientChannel::initializeControlClientChannel(int f std::next(static_cast(channelMemory), static_cast(sizeof(osal::IpcCommsSync))); result = ControlClientChannelP(static_cast(static_cast(controlClientStartPtr)), [](ControlClientChannel*) {}); - LM_LOG_DEBUG() << "ControlClientChannel mapped (creation path: " << std::boolalpha << (mem_ptr != nullptr) << ")"; - if (result) { std::unique_lock lock(init_mutex_); diff --git a/src/launch_manager_daemon/src/process_group_manager/processlauncher.cpp b/src/launch_manager_daemon/src/process_group_manager/processlauncher.cpp index 5d5af314..80fa6848 100644 --- a/src/launch_manager_daemon/src/process_group_manager/processlauncher.cpp +++ b/src/launch_manager_daemon/src/process_group_manager/processlauncher.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -170,6 +171,16 @@ void implementMemoryResourceLimits(const score::lcm::internal::osal::OsalConfig& sysexit(EXIT_FAILURE); } } + + if (score::lcm::internal::kCoreDumps != 0U) + { + limit.rlim_max = limit.rlim_cur = RLIM_INFINITY; + if (setrlimit(RLIMIT_CORE, &limit) == -1) + { + LM_LOG_ERROR() << "[New process] setrlimit(RLIMIT_CORE) failed:" << std::strerror(errno); + sysexit(EXIT_FAILURE); + } + } } void changeSecurityPolicy(const score::lcm::internal::osal::OsalConfig& config) @@ -424,6 +435,16 @@ OsalReturnType IProcess::setSchedulingAndSecurity(const OsalConfig& config) retval = OsalReturnType::kFail; } + // setuid() clears the dumpable flag + if (score::lcm::internal::kCoreDumps != 0U) + { + if (-1 == prctl(PR_SET_DUMPABLE, 1)) + { + LM_LOG_ERROR() << "prctl(PR_SET_DUMPABLE) failed:" << std::strerror(errno); + retval = OsalReturnType::kFail; + } + } + return retval; } From b373c1032eacd4e348927f64e37b2e174e8855b6 Mon Sep 17 00:00:00 2001 From: Maciej Kaszynski Date: Tue, 5 May 2026 16:48:57 +0100 Subject: [PATCH 07/10] Adding a fixture to auto download core dumps --- tests/utils/testing_utils/setup_test.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/utils/testing_utils/setup_test.py b/tests/utils/testing_utils/setup_test.py index c34fdd31..e3d4197a 100644 --- a/tests/utils/testing_utils/setup_test.py +++ b/tests/utils/testing_utils/setup_test.py @@ -39,3 +39,24 @@ def setup_test(request, target): assert res == 0, f"Couldn't extract tar {remote_tar}" logger.info("Test case setup finished") + + +@pytest.fixture(autouse=True, scope="function") +def download_core_dumps(target, remote_test_dir, test_output_dir): + """Downloads any core dump files from the remote after a test completes.""" + yield + + res, stdout = target.execute(f"find {remote_test_dir} -name 'core*' -type f") + if res != 0: + return + core_files = stdout.decode().strip().splitlines() + for remote_path in core_files: + remote_path = remote_path.strip() + if not remote_path: + continue + local_path = test_output_dir / Path(remote_path).name + try: + target.download(remote_path, str(local_path)) + logger.info(f"Downloaded core dump: {remote_path} -> {local_path}") + except Exception as e: + logger.warning(f"Failed to download core dump {remote_path}: {e}") From 062601da2844f8f55e53ae3b8a03be4352a72759 Mon Sep 17 00:00:00 2001 From: Maciej Kaszynski Date: Thu, 7 May 2026 11:14:14 +0100 Subject: [PATCH 08/10] Fixing dumps for QNX --- .../lcm/internal/osal/set_core_dumps.hpp | 38 +++++++++++++++++++ .../internal/osal/linux/set_core_dumps.cpp | 25 ++++++++++++ .../src/internal/osal/qnx/set_core_dumps.cpp | 23 +++++++++++ .../process_group_manager/processlauncher.cpp | 6 +-- 4 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 src/launch_manager_daemon/common/include/score/lcm/internal/osal/set_core_dumps.hpp create mode 100644 src/launch_manager_daemon/common/src/internal/osal/linux/set_core_dumps.cpp create mode 100644 src/launch_manager_daemon/common/src/internal/osal/qnx/set_core_dumps.cpp diff --git a/src/launch_manager_daemon/common/include/score/lcm/internal/osal/set_core_dumps.hpp b/src/launch_manager_daemon/common/include/score/lcm/internal/osal/set_core_dumps.hpp new file mode 100644 index 00000000..91501486 --- /dev/null +++ b/src/launch_manager_daemon/common/include/score/lcm/internal/osal/set_core_dumps.hpp @@ -0,0 +1,38 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + + +#ifndef SET_CORE_DUMPS_HPP_INCLUDED +#define SET_CORE_DUMPS_HPP_INCLUDED + +#include + +namespace score { + +namespace lcm { + +namespace internal { + +namespace osal { + +/// @brief Re-enable the dumpable flag after setuid(), which may clear it. +/// @details This doesn't need to be done on QNX and so this is a NO-OP. +/// @returns 0 on success, -1 on failure. +std::int32_t setCoreDumps() noexcept(true); + +} // namespace osal +} // namespace internal +} // namespace lcm +} // namespace score + +#endif diff --git a/src/launch_manager_daemon/common/src/internal/osal/linux/set_core_dumps.cpp b/src/launch_manager_daemon/common/src/internal/osal/linux/set_core_dumps.cpp new file mode 100644 index 00000000..00543b4a --- /dev/null +++ b/src/launch_manager_daemon/common/src/internal/osal/linux/set_core_dumps.cpp @@ -0,0 +1,25 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include + +#include + +namespace score::lcm::internal::osal +{ + +std::int32_t setCoreDumps() noexcept(true) +{ + return ::prctl(PR_SET_DUMPABLE, 1); +} + +} // namespace score::lcm::internal::osal diff --git a/src/launch_manager_daemon/common/src/internal/osal/qnx/set_core_dumps.cpp b/src/launch_manager_daemon/common/src/internal/osal/qnx/set_core_dumps.cpp new file mode 100644 index 00000000..cb294ba4 --- /dev/null +++ b/src/launch_manager_daemon/common/src/internal/osal/qnx/set_core_dumps.cpp @@ -0,0 +1,23 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include + +namespace score::lcm::internal::osal +{ + +std::int32_t setCoreDumps() noexcept(true) +{ + return 0; +} + +} // namespace score::lcm::internal::osal diff --git a/src/launch_manager_daemon/src/process_group_manager/processlauncher.cpp b/src/launch_manager_daemon/src/process_group_manager/processlauncher.cpp index 80fa6848..468524f1 100644 --- a/src/launch_manager_daemon/src/process_group_manager/processlauncher.cpp +++ b/src/launch_manager_daemon/src/process_group_manager/processlauncher.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -27,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -438,9 +438,9 @@ OsalReturnType IProcess::setSchedulingAndSecurity(const OsalConfig& config) // setuid() clears the dumpable flag if (score::lcm::internal::kCoreDumps != 0U) { - if (-1 == prctl(PR_SET_DUMPABLE, 1)) + if (-1 == score::lcm::internal::osal::setCoreDumps()) { - LM_LOG_ERROR() << "prctl(PR_SET_DUMPABLE) failed:" << std::strerror(errno); + LM_LOG_ERROR() << "setCoreDumps() failed:" << std::strerror(errno); retval = OsalReturnType::kFail; } } From f3bcd7338a9a3db3c4ceecf8cc5ed99fdd9d09cf Mon Sep 17 00:00:00 2001 From: Maciej Kaszynski Date: Thu, 7 May 2026 17:44:10 +0100 Subject: [PATCH 09/10] Addressing comments --- .../include/score/lcm/identifier_hash.hpp | 54 +++++----- .../common/include/score/lcm/internal/log.hpp | 4 +- .../process_group_manager/processlauncher.cpp | 100 +++++++----------- 3 files changed, 71 insertions(+), 87 deletions(-) diff --git a/src/launch_manager_daemon/common/include/score/lcm/identifier_hash.hpp b/src/launch_manager_daemon/common/include/score/lcm/identifier_hash.hpp index a97d67f5..b1e3b0a7 100644 --- a/src/launch_manager_daemon/common/include/score/lcm/identifier_hash.hpp +++ b/src/launch_manager_daemon/common/include/score/lcm/identifier_hash.hpp @@ -129,6 +129,27 @@ class IdentifierHash final std::size_t hash_id_ = 0; }; +namespace detail +{ + +template +inline StreamT& streamIdentifierHash(StreamT& stream, const IdentifierHash& id_hash) +{ + const auto& reg = IdentifierHash::get_registry(); + const auto it = reg.find(id_hash.data()); + if (it != reg.end()) + { + stream << it->second; + } + else + { + stream << ""; + } + return stream; +} + +} // namespace detail + /// @brief Overloaded stream insertion operator for IdentifierHash. /// /// This operator allows IdentifierHash objects to be output to an ostream. @@ -142,46 +163,27 @@ class IdentifierHash final /// @param os The output stream. /// @param id The IdentifierHash object to output. /// @return A reference to the output stream. -inline std::ostream& operator<<(std::ostream& os, const IdentifierHash& id) noexcept +inline std::ostream& operator<<(std::ostream& os, const IdentifierHash& id) noexcept(false) { - const auto& reg = IdentifierHash::get_registry(); - const auto it = reg.find(id.data()); - if (it != reg.end()) - { - os << it->second; - } - else - { - os << ""; - } - return os; + return detail::streamIdentifierHash(os, id); } } // namespace score::lcm -#if __has_include("score/mw/log/logger.h") +#ifdef LC_LOG_SCORE_MW_LOG #include "score/mw/log/logger.h" namespace score::lcm { -inline score::mw::log::LogStream& operator<<(score::mw::log::LogStream& log_stream, const IdentifierHash& id) noexcept +inline score::mw::log::LogStream& operator<<(score::mw::log::LogStream& log_stream, + const IdentifierHash& id) noexcept(false) { - const auto& reg = IdentifierHash::get_registry(); - const auto it = reg.find(id.data()); - if (it != reg.end()) - { - log_stream << it->second; - } - else - { - log_stream << ""; - } - return log_stream; + return detail::streamIdentifierHash(log_stream, id); } } // namespace score::lcm -#endif +#endif // LC_LOG_SCORE_MW_LOG #endif // IDENTIFIER_HASH_H_ diff --git a/src/launch_manager_daemon/common/include/score/lcm/internal/log.hpp b/src/launch_manager_daemon/common/include/score/lcm/internal/log.hpp index bb20f2f1..df2f76ac 100644 --- a/src/launch_manager_daemon/common/include/score/lcm/internal/log.hpp +++ b/src/launch_manager_daemon/common/include/score/lcm/internal/log.hpp @@ -278,7 +278,7 @@ inline Logger& _getLmLogger() noexcept #define LM_LOG_FATAL() (score::lcm::internal::_getLmLogger().LogFatal()) #define LM_LOG_ERROR() (score::lcm::internal::_getLmLogger().LogError()) #define LM_LOG_WARN() (score::lcm::internal::_getLmLogger().LogWarn()) -#define LM_LOG_INFO() (score::lcm::internal::_getLmLogger().LogWarn()) -#define LM_LOG_DEBUG() (score::lcm::internal::_getLmLogger().LogWarn()) +#define LM_LOG_INFO() (score::lcm::internal::_getLmLogger().LogInfo()) +#define LM_LOG_DEBUG() (score::lcm::internal::_getLmLogger().LogDebug()) #endif // LCM_LOG_HPP_INCLUDED diff --git a/src/launch_manager_daemon/src/process_group_manager/processlauncher.cpp b/src/launch_manager_daemon/src/process_group_manager/processlauncher.cpp index 468524f1..aba47aae 100644 --- a/src/launch_manager_daemon/src/process_group_manager/processlauncher.cpp +++ b/src/launch_manager_daemon/src/process_group_manager/processlauncher.cpp @@ -45,6 +45,37 @@ using score::lcm::internal::osal::CommsType; using score::lcm::internal::osal::IpcCommsSync; using score::lcm::internal::osal::sysexit; +/// @brief Applies the given limit. +/// @warning This will sysexit if the set is not succesful. +void applyLimitOrDie(const int resource, const rlimit& limit) noexcept(false) +{ + if (::setrlimit(resource, &limit) == -1) + { + LM_LOG_FATAL() << "[New process] Failed to set rlimit " + << std::strerror(errno); + sysexit(EXIT_FAILURE); + } +} + + +/// @brief Sets the limit if given a non-zero value, otherwise skips. +/// @warning This will sysexit if the set is not succesful. +void setLimit(const int resource, const std::size_t amount) noexcept +{ + if (amount == 0U) + { + return; + } + + const struct rlimit limit { + .rlim_cur = amount, + .rlim_max = amount, + }; + + applyLimitOrDie(resource, limit); +} + + void handleComms(score::lcm::internal::osal::ChildProcessConfig& param) { // kNoComms !fd3 & !fd4 @@ -122,65 +153,19 @@ void changeCurrentWorkingDirectory(const score::lcm::internal::osal::OsalConfig& void implementMemoryResourceLimits(const score::lcm::internal::osal::OsalConfig& config) { - rlimit limit; - - if (config.resource_limits_.data_ != 0U) - { - limit.rlim_max = limit.rlim_cur = config.resource_limits_.data_; - if (setrlimit(RLIMIT_DATA, &limit) == -1) - { - LM_LOG_ERROR() << "[New process] setrlimit(RLIMIT_DATA," << limit.rlim_cur - << ") failed:" << std::strerror(errno); - sysexit(EXIT_FAILURE); - } - } - - if (config.resource_limits_.as_ != 0U) - { - limit.rlim_max = limit.rlim_cur = config.resource_limits_.as_; - if (setrlimit(RLIMIT_AS, &limit) == -1) - { - LM_LOG_ERROR() << "[New process] setrlimit(RLIMIT_AS," << limit.rlim_cur - << "failed:" << std::strerror(errno); - sysexit(EXIT_FAILURE); - } - } - - if (config.resource_limits_.stack_ != 0U) - { - limit.rlim_max = limit.rlim_cur = config.resource_limits_.stack_; - if (setrlimit(RLIMIT_STACK, &limit) == -1) - { - LM_LOG_ERROR() << "[New process] setrlimit(RLIMIT_STACK," << limit.rlim_cur - << ") failed:" << std::strerror(errno); - sysexit(EXIT_FAILURE); - } - } + setLimit(RLIMIT_DATA, config.resource_limits_.data_); + setLimit(RLIMIT_AS, config.resource_limits_.as_); + setLimit(RLIMIT_STACK, config.resource_limits_.stack_); // Note about cpu limit: // Using setrlimit, this imposes a maximum time that a process will run for, which might not be // what you intend? Probably you'll want a maximum time in a time-slice, but you don't get that // with limits set by setrlimit... - if (config.resource_limits_.cpu_ != 0U) - { - limit.rlim_max = limit.rlim_cur = config.resource_limits_.cpu_; - if (setrlimit(RLIMIT_CPU, &limit) == -1) - { - LM_LOG_ERROR() << "[New process] setrlimit(RLIMIT_CPU," << limit.rlim_cur - << ") failed:" << std::strerror(errno); - sysexit(EXIT_FAILURE); - } - } + setLimit(RLIMIT_CPU, config.resource_limits_.cpu_); - if (score::lcm::internal::kCoreDumps != 0U) - { - limit.rlim_max = limit.rlim_cur = RLIM_INFINITY; - if (setrlimit(RLIMIT_CORE, &limit) == -1) - { - LM_LOG_ERROR() << "[New process] setrlimit(RLIMIT_CORE) failed:" << std::strerror(errno); - sysexit(EXIT_FAILURE); - } - } + // just set the max limit to enable core dumps + struct rlimit core_limit {.rlim_max = RLIM_INFINITY}; + applyLimitOrDie(RLIMIT_CORE, core_limit); } void changeSecurityPolicy(const score::lcm::internal::osal::OsalConfig& config) @@ -436,13 +421,10 @@ OsalReturnType IProcess::setSchedulingAndSecurity(const OsalConfig& config) } // setuid() clears the dumpable flag - if (score::lcm::internal::kCoreDumps != 0U) + if (-1 == score::lcm::internal::osal::setCoreDumps()) { - if (-1 == score::lcm::internal::osal::setCoreDumps()) - { - LM_LOG_ERROR() << "setCoreDumps() failed:" << std::strerror(errno); - retval = OsalReturnType::kFail; - } + LM_LOG_ERROR() << "setCoreDumps() failed:" << std::strerror(errno); + retval = OsalReturnType::kFail; } return retval; From 63581d285c72705c1eb87309634deb2dd16a5441 Mon Sep 17 00:00:00 2001 From: Maciej Kaszynski Date: Fri, 8 May 2026 11:50:51 +0100 Subject: [PATCH 10/10] Fixing formatting --- tests/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/README.md b/tests/README.md index fd330fef..d1321517 100644 --- a/tests/README.md +++ b/tests/README.md @@ -3,12 +3,12 @@ ## Bazel Args | Arg | What It Does | -|:-------------------------------:-----------------------------------------------------------------------------| +|:-------------------------------|:-----------------------------------------------------------------------------| |`--sandbox_add_mount_pair=/tmp` | Uses the users real `/tmp` dir for testing (only for `--config=host` builds | |`--test_output=streamed` | logs apear while running | |`--test_output=all` | outputs all logs after test | |`--nocache_test_results` | Reruns tests even if they have previously passed | -|`--runs_per_test=N` | Reruns the thests N times | +|`--runs_per_test=N` | Reruns the tests N times | |`--compilation_mode=dbg` | Builds the binaries in debug mode (debug symbols & `NDEBUG` **not** defined)| @@ -17,7 +17,7 @@ Note the arguments are written so that you can add them to the end of the bazel test command. -| Arg | What It Does | -|:-------------------------------|:----------------------------------------------| -|`--test_arg=--no-local-cleanup` | Integration tests don't cleanup after running | -|`--test_arg=-s` | More logging from the python test framework | +| Arg | What It Does | Example Command | +|:-------------------------------|:----------------------------------------------|:------------------------------------------------| +|`--test_arg=--no-local-cleanup` | Integration tests don't cleanup after running |`bazel test //... --test_arg=--no-local-cleanup` | +|`--test_arg=-s` | More logging from the python test framework |`bazel test //... --test_arg=-s` |