Skip to main content

oxicuda_launch/
trace.rs

1//! Launch telemetry / tracing integration.
2//!
3//! This module provides the [`kernel_launch_span!`] macro that emits a
4//! `tracing` instrumentation span around every kernel launch when the
5//! `tracing` Cargo feature is enabled. When the feature is disabled the
6//! macro expands to a unit expression `()` with zero overhead.
7//!
8//! # Feature gate
9//!
10//! Add `tracing` to the features in `Cargo.toml` to enable live spans:
11//!
12//! ```toml
13//! [dependencies]
14//! oxicuda-launch = { version = "*", features = ["tracing"] }
15//! ```
16//!
17//! # Span fields
18//!
19//! When enabled, each span carries:
20//!
21//! | Field    | Type     | Description                              |
22//! |----------|----------|------------------------------------------|
23//! | `kernel` | `&str`   | Function name as passed to `Kernel::new` |
24//! | `grid`   | Debug    | Grid dimensions `(x, y, z)`             |
25//! | `block`  | Debug    | Block dimensions `(x, y, z)`            |
26//!
27//! # Example (with tracing enabled)
28//!
29//! ```rust
30//! use oxicuda_launch::trace::kernel_launch_span;
31//!
32//! let _span = kernel_launch_span!("my_kernel", (4u32, 1u32, 1u32), (256u32, 1u32, 1u32));
33//! // The span is entered while `_span` is in scope.
34//! ```
35
36// =========================================================================
37// kernel_launch_span! macro
38// =========================================================================
39
40/// Emit a tracing span for a kernel launch.
41///
42/// # Arguments
43///
44/// * `$name` — kernel function name (string literal or `&str`).
45/// * `$grid` — grid dimensions (anything that implements `Debug`).
46/// * `$block` — block dimensions (anything that implements `Debug`).
47///
48/// # Behaviour
49///
50/// * With the `tracing` feature: returns an [`tracing::Span`] entered at
51///   `INFO` level.  Assign the result to a variable; the span closes when
52///   the variable is dropped.
53/// * Without the `tracing` feature: expands to `()`.
54#[cfg(feature = "tracing")]
55#[macro_export]
56macro_rules! kernel_launch_span {
57    ($name:expr, $grid:expr, $block:expr) => {
58        tracing::info_span!(
59            "kernel_launch",
60            kernel = $name,
61            grid   = ?$grid,
62            block  = ?$block,
63        )
64    };
65}
66
67/// No-op version used when the `tracing` feature is disabled.
68#[cfg(not(feature = "tracing"))]
69#[macro_export]
70macro_rules! kernel_launch_span {
71    ($name:expr, $grid:expr, $block:expr) => {
72        ()
73    };
74}
75
76// Re-export so callers can use `oxicuda_launch::trace::kernel_launch_span`.
77pub use kernel_launch_span;
78
79// =========================================================================
80// KernelSpanGuard — RAII wrapper that enters/exits the span
81// =========================================================================
82
83/// RAII guard that enters a kernel-launch tracing span and exits on drop.
84///
85/// When the `tracing` feature is disabled this struct is zero-sized.
86///
87/// # Usage
88///
89/// ```rust
90/// use oxicuda_launch::trace::KernelSpanGuard;
91///
92/// let _guard = KernelSpanGuard::enter("vector_add", (4u32, 1u32, 1u32), (256u32, 1u32, 1u32));
93/// // Span active here — dropped at end of scope.
94/// ```
95#[must_use = "The span is closed when the guard is dropped; don't discard it."]
96pub struct KernelSpanGuard {
97    #[cfg(feature = "tracing")]
98    _entered: tracing::span::EnteredSpan,
99}
100
101impl KernelSpanGuard {
102    /// Enter a new kernel-launch span.
103    ///
104    /// The span is active (entered) until this guard is dropped.
105    pub fn enter<G, B>(kernel_name: &str, grid: G, block: B) -> Self
106    where
107        G: std::fmt::Debug,
108        B: std::fmt::Debug,
109    {
110        #[cfg(feature = "tracing")]
111        {
112            let span = tracing::info_span!(
113                "kernel_launch",
114                kernel = kernel_name,
115                grid   = ?grid,
116                block  = ?block,
117            );
118            Self {
119                _entered: span.entered(),
120            }
121        }
122
123        #[cfg(not(feature = "tracing"))]
124        {
125            // Suppress "unused variable" warnings in no-tracing builds.
126            let _ = kernel_name;
127            let _ = grid;
128            let _ = block;
129            Self {}
130        }
131    }
132}
133
134// =========================================================================
135// Tests
136// =========================================================================
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141
142    /// KernelSpanGuard must be zero-sized when tracing is disabled, or a
143    /// reasonable size when enabled.
144    #[test]
145    fn test_kernel_span_guard_size() {
146        // Zero bytes without tracing; non-zero with tracing (contains EnteredSpan).
147        #[cfg(feature = "tracing")]
148        assert!(std::mem::size_of::<KernelSpanGuard>() > 0);
149
150        #[cfg(not(feature = "tracing"))]
151        assert_eq!(std::mem::size_of::<KernelSpanGuard>(), 0);
152    }
153
154    #[test]
155    fn test_kernel_span_guard_drop() {
156        // Should not panic when dropped.
157        {
158            let _guard =
159                KernelSpanGuard::enter("test_kernel", (1u32, 1u32, 1u32), (32u32, 1u32, 1u32));
160        }
161        // If we reach here, no panic occurred.
162    }
163
164    #[test]
165    fn test_kernel_span_guard_enter_and_exit() {
166        let guard = KernelSpanGuard::enter("matmul", (16u32, 16u32, 1u32), (16u32, 16u32, 1u32));
167        // Guard is active; doing some work.
168        let _ = 1 + 1;
169        drop(guard); // Span exited here.
170    }
171
172    #[test]
173    fn test_macro_invocation_compiles() {
174        // Verify the macro expands without error.
175        let _span = kernel_launch_span!("test_kernel", (4u32, 1u32, 1u32), (256u32, 1u32, 1u32));
176        // On tracing builds _span is a Span; on no-tracing builds it is ().
177        let _ = _span;
178    }
179}