mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Add a function to walk the stack, not yet perfect.
Could use improvements, but with a decent symbol table does very well so far in the games I tested it in.
This commit is contained in:
parent
92482dfcb3
commit
7ac67a8931
9 changed files with 227 additions and 2 deletions
|
@ -934,6 +934,8 @@ add_library(${CoreLibName} ${CoreLinkType}
|
|||
Core/MIPS/MIPSInt.h
|
||||
Core/MIPS/MIPSIntVFPU.cpp
|
||||
Core/MIPS/MIPSIntVFPU.h
|
||||
Core/MIPS/MIPSStackWalk.cpp
|
||||
Core/MIPS/MIPSStackWalk.h
|
||||
Core/MIPS/MIPSTables.cpp
|
||||
Core/MIPS/MIPSTables.h
|
||||
Core/MIPS/MIPSVFPUUtils.cpp
|
||||
|
|
|
@ -15,6 +15,7 @@ set(SRCS
|
|||
MIPS/MIPSDisVFPU.cpp
|
||||
MIPS/MIPSInt.cpp
|
||||
MIPS/MIPSIntVFPU.cpp
|
||||
MIPS/MIPSStackWalk.cpp
|
||||
MIPS/MIPSTables.cpp
|
||||
MIPS/MIPSVFPUUtils.cpp
|
||||
MIPS/JitCommon/JitCommon.cpp
|
||||
|
|
|
@ -336,6 +336,7 @@
|
|||
<ClCompile Include="PSPMixer.cpp" />
|
||||
<ClCompile Include="Reporting.cpp" />
|
||||
<ClCompile Include="SaveState.cpp" />
|
||||
<ClCompile Include="MIPS\MIPSStackWalk.cpp" />
|
||||
<ClCompile Include="System.cpp" />
|
||||
<ClCompile Include="Util\BlockAllocator.cpp" />
|
||||
<ClCompile Include="Util\PPGeDraw.cpp" />
|
||||
|
@ -487,6 +488,7 @@
|
|||
<ClInclude Include="PSPMixer.h" />
|
||||
<ClInclude Include="Reporting.h" />
|
||||
<ClInclude Include="SaveState.h" />
|
||||
<ClInclude Include="MIPS\MIPSStackWalk.h" />
|
||||
<ClInclude Include="System.h" />
|
||||
<ClInclude Include="ThreadEventQueue.h" />
|
||||
<ClInclude Include="Util\BlockAllocator.h" />
|
||||
|
|
|
@ -454,6 +454,9 @@
|
|||
<ClCompile Include="HW\AsyncIOManager.cpp">
|
||||
<Filter>HW</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MIPS\MIPSStackWalk.cpp">
|
||||
<Filter>MIPS</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ELF\ElfReader.h">
|
||||
|
@ -846,6 +849,9 @@
|
|||
<ClInclude Include="HW\AsyncIOManager.h">
|
||||
<Filter>HW</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MIPS\MIPSStackWalk.h">
|
||||
<Filter>MIPS</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="CMakeLists.txt" />
|
||||
|
|
|
@ -3539,6 +3539,8 @@ std::vector<DebugThreadInfo> GetThreadsInfo()
|
|||
info.name[KERNELOBJECT_MAX_NAME_LENGTH] = 0;
|
||||
info.status = t->nt.status;
|
||||
info.entrypoint = t->nt.entrypoint;
|
||||
info.initialStack = t->nt.initialStack;
|
||||
info.stackSize = (u32)t->nt.stackSize;
|
||||
info.priority = t->nt.currentPriority;
|
||||
info.waitType = t->nt.waitType;
|
||||
if(*iter == currentThread)
|
||||
|
|
|
@ -313,8 +313,10 @@ struct DebugThreadInfo
|
|||
SceUID id;
|
||||
char name[KERNELOBJECT_MAX_NAME_LENGTH+1];
|
||||
u32 status;
|
||||
int curPC;
|
||||
int entrypoint;
|
||||
u32 curPC;
|
||||
u32 entrypoint;
|
||||
u32 initialStack;
|
||||
int stackSize;
|
||||
int priority;
|
||||
WaitType waitType;
|
||||
bool isCurrent;
|
||||
|
|
173
Core/MIPS/MIPSStackWalk.cpp
Normal file
173
Core/MIPS/MIPSStackWalk.cpp
Normal file
|
@ -0,0 +1,173 @@
|
|||
// Copyright (c) 2012- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/Debugger/SymbolMap.h"
|
||||
#include "Core/MIPS/MIPSCodeUtils.h"
|
||||
#include "Core/MIPS/MIPSStackWalk.h"
|
||||
|
||||
#define _RS ((op >> 21) & 0x1F)
|
||||
#define _RT ((op >> 16) & 0x1F)
|
||||
#define _RD ((op >> 11) & 0x1F)
|
||||
#define _IMM16 ((signed short)(op & 0xFFFF))
|
||||
|
||||
#define MIPSTABLE_IMM_MASK 0xFC000000
|
||||
#define MIPSTABLE_SPECIAL_MASK 0xFC00003F
|
||||
|
||||
namespace MIPSStackWalk {
|
||||
using namespace MIPSCodeUtils;
|
||||
|
||||
// In the worst case, we scan this far above the pc for a 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(u32 op) {
|
||||
return (op & MIPSTABLE_IMM_MASK) == 0xAC000000;
|
||||
}
|
||||
|
||||
bool IsAddImmInstr(u32 op) {
|
||||
return (op & MIPSTABLE_IMM_MASK) == 0x20000000 || (op & MIPSTABLE_IMM_MASK) == 0x24000000;
|
||||
}
|
||||
|
||||
bool IsMovRegsInstr(u32 op) {
|
||||
// TODO: There are more options here. Let's assume addu for now.
|
||||
if ((op & MIPSTABLE_SPECIAL_MASK) == 0x00000021) {
|
||||
return _RS == 0 || _RT == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScanForAllocaSignature(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 (; Memory::IsValidAddress(pc) && pc >= stop; pc -= 4) {
|
||||
u32 op = Memory::Read_Instruction(pc);
|
||||
|
||||
// We're looking for a "mov fp, sp" close by a "addiu sp, sp, -N".
|
||||
if (IsMovRegsInstr(op) && _RD == MIPS_REG_FP && (_RS == MIPS_REG_SP || _RT == MIPS_REG_SP)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScanForEntry(StackFrame &frame, u32 entry, u32 &ra) {
|
||||
// TODO: Check if found entry is in the same symbol? Might be wrong sometimes...
|
||||
|
||||
int ra_offset = -1;
|
||||
u32 stop = entry == INVALIDTARGET ? 0 : entry;
|
||||
for (u32 pc = frame.pc; Memory::IsValidAddress(pc) && pc >= stop; pc -= 4) {
|
||||
u32 op = Memory::Read_Instruction(pc);
|
||||
|
||||
// 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(pc)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
frame.entry = pc;
|
||||
frame.stackSize = -_IMM16;
|
||||
if (ra_offset != -1) {
|
||||
ra = Memory::Read_U32(frame.sp + ra_offset);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DetermineFrameInfo(StackFrame &frame, u32 possibleEntry, u32 threadEntry, u32 &ra) {
|
||||
if (ScanForEntry(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(frame, newPossibleEntry, ra)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<StackFrame> Walk(u32 pc, u32 ra, u32 sp, u32 threadEntry, u32 threadStackTop) {
|
||||
std::vector<StackFrame> 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(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;
|
||||
}
|
||||
};
|
36
Core/MIPS/MIPSStackWalk.h
Normal file
36
Core/MIPS/MIPSStackWalk.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) 2012- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
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 in bytes.
|
||||
int stackSize;
|
||||
};
|
||||
|
||||
std::vector<StackFrame> Walk(u32 pc, u32 ra, u32 sp, u32 threadEntry, u32 threadStackTop);
|
||||
};
|
|
@ -282,6 +282,7 @@ LOCAL_SRC_FILES := \
|
|||
$(SRC)/Core/MIPS/MIPSDisVFPU.cpp \
|
||||
$(SRC)/Core/MIPS/MIPSInt.cpp.arm \
|
||||
$(SRC)/Core/MIPS/MIPSIntVFPU.cpp.arm \
|
||||
$(SRC)/Core/MIPS/MIPSStackWalk.cpp \
|
||||
$(SRC)/Core/MIPS/MIPSTables.cpp.arm \
|
||||
$(SRC)/Core/MIPS/MIPSVFPUUtils.cpp.arm \
|
||||
$(SRC)/Core/MIPS/MIPSCodeUtils.cpp.arm \
|
||||
|
|
Loading…
Add table
Reference in a new issue