diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp index cc025bab67..af8414ed14 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.cpp +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -322,18 +322,21 @@ bool spu_thread::write_reg(const u32 addr, const u32 value) void spu_load_exec(const spu_exec_object& elf) { auto ls0 = vm::cast(vm::falloc(RAW_SPU_BASE_ADDR, SPU_LS_SIZE, vm::spu)); - auto spu = idm::make_ptr>("TEST_SPU", ls0, nullptr, 0, "", 0); spu_thread::g_raw_spu_ctr++; - spu_thread::g_raw_spu_id[0] = spu->id; for (const auto& prog : elf.progs) { if (prog.p_type == 0x1u /* LOAD */ && prog.p_memsz) { - std::memcpy(spu->_ptr(prog.p_vaddr), prog.bin.data(), prog.p_filesz); + std::memcpy(vm::base(ls0 + prog.p_vaddr), prog.bin.data(), prog.p_filesz); } } + auto spu = idm::make_ptr>("TEST_SPU", ls0, nullptr, 0, "", 0); + + spu_thread::g_raw_spu_id[0] = spu->id; + spu->status_npc = {SPU_STATUS_RUNNING, elf.header.e_entry}; + atomic_storage::release(spu->pc, elf.header.e_entry); } diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 256440480f..15be3b017f 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -1,11 +1,14 @@ #include "stdafx.h" #include "Utilities/JIT.h" #include "Utilities/asm.h" +#include "Utilities/date_time.h" #include "Utilities/sysinfo.h" #include "Emu/Memory/vm.h" #include "Emu/Memory/vm_ptr.h" #include "Emu/Memory/vm_reservation.h" +#include "Loader/ELF.h" +#include "Emu/VFS.h" #include "Emu/IdManager.h" #include "Emu/RSX/RSXThread.h" #include "Emu/Cell/PPUThread.h" @@ -3514,6 +3517,111 @@ void spu_thread::fast_call(u32 ls_addr) gpr[1]._u32[3] = old_stack; } +bool spu_thread::capture_local_storage() const +{ + struct aligned_delete + { + void operator()(u8* ptr) + { + ::operator delete(ptr, std::align_val_t{64}); + } + }; + + std::unique_ptr ls_copy(static_cast(::operator new(SPU_LS_SIZE, std::align_val_t{64}))); + const auto ls_ptr = ls_copy.get(); + std::memcpy(ls_ptr, _ptr(0), SPU_LS_SIZE); + + std::bitset found; + alignas(64) constexpr spu_rdata_t zero{}; + + // Scan Local Storage in 512-byte blocks for non-zero blocks + for (s32 i = 0; i < SPU_LS_SIZE;) + { + if (!cmp_rdata(zero, *reinterpret_cast(ls_ptr + i))) + { + found.set(i / 512); + i = ::align(i + 1u, 512); + } + else + { + i += sizeof(spu_rdata_t); + } + } + + spu_exec_object spu_exec; + + // Now save the data in sequential segments + for (s32 i = 0, found_first = -1; i <= SPU_LS_SIZE; i += 512) + { + if (i == SPU_LS_SIZE || !found[i / 512]) + { + if (auto begin = std::exchange(found_first, -1); begin != -1) + { + // Save data as an executable segment, even the SPU stack + auto& prog = spu_exec.progs.emplace_back(SYS_SPU_SEGMENT_TYPE_COPY, 0x7, begin + 0u, i - begin + 0u, 512 + , std::vector(ls_ptr + begin, ls_ptr + i)); + + prog.p_paddr = prog.p_vaddr; + spu_log.success("Segment: p_type=0x%x, p_vaddr=0x%x, p_filesz=0x%x, p_memsz=0x%x", prog.p_type, prog.p_vaddr, prog.p_filesz, prog.p_memsz); + } + + continue; + } + + if (found_first == -1) + { + found_first = i; + } + } + + std::string name; + + if (get_type() == spu_type::threaded) + { + name = *spu_tname.load(); + + if (name.empty()) + { + // TODO: Maybe add thread group name here + fmt::append(name, "SPU.%u", lv2_id); + } + } + else + { + fmt::append(name, "RawSPU.%u", lv2_id); + } + + spu_exec.header.e_entry = pc; + + name = vfs::escape(name, true); + std::replace(name.begin(), name.end(), ' ', '_'); + + auto get_filename = [&]() -> std::string + { + return fs::get_cache_dir() + "spu_progs/" + Emu.GetTitleID() + "_" + vfs::escape(name, true) + '_' + date_time::current_time_narrow() + "_capture.elf"; + }; + + auto elf_path = get_filename(); + fs::file dump_file(elf_path, fs::create + fs::excl + fs::write); + + if (!dump_file) + { + // Wait 1 second so current_time_narrow() will return a different string + std::this_thread::sleep_for(1s); + + if (elf_path = get_filename(); !dump_file.open(elf_path, fs::create + fs::excl + fs::write)) + { + spu_log.error("Failed to create '%s' (error=%s)", elf_path, fs::g_tls_error); + return false; + } + } + + spu_exec.save(dump_file); + + spu_log.success("SPU Local Storage image saved to '%s'", elf_path); + return true; +} + spu_function_logger::spu_function_logger(spu_thread& spu, const char* func) : spu(spu) { diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index b37c792c17..460030293c 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -770,6 +770,8 @@ public: void fast_call(u32 ls_addr); + bool capture_local_storage() const; + // Convert specified SPU LS address to a pointer of specified (possibly converted to BE) type template to_be_t* _ptr(u32 lsa) const diff --git a/rpcs3/Loader/ELF.h b/rpcs3/Loader/ELF.h index 11a3c6197e..7459ae89b4 100644 --- a/rpcs3/Loader/ELF.h +++ b/rpcs3/Loader/ELF.h @@ -113,7 +113,7 @@ struct elf_prog final : elf_phdr base::p_vaddr = vaddr; base::p_memsz = memsz; base::p_align = align; - base::p_filesz = static_cast(bin.size()); + base::p_filesz = static_cast(this->bin.size()); base::p_paddr = 0; base::p_offset = -1; } diff --git a/rpcs3/rpcs3qt/debugger_frame.cpp b/rpcs3/rpcs3qt/debugger_frame.cpp index f85b8fc9a5..79548b5cba 100644 --- a/rpcs3/rpcs3qt/debugger_frame.cpp +++ b/rpcs3/rpcs3qt/debugger_frame.cpp @@ -234,7 +234,9 @@ void debugger_frame::keyPressEvent(QKeyEvent* event) const u32 pc = m_debugger_list->m_pc + i * 4; - if (QApplication::keyboardModifiers() & Qt::ControlModifier) + const auto modifiers = QApplication::keyboardModifiers(); + + if (modifiers & Qt::ControlModifier) { switch (event->key()) { @@ -261,7 +263,21 @@ void debugger_frame::keyPressEvent(QKeyEvent* event) dlg->show(); return; } + case Qt::Key_S: + { + if (modifiers & Qt::AltModifier) + { + const auto cpu = this->cpu.lock(); + if (!cpu || cpu->id_type() == 1) + { + return; + } + + static_cast(cpu.get())->capture_local_storage(); + } + return; + } case Qt::Key_F10: { DoStep(true);