Files

104 lines
3.4 KiB
C++
Raw Permalink Normal View History

2026-02-13 12:16:39 +01:00
#include "Logging.h"
2026-03-06 17:46:13 +01:00
#include <string>
#define MAX_ENTRY_LEN 256
#define MAX_LOG_LINES 16
// Simple ring buffer log, useful for error reporting when we encounter a crash
RTC_NOINIT_ATTR char logMessages[MAX_LOG_LINES][MAX_ENTRY_LEN];
RTC_NOINIT_ATTR size_t logHead = 0;
2026-03-09 21:53:38 +01:00
// Magic word written alongside logHead to detect uninitialized RTC memory.
// RTC_NOINIT_ATTR is not zeroed on cold boot, so logHead may appear in-range
// (0..MAX_LOG_LINES-1) by chance even though logMessages is garbage. The magic
// value is only set by clearLastLogs(), so its absence means the buffer was
// never properly initialized.
RTC_NOINIT_ATTR uint32_t rtcLogMagic;
static constexpr uint32_t LOG_RTC_MAGIC = 0xDEADBEEF;
2026-03-06 17:46:13 +01:00
void addToLogRingBuffer(const char* message) {
2026-03-09 21:53:38 +01:00
// Add the message to the ring buffer, overwriting old messages if necessary.
// If the magic is wrong or logHead is out of range (RTC_NOINIT_ATTR garbage
// on cold boot), clear the entire buffer so subsequent reads are safe.
if (rtcLogMagic != LOG_RTC_MAGIC || logHead >= MAX_LOG_LINES) {
memset(logMessages, 0, sizeof(logMessages));
logHead = 0;
rtcLogMagic = LOG_RTC_MAGIC;
}
2026-03-06 17:46:13 +01:00
strncpy(logMessages[logHead], message, MAX_ENTRY_LEN - 1);
logMessages[logHead][MAX_ENTRY_LEN - 1] = '\0';
logHead = (logHead + 1) % MAX_LOG_LINES;
}
2026-02-13 12:16:39 +01:00
// Since logging can take a large amount of flash, we want to make the format string as short as possible.
// This logPrintf prepend the timestamp, level and origin to the user-provided message, so that the user only needs to
// provide the format string for the message itself.
void logPrintf(const char* level, const char* origin, const char* format, ...) {
va_list args;
va_start(args, format);
2026-03-06 17:46:13 +01:00
char buf[MAX_ENTRY_LEN];
2026-02-13 12:16:39 +01:00
char* c = buf;
// add timestamp, level and origin
2026-02-13 12:16:39 +01:00
{
unsigned long ms = millis();
int len = snprintf(c, sizeof(buf), "[%lu] [%s] [%s] ", ms, level, origin);
2026-04-20 14:12:40 +08:00
// error while writing => return
2026-02-13 12:16:39 +01:00
if (len < 0) {
va_end(args);
return;
2026-02-13 12:16:39 +01:00
}
// clamp c to be in buffer range
c += std::min(len, MAX_ENTRY_LEN);
2026-02-13 12:16:39 +01:00
}
// add the user message
{
int len = vsnprintf(c, sizeof(buf) - (c - buf), format, args);
if (len < 0) {
va_end(args);
return;
}
}
2026-02-13 12:16:39 +01:00
va_end(args);
2026-03-06 17:46:13 +01:00
if (logSerial) {
logSerial.print(buf);
}
addToLogRingBuffer(buf);
}
std::string getLastLogs() {
2026-03-09 21:53:38 +01:00
if (rtcLogMagic != LOG_RTC_MAGIC) {
return {};
}
2026-03-06 17:46:13 +01:00
std::string output;
for (size_t i = 0; i < MAX_LOG_LINES; i++) {
size_t idx = (logHead + i) % MAX_LOG_LINES;
if (logMessages[idx][0] != '\0') {
2026-03-09 21:53:38 +01:00
const size_t len = strnlen(logMessages[idx], MAX_ENTRY_LEN);
output.append(logMessages[idx], len);
2026-03-06 17:46:13 +01:00
}
}
return output;
}
2026-03-09 21:53:38 +01:00
// Checks whether the RTC log state is consistent: rtcLogMagic must equal
// LOG_RTC_MAGIC and logHead must be in 0..MAX_LOG_LINES-1. Returns true if
// corruption is detected, in which case rtcLogMagic is still invalid and
// logMessages may contain garbage. Callers (e.g. HalSystem::begin on the
// panic-reboot path) must call clearLastLogs() after a true result to fully
// reinitialize the ring buffer and stamp the magic before getLastLogs() is used.
bool sanitizeLogHead() {
if (rtcLogMagic != LOG_RTC_MAGIC || logHead >= MAX_LOG_LINES) {
logHead = 0;
return true;
}
return false;
}
2026-03-06 17:46:13 +01:00
void clearLastLogs() {
for (size_t i = 0; i < MAX_LOG_LINES; i++) {
logMessages[i][0] = '\0';
}
logHead = 0;
2026-03-09 21:53:38 +01:00
rtcLogMagic = LOG_RTC_MAGIC;
2026-02-13 12:16:39 +01:00
}