mirror of
https://github.com/libretro/Mesen.git
synced 2024-11-30 20:40:54 +00:00
Debugger: Improved callstack reliability
This commit is contained in:
parent
23c85a447b
commit
cdbef431d6
@ -355,55 +355,75 @@ int32_t Debugger::EvaluateExpression(string expression, EvalResultType &resultTy
|
||||
return _watchExpEval.Evaluate(expression, state, resultType, operationInfo);
|
||||
}
|
||||
|
||||
void Debugger::RemoveExcessCallstackEntries()
|
||||
{
|
||||
while(_callstackRelative.size() >= 1022) {
|
||||
//Ensure callstack stays below 512 entries - some games never call RTI, causing an infinite stack
|
||||
_callstackRelative.pop_front();
|
||||
_callstackRelative.pop_front();
|
||||
_callstackAbsolute.pop_front();
|
||||
_callstackAbsolute.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::UpdateCallstack(uint8_t instruction, uint32_t addr)
|
||||
{
|
||||
if((instruction == 0x60 || instruction == 0x40) && !_callstackRelative.empty()) {
|
||||
//RTS & RTI
|
||||
_callstackRelative.pop_back();
|
||||
_callstackRelative.pop_back();
|
||||
_callstackAbsolute.pop_back();
|
||||
_callstackAbsolute.pop_back();
|
||||
if((instruction == 0x60 || instruction == 0x40) && !_callstack.empty()) {
|
||||
//RTS & RTI
|
||||
uint16_t expectedReturnAddress = _callstack[_callstack.size() - 1].JumpSource;
|
||||
|
||||
_callstack.pop_back();
|
||||
_subReturnAddresses.pop_back();
|
||||
|
||||
int spOffset = instruction == 0x40 ? 2 : 1; //RTI has an extra byte on the stack (flags)
|
||||
|
||||
uint16_t targetAddr = _memoryManager->DebugReadWord(0x100 + ((_debugState.CPU.SP + spOffset) & 0xFF));
|
||||
if(targetAddr < expectedReturnAddress || targetAddr - expectedReturnAddress > 3) {
|
||||
//Mismatch, pop that stack frame and add the new one
|
||||
if(!_callstack.empty()) {
|
||||
bool foundMatch = false;
|
||||
for(int i = (int)_callstack.size() - 1; i >= 0; i--) {
|
||||
if(targetAddr > _callstack[i].JumpSource && targetAddr < _callstack[i].JumpSource + 3) {
|
||||
//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();
|
||||
_subReturnAddresses.pop_back();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!foundMatch) {
|
||||
//Couldn't find a matching frame, replace the current one
|
||||
AddCallstackFrame(expectedReturnAddress, targetAddr, StackFrameFlags::None);
|
||||
_subReturnAddresses.push_back(expectedReturnAddress + 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_profiler->UnstackFunction();
|
||||
} else if(instruction == 0x20) {
|
||||
//JSR
|
||||
RemoveExcessCallstackEntries();
|
||||
|
||||
uint16_t targetAddr = _memoryManager->DebugRead(addr + 1) | (_memoryManager->DebugRead(addr + 2) << 8);
|
||||
_callstackRelative.push_back(addr);
|
||||
_callstackRelative.push_back(targetAddr);
|
||||
|
||||
_callstackAbsolute.push_back(_mapper->ToAbsoluteAddress(addr));
|
||||
_callstackAbsolute.push_back(_mapper->ToAbsoluteAddress(targetAddr));
|
||||
|
||||
_profiler->StackFunction(_mapper->ToAbsoluteAddress(addr), _mapper->ToAbsoluteAddress(targetAddr));
|
||||
|
||||
AddCallstackFrame(addr, targetAddr, StackFrameFlags::None);
|
||||
_subReturnAddresses.push_back(addr + 3);
|
||||
|
||||
_profiler->StackFunction(_mapper->ToAbsoluteAddress(addr), _mapper->ToAbsoluteAddress(targetAddr));
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::AddCallstackFrame(uint16_t source, uint16_t target, 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();
|
||||
_subReturnAddresses.pop_front();
|
||||
}
|
||||
|
||||
StackFrameInfo stackFrame;
|
||||
stackFrame.JumpSource = source;
|
||||
stackFrame.JumpSourceAbsolute = _mapper->ToAbsoluteAddress(source);
|
||||
|
||||
stackFrame.JumpTarget = target;
|
||||
stackFrame.JumpTargetAbsolute = _mapper->ToAbsoluteAddress(target);
|
||||
|
||||
stackFrame.Flags = flags;
|
||||
|
||||
_callstack.push_back(stackFrame);
|
||||
}
|
||||
|
||||
void Debugger::PrivateProcessInterrupt(uint16_t cpuAddr, uint16_t destCpuAddr, bool forNmi)
|
||||
{
|
||||
RemoveExcessCallstackEntries();
|
||||
|
||||
_callstackRelative.push_back(cpuAddr | (forNmi ? 0x40000 : 0x20000));
|
||||
_callstackRelative.push_back(destCpuAddr);
|
||||
|
||||
_callstackAbsolute.push_back(_mapper->ToAbsoluteAddress(cpuAddr));
|
||||
_callstackAbsolute.push_back(_mapper->ToAbsoluteAddress(destCpuAddr));
|
||||
|
||||
AddCallstackFrame(cpuAddr, destCpuAddr, forNmi ? StackFrameFlags::Nmi : StackFrameFlags::Irq);
|
||||
_subReturnAddresses.push_back(cpuAddr);
|
||||
|
||||
_profiler->StackFunction(-1, _mapper->ToAbsoluteAddress(destCpuAddr));
|
||||
@ -963,21 +983,15 @@ void Debugger::ProcessVramWriteOperation(uint16_t addr, uint8_t &value)
|
||||
#endif
|
||||
}
|
||||
|
||||
void Debugger::GetCallstack(int32_t* callstackAbsolute, int32_t* callstackRelative)
|
||||
void Debugger::GetCallstack(StackFrameInfo* callstackArray, uint32_t &callstackSize)
|
||||
{
|
||||
int callstackSize = std::min(1022, (int)_callstackRelative.size());
|
||||
for(int i = 0; i < callstackSize; i++) {
|
||||
callstackAbsolute[i] = _callstackAbsolute[i];
|
||||
|
||||
int32_t relativeAddr = _callstackRelative[i];
|
||||
if(_mapper->FromAbsoluteAddress(_callstackAbsolute[i]) == -1) {
|
||||
//Mark address as an unmapped memory addr
|
||||
relativeAddr |= 0x10000;
|
||||
}
|
||||
callstackRelative[i] = relativeAddr;
|
||||
DebugBreakHelper helper(this);
|
||||
int i = 0;
|
||||
for(StackFrameInfo &info : _callstack) {
|
||||
callstackArray[i] = info;
|
||||
i++;
|
||||
}
|
||||
callstackAbsolute[callstackSize] = -2;
|
||||
callstackRelative[callstackSize] = -2;
|
||||
callstackSize = i;
|
||||
}
|
||||
|
||||
int32_t Debugger::GetFunctionEntryPointCount()
|
||||
|
@ -73,8 +73,7 @@ private:
|
||||
|
||||
vector<uint8_t> _frozenAddresses;
|
||||
|
||||
deque<uint32_t> _callstackAbsolute;
|
||||
deque<uint32_t> _callstackRelative;
|
||||
deque<StackFrameInfo> _callstack;
|
||||
deque<int32_t> _subReturnAddresses;
|
||||
|
||||
int32_t _stepOutReturnAddress;
|
||||
@ -131,14 +130,13 @@ private:
|
||||
void PrivateProcessVramWriteOperation(uint16_t addr, uint8_t &value);
|
||||
void ProcessBreakpoints(BreakpointType type, OperationInfo &operationInfo, bool allowBreak = true);
|
||||
|
||||
void AddCallstackFrame(uint16_t source, uint16_t target, StackFrameFlags flags);
|
||||
void UpdateCallstack(uint8_t currentInstruction, uint32_t addr);
|
||||
void PrivateProcessInterrupt(uint16_t cpuAddr, uint16_t destCpuAddr, bool forNmi);
|
||||
|
||||
void ProcessStepConditions(uint32_t addr);
|
||||
bool SleepUntilResume(BreakSource source = BreakSource::Break);
|
||||
|
||||
void RemoveExcessCallstackEntries();
|
||||
|
||||
void AddDebugEvent(DebugEventType type, uint16_t address = -1, uint8_t value = 0, int16_t breakpointId = -1, int8_t ppuLatch = -1);
|
||||
|
||||
public:
|
||||
@ -155,7 +153,7 @@ public:
|
||||
void GetFunctionEntryPoints(int32_t* entryPoints, int32_t maxCount);
|
||||
int32_t GetFunctionEntryPointCount();
|
||||
|
||||
void GetCallstack(int32_t* callstackAbsolute, int32_t* callstackRelative);
|
||||
void GetCallstack(StackFrameInfo* callstackArray, uint32_t &callstackSize);
|
||||
|
||||
void GetApuState(ApuState *state);
|
||||
__forceinline void GetState(DebugState *state, bool includeMapperInfo = true);
|
||||
|
@ -147,4 +147,20 @@ enum class EventType
|
||||
InputPolled = 8,
|
||||
SpriteZeroHit = 9,
|
||||
EventTypeSize
|
||||
};
|
||||
|
||||
enum class StackFrameFlags : uint8_t
|
||||
{
|
||||
None = 0,
|
||||
Nmi = 1,
|
||||
Irq = 2
|
||||
};
|
||||
|
||||
struct StackFrameInfo
|
||||
{
|
||||
int32_t JumpSourceAbsolute;
|
||||
int32_t JumpTargetAbsolute;
|
||||
uint16_t JumpSource;
|
||||
uint16_t JumpTarget;
|
||||
StackFrameFlags Flags;
|
||||
};
|
@ -23,8 +23,7 @@ namespace Mesen.GUI.Debugger.Controls
|
||||
|
||||
public event EventHandler FunctionSelected;
|
||||
|
||||
private Int32[] _absoluteCallstack;
|
||||
private Int32[] _relativeCallstack;
|
||||
private StackFrameInfo[] _stackFrames;
|
||||
private Int32 _programCounter;
|
||||
|
||||
public ctrlCallstack()
|
||||
@ -43,7 +42,7 @@ namespace Mesen.GUI.Debugger.Controls
|
||||
int nmiHandler = InteropEmu.DebugGetMemoryValue(DebugMemoryType.CpuMemory, 0xFFFA) | (InteropEmu.DebugGetMemoryValue(DebugMemoryType.CpuMemory, 0xFFFB) << 8);
|
||||
int irqHandler = InteropEmu.DebugGetMemoryValue(DebugMemoryType.CpuMemory, 0xFFFE) | (InteropEmu.DebugGetMemoryValue(DebugMemoryType.CpuMemory, 0xFFFF) << 8);
|
||||
|
||||
InteropEmu.DebugGetCallstack(out _absoluteCallstack, out _relativeCallstack);
|
||||
_stackFrames = InteropEmu.DebugGetCallstack();
|
||||
DebugState state = new DebugState();
|
||||
InteropEmu.DebugGetState(ref state);
|
||||
_programCounter = state.CPU.DebugPC;
|
||||
@ -51,23 +50,19 @@ namespace Mesen.GUI.Debugger.Controls
|
||||
int relDestinationAddr = -1, absDestinationAddr = -1;
|
||||
|
||||
List<StackInfo> stack = new List<StackInfo>();
|
||||
for(int i = 0, len = _relativeCallstack.Length; i < len; i+=2) {
|
||||
if(_relativeCallstack[i] == -2) {
|
||||
break;
|
||||
}
|
||||
|
||||
int relSubEntryAddr = i == 0 ? -1 : _relativeCallstack[i-1] & 0xFFFF;
|
||||
int absSubEntryAddr = i == 0 ? -1 : _absoluteCallstack[i-1];
|
||||
for(int i = 0, len = _stackFrames.Length; i < len; i++) {
|
||||
int relSubEntryAddr = i == 0 ? -1 : _stackFrames[i-1].JumpTarget;
|
||||
int absSubEntryAddr = i == 0 ? -1 : _stackFrames[i-1].JumpTargetAbsolute;
|
||||
|
||||
stack.Add(new StackInfo() {
|
||||
SubName = this.GetFunctionName(relSubEntryAddr, absSubEntryAddr, nmiHandler, irqHandler),
|
||||
IsMapped = (_relativeCallstack[i] & 0x10000) != 0x10000,
|
||||
CurrentRelativeAddr = _relativeCallstack[i] & 0xFFFF,
|
||||
CurrentAbsoluteAddr = _absoluteCallstack[i]
|
||||
IsMapped = _stackFrames[i].JumpSourceAbsolute < 0 ? false : InteropEmu.DebugGetRelativeAddress((uint)_stackFrames[i].JumpSourceAbsolute, AddressType.PrgRom) >= 0,
|
||||
CurrentRelativeAddr = _stackFrames[i].JumpSource,
|
||||
CurrentAbsoluteAddr = _stackFrames[i].JumpSourceAbsolute
|
||||
});
|
||||
|
||||
relDestinationAddr = _relativeCallstack[i+1] & 0xFFFF;
|
||||
absDestinationAddr = _absoluteCallstack[i+1];
|
||||
relDestinationAddr = _stackFrames[i].JumpTarget;
|
||||
absDestinationAddr = _stackFrames[i].JumpTargetAbsolute;
|
||||
}
|
||||
|
||||
//Add current location
|
||||
@ -109,12 +104,7 @@ namespace Mesen.GUI.Debugger.Controls
|
||||
}
|
||||
this.lstCallstack.EndUpdate();
|
||||
}
|
||||
|
||||
private void UpdateList()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
private string GetFunctionName(int relSubEntryAddr, int absSubEntryAddr, int nmiHandler, int irqHandler)
|
||||
{
|
||||
if(relSubEntryAddr < 0) {
|
||||
@ -122,7 +112,7 @@ namespace Mesen.GUI.Debugger.Controls
|
||||
}
|
||||
|
||||
string funcName;
|
||||
CodeLabel label = LabelManager.GetLabel((UInt32)absSubEntryAddr, AddressType.PrgRom);
|
||||
CodeLabel label = absSubEntryAddr >= 0 ? LabelManager.GetLabel((UInt32)absSubEntryAddr, AddressType.PrgRom) : null;
|
||||
if(label != null) {
|
||||
funcName = label.Label + (relSubEntryAddr >= 0 ? (" ($" + relSubEntryAddr.ToString("X4") + ")") : "");
|
||||
} else {
|
||||
@ -143,10 +133,8 @@ namespace Mesen.GUI.Debugger.Controls
|
||||
if(this.lstCallstack.SelectedIndices[0] == 0) {
|
||||
this.FunctionSelected(_programCounter, null);
|
||||
} else {
|
||||
Int32 address = _relativeCallstack[(this.lstCallstack.Items.Count - 1 - this.lstCallstack.SelectedIndices[0]) * 2];
|
||||
if((address & 0x10000) == 0) {
|
||||
this.FunctionSelected?.Invoke(address & 0xFFFF, null);
|
||||
}
|
||||
StackFrameInfo stackFrameInfo = _stackFrames[(this.lstCallstack.Items.Count - 1 - this.lstCallstack.SelectedIndices[0])];
|
||||
this.FunctionSelected?.Invoke((int)stackFrameInfo.JumpSource, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -579,20 +579,21 @@ namespace Mesen.GUI
|
||||
return cdlData;
|
||||
}
|
||||
|
||||
[DllImport(DLLPath, EntryPoint = "DebugGetCallstack")] private static extern void DebugGetCallstackWrapper(IntPtr callstackAbsolute, IntPtr callstackRelative);
|
||||
public static void DebugGetCallstack(out Int32[] callstackAbsolute, out Int32[] callstackRelative)
|
||||
[DllImport(DLLPath, EntryPoint = "DebugGetCallstack")] private static extern void DebugGetCallstackWrapper(IntPtr callstackArray, ref UInt32 callstackSize);
|
||||
public static StackFrameInfo[] DebugGetCallstack()
|
||||
{
|
||||
callstackAbsolute = new Int32[1024];
|
||||
callstackRelative = new Int32[1024];
|
||||
StackFrameInfo[] callstack = new StackFrameInfo[512];
|
||||
UInt32 callstackSize = 0;
|
||||
|
||||
GCHandle hAbsolute = GCHandle.Alloc(callstackAbsolute, GCHandleType.Pinned);
|
||||
GCHandle hRelative = GCHandle.Alloc(callstackRelative, GCHandleType.Pinned);
|
||||
GCHandle hCallstack = GCHandle.Alloc(callstack, GCHandleType.Pinned);
|
||||
try {
|
||||
InteropEmu.DebugGetCallstackWrapper(hAbsolute.AddrOfPinnedObject(), hRelative.AddrOfPinnedObject());
|
||||
InteropEmu.DebugGetCallstackWrapper(hCallstack.AddrOfPinnedObject(), ref callstackSize);
|
||||
} finally {
|
||||
hAbsolute.Free();
|
||||
hRelative.Free();
|
||||
hCallstack.Free();
|
||||
}
|
||||
Array.Resize(ref callstack, (int)callstackSize);
|
||||
|
||||
return callstack;
|
||||
}
|
||||
[DllImport(DLLPath)] private static extern Int32 DebugGetFunctionEntryPointCount();
|
||||
[DllImport(DLLPath, EntryPoint = "DebugGetFunctionEntryPoints")] private static extern void DebugGetFunctionEntryPointsWrapper(IntPtr callstackAbsolute, Int32 maxCount);
|
||||
@ -1124,6 +1125,22 @@ namespace Mesen.GUI
|
||||
public SByte PpuLatch;
|
||||
}
|
||||
|
||||
public enum StackFrameFlags : byte
|
||||
{
|
||||
None = 0,
|
||||
Nmi = 1,
|
||||
Irq = 2
|
||||
}
|
||||
|
||||
public struct StackFrameInfo
|
||||
{
|
||||
public Int32 JumpSourceAbsolute;
|
||||
public Int32 JumpTargetAbsolute;
|
||||
public UInt16 JumpSource;
|
||||
public UInt16 JumpTarget;
|
||||
public StackFrameFlags Flags;
|
||||
};
|
||||
|
||||
public struct DebugState
|
||||
{
|
||||
public CPUState CPU;
|
||||
|
@ -63,7 +63,7 @@ extern "C"
|
||||
DllExport void __stdcall DebugGetSprites(uint32_t *frameBuffer) { GetDebugger()->GetMemoryDumper()->GetSprites(frameBuffer); }
|
||||
DllExport void __stdcall DebugGetPalette(uint32_t *frameBuffer) { GetDebugger()->GetMemoryDumper()->GetPalette(frameBuffer); }
|
||||
|
||||
DllExport void __stdcall DebugGetCallstack(int32_t *callstackAbsolute, int32_t *callstackRelative) { GetDebugger()->GetCallstack(callstackAbsolute, callstackRelative); }
|
||||
DllExport void __stdcall DebugGetCallstack(StackFrameInfo *callstackArray, uint32_t &callstackSize) { GetDebugger()->GetCallstack(callstackArray, callstackSize); }
|
||||
DllExport int32_t __stdcall DebugGetFunctionEntryPointCount() { return GetDebugger()->GetFunctionEntryPointCount(); }
|
||||
DllExport void __stdcall DebugGetFunctionEntryPoints(int32_t *entryPoints, int32_t maxCount) { GetDebugger()->GetFunctionEntryPoints(entryPoints, maxCount); }
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user