From 54285978cf5aa535fbf800d136d4e0d691252af3 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Wed, 7 Jan 2026 13:57:28 +1000 Subject: [PATCH] CPU: Add Host::ReportDebuggerEvent() And replace the old ReportDebuggerMessage(). Fixes breakpoint hit counts not updating in UI. --- src/core/cpu_core.cpp | 31 +++++++++------ src/core/cpu_core.h | 14 +++++++ src/core/host.h | 3 -- src/duckstation-mini/mini_host.cpp | 5 ++- src/duckstation-qt/debuggerwindow.cpp | 50 +++++++++++++++++++++++- src/duckstation-qt/debuggerwindow.h | 4 +- src/duckstation-qt/qthost.cpp | 6 --- src/duckstation-qt/qthost.h | 1 - src/duckstation-regtest/regtest_host.cpp | 5 ++- 9 files changed, 89 insertions(+), 30 deletions(-) diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index 82d77b979..c78acbf33 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -2276,7 +2276,7 @@ bool CPU::AddBreakpoint(BreakpointType type, VirtualMemoryAddress address, bool System::InterruptExecution(); if (!auto_clear) - Host::ReportDebuggerMessage(fmt::format("Added breakpoint at 0x{:08X}.", address)); + Host::ReportDebuggerEvent(DebuggerEvent::Message, fmt::format("Added breakpoint at 0x{:08X}.", address)); return true; } @@ -2303,8 +2303,9 @@ bool CPU::SetBreakpointEnabled(BreakpointType type, VirtualMemoryAddress address if (it == bplist.end()) return false; - Host::ReportDebuggerMessage(fmt::format("{} {} breakpoint at 0x{:08X}.", enabled ? "Enabled" : "Disabled", - GetBreakpointTypeName(type), address)); + Host::ReportDebuggerEvent(DebuggerEvent::Message, + fmt::format("{} {} breakpoint at 0x{:08X}.", enabled ? "Enabled" : "Disabled", + GetBreakpointTypeName(type), address)); it->enabled = enabled; if (UpdateDebugDispatcherFlag()) @@ -2324,7 +2325,8 @@ bool CPU::RemoveBreakpoint(BreakpointType type, VirtualMemoryAddress address) if (it == bplist.end()) return false; - Host::ReportDebuggerMessage(fmt::format("Removed {} breakpoint at 0x{:08X}.", GetBreakpointTypeName(type), address)); + Host::ReportDebuggerEvent(DebuggerEvent::Message, + fmt::format("Removed {} breakpoint at 0x{:08X}.", GetBreakpointTypeName(type), address)); bplist.erase(it); if (UpdateDebugDispatcherFlag()) @@ -2358,7 +2360,7 @@ bool CPU::AddStepOverBreakpoint() if (!IsCallInstruction(inst)) { - Host::ReportDebuggerMessage(fmt::format("0x{:08X} is not a call instruction.", g_state.pc)); + Host::ReportDebuggerEvent(DebuggerEvent::Message, fmt::format("0x{:08X} is not a call instruction.", g_state.pc)); return false; } @@ -2367,14 +2369,15 @@ bool CPU::AddStepOverBreakpoint() if (IsBranchInstruction(inst)) { - Host::ReportDebuggerMessage(fmt::format("Can't step over double branch at 0x{:08X}", g_state.pc)); + Host::ReportDebuggerEvent(DebuggerEvent::Message, + fmt::format("Can't step over double branch at 0x{:08X}", g_state.pc)); return false; } // skip the delay slot bp_pc += sizeof(Instruction); - Host::ReportDebuggerMessage(fmt::format("Stepping over to 0x{:08X}.", bp_pc)); + Host::ReportDebuggerEvent(DebuggerEvent::Message, fmt::format("Stepping over to 0x{:08X}.", bp_pc)); return AddBreakpoint(BreakpointType::Execute, bp_pc, true); } @@ -2390,20 +2393,22 @@ bool CPU::AddStepOutBreakpoint(u32 max_instructions_to_search) Instruction inst; if (!SafeReadInstruction(ret_pc, &inst.bits)) { - Host::ReportDebuggerMessage( + Host::ReportDebuggerEvent( + DebuggerEvent::Message, fmt::format("Instruction read failed at {:08X} while searching for function end.", ret_pc)); return false; } if (IsReturnInstruction(inst)) { - Host::ReportDebuggerMessage(fmt::format("Stepping out to 0x{:08X}.", ret_pc)); + Host::ReportDebuggerEvent(DebuggerEvent::Message, fmt::format("Stepping out to 0x{:08X}.", ret_pc)); return AddBreakpoint(BreakpointType::Execute, ret_pc, true); } } - Host::ReportDebuggerMessage(fmt::format("No return instruction found after {} instructions for step-out at {:08X}.", - max_instructions_to_search, g_state.pc)); + Host::ReportDebuggerEvent(DebuggerEvent::Message, + fmt::format("No return instruction found after {} instructions for step-out at {:08X}.", + max_instructions_to_search, g_state.pc)); return false; } @@ -2450,7 +2455,7 @@ ALWAYS_INLINE_RELEASE bool CPU::CheckBreakpointList(BreakpointType type, Virtual if (bp.auto_clear) { msg.format("Stopped execution at 0x{:08X}.", pc); - Host::ReportDebuggerMessage(msg); + Host::ReportDebuggerEvent(DebuggerEvent::Message, msg); bplist.erase(bplist.begin() + i); count--; UpdateDebugDispatcherFlag(); @@ -2459,7 +2464,7 @@ ALWAYS_INLINE_RELEASE bool CPU::CheckBreakpointList(BreakpointType type, Virtual { msg.format("Hit {} breakpoint {} at 0x{:08X}, Hit Count {}.", GetBreakpointTypeName(type), bp.number, address, bp.hit_count); - Host::ReportDebuggerMessage(msg); + Host::ReportDebuggerEvent(DebuggerEvent::BreakpointHit, msg); i++; } diff --git a/src/core/cpu_core.h b/src/core/cpu_core.h index d56d7ca44..de8e0880b 100644 --- a/src/core/cpu_core.h +++ b/src/core/cpu_core.h @@ -256,4 +256,18 @@ struct DebuggerRegisterListEntry inline constexpr u32 NUM_DEBUGGER_REGISTER_LIST_ENTRIES = 103; extern const std::array g_debugger_register_list; +// Debugger events, calls Host::ReportDebuggerEvent() +enum class DebuggerEvent : u8 +{ + Message, + BreakpointHit, +}; + } // namespace CPU + +namespace Host { + +/// Debugger feedback. +void ReportDebuggerEvent(CPU::DebuggerEvent event, std::string_view message); + +} // namespace Host diff --git a/src/core/host.h b/src/core/host.h index 8ac34511c..3c7f56f8a 100644 --- a/src/core/host.h +++ b/src/core/host.h @@ -37,9 +37,6 @@ std::optional GetResourceFileTimestamp(std::string_view filename, b /// Displays an asynchronous error on the UI thread, i.e. doesn't block the caller. void ReportErrorAsync(std::string_view title, std::string_view message); -/// Debugger feedback. -void ReportDebuggerMessage(std::string_view message); - /// Displays an asynchronous confirmation on the UI thread, but does not block the caller. /// The callback may be executed on a different thread. Use RunOnCoreThread() in the callback to ensure safety. using ConfirmMessageAsyncCallback = std::function; diff --git a/src/duckstation-mini/mini_host.cpp b/src/duckstation-mini/mini_host.cpp index ac6658321..ea88d3e7c 100644 --- a/src/duckstation-mini/mini_host.cpp +++ b/src/duckstation-mini/mini_host.cpp @@ -7,6 +7,7 @@ #include "core/bus.h" #include "core/controller.h" #include "core/core_private.h" +#include "core/cpu_core.h" #include "core/fullscreenui.h" #include "core/fullscreenui_widgets.h" #include "core/game_list.h" @@ -331,9 +332,9 @@ void MiniHost::SetDefaultSettings(SettingsInterface& si, bool system, bool contr } } -void Host::ReportDebuggerMessage(std::string_view message) +void Host::ReportDebuggerEvent(CPU::DebuggerEvent event, std::string_view message) { - ERROR_LOG("ReportDebuggerMessage(): {}", message); + ERROR_LOG("ReportDebuggerEvent(): {}", message); } std::span> Host::GetAvailableLanguageList() diff --git a/src/duckstation-qt/debuggerwindow.cpp b/src/duckstation-qt/debuggerwindow.cpp index 3d39f3678..187072ae3 100644 --- a/src/duckstation-qt/debuggerwindow.cpp +++ b/src/duckstation-qt/debuggerwindow.cpp @@ -12,6 +12,7 @@ #include "core/cpu_core_private.h" #include "common/assert.h" +#include "common/log.h" #include #include @@ -21,6 +22,8 @@ #include "moc_debuggerwindow.cpp" +LOG_CHANNEL(Host); + static constexpr int TIMER_REFRESH_INTERVAL_MS = 100; DebuggerWindow::DebuggerWindow(QWidget* parent /* = nullptr */) @@ -61,7 +64,7 @@ void DebuggerWindow::onSystemResumed() m_ui.actionPause->setChecked(false); } -void DebuggerWindow::onDebuggerMessageReported(const QString& message) +void DebuggerWindow::reportMessage(const QString& message) { m_ui.statusbar->showMessage(message, 0); } @@ -457,7 +460,6 @@ void DebuggerWindow::connectSignals() connect(g_core_thread, &CoreThread::systemResumed, this, &DebuggerWindow::onSystemResumed); connect(g_core_thread, &CoreThread::systemStarted, this, &DebuggerWindow::onSystemStarted); connect(g_core_thread, &CoreThread::systemDestroyed, this, &DebuggerWindow::onSystemDestroyed); - connect(g_core_thread, &CoreThread::debuggerMessageReported, this, &DebuggerWindow::onDebuggerMessageReported); connect(m_ui.actionPause, &QAction::triggered, this, &DebuggerWindow::onPauseActionTriggered); connect(m_ui.actionRunToCursor, &QAction::triggered, this, &DebuggerWindow::onRunToCursorTriggered); @@ -726,3 +728,47 @@ void DebuggerWindow::removeBreakpoint(CPU::BreakpointType type, u32 address) }); }); } + +void DebuggerWindow::updateBreakpointHitCounts(const CPU::BreakpointList& bps) +{ + for (size_t i = 0; i < bps.size(); i++) + { + const CPU::Breakpoint& bp = bps[i]; + QTreeWidgetItem* const item = m_ui.breakpointsWidget->topLevelItem(static_cast(i)); + if (!item) + continue; + + item->setText(3, QString::asprintf("%u", bp.hit_count)); + } +} + +void Host::ReportDebuggerEvent(CPU::DebuggerEvent event, std::string_view message) +{ + if (event == CPU::DebuggerEvent::Message) + { + if (!message.empty()) + return; + + INFO_LOG("Debugger message: {}", message); + Host::RunOnUIThread([message = QtUtils::StringViewToQString(message)]() { + DebuggerWindow* const win = g_main_window->getDebuggerWindow(); + if (!win) + return; + + win->reportMessage(message); + }); + } + else if (event == CPU::DebuggerEvent::BreakpointHit) + { + Host::RunOnUIThread([bps = CPU::CopyBreakpointList(), message = QtUtils::StringViewToQString(message)]() { + DebuggerWindow* const win = g_main_window->getDebuggerWindow(); + if (!win) + return; + + win->updateBreakpointHitCounts(bps); + + if (!message.isEmpty()) + win->reportMessage(message); + }); + } +} diff --git a/src/duckstation-qt/debuggerwindow.h b/src/duckstation-qt/debuggerwindow.h index 2e1a048ea..620f20be4 100644 --- a/src/duckstation-qt/debuggerwindow.h +++ b/src/duckstation-qt/debuggerwindow.h @@ -27,6 +27,9 @@ public: explicit DebuggerWindow(QWidget* parent = nullptr); ~DebuggerWindow(); + void reportMessage(const QString& message); + void updateBreakpointHitCounts(const CPU::BreakpointList& bps); + Q_SIGNALS: void closed(); @@ -55,7 +58,6 @@ private: void onSystemDestroyed(); void onSystemPaused(); void onSystemResumed(); - void onDebuggerMessageReported(const QString& message); void timerRefresh(); void refreshAll(); diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index a2b42e9f6..716708337 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -2552,12 +2552,6 @@ void QtHost::UpdateApplicationLocale(std::string_view language) } } -void Host::ReportDebuggerMessage(std::string_view message) -{ - INFO_LOG("Debugger message: {}", message); - emit g_core_thread->debuggerMessageReported(QString::fromUtf8(message)); -} - InputDeviceListModel::InputDeviceListModel(QObject* parent) : QAbstractListModel(parent) { } diff --git a/src/duckstation-qt/qthost.h b/src/duckstation-qt/qthost.h index 0a1860035..d52a24aa7 100644 --- a/src/duckstation-qt/qthost.h +++ b/src/duckstation-qt/qthost.h @@ -105,7 +105,6 @@ public: Q_SIGNALS: void errorReported(const QString& title, const QString& message); void statusMessage(const QString& message); - void debuggerMessageReported(const QString& message); void settingsResetToDefault(bool system, bool controller); void systemStarting(); void systemStarted(); diff --git a/src/duckstation-regtest/regtest_host.cpp b/src/duckstation-regtest/regtest_host.cpp index ee7bcd3fd..f912b8305 100644 --- a/src/duckstation-regtest/regtest_host.cpp +++ b/src/duckstation-regtest/regtest_host.cpp @@ -5,6 +5,7 @@ #include "core/bus.h" #include "core/controller.h" #include "core/core_private.h" +#include "core/cpu_core.h" #include "core/fullscreenui.h" #include "core/fullscreenui_widgets.h" #include "core/game_list.h" @@ -176,9 +177,9 @@ void Host::ConfirmMessageAsync(std::string_view title, std::string_view message, callback(true); } -void Host::ReportDebuggerMessage(std::string_view message) +void Host::ReportDebuggerEvent(CPU::DebuggerEvent event, std::string_view message) { - ERROR_LOG("ReportDebuggerMessage: {}", message); + ERROR_LOG("ReportDebuggerEvent: {}", message); } std::span> Host::GetAvailableLanguageList()