From 7b57b8f2cac6d00136b457e554ff33b069d1bf6a Mon Sep 17 00:00:00 2001 From: Eladash Date: Wed, 31 Mar 2021 20:08:01 +0300 Subject: [PATCH] debugger: Implement ability to pause entire emulation on breakpoint --- rpcs3/Emu/Cell/PPUThread.cpp | 16 ++++++++++- rpcs3/rpcs3qt/debugger_frame.cpp | 46 +++++++++++++++++++++++++++++--- rpcs3/rpcs3qt/debugger_frame.h | 1 + 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 3ec247d558..2fb94610da 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -455,11 +455,25 @@ extern void ppu_register_function_at(u32 addr, u32 size, ppu_function_t ptr) } } +atomic_t g_debugger_pause_all_threads_on_bp = true; + // Breakpoint entry point static bool ppu_break(ppu_thread& ppu, ppu_opcode_t) { + const bool pause_all = g_debugger_pause_all_threads_on_bp; + // Pause - ppu.state.atomic_op([](bs_t& state) { if (!(state & cpu_flag::dbg_step)) state += cpu_flag::dbg_pause; }); + ppu.state.atomic_op([&](bs_t& state) + { + if (pause_all) state += cpu_flag::dbg_global_pause; + if (pause_all || !(state & cpu_flag::dbg_step)) state += cpu_flag::dbg_pause; + }); + + if (pause_all) + { + // Pause all other threads + Emu.CallAfter([]() { Emu.Pause(); }); + } if (ppu.check_state()) { diff --git a/rpcs3/rpcs3qt/debugger_frame.cpp b/rpcs3/rpcs3qt/debugger_frame.cpp index 6be40d4c48..a52853044e 100644 --- a/rpcs3/rpcs3qt/debugger_frame.cpp +++ b/rpcs3/rpcs3qt/debugger_frame.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include "util/asm.hpp" @@ -37,6 +38,8 @@ constexpr auto qstr = QString::fromStdString; constexpr auto s_pause_flags = cpu_flag::dbg_pause + cpu_flag::dbg_global_pause; +extern atomic_t g_debugger_pause_all_threads_on_bp; + debugger_frame::debugger_frame(std::shared_ptr settings, QWidget *parent) : custom_dock_widget(tr("Debugger"), parent), xgui_settings(settings) { @@ -146,7 +149,7 @@ debugger_frame::debugger_frame(std::shared_ptr settings, QWidget * { // If paused, unpause. // If not paused, add dbg_pause. - const auto old = cpu->state.atomic_op([](bs_t& state) + const auto old = cpu->state.fetch_op([](bs_t& state) { if (state & s_pause_flags) { @@ -156,13 +159,17 @@ debugger_frame::debugger_frame(std::shared_ptr settings, QWidget * { state += cpu_flag::dbg_pause; } - - return state; }); // Notify only if no pause flags are set after this change - if (!(old & s_pause_flags)) + if (old & s_pause_flags) { + if (g_debugger_pause_all_threads_on_bp && Emu.IsPaused() && (old & s_pause_flags) == s_pause_flags) + { + // Resume all threads were paused by this breakpoint + Emu.Resume(); + } + cpu->state.notify_one(s_pause_flags); } } @@ -240,6 +247,31 @@ void debugger_frame::hideEvent(QHideEvent * event) QDockWidget::hideEvent(event); } +void debugger_frame::open_breakpoints_settings() +{ + QDialog* dlg = new QDialog(this); + dlg->setWindowTitle(tr("Breakpoint Settings")); + dlg->setModal(true); + + QCheckBox* check_box = new QCheckBox(tr("Pause All Threads On Hit"), dlg); + check_box->setCheckable(true); + check_box->setChecked(g_debugger_pause_all_threads_on_bp ? Qt::Checked : Qt::Unchecked); + check_box->setToolTip(tr("When set: a breakpoint hit will pause the emulation instead of the current thread." + "\nApplies on all breakpoints in all threads regardless if set before or after changing this setting.")); + + connect(check_box, &QCheckBox::clicked, dlg, [](bool checked) { g_debugger_pause_all_threads_on_bp = checked; }); + + QPushButton* button_ok = new QPushButton(tr("OK"), dlg); + connect(button_ok, &QAbstractButton::clicked, dlg, &QDialog::accept); + + QHBoxLayout* hbox_layout = new QHBoxLayout(dlg); + hbox_layout->addWidget(check_box); + hbox_layout->addWidget(button_ok); + dlg->setLayout(hbox_layout); + dlg->setAttribute(Qt::WA_DeleteOnClose); + dlg->exec(); +} + void debugger_frame::keyPressEvent(QKeyEvent* event) { if (!isActiveWindow()) @@ -260,6 +292,7 @@ void debugger_frame::keyPressEvent(QKeyEvent* event) QLabel* l = new QLabel(tr( "Keys Ctrl+G: Go to typed address." + "\nKeys Ctrl+B: Open breakpoints settings." "\nKeys Alt+S: Capture SPU images of selected SPU." "\nKey D: SPU MFC commands logger, MFC debug setting must be enabled." "\nKey E: Instruction Editor: click on the instruction you want to modify, then press E." @@ -308,6 +341,11 @@ void debugger_frame::keyPressEvent(QKeyEvent* event) ShowGotoAddressDialog(); return; } + case Qt::Key_B: + { + open_breakpoints_settings(); + return; + } } } else diff --git a/rpcs3/rpcs3qt/debugger_frame.h b/rpcs3/rpcs3qt/debugger_frame.h index c0e8fecca6..7fe9fcdb5b 100644 --- a/rpcs3/rpcs3qt/debugger_frame.h +++ b/rpcs3/rpcs3qt/debugger_frame.h @@ -73,6 +73,7 @@ class debugger_frame : public custom_dock_widget cpu_thread* get_cpu(); std::function make_check_cpu(cpu_thread* cpu); + void open_breakpoints_settings(); public: explicit debugger_frame(std::shared_ptr settings, QWidget *parent = 0);