mirror of
https://github.com/cesanta/mongoose.git
synced 2026-11-06 15:51:25 +00:00
157 lines
5.2 KiB
C
157 lines
5.2 KiB
C
// Device health monitoring
|
|
|
|
#pragma once
|
|
|
|
#include "arch.h"
|
|
#include "printf.h"
|
|
|
|
#if MG_ENABLE_HEALTH
|
|
|
|
#ifndef MG_HEALTH_MAGIC
|
|
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
|
#endif
|
|
|
|
#if !defined(MG_HEALTH_RAM) && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
|
|
#define MG_HEALTH_RAM __attribute__((section(".mg_health")))
|
|
#endif
|
|
|
|
#ifndef MG_HEALTH_DATA_SIZE
|
|
#if (defined(STM32H7_DEV_ID) || defined(STM32H5)) || defined(STM32C5)
|
|
#define MG_HEALTH_DATA_SIZE 250
|
|
#elif MG_ARCH == MG_ARCH_ESP32
|
|
#define MG_HEALTH_DATA_SIZE 23
|
|
#else
|
|
#define MG_HEALTH_DATA_SIZE 10
|
|
#endif
|
|
#endif // MG_HEALTH_DATA_SIZE
|
|
|
|
// 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
|
|
|
|
// CPU core architecture
|
|
enum mg_health_arch {
|
|
MG_HEALTH_ARCH_ARM = 0,
|
|
MG_HEALTH_ARCH_XTENSA,
|
|
MG_HEALTH_ARCH_RISCV
|
|
};
|
|
|
|
// 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
|
|
enum mg_health_reason {
|
|
MG_HEALTH_RESET_UNKNOWN = 0, // Nothing reported, or no masks configured
|
|
MG_HEALTH_RESET_POWER, // Power-on reset, RAM contents are lost
|
|
MG_HEALTH_RESET_BROWNOUT, // Supply dipped below the brown-out level
|
|
MG_HEALTH_RESET_PIN, // External reset pin, e.g. the reset button
|
|
MG_HEALTH_RESET_SOFTWARE, // Firmware asked for it, e.g. NVIC_SystemReset
|
|
MG_HEALTH_RESET_LOWPOWER, // Illegal or failed low-power transition
|
|
MG_HEALTH_RESET_WATCHDOG, // Watchdog fired: the application stopped
|
|
MG_HEALTH_RESET_FAULT // Fault handler ran, see regs[] and saved_stack
|
|
};
|
|
|
|
struct mg_health {
|
|
char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data
|
|
uint16_t reset_reason; // enum mg_health_reason that started this boot
|
|
uint16_t arch; // enum mg_health_arch, core architecture (ARM, etc)
|
|
uint32_t cpuid; // ARM CPU ID
|
|
uint32_t data[MG_HEALTH_DATA_SIZE];
|
|
};
|
|
|
|
extern struct mg_health mg_health_record; // Defined in health.c
|
|
|
|
// Return true if the record holds valid data, i.e. it survived the reset and
|
|
// was written by this firmware layout
|
|
static inline bool mg_health_valid(void) {
|
|
static const char magic[] = MG_HEALTH_MAGIC;
|
|
return memcmp(mg_health_record.magic, magic,
|
|
sizeof(mg_health_record.magic)) == 0;
|
|
}
|
|
|
|
static inline int mg_health_reason(void) {
|
|
return mg_health_valid() ? (int) mg_health_record.reset_reason
|
|
: MG_HEALTH_RESET_UNKNOWN;
|
|
}
|
|
|
|
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";
|
|
}
|
|
}
|
|
|
|
#if MG_ARCH == MG_ARCH_ESP32
|
|
#ifndef CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
|
#define CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH 0
|
|
#endif
|
|
#ifndef ESP_PARTITION_SUBTYPE_DATA_COREDUMP
|
|
#define ESP_PARTITION_SUBTYPE_DATA_COREDUMP 0x03
|
|
#endif
|
|
// clang-format off
|
|
static inline uint16_t esp2mg(esp_reset_reason_t rr) {
|
|
switch(rr) {
|
|
case ESP_RST_POWERON: return (uint16_t) MG_HEALTH_RESET_POWER;
|
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 2)
|
|
case ESP_RST_PWR_GLITCH:
|
|
#endif
|
|
case ESP_RST_BROWNOUT: return (uint16_t) MG_HEALTH_RESET_BROWNOUT;
|
|
case ESP_RST_EXT: return (uint16_t) MG_HEALTH_RESET_PIN;
|
|
case ESP_RST_SW: return (uint16_t) MG_HEALTH_RESET_SOFTWARE;
|
|
case ESP_RST_DEEPSLEEP: return (uint16_t) MG_HEALTH_RESET_LOWPOWER;
|
|
case ESP_RST_INT_WDT:
|
|
case ESP_RST_TASK_WDT:
|
|
case ESP_RST_WDT: return (uint16_t) MG_HEALTH_RESET_WATCHDOG;
|
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 2)
|
|
case ESP_RST_CPU_LOCKUP:
|
|
#endif
|
|
case ESP_RST_PANIC: return (uint16_t) MG_HEALTH_RESET_FAULT;
|
|
default: break;
|
|
}
|
|
return (uint16_t) MG_HEALTH_RESET_UNKNOWN;
|
|
}
|
|
// clang-format on
|
|
#endif
|
|
|
|
#define MG_HEALTH_INIT() mg_health_init()
|
|
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
|
|
extern void mg_health_init(void);
|
|
extern bool mg_health_coredump_begin(size_t *size);
|
|
extern bool mg_health_coredump_read(size_t offset, void *buf, size_t *len);
|
|
extern bool mg_health_coredump_end(void);
|
|
#else
|
|
#define MG_HEALTH_INIT()
|
|
static inline int mg_health_reason(void) {
|
|
return 0;
|
|
}
|
|
static inline const char *mg_health_reason_str(int reason) {
|
|
(void) reason;
|
|
return "unknown";
|
|
}
|
|
static inline size_t mg_print_crash_record(mg_pfn_t fn, void *arg,
|
|
va_list *ap) {
|
|
(void) ap;
|
|
return mg_xprintf(fn, arg, "{}");
|
|
}
|
|
static inline bool mg_health_coredump_begin(size_t *size) {
|
|
(void) size;
|
|
return false;
|
|
}
|
|
static inline bool mg_health_coredump_read(size_t offset, void *buf,
|
|
size_t *len) {
|
|
(void) offset, (void) buf, (void) len;
|
|
return false;
|
|
}
|
|
static inline bool mg_health_coredump_end(void) {
|
|
return false;
|
|
}
|
|
|
|
#endif
|