Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debugger improvements #7905

Merged
merged 7 commits into from Apr 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 4 additions & 4 deletions Utilities/Thread.cpp
Expand Up @@ -1505,7 +1505,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context) no
{
if (!access_violation_recovered)
{
vm_log.notice("\n%s", cpu->dump());
vm_log.notice("\n%s", cpu->dump_all());
vm_log.error("Access violation %s location 0x%x (%s)", is_writing ? "writing" : "reading", addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory");
}

Expand Down Expand Up @@ -1536,7 +1536,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context) no

if (cpu && !access_violation_recovered)
{
vm_log.notice("\n%s", cpu->dump());
vm_log.notice("\n%s", cpu->dump_all());
}

// Note: a thread may access violate more than once after hack_alloc recovery
Expand Down Expand Up @@ -1617,7 +1617,7 @@ static LONG exception_filter(PEXCEPTION_POINTERS pExp) noexcept

if (const auto cpu = get_current_cpu_thread())
{
sys_log.notice("\n%s", cpu->dump());
sys_log.notice("\n%s", cpu->dump_all());
}
}

Expand Down Expand Up @@ -1746,7 +1746,7 @@ static void signal_handler(int sig, siginfo_t* info, void* uct) noexcept

if (const auto cpu = get_current_cpu_thread())
{
sys_log.notice("\n%s", cpu->dump());
sys_log.notice("\n%s", cpu->dump_all());
}

std::string msg = fmt::format("Segfault %s location %p at %p.\n", cause, info->si_addr, RIP(context));
Expand Down
24 changes: 22 additions & 2 deletions rpcs3/Emu/CPU/CPUThread.cpp
Expand Up @@ -412,7 +412,7 @@ void cpu_thread::operator()()
{
if (_this)
{
sys_log.warning("CPU Thread '%s' terminated abnormally:\n%s", name, _this->dump());
sys_log.warning("CPU Thread '%s' terminated abnormally:\n%s", name, _this->dump_all());
cleanup();
}
}
Expand Down Expand Up @@ -599,7 +599,27 @@ std::string cpu_thread::get_name() const
}
}

std::string cpu_thread::dump() const
std::string cpu_thread::dump_all() const
{
return {};
}

std::string cpu_thread::dump_regs() const
{
return {};
}

std::string cpu_thread::dump_callstack() const
{
return {};
}

std::vector<u32> cpu_thread::dump_callstack_list() const
{
return {};
}

std::string cpu_thread::dump_misc() const
{
return fmt::format("Type: %s\n" "State: %s\n", typeid(*this).name(), state.load());
}
Expand Down
20 changes: 17 additions & 3 deletions rpcs3/Emu/CPU/CPUThread.h
@@ -1,8 +1,10 @@
#pragma once
#pragma once

#include "../Utilities/Thread.h"
#include "../Utilities/bit_set.h"

#include <vector>

// Thread state flags
enum class cpu_flag : u32
{
Expand Down Expand Up @@ -91,8 +93,20 @@ class cpu_thread
// Get thread name (as assigned to named_thread)
std::string get_name() const;

// Get CPU state dump
virtual std::string dump() const;
// Get CPU state dump (everything)
virtual std::string dump_all() const = 0;

// Get CPU register dump
virtual std::string dump_regs() const;

// Get CPU call stack dump
virtual std::string dump_callstack() const;

// Get CPU call stack list
virtual std::vector<u32> dump_callstack_list() const;

// Get CPU dump of misc information
virtual std::string dump_misc() const;

// Thread entry point function
virtual void cpu_task() = 0;
Expand Down
170 changes: 126 additions & 44 deletions rpcs3/Emu/Cell/PPUThread.cpp
Expand Up @@ -58,6 +58,8 @@

#include <thread>
#include <cfenv>
#include <cctype>
#include <string>

const bool s_use_ssse3 = utils::has_ssse3();

Expand Down Expand Up @@ -368,52 +370,60 @@ extern bool ppu_patch(u32 addr, u32 value)
return true;
}

