veridian_kernel/perf/
trace.rs1use core::{
9 cell::UnsafeCell,
10 sync::atomic::{AtomicBool, AtomicUsize, Ordering},
11};
12
13pub(crate) static TRACING_ENABLED: AtomicBool = AtomicBool::new(false);
15
16#[repr(u8)]
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub(crate) enum TraceEventType {
20 SyscallEntry = 0,
22 SyscallExit = 1,
24 SchedSwitchOut = 2,
26 SchedSwitchIn = 3,
28 IpcFastSend = 4,
30 IpcFastReceive = 5,
32 FrameAlloc = 6,
34 FrameFree = 7,
36 PageFault = 8,
38 IpcSlowPath = 9,
40}
41
42#[repr(C)]
44#[derive(Clone, Copy)]
45pub(crate) struct TraceEvent {
46 pub(crate) timestamp: u64,
48 pub(crate) event_type: u8,
50 pub(crate) cpu: u8,
52 _pad: [u8; 6],
54 pub(crate) data: [u64; 2],
56}
57
58impl TraceEvent {
59 const fn empty() -> Self {
60 Self {
61 timestamp: 0,
62 event_type: 0,
63 cpu: 0,
64 _pad: [0; 6],
65 data: [0; 2],
66 }
67 }
68}
69
70const RING_SIZE: usize = 4096;
72
73struct TraceRing {
75 events: [TraceEvent; RING_SIZE],
76 write_idx: AtomicUsize,
77}
78
79impl TraceRing {
80 const fn new() -> Self {
81 Self {
82 events: [TraceEvent::empty(); RING_SIZE],
83 write_idx: AtomicUsize::new(0),
84 }
85 }
86
87 #[inline]
89 fn record(&mut self, event: TraceEvent) {
90 let idx = self.write_idx.load(Ordering::Relaxed) % RING_SIZE;
91 self.events[idx] = event;
92 self.write_idx.fetch_add(1, Ordering::Relaxed);
93 }
94
95 fn total_events(&self) -> usize {
97 self.write_idx.load(Ordering::Relaxed)
98 }
99
100 fn read_recent(&self, count: usize) -> impl Iterator<Item = &TraceEvent> {
102 let total = self.total_events();
103 let available = total.min(RING_SIZE);
104 let to_read = count.min(available);
105
106 let start_raw = if total >= RING_SIZE {
107 total - to_read
108 } else {
109 total.saturating_sub(to_read)
110 };
111
112 (start_raw..start_raw + to_read).map(move |i| &self.events[i % RING_SIZE])
113 }
114}
115
116const MAX_TRACE_CPUS: usize = 16;
118
119struct PerCpuTraceRings([UnsafeCell<TraceRing>; MAX_TRACE_CPUS]);
124
125unsafe impl Sync for PerCpuTraceRings {}
129
130static TRACE_RINGS: PerCpuTraceRings =
131 PerCpuTraceRings([const { UnsafeCell::new(TraceRing::new()) }; MAX_TRACE_CPUS]);
132
133#[inline(always)]
138pub(crate) fn trace_event(event_type: TraceEventType, data0: u64, data1: u64) {
139 if !TRACING_ENABLED.load(Ordering::Relaxed) {
140 return;
141 }
142
143 let cpu = crate::sched::smp::current_cpu_id() as usize;
144 let event = TraceEvent {
145 timestamp: crate::bench::read_timestamp(),
146 event_type: event_type as u8,
147 cpu: cpu as u8,
148 _pad: [0; 6],
149 data: [data0, data1],
150 };
151
152 unsafe {
155 (*TRACE_RINGS.0[cpu.min(MAX_TRACE_CPUS - 1)].get()).record(event);
156 }
157}
158
159pub(crate) fn enable() {
161 TRACING_ENABLED.store(true, Ordering::Release);
162}
163
164pub(crate) fn disable() {
166 TRACING_ENABLED.store(false, Ordering::Release);
167}
168
169pub(crate) fn is_enabled() -> bool {
171 TRACING_ENABLED.load(Ordering::Relaxed)
172}
173
174pub(crate) fn dump_trace(count: usize) {
178 let was_enabled = is_enabled();
179 disable(); crate::println!("=== Trace Dump (most recent {} per CPU) ===", count);
182 crate::println!(
183 "{:>12} {:>4} {:>18} {:>16} {:>16}",
184 "TIMESTAMP",
185 "CPU",
186 "EVENT",
187 "DATA0",
188 "DATA1"
189 );
190
191 #[allow(clippy::needless_range_loop)]
192 for cpu in 0..MAX_TRACE_CPUS {
193 let ring = unsafe { &*TRACE_RINGS.0[cpu].get() };
196 let total = ring.total_events();
197 if total == 0 {
198 continue;
199 }
200
201 for event in ring.read_recent(count) {
202 if event.timestamp == 0 {
203 continue;
204 }
205 let name = event_type_name(event.event_type);
206 crate::println!(
207 "{:>12} {:>4} {:>18} {:#016x} {:#016x}",
208 event.timestamp,
209 event.cpu,
210 name,
211 event.data[0],
212 event.data[1]
213 );
214 }
215 }
216
217 let total: usize = (0..MAX_TRACE_CPUS)
218 .map(|cpu| unsafe { (*TRACE_RINGS.0[cpu].get()).total_events() })
219 .sum();
220 crate::println!("=== Total events recorded: {} ===", total);
221
222 if was_enabled {
223 enable(); }
225}
226
227pub(crate) fn total_events() -> usize {
229 (0..MAX_TRACE_CPUS)
230 .map(|cpu| unsafe { (*TRACE_RINGS.0[cpu].get()).total_events() })
231 .sum()
232}
233
234fn event_type_name(t: u8) -> &'static str {
235 match t {
236 0 => "syscall_entry",
237 1 => "syscall_exit",
238 2 => "sched_switch_out",
239 3 => "sched_switch_in",
240 4 => "ipc_fast_send",
241 5 => "ipc_fast_recv",
242 6 => "frame_alloc",
243 7 => "frame_free",
244 8 => "page_fault",
245 9 => "ipc_slow_path",
246 _ => "unknown",
247 }
248}
249
250#[macro_export]
253macro_rules! trace {
254 ($event_type:expr, $data0:expr, $data1:expr) => {
255 $crate::perf::trace::trace_event($event_type, $data0, $data1)
256 };
257 ($event_type:expr, $data0:expr) => {
258 $crate::perf::trace::trace_event($event_type, $data0, 0)
259 };
260 ($event_type:expr) => {
261 $crate::perf::trace::trace_event($event_type, 0, 0)
262 };
263}