2018-06-25 06:42:26 +00:00
|
|
|
#include <switch.h>
|
|
|
|
#include "creport_crash_report.hpp"
|
2018-06-25 07:45:25 +00:00
|
|
|
#include "creport_debug_types.hpp"
|
2018-06-25 06:42:26 +00:00
|
|
|
|
|
|
|
void CrashReport::BuildReport(u64 pid, bool has_extra_info) {
|
|
|
|
this->has_extra_info = has_extra_info;
|
|
|
|
if (OpenProcess(pid)) {
|
2018-06-25 07:45:25 +00:00
|
|
|
ProcessExceptions();
|
|
|
|
|
|
|
|
/* TODO: More stuff here (sub_7100002260)... */
|
2018-06-25 06:42:26 +00:00
|
|
|
Close();
|
|
|
|
}
|
2018-06-25 07:45:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CrashReport::ProcessExceptions() {
|
|
|
|
if (!IsOpen()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
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:
|
|
|
|
case DebugEventType::ExitProcess:
|
|
|
|
case DebugEventType::ExitThread:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CrashReport::HandleAttachProcess(DebugEventInfo &d) {
|
|
|
|
this->process_info = d.info.attach_process;
|
|
|
|
if (kernelAbove500() && IsApplication()) {
|
|
|
|
/* Parse out user data. */
|
|
|
|
MemoryInfo mi;
|
|
|
|
u32 pi;
|
|
|
|
u64 address = this->process_info.user_exception_context_address;
|
|
|
|
u64 userdata_address = 0;
|
|
|
|
u64 userdata_size = 0;
|
|
|
|
|
|
|
|
if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, this->debug_handle, address))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Must be read or read-write */
|
|
|
|
if ((mi.perm | Perm_W) != Perm_Rw) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Must have space for both userdata address and userdata size. */
|
|
|
|
if (address < mi.addr || mi.addr + mi.size < address + 2 * sizeof(u64)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read userdata address. */
|
|
|
|
if (R_FAILED(svcReadDebugProcessMemory(&userdata_address, this->debug_handle, address, sizeof(userdata_address)))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Validate userdata address. */
|
|
|
|
if (userdata_address == 0 || userdata_address & 0xFFF) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read userdata size. */
|
|
|
|
if (R_FAILED(svcReadDebugProcessMemory(&userdata_size, this->debug_handle, address + sizeof(userdata_address), sizeof(userdata_size)))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Cap userdata size. */
|
|
|
|
if (userdata_size > 0x1000) {
|
|
|
|
userdata_size = 0x1000;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Assign. */
|
|
|
|
this->userdata_5x_address = userdata_address;
|
|
|
|
this->userdata_5x_size = userdata_size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CrashReport::HandleException(DebugEventInfo &d) {
|
|
|
|
switch (d.info.exception.type) {
|
|
|
|
case DebugExceptionType::UndefinedInstruction:
|
|
|
|
case DebugExceptionType::InstructionAbort:
|
|
|
|
case DebugExceptionType::DataAbort:
|
|
|
|
case DebugExceptionType::AlignmentFault:
|
|
|
|
case DebugExceptionType::UserBreak:
|
|
|
|
case DebugExceptionType::BadSvc:
|
|
|
|
case DebugExceptionType::UnknownNine:
|
|
|
|
/* TODO: Handle these exceptions...creport seems to discard all but the latest exception? */
|
|
|
|
break;
|
|
|
|
case DebugExceptionType::DebuggerAttached:
|
|
|
|
case DebugExceptionType::BreakPoint:
|
|
|
|
case DebugExceptionType::DebuggerBreak:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2018-06-25 06:42:26 +00:00
|
|
|
}
|