mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-26 13:52:21 +00:00
creport: Complete crash report saving.
This commit is contained in:
parent
d5b303f852
commit
4eadeb021b
7 changed files with 231 additions and 45 deletions
|
@ -2,6 +2,19 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include "creport_code_info.hpp"
|
#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) {
|
void CodeList::ReadCodeRegionsFromProcess(Handle debug_handle, u64 pc, u64 lr) {
|
||||||
u64 code_base;
|
u64 code_base;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
#include "creport_debug_types.hpp"
|
#include "creport_debug_types.hpp"
|
||||||
|
|
||||||
|
@ -19,6 +20,7 @@ class CodeList {
|
||||||
CodeList() : code_count(0) { }
|
CodeList() : code_count(0) { }
|
||||||
|
|
||||||
void ReadCodeRegionsFromProcess(Handle debug_handle, u64 pc, u64 lr);
|
void ReadCodeRegionsFromProcess(Handle debug_handle, u64 pc, u64 lr);
|
||||||
|
void SaveToFile(FILE *f_report);
|
||||||
private:
|
private:
|
||||||
bool TryFindCodeRegion(Handle debug_handle, u64 guess, u64 *address);
|
bool TryFindCodeRegion(Handle debug_handle, u64 guess, u64 *address);
|
||||||
void GetCodeInfoName(u64 debug_handle, u64 ro_address, char *name);
|
void GetCodeInfoName(u64 debug_handle, u64 ro_address, char *name);
|
||||||
|
|
|
@ -7,43 +7,6 @@
|
||||||
#include "creport_crash_report.hpp"
|
#include "creport_crash_report.hpp"
|
||||||
#include "creport_debug_types.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) {
|
void CrashReport::BuildReport(u64 pid, bool has_extra_info) {
|
||||||
this->has_extra_info = has_extra_info;
|
this->has_extra_info = has_extra_info;
|
||||||
if (OpenProcess(pid)) {
|
if (OpenProcess(pid)) {
|
||||||
|
@ -125,14 +88,13 @@ void CrashReport::HandleAttachProcess(DebugEventInfo &d) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashReport::HandleException(DebugEventInfo &d) {
|
void CrashReport::HandleException(DebugEventInfo &d) {
|
||||||
this->exception_info = d.info.exception;
|
|
||||||
switch (d.info.exception.type) {
|
switch (d.info.exception.type) {
|
||||||
case DebugExceptionType::UndefinedInstruction:
|
case DebugExceptionType::UndefinedInstruction:
|
||||||
this->result = (Result)CrashReportResult::UndefinedInstruction;
|
this->result = (Result)CrashReportResult::UndefinedInstruction;
|
||||||
break;
|
break;
|
||||||
case DebugExceptionType::InstructionAbort:
|
case DebugExceptionType::InstructionAbort:
|
||||||
this->result = (Result)CrashReportResult::InstructionAbort;
|
this->result = (Result)CrashReportResult::InstructionAbort;
|
||||||
this->exception_info.specific.raw = 0;
|
d.info.exception.specific.raw = 0;
|
||||||
break;
|
break;
|
||||||
case DebugExceptionType::DataAbort:
|
case DebugExceptionType::DataAbort:
|
||||||
this->result = (Result)CrashReportResult::DataAbort;
|
this->result = (Result)CrashReportResult::DataAbort;
|
||||||
|
@ -143,8 +105,8 @@ void CrashReport::HandleException(DebugEventInfo &d) {
|
||||||
case DebugExceptionType::UserBreak:
|
case DebugExceptionType::UserBreak:
|
||||||
this->result = (Result)CrashReportResult::UserBreak;
|
this->result = (Result)CrashReportResult::UserBreak;
|
||||||
/* Try to parse out the user break result. */
|
/* Try to parse out the user break result. */
|
||||||
if (kernelAbove500() && IsAddressReadable(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, this->exception_info.specific.user_break.address, sizeof(this->result));
|
svcReadDebugProcessMemory(&this->result, this->debug_handle, d.info.exception.specific.user_break.address, sizeof(this->result));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DebugExceptionType::BadSvc:
|
case DebugExceptionType::BadSvc:
|
||||||
|
@ -152,7 +114,7 @@ void CrashReport::HandleException(DebugEventInfo &d) {
|
||||||
break;
|
break;
|
||||||
case DebugExceptionType::UnknownNine:
|
case DebugExceptionType::UnknownNine:
|
||||||
this->result = (Result)CrashReportResult::UnknownNine;
|
this->result = (Result)CrashReportResult::UnknownNine;
|
||||||
this->exception_info.specific.raw = 0;
|
d.info.exception.specific.raw = 0;
|
||||||
break;
|
break;
|
||||||
case DebugExceptionType::DebuggerAttached:
|
case DebugExceptionType::DebuggerAttached:
|
||||||
case DebugExceptionType::BreakPoint:
|
case DebugExceptionType::BreakPoint:
|
||||||
|
@ -160,6 +122,7 @@ void CrashReport::HandleException(DebugEventInfo &d) {
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this->exception_info = d.info.exception;
|
||||||
/* Parse crashing thread info. */
|
/* Parse crashing thread info. */
|
||||||
this->crashed_thread_info.ReadFromProcess(this->debug_handle, d.thread_id, Is64Bit());
|
this->crashed_thread_info.ReadFromProcess(this->debug_handle, d.thread_id, Is64Bit());
|
||||||
}
|
}
|
||||||
|
@ -237,3 +200,121 @@ bool CrashReport::GetCurrentTime(u64 *out) {
|
||||||
}
|
}
|
||||||
return success;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
#include "creport_debug_types.hpp"
|
#include "creport_debug_types.hpp"
|
||||||
#include "creport_thread_info.hpp"
|
#include "creport_thread_info.hpp"
|
||||||
|
@ -43,14 +44,16 @@ class CrashReport {
|
||||||
ThreadList thread_list;
|
ThreadList thread_list;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CrashReport() : debug_handle(INVALID_HANDLE), result((Result)CrashReportResult::IncompleteReport), process_info({}), dying_message_address(0),
|
CrashReport() : debug_handle(INVALID_HANDLE), result((Result)CrashReportResult::IncompleteReport), process_info{0}, dying_message_address(0),
|
||||||
dying_message_size(0), dying_message{}, exception_info({}) { }
|
dying_message_size(0), dying_message{0}, exception_info({}) { }
|
||||||
|
|
||||||
void BuildReport(u64 pid, bool has_extra_info);
|
void BuildReport(u64 pid, bool has_extra_info);
|
||||||
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);
|
||||||
|
|
||||||
Result GetResult() {
|
Result GetResult() {
|
||||||
return this->result;
|
return this->result;
|
||||||
}
|
}
|
||||||
|
@ -91,6 +94,8 @@ class CrashReport {
|
||||||
void HandleAttachProcess(DebugEventInfo &d);
|
void HandleAttachProcess(DebugEventInfo &d);
|
||||||
void HandleException(DebugEventInfo &d);
|
void HandleException(DebugEventInfo &d);
|
||||||
|
|
||||||
|
void SaveToFile(FILE *f);
|
||||||
|
|
||||||
void EnsureReportDirectories();
|
void EnsureReportDirectories();
|
||||||
bool GetCurrentTime(u64 *out);
|
bool GetCurrentTime(u64 *out);
|
||||||
};
|
};
|
|
@ -36,6 +36,33 @@ enum class DebugExceptionType : u32 {
|
||||||
UnknownNine = 9,
|
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 {
|
struct UndefinedInstructionInfo {
|
||||||
u32 insn;
|
u32 insn;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,29 @@
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include "creport_thread_info.hpp"
|
#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) {
|
bool ThreadInfo::ReadFromProcess(Handle debug_handle, u64 thread_id, bool is_64_bit) {
|
||||||
this->thread_id = thread_id;
|
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) {
|
void ThreadList::ReadThreadsFromProcess(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];
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
#include "creport_debug_types.hpp"
|
#include "creport_debug_types.hpp"
|
||||||
|
|
||||||
|
@ -37,12 +38,15 @@ class ThreadInfo {
|
||||||
u64 stack_trace[0x20];
|
u64 stack_trace[0x20];
|
||||||
u32 stack_trace_size;
|
u32 stack_trace_size;
|
||||||
public:
|
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 GetPC() { return context.pc; }
|
||||||
u64 GetLR() { return context.lr; }
|
u64 GetLR() { return context.lr; }
|
||||||
|
u64 GetId() { return thread_id; }
|
||||||
|
|
||||||
bool ReadFromProcess(Handle debug_handle, u64 thread_id, bool is_64_bit);
|
bool ReadFromProcess(Handle debug_handle, u64 thread_id, bool is_64_bit);
|
||||||
|
void SaveToFile(FILE *f_report);
|
||||||
|
void DumpBinary(FILE *f_bin);
|
||||||
private:
|
private:
|
||||||
void TryGetStackInfo(Handle debug_handle);
|
void TryGetStackInfo(Handle debug_handle);
|
||||||
};
|
};
|
||||||
|
@ -55,5 +59,7 @@ class ThreadList {
|
||||||
public:
|
public:
|
||||||
ThreadList() : thread_count(0) { }
|
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);
|
void ReadThreadsFromProcess(Handle debug_handle, bool is_64_bit);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue