tracy_client/
span.rs

1use crate::{adjust_stack_depth, Client};
2use std::ffi::CString;
3
4/// A handle representing a span of execution.
5///
6/// The trace span will be ended when this type is dropped.
7pub struct Span {
8    #[cfg(feature = "enable")]
9    client: Client,
10    #[cfg(feature = "enable")]
11    zone: sys::___tracy_c_zone_context,
12    #[cfg(feature = "enable")]
13    _no_send_sync: std::marker::PhantomData<*mut sys::___tracy_c_zone_context>,
14    #[cfg(not(feature = "enable"))]
15    _no_send_sync: std::marker::PhantomData<*mut ()>,
16}
17
18/// A statically allocated location information for a span.
19///
20/// Construct with the [`span_location!`](crate::span_location) macro.
21pub struct SpanLocation {
22    #[cfg(feature = "enable")]
23    pub(crate) _function_name: CString,
24    #[cfg(feature = "enable")]
25    pub(crate) data: sys::___tracy_source_location_data,
26    #[cfg(not(feature = "enable"))]
27    pub(crate) _internal: (),
28}
29
30unsafe impl Send for SpanLocation {}
31unsafe impl Sync for SpanLocation {}
32
33/// Instrumentation for timed regions, spans or zones of execution.
34impl Client {
35    /// Start a new Tracy span/zone.
36    ///
37    /// In order to obtain a [`SpanLocation`] value to provide to this function use the
38    /// [`span_location!`](crate::span_location) macro.
39    ///
40    /// Specifying a non-zero `callstack_depth` will enable collection of callstack for this
41    /// message. The number provided will limit the number of call frames collected. Note that
42    /// enabling callstack collection introduces a non-trivial amount of overhead to this call. On
43    /// some systems this value may be clamped to a maximum value supported by the target.
44    ///
45    /// The [`span!`](crate::span!) macro is a convenience wrapper over this method.
46    ///
47    /// # Example
48    ///
49    /// In the following example the span is created with the location at which the
50    /// `span_location!` macro appears and will measure the execution of the 100ms long sleep.
51    ///
52    /// ```rust
53    /// use tracy_client::{Client, span_location};
54    /// let client = Client::start();
55    /// {
56    ///     let _span = client.span(span_location!("sleeping"), 100);
57    ///     std::thread::sleep(std::time::Duration::from_millis(100));
58    /// } // _span ends
59    /// ```
60    #[inline]
61    #[must_use]
62    pub fn span(self, loc: &'static SpanLocation, callstack_depth: u16) -> Span {
63        #[cfg(feature = "enable")]
64        unsafe {
65            let zone = if callstack_depth == 0 {
66                sys::___tracy_emit_zone_begin(&loc.data, 1)
67            } else {
68                let stack_depth = adjust_stack_depth(callstack_depth).into();
69                sys::___tracy_emit_zone_begin_callstack(&loc.data, stack_depth, 1)
70            };
71            Span {
72                client: self,
73                zone,
74                _no_send_sync: std::marker::PhantomData,
75            }
76        }
77        #[cfg(not(feature = "enable"))]
78        Span {
79            _no_send_sync: std::marker::PhantomData,
80        }
81    }
82
83    /// Start a new Tracy span/zone.
84    ///
85    /// This function allocates the span information on the heap until it is read out by the
86    /// profiler. Prefer the [`Client::span`] as a allocation-free and faster alternative when
87    /// possible.
88    ///
89    /// Specifying a non-zero `callstack_depth` will enable collection of callstack for this
90    /// message. The number provided will limit the number of call frames collected. Note that
91    /// enabling callstack collection introduces a non-trivial amount of overhead to this call. On
92    /// some systems this value may be clamped to a maximum value supported by the target.
93    ///
94    /// # Example
95    ///
96    /// In the following example the span is created with custom span source data and will measure
97    /// the execution of the 100ms long sleep.
98    ///
99    /// ```rust
100    /// use tracy_client::Client;
101    /// let client = Client::start();
102    /// {
103    ///     let _span = client.span_alloc(Some("hello"), "my_function", "hello.rs", 42, 100);
104    ///     std::thread::sleep(std::time::Duration::from_millis(100));
105    /// } // _span ends
106    /// ```
107    #[inline]
108    #[must_use]
109    pub fn span_alloc(
110        self,
111        name: Option<&str>,
112        function: &str,
113        file: &str,
114        line: u32,
115        callstack_depth: u16,
116    ) -> Span {
117        #[cfg(feature = "enable")]
118        unsafe {
119            let loc = sys::___tracy_alloc_srcloc_name(
120                line,
121                file.as_ptr().cast(),
122                file.len(),
123                function.as_ptr().cast(),
124                function.len(),
125                name.map_or(std::ptr::null(), |n| n.as_ptr().cast()),
126                name.unwrap_or("").len(),
127                0,
128            );
129            let zone = if callstack_depth == 0 {
130                sys::___tracy_emit_zone_begin_alloc(loc, 1)
131            } else {
132                let stack_depth = adjust_stack_depth(callstack_depth).into();
133                sys::___tracy_emit_zone_begin_alloc_callstack(loc, stack_depth, 1)
134            };
135            Span {
136                client: self,
137                zone,
138                _no_send_sync: std::marker::PhantomData,
139            }
140        }
141        #[cfg(not(feature = "enable"))]
142        Span {
143            _no_send_sync: std::marker::PhantomData,
144        }
145    }
146}
147
148impl Span {
149    /// Emit a numeric value associated with this span.
150    pub fn emit_value(&self, value: u64) {
151        #[cfg(feature = "enable")]
152        unsafe {
153            // SAFE: the only way to construct `Span` is by creating a valid tracy zone context.
154            let () = sys::___tracy_emit_zone_value(self.zone, value);
155        }
156    }
157
158    /// Emit some text associated with this span.
159    pub fn emit_text(&self, text: &str) {
160        #[cfg(feature = "enable")]
161        unsafe {
162            // SAFE: the only way to construct `Span` is by creating a valid tracy zone context.
163            let () = sys::___tracy_emit_zone_text(self.zone, text.as_ptr().cast(), text.len());
164        }
165    }
166
167    /// Emit a color associated with this span.
168    ///
169    /// The color is specified as RGB. It is most straightforward to specify them as hex literals
170    /// such as `0xFF0000` for red, `0x00FF00` for green or `0x0000FF` for blue.
171    pub fn emit_color(&self, color: u32) {
172        #[cfg(feature = "enable")]
173        unsafe {
174            // SAFE: the only way to construct `Span` is by creating a valid tracy zone context.
175            // TODO: verify if we need to shift by 8 or not...?
176            let () = sys::___tracy_emit_zone_color(self.zone, color);
177        }
178    }
179}
180
181impl Drop for Span {
182    fn drop(&mut self) {
183        #[cfg(feature = "enable")]
184        unsafe {
185            // SAFE: The only way to construct `Span` is by creating a valid tracy zone context. We
186            // also still have an owned Client handle.
187            let () = sys::___tracy_emit_zone_end(self.zone);
188            std::convert::identity(&self.client);
189        }
190    }
191}
192
193/// Construct a <code>&'static [SpanLocation]</code>.
194///
195/// The returned `SpanLocation` is allocated statically and is cached between invocations. This
196/// `SpanLocation` will refer to the file and line at which this macro has been invoked, as well as
197/// to the item containing this macro invocation.
198///
199/// The resulting value may be used as an argument for the [`Client::span`] method.
200///
201/// # Example
202///
203/// ```rust
204/// let location: &'static tracy_client::SpanLocation = tracy_client::span_location!("some name");
205/// ```
206#[macro_export]
207macro_rules! span_location {
208    () => {{
209        struct S;
210        // String processing in `const` when, Oli?
211        static LOC: $crate::internal::Lazy<$crate::internal::SpanLocation> =
212            $crate::internal::Lazy::new(|| {
213                $crate::internal::make_span_location(
214                    $crate::internal::type_name::<S>(),
215                    $crate::internal::null(),
216                    concat!(file!(), "\0").as_ptr(),
217                    line!(),
218                )
219            });
220        &*LOC
221    }};
222    ($name: expr) => {{
223        struct S;
224        // String processing in `const` when, Oli?
225        static LOC: $crate::internal::Lazy<$crate::internal::SpanLocation> =
226            $crate::internal::Lazy::new(|| {
227                $crate::internal::make_span_location(
228                    $crate::internal::type_name::<S>(),
229                    concat!($name, "\0").as_ptr(),
230                    concat!(file!(), "\0").as_ptr(),
231                    line!(),
232                )
233            });
234        &*LOC
235    }};
236}
237
238/// Start a new Tracy span with function, file, and line determined automatically.
239///
240/// # Panics
241///
242/// `span!` will panic if the Client isn't running at the time this macro is invoked.
243///
244/// # Examples
245///
246/// Begin a span region, which will be terminated once `_span` goes out of scope:
247///
248/// ```
249/// use tracy_client::{Client, span};
250/// # let _client = tracy_client::Client::start();
251/// let _span = span!("some span");
252/// ```
253///
254/// It is also possible to enable collection of the callstack by specifying a limit of call stack
255/// frames to record:
256///
257/// ```
258/// use tracy_client::span;
259/// # let _client = tracy_client::Client::start();
260/// let _span = span!("some span", 32);
261/// ```
262///
263/// Note, however, that collecting callstack introduces a non-trivial overhead at the point of
264/// instrumentation.
265#[macro_export]
266macro_rules! span {
267    () => {
268        $crate::Client::running()
269            .expect("span! without a running Client")
270            .span($crate::span_location!(), 0)
271    };
272    ($name: expr) => {
273        $crate::span!($name, 0)
274    };
275    ($name: expr, $callstack_depth: expr) => {{
276        let location = $crate::span_location!($name);
277        $crate::Client::running()
278            .expect("span! without a running Client")
279            .span(location, $callstack_depth)
280    }};
281}