From 4768f293bf911372c1d2b595f3d07d1ca6669d3c Mon Sep 17 00:00:00 2001 From: Kingcom Date: Mon, 25 Aug 2014 17:01:17 +0200 Subject: [PATCH] Port stack walker from PPSSPP --- pcsx2/CMakeLists.txt | 2 + pcsx2/DebugTools/BiosDebugData.h | 2 +- pcsx2/DebugTools/MipsStackWalk.cpp | 190 ++++++++++++++++++ pcsx2/DebugTools/MipsStackWalk.h | 21 ++ pcsx2/windows/VCprojects/pcsx2.vcxproj | 4 +- .../windows/VCprojects/pcsx2.vcxproj.filters | 8 +- pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj | 4 +- .../VCprojects/pcsx2_vs2012.vcxproj.filters | 8 +- pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj | 2 + .../VCprojects/pcsx2_vs2013.vcxproj.filters | 6 + 10 files changed, 242 insertions(+), 5 deletions(-) create mode 100644 pcsx2/DebugTools/MipsStackWalk.cpp create mode 100644 pcsx2/DebugTools/MipsStackWalk.h diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index fa5bd9199f..7cf254737c 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -276,6 +276,7 @@ set(pcsx2DebugToolsSources DebugTools/MIPSAnalyst.cpp DebugTools/MipsAssembler.cpp DebugTools/MipsAssemblerTables.cpp + DebugTools/MipsStackWalk.cpp DebugTools/Breakpoints.cpp DebugTools/SymbolMap.cpp DebugTools/DisR3000A.cpp @@ -292,6 +293,7 @@ set(pcsx2DebugToolsHeaders DebugTools/MIPSAnalyst.h DebugTools/MipsAssembler.h DebugTools/MipsAssemblerTables.h + DebugTools/MipsStackWalk.h DebugTools/Breakpoints.h DebugTools/SymbolMap.h DebugTools/Debug.h diff --git a/pcsx2/DebugTools/BiosDebugData.h b/pcsx2/DebugTools/BiosDebugData.h index e0daca58c2..bbda49879e 100644 --- a/pcsx2/DebugTools/BiosDebugData.h +++ b/pcsx2/DebugTools/BiosDebugData.h @@ -43,7 +43,7 @@ enum { struct EEThread { - u32 tid; + int tid; EEInternalThread data; }; diff --git a/pcsx2/DebugTools/MipsStackWalk.cpp b/pcsx2/DebugTools/MipsStackWalk.cpp new file mode 100644 index 0000000000..2b9427b738 --- /dev/null +++ b/pcsx2/DebugTools/MipsStackWalk.cpp @@ -0,0 +1,190 @@ +#include "PrecompiledHeader.h" +#include "MipsStackWalk.h" +#include "SymbolMap.h" +#include "MIPSAnalyst.h" +#include "DebugInterface.h" +#include "../R5900OpcodeTables.h" + +#define _RS ((rawOp >> 21) & 0x1F) +#define _RT ((rawOp >> 16) & 0x1F) +#define _RD ((rawOp >> 11) & 0x1F) +#define _IMM16 ((signed short)(rawOp & 0xFFFF)) +#define MIPS_REG_SP 29 +#define MIPS_REG_FP 30 +#define MIPS_REG_RA 31 + +#define INVALIDTARGET 0xFFFFFFFF + +namespace MipsStackWalk { + // In the worst case, we scan this far above the pc for an entry. + const int MAX_FUNC_SIZE = 32768 * 4; + // After this we assume we're stuck. + const size_t MAX_DEPTH = 1024; + + static u32 GuessEntry(u32 pc) { + SymbolInfo info; + if (symbolMap.GetSymbolInfo(&info, pc)) { + return info.address; + } + return INVALIDTARGET; + } + + bool IsSWInstr(const R5900::OPCODE& op) { + if ((op.flags & IS_MEMORY) && (op.flags & IS_STORE)) + { + u32 type = op.flags & MEMTYPE_MASK; + switch (op.flags & MEMTYPE_MASK) + { + case MEMTYPE_WORD: + case MEMTYPE_DWORD: + case MEMTYPE_QWORD: + return true; + } + } + + return false; + } + + bool IsAddImmInstr(const R5900::OPCODE& op) { + if (op.flags & IS_ALU) + return (op.flags & ALUTYPE_MASK) == ALUTYPE_ADDI; + + return false; + } + + bool IsMovRegsInstr(const R5900::OPCODE& op, u32 rawOp) { + if (op.flags & IS_ALU) + return (op.flags & ALUTYPE_MASK) == ALUTYPE_ADDI && (_RS == 0 || _RT == 0); + + return false; + } + + bool ScanForAllocaSignature(DebugInterface* cpu, u32 pc) { + // In God Eater Burst, for example, after 0880E750, there's what looks like an alloca(). + // It's surrounded by "mov fp, sp" and "mov sp, fp", which is unlikely to be used for other reasons. + + // It ought to be pretty close. + u32 stop = pc - 32 * 4; + for (; cpu->isValidAddress(pc) && pc >= stop; pc -= 4) { + u32 rawOp = cpu->read32(pc); + const R5900::OPCODE& op = R5900::GetInstruction(rawOp); + + // We're looking for a "mov fp, sp" close by a "addiu sp, sp, -N". + if (IsMovRegsInstr(op,rawOp) && _RD == MIPS_REG_FP && (_RS == MIPS_REG_SP || _RT == MIPS_REG_SP)) { + return true; + } + } + return false; + } + + bool ScanForEntry(DebugInterface* cpu, StackFrame &frame, u32 entry, u32 &ra) { + // Let's hope there are no > 1MB functions on the PSP, for the sake of humanity... + const u32 LONGEST_FUNCTION = 1024 * 1024; + // TODO: Check if found entry is in the same symbol? Might be wrong sometimes... + + int ra_offset = -1; + const u32 start = frame.pc; + u32 stop = entry; + if (entry == INVALIDTARGET) { +/* if (start >= PSP_GetUserMemoryBase()) { + stop = PSP_GetUserMemoryBase(); + } else if (start >= PSP_GetKernelMemoryBase()) { + stop = PSP_GetKernelMemoryBase(); + } else if (start >= PSP_GetScratchpadMemoryBase()) { + stop = PSP_GetScratchpadMemoryBase(); + }*/ + stop = 0x80000; + } + if (stop < start - LONGEST_FUNCTION) { + stop = start - LONGEST_FUNCTION; + } + for (u32 pc = start; cpu->isValidAddress(pc) && pc >= stop; pc -= 4) { + u32 rawOp = cpu->read32(pc); + const R5900::OPCODE& op = R5900::GetInstruction(rawOp); + + // Here's where they store the ra address. + if (IsSWInstr(op) && _RT == MIPS_REG_RA && _RS == MIPS_REG_SP) { + ra_offset = _IMM16; + } + + if (IsAddImmInstr(op) && _RT == MIPS_REG_SP && _RS == MIPS_REG_SP) { + // A positive imm either means alloca() or we went too far. + if (_IMM16 > 0) { + // TODO: Maybe check for any alloca() signature and bail? + continue; + } + if (ScanForAllocaSignature(cpu,pc)) { + continue; + } + + frame.entry = pc; + frame.stackSize = -_IMM16; + if (ra_offset != -1 && cpu->isValidAddress(frame.sp + ra_offset)) { + ra = cpu->read32(frame.sp + ra_offset); + } + return true; + } + } + return false; + } + + bool DetermineFrameInfo(DebugInterface* cpu, StackFrame &frame, u32 possibleEntry, u32 threadEntry, u32 &ra) { + if (ScanForEntry(cpu, frame, possibleEntry, ra)) { + // Awesome, found one that looks right. + return true; + } else if (ra != INVALIDTARGET && possibleEntry != INVALIDTARGET) { + // Let's just assume it's a leaf. + frame.entry = possibleEntry; + frame.stackSize = 0; + return true; + } + + // Okay, we failed to get one. Our possibleEntry could be wrong, it often is. + // Let's just scan upward. + u32 newPossibleEntry = frame.pc > threadEntry ? threadEntry : frame.pc - MAX_FUNC_SIZE; + if (ScanForEntry(cpu, frame, newPossibleEntry, ra)) { + return true; + } else { + return false; + } + } + + std::vector Walk(DebugInterface* cpu, u32 pc, u32 ra, u32 sp, u32 threadEntry, u32 threadStackTop) { + std::vector frames; + StackFrame current; + current.pc = pc; + current.sp = sp; + current.entry = INVALIDTARGET; + current.stackSize = -1; + + u32 prevEntry = INVALIDTARGET; + while (pc != threadEntry) { + u32 possibleEntry = GuessEntry(current.pc); + if (DetermineFrameInfo(cpu, current, possibleEntry, threadEntry, ra)) { + frames.push_back(current); + if (current.entry == threadEntry || GuessEntry(current.entry) == threadEntry) { + break; + } + if (current.entry == prevEntry || frames.size() >= MAX_DEPTH) { + // Recursion, means we're screwed. Let's just give up. + break; + } + prevEntry = current.entry; + + current.pc = ra; + current.sp += current.stackSize; + ra = INVALIDTARGET; + current.entry = INVALIDTARGET; + current.stackSize = -1; + } else { + // Well, we got as far as we could. + current.entry = possibleEntry; + current.stackSize = 0; + frames.push_back(current); + break; + } + } + + return frames; + } +}; \ No newline at end of file diff --git a/pcsx2/DebugTools/MipsStackWalk.h b/pcsx2/DebugTools/MipsStackWalk.h new file mode 100644 index 0000000000..285b2b0d84 --- /dev/null +++ b/pcsx2/DebugTools/MipsStackWalk.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include "Pcsx2Types.h" + +class DebugInterface; + +namespace MipsStackWalk { + struct StackFrame { + // Beginning of function symbol (may be estimated.) + u32 entry; + // Next position within function. + u32 pc; + // Value of SP inside this function (assuming no alloca()...) + u32 sp; + // Size of stack frame in bytes. + int stackSize; + }; + + std::vector Walk(DebugInterface* cpu, u32 pc, u32 ra, u32 sp, u32 threadEntry, u32 threadStackTop); +}; \ No newline at end of file diff --git a/pcsx2/windows/VCprojects/pcsx2.vcxproj b/pcsx2/windows/VCprojects/pcsx2.vcxproj index 18f6823584..deb70be028 100644 --- a/pcsx2/windows/VCprojects/pcsx2.vcxproj +++ b/pcsx2/windows/VCprojects/pcsx2.vcxproj @@ -179,6 +179,7 @@ %(RootDir)%(Directory)\%(Filename).h + @@ -718,6 +719,7 @@ + @@ -935,4 +937,4 @@ - + \ No newline at end of file diff --git a/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters b/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters index 8ebc4642ab..58eb17b43b 100644 --- a/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters +++ b/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters @@ -856,6 +856,9 @@ System\ISO + + System\Ps2\Debug + @@ -1269,6 +1272,9 @@ System\ISO + + System\Ps2\Debug + @@ -1351,4 +1357,4 @@ AppHost\Resources - + \ No newline at end of file diff --git a/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj b/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj index 26b283f10f..648b4ceb7f 100644 --- a/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj +++ b/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj @@ -416,6 +416,7 @@ + @@ -704,6 +705,7 @@ + @@ -940,4 +942,4 @@ - + \ No newline at end of file diff --git a/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj.filters b/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj.filters index 1153d41a4f..3bb1e35861 100644 --- a/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj.filters +++ b/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj.filters @@ -853,6 +853,9 @@ System\ISO + + System\Ps2\Debug + @@ -1269,6 +1272,9 @@ System\ISO + + System\Ps2\Debug + @@ -1351,4 +1357,4 @@ AppHost\Resources - + \ No newline at end of file diff --git a/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj b/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj index 5f345811a1..98dea47e9a 100644 --- a/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj +++ b/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj @@ -416,6 +416,7 @@ + @@ -704,6 +705,7 @@ + diff --git a/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj.filters b/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj.filters index ec441c1b3e..62717f06c6 100644 --- a/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj.filters +++ b/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj.filters @@ -853,6 +853,9 @@ System\Ps2\Debug + + System\Ps2\Debug + @@ -1269,6 +1272,9 @@ System\Ps2\Debug + + System\Ps2\Debug +