diff options
Diffstat (limited to 'src/core/debugger/gdbstub.cpp')
-rw-r--r-- | src/core/debugger/gdbstub.cpp | 99 |
1 files changed, 92 insertions, 7 deletions
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index ee7598165..0c36069a6 100644 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp @@ -6,8 +6,7 @@ #include <optional> #include <thread> -#include <boost/asio.hpp> -#include <boost/process/async_pipe.hpp> +#include <boost/algorithm/string.hpp> #include "common/hex_util.h" #include "common/logging/log.h" @@ -114,6 +113,11 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction return; } + if (packet.starts_with("vCont")) { + HandleVCont(packet.substr(5), actions); + return; + } + std::string_view command{packet.substr(1, packet.size())}; switch (packet[0]) { @@ -122,6 +126,8 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction s64 thread_id{strtoll(command.data() + 1, nullptr, 16)}; if (thread_id >= 1) { thread = GetThreadByID(thread_id); + } else { + thread = backend.GetActiveThread(); } if (thread) { @@ -141,6 +147,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction } break; } + case 'Q': case 'q': HandleQuery(command); break; @@ -204,7 +211,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction break; } case 's': - actions.push_back(DebuggerAction::StepThread); + actions.push_back(DebuggerAction::StepThreadLocked); break; case 'C': case 'c': @@ -248,12 +255,47 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction } } +static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) { + switch (thread->GetWaitReasonForDebugging()) { + 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 id="{:x}" core="{:d}" name="Thread {:d}"/>)", - thread->GetThreadID(), thread->GetActiveCore(), thread->GetThreadID()); + buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="Thread {:d}">{}</thread>)", + thread->GetThreadID(), thread->GetActiveCore(), + thread->GetThreadID(), GetThreadState(thread)); } buffer += "</threads>"; 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<DebuggerAction>& 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<std::string> entries; + boost::split(entries, command.substr(1), boost::is_any_of(";")); + for (const auto& thread_action : entries) { + std::vector<std::string> 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<u8, 1> buf = {static_cast<u8>(status)}; LOG_TRACE(Debug_GDBStub, "Writing status: {}", status); backend.WriteToClient(buf); |