summaryrefslogtreecommitdiffstats
path: root/rsnix/src/klog.rs
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-08-01 14:21:11 +0200
committersalmonik <salmonik@tuta.io>2026-08-01 14:21:11 +0200
commit6447eb12948d37b74adae2fdbf1db5a9d7b363d5 (patch)
tree0aa03cc79db7ff49f5a64a484ff197ba05f9b6bc /rsnix/src/klog.rs
parentklog: kernel log ring + /dev/klog + klog shell command (diff)
downloadrs-dos-6447eb12948d37b74adae2fdbf1db5a9d7b363d5.tar.gz
rs-dos-6447eb12948d37b74adae2fdbf1db5a9d7b363d5.zip
klog: [ssss.mmm] millisecond timestamps from the PIT
- interrupts::uptime_ms(): monotonic ms since boot — the 100 Hz PIT tick (10 ms) plus interpolation from the PIT's live 1.19 MHz down-counter (read twice around the tick load; a count jump means a tick fired between reads) for true sub-ms resolution. TICKS widened AtomicU16 -> AtomicU64 (u16 wrapped after ~11 min). - PIT_READY gates interpolation until interrupts::init configures the timer, so early-boot lines are deterministically [0000.000] instead of uninitialised-PIT noise; klog clamps timestamps monotonic. - The CMOS RTC cannot provide milliseconds (1 Hz BCD device, seconds only) — millis come from the PIT. - klog stores ms per line at append time; dump format is now '[0000.123] 000000 === rsnix boot ===' (user-specified format). Verified in QEMU: first line [0000.000], Phase-2 lines show real monotonic ms (fs: vfs ready at [0000.519], self-test [0000.524]), per-dump monotonicity, boot.log persists timestamps, test-disk round trip fsck-clean.
Diffstat (limited to '')
-rw-r--r--rsnix/src/klog.rs30
1 files changed, 24 insertions, 6 deletions
diff --git a/rsnix/src/klog.rs b/rsnix/src/klog.rs
index 1e23460..0c7dc06 100644
--- a/rsnix/src/klog.rs
+++ b/rsnix/src/klog.rs
@@ -32,6 +32,10 @@ struct Ring {
32 count: usize, 32 count: usize,
33 /// Slot the next line will be written to (oldest once full). 33 /// Slot the next line will be written to (oldest once full).
34 next: usize, 34 next: usize,
35 /// Highest timestamp seen, so the log stays monotonic even before
36 /// the timer is running (early-boot PIT reads are uninitialised).
37 last_ms: u64,
38 ms: [u64; MAX_LINES],
35 lens: [u8; MAX_LINES], 39 lens: [u8; MAX_LINES],
36 bytes: [[u8; MAX_LINE]; MAX_LINES], 40 bytes: [[u8; MAX_LINE]; MAX_LINES],
37} 41}
@@ -41,6 +45,8 @@ static mut RING: Ring = Ring {
41 dropped: 0, 45 dropped: 0,
42 count: 0, 46 count: 0,
43 next: 0, 47 next: 0,
48 last_ms: 0,
49 ms: [0; MAX_LINES],
44 lens: [0; MAX_LINES], 50 lens: [0; MAX_LINES],
45 bytes: [[0; MAX_LINE]; MAX_LINES], 51 bytes: [[0; MAX_LINE]; MAX_LINES],
46}; 52};
@@ -49,10 +55,16 @@ fn ring() -> &'static mut Ring {
49 unsafe { &mut *core::ptr::addr_of_mut!(RING) } 55 unsafe { &mut *core::ptr::addr_of_mut!(RING) }
50} 56}
51 57
52/// Append one line to the ring (line-oriented; newlines are stripped). 58/// Append one line to the ring with its timestamp in ms (line-oriented;
53/// Truncated at MAX_LINE bytes. Never allocates, never logs. 59/// newlines are stripped). Truncated at MAX_LINE bytes. Never
54pub fn append(line: &str) { 60/// allocates, never logs. Timestamps are clamped to be monotonic.
61pub fn append(line: &str, mut ms: u64) {
55 let r = ring(); 62 let r = ring();
63 if ms > r.last_ms {
64 r.last_ms = ms;
65 } else {
66 ms = r.last_ms;
67 }
56 let b = line.as_bytes(); 68 let b = line.as_bytes();
57 let n = b.len().min(MAX_LINE); 69 let n = b.len().min(MAX_LINE);
58 if r.count == MAX_LINES { 70 if r.count == MAX_LINES {
@@ -60,6 +72,7 @@ pub fn append(line: &str) {
60 } else { 72 } else {
61 r.count += 1; 73 r.count += 1;
62 } 74 }
75 r.ms[r.next] = ms;
63 r.bytes[r.next][..n].copy_from_slice(&b[..n]); 76 r.bytes[r.next][..n].copy_from_slice(&b[..n]);
64 r.lens[r.next] = n as u8; 77 r.lens[r.next] = n as u8;
65 r.next = (r.next + 1) % MAX_LINES; 78 r.next = (r.next + 1) % MAX_LINES;
@@ -68,11 +81,14 @@ pub fn append(line: &str) {
68 81
69/// Append the formatted output of a `serial_println!` — the macro hook. 82/// Append the formatted output of a `serial_println!` — the macro hook.
70/// `fmt::Write` may deliver the line in several chunks, so accumulate 83/// `fmt::Write` may deliver the line in several chunks, so accumulate
71/// into one line buffer first, then append the whole line. 84/// into one line buffer first, then append the whole line. The
85/// timestamp is taken when the line is emitted (monotonic ms since
86/// boot, from the PIT tick + live-counter interpolation).
72pub fn append_fmt(args: core::fmt::Arguments<'_>) { 87pub fn append_fmt(args: core::fmt::Arguments<'_>) {
88 let ms = crate::interrupts::uptime_ms();
73 let mut line = LineBuf::new(); 89 let mut line = LineBuf::new();
74 let _ = core::fmt::write(&mut line, args); 90 let _ = core::fmt::write(&mut line, args);
75 append(line.as_str()); 91 append(line.as_str(), ms);
76} 92}
77 93
78struct LineBuf { 94struct LineBuf {
@@ -124,7 +140,9 @@ pub fn dump() -> String {
124 let idx = (start + i) % MAX_LINES; 140 let idx = (start + i) % MAX_LINES;
125 let n = r.lens[idx] as usize; 141 let n = r.lens[idx] as usize;
126 let text = core::str::from_utf8(&r.bytes[idx][..n]).unwrap_or(""); 142 let text = core::str::from_utf8(&r.bytes[idx][..n]).unwrap_or("");
127 out += &alloc::format!("{:06} {}\n", first + i as u64, text); 143 let s = r.ms[idx] / 1000;
144 let m = r.ms[idx] % 1000;
145 out += &alloc::format!("[{:04}.{:03}] {:06} {}\n", s, m, first + i as u64, text);
128 } 146 }
129 out 147 out
130} 148}