mirror of
https://github.com/cemu-project/Cemu.git
synced 2024-11-29 12:34:17 +01:00
debugger: Add logging breakpoint + misc fixes (#927)
This commit is contained in:
parent
1d1e1e781b
commit
651e5336b4
@ -1,5 +1,6 @@
|
|||||||
#include "gui/guiWrapper.h"
|
#include "gui/guiWrapper.h"
|
||||||
#include "Debugger.h"
|
#include "Debugger.h"
|
||||||
|
#include "Cafe/OS/RPL/rpl_structs.h"
|
||||||
#include "Cemu/PPCAssembler/ppcAssembler.h"
|
#include "Cemu/PPCAssembler/ppcAssembler.h"
|
||||||
#include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h"
|
#include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h"
|
||||||
#include "Cemu/ExpressionParser/ExpressionParser.h"
|
#include "Cemu/ExpressionParser/ExpressionParser.h"
|
||||||
@ -74,7 +75,7 @@ uint32 debugger_getAddressOriginalOpcode(uint32 address)
|
|||||||
auto bpItr = debugger_getFirstBP(address);
|
auto bpItr = debugger_getFirstBP(address);
|
||||||
while (bpItr)
|
while (bpItr)
|
||||||
{
|
{
|
||||||
if (bpItr->bpType == DEBUGGER_BP_T_NORMAL || bpItr->bpType == DEBUGGER_BP_T_ONE_SHOT)
|
if (bpItr->isExecuteBP())
|
||||||
return bpItr->originalOpcodeValue;
|
return bpItr->originalOpcodeValue;
|
||||||
bpItr = bpItr->next;
|
bpItr = bpItr->next;
|
||||||
}
|
}
|
||||||
@ -121,32 +122,23 @@ void debugger_updateExecutionBreakpoint(uint32 address, bool forceRestore)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void debugger_createExecuteBreakpoint(uint32 address)
|
void debugger_createCodeBreakpoint(uint32 address, uint8 bpType)
|
||||||
{
|
{
|
||||||
// check if breakpoint already exists
|
// check if breakpoint already exists
|
||||||
auto existingBP = debugger_getFirstBP(address);
|
auto existingBP = debugger_getFirstBP(address);
|
||||||
if (existingBP && debuggerBPChain_hasType(existingBP, DEBUGGER_BP_T_NORMAL))
|
if (existingBP && debuggerBPChain_hasType(existingBP, bpType))
|
||||||
return; // breakpoint already exists
|
return; // breakpoint already exists
|
||||||
// get original opcode at address
|
// get original opcode at address
|
||||||
uint32 originalOpcode = debugger_getAddressOriginalOpcode(address);
|
uint32 originalOpcode = debugger_getAddressOriginalOpcode(address);
|
||||||
// init breakpoint object
|
// init breakpoint object
|
||||||
DebuggerBreakpoint* bp = new DebuggerBreakpoint(address, originalOpcode, DEBUGGER_BP_T_NORMAL, true);
|
DebuggerBreakpoint* bp = new DebuggerBreakpoint(address, originalOpcode, bpType, true);
|
||||||
debuggerBPChain_add(address, bp);
|
debuggerBPChain_add(address, bp);
|
||||||
debugger_updateExecutionBreakpoint(address);
|
debugger_updateExecutionBreakpoint(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
void debugger_createSingleShotExecuteBreakpoint(uint32 address)
|
void debugger_createExecuteBreakpoint(uint32 address)
|
||||||
{
|
{
|
||||||
// check if breakpoint already exists
|
debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_NORMAL);
|
||||||
auto existingBP = debugger_getFirstBP(address);
|
|
||||||
if (existingBP && debuggerBPChain_hasType(existingBP, DEBUGGER_BP_T_ONE_SHOT))
|
|
||||||
return; // breakpoint already exists
|
|
||||||
// get original opcode at address
|
|
||||||
uint32 originalOpcode = debugger_getAddressOriginalOpcode(address);
|
|
||||||
// init breakpoint object
|
|
||||||
DebuggerBreakpoint* bp = new DebuggerBreakpoint(address, originalOpcode, DEBUGGER_BP_T_ONE_SHOT, true);
|
|
||||||
debuggerBPChain_add(address, bp);
|
|
||||||
debugger_updateExecutionBreakpoint(address);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace coreinit
|
namespace coreinit
|
||||||
@ -218,7 +210,7 @@ void debugger_handleSingleStepException(uint64 dr6)
|
|||||||
}
|
}
|
||||||
if (catchBP)
|
if (catchBP)
|
||||||
{
|
{
|
||||||
debugger_createSingleShotExecuteBreakpoint(ppcInterpreterCurrentInstance->instructionPointer + 4);
|
debugger_createCodeBreakpoint(ppcInterpreterCurrentInstance->instructionPointer + 4, DEBUGGER_BP_T_ONE_SHOT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,7 +242,7 @@ void debugger_handleEntryBreakpoint(uint32 address)
|
|||||||
if (!debuggerState.breakOnEntry)
|
if (!debuggerState.breakOnEntry)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
debugger_createExecuteBreakpoint(address);
|
debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void debugger_deleteBreakpoint(DebuggerBreakpoint* bp)
|
void debugger_deleteBreakpoint(DebuggerBreakpoint* bp)
|
||||||
@ -298,11 +290,13 @@ void debugger_toggleExecuteBreakpoint(uint32 address)
|
|||||||
{
|
{
|
||||||
// delete existing breakpoint
|
// delete existing breakpoint
|
||||||
debugger_deleteBreakpoint(existingBP);
|
debugger_deleteBreakpoint(existingBP);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// create new
|
else
|
||||||
|
{
|
||||||
|
// create new breakpoint
|
||||||
debugger_createExecuteBreakpoint(address);
|
debugger_createExecuteBreakpoint(address);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void debugger_forceBreak()
|
void debugger_forceBreak()
|
||||||
{
|
{
|
||||||
@ -327,7 +321,7 @@ void debugger_toggleBreakpoint(uint32 address, bool state, DebuggerBreakpoint* b
|
|||||||
{
|
{
|
||||||
if (bpItr == bp)
|
if (bpItr == bp)
|
||||||
{
|
{
|
||||||
if (bpItr->bpType == DEBUGGER_BP_T_NORMAL)
|
if (bpItr->bpType == DEBUGGER_BP_T_NORMAL || bpItr->bpType == DEBUGGER_BP_T_LOGGING)
|
||||||
{
|
{
|
||||||
bp->enabled = state;
|
bp->enabled = state;
|
||||||
debugger_updateExecutionBreakpoint(address);
|
debugger_updateExecutionBreakpoint(address);
|
||||||
@ -486,7 +480,7 @@ bool debugger_stepOver(PPCInterpreter_t* hCPU)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// create one-shot breakpoint at next instruction
|
// create one-shot breakpoint at next instruction
|
||||||
debugger_createSingleShotExecuteBreakpoint(initialIP +4);
|
debugger_createCodeBreakpoint(initialIP + 4, DEBUGGER_BP_T_ONE_SHOT);
|
||||||
// step over current instruction (to avoid breakpoint)
|
// step over current instruction (to avoid breakpoint)
|
||||||
debugger_stepInto(hCPU);
|
debugger_stepInto(hCPU);
|
||||||
debuggerWindow_moveIP();
|
debuggerWindow_moveIP();
|
||||||
@ -506,8 +500,39 @@ void debugger_createPPCStateSnapshot(PPCInterpreter_t* hCPU)
|
|||||||
debuggerState.debugSession.ppcSnapshot.cr[i] = hCPU->cr[i];
|
debuggerState.debugSession.ppcSnapshot.cr[i] = hCPU->cr[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DebugLogStackTrace(OSThread_t* thread, MPTR sp);
|
||||||
|
|
||||||
void debugger_enterTW(PPCInterpreter_t* hCPU)
|
void debugger_enterTW(PPCInterpreter_t* hCPU)
|
||||||
{
|
{
|
||||||
|
// handle logging points
|
||||||
|
DebuggerBreakpoint* bp = debugger_getFirstBP(hCPU->instructionPointer);
|
||||||
|
bool shouldBreak = debuggerBPChain_hasType(bp, DEBUGGER_BP_T_NORMAL) || debuggerBPChain_hasType(bp, DEBUGGER_BP_T_ONE_SHOT);
|
||||||
|
while (bp)
|
||||||
|
{
|
||||||
|
if (bp->bpType == DEBUGGER_BP_T_LOGGING && bp->enabled)
|
||||||
|
{
|
||||||
|
std::wstring logName = !bp->comment.empty() ? L"Breakpoint '"+bp->comment+L"'" : fmt::format(L"Breakpoint at 0x{:08X} (no comment)", bp->address);
|
||||||
|
std::wstring logContext = fmt::format(L"Thread: {:08x} LR: 0x{:08x}", coreinitThread_getCurrentThreadMPTRDepr(hCPU), hCPU->spr.LR, cemuLog_advancedPPCLoggingEnabled() ? L" Stack Trace:" : L"");
|
||||||
|
cemuLog_log(LogType::Force, L"[Debugger] {} was executed! {}", logName, logContext);
|
||||||
|
if (cemuLog_advancedPPCLoggingEnabled())
|
||||||
|
DebugLogStackTrace(coreinitThread_getCurrentThreadDepr(hCPU), hCPU->gpr[1]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bp = bp->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return early if it's only a non-pausing logging breakpoint to prevent a modified debugger state and GUI updates
|
||||||
|
if (!shouldBreak)
|
||||||
|
{
|
||||||
|
uint32 backupIP = debuggerState.debugSession.instructionPointer;
|
||||||
|
debuggerState.debugSession.instructionPointer = hCPU->instructionPointer;
|
||||||
|
debugger_stepInto(hCPU, false);
|
||||||
|
PPCInterpreterSlim_executeInstruction(hCPU);
|
||||||
|
debuggerState.debugSession.instructionPointer = backupIP;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle breakpoints
|
||||||
debuggerState.debugSession.isTrapped = true;
|
debuggerState.debugSession.isTrapped = true;
|
||||||
debuggerState.debugSession.debuggedThreadMPTR = coreinitThread_getCurrentThreadMPTRDepr(hCPU);
|
debuggerState.debugSession.debuggedThreadMPTR = coreinitThread_getCurrentThreadMPTRDepr(hCPU);
|
||||||
debuggerState.debugSession.instructionPointer = hCPU->instructionPointer;
|
debuggerState.debugSession.instructionPointer = hCPU->instructionPointer;
|
||||||
@ -579,6 +604,20 @@ void debugger_shouldBreak(PPCInterpreter_t* hCPU)
|
|||||||
|
|
||||||
void debugger_addParserSymbols(class ExpressionParser& ep)
|
void debugger_addParserSymbols(class ExpressionParser& ep)
|
||||||
{
|
{
|
||||||
|
const auto module_count = RPLLoader_GetModuleCount();
|
||||||
|
const auto module_list = RPLLoader_GetModuleList();
|
||||||
|
|
||||||
|
std::vector<double> module_tmp(module_count);
|
||||||
|
for (int i = 0; i < module_count; i++)
|
||||||
|
{
|
||||||
|
const auto module = module_list[i];
|
||||||
|
if (module)
|
||||||
|
{
|
||||||
|
module_tmp[i] = (double)module->regionMappingBase_text.GetMPTR();
|
||||||
|
ep.AddConstant(module->moduleName2, module_tmp[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (sint32 i = 0; i < 32; i++)
|
for (sint32 i = 0; i < 32; i++)
|
||||||
ep.AddConstant(fmt::format("r{}", i), debuggerState.debugSession.ppcSnapshot.gpr[i]);
|
ep.AddConstant(fmt::format("r{}", i), debuggerState.debugSession.ppcSnapshot.gpr[i]);
|
||||||
}
|
}
|
@ -7,6 +7,7 @@
|
|||||||
#define DEBUGGER_BP_T_ONE_SHOT 1 // normal breakpoint, deletes itself after trigger (used for stepping)
|
#define DEBUGGER_BP_T_ONE_SHOT 1 // normal breakpoint, deletes itself after trigger (used for stepping)
|
||||||
#define DEBUGGER_BP_T_MEMORY_READ 2 // memory breakpoint
|
#define DEBUGGER_BP_T_MEMORY_READ 2 // memory breakpoint
|
||||||
#define DEBUGGER_BP_T_MEMORY_WRITE 3 // memory breakpoint
|
#define DEBUGGER_BP_T_MEMORY_WRITE 3 // memory breakpoint
|
||||||
|
#define DEBUGGER_BP_T_LOGGING 4 // logging breakpoint, prints the breakpoint comment and stack trace whenever hit
|
||||||
|
|
||||||
#define DEBUGGER_BP_T_GDBSTUB 1 // breakpoint created by GDBStub
|
#define DEBUGGER_BP_T_GDBSTUB 1 // breakpoint created by GDBStub
|
||||||
#define DEBUGGER_BP_T_DEBUGGER 2 // breakpoint created by Cemu's debugger
|
#define DEBUGGER_BP_T_DEBUGGER 2 // breakpoint created by Cemu's debugger
|
||||||
@ -42,7 +43,7 @@ struct DebuggerBreakpoint
|
|||||||
|
|
||||||
bool isExecuteBP() const
|
bool isExecuteBP() const
|
||||||
{
|
{
|
||||||
return bpType == DEBUGGER_BP_T_NORMAL || bpType == DEBUGGER_BP_T_ONE_SHOT;
|
return bpType == DEBUGGER_BP_T_NORMAL || bpType == DEBUGGER_BP_T_LOGGING || bpType == DEBUGGER_BP_T_ONE_SHOT;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isMemBP() const
|
bool isMemBP() const
|
||||||
@ -98,8 +99,9 @@ extern debuggerState_t debuggerState;
|
|||||||
|
|
||||||
// new API
|
// new API
|
||||||
DebuggerBreakpoint* debugger_getFirstBP(uint32 address);
|
DebuggerBreakpoint* debugger_getFirstBP(uint32 address);
|
||||||
void debugger_toggleExecuteBreakpoint(uint32 address); // create/remove execute breakpoint
|
void debugger_createCodeBreakpoint(uint32 address, uint8 bpType);
|
||||||
void debugger_createExecuteBreakpoint(uint32 address);
|
void debugger_createExecuteBreakpoint(uint32 address);
|
||||||
|
void debugger_toggleExecuteBreakpoint(uint32 address); // create/remove execute breakpoint
|
||||||
void debugger_toggleBreakpoint(uint32 address, bool state, DebuggerBreakpoint* bp);
|
void debugger_toggleBreakpoint(uint32 address, bool state, DebuggerBreakpoint* bp);
|
||||||
|
|
||||||
void debugger_createMemoryBreakpoint(uint32 address, bool onRead, bool onWrite);
|
void debugger_createMemoryBreakpoint(uint32 address, bool onRead, bool onWrite);
|
||||||
|
@ -11,9 +11,11 @@
|
|||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
MENU_ID_CREATE_MEM_BP_READ = 1,
|
MENU_ID_CREATE_CODE_BP_EXECUTION = 1,
|
||||||
|
MENU_ID_CREATE_CODE_BP_LOGGING,
|
||||||
|
MENU_ID_CREATE_MEM_BP_READ,
|
||||||
MENU_ID_CREATE_MEM_BP_WRITE,
|
MENU_ID_CREATE_MEM_BP_WRITE,
|
||||||
|
MENU_ID_DELETE_BP,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ItemColumns
|
enum ItemColumns
|
||||||
@ -118,6 +120,8 @@ void BreakpointWindow::OnUpdateView()
|
|||||||
const char* typeName = "UKN";
|
const char* typeName = "UKN";
|
||||||
if (bp->bpType == DEBUGGER_BP_T_NORMAL)
|
if (bp->bpType == DEBUGGER_BP_T_NORMAL)
|
||||||
typeName = "X";
|
typeName = "X";
|
||||||
|
else if (bp->bpType == DEBUGGER_BP_T_LOGGING)
|
||||||
|
typeName = "LOG";
|
||||||
else if (bp->bpType == DEBUGGER_BP_T_ONE_SHOT)
|
else if (bp->bpType == DEBUGGER_BP_T_ONE_SHOT)
|
||||||
typeName = "XS";
|
typeName = "XS";
|
||||||
else if (bp->bpType == DEBUGGER_BP_T_MEMORY_READ)
|
else if (bp->bpType == DEBUGGER_BP_T_MEMORY_READ)
|
||||||
@ -210,32 +214,57 @@ void BreakpointWindow::OnLeftDClick(wxMouseEvent& event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BreakpointWindow::OnRightDown(wxMouseEvent& event)
|
void BreakpointWindow::OnRightDown(wxMouseEvent& event)
|
||||||
|
{
|
||||||
|
const auto position = event.GetPosition();
|
||||||
|
const sint32 index = (position.y / m_breakpoints->GetCharHeight()) - 2;
|
||||||
|
if (index < 0 || index >= m_breakpoints->GetItemCount())
|
||||||
{
|
{
|
||||||
wxMenu menu;
|
wxMenu menu;
|
||||||
|
menu.Append(MENU_ID_CREATE_CODE_BP_EXECUTION, _("Create execution breakpoint"));
|
||||||
|
menu.Append(MENU_ID_CREATE_CODE_BP_LOGGING, _("Create logging breakpoint"));
|
||||||
menu.Append(MENU_ID_CREATE_MEM_BP_READ, _("Create memory breakpoint (read)"));
|
menu.Append(MENU_ID_CREATE_MEM_BP_READ, _("Create memory breakpoint (read)"));
|
||||||
menu.Append(MENU_ID_CREATE_MEM_BP_WRITE, _("Create memory breakpoint (write)"));
|
menu.Append(MENU_ID_CREATE_MEM_BP_WRITE, _("Create memory breakpoint (write)"));
|
||||||
|
|
||||||
menu.Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(BreakpointWindow::OnContextMenuClick), nullptr, this);
|
menu.Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(BreakpointWindow::OnContextMenuClick), nullptr, this);
|
||||||
PopupMenu(&menu);
|
PopupMenu(&menu);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_breakpoints->SetItemState(index, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
|
||||||
|
m_breakpoints->SetItemState(index, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
|
||||||
|
|
||||||
|
wxMenu menu;
|
||||||
|
menu.Append(MENU_ID_DELETE_BP, _("Delete breakpoint"));
|
||||||
|
|
||||||
|
menu.Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(BreakpointWindow::OnContextMenuClickSelected), nullptr, this);
|
||||||
|
PopupMenu(&menu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BreakpointWindow::OnContextMenuClickSelected(wxCommandEvent& evt)
|
||||||
|
{
|
||||||
|
if (evt.GetId() == MENU_ID_DELETE_BP)
|
||||||
|
{
|
||||||
|
long sel = m_breakpoints->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||||
|
if (sel != -1)
|
||||||
|
{
|
||||||
|
if (sel >= debuggerState.breakpoints.size())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto it = debuggerState.breakpoints.begin();
|
||||||
|
std::advance(it, sel);
|
||||||
|
|
||||||
|
debugger_deleteBreakpoint(*it);
|
||||||
|
|
||||||
|
wxCommandEvent evt(wxEVT_BREAKPOINT_CHANGE);
|
||||||
|
wxPostEvent(this->m_parent, evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void BreakpointWindow::OnContextMenuClick(wxCommandEvent& evt)
|
void BreakpointWindow::OnContextMenuClick(wxCommandEvent& evt)
|
||||||
{
|
{
|
||||||
switch (evt.GetId())
|
wxTextEntryDialog goto_dialog(this, _("Enter a memory address"), _("Set breakpoint"), wxEmptyString);
|
||||||
{
|
|
||||||
case MENU_ID_CREATE_MEM_BP_READ:
|
|
||||||
MemoryBreakpointDialog(false);
|
|
||||||
return;
|
|
||||||
case MENU_ID_CREATE_MEM_BP_WRITE:
|
|
||||||
MemoryBreakpointDialog(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BreakpointWindow::MemoryBreakpointDialog(bool isWrite)
|
|
||||||
{
|
|
||||||
wxTextEntryDialog goto_dialog(this, _("Enter a memory address"), _("Memory breakpoint"), wxEmptyString);
|
|
||||||
if (goto_dialog.ShowModal() == wxID_OK)
|
if (goto_dialog.ShowModal() == wxID_OK)
|
||||||
{
|
{
|
||||||
ExpressionParser parser;
|
ExpressionParser parser;
|
||||||
@ -243,22 +272,34 @@ void BreakpointWindow::MemoryBreakpointDialog(bool isWrite)
|
|||||||
auto value = goto_dialog.GetValue().ToStdString();
|
auto value = goto_dialog.GetValue().ToStdString();
|
||||||
std::transform(value.begin(), value.end(), value.begin(), tolower);
|
std::transform(value.begin(), value.end(), value.begin(), tolower);
|
||||||
|
|
||||||
|
uint32_t newBreakpointAddress = 0;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
debugger_addParserSymbols(parser);
|
debugger_addParserSymbols(parser);
|
||||||
const auto result = (uint32)parser.Evaluate(value);
|
newBreakpointAddress = parser.IsConstantExpression("0x"+value) ? (uint32)parser.Evaluate("0x"+value) : (uint32)parser.Evaluate(value);
|
||||||
debug_printf("goto eval result: %x\n", result);
|
}
|
||||||
|
catch (const std::exception& ex)
|
||||||
|
{
|
||||||
|
wxMessageBox(ex.what(), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (evt.GetId())
|
||||||
|
{
|
||||||
|
case MENU_ID_CREATE_CODE_BP_EXECUTION:
|
||||||
|
debugger_createCodeBreakpoint(newBreakpointAddress, DEBUGGER_BP_T_NORMAL);
|
||||||
|
break;
|
||||||
|
case MENU_ID_CREATE_CODE_BP_LOGGING:
|
||||||
|
debugger_createCodeBreakpoint(newBreakpointAddress, DEBUGGER_BP_T_LOGGING);
|
||||||
|
break;
|
||||||
|
case MENU_ID_CREATE_MEM_BP_READ:
|
||||||
|
debugger_createMemoryBreakpoint(newBreakpointAddress, true, false);
|
||||||
|
break;
|
||||||
|
case MENU_ID_CREATE_MEM_BP_WRITE:
|
||||||
|
debugger_createMemoryBreakpoint(newBreakpointAddress, false, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
debugger_createMemoryBreakpoint(result, isWrite == false, isWrite == true);
|
|
||||||
this->OnUpdateView();
|
this->OnUpdateView();
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
//ctx.errorHandler.printError(nullptr, -1, fmt::format("Unexpected error in expression \"{}\"", expressionString));
|
|
||||||
//return EXPRESSION_RESOLVE_RESULT::EXPRESSION_ERROR;
|
|
||||||
wxMessageBox(e.what(), "Invalid expression");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -19,8 +19,7 @@ private:
|
|||||||
void OnRightDown(wxMouseEvent& event);
|
void OnRightDown(wxMouseEvent& event);
|
||||||
|
|
||||||
void OnContextMenuClick(wxCommandEvent& evt);
|
void OnContextMenuClick(wxCommandEvent& evt);
|
||||||
|
void OnContextMenuClickSelected(wxCommandEvent& evt);
|
||||||
void MemoryBreakpointDialog(bool isWrite);
|
|
||||||
|
|
||||||
wxCheckedListCtrl* m_breakpoints;
|
wxCheckedListCtrl* m_breakpoints;
|
||||||
};
|
};
|
@ -118,7 +118,7 @@ void DebuggerModuleStorage::Load(XMLConfigParser& parser)
|
|||||||
const auto comment = element.get("Comment", "");
|
const auto comment = element.get("Comment", "");
|
||||||
|
|
||||||
// calculate absolute address
|
// calculate absolute address
|
||||||
uint32 module_base_address = (type == DEBUGGER_BP_T_NORMAL ? this->rpl_module->regionMappingBase_text.GetMPTR() : this->rpl_module->regionMappingBase_data);
|
uint32 module_base_address = (type == DEBUGGER_BP_T_NORMAL || type == DEBUGGER_BP_T_LOGGING) ? this->rpl_module->regionMappingBase_text.GetMPTR() : this->rpl_module->regionMappingBase_data;
|
||||||
uint32 address = module_base_address + relative_address;
|
uint32 address = module_base_address + relative_address;
|
||||||
|
|
||||||
// don't change anything if there's already a breakpoint
|
// don't change anything if there's already a breakpoint
|
||||||
@ -127,7 +127,9 @@ void DebuggerModuleStorage::Load(XMLConfigParser& parser)
|
|||||||
|
|
||||||
// register breakpoints in debugger
|
// register breakpoints in debugger
|
||||||
if (type == DEBUGGER_BP_T_NORMAL)
|
if (type == DEBUGGER_BP_T_NORMAL)
|
||||||
debugger_createExecuteBreakpoint(address);
|
debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_NORMAL);
|
||||||
|
else if (type == DEBUGGER_BP_T_LOGGING)
|
||||||
|
debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_LOGGING);
|
||||||
else if (type == DEBUGGER_BP_T_MEMORY_READ)
|
else if (type == DEBUGGER_BP_T_MEMORY_READ)
|
||||||
debugger_createMemoryBreakpoint(address, true, false);
|
debugger_createMemoryBreakpoint(address, true, false);
|
||||||
else if (type == DEBUGGER_BP_T_MEMORY_WRITE)
|
else if (type == DEBUGGER_BP_T_MEMORY_WRITE)
|
||||||
@ -173,7 +175,7 @@ void DebuggerModuleStorage::Save(XMLConfigParser& parser)
|
|||||||
|
|
||||||
// check whether the breakpoint is part of the current module being saved
|
// check whether the breakpoint is part of the current module being saved
|
||||||
RPLModule* address_module;
|
RPLModule* address_module;
|
||||||
if (bp->bpType == DEBUGGER_BP_T_NORMAL) address_module = RPLLoader_FindModuleByCodeAddr(bp->address);
|
if (bp->bpType == DEBUGGER_BP_T_NORMAL || bp->bpType == DEBUGGER_BP_T_LOGGING) address_module = RPLLoader_FindModuleByCodeAddr(bp->address);
|
||||||
else if (bp->isMemBP()) address_module = RPLLoader_FindModuleByDataAddr(bp->address);
|
else if (bp->isMemBP()) address_module = RPLLoader_FindModuleByDataAddr(bp->address);
|
||||||
else continue;
|
else continue;
|
||||||
|
|
||||||
@ -259,7 +261,7 @@ void DebuggerWindow2::LoadModuleStorage(const RPLModule* module)
|
|||||||
bool already_loaded = std::any_of(m_modules_storage.begin(), m_modules_storage.end(), [path](const std::unique_ptr<XMLDebuggerModuleConfig>& debug) { return debug->GetFilename() == path; });
|
bool already_loaded = std::any_of(m_modules_storage.begin(), m_modules_storage.end(), [path](const std::unique_ptr<XMLDebuggerModuleConfig>& debug) { return debug->GetFilename() == path; });
|
||||||
if (!path.empty() && !already_loaded)
|
if (!path.empty() && !already_loaded)
|
||||||
{
|
{
|
||||||
m_modules_storage.emplace_back(std::move(new XMLDebuggerModuleConfig(path, { module->moduleName2, module->patchCRC, module, false })));
|
m_modules_storage.emplace_back(new XMLDebuggerModuleConfig(path, { module->moduleName2, module->patchCRC, module, false }))->Load();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -522,6 +524,7 @@ void DebuggerWindow2::OnToolClicked(wxCommandEvent& event)
|
|||||||
void DebuggerWindow2::OnBreakpointChange(wxCommandEvent& event)
|
void DebuggerWindow2::OnBreakpointChange(wxCommandEvent& event)
|
||||||
{
|
{
|
||||||
m_breakpoint_window->OnUpdateView();
|
m_breakpoint_window->OnUpdateView();
|
||||||
|
m_disasm_ctrl->RefreshControl();
|
||||||
UpdateModuleLabel();
|
UpdateModuleLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ void DisasmCtrl::DrawDisassemblyLine(wxDC& dc, const wxPoint& linePosition, MPTR
|
|||||||
else if (is_active_bp)
|
else if (is_active_bp)
|
||||||
background_colour = wxColour(0xFF80A0FF);
|
background_colour = wxColour(0xFF80A0FF);
|
||||||
else if (bp != nullptr)
|
else if (bp != nullptr)
|
||||||
background_colour = wxColour(0xFF8080FF);
|
background_colour = wxColour(bp->bpType == DEBUGGER_BP_T_NORMAL ? 0xFF8080FF : 0x80FFFFFF);
|
||||||
else if(virtualAddress == m_lastGotoTarget)
|
else if(virtualAddress == m_lastGotoTarget)
|
||||||
background_colour = wxColour(0xFFE0E0E0);
|
background_colour = wxColour(0xFFE0E0E0);
|
||||||
else
|
else
|
||||||
@ -540,8 +540,6 @@ void DisasmCtrl::OnKeyPressed(sint32 key_code, const wxPoint& position)
|
|||||||
{
|
{
|
||||||
debugger_toggleExecuteBreakpoint(*optVirtualAddress);
|
debugger_toggleExecuteBreakpoint(*optVirtualAddress);
|
||||||
|
|
||||||
RefreshControl();
|
|
||||||
|
|
||||||
wxCommandEvent evt(wxEVT_BREAKPOINT_CHANGE);
|
wxCommandEvent evt(wxEVT_BREAKPOINT_CHANGE);
|
||||||
wxPostEvent(this->m_parent, evt);
|
wxPostEvent(this->m_parent, evt);
|
||||||
}
|
}
|
||||||
@ -767,40 +765,31 @@ void DisasmCtrl::GoToAddressDialog()
|
|||||||
auto value = goto_dialog.GetValue().ToStdString();
|
auto value = goto_dialog.GetValue().ToStdString();
|
||||||
std::transform(value.begin(), value.end(), value.begin(), tolower);
|
std::transform(value.begin(), value.end(), value.begin(), tolower);
|
||||||
|
|
||||||
const auto module_count = RPLLoader_GetModuleCount();
|
debugger_addParserSymbols(parser);
|
||||||
const auto module_list = RPLLoader_GetModuleList();
|
|
||||||
|
|
||||||
std::vector<double> module_tmp(module_count);
|
// try to parse expression as hex value first (it should interpret 1234 as 0x1234, not 1234)
|
||||||
for (int i = 0; i < module_count; i++)
|
if (parser.IsConstantExpression("0x"+value))
|
||||||
{
|
{
|
||||||
const auto module = module_list[i];
|
const auto result = (uint32)parser.Evaluate("0x"+value);
|
||||||
if (module)
|
|
||||||
{
|
|
||||||
module_tmp[i] = (double)module->regionMappingBase_text.GetMPTR();
|
|
||||||
parser.AddConstant(module->moduleName2, module_tmp[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double grp_tmp[32];
|
|
||||||
PPCSnapshot& ppc_snapshot = debuggerState.debugSession.ppcSnapshot;
|
|
||||||
for (int i = 0; i < 32; i++)
|
|
||||||
{
|
|
||||||
char var_name[32];
|
|
||||||
sprintf(var_name, "r%d", i);
|
|
||||||
grp_tmp[i] = ppc_snapshot.gpr[i];
|
|
||||||
parser.AddConstant(var_name, grp_tmp[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const auto result = (uint32)parser.Evaluate(value);
|
|
||||||
debug_printf("goto eval result: %x\n", result);
|
|
||||||
m_lastGotoTarget = result;
|
m_lastGotoTarget = result;
|
||||||
CenterOffset(result);
|
CenterOffset(result);
|
||||||
debuggerWindow_updateViewThreadsafe2();
|
|
||||||
}
|
}
|
||||||
catch (const std::exception& )
|
else if (parser.IsConstantExpression(value))
|
||||||
{
|
{
|
||||||
|
const auto result = (uint32)parser.Evaluate(value);
|
||||||
|
m_lastGotoTarget = result;
|
||||||
|
CenterOffset(result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto _ = (uint32)parser.Evaluate(value);
|
||||||
|
}
|
||||||
|
catch (const std::exception& ex)
|
||||||
|
{
|
||||||
|
wxMessageBox(ex.what(), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -213,50 +213,40 @@ void DumpCtrl::GoToAddressDialog()
|
|||||||
{
|
{
|
||||||
wxTextEntryDialog goto_dialog(this, _("Enter a target address."), _("GoTo address"), wxEmptyString);
|
wxTextEntryDialog goto_dialog(this, _("Enter a target address."), _("GoTo address"), wxEmptyString);
|
||||||
if (goto_dialog.ShowModal() == wxID_OK)
|
if (goto_dialog.ShowModal() == wxID_OK)
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
ExpressionParser parser;
|
ExpressionParser parser;
|
||||||
|
|
||||||
auto value = goto_dialog.GetValue().ToStdString();
|
auto value = goto_dialog.GetValue().ToStdString();
|
||||||
std::transform(value.begin(), value.end(), value.begin(), tolower);
|
std::transform(value.begin(), value.end(), value.begin(), tolower);
|
||||||
//parser.SetExpr(value);
|
|
||||||
|
|
||||||
const auto module_count = RPLLoader_GetModuleCount();
|
debugger_addParserSymbols(parser);
|
||||||
const auto module_list = RPLLoader_GetModuleList();
|
|
||||||
|
|
||||||
std::vector<double> module_tmp(module_count);
|
// try to parse expression as hex value first (it should interpret 1234 as 0x1234, not 1234)
|
||||||
for (int i = 0; i < module_count; i++)
|
if (parser.IsConstantExpression("0x"+value))
|
||||||
{
|
{
|
||||||
const auto module = module_list[i];
|
const auto result = (uint32)parser.Evaluate("0x"+value);
|
||||||
if (module)
|
|
||||||
{
|
|
||||||
module_tmp[i] = (double)module->regionMappingBase_text.GetMPTR();
|
|
||||||
parser.AddConstant(module->moduleName2, module_tmp[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double grp_tmp[32];
|
|
||||||
PPCSnapshot& ppc_snapshot = debuggerState.debugSession.ppcSnapshot;
|
|
||||||
for (int i = 0; i < 32; i++)
|
|
||||||
{
|
|
||||||
char var_name[32];
|
|
||||||
sprintf(var_name, "r%d", i);
|
|
||||||
grp_tmp[i] = ppc_snapshot.gpr[i];
|
|
||||||
parser.AddConstant(var_name, grp_tmp[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto result = (uint32)parser.Evaluate(value);
|
|
||||||
debug_printf("goto eval result: %x\n", result);
|
|
||||||
m_lastGotoOffset = result;
|
m_lastGotoOffset = result;
|
||||||
CenterOffset(result);
|
CenterOffset(result);
|
||||||
}
|
}
|
||||||
|
else if (parser.IsConstantExpression(value))
|
||||||
|
{
|
||||||
|
const auto result = (uint32)parser.Evaluate(value);
|
||||||
|
m_lastGotoOffset = result;
|
||||||
|
CenterOffset(result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto _ = (uint32)parser.Evaluate(value);
|
||||||
|
}
|
||||||
catch (const std::exception& ex)
|
catch (const std::exception& ex)
|
||||||
{
|
{
|
||||||
wxMessageBox(ex.what(), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this);
|
wxMessageBox(ex.what(), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DumpCtrl::CenterOffset(uint32 offset)
|
void DumpCtrl::CenterOffset(uint32 offset)
|
||||||
{
|
{
|
||||||
|
@ -280,9 +280,7 @@ void DebugLogStackTrace(OSThread_t* thread, MPTR sp);
|
|||||||
|
|
||||||
void DebugPPCThreadsWindow::DumpStackTrace(OSThread_t* thread)
|
void DebugPPCThreadsWindow::DumpStackTrace(OSThread_t* thread)
|
||||||
{
|
{
|
||||||
cemuLog_log(LogType::Force, fmt::format("Dumping stack trace for thread {0:08x} LR: {1:08x}",
|
cemuLog_log(LogType::Force, "Dumping stack trace for thread {0:08x} LR: {1:08x}", memory_getVirtualOffsetFromPointer(thread), _swapEndianU32(thread->context.lr));
|
||||||
memory_getVirtualOffsetFromPointer(thread),
|
|
||||||
_swapEndianU32(thread->context.lr)));
|
|
||||||
DebugLogStackTrace(thread, _swapEndianU32(thread->context.gpr[1]));
|
DebugLogStackTrace(thread, _swapEndianU32(thread->context.gpr[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user