From 20eb4352fbc4099848b52c4168041b9c670b489f Mon Sep 17 00:00:00 2001 From: Eladash Date: Fri, 19 Feb 2021 13:53:09 +0200 Subject: [PATCH] debugger: Fix single stepping (#9793) --- rpcs3/Emu/CPU/CPUThread.cpp | 51 +++++++++++++++++++++++++++----- rpcs3/Emu/CPU/CPUThread.h | 1 + rpcs3/Emu/Cell/PPUThread.cpp | 2 +- rpcs3/Emu/Cell/PPUThread.h | 2 ++ rpcs3/Emu/Cell/SPUThread.h | 1 + rpcs3/Emu/RSX/RSXThread.h | 1 + rpcs3/rpcs3qt/debugger_frame.cpp | 11 +++++-- 7 files changed, 58 insertions(+), 11 deletions(-) diff --git a/rpcs3/Emu/CPU/CPUThread.cpp b/rpcs3/Emu/CPU/CPUThread.cpp index f9bd141a1a..b6e67837a2 100644 --- a/rpcs3/Emu/CPU/CPUThread.cpp +++ b/rpcs3/Emu/CPU/CPUThread.cpp @@ -667,6 +667,26 @@ bool cpu_thread::check_state() noexcept store = true; } + // Can't process dbg_step if we only paused temporarily + if (cpu_can_stop && flags & cpu_flag::dbg_step) + { + if (u32 pc = get_pc(), *pc2 = get_pc2(); pc != umax && pc2) + { + if (pc != *pc2) + { + flags -= cpu_flag::dbg_step; + flags += cpu_flag::dbg_pause; + store = true; + } + } + else + { + // Can't test, ignore flag + flags -= cpu_flag::dbg_step; + store = true; + } + } + // Atomically clean wait flag and escape if (!(flags & (cpu_flag::exit + cpu_flag::ret + cpu_flag::stop))) { @@ -703,14 +723,6 @@ bool cpu_thread::check_state() noexcept retval = cpu_can_stop; } - if (cpu_can_stop && flags & cpu_flag::dbg_step) - { - // Can't process dbg_step if we only paused temporarily - flags += cpu_flag::dbg_pause; - flags -= cpu_flag::dbg_step; - store = true; - } - escape = true; state1 = flags; return store; @@ -876,6 +888,29 @@ u32 cpu_thread::get_pc() const return pc ? atomic_storage::load(*pc) : UINT32_MAX; } +u32* cpu_thread::get_pc2() +{ + switch (id_type()) + { + case 1: + { + return &static_cast(this)->dbg_step_pc; + } + case 2: + { + return &static_cast(this)->dbg_step_pc; + } + case 0x55: + { + const auto ctrl = static_cast(this)->ctrl; + return ctrl ? &static_cast(this)->dbg_step_pc : nullptr; + } + default: break; + } + + return nullptr; +} + std::string cpu_thread::dump_all() const { std::string ret = cpu_thread::dump_misc(); diff --git a/rpcs3/Emu/CPU/CPUThread.h b/rpcs3/Emu/CPU/CPUThread.h index 96301df6ee..087c35f944 100644 --- a/rpcs3/Emu/CPU/CPUThread.h +++ b/rpcs3/Emu/CPU/CPUThread.h @@ -105,6 +105,7 @@ public: } u32 get_pc() const; + u32* get_pc2(); // Last PC before stepping for the debugger (may be null) void notify(); diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 973a2d75fd..ab0ace64c4 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -455,7 +455,7 @@ extern void ppu_register_function_at(u32 addr, u32 size, ppu_function_t ptr) static bool ppu_break(ppu_thread& ppu, ppu_opcode_t op) { // Pause - ppu.state += cpu_flag::dbg_pause; + ppu.state.atomic_op([](bs_t& state) { if (!(state & cpu_flag::dbg_step)) state += cpu_flag::dbg_pause; }); if (ppu.check_state()) { diff --git a/rpcs3/Emu/Cell/PPUThread.h b/rpcs3/Emu/Cell/PPUThread.h index 8a9c85b951..b2d806d6a6 100644 --- a/rpcs3/Emu/Cell/PPUThread.h +++ b/rpcs3/Emu/Cell/PPUThread.h @@ -277,6 +277,8 @@ public: u64 last_fail = 0; u64 last_succ = 0; + u32 dbg_step_pc = 0; + be_t* get_stack_arg(s32 i, u64 align = alignof(u64)); void exec_task(); void fast_call(u32 addr, u32 rtoc); diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index a5f331622c..8579e43c5b 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -643,6 +643,7 @@ public: spu_thread(lv2_spu_group* group, u32 index, std::string_view name, u32 lv2_id, bool is_isolated = false, u32 option = 0); u32 pc = 0; + u32 dbg_step_pc = 0; // May be used internally by recompilers. u32 base_pc = 0; diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index ad8842cf29..ea059e9ed1 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -648,6 +648,7 @@ namespace rsx u32 dma_address{0}; rsx_iomap_table iomap_table; u32 restore_point = 0; + u32 dbg_step_pc = 0; atomic_t external_interrupt_lock{ 0 }; atomic_t external_interrupt_ack{ false }; bool is_fifo_idle() const; diff --git a/rpcs3/rpcs3qt/debugger_frame.cpp b/rpcs3/rpcs3qt/debugger_frame.cpp index 4348682e1a..02bd401df9 100644 --- a/rpcs3/rpcs3qt/debugger_frame.cpp +++ b/rpcs3/rpcs3qt/debugger_frame.cpp @@ -825,7 +825,7 @@ void debugger_frame::DoStep(bool stepOver) { bool should_step_over = stepOver && cpu->id_type() == 1; - if (cpu->state & s_pause_flags) + if (auto _state = +cpu->state; _state & s_pause_flags && _state & cpu_flag::wait && !(_state & cpu_flag::dbg_step)) { if (should_step_over) { @@ -849,7 +849,14 @@ void debugger_frame::DoStep(bool stepOver) { state -= s_pause_flags; - if (!should_step_over) state += cpu_flag::dbg_step; + if (!should_step_over) + { + if (u32* ptr = cpu->get_pc2()) + { + state += cpu_flag::dbg_step; + *ptr = cpu->get_pc(); + } + } }); cpu->state.notify_one(s_pause_flags);