Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

debugger: add logging breakpoint and misc fixes #927

Merged
merged 2 commits into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 61 additions & 22 deletions src/Cafe/HW/Espresso/Debugger/Debugger.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "gui/guiWrapper.h"
#include "Debugger.h"
#include "Cafe/OS/RPL/rpl_structs.h"
#include "Cemu/PPCAssembler/ppcAssembler.h"
#include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h"
#include "Cemu/ExpressionParser/ExpressionParser.h"
Expand Down Expand Up @@ -74,7 +75,7 @@ uint32 debugger_getAddressOriginalOpcode(uint32 address)
auto bpItr = debugger_getFirstBP(address);
while (bpItr)
{
if (bpItr->bpType == DEBUGGER_BP_T_NORMAL || bpItr->bpType == DEBUGGER_BP_T_ONE_SHOT)
if (bpItr->isExecuteBP())
return bpItr->originalOpcodeValue;
bpItr = bpItr->next;
}
Expand Down Expand Up @@ -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
auto existingBP = debugger_getFirstBP(address);
if (existingBP && debuggerBPChain_hasType(existingBP, DEBUGGER_BP_T_NORMAL))
if (existingBP && debuggerBPChain_hasType(existingBP, bpType))
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_NORMAL, true);
DebuggerBreakpoint* bp = new DebuggerBreakpoint(address, originalOpcode, bpType, true);
debuggerBPChain_add(address, bp);
debugger_updateExecutionBreakpoint(address);
}

void debugger_createSingleShotExecuteBreakpoint(uint32 address)
void debugger_createExecuteBreakpoint(uint32 address)
{
// check if breakpoint already exists
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);
debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_NORMAL);
}

namespace coreinit
Expand Down Expand Up @@ -218,7 +210,7 @@ void debugger_handleSingleStepException(uint64 dr6)
}
if (catchBP)
{
debugger_createSingleShotExecuteBreakpoint(ppcInterpreterCurrentInstance->instructionPointer + 4);
debugger_createCodeBreakpoint(ppcInterpreterCurrentInstance->instructionPointer + 4, DEBUGGER_BP_T_ONE_SHOT);
}
}

Expand Down Expand Up @@ -250,7 +242,7 @@ void debugger_handleEntryBreakpoint(uint32 address)
if (!debuggerState.breakOnEntry)
return;

debugger_createExecuteBreakpoint(address);
debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_NORMAL);
}

void debugger_deleteBreakpoint(DebuggerBreakpoint* bp)
Expand Down Expand Up @@ -298,10 +290,12 @@ void debugger_toggleExecuteBreakpoint(uint32 address)
{
// delete existing breakpoint
debugger_deleteBreakpoint(existingBP);
return;
}
// create new
debugger_createExecuteBreakpoint(address);
else
{
// create new breakpoint
debugger_createExecuteBreakpoint(address);
}
}

void debugger_forceBreak()
Expand All @@ -327,7 +321,7 @@ void debugger_toggleBreakpoint(uint32 address, bool state, DebuggerBreakpoint* b
{
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;
debugger_updateExecutionBreakpoint(address);
Expand Down Expand Up @@ -486,7 +480,7 @@ bool debugger_stepOver(PPCInterpreter_t* hCPU)
return false;
}
// 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)
debugger_stepInto(hCPU);
debuggerWindow_moveIP();
Expand All @@ -506,8 +500,39 @@ void debugger_createPPCStateSnapshot(PPCInterpreter_t* hCPU)
debuggerState.debugSession.ppcSnapshot.cr[i] = hCPU->cr[i];
}

void DebugLogStackTrace(OSThread_t* thread, MPTR sp);

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.debuggedThreadMPTR = coreinitThread_getCurrentThreadMPTRDepr(hCPU);
debuggerState.debugSession.instructionPointer = hCPU->instructionPointer;
Expand Down Expand Up @@ -579,6 +604,20 @@ void debugger_shouldBreak(PPCInterpreter_t* hCPU)

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++)
ep.AddConstant(fmt::format("r{}", i), debuggerState.debugSession.ppcSnapshot.gpr[i]);
}
6 changes: 4 additions & 2 deletions src/Cafe/HW/Espresso/Debugger/Debugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#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_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_DEBUGGER 2 // breakpoint created by Cemu's debugger
Expand Down Expand Up @@ -42,7 +43,7 @@ struct DebuggerBreakpoint

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
Expand Down Expand Up @@ -98,8 +99,9 @@ extern debuggerState_t debuggerState;

// new API
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_toggleExecuteBreakpoint(uint32 address); // create/remove execute breakpoint
void debugger_toggleBreakpoint(uint32 address, bool state, DebuggerBreakpoint* bp);

void debugger_createMemoryBreakpoint(uint32 address, bool onRead, bool onWrite);
Expand Down
99 changes: 70 additions & 29 deletions src/gui/debugger/BreakpointWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@

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_DELETE_BP,
};