std::string ppu_thread::dump() const
std::string ppu_thread::dump_all() const
{
std::string ret = cpu_thread::dump();
fmt::append(ret, "Priority: %d\n", +prio);
fmt::append(ret, "Stack: 0x%x..0x%x\n", stack_addr, stack_addr + stack_size - 1);
fmt::append(ret, "Joiner: %s\n", joiner.load());
fmt::append(ret, "Commands: %u\n", cmd_queue.size());
std::string ret = cpu_thread::dump_misc() + '\n';

const char* _func = current_function;
ret += dump_misc() + '\n';
ret += dump_regs() + '\n';
ret += dump_callstack();

if (_func)
{
ret += "Current function: ";
ret += _func;
ret += '\n';
return ret;
}

for (u32 i = 3; i <= 6; i++)
fmt::append(ret, " ** GPR[%d] = 0x%llx\n", i, syscall_args[i - 3]);
}
else if (is_paused())
std::string ppu_thread::dump_regs() const
{
std::string ret;

for (uint i = 0; i < 32; ++i)
{
if (const auto last_func = last_function)
auto reg = gpr[i];

fmt::append(ret, "GPR[%-2d] = 0x%-8llx", i, reg);

const u32 max_str_len = 32;
const u32 hex_count = 10;

if (vm::check_addr(reg, max_str_len, vm::page_readable))
{
_func = last_func;
ret += "Last function: ";
ret += _func;
ret += '\n';
}
}
const u64 reg_ptr = vm::read64(reg);

if (const auto _time = start_time)
{
fmt::append(ret, "Waiting: %fs\n", (get_guest_system_time() - _time) / 1000000.);
}
else
{
ret += '\n';
}
if (vm::check_addr(reg_ptr, max_str_len, vm::page_readable))
{
reg = reg_ptr;
}

if (!_func)
{
ret += '\n';
}
const auto gpr_buf = vm::get_super_ptr<u8>(reg);

ret += "\nRegisters:\n=========\n";
for (uint i = 0; i < 32; ++i) fmt::append(ret, "GPR[%d] = 0x%llx\n", i, gpr[i]);
std::string buf_tmp(gpr_buf, gpr_buf + max_str_len);

if (std::isprint(static_cast<u8>(buf_tmp[0])) && std::isprint(static_cast<u8>(buf_tmp[1])) && std::isprint(static_cast<u8>(buf_tmp[2])))
{
fmt::append(ret, " -> \"%s\"", buf_tmp.c_str());
}
else
{
fmt::append(ret, " -> ");

for (u32 j = 0; j < hex_count; ++j)
{
fmt::append(ret, "%02x ", buf_tmp[j]);
}
}
}

fmt::append(ret, "\n");
}
for (uint i = 0; i < 32; ++i) fmt::append(ret, "FPR[%d] = %.6G\n", i, fpr[i]);
for (uint i = 0; i < 32; ++i) fmt::append(ret, "VR[%d] = %s [x: %g y: %g z: %g w: %g]\n", i, vr[i], vr[i]._f[3], vr[i]._f[2], vr[i]._f[1], vr[i]._f[0]);

Expand All @@ -424,17 +434,36 @@ std::string ppu_thread::dump() const
fmt::append(ret, "XER = [CA=%u | OV=%u | SO=%u | CNT=%u]\n", xer.ca, xer.ov, xer.so, xer.cnt);
fmt::append(ret, "VSCR = [SAT=%u | NJ=%u]\n", sat, nj);
fmt::append(ret, "FPSCR = [FL=%u | FG=%u | FE=%u | FU=%u]\n", fpscr.fl, fpscr.fg, fpscr.fe, fpscr.fu);
fmt::append(ret, "\nCall stack:\n=========\n0x%08x (0x0) called\n", cia);

return ret;
}

std::string ppu_thread::dump_callstack() const
{
std::string ret;

fmt::append(ret, "Call stack:\n=========\n0x%08x (0x0) called\n", cia);

for (u32 sp : dump_callstack_list())
{
// TODO: function addresses too
fmt::append(ret, "> from 0x%04llx (0x0)\n", vm::read64(static_cast<u32>(sp + 16)));
}

return ret;
}

