mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-06-03 08:08:39 -04:00
strat: use m_ for member variables
This commit is contained in:
parent
ce28591ab2
commit
a595c232b9
425 changed files with 8531 additions and 8484 deletions
|
@ -86,31 +86,31 @@ namespace ams::creport {
|
|||
|
||||
void CrashReport::Initialize() {
|
||||
/* Initialize the heap. */
|
||||
this->heap_handle = lmem::CreateExpHeap(this->heap_storage, sizeof(this->heap_storage), lmem::CreateOption_None);
|
||||
m_heap_handle = lmem::CreateExpHeap(m_heap_storage, sizeof(m_heap_storage), lmem::CreateOption_None);
|
||||
|
||||
/* Allocate members. */
|
||||
this->module_list = std::construct_at(static_cast<ModuleList *>(lmem::AllocateFromExpHeap(this->heap_handle, sizeof(ModuleList))));
|
||||
this->thread_list = std::construct_at(static_cast<ThreadList *>(lmem::AllocateFromExpHeap(this->heap_handle, sizeof(ThreadList))));
|
||||
this->dying_message = static_cast<u8 *>(lmem::AllocateFromExpHeap(this->heap_handle, DyingMessageSizeMax));
|
||||
if (this->dying_message != nullptr) {
|
||||
std::memset(this->dying_message, 0, DyingMessageSizeMax);
|
||||
m_module_list = std::construct_at(static_cast<ModuleList *>(lmem::AllocateFromExpHeap(m_heap_handle, sizeof(ModuleList))));
|
||||
m_thread_list = std::construct_at(static_cast<ThreadList *>(lmem::AllocateFromExpHeap(m_heap_handle, sizeof(ThreadList))));
|
||||
m_dying_message = static_cast<u8 *>(lmem::AllocateFromExpHeap(m_heap_handle, DyingMessageSizeMax));
|
||||
if (m_dying_message != nullptr) {
|
||||
std::memset(m_dying_message, 0, DyingMessageSizeMax);
|
||||
}
|
||||
}
|
||||
|
||||
void CrashReport::BuildReport(os::ProcessId process_id, bool has_extra_info) {
|
||||
this->has_extra_info = has_extra_info;
|
||||
m_has_extra_info = has_extra_info;
|
||||
|
||||
if (this->OpenProcess(process_id)) {
|
||||
ON_SCOPE_EXIT { this->Close(); };
|
||||
|
||||
/* Parse info from the crashed process. */
|
||||
this->ProcessExceptions();
|
||||
this->module_list->FindModulesFromThreadInfo(this->debug_handle, this->crashed_thread);
|
||||
this->thread_list->ReadFromProcess(this->debug_handle, this->thread_tls_map, this->Is64Bit());
|
||||
m_module_list->FindModulesFromThreadInfo(m_debug_handle, m_crashed_thread);
|
||||
m_thread_list->ReadFromProcess(m_debug_handle, m_thread_tls_map, this->Is64Bit());
|
||||
|
||||
/* Associate module list to threads. */
|
||||
this->crashed_thread.SetModuleList(this->module_list);
|
||||
this->thread_list->SetModuleList(this->module_list);
|
||||
m_crashed_thread.SetModuleList(m_module_list);
|
||||
m_thread_list->SetModuleList(m_module_list);
|
||||
|
||||
/* Process dying message for applications. */
|
||||
if (this->IsApplication()) {
|
||||
|
@ -119,13 +119,13 @@ namespace ams::creport {
|
|||
|
||||
/* Nintendo's creport finds extra modules by looking at all threads if application, */
|
||||
/* but there's no reason for us not to always go looking. */
|
||||
for (size_t i = 0; i < this->thread_list->GetThreadCount(); i++) {
|
||||
this->module_list->FindModulesFromThreadInfo(this->debug_handle, this->thread_list->GetThreadInfo(i));
|
||||
for (size_t i = 0; i < m_thread_list->GetThreadCount(); i++) {
|
||||
m_module_list->FindModulesFromThreadInfo(m_debug_handle, m_thread_list->GetThreadInfo(i));
|
||||
}
|
||||
|
||||
/* Cache the module base address to send to fatal. */
|
||||
if (this->module_list->GetModuleCount()) {
|
||||
this->module_base_address = this->module_list->GetModuleStartAddress(0);
|
||||
if (m_module_list->GetModuleCount()) {
|
||||
m_module_base_address = m_module_list->GetModuleStartAddress(0);
|
||||
}
|
||||
|
||||
/* Nintendo's creport saves the report to erpt here, but we'll save to SD card later. */
|
||||
|
@ -139,33 +139,33 @@ namespace ams::creport {
|
|||
|
||||
/* TODO: Support generating 32-bit fatal contexts? */
|
||||
out->architecture = fatal::CpuContext::Architecture_Aarch64;
|
||||
out->type = static_cast<u32>(this->exception_info.type);
|
||||
out->type = static_cast<u32>(m_exception_info.type);
|
||||
|
||||
for (size_t i = 0; i < fatal::aarch64::RegisterName_FP; i++) {
|
||||
out->aarch64_ctx.SetRegisterValue(static_cast<fatal::aarch64::RegisterName>(i), this->crashed_thread.GetGeneralPurposeRegister(i));
|
||||
out->aarch64_ctx.SetRegisterValue(static_cast<fatal::aarch64::RegisterName>(i), m_crashed_thread.GetGeneralPurposeRegister(i));
|
||||
}
|
||||
out->aarch64_ctx.SetRegisterValue(fatal::aarch64::RegisterName_FP, this->crashed_thread.GetFP());
|
||||
out->aarch64_ctx.SetRegisterValue(fatal::aarch64::RegisterName_LR, this->crashed_thread.GetLR());
|
||||
out->aarch64_ctx.SetRegisterValue(fatal::aarch64::RegisterName_SP, this->crashed_thread.GetSP());
|
||||
out->aarch64_ctx.SetRegisterValue(fatal::aarch64::RegisterName_PC, this->crashed_thread.GetPC());
|
||||
out->aarch64_ctx.SetRegisterValue(fatal::aarch64::RegisterName_FP, m_crashed_thread.GetFP());
|
||||
out->aarch64_ctx.SetRegisterValue(fatal::aarch64::RegisterName_LR, m_crashed_thread.GetLR());
|
||||
out->aarch64_ctx.SetRegisterValue(fatal::aarch64::RegisterName_SP, m_crashed_thread.GetSP());
|
||||
out->aarch64_ctx.SetRegisterValue(fatal::aarch64::RegisterName_PC, m_crashed_thread.GetPC());
|
||||
|
||||
out->aarch64_ctx.stack_trace_size = this->crashed_thread.GetStackTraceSize();
|
||||
out->aarch64_ctx.stack_trace_size = m_crashed_thread.GetStackTraceSize();
|
||||
for (size_t i = 0; i < out->aarch64_ctx.stack_trace_size; i++) {
|
||||
out->aarch64_ctx.stack_trace[i] = this->crashed_thread.GetStackTrace(i);
|
||||
out->aarch64_ctx.stack_trace[i] = m_crashed_thread.GetStackTrace(i);
|
||||
}
|
||||
|
||||
if (this->module_base_address != 0) {
|
||||
out->aarch64_ctx.SetBaseAddress(this->module_base_address);
|
||||
if (m_module_base_address != 0) {
|
||||
out->aarch64_ctx.SetBaseAddress(m_module_base_address);
|
||||
}
|
||||
|
||||
/* For ams fatal, which doesn't use afsr0, pass program_id instead. */
|
||||
out->aarch64_ctx.SetProgramIdForAtmosphere(ncm::ProgramId{this->process_info.program_id});
|
||||
out->aarch64_ctx.SetProgramIdForAtmosphere(ncm::ProgramId{m_process_info.program_id});
|
||||
}
|
||||
|
||||
void CrashReport::ProcessExceptions() {
|
||||
/* Loop all debug events. */
|
||||
svc::DebugEventInfo d;
|
||||
while (R_SUCCEEDED(svc::GetDebugEvent(std::addressof(d), this->debug_handle))) {
|
||||
while (R_SUCCEEDED(svc::GetDebugEvent(std::addressof(d), m_debug_handle))) {
|
||||
switch (d.type) {
|
||||
case svc::DebugEvent_CreateProcess:
|
||||
this->HandleDebugEventInfoCreateProcess(d);
|
||||
|
@ -183,11 +183,11 @@ namespace ams::creport {
|
|||
}
|
||||
|
||||
/* Parse crashed thread info. */
|
||||
this->crashed_thread.ReadFromProcess(this->debug_handle, this->thread_tls_map, this->crashed_thread_id, this->Is64Bit());
|
||||
m_crashed_thread.ReadFromProcess(m_debug_handle, m_thread_tls_map, m_crashed_thread_id, this->Is64Bit());
|
||||
}
|
||||
|
||||
void CrashReport::HandleDebugEventInfoCreateProcess(const svc::DebugEventInfo &d) {
|
||||
this->process_info = d.info.create_process;
|
||||
m_process_info = d.info.create_process;
|
||||
|
||||
/* On 5.0.0+, we want to parse out a dying message from application crashes. */
|
||||
if (hos::GetVersion() < hos::Version_5_0_0 || !IsApplication()) {
|
||||
|
@ -195,12 +195,12 @@ namespace ams::creport {
|
|||
}
|
||||
|
||||
/* Parse out user data. */
|
||||
const u64 address = this->process_info.user_exception_context_address + DyingMessageAddressOffset;
|
||||
const u64 address = m_process_info.user_exception_context_address + DyingMessageAddressOffset;
|
||||
u64 userdata_address = 0;
|
||||
u64 userdata_size = 0;
|
||||
|
||||
/* Read userdata address. */
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(userdata_address)), this->debug_handle, address, sizeof(userdata_address)))) {
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(userdata_address)), m_debug_handle, address, sizeof(userdata_address)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -210,48 +210,48 @@ namespace ams::creport {
|
|||
}
|
||||
|
||||
/* Read userdata size. */
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(userdata_size)), this->debug_handle, address + sizeof(userdata_address), sizeof(userdata_size)))) {
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(userdata_size)), m_debug_handle, address + sizeof(userdata_address), sizeof(userdata_size)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Cap userdata size. */
|
||||
userdata_size = std::min(size_t(userdata_size), DyingMessageSizeMax);
|
||||
|
||||
this->dying_message_address = userdata_address;
|
||||
this->dying_message_size = userdata_size;
|
||||
m_dying_message_address = userdata_address;
|
||||
m_dying_message_size = userdata_size;
|
||||
}
|
||||
|
||||
void CrashReport::HandleDebugEventInfoCreateThread(const svc::DebugEventInfo &d) {
|
||||
/* Save info on the thread's TLS address for later. */
|
||||
this->thread_tls_map.SetThreadTls(d.info.create_thread.thread_id, d.info.create_thread.tls_address);
|
||||
m_thread_tls_map.SetThreadTls(d.info.create_thread.thread_id, d.info.create_thread.tls_address);
|
||||
}
|
||||
|
||||
void CrashReport::HandleDebugEventInfoException(const svc::DebugEventInfo &d) {
|
||||
switch (d.info.exception.type) {
|
||||
case svc::DebugException_UndefinedInstruction:
|
||||
this->result = creport::ResultUndefinedInstruction();
|
||||
m_result = creport::ResultUndefinedInstruction();
|
||||
break;
|
||||
case svc::DebugException_InstructionAbort:
|
||||
this->result = creport::ResultInstructionAbort();
|
||||
m_result = creport::ResultInstructionAbort();
|
||||
break;
|
||||
case svc::DebugException_DataAbort:
|
||||
this->result = creport::ResultDataAbort();
|
||||
m_result = creport::ResultDataAbort();
|
||||
break;
|
||||
case svc::DebugException_AlignmentFault:
|
||||
this->result = creport::ResultAlignmentFault();
|
||||
m_result = creport::ResultAlignmentFault();
|
||||
break;
|
||||
case svc::DebugException_UserBreak:
|
||||
this->result = creport::ResultUserBreak();
|
||||
m_result = creport::ResultUserBreak();
|
||||
/* Try to parse out the user break result. */
|
||||
if (hos::GetVersion() >= hos::Version_5_0_0) {
|
||||
svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(this->result)), this->debug_handle, d.info.exception.specific.user_break.address, sizeof(this->result));
|
||||
svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(m_result)), m_debug_handle, d.info.exception.specific.user_break.address, sizeof(m_result));
|
||||
}
|
||||
break;
|
||||
case svc::DebugException_UndefinedSystemCall:
|
||||
this->result = creport::ResultUndefinedSystemCall();
|
||||
m_result = creport::ResultUndefinedSystemCall();
|
||||
break;
|
||||
case svc::DebugException_MemorySystemError:
|
||||
this->result = creport::ResultMemorySystemError();
|
||||
m_result = creport::ResultMemorySystemError();
|
||||
break;
|
||||
case svc::DebugException_DebuggerAttached:
|
||||
case svc::DebugException_BreakPoint:
|
||||
|
@ -260,8 +260,8 @@ namespace ams::creport {
|
|||
}
|
||||
|
||||
/* Save exception info. */
|
||||
this->exception_info = d.info.exception;
|
||||
this->crashed_thread_id = d.thread_id;
|
||||
m_exception_info = d.info.exception;
|
||||
m_crashed_thread_id = d.thread_id;
|
||||
}
|
||||
|
||||
void CrashReport::ProcessDyingMessage() {
|
||||
|
@ -271,10 +271,10 @@ namespace ams::creport {
|
|||
}
|
||||
|
||||
/* Validate address/size. */
|
||||
if (this->dying_message_address == 0 || this->dying_message_address & 0xFFF) {
|
||||
if (m_dying_message_address == 0 || m_dying_message_address & 0xFFF) {
|
||||
return;
|
||||
}
|
||||
if (this->dying_message_size > DyingMessageSizeMax) {
|
||||
if (m_dying_message_size > DyingMessageSizeMax) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -284,12 +284,12 @@ namespace ams::creport {
|
|||
}
|
||||
|
||||
/* Verify that we have a dying message buffer. */
|
||||
if (this->dying_message == nullptr) {
|
||||
if (m_dying_message == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Read the dying message. */
|
||||
svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(this->dying_message), this->debug_handle, this->dying_message_address, this->dying_message_size);
|
||||
svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(m_dying_message), m_debug_handle, m_dying_message_address, m_dying_message_size);
|
||||
}
|
||||
|
||||
void CrashReport::SaveReport(bool enable_screenshot) {
|
||||
|
@ -307,7 +307,7 @@ namespace ams::creport {
|
|||
char file_path[fs::EntryNameLengthMax + 1];
|
||||
|
||||
/* Save crash report. */
|
||||
util::SNPrintf(file_path, sizeof(file_path), "sdmc:/atmosphere/crash_reports/%011lu_%016lx.log", timestamp, this->process_info.program_id);
|
||||
util::SNPrintf(file_path, sizeof(file_path), "sdmc:/atmosphere/crash_reports/%011lu_%016lx.log", timestamp, m_process_info.program_id);
|
||||
{
|
||||
ScopedFile file(file_path);
|
||||
if (file.IsOpen()) {
|
||||
|
@ -316,25 +316,25 @@ namespace ams::creport {
|
|||
}
|
||||
|
||||
/* Dump threads. */
|
||||
util::SNPrintf(file_path, sizeof(file_path), "sdmc:/atmosphere/crash_reports/dumps/%011lu_%016lx_thread_info.bin", timestamp, this->process_info.program_id);
|
||||
util::SNPrintf(file_path, sizeof(file_path), "sdmc:/atmosphere/crash_reports/dumps/%011lu_%016lx_thread_info.bin", timestamp, m_process_info.program_id);
|
||||
{
|
||||
ScopedFile file(file_path);
|
||||
if (file.IsOpen()) {
|
||||
this->thread_list->DumpBinary(file, this->crashed_thread.GetThreadId());
|
||||
m_thread_list->DumpBinary(file, m_crashed_thread.GetThreadId());
|
||||
}
|
||||
}
|
||||
|
||||
/* Finalize our heap. */
|
||||
std::destroy_at(this->module_list);
|
||||
std::destroy_at(this->thread_list);
|
||||
lmem::FreeToExpHeap(this->heap_handle, this->module_list);
|
||||
lmem::FreeToExpHeap(this->heap_handle, this->thread_list);
|
||||
if (this->dying_message != nullptr) {
|
||||
lmem::FreeToExpHeap(this->heap_handle, this->dying_message);
|
||||
std::destroy_at(m_module_list);
|
||||
std::destroy_at(m_thread_list);
|
||||
lmem::FreeToExpHeap(m_heap_handle, m_module_list);
|
||||
lmem::FreeToExpHeap(m_heap_handle, m_thread_list);
|
||||
if (m_dying_message != nullptr) {
|
||||
lmem::FreeToExpHeap(m_heap_handle, m_dying_message);
|
||||
}
|
||||
this->module_list = nullptr;
|
||||
this->thread_list = nullptr;
|
||||
this->dying_message = nullptr;
|
||||
m_module_list = nullptr;
|
||||
m_thread_list = nullptr;
|
||||
m_dying_message = nullptr;
|
||||
|
||||
/* Try to take a screenshot. */
|
||||
/* NOTE: Nintendo validates that enable_screenshot is true here, and validates that the application id is not in a blacklist. */
|
||||
|
@ -345,11 +345,11 @@ namespace ams::creport {
|
|||
ON_SCOPE_EXIT { capsrv::FinalizeScreenShotControl(); };
|
||||
|
||||
u64 jpeg_size;
|
||||
if (R_SUCCEEDED(capsrv::CaptureJpegScreenshot(std::addressof(jpeg_size), this->heap_storage, sizeof(this->heap_storage), vi::LayerStack_ApplicationForDebug, TimeSpan::FromSeconds(10)))) {
|
||||
util::SNPrintf(file_path, sizeof(file_path), "sdmc:/atmosphere/crash_reports/%011lu_%016lx.jpg", timestamp, this->process_info.program_id);
|
||||
if (R_SUCCEEDED(capsrv::CaptureJpegScreenshot(std::addressof(jpeg_size), m_heap_storage, sizeof(m_heap_storage), vi::LayerStack_ApplicationForDebug, TimeSpan::FromSeconds(10)))) {
|
||||
util::SNPrintf(file_path, sizeof(file_path), "sdmc:/atmosphere/crash_reports/%011lu_%016lx.jpg", timestamp, m_process_info.program_id);
|
||||
ScopedFile file(file_path);
|
||||
if (file.IsOpen()) {
|
||||
file.Write(this->heap_storage, jpeg_size);
|
||||
file.Write(m_heap_storage, jpeg_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -360,42 +360,42 @@ namespace ams::creport {
|
|||
void CrashReport::SaveToFile(ScopedFile &file) {
|
||||
file.WriteFormat("Atmosphère Crash Report (v1.6):\n");
|
||||
|
||||
file.WriteFormat("Result: 0x%X (2%03d-%04d)\n\n", this->result.GetValue(), this->result.GetModule(), this->result.GetDescription());
|
||||
file.WriteFormat("Result: 0x%X (2%03d-%04d)\n\n", m_result.GetValue(), m_result.GetModule(), m_result.GetDescription());
|
||||
|
||||
/* Process Info. */
|
||||
char name_buf[0x10] = {};
|
||||
static_assert(sizeof(name_buf) >= sizeof(this->process_info.name), "buffer overflow!");
|
||||
std::memcpy(name_buf, this->process_info.name, sizeof(this->process_info.name));
|
||||
static_assert(sizeof(name_buf) >= sizeof(m_process_info.name), "buffer overflow!");
|
||||
std::memcpy(name_buf, m_process_info.name, sizeof(m_process_info.name));
|
||||
file.WriteFormat("Process Info:\n");
|
||||
file.WriteFormat(" Process Name: %s\n", name_buf);
|
||||
file.WriteFormat(" Program ID: %016lx\n", this->process_info.program_id);
|
||||
file.WriteFormat(" Process ID: %016lx\n", this->process_info.process_id);
|
||||
file.WriteFormat(" Process Flags: %08x\n", this->process_info.flags);
|
||||
file.WriteFormat(" Program ID: %016lx\n", m_process_info.program_id);
|
||||
file.WriteFormat(" Process ID: %016lx\n", m_process_info.process_id);
|
||||
file.WriteFormat(" Process Flags: %08x\n", m_process_info.flags);
|
||||
if (hos::GetVersion() >= hos::Version_5_0_0) {
|
||||
file.WriteFormat(" User Exception Address: %s\n", this->module_list->GetFormattedAddressString(this->process_info.user_exception_context_address));
|
||||
file.WriteFormat(" User Exception Address: %s\n", m_module_list->GetFormattedAddressString(m_process_info.user_exception_context_address));
|
||||
}
|
||||
|
||||
/* Exception Info. */
|
||||
file.WriteFormat("Exception Info:\n");
|
||||
file.WriteFormat(" Type: %s\n", GetDebugExceptionString(this->exception_info.type));
|
||||
file.WriteFormat(" Address: %s\n", this->module_list->GetFormattedAddressString(this->exception_info.address));
|
||||
switch (this->exception_info.type) {
|
||||
file.WriteFormat(" Type: %s\n", GetDebugExceptionString(m_exception_info.type));
|
||||
file.WriteFormat(" Address: %s\n", m_module_list->GetFormattedAddressString(m_exception_info.address));
|
||||
switch (m_exception_info.type) {
|
||||
case svc::DebugException_UndefinedInstruction:
|
||||
file.WriteFormat(" Opcode: %08x\n", this->exception_info.specific.undefined_instruction.insn);
|
||||
file.WriteFormat(" Opcode: %08x\n", m_exception_info.specific.undefined_instruction.insn);
|
||||
break;
|
||||
case svc::DebugException_DataAbort:
|
||||
case svc::DebugException_AlignmentFault:
|
||||
if (this->exception_info.specific.raw != this->exception_info.address) {
|
||||
file.WriteFormat(" Fault Address: %s\n", this->module_list->GetFormattedAddressString(this->exception_info.specific.raw));
|
||||
if (m_exception_info.specific.raw != m_exception_info.address) {
|
||||
file.WriteFormat(" Fault Address: %s\n", m_module_list->GetFormattedAddressString(m_exception_info.specific.raw));
|
||||
}
|
||||
break;
|
||||
case svc::DebugException_UndefinedSystemCall:
|
||||
file.WriteFormat(" Svc Id: 0x%02x\n", this->exception_info.specific.undefined_system_call.id);
|
||||
file.WriteFormat(" Svc Id: 0x%02x\n", m_exception_info.specific.undefined_system_call.id);
|
||||
break;
|
||||
case svc::DebugException_UserBreak:
|
||||
file.WriteFormat(" Break Reason: 0x%x\n", this->exception_info.specific.user_break.break_reason);
|
||||
file.WriteFormat(" Break Address: %s\n", this->module_list->GetFormattedAddressString(this->exception_info.specific.user_break.address));
|
||||
file.WriteFormat(" Break Size: 0x%lx\n", this->exception_info.specific.user_break.size);
|
||||
file.WriteFormat(" Break Reason: 0x%x\n", m_exception_info.specific.user_break.break_reason);
|
||||
file.WriteFormat(" Break Address: %s\n", m_module_list->GetFormattedAddressString(m_exception_info.specific.user_break.address));
|
||||
file.WriteFormat(" Break Size: 0x%lx\n", m_exception_info.specific.user_break.size);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -403,23 +403,23 @@ namespace ams::creport {
|
|||
|
||||
/* Crashed Thread Info. */
|
||||
file.WriteFormat("Crashed Thread Info:\n");
|
||||
this->crashed_thread.SaveToFile(file);
|
||||
m_crashed_thread.SaveToFile(file);
|
||||
|
||||
/* Dying Message. */
|
||||
if (hos::GetVersion() >= hos::Version_5_0_0 && this->dying_message_size != 0) {
|
||||
if (hos::GetVersion() >= hos::Version_5_0_0 && m_dying_message_size != 0) {
|
||||
file.WriteFormat("Dying Message Info:\n");
|
||||
file.WriteFormat(" Address: 0x%s\n", this->module_list->GetFormattedAddressString(this->dying_message_address));
|
||||
file.WriteFormat(" Size: 0x%016lx\n", this->dying_message_size);
|
||||
file.DumpMemory( " Dying Message: ", this->dying_message, this->dying_message_size);
|
||||
file.WriteFormat(" Address: 0x%s\n", m_module_list->GetFormattedAddressString(m_dying_message_address));
|
||||
file.WriteFormat(" Size: 0x%016lx\n", m_dying_message_size);
|
||||
file.DumpMemory( " Dying Message: ", m_dying_message, m_dying_message_size);
|
||||
}
|
||||
|
||||
/* Module Info. */
|
||||
file.WriteFormat("Module Info:\n");
|
||||
this->module_list->SaveToFile(file);
|
||||
m_module_list->SaveToFile(file);
|
||||
|
||||
/* Thread Info. */
|
||||
file.WriteFormat("Thread Report:\n");
|
||||
this->thread_list->SaveToFile(file);
|
||||
m_thread_list->SaveToFile(file);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,66 +25,66 @@ namespace ams::creport {
|
|||
static constexpr size_t MemoryHeapSize = 512_KB;
|
||||
static_assert(MemoryHeapSize >= DyingMessageSizeMax + sizeof(ModuleList) + sizeof(ThreadList) + os::MemoryPageSize);
|
||||
private:
|
||||
os::NativeHandle debug_handle = os::InvalidNativeHandle;
|
||||
bool has_extra_info = true;
|
||||
Result result = creport::ResultIncompleteReport();
|
||||
os::NativeHandle m_debug_handle = os::InvalidNativeHandle;
|
||||
bool m_has_extra_info = true;
|
||||
Result m_result = creport::ResultIncompleteReport();
|
||||
|
||||
/* Meta, used for building module/thread list. */
|
||||
ThreadTlsMap thread_tls_map = {};
|
||||
ThreadTlsMap m_thread_tls_map = {};
|
||||
|
||||
/* Attach process info. */
|
||||
svc::DebugInfoCreateProcess process_info = {};
|
||||
u64 dying_message_address = 0;
|
||||
u64 dying_message_size = 0;
|
||||
u8 *dying_message = nullptr;
|
||||
svc::DebugInfoCreateProcess m_process_info = {};
|
||||
u64 m_dying_message_address = 0;
|
||||
u64 m_dying_message_size = 0;
|
||||
u8 *m_dying_message = nullptr;
|
||||
|
||||
/* Exception info. */
|
||||
svc::DebugInfoException exception_info = {};
|
||||
u64 module_base_address = 0;
|
||||
u64 crashed_thread_id = 0;
|
||||
ThreadInfo crashed_thread;
|
||||
svc::DebugInfoException m_exception_info = {};
|
||||
u64 m_module_base_address = 0;
|
||||
u64 m_crashed_thread_id = 0;
|
||||
ThreadInfo m_crashed_thread;
|
||||
|
||||
/* Lists. */
|
||||
ModuleList *module_list = nullptr;
|
||||
ThreadList *thread_list = nullptr;
|
||||
ModuleList *m_module_list = nullptr;
|
||||
ThreadList *m_thread_list = nullptr;
|
||||
|
||||
/* Memory heap. */
|
||||
lmem::HeapHandle heap_handle = nullptr;
|
||||
u8 heap_storage[MemoryHeapSize] = {};
|
||||
lmem::HeapHandle m_heap_handle = nullptr;
|
||||
u8 m_heap_storage[MemoryHeapSize] = {};
|
||||
public:
|
||||
constexpr CrashReport() = default;
|
||||
|
||||
Result GetResult() const {
|
||||
return this->result;
|
||||
return m_result;
|
||||
}
|
||||
|
||||
bool IsComplete() const {
|
||||
return !ResultIncompleteReport::Includes(this->result);
|
||||
return !ResultIncompleteReport::Includes(m_result);
|
||||
}
|
||||
|
||||
bool IsOpen() const {
|
||||
return this->debug_handle != os::InvalidNativeHandle;
|
||||
return m_debug_handle != os::InvalidNativeHandle;
|
||||
}
|
||||
|
||||
bool IsApplication() const {
|
||||
return (this->process_info.flags & svc::CreateProcessFlag_IsApplication) != 0;
|
||||
return (m_process_info.flags & svc::CreateProcessFlag_IsApplication) != 0;
|
||||
}
|
||||
|
||||
bool Is64Bit() const {
|
||||
return (this->process_info.flags & svc::CreateProcessFlag_Is64Bit) != 0;
|
||||
return (m_process_info.flags & svc::CreateProcessFlag_Is64Bit) != 0;
|
||||
}
|
||||
|
||||
bool IsUserBreak() const {
|
||||
return this->exception_info.type == svc::DebugException_UserBreak;
|
||||
return m_exception_info.type == svc::DebugException_UserBreak;
|
||||
}
|
||||
|
||||
bool OpenProcess(os::ProcessId process_id) {
|
||||
return R_SUCCEEDED(svc::DebugActiveProcess(std::addressof(this->debug_handle), process_id.value));
|
||||
return R_SUCCEEDED(svc::DebugActiveProcess(std::addressof(m_debug_handle), process_id.value));
|
||||
}
|
||||
|
||||
void Close() {
|
||||
os::CloseNativeHandle(this->debug_handle);
|
||||
this->debug_handle = os::InvalidNativeHandle;
|
||||
os::CloseNativeHandle(m_debug_handle);
|
||||
m_debug_handle = os::InvalidNativeHandle;
|
||||
}
|
||||
|
||||
void Initialize();
|
||||
|
|
|
@ -46,12 +46,12 @@ namespace ams::creport {
|
|||
}
|
||||
|
||||
void ModuleList::SaveToFile(ScopedFile &file) {
|
||||
file.WriteFormat(" Number of Modules: %zu\n", this->num_modules);
|
||||
for (size_t i = 0; i < this->num_modules; i++) {
|
||||
const auto& module = this->modules[i];
|
||||
file.WriteFormat(" Number of Modules: %zu\n", m_num_modules);
|
||||
for (size_t i = 0; i < m_num_modules; i++) {
|
||||
const auto& module = m_modules[i];
|
||||
file.WriteFormat(" Module %02zu:\n", i);
|
||||
file.WriteFormat(" Address: %016lx-%016lx\n", module.start_address, module.end_address);
|
||||
if (std::strcmp(this->modules[i].name, "") != 0) {
|
||||
if (std::strcmp(m_modules[i].name, "") != 0) {
|
||||
file.WriteFormat(" Name: %s\n", module.name);
|
||||
}
|
||||
file.DumpMemory(" Build Id: ", module.build_id, sizeof(module.build_id));
|
||||
|
@ -60,7 +60,7 @@ namespace ams::creport {
|
|||
|
||||
void ModuleList::FindModulesFromThreadInfo(os::NativeHandle debug_handle, const ThreadInfo &thread) {
|
||||
/* Set the debug handle, for access in other member functions. */
|
||||
this->debug_handle = debug_handle;
|
||||
m_debug_handle = debug_handle;
|
||||
|
||||
/* Try to add the thread's PC. */
|
||||
this->TryAddModule(thread.GetPC());
|
||||
|
@ -82,25 +82,25 @@ namespace ams::creport {
|
|||
}
|
||||
|
||||
/* Check whether we already have this module. */
|
||||
for (size_t i = 0; i < this->num_modules; i++) {
|
||||
if (this->modules[i].start_address <= base_address && base_address < this->modules[i].end_address) {
|
||||
for (size_t i = 0; i < m_num_modules; i++) {
|
||||
if (m_modules[i].start_address <= base_address && base_address < m_modules[i].end_address) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add all contiguous modules. */
|
||||
uintptr_t cur_address = base_address;
|
||||
while (this->num_modules < ModuleCountMax) {
|
||||
while (m_num_modules < ModuleCountMax) {
|
||||
/* Get the region extents. */
|
||||
svc::MemoryInfo mi;
|
||||
svc::PageInfo pi;
|
||||
if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), this->debug_handle, cur_address))) {
|
||||
if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), m_debug_handle, cur_address))) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Parse module. */
|
||||
if (mi.permission == svc::MemoryPermission_ReadExecute) {
|
||||
auto& module = this->modules[this->num_modules++];
|
||||
auto& module = m_modules[m_num_modules++];
|
||||
module.start_address = mi.base_address;
|
||||
module.end_address = mi.base_address + mi.size;
|
||||
GetModuleName(module.name, module.start_address, module.end_address);
|
||||
|
@ -129,20 +129,20 @@ namespace ams::creport {
|
|||
/* Query the memory region our guess falls in. */
|
||||
svc::MemoryInfo mi;
|
||||
svc::PageInfo pi;
|
||||
if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), this->debug_handle, guess))) {
|
||||
if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), m_debug_handle, guess))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If we fall into a RW region, it may be rwdata. Query the region before it, which may be rodata or text. */
|
||||
if (mi.permission == svc::MemoryPermission_ReadWrite) {
|
||||
if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), debug_handle, mi.base_address - 4))) {
|
||||
if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), m_debug_handle, mi.base_address - 4))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we fall into an RO region, it may be rodata. Query the region before it, which should be text. */
|
||||
if (mi.permission == svc::MemoryPermission_Read) {
|
||||
if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), debug_handle, mi.base_address - 4))) {
|
||||
if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), m_debug_handle, mi.base_address - 4))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -155,7 +155,7 @@ namespace ams::creport {
|
|||
/* Modules are a series of contiguous (text/rodata/rwdata) regions. */
|
||||
/* Iterate backwards until we find unmapped memory, to find the start of the set of modules loaded here. */
|
||||
while (mi.base_address > 0) {
|
||||
if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), debug_handle, mi.base_address - 4))) {
|
||||
if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), m_debug_handle, mi.base_address - 4))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -181,7 +181,7 @@ namespace ams::creport {
|
|||
svc::PageInfo pi;
|
||||
|
||||
/* Verify .rodata is read-only. */
|
||||
if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), this->debug_handle, ro_start_address)) || mi.permission != svc::MemoryPermission_Read) {
|
||||
if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), m_debug_handle, ro_start_address)) || mi.permission != svc::MemoryPermission_Read) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -189,7 +189,7 @@ namespace ams::creport {
|
|||
const u64 rw_start_address = mi.base_address + mi.size;
|
||||
|
||||
/* Read start of .rodata. */
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(rodata_start)), this->debug_handle, ro_start_address, sizeof(rodata_start)))) {
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(rodata_start)), m_debug_handle, ro_start_address, sizeof(rodata_start)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -228,13 +228,13 @@ namespace ams::creport {
|
|||
/* Verify .rodata is read-only. */
|
||||
svc::MemoryInfo mi;
|
||||
svc::PageInfo pi;
|
||||
if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), this->debug_handle, ro_start_address)) || mi.permission != svc::MemoryPermission_Read) {
|
||||
if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), m_debug_handle, ro_start_address)) || mi.permission != svc::MemoryPermission_Read) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* We want to read the last two pages of .rodata. */
|
||||
const size_t read_size = mi.size >= sizeof(g_last_rodata_pages) ? sizeof(g_last_rodata_pages) : (sizeof(g_last_rodata_pages) / 2);
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(g_last_rodata_pages), this->debug_handle, mi.base_address + mi.size - read_size, read_size))) {
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(g_last_rodata_pages), m_debug_handle, mi.base_address + mi.size - read_size, read_size))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -249,18 +249,18 @@ namespace ams::creport {
|
|||
|
||||
const char *ModuleList::GetFormattedAddressString(uintptr_t address) {
|
||||
/* Print default formatted string. */
|
||||
util::SNPrintf(this->address_str_buf, sizeof(this->address_str_buf), "%016lx", address);
|
||||
util::SNPrintf(m_address_str_buf, sizeof(m_address_str_buf), "%016lx", address);
|
||||
|
||||
/* See if the address is inside a module, for pretty-printing. */
|
||||
for (size_t i = 0; i < this->num_modules; i++) {
|
||||
const auto& module = this->modules[i];
|
||||
for (size_t i = 0; i < m_num_modules; i++) {
|
||||
const auto& module = m_modules[i];
|
||||
if (module.start_address <= address && address < module.end_address) {
|
||||
util::SNPrintf(this->address_str_buf, sizeof(this->address_str_buf), "%016lx (%s + 0x%lx)", address, module.name, address - module.start_address);
|
||||
util::SNPrintf(m_address_str_buf, sizeof(m_address_str_buf), "%016lx (%s + 0x%lx)", address, module.name, address - module.start_address);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return this->address_str_buf;
|
||||
return m_address_str_buf;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,23 +32,23 @@ namespace ams::creport {
|
|||
u64 end_address;
|
||||
};
|
||||
private:
|
||||
os::NativeHandle debug_handle;
|
||||
size_t num_modules;
|
||||
ModuleInfo modules[ModuleCountMax];
|
||||
os::NativeHandle m_debug_handle;
|
||||
size_t m_num_modules;
|
||||
ModuleInfo m_modules[ModuleCountMax];
|
||||
|
||||
/* For pretty-printing. */
|
||||
char address_str_buf[0x280];
|
||||
char m_address_str_buf[0x280];
|
||||
public:
|
||||
ModuleList() : debug_handle(os::InvalidNativeHandle), num_modules(0) {
|
||||
std::memset(this->modules, 0, sizeof(this->modules));
|
||||
ModuleList() : m_debug_handle(os::InvalidNativeHandle), m_num_modules(0) {
|
||||
std::memset(m_modules, 0, sizeof(m_modules));
|
||||
}
|
||||
|
||||
size_t GetModuleCount() const {
|
||||
return this->num_modules;
|
||||
return m_num_modules;
|
||||
}
|
||||
|
||||
u64 GetModuleStartAddress(size_t i) const {
|
||||
return this->modules[i].start_address;
|
||||
return m_modules[i].start_address;
|
||||
}
|
||||
|
||||
void FindModulesFromThreadInfo(os::NativeHandle debug_handle, const ThreadInfo &thread);
|
||||
|
|
|
@ -90,8 +90,8 @@ namespace ams::creport {
|
|||
}
|
||||
|
||||
/* Advance, if we write successfully. */
|
||||
if (R_SUCCEEDED(fs::WriteFile(this->file, this->offset, data, size, fs::WriteOption::Flush))) {
|
||||
this->offset += size;
|
||||
if (R_SUCCEEDED(fs::WriteFile(m_file, m_offset, data, size, fs::WriteOption::Flush))) {
|
||||
m_offset += size;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,24 +22,24 @@ namespace ams::creport {
|
|||
NON_COPYABLE(ScopedFile);
|
||||
NON_MOVEABLE(ScopedFile);
|
||||
private:
|
||||
fs::FileHandle file;
|
||||
s64 offset;
|
||||
bool opened;
|
||||
fs::FileHandle m_file;
|
||||
s64 m_offset;
|
||||
bool m_opened;
|
||||
public:
|
||||
ScopedFile(const char *path) : file(), offset(), opened(false) {
|
||||
ScopedFile(const char *path) : m_file(), m_offset(), m_opened(false) {
|
||||
if (R_SUCCEEDED(fs::CreateFile(path, 0))) {
|
||||
this->opened = R_SUCCEEDED(fs::OpenFile(std::addressof(this->file), path, fs::OpenMode_Write | fs::OpenMode_AllowAppend));
|
||||
m_opened = R_SUCCEEDED(fs::OpenFile(std::addressof(m_file), path, fs::OpenMode_Write | fs::OpenMode_AllowAppend));
|
||||
}
|
||||
}
|
||||
|
||||
~ScopedFile() {
|
||||
if (this->opened) {
|
||||
fs::CloseFile(file);
|
||||
if (m_opened) {
|
||||
fs::CloseFile(m_file);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsOpen() const {
|
||||
return this->opened;
|
||||
return m_opened;
|
||||
}
|
||||
|
||||
void WriteString(const char *str);
|
||||
|
|
|
@ -61,67 +61,67 @@ namespace ams::creport {
|
|||
}
|
||||
|
||||
void ThreadList::SaveToFile(ScopedFile &file) {
|
||||
file.WriteFormat("Number of Threads: %02zu\n", this->thread_count);
|
||||
for (size_t i = 0; i < this->thread_count; i++) {
|
||||
file.WriteFormat("Number of Threads: %02zu\n", m_thread_count);
|
||||
for (size_t i = 0; i < m_thread_count; i++) {
|
||||
file.WriteFormat("Threads[%02zu]:\n", i);
|
||||
this->threads[i].SaveToFile(file);
|
||||
m_threads[i].SaveToFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadInfo::SaveToFile(ScopedFile &file) {
|
||||
file.WriteFormat(" Thread ID: %016lx\n", this->thread_id);
|
||||
if (std::strcmp(this->name, "") != 0) {
|
||||
file.WriteFormat(" Thread Name: %s\n", this->name);
|
||||
file.WriteFormat(" Thread ID: %016lx\n", m_thread_id);
|
||||
if (std::strcmp(m_name, "") != 0) {
|
||||
file.WriteFormat(" Thread Name: %s\n", m_name);
|
||||
}
|
||||
if (this->stack_top != 0) {
|
||||
file.WriteFormat(" Stack Region: %016lx-%016lx\n", this->stack_bottom, this->stack_top);
|
||||
if (m_stack_top != 0) {
|
||||
file.WriteFormat(" Stack Region: %016lx-%016lx\n", m_stack_bottom, m_stack_top);
|
||||
}
|
||||
file.WriteFormat(" Registers:\n");
|
||||
{
|
||||
for (unsigned int i = 0; i <= 28; i++) {
|
||||
file.WriteFormat(" X[%02u]: %s\n", i, this->module_list->GetFormattedAddressString(this->context.r[i]));
|
||||
file.WriteFormat(" X[%02u]: %s\n", i, m_module_list->GetFormattedAddressString(m_context.r[i]));
|
||||
}
|
||||
file.WriteFormat(" FP: %s\n", this->module_list->GetFormattedAddressString(this->context.fp));
|
||||
file.WriteFormat(" LR: %s\n", this->module_list->GetFormattedAddressString(this->context.lr));
|
||||
file.WriteFormat(" SP: %s\n", this->module_list->GetFormattedAddressString(this->context.sp));
|
||||
file.WriteFormat(" PC: %s\n", this->module_list->GetFormattedAddressString(this->context.pc));
|
||||
file.WriteFormat(" FP: %s\n", m_module_list->GetFormattedAddressString(m_context.fp));
|
||||
file.WriteFormat(" LR: %s\n", m_module_list->GetFormattedAddressString(m_context.lr));
|
||||
file.WriteFormat(" SP: %s\n", m_module_list->GetFormattedAddressString(m_context.sp));
|
||||
file.WriteFormat(" PC: %s\n", m_module_list->GetFormattedAddressString(m_context.pc));
|
||||
}
|
||||
if (this->stack_trace_size != 0) {
|
||||
if (m_stack_trace_size != 0) {
|
||||
file.WriteFormat(" Stack Trace:\n");
|
||||
for (size_t i = 0; i < this->stack_trace_size; i++) {
|
||||
file.WriteFormat(" ReturnAddress[%02zu]: %s\n", i, this->module_list->GetFormattedAddressString(this->stack_trace[i]));
|
||||
for (size_t i = 0; i < m_stack_trace_size; i++) {
|
||||
file.WriteFormat(" ReturnAddress[%02zu]: %s\n", i, m_module_list->GetFormattedAddressString(m_stack_trace[i]));
|
||||
}
|
||||
}
|
||||
if (this->stack_dump_base != 0) {
|
||||
if (m_stack_dump_base != 0) {
|
||||
file.WriteFormat(" Stack Dump: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
|
||||
for (size_t i = 0; i < 0x10; i++) {
|
||||
const size_t ofs = i * 0x10;
|
||||
file.WriteFormat(" %012lx %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
this->stack_dump_base + ofs, this->stack_dump[ofs + 0], this->stack_dump[ofs + 1], this->stack_dump[ofs + 2], this->stack_dump[ofs + 3], this->stack_dump[ofs + 4], this->stack_dump[ofs + 5], this->stack_dump[ofs + 6], this->stack_dump[ofs + 7],
|
||||
this->stack_dump[ofs + 8], this->stack_dump[ofs + 9], this->stack_dump[ofs + 10], this->stack_dump[ofs + 11], this->stack_dump[ofs + 12], this->stack_dump[ofs + 13], this->stack_dump[ofs + 14], this->stack_dump[ofs + 15]);
|
||||
m_stack_dump_base + ofs, m_stack_dump[ofs + 0], m_stack_dump[ofs + 1], m_stack_dump[ofs + 2], m_stack_dump[ofs + 3], m_stack_dump[ofs + 4], m_stack_dump[ofs + 5], m_stack_dump[ofs + 6], m_stack_dump[ofs + 7],
|
||||
m_stack_dump[ofs + 8], m_stack_dump[ofs + 9], m_stack_dump[ofs + 10], m_stack_dump[ofs + 11], m_stack_dump[ofs + 12], m_stack_dump[ofs + 13], m_stack_dump[ofs + 14], m_stack_dump[ofs + 15]);
|
||||
}
|
||||
}
|
||||
if (this->tls_address != 0) {
|
||||
file.WriteFormat(" TLS Address: %016lx\n", this->tls_address);
|
||||
if (m_tls_address != 0) {
|
||||
file.WriteFormat(" TLS Address: %016lx\n", m_tls_address);
|
||||
file.WriteFormat(" TLS Dump: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
|
||||
for (size_t i = 0; i < 0x10; i++) {
|
||||
const size_t ofs = i * 0x10;
|
||||
file.WriteFormat(" %012lx %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
this->tls_address + ofs, this->tls[ofs + 0], this->tls[ofs + 1], this->tls[ofs + 2], this->tls[ofs + 3], this->tls[ofs + 4], this->tls[ofs + 5], this->tls[ofs + 6], this->tls[ofs + 7],
|
||||
this->tls[ofs + 8], this->tls[ofs + 9], this->tls[ofs + 10], this->tls[ofs + 11], this->tls[ofs + 12], this->tls[ofs + 13], this->tls[ofs + 14], this->tls[ofs + 15]);
|
||||
m_tls_address + ofs, m_tls[ofs + 0], m_tls[ofs + 1], m_tls[ofs + 2], m_tls[ofs + 3], m_tls[ofs + 4], m_tls[ofs + 5], m_tls[ofs + 6], m_tls[ofs + 7],
|
||||
m_tls[ofs + 8], m_tls[ofs + 9], m_tls[ofs + 10], m_tls[ofs + 11], m_tls[ofs + 12], m_tls[ofs + 13], m_tls[ofs + 14], m_tls[ofs + 15]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ThreadInfo::ReadFromProcess(os::NativeHandle debug_handle, ThreadTlsMap &tls_map, u64 thread_id, bool is_64_bit) {
|
||||
/* Set thread id. */
|
||||
this->thread_id = thread_id;
|
||||
m_thread_id = thread_id;
|
||||
|
||||
/* Verify that the thread is running or waiting. */
|
||||
{
|
||||
u64 _;
|
||||
u32 _thread_state;
|
||||
if (R_FAILED(svc::GetDebugThreadParam(&_, &_thread_state, debug_handle, this->thread_id, svc::DebugThreadParam_State))) {
|
||||
if (R_FAILED(svc::GetDebugThreadParam(&_, &_thread_state, debug_handle, m_thread_id, svc::DebugThreadParam_State))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -132,24 +132,24 @@ namespace ams::creport {
|
|||
}
|
||||
|
||||
/* Get the thread context. */
|
||||
if (R_FAILED(svc::GetDebugThreadContext(std::addressof(this->context), debug_handle, this->thread_id, svc::ThreadContextFlag_All))) {
|
||||
if (R_FAILED(svc::GetDebugThreadContext(std::addressof(m_context), debug_handle, m_thread_id, svc::ThreadContextFlag_All))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* In aarch32 mode svc::GetDebugThreadContext does not set the LR, FP, and SP registers correctly. */
|
||||
if (!is_64_bit) {
|
||||
this->context.fp = this->context.r[11];
|
||||
this->context.sp = this->context.r[13];
|
||||
this->context.lr = this->context.r[14];
|
||||
m_context.fp = m_context.r[11];
|
||||
m_context.sp = m_context.r[13];
|
||||
m_context.lr = m_context.r[14];
|
||||
}
|
||||
|
||||
/* Read TLS, if present. */
|
||||
/* TODO: struct definitions for nnSdk's ThreadType/TLS Layout? */
|
||||
this->tls_address = 0;
|
||||
if (tls_map.GetThreadTls(std::addressof(this->tls_address), thread_id)) {
|
||||
m_tls_address = 0;
|
||||
if (tls_map.GetThreadTls(std::addressof(m_tls_address), thread_id)) {
|
||||
u8 thread_tls[sizeof(svc::ThreadLocalRegion)];
|
||||
if (R_SUCCEEDED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(thread_tls), debug_handle, this->tls_address, sizeof(thread_tls)))) {
|
||||
std::memcpy(this->tls, thread_tls, sizeof(this->tls));
|
||||
if (R_SUCCEEDED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(thread_tls), debug_handle, m_tls_address, sizeof(thread_tls)))) {
|
||||
std::memcpy(m_tls, thread_tls, sizeof(m_tls));
|
||||
/* Try to detect libnx threads, and skip name parsing then. */
|
||||
if (*(reinterpret_cast<u32 *>(std::addressof(thread_tls[0x1E0]))) != LibnxThreadVarMagic) {
|
||||
u8 thread_type[0x1C0];
|
||||
|
@ -161,12 +161,12 @@ namespace ams::creport {
|
|||
/* Check thread name is actually at thread name. */
|
||||
static_assert(0x1A8 - 0x188 == NameLengthMax, "NameLengthMax definition!");
|
||||
if (*(reinterpret_cast<u64 *>(std::addressof(thread_type[0x1A8]))) == thread_type_addr + 0x188) {
|
||||
std::memcpy(this->name, thread_type + 0x188, NameLengthMax);
|
||||
std::memcpy(m_name, thread_type + 0x188, NameLengthMax);
|
||||
}
|
||||
} else if (thread_version == 1) {
|
||||
static_assert(0x1A0 - 0x180 == NameLengthMax, "NameLengthMax definition!");
|
||||
if (*(reinterpret_cast<u64 *>(std::addressof(thread_type[0x1A0]))) == thread_type_addr + 0x180) {
|
||||
std::memcpy(this->name, thread_type + 0x180, NameLengthMax);
|
||||
std::memcpy(m_name, thread_type + 0x180, NameLengthMax);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -179,9 +179,9 @@ namespace ams::creport {
|
|||
|
||||
/* Dump stack trace. */
|
||||
if (is_64_bit) {
|
||||
ReadStackTrace<u64>(std::addressof(this->stack_trace_size), this->stack_trace, StackTraceSizeMax, debug_handle, this->context.fp);
|
||||
ReadStackTrace<u64>(std::addressof(m_stack_trace_size), m_stack_trace, StackTraceSizeMax, debug_handle, m_context.fp);
|
||||
} else {
|
||||
ReadStackTrace<u32>(std::addressof(this->stack_trace_size), this->stack_trace, StackTraceSizeMax, debug_handle, this->context.fp);
|
||||
ReadStackTrace<u32>(std::addressof(m_stack_trace_size), m_stack_trace, StackTraceSizeMax, debug_handle, m_context.fp);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -191,7 +191,7 @@ namespace ams::creport {
|
|||
/* Query stack region. */
|
||||
svc::MemoryInfo mi;
|
||||
svc::PageInfo pi;
|
||||
if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), debug_handle, this->context.sp))) {
|
||||
if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), debug_handle, m_context.sp))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -204,56 +204,56 @@ namespace ams::creport {
|
|||
}
|
||||
|
||||
/* Save stack extents. */
|
||||
this->stack_bottom = mi.base_address;
|
||||
this->stack_top = mi.base_address + mi.size;
|
||||
m_stack_bottom = mi.base_address;
|
||||
m_stack_top = mi.base_address + mi.size;
|
||||
|
||||
/* We always want to dump 0x100 of stack, starting from the lowest 0x10-byte aligned address below the stack pointer. */
|
||||
/* Note: if the stack pointer is below the stack bottom, we will start dumping from the stack bottom. */
|
||||
this->stack_dump_base = std::min(std::max(this->context.sp & ~0xFul, this->stack_bottom), this->stack_top - sizeof(this->stack_dump));
|
||||
m_stack_dump_base = std::min(std::max(m_context.sp & ~0xFul, m_stack_bottom), m_stack_top - sizeof(m_stack_dump));
|
||||
|
||||
/* Try to read stack. */
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(this->stack_dump), debug_handle, this->stack_dump_base, sizeof(this->stack_dump)))) {
|
||||
this->stack_dump_base = 0;
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(m_stack_dump), debug_handle, m_stack_dump_base, sizeof(m_stack_dump)))) {
|
||||
m_stack_dump_base = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadInfo::DumpBinary(ScopedFile &file) {
|
||||
/* Dump id and context. */
|
||||
file.Write(std::addressof(this->thread_id), sizeof(this->thread_id));
|
||||
file.Write(std::addressof(this->context), sizeof(this->context));
|
||||
file.Write(std::addressof(m_thread_id), sizeof(m_thread_id));
|
||||
file.Write(std::addressof(m_context), sizeof(m_context));
|
||||
|
||||
/* Dump TLS info and name. */
|
||||
file.Write(std::addressof(this->tls_address), sizeof(this->tls_address));
|
||||
file.Write(std::addressof(this->tls), sizeof(this->tls));
|
||||
file.Write(std::addressof(this->name), sizeof(this->name));
|
||||
file.Write(std::addressof(m_tls_address), sizeof(m_tls_address));
|
||||
file.Write(std::addressof(m_tls), sizeof(m_tls));
|
||||
file.Write(std::addressof(m_name), sizeof(m_name));
|
||||
|
||||
/* Dump stack extents and stack dump. */
|
||||
file.Write(std::addressof(this->stack_bottom), sizeof(this->stack_bottom));
|
||||
file.Write(std::addressof(this->stack_top), sizeof(this->stack_top));
|
||||
file.Write(std::addressof(this->stack_dump_base), sizeof(this->stack_dump_base));
|
||||
file.Write(std::addressof(this->stack_dump), sizeof(this->stack_dump));
|
||||
file.Write(std::addressof(m_stack_bottom), sizeof(m_stack_bottom));
|
||||
file.Write(std::addressof(m_stack_top), sizeof(m_stack_top));
|
||||
file.Write(std::addressof(m_stack_dump_base), sizeof(m_stack_dump_base));
|
||||
file.Write(std::addressof(m_stack_dump), sizeof(m_stack_dump));
|
||||
|
||||
/* Dump stack trace. */
|
||||
{
|
||||
const u64 sts = this->stack_trace_size;
|
||||
const u64 sts = m_stack_trace_size;
|
||||
file.Write(std::addressof(sts), sizeof(sts));
|
||||
}
|
||||
file.Write(this->stack_trace, this->stack_trace_size);
|
||||
file.Write(m_stack_trace, m_stack_trace_size);
|
||||
}
|
||||
|
||||
void ThreadList::DumpBinary(ScopedFile &file, u64 crashed_thread_id) {
|
||||
const u32 magic = DumpedThreadInfoMagic;
|
||||
const u32 count = this->thread_count;
|
||||
const u32 count = m_thread_count;
|
||||
file.Write(std::addressof(magic), sizeof(magic));
|
||||
file.Write(std::addressof(count), sizeof(count));
|
||||
file.Write(std::addressof(crashed_thread_id), sizeof(crashed_thread_id));
|
||||
for (size_t i = 0; i < this->thread_count; i++) {
|
||||
this->threads[i].DumpBinary(file);
|
||||
for (size_t i = 0; i < m_thread_count; i++) {
|
||||
m_threads[i].DumpBinary(file);
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadList::ReadFromProcess(os::NativeHandle debug_handle, ThreadTlsMap &tls_map, bool is_64_bit) {
|
||||
this->thread_count = 0;
|
||||
m_thread_count = 0;
|
||||
|
||||
/* Get thread list. */
|
||||
s32 num_threads;
|
||||
|
@ -267,8 +267,8 @@ namespace ams::creport {
|
|||
|
||||
/* Parse thread infos. */
|
||||
for (s32 i = 0; i < num_threads; i++) {
|
||||
if (this->threads[this->thread_count].ReadFromProcess(debug_handle, tls_map, thread_ids[i], is_64_bit)) {
|
||||
this->thread_count++;
|
||||
if (m_threads[m_thread_count].ReadFromProcess(debug_handle, tls_map, thread_ids[i], is_64_bit)) {
|
||||
m_thread_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,53 +60,53 @@ namespace ams::creport {
|
|||
static constexpr size_t StackTraceSizeMax = 0x20;
|
||||
static constexpr size_t NameLengthMax = 0x20;
|
||||
private:
|
||||
svc::ThreadContext context = {};
|
||||
u64 thread_id = 0;
|
||||
u64 stack_top = 0;
|
||||
u64 stack_bottom = 0;
|
||||
u64 stack_trace[StackTraceSizeMax] = {};
|
||||
size_t stack_trace_size = 0;
|
||||
u64 tls_address = 0;
|
||||
u8 tls[0x100] = {};
|
||||
u64 stack_dump_base = 0;
|
||||
u8 stack_dump[0x100] = {};
|
||||
char name[NameLengthMax + 1] = {};
|
||||
ModuleList *module_list = nullptr;
|
||||
svc::ThreadContext m_context = {};
|
||||
u64 m_thread_id = 0;
|
||||
u64 m_stack_top = 0;
|
||||
u64 m_stack_bottom = 0;
|
||||
u64 m_stack_trace[StackTraceSizeMax] = {};
|
||||
size_t m_stack_trace_size = 0;
|
||||
u64 m_tls_address = 0;
|
||||
u8 m_tls[0x100] = {};
|
||||
u64 m_stack_dump_base = 0;
|
||||
u8 m_stack_dump[0x100] = {};
|
||||
char m_name[NameLengthMax + 1] = {};
|
||||
ModuleList *m_module_list = nullptr;
|
||||
public:
|
||||
u64 GetGeneralPurposeRegister(size_t i) const {
|
||||
return this->context.r[i];
|
||||
return m_context.r[i];
|
||||
}
|
||||
|
||||
u64 GetPC() const {
|
||||
return this->context.pc;
|
||||
return m_context.pc;
|
||||
}
|
||||
|
||||
u64 GetLR() const {
|
||||
return this->context.lr;
|
||||
return m_context.lr;
|
||||
}
|
||||
|
||||
u64 GetFP() const {
|
||||
return this->context.fp;
|
||||
return m_context.fp;
|
||||
}
|
||||
|
||||
u64 GetSP() const {
|
||||
return this->context.sp;
|
||||
return m_context.sp;
|
||||
}
|
||||
|
||||
u64 GetThreadId() const {
|
||||
return this->thread_id;
|
||||
return m_thread_id;
|
||||
}
|
||||
|
||||
size_t GetStackTraceSize() const {
|
||||
return this->stack_trace_size;
|
||||
return m_stack_trace_size;
|
||||
}
|
||||
|
||||
u64 GetStackTrace(size_t i) const {
|
||||
return this->stack_trace[i];
|
||||
return m_stack_trace[i];
|
||||
}
|
||||
|
||||
void SetModuleList(ModuleList *ml) {
|
||||
this->module_list = ml;
|
||||
m_module_list = ml;
|
||||
}
|
||||
|
||||
bool ReadFromProcess(os::NativeHandle debug_handle, ThreadTlsMap &tls_map, u64 thread_id, bool is_64_bit);
|
||||
|
@ -118,20 +118,20 @@ namespace ams::creport {
|
|||
|
||||
class ThreadList {
|
||||
private:
|
||||
size_t thread_count = 0;
|
||||
ThreadInfo threads[ThreadCountMax];
|
||||
size_t m_thread_count = 0;
|
||||
ThreadInfo m_threads[ThreadCountMax];
|
||||
public:
|
||||
size_t GetThreadCount() const {
|
||||
return this->thread_count;
|
||||
return m_thread_count;
|
||||
}
|
||||
|
||||
const ThreadInfo &GetThreadInfo(size_t i) const {
|
||||
return this->threads[i];
|
||||
return m_threads[i];
|
||||
}
|
||||
|
||||
void SetModuleList(ModuleList *ml) {
|
||||
for (size_t i = 0; i < this->thread_count; i++) {
|
||||
this->threads[i].SetModuleList(ml);
|
||||
for (size_t i = 0; i < m_thread_count; i++) {
|
||||
m_threads[i].SetModuleList(ml);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue