Merge pull request #8402 from liamwhite/better-step

core/debugger: Improved stepping mechanism and misc fixes
This commit is contained in:
Morph 2022-06-01 20:46:10 -04:00 committed by GitHub
commit a08c0ca9c3
16 changed files with 252 additions and 122 deletions

View file

@ -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);