creport: dump tls/name on crash (closes #310)

This commit is contained in:
Michael Scire 2019-05-25 13:32:34 -07:00
parent 5f5a8567ce
commit 766097d0b7
8 changed files with 195 additions and 141 deletions

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <switch.h> #include <switch.h>
#include <cstring> #include <cstring>
@ -25,7 +25,7 @@ void CodeList::SaveToFile(FILE *f_report) {
for (unsigned int i = 0; i < this->code_count; i++) { for (unsigned int i = 0; i < this->code_count; i++) {
fprintf(f_report, " Code Region %02u:\n", 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); 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]) { if (this->code_infos[i].name[0]) {
fprintf(f_report, " Name: %s\n", this->code_infos[i].name); 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)); CrashReport::Memdump(f_report, " Build Id: ", this->code_infos[i].build_id, sizeof(this->code_infos[i].build_id));
@ -34,17 +34,17 @@ void CodeList::SaveToFile(FILE *f_report) {
void CodeList::ReadCodeRegionsFromThreadInfo(Handle debug_handle, const ThreadInfo *thread) { void CodeList::ReadCodeRegionsFromThreadInfo(Handle debug_handle, const ThreadInfo *thread) {
u64 code_base; u64 code_base;
/* Try to add the thread's PC. */ /* Try to add the thread's PC. */
if (TryFindCodeRegion(debug_handle, thread->GetPC(), &code_base)) { if (TryFindCodeRegion(debug_handle, thread->GetPC(), &code_base)) {
AddCodeRegion(debug_handle, code_base); AddCodeRegion(debug_handle, code_base);
} }
/* Try to add the thread's LR. */ /* Try to add the thread's LR. */
if (TryFindCodeRegion(debug_handle, thread->GetLR(), &code_base)) { if (TryFindCodeRegion(debug_handle, thread->GetLR(), &code_base)) {
AddCodeRegion(debug_handle, code_base); AddCodeRegion(debug_handle, code_base);
} }
/* Try to add all the addresses in the thread's stacktrace. */ /* Try to add all the addresses in the thread's stacktrace. */
for (u32 i = 0; i < thread->GetStackTraceSize(); i++) { for (u32 i = 0; i < thread->GetStackTraceSize(); i++) {
if (TryFindCodeRegion(debug_handle, thread->GetStackTrace(i), &code_base)) { if (TryFindCodeRegion(debug_handle, thread->GetStackTrace(i), &code_base)) {
@ -60,7 +60,7 @@ void CodeList::AddCodeRegion(u64 debug_handle, u64 code_address) {
return; return;
} }
} }
/* Add all contiguous code regions. */ /* Add all contiguous code regions. */
u64 cur_ptr = code_address; u64 cur_ptr = code_address;
while (this->code_count < max_code_count) { while (this->code_count < max_code_count) {
@ -69,32 +69,32 @@ void CodeList::AddCodeRegion(u64 debug_handle, u64 code_address) {
if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, cur_ptr))) { if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, cur_ptr))) {
break; break;
} }
if (mi.perm == Perm_Rx) { if (mi.perm == Perm_Rx) {
/* Parse CodeInfo. */ /* Parse CodeInfo. */
this->code_infos[this->code_count].start_address = mi.addr; this->code_infos[this->code_count].start_address = mi.addr;
this->code_infos[this->code_count].end_address = mi.addr + mi.size; 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); 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); 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') { 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], 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[1],
this->code_infos[this->code_count].build_id[2], this->code_infos[this->code_count].build_id[2],
this->code_infos[this->code_count].build_id[3]); this->code_infos[this->code_count].build_id[3]);
} }
this->code_count++; this->code_count++;
} }
/* If we're out of readable memory, we're done reading code. */ /* If we're out of readable memory, we're done reading code. */
if (mi.type == MemType_Unmapped || mi.type == MemType_Reserved) { if (mi.type == MemType_Unmapped || mi.type == MemType_Reserved) {
break; break;
} }
/* Verify we're not getting stuck in an infinite loop. */ /* Verify we're not getting stuck in an infinite loop. */
if (mi.size == 0 || U64_MAX - mi.size <= cur_ptr) { if (mi.size == 0 || U64_MAX - mi.size <= cur_ptr) {
break; break;
} }
cur_ptr += mi.size; cur_ptr += mi.size;
} }
} }
@ -105,37 +105,37 @@ bool CodeList::TryFindCodeRegion(Handle debug_handle, u64 guess, u64 *address) {
if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, guess))) { if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, guess))) {
return false; return false;
} }
if (mi.perm == Perm_Rw) { if (mi.perm == Perm_Rw) {
guess = mi.addr - 4; guess = mi.addr - 4;
if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, guess))) { if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, guess))) {
return false; return false;
} }
} }
if (mi.perm == Perm_R) { if (mi.perm == Perm_R) {
guess = mi.addr - 4; guess = mi.addr - 4;
if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, guess))) { if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, guess))) {
return false; return false;
} }
} }
if (mi.perm != Perm_Rx) { if (mi.perm != Perm_Rx) {
return false; return false;
} }
/* Iterate backwards until we find the memory before the code region. */ /* Iterate backwards until we find the memory before the code region. */
while (mi.addr > 0) { while (mi.addr > 0) {
if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, guess))) { if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, guess))) {
return false; return false;
} }
if (mi.type == MemType_Unmapped) { if (mi.type == MemType_Unmapped) {
/* Code region will be at the end of the unmapped region preceding it. */ /* Code region will be at the end of the unmapped region preceding it. */
*address = mi.addr + mi.size; *address = mi.addr + mi.size;
return true; return true;
} }
guess = mi.addr - 4; guess = mi.addr - 4;
} }
return false; return false;
@ -143,47 +143,47 @@ bool CodeList::TryFindCodeRegion(Handle debug_handle, u64 guess, u64 *address) {
void CodeList::GetCodeInfoName(u64 debug_handle, u64 rx_address, u64 rodata_addr, char *name) { void CodeList::GetCodeInfoName(u64 debug_handle, u64 rx_address, u64 rodata_addr, char *name) {
char name_in_proc[0x200]; char name_in_proc[0x200];
/* Clear name. */ /* Clear name. */
memset(name, 0, 0x20); memset(name, 0, 0x20);
/* Check whether this NSO *has* a name... */ /* Check whether this NSO *has* a name... */
{ {
u64 rodata_start[0x20/sizeof(u64)]; u64 rodata_start[0x20/sizeof(u64)];
MemoryInfo mi; MemoryInfo mi;
u32 pi; u32 pi;
u64 rw_address; u64 rw_address;
/* Verify .rodata is read-only. */ /* Verify .rodata is read-only. */
if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, rodata_addr)) || mi.perm != Perm_R) { if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, rodata_addr)) || mi.perm != Perm_R) {
return; return;
} }
/* rwdata is after rodata. */ /* rwdata is after rodata. */
rw_address = mi.addr + mi.size; rw_address = mi.addr + mi.size;
/* Read start of .rodata. */ /* Read start of .rodata. */
if (R_FAILED(svcReadDebugProcessMemory(rodata_start, debug_handle, rodata_addr, sizeof(rodata_start)))) { if (R_FAILED(svcReadDebugProcessMemory(rodata_start, debug_handle, rodata_addr, sizeof(rodata_start)))) {
return; return;
} }
/* Check if name section is present. */ /* Check if name section is present. */
if (rodata_start[0] == (rw_address - rx_address)) { if (rodata_start[0] == (rw_address - rx_address)) {
return; return;
} }
} }
/* Read name out of .rodata. */ /* Read name out of .rodata. */
if (R_FAILED(svcReadDebugProcessMemory(name_in_proc, debug_handle, rodata_addr + 8, sizeof(name_in_proc)))) { if (R_FAILED(svcReadDebugProcessMemory(name_in_proc, debug_handle, rodata_addr + 8, sizeof(name_in_proc)))) {
return; return;
} }
/* Start after last slash in path. */ /* Start after last slash in path. */
int ofs = strnlen(name_in_proc, sizeof(name_in_proc)); int ofs = strnlen(name_in_proc, sizeof(name_in_proc));
while (ofs >= 0 && name_in_proc[ofs] != '/' && name_in_proc[ofs] != '\\') { while (ofs >= 0 && name_in_proc[ofs] != '/' && name_in_proc[ofs] != '\\') {
ofs--; ofs--;
} }
strncpy(name, name_in_proc + ofs + 1, 0x20); strncpy(name, name_in_proc + ofs + 1, 0x20);
name[0x1F] = '\x00'; name[0x1F] = '\x00';
} }
@ -191,22 +191,22 @@ void CodeList::GetCodeInfoName(u64 debug_handle, u64 rx_address, u64 rodata_addr
void CodeList::GetCodeInfoBuildId(u64 debug_handle, u64 rodata_addr, u8 *build_id) { void CodeList::GetCodeInfoBuildId(u64 debug_handle, u64 rodata_addr, u8 *build_id) {
MemoryInfo mi; MemoryInfo mi;
u32 pi; u32 pi;
/* Clear build id. */ /* Clear build id. */
memset(build_id, 0, 0x20); memset(build_id, 0, 0x20);
/* Verify .rodata is read-only. */ /* Verify .rodata is read-only. */
if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, rodata_addr)) || mi.perm != Perm_R) { if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, rodata_addr)) || mi.perm != Perm_R) {
return; return;
} }
/* We want to read the last two pages of .rodata. */ /* We want to read the last two pages of .rodata. */
u8 last_pages[0x2000]; u8 last_pages[0x2000];
size_t last_pages_size = mi.size >= 0x2000 ? 0x2000 : 0x1000; 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))) { if (R_FAILED(svcReadDebugProcessMemory(last_pages, debug_handle, mi.addr + mi.size - last_pages_size, last_pages_size))) {
return; return;
} }
/* Find GNU\x00 to locate start of Build ID. */ /* Find GNU\x00 to locate start of Build ID. */
for (int ofs = last_pages_size - 0x24; ofs >= 0; ofs--) { for (int ofs = last_pages_size - 0x24; ofs >= 0; ofs--) {
if (memcmp(last_pages + ofs, "GNU\x00", 4) == 0) { if (memcmp(last_pages + ofs, "GNU\x00", 4) == 0) {

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <switch.h> #include <switch.h>
#include <cstdio> #include <cstdio>
@ -33,10 +33,10 @@ class CodeList {
static const size_t max_code_count = 0x60; static const size_t max_code_count = 0x60;
u32 code_count = 0; u32 code_count = 0;
CodeInfo code_infos[max_code_count]; CodeInfo code_infos[max_code_count];
/* For pretty-printing. */ /* For pretty-printing. */
char address_str_buf[0x280]; char address_str_buf[0x280];
public: public:
void ReadCodeRegionsFromThreadInfo(Handle debug_handle, const ThreadInfo *thread); void ReadCodeRegionsFromThreadInfo(Handle debug_handle, const ThreadInfo *thread);
const char *GetFormattedAddressString(u64 address); const char *GetFormattedAddressString(u64 address);
void SaveToFile(FILE *f_report); void SaveToFile(FILE *f_report);

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <sys/stat.h> #include <sys/stat.h>
@ -28,21 +28,21 @@ void CrashReport::BuildReport(u64 pid, bool has_extra_info) {
if (OpenProcess(pid)) { if (OpenProcess(pid)) {
ProcessExceptions(); ProcessExceptions();
this->code_list.ReadCodeRegionsFromThreadInfo(this->debug_handle, &this->crashed_thread_info); this->code_list.ReadCodeRegionsFromThreadInfo(this->debug_handle, &this->crashed_thread_info);
this->thread_list.ReadThreadsFromProcess(this->debug_handle, Is64Bit()); this->thread_list.ReadThreadsFromProcess(this->thread_tls_map, this->debug_handle, Is64Bit());
this->crashed_thread_info.SetCodeList(&this->code_list); this->crashed_thread_info.SetCodeList(&this->code_list);
this->thread_list.SetCodeList(&this->code_list); this->thread_list.SetCodeList(&this->code_list);
if (IsApplication()) { if (IsApplication()) {
ProcessDyingMessage(); ProcessDyingMessage();
} }
/* Real creport only does this if application, but there's no reason not to do it all the time. */ /* 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++) { for (u32 i = 0; i < this->thread_list.GetThreadCount(); i++) {
this->code_list.ReadCodeRegionsFromThreadInfo(this->debug_handle, this->thread_list.GetThreadInfo(i)); this->code_list.ReadCodeRegionsFromThreadInfo(this->debug_handle, this->thread_list.GetThreadInfo(i));
} }
/* Real creport builds the report here. We do it later. */ /* Real creport builds the report here. We do it later. */
Close(); Close();
} }
} }
@ -50,29 +50,29 @@ void CrashReport::BuildReport(u64 pid, bool has_extra_info) {
FatalContext *CrashReport::GetFatalContext() { FatalContext *CrashReport::GetFatalContext() {
FatalContext *ctx = new FatalContext; FatalContext *ctx = new FatalContext;
*ctx = (FatalContext){0}; *ctx = (FatalContext){0};
ctx->is_aarch32 = false; ctx->is_aarch32 = false;
ctx->type = static_cast<u32>(this->exception_info.type); ctx->type = static_cast<u32>(this->exception_info.type);
for (size_t i = 0; i < 29; i++) { 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.x[i] = this->crashed_thread_info.context.cpu_gprs[i].x;
} }
ctx->aarch64_ctx.fp = this->crashed_thread_info.context.fp; ctx->aarch64_ctx.fp = this->crashed_thread_info.context.fp;
ctx->aarch64_ctx.lr = this->crashed_thread_info.context.lr; ctx->aarch64_ctx.lr = this->crashed_thread_info.context.lr;
ctx->aarch64_ctx.pc = this->crashed_thread_info.context.pc.x; ctx->aarch64_ctx.pc = this->crashed_thread_info.context.pc.x;
ctx->aarch64_ctx.stack_trace_size = this->crashed_thread_info.stack_trace_size; 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++) { 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]; ctx->aarch64_ctx.stack_trace[i] = this->crashed_thread_info.stack_trace[i];
} }
if (this->code_list.code_count) { if (this->code_list.code_count) {
ctx->aarch64_ctx.start_address = this->code_list.code_infos[0].start_address; ctx->aarch64_ctx.start_address = this->code_list.code_infos[0].start_address;
} }
/* For ams fatal... */ /* For ams fatal... */
ctx->aarch64_ctx.afsr0 = this->process_info.title_id; ctx->aarch64_ctx.afsr0 = this->process_info.title_id;
return ctx; return ctx;
} }
@ -80,7 +80,7 @@ void CrashReport::ProcessExceptions() {
if (!IsOpen()) { if (!IsOpen()) {
return; return;
} }
DebugEventInfo d; DebugEventInfo d;
while (R_SUCCEEDED(svcGetDebugEvent((u8 *)&d, this->debug_handle))) { while (R_SUCCEEDED(svcGetDebugEvent((u8 *)&d, this->debug_handle))) {
switch (d.type) { switch (d.type) {
@ -91,12 +91,16 @@ void CrashReport::ProcessExceptions() {
HandleException(d); HandleException(d);
break; break;
case DebugEventType::AttachThread: case DebugEventType::AttachThread:
HandleAttachThread(d);
case DebugEventType::ExitProcess: case DebugEventType::ExitProcess:
case DebugEventType::ExitThread: case DebugEventType::ExitThread:
default: default:
break; break;
} }
} }
/* Parse crashing thread info. */
this->crashed_thread_info.ReadFromProcess(this->thread_tls_map, this->debug_handle, this->crashed_thread_id, Is64Bit());
} }
void CrashReport::HandleAttachProcess(DebugEventInfo &d) { void CrashReport::HandleAttachProcess(DebugEventInfo &d) {
@ -106,31 +110,31 @@ void CrashReport::HandleAttachProcess(DebugEventInfo &d) {
u64 address = this->process_info.user_exception_context_address; u64 address = this->process_info.user_exception_context_address;
u64 userdata_address = 0; u64 userdata_address = 0;
u64 userdata_size = 0; u64 userdata_size = 0;
if (!IsAddressReadable(address, sizeof(userdata_address) + sizeof(userdata_size))) { if (!IsAddressReadable(address, sizeof(userdata_address) + sizeof(userdata_size))) {
return; return;
} }
/* Read userdata address. */ /* Read userdata address. */
if (R_FAILED(svcReadDebugProcessMemory(&userdata_address, this->debug_handle, address, sizeof(userdata_address)))) { if (R_FAILED(svcReadDebugProcessMemory(&userdata_address, this->debug_handle, address, sizeof(userdata_address)))) {
return; return;
} }
/* Validate userdata address. */ /* Validate userdata address. */
if (userdata_address == 0 || userdata_address & 0xFFF) { if (userdata_address == 0 || userdata_address & 0xFFF) {
return; return;
} }
/* Read userdata size. */ /* Read userdata size. */
if (R_FAILED(svcReadDebugProcessMemory(&userdata_size, this->debug_handle, address + sizeof(userdata_address), sizeof(userdata_size)))) { if (R_FAILED(svcReadDebugProcessMemory(&userdata_size, this->debug_handle, address + sizeof(userdata_address), sizeof(userdata_size)))) {
return; return;
} }
/* Cap userdata size. */ /* Cap userdata size. */
if (userdata_size > sizeof(this->dying_message)) { if (userdata_size > sizeof(this->dying_message)) {
userdata_size = sizeof(this->dying_message); userdata_size = sizeof(this->dying_message);
} }
/* Assign. */ /* Assign. */
this->dying_message_address = userdata_address; this->dying_message_address = userdata_address;
this->dying_message_size = userdata_size; this->dying_message_size = userdata_size;
@ -180,8 +184,11 @@ void CrashReport::HandleException(DebugEventInfo &d) {
return; return;
} }
this->exception_info = d.info.exception; this->exception_info = d.info.exception;
/* Parse crashing thread info. */ this->crashed_thread_id = d.thread_id;
this->crashed_thread_info.ReadFromProcess(this->debug_handle, d.thread_id, Is64Bit()); }
void CrashReport::HandleAttachThread(DebugEventInfo &d) {
this->thread_tls_map[d.info.attach_thread.thread_id] = d.info.attach_thread.tls_address;
} }
void CrashReport::ProcessDyingMessage() { void CrashReport::ProcessDyingMessage() {
@ -189,7 +196,7 @@ void CrashReport::ProcessDyingMessage() {
if ((GetRuntimeFirmwareVersion() < FirmwareVersion_500)) { if ((GetRuntimeFirmwareVersion() < FirmwareVersion_500)) {
return; return;
} }
/* Validate the message address/size. */ /* Validate the message address/size. */
if (this->dying_message_address == 0 || this->dying_message_address & 0xFFF) { if (this->dying_message_address == 0 || this->dying_message_address & 0xFFF) {
return; return;
@ -197,36 +204,36 @@ void CrashReport::ProcessDyingMessage() {
if (this->dying_message_size > sizeof(this->dying_message)) { if (this->dying_message_size > sizeof(this->dying_message)) {
return; return;
} }
/* Validate that the report isn't garbage. */ /* Validate that the report isn't garbage. */
if (!IsOpen() || !WasSuccessful()) { if (!IsOpen() || !WasSuccessful()) {
return; return;
} }
if (!IsAddressReadable(this->dying_message_address, this->dying_message_size)) { if (!IsAddressReadable(this->dying_message_address, this->dying_message_size)) {
return; return;
} }
svcReadDebugProcessMemory(this->dying_message, this->debug_handle, this->dying_message_address, this->dying_message_size); 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) { bool CrashReport::IsAddressReadable(u64 address, u64 size, MemoryInfo *o_mi) {
MemoryInfo mi; MemoryInfo mi;
u32 pi; u32 pi;
if (o_mi == NULL) { if (o_mi == NULL) {
o_mi = &mi; o_mi = &mi;
} }
if (R_FAILED(svcQueryDebugProcessMemory(o_mi, &pi, this->debug_handle, address))) { if (R_FAILED(svcQueryDebugProcessMemory(o_mi, &pi, this->debug_handle, address))) {
return false; return false;
} }
/* Must be read or read-write */ /* Must be read or read-write */
if ((o_mi->perm | Perm_W) != Perm_Rw) { if ((o_mi->perm | Perm_W) != Perm_Rw) {
return false; return false;
} }
/* Must have space for both userdata address and userdata size. */ /* Must have space for both userdata address and userdata size. */
if (address < o_mi->addr || o_mi->addr + o_mi->size < address + size) { if (address < o_mi->addr || o_mi->addr + o_mi->size < address + size) {
return false; return false;
@ -237,7 +244,7 @@ bool CrashReport::IsAddressReadable(u64 address, u64 size, MemoryInfo *o_mi) {
bool CrashReport::GetCurrentTime(u64 *out) { bool CrashReport::GetCurrentTime(u64 *out) {
*out = 0; *out = 0;
/* Verify that pcv isn't dead. */ /* Verify that pcv isn't dead. */
{ {
bool has_time_service; bool has_time_service;
@ -254,7 +261,7 @@ bool CrashReport::GetCurrentTime(u64 *out) {
return false; return false;
} }
} }
/* Try to get the current time. */ /* Try to get the current time. */
bool success = true; bool success = true;
DoWithSmSession([&]() { DoWithSmSession([&]() {
@ -268,7 +275,7 @@ bool CrashReport::GetCurrentTime(u64 *out) {
} }
void CrashReport::EnsureReportDirectories() { void CrashReport::EnsureReportDirectories() {
char path[FS_MAX_PATH]; char path[FS_MAX_PATH];
strcpy(path, "sdmc:/atmosphere"); strcpy(path, "sdmc:/atmosphere");
mkdir(path, S_IRWXU); mkdir(path, S_IRWXU);
strcat(path, "/crash_reports"); strcat(path, "/crash_reports");
@ -280,16 +287,16 @@ void CrashReport::EnsureReportDirectories() {
void CrashReport::SaveReport() { void CrashReport::SaveReport() {
/* Save the report to the SD card. */ /* Save the report to the SD card. */
char report_path[FS_MAX_PATH]; char report_path[FS_MAX_PATH];
/* Ensure path exists. */ /* Ensure path exists. */
EnsureReportDirectories(); EnsureReportDirectories();
/* Get a timestamp. */ /* Get a timestamp. */
u64 timestamp; u64 timestamp;
if (!GetCurrentTime(&timestamp)) { if (!GetCurrentTime(&timestamp)) {
timestamp = svcGetSystemTick(); timestamp = svcGetSystemTick();
} }
/* Open report file. */ /* Open report file. */
snprintf(report_path, sizeof(report_path) - 1, "sdmc:/atmosphere/crash_reports/%011lu_%016lx.log", timestamp, process_info.title_id); 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"); FILE *f_report = fopen(report_path, "w");
@ -298,7 +305,7 @@ void CrashReport::SaveReport() {
} }
this->SaveToFile(f_report); this->SaveToFile(f_report);
fclose(f_report); fclose(f_report);
/* Dump threads. */ /* Dump threads. */
snprintf(report_path, sizeof(report_path) - 1, "sdmc:/atmosphere/crash_reports/dumps/%011lu_%016lx_thread_info.bin", timestamp, process_info.title_id); 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"); f_report = fopen(report_path, "wb");
@ -308,9 +315,9 @@ void CrashReport::SaveReport() {
void CrashReport::SaveToFile(FILE *f_report) { void CrashReport::SaveToFile(FILE *f_report) {
char buf[0x10] = {0}; char buf[0x10] = {0};
fprintf(f_report, "Atmosphère Crash Report (v1.2):\n"); 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)); fprintf(f_report, "Result: 0x%X (2%03d-%04d)\n\n", this->result, R_MODULE(this->result), R_DESCRIPTION(this->result));
/* Process Info. */ /* Process Info. */
memcpy(buf, this->process_info.name, sizeof(this->process_info.name)); memcpy(buf, this->process_info.name, sizeof(this->process_info.name));
fprintf(f_report, "Process Info:\n"); fprintf(f_report, "Process Info:\n");
@ -321,7 +328,7 @@ void CrashReport::SaveToFile(FILE *f_report) {
if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_500)) { 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, " 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, "Exception Info:\n");
fprintf(f_report, " Type: %s\n", GetDebugExceptionTypeStr(this->exception_info.type)); 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)); fprintf(f_report, " Address: %s\n", this->code_list.GetFormattedAddressString(this->exception_info.address));
@ -331,7 +338,7 @@ void CrashReport::SaveToFile(FILE *f_report) {
break; break;
case DebugExceptionType::DataAbort: case DebugExceptionType::DataAbort:
case DebugExceptionType::AlignmentFault: case DebugExceptionType::AlignmentFault:
if (this->exception_info.specific.raw != this->exception_info.address) { 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)); fprintf(f_report, " Fault Address: %s\n", this->code_list.GetFormattedAddressString(this->exception_info.specific.raw));
} }
break; break;
@ -346,10 +353,10 @@ void CrashReport::SaveToFile(FILE *f_report) {
default: default:
break; break;
} }
fprintf(f_report, "Crashed Thread Info:\n"); fprintf(f_report, "Crashed Thread Info:\n");
this->crashed_thread_info.SaveToFile(f_report); this->crashed_thread_info.SaveToFile(f_report);
if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_500)) { if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_500)) {
if (this->dying_message_size) { if (this->dying_message_size) {
fprintf(f_report, "Dying Message Info:\n"); fprintf(f_report, "Dying Message Info:\n");

View file

@ -13,12 +13,13 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <switch.h> #include <switch.h>
#include <stratosphere.hpp> #include <stratosphere.hpp>
#include <cstdio> #include <cstdio>
#include <map>
#include "creport_debug_types.hpp" #include "creport_debug_types.hpp"
#include "creport_thread_info.hpp" #include "creport_thread_info.hpp"
@ -29,63 +30,67 @@ class CrashReport {
Handle debug_handle = INVALID_HANDLE; Handle debug_handle = INVALID_HANDLE;
bool has_extra_info; bool has_extra_info;
Result result = ResultCreportIncompleteReport; Result result = ResultCreportIncompleteReport;
/* Attach Process Info. */ /* Attach Process Info. */
AttachProcessInfo process_info{}; AttachProcessInfo process_info{};
u64 dying_message_address = 0; u64 dying_message_address = 0;
u64 dying_message_size = 0; u64 dying_message_size = 0;
u8 dying_message[0x1000]{}; u8 dying_message[0x1000]{};
static_assert(sizeof(dying_message) == 0x1000, "Incorrect definition for dying message!"); static_assert(sizeof(dying_message) == 0x1000, "Incorrect definition for dying message!");
/* Exception Info. */ /* Exception Info. */
ExceptionInfo exception_info{}; ExceptionInfo exception_info{};
u64 crashed_thread_id = 0;
ThreadInfo crashed_thread_info; ThreadInfo crashed_thread_info;
/* Extra Info. */ /* Extra Info. */
CodeList code_list; CodeList code_list;
ThreadList thread_list; ThreadList thread_list;
/* Meta, used for building list. */
std::map<u64, u64> thread_tls_map;
public: public:
void BuildReport(u64 pid, bool has_extra_info); void BuildReport(u64 pid, bool has_extra_info);
FatalContext *GetFatalContext(); FatalContext *GetFatalContext();
void SaveReport(); void SaveReport();
bool IsAddressReadable(u64 address, u64 size, MemoryInfo *mi = NULL); bool IsAddressReadable(u64 address, u64 size, MemoryInfo *mi = NULL);
static void Memdump(FILE *f, const char *prefix, const void *data, size_t size); static void Memdump(FILE *f, const char *prefix, const void *data, size_t size);
Result GetResult() { Result GetResult() {
return this->result; return this->result;
} }
bool WasSuccessful() { bool WasSuccessful() {
return this->result != ResultCreportIncompleteReport; return this->result != ResultCreportIncompleteReport;
} }
bool OpenProcess(u64 pid) { bool OpenProcess(u64 pid) {
return R_SUCCEEDED(svcDebugActiveProcess(&debug_handle, pid)); return R_SUCCEEDED(svcDebugActiveProcess(&debug_handle, pid));
} }
bool IsOpen() { bool IsOpen() {
return this->debug_handle != INVALID_HANDLE; return this->debug_handle != INVALID_HANDLE;
} }
void Close() { void Close() {
if (IsOpen()) { if (IsOpen()) {
svcCloseHandle(debug_handle); svcCloseHandle(debug_handle);
debug_handle = INVALID_HANDLE; debug_handle = INVALID_HANDLE;
} }
} }
bool IsApplication() { bool IsApplication() {
return (process_info.flags & 0x40) != 0; return (process_info.flags & 0x40) != 0;
} }
bool Is64Bit() { bool Is64Bit() {
return (process_info.flags & 0x01) != 0; return (process_info.flags & 0x01) != 0;
} }
bool IsUserBreak() { bool IsUserBreak() {
return this->exception_info.type == DebugExceptionType::UserBreak; return this->exception_info.type == DebugExceptionType::UserBreak;
} }
@ -94,9 +99,10 @@ class CrashReport {
void ProcessDyingMessage(); void ProcessDyingMessage();
void HandleAttachProcess(DebugEventInfo &d); void HandleAttachProcess(DebugEventInfo &d);
void HandleException(DebugEventInfo &d); void HandleException(DebugEventInfo &d);
void HandleAttachThread(DebugEventInfo &d);
void SaveToFile(FILE *f); void SaveToFile(FILE *f);
void EnsureReportDirectories(); void EnsureReportDirectories();
bool GetCurrentTime(u64 *out); bool GetCurrentTime(u64 *out);
}; };

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <switch.h> #include <switch.h>

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cstdlib> #include <cstdlib>
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
@ -34,7 +34,7 @@ extern "C" {
#define INNER_HEAP_SIZE 0x100000 #define INNER_HEAP_SIZE 0x100000
size_t nx_inner_heap_size = INNER_HEAP_SIZE; size_t nx_inner_heap_size = INNER_HEAP_SIZE;
char nx_inner_heap[INNER_HEAP_SIZE]; char nx_inner_heap[INNER_HEAP_SIZE];
void __libnx_initheap(void); void __libnx_initheap(void);
void __appInit(void); void __appInit(void);
void __appExit(void); void __appExit(void);
@ -66,16 +66,16 @@ void __libnx_initheap(void) {
void __appInit(void) { void __appInit(void) {
Result rc; Result rc;
SetFirmwareVersionForLibnx(); SetFirmwareVersionForLibnx();
DoWithSmSession([&]() { DoWithSmSession([&]() {
rc = fsInitialize(); rc = fsInitialize();
if (R_FAILED(rc)) { if (R_FAILED(rc)) {
fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_FS)); fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_FS));
} }
}); });
rc = fsdevMountSdmc(); rc = fsdevMountSdmc();
if (R_FAILED(rc)) { if (R_FAILED(rc)) {
fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_FS)); fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_FS));
@ -114,22 +114,22 @@ int main(int argc, char **argv) {
return 0; return 0;
} }
} }
/* Parse crashed PID. */ /* Parse crashed PID. */
u64 crashed_pid = creport_parse_u64(argv[0]); u64 crashed_pid = creport_parse_u64(argv[0]);
/* Try to debug the crashed process. */ /* Try to debug the crashed process. */
g_Creport.BuildReport(crashed_pid, argv[1][0] == '1'); g_Creport.BuildReport(crashed_pid, argv[1][0] == '1');
if (g_Creport.WasSuccessful()) { if (g_Creport.WasSuccessful()) {
g_Creport.SaveReport(); g_Creport.SaveReport();
DoWithSmSession([&]() { DoWithSmSession([&]() {
if (R_SUCCEEDED(nsdevInitialize())) { if (R_SUCCEEDED(nsdevInitialize())) {
nsdevTerminateProcess(crashed_pid); nsdevTerminateProcess(crashed_pid);
nsdevExit(); nsdevExit();
} }
}); });
/* Don't fatal if we have extra info. */ /* Don't fatal if we have extra info. */
if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_500)) { if ((GetRuntimeFirmwareVersion() >= FirmwareVersion_500)) {
if (g_Creport.IsApplication()) { if (g_Creport.IsApplication()) {
@ -138,15 +138,15 @@ int main(int argc, char **argv) {
} else if (argv[1][0] == '1') { } else if (argv[1][0] == '1') {
return 0; return 0;
} }
/* Also don't fatal if we're a user break. */ /* Also don't fatal if we're a user break. */
if (g_Creport.IsUserBreak()) { if (g_Creport.IsUserBreak()) {
return 0; return 0;
} }
FatalContext *ctx = g_Creport.GetFatalContext(); FatalContext *ctx = g_Creport.GetFatalContext();
fatalWithContext(g_Creport.GetResult(), FatalType_ErrorScreen, ctx); fatalWithContext(g_Creport.GetResult(), FatalType_ErrorScreen, ctx);
} }
} }

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <switch.h> #include <switch.h>
#include <cstring> #include <cstring>
@ -22,12 +22,15 @@
void ThreadInfo::SaveToFile(FILE *f_report) { void ThreadInfo::SaveToFile(FILE *f_report) {
fprintf(f_report, " Thread ID: %016lx\n", this->thread_id); fprintf(f_report, " Thread ID: %016lx\n", this->thread_id);
if (stack_top) { if (strcmp(name, "") != 0) {
fprintf(f_report, " Thread Name: %s\n", this->name);
}
if (stack_top) {
fprintf(f_report, " Stack: %016lx-%016lx\n", this->stack_bottom, this->stack_top); fprintf(f_report, " Stack: %016lx-%016lx\n", this->stack_bottom, this->stack_top);
} }
fprintf(f_report, " Registers:\n"); fprintf(f_report, " Registers:\n");
{ {
for (unsigned int i = 0; i <= 28; i++) { for (unsigned int i = 0; i <= 28; i++) {
fprintf(f_report, " X[%02u]: %s\n", i, this->code_list->GetFormattedAddressString(this->context.cpu_gprs[i].x)); fprintf(f_report, " X[%02u]: %s\n", i, this->code_list->GetFormattedAddressString(this->context.cpu_gprs[i].x));
} }
fprintf(f_report, " FP: %s\n", this->code_list->GetFormattedAddressString(this->context.fp)); fprintf(f_report, " FP: %s\n", this->code_list->GetFormattedAddressString(this->context.fp));
@ -39,11 +42,21 @@ void ThreadInfo::SaveToFile(FILE *f_report) {
for (unsigned int i = 0; i < this->stack_trace_size; i++) { for (unsigned int i = 0; i < this->stack_trace_size; i++) {
fprintf(f_report, " ReturnAddress[%02u]: %s\n", i, this->code_list->GetFormattedAddressString(this->stack_trace[i])); fprintf(f_report, " ReturnAddress[%02u]: %s\n", i, this->code_list->GetFormattedAddressString(this->stack_trace[i]));
} }
if (this->tls_address != 0) {
fprintf(f_report, " TLS Address: %016lx\n", this->tls_address);
fprintf(f_report, " 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 u32 ofs = i * 0x10;
fprintf(f_report, " %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]);
}
}
} }
bool ThreadInfo::ReadFromProcess(Handle debug_handle, u64 thread_id, bool is_64_bit) { bool ThreadInfo::ReadFromProcess(std::map<u64, u64> &tls_map, Handle debug_handle, u64 thread_id, bool is_64_bit) {
this->thread_id = thread_id; this->thread_id = thread_id;
/* Verify that the thread is running or waiting. */ /* Verify that the thread is running or waiting. */
{ {
u64 _; u64 _;
@ -51,43 +64,63 @@ bool ThreadInfo::ReadFromProcess(Handle debug_handle, u64 thread_id, bool is_64_
if (R_FAILED(svcGetDebugThreadParam(&_, &thread_state, debug_handle, this->thread_id, DebugThreadParam_State))) { if (R_FAILED(svcGetDebugThreadParam(&_, &thread_state, debug_handle, this->thread_id, DebugThreadParam_State))) {
return false; return false;
} }
if (thread_state > 1) { if (thread_state > 1) {
return false; return false;
} }
} }
/* Get the thread context. */ /* Get the thread context. */
if (R_FAILED(svcGetDebugThreadContext(&this->context, debug_handle, this->thread_id, 0xF))) { if (R_FAILED(svcGetDebugThreadContext(&this->context, debug_handle, this->thread_id, 0xF))) {
return false; return false;
} }
/* Don't try to parse stack frames if 32-bit. */ /* Don't try to parse stack frames if 32-bit. */
if (!is_64_bit) { if (!is_64_bit) {
return true; return true;
} }
/* Parse information from TLS if present. */
if (tls_map.find(thread_id) != tls_map.end()) {
this->tls_address = tls_map[thread_id];
u8 thread_tls[0x200];
if (R_SUCCEEDED(svcReadDebugProcessMemory(thread_tls, debug_handle, this->tls_address, sizeof(thread_tls)))) {
std::memcpy(this->tls, thread_tls, sizeof(this->tls));
/* Try to detect libnx threads, and skip name parsing then. */
if (*(reinterpret_cast<u32 *>(&thread_tls[0x1E0])) != 0x21545624) {
u8 thread_type[0x1D0];
const u64 thread_type_addr = *(reinterpret_cast<u64 *>(&thread_tls[0x1F8]));
if (R_SUCCEEDED(svcReadDebugProcessMemory(thread_type, debug_handle, thread_type_addr, sizeof(thread_type)))) {
/* Check thread name is actually at thread name. */
if (*(reinterpret_cast<u64 *>(&thread_type[0x1A8])) == thread_type_addr + 0x188) {
std::memcpy(this->name, thread_type + 0x188, 0x20);
}
}
}
}
}
/* Try to locate stack top/bottom. */ /* Try to locate stack top/bottom. */
TryGetStackInfo(debug_handle); TryGetStackInfo(debug_handle);
u64 cur_fp = this->context.fp; u64 cur_fp = this->context.fp;
for (unsigned int i = 0; i < sizeof(this->stack_trace)/sizeof(u64); i++) { for (unsigned int i = 0; i < sizeof(this->stack_trace)/sizeof(u64); i++) {
/* Validate the current frame. */ /* Validate the current frame. */
if (cur_fp == 0 || (cur_fp & 0xF)) { if (cur_fp == 0 || (cur_fp & 0xF)) {
break; break;
} }
/* Read a new frame. */ /* Read a new frame. */
StackFrame cur_frame; StackFrame cur_frame;
if (R_FAILED(svcReadDebugProcessMemory(&cur_frame, debug_handle, cur_fp, sizeof(StackFrame)))) { if (R_FAILED(svcReadDebugProcessMemory(&cur_frame, debug_handle, cur_fp, sizeof(StackFrame)))) {
break; break;
} }
/* Advance to the next frame. */ /* Advance to the next frame. */
this->stack_trace[this->stack_trace_size++] = cur_frame.lr; this->stack_trace[this->stack_trace_size++] = cur_frame.lr;
cur_fp = cur_frame.fp; cur_fp = cur_frame.fp;
} }
return true; return true;
} }
@ -97,19 +130,19 @@ void ThreadInfo::TryGetStackInfo(Handle debug_handle) {
if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, this->context.sp))) { if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, this->context.sp))) {
return; return;
} }
/* Check if sp points into the stack. */ /* Check if sp points into the stack. */
if (mi.type == MemType_MappedMemory) { if (mi.type == MemType_MappedMemory) {
this->stack_bottom = mi.addr; this->stack_bottom = mi.addr;
this->stack_top = mi.addr + mi.size; this->stack_top = mi.addr + mi.size;
return; return;
} }
/* It's possible that sp is below the stack... */ /* It's possible that sp is below the stack... */
if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, mi.addr + mi.size))) { if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, mi.addr + mi.size))) {
return; return;
} }
if (mi.type == MemType_MappedMemory) { if (mi.type == MemType_MappedMemory) {
this->stack_bottom = mi.addr; this->stack_bottom = mi.addr;
this->stack_top = mi.addr + mi.size; this->stack_top = mi.addr + mi.size;
@ -119,7 +152,11 @@ void ThreadInfo::TryGetStackInfo(Handle debug_handle) {
void ThreadInfo::DumpBinary(FILE *f_bin) { void ThreadInfo::DumpBinary(FILE *f_bin) {
fwrite(&this->thread_id, sizeof(this->thread_id), 1, f_bin); fwrite(&this->thread_id, sizeof(this->thread_id), 1, f_bin);
fwrite(&this->context, sizeof(this->context), 1, f_bin); fwrite(&this->context, sizeof(this->context), 1, f_bin);
fwrite(&this->tls_address, sizeof(this->tls_address), 1, f_bin);
fwrite(&this->tls, sizeof(this->tls), 1, f_bin);
fwrite(&this->name, sizeof(this->name), 1, f_bin);
u64 sts = this->stack_trace_size; u64 sts = this->stack_trace_size;
fwrite(&sts, sizeof(sts), 1, f_bin); fwrite(&sts, sizeof(sts), 1, f_bin);
fwrite(this->stack_trace, sizeof(u64), this->stack_trace_size, f_bin); fwrite(this->stack_trace, sizeof(u64), this->stack_trace_size, f_bin);
@ -128,7 +165,7 @@ void ThreadInfo::DumpBinary(FILE *f_bin) {
} }
void ThreadList::DumpBinary(FILE *f_bin, u64 crashed_id) { void ThreadList::DumpBinary(FILE *f_bin, u64 crashed_id) {
u32 magic = 0x30495444; /* 'DTI0' */ u32 magic = 0x31495444; /* 'DTI1' */
fwrite(&magic, sizeof(magic), 1, f_bin); fwrite(&magic, sizeof(magic), 1, f_bin);
fwrite(&this->thread_count, sizeof(u32), 1, f_bin); fwrite(&this->thread_count, sizeof(u32), 1, f_bin);
fwrite(&crashed_id, sizeof(crashed_id), 1, f_bin); fwrite(&crashed_id, sizeof(crashed_id), 1, f_bin);
@ -145,21 +182,21 @@ void ThreadList::SaveToFile(FILE *f_report) {
} }
} }
void ThreadList::ReadThreadsFromProcess(Handle debug_handle, bool is_64_bit) { void ThreadList::ReadThreadsFromProcess(std::map<u64, u64> &tls_map, Handle debug_handle, bool is_64_bit) {
u32 thread_count; u32 thread_count;
u64 thread_ids[max_thread_count]; u64 thread_ids[max_thread_count];
if (R_FAILED(svcGetThreadList(&thread_count, thread_ids, max_thread_count, debug_handle))) { if (R_FAILED(svcGetThreadList(&thread_count, thread_ids, max_thread_count, debug_handle))) {
this->thread_count = 0; this->thread_count = 0;
return; return;
} }
if (thread_count > max_thread_count) { if (thread_count > max_thread_count) {
thread_count = max_thread_count; thread_count = max_thread_count;
} }
for (unsigned int i = 0; i < thread_count; i++) { for (unsigned int i = 0; i < thread_count; i++) {
if (this->thread_infos[this->thread_count].ReadFromProcess(debug_handle, thread_ids[this->thread_count], is_64_bit)) { if (this->thread_infos[this->thread_count].ReadFromProcess(tls_map, debug_handle, thread_ids[this->thread_count], is_64_bit)) {
this->thread_count++; this->thread_count++;
} }
} }

View file

@ -13,10 +13,11 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <switch.h> #include <switch.h>
#include <cstdio> #include <cstdio>
#include <map>
#include "creport_debug_types.hpp" #include "creport_debug_types.hpp"
@ -30,15 +31,18 @@ class ThreadInfo {
u64 stack_bottom = 0; u64 stack_bottom = 0;
u64 stack_trace[0x20]{}; u64 stack_trace[0x20]{};
u32 stack_trace_size = 0; u32 stack_trace_size = 0;
u64 tls_address = 0;
u8 tls[0x100]{};
char name[0x40]{};
CodeList *code_list; CodeList *code_list;
public: public:
u64 GetPC() const { return context.pc.x; } u64 GetPC() const { return context.pc.x; }
u64 GetLR() const { return context.lr; } u64 GetLR() const { return context.lr; }
u64 GetId() const { return thread_id; } u64 GetId() const { return thread_id; }
u32 GetStackTraceSize() const { return stack_trace_size; } u32 GetStackTraceSize() const { return stack_trace_size; }
u64 GetStackTrace(u32 i) const { return stack_trace[i]; } u64 GetStackTrace(u32 i) const { return stack_trace[i]; }
bool ReadFromProcess(Handle debug_handle, u64 thread_id, bool is_64_bit); bool ReadFromProcess(std::map<u64, u64> &tls_map, Handle debug_handle, u64 thread_id, bool is_64_bit);
void SaveToFile(FILE *f_report); void SaveToFile(FILE *f_report);
void DumpBinary(FILE *f_bin); void DumpBinary(FILE *f_bin);
void SetCodeList(CodeList *cl) { this->code_list = cl; } void SetCodeList(CodeList *cl) { this->code_list = cl; }
@ -54,11 +58,11 @@ class ThreadList {
public: public:
u32 GetThreadCount() const { return thread_count; } u32 GetThreadCount() const { return thread_count; }
const ThreadInfo *GetThreadInfo(u32 i) const { return &thread_infos[i]; } const ThreadInfo *GetThreadInfo(u32 i) const { return &thread_infos[i]; }
void SaveToFile(FILE *f_report); void SaveToFile(FILE *f_report);
void DumpBinary(FILE *f_bin, u64 crashed_id); void DumpBinary(FILE *f_bin, u64 crashed_id);
void ReadThreadsFromProcess(Handle debug_handle, bool is_64_bit); void ReadThreadsFromProcess(std::map<u64, u64> &tls_map, Handle debug_handle, bool is_64_bit);
void SetCodeList(CodeList *cl) { void SetCodeList(CodeList *cl) {
for (u32 i = 0; i < thread_count; i++) { for (u32 i = 0; i < thread_count; i++) {
thread_infos[i].SetCodeList(cl); thread_infos[i].SetCodeList(cl);
} }