diff options
| author | 2022-06-01 20:46:10 -0400 | |
|---|---|---|
| committer | 2022-06-01 20:46:10 -0400 | |
| commit | 858f8ac6d9f39a1be95dc2f5e83c752b725136ad (patch) | |
| tree | 26d5733fe69193e76402bad78b1a3a8b791b6095 /src/core/debugger/gdbstub.cpp | |
| parent | Merge pull request #8400 from Docteh/fullscreen_glitch (diff) | |
| parent | core/debugger: Improved stepping mechanism and misc fixes (diff) | |
| download | yuzu-858f8ac6d9f39a1be95dc2f5e83c752b725136ad.tar.gz yuzu-858f8ac6d9f39a1be95dc2f5e83c752b725136ad.tar.xz yuzu-858f8ac6d9f39a1be95dc2f5e83c752b725136ad.zip | |
Merge pull request #8402 from liamwhite/better-step
core/debugger: Improved stepping mechanism and misc fixes
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 @@ | |||
| 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 | ||
| 258 | static 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 | |||
| 277 | static 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 | |||
| 251 | void GDBStub::HandleQuery(std::string_view command) { | 292 | void 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 | ||
| 359 | void 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 | |||
| 312 | Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { | 393 | Kernel::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 | ||
| 376 | void GDBStub::SendStatus(char status) { | 457 | void 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); |