Skip to content

Commit

Permalink
memory viewer: Implement RSX mode
Browse files Browse the repository at this point in the history
* Set the ground for RSX modes of register editor and insttruction editor, do not use shared ptrs directly.
* Make register editor and instruction editor modeless to allow to copypaste values from thread context etc in the background.
  • Loading branch information
elad335 committed Feb 1, 2021
1 parent 58eef98 commit 6905cce
Show file tree
Hide file tree
Showing 13 changed files with 315 additions and 117 deletions.
7 changes: 6 additions & 1 deletion rpcs3/Emu/RSX/RSXThread.cpp
Expand Up @@ -43,7 +43,7 @@ namespace rsx
{
std::function<bool(u32 addr, bool is_writing)> g_access_violation_handler;

u32 get_address(u32 offset, u32 location, u32 line, u32 col, const char* file, const char* func)
u32 get_address(u32 offset, u32 location, bool allow_failure, u32 line, u32 col, const char* file, const char* func)
{
const auto render = get_current_renderer();
std::string_view msg;
Expand Down Expand Up @@ -150,6 +150,11 @@ namespace rsx
}
}

if (allow_failure)
{
return 0;
}

fmt::throw_exception("rsx::get_address(offset=0x%x, location=0x%x): %s%s", offset, location, msg, src_loc{line, col, file, func});
}

Expand Down
2 changes: 1 addition & 1 deletion rpcs3/Emu/RSX/RSXThread.h
Expand Up @@ -172,7 +172,7 @@ namespace rsx

u32 get_vertex_type_size_on_host(vertex_base_type type, u32 size);

u32 get_address(u32 offset, u32 location,
u32 get_address(u32 offset, u32 location, bool allow_failure = false,
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
Expand Down
179 changes: 130 additions & 49 deletions rpcs3/rpcs3qt/debugger_frame.cpp
Expand Up @@ -313,10 +313,14 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
{
case Qt::Key_E:
{
if (m_cpu)
if (cpu->id_type() == 1 || cpu->id_type() == 2)
{
instruction_editor_dialog* dlg = new instruction_editor_dialog(this, pc, m_cpu, m_disasm.get());
dlg->show();
if (!m_inst_editor)
{
m_inst_editor = new instruction_editor_dialog(this, pc, m_disasm.get(), make_check_cpu(cpu));
connect(m_inst_editor, &QDialog::finished, this, [this]() { m_inst_editor = nullptr; });
m_inst_editor->show();
}
}
return;
}
Expand All @@ -332,10 +336,14 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
}
case Qt::Key_R:
{
if (m_cpu)
if (cpu->id_type() == 1 || cpu->id_type() == 2)
{
register_editor_dialog* dlg = new register_editor_dialog(this, m_cpu, m_disasm.get());
dlg->show();
if (!m_reg_editor)
{
m_reg_editor = new register_editor_dialog(this, m_disasm.get(), make_check_cpu(cpu));
connect(m_reg_editor, &QDialog::finished, this, [this]() { m_reg_editor = nullptr; });
m_reg_editor->show();
}
}
return;
}
Expand Down Expand Up @@ -386,7 +394,7 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
case Qt::Key_M:
{
// Memory viewer
if (m_cpu) idm::make<memory_viewer_handle>(this, pc, m_cpu);
idm::make<memory_viewer_handle>(this, pc, make_check_cpu(cpu));
return;
}
case Qt::Key_F10:
Expand Down Expand Up @@ -417,19 +425,63 @@ cpu_thread* debugger_frame::get_cpu()
// m_rsx is raw pointer, when emulation is stopped it won't be cleared
// Therefore need to do invalidation checks manually

if (m_emu_state == system_state::stopped)
{
m_rsx = nullptr;
return m_rsx;
}

if (g_fxo->get<rsx::thread>() != m_rsx)
{
m_rsx = nullptr;
return m_rsx;
}

if (m_rsx && !m_rsx->ctrl)
{
m_rsx = nullptr;
return m_rsx;
}

return m_rsx;
}

std::function<cpu_thread*()> debugger_frame::make_check_cpu(cpu_thread* cpu)
{
const u32 id = cpu ? cpu->id : UINT32_MAX;
const u32 type = id >> 24;

std::shared_ptr<cpu_thread> shared = type == 1 ? static_cast<std::shared_ptr<cpu_thread>>(idm::get<named_thread<ppu_thread>>(id)) :
type == 2 ? idm::get<named_thread<spu_thread>>(id) : nullptr;

if (shared.get() != cpu)
{
shared.reset();
}

return [&rsx = m_rsx, cpu, type, shared = std::move(shared)]() -> cpu_thread*
{
if (type == 1 || type == 2)
{
// SPU and PPU
if (!shared || (shared->state & cpu_flag::exit && shared->state & cpu_flag::wait))
{
return nullptr;
}

return shared.get();
}

// RSX
if (rsx == cpu)
{
return cpu;
}

return nullptr;
};
}

void debugger_frame::UpdateUI()
{
UpdateUnitList();
Expand Down Expand Up @@ -476,8 +528,9 @@ void debugger_frame::UpdateUI()
}
}

