Mesen2/Core/Debugger/CallstackManager.cpp

119 lines
3.2 KiB
C++

#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();
}