Debugger: Implement code flow tracking

This commit is contained in:
Eladash 2020-11-23 19:57:34 +02:00 committed by Ivan
parent 427cf91447
commit 15a12afe25
6 changed files with 100 additions and 1 deletions

View file

@ -72,6 +72,8 @@ constexpr u32 ppu_decode(u32 inst)
return ((inst >> 26) | (inst << 6)) & 0x1ffff; // Rotate + mask
}
std::array<u32, 2> op_branch_targets(u32 pc, ppu_opcode_t op);
// PPU decoder object. D provides functions. T is function pointer type returned.
template <typename D, typename T = decltype(&D::UNK)>
class ppu_decoder

View file

@ -422,6 +422,31 @@ extern bool ppu_patch(u32 addr, u32 value)
return true;
}
std::array<u32, 2> op_branch_targets(u32 pc, ppu_opcode_t op)
{
std::array<u32, 2> res{pc + 4, UINT32_MAX};
switch (const auto type = g_ppu_itype.decode(op.opcode))
{
case ppu_itype::B:
case ppu_itype::BC:
{
res[type == ppu_itype::BC ? 1 : 0] = ((op.aa ? 0 : pc) + (type == ppu_itype::B ? +op.bt24 : +op.bt14));
break;
}
case ppu_itype::BCCTR:
case ppu_itype::BCLR:
case ppu_itype::UNK:
{
res[0] = UINT32_MAX;
break;
}
default: break;
}
return res;
}
std::string ppu_thread::dump_all() const
{
std::string ret = cpu_thread::dump_misc();

View file

@ -41,6 +41,8 @@ static u32 spu_decode(u32 inst)
return inst >> 21;
}
std::array<u32, 2> op_branch_targets(u32 pc, spu_opcode_t op);
// SPU decoder object. D provides functions. T is function pointer type returned.
template <typename D, typename T = decltype(&D::UNK)>
class spu_decoder

View file

@ -373,6 +373,45 @@ namespace spu
}
}
std::array<u32, 2> op_branch_targets(u32 pc, spu_opcode_t op)
{
std::array<u32, 2> res{spu_branch_target(pc + 4), UINT32_MAX};
switch (const auto type = s_spu_itype.decode(op.opcode))
{
case spu_itype::BR:
case spu_itype::BRA:
case spu_itype::BRNZ:
case spu_itype::BRZ:
case spu_itype::BRHNZ:
case spu_itype::BRHZ:
case spu_itype::BRSL:
case spu_itype::BRASL:
{
const int index = (type == spu_itype::BR || type == spu_itype::BRA || type == spu_itype::BRSL || type == spu_itype::BRASL ? 0 : 1);
res[index] = (spu_branch_target(type == spu_itype::BRASL || type == spu_itype::BRA ? 0 : pc, op.i16));
break;
}
case spu_itype::IRET:
case spu_itype::BI:
case spu_itype::BISLED:
case spu_itype::BISL:
case spu_itype::BIZ:
case spu_itype::BINZ:
case spu_itype::BIHZ:
case spu_itype::BIHNZ: // TODO (detect constant address branches, such as for interrupts enable/disable pattern)
case spu_itype::UNK:
{
res[0] = UINT32_MAX;
break;
}
default: break;
}
return res;
}
const auto spu_putllc_tx = build_function_asm<u64(*)(u32 raddr, u64 rtime, void* _old, const void* _new)>([](asmjit::X86Assembler& c, auto& args)
{
using namespace asmjit;

View file

@ -283,6 +283,37 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
}
return;
}
case Qt::Key_N:
{
// Next instruction according to code flow
// Known branch targets are selected over next PC for conditional branches
// Indirect branches (unknown targets, such as function return) do not proceed to any instruction
std::array<u32, 2> res{UINT32_MAX, UINT32_MAX};
switch (cpu->id_type())
{
case 2:
{
res = op_branch_targets(pc, spu_opcode_t{static_cast<spu_thread*>(cpu.get())->_ref<u32>(pc)});
break;
}
case 1:
{
be_t<ppu_opcode_t> op{};
if (vm::try_access(pc, &op, 4, false))
res = op_branch_targets(pc, op);
break;
}
default: break;
}
if (auto pos = std::basic_string_view<u32>(res.data(), 2).find_last_not_of(UINT32_MAX); pos != umax)
m_debugger_list->ShowAddress(res[pos] - std::max(i, 0) * 4, true);
return;
}
case Qt::Key_F10:
{
DoStep(true);

View file

@ -134,7 +134,7 @@ void debugger_list::ShowAddress(u32 addr, bool force)
void debugger_list::keyPressEvent(QKeyEvent* event)
{
if (!isActiveWindow() || currentRow() < 0 || !this->cpu.lock())
if (!isActiveWindow())
{
return;
}