feat: show crash reason on boot (#1453)

## Summary

If the system reboots from crash, display the reason and tell user to
include `crash_report.txt` file.

To test this, simply add an `assert(false)` somewhere inside the code.


![screenshot-74453.bmp](https://github.com/user-attachments/files/26160373/screenshot-74453.bmp)

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? **NO**
This commit is contained in:
Xuan-Son Nguyen
2026-04-13 15:23:45 -05:00
committed by GitHub
parent 4e9c7a787f
commit 243ae8b408
6 changed files with 89 additions and 5 deletions
+4
View File
@@ -294,3 +294,7 @@ STR_LINK: "[link]"
STR_SCREENSHOT_BUTTON: "Take screenshot"
STR_AUTO_TURN_ENABLED: "Auto Turn Enabled: "
STR_AUTO_TURN_PAGES_PER_MIN: "Auto Turn (Pages Per Minute)"
STR_CRASH_TITLE: "System Crash"
STR_CRASH_DESCRIPTION: "A detailed report was saved to crash_report.txt. Please include this file in your bug report."
STR_CRASH_REASON: "Crash reason:"
STR_CRASH_NO_REASON: "(No reason was recorded)"
+3
View File
@@ -5,6 +5,7 @@
#include "boot_sleep/BootActivity.h"
#include "boot_sleep/SleepActivity.h"
#include "browser/OpdsBookBrowserActivity.h"
#include "home/CrashActivity.h"
#include "home/FileBrowserActivity.h"
#include "home/HomeActivity.h"
#include "home/RecentBooksActivity.h"
@@ -196,6 +197,8 @@ void ActivityManager::goToFullScreenMessage(std::string message, EpdFontFamily::
replaceActivity(std::make_unique<FullScreenMessageActivity>(renderer, mappedInput, std::move(message), style));
}
void ActivityManager::goToCrashReport() { replaceActivity(std::make_unique<CrashActivity>(renderer, mappedInput)); }
void ActivityManager::goHome() { replaceActivity(std::make_unique<HomeActivity>(renderer, mappedInput)); }
void ActivityManager::pushActivity(std::unique_ptr<Activity>&& activity) {
+1
View File
@@ -86,6 +86,7 @@ class ActivityManager {
void goToSleep();
void goToBoot();
void goToFullScreenMessage(std::string message, EpdFontFamily::Style style = EpdFontFamily::REGULAR);
void goToCrashReport();
void goHome();
// This will move current activity to stack instead of deleting it
+61
View File
@@ -0,0 +1,61 @@
#include "CrashActivity.h"
#include <GfxRenderer.h>
#include <HalSystem.h>
#include <I18n.h>
#include "components/UITheme.h"
#include "fontIds.h"
void CrashActivity::onEnter() {
Activity::onEnter();
panicMessage = HalSystem::getPanicInfo(false);
if (panicMessage.empty()) {
panicMessage = tr(STR_CRASH_NO_REASON);
}
HalSystem::clearPanic();
requestUpdateAndWait();
}
void CrashActivity::loop() {
if (mappedInput.isPressed(MappedInputManager::Button::Back)) {
finish();
}
}
void CrashActivity::render(RenderLock&&) {
renderer.clearScreen();
const auto& metrics = UITheme::getInstance().getMetrics();
const auto pageWidth = renderer.getScreenWidth();
const auto contentWidth = pageWidth - 2 * metrics.contentSidePadding;
const auto x = metrics.contentSidePadding;
const auto lineHeight = renderer.getLineHeight(UI_10_FONT_ID);
GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_CRASH_TITLE));
int y = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing;
auto descLines = renderer.wrappedText(UI_10_FONT_ID, tr(STR_CRASH_DESCRIPTION), contentWidth, 10);
for (const auto& line : descLines) {
renderer.drawText(UI_10_FONT_ID, x, y, line.c_str());
y += lineHeight;
}
y += metrics.verticalSpacing * 2;
renderer.drawText(UI_10_FONT_ID, x, y, tr(STR_CRASH_REASON));
y += lineHeight + metrics.verticalSpacing;
auto panicLines = renderer.wrappedText(UI_10_FONT_ID, panicMessage.c_str(), contentWidth, 5);
for (const auto& line : panicLines) {
renderer.drawText(UI_10_FONT_ID, x, y, line.c_str());
y += lineHeight;
}
const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", "");
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
renderer.displayBuffer();
}
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include "../Activity.h"
class CrashActivity final : public Activity {
std::string panicMessage;
public:
explicit CrashActivity(GfxRenderer& renderer, MappedInputManager& mappedInput)
: Activity("Crash", renderer, mappedInput) {}
void onEnter() override;
void loop() override;
void render(RenderLock&&) override;
};
+7 -5
View File
@@ -253,7 +253,6 @@ void setup() {
}
HalSystem::checkPanic();
HalSystem::clearPanic(); // TODO: move this to an activity when we have one to display the panic info
SETTINGS.loadFromFile();
I18N.loadSettings();
@@ -290,10 +289,13 @@ void setup() {
APP_STATE.loadFromFile();
RECENT_BOOKS.loadFromFile();
// Boot to home screen if no book is open, last sleep was not from reader, back button is held, or reader activity
// crashed (indicated by readerActivityLoadCount > 0)
if (APP_STATE.openEpubPath.empty() || !APP_STATE.lastSleepFromReader ||
mappedInputManager.isPressed(MappedInputManager::Button::Back) || APP_STATE.readerActivityLoadCount > 0) {
if (HalSystem::isRebootFromPanic()) {
// If we rebooted from a panic, go to crash report screen to show the panic info
activityManager.goToCrashReport();
} else if (APP_STATE.openEpubPath.empty() || !APP_STATE.lastSleepFromReader ||
mappedInputManager.isPressed(MappedInputManager::Button::Back) || APP_STATE.readerActivityLoadCount > 0) {
// Boot to home screen if no book is open, last sleep was not from reader, back button is held, or reader activity
// crashed (indicated by readerActivityLoadCount > 0)
activityManager.goHome();
} else {
// Clear app state to avoid getting into a boot loop if the epub doesn't load