Merge a1da3bfc12f77aacae9fb409df40d5151465dea4 into 5f4422d68dc3530c353af1f87499de1c864b60ad

This commit is contained in:
hodlinator 2025-03-17 09:50:15 +07:00 committed by GitHub
commit 578c4430e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 98 additions and 1 deletions

View File

@ -132,6 +132,7 @@ option(ENABLE_HARDENING "Attempt to harden the resulting executables." ON)
option(REDUCE_EXPORTS "Attempt to reduce exported symbols in the resulting executables." OFF) option(REDUCE_EXPORTS "Attempt to reduce exported symbols in the resulting executables." OFF)
option(WERROR "Treat compiler warnings as errors." OFF) option(WERROR "Treat compiler warnings as errors." OFF)
option(WITH_CCACHE "Attempt to use ccache for compiling." ON) option(WITH_CCACHE "Attempt to use ccache for compiling." ON)
option(WAIT_FOR_DEBUGGER "Support for waiting during startup for debugger to be attached." OFF)
option(WITH_ZMQ "Enable ZMQ notifications." OFF) option(WITH_ZMQ "Enable ZMQ notifications." OFF)
if(WITH_ZMQ) if(WITH_ZMQ)
@ -246,6 +247,10 @@ target_compile_definitions(core_interface_debug INTERFACE
ABORT_ON_FAILED_ASSUME ABORT_ON_FAILED_ASSUME
) )
if(WAIT_FOR_DEBUGGER)
add_compile_definitions(WAIT_FOR_DEBUGGER=1)
endif()
if(WIN32) if(WIN32)
#[=[ #[=[
This build system supports two ways to build binaries for Windows. This build system supports two ways to build binaries for Windows.

View File

@ -28,7 +28,19 @@
#include <util/tokenpipe.h> #include <util/tokenpipe.h>
#include <util/translation.h> #include <util/translation.h>
#ifdef WIN32
#include <windows.h>
#include <debugapi.h>
#elif defined(__APPLE__)
#include <sys/sysctl.h>
#elif defined(__linux__)
#include <linux/prctl.h>
#include <sys/prctl.h>
#endif
#include <any> #include <any>
#include <cstdlib>
#include <fstream>
#include <functional> #include <functional>
#include <optional> #include <optional>
@ -159,6 +171,60 @@ static bool ProcessInitCommands(ArgsManager& args)
return false; return false;
} }
#ifdef WAIT_FOR_DEBUGGER
static int HandleWaitForDebugger(int argc, char* argv[])
{
for (int i = 0; i < argc; ++i) {
if (strcmp(argv[i], "-waitfordebugger") == 0) {
while (true) {
bool attached{false};
# if defined(__linux__)
// Allow any process to attach to us.
prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY);
std::ifstream sf{"/proc/self/status", std::ios::in};
if (!sf.good()) {
return EXIT_FAILURE;
}
std::string s;
uint pid{0};
while (sf >> s) {
if (s == "TracerPid:") {
sf >> pid;
break;
}
std::getline(sf, s);
}
attached = pid > 0;
# elif defined(__APPLE__)
const int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() };
kinfo_proc info;
size_t size{sizeof(info)};
const int ret{sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0)};
if (ret != EXIT_SUCCESS) {
return ret;
}
attached = info.kp_proc.p_flag & P_TRACED;
# elif defined(WIN32)
attached = IsDebuggerPresent();
# else
# error "Platform doesn't support -waitfordebugger.";
# endif // platform
if (attached) {
break;
} else {
std::this_thread::sleep_for(100ms);
}
}
}
}
return EXIT_SUCCESS;
}
#endif // WAIT_FOR_DEBUGGER
static bool AppInit(NodeContext& node) static bool AppInit(NodeContext& node)
{ {
bool fRet = false; bool fRet = false;
@ -254,6 +320,10 @@ static bool AppInit(NodeContext& node)
MAIN_FUNCTION MAIN_FUNCTION
{ {
#ifdef WAIT_FOR_DEBUGGER
if (int ret{HandleWaitForDebugger(argc, argv)}; ret != EXIT_SUCCESS) return ret;
#endif
#ifdef WIN32 #ifdef WIN32
common::WinCmdLineArgs winArgs; common::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get(); std::tie(argc, argv) = winArgs.get();

View File

@ -681,6 +681,7 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
hidden_args.emplace_back("-daemon"); hidden_args.emplace_back("-daemon");
hidden_args.emplace_back("-daemonwait"); hidden_args.emplace_back("-daemonwait");
#endif #endif
argsman.AddArg("-waitfordebugger", "Spin until a debugger is attached", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
// Add the hidden options // Add the hidden options
argsman.AddHiddenArgs(hidden_args); argsman.AddHiddenArgs(hidden_args);
@ -1119,6 +1120,12 @@ bool AppInitParameterInteraction(const ArgsManager& args)
} }
} }
#ifndef WAIT_FOR_DEBUGGER
if (args.GetBoolArg("-waitfordebugger", false)) {
return InitError(Untranslated("-waitfordebugger support was not included as WAIT_FOR_DEBUGGER is not #defined."));
}
#endif
return true; return true;
} }