Q_DECLARE_METATYPE(std::weak_ptr<cpu_thread>);
Q_DECLARE_METATYPE(::rsx::thread*);
using data_type = std::pair<cpu_thread*, u32>;

Q_DECLARE_METATYPE(data_type);

void debugger_frame::UpdateUnitList()
{
Expand All @@ -500,15 +553,20 @@ void debugger_frame::UpdateUnitList()
//const int old_size = m_choice_units->count();
QVariant old_cpu = m_choice_units->currentData();

bool reselected = false;

const auto on_select = [&](u32 id, cpu_thread& cpu)
{
if (emu_state == system_state::stopped) return;

QVariant var_cpu = QVariant::fromValue<std::weak_ptr<cpu_thread>>(
id >> 24 == 1 ? static_cast<std::weak_ptr<cpu_thread>>(idm::get_unlocked<named_thread<ppu_thread>>(id)) : idm::get_unlocked<named_thread<spu_thread>>(id));
QVariant var_cpu = QVariant::fromValue<std::pair<cpu_thread*, u32>>(std::make_pair(&cpu, id));
m_choice_units->addItem(qstr(id >> 24 == 0x55 ? "RSX[0x55555555]" : cpu.get_name()), var_cpu);

m_choice_units->addItem(qstr(cpu.get_name()), var_cpu);
if (old_cpu == var_cpu) m_choice_units->setCurrentIndex(m_choice_units->count() - 1);
if (!reselected && old_cpu == var_cpu)
{
m_choice_units->setCurrentIndex(m_choice_units->count() - 1);
reselected = true;
}
};

{
Expand All @@ -522,78 +580,101 @@ void debugger_frame::UpdateUnitList()

if (auto render = g_fxo->get<rsx::thread>(); emu_state != system_state::stopped && render && render->ctrl)
{
QVariant var_cpu = QVariant::fromValue<rsx::thread*>(render);
m_choice_units->addItem("RSX[0x55555555]", var_cpu);
if (old_cpu == var_cpu) m_choice_units->setCurrentIndex(m_choice_units->count() - 1);
on_select(render->id, *render);
}
}

// Close dialogs which are tied to the specific thread selected
if (!reselected)
{
if (m_reg_editor) m_reg_editor->close();
if (m_inst_editor) m_inst_editor->close();
}

OnSelectUnit();

m_choice_units->update();
}

