mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-20 19:45:20 +00:00
Debugger: Implement SPU breakpoints
This commit is contained in:
parent
ec6d6adebc
commit
ccb2724fc4
10 changed files with 136 additions and 32 deletions
|
@ -52,6 +52,7 @@ void fmt_class_string<cpu_flag>::format(std::string& out, u64 arg)
|
|||
case cpu_flag::signal: return "sig";
|
||||
case cpu_flag::memory: return "mem";
|
||||
case cpu_flag::pending: return "pend";
|
||||
case cpu_flag::pending_recheck: return "pend-re";
|
||||
case cpu_flag::dbg_global_pause: return "G-PAUSE";
|
||||
case cpu_flag::dbg_pause: return "PAUSE";
|
||||
case cpu_flag::dbg_step: return "STEP";
|
||||
|
@ -624,6 +625,11 @@ cpu_thread::cpu_thread(u32 id)
|
|||
}
|
||||
|
||||
g_threads_created++;
|
||||
|
||||
if (u32* pc2 = get_pc2())
|
||||
{
|
||||
*pc2 = umax;
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_thread::cpu_wait(bs_t<cpu_flag> old)
|
||||
|
@ -762,10 +768,18 @@ bool cpu_thread::check_state() noexcept
|
|||
cpu_counter::add(this);
|
||||
}
|
||||
|
||||
if ((state0 & (cpu_flag::pending + cpu_flag::temp)) == cpu_flag::pending)
|
||||
constexpr auto pending_and_temp = (cpu_flag::pending + cpu_flag::temp);
|
||||
|
||||
if ((state0 & pending_and_temp) == cpu_flag::pending)
|
||||
{
|
||||
// Execute pending work
|
||||
cpu_work();
|
||||
|
||||
if ((state1 ^ state) - pending_and_temp)
|
||||
{
|
||||
// Work could have changed flags
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (retval)
|
||||
|
|
|
@ -22,6 +22,7 @@ enum class cpu_flag : u32
|
|||
signal, // Thread received a signal (HLE)
|
||||
memory, // Thread must unlock memory mutex
|
||||
pending, // Thread has postponed work
|
||||
pending_recheck, // Thread needs to recheck if there is pending work before ::pending removal
|
||||
|
||||
dbg_global_pause, // Emulation paused
|
||||
dbg_pause, // Thread paused
|
||||
|
|
|
@ -1503,10 +1503,30 @@ void spu_thread::cpu_work()
|
|||
|
||||
const u32 old_iter_count = cpu_work_iteration_count++;
|
||||
|
||||
const auto timeout = +g_cfg.core.mfc_transfers_timeout;
|
||||
|
||||
bool work_left = false;
|
||||
|
||||
if (has_active_local_bps)
|
||||
{
|
||||
if (local_breakpoints[pc / 4])
|
||||
{
|
||||
// Ignore repeatations until a different instruction is issued
|
||||
if (pc != current_bp_pc)
|
||||
{
|
||||
// Breakpoint hit
|
||||
state += cpu_flag::dbg_pause;
|
||||
}
|
||||
}
|
||||
|
||||
current_bp_pc = pc;
|
||||
work_left = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
current_bp_pc = umax;
|
||||
}
|
||||
|
||||
const auto timeout = +g_cfg.core.mfc_transfers_timeout;
|
||||
|
||||
if (u32 shuffle_count = g_cfg.core.mfc_transfers_shuffling)
|
||||
{
|
||||
// If either MFC size exceeds limit or timeout has been reached execute pending MFC commands
|
||||
|
@ -1544,7 +1564,19 @@ void spu_thread::cpu_work()
|
|||
|
||||
if (!work_left)
|
||||
{
|
||||
state -= cpu_flag::pending;
|
||||
// No more pending work
|
||||
state.atomic_op([](bs_t<cpu_flag>& flags)
|
||||
{
|
||||
if (flags & cpu_flag::pending_recheck)
|
||||
{
|
||||
// Do not really remove ::pending because external thread may have pushed more pending work
|
||||
flags -= cpu_flag::pending_recheck;
|
||||
}
|
||||
else
|
||||
{
|
||||
flags -= cpu_flag::pending;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (gen_interrupt)
|
||||
|
|
|
@ -843,6 +843,11 @@ public:
|
|||
|
||||
atomic_t<u8> debugger_float_mode = 0;
|
||||
|
||||
// PC-based breakpoint list
|
||||
std::array<atomic_t<bool>, SPU_LS_SIZE / 4> local_breakpoints{};
|
||||
atomic_t<bool> has_active_local_bps = false;
|
||||
u32 current_bp_pc = umax;
|
||||
|
||||
void push_snr(u32 number, u32 value);
|
||||
static void do_dma_transfer(spu_thread* _this, const spu_mfc_cmd& args, u8* ls);
|
||||
bool do_dma_check(const spu_mfc_cmd& args);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "Emu/CPU/CPUDisAsm.h"
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
|
@ -11,7 +12,7 @@ constexpr auto qstr = QString::fromStdString;
|
|||
|
||||
extern bool is_using_interpreter(u32 id_type);
|
||||
|
||||
breakpoint_list::breakpoint_list(QWidget* parent, breakpoint_handler* handler) : QListWidget(parent), m_breakpoint_handler(handler)
|
||||
breakpoint_list::breakpoint_list(QWidget* parent, breakpoint_handler* handler) : QListWidget(parent), m_ppu_breakpoint_handler(handler)
|
||||
{
|
||||
setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
@ -42,14 +43,14 @@ void breakpoint_list::ClearBreakpoints()
|
|||
{
|
||||
auto* currentItem = takeItem(0);
|
||||
const u32 loc = currentItem->data(Qt::UserRole).value<u32>();
|
||||
m_breakpoint_handler->RemoveBreakpoint(loc);
|
||||
m_ppu_breakpoint_handler->RemoveBreakpoint(loc);
|
||||
delete currentItem;
|
||||
}
|
||||
}
|
||||
|
||||
void breakpoint_list::RemoveBreakpoint(u32 addr)
|
||||
{
|
||||
m_breakpoint_handler->RemoveBreakpoint(addr);
|
||||
m_ppu_breakpoint_handler->RemoveBreakpoint(addr);
|
||||
|
||||
for (int i = 0; i < count(); i++)
|
||||
{
|
||||
|
@ -67,7 +68,7 @@ void breakpoint_list::RemoveBreakpoint(u32 addr)
|
|||
|
||||
bool breakpoint_list::AddBreakpoint(u32 pc)
|
||||
{
|
||||
if (!m_breakpoint_handler->AddBreakpoint(pc))
|
||||
if (!m_ppu_breakpoint_handler->AddBreakpoint(pc))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -98,10 +99,55 @@ void breakpoint_list::HandleBreakpointRequest(u32 loc)
|
|||
return;
|
||||
}
|
||||
|
||||
if (m_cpu->id_type() != 1)
|
||||
if (!is_using_interpreter(m_cpu->id_type()))
|
||||
{
|
||||
// TODO: SPU breakpoints
|
||||
QMessageBox::warning(this, tr("Unimplemented Breakpoints For Thread Type!"), tr("Cannot set breakpoints on non-PPU thread currently, sorry."));
|
||||
QMessageBox::warning(this, tr("Interpreters-Only Feature!"), tr("Cannot set breakpoints on non-interpreter decoders."));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (m_cpu->id_type())
|
||||
{
|
||||
case 2:
|
||||
{
|
||||
if (loc >= SPU_LS_SIZE || loc % 4)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Invalid Memory For Breakpoints!"), tr("Cannot set breakpoints on non-SPU executable memory!"));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto spu = static_cast<spu_thread*>(m_cpu);
|
||||
auto& list = spu->local_breakpoints;
|
||||
|
||||
if (list[loc / 4].test_and_invert())
|
||||
{
|
||||
if (std::none_of(list.begin(), list.end(), [](auto& val){ return val.load(); }))
|
||||
{
|
||||
spu->has_active_local_bps = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!spu->has_active_local_bps.exchange(true))
|
||||
{
|
||||
spu->state.atomic_op([](bs_t<cpu_flag>& flags)
|
||||
{
|
||||
if (flags & cpu_flag::pending)
|
||||
{
|
||||
flags += cpu_flag::pending_recheck;
|
||||
}
|
||||
else
|
||||
{
|
||||
flags += cpu_flag::pending;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
case 1: break;
|
||||
default:
|
||||
QMessageBox::warning(this, tr("Unimplemented Breakpoints For Thread Type!"), tr("Cannot set breakpoints on a thread not an PPU/SPU currently, sorry."));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -111,13 +157,7 @@ void breakpoint_list::HandleBreakpointRequest(u32 loc)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!is_using_interpreter(m_cpu->id_type()))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Interpreters-Only Feature!"), tr("Cannot set breakpoints on non-interpreter decoders."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_breakpoint_handler->HasBreakpoint(loc))
|
||||
if (m_ppu_breakpoint_handler->HasBreakpoint(loc))
|
||||
{
|
||||
RemoveBreakpoint(loc);
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ private Q_SLOTS:
|
|||
void OnBreakpointListRightClicked(const QPoint &pos);
|
||||
void OnBreakpointListDelete();
|
||||
private:
|
||||
breakpoint_handler* m_breakpoint_handler;
|
||||
breakpoint_handler* m_ppu_breakpoint_handler;
|
||||
QMenu* m_context_menu = nullptr;
|
||||
QAction* m_delete_action;
|
||||
cpu_thread* m_cpu = nullptr;
|
||||
|
|
|
@ -80,10 +80,10 @@ debugger_frame::debugger_frame(std::shared_ptr<gui_settings> gui_settings, QWidg
|
|||
QHBoxLayout* hbox_b_main = new QHBoxLayout();
|
||||
hbox_b_main->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
m_breakpoint_handler = new breakpoint_handler();
|
||||
m_breakpoint_list = new breakpoint_list(this, m_breakpoint_handler);
|
||||
m_ppu_breakpoint_handler = new breakpoint_handler();
|
||||
m_breakpoint_list = new breakpoint_list(this, m_ppu_breakpoint_handler);
|
||||
|
||||
m_debugger_list = new debugger_list(this, m_gui_settings, m_breakpoint_handler);
|
||||
m_debugger_list = new debugger_list(this, m_gui_settings, m_ppu_breakpoint_handler);
|
||||
m_debugger_list->installEventFilter(this);
|
||||
|
||||
m_call_stack_list = new call_stack_list(this);
|
||||
|
@ -823,7 +823,7 @@ void debugger_frame::UpdateUnitList()
|
|||
{
|
||||
if (emu_state == system_state::stopped) return;
|
||||
|
||||
const QVariant var_cpu = QVariant::fromValue<std::pair<cpu_thread*, u32>>(std::make_pair(&cpu, id));
|
||||
const QVariant var_cpu = QVariant::fromValue<data_type>(std::make_pair(&cpu, id));
|
||||
|
||||
// Space at the end is to pad a gap on the right
|
||||
m_choice_units->addItem(qstr((id >> 24 == 0x55 ? "RSX[0x55555555]" : cpu.get_name()) + ' '), var_cpu);
|
||||
|
@ -869,7 +869,7 @@ void debugger_frame::UpdateUnitList()
|
|||
|
||||
void debugger_frame::OnSelectUnit()
|
||||
{
|
||||
auto [selected, cpu_id] = m_choice_units->currentData().value<std::pair<cpu_thread*, u32>>();
|
||||
auto [selected, cpu_id] = m_choice_units->currentData().value<data_type>();
|
||||
|
||||
if (m_emu_state != system_state::stopped)
|
||||
{
|
||||
|
@ -963,7 +963,7 @@ void debugger_frame::DoUpdate()
|
|||
// Check if we need to disable a step over bp
|
||||
if (const auto cpu0 = get_cpu(); cpu0 && m_last_step_over_breakpoint != umax && cpu0->get_pc() == m_last_step_over_breakpoint)
|
||||
{
|
||||
m_breakpoint_handler->RemoveBreakpoint(m_last_step_over_breakpoint);
|
||||
m_ppu_breakpoint_handler->RemoveBreakpoint(m_last_step_over_breakpoint);
|
||||
m_last_step_over_breakpoint = -1;
|
||||
}
|
||||
|
||||
|
@ -1116,13 +1116,13 @@ void debugger_frame::DoStep(bool step_over)
|
|||
|
||||
// Set breakpoint on next instruction
|
||||
const u32 next_instruction_pc = current_instruction_pc + 4;
|
||||
m_breakpoint_handler->AddBreakpoint(next_instruction_pc);
|
||||
m_ppu_breakpoint_handler->AddBreakpoint(next_instruction_pc);
|
||||
|
||||
// Undefine previous step over breakpoint if it hasnt been already
|
||||
// This can happen when the user steps over a branch that doesn't return to itself
|
||||
if (m_last_step_over_breakpoint != umax)
|
||||
{
|
||||
m_breakpoint_handler->RemoveBreakpoint(next_instruction_pc);
|
||||
m_ppu_breakpoint_handler->RemoveBreakpoint(next_instruction_pc);
|
||||
}
|
||||
|
||||
m_last_step_over_breakpoint = next_instruction_pc;
|
||||
|
|
|
@ -64,7 +64,7 @@ class debugger_frame : public custom_dock_widget
|
|||
rsx::thread* m_rsx = nullptr;
|
||||
|
||||
breakpoint_list* m_breakpoint_list;
|
||||
breakpoint_handler* m_breakpoint_handler;
|
||||
breakpoint_handler* m_ppu_breakpoint_handler;
|
||||
call_stack_list* m_call_stack_list;
|
||||
instruction_editor_dialog* m_inst_editor = nullptr;
|
||||
register_editor_dialog* m_reg_editor = nullptr;
|
||||
|
|
|
@ -23,7 +23,7 @@ constexpr auto qstr = QString::fromStdString;
|
|||
debugger_list::debugger_list(QWidget* parent, std::shared_ptr<gui_settings> gui_settings, breakpoint_handler* handler)
|
||||
: QListWidget(parent)
|
||||
, m_gui_settings(std::move(gui_settings))
|
||||
, m_breakpoint_handler(handler)
|
||||
, m_ppu_breakpoint_handler(handler)
|
||||
{
|
||||
setWindowTitle(tr("ASM"));
|
||||
|
||||
|
@ -54,9 +54,21 @@ u32 debugger_list::GetCenteredAddress(u32 address) const
|
|||
|
||||
void debugger_list::ShowAddress(u32 addr, bool select_addr, bool force)
|
||||
{
|
||||
auto IsBreakpoint = [this](u32 pc)
|
||||
const decltype(spu_thread::local_breakpoints)* spu_bps_list;
|
||||
|
||||
if (m_cpu && m_cpu->id_type() == 2)
|
||||
{
|
||||
return m_cpu && m_cpu->id_type() == 1 && m_breakpoint_handler->HasBreakpoint(pc);
|
||||
spu_bps_list = &static_cast<spu_thread*>(m_cpu)->local_breakpoints;
|
||||
}
|
||||
|
||||
auto IsBreakpoint = [&](u32 pc)
|
||||
{
|
||||
switch (m_cpu ? m_cpu->id_type() : 0)
|
||||
{
|
||||
case 1: return m_ppu_breakpoint_handler->HasBreakpoint(pc);
|
||||
case 2: return (*spu_bps_list)[pc / 4].load();
|
||||
default: return false;
|
||||
}
|
||||
};
|
||||
|
||||
bool center_pc = m_gui_settings->GetValue(gui::d_centerPC).toBool();
|
||||
|
|
|
@ -52,7 +52,7 @@ private:
|
|||
|
||||
std::shared_ptr<gui_settings> m_gui_settings;
|
||||
|
||||
breakpoint_handler* m_breakpoint_handler;
|
||||
breakpoint_handler* m_ppu_breakpoint_handler;
|
||||
cpu_thread* m_cpu = nullptr;
|
||||
CPUDisAsm* m_disasm = nullptr;
|
||||
QDialog* m_cmd_detail = nullptr;
|
||||
|
|
Loading…
Add table
Reference in a new issue