1/* @title: Logging */
2#pragma once
3#include <bootstage.h>
4#include <colors.h>
5#include <compiler/diagnostic.h>
6#include <compiler/intrinsic.h>
7#include <linker/symbols.h>
8#include <sch/irql.h>
9#include <stdbool.h>
10#include <stddef.h>
11#include <stdint.h>
12#include <structures/list.h>
13#include <types/refcount.h>
14#include <types/types.h>
15
16#define LOG_NARGS 10
17struct log_site;
18struct log_record;
19
20enum log_handle_flags : uint32_t {
21 LOG_HANDLE_PRINT = 1 << 0, /* emit to console immediately */
22 LOG_HANDLE_IMPORTANT = 1 << 1, /* never drop / elevated visibility */
23 LOG_HANDLE_RATELIMIT = 1 << 2, /* suppress floods */
24 LOG_HANDLE_ONCE = 1 << 3, /* print only first occurrence */
25 LOG_HANDLE_PANIC = 1 << 4, /* fatal if level >= ERROR */
26 LOG_HANDLE_NO_NEWLINE = 1 << 5,
27 LOG_HANDLE_FLAGS_DEFAULT = 0,
28};
29
30enum log_level : uint8_t {
31 LOG_TRACE,
32 LOG_DEBUG,
33 LOG_INFO,
34 LOG_WARN,
35 LOG_ERROR,
36};
37
38enum log_site_flags : uint32_t {
39 LOG_SITE_PRINT = 1 << 0, /* print all logs in site */
40 LOG_SITE_DROP_OLD = 1 << 1, /* overwrite oldest on overflow */
41 LOG_SITE_NO_IRQ = 1 << 2, /* suppress in IRQ context */
42 LOG_SITE_PANIC_VISIBLE = 1 << 3,
43
44 LOG_SITE_NDJSON = 1 << 4, /* emit records directly to NDJSON wire */
45 LOG_SITE_NONE = 0,
46 LOG_SITE_DEFAULT = LOG_SITE_DROP_OLD,
47};
48
49enum log_record_flags : uint16_t {
50 LOG_REC_FROM_IRQ = 1 << 0,
51 LOG_REC_TRUNCATED = 1 << 1,
52};
53
54/* Governs what to print */
55struct log_dump_options {
56 uint8_t min_level;
57 bool show_cpu : 1;
58 bool show_tid : 1;
59 bool show_irql : 1;
60 bool show_caller : 1;
61 bool resolve_symbols : 1;
62 bool clear_after_dump : 1;
63};
64
65struct log_handle {
66 void (*print)(const struct log_site *site, const struct log_record *rec,
67 void (*print)(const char *fmt, ...) cc_printf_like(1, 2));
68 enum log_handle_flags flags;
69 atomic_uint32_t seen_internal;
70 atomic_uint64_t last_ts_internal;
71};
72
73struct log_record {
74 time_ms_t timestamp;
75 cpu_id_t cpu;
76 uint32_t tid;
77
78 const struct log_handle *handle;
79 enum log_level level;
80 enum bootstage boostage;
81
82 uint16_t msg_len;
83
84 char *fmt; /* Becomes a pointer to the shadow_buf in cases where
85 * LOG_SITE_DUP_MESSAGES is set */
86 uint8_t nargs;
87 uint64_t args[LOG_NARGS];
88
89 char *caller_fn;
90 char *caller_file;
91 int32_t caller_line;
92 uintptr_t caller_pc;
93 enum log_record_flags flags;
94 enum irql logged_at_irql;
95};
96
97struct log_ring_slot {
98 atomic_uint64_t seq;
99 struct log_record rec;
100 char *shadow_buf; /* With a len of msg_max_len */
101};
102
103struct log_ringbuf {
104 struct log_ring_slot *slots;
105
106 atomic_uint64_t head;
107 atomic_uint64_t tail;
108};
109
110/* Ephemeral argument passed in creation */
111struct log_site_options {
112 char *name;
113 enum log_site_flags flags;
114 size_t capacity;
115 size_t msg_max_len; /* only if LOG_SITE_DUP_MESSAGES on,
116 * will truncate trailing characters if strlen
117 * of the input fmt str is too long */
118
119 uint32_t enabled_mask;
120 struct log_dump_options dump_opts;
121};
122
123struct log_site {
124 struct list_node list;
125 char *name;
126 struct log_ringbuf rb;
127 uint32_t enabled_mask; /* Governs what actually goes in the site */
128 size_t capacity;
129
130 /* Only relevant for dynamic log sites */
131 refcount_t refcount;
132
133 size_t msg_max_len;
134 uint32_t dropped; /* Accumulation of all missed logs */
135 enum log_site_flags flags;
136 struct log_dump_options dump_opts; /* If LOG_SITE_PRINT, use this */
137};
138
139static inline const char *log_level_color(enum log_level l) {
140 switch (l) {
141 case LOG_INFO: return ANSI_GREEN;
142 case LOG_WARN: return ANSI_YELLOW;
143 case LOG_ERROR: return ANSI_RED;
144 case LOG_TRACE: return ANSI_BLUE;
145 case LOG_DEBUG: return ANSI_MAGENTA;
146 default: return ANSI_RESET;
147 }
148}
149
150static inline bool log_handle_should_print(const struct log_handle *h,
151 const struct log_site *s,
152 uint8_t level) {
153 if (h->flags & LOG_HANDLE_PRINT)
154 return true;
155
156 if (s && (s->flags & LOG_SITE_PRINT))
157 return true;
158
159 if ((h->flags & LOG_HANDLE_PANIC) && level >= LOG_ERROR)
160 return true;
161
162 return false;
163}
164
165static inline bool log_site_enabled(const struct log_site *ss, uint8_t level) {
166 if (!ss)
167 return true;
168
169 return ss->enabled_mask & (1u << level);
170}
171
172void log_emit_full(struct log_site *, struct log_handle *, enum log_level,
173 const char *func, const char *fname, int32_t line,
174 uintptr_t ip, uint8_t nargs, char *fmt, ...)
175 cc_printf_like(9, 10);
176void log_dump_site_with_opts(struct log_site *, struct log_dump_options opts);
177void log_dump_site(struct log_site *site);
178void log_dump_site_default(struct log_site *);
179void log_dump_all(void);
180void log_dump_panic(void);
181void log_sites_init(void);
182void log_site_free(struct log_site *site);
183struct log_site *log_site_create(struct log_site_options opts);
184void log_handle_init(struct log_handle *h, enum log_handle_flags f);
185
186static inline bool log_site_getref(struct log_site *site) {
187 return refcount_inc_not_zero(rc: &site->refcount);
188}
189
190static inline void log_site_putref(struct log_site *site) {
191 if (refcount_dec_and_test(rc: &site->refcount))
192 log_site_free(site);
193}
194
195/* NOTE: RACY, only usable as a heuristic */
196static inline size_t log_site_message_count(struct log_site *site) {
197 uint64_t head = atomic_load_acq(&site->rb.head);
198 uint64_t tail = atomic_load_acq(&site->rb.tail);
199
200 return head - tail;
201}
202
203#define LOG_HANDLE_DEFAULT \
204 (struct log_handle){.flags = LOG_HANDLE_FLAGS_DEFAULT, \
205 .seen_internal = 0, \
206 .last_ts_internal = 0}
207
208#define LOG_DUMP_DEFAULT \
209 (struct log_dump_options) { \
210 .min_level = LOG_TRACE, .show_cpu = true, .show_tid = true, \
211 .show_irql = true, .show_caller = true, .resolve_symbols = true, \
212 .clear_after_dump = false, \
213 }
214
215#define LOG_DUMP_CONSOLE \
216 (struct log_dump_options) { \
217 .min_level = LOG_TRACE, .show_cpu = false, .show_tid = false, \
218 .show_irql = false, .show_caller = false, .resolve_symbols = false, \
219 .clear_after_dump = false, \
220 }
221
222#define log(site, handle, lvl, fmt, ...) \
223 log_emit_full(site, handle, lvl, __func__, __FILE__, __LINE__, \
224 (uintptr_t) ci_return_address(0), PP_NARG(__VA_ARGS__), fmt, \
225 ##__VA_ARGS__)
226
227#define log_global(handle, lvl, fmt, ...) \
228 log_emit_full(LOG_SITE(global), handle, lvl, __func__, __FILE__, __LINE__, \
229 (uintptr_t) ci_return_address(0), PP_NARG(__VA_ARGS__), fmt, \
230 ##__VA_ARGS__)
231
232#define log_msg(lvl, fmt, ...) \
233 log_emit_full(LOG_SITE(global), LOG_HANDLE(global), lvl, __func__, \
234 __FILE__, __LINE__, (uintptr_t) ci_return_address(0), \
235 PP_NARG(__VA_ARGS__), fmt, ##__VA_ARGS__)
236
237#define log_once(site, handle, lvl, fmt, ...) \
238 do { \
239 static bool done_internal_log = false; \
240 if (!done_internal_log) { \
241 log(site, handle, lvl, fmt, ##__VA_ARGS__); \
242 done_internal_log = true; \
243 } \
244 } while (0)
245
246#define log_global_once(handle, lvl, fmt, ...) \
247 log_once(LOG_SITE(global), handle, lvl, fmt, ##__VA_ARGS__)
248
249#define log_msg_once(lvl, fmt, ...) \
250 log_global_once(LOG_HANDLE(global), lvl, fmt, ##__VA_ARGS__)
251
252#define log_err_global(handle, fmt, ...) \
253 log_global(handle, LOG_ERROR, fmt, ##__VA_ARGS__)
254#define log_warn_global(handle, fmt, ...) \
255 log_global(handle, LOG_WARN, fmt, ##__VA_ARGS__)
256#define log_info_global(handle, fmt, ...) \
257 log_global(handle, LOG_INFO, fmt, ##__VA_ARGS__)
258#define log_debug_global(handle, fmt, ...) \
259 log_global(handle, LOG_DEBUG, fmt, ##__VA_ARGS__)
260#define log_trace_global(handle, fmt, ...) \
261 log_global(handle, LOG_TRACE, fmt, ##__VA_ARGS__)
262
263#define LOG_SITE_CAPACITY_DEFAULT 128 /* good enough for most purposes */
264#define LOG_SITE_EXTERN(name) extern struct log_site __log_site_##name
265
266/* For static ones */
267#define LOG_SITE_LEVEL(l) (1u << l)
268#define LOG_SITE_ALL UINT32_MAX
269
270#define LOG_SITE_DEFINE(_name, ...) \
271 cc_wno_override_init_start LINKER_SECTION_ATTRIBUTE(log_sites) \
272 struct log_site __log_site_##_name = {.capacity = \
273 LOG_SITE_CAPACITY_DEFAULT, \
274 .enabled_mask = LOG_SITE_ALL, \
275 .dump_opts = LOG_DUMP_CONSOLE, \
276 .name = #_name, \
277 __VA_ARGS__}; \
278 cc_wno_override_init_end
279
280#define LOG_SITE_DEFINE_PRINT(_name, ...) \
281 cc_wno_override_init_start LINKER_SECTION_ATTRIBUTE(log_sites) \
282 struct log_site __log_site_##_name = { \
283 .name = #_name, \
284 .flags = LOG_SITE_DEFAULT, \
285 .capacity = LOG_SITE_CAPACITY_DEFAULT, \
286 .enabled_mask = LOG_SITE_ALL, \
287 .dump_opts = LOG_DUMP_CONSOLE, \
288 __VA_ARGS__}; /* Rest will get initialized at boot */ \
289 cc_wno_override_init_end
290
291#define LOG_SITE(name) (&(__log_site_##name))
292
293#define LOG_HANDLE_SUBSYSTEM_NONE NULL
294#define LOG_HANDLE_EXTERN(name) extern struct log_handle __log_handle_##name
295
296#define LOG_HANDLE_DEFINE(_name, ...) \
297 cc_wno_override_init_start struct log_handle __log_handle_##_name = { \
298 .seen_internal = 0, .last_ts_internal = 0, __VA_ARGS__}; \
299 cc_wno_override_init_end
300
301#define _LOG_HANDLE_DEFINE_PRINT(n, storage, ...) \
302 storage struct log_handle __log_handle_##n = {.flags = LOG_HANDLE_PRINT, \
303 .seen_internal = 0, \
304 .last_ts_internal = 0, \
305 __VA_ARGS__}
306
307#define LOG_HANDLE_DEFINE_PRINT(n, ...) \
308 cc_wno_override_init_start _LOG_HANDLE_DEFINE_PRINT(n, , __VA_ARGS__); \
309 cc_wno_override_init_end
310
311#define LOG_HANDLE_DEFINE_PRINT_STATIC(n, ...) \
312 cc_wno_override_init_start _LOG_HANDLE_DEFINE_PRINT(n, static, \
313 __VA_ARGS__); \
314 cc_wno_override_init_end
315
316#define LOG_HANDLE(name) &(__log_handle_##name)
317
318LINKER_SECTION_EXTERN(struct log_site, log_sites);
319
320LOG_HANDLE_EXTERN(global);
321LOG_SITE_EXTERN(global);
322