mirror of
				https://git.h3cjp.net/H3cJP/citra.git
				synced 2025-10-30 22:44:58 +00:00 
			
		
		
		
	Merge pull request #8446 from liamwhite/cmd-gdb
core/debugger: support operation in yuzu-cmd
This commit is contained in:
		
						commit
						a0407a8e64
					
				|  | @ -493,6 +493,12 @@ void System::Shutdown() { | |||
|     impl->Shutdown(); | ||||
| } | ||||
| 
 | ||||
| void System::DetachDebugger() { | ||||
|     if (impl->debugger) { | ||||
|         impl->debugger->NotifyShutdown(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::unique_lock<std::mutex> System::StallCPU() { | ||||
|     return impl->StallCPU(); | ||||
| } | ||||
|  |  | |||
|  | @ -160,6 +160,9 @@ public: | |||
|     /// Shutdown the emulated system.
 | ||||
|     void Shutdown(); | ||||
| 
 | ||||
|     /// Forcibly detach the debugger if it is running.
 | ||||
|     void DetachDebugger(); | ||||
| 
 | ||||
|     std::unique_lock<std::mutex> StallCPU(); | ||||
|     void UnstallCPU(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -42,6 +42,16 @@ static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) { | |||
|     return received_data; | ||||
| } | ||||
| 
 | ||||
| enum class SignalType { | ||||
|     Stopped, | ||||
|     ShuttingDown, | ||||
| }; | ||||
| 
 | ||||
| struct SignalInfo { | ||||
|     SignalType type; | ||||
|     Kernel::KThread* thread; | ||||
| }; | ||||
| 
 | ||||
| namespace Core { | ||||
| 
 | ||||
| class DebuggerImpl : public DebuggerBackend { | ||||
|  | @ -56,7 +66,7 @@ public: | |||
|         ShutdownServer(); | ||||
|     } | ||||
| 
 | ||||
|     bool NotifyThreadStopped(Kernel::KThread* thread) { | ||||
|     bool SignalDebugger(SignalInfo signal_info) { | ||||
|         std::scoped_lock lk{connection_lock}; | ||||
| 
 | ||||
|         if (stopped) { | ||||
|  | @ -64,9 +74,13 @@ public: | |||
|             // It should be ignored.
 | ||||
|             return false; | ||||
|         } | ||||
|         stopped = true; | ||||
| 
 | ||||
|         boost::asio::write(signal_pipe, boost::asio::buffer(&thread, sizeof(thread))); | ||||
|         // Set up the state.
 | ||||
|         stopped = true; | ||||
|         info = signal_info; | ||||
| 
 | ||||
|         // Write a single byte into the pipe to wake up the debug interface.
 | ||||
|         boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped))); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|  | @ -124,7 +138,7 @@ private: | |||
|         Common::SetCurrentThreadName("yuzu:Debugger"); | ||||
| 
 | ||||
|         // Set up the client signals for new data.
 | ||||
|         AsyncReceiveInto(signal_pipe, active_thread, [&](auto d) { PipeData(d); }); | ||||
|         AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); }); | ||||
|         AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); }); | ||||
| 
 | ||||
|         // Stop the emulated CPU.
 | ||||
|  | @ -142,9 +156,28 @@ private: | |||
|     } | ||||
| 
 | ||||
