reovim_kernel/debug/trace.rs
1//! Kernel tracing infrastructure.
2//!
3//! Linux equivalent: `kernel/trace/`
4//!
5//! Provides trace points for debugging without external dependencies.
6//! The driver layer can bridge to `tracing`/`tokio-tracing` if needed.
7
8use std::sync::{
9 OnceLock,
10 atomic::{AtomicBool, Ordering},
11};
12
13/// A trace point that can be enabled/disabled at runtime.
14///
15/// Linux equivalent: `struct tracepoint`
16///
17/// # Example
18///
19/// ```ignore
20/// use reovim_kernel::debug::TracePoint;
21///
22/// static TP_BUFFER_CREATE: TracePoint = TracePoint::new("buffer_create", "mm");
23///
24/// if TP_BUFFER_CREATE.is_enabled() {
25/// // emit trace event
26/// }
27/// ```
28pub struct TracePoint {
29 /// Name of the trace point.
30 pub name: &'static str,
31 /// Module where the trace point is defined.
32 pub module: &'static str,
33 /// Whether the trace point is enabled.
34 enabled: AtomicBool,
35}
36
37impl TracePoint {
38 /// Create a new trace point (const for static initialization).
39 #[must_use]
40 pub const fn new(name: &'static str, module: &'static str) -> Self {
41 Self {
42 name,
43 module,
44 enabled: AtomicBool::new(false),
45 }
46 }
47
48 /// Check if the trace point is enabled.
49 #[must_use]
50 pub fn is_enabled(&self) -> bool {
51 self.enabled.load(Ordering::Relaxed)
52 }
53
54 /// Enable the trace point.
55 pub fn enable(&self) {
56 self.enabled.store(true, Ordering::Relaxed);
57 }
58
59 /// Disable the trace point.
60 pub fn disable(&self) {
61 self.enabled.store(false, Ordering::Relaxed);
62 }
63}
64
65/// Trace event data.
66#[derive(Debug, Clone)]
67pub struct TraceEvent {
68 /// Timestamp in nanoseconds since Unix epoch.
69 pub timestamp_ns: u64,
70 /// Name of the trace point that emitted this event.
71 pub tracepoint: &'static str,
72 /// Module where the trace point is defined.
73 pub module: &'static str,
74 /// Event message.
75 pub message: String,
76}
77
78/// Trace sink trait - driver layer implements this.
79///
80/// Allows the driver layer to capture trace events and forward them
81/// to an external tracing system (e.g., `tracing` crate).
82pub trait TraceSink: Send + Sync {
83 /// Emit a trace event.
84 fn emit(&self, event: TraceEvent);
85}
86
87/// Global trace sink (set by driver layer).
88static TRACE_SINK: OnceLock<Box<dyn TraceSink>> = OnceLock::new();
89
90/// Set the global trace sink.
91///
92/// Can only be called once; subsequent calls are ignored.
93pub fn set_trace_sink(sink: Box<dyn TraceSink>) {
94 let _ = TRACE_SINK.set(sink);
95}
96
97/// Emit a trace event to the global sink.
98///
99/// If no sink is set, this is a no-op.
100#[allow(clippy::cast_possible_truncation)] // Timestamp truncation acceptable
101pub fn emit_trace(tracepoint: &'static str, module: &'static str, message: String) {
102 if let Some(sink) = TRACE_SINK.get() {
103 sink.emit(TraceEvent {
104 timestamp_ns: std::time::SystemTime::now()
105 .duration_since(std::time::UNIX_EPOCH)
106 .map_or(0, |d| d.as_nanos() as u64),
107 tracepoint,
108 module,
109 message,
110 });
111 }
112}
113
114/// Define a trace point.
115///
116/// # Example
117///
118/// ```ignore
119/// define_tracepoint!(TP_BUFFER_CREATE, "mm");
120///
121/// trace_event!(TP_BUFFER_CREATE, "Created buffer {}", id);
122/// ```
123#[macro_export]
124macro_rules! define_tracepoint {
125 ($name:ident, $module:expr) => {
126 static $name: $crate::debug::TracePoint =
127 $crate::debug::TracePoint::new(stringify!($name), $module);
128 };
129}
130
131/// Emit a trace event if the trace point is enabled.
132///
133/// # Example
134///
135/// ```ignore
136/// trace_event!(TP_BUFFER_CREATE, "Created buffer with id={}", buffer_id);
137/// ```
138#[macro_export]
139macro_rules! trace_event {
140 ($tp:expr, $($arg:tt)*) => {
141 if $tp.is_enabled() {
142 $crate::debug::emit_trace($tp.name, $tp.module, format!($($arg)*));
143 }
144 };
145}