A fork of https://github.com/crosspoint-reader/crosspoint-reader
1#include "Logging.h"
2
3#include <string>
4
5#define MAX_ENTRY_LEN 256
6#define MAX_LOG_LINES 16
7
8// Simple ring buffer log, useful for error reporting when we encounter a crash
9RTC_NOINIT_ATTR char logMessages[MAX_LOG_LINES][MAX_ENTRY_LEN];
10RTC_NOINIT_ATTR size_t logHead = 0;
11// Magic word written alongside logHead to detect uninitialized RTC memory.
12// RTC_NOINIT_ATTR is not zeroed on cold boot, so logHead may appear in-range
13// (0..MAX_LOG_LINES-1) by chance even though logMessages is garbage. The magic
14// value is only set by clearLastLogs(), so its absence means the buffer was
15// never properly initialized.
16RTC_NOINIT_ATTR uint32_t rtcLogMagic;
17static constexpr uint32_t LOG_RTC_MAGIC = 0xDEADBEEF;
18
19void addToLogRingBuffer(const char* message) {
20 // Add the message to the ring buffer, overwriting old messages if necessary.
21 // If the magic is wrong or logHead is out of range (RTC_NOINIT_ATTR garbage
22 // on cold boot), clear the entire buffer so subsequent reads are safe.
23 if (rtcLogMagic != LOG_RTC_MAGIC || logHead >= MAX_LOG_LINES) {
24 memset(logMessages, 0, sizeof(logMessages));
25 logHead = 0;
26 rtcLogMagic = LOG_RTC_MAGIC;
27 }
28 strncpy(logMessages[logHead], message, MAX_ENTRY_LEN - 1);
29 logMessages[logHead][MAX_ENTRY_LEN - 1] = '\0';
30 logHead = (logHead + 1) % MAX_LOG_LINES;
31}
32
33// Since logging can take a large amount of flash, we want to make the format string as short as possible.
34// This logPrintf prepend the timestamp, level and origin to the user-provided message, so that the user only needs to
35// provide the format string for the message itself.
36void logPrintf(const char* level, const char* origin, const char* format, ...) {
37 va_list args;
38 va_start(args, format);
39 char buf[MAX_ENTRY_LEN];
40 char* c = buf;
41 // add timestamp, level and origin
42 {
43 unsigned long ms = millis();
44 int len = snprintf(c, sizeof(buf), "[%lu] [%s] [%s] ", ms, level, origin);
45 // error while writing => return
46 if (len < 0) {
47 va_end(args);
48 return;
49 }
50 // clamp c to be in buffer range
51 c += std::min(len, MAX_ENTRY_LEN);
52 }
53 // add the user message
54 {
55 int len = vsnprintf(c, sizeof(buf) - (c - buf), format, args);
56 if (len < 0) {
57 va_end(args);
58 return;
59 }
60 }
61 va_end(args);
62 if (logSerial) {
63 logSerial.print(buf);
64 }
65 addToLogRingBuffer(buf);
66}
67
68std::string getLastLogs() {
69 if (rtcLogMagic != LOG_RTC_MAGIC) {
70 return {};
71 }
72 std::string output;
73 for (size_t i = 0; i < MAX_LOG_LINES; i++) {
74 size_t idx = (logHead + i) % MAX_LOG_LINES;
75 if (logMessages[idx][0] != '\0') {
76 const size_t len = strnlen(logMessages[idx], MAX_ENTRY_LEN);
77 output.append(logMessages[idx], len);
78 }
79 }
80 return output;
81}
82
83// Checks whether the RTC log state is consistent: rtcLogMagic must equal
84// LOG_RTC_MAGIC and logHead must be in 0..MAX_LOG_LINES-1. Returns true if
85// corruption is detected, in which case rtcLogMagic is still invalid and
86// logMessages may contain garbage. Callers (e.g. HalSystem::begin on the
87// panic-reboot path) must call clearLastLogs() after a true result to fully
88// reinitialize the ring buffer and stamp the magic before getLastLogs() is used.
89bool sanitizeLogHead() {
90 if (rtcLogMagic != LOG_RTC_MAGIC || logHead >= MAX_LOG_LINES) {
91 logHead = 0;
92 return true;
93 }
94 return false;
95}
96
97void clearLastLogs() {
98 for (size_t i = 0; i < MAX_LOG_LINES; i++) {
99 logMessages[i][0] = '\0';
100 }
101 logHead = 0;
102 rtcLogMagic = LOG_RTC_MAGIC;
103}