test: don't throw from the destructor of DebugLogHelper
What changed, and why it matters
This is a small cleanup in Bitcoin Core's own test code. It changes a test helper so that, if a test fails to find an expected log message, it prints an error and aborts the program instead of throwing an exception from a destructor. Throwing from destructors is considered bad C++ practice because it can cause crashes or unexpected behavior during stack unwinding, but this only affects internal test infrastructure and is not a security vulnerability in the live Bitcoin network software.
No action required. Treat as a normal code-quality/test-hardening change. Reviewers can verify the destructor is now noexcept and that test failures still terminate the process visibly.
Security signals we found
throwing destructor removed
test-only code change
std::abort introduced for test failure path
no live network or consensus code affected
Evidence from the diff
The commit modifies DebugLogHelper, a RAII helper used only in the test suite (src/test/util/logging.cpp/.h). Previously ~DebugLogHelper() was marked noexcept(false) and called check_found(), which threw std::runtime_error if the expected debug log message was not found. The patch removes check_found(), makes the destructor noexcept (implicitly) and non-throwing, and instead calls std::abort() after printing to stderr. This eliminates a throwing destructor, which is undefined-behavior-prone during stack unwinding, but the change is confined to test-only code invoked through ASSERT_DEBUG_LOG macros.
Changed components
src/test/util/logging.cppsrc/test/util/logging.hDebugLogHelper RAII test helperInspect captured patch +14 / −12
diff --git a/src/test/util/logging.cpp b/src/test/util/logging.cpp
index 753e50d0..78ad9c83 100644
--- a/src/test/util/logging.cpp
+++ b/src/test/util/logging.cpp
@@ -8,7 +8,8 @@
#include <noui.h>
#include <tinyformat.h>
-#include <stdexcept>
+#include <cstdlib>
+#include <iostream>
DebugLogHelper::DebugLogHelper(std::string message, MatchFn match)
: m_message{std::move(message)}, m_match(std::move(match))
@@ -21,11 +22,12 @@ DebugLogHelper::DebugLogHelper(std::string message, MatchFn match)
noui_test_redirect();
}
-void DebugLogHelper::check_found()
+DebugLogHelper::~DebugLogHelper()
{
noui_reconnect();
LogInstance().DeleteCallback(m_print_connection);
if (!m_found && m_match(nullptr)) {
- throw std::runtime_error(strprintf("'%s' not found in debug log\n", m_message));
+ tfm::format(std::cerr, "Fatal error: expected message not found in the debug log: '%s'\n", m_message);
+ std::abort();
}
}
diff --git a/src/test/util/logging.h b/src/test/util/logging.h
index 62b11c28..104a21f9 100644
--- a/src/test/util/logging.h
+++ b/src/test/util/logging.h
@@ -13,10 +13,7 @@
class DebugLogHelper
{
- const std::string m_message;
- bool m_found{false};
- std::list<std::function<void(const std::string&)>>::iterator m_print_connection;
-
+public:
//! Custom match checking function.
//!
//! Invoked with pointers to lines containing matching strings, and with
@@ -27,13 +24,16 @@ class DebugLogHelper
//! (2) raising an error in check_found if no match was found
//! Can return false to do the opposite in either case.
using MatchFn = std::function<bool(const std::string* line)>;
- MatchFn m_match;
-
- void check_found();
-public:
explicit DebugLogHelper(std::string message, MatchFn match = [](const std::string*){ return true; });
- ~DebugLogHelper() noexcept(false) { check_found(); }
+
+ ~DebugLogHelper();
+
+private:
+ const std::string m_message;
+ bool m_found{false};
+ std::list<std::function<void(const std::string&)>>::iterator m_print_connection;
+ MatchFn m_match;
};
#define ASSERT_DEBUG_LOG(message) DebugLogHelper UNIQUE_NAME(debugloghelper)(message)
Why this scored 16/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.