View File

@ -205,6 +205,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
help="Explicitly use v1 transport (can be used to overwrite global --v2transport option)") help="Explicitly use v1 transport (can be used to overwrite global --v2transport option)")
parser.add_argument("--test_methods", dest="test_methods", nargs='*', parser.add_argument("--test_methods", dest="test_methods", nargs='*',
help="Run specified test methods sequentially instead of the full test. Use only for methods that do not depend on any context set up in run_test or other methods.") help="Run specified test methods sequentially instead of the full test. Use only for methods that do not depend on any context set up in run_test or other methods.")
parser.add_argument("--debug_runs", dest="debug_runs", nargs="*", type=int, default=[],
help="Node executions in the test to stall and wait for debugger, 0-based.")
self.add_options(parser) self.add_options(parser)
# Running TestShell in a Jupyter notebook causes an additional -f argument # Running TestShell in a Jupyter notebook causes an additional -f argument
@ -239,6 +241,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
PortSeed.n = self.options.port_seed PortSeed.n = self.options.port_seed
TestNode.debug_runs = self.options.debug_runs
def set_binary_paths(self): def set_binary_paths(self):
"""Update self.options with the paths of all binaries from environment variables or their default values""" """Update self.options with the paths of all binaries from environment variables or their default values"""

View File

@ -76,6 +76,9 @@ class TestNode():
To make things easier for the test writer, any unrecognised messages will To make things easier for the test writer, any unrecognised messages will
be dispatched to the RPC connection.""" be dispatched to the RPC connection."""
runs = 0
debug_runs = None
def __init__(self, i, datadir_path, *, chain, rpchost, timewait, timeout_factor, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False, use_valgrind=False, version=None, descriptors=False, v2transport=False): def __init__(self, i, datadir_path, *, chain, rpchost, timewait, timeout_factor, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False, use_valgrind=False, version=None, descriptors=False, v2transport=False):
""" """
Kwargs: Kwargs:
@ -253,10 +256,18 @@ class TestNode():
if env is not None: if env is not None:
subp_env.update(env) subp_env.update(env)
wait_for_debugger = TestNode.debug_runs is not None and TestNode.runs in TestNode.debug_runs
if wait_for_debugger:
extra_args.append("-waitfordebugger")
self.process = subprocess.Popen(self.args + extra_args, env=subp_env, stdout=stdout, stderr=stderr, cwd=cwd, **kwargs) self.process = subprocess.Popen(self.args + extra_args, env=subp_env, stdout=stdout, stderr=stderr, cwd=cwd, **kwargs)
self.running = True self.running = True
self.log.debug("bitcoind started, waiting for RPC to come up") if wait_for_debugger:
self.log.info(f"bitcoind started (run #{TestNode.runs}, node #{self.index}), waiting for debugger, PID: {self.process.pid}")
else:
self.log.debug(f"bitcoind started (run #{TestNode.runs}, node #{self.index}), waiting for RPC to come up")
TestNode.runs += 1
if self.start_perf: if self.start_perf:
self._start_perf() self._start_perf()