Merge pull request #22274 from hrydgard/headless-debugger-run

headless: say when we're waiting at the entry point, and add --debugger-run
This commit is contained in:
Henrik Rydgård
2026-09-09 17:13:04 -06:00
committed by GitHub
3 changed files with 33 additions and 12 deletions
+4 -3
View File
@@ -223,6 +223,7 @@ static const CommandLineParam g_autoParams[] = {
{POFF(generateInterpreterDispatch), CmdParamType::Bool, "generate-interpreter-dispatch", '\0', "Generate C++ interpreter dispatch code (ExecInstruction) to stdout and exit", CmdLineMode::Headless},
{POFF(resolutionScale), CmdParamType::Int, "resolution-scale", '\0', "Set the resolution scale factor"},
{POFF(debuggerPort), CmdParamType::Int, "debugger", '\0', "Enable the WebSocket debugger on this port (0 = pick automatically); see docs/WebSocketDebugger.md"},
{POFF(debuggerRunPort), CmdParamType::Int, "debugger-run", '\0', "Like --debugger, but starts running instead of waiting at the entry point", CmdLineMode::Headless},
{POFF(autoSaveLoadSymbols), CmdParamType::Bool, "auto-save-load-symbols", '\0', "Auto save/load per-module and per-game symbol files (see bAutoSaveLoadSymbols)", CmdLineMode::Both},
{POFF(bootVSH), CmdParamType::Bool, "vsh", '\0', "Boot the VSH (requires files dumped from a PSP in the flash0 directory)"},
{POFF(disableHLE), CmdParamType::Int, "disable-hle", '\0', "Bitmask of libraries to run the real firmware module for instead of our HLE", CmdLineMode::Both},
@@ -538,14 +539,14 @@ void CommandLineOptions::ApplyToConfig() const {
if (pauseMenuExit.has_value()) {
g_Config.bPauseMenuExitsEmulator = pauseMenuExit.value();
}
if (debuggerPort.has_value()) {
g_Config.iRemoteISOPort = debuggerPort.value();
if (DebuggerPort().has_value()) {
g_Config.iRemoteISOPort = DebuggerPort().value();
g_Config.DoNotSaveSetting(&g_Config.iRemoteISOPort);
g_Config.bRemoteDebuggerOnStartup = true;
g_Config.DoNotSaveSetting(&g_Config.bRemoteDebuggerOnStartup);
// --debugger=0 still means "pick any free port", but a specific port was asked for by
// something that intends to connect to it, so don't quietly come up on a different one.
WebServerSetRequireExactPort(debuggerPort.value() != 0);
WebServerSetRequireExactPort(DebuggerPort().value() != 0);
}
if (autoSaveLoadSymbols.has_value()) {
+11
View File
@@ -46,6 +46,17 @@ struct CommandLineOptions {
// Enables the WebSocket debugger on startup, on this port (0 = pick automatically).
// Also breaks the CPU at start in the headless build. See docs/WebSocketDebugger.md.
std::optional<int> debuggerPort;
// Same, but without breaking at the entry point first.
std::optional<int> debuggerRunPort;
// The port either of the two debugger options asked for, if any.
std::optional<int> DebuggerPort() const {
return debuggerPort.has_value() ? debuggerPort : debuggerRunPort;
}
// --debugger waits at the entry point for a client to drive it; --debugger-run doesn't.
bool DebuggerBreaksAtStart() const {
return debuggerPort.has_value();
}
// Overrides g_Config.bAutoSaveLoadSymbols for this run only (see SymbolMap::SaveModuleSymbols/
// LoadModuleSymbols and Core/HLE/sceKernelModule.cpp) - handy for headless runs that want
+18 -9
View File
@@ -581,7 +581,7 @@ int main(int argc, const char* argv[]) {
// Needed before any sockets can be used (WSAStartup on Windows) - without this, the
// WebSocket debugger silently fails to listen. Only done when requested since headless
// otherwise has no use for networking.
if (cmdLineOptions.debuggerPort.has_value())
if (cmdLineOptions.DebuggerPort().has_value())
net::Init();
AutoTestOptions testOptions{};
@@ -767,9 +767,12 @@ int main(int argc, const char* argv[]) {
// overrides above, so a matching command line flag always wins.
cmdLineOptions.ApplyToConfig();
// Run all modules as HLE - a headless run normally has no firmware to load them from. An
// explicit --disable-hle means the caller does have a dump and wants the real thing, so leave
// the modules they asked for alone.
// Run all modules as HLE - a headless run normally has no firmware to load them from, and the
// homebrew that pspautotests is made of doesn't ship the user libraries a retail disc does, so
// even the graduated modules (scePsmfPlayer and friends) have nothing real to run. An explicit
// --disable-hle means the caller does have what's needed and wants the real thing, so leave
// the modules they asked for alone - including the graduated ones, which is how you get a disc
// game's own libpsmfplayer.prx to run here the way it does in the app.
g_Config.iForceEnableHLE = 0xFFFFFFFF & ~g_Config.iDisableHLE;
@@ -936,13 +939,19 @@ int main(int argc, const char* argv[]) {
return printUsage(cmdLineOptions, argv[0], argc <= 1 ? NULL : "No executables specified");
}
if (cmdLineOptions.debuggerPort.has_value()) {
coreParameter.startBreak = true;
if (cmdLineOptions.DebuggerPort().has_value()) {
coreParameter.startBreak = cmdLineOptions.DebuggerBreaksAtStart();
if (coreParameter.startBreak) {
// Worth saying out loud: a run that looks frozen with zero progress is usually this,
// not the game. Send cpu.resume, or use --debugger-run to skip the wait entirely.
fprintf(stderr, "--debugger: breaking at the entry point, waiting for a client to "
"resume the CPU (cpu.resume). Use --debugger-run to start running instead.\n");
}
StartWebServer(WebServerFlags::DEBUGGER);
// We break at start and wait for a debugger to drive us, so coming up without one just
// hangs until the timeout. Better to say why and bail - see WebServerSetRequireExactPort().
if (!WebServerWaitForStartup()) {
fprintf(stderr, "Failed to start the debugger web server on port %d\n", cmdLineOptions.debuggerPort.value());
fprintf(stderr, "Failed to start the debugger web server on port %d\n", cmdLineOptions.DebuggerPort().value());
// The server thread has exited but is still joinable - without this, its std::thread
// destructor would call std::terminate() on the way out and we'd abort instead of
// returning a useful exit code.
@@ -992,7 +1001,7 @@ int main(int argc, const char* argv[]) {
delete graphicsContext;
if (cmdLineOptions.debuggerPort.has_value()) {
if (cmdLineOptions.DebuggerPort().has_value()) {
ShutdownWebServer();
}
@@ -1006,7 +1015,7 @@ int main(int argc, const char* argv[]) {
g_VFS.Clear();
g_logManager.Shutdown();
if (cmdLineOptions.debuggerPort.has_value()) {
if (cmdLineOptions.DebuggerPort().has_value()) {
net::Shutdown();
}
TimeShutdown();