From 2759091ede38c2a39c1d9dc2471f02e3cd8e5ffe Mon Sep 17 00:00:00 2001 From: Eladash Date: Sun, 25 Sep 2022 01:53:27 +0300 Subject: [PATCH] Debugger: Rewind SPU captures Very basic implementation, can be improved. --- rpcs3/Emu/Cell/SPUThread.cpp | 75 ++++++++++++++++++++++++++------ rpcs3/Emu/Cell/SPUThread.h | 6 ++- rpcs3/rpcs3qt/debugger_frame.cpp | 16 ++++++- 3 files changed, 81 insertions(+), 16 deletions(-) diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 25b81af3f3..21f9b98f23 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -1722,6 +1722,20 @@ void spu_thread::serialize_common(utils::serial& ar) , run_ctrl, exit_status.data, status_npc.raw().status, ch_dec_start_timestamp, ch_dec_value, is_dec_frozen); ar(std::span(mfc_queue, mfc_size)); + + u32 vals[4]{}; + + if (ar.is_writing()) + { + const u8 count = ch_in_mbox.try_read(vals); + ar(count, std::span(vals, count)); + } + else + { + const u8 count = ar; + ar(std::span(vals, count)); + ch_in_mbox.set_values(count, vals[0], vals[1], vals[2], vals[3]); + } } spu_thread::spu_thread(utils::serial& ar, lv2_spu_group* group) @@ -1769,13 +1783,6 @@ spu_thread::spu_thread(utils::serial& ar, lv2_spu_group* group) serialize_common(ar); - { - u32 vals[4]{}; - const u8 count = ar; - ar(std::span(vals, count)); - ch_in_mbox.set_values(count, vals[0], vals[1], vals[2], vals[3]); - } - status_npc.raw().npc = pc | u8{interrupts_enabled}; if (get_type() == spu_type::threaded) @@ -1827,12 +1834,6 @@ void spu_thread::save(utils::serial& ar) serialize_common(ar); - { - u32 vals[4]{}; - const u8 count = ch_in_mbox.try_read(vals); - ar(count, std::span(vals, count)); - } - if (get_type() == spu_type::threaded) { for (const auto& [key, q] : spuq) @@ -5339,8 +5340,10 @@ void spu_thread::fast_call(u32 ls_addr) gpr[1]._u32[3] = old_stack; } -bool spu_thread::capture_local_storage() const +bool spu_thread::capture_state() { + ensure(state & cpu_flag::wait); + spu_exec_object spu_exec; // Save data as an executable segment, even the SPU stack @@ -5424,6 +5427,50 @@ bool spu_thread::capture_local_storage() const } spu_log.success("SPU Local Storage image saved to '%s'", elf_path); + + if (g_cfg.core.spu_decoder == spu_decoder_type::asmjit || g_cfg.core.spu_decoder == spu_decoder_type::llvm) + { + return true; + } + + auto& rewind = rewind_captures[current_rewind_capture_idx++ % rewind_captures.size()]; + + if (rewind) + { + spu_log.error("Due to resource limits the 16th SPU rewind capture is being overwritten with a new one. (free the most recent by loading it)"); + rewind.reset(); + } + + rewind = std::make_shared(); + (*rewind)(std::span(prog.bin.data(), prog.bin.size())); // span serialization doesn't remember size which is what we need + serialize_common(*rewind); + + // TODO: Save and restore decrementer state properly + + spu_log.success("SPU rewind image has been saved in memory. (%d free slots left)", std::count_if(rewind_captures.begin(), rewind_captures.end(), FN(!x.operator bool()))); + return true; +} + +bool spu_thread::try_load_debug_capture() +{ + if (cpu_flag::wait - state) + { + return false; + } + + auto rewind = std::move(rewind_captures[(current_rewind_capture_idx - 1) % rewind_captures.size()]); + + if (!rewind) + { + return false; + } + + rewind->set_reading_state(); + (*rewind)(std::span(ls, SPU_LS_SIZE)); // span serialization doesn't remember size which is what we need + serialize_common(*rewind); + current_rewind_capture_idx--; + + spu_log.success("Last SPU rewind image has been loaded."); return true; } diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 0cd0e689fb..2b88b3120c 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -813,7 +813,11 @@ public: void fast_call(u32 ls_addr); - bool capture_local_storage() const; + std::array, 32> rewind_captures; // shared_ptr to avoid header inclusion + u8 current_rewind_capture_idx = 0; + + bool capture_state(); + bool try_load_debug_capture(); void wakeup_delay(u32 div = 1) const; // Convert specified SPU LS address to a pointer of specified (possibly converted to BE) type diff --git a/rpcs3/rpcs3qt/debugger_frame.cpp b/rpcs3/rpcs3qt/debugger_frame.cpp index a6d4f11803..83163a81b2 100644 --- a/rpcs3/rpcs3qt/debugger_frame.cpp +++ b/rpcs3/rpcs3qt/debugger_frame.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include "util/asm.hpp" @@ -324,6 +325,7 @@ void debugger_frame::keyPressEvent(QKeyEvent* event) "\nKeys Ctrl+B: Open breakpoints settings." "\nKeys Ctrl+S: Search memory string utility." "\nKeys Alt+S: Capture SPU images of selected SPU." + "\nKeys Alt+R: Load last saved SPU state capture." "\nKey D: SPU MFC commands logger, MFC debug setting must be enabled." "\nKey D: Also PPU calling history logger, interpreter and non-zero call history size must be used." "\nKey E: Instruction Editor: click on the instruction you want to modify, then press E." @@ -594,6 +596,12 @@ void debugger_frame::keyPressEvent(QKeyEvent* event) if (cpu->id_type() == 1 || cpu->id_type() == 2) { + if (cpu->id_type() == 2 && modifiers & Qt::AltModifier) + { + static_cast(cpu)->try_load_debug_capture(); + return; + } + if (!m_reg_editor) { m_reg_editor = new register_editor_dialog(this, m_disasm.get(), make_check_cpu(cpu)); @@ -615,7 +623,13 @@ void debugger_frame::keyPressEvent(QKeyEvent* event) return; } - static_cast(cpu)->capture_local_storage(); + if (!cpu->state.all_of(cpu_flag::wait + cpu_flag::dbg_pause)) + { + QMessageBox::warning(this, QObject::tr("Pause the SPU Thread!"), QObject::tr("Cannot perform SPU capture due to the thread need manual pausing!")); + return; + } + + static_cast(cpu)->capture_state(); } return; }