ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
logbuf.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file logbuf.cpp
6 * @brief Fixed-RAM rotating log ring + severity trap - implementation (pure).
7 */
8
10#include "shared_primitives/strbuf.h" // pc_sb frame builder
11
12#if PC_ENABLE_LOGBUF
13
14#include <stdio.h>
15#include <string.h>
16
17namespace
18{
19// All log-ring state, owned by one instance (internal linkage): the line/severity ring, its
20// head/count cursors, and the severity trap, grouped so it is one named owner, unreachable
21// from any other translation unit.
22struct LogbufCtx
23{
24 char lines[PC_LOG_LINES][PC_LOG_LINE_LEN]; // ring storage (BSS)
25 uint8_t level[PC_LOG_LINES]; // per-line severity
26 uint16_t head = 0; // index of the oldest line
27 uint16_t count = 0; // lines currently held
28 uint8_t trap_threshold = 0xFF; // 0xFF = trap disabled
29 pc_log_trap_fn trap = nullptr;
30};
31LogbufCtx s_log;
32
33char level_letter(uint8_t level)
34{
35 switch (level)
36 {
37 case pc_log_level::PC_LOG_ERROR:
38 return 'E';
39 case pc_log_level::PC_LOG_WARN:
40 return 'W';
41 case pc_log_level::PC_LOG_INFO:
42 return 'I';
43 default:
44 return 'D';
45 }
46}
47} // namespace
48
49void pc_logbuf_reset(void)
50{
51 s_log.head = 0;
52 s_log.count = 0;
53}
54
55void pc_log(uint8_t level, const char *msg)
56{
57 uint16_t slot;
58 if (s_log.count < PC_LOG_LINES)
59 {
60 slot = (uint16_t)((s_log.head + s_log.count) % PC_LOG_LINES);
61 s_log.count++;
62 }
63 else // full: overwrite the oldest and advance head
64 {
65 slot = s_log.head;
66 s_log.head = (uint16_t)((s_log.head + 1) % PC_LOG_LINES);
67 }
68 pc_sb sb67 = {s_log.lines[slot], PC_LOG_LINE_LEN, 0, true};
69 pc_sb_ch(&sb67, (char)(level_letter(level)));
70 pc_sb_put(&sb67, " ");
71 pc_sb_put(&sb67, msg ? msg : "");
72 if (pc_sb_finish(&sb67) == 0)
73 {
74 sb67.p[0] = '\0';
75 }
76 s_log.level[slot] = level;
77
78 if (s_log.trap && level >= s_log.trap_threshold)
79 {
80 s_log.trap(level, s_log.lines[slot]);
81 }
82}
83
84uint16_t pc_log_count(void)
85{
86 return s_log.count;
87}
88
89const char *pc_log_at(uint16_t i)
90{
91 if (i >= s_log.count)
92 {
93 return nullptr;
94 }
95 return s_log.lines[(s_log.head + i) % PC_LOG_LINES];
96}
97
98int pc_log_dump(char *out, size_t cap)
99{
100 if (!out || cap == 0)
101 {
102 return 0;
103 }
104 out[0] = '\0';
105 size_t pos = 0;
106 for (uint16_t i = 0; i < s_log.count; i++)
107 {
108 const char *line = s_log.lines[(s_log.head + i) % PC_LOG_LINES];
109 size_t n = strnlen(line, cap);
110 size_t need = n + (i + 1 < s_log.count ? 1 : 0); // +1 for the '\n' separator
111 if (pos + need >= cap) // keep room for the null terminator
112 {
113 out[0] = '\0';
114 return 0;
115 }
116 memcpy(out + pos, line, n);
117 pos += n;
118 if (i + 1 < s_log.count)
119 {
120 out[pos++] = '\n';
121 }
122 }
123 out[pos] = '\0';
124 return (int)pos;
125}
126
127void pc_log_set_trap(uint8_t threshold, pc_log_trap_fn cb)
128{
129 s_log.trap_threshold = threshold;
130 s_log.trap = cb;
131}
132
133#endif // PC_ENABLE_LOGBUF
Fixed-RAM rotating log buffer with severity traps (PC_ENABLE_LOGBUF).
#define PC_LOG_LINE_LEN
Maximum length of one stored log line (bytes, including null).
#define PC_LOG_LINES
Number of log lines retained in the ring.
Bounded no-heap string builder that fails closed on overflow (one shared copy).
size_t pc_sb_finish(pc_sb *b)
NUL-terminate and return the built length, or 0 if the build overflowed.
Definition strbuf.h:648
void pc_sb_ch(pc_sb *b, char c)
Append a single character.
Definition strbuf.h:186
void pc_sb_put(pc_sb *b, const char *s)
Append NUL-terminated s; leaves the buffer untouched and clears ok if it would not fit.
Definition strbuf.h:60
Bump-append target; ok latches false once an append would overflow cap.
Definition strbuf.h:30
char * p
Definition strbuf.h:31