Add generic mg_health report for mdash

This commit is contained in:
robert committed 2026-09-23 12:07:11 +03:00
1 parent 60c75a22b8
commit 4e7c664455
16 files changed
+1225 -230

No files matched your search

+117 -72
View File
@@ -4945,44 +4945,25 @@ void mg_ota_device_id(char *buf, size_t len);
void mg_ota_poll(struct mg_mgr *);
// 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.
// A health provider owns the report format and returns it as an opaque blob.
// Applications and transports use mg_health_get_blob() without knowing how the
// report was collected or encoded.
#define MG_HEALTH_NONE 0
#define MG_HEALTH_CORTEX 1
#ifndef MG_HEALTH
#define MG_HEALTH MG_HEALTH_NONE
#endif
#ifndef MG_HEALTH_MAGIC
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
#endif
// Places the record in RAM that survives a reset. Define in mongoose_config.h
// to match the linker script, e.g. __attribute__((section(".mg_health")))
#ifndef MG_HEALTH_RAM
#define MG_HEALTH_RAM
#endif
// Why the device restarted. Hardware usually reports several flags at once,
// so these are ordered by how much they tell you: the most specific cause a
// device reports wins
@@ -4997,15 +4978,16 @@ enum mg_health_reason {
MG_HEALTH_RESET_FAULT // Fault handler ran, see regs[] and saved_stack
};
#define MG_HEALTH_BACKTRACE 20 // Return addresses in mg_health::backtrace
typedef struct mg_str mg_health_blob;
struct mg_health {
char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data
uint32_t reset_reason; // enum mg_health_reason that started this boot
uint32_t backtrace[MG_HEALTH_BACKTRACE]; // Crash backtrace, frame 0 first
char magic[4];
mg_health_blob (*get_blob)(void *);
void *fn_data;
uint32_t reset_reason;
};
extern struct mg_health mg_health_record; // Defined in health.c
extern struct mg_health mg_health_record;
// Return true if the record holds valid data, i.e. it survived the reset and
// was written by this firmware layout
@@ -5015,6 +4997,14 @@ static inline bool mg_health_valid(void) {
sizeof(mg_health_record.magic)) == 0;
}
// Select and initialise the configured health provider. Call after
// initializing the board-specific persistent-storage
void mg_health_init(void);
// Return the provider's opaque report. The returned memory is provider-owned
// and remains valid until the next call to this function
struct mg_str mg_health_get_blob(void);
static inline int mg_health_reason(void) {
return mg_health_valid() ? (int) mg_health_record.reset_reason
: MG_HEALTH_RESET_UNKNOWN;
@@ -5022,48 +5012,103 @@ static inline int mg_health_reason(void) {
static inline const char *mg_health_reason_str(int reason) {
switch (reason) {
case MG_HEALTH_RESET_POWER: return "power";
case MG_HEALTH_RESET_BROWNOUT: return "brownout";
case MG_HEALTH_RESET_PIN: return "pin";
case MG_HEALTH_RESET_SOFTWARE: return "software";
case MG_HEALTH_RESET_LOWPOWER: return "lowpower";
case MG_HEALTH_RESET_WATCHDOG: return "watchdog";
case MG_HEALTH_RESET_FAULT: return "fault";
default: return "unknown";
case MG_HEALTH_RESET_POWER:
return "power";
case MG_HEALTH_RESET_BROWNOUT:
return "brownout";
case MG_HEALTH_RESET_PIN:
return "pin";
case MG_HEALTH_RESET_SOFTWARE:
return "software";
case MG_HEALTH_RESET_LOWPOWER:
return "lowpower";
case MG_HEALTH_RESET_WATCHDOG:
return "watchdog";
case MG_HEALTH_RESET_FAULT:
return "fault";
default:
return "unknown";
}
}
// Start a new boot: validate the record, count the boot, store the reset
// reason. The current boot moves to prev_* slots first, so the previous
// boot's uptime and reason stay readable. Called by MG_HEALTH_INIT()
static inline void mg_health_init(void) {
char magic[] = MG_HEALTH_MAGIC;
// struct mg_health *h = mg_health_get();
if (!mg_health_valid()) { // First boot, or RAM lost its contents
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()
#define MG_HEALTH_CORTEX_VERSION 1U
#ifndef MG_HEALTH_CORTEX_STORAGE
#define MG_HEALTH_CORTEX_STORAGE NULL
#endif
#ifndef MG_HEALTH_CORTEX_STORAGE_SIZE
#define MG_HEALTH_CORTEX_STORAGE_SIZE 0
#endif
#ifndef MG_HEALTH_CORTEX_STACK_WORDS
#define MG_HEALTH_CORTEX_STACK_WORDS 980U
#endif
#ifndef MG_HEALTH_CORTEX_REPORT_SIZE
#define MG_HEALTH_CORTEX_REPORT_SIZE 16384U
#endif
// Storage operations are deliberately format-agnostic. H723 uses the default
// direct-memory implementation; boards with different retained storage can
// replace these callbacks before calling MG_HEALTH_INIT()
struct mg_health_cortex {
void *storage;
size_t storage_size;
bool (*init)(void *, size_t);
bool (*read)(const void *, size_t, void *, size_t);
bool (*write)(void *, size_t, const void *, size_t);
};
struct mg_health_cortex_record {
char magic[4];
uint32_t version;
uint32_t cpuid;
uint32_t exception_sp;
uint32_t sp;
uint32_t exc_return;
int32_t exception_valid;
int32_t stack_valid;
uint32_t r0;
uint32_t r1;
uint32_t r2;
uint32_t r3;
uint32_t r4;
uint32_t r5;
uint32_t r6;
uint32_t r7;
uint32_t r8;
uint32_t r9;
uint32_t r10;
uint32_t r11;
uint32_t r12;
uint32_t lr;
uint32_t pc;
uint32_t psr;
uint32_t cfsr;
uint32_t hfsr;
uint32_t dfsr;
uint32_t afsr;
uint32_t bfar;
uint32_t mmfar;
uint32_t abfsr;
uint32_t stack_word_count;
uint32_t stack_words[MG_HEALTH_CORTEX_STACK_WORDS];
};
extern struct mg_health_cortex mg_health_cortex;
void mg_health_cortex_init(struct mg_health_cortex *);
struct mg_str mg_health_cortex_get_blob(void *);
// Cloud connector API to https://mdash.net - device management cloud