mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-19 19:15:26 +00:00
Debugger: Dump related thread information on crash
This commit is contained in:
parent
cf850598eb
commit
926de68a79
3 changed files with 128 additions and 10 deletions
|
@ -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;
|
||||
|
|
|
@ -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 <thread>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <shared_mutex>
|
||||
|
||||
#if defined(ARCH_X64)
|
||||
#include <emmintrin.h>
|
||||
|
@ -75,6 +77,87 @@ void fmt_class_string<bs_t<cpu_flag>>::format(std::string& out, u64 arg)
|
|||
format_bitset(out, arg, "[", "|", "]", &fmt_class_string<cpu_flag>::format);
|
||||
}
|
||||
|
||||
enum cpu_threads_emulation_info_dump_t : u32 {};
|
||||
|
||||
template<>
|
||||
void fmt_class_string<cpu_threads_emulation_info_dump_t>::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<u32>(arg);
|
||||
|
||||
// Dump main_thread
|
||||
const auto main_ppu = idm::get<named_thread<ppu_thread>>(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<named_thread<ppu_thread>>(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<named_thread<spu_thread>>(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
|
||||
{
|
||||
|
|
|
@ -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<v128> gpr_saved(128);
|
||||
be_t<u32> 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<u32> 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<spu_channel_4_t>::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<spu_channel_4_t>::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){};
|
||||
|
|
Loading…
Add table
Reference in a new issue