From 989d4a7a41f449af0ea09e34bee331a3a3ac8170 Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 31 May 2022 14:37:37 -0400 Subject: core/debugger: Improved stepping mechanism and misc fixes --- src/core/debugger/debugger.cpp | 61 ++++++++++++--------- src/core/debugger/debugger.h | 5 -- src/core/debugger/debugger_interface.h | 9 ++-- src/core/debugger/gdbstub.cpp | 99 +++++++++++++++++++++++++++++++--- src/core/debugger/gdbstub.h | 2 + 5 files changed, 134 insertions(+), 42 deletions(-) (limited to 'src/core/debugger') diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp index 7a2012d3c..a73f2279d 100644 --- a/src/core/debugger/debugger.cpp +++ b/src/core/debugger/debugger.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include @@ -84,31 +85,31 @@ public: return active_thread; } - bool IsStepping() const { - return stepping; - } - private: void InitializeServer(u16 port) { using boost::asio::ip::tcp; LOG_INFO(Debug_GDBStub, "Starting server on port {}...", port); - // Initialize the listening socket and accept a new client. - tcp::endpoint endpoint{boost::asio::ip::address_v4::loopback(), port}; - tcp::acceptor acceptor{io_context, endpoint}; - client_socket = acceptor.accept(); - // Run the connection thread. - connection_thread = std::jthread([&](std::stop_token stop_token) { + 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::acceptor acceptor{io_context, endpoint}; + + acceptor.async_accept(client_socket, [](const auto&) {}); + io_context.run_one(); + io_context.restart(); + + if (stop_token.stop_requested()) { + return; + } + ThreadLoop(stop_token); } catch (const std::exception& ex) { LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what()); } - - client_socket.shutdown(client_socket.shutdown_both); - client_socket.close(); }); } @@ -129,8 +130,7 @@ private: AllCoreStop(); // Set the active thread. - active_thread = ThreadList()[0]; - active_thread->Resume(Kernel::SuspendType::Debug); + UpdateActiveThread(); // Set up the frontend. frontend->Connected(); @@ -142,7 +142,7 @@ private: void PipeData(std::span data) { AllCoreStop(); - active_thread->Resume(Kernel::SuspendType::Debug); + UpdateActiveThread(); frontend->Stopped(active_thread); } @@ -156,18 +156,22 @@ private: stopped = true; } AllCoreStop(); - active_thread = ThreadList()[0]; - active_thread->Resume(Kernel::SuspendType::Debug); + UpdateActiveThread(); frontend->Stopped(active_thread); break; } case DebuggerAction::Continue: - stepping = false; + active_thread->SetStepState(Kernel::StepState::NotStepping); ResumeInactiveThreads(); AllCoreResume(); break; - case DebuggerAction::StepThread: - stepping = true; + case DebuggerAction::StepThreadUnlocked: + active_thread->SetStepState(Kernel::StepState::StepPending); + ResumeInactiveThreads(); + AllCoreResume(); + break; + case DebuggerAction::StepThreadLocked: + active_thread->SetStepState(Kernel::StepState::StepPending); SuspendInactiveThreads(); AllCoreResume(); break; @@ -212,10 +216,20 @@ private: for (auto* thread : ThreadList()) { if (thread != active_thread) { thread->Resume(Kernel::SuspendType::Debug); + thread->SetStepState(Kernel::StepState::NotStepping); } } } + void UpdateActiveThread() { + const auto& threads{ThreadList()}; + 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& ThreadList() { return system.GlobalSchedulerContext().GetThreadList(); } @@ -233,7 +247,6 @@ private: Kernel::KThread* active_thread; bool stopped; - bool stepping; std::array client_data; }; @@ -252,8 +265,4 @@ bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) { return impl && impl->NotifyThreadStopped(thread); } -bool Debugger::IsStepping() const { - return impl && impl->IsStepping(); -} - } // namespace Core diff --git a/src/core/debugger/debugger.h b/src/core/debugger/debugger.h index 7acd11815..ea36c6ab2 100644 --- a/src/core/debugger/debugger.h +++ b/src/core/debugger/debugger.h @@ -35,11 +35,6 @@ public: */ bool NotifyThreadStopped(Kernel::KThread* thread); - /** - * Returns whether a step is in progress. - */ - bool IsStepping() const; - private: std::unique_ptr impl; }; diff --git a/src/core/debugger/debugger_interface.h b/src/core/debugger/debugger_interface.h index 0b357fcb5..6ae3dc091 100644 --- a/src/core/debugger/debugger_interface.h +++ b/src/core/debugger/debugger_interface.h @@ -16,10 +16,11 @@ class KThread; namespace Core { enum class DebuggerAction { - Interrupt, // Stop emulation as soon as possible. - Continue, // Resume emulation. - StepThread, // Step the currently-active thread. - ShutdownEmulation, // Shut down the emulator. + Interrupt, ///< Stop emulation as soon as possible. + Continue, ///< Resume emulation. + StepThreadLocked, ///< Step the currently-active thread without resuming others. + StepThreadUnlocked, ///< Step the currently-active thread and resume others. + ShutdownEmulation, ///< Shut down the emulator. }; class DebuggerBackend { diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index 718c45952..44ebbe3e0 100644 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp @@ -6,8 +6,7 @@ #include #include -#include -#include +#include #include "common/hex_util.h" #include "common/logging/log.h" @@ -114,6 +113,11 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector= 1) { thread = GetThreadByID(thread_id); + } else { + thread = backend.GetActiveThread(); } if (thread) { @@ -141,6 +147,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vectorGetWaitReasonForDebugging()) { + case Kernel::ThreadWaitReasonForDebugging::Sleep: + return "Sleep"; + case Kernel::ThreadWaitReasonForDebugging::IPC: + return "IPC"; + case Kernel::ThreadWaitReasonForDebugging::Synchronization: + return "Synchronization"; + case Kernel::ThreadWaitReasonForDebugging::ConditionVar: + return "ConditionVar"; + case Kernel::ThreadWaitReasonForDebugging::Arbitration: + return "Arbitration"; + case Kernel::ThreadWaitReasonForDebugging::Suspended: + return "Suspended"; + default: + return "Unknown"; + } +} + +static std::string GetThreadState(const Kernel::KThread* thread) { + switch (thread->GetState()) { + case Kernel::ThreadState::Initialized: + return "Initialized"; + case Kernel::ThreadState::Waiting: + return fmt::format("Waiting ({})", GetThreadWaitReason(thread)); + case Kernel::ThreadState::Runnable: + return "Runnable"; + case Kernel::ThreadState::Terminated: + return "Terminated"; + default: + return "Unknown"; + } +} + void GDBStub::HandleQuery(std::string_view command) { if (command.starts_with("TStatus")) { // no tracepoint support SendReply("T0"); } else if (command.starts_with("Supported")) { - SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+"); + SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;" + "vContSupported+;QStartNoAckMode+"); } else if (command.starts_with("Xfer:features:read:target.xml:")) { const auto offset{command.substr(30)}; const auto amount{command.substr(command.find(',') + 1)}; @@ -297,18 +339,57 @@ void GDBStub::HandleQuery(std::string_view command) { const auto& threads = system.GlobalSchedulerContext().GetThreadList(); for (const auto& thread : threads) { - buffer += - fmt::format(R"()", - thread->GetThreadID(), thread->GetActiveCore(), thread->GetThreadID()); + buffer += fmt::format(R"({})", + thread->GetThreadID(), thread->GetActiveCore(), + thread->GetThreadID(), GetThreadState(thread)); } buffer += ""; SendReply(buffer); + } else if (command.starts_with("Attached")) { + SendReply("0"); + } else if (command.starts_with("StartNoAckMode")) { + no_ack = true; + SendReply(GDB_STUB_REPLY_OK); } else { SendReply(GDB_STUB_REPLY_EMPTY); } } +void GDBStub::HandleVCont(std::string_view command, std::vector& actions) { + if (command == "?") { + // Continuing and stepping are supported + // (signal is ignored, but required for GDB to use vCont) + SendReply("vCont;c;C;s;S"); + return; + } + + Kernel::KThread* stepped_thread{nullptr}; + bool lock_execution{true}; + + std::vector entries; + boost::split(entries, command.substr(1), boost::is_any_of(";")); + for (const auto& thread_action : entries) { + std::vector parts; + boost::split(parts, thread_action, boost::is_any_of(":")); + + if (parts.size() == 1 && (parts[0] == "c" || parts[0].starts_with("C"))) { + lock_execution = false; + } + if (parts.size() == 2 && (parts[0] == "s" || parts[0].starts_with("S"))) { + stepped_thread = GetThreadByID(strtoll(parts[1].data(), nullptr, 16)); + } + } + + if (stepped_thread) { + backend.SetActiveThread(stepped_thread); + actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked + : DebuggerAction::StepThreadUnlocked); + } else { + actions.push_back(DebuggerAction::Continue); + } +} + Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { const auto& threads{system.GlobalSchedulerContext().GetThreadList()}; for (auto* thread : threads) { @@ -374,6 +455,10 @@ void GDBStub::SendReply(std::string_view data) { } void GDBStub::SendStatus(char status) { + if (no_ack) { + return; + } + std::array buf = {static_cast(status)}; LOG_TRACE(Debug_GDBStub, "Writing status: {}", status); backend.WriteToClient(buf); diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h index b93a3a511..6f8ac2263 100644 --- a/src/core/debugger/gdbstub.h +++ b/src/core/debugger/gdbstub.h @@ -28,6 +28,7 @@ public: private: void ProcessData(std::vector& actions); void ExecuteCommand(std::string_view packet, std::vector& actions); + void HandleVCont(std::string_view command, std::vector& actions); void HandleQuery(std::string_view command); std::vector::const_iterator CommandEnd() const; std::optional DetachCommand(); @@ -42,6 +43,7 @@ private: std::unique_ptr arch; std::vector current_command; std::map replaced_instructions; + bool no_ack{}; }; } // namespace Core -- cgit v1.2.3