1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-22 02:32:36 +01:00

New RSX Debugger

This commit is contained in:
Eladash 2021-01-22 10:11:54 +02:00 committed by Ivan
parent 67dd6754a6
commit 0652870204
42 changed files with 583 additions and 271 deletions

View File

@ -121,10 +121,6 @@ std::string dump_useful_thread_info()
{
result = cpu->dump_all();
}
else if (auto render = rsx::get_current_renderer(); render && render->is_current_thread())
{
result = render->dump_regs();
}
guard = false;
return result;
@ -1422,7 +1418,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context) no
return vm::check_addr(addr, is_writing ? vm::page_writable : vm::page_readable);
};
if (cpu)
if (cpu && (cpu->id_type() == 1 || cpu->id_type() == 2))
{
vm::temporary_unlock(*cpu);
u32 pf_port_id = 0;

View File

@ -399,6 +399,7 @@ target_sources(rpcs3_emu PRIVATE
RSX/RSXTexture.cpp
RSX/RSXThread.cpp
RSX/rsx_utils.cpp
RSX/RSXDisAsm.cpp
RSX/Common/BufferUtils.cpp
RSX/Common/FragmentProgramDecompiler.cpp
RSX/Common/GLSLCommon.cpp

View File

@ -4,26 +4,30 @@
#include <limits>
#include "Utilities/StrFmt.h"
enum CPUDisAsmMode
enum class cpu_disasm_mode
{
CPUDisAsm_DumpMode,
CPUDisAsm_InterpreterMode,
CPUDisAsm_NormalMode,
CPUDisAsm_CompilerElfMode,
dump,
interpreter,
normal,
compiler_elf,
list, // RSX exclusive
};
class cpu_thread;
class CPUDisAsm
{
protected:
const CPUDisAsmMode m_mode;
const cpu_disasm_mode m_mode;
const std::add_pointer_t<const u8> m_offset;
const std::add_pointer_t<const cpu_thread> m_cpu;
u32 m_op = 0;
virtual void Write(const std::string& value)
{
switch (m_mode)
{
case CPUDisAsm_DumpMode:
case cpu_disasm_mode::dump:
{
last_opcode = fmt::format("\t%08x:\t%02x %02x %02x %02x\t%s\n", dump_pc,
static_cast<u8>(m_op >> 24),
@ -33,7 +37,7 @@ protected:
break;
}
case CPUDisAsm_InterpreterMode:
case cpu_disasm_mode::interpreter:
{
last_opcode = fmt::format("[%08x] %02x %02x %02x %02x: %s", dump_pc,
static_cast<u8>(m_op >> 24),
@ -43,12 +47,12 @@ protected:
break;
}
case CPUDisAsm_CompilerElfMode:
case cpu_disasm_mode::compiler_elf:
{
last_opcode = value + '\n';
break;
}
case CPUDisAsm_NormalMode:
case cpu_disasm_mode::normal:
{
last_opcode = value;
break;
@ -61,14 +65,21 @@ public:
std::string last_opcode;
u32 dump_pc;
template <typename T, std::enable_if_t<std::is_base_of_v<CPUDisAsm, T>, int> = 0>
static T copy_and_change_mode(const T& dis, cpu_disasm_mode mode)
{
return T{mode, dis.m_offset, dis.m_cpu};
}
protected:
CPUDisAsm(CPUDisAsmMode mode, const u8* offset)
CPUDisAsm(cpu_disasm_mode mode, const u8* offset, const cpu_thread* cpu = nullptr)
: m_mode(mode)
, m_offset(offset)
, m_cpu(cpu)
{
}
virtual u32 DisAsmBranchTarget(const s32 imm) = 0;
virtual u32 DisAsmBranchTarget(const s32 imm) { return 0; };
// TODO: Add builtin fmt helpper for best performance
template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
@ -95,7 +106,7 @@ protected:
std::string FixOp(std::string op) const
{
if (m_mode != CPUDisAsm_NormalMode)
if (m_mode != cpu_disasm_mode::normal)
{
op.resize(std::max<usz>(op.length(), 10), ' ');
}

View File

@ -8,6 +8,7 @@
#include "Emu/GDB.h"
#include "Emu/Cell/PPUThread.h"
#include "Emu/Cell/SPUThread.h"
#include "Emu/RSX/RSXThread.h"
#include "Emu/perf_meter.hpp"
#include "util/asm.hpp"
@ -49,7 +50,6 @@ 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::dbg_global_pause: return "G-PAUSE";
case cpu_flag::dbg_global_stop: return "G-EXIT";
case cpu_flag::dbg_pause: return "PAUSE";
case cpu_flag::dbg_step: return "STEP";
case cpu_flag::__bitset_enum_max: break;
@ -541,7 +541,7 @@ void cpu_thread::operator()()
cleanup.name = thread_ctrl::get_name();
// Check thread status
while (!(state & (cpu_flag::exit + cpu_flag::dbg_global_stop)) && thread_ctrl::state() != thread_state::aborting)
while (!(state & cpu_flag::exit) && thread_ctrl::state() != thread_state::aborting)
{
// Check stop status
if (!(state & cpu_flag::stop))
@ -580,6 +580,11 @@ cpu_thread::cpu_thread(u32 id)
g_threads_created++;
}
void cpu_thread::cpu_wait()
{
thread_ctrl::wait();
}
bool cpu_thread::check_state() noexcept
{
bool cpu_sleep_called = false;
@ -642,7 +647,7 @@ bool cpu_thread::check_state() noexcept
}
// Atomically clean wait flag and escape
if (!(flags & (cpu_flag::exit + cpu_flag::dbg_global_stop + cpu_flag::ret + cpu_flag::stop)))
if (!(flags & (cpu_flag::exit + cpu_flag::ret + cpu_flag::stop)))
{
// Check pause flags which hold thread inside check_state (ignore suspend on cpu_flag::temp)
if (flags & (cpu_flag::pause + cpu_flag::dbg_global_pause + cpu_flag::dbg_pause + cpu_flag::memory + (cpu_can_stop ? cpu_flag::suspend : cpu_flag::pause)))
@ -721,7 +726,7 @@ bool cpu_thread::check_state() noexcept
g_fxo->get<gdb_server>()->pause_from(this);
}
thread_ctrl::wait();
cpu_wait();
}
else
{
@ -772,7 +777,7 @@ void cpu_thread::notify()
{
thread_ctrl::notify(*static_cast<named_thread<spu_thread>*>(this));
}
else
else if (id_type() != 0x55)
{
fmt::throw_exception("Invalid cpu_thread type");
}
@ -828,6 +833,11 @@ u32 cpu_thread::get_pc() const
pc = &static_cast<const spu_thread*>(this)->pc;
break;
}
case 0x55:
{
const auto ctrl = static_cast<const rsx::thread*>(this)->ctrl;
return ctrl ? ctrl->get : UINT32_MAX;
}
default: break;
}
@ -836,7 +846,15 @@ u32 cpu_thread::get_pc() const
std::string cpu_thread::dump_all() const
{
return {};
std::string ret = cpu_thread::dump_misc();
ret += '\n';
ret += dump_misc();
ret += '\n';
ret += dump_regs();
ret += '\n';
ret += dump_callstack();
return ret;
}
std::string cpu_thread::dump_regs() const
@ -846,7 +864,16 @@ std::string cpu_thread::dump_regs() const
std::string cpu_thread::dump_callstack() const
{
return {};
std::string ret;
fmt::append(ret, "Call stack:\n=========\n0x%08x (0x0) called\n", get_pc());
for (const auto& sp : dump_callstack_list())
{
fmt::append(ret, "> from 0x%08x (sp=0x%08x)\n", sp.first, sp.second);
}
return ret;
}
std::vector<std::pair<u32, u32>> cpu_thread::dump_callstack_list() const
@ -1035,7 +1062,7 @@ void cpu_thread::stop_all() noexcept
{
auto on_stop = [](u32, cpu_thread& cpu)
{
cpu.state += cpu_flag::dbg_global_stop;
cpu.state += cpu_flag::exit;
cpu.abort();
};

View File

@ -19,7 +19,6 @@ enum class cpu_flag : u32
memory, // Thread must unlock memory mutex
dbg_global_pause, // Emulation paused
dbg_global_stop, // Emulation stopped
dbg_pause, // Thread paused
dbg_step, // Thread forced to pause after one step (one instruction, etc)
@ -64,7 +63,7 @@ public:
// Test stopped state
bool is_stopped() const
{
return !!(state & (cpu_flag::stop + cpu_flag::exit + cpu_flag::dbg_global_stop));
return !!(state & (cpu_flag::stop + cpu_flag::exit));
}
// Test paused state
@ -122,6 +121,9 @@ public:
// Callback for cpu_flag::ret
virtual void cpu_return() {}
// Callback for thread_ctrl::wait or RSX wait
virtual void cpu_wait();
// For internal use
struct suspend_work
{
@ -211,7 +213,7 @@ public:
}
}
// Stop all threads with cpu_flag::dbg_global_stop
// Stop all threads with cpu_flag::exit
static void stop_all() noexcept;
// Send signal to the profiler(s) to flush results

View File

@ -5,7 +5,7 @@
class PPCDisAsm : public CPUDisAsm
{
protected:
PPCDisAsm(CPUDisAsmMode mode, const u8* offset) : CPUDisAsm(mode, offset)
PPCDisAsm(cpu_disasm_mode mode, const u8* offset) : CPUDisAsm(mode, offset)
{
}
@ -89,7 +89,7 @@ protected:
}
void DisAsm_F1_R2(const std::string& op, u32 f0, u32 r0, u32 r1)
{
if(m_mode == CPUDisAsm_CompilerElfMode)
if(m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("%s f%d,r%d,r%d", FixOp(op), f0, r0, r1));
return;
@ -99,7 +99,7 @@ protected:
}
void DisAsm_F1_IMM_R1_RC(const std::string& op, u32 f0, s32 imm0, u32 r0, u32 rc)
{
if(m_mode == CPUDisAsm_CompilerElfMode)
if(m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("%s%s f%d,r%d,%s", FixOp(op), (rc ? "." : ""), f0, r0, SignedHex(imm0)));
return;
@ -177,7 +177,7 @@ protected:
}
void DisAsm_R2_IMM(const std::string& op, u32 r0, u32 r1, s32 imm0)
{
if(m_mode == CPUDisAsm_CompilerElfMode)
if(m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("%s r%d,r%d,%s", FixOp(op), r0, r1, SignedHex(imm0)));
return;

View File

@ -935,7 +935,7 @@ void PPUDisAsm::BC(ppu_opcode_t op)
const u32 aa = op.aa;
const u32 lk = op.lk;
if (m_mode == CPUDisAsm_CompilerElfMode)
if (m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("bc 0x%x, 0x%x, 0x%x, %d, %d", bo, bi, bd, aa, lk));
return;
@ -995,7 +995,7 @@ void PPUDisAsm::B(ppu_opcode_t op)
const u32 aa = op.aa;
const u32 lk = op.lk;
if (m_mode == CPUDisAsm_CompilerElfMode)
if (m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("b 0x%x, %d, %d", li, aa, lk));
return;

View File

@ -6,7 +6,7 @@
class PPUDisAsm final : public PPCDisAsm
{
public:
PPUDisAsm(CPUDisAsmMode mode, const u8* offset) : PPCDisAsm(mode, offset)
PPUDisAsm(cpu_disasm_mode mode, const u8* offset) : PPCDisAsm(mode, offset)
{
}
@ -107,7 +107,7 @@ private:
}
void DisAsm_F1_R2(const std::string& op, u32 f0, u32 r0, u32 r1)
{
if(m_mode == CPUDisAsm_CompilerElfMode)
if (m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("%s f%d,r%d,r%d", FixOp(op), f0, r0, r1));
return;
@ -117,7 +117,7 @@ private:
}
void DisAsm_F1_IMM_R1_RC(const std::string& op, u32 f0, s32 imm0, u32 r0, u32 rc)
{
if(m_mode == CPUDisAsm_CompilerElfMode)
if (m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("%s f%d,r%d,%s", FixOp(op + (rc ? "." : "")), f0, r0, SignedHex(imm0)));
return;
@ -195,7 +195,7 @@ private:
}
void DisAsm_R2_IMM(const std::string& op, u32 r0, u32 r1, s32 imm0)
{
if(m_mode == CPUDisAsm_CompilerElfMode)
if (m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("%s r%d,r%d,%s", FixOp(op), r0, r1, SignedHex(imm0)));
return;

View File

@ -443,19 +443,6 @@ std::array<u32, 2> op_branch_targets(u32 pc, ppu_opcode_t op)
return res;
}
std::string ppu_thread::dump_all() const
{
std::string ret = cpu_thread::dump_misc();
ret += '\n';
ret += dump_misc();
ret += '\n';
ret += dump_regs();
ret += '\n';
ret += dump_callstack();
return ret;
}
std::string ppu_thread::dump_regs() const
{
std::string ret;
@ -505,7 +492,7 @@ std::string ppu_thread::dump_regs() const
}
else
{
PPUDisAsm dis_asm(CPUDisAsm_NormalMode, vm::g_sudo_addr);
PPUDisAsm dis_asm(cpu_disasm_mode::normal, vm::g_sudo_addr);
dis_asm.disasm(reg);
fmt::append(ret, " -> %s", dis_asm.last_opcode);
}
@ -834,7 +821,7 @@ void ppu_thread::exec_task()
{
if (g_cfg.core.ppu_decoder == ppu_decoder_type::llvm)
{
while (!(state & (cpu_flag::ret + cpu_flag::exit + cpu_flag::stop + cpu_flag::dbg_global_stop)))
while (!(state & (cpu_flag::ret + cpu_flag::exit + cpu_flag::stop)))
{
reinterpret_cast<ppu_function_t>(ppu_ref(cia))(*this);
}

View File

@ -118,7 +118,6 @@ public:
static const u32 id_count = 100;
static constexpr std::pair<u32, u32> id_invl_range = {12, 12};
virtual std::string dump_all() const override;
virtual std::string dump_regs() const override;
virtual std::string dump_callstack() const override;
virtual std::vector<std::pair<u32, u32>> dump_callstack_list() const override;

View File

@ -20,7 +20,7 @@ u32 SPUDisAsm::disasm(u32 pc)
std::pair<bool, v128> SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const
{
if (m_mode != CPUDisAsm_InterpreterMode)
if (m_mode != cpu_disasm_mode::interpreter)
{
return {};
}

View File

@ -71,7 +71,7 @@ static constexpr const char* spu_ch_name[128] =
class SPUDisAsm final : public PPCDisAsm
{
public:
SPUDisAsm(CPUDisAsmMode mode, const u8* offset) : PPCDisAsm(mode, offset)
SPUDisAsm(cpu_disasm_mode mode, const u8* offset) : PPCDisAsm(mode, offset)
{
}
@ -99,7 +99,7 @@ private:
private:
std::string& FixOp(std::string& op)
{
if (m_mode != CPUDisAsm_NormalMode)
if (m_mode != cpu_disasm_mode::normal)
{
op.append(std::max<int>(10 - ::narrow<int>(op.size()), 0), ' ');
}

View File

@ -3151,7 +3151,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point)
void spu_recompiler_base::dump(const spu_program& result, std::string& out)
{
SPUDisAsm dis_asm(CPUDisAsm_DumpMode, reinterpret_cast<const u8*>(result.data.data()) - result.lower_bound);
SPUDisAsm dis_asm(cpu_disasm_mode::dump, reinterpret_cast<const u8*>(result.data.data()) - result.lower_bound);
std::string hash;
{

View File

@ -1218,18 +1218,6 @@ spu_imm_table_t::spu_imm_table_t()
}
}
std::string spu_thread::dump_all() const
{
std::string ret = cpu_thread::dump_misc();
ret += '\n';
ret += dump_misc();
ret += '\n';
ret += dump_regs();
ret += '\n';
return ret;
}
std::string spu_thread::dump_regs() const
{
std::string ret;
@ -1289,7 +1277,7 @@ std::string spu_thread::dump_regs() const
if (i3 >= 0x80 && is_exec_code(i3))
{
SPUDisAsm dis_asm(CPUDisAsm_NormalMode, ls);
SPUDisAsm dis_asm(cpu_disasm_mode::normal, ls);
dis_asm.disasm(i3);
fmt::append(ret, " -> %s", dis_asm.last_opcode);
}
@ -1700,7 +1688,7 @@ void spu_thread::cleanup()
}
// Free range lock (and signals cleanup was called to the destructor)
vm::free_range_lock(std::exchange(range_lock, nullptr));
vm::free_range_lock(range_lock);
}
spu_thread::~spu_thread()
@ -1710,9 +1698,6 @@ spu_thread::~spu_thread()
shm->unmap(ls);
shm->unmap(ls - SPU_LS_SIZE);
// Free range lock if not freed already
if (range_lock) vm::free_range_lock(range_lock);
perf_log.notice("Perf stats for transactions: success %u, failure %u", stx, ftx);
perf_log.notice("Perf stats for PUTLLC reload: successs %u, failure %u", last_succ, last_fail);
}

View File

@ -626,7 +626,6 @@ enum class spu_type : u32
class spu_thread : public cpu_thread
{
public:
virtual std::string dump_all() const override;
virtual std::string dump_regs() const override;
virtual std::string dump_callstack() const override;
virtual std::vector<std::pair<u32, u32>> dump_callstack_list() const override;

View File

@ -342,7 +342,7 @@ void _sys_process_exit(ppu_thread& ppu, s32 status, u32 arg2, u32 arg3)
Emu.Stop();
});
ppu.state += cpu_flag::dbg_global_stop;
ppu.state += cpu_flag::exit;
}
void _sys_process_exit2(ppu_thread& ppu, s32 status, vm::ptr<sys_exit2_param> arg, u32 arg_size, u32 arg4)
@ -419,7 +419,7 @@ void _sys_process_exit2(ppu_thread& ppu, s32 status, vm::ptr<sys_exit2_param> ar
}
});
ppu.state += cpu_flag::dbg_global_stop;
ppu.state += cpu_flag::exit;
}
error_code sys_process_spawns_a_self2(vm::ptr<u32> pid, u32 primary_prio, u64 flags, vm::ptr<void> stack, u32 stack_size, u32 mem_id, vm::ptr<void> param_sfo, vm::ptr<void> dbg_data)

View File

@ -55,24 +55,18 @@ void lv2_rsx_config::send_event(u64 data1, u64 event_flags, u64 data3) const
{
auto cpu = get_current_cpu_thread();
if (cpu && cpu->id_type() != 1)
{
cpu = nullptr;
}
if (cpu)
if (cpu && cpu->id_type() == 1)
{
// Deschedule
lv2_obj::sleep(*cpu, 100);
}
else if (const auto rsx = rsx::get_current_renderer(); rsx->is_current_thread())
{
rsx->on_semaphore_acquire_wait();
}
// Wait a bit before resending event
thread_ctrl::wait_for(100);
if (cpu && cpu->id_type() == 0x55)
cpu->cpu_wait();
if (Emu.IsStopped() || (cpu && cpu->check_state()))
{
error = 0;

View File

@ -1663,6 +1663,9 @@ namespace vm
utils::memory_decommit(g_base_addr, 0x200000000);
utils::memory_decommit(g_exec_addr, 0x200000000);
utils::memory_decommit(g_stat_addr, 0x100000000);
std::memset(g_range_lock_set, 0, sizeof(g_range_lock_set));
g_range_lock_bits = 0;
}
}

165
rpcs3/Emu/RSX/RSXDisAsm.cpp Normal file
View File

@ -0,0 +1,165 @@
#include "stdafx.h"
#include "RSXDisAsm.h"
#include "RSXThread.h"
#include "gcm_enums.h"
#include "gcm_printing.h"
#include "rsx_methods.h"
namespace rsx
{
void invalid_method(thread*, u32, u32);
}
u32 RSXDisAsm::disasm(u32 pc)
{
last_opcode.clear();
u32 addr = static_cast<const rsx::thread*>(m_cpu)->iomap_table.get_addr(pc);
if (addr == umax) return 0;
m_op = *reinterpret_cast<const atomic_be_t<u32>*>(m_offset + addr);
dump_pc = pc;
if (m_op & RSX_METHOD_NON_METHOD_CMD_MASK)
{
if (m_mode == cpu_disasm_mode::list)
{
return 0;
}
if ((m_op & RSX_METHOD_OLD_JUMP_CMD_MASK) == RSX_METHOD_OLD_JUMP_CMD)
{
u32 jumpAddr = m_op & RSX_METHOD_OLD_JUMP_OFFSET_MASK;
Write(fmt::format("jump 0x%07x", jumpAddr), -1);
}
else if ((m_op & RSX_METHOD_NEW_JUMP_CMD_MASK) == RSX_METHOD_NEW_JUMP_CMD)
{
u32 jumpAddr = m_op & RSX_METHOD_NEW_JUMP_OFFSET_MASK;
Write(fmt::format("jump 0x%07x", jumpAddr), -1);
}
else if ((m_op & RSX_METHOD_CALL_CMD_MASK) == RSX_METHOD_CALL_CMD)
{
u32 callAddr = m_op & RSX_METHOD_CALL_OFFSET_MASK;
Write(fmt::format("call 0x%07x", callAddr), -1);
}
else if ((m_op & RSX_METHOD_RETURN_MASK) == RSX_METHOD_RETURN_CMD)
{
Write("ret", -1);
}
else
{
Write("?? ??", -1);
}
return 4;
}
else if ((m_op & RSX_METHOD_NOP_MASK) == RSX_METHOD_NOP_CMD)
{
if (m_mode == cpu_disasm_mode::list)
{
return 0;
}
u32 i = 1;
for (pc += 4; i < 4096; i++, pc += 4)
{
addr = static_cast<const rsx::thread*>(m_cpu)->iomap_table.get_addr(pc);
if (addr == umax)
{
break;
}
m_op = *reinterpret_cast<const atomic_be_t<u32>*>(m_offset + addr);
if ((m_op & RSX_METHOD_NOP_MASK) != RSX_METHOD_NOP_CMD)
{
break;
}
}
if (i == 1)
Write("nop", 0);
else
Write(fmt::format("nop x%u", i), 0);
return i * 4;
}
else
{
const u32 count = (m_op & RSX_METHOD_COUNT_MASK) >> RSX_METHOD_COUNT_SHIFT;
const bool non_inc = (m_op & RSX_METHOD_NON_INCREMENT_CMD_MASK) == RSX_METHOD_NON_INCREMENT_CMD && count > 1;
const u32 id_start = (m_op & 0x3ffff) >> 2;
if (count > 10 && id_start == NV4097_SET_OBJECT)
{
// Hack: 0 method with large count is unlikely to be a command
// But is very common in floating point args, messing up debugger's code-flow
Write("?? ??", -1);
return 4;
}
pc += 4;
for (u32 i = 0; i < (m_mode == cpu_disasm_mode::list ? count : 1); i++, pc += 4)
{
addr = static_cast<const rsx::thread*>(m_cpu)->iomap_table.get_addr(pc);
if (addr == umax)
{
last_opcode.clear();
Write("?? ??", -1);
return 4;
}
m_op = *reinterpret_cast<const atomic_be_t<u32>*>(m_offset + addr);
const u32 id = id_start + (non_inc ? 0 : i);
if (rsx::methods[id] == &rsx::invalid_method)
{
last_opcode.clear();
Write("?? ??", -1);
return 4;
}
std::string str = rsx::get_pretty_printing_function(id)(id, m_op);
Write(str, m_mode == cpu_disasm_mode::list ? i : count, non_inc, id);
}
return (count + 1) * 4;
}
}
void RSXDisAsm::Write(const std::string& str, s32 count, bool is_non_inc, u32 id)
{
switch (m_mode)
{
case cpu_disasm_mode::interpreter:
{
last_opcode = count >= 0 ? fmt::format("[%08x] (%s%u)", dump_pc, is_non_inc ? "+" : "", count) :
fmt::format("[%08x] (x)", dump_pc);
auto& res = last_opcode;
res.resize(7 + 11);
std::replace(res.begin(), res.end(), '\0', ' ');
res += str;
break;
}
case cpu_disasm_mode::list:
{
if (!last_opcode.empty())
last_opcode += '\n';
fmt::append(last_opcode, "[%04x] 0x%08x: %s", id, m_op, str);
break;
}
default:
break;
}
}

17
rpcs3/Emu/RSX/RSXDisAsm.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include "Emu/Cell/PPCDisAsm.h"
class RSXDisAsm final : public CPUDisAsm
{
public:
RSXDisAsm(cpu_disasm_mode mode, const u8* offset, const cpu_thread* cpu) : CPUDisAsm(mode, offset, cpu)
{
}
private:
void Write(const std::string& str, s32 count, bool is_non_inc = false, u32 id = 0);
public:
u32 disasm(u32 pc) override;
};

View File

@ -360,6 +360,7 @@ namespace rsx
}
thread::thread()
: cpu_thread(0x5555'5555)
{
g_access_violation_handler = [this](u32 address, bool is_writing)
{
@ -378,6 +379,8 @@ namespace rsx
{
m_overlay_manager = g_fxo->init<rsx::overlays::display_manager>(0);
}
state -= cpu_flag::stop + cpu_flag::wait; // TODO: Remove workaround
}
void thread::capture_frame(const std::string &name)
@ -496,7 +499,7 @@ namespace rsx
while (method_registers.current_draw_clause.next());
}
void thread::operator()()
void thread::cpu_task()
{
{
// Wait for startup (TODO)
@ -510,7 +513,7 @@ namespace rsx
thread_ctrl::wait_for(1000);
if (Emu.IsStopped())
if (is_stopped())
{
return;
}
@ -522,6 +525,17 @@ namespace rsx
on_exit();
}
void thread::cpu_wait()
{
if (external_interrupt_lock)
{
wait_pause();
}
on_semaphore_acquire_wait();
std::this_thread::yield();
}
void thread::on_task()
{
m_rsx_thread = std::this_thread::get_id();
@ -560,7 +574,7 @@ namespace rsx
u64 start_time = get_system_time();
// TODO: exit condition
while (!Emu.IsStopped() && !m_rsx_thread_exiting)
while (!is_stopped())
{
const u64 period_time = 1000000 / g_cfg.video.vblank_rate;
const u64 wait_sleep = period_time - u64{period_time >= host_min_quantum} * host_min_quantum;
@ -602,7 +616,7 @@ namespace rsx
// Save the difference before pause
start_time = get_system_time() - start_time;
while (Emu.IsPaused() && !m_rsx_thread_exiting)
while (Emu.IsPaused() && !is_stopped())
{
thread_ctrl::wait_for(wait_sleep);
}
@ -626,8 +640,7 @@ namespace rsx
// Round to nearest to deal with forward/reverse scaling
fesetround(FE_TONEAREST);
// TODO: exit condition
while (true)
while (!test_stopped())
{
// Wait for external pause events
if (external_interrupt_lock)
@ -651,20 +664,6 @@ namespace rsx
// Execute FIFO queue
run_FIFO();
if (!Emu.IsRunning())
{
// Idle if emulation paused
while (Emu.IsPaused())
{
std::this_thread::sleep_for(1ms);
}
if (Emu.IsStopped())
{
break;
}
}
}
}
@ -679,6 +678,7 @@ namespace rsx
m_rsx_thread_exiting = true;
g_fxo->get<rsx::dma_manager>()->join();
state += cpu_flag::exit;
}
void thread::fill_scale_offset_data(void *buffer, bool flip_y) const
@ -2577,7 +2577,7 @@ namespace rsx
recovered_fifo_cmds_history.push({fifo_ctrl->last_cmd(), current_time});
}
std::vector<std::pair<u32, u32>> thread::dump_callstack() const
std::vector<std::pair<u32, u32>> thread::dump_callstack_list() const
{
std::vector<std::pair<u32, u32>> result;

View File

@ -592,13 +592,14 @@ namespace rsx
struct sampled_image_descriptor_base;
class thread
class thread : public cpu_thread
{
u64 timestamp_ctrl = 0;
u64 timestamp_subvalue = 0;
display_flip_info_t m_queued_flip{};
void cpu_task() override;
protected:
std::thread::id m_rsx_thread;
atomic_t<bool> m_rsx_thread_exiting{ true };
@ -615,7 +616,7 @@ namespace rsx
// FIFO
public:
std::unique_ptr<FIFO::FIFO_control> fifo_ctrl;
std::vector<std::pair<u32, u32>> dump_callstack() const;
std::vector<std::pair<u32, u32>> dump_callstack_list() const override;
protected:
FIFO::flattening_helper m_flattener;
@ -655,7 +656,8 @@ namespace rsx
static void fifo_wake_delay(u64 div = 1);
u32 get_fifo_cmd() const;
std::string dump_regs() const;
std::string dump_regs() const override;
void cpu_wait() override;
// Performance approximation counters
struct
@ -783,7 +785,6 @@ namespace rsx
reports::conditional_render_eval cond_render_ctrl;
void operator()();
virtual u64 get_cycles() = 0;
virtual ~thread();

View File

@ -1159,15 +1159,11 @@ enum Method : u32
RSX_METHOD_INCREMENT_CMD_MASK = 0xe0030003,
RSX_METHOD_INCREMENT_CMD = 0,
RSX_METHOD_INCREMENT_COUNT_MASK = 0x1ffc0000,
RSX_METHOD_INCREMENT_COUNT_SHIFT = 18,
RSX_METHOD_INCREMENT_METHOD_MASK = 0x0000fffc,
RSX_METHOD_NON_INCREMENT_CMD_MASK = 0xe0030003,
RSX_METHOD_NON_INCREMENT_CMD = 0x40000000,
RSX_METHOD_NON_INCREMENT_COUNT_MASK = 0x1ffc0000,
RSX_METHOD_NON_INCREMENT_COUNT_SHIFT = 18,
RSX_METHOD_NON_INCREMENT_METHOD_MASK = 0x0000fffc,
RSX_METHOD_COUNT_MASK = 0x1ffc0000,
RSX_METHOD_COUNT_SHIFT = 18,
RSX_METHOD_METHOD_MASK = 0x0000fffc,
RSX_METHOD_NEW_JUMP_CMD_MASK = 0xe0000003,
RSX_METHOD_NEW_JUMP_CMD = 0x00000001,

View File

@ -77,25 +77,20 @@ namespace rsx
u64 start = get_system_time();
while (sema != arg)
{
if (Emu.IsStopped())
return;
// Wait for external pause events
if (rsx->external_interrupt_lock)
if (rsx->is_stopped())
{
rsx->wait_pause();
continue;
return;
}
if (const auto tdr = static_cast<u64>(g_cfg.video.driver_recovery_timeout))
{
if (Emu.IsPaused())
if (rsx->is_paused())
{
const u64 start0 = get_system_time();
while (Emu.IsPaused())
while (rsx->is_paused())
{
std::this_thread::sleep_for(1ms);
rsx->cpu_wait();
}
// Reset
@ -112,8 +107,7 @@ namespace rsx
}
}
rsx->on_semaphore_acquire_wait();
std::this_thread::yield();
rsx->cpu_wait();
}
rsx->fifo_wake_delay();

View File

@ -13,6 +13,7 @@
#include "Emu/Cell/PPUAnalyser.h"
#include "Emu/Cell/SPUThread.h"
#include "Emu/Cell/RawSPUThread.h"
#include "Emu/RSX/RSXThread.h"
#include "Emu/Cell/lv2/sys_process.h"
#include "Emu/Cell/lv2/sys_memory.h"
#include "Emu/Cell/lv2/sys_sync.h"
@ -1788,6 +1789,7 @@ bool Emulator::Pause()
idm::select<named_thread<ppu_thread>>(on_select);
idm::select<named_thread<spu_thread>>(on_select);
g_fxo->get<rsx::thread>()->state += cpu_flag::dbg_global_pause;
// Always Enable display sleep, not only if it was prevented.
enable_display_sleep();
@ -1809,7 +1811,7 @@ void Emulator::Resume()
// Print and reset debug data collected
if (m_state == system_state::paused && g_cfg.core.ppu_debug)
{
PPUDisAsm dis_asm(CPUDisAsm_DumpMode, vm::g_sudo_addr);
PPUDisAsm dis_asm(cpu_disasm_mode::dump, vm::g_sudo_addr);
std::string dump;
@ -1856,6 +1858,8 @@ void Emulator::Resume()
idm::select<named_thread<ppu_thread>>(on_select);
idm::select<named_thread<spu_thread>>(on_select);
g_fxo->get<rsx::thread>()->state -= cpu_flag::dbg_global_pause;
GetCallbacks().on_resume();
if (g_cfg.misc.prevent_display_sleep)
@ -1906,6 +1910,7 @@ void Emulator::Stop(bool restart)
GetCallbacks().on_stop();
g_fxo->get<rsx::thread>()->state += cpu_flag::exit;
cpu_thread::stop_all();
g_fxo->reset();

View File

@ -13,7 +13,7 @@ u64 get_guest_system_time();
enum class localized_string_id;
enum class video_renderer;
enum class system_state
enum class system_state : u32
{
running,
paused,

View File

@ -88,6 +88,7 @@
<ClCompile Include="Emu\RSX\Overlays\overlay_utils.cpp" />
<ClCompile Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog.cpp" />
<ClCompile Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog_native.cpp" />
<ClCompile Include="Emu\RSX\RSXDisAsm.cpp" />
<ClCompile Include="Emu\system_config_types.cpp" />
<ClCompile Include="Emu\perf_meter.cpp" />
<ClCompile Include="Emu\title.cpp" />
@ -476,6 +477,7 @@
<ClInclude Include="Emu\RSX\Overlays\overlay_utils.h" />
<ClInclude Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog.h" />
<ClInclude Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog_native.h" />
<ClInclude Include="Emu\RSX\RSXDisAsm.h" />
<ClInclude Include="Emu\title.h" />
<ClInclude Include="Emu\system_config.h" />
<ClInclude Include="Emu\system_config_types.h" />

View File

@ -974,6 +974,9 @@
<ClCompile Include="Emu\Io\interception.cpp">
<Filter>Emu\Io</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\RSXDisAsm.cpp">
<Filter>Emu\GPU\RSX</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Crypto\aes.h">
@ -1564,9 +1567,6 @@
<ClInclude Include="Emu\RSX\rsx_cache.h">
<Filter>Emu\GPU\RSX</Filter>
</ClInclude>
<ClInclude Include="util\dyn_lib.hpp">
<Filter>Utilities</Filter>
</ClInclude>
<ClInclude Include="..\Utilities\version.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -1897,6 +1897,12 @@
<ClInclude Include="Emu\RSX\display.h">
<Filter>Emu\GPU\RSX</Filter>
</ClInclude>
<ClInclude Include="..\Utilities\dyn_lib.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\RSXDisAsm.h">
<Filter>Emu\GPU\RSX</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="Emu\RSX\Common\Interpreter\FragmentInterpreter.glsl">

View File

@ -23,9 +23,9 @@ breakpoint_list::breakpoint_list(QWidget* parent, breakpoint_handler* handler) :
/**
* It's unfortunate I need a method like this to sync these. Should ponder a cleaner way to do this.
*/
void breakpoint_list::UpdateCPUData(std::weak_ptr<cpu_thread> cpu, std::shared_ptr<CPUDisAsm> disasm)
void breakpoint_list::UpdateCPUData(cpu_thread* cpu, CPUDisAsm* disasm)
{
this->cpu = cpu;
m_cpu = cpu;
m_disasm = disasm;
}
@ -62,8 +62,6 @@ void breakpoint_list::AddBreakpoint(u32 pc)
{
m_breakpoint_handler->AddBreakpoint(pc);
const auto cpu = this->cpu.lock();
m_disasm->disasm(pc);
QString breakpointItemText = qstr(m_disasm->last_opcode);
@ -86,9 +84,7 @@ void breakpoint_list::AddBreakpoint(u32 pc)
*/
void breakpoint_list::HandleBreakpointRequest(u32 loc)
{
const auto cpu = this->cpu.lock();
if (!cpu || cpu->id_type() != 1 || !vm::check_addr(loc, vm::page_allocated | vm::page_executable))
if (!m_cpu || m_cpu->id_type() != 1 || !vm::check_addr(loc, vm::page_allocated | vm::page_executable))
{
// TODO: SPU breakpoints
return;

View File

@ -15,7 +15,7 @@ class breakpoint_list : public QListWidget
public:
breakpoint_list(QWidget* parent, breakpoint_handler* handler);
void UpdateCPUData(std::weak_ptr<cpu_thread> cpu, std::shared_ptr<CPUDisAsm> disasm);
void UpdateCPUData(cpu_thread* cpu, CPUDisAsm* disasm);
void ClearBreakpoints();
void AddBreakpoint(u32 addr);
void RemoveBreakpoint(u32 addr);
@ -33,6 +33,6 @@ private Q_SLOTS:
private:
breakpoint_handler* m_breakpoint_handler;
std::weak_ptr<cpu_thread> cpu;
std::shared_ptr<CPUDisAsm> m_disasm;
cpu_thread* m_cpu;
CPUDisAsm* m_disasm;
};

View File

@ -14,11 +14,6 @@ call_stack_list::call_stack_list(QWidget* parent) : QListWidget(parent)
connect(this, &QListWidget::itemDoubleClicked, this, &call_stack_list::OnCallStackListDoubleClicked);
}
void call_stack_list::UpdateCPUData(std::weak_ptr<cpu_thread> cpu, std::shared_ptr<CPUDisAsm> disasm)
{
this->cpu = cpu;
}
void call_stack_list::HandleUpdate(std::vector<std::pair<u32, u32>> call_stack)
{
clear();

View File

@ -15,7 +15,6 @@ class call_stack_list : public QListWidget
public:
call_stack_list(QWidget* parent);
void UpdateCPUData(std::weak_ptr<cpu_thread> cpu, std::shared_ptr<CPUDisAsm> disasm);
Q_SIGNALS:
void RequestShowAddress(u32 addr, bool force = false);
@ -23,6 +22,4 @@ public Q_SLOTS:
void HandleUpdate(std::vector<std::pair<u32, u32>> call_stack);
private Q_SLOTS:
void OnCallStackListDoubleClicked();
private:
std::weak_ptr<cpu_thread> cpu;
};

View File

@ -12,6 +12,7 @@
#include "Emu/System.h"
#include "Emu/IdManager.h"
#include "Emu/RSX/RSXThread.h"
#include "Emu/RSX/RSXDisAsm.h"
#include "Emu/Cell/PPUDisAsm.h"
#include "Emu/Cell/PPUThread.h"
#include "Emu/Cell/SPUDisAsm.h"
@ -42,7 +43,7 @@ debugger_frame::debugger_frame(std::shared_ptr<gui_settings> settings, QWidget *
EnableUpdateTimer(true);
m_mono = QFontDatabase::systemFont(QFontDatabase::FixedFont);
m_mono.setPointSize(10);
m_mono.setPointSize(9);
QVBoxLayout* vbox_p_main = new QVBoxLayout();
vbox_p_main->setContentsMargins(5, 5, 5, 5);
@ -137,7 +138,7 @@ debugger_frame::debugger_frame(std::shared_ptr<gui_settings> settings, QWidget *
connect(m_btn_run, &QAbstractButton::clicked, [this]()
{
if (const auto cpu = this->cpu.lock())
if (const auto cpu = get_cpu())
{
// Alter dbg_pause bit state (disable->enable, enable->disable)
const auto old = cpu->state.xor_fetch(cpu_flag::dbg_pause);
@ -229,7 +230,7 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
return;
}
const auto cpu = this->cpu.lock();
const auto cpu = get_cpu();
int i = m_debugger_list->currentRow();
switch (event->key())
@ -248,11 +249,13 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
"\nKey R: Registers Editor for selected thread."
"\nKey N: Show next instruction the thread will execute after marked instruction, does nothing if target is not predictable."
"\nKey M: Show the Memory Viewer with initial address pointing to the marked instruction."
"\nKey I: Show RSX method detail."
"\nKey F10: Perform single-stepping on instructions."
"\nKey F11: Perform step-over on instructions. (skip function calls)"
"\nKey F1: Show this help dialog."));
"\nKey F1: Show this help dialog."
"\nDouble-click: Set breakpoints."));
l->setFont([](QFont f) { f.setPointSize(9); return f; }(l->font()));
gui::utils::set_font_size(*l, 9);
QVBoxLayout* layout = new QVBoxLayout();
layout->addWidget(l);
@ -291,8 +294,11 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
{
case Qt::Key_E:
{
instruction_editor_dialog* dlg = new instruction_editor_dialog(this, pc, cpu, m_disasm.get());
dlg->show();
if (m_cpu)
{
instruction_editor_dialog* dlg = new instruction_editor_dialog(this, pc, m_cpu, m_disasm.get());
dlg->show();
}
return;
}
case Qt::Key_F:
@ -302,13 +308,16 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
return;
}
static_cast<spu_thread*>(cpu.get())->debugger_float_mode ^= 1; // Switch mode
static_cast<spu_thread*>(cpu)->debugger_float_mode ^= 1; // Switch mode
return;
}
case Qt::Key_R:
{
register_editor_dialog* dlg = new register_editor_dialog(this, cpu, m_disasm.get());
dlg->show();
if (m_cpu)
{
register_editor_dialog* dlg = new register_editor_dialog(this, m_cpu, m_disasm.get());
dlg->show();
}
return;
}
case Qt::Key_S:
@ -320,7 +329,7 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
return;
}
static_cast<spu_thread*>(cpu.get())->capture_local_storage();
static_cast<spu_thread*>(cpu)->capture_local_storage();
}
return;
}
@ -335,7 +344,7 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
{
case 2:
{
res = op_branch_targets(pc, spu_opcode_t{static_cast<spu_thread*>(cpu.get())->_ref<u32>(pc)});
res = op_branch_targets(pc, spu_opcode_t{static_cast<spu_thread*>(cpu)->_ref<u32>(pc)});
break;
}
case 1:
@ -358,7 +367,7 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
case Qt::Key_M:
{
// Memory viewer
idm::make<memory_viewer_handle>(this, pc, cpu);
if (m_cpu) idm::make<memory_viewer_handle>(this, pc, m_cpu);
return;
}
case Qt::Key_F10:
@ -375,8 +384,17 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
}
}
rsx::thread* debugger_frame::get_rsx()
cpu_thread* debugger_frame::get_cpu()
{
// Wait flag is raised by the thread itself, acknowledging exit
if (m_cpu)
{
if (+m_cpu->state + cpu_flag::wait + cpu_flag::exit != +m_cpu->state)
{
return m_cpu.get();
}
}
// m_rsx is raw pointer, when emulation is stopped it won't be cleared
// Therefore need to do invalidation checks manually
@ -397,10 +415,9 @@ void debugger_frame::UpdateUI()
{
UpdateUnitList();
const auto cpu = this->cpu.lock();
const auto rsx = this->get_rsx();
const auto cpu = get_cpu();
if (!cpu && !rsx)
if (!cpu)
{
if (m_last_pc != umax || !m_last_query_state.empty())
{
@ -409,25 +426,17 @@ void debugger_frame::UpdateUI()
DoUpdate();
}
}
else if (rsx)
{
if (m_last_pc != rsx->ctrl->get || !m_last_query_state.empty())
{
m_last_query_state.clear();
m_last_pc = rsx->ctrl->get;
DoUpdate();
}
}
else
{
const auto cia = cpu->get_pc();
const auto size_context = cpu->id_type() == 1 ? sizeof(ppu_thread) : sizeof(spu_thread);
const auto size_context = cpu->id_type() == 1 ? sizeof(ppu_thread) :
cpu->id_type() == 2 ? sizeof(spu_thread) : sizeof(cpu_thread);
if (m_last_pc != cia || m_last_query_state.size() != size_context || std::memcmp(m_last_query_state.data(), cpu.get(), size_context))
if (m_last_pc != cia || m_last_query_state.size() != size_context || std::memcmp(m_last_query_state.data(), cpu, size_context))
{
// Copy thread data
m_last_query_state.resize(size_context);
std::memcpy(m_last_query_state.data(), cpu.get(), size_context);
std::memcpy(m_last_query_state.data(), cpu, size_context);
m_last_pc = cia;
DoUpdate();
@ -455,11 +464,13 @@ void debugger_frame::UpdateUnitList()
{
const u64 threads_created = cpu_thread::g_threads_created;
const u64 threads_deleted = cpu_thread::g_threads_deleted;
const system_state emu_state = Emu.GetStatus();
if (threads_created != m_threads_created || threads_deleted != m_threads_deleted)
if (threads_created != m_threads_created || threads_deleted != m_threads_deleted || emu_state != m_emu_state)
{
m_threads_created = threads_created;
m_threads_deleted = threads_deleted;
m_emu_state = emu_state;
}
else
{
@ -472,6 +483,8 @@ void debugger_frame::UpdateUnitList()
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));
@ -488,7 +501,7 @@ void debugger_frame::UpdateUnitList()
idm::select<named_thread<ppu_thread>>(on_select);
idm::select<named_thread<spu_thread>>(on_select);
if (auto render = g_fxo->get<rsx::thread>(); render && render->ctrl)
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);
@ -503,24 +516,34 @@ void debugger_frame::UpdateUnitList()
void debugger_frame::OnSelectUnit()
{
if (m_choice_units->count() < 1) return;
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*>();
if (!render && !weak.owner_before(cpu) && !cpu.owner_before(weak))
if (m_emu_state != system_state::stopped)
{
// They match, nothing to do.
return;
}
if (!render && !weak.owner_before(m_cpu) && !m_cpu.owner_before(weak))
{
// They match, nothing to do.
return;
}
if (render && render == this->get_rsx())
{
return;
if (render && render == get_cpu())
{
return;
}
}
m_disasm.reset();
cpu.reset();
m_cpu.reset();
m_rsx = nullptr;
if (!weak.expired())
@ -531,16 +554,16 @@ void debugger_frame::OnSelectUnit()
{
if (cpu0.get() == idm::check<named_thread<ppu_thread>>(cpu0->id))
{
cpu = cpu0;
m_disasm = std::make_unique<PPUDisAsm>(CPUDisAsm_InterpreterMode, vm::g_sudo_addr);
m_cpu = cpu0;
m_disasm = std::make_shared<PPUDisAsm>(cpu_disasm_mode::interpreter, vm::g_sudo_addr);
}
}
else if (cpu0->id_type() == 2)
{
if (cpu0.get() == idm::check<named_thread<spu_thread>>(cpu0->id))
{
cpu = cpu0;
m_disasm = std::make_unique<SPUDisAsm>(CPUDisAsm_InterpreterMode, static_cast<const spu_thread*>(cpu0.get())->ls);
m_cpu = cpu0;
m_disasm = std::make_shared<SPUDisAsm>(cpu_disasm_mode::interpreter, static_cast<const spu_thread*>(cpu0.get())->ls);
}
}
}
@ -548,13 +571,17 @@ void debugger_frame::OnSelectUnit()
else
{
m_rsx = render;
if (get_cpu())
{
m_disasm = std::make_shared<RSXDisAsm>(cpu_disasm_mode::interpreter, vm::g_sudo_addr, m_rsx);
}
}
EnableButtons(true);
m_debugger_list->UpdateCPUData(this->cpu, m_disasm);
m_breakpoint_list->UpdateCPUData(this->cpu, m_disasm);
m_call_stack_list->UpdateCPUData(this->cpu, m_disasm);
m_debugger_list->UpdateCPUData(get_cpu(), m_disasm.get());
m_breakpoint_list->UpdateCPUData(get_cpu(), m_disasm.get());
DoUpdate();
UpdateUI();
}
@ -562,7 +589,7 @@ void debugger_frame::OnSelectUnit()
void debugger_frame::DoUpdate()
{
// Check if we need to disable a step over bp
if (auto cpu0 = cpu.lock(); cpu0 && m_last_step_over_breakpoint != umax && cpu0->get_pc() == m_last_step_over_breakpoint)
if (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_last_step_over_breakpoint = -1;
@ -574,10 +601,9 @@ void debugger_frame::DoUpdate()
void debugger_frame::WritePanels()
{
const auto cpu = this->cpu.lock();
const auto rsx = this->get_rsx();
const auto cpu = get_cpu();
if (!cpu && !rsx)
if (!cpu)
{
m_misc_state->clear();
m_regs->clear();
@ -588,15 +614,15 @@ void debugger_frame::WritePanels()
loc = m_misc_state->verticalScrollBar()->value();
m_misc_state->clear();
m_misc_state->setText(qstr(rsx ? "" : cpu->dump_misc()));
m_misc_state->setText(qstr(cpu->dump_misc()));
m_misc_state->verticalScrollBar()->setValue(loc);
loc = m_regs->verticalScrollBar()->value();
m_regs->clear();
m_regs->setText(qstr(rsx ? rsx->dump_regs() : cpu->dump_regs()));
m_regs->setText(qstr(cpu->dump_regs()));
m_regs->verticalScrollBar()->setValue(loc);
Q_EMIT CallStackUpdateRequested(rsx ? rsx->dump_callstack() : cpu->dump_callstack_list());
Q_EMIT CallStackUpdateRequested(cpu->dump_callstack_list());
}
void debugger_frame::ShowGotoAddressDialog()
@ -615,7 +641,7 @@ void debugger_frame::ShowGotoAddressDialog()
expression_input->setFont(m_mono);
expression_input->setMaxLength(18);
if (auto thread = cpu.lock(); !thread || thread->id_type() != 2)
if (auto thread = get_cpu(); !thread || thread->id_type() != 2)
{
expression_input->setValidator(new QRegExpValidator(QRegExp("^(0[xX])?0*[a-fA-F0-9]{0,8}$")));
}
@ -639,7 +665,7 @@ void debugger_frame::ShowGotoAddressDialog()
dlg->setLayout(vbox_panel);
const auto cpu = this->cpu.lock();
const auto cpu = get_cpu();
const QFont font = expression_input->font();
// -1 from get_pc() turns into 0
@ -670,8 +696,7 @@ u64 debugger_frame::EvaluateExpression(const QString& expression)
const u64 res = static_cast<u64>(fixed_expression.toULong(&ok, 16));
if (ok) return res;
if (auto thread = get_rsx()) return thread->ctrl->get;
if (auto thread = cpu.lock()) return thread->get_pc();
if (auto thread = get_cpu()) return thread->get_pc();
return 0;
}
@ -687,17 +712,16 @@ void debugger_frame::ClearCallStack()
void debugger_frame::ShowPC()
{
const auto cpu0 = cpu.lock();
const auto cpu0 = get_cpu();
const u32 pc = get_rsx() ? +m_rsx->ctrl->get
: (cpu0 ? cpu0->get_pc() : 0);
const u32 pc = (cpu0 ? cpu0->get_pc() : 0);
m_debugger_list->ShowAddress(pc);
}
void debugger_frame::DoStep(bool stepOver)
{
if (const auto cpu = this->cpu.lock())
if (const auto cpu = get_cpu())
{
bool should_step_over = stepOver && cpu->id_type() == 1;
@ -741,7 +765,7 @@ void debugger_frame::EnableUpdateTimer(bool enable)
void debugger_frame::EnableButtons(bool enable)
{
if (cpu.expired()) enable = false;
if (!get_cpu()) enable = false;
m_go_to_addr->setEnabled(enable);
m_go_to_pc->setEnabled(enable);

View File

@ -25,6 +25,8 @@ namespace rsx
class thread;
}
enum class system_state : u32;
class debugger_frame : public custom_dock_widget
{
Q_OBJECT
@ -49,12 +51,13 @@ class debugger_frame : public custom_dock_widget
u64 m_threads_created = -1;
u64 m_threads_deleted = -1;
system_state m_emu_state{};
u32 m_last_pc = -1;
std::vector<char> m_last_query_state;
u32 m_last_step_over_breakpoint = -1;
std::shared_ptr<CPUDisAsm> m_disasm;
std::weak_ptr<cpu_thread> cpu;
std::shared_ptr<CPUDisAsm> m_disasm; // Only shared to allow base/derived functionality
std::shared_ptr<cpu_thread> m_cpu;
rsx::thread* m_rsx = nullptr;
breakpoint_list* m_breakpoint_list;
@ -64,7 +67,7 @@ class debugger_frame : public custom_dock_widget
std::shared_ptr<gui_settings> xgui_settings;
rsx::thread* get_rsx(); // Do not read m_rsx directy, use this instead.
cpu_thread* get_cpu();
public:
explicit debugger_frame(std::shared_ptr<gui_settings> settings, QWidget *parent = 0);

View File

@ -1,15 +1,19 @@
#include "debugger_list.h"
#include "gui_settings.h"
#include "qt_utils.h"
#include "breakpoint_handler.h"
#include "Emu/Cell/SPUThread.h"
#include "Emu/Cell/PPUThread.h"
#include "Emu/CPU/CPUDisAsm.h"
#include "Emu/CPU/CPUThread.h"
#include "Emu/RSX/RSXDisAsm.h"
#include "Emu/System.h"
#include <QMouseEvent>
#include <QWheelEvent>
#include <QVBoxLayout>
#include <QLabel>
#include <memory>
@ -28,9 +32,9 @@ debugger_list::debugger_list(QWidget* parent, std::shared_ptr<gui_settings> sett
setSizeAdjustPolicy(QListWidget::AdjustToContents);
}
void debugger_list::UpdateCPUData(std::weak_ptr<cpu_thread> cpu, std::shared_ptr<CPUDisAsm> disasm)
void debugger_list::UpdateCPUData(cpu_thread* cpu, CPUDisAsm* disasm)
{
this->cpu = cpu;
m_cpu = cpu;
m_disasm = disasm;
}
@ -43,14 +47,21 @@ void debugger_list::ShowAddress(u32 addr, bool force)
{
auto IsBreakpoint = [this](u32 pc)
{
return m_breakpoint_handler->HasBreakpoint(pc);
return m_cpu && m_cpu->id_type() == 1 && m_breakpoint_handler->HasBreakpoint(pc);
};
const bool center_pc = xgui_settings->GetValue(gui::d_centerPC).toBool();
bool center_pc = xgui_settings->GetValue(gui::d_centerPC).toBool();
// How many spaces addr can move down without us needing to move the entire view
const u32 addr_margin = (m_item_count / (center_pc ? 2 : 1) - 4); // 4 is just a buffer of 4 spaces at the bottom
if (m_cpu && m_cpu->id_type() == 0x55)
{
// RSX instructions' size is not consistent, this is the only valid mode for it
force = true;
center_pc = false;
}
if (force || addr - m_pc > addr_margin * 4) // 4 is the number of bytes in each instruction
{
if (center_pc)
@ -63,12 +74,10 @@ void debugger_list::ShowAddress(u32 addr, bool force)
}
}
const auto cpu = this->cpu.lock();
const auto default_foreground = palette().color(foregroundRole());
const auto default_background = palette().color(backgroundRole());
if (!cpu || !m_disasm)
if (!m_cpu || !m_disasm || +m_cpu->state + cpu_flag::exit + cpu_flag::wait == +m_cpu->state)
{
for (uint i = 0; i < m_item_count; ++i)
{
@ -79,14 +88,14 @@ void debugger_list::ShowAddress(u32 addr, bool force)
}
else
{
const bool is_spu = cpu->id_type() != 1;
const bool is_spu = m_cpu->id_type() == 2;
const u32 address_limits = (is_spu ? 0x3fffc : ~3);
m_pc &= address_limits;
u32 pc = m_pc;
for (uint i = 0, count = 4; i<m_item_count; ++i, pc = (pc + count) & address_limits)
{
if (cpu->is_paused() && pc == cpu->get_pc())
if (m_cpu->is_paused() && pc == m_cpu->get_pc())
{
item(i)->setForeground(m_text_color_pc);
item(i)->setBackground(m_color_pc);
@ -102,14 +111,14 @@ void debugger_list::ShowAddress(u32 addr, bool force)
item(i)->setBackground(default_background);
}
if (!is_spu && !vm::check_addr(pc))
if (m_cpu->id_type() == 1 && !vm::check_addr(pc))
{
item(i)->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(fmt::format("[%08x] ?? ?? ?? ??:", pc)));
count = 4;
continue;
}
if (!is_spu && !vm::check_addr(pc, vm::page_executable))
if (m_cpu->id_type() == 1 && !vm::check_addr(pc, vm::page_executable))
{
const u32 data = *vm::get_super_ptr<atomic_be_t<u32>>(pc);
item(i)->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(fmt::format("[%08x] %02x %02x %02x %02x:", pc,
@ -123,6 +132,13 @@ void debugger_list::ShowAddress(u32 addr, bool force)
count = m_disasm->disasm(pc);
if (!count)
{
item(i)->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(fmt::format("[%08x] ??? ?? ??", pc)));
count = 4;
continue;
}
item(i)->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(m_disasm->last_opcode));
}
}
@ -130,6 +146,19 @@ void debugger_list::ShowAddress(u32 addr, bool force)
setLineWidth(-1);
}
void debugger_list::scroll(s32 steps)
{
while (m_cpu && m_cpu->id_type() == 0x55 && steps > 0)
{
// If scrolling forwards (downwards), we can skip entire commands
// Backwards is impossible though
m_pc += std::max<u32>(m_disasm->disasm(m_pc), 4);
steps--;
}
ShowAddress(m_pc + (steps * 4), true);
}
void debugger_list::keyPressEvent(QKeyEvent* event)
{
if (!isActiveWindow())
@ -139,14 +168,84 @@ void debugger_list::keyPressEvent(QKeyEvent* event)
switch (event->key())
{
case Qt::Key_PageUp: ShowAddress(m_pc - (m_item_count * 4), true); return;
case Qt::Key_PageDown: ShowAddress(m_pc + (m_item_count * 4), true); return;
case Qt::Key_Up: ShowAddress(m_pc - 4, true); return;
case Qt::Key_Down: ShowAddress(m_pc + 4, true); return;
case Qt::Key_PageUp: scroll(0 - m_item_count); return;
case Qt::Key_PageDown: scroll(m_item_count); return;
case Qt::Key_Up: scroll(1); return;
case Qt::Key_Down: scroll(-1); return;
case Qt::Key_I:
{
if (m_cpu && m_cpu->id_type() == 0x55)
{
create_rsx_command_detail(m_pc, currentRow());
return;
}
return;
}
default: break;
}
}
void debugger_list::showEvent(QShowEvent* event)
{
if (m_cmd_detail) m_cmd_detail->show();
QListWidget::showEvent(event);
}
void debugger_list::hideEvent(QHideEvent* event)
{
if (m_cmd_detail) m_cmd_detail->hide();
QListWidget::hideEvent(event);
}
void debugger_list::create_rsx_command_detail(u32 pc, int row)
{
if (row < 0)
{
return;
}
for (; row > 0; row--)
{
// Skip methods
pc += std::max<u32>(m_disasm->disasm(pc), 4);
}
RSXDisAsm rsx_dis = CPUDisAsm::copy_and_change_mode(*static_cast<RSXDisAsm*>(m_disasm), cpu_disasm_mode::list);
// Either invalid or not a method
if (rsx_dis.disasm(pc) <= 4) return;
if (m_cmd_detail)
{
// Edit the existing dialog
m_detail_label->setText(QString::fromStdString(rsx_dis.last_opcode));
m_cmd_detail->setFixedSize(m_cmd_detail->sizeHint());
return;
}
m_cmd_detail = new QDialog(this);
m_cmd_detail->setWindowTitle(tr("RSX Command Detail"));
m_detail_label = new QLabel(QString::fromStdString(rsx_dis.last_opcode), this);
m_detail_label->setFont(font());
gui::utils::set_font_size(*m_detail_label, 10);
m_detail_label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
QVBoxLayout* layout = new QVBoxLayout(this);
layout->addWidget(m_detail_label);
m_cmd_detail->setLayout(layout);
m_cmd_detail->setFixedSize(m_cmd_detail->sizeHint());
m_cmd_detail->show();
connect(m_cmd_detail, &QDialog::finished, [this](int)
{
// Cleanup
std::exchange(m_cmd_detail, nullptr)->deleteLater();
});
}
void debugger_list::mouseDoubleClickEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton)
@ -169,7 +268,7 @@ void debugger_list::wheelEvent(QWheelEvent* event)
const int value = numSteps.y();
const auto direction = (event->modifiers() == Qt::ControlModifier);
ShowAddress(m_pc + (direction ? value : -value) * 4, true);
scroll(direction ? value : -value);
}
void debugger_list::resizeEvent(QResizeEvent* event)

View File

@ -10,6 +10,7 @@ class breakpoint_handler;
class CPUDisAsm;
class cpu_thread;
class gui_settings;
class QLabel;
class debugger_list : public QListWidget
{
@ -27,7 +28,7 @@ Q_SIGNALS:
void BreakpointRequested(u32 loc);
public:
debugger_list(QWidget* parent, std::shared_ptr<gui_settings> settings, breakpoint_handler* handler);
void UpdateCPUData(std::weak_ptr<cpu_thread> cpu, std::shared_ptr<CPUDisAsm> disasm);
void UpdateCPUData(cpu_thread* cpu, CPUDisAsm* disasm);
public Q_SLOTS:
void ShowAddress(u32 addr, bool force = false);
protected:
@ -35,6 +36,10 @@ protected:
void mouseDoubleClickEvent(QMouseEvent* event) override;
void wheelEvent(QWheelEvent* event) override;
void resizeEvent(QResizeEvent* event) override;
void showEvent(QShowEvent* event) override;
void hideEvent(QHideEvent* event) override;
void scroll(s32 steps);
void create_rsx_command_detail(u32 pc, int row);
private:
/**
* It really upsetted me I had to copy this code to make debugger_list/frame not circularly dependent.
@ -44,6 +49,8 @@ private:
std::shared_ptr<gui_settings> xgui_settings;
breakpoint_handler* m_breakpoint_handler;
std::weak_ptr<cpu_thread> cpu;
std::shared_ptr<CPUDisAsm> m_disasm;
cpu_thread* m_cpu = nullptr;
CPUDisAsm* m_disasm;
QDialog* m_cmd_detail = nullptr;
QLabel* m_detail_label = nullptr;
};

View File

@ -17,14 +17,13 @@ instruction_editor_dialog::instruction_editor_dialog(QWidget *parent, u32 _pc, c
: QDialog(parent)
, m_pc(_pc)
, m_disasm(_disasm)
, cpu(_cpu)
, m_cpu(_cpu)
{
setWindowTitle(tr("Edit instruction"));
setAttribute(Qt::WA_DeleteOnClose);
setMinimumSize(300, sizeHint().height());
const auto cpu = _cpu.get();
m_cpu_offset = cpu->id_type() == 2 ? static_cast<spu_thread&>(*cpu).ls : vm::g_sudo_addr;
m_cpu_offset = m_cpu->id_type() == 2 ? static_cast<spu_thread&>(*m_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());
@ -81,7 +80,7 @@ instruction_editor_dialog::instruction_editor_dialog(QWidget *parent, u32 _pc, c
QMessageBox::critical(this, tr("Error"), tr("Failed to parse PPU instruction."));
return;
}
else if (cpu->id_type() == 1)
else if (m_cpu->id_type() == 1)
{
if (!ppu_patch(m_pc, static_cast<u32>(opcode)))
{

View File

@ -23,7 +23,7 @@ private:
QLabel* m_preview;
public:
std::weak_ptr<cpu_thread> cpu;
std::shared_ptr<cpu_thread> m_cpu;
instruction_editor_dialog(QWidget *parent, u32 _pc, const std::shared_ptr<cpu_thread>& _cpu, CPUDisAsm* _disasm);

View File

@ -1261,9 +1261,7 @@ void main_window::OnEmuStop()
const QString play_tooltip = Emu.IsReady() ? tr("Play %0").arg(title) : tr("Resume %0").arg(title);
const QString restart_tooltip = tr("Restart %0").arg(title);
m_debugger_frame->EnableButtons(false);
m_debugger_frame->ClearBreakpoints();
m_debugger_frame->ClearCallStack();
m_debugger_frame->UpdateUI();
ui->sysPauseAct->setText(Emu.IsReady() ? tr("&Play\tCtrl+E") : tr("&Resume\tCtrl+E"));
ui->sysPauseAct->setIcon(m_icon_play);

View File

@ -49,6 +49,14 @@ namespace gui
// Returns the width of the text
int get_label_width(const QString& text, const QFont* font = nullptr);
template <typename T>
void set_font_size(T& qobj, int size)
{
QFont font = qobj.font();
font.setPointSize(size);
qobj.setFont(font);
}
// Returns the part of the image loaded from path that is inside the bounding box of its opaque areas
QImage get_opaque_image_area(const QString& path);

View File

@ -61,7 +61,7 @@ enum registers : int
register_editor_dialog::register_editor_dialog(QWidget *parent, const std::shared_ptr<cpu_thread>& _cpu, CPUDisAsm* _disasm)
: QDialog(parent)
, m_disasm(_disasm)
, cpu(_cpu)
, m_cpu(_cpu)
{
setWindowTitle(tr("Edit registers"));
setAttribute(Qt::WA_DeleteOnClose);
@ -97,7 +97,7 @@ register_editor_dialog::register_editor_dialog(QWidget *parent, const std::share
if (1)
{
if (_cpu->id_type() == 1)
if (m_cpu->id_type() == 1)
{
for (int i = ppu_r0; i <= ppu_r31; i++) m_register_combo->addItem(qstr(fmt::format("r%d", i % 32)), i);
for (int i = ppu_f0; i <= ppu_f31; i++) m_register_combo->addItem(qstr(fmt::format("f%d", i % 32)), i);
@ -113,7 +113,7 @@ register_editor_dialog::register_editor_dialog(QWidget *parent, const std::share
m_register_combo->addItem("Priority", +PPU_PRIO);
//m_register_combo->addItem("Priority 2", +PPU_PRIO2);
}
else if (_cpu->id_type() == 2)
else if (m_cpu->id_type() == 2)
{
for (int i = spu_r0; i <= spu_r127; i++) m_register_combo->addItem(qstr(fmt::format("r%d", i % 128)), i);
m_register_combo->addItem("MFC Pending Events", +MFC_PEVENTS);
@ -144,7 +144,7 @@ register_editor_dialog::register_editor_dialog(QWidget *parent, const std::share
setModal(true);
// Events
connect(button_ok, &QAbstractButton::clicked, this, [=, this](){OnOkay(_cpu); accept();});
connect(button_ok, &QAbstractButton::clicked, this, [this](){ OnOkay(); accept(); });
connect(button_cancel, &QAbstractButton::clicked, this, &register_editor_dialog::reject);
connect(m_register_combo, &QComboBox::currentTextChanged, this, [this](const QString&)
{
@ -159,15 +159,14 @@ register_editor_dialog::register_editor_dialog(QWidget *parent, const std::share
void register_editor_dialog::updateRegister(int reg)
{
const auto cpu = this->cpu.lock();
std::string str = sstr(tr("Error parsing register value!"));
if (!cpu)
if (!m_cpu)
{
}
else if (cpu->id_type() == 1)
else if (m_cpu->id_type() == 1)
{
const auto& ppu = *static_cast<const ppu_thread*>(cpu.get());
const auto& ppu = *static_cast<const ppu_thread*>(m_cpu.get());
if (reg >= ppu_r0 && reg <= ppu_v31)
{
@ -190,9 +189,9 @@ void register_editor_dialog::updateRegister(int reg)
else if (reg == RESERVATION_LOST) str = sstr(ppu.raddr ? tr("Lose reservation on OK") : tr("Reservation is inactive"));
else if (reg == PC) str = fmt::format("%08x", ppu.cia);
}
else if (cpu->id_type() == 2)
else if (m_cpu->id_type() == 2)
{
const auto& spu = *static_cast<const spu_thread*>(cpu.get());
const auto& spu = *static_cast<const spu_thread*>(m_cpu.get());
if (reg >= spu_r0 && reg <= spu_r127)
{
@ -216,10 +215,8 @@ void register_editor_dialog::updateRegister(int reg)
m_value_line->setText(qstr(str));
}
void register_editor_dialog::OnOkay(const std::shared_ptr<cpu_thread>& _cpu)
void register_editor_dialog::OnOkay()
{
const auto cpu = _cpu.get();
const int reg = m_register_combo->currentData().toInt();
std::string value = sstr(m_value_line->text());
@ -247,12 +244,12 @@ void register_editor_dialog::OnOkay(const std::shared_ptr<cpu_thread>& _cpu)
}
}
if (!cpu || value.empty())
if (!m_cpu || value.empty())
{
}
else if (cpu->id_type() == 1)
else if (m_cpu->id_type() == 1)
{
auto& ppu = *static_cast<ppu_thread*>(cpu);
auto& ppu = *static_cast<ppu_thread*>(m_cpu.get());
if (reg >= ppu_r0 && reg <= ppu_v31)
{
@ -337,9 +334,9 @@ void register_editor_dialog::OnOkay(const std::shared_ptr<cpu_thread>& _cpu)
return;
}
}
else if (cpu->id_type() == 2)
else if (m_cpu->id_type() == 2)
{
auto& spu = *static_cast<spu_thread*>(cpu);
auto& spu = *static_cast<spu_thread*>(m_cpu.get());
if (reg >= spu_r0 && reg <= spu_r127)
{

View File

@ -19,14 +19,13 @@ class register_editor_dialog : public QDialog
QComboBox* m_register_combo;
QLineEdit* m_value_line;
public:
std::weak_ptr<cpu_thread> cpu;
public:
register_editor_dialog(QWidget *parent, const std::shared_ptr<cpu_thread>& _cpu, CPUDisAsm* _disasm);
private:
void OnOkay(const std::shared_ptr<cpu_thread>& _cpu);
void OnOkay();
std::shared_ptr<cpu_thread> m_cpu;
private Q_SLOTS:
void updateRegister(int reg);