diff options
Diffstat (limited to '')
-rw-r--r-- | src/core/debugger/debugger.cpp | 154 |
1 files changed, 100 insertions, 54 deletions
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp index 8d64990ed..ac64d2f9d 100644 --- a/src/core/debugger/debugger.cpp +++ b/src/core/debugger/debugger.cpp @@ -42,6 +42,18 @@ static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) { return received_data; } +enum class SignalType { + Stopped, + Watchpoint, + ShuttingDown, +}; + +struct SignalInfo { + SignalType type; + Kernel::KThread* thread; + const Kernel::DebugWatchpoint* watchpoint; +}; + namespace Core { class DebuggerImpl : public DebuggerBackend { @@ -56,17 +68,23 @@ public: ShutdownServer(); } - bool NotifyThreadStopped(Kernel::KThread* thread) { - std::scoped_lock lk{connection_lock}; + bool SignalDebugger(SignalInfo signal_info) { + { + std::scoped_lock lk{connection_lock}; + + if (stopped) { + // Do not notify the debugger about another event. + // It should be ignored. + return false; + } - if (stopped) { - // Do not notify the debugger about another event. - // It should be ignored. - return false; + // Set up the state. + stopped = true; + info = signal_info; } - stopped = true; - boost::asio::write(signal_pipe, boost::asio::buffer(&thread, sizeof(thread))); + // 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; } @@ -96,7 +114,7 @@ private: connection_thread = std::jthread([&, port](std::stop_token stop_token) { try { // Initialize the listening socket and accept a new client. - tcp::endpoint endpoint{boost::asio::ip::address_v4::loopback(), port}; + tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port}; tcp::acceptor acceptor{io_context, endpoint}; acceptor.async_accept(client_socket, [](const auto&) {}); @@ -124,12 +142,9 @@ 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. - AllCoreStop(); - // Set the active thread. UpdateActiveThread(); @@ -142,9 +157,33 @@ private: } void PipeData(std::span<const u8> data) { - AllCoreStop(); - UpdateActiveThread(); - frontend->Stopped(active_thread); + switch (info.type) { + case SignalType::Stopped: + case SignalType::Watchpoint: + // Stop emulation. + PauseEmulation(); + + // Notify the client. + active_thread = info.thread; + UpdateActiveThread(); + + if (info.type == SignalType::Watchpoint) { + frontend->Watchpoint(active_thread, *info.watchpoint); + } else { + frontend->Stopped(active_thread); + } + + break; + case SignalType::ShuttingDown: + frontend->ShuttingDown(); + + // Wait for emulation to shut down gracefully now. + 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) { @@ -156,32 +195,29 @@ private: std::scoped_lock lk{connection_lock}; stopped = true; } - AllCoreStop(); + PauseEmulation(); UpdateActiveThread(); frontend->Stopped(active_thread); break; } case DebuggerAction::Continue: - active_thread->SetStepState(Kernel::StepState::NotStepping); - ResumeInactiveThreads(); - AllCoreResume(); + MarkResumed([&] { ResumeEmulation(); }); break; case DebuggerAction::StepThreadUnlocked: - active_thread->SetStepState(Kernel::StepState::StepPending); - ResumeInactiveThreads(); - AllCoreResume(); + MarkResumed([&] { + active_thread->SetStepState(Kernel::StepState::StepPending); + active_thread->Resume(Kernel::SuspendType::Debug); + ResumeEmulation(active_thread); + }); break; - case DebuggerAction::StepThreadLocked: - active_thread->SetStepState(Kernel::StepState::StepPending); - SuspendInactiveThreads(); - AllCoreResume(); + case DebuggerAction::StepThreadLocked: { + MarkResumed([&] { + active_thread->SetStepState(Kernel::StepState::StepPending); + active_thread->Resume(Kernel::SuspendType::Debug); + }); break; + } case DebuggerAction::ShutdownEmulation: { - // Suspend all threads and release any locks held - active_thread->RequestSuspend(Kernel::SuspendType::Debug); - SuspendInactiveThreads(); - AllCoreResume(); - // Spawn another thread that will exit after shutdown, // to avoid a deadlock Core::System* system_ref{&system}; @@ -193,33 +229,33 @@ private: } } - void AllCoreStop() { - if (!suspend) { - suspend = system.StallCPU(); + void PauseEmulation() { + // Put all threads to sleep on next scheduler round. + for (auto* thread : ThreadList()) { + thread->RequestSuspend(Kernel::SuspendType::Debug); } - } - void AllCoreResume() { - stopped = false; - system.UnstallCPU(); - suspend.reset(); + // Signal an interrupt so that scheduler will fire. + system.Kernel().InterruptAllPhysicalCores(); } - void SuspendInactiveThreads() { + void ResumeEmulation(Kernel::KThread* except = nullptr) { + // Wake up all threads. for (auto* thread : ThreadList()) { - if (thread != active_thread) { - thread->RequestSuspend(Kernel::SuspendType::Debug); + if (thread == except) { + continue; } + + thread->SetStepState(Kernel::StepState::NotStepping); + thread->Resume(Kernel::SuspendType::Debug); } } - void ResumeInactiveThreads() { - for (auto* thread : ThreadList()) { - if (thread != active_thread) { - thread->Resume(Kernel::SuspendType::Debug); - thread->SetStepState(Kernel::StepState::NotStepping); - } - } + template <typename Callback> + void MarkResumed(Callback&& cb) { + std::scoped_lock lk{connection_lock}; + stopped = false; + cb(); } void UpdateActiveThread() { @@ -227,8 +263,6 @@ private: if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) { active_thread = threads[0]; } - active_thread->Resume(Kernel::SuspendType::Debug); - active_thread->SetStepState(Kernel::StepState::NotStepping); } const std::vector<Kernel::KThread*>& ThreadList() { @@ -244,9 +278,10 @@ private: boost::asio::io_context io_context; boost::process::async_pipe signal_pipe; 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,18 @@ 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, nullptr}); +} + +bool Debugger::NotifyThreadWatchpoint(Kernel::KThread* thread, + const Kernel::DebugWatchpoint& watch) { + return impl && impl->SignalDebugger(SignalInfo{SignalType::Watchpoint, thread, &watch}); +} + +void Debugger::NotifyShutdown() { + if (impl) { + impl->SignalDebugger(SignalInfo{SignalType::ShuttingDown, nullptr, nullptr}); + } } } // namespace Core |