void debugger_frame::OnSelectUnit()
{
if (m_choice_units->count() < 1)
{
m_debugger_list->UpdateCPUData(nullptr, nullptr);
m_breakpoint_list->UpdateCPUData(nullptr, nullptr);
m_disasm.reset();
m_cpu.reset();
return;
}

const auto weak = m_choice_units->currentData().value<std::weak_ptr<cpu_thread>>();
const auto render = m_choice_units->currentData().value<rsx::thread*>();
auto [selected, cpu_id] = m_choice_units->currentData().value<std::pair<cpu_thread*, u32>>();

if (m_emu_state != system_state::stopped)
{
if (!render && !weak.owner_before(m_cpu) && !m_cpu.owner_before(weak))
if (selected && m_cpu.get() == selected)
{
// They match, nothing to do.
return;
}

if (render && render == get_cpu())
if (selected && m_rsx == selected)
{
return;
}

if (!selected && !m_rsx && !m_cpu)
{
return;
}
}
else
{
selected = nullptr;
}

m_disasm.reset();
m_cpu.reset();
m_rsx = nullptr;

if (!weak.expired())
if (selected)
{
if (const auto cpu0 = weak.lock())
switch (cpu_id >> 24)
{
case 1:
{
if (cpu0->id_type() == 1)
m_cpu = idm::get<named_thread<ppu_thread>>(cpu_id);

if (selected == m_cpu.get())
{
if (cpu0.get() == idm::check<named_thread<ppu_thread>>(cpu0->id))
{
m_cpu = cpu0;
m_disasm = std::make_shared<PPUDisAsm>(cpu_disasm_mode::interpreter, vm::g_sudo_addr);
}
m_disasm = std::make_shared<PPUDisAsm>(cpu_disasm_mode::interpreter, vm::g_sudo_addr);
}
else if (cpu0->id_type() == 2)
else
{
if (cpu0.get() == idm::check<named_thread<spu_thread>>(cpu0->id))
{
m_cpu = cpu0;
m_disasm = std::make_shared<SPUDisAsm>(cpu_disasm_mode::interpreter, static_cast<const spu_thread*>(cpu0.get())->ls);
}
m_cpu.reset();
selected = nullptr;
}

break;
}
}
else
{
m_rsx = render;
case 2:
{
m_cpu = idm::get<named_thread<spu_thread>>(cpu_id);

if (get_cpu())
if (selected == m_cpu.get())
{
m_disasm = std::make_shared<SPUDisAsm>(cpu_disasm_mode::interpreter, static_cast<const spu_thread*>(m_cpu.get())->ls);
}
else
{
m_cpu.reset();
selected = nullptr;
}

break;
}
case 0x55:
{
m_disasm = std::make_shared<RSXDisAsm>(cpu_disasm_mode::interpreter, vm::g_sudo_addr, m_rsx);
m_rsx = static_cast<rsx::thread*>(selected);

if (get_cpu())
{
m_disasm = std::make_shared<RSXDisAsm>(cpu_disasm_mode::interpreter, vm::g_sudo_addr, m_rsx);
}

break;
}
default: break;
}
}

Expand Down
8 changes: 7 additions & 1 deletion rpcs3/rpcs3qt/debugger_frame.h
Expand Up @@ -27,6 +27,9 @@ namespace rsx

enum class system_state : u32;

class instruction_editor_dialog;
class register_editor_dialog;

class debugger_frame : public custom_dock_widget
{
Q_OBJECT
Expand Down Expand Up @@ -62,12 +65,15 @@ class debugger_frame : public custom_dock_widget

breakpoint_list* m_breakpoint_list;
breakpoint_handler* m_breakpoint_handler;

call_stack_list* m_call_stack_list;
instruction_editor_dialog* m_inst_editor = nullptr;
register_editor_dialog* m_reg_editor = nullptr;

std::shared_ptr<gui_settings> xgui_settings;

cpu_thread* get_cpu();
std::function<cpu_thread*()> make_check_cpu(cpu_thread* cpu);

public:
explicit debugger_frame(std::shared_ptr<gui_settings> settings, QWidget *parent = 0);

Expand Down
19 changes: 14 additions & 5 deletions rpcs3/rpcs3qt/instruction_editor_dialog.cpp
Expand Up @@ -13,17 +13,19 @@ constexpr auto qstr = QString::fromStdString;

extern bool ppu_patch(u32 addr, u32 value);

instruction_editor_dialog::instruction_editor_dialog(QWidget *parent, u32 _pc, const std::shared_ptr<cpu_thread>& _cpu, CPUDisAsm* _disasm)
instruction_editor_dialog::instruction_editor_dialog(QWidget *parent, u32 _pc, CPUDisAsm* _disasm, std::function<cpu_thread*()> func)
: QDialog(parent)
, m_pc(_pc)
, m_disasm(_disasm)
, m_cpu(_cpu)
, m_get_cpu(std::move(func))
{
setWindowTitle(tr("Edit instruction"));
setAttribute(Qt::WA_DeleteOnClose);
setMinimumSize(300, sizeHint().height());

m_cpu_offset = m_cpu->id_type() == 2 ? static_cast<spu_thread&>(*m_cpu).ls : vm::g_sudo_addr;
const auto cpu = m_get_cpu();

m_cpu_offset = cpu && cpu->id_type() == 2 ? static_cast<spu_thread&>(*cpu).ls : vm::g_sudo_addr;
QString instruction = qstr(fmt::format("%08x", *reinterpret_cast<be_t<u32>*>(m_cpu_offset + m_pc)));

QVBoxLayout* vbox_panel(new QVBoxLayout());
Expand Down Expand Up @@ -68,19 +70,26 @@ instruction_editor_dialog::instruction_editor_dialog(QWidget *parent, u32 _pc, c
vbox_panel->addSpacing(10);
vbox_panel->addLayout(hbox_b_panel);
setLayout(vbox_panel);
setModal(true);

// Events
connect(button_ok, &QAbstractButton::clicked, [=, this]()
{
const auto cpu = m_get_cpu();

if (!cpu)
{
close();
return;
}

bool ok;
ulong opcode = m_instr->text().toULong(&ok, 16);
if (!ok || opcode > UINT32_MAX)
{
QMessageBox::critical(this, tr("Error"), tr("Failed to parse PPU instruction."));
return;
}
else if (m_cpu->id_type() == 1)
else if (cpu->id_type() == 1)
{
if (!ppu_patch(m_pc, static_cast<u32>(opcode)))
{
Expand Down

0 comments on commit 6905cce

Please sign in to comment.