mirror of
https://github.com/cesanta/mongoose.git
synced 2026-11-07 16:01:30 +00:00
Move crash handler to src/health.c
This commit is contained in:
1 parent
ce35cbf586
commit
d7e708b9bc
14 files changed
+179
-267
No files matched your search
@@ -226,3 +226,7 @@
|
||||
#ifndef MG_ENABLE_CHACHA20
|
||||
#define MG_ENABLE_CHACHA20 1 // When set to 0, GCM is used. For MG_TLS_BUILTIN
|
||||
#endif
|
||||
|
||||
#ifndef MG_ENABLE_HEALTH
|
||||
#define MG_ENABLE_HEALTH 0
|
||||
#endif
|
||||
@@ -1,5 +1,73 @@
|
||||
#include "config.h"
|
||||
#include "health.h"
|
||||
|
||||
// The one health record. Lives in RAM that survives a warm reset, see
|
||||
// MG_HEALTH_RAM and the .mg_health region in the linker script
|
||||
struct mg_health mg_health_record MG_HEALTH_RAM;
|
||||
|
||||
// STM32 H723 support. Needs the _estack symbol from the linker script
|
||||
#if MG_ENABLE_HEALTH && defined(STM32H723xx_H)
|
||||
// Fault handler body. Runs in exception context: no printf, no malloc, no
|
||||
// blocking calls. Copies the unwind registers and raw stack into the health
|
||||
// record, then resets.
|
||||
// "used" keeps the linker from garbage-collecting this section: the only
|
||||
// reference is the "b fault_c" branch in the naked handler below
|
||||
__attribute__((used, noinline)) static void fault_c(uint32_t *frame,
|
||||
uint32_t exc_return) {
|
||||
extern uint32_t _estack;
|
||||
uint32_t *stack = frame + 8 + ((exc_return & (1U << 4)) ? 0 : 18);
|
||||
uint32_t *r = mg_health_record.data;
|
||||
|
||||
if (frame[7] & (1U << 9)) stack++; // Eight-byte stack alignment padding
|
||||
r[0] = (uint32_t) (uintptr_t) stack; // SP
|
||||
r[1] = frame[5]; // LR
|
||||
r[2] = frame[6]; // PC
|
||||
for (size_t i = 0; i < MG_HEALTH_DATA_SIZE - 3; i++) { // Raw stack
|
||||
r[3 + i] = stack + i < &_estack ? stack[i] : 0;
|
||||
}
|
||||
mg_health_record.cpuid = SCB->CPUID;
|
||||
mg_health_record.reset_reason = MG_HEALTH_RESET_FAULT;
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
// Common fault entry. EXC_RETURN bit 2 tells which stack was in use:
|
||||
// 0 = MSP, 1 = PSP. Load the faulting SP into r0 and hand it to fault_c()
|
||||
__attribute__((naked)) void HardFault_Handler(void) {
|
||||
__asm volatile(
|
||||
"mov r1, lr\n\t"
|
||||
"tst lr, #4\n\t"
|
||||
"ite eq\n\t"
|
||||
"mrseq r0, msp\n\t"
|
||||
"mrsne r0, psp\n\t"
|
||||
"b fault_c\n\t");
|
||||
}
|
||||
|
||||
// Route the other fault types through the same entry point
|
||||
void MemManage_Handler(void) __attribute__((alias("HardFault_Handler")));
|
||||
void BusFault_Handler(void) __attribute__((alias("HardFault_Handler")));
|
||||
void UsageFault_Handler(void) __attribute__((alias("HardFault_Handler")));
|
||||
#endif
|
||||
|
||||
static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||
size_t n = va_arg(*ap, size_t);
|
||||
uint32_t *p = va_arg(*ap, uint32_t *);
|
||||
size_t len = 0;
|
||||
while (n--) len += mg_xprintf(fn, arg, "%s%lu", len == 0 ? "" : ",", *p++);
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||
size_t len = 0;
|
||||
if (mg_health_valid() &&
|
||||
mg_health_record.reset_reason == MG_HEALTH_RESET_FAULT) {
|
||||
size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]);
|
||||
len += mg_xprintf(fn, arg, "{%m:%hhu,%m:%u,%m:[%M]}", //
|
||||
MG_ESC("version"), mg_health_record.magic[3], //
|
||||
MG_ESC("cpuid"), mg_health_record.cpuid, //
|
||||
MG_ESC("data"), print_uint32, n, mg_health_record.data);
|
||||
} else {
|
||||
len += mg_xprintf(fn, arg, "{}");
|
||||
}
|
||||
(void) ap;
|
||||
return len;
|
||||
}
|
||||
+2
-42
@@ -1,32 +1,9 @@
|
||||
// Device health monitoring
|
||||
//
|
||||
// Keeps a small record in a RAM region that survives a warm reset, so a device
|
||||
// can tell why it restarted. The record holds a boot counter, the uptime this
|
||||
// boot has reached, and the reset reason reported by the hardware. A crash
|
||||
// loop, a hang loop and a marginal power supply each leave a different mark.
|
||||
//
|
||||
// Put MG_HEALTH_INIT() in main(), right after clock and memory init:
|
||||
//
|
||||
// ```c
|
||||
// int main(void) {
|
||||
// hal_clock_init();
|
||||
// MG_HEALTH_INIT();
|
||||
// ...
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// The record must live in RAM that startup code neither copies nor zeroes.
|
||||
// See tutorials/stm32/nucleo-h723zg/minimal/link.ld for the .mg_health region,
|
||||
// and tutorials/stm32/nucleo-h723zg/minimal/mongoose_config.h for the
|
||||
// reset-reason hooks.
|
||||
//
|
||||
// On Cortex-M, a fault handler can record the crash: it sets reset_reason to
|
||||
// MG_HEALTH_RESET_FAULT and walks the stack into backtrace[]. See
|
||||
// tutorials/stm32/nucleo-h723zg/minimal/main.c for a sample HardFault_Handler.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "arch.h"
|
||||
#include "printf.h"
|
||||
|
||||
#ifndef MG_HEALTH_MAGIC
|
||||
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
||||
@@ -101,24 +78,7 @@ static inline void mg_health_init(void) {
|
||||
memset(&mg_health_record, 0, sizeof(mg_health_record));
|
||||
memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic));
|
||||
}
|
||||
// h->prev_uptime = h->uptime;
|
||||
// h->prev_reason = h->reason;
|
||||
// h->counter++;
|
||||
// h->uptime = 0;
|
||||
// h->reason = mg_health_decode_reason();
|
||||
// MG_HEALTH_RESET_CLEAR();
|
||||
}
|
||||
|
||||
// // Record that the main loop is still alive. Called from mg_mgr_poll(), so a
|
||||
// // hung application leaves uptime frozen at the hang
|
||||
// extern uint64_t mg_health_next_ms; // Defined in health.c
|
||||
|
||||
// static inline void mg_health_uptime(uint64_t now_ms) {
|
||||
// struct mg_health *h = mg_health_get();
|
||||
// if (now_ms >= mg_health_next_ms && mg_health_valid()) {
|
||||
// mg_health_next_ms = now_ms + 1000;
|
||||
// h->uptime++;
|
||||
// }
|
||||
// }
|
||||
|
||||
#define MG_HEALTH_INIT() mg_health_init()
|
||||
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
|
||||
+5
-26
@@ -95,35 +95,14 @@ static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
(void) c;
|
||||
}
|
||||
|
||||
static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||
size_t n = va_arg(*ap, size_t);
|
||||
uint32_t *p = va_arg(*ap, uint32_t *);
|
||||
size_t len = 0;
|
||||
while (n--) len += mg_xprintf(fn, arg, "%s%lu", len == 0 ? "" : ",", *p++);
|
||||
return len;
|
||||
}
|
||||
|
||||
static size_t print_crash(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||
size_t len = 0;
|
||||
if (mg_health_valid()) {
|
||||
size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]);
|
||||
len += mg_xprintf(fn, arg, ",%m:{%m:%hhu,%m:%u,%m:[%M]}", MG_ESC("crash"),
|
||||
MG_ESC("version"), mg_health_record.magic[3], //
|
||||
MG_ESC("cpuid"), mg_health_record.cpuid, //
|
||||
MG_ESC("data"), print_uint32, n, mg_health_record.data);
|
||||
}
|
||||
(void) ap;
|
||||
return len;
|
||||
}
|
||||
|
||||
static void mg_mdash_rpc_get_info(struct mg_rpc_req *r) {
|
||||
const char *reset_reason = mg_health_reason_str(mg_health_reason());
|
||||
mg_rpc_ok(r, "{%m:%m,%m:%llu,%m:%m,%m:\"mws.%d\"%M}", //
|
||||
mg_rpc_ok(r, "{%m:%m,%m:%llu,%m:%m,%m:\"mws.%d\",%m:%M}", //
|
||||
MG_ESC("fw_version"), MG_ESC(MG_FIRMWARE_VERSION), //
|
||||
MG_ESC("uptime"), (uint64_t) (mg_millis() / 1000), //
|
||||
MG_ESC("reboot_reason"), MG_ESC(reset_reason), //
|
||||
MG_ESC("arch"), MG_ARCH, //
|
||||
print_crash);
|
||||
MG_ESC("uptime"), (uint64_t) (mg_millis() / 1000), //
|
||||
MG_ESC("reboot_reason"), MG_ESC(reset_reason), //
|
||||
MG_ESC("arch"), MG_ARCH, //
|
||||
MG_ESC("crash"), mg_print_crash_record);
|
||||
}
|
||||
|
||||
static void mg_mdash_rpc_ota_begin(struct mg_rpc_req *r) {
|
||||
|
||||
Reference in new issue
Block a user