diff options
| author | 2022-06-21 16:04:57 -0700 | |
|---|---|---|
| committer | 2022-06-21 16:04:57 -0700 | |
| commit | 737c446fc18618cf80a1243104ac8f5114c29a22 (patch) | |
| tree | 881e88bcc52e4d0639906f61a9b1a5292e36767d /src/core/debugger/gdbstub.cpp | |
| parent | Merge pull request #8468 from liamwhite/dispatch-tracking (diff) | |
| parent | core/debugger: memory breakpoint support (diff) | |
| download | yuzu-737c446fc18618cf80a1243104ac8f5114c29a22.tar.gz yuzu-737c446fc18618cf80a1243104ac8f5114c29a22.tar.xz yuzu-737c446fc18618cf80a1243104ac8f5114c29a22.zip | |
Merge pull request #8432 from liamwhite/watchpoint
core/debugger: memory breakpoint support
Diffstat (limited to 'src/core/debugger/gdbstub.cpp')
| -rw-r--r-- | src/core/debugger/gdbstub.cpp | 139 |
1 files changed, 118 insertions, 21 deletions
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index 52e76f659..f5e9a303d 100644 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp | |||
| @@ -112,6 +112,23 @@ void GDBStub::Stopped(Kernel::KThread* thread) { | |||
| 112 | SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP)); | 112 | SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP)); |
| 113 | } | 113 | } |
| 114 | 114 | ||
| 115 | void GDBStub::Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) { | ||
| 116 | const auto status{arch->ThreadStatus(thread, GDB_STUB_SIGTRAP)}; | ||
| 117 | |||
| 118 | switch (watch.type) { | ||
| 119 | case Kernel::DebugWatchpointType::Read: | ||
| 120 | SendReply(fmt::format("{}rwatch:{:x};", status, watch.start_address)); | ||
| 121 | break; | ||
| 122 | case Kernel::DebugWatchpointType::Write: | ||
| 123 | SendReply(fmt::format("{}watch:{:x};", status, watch.start_address)); | ||
| 124 | break; | ||
| 125 | case Kernel::DebugWatchpointType::ReadOrWrite: | ||
| 126 | default: | ||
| 127 | SendReply(fmt::format("{}awatch:{:x};", status, watch.start_address)); | ||
| 128 | break; | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 115 | std::vector<DebuggerAction> GDBStub::ClientData(std::span<const u8> data) { | 132 | std::vector<DebuggerAction> GDBStub::ClientData(std::span<const u8> data) { |
| 116 | std::vector<DebuggerAction> actions; | 133 | std::vector<DebuggerAction> actions; |
| 117 | current_command.insert(current_command.end(), data.begin(), data.end()); | 134 | current_command.insert(current_command.end(), data.begin(), data.end()); |
| @@ -278,41 +295,121 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction | |||
| 278 | case 'c': | 295 | case 'c': |
| 279 | actions.push_back(DebuggerAction::Continue); | 296 | actions.push_back(DebuggerAction::Continue); |
| 280 | break; | 297 | break; |
| 281 | case 'Z': { | 298 | case 'Z': |
| 282 | const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; | 299 | HandleBreakpointInsert(command); |
| 283 | const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))}; | 300 | break; |
| 301 | case 'z': | ||
| 302 | HandleBreakpointRemove(command); | ||
| 303 | break; | ||
| 304 | default: | ||
| 305 | SendReply(GDB_STUB_REPLY_EMPTY); | ||
| 306 | break; | ||
| 307 | } | ||
| 308 | } | ||
| 284 | 309 | ||
| 285 | if (system.Memory().IsValidVirtualAddress(addr)) { | 310 | enum class BreakpointType { |
| 286 | replaced_instructions[addr] = system.Memory().Read32(addr); | 311 | Software = 0, |
| 287 | system.Memory().Write32(addr, arch->BreakpointInstruction()); | 312 | Hardware = 1, |
| 288 | system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32)); | 313 | WriteWatch = 2, |
| 314 | ReadWatch = 3, | ||
| 315 | AccessWatch = 4, | ||
| 316 | }; | ||
| 317 | |||
| 318 | void GDBStub::HandleBreakpointInsert(std::string_view command) { | ||
| 319 | const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))}; | ||
| 320 | const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; | ||
| 321 | const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') - | ||
| 322 | command.begin() + 1}; | ||
| 323 | const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))}; | ||
| 324 | const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))}; | ||
| 325 | |||
| 326 | if (!system.Memory().IsValidVirtualAddressRange(addr, size)) { | ||
| 327 | SendReply(GDB_STUB_REPLY_ERR); | ||
| 328 | return; | ||
| 329 | } | ||
| 289 | 330 | ||
| 290 | SendReply(GDB_STUB_REPLY_OK); | 331 | bool success{}; |
| 291 | } else { | 332 | |
| 292 | SendReply(GDB_STUB_REPLY_ERR); | 333 | switch (type) { |
| 293 | } | 334 | case BreakpointType::Software: |
| 335 | replaced_instructions[addr] = system.Memory().Read32(addr); | ||
| 336 | system.Memory().Write32(addr, arch->BreakpointInstruction()); | ||
| 337 | system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32)); | ||
| 338 | success = true; | ||
| 339 | break; | ||
| 340 | case BreakpointType::WriteWatch: | ||
| 341 | success = system.CurrentProcess()->InsertWatchpoint(system, addr, size, | ||
| 342 | Kernel::DebugWatchpointType::Write); | ||
| 294 | break; | 343 | break; |
| 344 | case BreakpointType::ReadWatch: | ||
| 345 | success = system.CurrentProcess()->InsertWatchpoint(system, addr, size, | ||
| 346 | Kernel::DebugWatchpointType::Read); | ||
| 347 | break; | ||
| 348 | case BreakpointType::AccessWatch: | ||
| 349 | success = system.CurrentProcess()->InsertWatchpoint( | ||
| 350 | system, addr, size, Kernel::DebugWatchpointType::ReadOrWrite); | ||
| 351 | break; | ||
| 352 | case BreakpointType::Hardware: | ||
| 353 | default: | ||
| 354 | SendReply(GDB_STUB_REPLY_EMPTY); | ||
| 355 | return; | ||
| 356 | } | ||
| 357 | |||
| 358 | if (success) { | ||
| 359 | SendReply(GDB_STUB_REPLY_OK); | ||
| 360 | } else { | ||
| 361 | SendReply(GDB_STUB_REPLY_ERR); | ||
| 295 | } | 362 | } |
| 296 | case 'z': { | 363 | } |
| 297 | const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; | 364 | |
| 298 | const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))}; | 365 | void GDBStub::HandleBreakpointRemove(std::string_view command) { |
| 366 | const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))}; | ||
| 367 | const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; | ||
| 368 | const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') - | ||
| 369 | command.begin() + 1}; | ||
| 370 | const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))}; | ||
| 371 | const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))}; | ||
| 372 | |||
| 373 | if (!system.Memory().IsValidVirtualAddressRange(addr, size)) { | ||
| 374 | SendReply(GDB_STUB_REPLY_ERR); | ||
| 375 | return; | ||
| 376 | } | ||
| 377 | |||
| 378 | bool success{}; | ||
| 299 | 379 | ||
| 380 | switch (type) { | ||
| 381 | case BreakpointType::Software: { | ||
| 300 | const auto orig_insn{replaced_instructions.find(addr)}; | 382 | const auto orig_insn{replaced_instructions.find(addr)}; |
| 301 | if (system.Memory().IsValidVirtualAddress(addr) && | 383 | if (orig_insn != replaced_instructions.end()) { |
| 302 | orig_insn != replaced_instructions.end()) { | ||
| 303 | system.Memory().Write32(addr, orig_insn->second); | 384 | system.Memory().Write32(addr, orig_insn->second); |
| 304 | system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32)); | 385 | system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32)); |
| 305 | replaced_instructions.erase(addr); | 386 | replaced_instructions.erase(addr); |
| 306 | 387 | success = true; | |
| 307 | SendReply(GDB_STUB_REPLY_OK); | ||
| 308 | } else { | ||
| 309 | SendReply(GDB_STUB_REPLY_ERR); | ||
| 310 | } | 388 | } |
| 311 | break; | 389 | break; |
| 312 | } | 390 | } |
| 391 | case BreakpointType::WriteWatch: | ||
| 392 | success = system.CurrentProcess()->RemoveWatchpoint(system, addr, size, | ||
| 393 | Kernel::DebugWatchpointType::Write); | ||
| 394 | break; | ||
| 395 | case BreakpointType::ReadWatch: | ||
| 396 | success = system.CurrentProcess()->RemoveWatchpoint(system, addr, size, | ||
| 397 | Kernel::DebugWatchpointType::Read); | ||
| 398 | break; | ||
| 399 | case BreakpointType::AccessWatch: | ||
| 400 | success = system.CurrentProcess()->RemoveWatchpoint( | ||
| 401 | system, addr, size, Kernel::DebugWatchpointType::ReadOrWrite); | ||
| 402 | break; | ||
| 403 | case BreakpointType::Hardware: | ||
| 313 | default: | 404 | default: |
| 314 | SendReply(GDB_STUB_REPLY_EMPTY); | 405 | SendReply(GDB_STUB_REPLY_EMPTY); |
| 315 | break; | 406 | return; |
| 407 | } | ||
| 408 | |||
| 409 | if (success) { | ||
| 410 | SendReply(GDB_STUB_REPLY_OK); | ||
| 411 | } else { | ||
| 412 | SendReply(GDB_STUB_REPLY_ERR); | ||
| 316 | } | 413 | } |
| 317 | } | 414 | } |
| 318 | 415 | ||