diff --git a/Core/CmdLine.cpp b/Core/CmdLine.cpp index bde980c48b..f29a2f0528 100644 --- a/Core/CmdLine.cpp +++ b/Core/CmdLine.cpp @@ -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()) { diff --git a/Core/CmdLine.h b/Core/CmdLine.h index 2adbac398c..a20df091e7 100644 --- a/Core/CmdLine.h +++ b/Core/CmdLine.h @@ -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 debuggerPort; + // Same, but without breaking at the entry point first. + std::optional debuggerRunPort; + + // The port either of the two debugger options asked for, if any. + std::optional 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 diff --git a/headless/Headless.cpp b/headless/Headless.cpp index c04566fb96..fcb61f6604 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -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();