summaryrefslogtreecommitdiff
path: root/src/core/debugger/gdbstub.cpp
diff options
context:
space:
mode:
authorGravatar Liam2022-05-31 14:37:37 -0400
committerGravatar Liam2022-06-01 02:15:15 -0400
commit989d4a7a41f449af0ea09e34bee331a3a3ac8170 (patch)
treedf24bd9d7e6942b939e3ea42d08c0d65006e539f /src/core/debugger/gdbstub.cpp
parentcore/debugger: Implement new GDB stub debugger (diff)
downloadyuzu-989d4a7a41f449af0ea09e34bee331a3a3ac8170.tar.gz
yuzu-989d4a7a41f449af0ea09e34bee331a3a3ac8170.tar.xz
yuzu-989d4a7a41f449af0ea09e34bee331a3a3ac8170.zip
core/debugger: Improved stepping mechanism and misc fixes
Diffstat (limited to 'src/core/debugger/gdbstub.cpp')
-rw-r--r--src/core/debugger/gdbstub.cpp99
1 files changed, 92 insertions, 7 deletions
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 @@
6#include <optional> 6#include <optional>
7#include <thread> 7#include <thread>
8 8
9#include <boost/asio.hpp> 9#include <boost/algorithm/string.hpp>
10#include <boost/process/async_pipe.hpp>
11 10
12#include "common/hex_util.h" 11#include "common/hex_util.h"
13#include "common/logging/log.h" 12#include "common/logging/log.h"
@@ -114,6 +113,11 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
114 return; 113 return;
115 } 114 }
116 115
116 if (packet.starts_with("vCont")) {
117 HandleVCont(packet.substr(5), actions);
118 return;
119 }
120
117 std::string_view command{packet.substr(1, packet.size())}; 121 std::string_view command{packet.substr(1, packet.size())};
118 122
119 switch (packet[0]) { 123 switch (packet[0]) {
@@ -122,6 +126,8 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
122 s64 thread_id{strtoll(command.data() + 1, nullptr, 16)}; 126 s64 thread_id{strtoll(command.data() + 1, nullptr, 16)};
123 if (thread_id >= 1) { 127 if (thread_id >= 1) {
124 thread = GetThreadByID(thread_id); 128 thread = GetThreadByID(thread_id);
129 } else {
130 thread = backend.GetActiveThread();
125 } 131 }
126 132
127 if (thread) { 133 if (thread) {
@@ -141,6 +147,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
141 } 147 }
142 break; 148 break;
143 } 149 }
150 case 'Q':
144 case 'q': 151 case 'q':
145 HandleQuery(command); 152 HandleQuery(command);
146 break; 153 break;
@@ -204,7 +211,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
204 break; 211 break;
205 } 212 }
206 case 's': 213 case 's':
207 actions.push_back(DebuggerAction::StepThread); 214 actions.push_back(DebuggerAction::StepThreadLocked);
208 break; 215 break;
209 case 'C': 216 case 'C':
210 case 'c': 217 case 'c':
@@ -248,12 +255,47 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
248 } 255 }
249} 256}
250 257
258static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) {
259 switch (thread->GetWaitReasonForDebugging()) {
260 case Kernel::ThreadWaitReasonForDebugging::Sleep:
261 return "Sleep";
262 case Kernel::ThreadWaitReasonForDebugging::IPC:
263 return "IPC";
264 case Kernel::ThreadWaitReasonForDebugging::Synchronization:
265 return "Synchronization";
266 case Kernel::ThreadWaitReasonForDebugging::ConditionVar:
267 return "ConditionVar";
268 case Kernel::ThreadWaitReasonForDebugging::Arbitration:
269 return "Arbitration";
270 case Kernel::ThreadWaitReasonForDebugging::Suspended:
271 return "Suspended";
272 default:
273 return "Unknown";
274 }
275}
276
277static std::string GetThreadState(const Kernel::KThread* thread) {
278 switch (thread->GetState()) {
279 case Kernel::ThreadState::Initialized:
280 return "Initialized";
281 case Kernel::ThreadState::Waiting:
282 return fmt::format("Waiting ({})", GetThreadWaitReason(thread));
283 case Kernel::ThreadState::Runnable:
284 return "Runnable";
285 case Kernel::ThreadState::Terminated:
286 return "Terminated";
287 default:
288 return "Unknown";
289 }
290}
291
251void GDBStub::HandleQuery(std::string_view command) { 292void GDBStub::HandleQuery(std::string_view command) {
252 if (command.starts_with("TStatus")) { 293 if (command.starts_with("TStatus")) {
253 // no tracepoint support 294 // no tracepoint support
254 SendReply("T0"); 295 SendReply("T0");
255 } else if (command.starts_with("Supported")) { 296 } else if (command.starts_with("Supported")) {
256 SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+"); 297 SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;"
298 "vContSupported+;QStartNoAckMode+");
257 } else if (command.starts_with("Xfer:features:read:target.xml:")) { 299 } else if (command.starts_with("Xfer:features:read:target.xml:")) {
258 const auto offset{command.substr(30)}; 300 const auto offset{command.substr(30)};
259 const auto amount{command.substr(command.find(',') + 1)}; 301 const auto amount{command.substr(command.find(',') + 1)};
@@ -297,18 +339,57 @@ void GDBStub::HandleQuery(std::string_view command) {
297 339
298 const auto& threads = system.GlobalSchedulerContext().GetThreadList(); 340 const auto& threads = system.GlobalSchedulerContext().GetThreadList();
299 for (const auto& thread : threads) { 341 for (const auto& thread : threads) {
300 buffer += 342 buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="Thread {:d}">{}</thread>)",
301 fmt::format(R"(<thread id="{:x}" core="{:d}" name="Thread {:d}"/>)", 343 thread->GetThreadID(), thread->GetActiveCore(),
302 thread->GetThreadID(), thread->GetActiveCore(), thread->GetThreadID()); 344 thread->GetThreadID(), GetThreadState(thread));
303 } 345 }
304 346
305 buffer += "</threads>"; 347 buffer += "</threads>";
306 SendReply(buffer); 348 SendReply(buffer);
349 } else if (command.starts_with("Attached")) {
350 SendReply("0");
351 } else if (command.starts_with("StartNoAckMode")) {
352 no_ack = true;
353 SendReply(GDB_STUB_REPLY_OK);
307 } else { 354 } else {
308 SendReply(GDB_STUB_REPLY_EMPTY); 355 SendReply(GDB_STUB_REPLY_EMPTY);
309 } 356 }
310} 357}
311 358
359void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions) {
360 if (command == "?") {
361 // Continuing and stepping are supported
362 // (signal is ignored, but required for GDB to use vCont)
363 SendReply("vCont;c;C;s;S");
364 return;
365 }
366
367 Kernel::KThread* stepped_thread{nullptr};
368 bool lock_execution{true};
369
370 std::vector<std::string> entries;
371 boost::split(entries, command.substr(1), boost::is_any_of(";"));
372 for (const auto& thread_action : entries) {
373 std::vector<std::string> parts;
374 boost::split(parts, thread_action, boost::is_any_of(":"));
375
376 if (parts.size() == 1 && (parts[0] == "c" || parts[0].starts_with("C"))) {
377 lock_execution = false;
378 }
379 if (parts.size() == 2 && (parts[0] == "s" || parts[0].starts_with("S"))) {
380 stepped_thread = GetThreadByID(strtoll(parts[1].data(), nullptr, 16));
381 }
382 }
383
384 if (stepped_thread) {
385 backend.SetActiveThread(stepped_thread);
386 actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked
387 : DebuggerAction::StepThreadUnlocked);
388 } else {
389 actions.push_back(DebuggerAction::Continue);
390 }
391}
392
312Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { 393Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
313 const auto& threads{system.GlobalSchedulerContext().GetThreadList()}; 394 const auto& threads{system.GlobalSchedulerContext().GetThreadList()};
314 for (auto* thread : threads) { 395 for (auto* thread : threads) {
@@ -374,6 +455,10 @@ void GDBStub::SendReply(std::string_view data) {
374} 455}
375 456
376void GDBStub::SendStatus(char status) { 457void GDBStub::SendStatus(char status) {
458 if (no_ack) {
459 return;
460 }
461
377 std::array<u8, 1> buf = {static_cast<u8>(status)}; 462 std::array<u8, 1> buf = {static_cast<u8>(status)};
378 LOG_TRACE(Debug_GDBStub, "Writing status: {}", status); 463 LOG_TRACE(Debug_GDBStub, "Writing status: {}", status);
379 backend.WriteToClient(buf); 464 backend.WriteToClient(buf);