diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index bf89cb186d..000910848f 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -109,13 +109,15 @@ extern thread_local std::string(*g_tls_log_prefix)(); // Report error and call std::abort(), defined in main.cpp [[noreturn]] void report_fatal_error(std::string_view text, bool is_html = false, bool include_help_text = true); +enum cpu_threads_emulation_info_dump_t : u32 {}; + std::string dump_useful_thread_info() { std::string result; if (auto cpu = get_current_cpu_thread()) { - cpu->dump_all(result); + fmt::append(result, "%s", cpu_threads_emulation_info_dump_t{cpu->id}); } return result; diff --git a/rpcs3/Emu/CPU/CPUThread.cpp b/rpcs3/Emu/CPU/CPUThread.cpp index 43b433bf04..e78824c0b5 100644 --- a/rpcs3/Emu/CPU/CPUThread.cpp +++ b/rpcs3/Emu/CPU/CPUThread.cpp @@ -8,6 +8,7 @@ #include "Emu/Memory/vm_reservation.h" #include "Emu/IdManager.h" #include "Emu/GDB.h" +#include "Emu/Cell/lv2/sys_spu.h" #include "Emu/Cell/PPUThread.h" #include "Emu/Cell/SPUThread.h" #include "Emu/RSX/RSXThread.h" @@ -17,6 +18,7 @@ #include #include #include +#include #if defined(ARCH_X64) #include @@ -75,6 +77,87 @@ void fmt_class_string>::format(std::string& out, u64 arg) format_bitset(out, arg, "[", "|", "]", &fmt_class_string::format); } +enum cpu_threads_emulation_info_dump_t : u32 {}; + +template<> +void fmt_class_string::format(std::string& out, u64 arg) +{ + // Do not dump all threads, only select few + // Aided by thread ID for filtering + const u32 must_have_cpu_id = static_cast(arg); + + // Dump main_thread + const auto main_ppu = idm::get>(ppu_thread::id_base); + + if (main_ppu) + { + fmt::append(out, "\n%s's thread context:\n", main_ppu->get_name()); + main_ppu->dump_all(out); + } + + if (must_have_cpu_id >> 24 == ppu_thread::id_base >> 24) + { + if (must_have_cpu_id != ppu_thread::id_base) + { + const auto selected_ppu = idm::get>(must_have_cpu_id); + + if (selected_ppu) + { + fmt::append(out, "\n%s's thread context:\n", selected_ppu->get_name()); + selected_ppu->dump_all(out); + } + } + } + else if (must_have_cpu_id >> 24 == spu_thread::id_base >> 24) + { + const auto selected_spu = idm::get>(must_have_cpu_id); + + if (selected_spu) + { + if (selected_spu->get_type() == spu_type::threaded && selected_spu->group->max_num > 1u) + { + // Dump the information of the entire group + // Do not block because it is a potentially sensitive context + std::shared_lock rlock(selected_spu->group->mutex, std::defer_lock); + + for (u32 i = 0; !rlock.try_lock() && i < 100; i++) + { + busy_wait(); + } + + if (rlock) + { + for (const auto& spu : selected_spu->group->threads) + { + if (spu && spu != selected_spu) + { + fmt::append(out, "\n%s's thread context:\n", spu->get_name()); + spu->dump_all(out); + } + } + } + else + { + fmt::append(out, "\nFailed to dump SPU thread group's thread's information!"); + } + + // Print the specified SPU thread last + } + + fmt::append(out, "\n%s's thread context:\n", selected_spu->get_name()); + selected_spu->dump_all(out); + } + } + else if (must_have_cpu_id >> 24 == rsx::thread::id_base >> 24) + { + if (auto rsx = rsx::get_current_renderer()) + { + fmt::append(out, "\n%s's thread context:\n", rsx->get_name()); + rsx->dump_all(out); + } + } +} + // CPU profiler thread struct cpu_prof { diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 6d21098b0e..ad02909767 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -1187,11 +1187,26 @@ void spu_thread::dump_regs(std::string& ret, std::any& /*custom_data*/) const SPUDisAsm dis_asm(cpu_disasm_mode::normal, ls); + std::vector gpr_saved(128); + be_t rdata_saved[32]{}; + u32 saved_pc = umax; + + // Load PC, GPRs and reservation data atomically + // We may not load the entire context atomically, but there is importance their state being intact for debugging + do + { + saved_pc = pc; + std::memcpy(gpr_saved.data(), gpr.data(), sizeof(v128) * gpr.size()); + std::memcpy(rdata_saved, rdata, sizeof(rdata)); + atomic_fence_acquire(); + } + while (saved_pc != pc || std::memcmp(rdata_saved, rdata, sizeof(rdata)) != 0 || std::memcmp(gpr_saved.data(), gpr.data(), sizeof(v128) * gpr.size()) != 0); + for (u32 i = 0; i < 128; i++, ret += '\n') { - const auto r = gpr[i]; + const auto r = gpr_saved[i]; - auto [is_const, const_value] = dis_asm.try_get_const_value(i, pc); + auto [is_const, const_value] = dis_asm.try_get_const_value(i, saved_pc & ~3); if (const_value != r) { @@ -1353,13 +1368,10 @@ void spu_thread::dump_regs(std::string& ret, std::any& /*custom_data*/) const fmt::append(ret, "Reservation Data:\n"); - be_t data[32]{}; - std::memcpy(data, rdata, sizeof(rdata)); // Show the data even if the reservation was lost inside the atomic loop - - for (usz i = 0; i < std::size(data); i += 4) + for (usz i = 0; i < std::size(rdata_saved); i += 4) { - fmt::append(ret, "[0x%02x] %08x %08x %08x %08x\n", i * sizeof(data[0]) - , data[i + 0], data[i + 1], data[i + 2], data[i + 3]); + fmt::append(ret, "[0x%02x] %08x %08x %08x %08x\n", i * sizeof(rdata_saved[0]) + , rdata_saved[i + 0], rdata_saved[i + 1], rdata_saved[i + 2], rdata_saved[i + 3]); } } @@ -7417,7 +7429,22 @@ void fmt_class_string::format(std::string& out, u64 arg) u32 vals[4]{}; const uint count = ch.try_read(vals); - fmt::append(out, "count = %d, data:\n", count); + if (count == 0u) + { + out += "empty\n\n"; + return; + } + + fmt::append(out, "data:"); + + if (count > 1u) + { + out += '\n'; + } + else + { + out += ' '; + } out += "{ "; @@ -7432,6 +7459,12 @@ void fmt_class_string::format(std::string& out, u64 arg) } out += " }\n"; + + if (count <= 1u) + { + // Keep the amount of lines consistent + out += '\n'; + } } DECLARE(spu_thread::g_raw_spu_ctr){};