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}