enum ItemColumns
Expand Down Expand Up @@ -118,6 +120,8 @@ void BreakpointWindow::OnUpdateView()
const char* typeName = "UKN";
if (bp->bpType == DEBUGGER_BP_T_NORMAL)
typeName = "X";
else if (bp->bpType == DEBUGGER_BP_T_LOGGING)
typeName = "LOG";
else if (bp->bpType == DEBUGGER_BP_T_ONE_SHOT)
typeName = "XS";
else if (bp->bpType == DEBUGGER_BP_T_MEMORY_READ)
Expand Down Expand Up @@ -211,54 +215,91 @@ void BreakpointWindow::OnLeftDClick(wxMouseEvent& event)

void BreakpointWindow::OnRightDown(wxMouseEvent& event)
{
wxMenu menu;
const auto position = event.GetPosition();
const sint32 index = (position.y / m_breakpoints->GetCharHeight()) - 2;
if (index < 0 || index >= m_breakpoints->GetItemCount())
{
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_WRITE, _("Create memory breakpoint (write)"));

menu.Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(BreakpointWindow::OnContextMenuClick), nullptr, this);
PopupMenu(&menu);
}
else
{
m_breakpoints->SetItemState(index, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
m_breakpoints->SetItemState(index, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);

menu.Append(MENU_ID_CREATE_MEM_BP_READ, _("Create memory breakpoint (read)"));
menu.Append(MENU_ID_CREATE_MEM_BP_WRITE, _("Create memory breakpoint (write)"));
wxMenu menu;
menu.Append(MENU_ID_DELETE_BP, _("Delete breakpoint"));

menu.Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(BreakpointWindow::OnContextMenuClick), nullptr, this);
PopupMenu(&menu);
menu.Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(BreakpointWindow::OnContextMenuClickSelected), nullptr, this);
PopupMenu(&menu);
}
}

void BreakpointWindow::OnContextMenuClick(wxCommandEvent& evt)
void BreakpointWindow::OnContextMenuClickSelected(wxCommandEvent& evt)
{
switch (evt.GetId())
if (evt.GetId() == MENU_ID_DELETE_BP)
{
case MENU_ID_CREATE_MEM_BP_READ:
MemoryBreakpointDialog(false);
return;
case MENU_ID_CREATE_MEM_BP_WRITE:
MemoryBreakpointDialog(true);
return;
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::MemoryBreakpointDialog(bool isWrite)
void BreakpointWindow::OnContextMenuClick(wxCommandEvent& evt)
{
wxTextEntryDialog goto_dialog(this, _("Enter a memory address"), _("Memory breakpoint"), wxEmptyString);
wxTextEntryDialog goto_dialog(this, _("Enter a memory address"), _("Set breakpoint"), wxEmptyString);
if (goto_dialog.ShowModal() == wxID_OK)
{
ExpressionParser parser;

auto value = goto_dialog.GetValue().ToStdString();
std::transform(value.begin(), value.end(), value.begin(), tolower);


uint32_t newBreakpointAddress = 0;
try
{
debugger_addParserSymbols(parser);
const auto result = (uint32)parser.Evaluate(value);
debug_printf("goto eval result: %x\n", result);

debugger_createMemoryBreakpoint(result, isWrite == false, isWrite == true);
this->OnUpdateView();
newBreakpointAddress = parser.IsConstantExpression("0x"+value) ? (uint32)parser.Evaluate("0x"+value) : (uint32)parser.Evaluate(value);
}
catch (const std::exception& e)
catch (const std::exception& ex)
{
//ctx.errorHandler.printError(nullptr, -1, fmt::format("Unexpected error in expression \"{}\"", expressionString));
//return EXPRESSION_RESOLVE_RESULT::EXPRESSION_ERROR;
wxMessageBox(e.what(), "Invalid expression");
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;
}

this->OnUpdateView();
}
}
}
3 changes: 1 addition & 2 deletions src/gui/debugger/BreakpointWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ class BreakpointWindow : public wxFrame
void OnRightDown(wxMouseEvent& event);

void OnContextMenuClick(wxCommandEvent& evt);

void MemoryBreakpointDialog(bool isWrite);
void OnContextMenuClickSelected(wxCommandEvent& evt);

wxCheckedListCtrl* m_breakpoints;
};
11 changes: 7 additions & 4 deletions src/gui/debugger/DebuggerWindow2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ void DebuggerModuleStorage::Load(XMLConfigParser& parser)
const auto comment = element.get("Comment", "");

// 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;

// don't change anything if there's already a breakpoint
Expand All @@ -127,7 +127,9 @@ void DebuggerModuleStorage::Load(XMLConfigParser& parser)

// register breakpoints in debugger
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)
debugger_createMemoryBreakpoint(address, true, false);
else if (type == DEBUGGER_BP_T_MEMORY_WRITE)
Expand Down Expand Up @@ -173,7 +175,7 @@ void DebuggerModuleStorage::Save(XMLConfigParser& parser)

// check whether the breakpoint is part of the current module being saved
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 continue;

Expand Down Expand Up @@ -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; });
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();
}
}

Expand Down Expand Up @@ -522,6 +524,7 @@ void DebuggerWindow2::OnToolClicked(wxCommandEvent& event)
void DebuggerWindow2::OnBreakpointChange(wxCommandEvent& event)
{
m_breakpoint_window->OnUpdateView();
m_disasm_ctrl->RefreshControl();
UpdateModuleLabel();
}

Expand Down
Loading