ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
exc_decoder.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file exc_decoder.h
6 * @brief ESP32 panic / exception decoder for a live diagnostics panel (PC_ENABLE_EXC_DECODER).
7 *
8 * When an ESP32 panics it prints a Guru Meditation dump: a cause ("LoadProhibited"), a per-core register
9 * dump (PC, EXCVADDR, ...), and a backtrace of `PC:SP` frame pairs. To resolve those PCs to file:line an
10 * addr2line-style panel needs the firmware ELF, which lives off-device - so the on-device job is to
11 * *extract and present* the raw decode: the cause, the faulting PC + data address, and the ordered
12 * backtrace PC list, served as JSON for a live "/exception" panel (the browser or a build server then
13 * resolves symbols). This is that extractor.
14 *
15 * It parses the panic text an app captures (from the console, a saved crash line, or a text rendering of
16 * the core-dump partition) into a structured ExcInfo, and serializes it. Pure, zero heap, no stdlib
17 * (hand-rolled hex/decimal parsing), host-testable against a captured panic string.
18 */
19
20#ifndef PROTOCORE_EXC_DECODER_H
21#define PROTOCORE_EXC_DECODER_H
22
23#include "protocore_config.h"
24#include <stddef.h>
25#include <stdint.h>
26
27#if PC_ENABLE_EXC_DECODER
28
29#ifndef PC_EXC_MAX_FRAMES
30#define PC_EXC_MAX_FRAMES 32 ///< backtrace frames retained (Xtensa panics rarely exceed this).
31#endif
32
33/** @brief One backtrace frame: a program counter and its stack pointer. */
34struct ExcFrame
35{
36 uint32_t pc;
37 uint32_t sp;
38};
39
40/** @brief A decoded panic. Fields not found in the input are left at their zeroed / -1 defaults. */
41struct ExcInfo
42{
43 int core; ///< panicking core number, or -1 if not present.
44 char cause[32]; ///< exception cause text (e.g. "LoadProhibited"), "" if absent.
45 uint32_t pc; ///< faulting PC (register-dump PC, else first backtrace frame).
46 uint32_t excvaddr; ///< faulting data address (EXCVADDR), 0 if absent.
47 bool has_excvaddr; ///< true if an EXCVADDR field was present.
48 ExcFrame frames[PC_EXC_MAX_FRAMES]; ///< backtrace, outermost-first as printed.
49 size_t frame_count;
50};
51
52/**
53 * @brief Parse an ESP32 panic dump into @p out.
54 *
55 * Recognizes the Guru Meditation cause, the core number, the register-dump PC + EXCVADDR, and the
56 * `Backtrace: pc:sp pc:sp ...` frame list. Tolerant of missing fields and of a trailing "|<-CORRUPTED".
57 * @return true if at least one of {cause, pc, a backtrace frame} was found.
58 */
59bool pc_exc_parse(const char *text, ExcInfo *out);
60
61/**
62 * @brief Serialize a decoded panic as
63 * `{"core":N,"cause":"..","pc":"0x..","excvaddr":"0x..","backtrace":["0x..",...]}`.
64 * `core` is omitted when -1; `excvaddr` is omitted when absent.
65 * @return length written (excl NUL), or 0 on overflow / bad args.
66 */
67size_t pc_exc_json(const ExcInfo *info, char *out, size_t cap);
68
69#if defined(ARDUINO)
70// --- Core-dump partition (ESP32) ---------------------------------------------------------
71//
72// A panic that reboots the device takes its console output with it. ESP-IDF also writes a core
73// dump to a flash partition, which survives the reboot - so the next boot can report what crashed
74// and hand the raw image off somewhere durable before it is overwritten by the next crash.
75//
76// Requires a `coredump` partition in the partition table (the default Arduino tables have one).
77
78namespace fs
79{
80class FS;
81}
82
83/** @brief Where the stored core dump lives, and how big it is. */
84struct ExcCoreDump
85{
86 uint32_t addr; ///< absolute flash address of the image
87 size_t size; ///< image size in bytes
88};
89
90/**
91 * @brief Is a core dump stored, and is its checksum intact?
92 * @param out optional; filled with the image address/size when one is present.
93 * @return true if a valid dump is waiting to be read.
94 */
95bool pc_exc_coredump_present(ExcCoreDump *out);
96
97/**
98 * @brief Fill an ExcInfo from the stored dump's summary, so the existing `/exception` panel can
99 * render a crash recovered after reboot rather than only a live console capture.
100 *
101 * Architecture differs, and the result says so honestly:
102 * - **Xtensa** (ESP32 / S2 / S3): a real backtrace is recorded on-device, so `frames` is populated
103 * (pc only; sp is not part of the summary) along with the exception cause and faulting address.
104 * - **RISC-V** (C3 / C6 / H2 / P4): the summary carries a stack dump, not a backtrace - resolving
105 * it needs DWARF off-device. `frame_count` is therefore 0 and only the faulting PC and the
106 * machine trap cause / value are filled. Offload the image (pc_exc_coredump_save) and resolve it
107 * with `esp-coredump` or GDB against the firmware ELF.
108 *
109 * @return true if a summary was read.
110 */
111bool pc_exc_coredump_summary(ExcInfo *out);
112
113/**
114 * @brief Read @p len raw bytes of the stored image starting at @p offset within it.
115 *
116 * The transport-agnostic seam: the image is pulled in whatever chunks the consumer wants, so a
117 * dump can go to a filesystem, up an FTP control/data pair, into an HTTP POST body, or anywhere
118 * else without this owner knowing about any of them. @p offset is relative to the start of the
119 * image (byte 0 = the size word), not to the flash partition.
120 *
121 * @return true if the whole range was read; false if no valid dump is stored or the range runs
122 * past its end (a short read is never reported as success).
123 */
124bool pc_exc_coredump_read(size_t offset, void *buf, size_t len);
125
126/**
127 * @brief Copy the raw core-dump image out of flash into a file (SD, LittleFS, ...).
128 *
129 * Streams in PC_EXC_COREDUMP_CHUNK-sized pieces, so it costs no heap and a large dump does not
130 * need to fit RAM.
131 *
132 * The written file is the **raw core-dump image** in ESP-IDF's flash format, not a bare ELF: a
133 * 24-byte header (the first word is the total image size) followed by an `ET_CORE` ELF at offset 24
134 * and a trailing checksum. Feed it to the host tool as raw - `esp-coredump info_corefile -c
135 * <file> -t raw <firmware.elf>` - or skip the first 24 bytes to get a plain ELF. It is stored
136 * verbatim so nothing is lost in translation.
137 *
138 * @return true if the whole image was written.
139 */
140bool pc_exc_coredump_save(fs::FS &file_sys, const char *path);
141
142/**
143 * @brief Erase the stored dump so the next boot does not re-offload the same crash.
144 * Call only after a successful save.
145 */
146bool pc_exc_coredump_erase(void);
147#endif // ARDUINO
148
149#endif // PC_ENABLE_EXC_DECODER
150#endif // PROTOCORE_EXC_DECODER_H
User-facing configuration for ProtoCore.