diff --git a/stratosphere/creport/source/creport_code_info.cpp b/stratosphere/creport/source/creport_code_info.cpp index 2fc38240d..ba7ceb12c 100644 --- a/stratosphere/creport/source/creport_code_info.cpp +++ b/stratosphere/creport/source/creport_code_info.cpp @@ -2,6 +2,19 @@ #include #include "creport_code_info.hpp" +#include "creport_crash_report.hpp" + +void CodeList::SaveToFile(FILE *f_report) { + fprintf(f_report, " Number of Code Regions: %u\n", this->code_count); + for (unsigned int i = 0; i < this->code_count; i++) { + fprintf(f_report, " Code Region %02u:\n", i); + fprintf(f_report, " Address: %016lx-%016lx\n", this->code_infos[i].start_address, this->code_infos[i].end_address); + if (this->code_infos[i].name[0]) { + fprintf(f_report, " Name: %s\n", this->code_infos[i].name); + } + CrashReport::Memdump(f_report, " Build Id: ", this->code_infos[i].build_id, sizeof(this->code_infos[i].build_id)); + } +} void CodeList::ReadCodeRegionsFromProcess(Handle debug_handle, u64 pc, u64 lr) { u64 code_base; diff --git a/stratosphere/creport/source/creport_code_info.hpp b/stratosphere/creport/source/creport_code_info.hpp index 279609417..3f1527d2a 100644 --- a/stratosphere/creport/source/creport_code_info.hpp +++ b/stratosphere/creport/source/creport_code_info.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include "creport_debug_types.hpp" @@ -19,6 +20,7 @@ class CodeList { CodeList() : code_count(0) { } void ReadCodeRegionsFromProcess(Handle debug_handle, u64 pc, u64 lr); + void SaveToFile(FILE *f_report); private: bool TryFindCodeRegion(Handle debug_handle, u64 guess, u64 *address); void GetCodeInfoName(u64 debug_handle, u64 ro_address, char *name); diff --git a/stratosphere/creport/source/creport_crash_report.cpp b/stratosphere/creport/source/creport_crash_report.cpp index 4930afaec..7924fea86 100644 --- a/stratosphere/creport/source/creport_crash_report.cpp +++ b/stratosphere/creport/source/creport_crash_report.cpp @@ -7,43 +7,6 @@ #include "creport_crash_report.hpp" #include "creport_debug_types.hpp" -void CrashReport::EnsureReportDirectories() { - char path[FS_MAX_PATH]; - strcpy(path, "sdmc:/atmosphere"); - mkdir(path, S_IRWXU); - strcat(path, "/crash reports"); - mkdir(path, S_IRWXU); - strcat(path, "/dumps"); - mkdir(path, S_IRWXU); -} - -void CrashReport::SaveReport() { - /* TODO: Save the report to the SD card. */ - char report_path[FS_MAX_PATH]; - - /* Ensure path exists. */ - EnsureReportDirectories(); - - /* Get a timestamp. */ - u64 timestamp; - if (!GetCurrentTime(×tamp)) { - timestamp = svcGetSystemTick(); - } - - /* Open report file. */ - snprintf(report_path, sizeof(report_path) - 1, "sdmc:/atmosphere/crash reports/%020lu_%016lx.log", timestamp, process_info.title_id); - FILE *f_report = fopen(report_path, "w"); - if (f_report == NULL) { - return; - } - - fprintf(f_report, "Atmosphere Crash Report:\n"); - - /* TODO: Actually report about the crash. */ - - fclose(f_report); -} - void CrashReport::BuildReport(u64 pid, bool has_extra_info) { this->has_extra_info = has_extra_info; if (OpenProcess(pid)) { @@ -125,14 +88,13 @@ void CrashReport::HandleAttachProcess(DebugEventInfo &d) { } void CrashReport::HandleException(DebugEventInfo &d) { - this->exception_info = d.info.exception; switch (d.info.exception.type) { case DebugExceptionType::UndefinedInstruction: this->result = (Result)CrashReportResult::UndefinedInstruction; break; case DebugExceptionType::InstructionAbort: this->result = (Result)CrashReportResult::InstructionAbort; - this->exception_info.specific.raw = 0; + d.info.exception.specific.raw = 0; break; case DebugExceptionType::DataAbort: this->result = (Result)CrashReportResult::DataAbort; @@ -143,8 +105,8 @@ void CrashReport::HandleException(DebugEventInfo &d) { case DebugExceptionType::UserBreak: this->result = (Result)CrashReportResult::UserBreak; /* Try to parse out the user break result. */ - if (kernelAbove500() && IsAddressReadable(this->exception_info.specific.user_break.address, sizeof(this->result))) { - svcReadDebugProcessMemory(&this->result, this->debug_handle, this->exception_info.specific.user_break.address, sizeof(this->result)); + if (kernelAbove500() && IsAddressReadable(d.info.exception.specific.user_break.address, sizeof(this->result))) { + svcReadDebugProcessMemory(&this->result, this->debug_handle, d.info.exception.specific.user_break.address, sizeof(this->result)); } break; case DebugExceptionType::BadSvc: @@ -152,7 +114,7 @@ void CrashReport::HandleException(DebugEventInfo &d) { break; case DebugExceptionType::UnknownNine: this->result = (Result)CrashReportResult::UnknownNine; - this->exception_info.specific.raw = 0; + d.info.exception.specific.raw = 0; break; case DebugExceptionType::DebuggerAttached: case DebugExceptionType::BreakPoint: @@ -160,6 +122,7 @@ void CrashReport::HandleException(DebugEventInfo &d) { default: return; } + this->exception_info = d.info.exception; /* Parse crashing thread info. */ this->crashed_thread_info.ReadFromProcess(this->debug_handle, d.thread_id, Is64Bit()); } @@ -237,3 +200,121 @@ bool CrashReport::GetCurrentTime(u64 *out) { } return success; } + +void CrashReport::EnsureReportDirectories() { + char path[FS_MAX_PATH]; + strcpy(path, "sdmc:/atmosphere"); + mkdir(path, S_IRWXU); + strcat(path, "/crash reports"); + mkdir(path, S_IRWXU); + strcat(path, "/dumps"); + mkdir(path, S_IRWXU); +} + +void CrashReport::SaveReport() { + /* TODO: Save the report to the SD card. */ + char report_path[FS_MAX_PATH]; + + /* Ensure path exists. */ + EnsureReportDirectories(); + + /* Get a timestamp. */ + u64 timestamp; + if (!GetCurrentTime(×tamp)) { + timestamp = svcGetSystemTick(); + } + + /* Open report file. */ + snprintf(report_path, sizeof(report_path) - 1, "sdmc:/atmosphere/crash reports/%011lu_%016lx.log", timestamp, process_info.title_id); + FILE *f_report = fopen(report_path, "w"); + if (f_report == NULL) { + return; + } + this->SaveToFile(f_report); + fclose(f_report); + + /* Dump threads. */ + snprintf(report_path, sizeof(report_path) - 1, "sdmc:/atmosphere/crash reports/dumps/%011lu_%016lx_thread_info.bin", timestamp, process_info.title_id); + f_report = fopen(report_path, "wb"); + this->thread_list.DumpBinary(f_report, this->crashed_thread_info.GetId()); + fclose(f_report); +} + +void CrashReport::SaveToFile(FILE *f_report) { + char buf[0x10] = {0}; + fprintf(f_report, "Atmosphère Crash Report (v1.0):\n"); + fprintf(f_report, "Result: 0x%X (2%03d-%04d)\n\n", this->result, R_MODULE(this->result), R_DESCRIPTION(this->result)); + + /* Process Info. */ + memcpy(buf, this->process_info.name, sizeof(this->process_info.name)); + fprintf(f_report, "Process Info:\n"); + fprintf(f_report, " Process Name: %s\n", buf); + fprintf(f_report, " Title ID: %016lx\n", this->process_info.title_id); + fprintf(f_report, " Process ID: %016lx\n", this->process_info.process_id); + fprintf(f_report, " Process Flags: %08x\n", this->process_info.flags); + if (kernelAbove500()) { + fprintf(f_report, " User Exception Address: %016lx\n", this->process_info.user_exception_context_address); + } + + fprintf(f_report, "Exception Info:\n"); + fprintf(f_report, " Type: %s\n", GetDebugExceptionTypeStr(this->exception_info.type)); + fprintf(f_report, " Address: %016lx\n", this->exception_info.address); + switch (this->exception_info.type) { + case DebugExceptionType::UndefinedInstruction: + fprintf(f_report, " Opcode: %08x\n", this->exception_info.specific.undefined_instruction.insn); + break; + case DebugExceptionType::DataAbort: + case DebugExceptionType::AlignmentFault: + if (this->exception_info.specific.raw != this->exception_info.address) { + fprintf(f_report, " Fault Address: %016lx\n", this->exception_info.specific.raw); + } + break; + case DebugExceptionType::BadSvc: + fprintf(f_report, " Svc Id: 0x%02x\n", this->exception_info.specific.bad_svc.id); + break; + default: + break; + } + + fprintf(f_report, "Crashed Thread Info:\n"); + this->crashed_thread_info.SaveToFile(f_report); + + if (kernelAbove500()) { + fprintf(f_report, "Code Region Info:\n"); + this->code_list.SaveToFile(f_report); + fprintf(f_report, "\nThread Report:\n"); + this->thread_list.SaveToFile(f_report); + } +} + +/* Lifted from hactool. */ +void CrashReport::Memdump(FILE *f, const char *prefix, const void *data, size_t size) { + uint8_t *p = (uint8_t *)data; + + unsigned int prefix_len = strlen(prefix); + size_t offset = 0; + int first = 1; + + while (size) { + unsigned int max = 32; + + if (max > size) { + max = size; + } + + if (first) { + fprintf(f, "%s", prefix); + first = 0; + } else { + fprintf(f, "%*s", prefix_len, ""); + } + + for (unsigned int i = 0; i < max; i++) { + fprintf(f, "%02X", p[offset++]); + } + + fprintf(f, "\n"); + + size -= max; + } +} diff --git a/stratosphere/creport/source/creport_crash_report.hpp b/stratosphere/creport/source/creport_crash_report.hpp index d9a5bd588..3e59a88dd 100644 --- a/stratosphere/creport/source/creport_crash_report.hpp +++ b/stratosphere/creport/source/creport_crash_report.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include "creport_debug_types.hpp" #include "creport_thread_info.hpp" @@ -43,14 +44,16 @@ class CrashReport { ThreadList thread_list; public: - CrashReport() : debug_handle(INVALID_HANDLE), result((Result)CrashReportResult::IncompleteReport), process_info({}), dying_message_address(0), - dying_message_size(0), dying_message{}, exception_info({}) { } + CrashReport() : debug_handle(INVALID_HANDLE), result((Result)CrashReportResult::IncompleteReport), process_info{0}, dying_message_address(0), + dying_message_size(0), dying_message{0}, exception_info({}) { } void BuildReport(u64 pid, bool has_extra_info); void SaveReport(); bool IsAddressReadable(u64 address, u64 size, MemoryInfo *mi = NULL); + static void Memdump(FILE *f, const char *prefix, const void *data, size_t size); + Result GetResult() { return this->result; } @@ -91,6 +94,8 @@ class CrashReport { void HandleAttachProcess(DebugEventInfo &d); void HandleException(DebugEventInfo &d); + void SaveToFile(FILE *f); + void EnsureReportDirectories(); bool GetCurrentTime(u64 *out); }; \ No newline at end of file diff --git a/stratosphere/creport/source/creport_debug_types.hpp b/stratosphere/creport/source/creport_debug_types.hpp index 0e91418f3..22421dbba 100644 --- a/stratosphere/creport/source/creport_debug_types.hpp +++ b/stratosphere/creport/source/creport_debug_types.hpp @@ -36,6 +36,33 @@ enum class DebugExceptionType : u32 { UnknownNine = 9, }; +static inline const char *GetDebugExceptionTypeStr(DebugExceptionType type) { + switch (type) { + case DebugExceptionType::UndefinedInstruction: + return "Undefined Instruction"; + case DebugExceptionType::InstructionAbort: + return "Instruction Abort"; + case DebugExceptionType::DataAbort: + return "Data Abort"; + case DebugExceptionType::AlignmentFault: + return "Alignment Fault"; + case DebugExceptionType::DebuggerAttached: + return "Debugger Attached"; + case DebugExceptionType::BreakPoint: + return "Break Point"; + case DebugExceptionType::UserBreak: + return "User Break"; + case DebugExceptionType::DebuggerBreak: + return "Debugger Break"; + case DebugExceptionType::BadSvc: + return "Bad Svc"; + case DebugExceptionType::UnknownNine: + return "Unknown Nine"; + default: + return "Unknown"; + } +} + struct UndefinedInstructionInfo { u32 insn; }; diff --git a/stratosphere/creport/source/creport_thread_info.cpp b/stratosphere/creport/source/creport_thread_info.cpp index 95a6127a5..f881e1700 100644 --- a/stratosphere/creport/source/creport_thread_info.cpp +++ b/stratosphere/creport/source/creport_thread_info.cpp @@ -1,6 +1,29 @@ #include +#include #include "creport_thread_info.hpp" +#include "creport_crash_report.hpp" + +void ThreadInfo::SaveToFile(FILE *f_report) { + fprintf(f_report, " Thread ID: %016lx\n", this->thread_id); + if (stack_top) { + fprintf(f_report, " Stack: %016lx-%016lx\n", this->stack_bottom, this->stack_top); + } + fprintf(f_report, " Registers:\n"); + { + for (unsigned int i = 0; i <= 28; i++) { + fprintf(f_report, " X[%02u]: %016lx\n", i, this->context.x[i]); + } + fprintf(f_report, " FP: %016lx\n", this->context.fp); + fprintf(f_report, " LR: %016lx\n", this->context.lr); + fprintf(f_report, " SP: %016lx\n", this->context.sp); + fprintf(f_report, " PC: %016lx\n", this->context.pc); + } + fprintf(f_report, " Stack Trace:\n"); + for (unsigned int i = 0; i < this->stack_trace_size; i++) { + fprintf(f_report, " ReturnAddress[%02u]: %016lx\n", i, this->stack_trace[i]); + } +} bool ThreadInfo::ReadFromProcess(Handle debug_handle, u64 thread_id, bool is_64_bit) { this->thread_id = thread_id; @@ -77,6 +100,35 @@ void ThreadInfo::TryGetStackInfo(Handle debug_handle) { } } +void ThreadInfo::DumpBinary(FILE *f_bin) { + fwrite(&this->thread_id, sizeof(this->thread_id), 1, f_bin); + fwrite(&this->context, sizeof(this->context), 1, f_bin); + + u64 sts = this->stack_trace_size; + fwrite(&sts, sizeof(sts), 1, f_bin); + fwrite(this->stack_trace, sizeof(u64), this->stack_trace_size, f_bin); + fwrite(&this->stack_bottom, sizeof(this->stack_bottom), 1, f_bin); + fwrite(&this->stack_top, sizeof(this->stack_top), 1, f_bin); +} + +void ThreadList::DumpBinary(FILE *f_bin, u64 crashed_id) { + u32 magic = 0x30495444; /* 'DTI0' */ + fwrite(&magic, sizeof(magic), 1, f_bin); + fwrite(&this->thread_count, sizeof(u32), 1, f_bin); + fwrite(&crashed_id, sizeof(crashed_id), 1, f_bin); + for (unsigned int i = 0; i < this->thread_count; i++) { + this->thread_infos[i].DumpBinary(f_bin); + } +} + +void ThreadList::SaveToFile(FILE *f_report) { + fprintf(f_report, "Number of Threads: %02u\n", this->thread_count); + for (unsigned int i = 0; i < this->thread_count; i++) { + fprintf(f_report, "Threads[%02u]:\n", i); + this->thread_infos[i].SaveToFile(f_report); + } +} + void ThreadList::ReadThreadsFromProcess(Handle debug_handle, bool is_64_bit) { u32 thread_count; u64 thread_ids[max_thread_count]; diff --git a/stratosphere/creport/source/creport_thread_info.hpp b/stratosphere/creport/source/creport_thread_info.hpp index 19879e209..ad90250ec 100644 --- a/stratosphere/creport/source/creport_thread_info.hpp +++ b/stratosphere/creport/source/creport_thread_info.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include "creport_debug_types.hpp" @@ -37,12 +38,15 @@ class ThreadInfo { u64 stack_trace[0x20]; u32 stack_trace_size; public: - ThreadInfo() : context{}, thread_id(0), stack_top(0), stack_bottom(0), stack_trace{}, stack_trace_size(0) { } + ThreadInfo() : context{0}, thread_id(0), stack_top(0), stack_bottom(0), stack_trace{0}, stack_trace_size(0) { } u64 GetPC() { return context.pc; } u64 GetLR() { return context.lr; } + u64 GetId() { return thread_id; } bool ReadFromProcess(Handle debug_handle, u64 thread_id, bool is_64_bit); + void SaveToFile(FILE *f_report); + void DumpBinary(FILE *f_bin); private: void TryGetStackInfo(Handle debug_handle); }; @@ -55,5 +59,7 @@ class ThreadList { public: ThreadList() : thread_count(0) { } + void SaveToFile(FILE *f_report); + void DumpBinary(FILE *f_bin, u64 crashed_id); void ReadThreadsFromProcess(Handle debug_handle, bool is_64_bit); };