From 3cb5fd8ebc817af35c6b23698caf15b185c6be14 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 7 Nov 2020 18:12:52 +0200 Subject: [PATCH] Debugger: Implement SPU callstack, fix PPU callstack --- rpcs3/Emu/Cell/PPUThread.cpp | 32 ++++++++++++++++--- rpcs3/Emu/Cell/SPUThread.cpp | 60 ++++++++++++++++++++++++++++++++++-- rpcs3/Emu/Cell/SPUThread.h | 2 +- 3 files changed, 86 insertions(+), 8 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 8cb5998314..3de988ecf8 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -538,7 +538,7 @@ std::string ppu_thread::dump_callstack() const for (const auto& sp : dump_callstack_list()) { // TODO: function addresses too - fmt::append(ret, "> from 0x%08x (r1=0x%08x)\n", sp.first, sp.second); + fmt::append(ret, "> from 0x%08x (sp=0x%08x)\n", sp.first, sp.second); } return ret; @@ -579,17 +579,39 @@ std::vector> ppu_thread::dump_callstack_list() const std::vector> call_stack_list; + bool first = true; + for ( - u64 sp = *vm::get_super_ptr(stack_ptr); + u64 sp = r1; sp % 0x10 == 0u && sp >= stack_min && sp <= stack_max - ppu_stack_start_offset; sp = *vm::get_super_ptr(static_cast(sp)) ) { - const u64 addr = *vm::get_super_ptr(static_cast(sp + 16)); + u64 addr = *vm::get_super_ptr(static_cast(sp + 16)); - if (addr > UINT32_MAX || addr % 4 || !vm::check_addr(static_cast(addr), 1, vm::page_executable)) + auto is_invalid = [](u64 addr) { - break; + return (addr > UINT32_MAX || addr % 4 || !vm::check_addr(static_cast(addr), 1, vm::page_executable)); + }; + + if (is_invalid(addr)) + { + if (std::exchange(first, false)) + { + // Function hasn't saved LR, could be because it's a leaf function + // Use LR directly instead + addr = lr; + + if (is_invalid(addr)) + { + // Skip it, workaround + continue; + } + } + else + { + break; + } } // TODO: function addresses too diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 05e7faa3b2..84ad06c519 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -1245,12 +1245,68 @@ std::string spu_thread::dump_regs() const std::string spu_thread::dump_callstack() const { - return {}; + std::string ret; + + fmt::append(ret, "Call stack:\n=========\n0x%08x (0x0) called\n", pc); + + for (const auto& sp : dump_callstack_list()) + { + // TODO: function addresses too + fmt::append(ret, "> from 0x%08x (sp=0x%08x)\n", sp.first, sp.second); + } + + return ret; } std::vector> spu_thread::dump_callstack_list() const { - return {}; + std::vector> call_stack_list; + + bool first = true; + + // Declare first 128-bytes as invalid for stack (common values such as 0 do not make sense here) + for (u32 sp = gpr[1]._u32[3]; (sp & ~0x3FFF0) == 0u && sp >= 0x80u; sp = _ref(sp)) + { + v128 lr = _ref(sp + 16); + + auto is_invalid = [](v128 v) + { + const u32 addr = v._u32[3]; + + if (v != v128::from32r(addr)) + { + // Non-zero lower words are invalid (because BRSL-like instructions generate only zeroes) + return true; + } + + return !!(addr & ~0x3FFFC); + }; + + if (is_invalid(lr)) + { + if (std::exchange(first, false)) + { + // Function hasn't saved LR, could be because it's a leaf function + // Use LR directly instead + lr = gpr[0]; + + if (is_invalid(lr)) + { + // Skip it, workaround + continue; + } + } + else + { + break; + } + } + + // TODO: function addresses too + call_stack_list.emplace_back(lr._u32[3], sp); + } + + return call_stack_list; } std::string spu_thread::dump_misc() const diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index a5fae796cd..cf694ec1d5 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -792,7 +792,7 @@ public: template to_be_t* _ptr(u32 lsa) const { - return reinterpret_cast*>(ls + lsa); + return reinterpret_cast*>(ls + (lsa % SPU_LS_SIZE)); } // Convert specified SPU LS address to a reference of specified (possibly converted to BE) type