|     void PipeData(std::span<const u8> data) { | ||||
|         AllCoreStop(); | ||||
|         UpdateActiveThread(); | ||||
|         frontend->Stopped(active_thread); | ||||
|         switch (info.type) { | ||||
|         case SignalType::Stopped: | ||||
|             // Stop emulation.
 | ||||
|             AllCoreStop(); | ||||
| 
 | ||||
|             // Notify the client.
 | ||||
|             active_thread = info.thread; | ||||
|             UpdateActiveThread(); | ||||
|             frontend->Stopped(active_thread); | ||||
| 
 | ||||
|             break; | ||||
|         case SignalType::ShuttingDown: | ||||
|             frontend->ShuttingDown(); | ||||
| 
 | ||||
|             // Wait for emulation to shut down gracefully now.
 | ||||
|             suspend.reset(); | ||||
|             signal_pipe.close(); | ||||
|             client_socket.shutdown(boost::asio::socket_base::shutdown_both); | ||||
|             LOG_INFO(Debug_GDBStub, "Shut down server"); | ||||
| 
 | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void ClientData(std::span<const u8> data) { | ||||
|  | @ -246,7 +279,9 @@ private: | |||
|     boost::asio::ip::tcp::socket client_socket; | ||||
|     std::optional<std::unique_lock<std::mutex>> suspend; | ||||
| 
 | ||||
|     SignalInfo info; | ||||
|     Kernel::KThread* active_thread; | ||||
|     bool pipe_data; | ||||
|     bool stopped; | ||||
| 
 | ||||
|     std::array<u8, 4096> client_data; | ||||
|  | @ -263,7 +298,13 @@ Debugger::Debugger(Core::System& system, u16 port) { | |||
| Debugger::~Debugger() = default; | ||||
| 
 | ||||
| bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) { | ||||
|     return impl && impl->NotifyThreadStopped(thread); | ||||
|     return impl && impl->SignalDebugger(SignalInfo{SignalType::Stopped, thread}); | ||||
| } | ||||
| 
 | ||||
| void Debugger::NotifyShutdown() { | ||||
|     if (impl) { | ||||
|         impl->SignalDebugger(SignalInfo{SignalType::ShuttingDown, nullptr}); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| } // namespace Core
 | ||||
|  |  | |||
|  | @ -35,6 +35,11 @@ public: | |||
|      */ | ||||
|     bool NotifyThreadStopped(Kernel::KThread* thread); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Notify the debugger that a shutdown is being performed now and disconnect. | ||||
|      */ | ||||
|     void NotifyShutdown(); | ||||
| 
 | ||||
| private: | ||||
|     std::unique_ptr<DebuggerImpl> impl; | ||||
| }; | ||||
|  |  | |||
|  | @ -66,6 +66,11 @@ public: | |||
|      */ | ||||
|     virtual void Stopped(Kernel::KThread* thread) = 0; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Called when emulation is shutting down. | ||||
|      */ | ||||
|     virtual void ShuttingDown() = 0; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Called when new data is asynchronously received on the client socket. | ||||
|      * A list of actions to perform is returned. | ||||
|  |  | |||
|  | @ -106,6 +106,8 @@ GDBStub::~GDBStub() = default; | |||
| 
 | ||||
| void GDBStub::Connected() {} | ||||
| 
 | ||||
| void GDBStub::ShuttingDown() {} | ||||
| 
 | ||||
| void GDBStub::Stopped(Kernel::KThread* thread) { | ||||
|     SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP)); | ||||
| } | ||||
|  |  | |||
|  | @ -23,6 +23,7 @@ public: | |||
| 
 | ||||
|     void Connected() override; | ||||
|     void Stopped(Kernel::KThread* thread) override; | ||||
|     void ShuttingDown() override; | ||||
|     std::vector<DebuggerAction> ClientData(std::span<const u8> data) override; | ||||
| 
 | ||||
| private: | ||||
|  |  | |||
|  | @ -1591,6 +1591,7 @@ void GMainWindow::ShutdownGame() { | |||
| 
 | ||||
|     AllowOSSleep(); | ||||
| 
 | ||||
|     system->DetachDebugger(); | ||||
|     discord_rpc->Pause(); | ||||
|     emu_thread->RequestStop(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -344,6 +344,8 @@ void Config::ReadValues() { | |||
|     ReadSetting("Debugging", Settings::values.use_debug_asserts); | ||||
|     ReadSetting("Debugging", Settings::values.use_auto_stub); | ||||
|     ReadSetting("Debugging", Settings::values.disable_macro_jit); | ||||
|     ReadSetting("Debugging", Settings::values.use_gdbstub); | ||||
|     ReadSetting("Debugging", Settings::values.gdbstub_port); | ||||
| 
 | ||||
|     const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); | ||||
|     std::stringstream ss(title_list); | ||||
|  |  | |||
|  | @ -437,6 +437,11 @@ disable_macro_jit=false | |||
| # Presents guest frames as they become available. Experimental. | ||||
| # false: Disabled (default), true: Enabled | ||||
| disable_fps_limit=false | ||||
| # Determines whether to enable the GDB stub and wait for the debugger to attach before running. | ||||
| # false: Disabled (default), true: Enabled | ||||
| use_gdbstub=false | ||||
| # The port to use for the GDB server, if it is enabled. | ||||
| gdbstub_port=6543 | ||||
| 
 | ||||
| [WebService] | ||||
| # Whether or not to enable telemetry | ||||
|  |  | |||
|  | @ -162,7 +162,15 @@ void EmuWindow_SDL2::WaitEvent() { | |||
|     SDL_Event event; | ||||
| 
 | ||||
|     if (!SDL_WaitEvent(&event)) { | ||||
|         LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", SDL_GetError()); | ||||
|         const char* error = SDL_GetError(); | ||||
|         if (!error || strcmp(error, "") == 0) { | ||||
|             // https://github.com/libsdl-org/SDL/issues/5780
 | ||||
|             // Sometimes SDL will return without actually having hit an error condition;
 | ||||
|             // just ignore it in this case.
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", error); | ||||
|         exit(1); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -217,10 +217,19 @@ int main(int argc, char** argv) { | |||
|             [](VideoCore::LoadCallbackStage, size_t value, size_t total) {}); | ||||
|     } | ||||
| 
 | ||||
|     system.RegisterExitCallback([&] { | ||||
|         // Just exit right away.
 | ||||
|         exit(0); | ||||
|     }); | ||||
| 
 | ||||
|     void(system.Run()); | ||||
|     if (system.DebuggerEnabled()) { | ||||
|         system.InitializeDebugger(); | ||||
|     } | ||||
|     while (emu_window->IsOpen()) { | ||||
|         emu_window->WaitEvent(); | ||||
|     } | ||||
|     system.DetachDebugger(); | ||||
|     void(system.Pause()); | ||||
|     system.Shutdown(); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue