From 186e92221a387ccad76cdfda661cd1bd94d82429 Mon Sep 17 00:00:00 2001 From: Crementif <26669564+Crementif@users.noreply.github.com> Date: Fri, 7 Mar 2025 23:40:17 +0100 Subject: [PATCH] debugger: allow printing registers using logging breakpoint placeholders (#1510) This allows a savy user, developer or modder to change the comment field of a logging breakpoint to include placeholders such as {r3} or {f3} to log the register values whenever that code is hit. --- src/Cafe/HW/Espresso/Debugger/Debugger.cpp | 68 +++++++++++++++++++--- src/Cafe/HW/Espresso/Debugger/Debugger.h | 2 +- src/gui/debugger/BreakpointWindow.cpp | 12 ++-- src/gui/debugger/DisasmCtrl.cpp | 12 +++- src/gui/debugger/DisasmCtrl.h | 1 + 5 files changed, 78 insertions(+), 17 deletions(-) diff --git a/src/Cafe/HW/Espresso/Debugger/Debugger.cpp b/src/Cafe/HW/Espresso/Debugger/Debugger.cpp index 37e374d6..e84c9fda 100644 --- a/src/Cafe/HW/Espresso/Debugger/Debugger.cpp +++ b/src/Cafe/HW/Espresso/Debugger/Debugger.cpp @@ -8,6 +8,7 @@ #include "gui/debugger/DebuggerWindow2.h" #include "Cafe/OS/libs/coreinit/coreinit.h" +#include "util/helpers/helpers.h" #if BOOST_OS_WINDOWS #include @@ -136,11 +137,6 @@ void debugger_createCodeBreakpoint(uint32 address, uint8 bpType) debugger_updateExecutionBreakpoint(address); } -void debugger_createExecuteBreakpoint(uint32 address) -{ - debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_NORMAL); -} - namespace coreinit { std::vector& OSGetSchedulerThreads(); @@ -294,8 +290,23 @@ void debugger_toggleExecuteBreakpoint(uint32 address) } else { - // create new breakpoint - debugger_createExecuteBreakpoint(address); + // create new execution breakpoint + debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_NORMAL); + } +} + +void debugger_toggleLoggingBreakpoint(uint32 address) +{ + auto existingBP = debugger_getFirstBP(address, DEBUGGER_BP_T_LOGGING); + if (existingBP) + { + // delete existing breakpoint + debugger_deleteBreakpoint(existingBP); + } + else + { + // create new logging breakpoint + debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_LOGGING); } } @@ -538,7 +549,48 @@ void debugger_enterTW(PPCInterpreter_t* hCPU) { if (bp->bpType == DEBUGGER_BP_T_LOGGING && bp->enabled) { - std::string logName = !bp->comment.empty() ? "Breakpoint '"+boost::nowide::narrow(bp->comment)+"'" : fmt::format("Breakpoint at 0x{:08X} (no comment)", bp->address); + std::string comment = !bp->comment.empty() ? boost::nowide::narrow(bp->comment) : fmt::format("Breakpoint at 0x{:08X} (no comment)", bp->address); + + auto replacePlaceholders = [&](const std::string& prefix, const auto& formatFunc) + { + size_t pos = 0; + while ((pos = comment.find(prefix, pos)) != std::string::npos) + { + size_t endPos = comment.find('}', pos); + if (endPos == std::string::npos) + break; + + try + { + if (int regNum = ConvertString(comment.substr(pos + prefix.length(), endPos - pos - prefix.length())); regNum >= 0 && regNum < 32) + { + std::string replacement = formatFunc(regNum); + comment.replace(pos, endPos - pos + 1, replacement); + pos += replacement.length(); + } + else + { + pos = endPos + 1; + } + } + catch (...) + { + pos = endPos + 1; + } + } + }; + + // Replace integer register placeholders {rX} + replacePlaceholders("{r", [&](int regNum) { + return fmt::format("0x{:08X}", hCPU->gpr[regNum]); + }); + + // Replace floating point register placeholders {fX} + replacePlaceholders("{f", [&](int regNum) { + return fmt::format("{}", hCPU->fpr[regNum].fpr); + }); + + std::string logName = "Breakpoint '" + comment + "'"; std::string logContext = fmt::format("Thread: {:08x} LR: 0x{:08x}", MEMPTR(coreinit::OSGetCurrentThread()).GetMPTR(), hCPU->spr.LR, cemuLog_advancedPPCLoggingEnabled() ? " Stack Trace:" : ""); cemuLog_log(LogType::Force, "[Debugger] {} was executed! {}", logName, logContext); if (cemuLog_advancedPPCLoggingEnabled()) diff --git a/src/Cafe/HW/Espresso/Debugger/Debugger.h b/src/Cafe/HW/Espresso/Debugger/Debugger.h index 249c47b8..c220eb8a 100644 --- a/src/Cafe/HW/Espresso/Debugger/Debugger.h +++ b/src/Cafe/HW/Espresso/Debugger/Debugger.h @@ -100,8 +100,8 @@ extern debuggerState_t debuggerState; // new API DebuggerBreakpoint* debugger_getFirstBP(uint32 address); void debugger_createCodeBreakpoint(uint32 address, uint8 bpType); -void debugger_createExecuteBreakpoint(uint32 address); void debugger_toggleExecuteBreakpoint(uint32 address); // create/remove execute breakpoint +void debugger_toggleLoggingBreakpoint(uint32 address); // create/remove logging breakpoint void debugger_toggleBreakpoint(uint32 address, bool state, DebuggerBreakpoint* bp); void debugger_createMemoryBreakpoint(uint32 address, bool onRead, bool onWrite); diff --git a/src/gui/debugger/BreakpointWindow.cpp b/src/gui/debugger/BreakpointWindow.cpp index 63b92626..658a51ad 100644 --- a/src/gui/debugger/BreakpointWindow.cpp +++ b/src/gui/debugger/BreakpointWindow.cpp @@ -202,14 +202,14 @@ void BreakpointWindow::OnLeftDClick(wxMouseEvent& event) auto it = debuggerState.breakpoints.begin(); std::advance(it, index); - wxTextEntryDialog set_value_dialog(this, _("Enter a new comment."), wxString::Format(_("Set comment for breakpoint at address %08x"), address), (*it)->comment); - if (set_value_dialog.ShowModal() == wxID_OK) + const wxString dialogTitle = (*it)->bpType == DEBUGGER_BP_T_LOGGING ? _("Enter a new logging message") : _("Enter a new comment"); + const wxString dialogMessage = (*it)->bpType == DEBUGGER_BP_T_LOGGING ? _("Set logging message when code at address %08x is ran.\nUse placeholders like {r3} or {f3} to log register values") : _("Set comment for breakpoint at address %08x"); + wxTextEntryDialog set_comment_dialog(this, dialogMessage, dialogTitle, (*it)->comment); + if (set_comment_dialog.ShowModal() == wxID_OK) { - (*it)->comment = set_value_dialog.GetValue().ToStdWstring(); - m_breakpoints->SetItem(index, ColumnComment, set_value_dialog.GetValue()); + (*it)->comment = set_comment_dialog.GetValue().ToStdWstring(); + m_breakpoints->SetItem(index, ColumnComment, set_comment_dialog.GetValue()); } - - return; } } diff --git a/src/gui/debugger/DisasmCtrl.cpp b/src/gui/debugger/DisasmCtrl.cpp index 2f38d55e..e74d64b9 100644 --- a/src/gui/debugger/DisasmCtrl.cpp +++ b/src/gui/debugger/DisasmCtrl.cpp @@ -538,7 +538,7 @@ void DisasmCtrl::OnKeyPressed(sint32 key_code, const wxPoint& position) auto optVirtualAddress = LinePixelPosToAddress(position.y); switch (key_code) { - case WXK_F9: + case WXK_F9: { if (optVirtualAddress) { @@ -549,7 +549,7 @@ void DisasmCtrl::OnKeyPressed(sint32 key_code, const wxPoint& position) } return; } - case 'G': + case 'G': { if(IsKeyDown(WXK_CONTROL)) { @@ -686,6 +686,7 @@ void DisasmCtrl::OnContextMenu(const wxPoint& position, uint32 line) // show dialog wxMenu menu; menu.Append(IDContextMenu_ToggleBreakpoint, _("Toggle breakpoint")); + menu.Append(IDContextMenu_ToggleLoggingBreakpoint, _("Toggle logging point")); if(debugger_hasPatch(virtualAddress)) menu.Append(IDContextMenu_RestoreOriginalInstructions, _("Restore original instructions")); menu.AppendSeparator(); @@ -707,6 +708,13 @@ void DisasmCtrl::OnContextMenuEntryClicked(wxCommandEvent& event) wxPostEvent(this->m_parent, evt); break; } + case IDContextMenu_ToggleLoggingBreakpoint: + { + debugger_toggleLoggingBreakpoint(m_contextMenuAddress); + wxCommandEvent evt(wxEVT_BREAKPOINT_CHANGE); + wxPostEvent(this->m_parent, evt); + break; + } case IDContextMenu_RestoreOriginalInstructions: { debugger_removePatch(m_contextMenuAddress); diff --git a/src/gui/debugger/DisasmCtrl.h b/src/gui/debugger/DisasmCtrl.h index 5a67e49a..b526e8f9 100644 --- a/src/gui/debugger/DisasmCtrl.h +++ b/src/gui/debugger/DisasmCtrl.h @@ -8,6 +8,7 @@ class DisasmCtrl : public TextList enum { IDContextMenu_ToggleBreakpoint = wxID_HIGHEST + 1, + IDContextMenu_ToggleLoggingBreakpoint, IDContextMenu_RestoreOriginalInstructions, IDContextMenu_CopyAddress, IDContextMenu_CopyUnrelocatedAddress,