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.
This commit is contained in:
Crementif 2025-03-07 23:40:17 +01:00 committed by GitHub
parent 31d2db6f78
commit 186e92221a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 78 additions and 17 deletions

View file

@ -8,6 +8,7 @@
#include "gui/debugger/DebuggerWindow2.h" #include "gui/debugger/DebuggerWindow2.h"
#include "Cafe/OS/libs/coreinit/coreinit.h" #include "Cafe/OS/libs/coreinit/coreinit.h"
#include "util/helpers/helpers.h"
#if BOOST_OS_WINDOWS #if BOOST_OS_WINDOWS
#include <Windows.h> #include <Windows.h>
@ -136,11 +137,6 @@ void debugger_createCodeBreakpoint(uint32 address, uint8 bpType)
debugger_updateExecutionBreakpoint(address); debugger_updateExecutionBreakpoint(address);
} }
void debugger_createExecuteBreakpoint(uint32 address)
{
debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_NORMAL);
}
namespace coreinit namespace coreinit
{ {
std::vector<std::thread::native_handle_type>& OSGetSchedulerThreads(); std::vector<std::thread::native_handle_type>& OSGetSchedulerThreads();
@ -294,8 +290,23 @@ void debugger_toggleExecuteBreakpoint(uint32 address)
} }
else else
{ {
// create new breakpoint // create new execution breakpoint
debugger_createExecuteBreakpoint(address); 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) 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<int>(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<OSThread_t>(coreinit::OSGetCurrentThread()).GetMPTR(), hCPU->spr.LR, cemuLog_advancedPPCLoggingEnabled() ? " Stack Trace:" : ""); std::string logContext = fmt::format("Thread: {:08x} LR: 0x{:08x}", MEMPTR<OSThread_t>(coreinit::OSGetCurrentThread()).GetMPTR(), hCPU->spr.LR, cemuLog_advancedPPCLoggingEnabled() ? " Stack Trace:" : "");
cemuLog_log(LogType::Force, "[Debugger] {} was executed! {}", logName, logContext); cemuLog_log(LogType::Force, "[Debugger] {} was executed! {}", logName, logContext);
if (cemuLog_advancedPPCLoggingEnabled()) if (cemuLog_advancedPPCLoggingEnabled())

View file

@ -100,8 +100,8 @@ extern debuggerState_t debuggerState;
// new API // new API
DebuggerBreakpoint* debugger_getFirstBP(uint32 address); DebuggerBreakpoint* debugger_getFirstBP(uint32 address);
void debugger_createCodeBreakpoint(uint32 address, uint8 bpType); void debugger_createCodeBreakpoint(uint32 address, uint8 bpType);
void debugger_createExecuteBreakpoint(uint32 address);
void debugger_toggleExecuteBreakpoint(uint32 address); // create/remove execute breakpoint 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_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);

View file

@ -202,14 +202,14 @@ void BreakpointWindow::OnLeftDClick(wxMouseEvent& event)
auto it = debuggerState.breakpoints.begin(); auto it = debuggerState.breakpoints.begin();
std::advance(it, index); 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); const wxString dialogTitle = (*it)->bpType == DEBUGGER_BP_T_LOGGING ? _("Enter a new logging message") : _("Enter a new comment");
if (set_value_dialog.ShowModal() == wxID_OK) 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(); (*it)->comment = set_comment_dialog.GetValue().ToStdWstring();
m_breakpoints->SetItem(index, ColumnComment, set_value_dialog.GetValue()); m_breakpoints->SetItem(index, ColumnComment, set_comment_dialog.GetValue());
} }
return;
} }
} }

View file

@ -686,6 +686,7 @@ void DisasmCtrl::OnContextMenu(const wxPoint& position, uint32 line)
// show dialog // show dialog
wxMenu menu; wxMenu menu;
menu.Append(IDContextMenu_ToggleBreakpoint, _("Toggle breakpoint")); menu.Append(IDContextMenu_ToggleBreakpoint, _("Toggle breakpoint"));
menu.Append(IDContextMenu_ToggleLoggingBreakpoint, _("Toggle logging point"));
if(debugger_hasPatch(virtualAddress)) if(debugger_hasPatch(virtualAddress))
menu.Append(IDContextMenu_RestoreOriginalInstructions, _("Restore original instructions")); menu.Append(IDContextMenu_RestoreOriginalInstructions, _("Restore original instructions"));
menu.AppendSeparator(); menu.AppendSeparator();
@ -707,6 +708,13 @@ void DisasmCtrl::OnContextMenuEntryClicked(wxCommandEvent& event)
wxPostEvent(this->m_parent, evt); wxPostEvent(this->m_parent, evt);
break; break;
} }
case IDContextMenu_ToggleLoggingBreakpoint:
{
debugger_toggleLoggingBreakpoint(m_contextMenuAddress);
wxCommandEvent evt(wxEVT_BREAKPOINT_CHANGE);
wxPostEvent(this->m_parent, evt);
break;
}
case IDContextMenu_RestoreOriginalInstructions: case IDContextMenu_RestoreOriginalInstructions:
{ {
debugger_removePatch(m_contextMenuAddress); debugger_removePatch(m_contextMenuAddress);

View file

@ -8,6 +8,7 @@ class DisasmCtrl : public TextList
enum enum
{ {
IDContextMenu_ToggleBreakpoint = wxID_HIGHEST + 1, IDContextMenu_ToggleBreakpoint = wxID_HIGHEST + 1,
IDContextMenu_ToggleLoggingBreakpoint,
IDContextMenu_RestoreOriginalInstructions, IDContextMenu_RestoreOriginalInstructions,
IDContextMenu_CopyAddress, IDContextMenu_CopyAddress,
IDContextMenu_CopyUnrelocatedAddress, IDContextMenu_CopyUnrelocatedAddress,