#include "pch.h" #include "Debugger/CallstackManager.h" #include "Debugger/Debugger.h" #include "Debugger/IDebugger.h" #include "Debugger/DebugBreakHelper.h" #include "Debugger/Profiler.h" CallstackManager::CallstackManager(Debugger* debugger, IDebugger* cpuDebugger) { _debugger = debugger; _profiler.reset(new Profiler(debugger, cpuDebugger)); } CallstackManager::~CallstackManager() { } void CallstackManager::Push(AddressInfo &src, uint32_t srcAddr, AddressInfo& dest, uint32_t destAddr, AddressInfo& ret, uint32_t returnAddress, uint32_t returnStackPointer, StackFrameFlags flags) { if(_callstack.size() >= 511) { //Ensure callstack stays below 512 entries - games can use various tricks that could keep making the callstack grow _callstack.pop_front(); } StackFrameInfo stackFrame; stackFrame.Source = srcAddr; stackFrame.AbsSource = src; stackFrame.Target = destAddr; stackFrame.AbsTarget = dest; stackFrame.Return = returnAddress; stackFrame.ReturnStackPointer = returnStackPointer; stackFrame.AbsReturn = ret; stackFrame.Flags = flags; _callstack.push_back(stackFrame); _profiler->StackFunction(dest, flags); } void CallstackManager::Pop(AddressInfo& dest, uint32_t destAddress, uint32_t stackPointer) { if(_callstack.empty()) { return; } StackFrameInfo prevFrame = _callstack.back(); _callstack.pop_back(); _profiler->UnstackFunction(); uint32_t returnAddr = prevFrame.Return; if(!_callstack.empty() && destAddress != returnAddr) { //Mismatch, try to find a matching address higher in the stack bool foundMatch = false; for(int i = (int)_callstack.size() - 1; i >= 0; i--) { if(destAddress == _callstack[i].Return) { //Found a matching stack frame, unstack until that point foundMatch = true; for(int j = (int)_callstack.size() - i - 1; j >= 0; j--) { _callstack.pop_back(); _profiler->UnstackFunction(); } break; } } if(!foundMatch) { //Couldn't find a matching frame //If the new stack pointer doesn't match the last frame, push a new frame for it //Otherwise, presume that the code has returned to the last function on the stack //This can happen in some patterns, e.g if putting call parameters after the JSR //call, and manipulating the stack upon return to return to the code after the //parameters. if(_callstack.back().ReturnStackPointer != stackPointer) { Push(prevFrame.AbsReturn, returnAddr, dest, destAddress, prevFrame.AbsReturn, returnAddr, stackPointer, StackFrameFlags::None); } } } } void CallstackManager::GetCallstack(StackFrameInfo* callstackArray, uint32_t &callstackSize) { DebugBreakHelper helper(_debugger); int i = 0; for(StackFrameInfo &info : _callstack) { callstackArray[i] = info; i++; } callstackSize = i; } int32_t CallstackManager::GetReturnAddress() { DebugBreakHelper helper(_debugger); if(_callstack.empty()) { return -1; } return _callstack.back().Return; } int64_t CallstackManager::GetReturnStackPointer() { DebugBreakHelper helper(_debugger); if(_callstack.empty()) { return -1; } return _callstack.back().ReturnStackPointer; } Profiler* CallstackManager::GetProfiler() { return _profiler.get(); } void CallstackManager::Clear() { _callstack.clear(); _profiler->ResetState(); }