app-module events & callstack config, crash diagnostics (#630)
- 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.
This commit is contained in:
committed by
GitHub
parent
c656ee9ffd
commit
92ca046681
@@ -1,8 +1,12 @@
|
||||
#ifdef ESP_PLATFORM
|
||||
|
||||
#include "Tactility/PanicHandler.h"
|
||||
|
||||
|
||||
#include <Tactility/app/crashdiagnostics/QrHelpers.h>
|
||||
#include <Tactility/app/crashdiagnostics/QrUrl.h>
|
||||
#include <Tactility/app/launcher/Launcher.h>
|
||||
#include <Tactility/file/File.h>
|
||||
#include <Tactility/lvgl/Statusbar.h>
|
||||
|
||||
#include <app/event.h>
|
||||
@@ -17,8 +21,19 @@
|
||||
#include <tactility/check.h>
|
||||
#include <tactility/drivers/pointer.h>
|
||||
#include <tactility/log.h>
|
||||
#include <tactility/paths.h>
|
||||
|
||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
#include <esp_cpu_utils.h>
|
||||
#else
|
||||
#include <esp_cpu.h>
|
||||
#endif
|
||||
|
||||
#include <sdkconfig.h>
|
||||
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
namespace tt::app::crashdiagnostics {
|
||||
|
||||
@@ -41,16 +56,70 @@ struct Context {
|
||||
};
|
||||
|
||||
|
||||
const char* crashCauseToString(CrashCause cause) {
|
||||
switch (cause) {
|
||||
case CrashCause::Debug: return "Debug";
|
||||
case CrashCause::WatchdogInterrupt: return "Watchdog (interrupt)";
|
||||
case CrashCause::WatchdogTask: return "Watchdog (task)";
|
||||
case CrashCause::Abort: return "Abort";
|
||||
case CrashCause::Fault: return "Fault";
|
||||
case CrashCause::Unknown:
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
std::string formatCrashData(const CrashData& crashData) {
|
||||
std::stringstream stream;
|
||||
|
||||
stream << "Cause: " << crashCauseToString(crashData.cause) << "\n";
|
||||
|
||||
stream << "Reason: ";
|
||||
if (crashData.reason[0] != '\0') {
|
||||
stream << crashData.reason;
|
||||
} else {
|
||||
stream << "unknown";
|
||||
}
|
||||
stream << "\n";
|
||||
|
||||
stream << "Fault address: " << std::hex << std::setw(8) << std::setfill('0') << crashData.faultAddress << std::dec << "\n";
|
||||
|
||||
stream << "Callstack" << (crashData.callstackCorrupted ? " (corrupted)" : "") << ":";
|
||||
if (crashData.callstackLength > 0) {
|
||||
stream << "\n";
|
||||
for (uint8_t i = 0; i < crashData.callstackLength; i++) {
|
||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
uint32_t pc = esp_cpu_process_stack_pc(crashData.callstack[i].pc);
|
||||
#else
|
||||
uint32_t pc = crashData.callstack[i].pc; // No processing needed on RISC-V
|
||||
#endif
|
||||
stream << std::hex << std::setw(8) << std::setfill('0') << pc << std::dec << " ";
|
||||
}
|
||||
} else {
|
||||
stream << " empty" << "\n";
|
||||
}
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
// Best-effort: crash.txt is a convenience for offline inspection, not required for the app to work.
|
||||
void writeCrashLogFile(const CrashData& crashData) {
|
||||
char root[128];
|
||||
if (paths_get_data_path(root, sizeof(root)) != ERROR_NONE) {
|
||||
LOG_E(TAG, "Failed to resolve data path for crash.txt");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string path = std::string(root) + "/crash.txt";
|
||||
file::FileMutexGuard guard(path);
|
||||
if (!file::writeString(path, formatCrashData(crashData))) {
|
||||
LOG_E(TAG, "Failed to write %s", path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void onContinuePressed(lv_event_t* event) {
|
||||
auto* ctx = static_cast<Context*>(lv_event_get_user_data(event));
|
||||
ctx->continuePressed = true;
|
||||
// Async, non-blocking - must NOT call app_manager_stop() directly here: that bound-waits
|
||||
// (thread_join) for this app's own thread to finish, which needs the LVGL lock
|
||||
// (window_manager_remove()) - but this callback runs ON the LVGL task, which would
|
||||
// deadlock against itself. launcher::start() is deferred to appMain(), after this app's
|
||||
// own thread has finished cleaning up.
|
||||
AppEvent closeEvent { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} };
|
||||
app_event_emit(ctx->appInstanceId, &closeEvent);
|
||||
app_event_emit_close(ctx->appInstanceId);
|
||||
}
|
||||
|
||||
void createWidgets(lv_obj_t* parent, void* userData) {
|
||||
@@ -72,7 +141,9 @@ void createWidgets(lv_obj_t* parent, void* userData) {
|
||||
}
|
||||
lv_obj_align(bottom_label, LV_ALIGN_BOTTOM_MID, 0, -2);
|
||||
|
||||
std::string url = getUrlFromCrashData();
|
||||
const auto& crash_data = getRtcCrashData();
|
||||
|
||||
std::string url = getUrlFromCrashData(crash_data);
|
||||
LOG_I(TAG, "%s", url.c_str());
|
||||
size_t url_length = url.length();
|
||||
|
||||
@@ -156,6 +227,8 @@ int32_t appMain(int argc, char* argv[]) {
|
||||
Context ctx {};
|
||||
ctx.appInstanceId = appInstanceId;
|
||||
|
||||
writeCrashLogFile(getRtcCrashData());
|
||||
|
||||
TaskEventGroup event_group {};
|
||||
task_event_group_construct(&event_group);
|
||||
|
||||
@@ -207,7 +280,7 @@ extern const ::AppManifest manifest = {
|
||||
.id = "tactility.crashdiagnostics",
|
||||
.name = "Crash Diagnostics",
|
||||
.category = APP_CATEGORY_SYSTEM,
|
||||
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
|
||||
.location = { .type = APP_LOCATION_MEMORY, .location = reinterpret_cast<void*>(appMain) },
|
||||
.flags = APP_MANIFEST_FLAG_HIDDEN,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#ifdef ESP_PLATFORM
|
||||
|
||||
#include <Tactility/app/crashdiagnostics/QrUrl.h>
|
||||
#include <Tactility/PanicHandler.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
@@ -14,8 +13,7 @@
|
||||
|
||||
#include <sdkconfig.h>
|
||||
|
||||
std::string getUrlFromCrashData() {
|
||||
auto crash_data = getRtcCrashData();
|
||||
std::string getUrlFromCrashData(const CrashData& crash_data) {
|
||||
std::vector<uint32_t> stack_buffer(crash_data.callstackLength * 2);
|
||||
for (int i = 0; i < crash_data.callstackLength; ++i) {
|
||||
const CallstackFrame&frame = crash_data.callstack[i];
|
||||
|
||||
Reference in New Issue
Block a user