From 6aaad1eb83819bcfb9109da2e5e09b1f8d776722 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Sun, 15 Dec 2024 21:47:05 +0100 Subject: [PATCH] Debugger: Added right click context menu to disasm view + small fixes --- src/Cafe/HW/Espresso/Debugger/Debugger.cpp | 28 +++++++ src/Cafe/HW/Espresso/Debugger/Debugger.h | 1 + src/gui/debugger/DebuggerWindow2.cpp | 11 ++- src/gui/debugger/DebuggerWindow2.h | 2 + src/gui/debugger/DisasmCtrl.cpp | 86 +++++++++++++++++----- src/gui/debugger/DisasmCtrl.h | 13 ++++ 6 files changed, 121 insertions(+), 20 deletions(-) diff --git a/src/Cafe/HW/Espresso/Debugger/Debugger.cpp b/src/Cafe/HW/Espresso/Debugger/Debugger.cpp index e7369af6..1fed07cd 100644 --- a/src/Cafe/HW/Espresso/Debugger/Debugger.cpp +++ b/src/Cafe/HW/Espresso/Debugger/Debugger.cpp @@ -447,6 +447,34 @@ bool debugger_hasPatch(uint32 address) return false; } +void debugger_removePatch(uint32 address) +{ + for (sint32 i = 0; i < debuggerState.patches.size(); i++) + { + auto& patch = debuggerState.patches[i]; + if (address < patch->address || address >= (patch->address + patch->length)) + continue; + MPTR startAddress = patch->address; + MPTR endAddress = patch->address + patch->length; + // remove any breakpoints overlapping with the patch + for (auto& bp : debuggerState.breakpoints) + { + if (bp->address + 4 > startAddress && bp->address < endAddress) + { + bp->enabled = false; + debugger_updateExecutionBreakpoint(bp->address); + } + } + // restore original data + memcpy(MEMPTR(startAddress).GetPtr(), patch->origData.data(), patch->length); + PPCRecompiler_invalidateRange(startAddress, endAddress); + // remove patch + delete patch; + debuggerState.patches.erase(debuggerState.patches.begin() + i); + return; + } +} + void debugger_stepInto(PPCInterpreter_t* hCPU, bool updateDebuggerWindow = true) { bool isRecEnabled = ppcRecompilerEnabled; diff --git a/src/Cafe/HW/Espresso/Debugger/Debugger.h b/src/Cafe/HW/Espresso/Debugger/Debugger.h index 717df28a..249c47b8 100644 --- a/src/Cafe/HW/Espresso/Debugger/Debugger.h +++ b/src/Cafe/HW/Espresso/Debugger/Debugger.h @@ -114,6 +114,7 @@ void debugger_updateExecutionBreakpoint(uint32 address, bool forceRestore = fals void debugger_createPatch(uint32 address, std::span patchData); bool debugger_hasPatch(uint32 address); +void debugger_removePatch(uint32 address); void debugger_forceBreak(); // force breakpoint at the next possible instruction bool debugger_isTrapped(); diff --git a/src/gui/debugger/DebuggerWindow2.cpp b/src/gui/debugger/DebuggerWindow2.cpp index 969e40bd..9f25cf96 100644 --- a/src/gui/debugger/DebuggerWindow2.cpp +++ b/src/gui/debugger/DebuggerWindow2.cpp @@ -64,6 +64,7 @@ wxBEGIN_EVENT_TABLE(DebuggerWindow2, wxFrame) EVT_COMMAND(wxID_ANY, wxEVT_RUN, DebuggerWindow2::OnRunProgram) EVT_COMMAND(wxID_ANY, wxEVT_NOTIFY_MODULE_LOADED, DebuggerWindow2::OnNotifyModuleLoaded) EVT_COMMAND(wxID_ANY, wxEVT_NOTIFY_MODULE_UNLOADED, DebuggerWindow2::OnNotifyModuleUnloaded) + EVT_COMMAND(wxID_ANY, wxEVT_DISASMCTRL_NOTIFY_GOTO_ADDRESS, DebuggerWindow2::OnDisasmCtrlGotoAddress) // file menu EVT_MENU(MENU_ID_FILE_EXIT, DebuggerWindow2::OnExit) // window @@ -383,6 +384,12 @@ void DebuggerWindow2::OnMoveIP(wxCommandEvent& event) m_disasm_ctrl->CenterOffset(ip); } +void DebuggerWindow2::OnDisasmCtrlGotoAddress(wxCommandEvent& event) +{ + uint32 address = static_cast(event.GetExtraLong()); + UpdateModuleLabel(address); +} + void DebuggerWindow2::OnParentMove(const wxPoint& main_position, const wxSize& main_size) { m_main_position = main_position; @@ -416,7 +423,7 @@ void DebuggerWindow2::OnNotifyModuleLoaded(wxCommandEvent& event) void DebuggerWindow2::OnNotifyModuleUnloaded(wxCommandEvent& event) { - RPLModule* module = (RPLModule*)event.GetClientData(); + RPLModule* module = (RPLModule*)event.GetClientData(); // todo - the RPL module is already unloaded at this point. Find a better way to handle this SaveModuleStorage(module, true); m_module_window->OnGameLoaded(); m_symbol_window->OnGameLoaded(); @@ -659,7 +666,7 @@ void DebuggerWindow2::CreateMenuBar() void DebuggerWindow2::UpdateModuleLabel(uint32 address) { - if(address == 0) + if (address == 0) address = m_disasm_ctrl->GetViewBaseAddress(); RPLModule* module = RPLLoader_FindModuleByCodeAddr(address); diff --git a/src/gui/debugger/DebuggerWindow2.h b/src/gui/debugger/DebuggerWindow2.h index 0ca44c44..145b5e1d 100644 --- a/src/gui/debugger/DebuggerWindow2.h +++ b/src/gui/debugger/DebuggerWindow2.h @@ -86,6 +86,8 @@ private: void OnMoveIP(wxCommandEvent& event); void OnNotifyModuleLoaded(wxCommandEvent& event); void OnNotifyModuleUnloaded(wxCommandEvent& event); + // events from DisasmCtrl + void OnDisasmCtrlGotoAddress(wxCommandEvent& event); void CreateMenuBar(); void UpdateModuleLabel(uint32 address = 0); diff --git a/src/gui/debugger/DisasmCtrl.cpp b/src/gui/debugger/DisasmCtrl.cpp index c2cd5722..2f38d55e 100644 --- a/src/gui/debugger/DisasmCtrl.cpp +++ b/src/gui/debugger/DisasmCtrl.cpp @@ -15,6 +15,8 @@ #include "Cafe/HW/Espresso/Debugger/DebugSymbolStorage.h" #include // for wxMemoryInputStream +wxDEFINE_EVENT(wxEVT_DISASMCTRL_NOTIFY_GOTO_ADDRESS, wxCommandEvent); + #define MAX_SYMBOL_LEN (120) #define COLOR_DEBUG_ACTIVE_BP 0xFFFFA0FF @@ -74,6 +76,8 @@ DisasmCtrl::DisasmCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& po auto tooltip_sizer = new wxBoxSizer(wxVERTICAL); tooltip_sizer->Add(new wxStaticText(m_tooltip_window, wxID_ANY, wxEmptyString), 0, wxALL, 5); m_tooltip_window->SetSizer(tooltip_sizer); + + Bind(wxEVT_MENU, &DisasmCtrl::OnContextMenuEntryClicked, this, IDContextMenu_ToggleBreakpoint, IDContextMenu_Last); } void DisasmCtrl::Init() @@ -662,29 +666,67 @@ void DisasmCtrl::CopyToClipboard(std::string text) { #endif } +static uint32 GetUnrelocatedAddress(MPTR address) +{ + RPLModule* rplModule = RPLLoader_FindModuleByCodeAddr(address); + if (!rplModule) + return 0; + if (address >= rplModule->regionMappingBase_text.GetMPTR() && address < (rplModule->regionMappingBase_text.GetMPTR() + rplModule->regionSize_text)) + return 0x02000000 + (address - rplModule->regionMappingBase_text.GetMPTR()); + return 0; +} + void DisasmCtrl::OnContextMenu(const wxPoint& position, uint32 line) { - wxPoint pos = position; auto optVirtualAddress = LinePixelPosToAddress(position.y - GetViewStart().y * m_line_height); if (!optVirtualAddress) return; MPTR virtualAddress = *optVirtualAddress; + m_contextMenuAddress = virtualAddress; + // show dialog + wxMenu menu; + menu.Append(IDContextMenu_ToggleBreakpoint, _("Toggle breakpoint")); + if(debugger_hasPatch(virtualAddress)) + menu.Append(IDContextMenu_RestoreOriginalInstructions, _("Restore original instructions")); + menu.AppendSeparator(); + menu.Append(IDContextMenu_CopyAddress, _("Copy address")); + uint32 unrelocatedAddress = GetUnrelocatedAddress(virtualAddress); + if (unrelocatedAddress && unrelocatedAddress != virtualAddress) + menu.Append(IDContextMenu_CopyUnrelocatedAddress, _("Copy virtual address (for IDA/Ghidra)")); + PopupMenu(&menu); +} - // address - if (pos.x <= OFFSET_ADDRESS + OFFSET_ADDRESS_RELATIVE) +void DisasmCtrl::OnContextMenuEntryClicked(wxCommandEvent& event) +{ + switch(event.GetId()) { - CopyToClipboard(fmt::format("{:#10x}", virtualAddress)); - return; - } - else if (pos.x <= OFFSET_ADDRESS + OFFSET_ADDRESS_RELATIVE + OFFSET_DISASSEMBLY) - { - // double-clicked on disassembly (operation and operand data) - return; - } - else - { - // comment - return; + case IDContextMenu_ToggleBreakpoint: + { + debugger_toggleExecuteBreakpoint(m_contextMenuAddress); + wxCommandEvent evt(wxEVT_BREAKPOINT_CHANGE); + wxPostEvent(this->m_parent, evt); + break; + } + case IDContextMenu_RestoreOriginalInstructions: + { + debugger_removePatch(m_contextMenuAddress); + wxCommandEvent evt(wxEVT_BREAKPOINT_CHANGE); // This also refreshes the disassembly view + wxPostEvent(this->m_parent, evt); + break; + } + case IDContextMenu_CopyAddress: + { + CopyToClipboard(fmt::format("{:#10x}", m_contextMenuAddress)); + break; + } + case IDContextMenu_CopyUnrelocatedAddress: + { + uint32 unrelocatedAddress = GetUnrelocatedAddress(m_contextMenuAddress); + CopyToClipboard(fmt::format("{:#10x}", unrelocatedAddress)); + break; + } + default: + UNREACHABLE; } } @@ -722,7 +764,6 @@ std::optional DisasmCtrl::LinePixelPosToAddress(sint32 posY) if (posY < 0) return std::nullopt; - sint32 lineIndex = posY / m_line_height; if (lineIndex >= m_lineToAddress.size()) return std::nullopt; @@ -751,8 +792,6 @@ void DisasmCtrl::CenterOffset(uint32 offset) m_active_line = line; RefreshLine(m_active_line); - - debug_printf("scroll to %x\n", debuggerState.debugSession.instructionPointer); } void DisasmCtrl::GoToAddressDialog() @@ -765,6 +804,10 @@ void DisasmCtrl::GoToAddressDialog() auto value = goto_dialog.GetValue().ToStdString(); std::transform(value.begin(), value.end(), value.begin(), tolower); + // trim any leading spaces + while(!value.empty() && value[0] == ' ') + value.erase(value.begin()); + debugger_addParserSymbols(parser); // try to parse expression as hex value first (it should interpret 1234 as 0x1234, not 1234) @@ -773,17 +816,24 @@ void DisasmCtrl::GoToAddressDialog() const auto result = (uint32)parser.Evaluate("0x"+value); m_lastGotoTarget = result; CenterOffset(result); + wxCommandEvent evt(wxEVT_DISASMCTRL_NOTIFY_GOTO_ADDRESS); + evt.SetExtraLong(static_cast(result)); + wxPostEvent(GetParent(), evt); } else if (parser.IsConstantExpression(value)) { const auto result = (uint32)parser.Evaluate(value); m_lastGotoTarget = result; CenterOffset(result); + wxCommandEvent evt(wxEVT_DISASMCTRL_NOTIFY_GOTO_ADDRESS); + evt.SetExtraLong(static_cast(result)); + wxPostEvent(GetParent(), evt); } else { try { + // if not a constant expression (i.e. relying on unknown variables), then evaluating will throw an exception with a detailed error message const auto _ = (uint32)parser.Evaluate(value); } catch (const std::exception& ex) diff --git a/src/gui/debugger/DisasmCtrl.h b/src/gui/debugger/DisasmCtrl.h index 993d5697..5a67e49a 100644 --- a/src/gui/debugger/DisasmCtrl.h +++ b/src/gui/debugger/DisasmCtrl.h @@ -1,9 +1,20 @@ #pragma once #include "gui/components/TextList.h" +wxDECLARE_EVENT(wxEVT_DISASMCTRL_NOTIFY_GOTO_ADDRESS, wxCommandEvent); // Notify parent that goto address operation completed. Event contains the address that was jumped to. + class DisasmCtrl : public TextList { + enum + { + IDContextMenu_ToggleBreakpoint = wxID_HIGHEST + 1, + IDContextMenu_RestoreOriginalInstructions, + IDContextMenu_CopyAddress, + IDContextMenu_CopyUnrelocatedAddress, + IDContextMenu_Last + }; public: + DisasmCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size, long style); void Init(); @@ -26,6 +37,7 @@ protected: void OnKeyPressed(sint32 key_code, const wxPoint& position) override; void OnMouseDClick(const wxPoint& position, uint32 line) override; void OnContextMenu(const wxPoint& position, uint32 line) override; + void OnContextMenuEntryClicked(wxCommandEvent& event); bool OnShowTooltip(const wxPoint& position, uint32 line) override; void ScrollWindow(int dx, int dy, const wxRect* prect) override; @@ -40,6 +52,7 @@ private: sint32 m_mouse_line, m_mouse_line_drawn; sint32 m_active_line; uint32 m_lastGotoTarget{}; + uint32 m_contextMenuAddress{}; // code region info uint32 currentCodeRegionStart; uint32 currentCodeRegionEnd;