92ca046681
- Apps can specify task stack depth and preferred memory placement in their manifests. - App identifiers are validated against length and character requirements. - Crash diagnostics now show the crash cause, reason, fault address, call stack, and program-counter details, with logs saved for review. - App closing is more consistent across built-in screens. There's now a dedicated function, and the old _emit() function is made private. - Crash diagnostics no longer display a QR code without a call stack. - Improved memory allocation for unrestricted requests.
129 lines
4.2 KiB
C++
129 lines
4.2 KiB
C++
#if defined(ESP_PLATFORM)
|
|
#include <sdkconfig.h>
|
|
#endif
|
|
|
|
#if defined(ESP_PLATFORM) && defined(CONFIG_IDF_TARGET_ARCH_XTENSA)
|
|
|
|
#include <Tactility/PanicHandler.h>
|
|
|
|
#include <esp_attr.h>
|
|
#include <esp_cpu.h>
|
|
#include <esp_cpu_utils.h>
|
|
#include <esp_debug_helpers.h>
|
|
#include <esp_memory_utils.h>
|
|
#include <esp_private/panic_internal.h>
|
|
#include <xtensa/xtruntime.h>
|
|
|
|
#include <cstring>
|
|
|
|
extern "C" {
|
|
|
|
/**
|
|
* This static variable survives a crash reboot.
|
|
* It is reset by the Boot app.
|
|
*/
|
|
static RTC_NOINIT_ATTR CrashData crashData;
|
|
|
|
void __real_esp_panic_handler(void* info);
|
|
|
|
void __wrap_esp_panic_handler(void* info) {
|
|
|
|
esp_backtrace_frame_t frame = {
|
|
.pc = 0,
|
|
.sp = 0,
|
|
.next_pc = 0,
|
|
.exc_frame = nullptr
|
|
};
|
|
|
|
const auto* panic_info = static_cast<const panic_info_t*>(info);
|
|
|
|
switch (panic_info->exception) {
|
|
// Watchdag timer issues are not consider real crashes: they trigger relatively often
|
|
// and could cause a previous real crash to be overwritten by a watchdog timer warning during reboot.
|
|
case PANIC_EXCEPTION_IWDT: crashData.cause = CrashCause::WatchdogInterrupt; return;
|
|
case PANIC_EXCEPTION_TWDT: crashData.cause = CrashCause::WatchdogTask; return;
|
|
// We also don't care about debugger errors:
|
|
case PANIC_EXCEPTION_DEBUG: crashData.cause = CrashCause::Debug; return;
|
|
// We only care about 'real' crashes:
|
|
case PANIC_EXCEPTION_ABORT: crashData.cause = CrashCause::Abort; break;
|
|
case PANIC_EXCEPTION_FAULT:
|
|
default: crashData.cause = CrashCause::Fault; break;
|
|
}
|
|
|
|
crashData.callstackLength = 0;
|
|
crashData.faultAddress = reinterpret_cast<uint32_t>(panic_info->addr);
|
|
|
|
// g_panic_abort_details carries the actual assert()/abort() message when present; panic_info->reason
|
|
// is ESP-IDF's generic description otherwise (e.g. "IllegalInstruction").
|
|
const char* reason = (panic_info->exception == PANIC_EXCEPTION_ABORT && g_panic_abort_details != nullptr)
|
|
? g_panic_abort_details
|
|
: panic_info->reason;
|
|
crashData.reason[0] = '\0';
|
|
if (reason != nullptr) {
|
|
strncpy(crashData.reason, reason, sizeof(crashData.reason) - 1);
|
|
crashData.reason[sizeof(crashData.reason) - 1] = '\0';
|
|
}
|
|
|
|
esp_backtrace_get_start(&frame.pc, &frame.sp, &frame.next_pc);
|
|
crashData.callstack[0].pc = frame.pc;
|
|
#if CRASH_DATA_INCLUDES_SP
|
|
crashData.callstack[0].sp = frame.sp;
|
|
#endif
|
|
crashData.callstackLength++;
|
|
|
|
uint32_t processed_pc = esp_cpu_process_stack_pc(frame.pc);
|
|
bool pc_is_valid = esp_ptr_executable((void *)processed_pc);
|
|
|
|
/* Ignore the first corrupted PC in case of InstrFetchProhibited on Xtensa */
|
|
if (frame.exc_frame && ((XtExcFrame *)frame.exc_frame)->exccause == EXCCAUSE_INSTR_PROHIBITED) {
|
|
pc_is_valid = true;
|
|
}
|
|
|
|
crashData.callstackCorrupted = !(esp_stack_ptr_is_sane(frame.sp) && pc_is_valid);
|
|
|
|
while (
|
|
frame.next_pc != 0 &&
|
|
!crashData.callstackCorrupted
|
|
&& crashData.callstackLength < CRASH_DATA_CALLSTACK_LIMIT
|
|
) {
|
|
if (esp_backtrace_get_next_frame(&frame)) {
|
|
// Validate the current frame
|
|
uint32_t processed_frame_pc = esp_cpu_process_stack_pc(frame.pc);
|
|
bool frame_pc_is_valid = esp_ptr_executable((void *)processed_frame_pc);
|
|
|
|
if (!esp_stack_ptr_is_sane(frame.sp) || !frame_pc_is_valid) {
|
|
crashData.callstackCorrupted = true;
|
|
break;
|
|
}
|
|
crashData.callstack[crashData.callstackLength].pc = frame.pc;
|
|
#if CRASH_DATA_INCLUDES_SP
|
|
crashData.callstack[crashData.callstackLength].sp = frame.sp;
|
|
#endif
|
|
crashData.callstackLength++;
|
|
} else {
|
|
crashData.callstackCorrupted = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// TODO: Handle corrupted logic
|
|
|
|
__real_esp_panic_handler(info);
|
|
}
|
|
|
|
}
|
|
|
|
const CrashData& getRtcCrashData() { return crashData; }
|
|
|
|
#elif defined(ESP_PLATFORM)
|
|
|
|
// Stub implementation for RISC-V and other architectures
|
|
// TODO: Implement crash data collection for RISC-V using frame pointer or EH frame
|
|
|
|
#include <Tactility/PanicHandler.h>
|
|
|
|
static CrashData emptyCrashData = {};
|
|
|
|
const CrashData& getRtcCrashData() { return emptyCrashData; }
|
|
|
|
#endif |