std::vector<u32> ppu_thread::dump_callstack_list() const
{
//std::shared_lock rlock(vm::g_mutex); // Needs optimizations
VelocityRa marked this conversation as resolved.
Show resolved Hide resolved

// Determine stack range
u32 stack_ptr = static_cast<u32>(gpr[1]);
const u32 stack_ptr = static_cast<u32>(gpr[1]);
VelocityRa marked this conversation as resolved.
Show resolved Hide resolved

if (!vm::check_addr(stack_ptr, 1, vm::page_writable))
{
// Normally impossible unless the code does not follow ABI rules
return ret;
return {};
}

u32 stack_min = stack_ptr & ~0xfff;
Expand All @@ -450,12 +479,65 @@ std::string ppu_thread::dump() const
stack_max += 4096;
}

for (u64 sp = *vm::get_super_ptr<u64>(stack_ptr); sp >= stack_min && std::max(sp, sp + 0x200) < stack_max; sp = *vm::get_super_ptr<u64>(static_cast<u32>(sp)))
std::vector<u32> call_stack_list;

for (
u64 sp = *vm::get_super_ptr<u64>(stack_ptr);
sp >= stack_min && std::max(sp, sp + 0x200) < stack_max;
sp = *vm::get_super_ptr<u64>(static_cast<u32>(sp))
)
{
// TODO: print also function addresses
fmt::append(ret, "> from 0x%08llx (0x0)\n", *vm::get_super_ptr<u64>(static_cast<u32>(sp + 16)));
// TODO: function addresses too
call_stack_list.push_back(*vm::get_super_ptr<u64>(static_cast<u32>(sp + 16)));
}

return call_stack_list;
}

std::string ppu_thread::dump_misc() const
{
std::string ret;

fmt::append(ret, "Priority: %d\n", +prio);
fmt::append(ret, "Stack: 0x%x..0x%x\n", stack_addr, stack_addr + stack_size - 1);
fmt::append(ret, "Joiner: %s\n", joiner.load());
fmt::append(ret, "Commands: %u\n", cmd_queue.size());

const char* _func = current_function;

if (_func)
{
ret += "Current function: ";
ret += _func;
ret += '\n';

for (u32 i = 3; i <= 6; i++)
fmt::append(ret, " ** GPR[%d] = 0x%llx\n", i, syscall_args[i - 3]);
}
else if (is_paused())
{
if (const auto last_func = last_function)
{
_func = last_func;
ret += "Last function: ";
ret += _func;
ret += '\n';
}
}

if (const auto _time = start_time)
{
fmt::append(ret, "Waiting: %fs\n", (get_guest_system_time() - _time) / 1000000.);
}
else
{
ret += '\n';
}

if (!_func)
{
ret += '\n';
}
return ret;
}

Expand Down Expand Up @@ -586,7 +668,7 @@ void ppu_thread::exec_task()
{
const auto exec_op = [this](u64 op)
{
return reinterpret_cast<func_t>(op & 0xffffffff)(*this, {static_cast<u32>(op >> 32)});
return reinterpret_cast<func_t>(op & 0xffffffff)(*this, { static_cast<u32>(op >> 32) });
};

if (cia % 8 || state) [[unlikely]]
Expand Down
6 changes: 5 additions & 1 deletion rpcs3/Emu/Cell/PPUThread.h
Expand Up @@ -53,7 +53,11 @@ class ppu_thread : public cpu_thread
static const u32 id_step = 1;
static const u32 id_count = 2048;

virtual std::string dump() const override;
virtual std::string dump_all() const override;
virtual std::string dump_regs() const override;
virtual std::string dump_callstack() const override;
virtual std::vector<u32> dump_callstack_list() const override;
virtual std::string dump_misc() const override;
virtual void cpu_task() override final;
virtual void cpu_sleep() override;
virtual void cpu_mem() override;
Expand Down