Skip to main content

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}