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