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:
+49
-1
@@ -58,7 +58,7 @@ pub const SystemCall = enum(u64) {
|
||||
signal_bind = 29, // signal_bind(endpoint) -> 0/-errno: nominate the endpoint this process's signals arrive on
|
||||
process_signal = 30, // process_signal(id, signal) -> 0/-errno: post a signal to a child (or to yourself)
|
||||
timer_bind = 31, // timer_bind(endpoint, ms) -> 0/-errno: one-shot timer — posts a notification when ms elapse
|
||||
klog_read = 32, // klog_read(offset, ptr, len) -> bytes copied: copy the kernel RAM log buffer out to a user buffer (for persisting the boot log to disk)
|
||||
klog_read = 32, // klog_read(offset, ptr, len) -> bytes copied: copy tagged log-ring stream bytes from `offset` out to a user buffer; fails once `offset` falls behind the ring's tail (re-sync via klog_status)
|
||||
wall_clock = 33, // wall_clock() -> Unix epoch seconds (UTC): the RTC wall-clock time, for filesystem timestamps (mtime). Monotonic time is `clock`.
|
||||
shm_create = 34, // shm_create(len) -> virtual_address (rax), handle (rdx): a shareable, zeroed, cacheable RAM region mapped into this AS; the handle is a capability passed to another process as an ipc_call send_cap (docs/display-v2.md)
|
||||
shm_map = 35, // shm_map(cap) -> virtual_address: map the shared region named by a received capability into this address space (the same physical pages the creator sees)
|
||||
@@ -71,6 +71,7 @@ pub const SystemCall = enum(u64) {
|
||||
thread_self = 42, // thread_self() -> tid: the calling thread's kernel task id (runtime.Thread.getCurrentId)
|
||||
thread_join = 43, // thread_join(tid) -> 0: block until the thread with id `tid` has exited (runtime.Thread.join; no per-thread IPC endpoint) (docs/threading.md)
|
||||
set_thread_pointer = 44, // set_thread_pointer(addr) -> 0: set the caller's thread pointer (user-space TLS base; x86_64 IA32_FS_BASE, aarch64 TPIDR_EL0); restored per task across context switches (docs/threading-plan.md M10)
|
||||
klog_status = 45, // klog_status(ptr) -> 0: copy a KlogStatus (ring cursors + the boot wall-clock anchor) out to a user buffer
|
||||
_,
|
||||
};
|
||||
|
||||
@@ -187,6 +188,53 @@ pub const ProcessDescriptor = extern struct {
|
||||
name: [maximum_process_name]u8, // argv[0] at spawn; empty for kernel tasks
|
||||
};
|
||||
|
||||
// --- the tagged kernel log ring (klog) ---------------------------------------
|
||||
// Every `debug_write` becomes one RECORD per payload line, stamped by the kernel
|
||||
// with the sender's pid, task name (its binary path), level, a per-boot sequence
|
||||
// number, and a monotonic timestamp. `klog_read` copies raw stream bytes — a
|
||||
// reader parses [KlogRecordHeader][name][message] frames, each padded to
|
||||
// `klog_record_alignment`. Sequence gaps tell a reader exactly how many records
|
||||
// the ring overwrote while it wasn't looking.
|
||||
|
||||
/// Log level of a klog record — std.log's levels plus `raw` (untagged bytes:
|
||||
/// kernel prints and legacy runtime.system.write output).
|
||||
pub const KlogLevel = enum(u8) { err = 0, warn = 1, info = 2, debug = 3, raw = 4 };
|
||||
|
||||
/// "RK" — leads every record; a parser's resync/corruption guard.
|
||||
pub const klog_record_magic: u16 = 0x4B52;
|
||||
|
||||
/// KlogRecordHeader.flags bit: the emitter truncated the payload to fit.
|
||||
pub const klog_flag_truncated: u8 = 1;
|
||||
|
||||
/// Header of one ring record, followed by `name_len` bytes of task name and
|
||||
/// `message_len` bytes of payload; the whole record is padded to 8 bytes.
|
||||
pub const KlogRecordHeader = extern struct {
|
||||
magic: u16, // klog_record_magic
|
||||
level: KlogLevel,
|
||||
name_len: u8, // 0..maximum_process_name
|
||||
pid: u32, // sender process id; 0 = the kernel itself
|
||||
sequence: u64, // per-boot monotonic record number (gaps = lost records)
|
||||
timestamp_ns: u64, // monotonic ns since boot (the `clock` timebase)
|
||||
message_len: u16, // payload bytes (excludes the record's trailing pad)
|
||||
flags: u8, // klog_flag_* bits
|
||||
_reserved: [5]u8,
|
||||
};
|
||||
|
||||
pub const klog_record_header_size: usize = 32; // @sizeOf(KlogRecordHeader), pinned by a test
|
||||
pub const klog_record_alignment: usize = 8;
|
||||
/// Per-record payload cap (one line; longer emitter lines are truncated).
|
||||
pub const klog_maximum_message: usize = 256;
|
||||
|
||||
/// The klog_status copy-out: the ring's live cursors plus the wall-clock time
|
||||
/// of boot — the anchor a log persister names its per-boot directory with and
|
||||
/// combines with record timestamps for wall-clock line stamps.
|
||||
pub const KlogStatus = extern struct {
|
||||
tail: u64, // oldest retained stream offset — always a record boundary
|
||||
head: u64, // next byte to be written (end of stream)
|
||||
next_sequence: u64, // the sequence the next record will get
|
||||
boot_unix_seconds: u64, // wall-clock time of boot (RTC anchor)
|
||||
};
|
||||
|
||||
/// Well-known IPC service ids for the bootstrap name registry (create_ipc_endpoint +
|
||||
/// ipc_register/ipc_lookup). Small integers, so no string interning is needed
|
||||
/// during bring-up. The VFS server registers under `vfs`; clients look it up.
|
||||
|
||||
Reference in New Issue
Block a user