kernel: tagged log ring — per-line pid/name/level records, klog_status

Replace the linear keep-earliest RAM buffer with a 512 KiB ring of
framed records (log-ring.zig, host-tested): every debug_write becomes
one record per payload line, stamped by the kernel with the sender's
pid, task name (its binary path), level, per-boot sequence number, and
monotonic timestamp. Attribution is structural — a payload cannot forge
another sender's tag, and newline injection lands inside the forger's
own next record. Oldest records are overwritten when full; sequence
gaps make the loss countable.

debug_write gains a level argument (err/warn/info/debug/raw; old
two-arg callers clamp to raw). klog_read becomes a stream-offset read
that fails once the cursor falls behind the ring's tail; the new
klog_status (#45) returns the cursors plus the boot wall-clock anchor —
what the logger service will name per-boot log directories with.

The log now guards itself with a dedicated spinlock (BKL -> log lock
order, never the reverse); panic paths use a bounded try-acquire and
fall back to sinks-only. Serial rendering keeps the historical
transcript byte-identical for kernel and legacy raw output; leveled
records get a kernel-rendered name prefix. log-flush/init's interim
drains start at the ring tail and write framed records until the logger
service replaces them.
This commit is contained in:
Daniel Samson
2026-07-21 15:30:28 +01:00
parent 0a84e52bf8
commit 9f18d8340e
10 changed files with 549 additions and 80 deletions
+2 -2
View File
@@ -75,7 +75,7 @@ fn kmain(boot_information: *const BootInformation) noreturn {
// Retain the whole stream in a RAM buffer too, so a user program can later
// read it back (klog_read) and persist the boot log to disk — the only way to
// see it on a headless/real machine with no host capturing serial.
log.addSink(log.ramSink);
// (Retention is the tagged ring inside log.zig — not a sink.)
// The **framebuffer** is deliberately *not* a log sink. It's a separate output
// surface — a bootstrap text console today, a graphics device driver later — so
@@ -426,7 +426,7 @@ fn status(message: []const u8) void {
/// any display service holding the framebuffer. The console is otherwise silent in normal
/// operation (see `status`); it exists now only for early-boot and fatal output.
fn fatal(message: []const u8) void {
log.write(message);
log.appendPanic(message); // bounded lock wait: a panic never deadlocks on the log
console.setSuppressed(false);
console.write(message);
}