diff --git a/stratosphere/creport/source/creport_code_info.cpp b/stratosphere/creport/source/creport_code_info.cpp
deleted file mode 100644
index 70d9d21af..000000000
--- a/stratosphere/creport/source/creport_code_info.cpp
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Copyright (c) 2018-2019 Atmosphère-NX
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#include
-#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::ReadCodeRegionsFromThreadInfo(Handle debug_handle, const ThreadInfo *thread) {
- u64 code_base;
-
- /* Try to add the thread's PC. */
- if (TryFindCodeRegion(debug_handle, thread->GetPC(), &code_base)) {
- AddCodeRegion(debug_handle, code_base);
- }
-
- /* Try to add the thread's LR. */
- if (TryFindCodeRegion(debug_handle, thread->GetLR(), &code_base)) {
- AddCodeRegion(debug_handle, code_base);
- }
-
- /* Try to add all the addresses in the thread's stacktrace. */
- for (u32 i = 0; i < thread->GetStackTraceSize(); i++) {
- if (TryFindCodeRegion(debug_handle, thread->GetStackTrace(i), &code_base)) {
- AddCodeRegion(debug_handle, code_base);
- }
- }
-}
-
-void CodeList::AddCodeRegion(u64 debug_handle, u64 code_address) {
- /* Check whether we already have this code region. */
- for (size_t i = 0; i < this->code_count; i++) {
- if (this->code_infos[i].start_address <= code_address && code_address < this->code_infos[i].end_address) {
- return;
- }
- }
-
- /* Add all contiguous code regions. */
- u64 cur_ptr = code_address;
- while (this->code_count < max_code_count) {
- MemoryInfo mi;
- u32 pi;
- if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, cur_ptr))) {
- break;
- }
-
- if (mi.perm == Perm_Rx) {
- /* Parse CodeInfo. */
- this->code_infos[this->code_count].start_address = mi.addr;
- this->code_infos[this->code_count].end_address = mi.addr + mi.size;
- GetCodeInfoName(debug_handle, mi.addr, mi.addr + mi.size, this->code_infos[this->code_count].name);
- GetCodeInfoBuildId(debug_handle, mi.addr + mi.size, this->code_infos[this->code_count].build_id);
- if (this->code_infos[this->code_count].name[0] == '\x00') {
- snprintf(this->code_infos[this->code_count].name, 0x1F, "[%02x%02x%02x%02x]", this->code_infos[this->code_count].build_id[0],
- this->code_infos[this->code_count].build_id[1],
- this->code_infos[this->code_count].build_id[2],
- this->code_infos[this->code_count].build_id[3]);
- }
- this->code_count++;
- }
-
- /* If we're out of readable memory, we're done reading code. */
- if (mi.type == MemType_Unmapped || mi.type == MemType_Reserved) {
- break;
- }
-
- /* Verify we're not getting stuck in an infinite loop. */
- if (mi.size == 0 || U64_MAX - mi.size <= cur_ptr) {
- break;
- }
-
- cur_ptr += mi.size;
- }
-}
-
-bool CodeList::TryFindCodeRegion(Handle debug_handle, u64 guess, u64 *address) {
- MemoryInfo mi;
- u32 pi;
- if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, guess))) {
- return false;
- }
-
- if (mi.perm == Perm_Rw) {
- guess = mi.addr - 4;
- if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, guess))) {
- return false;
- }
- }
-
- if (mi.perm == Perm_R) {
- guess = mi.addr - 4;
- if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, guess))) {
- return false;
- }
- }
-
- if (mi.perm != Perm_Rx) {
- return false;
- }
-
- /* Iterate backwards until we find the memory before the code region. */
- while (mi.addr > 0) {
- if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, guess))) {
- return false;
- }
-
- if (mi.type == MemType_Unmapped) {
- /* Code region will be at the end of the unmapped region preceding it. */
- *address = mi.addr + mi.size;
- return true;
- }
-
- guess = mi.addr - 4;
- }
- return false;
-}
-
-void CodeList::GetCodeInfoName(u64 debug_handle, u64 rx_address, u64 rodata_addr, char *name) {
- char name_in_proc[0x200];
-
- /* Clear name. */
- memset(name, 0, 0x20);
-
- /* Check whether this NSO *has* a name... */
- {
- u64 rodata_start[0x20/sizeof(u64)];
- MemoryInfo mi;
- u32 pi;
- u64 rw_address;
-
- /* Verify .rodata is read-only. */
- if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, rodata_addr)) || mi.perm != Perm_R) {
- return;
- }
-
- /* rwdata is after rodata. */
- rw_address = mi.addr + mi.size;
-
- /* Read start of .rodata. */
- if (R_FAILED(svcReadDebugProcessMemory(rodata_start, debug_handle, rodata_addr, sizeof(rodata_start)))) {
- return;
- }
-
- /* Check if name section is present. */
- if (rodata_start[0] == (rw_address - rx_address)) {
- return;
- }
- }
-
- /* Read name out of .rodata. */
- if (R_FAILED(svcReadDebugProcessMemory(name_in_proc, debug_handle, rodata_addr + 8, sizeof(name_in_proc)))) {
- return;
- }
-
- /* Start after last slash in path. */
- int ofs = strnlen(name_in_proc, sizeof(name_in_proc));
- while (ofs >= 0 && name_in_proc[ofs] != '/' && name_in_proc[ofs] != '\\') {
- ofs--;
- }
-
- strncpy(name, name_in_proc + ofs + 1, 0x20);
- name[0x1F] = '\x00';
-}
-
-void CodeList::GetCodeInfoBuildId(u64 debug_handle, u64 rodata_addr, u8 *build_id) {
- MemoryInfo mi;
- u32 pi;
-
- /* Clear build id. */
- memset(build_id, 0, 0x20);
-
- /* Verify .rodata is read-only. */
- if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, rodata_addr)) || mi.perm != Perm_R) {
- return;
- }
-
- /* We want to read the last two pages of .rodata. */
- u8 last_pages[0x2000];
- size_t last_pages_size = mi.size >= 0x2000 ? 0x2000 : 0x1000;
- if (R_FAILED(svcReadDebugProcessMemory(last_pages, debug_handle, mi.addr + mi.size - last_pages_size, last_pages_size))) {
- return;
- }
-
- /* Find GNU\x00 to locate start of Build ID. */
- for (int ofs = last_pages_size - 0x24; ofs >= 0; ofs--) {
- if (memcmp(last_pages + ofs, "GNU\x00", 4) == 0) {
- memcpy(build_id, last_pages + ofs + 4, 0x20);
- }
- }
-}
-
-
-const char *CodeList::GetFormattedAddressString(u64 address) {
- memset(this->address_str_buf, 0, sizeof(this->address_str_buf));
- for (unsigned int i = 0; i < this->code_count; i++) {
- if (this->code_infos[i].start_address <= address && address < this->code_infos[i].end_address) {
- snprintf(this->address_str_buf, sizeof(this->address_str_buf) - 1, "%016lx (%s + 0x%lx)", address, this->code_infos[i].name, address - this->code_infos[i].start_address);
- return this->address_str_buf;
- }
- }
- snprintf(this->address_str_buf, sizeof(this->address_str_buf) - 1, "%016lx", address);
- return this->address_str_buf;
-}
\ No newline at end of file
diff --git a/stratosphere/creport/source/creport_code_info.hpp b/stratosphere/creport/source/creport_code_info.hpp
deleted file mode 100644
index bd62a2c13..000000000
--- a/stratosphere/creport/source/creport_code_info.hpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (c) 2018-2019 Atmosphère-NX
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#pragma once
-#include
-#include
-
-#include "creport_debug_types.hpp"
-#include "creport_thread_info.hpp"
-
-struct CodeInfo {
- char name[0x20];
- u8 build_id[0x20];
- u64 start_address;
- u64 end_address;
-};
-
-class CodeList {
- public:
- static const size_t max_code_count = 0x60;
- u32 code_count = 0;
- CodeInfo code_infos[max_code_count];
-
- /* For pretty-printing. */
- char address_str_buf[0x280];
- public:
- void ReadCodeRegionsFromThreadInfo(Handle debug_handle, const ThreadInfo *thread);
- const char *GetFormattedAddressString(u64 address);
- void SaveToFile(FILE *f_report);
- private:
- bool TryFindCodeRegion(Handle debug_handle, u64 guess, u64 *address);
- void AddCodeRegion(u64 debug_handle, u64 code_address);
- void GetCodeInfoName(u64 debug_handle, u64 rx_address, u64 ro_address, char *name);
- void GetCodeInfoBuildId(u64 debug_handle, u64 ro_address, u8 *build_id);
-};
diff --git a/stratosphere/creport/source/creport_crash_report.cpp b/stratosphere/creport/source/creport_crash_report.cpp
index df0031276..4e3ddd737 100644
--- a/stratosphere/creport/source/creport_crash_report.cpp
+++ b/stratosphere/creport/source/creport_crash_report.cpp
@@ -14,104 +14,195 @@
* along with this program. If not, see .
*/
-#include
-#include
+
#include
#include
-#include
#include "creport_crash_report.hpp"
-#include "creport_debug_types.hpp"
+#include "creport_utils.hpp"
-void CrashReport::BuildReport(u64 pid, bool has_extra_info) {
- this->has_extra_info = has_extra_info;
- if (OpenProcess(pid)) {
- ProcessExceptions();
- this->code_list.ReadCodeRegionsFromThreadInfo(this->debug_handle, &this->crashed_thread_info);
- this->thread_list.ReadThreadsFromProcess(this->thread_tls_map, this->debug_handle, Is64Bit());
- this->crashed_thread_info.SetCodeList(&this->code_list);
- this->thread_list.SetCodeList(&this->code_list);
+namespace sts::creport {
- if (IsApplication()) {
- ProcessDyingMessage();
+ namespace {
+
+ /* Convenience definitions. */
+ constexpr size_t DyingMessageAddressOffset = 0x1C0;
+
+ /* Helper functions. */
+ bool IsAddressReadable(Handle debug_handle, u64 address, size_t size) {
+ MemoryInfo mi;
+ u32 pi;
+ if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, address))) {
+ return false;
+ }
+
+ /* Must be read or read-write */
+ if ((mi.perm | Perm_W) != Perm_Rw) {
+ return false;
+ }
+
+ /* Must have space for both userdata address and userdata size. */
+ if (address < mi.addr || mi.addr + mi.size < address + size) {
+ return false;
+ }
+
+ return true;
}
- /* Real creport only does this if application, but there's no reason not to do it all the time. */
- for (u32 i = 0; i < this->thread_list.GetThreadCount(); i++) {
- this->code_list.ReadCodeRegionsFromThreadInfo(this->debug_handle, this->thread_list.GetThreadInfo(i));
+ bool TryGetCurrentTimestamp(u64 *out) {
+ /* Clear output. */
+ *out = 0;
+
+ /* Check if we have time service. */
+ {
+ bool has_time_service = false;
+ if (R_FAILED(sm::HasService(&has_time_service, sm::ServiceName::Encode("time:s"))) || !has_time_service) {
+ return false;
+ }
+ }
+
+ /* Try to get the current time. */
+ {
+ auto time_holder = sm::ScopedServiceHolder();
+ return R_SUCCEEDED(time_holder.GetResult()) && R_SUCCEEDED(timeGetCurrentTime(TimeType_LocalSystemClock, out));
+ }
}
- /* Real creport builds the report here. We do it later. */
+ void EnsureReportDirectories() {
+ mkdir("sdmc:/atmosphere", S_IRWXU);
+ mkdir("sdmc:/atmosphere/crash_reports", S_IRWXU);
+ mkdir("sdmc:/atmosphere/crash_reports/dumps", S_IRWXU);
+ mkdir("sdmc:/atmosphere/fatal_reports", S_IRWXU);
+ mkdir("sdmc:/atmosphere/fatal_reports/dumps", S_IRWXU);
+ }
- Close();
- }
-}
+ constexpr const char *GetDebugExceptionTypeString(const svc::DebugExceptionType type) {
+ switch (type) {
+ case svc::DebugExceptionType::UndefinedInstruction:
+ return "Undefined Instruction";
+ case svc::DebugExceptionType::InstructionAbort:
+ return "Instruction Abort";
+ case svc::DebugExceptionType::DataAbort:
+ return "Data Abort";
+ case svc::DebugExceptionType::AlignmentFault:
+ return "Alignment Fault";
+ case svc::DebugExceptionType::DebuggerAttached:
+ return "Debugger Attached";
+ case svc::DebugExceptionType::BreakPoint:
+ return "Break Point";
+ case svc::DebugExceptionType::UserBreak:
+ return "User Break";
+ case svc::DebugExceptionType::DebuggerBreak:
+ return "Debugger Break";
+ case svc::DebugExceptionType::UndefinedSystemCall:
+ return "Undefined System Call";
+ case svc::DebugExceptionType::SystemMemoryError:
+ return "System Memory Error";
+ default:
+ return "Unknown";
+ }
+ }
-FatalContext *CrashReport::GetFatalContext() {
- FatalContext *ctx = new FatalContext;
- *ctx = (FatalContext){0};
-
- ctx->is_aarch32 = false;
- ctx->type = static_cast(this->exception_info.type);
-
- for (size_t i = 0; i < 29; i++) {
- ctx->aarch64_ctx.x[i] = this->crashed_thread_info.context.cpu_gprs[i].x;
- }
- ctx->aarch64_ctx.fp = this->crashed_thread_info.context.fp;
- ctx->aarch64_ctx.lr = this->crashed_thread_info.context.lr;
- ctx->aarch64_ctx.pc = this->crashed_thread_info.context.pc.x;
-
- ctx->aarch64_ctx.stack_trace_size = this->crashed_thread_info.stack_trace_size;
- for (size_t i = 0; i < ctx->aarch64_ctx.stack_trace_size; i++) {
- ctx->aarch64_ctx.stack_trace[i] = this->crashed_thread_info.stack_trace[i];
}
- if (this->code_list.code_count) {
- ctx->aarch64_ctx.start_address = this->code_list.code_infos[0].start_address;
- }
+ void CrashReport::BuildReport(u64 process_id, bool has_extra_info) {
+ this->has_extra_info = has_extra_info;
- /* For ams fatal... */
- ctx->aarch64_ctx.afsr0 = this->process_info.title_id;
+ if (this->OpenProcess(process_id)) {
+ ON_SCOPE_EXIT { this->Close(); };
- return ctx;
-}
+ /* 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());
-void CrashReport::ProcessExceptions() {
- if (!IsOpen()) {
- return;
- }
+ /* Associate module list to threads. */
+ this->crashed_thread.SetModuleList(&this->module_list);
+ this->thread_list.SetModuleList(&this->module_list);
- DebugEventInfo d;
- while (R_SUCCEEDED(svcGetDebugEvent((u8 *)&d, this->debug_handle))) {
- switch (d.type) {
- case DebugEventType::AttachProcess:
- HandleAttachProcess(d);
- break;
- case DebugEventType::Exception:
- HandleException(d);
- break;
- case DebugEventType::AttachThread:
- HandleAttachThread(d);
- case DebugEventType::ExitProcess:
- case DebugEventType::ExitThread:
- default:
- break;
+ /* Process dying message for applications. */
+ if (this->IsApplication()) {
+ this->ProcessDyingMessage();
+ }
+
+ /* 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));
+ }
+
+ /* Nintendo's creport builds the report here, but we'll do it later. */
}
}
- /* Parse crashing thread info. */
- this->crashed_thread_info.ReadFromProcess(this->thread_tls_map, this->debug_handle, this->crashed_thread_id, Is64Bit());
-}
+ void CrashReport::GetFatalContext(FatalContext *out) const {
+ std::memset(out, 0, sizeof(*out));
+
+ out->is_aarch32 = false;
+ out->type = static_cast(this->exception_info.type);
+
+ for (size_t i = 0; i < 29; i++) {
+ out->aarch64_ctx.x[i] = this->crashed_thread.GetGeneralPurposeRegister(i);
+ }
+ out->aarch64_ctx.fp = this->crashed_thread.GetFP();
+ out->aarch64_ctx.lr = this->crashed_thread.GetLR();
+ out->aarch64_ctx.pc = this->crashed_thread.GetPC();
+
+ out->aarch64_ctx.stack_trace_size = this->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);
+ }
+
+ if (this->module_list.GetModuleCount()) {
+ out->aarch64_ctx.start_address = this->module_list.GetModuleStartAddress(0);
+ }
+
+ /* For ams fatal, which doesn't use afsr0, pass title_id instead. */
+ out->aarch64_ctx.afsr0 = this->process_info.title_id;
+ }
+
+ void CrashReport::ProcessExceptions() {
+ if (!this->IsOpen()) {
+ return;
+ }
+
+ /* Loop all debug events. */
+ svc::DebugEventInfo d;
+ while (R_SUCCEEDED(svcGetDebugEvent(reinterpret_cast(&d), this->debug_handle))) {
+ switch (d.type) {
+ case svc::DebugEventType::AttachProcess:
+ this->HandleDebugEventInfoAttachProcess(d);
+ break;
+ case svc::DebugEventType::AttachThread:
+ this->HandleDebugEventInfoAttachThread(d);
+ break;
+ case svc::DebugEventType::Exception:
+ this->HandleDebugEventInfoException(d);
+ break;
+ case svc::DebugEventType::ExitProcess:
+ case svc::DebugEventType::ExitThread:
+ break;
+ }
+ }
+
+ /* Parse crashed thread info. */
+ this->crashed_thread.ReadFromProcess(this->debug_handle, this->thread_tls_map, this->crashed_thread_id, this->Is64Bit());
+ }
+
+ void CrashReport::HandleDebugEventInfoAttachProcess(const svc::DebugEventInfo &d) {
+ this->process_info = d.info.attach_process;
+
+ /* On 5.0.0+, we want to parse out a dying message from application crashes. */
+ if (GetRuntimeFirmwareVersion() < FirmwareVersion_500 || !IsApplication()) {
+ return;
+ }
-void CrashReport::HandleAttachProcess(DebugEventInfo &d) {
- this->process_info = d.info.attach_process;
- if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_500) && IsApplication()) {
/* Parse out user data. */
- u64 address = this->process_info.user_exception_context_address;
+ const u64 address = this->process_info.user_exception_context_address + DyingMessageAddressOffset;
u64 userdata_address = 0;
u64 userdata_size = 0;
- if (!IsAddressReadable(address, sizeof(userdata_address) + sizeof(userdata_size))) {
+ if (!IsAddressReadable(this->debug_handle, address, sizeof(userdata_address) + sizeof(userdata_size))) {
return;
}
@@ -131,274 +222,186 @@ void CrashReport::HandleAttachProcess(DebugEventInfo &d) {
}
/* Cap userdata size. */
- if (userdata_size > sizeof(this->dying_message)) {
- userdata_size = sizeof(this->dying_message);
- }
+ userdata_size = std::min(size_t(userdata_size), sizeof(this->dying_message));
- /* Assign. */
this->dying_message_address = userdata_address;
this->dying_message_size = userdata_size;
}
-}
-void CrashReport::HandleException(DebugEventInfo &d) {
- switch (d.info.exception.type) {
- case DebugExceptionType::UndefinedInstruction:
- this->result = ResultCreportUndefinedInstruction;
- break;
- case DebugExceptionType::InstructionAbort:
- this->result = ResultCreportInstructionAbort;
- d.info.exception.specific.raw = 0;
- break;
- case DebugExceptionType::DataAbort:
- this->result = ResultCreportDataAbort;
- break;
- case DebugExceptionType::AlignmentFault:
- this->result = ResultCreportAlignmentFault;
- break;
- case DebugExceptionType::UserBreak:
- this->result = ResultCreportUserBreak;
- /* Try to parse out the user break result. */
- if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_500)) {
- Result user_result = 0;
- if (IsAddressReadable(d.info.exception.specific.user_break.address, sizeof(user_result))) {
- svcReadDebugProcessMemory(&user_result, this->debug_handle, d.info.exception.specific.user_break.address, sizeof(user_result));
+ void CrashReport::HandleDebugEventInfoAttachThread(const svc::DebugEventInfo &d) {
+ /* Save info on the thread's TLS address for later. */
+ this->thread_tls_map[d.info.attach_thread.thread_id] = d.info.attach_thread.tls_address;
+ }
+
+ void CrashReport::HandleDebugEventInfoException(const svc::DebugEventInfo &d) {
+ switch (d.info.exception.type) {
+ case svc::DebugExceptionType::UndefinedInstruction:
+ this->result = ResultCreportUndefinedInstruction;
+ break;
+ case svc::DebugExceptionType::InstructionAbort:
+ this->result = ResultCreportInstructionAbort;
+ break;
+ case svc::DebugExceptionType::DataAbort:
+ this->result = ResultCreportDataAbort;
+ break;
+ case svc::DebugExceptionType::AlignmentFault:
+ this->result = ResultCreportAlignmentFault;
+ break;
+ case svc::DebugExceptionType::UserBreak:
+ this->result = ResultCreportUserBreak;
+ /* Try to parse out the user break result. */
+ if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) {
+ Result user_result = ResultSuccess;
+ if (IsAddressReadable(this->debug_handle, d.info.exception.specific.user_break.address, sizeof(user_result))) {
+ svcReadDebugProcessMemory(&user_result, this->debug_handle, d.info.exception.specific.user_break.address, sizeof(user_result));
+ }
+ /* Only copy over the user result if it gives us information (as by default nnSdk uses the success code, which is confusing). */
+ if (R_FAILED(user_result)) {
+ this->result = user_result;
+ }
}
- /* Only copy over the user result if it gives us information (as by default nnSdk uses the success code, which is confusing). */
- if (R_FAILED(user_result)) {
- this->result = user_result;
- }
- }
- break;
- case DebugExceptionType::BadSvc:
- this->result = ResultCreportBadSvc;
- break;
- case DebugExceptionType::SystemMemoryError:
- this->result = ResultCreportSystemMemoryError;
- d.info.exception.specific.raw = 0;
- break;
- case DebugExceptionType::DebuggerAttached:
- case DebugExceptionType::BreakPoint:
- case DebugExceptionType::DebuggerBreak:
- default:
+ break;
+ case svc::DebugExceptionType::UndefinedSystemCall:
+ this->result = ResultCreportUndefinedSystemCall;
+ break;
+ case svc::DebugExceptionType::SystemMemoryError:
+ this->result = ResultCreportSystemMemoryError;
+ break;
+ case svc::DebugExceptionType::DebuggerAttached:
+ case svc::DebugExceptionType::BreakPoint:
+ case svc::DebugExceptionType::DebuggerBreak:
+ return;
+ }
+
+ /* Save exception info. */
+ this->exception_info = d.info.exception;
+ this->crashed_thread_id = d.thread_id;
+ }
+
+ void CrashReport::ProcessDyingMessage() {
+ /* Dying message is only stored starting in 5.0.0. */
+ if (GetRuntimeFirmwareVersion() < FirmwareVersion_500) {
return;
- }
- this->exception_info = d.info.exception;
- this->crashed_thread_id = d.thread_id;
-}
+ }
-void CrashReport::HandleAttachThread(DebugEventInfo &d) {
- this->thread_tls_map[d.info.attach_thread.thread_id] = d.info.attach_thread.tls_address;
-}
+ /* Validate address/size. */
+ if (this->dying_message_address == 0 || this->dying_message_address & 0xFFF) {
+ return;
+ }
+ if (this->dying_message_size > sizeof(this->dying_message)) {
+ return;
+ }
-void CrashReport::ProcessDyingMessage() {
- /* Dying message is only stored starting in 5.0.0. */
- if ((GetRuntimeFirmwareVersion() < FirmwareVersion_500)) {
- return;
+ /* Validate that the current report isn't garbage. */
+ if (!IsOpen() || !IsComplete()) {
+ return;
+ }
+
+ /* Validate that we can read the dying message. */
+ if (!IsAddressReadable(this->debug_handle, this->dying_message_address, this->dying_message_size)) {
+ return;
+ }
+
+ /* Read the dying message. */
+ svcReadDebugProcessMemory(this->dying_message, this->debug_handle, this->dying_message_address, this->dying_message_size);
}
- /* Validate the message address/size. */
- if (this->dying_message_address == 0 || this->dying_message_address & 0xFFF) {
- return;
- }
- if (this->dying_message_size > sizeof(this->dying_message)) {
- return;
- }
+ void CrashReport::SaveReport() {
+ /* Ensure path exists. */
+ EnsureReportDirectories();
- /* Validate that the report isn't garbage. */
- if (!IsOpen() || !WasSuccessful()) {
- return;
- }
+ /* Get a timestamp. */
+ u64 timestamp;
+ if (!TryGetCurrentTimestamp(×tamp)) {
+ timestamp = svcGetSystemTick();
+ }
- if (!IsAddressReadable(this->dying_message_address, this->dying_message_size)) {
- return;
- }
+ /* Save files. */
+ {
+ char file_path[FS_MAX_PATH];
- svcReadDebugProcessMemory(this->dying_message, this->debug_handle, this->dying_message_address, this->dying_message_size);
-}
-
-bool CrashReport::IsAddressReadable(u64 address, u64 size, MemoryInfo *o_mi) {
- MemoryInfo mi;
- u32 pi;
-
- if (o_mi == NULL) {
- o_mi = &mi;
- }
-
- if (R_FAILED(svcQueryDebugProcessMemory(o_mi, &pi, this->debug_handle, address))) {
- return false;
- }
-
- /* Must be read or read-write */
- if ((o_mi->perm | Perm_W) != Perm_Rw) {
- return false;
- }
-
- /* Must have space for both userdata address and userdata size. */
- if (address < o_mi->addr || o_mi->addr + o_mi->size < address + size) {
- return false;
- }
-
- return true;
-}
-
-bool CrashReport::GetCurrentTime(u64 *out) {
- *out = 0;
-
- /* Verify that pcv isn't dead. */
- {
- bool has_time_service;
- DoWithSmSession([&]() {
- Handle dummy;
- if (R_SUCCEEDED(smRegisterService(&dummy, "time:s", false, 0x20))) {
- svcCloseHandle(dummy);
- has_time_service = false;
- } else {
- has_time_service = true;
+ /* Save crash report. */
+ std::snprintf(file_path, sizeof(file_path), "sdmc:/atmosphere/crash_reports/%011lu_%016lx.log", timestamp, this->process_info.title_id);
+ FILE *fp = fopen(file_path, "w");
+ if (fp != nullptr) {
+ this->SaveToFile(fp);
+ fclose(fp);
+ fp = nullptr;
+ }
+
+ /* Dump threads. */
+ std::snprintf(file_path, sizeof(file_path), "sdmc:/atmosphere/crash_reports/dumps/%011lu_%016lx_thread_info.bin", timestamp, this->process_info.title_id);
+ fp = fopen(file_path, "wb");
+ if (fp != nullptr) {
+ this->thread_list.DumpBinary(fp, this->crashed_thread.GetThreadId());
+ fclose(fp);
+ fp = nullptr;
}
- });
- if (!has_time_service) {
- return false;
}
}
- /* Try to get the current time. */
- bool success = true;
- DoWithSmSession([&]() {
- success &= R_SUCCEEDED(timeInitialize());
- });
- if (success) {
- success &= R_SUCCEEDED(timeGetCurrentTime(TimeType_LocalSystemClock, out));
- timeExit();
- }
- return success;
-}
+ void CrashReport::SaveToFile(FILE *f_report) {
+ fprintf(f_report, "Atmosphère Crash Report (v1.4):\n");
+ fprintf(f_report, "Result: 0x%X (2%03d-%04d)\n\n", this->result, R_MODULE(this->result), R_DESCRIPTION(this->result));
-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);
-}
+ /* 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));
+ fprintf(f_report, "Process Info:\n");
+ fprintf(f_report, " Process Name: %s\n", name_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 (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) {
+ fprintf(f_report, " User Exception Address: %s\n", this->module_list.GetFormattedAddressString(this->process_info.user_exception_context_address));
+ }
-void CrashReport::SaveReport() {
- /* Save the report to the SD card. */
- char report_path[FS_MAX_PATH];
+ /* Exception Info. */
+ fprintf(f_report, "Exception Info:\n");
+ fprintf(f_report, " Type: %s\n", GetDebugExceptionTypeString(this->exception_info.type));
+ fprintf(f_report, " Address: %s\n", this->module_list.GetFormattedAddressString(this->exception_info.address));
+ switch (this->exception_info.type) {
+ case svc::DebugExceptionType::UndefinedInstruction:
+ fprintf(f_report, " Opcode: %08x\n", this->exception_info.specific.undefined_instruction.insn);
+ break;
+ case svc::DebugExceptionType::DataAbort:
+ case svc::DebugExceptionType::AlignmentFault:
+ if (this->exception_info.specific.raw != this->exception_info.address) {
+ fprintf(f_report, " Fault Address: %s\n", this->module_list.GetFormattedAddressString(this->exception_info.specific.raw));
+ }
+ break;
+ case svc::DebugExceptionType::UndefinedSystemCall:
+ fprintf(f_report, " Svc Id: 0x%02x\n", this->exception_info.specific.undefined_system_call.id);
+ break;
+ case svc::DebugExceptionType::UserBreak:
+ fprintf(f_report, " Break Reason: 0x%x\n", this->exception_info.specific.user_break.break_reason);
+ fprintf(f_report, " Break Address: %s\n", this->module_list.GetFormattedAddressString(this->exception_info.specific.user_break.address));
+ fprintf(f_report, " Break Size: 0x%lx\n", this->exception_info.specific.user_break.size);
+ break;
+ default:
+ break;
+ }
- /* Ensure path exists. */
- EnsureReportDirectories();
+ /* Crashed Thread Info. */
+ fprintf(f_report, "Crashed Thread Info:\n");
+ this->crashed_thread.SaveToFile(f_report);
- /* 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.3):\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 ((GetRuntimeFirmwareVersion() >= FirmwareVersion_500)) {
- fprintf(f_report, " User Exception Address: %s\n", this->code_list.GetFormattedAddressString(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: %s\n", this->code_list.GetFormattedAddressString(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: %s\n", this->code_list.GetFormattedAddressString(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;
- case DebugExceptionType::UserBreak:
- fprintf(f_report, " Break Reason: 0x%lx\n", this->exception_info.specific.user_break.break_reason);
- fprintf(f_report, " Break Address: %s\n", this->code_list.GetFormattedAddressString(this->exception_info.specific.user_break.address));
- fprintf(f_report, " Break Size: 0x%lx\n", this->exception_info.specific.user_break.size);
- break;
- default:
- break;
- }
-
- fprintf(f_report, "Crashed Thread Info:\n");
- this->crashed_thread_info.SaveToFile(f_report);
-
- if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_500)) {
- if (this->dying_message_size) {
+ /* Dying Message. */
+ if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500 && this->dying_message_size != 0) {
fprintf(f_report, "Dying Message Info:\n");
- fprintf(f_report, " Address: 0x%s\n", this->code_list.GetFormattedAddressString(this->dying_message_address));
+ fprintf(f_report, " Address: 0x%s\n", this->module_list.GetFormattedAddressString(this->dying_message_address));
fprintf(f_report, " Size: 0x%016lx\n", this->dying_message_size);
- CrashReport::Memdump(f_report, " Dying Message: ", this->dying_message, this->dying_message_size);
+ DumpMemoryHexToFile(f_report, " Dying Message: ", this->dying_message, this->dying_message_size);
}
+
+ /* Module Info. */
+ fprintf(f_report, "Module Info:\n");
+ this->module_list.SaveToFile(f_report);
+
+ /* Thread Info. */
+ fprintf(f_report, "\nThread Report:\n");
+ this->thread_list.SaveToFile(f_report);
}
- 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 7c7e2e706..b6c7a8196 100644
--- a/stratosphere/creport/source/creport_crash_report.hpp
+++ b/stratosphere/creport/source/creport_crash_report.hpp
@@ -15,94 +15,87 @@
*/
#pragma once
-
#include
#include
-#include
#include