Skip to main content

fuchsia_trace/
lib.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use fuchsia_rcu_traits::RcuDroppable;
6#[cfg(not(fuchsia_api_level_at_least = "31"))]
7use fuchsia_runtime as _;
8use pin_project::pin_project;
9use std::ffi::CStr;
10use std::future::Future;
11use std::marker::PhantomData;
12use std::pin::Pin;
13use std::sync::atomic::Ordering;
14use std::task::Poll;
15use std::{mem, ptr};
16
17pub use sys::{
18    TRACE_BLOB_TYPE_DATA, TRACE_BLOB_TYPE_LAST_BRANCH, TRACE_BLOB_TYPE_PERFETTO, trace_site_t,
19    trace_string_ref_t,
20};
21
22/// `Scope` represents the scope of a trace event.
23#[derive(Copy, Clone)]
24pub enum Scope {
25    Thread,
26    Process,
27    Global,
28}
29
30impl Scope {
31    fn into_raw(self) -> sys::trace_scope_t {
32        match self {
33            Scope::Thread => sys::TRACE_SCOPE_THREAD,
34            Scope::Process => sys::TRACE_SCOPE_PROCESS,
35            Scope::Global => sys::TRACE_SCOPE_GLOBAL,
36        }
37    }
38}
39
40/// Returns true if tracing is enabled.
41#[inline]
42pub fn is_enabled() -> bool {
43    // Trivial no-argument function that will not race
44    unsafe { sys::trace_state() != sys::TRACE_STOPPED }
45}
46
47/// Returns true if tracing has been enabled for the given category.
48pub fn category_enabled<S: CategoryString>(category: S) -> bool {
49    category.is_category_enabled()
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
53pub enum TraceState {
54    Stopped,
55    Started,
56    Stopping,
57}
58
59pub fn trace_state() -> TraceState {
60    match unsafe { sys::trace_state() } {
61        sys::TRACE_STOPPED => TraceState::Stopped,
62        sys::TRACE_STARTED => TraceState::Started,
63        sys::TRACE_STOPPING => TraceState::Stopping,
64        s => panic!("Unknown trace state {:?}", s),
65    }
66}
67
68/// Requests that the trace engine flush its buffers immediately, even if they are not yet full.
69/// Only has effect in streaming mode.
70///
71/// Returns `Ok(())` if the trace engine is in `TRACE_STARTED` or `TRACE_STOPPING` state.
72/// Returns `Err(zx::Status::BAD_STATE)` if tracing is stopped.
73#[cfg(fuchsia_api_level_at_least = "31")]
74#[inline]
75pub fn flush_buffer() -> Result<(), zx::Status> {
76    // SAFETY:
77    // Obligation: `trace_engine_flush_buffer` must be safe to call.
78    // Artifact facts:
79    // - `trace_engine_flush_buffer` is an FFI function defined in `libtrace-engine/handler.h`.
80    // Semantic premises:
81    // - The `libtrace-engine` C documentation states that `trace_engine_flush_buffer` is fully
82    //   thread-safe and may be called at any time without external synchronization, regardless
83    //   of the engine's initialization or shutdown state. It returns `ZX_ERR_BAD_STATE` if
84    //   tracing is stopped or not streaming.
85    // Derivation:
86    // - The thread-safe nature of the C implementation covers any potential race between
87    //   checking trace engine state and making the FFI call, ensuring no undefined behavior
88    //   or data races occur if the engine shuts down concurrently.
89    // Result:
90    // - The FFI call is safe.
91    let status = unsafe { sys::trace_engine_flush_buffer() };
92    zx::Status::ok(status)
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
96#[repr(i32)]
97pub enum BufferingMode {
98    OneShot = sys::TRACE_BUFFERING_MODE_ONESHOT,
99    Circular = sys::TRACE_BUFFERING_MODE_CIRCULAR,
100    Streaming = sys::TRACE_BUFFERING_MODE_STREAMING,
101}
102
103/// An identifier for flows and async spans.
104#[repr(transparent)]
105#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, RcuDroppable)]
106pub struct Id(u64);
107
108impl Id {
109    /// Creates a new `Id`.
110    pub fn new() -> Self {
111        // Creates a new `Id` based on the current monotonic time and a random `u16` to, with high
112        // probability, be globally unique for the duration of the trace.
113        let ts = zx::BootInstant::get().into_nanos() as u64;
114        let high_order = ts << 16;
115        let low_order = rand::random::<u16>() as u64;
116        Self(high_order | low_order)
117    }
118}
119
120impl From<u64> for Id {
121    fn from(u: u64) -> Self {
122        Self(u)
123    }
124}
125
126impl From<Id> for u64 {
127    fn from(id: Id) -> Self {
128        id.0
129    }
130}
131
132pub trait CategoryString: Copy {
133    /// Registers `self` as with the provided context and returns a string ref for it.
134    fn register(&self, context: &Context) -> sys::trace_string_ref_t;
135
136    /// Acquires a context for the category named by `self` if the category is enabled, returning
137    /// None otherwise.
138    fn acquire_context(&self) -> Option<TraceCategoryContext>;
139
140    /// Same as `acquire_context`, but uses the additional `site` parameter to cache the result.
141    fn acquire_context_cached(&self, site: &sys::trace_site_t) -> Option<TraceCategoryContext>;
142
143    /// Returns true if the category named by `self` is enabled.
144    fn is_category_enabled(&self) -> bool;
145}
146
147impl CategoryString for &'static CStr {
148    fn register(&self, context: &Context) -> sys::trace_string_ref_t {
149        unsafe {
150            let mut self_ref = mem::MaybeUninit::<sys::trace_string_ref_t>::uninit();
151            sys::trace_context_register_string_literal(
152                context.raw,
153                self.as_ptr(),
154                self_ref.as_mut_ptr(),
155            );
156            self_ref.assume_init()
157        }
158    }
159
160    fn acquire_context(&self) -> Option<TraceCategoryContext> {
161        unsafe {
162            let mut category_ref = mem::MaybeUninit::<sys::trace_string_ref_t>::uninit();
163            let raw =
164                sys::trace_acquire_context_for_category(self.as_ptr(), category_ref.as_mut_ptr());
165            if raw != ptr::null() {
166                Some(TraceCategoryContext {
167                    context: Context { raw },
168                    category_ref: category_ref.assume_init(),
169                })
170            } else {
171                None
172            }
173        }
174    }
175
176    #[inline]
177    fn acquire_context_cached(&self, site: &sys::trace_site_t) -> Option<TraceCategoryContext> {
178        let current_state = site.load(Ordering::Relaxed);
179        // kSiteStateDisabled = 1, and the top bits are used for other tracking, so we use a mask
180        // instead of equality.
181        if (current_state & 1) != 0 {
182            return None;
183        }
184        unsafe {
185            // SAFETY: The call to `trace_acquire_context_for_category_cached` is sound because
186            // all arguments are live and non-null. If this function returns a non-null
187            // pointer then it also guarantees that `category_ref` will have been initialized.
188            // Internally, it uses relaxed atomic semantics to load and store site.
189            let mut category_ref = mem::MaybeUninit::<sys::trace_string_ref_t>::uninit();
190            let raw = sys::trace_acquire_context_for_category_cached(
191                self.as_ptr(),
192                site.as_ptr(),
193                category_ref.as_mut_ptr(),
194            );
195            if raw != ptr::null() {
196                Some(TraceCategoryContext {
197                    context: Context { raw },
198                    category_ref: category_ref.assume_init(),
199                })
200            } else {
201                None
202            }
203        }
204    }
205
206    fn is_category_enabled(&self) -> bool {
207        unsafe { sys::trace_is_category_enabled(self.as_ptr()) }
208    }
209}
210
211#[cfg(fuchsia_api_level_at_least = "27")]
212impl CategoryString for &'static str {
213    fn register(&self, context: &Context) -> sys::trace_string_ref_t {
214        unsafe {
215            let mut self_ref = mem::MaybeUninit::<sys::trace_string_ref_t>::uninit();
216            sys::trace_context_register_bytestring(
217                context.raw,
218                self.as_ptr().cast::<libc::c_char>(),
219                self.len(),
220                self_ref.as_mut_ptr(),
221            );
222            self_ref.assume_init()
223        }
224    }
225
226    fn acquire_context(&self) -> Option<TraceCategoryContext> {
227        unsafe {
228            let mut category_ref = mem::MaybeUninit::<sys::trace_string_ref_t>::uninit();
229            let raw = sys::trace_acquire_context_for_category_bytestring(
230                self.as_ptr(),
231                self.len(),
232                category_ref.as_mut_ptr(),
233            );
234            if raw != ptr::null() {
235                Some(TraceCategoryContext {
236                    context: Context { raw },
237                    category_ref: category_ref.assume_init(),
238                })
239            } else {
240                None
241            }
242        }
243    }
244
245    #[inline]
246    fn acquire_context_cached(&self, site: &sys::trace_site_t) -> Option<TraceCategoryContext> {
247        let current_state = site.load(Ordering::Relaxed);
248        // kSiteStateDisabled = 1, and the top bits are used for other tracking, so we use a mask
249        // instead of equality.
250        if (current_state & 1) != 0 {
251            return None;
252        }
253        unsafe {
254            // SAFETY: The call to `trace_acquire_context_for_category_bytestring_cached` is sound
255            // because all arguments are live and non-null. If this function returns a non-null
256            // pointer then it also guarantees that `category_ref` will have been initialized.
257            // Internally, it uses relaxed atomic semantics to load and store site.
258            let mut category_ref = mem::MaybeUninit::<sys::trace_string_ref_t>::uninit();
259            let raw = sys::trace_acquire_context_for_category_bytestring_cached(
260                self.as_ptr(),
261                self.len(),
262                site.as_ptr(),
263                category_ref.as_mut_ptr(),
264            );
265            if raw != ptr::null() {
266                Some(TraceCategoryContext {
267                    context: Context { raw },
268                    category_ref: category_ref.assume_init(),
269                })
270            } else {
271                None
272            }
273        }
274    }
275
276    fn is_category_enabled(&self) -> bool {
277        unsafe { sys::trace_is_category_bytestring_enabled(self.as_ptr(), self.len()) }
278    }
279}
280
281pub trait AlertString {
282    /// Sends an alert named by `self` to the provided context.
283    fn send_alert(&self, context: &Context);
284}
285
286impl AlertString for &CStr {
287    fn send_alert(&self, context: &Context) {
288        unsafe {
289            sys::trace_context_send_alert(context.raw, self.as_ptr());
290        }
291    }
292}
293
294#[cfg(fuchsia_api_level_at_least = "27")]
295impl AlertString for &str {
296    fn send_alert(&self, context: &Context) {
297        unsafe {
298            sys::trace_context_send_alert_bytestring(context.raw, self.as_ptr(), self.len());
299        }
300    }
301}
302
303#[cfg(fuchsia_api_level_at_least = "27")]
304impl AlertString for &String {
305    fn send_alert(&self, context: &Context) {
306        unsafe {
307            sys::trace_context_send_alert_bytestring(context.raw, self.as_ptr(), self.len());
308        }
309    }
310}
311
312pub trait AsTraceStrRef {
313    fn as_trace_str_ref(&self, context: &TraceCategoryContext) -> sys::trace_string_ref_t;
314}
315
316impl AsTraceStrRef for &'static CStr {
317    #[inline]
318    fn as_trace_str_ref(&self, context: &TraceCategoryContext) -> sys::trace_string_ref_t {
319        context.register_string_literal(*self)
320    }
321}
322
323// NOTE: Ideally we'd implement AsTraceStrRef for non-static &str using inline refs. There
324// isn't a good way to do this right now because trait specialization is unstable. Hopefully
325// supporting inline refs for &String suffices in the meantime.
326impl AsTraceStrRef for &'static str {
327    #[inline]
328    fn as_trace_str_ref(&self, context: &TraceCategoryContext) -> sys::trace_string_ref_t {
329        context.register_str(self)
330    }
331}
332
333impl AsTraceStrRef for String {
334    #[inline]
335    fn as_trace_str_ref(&self, _context: &TraceCategoryContext) -> sys::trace_string_ref_t {
336        trace_make_inline_string_ref(self.as_str())
337    }
338}
339
340impl AsTraceStrRef for std::borrow::Cow<'static, str> {
341    #[inline]
342    fn as_trace_str_ref(&self, context: &TraceCategoryContext) -> sys::trace_string_ref_t {
343        match self {
344            std::borrow::Cow::Borrowed(s) => s.as_trace_str_ref(context),
345            std::borrow::Cow::Owned(s) => s.as_trace_str_ref(context),
346        }
347    }
348}
349
350// This effectively makes deref coercion work for `as_trace_str_ref` calls.
351impl<T: AsTraceStrRef> AsTraceStrRef for &T {
352    #[inline]
353    fn as_trace_str_ref(&self, context: &TraceCategoryContext) -> sys::trace_string_ref_t {
354        (*self).as_trace_str_ref(context)
355    }
356}
357
358/// `Arg` holds an argument to a tracing function, which can be one of many types.
359#[repr(transparent)]
360pub struct Arg<'a>(sys::trace_arg_t, PhantomData<&'a ()>);
361
362/// A trait for types that can be the values of an argument set.
363///
364/// This trait is not implementable by users of the library.
365/// Users should instead use one of the common types which implements
366/// `ArgValue`, such as `i32`, `f64`, or `&str`.
367pub trait ArgValue {
368    fn of<'a>(key: &'a str, value: Self) -> Arg<'a>
369    where
370        Self: 'a;
371    fn of_registered<'a>(name_ref: sys::trace_string_ref_t, value: Self) -> Arg<'a>
372    where
373        Self: 'a;
374}
375
376// Implements `arg_from` for many types.
377// $valname is the name to which to bind the `Self` value in the $value expr
378// $ty is the type
379// $tag is the union tag indicating the variant of trace_arg_union_t being used
380// $value is the union value for that particular type
381macro_rules! arg_from {
382    ($valname:ident, $(($type:ty, $tag:expr, $value:expr))*) => {
383        $(
384            impl ArgValue for $type {
385                #[inline]
386                fn of<'a>(key: &'a str, $valname: Self) -> Arg<'a>
387                    where Self: 'a
388                {
389                    #[allow(unused)]
390                    let $valname = $valname;
391
392                    Arg(sys::trace_arg_t {
393                        name_ref: trace_make_inline_string_ref(key),
394                        value: sys::trace_arg_value_t {
395                            type_: $tag,
396                            value: $value,
397                        },
398                    }, PhantomData)
399                }
400                #[inline]
401                fn of_registered<'a>(name_ref: sys::trace_string_ref_t, $valname: Self) -> Arg<'a>
402                    where Self: 'a
403                {
404                    #[allow(unused)]
405                    let $valname = $valname;
406
407                    Arg(sys::trace_arg_t {
408                        name_ref,
409                        value: sys::trace_arg_value_t {
410                            type_: $tag,
411                            value: $value,
412                        },
413                    }, PhantomData)
414                }
415            }
416        )*
417    }
418}
419
420// Implement ArgFrom for a variety of types
421#[rustfmt::skip]
422arg_from!(val,
423    ((), sys::TRACE_ARG_NULL, sys::trace_arg_union_t { int32_value: 0 })
424    (bool, sys::TRACE_ARG_BOOL, sys::trace_arg_union_t { bool_value: val })
425    (i32, sys::TRACE_ARG_INT32, sys::trace_arg_union_t { int32_value: val })
426    (u32, sys::TRACE_ARG_UINT32, sys::trace_arg_union_t { uint32_value: val })
427    (i64, sys::TRACE_ARG_INT64, sys::trace_arg_union_t { int64_value: val })
428    (u64, sys::TRACE_ARG_UINT64, sys::trace_arg_union_t { uint64_value: val })
429    (isize, sys::TRACE_ARG_INT64, sys::trace_arg_union_t { int64_value: val as i64 })
430    (usize, sys::TRACE_ARG_UINT64, sys::trace_arg_union_t { uint64_value: val as u64 })
431    (f64, sys::TRACE_ARG_DOUBLE, sys::trace_arg_union_t { double_value: val })
432    (zx::Koid, sys::TRACE_ARG_KOID, sys::trace_arg_union_t { koid_value: val.raw_koid() })
433);
434
435impl<T> ArgValue for *const T {
436    #[inline]
437    fn of<'a>(key: &'a str, val: Self) -> Arg<'a>
438    where
439        Self: 'a,
440    {
441        Arg(
442            sys::trace_arg_t {
443                name_ref: trace_make_inline_string_ref(key),
444                value: sys::trace_arg_value_t {
445                    type_: sys::TRACE_ARG_POINTER,
446                    value: sys::trace_arg_union_t { pointer_value: val as usize },
447                },
448            },
449            PhantomData,
450        )
451    }
452    #[inline]
453    fn of_registered<'a>(name_ref: sys::trace_string_ref_t, val: Self) -> Arg<'a>
454    where
455        Self: 'a,
456    {
457        Arg(
458            sys::trace_arg_t {
459                name_ref,
460                value: sys::trace_arg_value_t {
461                    type_: sys::TRACE_ARG_POINTER,
462                    value: sys::trace_arg_union_t { pointer_value: val as usize },
463                },
464            },
465            PhantomData,
466        )
467    }
468}
469
470impl<T> ArgValue for *mut T {
471    #[inline]
472    fn of<'a>(key: &'a str, val: Self) -> Arg<'a>
473    where
474        Self: 'a,
475    {
476        Arg(
477            sys::trace_arg_t {
478                name_ref: trace_make_inline_string_ref(key),
479                value: sys::trace_arg_value_t {
480                    type_: sys::TRACE_ARG_POINTER,
481                    value: sys::trace_arg_union_t { pointer_value: val as usize },
482                },
483            },
484            PhantomData,
485        )
486    }
487    #[inline]
488    fn of_registered<'a>(name_ref: sys::trace_string_ref_t, val: Self) -> Arg<'a>
489    where
490        Self: 'a,
491    {
492        Arg(
493            sys::trace_arg_t {
494                name_ref,
495                value: sys::trace_arg_value_t {
496                    type_: sys::TRACE_ARG_POINTER,
497                    value: sys::trace_arg_union_t { pointer_value: val as usize },
498                },
499            },
500            PhantomData,
501        )
502    }
503}
504
505impl<'a> ArgValue for &'a str {
506    #[inline]
507    fn of<'b>(key: &'b str, val: Self) -> Arg<'b>
508    where
509        Self: 'b,
510    {
511        Arg(
512            sys::trace_arg_t {
513                name_ref: trace_make_inline_string_ref(key),
514                value: sys::trace_arg_value_t {
515                    type_: sys::TRACE_ARG_STRING,
516                    value: sys::trace_arg_union_t {
517                        string_value_ref: trace_make_inline_string_ref(val),
518                    },
519                },
520            },
521            PhantomData,
522        )
523    }
524    #[inline]
525    fn of_registered<'b>(name_ref: sys::trace_string_ref_t, val: Self) -> Arg<'b>
526    where
527        Self: 'b,
528    {
529        Arg(
530            sys::trace_arg_t {
531                name_ref,
532                value: sys::trace_arg_value_t {
533                    type_: sys::TRACE_ARG_STRING,
534                    value: sys::trace_arg_union_t {
535                        string_value_ref: trace_make_inline_string_ref(val),
536                    },
537                },
538            },
539            PhantomData,
540        )
541    }
542}
543
544impl<'a> ArgValue for sys::trace_string_ref_t {
545    #[inline]
546    fn of<'b>(key: &'b str, val: Self) -> Arg<'b>
547    where
548        Self: 'b,
549    {
550        Arg(
551            sys::trace_arg_t {
552                name_ref: trace_make_inline_string_ref(key),
553                value: sys::trace_arg_value_t {
554                    type_: sys::TRACE_ARG_STRING,
555                    value: sys::trace_arg_union_t { string_value_ref: val },
556                },
557            },
558            PhantomData,
559        )
560    }
561    #[inline]
562    fn of_registered<'b>(name_ref: sys::trace_string_ref_t, val: Self) -> Arg<'b>
563    where
564        Self: 'b,
565    {
566        Arg(
567            sys::trace_arg_t {
568                name_ref,
569                value: sys::trace_arg_value_t {
570                    type_: sys::TRACE_ARG_STRING,
571                    value: sys::trace_arg_union_t { string_value_ref: val },
572                },
573            },
574            PhantomData,
575        )
576    }
577}
578
579/// Convenience macro for the `instant` function.
580///
581/// Example:
582///
583/// ```rust
584/// instant!("foo", "bar", Scope::Process, "x" => 5, "y" => "boo");
585/// ```
586///
587/// is equivalent to
588///
589/// ```rust
590/// instant("foo", "bar", Scope::Process,
591///     &[ArgValue::of("x", 5), ArgValue::of("y", "boo")]);
592/// ```
593/// or
594/// ```rust
595/// const FOO: &'static str = "foo";
596/// const BAR: &'static str = "bar";
597/// instant(FOO, BAR, Scope::Process,
598///     &[ArgValue::of("x", 5), ArgValue::of("y", "boo")]);
599/// ```
600#[macro_export]
601macro_rules! instant {
602    ($category:expr, $name:expr, $scope:expr $(, $key:expr => $val:expr)*) => {
603        {
604            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
605            use $crate::AsTraceStrRef;
606            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
607                $crate::instant(&context, $name, $scope, &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]);
608            }
609        }
610    }
611}
612
613/// Writes an instant event representing a single moment in time.
614/// The number of `args` must not be greater than 15.
615#[inline]
616pub fn instant<S: AsTraceStrRef>(
617    context: &TraceCategoryContext,
618    name: S,
619    scope: Scope,
620    args: &[Arg<'_>],
621) {
622    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
623
624    let name_ref = name.as_trace_str_ref(context);
625    context.write_instant(name_ref, scope, args);
626}
627
628/// Convenience macro for the `alert` function.
629///
630/// Example:
631///
632/// ```rust
633/// alert!("foo", "bar");
634/// ```
635///
636/// is equivalent to
637///
638/// ```rust
639/// alert("foo", "bar");
640/// ```
641#[macro_export]
642macro_rules! alert {
643    ($category:expr, $name:expr) => {
644        $crate::alert($category, $name)
645    };
646}
647
648/// Sends an alert, which can be mapped to an action.
649pub fn alert<C: CategoryString, S: AlertString>(category: C, name: S) {
650    if let Some(context) = category.acquire_context() {
651        name.send_alert(&context.context);
652    }
653}
654
655/// Convenience macro for the `counter` function.
656///
657/// Example:
658///
659/// ```rust
660/// let id = 555;
661/// counter!("foo", "bar", id, "x" => 5, "y" => 10);
662/// ```
663///
664/// is equivalent to
665///
666/// ```rust
667/// let id = 555;
668/// counter("foo", "bar", id,
669///     &[ArgValue::of("x", 5), ArgValue::of("y", 10)]);
670/// ```
671#[macro_export]
672macro_rules! counter {
673    ($category:expr, $name:expr, $counter_id:expr $(, $key:expr => $val:expr)*) => {
674        {
675            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
676            use $crate::AsTraceStrRef;
677            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
678                $crate::counter(&context, $name, $counter_id,
679                    &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
680            }
681        }
682    }
683}
684
685/// Writes a counter event with the specified id.
686///
687/// The arguments to this event are numeric samples and are typically
688/// represented by the visualizer as a stacked area chart. The id serves to
689/// distinguish multiple instances of counters which share the same category
690/// and name within the same process.
691///
692/// 1 to 15 numeric arguments can be associated with an event, each of which is
693/// interpreted as a distinct time series.
694pub fn counter<S: AsTraceStrRef>(
695    context: &TraceCategoryContext,
696    name: S,
697    counter_id: u64,
698    args: &[Arg<'_>],
699) {
700    assert!(args.len() >= 1, "trace counter args must include at least one numeric argument");
701    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
702
703    let name_ref = name.as_trace_str_ref(context);
704    context.write_counter(name_ref, counter_id, args);
705}
706
707/// The scope of a duration event, returned by the `duration` function and the `duration!` macro.
708/// The duration will be `end'ed` when this object is dropped.
709#[must_use = "DurationScope must be `end`ed to be recorded"]
710pub struct DurationScope<'a, C: CategoryString, S: AsTraceStrRef> {
711    category: C,
712    name: S,
713    args: &'a [Arg<'a>],
714    start_time: zx::BootTicks,
715}
716
717impl<'a, C: CategoryString, S: AsTraceStrRef> DurationScope<'a, C, S> {
718    /// Starts a new duration scope that starts now and will be end'ed when
719    /// this object is dropped.
720    pub fn begin(category: C, name: S, args: &'a [Arg<'_>]) -> Self {
721        let start_time = zx::BootTicks::get();
722        Self { category, name, args, start_time }
723    }
724}
725
726impl<'a, C: CategoryString, S: AsTraceStrRef> Drop for DurationScope<'a, C, S> {
727    fn drop(&mut self) {
728        if let Some(context) = TraceCategoryContext::acquire(self.category) {
729            let name_ref = self.name.as_trace_str_ref(&context);
730            context.write_duration(name_ref, self.start_time, self.args);
731        }
732    }
733}
734
735/// Write a "duration complete" record representing both the beginning and end of a duration.
736pub fn complete_duration<C: CategoryString, S: AsTraceStrRef>(
737    category: C,
738    name: S,
739    start_time: zx::BootTicks,
740    args: &[Arg<'_>],
741) {
742    if let Some(context) = TraceCategoryContext::acquire(category) {
743        let name_ref = name.as_trace_str_ref(&context);
744        context.write_duration(name_ref, start_time, args);
745    }
746}
747
748/// Convenience macro for the `duration` function that can be used to trace
749/// the duration of a scope. If you need finer grained control over when a
750/// duration starts and stops, see `duration_begin` and `duration_end`.
751///
752/// Example:
753///
754/// ```rust
755///   {
756///       duration!("foo", "bar", "x" => 5, "y" => 10);
757///       ...
758///       ...
759///       // event will be recorded on drop.
760///   }
761/// ```
762///
763/// is equivalent to
764///
765/// ```rust
766///   {
767///       let mut args;
768///       let _scope =  {
769///           static CACHE: trace_site_t = trace_site_t::new(0);
770///           if let Some(_context) = TraceCategoryContext::acquire_cached("foo", &CACHE) {
771///               args = [ArgValue::of("x", 5), ArgValue::of("y", 10)];
772///               Some($crate::duration("foo", "bar", &args))
773///           } else {
774///               None
775///           }
776///       };
777///       ...
778///       ...
779///       // event will be recorded on drop.
780///   }
781/// ```
782#[macro_export]
783macro_rules! duration {
784    ($category:expr, $name:expr $(, $key:expr => $val:expr)* $(,)?) => {
785        let mut args;
786        let _scope =  {
787            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
788            // NB: It is intentional that _context is not used here.  This cached context is used to
789            // elide the expensive context lookup if tracing is disabled.  When the duration ends,
790            // it will do a second lookup, but this cost is dwarfed by the cost of writing the trace
791            // event, so this second lookup is irrelevant.  Retaining the context for the lifetime
792            // of the DurationScope to avoid this second lookup would prevent the trace buffers from
793            // flushing until the DurationScope is dropped.
794            use $crate::AsTraceStrRef;
795            if let Some(context) =
796                    $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
797                args = [$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*];
798                Some($crate::duration($category, $name, &args))
799            } else {
800                None
801            }
802        };
803    }
804}
805
806/// Writes a duration event which ends when the current scope exits, or the
807/// `end` method is manually called.
808///
809/// Durations describe work which is happening synchronously on one thread.
810/// They can be nested to represent a control flow stack.
811///
812/// 0 to 15 arguments can be associated with the event, each of which is used
813/// to annotate the duration with additional information.
814///
815/// NOTE: For performance reasons, it is advisable to create a cached context scope, which will
816/// avoid expensive lookups when tracing is disabled.  See the example in the `duration!` macro.
817pub fn duration<'a, C: CategoryString, S: AsTraceStrRef>(
818    category: C,
819    name: S,
820    args: &'a [Arg<'_>],
821) -> DurationScope<'a, C, S> {
822    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
823    DurationScope::begin(category, name, args)
824}
825
826/// Convenience macro for the `duration_begin` function.
827///
828/// Examples:
829///
830/// ```rust
831/// duration_begin!("foo", "bar", "x" => 5, "y" => "boo");
832/// ```
833///
834/// ```rust
835/// const FOO: &'static str = "foo";
836/// const BAR: &'static str = "bar";
837/// duration_begin!(FOO, BAR, "x" => 5, "y" => "boo");
838/// ```
839#[macro_export]
840macro_rules! duration_begin {
841    ($category:expr, $name:expr $(, $key:expr => $val:expr)* $(,)?) => {
842        {
843            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
844            use $crate::AsTraceStrRef;
845            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
846                $crate::duration_begin(&context, $name,
847                                       &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
848            }
849        }
850    };
851}
852
853/// Convenience macro for the `vthread_duration_begin` function, which writes events to a track
854/// specified by `vthread_name` and `vthread_id`.
855///
856/// Examples:
857///
858/// ```rust
859/// vthread_duration_begin!("foo", "bar", "my_vthread", 123, "x" => 5, "y" => "boo");
860/// ```
861///
862/// ```rust
863/// const FOO: &'static str = "foo";
864/// const BAR: &'static str = "bar";
865/// const VTHREAD_NAME: &'static str = "my_vthread";
866/// vthread_duration_begin!(FOO, BAR, VTHREAD_NAME, 123, "x" => 5, "y" => "boo");
867/// ```
868#[cfg(fuchsia_api_level_at_least = "31")]
869#[macro_export]
870macro_rules! vthread_duration_begin {
871    ($category:expr, $name:expr, $vthread_name:expr, $vthread_id:expr $(, $key:expr => $val:expr)* $(,)?) => {
872        {
873            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
874            let vthread = $crate::VThread::new($vthread_name, $vthread_id);
875            use $crate::AsTraceStrRef;
876            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
877                $crate::vthread_duration_begin(
878                    &context,
879                    $name,
880                    &vthread,
881                    &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]
882                )
883            }
884        }
885    };
886}
887
888/// Convenience macro for the `duration_end` function.
889///
890/// Examples:
891///
892/// ```rust
893/// duration_end!("foo", "bar", "x" => 5, "y" => "boo");
894/// ```
895///
896/// ```rust
897/// const FOO: &'static str = "foo";
898/// const BAR: &'static str = "bar";
899/// duration_end!(FOO, BAR, "x" => 5, "y" => "boo");
900/// ```
901#[macro_export]
902macro_rules! duration_end {
903    ($category:expr, $name:expr $(, $key:expr => $val:expr)* $(,)?) => {
904        {
905            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
906            use $crate::AsTraceStrRef;
907            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
908                $crate::duration_end(&context, $name, &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
909            }
910        }
911    };
912}
913
914/// Convenience function for the `vthread_duration_end` function, which writes events to a track
915/// specified by `vthread_name` and `vthread_id`.
916///
917/// Examples:
918///
919/// ```rust
920/// vthread_duration_end!("foo", "bar", "my_vthread", 123, "x" => 5, "y" => "boo")
921/// ```
922///
923/// ```rust
924/// const FOO: &'static str = "foo";
925/// const BAR: &'static str = "bar";
926/// const VTHREAD_NAME: &'static str = "my_vthread";
927/// vthread_duration_end!(FOO, BAR, VTHREAD_NAME, 123, "x" => 5, "y" => "boo");
928/// ```
929#[cfg(fuchsia_api_level_at_least = "31")]
930#[macro_export]
931macro_rules! vthread_duration_end {
932    ($category:expr, $name:expr, $vthread_name:expr, $vthread_id:expr $(, $key:expr => $val:expr)* $(,)?) => {
933        {
934            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
935            let vthread = $crate::VThread::new($vthread_name, $vthread_id);
936            use $crate::AsTraceStrRef;
937            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
938                $crate::vthread_duration_end(
939                    &context,
940                    $name,
941                    &vthread,
942                    &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]
943                )
944            }
945        }
946    };
947}
948
949/// Writes an instant event to a custom track.
950///
951/// NOTE: This macro requires API level 31 or higher because the underlying virtual thread
952/// (`VThread`) support was stabilized and introduced in Fuchsia API level 31.
953#[cfg(fuchsia_api_level_at_least = "31")]
954#[macro_export]
955macro_rules! track_instant {
956    ($category:expr, $track:expr, $name:expr $(, $key:expr => $val:expr)* $(,)?) => {
957        {
958            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
959            use $crate::AsTraceStrRef;
960            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
961                $crate::track_instant(
962                    &context,
963                    $name,
964                    &$track,
965                    $crate::Scope::Thread,
966                    &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]
967                )
968            }
969        }
970    };
971}
972
973/// Writes a duration begin event to a custom track.
974#[cfg(fuchsia_api_level_at_least = "31")]
975#[macro_export]
976macro_rules! track_duration_begin {
977    ($category:expr, $track:expr, $name:expr $(, $key:expr => $val:expr)* $(,)?) => {
978        {
979            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
980            use $crate::AsTraceStrRef;
981            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
982                $crate::track_duration_begin(
983                    &context,
984                    $name,
985                    &$track,
986                    &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]
987                )
988            }
989        }
990    };
991}
992
993/// Writes a duration end event to a custom track.
994#[cfg(fuchsia_api_level_at_least = "31")]
995#[macro_export]
996macro_rules! track_duration_end {
997    ($category:expr, $track:expr, $name:expr $(, $key:expr => $val:expr)* $(,)?) => {
998        {
999            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1000            use $crate::AsTraceStrRef;
1001            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1002                $crate::track_duration_end(
1003                    &context,
1004                    $name,
1005                    &$track,
1006                    &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]
1007                )
1008            }
1009        }
1010    };
1011}
1012
1013/// Scoped duration for a custom track.
1014///
1015/// NOTE: This macro creates variables for `args` and `_scope` in the enclosing scope and relies on
1016/// Rust's lifetime rules to drop the duration guard to calculate the duration.
1017/// If you need to invoke `track_duration!` multiple times in the same function, you must wrap
1018/// each invocation in its own nested lexical block `{ ... }` to have separate duration events emitted.
1019#[cfg(fuchsia_api_level_at_least = "31")]
1020#[macro_export]
1021macro_rules! track_duration {
1022    ($category:expr, $track:expr, $name:expr $(, $key:expr => $val:expr)* $(,)?) => {
1023        // NB: `args` is declared uninitialized here and only initialized if the category is active.
1024        // This is a standard Rust macro pattern to ensure that `args` lives as long as the returned
1025        // `TrackDurationScope` (which borrows it), without requiring dynamic heap allocation.
1026        // Since `args` is never read if the category is disabled, this is completely safe and
1027        // will not trigger any uninitialized variable compiler errors.
1028        let mut args;
1029        let _scope = {
1030            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1031            use $crate::AsTraceStrRef;
1032            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1033                args = [$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*];
1034                Some($crate::track_duration($category, $name, &$track, &args))
1035            } else {
1036                None
1037            }
1038        };
1039    };
1040}
1041
1042/// Writes a duration begin event only.
1043/// This event must be matched by a duration end event with the same category and name.
1044///
1045/// Durations describe work which is happening synchronously on one thread.
1046/// They can be nested to represent a control flow stack.
1047///
1048/// 0 to 15 arguments can be associated with the event, each of which is used
1049/// to annotate the duration with additional information.  The arguments provided
1050/// to matching duration begin and duration end events are combined together in
1051/// the trace; it is not necessary to repeat them.
1052pub fn duration_begin<S: AsTraceStrRef>(context: &TraceCategoryContext, name: S, args: &[Arg<'_>]) {
1053    let ticks = zx::BootTicks::get();
1054    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1055
1056    let name_ref = name.as_trace_str_ref(&context);
1057    context.write_duration_begin(ticks, name_ref, args);
1058}
1059
1060#[cfg(fuchsia_api_level_at_least = "31")]
1061#[derive(Clone, Debug)]
1062pub struct VThread<S: AsTraceStrRef = &'static str> {
1063    name: S,
1064    id: sys::trace_vthread_id_t,
1065    process_koid: zx::sys::zx_koid_t,
1066}
1067
1068#[cfg(fuchsia_api_level_at_least = "31")]
1069impl<S: AsTraceStrRef> VThread<S> {
1070    pub fn new(name: S, id: sys::trace_vthread_id_t) -> Self {
1071        Self { name, id, process_koid: zx::sys::ZX_KOID_INVALID }
1072    }
1073
1074    pub fn new_with_process_koid(
1075        name: S,
1076        id: sys::trace_vthread_id_t,
1077        process_koid: zx::sys::zx_koid_t,
1078    ) -> Self {
1079        Self { name, id, process_koid }
1080    }
1081}
1082
1083/// Like `duration_begin`, but writes the event to a vthread track.
1084#[cfg(fuchsia_api_level_at_least = "31")]
1085pub fn vthread_duration_begin<S1: AsTraceStrRef, S2: AsTraceStrRef>(
1086    context: &TraceCategoryContext,
1087    name: S1,
1088    vthread: &VThread<S2>,
1089    args: &[Arg<'_>],
1090) {
1091    let ticks = zx::BootTicks::get();
1092    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1093
1094    let name_ref = name.as_trace_str_ref(context);
1095    context.write_vthread_duration_begin(ticks, name_ref, vthread, args);
1096}
1097
1098/// Writes a duration end event only.
1099///
1100/// Durations describe work which is happening synchronously on one thread.
1101/// They can be nested to represent a control flow stack.
1102///
1103/// 0 to 15 arguments can be associated with the event, each of which is used
1104/// to annotate the duration with additional information.  The arguments provided
1105/// to matching duration begin and duration end events are combined together in
1106/// the trace; it is not necessary to repeat them.
1107pub fn duration_end<S: AsTraceStrRef>(context: &TraceCategoryContext, name: S, args: &[Arg<'_>]) {
1108    let ticks = zx::BootTicks::get();
1109    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1110
1111    let name_ref = name.as_trace_str_ref(&context);
1112    context.write_duration_end(ticks, name_ref, args);
1113}
1114
1115/// Like `duration_end`, but writes the event to a vthread track.
1116#[cfg(fuchsia_api_level_at_least = "31")]
1117pub fn vthread_duration_end<S1: AsTraceStrRef, S2: AsTraceStrRef>(
1118    context: &TraceCategoryContext,
1119    name: S1,
1120    vthread: &VThread<S2>,
1121    args: &[Arg<'_>],
1122) {
1123    let ticks = zx::BootTicks::get();
1124    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1125
1126    let name_ref = name.as_trace_str_ref(context);
1127    context.write_vthread_duration_end(ticks, name_ref, vthread, args);
1128}
1129
1130/// Like `instant`, but writes the event to a vthread track.
1131#[cfg(fuchsia_api_level_at_least = "31")]
1132pub fn vthread_instant<S1: AsTraceStrRef, S2: AsTraceStrRef>(
1133    context: &TraceCategoryContext,
1134    name: S1,
1135    vthread: &VThread<S2>,
1136    scope: Scope,
1137    args: &[Arg<'_>],
1138) {
1139    let ticks = zx::BootTicks::get();
1140    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1141
1142    let name_ref = name.as_trace_str_ref(context);
1143    context.write_vthread_instant(ticks, name_ref, vthread, scope, args);
1144}
1145
1146/// Represents a custom track for grouping related trace events (e.g., state machines, status tracks).
1147///
1148/// Under the hood, this is backed by a virtual thread (`VThread`). A virtual thread acts like
1149/// a physical thread to the visualizer but is completely managed in userspace.
1150///
1151/// Encapsulating `VThread` here with a static string generic guarantees that the track name has
1152/// static lifetime, which prevents memory safety issues and makes registration cheap.
1153#[cfg(fuchsia_api_level_at_least = "31")]
1154#[derive(Clone, Debug)]
1155pub struct Track {
1156    pub(crate) vthread: VThread<&'static str>,
1157}
1158
1159#[cfg(fuchsia_api_level_at_least = "31")]
1160impl Track {
1161    /// Creates a new custom track with the given name.
1162    ///
1163    /// The track is automatically grouped under the current process.
1164    ///
1165    /// We use `Id::new()` to generate the underlying `VThread` ID. This is crucial because
1166    /// virtual threads generate artificial KOIDs based on their ID. Using a random ID minimizes
1167    /// the risk of cross-process fake KOID collisions in the visualizer.
1168    pub fn new(name: &'static str) -> Self {
1169        let id = Id::new();
1170        let process_koid = fuchsia_runtime::process_self()
1171            .koid()
1172            .map(|k| k.raw_koid())
1173            .unwrap_or(zx::sys::ZX_KOID_INVALID);
1174        Self { vthread: VThread::new_with_process_koid(name, id.into(), process_koid) }
1175    }
1176}
1177
1178/// Writes an instant event to a custom track.
1179///
1180/// NOTE: Instant events emitted to a custom track should generally be emitted with
1181/// `Scope::Thread` to guarantee that the Perfetto UI renders them directly on the custom track
1182/// rather than process-wide.
1183#[cfg(fuchsia_api_level_at_least = "31")]
1184pub fn track_instant<S1: AsTraceStrRef>(
1185    context: &TraceCategoryContext,
1186    name: S1,
1187    track: &Track,
1188    scope: Scope,
1189    args: &[Arg<'_>],
1190) {
1191    vthread_instant(context, name, &track.vthread, scope, args);
1192}
1193
1194/// Writes a duration begin event to a custom track.
1195#[cfg(fuchsia_api_level_at_least = "31")]
1196pub fn track_duration_begin<S1: AsTraceStrRef>(
1197    context: &TraceCategoryContext,
1198    name: S1,
1199    track: &Track,
1200    args: &[Arg<'_>],
1201) {
1202    vthread_duration_begin(context, name, &track.vthread, args);
1203}
1204
1205/// Writes a duration end event to a custom track.
1206#[cfg(fuchsia_api_level_at_least = "31")]
1207pub fn track_duration_end<S1: AsTraceStrRef>(
1208    context: &TraceCategoryContext,
1209    name: S1,
1210    track: &Track,
1211    args: &[Arg<'_>],
1212) {
1213    vthread_duration_end(context, name, &track.vthread, args);
1214}
1215
1216/// Scoped duration guard for a custom track. The duration ends and the trace record is written
1217/// when the guard is dropped.
1218///
1219/// Under the hood, when the guard is dropped, it writes a single, complete `DurationComplete`
1220/// event to the track backing virtual thread, which contains both the start time (stored in the
1221/// guard) and the end time (determined at drop). This is highly efficient as it only writes a
1222/// single record to the trace buffer.
1223#[cfg(fuchsia_api_level_at_least = "31")]
1224#[must_use = "TrackDurationScope must be held to be recorded"]
1225pub struct TrackDurationScope<'a, C: CategoryString, S: AsTraceStrRef> {
1226    category: C,
1227    name: S,
1228    track: &'a Track,
1229    args: &'a [Arg<'a>],
1230    start_time: zx::BootTicks,
1231}
1232
1233#[cfg(fuchsia_api_level_at_least = "31")]
1234impl<'a, C: CategoryString, S: AsTraceStrRef> TrackDurationScope<'a, C, S> {
1235    /// Starts a new duration scope that starts now and will be end'ed when dropped.
1236    pub fn begin(category: C, name: S, track: &'a Track, args: &'a [Arg<'_>]) -> Self {
1237        let start_time = zx::BootTicks::get();
1238        Self { category, name, track, args, start_time }
1239    }
1240}
1241
1242#[cfg(fuchsia_api_level_at_least = "31")]
1243impl<'a, C: CategoryString, S: AsTraceStrRef> Drop for TrackDurationScope<'a, C, S> {
1244    fn drop(&mut self) {
1245        if let Some(context) = TraceCategoryContext::acquire(self.category) {
1246            let name_ref = self.name.as_trace_str_ref(&context);
1247            context.write_vthread_duration(
1248                self.start_time,
1249                zx::BootTicks::get(),
1250                name_ref,
1251                &self.track.vthread,
1252                self.args,
1253            );
1254        }
1255    }
1256}
1257
1258/// Writes a duration event to a custom track which ends when the returned guard is dropped.
1259///
1260/// 0 to 15 arguments can be associated with the event, each of which is used to annotate
1261/// the duration with additional information.
1262#[cfg(fuchsia_api_level_at_least = "31")]
1263pub fn track_duration<'a, C: CategoryString, S: AsTraceStrRef>(
1264    category: C,
1265    name: S,
1266    track: &'a Track,
1267    args: &'a [Arg<'_>],
1268) -> TrackDurationScope<'a, C, S> {
1269    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1270    TrackDurationScope::begin(category, name, track, args)
1271}
1272
1273/// AsyncScope maintains state around the context of async events generated via the
1274/// async_enter! macro.
1275#[must_use = "emits an end event when dropped, so if dropped immediately creates an essentially \
1276              zero length duration that should just be an instant instead"]
1277pub struct AsyncScope<C: CategoryString = &'static CStr, S: AsTraceStrRef = &'static CStr> {
1278    // AsyncScope::end uses std::mem::forget to bypass AsyncScope's Drop impl, so if any fields
1279    // with Drop impls are added, AsyncScope::end should be updated.
1280    id: Id,
1281    category: C,
1282    name: S,
1283}
1284
1285impl<C: CategoryString, S: AsTraceStrRef> AsyncScope<C, S> {
1286    /// Starts a new async event scope, generating a begin event now, and ended when the
1287    /// object is dropped.
1288    pub fn begin(id: Id, category: C, name: S, args: &[Arg<'_>]) -> Self {
1289        async_begin(id, category, &name, args);
1290        Self { id, category, name }
1291    }
1292
1293    /// Manually end the async event scope with `args` instead of waiting until the guard is
1294    /// dropped (which would end the event scope with an empty `args`).
1295    pub fn end(self, args: &[Arg<'_>]) {
1296        async_end(self.id, self.category, &self.name, args);
1297        std::mem::forget(self);
1298    }
1299}
1300
1301impl<C: CategoryString, S: AsTraceStrRef> Drop for AsyncScope<C, S> {
1302    fn drop(&mut self) {
1303        // AsyncScope::end uses std::mem::forget to bypass this Drop impl (to avoid emitting
1304        // extraneous end events), so any logic added to this Drop impl (or any fields added to
1305        // AsyncScope that have Drop impls) should addressed (if necessary) in AsyncScope::end.
1306        async_end(self.id, self.category, &self.name, &[]);
1307    }
1308}
1309
1310/// Writes an async event which ends when the current scope exits, or the `end` method is is
1311/// manually called.
1312///
1313/// Async events describe concurrently-scheduled work items that may migrate between threads. They
1314/// may be nested by sharing id, and are otherwise differentiated by their id.
1315///
1316/// 0 to 15 arguments can be associated with the event, each of which is used to annotate the
1317/// duration with additional information.
1318pub fn async_enter<C: CategoryString, S: AsTraceStrRef>(
1319    id: Id,
1320    category: C,
1321    name: S,
1322    args: &[Arg<'_>],
1323) -> AsyncScope<C, S> {
1324    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1325    AsyncScope::begin(id, category, name, args)
1326}
1327
1328/// Convenience macro for the `async_enter` function, which can be used to trace the duration of a
1329/// scope containing async code. This macro returns the drop guard, which the caller may then
1330/// choose to manage.
1331///
1332/// Example:
1333///
1334/// ```rust
1335/// {
1336///     let id = Id::new();
1337///     let _guard = async_enter!(id, "foo", "bar", "x" => 5, "y" => 10);
1338///     ...
1339///     ...
1340///     // event recorded on drop
1341/// }
1342/// ```
1343///
1344/// is equivalent to
1345///
1346/// ```rust
1347/// {
1348///     let id = Id::new();
1349///     let _guard = AsyncScope::begin(id, "foo", "bar", &[ArgValue::of("x", 5),
1350///         ArgValue::of("y", 10)]);
1351///     ...
1352///     ...
1353///     // event recorded on drop
1354/// }
1355/// ```
1356///
1357/// Calls to async_enter! may be nested.
1358#[macro_export]
1359macro_rules! async_enter {
1360    ($id:expr, $category:expr, $name:expr $(, $key:expr => $val:expr)*) => {
1361        {
1362            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1363            use $crate::AsTraceStrRef;
1364            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1365                Some($crate::AsyncScope::begin($id, $category, $name, &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]))
1366            } else {
1367                None
1368            }
1369        }
1370    }
1371}
1372
1373/// Convenience macro for the `async_instant` function, which can be used to emit an async instant
1374/// event.
1375///
1376/// Example:
1377///
1378/// ```rust
1379/// {
1380///     let id = Id::new();
1381///     async_instant!(id, "foo", "bar", "x" => 5, "y" => 10);
1382/// }
1383/// ```
1384///
1385/// is equivalent to
1386///
1387/// ```rust
1388/// {
1389///     let id = Id::new();
1390///     async_instant(
1391///         id, "foo", "bar",
1392///         &[ArgValue::of("x", 5), ArgValue::of("y", 10)]
1393///     );
1394/// }
1395/// ```
1396#[macro_export]
1397macro_rules! async_instant {
1398    ($id:expr, $category:expr, $name:expr $(, $key:expr => $val:expr)*) => {
1399        {
1400            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1401            use $crate::AsTraceStrRef;
1402            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1403                $crate::async_instant($id, &context, $name, &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]);
1404            }
1405        }
1406    }
1407}
1408
1409/// Writes an async begin event. This event must be matched by an async end event with the same
1410/// id, category, and name. This function is intended to be called through use of the
1411/// `async_enter!` macro.
1412///
1413/// Async events describe concurrent work that may or may not migrate threads, or be otherwise
1414/// interleaved with other work on the same thread. They can be nested to represent a control
1415/// flow stack.
1416///
1417/// 0 to 15 arguments can be associated with the event, each of which is used to annotate the
1418/// async event with additional information. Arguments provided in matching async begin and end
1419/// events are combined together in the trace; it is not necessary to repeat them.
1420pub fn async_begin<C: CategoryString, S: AsTraceStrRef>(
1421    id: Id,
1422    category: C,
1423    name: S,
1424    args: &[Arg<'_>],
1425) {
1426    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1427
1428    if let Some(context) = TraceCategoryContext::acquire(category) {
1429        let name_ref = name.as_trace_str_ref(&context);
1430        context.write_async_begin(id, name_ref, args);
1431    }
1432}
1433
1434/// Writes an async end event. This event must be associated with a prior async begin event
1435/// with the same id, category, and name. This function is intended to be called implicitly
1436/// when the `AsyncScope` object created through use of the `async_enter!` macro is dropped.
1437///
1438/// Async events describe concurrent work that may or may not migrate threads, or be otherwise
1439/// interleaved with other work on the same thread. They can be nested to represent a control
1440/// flow stack.
1441///
1442/// 0 to 15 arguments can be associated with the event, each of which is used to annotate the
1443/// async event with additional information. Arguments provided in matching async begin and end
1444/// events are combined together in the trace; it is not necessary to repeat them.
1445pub fn async_end<C: CategoryString, S: AsTraceStrRef>(
1446    id: Id,
1447    category: C,
1448    name: S,
1449    args: &[Arg<'_>],
1450) {
1451    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1452
1453    if let Some(context) = TraceCategoryContext::acquire(category) {
1454        let name_ref = name.as_trace_str_ref(&context);
1455        context.write_async_end(id, name_ref, args);
1456    }
1457}
1458
1459/// Writes an async instant event with the specified id.
1460///
1461/// Asynchronous events describe work that is happening asynchronously and that
1462/// may span multiple threads.  Asynchronous events do not nest.  The id serves
1463/// to correlate the progress of distinct asynchronous operations that share
1464/// the same category and name within the same process.
1465///
1466/// 0 to 15 arguments can be associated with the event, each of which is used
1467/// to annotate the asynchronous operation with additional information.  The
1468/// arguments provided to matching async begin, async instant, and async end
1469/// events are combined together in the trace; it is not necessary to repeat them.
1470pub fn async_instant<S: AsTraceStrRef>(
1471    id: Id,
1472    context: &TraceCategoryContext,
1473    name: S,
1474    args: &[Arg<'_>],
1475) {
1476    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1477
1478    let name_ref = name.as_trace_str_ref(context);
1479    context.write_async_instant(id, name_ref, args);
1480}
1481
1482#[macro_export]
1483macro_rules! blob {
1484    ($category:expr, $name:expr, $bytes:expr $(, $key:expr => $val:expr)*) => {
1485        {
1486            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1487            use $crate::AsTraceStrRef;
1488            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1489                $crate::blob_fn(&context, $name, $bytes, &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
1490            }
1491        }
1492    }
1493}
1494pub fn blob_fn<S: AsTraceStrRef>(
1495    context: &TraceCategoryContext,
1496    name: S,
1497    bytes: &[u8],
1498    args: &[Arg<'_>],
1499) {
1500    let name_ref = name.as_trace_str_ref(context);
1501    context.write_blob(name_ref, bytes, args);
1502}
1503
1504/// Convenience macro for the `flow_begin` function.
1505///
1506/// Flow events must be enclosed in a duration event.
1507///
1508/// Example:
1509///
1510/// ```rust
1511/// let flow_id = 1234;
1512/// {
1513///     duration!("foo", "step_1");
1514///     flow_begin!("foo", "bar", flow_id, "x" => 5, "y" => "boo");
1515/// }
1516/// ```
1517///
1518/// ```rust
1519/// const FOO: &'static str = "foo";
1520/// const BAR: &'static str = "bar";
1521/// let flow_id = 1234;
1522/// {
1523///     duration!("foo", "step_1");
1524///     flow_begin!("foo", "bar", flow_id);
1525/// }
1526/// ```
1527#[macro_export]
1528macro_rules! flow_begin {
1529    ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)*) => {
1530        {
1531            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1532            use $crate::AsTraceStrRef;
1533            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1534                $crate::flow_begin(&context, $name, $flow_id,
1535                                   &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
1536            }
1537        }
1538    }
1539}
1540
1541/// Convenience macro for the `flow_step` function.
1542///
1543/// Flow events must be enclosed in a duration event.
1544///
1545/// Example:
1546///
1547/// ```rust
1548/// let flow_id = 1234;
1549/// {
1550///     duration!("foo", "step_2");
1551///     flow_step!("foo", "bar", flow_id, "x" => 5, "y" => "boo");
1552/// }
1553/// ```
1554///
1555/// ```rust
1556/// const FOO: &'static str = "foo";
1557/// const BAR: &'static str = "bar";
1558/// let flow_id = 1234;
1559/// {
1560///     duration!("foo", "step_2");
1561///     flow_step!("foo", "bar", flow_id);
1562/// }
1563/// ```
1564#[macro_export]
1565macro_rules! flow_step {
1566    ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)*) => {
1567        {
1568            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1569            use $crate::AsTraceStrRef;
1570            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1571                $crate::flow_step(&context, $name, $flow_id,
1572                                  &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
1573            }
1574        }
1575    }
1576}
1577
1578/// Convenience macro for the `flow_end` function.
1579///
1580/// Flow events must be enclosed in a duration event.
1581///
1582/// Example:
1583///
1584/// ```rust
1585/// let flow_id = 1234;
1586/// {
1587///     duration!("foo", "step_3");
1588///     flow_end!("foo", "bar", flow_id, "x" => 5, "y" => "boo");
1589/// }
1590/// ```
1591///
1592/// ```rust
1593/// const FOO: &'static str = "foo";
1594/// const BAR: &'static str = "bar";
1595/// let flow_id = 1234;
1596/// {
1597///     duration!("foo", "step_3");
1598///     flow_end!("foo", "bar", flow_id);
1599/// }
1600/// ```
1601#[macro_export]
1602macro_rules! flow_end {
1603    ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)*) => {
1604        {
1605            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1606            use $crate::AsTraceStrRef;
1607            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1608                $crate::flow_end(&context, $name, $flow_id,
1609                                 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
1610            }
1611        }
1612    }
1613}
1614
1615/// Writes a flow begin event with the specified id.
1616/// This event may be followed by flow steps events and must be matched by
1617/// a flow end event with the same category, name, and id.
1618///
1619/// Flow events describe control flow handoffs between threads or across processes.
1620/// They are typically represented as arrows in a visualizer.  Flow arrows are
1621/// from the end of the duration event which encloses the beginning of the flow
1622/// to the beginning of the duration event which encloses the next step or the
1623/// end of the flow.  The id serves to correlate flows across processes. Note
1624/// that flow IDs are global in Perfetto and not scoped to the category and
1625/// name; using non-unique IDs concurrently for different flows will lead to
1626/// ambiguous lines in the trace viewer.
1627///
1628/// This event must be enclosed in a duration event which represents where
1629/// the flow handoff occurs.
1630///
1631/// 0 to 15 arguments can be associated with the event, each of which is used
1632/// to annotate the flow with additional information.  The arguments provided
1633/// to matching flow begin, flow step, and flow end events are combined together
1634/// in the trace; it is not necessary to repeat them.
1635pub fn flow_begin<S: AsTraceStrRef>(
1636    context: &TraceCategoryContext,
1637    name: S,
1638    flow_id: Id,
1639    args: &[Arg<'_>],
1640) {
1641    let ticks = zx::BootTicks::get();
1642    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1643
1644    let name_ref = name.as_trace_str_ref(context);
1645    context.write_flow_begin(ticks, name_ref, flow_id, args);
1646}
1647
1648/// Writes a flow end event with the specified id.
1649///
1650/// Flow events describe control flow handoffs between threads or across processes.
1651/// They are typically represented as arrows in a visualizer.  Flow arrows are
1652/// from the end of the duration event which encloses the beginning of the flow
1653/// to the beginning of the duration event which encloses the next step or the
1654/// end of the flow.  The id serves to correlate flows across processes. Note
1655/// that flow IDs are global in Perfetto and not scoped to the category and
1656/// name; using non-unique IDs concurrently for different flows will lead to
1657/// ambiguous lines in the trace viewer.
1658///
1659/// This event must be enclosed in a duration event which represents where
1660/// the flow handoff occurs.
1661///
1662/// 0 to 15 arguments can be associated with the event, each of which is used
1663/// to annotate the flow with additional information.  The arguments provided
1664/// to matching flow begin, flow step, and flow end events are combined together
1665/// in the trace; it is not necessary to repeat them.
1666pub fn flow_end<S: AsTraceStrRef>(
1667    context: &TraceCategoryContext,
1668    name: S,
1669    flow_id: Id,
1670    args: &[Arg<'_>],
1671) {
1672    let ticks = zx::BootTicks::get();
1673    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1674
1675    let name_ref = name.as_trace_str_ref(context);
1676    context.write_flow_end(ticks, name_ref, flow_id, args);
1677}
1678
1679/// Writes a flow step event with the specified id.
1680///
1681/// Flow events describe control flow handoffs between threads or across processes.
1682/// They are typically represented as arrows in a visualizer.  Flow arrows are
1683/// from the end of the duration event which encloses the beginning of the flow
1684/// to the beginning of the duration event which encloses the next step or the
1685/// end of the flow.  The id serves to correlate flows across processes. Note
1686/// that flow IDs are global in Perfetto and not scoped to the category and
1687/// name; using non-unique IDs concurrently for different flows will lead to
1688/// ambiguous lines in the trace viewer.
1689///
1690/// This event must be enclosed in a duration event which represents where
1691/// the flow handoff occurs.
1692///
1693/// 0 to 15 arguments can be associated with the event, each of which is used
1694/// to annotate the flow with additional information.  The arguments provided
1695/// to matching flow begin, flow step, and flow end events are combined together
1696/// in the trace; it is not necessary to repeat them.
1697pub fn flow_step<S: AsTraceStrRef>(
1698    context: &TraceCategoryContext,
1699    name: S,
1700    flow_id: Id,
1701    args: &[Arg<'_>],
1702) {
1703    let ticks = zx::BootTicks::get();
1704    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1705
1706    let name_ref = name.as_trace_str_ref(context);
1707    context.write_flow_step(ticks, name_ref, flow_id, args);
1708}
1709
1710/// Convenience macro to emit the beginning of a flow attached to an instant event.
1711///
1712/// Flows must be attached to a duration event. This can be awkward when there isn't an obvious
1713/// duration event to attach to, or the relevant duration is very small, which makes visualizing
1714/// difficult. This emits a flow event wrapped in a self contained instant event that is also easy
1715/// to see in the tracing UI.
1716///
1717/// Example:
1718///
1719/// ```rust
1720/// let flow_id = 1234;
1721/// instaflow_begin!("category", "flow", "step", flow_id, "x" => 5, "y" => "boo");
1722/// ```
1723#[macro_export]
1724macro_rules! instaflow_begin {
1725    (
1726        $category:expr,
1727        $flow_name:expr,
1728        $step_name:expr,
1729        $flow_id:expr
1730        $(, $key:expr => $val:expr)*
1731    ) => {
1732        {
1733            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1734            use $crate::AsTraceStrRef;
1735            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1736                $crate::instaflow_begin(
1737                    &context,
1738                    $flow_name,
1739                    $step_name,
1740                    $flow_id,
1741                    &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*],
1742                )
1743            }
1744        }
1745    }
1746}
1747
1748/// Convenience macro to emit the end of a flow attached to an instant event.
1749///
1750/// Flows must be attached to a duration event. This can be awkward when there isn't an obvious
1751/// duration event to attach to, or the relevant duration is very small, which makes visualizing
1752/// difficult. This emits a flow event wrapped in a self contained instant event that is also easy
1753/// to see in the tracing UI.
1754///
1755/// Example:
1756///
1757/// ```rust
1758/// let flow_id = 1234;
1759/// instaflow_end!("category", "flow", "step", flow_id, "x" => 5, "y" => "boo");
1760/// ```
1761#[macro_export]
1762macro_rules! instaflow_end {
1763    (
1764        $category:expr,
1765        $flow_name:expr,
1766        $step_name:expr,
1767        $flow_id:expr
1768        $(, $key:expr => $val:expr)*
1769    ) => {
1770        {
1771            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1772            use $crate::AsTraceStrRef;
1773            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1774                $crate::instaflow_end(
1775                    &context,
1776                    $flow_name,
1777                    $step_name,
1778                    $flow_id,
1779                    &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*],
1780                )
1781            }
1782        }
1783    }
1784}
1785
1786/// Convenience macro to emit a step in a flow attached to an instant event.
1787///
1788/// Flows must be attached to a duration event. This can be awkward when there isn't an obvious
1789/// duration event to attach to, or the relevant duration is very small, which makes visualizing
1790/// difficult. This emits a flow event wrapped in a self contained instant event that is also easy
1791/// to see in the tracing UI.
1792///
1793/// Example:
1794///
1795/// ```rust
1796/// let flow_id = 1234;
1797/// instaflow_step!("category", "flow", "step", flow_id, "x" => 5, "y" => "boo");
1798/// ```
1799#[macro_export]
1800macro_rules! instaflow_step {
1801    (
1802        $category:expr,
1803        $flow_name:expr,
1804        $step_name:expr,
1805        $flow_id:expr
1806        $(, $key:expr => $val:expr)*
1807    ) => {
1808        {
1809            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1810            use $crate::AsTraceStrRef;
1811            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1812                $crate::instaflow_step(
1813                    &context,
1814                    $flow_name,
1815                    $step_name,
1816                    $flow_id,
1817                    &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*],
1818                )
1819            }
1820        }
1821    }
1822}
1823
1824/// Convenience function to emit the beginning of a flow attached to an instant event.
1825///
1826/// Flow events describe control flow handoffs between threads or across processes. They are
1827/// typically represented as arrows in a visualizer. Flow arrows are from the end of the duration
1828/// event which encloses the beginning of the flow to the beginning of the duration event which
1829/// encloses the next step or the end of the flow. The id serves to correlate flows across
1830/// processes. Note that flow IDs are global in Perfetto and not scoped to the category and
1831/// name; using non-unique IDs concurrently for different flows will lead to ambiguous lines
1832/// in the trace viewer.
1833///
1834/// 0 to 15 arguments can be associated with the event, each of which is used to annotate the flow
1835/// with additional information. The arguments provided to matching flow begin, flow step, and flow
1836/// end events are combined together in the trace; it is not necessary to repeat them.
1837pub fn instaflow_begin<S1: AsTraceStrRef, S2: AsTraceStrRef>(
1838    context: &TraceCategoryContext,
1839    flow_name: S1,
1840    step_name: S2,
1841    flow_id: Id,
1842    args: &[Arg<'_>],
1843) {
1844    let ticks = zx::BootTicks::get();
1845    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1846
1847    let flow_name_ref = flow_name.as_trace_str_ref(context);
1848    let step_name_ref = step_name.as_trace_str_ref(context);
1849
1850    context.write_duration_begin(ticks, step_name_ref, args);
1851    context.write_flow_begin(ticks, flow_name_ref, flow_id, args);
1852    context.write_duration_end(ticks, step_name_ref, args);
1853}
1854
1855/// Convenience function to the end of a flow attached to an instant event.
1856///
1857/// Flow events describe control flow handoffs between threads or across processes. They are
1858/// typically represented as arrows in a visualizer. Flow arrows are from the end of the duration
1859/// event which encloses the beginning of the flow to the beginning of the duration event which
1860/// encloses the next step or the end of the flow. The id serves to correlate flows across
1861/// processes. Note that flow IDs are global in Perfetto and not scoped to the category and
1862/// name; using non-unique IDs concurrently for different flows will lead to ambiguous lines
1863/// in the trace viewer.
1864///
1865/// 0 to 15 arguments can be associated with the event, each of which is used to annotate the flow
1866/// with additional information. The arguments provided to matching flow begin, flow step, and flow
1867/// end events are combined together in the trace; it is not necessary to repeat them.
1868pub fn instaflow_end<S1: AsTraceStrRef, S2: AsTraceStrRef>(
1869    context: &TraceCategoryContext,
1870    flow_name: S1,
1871    step_name: S2,
1872    flow_id: Id,
1873    args: &[Arg<'_>],
1874) {
1875    let ticks = zx::BootTicks::get();
1876    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1877
1878    let flow_name_ref = flow_name.as_trace_str_ref(context);
1879    let step_name_ref = step_name.as_trace_str_ref(context);
1880
1881    context.write_duration_begin(ticks, step_name_ref, args);
1882    context.write_flow_end(ticks, flow_name_ref, flow_id, args);
1883    context.write_duration_end(ticks, step_name_ref, args);
1884}
1885
1886/// Convenience function to emit a step in a flow attached to an instant event.
1887///
1888/// Flow events describe control flow handoffs between threads or across processes. They are
1889/// typically represented as arrows in a visualizer. Flow arrows are from the end of the duration
1890/// event which encloses the beginning of the flow to the beginning of the duration event which
1891/// encloses the next step or the end of the flow. The id serves to correlate flows across
1892/// processes. Note that flow IDs are global in Perfetto and not scoped to the category and
1893/// name; using non-unique IDs concurrently for different flows will lead to ambiguous lines
1894/// in the trace viewer.
1895///
1896/// 0 to 15 arguments can be associated with the event, each of which is used to annotate the flow
1897/// with additional information. The arguments provided to matching flow begin, flow step, and flow
1898/// end events are combined together in the trace; it is not necessary to repeat them.
1899pub fn instaflow_step<S1: AsTraceStrRef, S2: AsTraceStrRef>(
1900    context: &TraceCategoryContext,
1901    flow_name: S1,
1902    step_name: S2,
1903    flow_id: Id,
1904    args: &[Arg<'_>],
1905) {
1906    let ticks = zx::BootTicks::get();
1907    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1908
1909    let flow_name_ref = flow_name.as_trace_str_ref(context);
1910    let step_name_ref = step_name.as_trace_str_ref(context);
1911
1912    context.write_duration_begin(ticks, step_name_ref, args);
1913    context.write_flow_step(ticks, flow_name_ref, flow_id, args);
1914    context.write_duration_end(ticks, step_name_ref, args);
1915}
1916
1917// translated from trace-engine/types.h for inlining
1918const fn trace_make_empty_string_ref() -> sys::trace_string_ref_t {
1919    sys::trace_string_ref_t {
1920        encoded_value: sys::TRACE_ENCODED_STRING_REF_EMPTY,
1921        inline_string: ptr::null(),
1922    }
1923}
1924
1925#[inline]
1926fn trim_to_last_char_boundary(string: &str, max_len: usize) -> &[u8] {
1927    let mut len = string.len();
1928    if string.len() > max_len {
1929        // Trim to the last unicode character that fits within the max length.
1930        // We search for the last character boundary that is immediately followed
1931        // by another character boundary (end followed by beginning).
1932        len = max_len;
1933        while len > 0 {
1934            if string.is_char_boundary(len - 1) && string.is_char_boundary(len) {
1935                break;
1936            }
1937            len -= 1;
1938        }
1939    }
1940    &string.as_bytes()[0..len]
1941}
1942
1943// translated from trace-engine/types.h for inlining
1944// The resulting `trace_string_ref_t` only lives as long as the input `string`.
1945#[inline]
1946fn trace_make_inline_string_ref(string: &str) -> sys::trace_string_ref_t {
1947    let len = string.len() as u16;
1948    if len == 0 {
1949        return trace_make_empty_string_ref();
1950    }
1951
1952    let string = trim_to_last_char_boundary(string, sys::TRACE_ENCODED_STRING_REF_MAX_LENGTH);
1953
1954    sys::trace_string_ref_t {
1955        encoded_value: sys::TRACE_ENCODED_STRING_REF_INLINE_FLAG | len,
1956        inline_string: string.as_ptr() as *const libc::c_char,
1957    }
1958}
1959
1960/// RAII wrapper for a trace context for a specific category.
1961pub struct TraceCategoryContext {
1962    context: Context,
1963    category_ref: sys::trace_string_ref_t,
1964}
1965
1966impl TraceCategoryContext {
1967    #[inline]
1968    pub fn acquire_cached<C: CategoryString>(
1969        category: C,
1970        site: &sys::trace_site_t,
1971    ) -> Option<TraceCategoryContext> {
1972        category.acquire_context_cached(site)
1973    }
1974
1975    pub fn acquire<C: CategoryString>(category: C) -> Option<TraceCategoryContext> {
1976        category.acquire_context()
1977    }
1978
1979    #[inline]
1980    pub fn register_string_literal<T: CategoryString>(&self, name: T) -> sys::trace_string_ref_t {
1981        name.register(&self.context)
1982    }
1983
1984    #[inline]
1985    #[cfg(fuchsia_api_level_at_least = "27")]
1986    pub fn register_str(&self, name: &'static str) -> sys::trace_string_ref_t {
1987        unsafe {
1988            let mut name_ref = mem::MaybeUninit::<sys::trace_string_ref_t>::uninit();
1989            sys::trace_context_register_bytestring(
1990                self.context.raw,
1991                name.as_ptr().cast::<libc::c_char>(),
1992                name.len(),
1993                name_ref.as_mut_ptr(),
1994            );
1995            name_ref.assume_init()
1996        }
1997    }
1998    #[inline]
1999    #[cfg(not(fuchsia_api_level_at_least = "27"))]
2000    pub fn register_str(&self, name: &'static str) -> sys::trace_string_ref_t {
2001        trace_make_inline_string_ref(name)
2002    }
2003
2004    #[inline]
2005    fn register_current_thread(&self) -> sys::trace_thread_ref_t {
2006        unsafe {
2007            let mut thread_ref = mem::MaybeUninit::<sys::trace_thread_ref_t>::uninit();
2008            sys::trace_context_register_current_thread(self.context.raw, thread_ref.as_mut_ptr());
2009            thread_ref.assume_init()
2010        }
2011    }
2012
2013    #[cfg(fuchsia_api_level_at_least = "31")]
2014    #[inline]
2015    fn register_vthread<S: AsTraceStrRef>(
2016        &self,
2017        name: &S,
2018        id: sys::trace_vthread_id_t,
2019        process_koid: zx::sys::zx_koid_t,
2020    ) -> sys::trace_thread_ref_t {
2021        let name_ref = name.as_trace_str_ref(self);
2022        unsafe {
2023            let mut thread_ref = mem::MaybeUninit::<sys::trace_thread_ref_t>::uninit();
2024            sys::trace_context_register_vthread_by_ref(
2025                self.context.raw,
2026                process_koid,
2027                &name_ref,
2028                id,
2029                thread_ref.as_mut_ptr(),
2030            );
2031            thread_ref.assume_init()
2032        }
2033    }
2034
2035    #[inline]
2036    pub fn write_instant(&self, name_ref: sys::trace_string_ref_t, scope: Scope, args: &[Arg<'_>]) {
2037        let ticks = zx::BootTicks::get();
2038        let thread_ref = self.register_current_thread();
2039        unsafe {
2040            sys::trace_context_write_instant_event_record(
2041                self.context.raw,
2042                ticks.into_raw(),
2043                &thread_ref,
2044                &self.category_ref,
2045                &name_ref,
2046                scope.into_raw(),
2047                args.as_ptr() as *const sys::trace_arg_t,
2048                args.len(),
2049            );
2050        }
2051    }
2052
2053    pub fn write_instant_with_inline_name(&self, name: &str, scope: Scope, args: &[Arg<'_>]) {
2054        let name_ref = trace_make_inline_string_ref(name);
2055        self.write_instant(name_ref, scope, args)
2056    }
2057
2058    fn write_counter(&self, name_ref: sys::trace_string_ref_t, counter_id: u64, args: &[Arg<'_>]) {
2059        let ticks = zx::BootTicks::get();
2060        let thread_ref = self.register_current_thread();
2061        unsafe {
2062            sys::trace_context_write_counter_event_record(
2063                self.context.raw,
2064                ticks.into_raw(),
2065                &thread_ref,
2066                &self.category_ref,
2067                &name_ref,
2068                counter_id,
2069                args.as_ptr() as *const sys::trace_arg_t,
2070                args.len(),
2071            );
2072        }
2073    }
2074
2075    pub fn write_counter_with_inline_name(&self, name: &str, counter_id: u64, args: &[Arg<'_>]) {
2076        let name_ref = trace_make_inline_string_ref(name);
2077        self.write_counter(name_ref, counter_id, args);
2078    }
2079
2080    fn write_duration(
2081        &self,
2082        name_ref: sys::trace_string_ref_t,
2083        start_time: zx::BootTicks,
2084        args: &[Arg<'_>],
2085    ) {
2086        let ticks = zx::BootTicks::get();
2087        let thread_ref = self.register_current_thread();
2088        unsafe {
2089            sys::trace_context_write_duration_event_record(
2090                self.context.raw,
2091                start_time.into_raw(),
2092                ticks.into_raw(),
2093                &thread_ref,
2094                &self.category_ref,
2095                &name_ref,
2096                args.as_ptr() as *const sys::trace_arg_t,
2097                args.len(),
2098            );
2099        }
2100    }
2101
2102    pub fn write_duration_with_inline_name(
2103        &self,
2104        name: &str,
2105        start_time: zx::BootTicks,
2106        args: &[Arg<'_>],
2107    ) {
2108        let name_ref = trace_make_inline_string_ref(name);
2109        self.write_duration(name_ref, start_time, args);
2110    }
2111
2112    fn write_duration_begin(
2113        &self,
2114        ticks: zx::BootTicks,
2115        name_ref: sys::trace_string_ref_t,
2116        args: &[Arg<'_>],
2117    ) {
2118        let thread_ref = self.register_current_thread();
2119        unsafe {
2120            sys::trace_context_write_duration_begin_event_record(
2121                self.context.raw,
2122                ticks.into_raw(),
2123                &thread_ref,
2124                &self.category_ref,
2125                &name_ref,
2126                args.as_ptr() as *const sys::trace_arg_t,
2127                args.len(),
2128            );
2129        }
2130    }
2131
2132    #[cfg(fuchsia_api_level_at_least = "31")]
2133    fn write_vthread_duration_begin<S: AsTraceStrRef>(
2134        &self,
2135        ticks: zx::BootTicks,
2136        name_ref: sys::trace_string_ref_t,
2137        vthread: &VThread<S>,
2138        args: &[Arg<'_>],
2139    ) {
2140        let thread_ref = self.register_vthread(&vthread.name, vthread.id, vthread.process_koid);
2141        unsafe {
2142            sys::trace_context_write_duration_begin_event_record(
2143                self.context.raw,
2144                ticks.into_raw(),
2145                &thread_ref,
2146                &self.category_ref,
2147                &name_ref,
2148                args.as_ptr() as *const sys::trace_arg_t,
2149                args.len(),
2150            );
2151        }
2152    }
2153
2154    pub fn write_duration_begin_with_inline_name(&self, name: &str, args: &[Arg<'_>]) {
2155        let name_ref = trace_make_inline_string_ref(name);
2156        self.write_duration_begin(zx::BootTicks::get(), name_ref, args);
2157    }
2158
2159    fn write_duration_end(
2160        &self,
2161        ticks: zx::BootTicks,
2162        name_ref: sys::trace_string_ref_t,
2163        args: &[Arg<'_>],
2164    ) {
2165        let thread_ref = self.register_current_thread();
2166        unsafe {
2167            sys::trace_context_write_duration_end_event_record(
2168                self.context.raw,
2169                ticks.into_raw(),
2170                &thread_ref,
2171                &self.category_ref,
2172                &name_ref,
2173                args.as_ptr() as *const sys::trace_arg_t,
2174                args.len(),
2175            );
2176        }
2177    }
2178
2179    #[cfg(fuchsia_api_level_at_least = "31")]
2180    fn write_vthread_duration_end<S: AsTraceStrRef>(
2181        &self,
2182        ticks: zx::BootTicks,
2183        name_ref: sys::trace_string_ref_t,
2184        vthread: &VThread<S>,
2185        args: &[Arg<'_>],
2186    ) {
2187        let thread_ref = self.register_vthread(&vthread.name, vthread.id, vthread.process_koid);
2188        unsafe {
2189            sys::trace_context_write_duration_end_event_record(
2190                self.context.raw,
2191                ticks.into_raw(),
2192                &thread_ref,
2193                &self.category_ref,
2194                &name_ref,
2195                args.as_ptr() as *const sys::trace_arg_t,
2196                args.len(),
2197            );
2198        }
2199    }
2200
2201    #[cfg(fuchsia_api_level_at_least = "31")]
2202    fn write_vthread_instant<S: AsTraceStrRef>(
2203        &self,
2204        ticks: zx::BootTicks,
2205        name_ref: sys::trace_string_ref_t,
2206        vthread: &VThread<S>,
2207        scope: Scope,
2208        args: &[Arg<'_>],
2209    ) {
2210        let thread_ref = self.register_vthread(&vthread.name, vthread.id, vthread.process_koid);
2211        // SAFETY: The trace context is live, and all pointers (including string refs and args)
2212        // point to memory whose lifetimes are guaranteed to outlive this function call stack.
2213        unsafe {
2214            sys::trace_context_write_instant_event_record(
2215                self.context.raw,
2216                ticks.into_raw(),
2217                &thread_ref,
2218                &self.category_ref,
2219                &name_ref,
2220                scope.into_raw(),
2221                args.as_ptr() as *const sys::trace_arg_t,
2222                args.len(),
2223            );
2224        }
2225    }
2226
2227    #[cfg(fuchsia_api_level_at_least = "31")]
2228    fn write_vthread_duration<S: AsTraceStrRef>(
2229        &self,
2230        start_time: zx::BootTicks,
2231        ticks: zx::BootTicks,
2232        name_ref: sys::trace_string_ref_t,
2233        vthread: &VThread<S>,
2234        args: &[Arg<'_>],
2235    ) {
2236        let thread_ref = self.register_vthread(&vthread.name, vthread.id, vthread.process_koid);
2237        // SAFETY: The trace context is live, and all pointers (including string refs and args)
2238        // point to memory whose lifetimes are guaranteed to outlive this function call stack.
2239        unsafe {
2240            sys::trace_context_write_duration_event_record(
2241                self.context.raw,
2242                start_time.into_raw(),
2243                ticks.into_raw(),
2244                &thread_ref,
2245                &self.category_ref,
2246                &name_ref,
2247                args.as_ptr() as *const sys::trace_arg_t,
2248                args.len(),
2249            );
2250        }
2251    }
2252
2253    pub fn write_duration_end_with_inline_name(&self, name: &str, args: &[Arg<'_>]) {
2254        let name_ref = trace_make_inline_string_ref(name);
2255        self.write_duration_end(zx::BootTicks::get(), name_ref, args);
2256    }
2257
2258    fn write_async_begin(&self, id: Id, name_ref: sys::trace_string_ref_t, args: &[Arg<'_>]) {
2259        let ticks = zx::BootTicks::get();
2260        let thread_ref = self.register_current_thread();
2261        unsafe {
2262            sys::trace_context_write_async_begin_event_record(
2263                self.context.raw,
2264                ticks.into_raw(),
2265                &thread_ref,
2266                &self.category_ref,
2267                &name_ref,
2268                id.into(),
2269                args.as_ptr() as *const sys::trace_arg_t,
2270                args.len(),
2271            );
2272        }
2273    }
2274
2275    pub fn write_async_begin_with_inline_name(&self, id: Id, name: &str, args: &[Arg<'_>]) {
2276        let name_ref = trace_make_inline_string_ref(name);
2277        self.write_async_begin(id, name_ref, args);
2278    }
2279
2280    fn write_async_end(&self, id: Id, name_ref: sys::trace_string_ref_t, args: &[Arg<'_>]) {
2281        let ticks = zx::BootTicks::get();
2282        let thread_ref = self.register_current_thread();
2283        unsafe {
2284            sys::trace_context_write_async_end_event_record(
2285                self.context.raw,
2286                ticks.into_raw(),
2287                &thread_ref,
2288                &self.category_ref,
2289                &name_ref,
2290                id.into(),
2291                args.as_ptr() as *const sys::trace_arg_t,
2292                args.len(),
2293            );
2294        }
2295    }
2296
2297    pub fn write_async_end_with_inline_name(&self, id: Id, name: &str, args: &[Arg<'_>]) {
2298        let name_ref = trace_make_inline_string_ref(name);
2299        self.write_async_end(id, name_ref, args);
2300    }
2301
2302    fn write_async_instant(&self, id: Id, name_ref: sys::trace_string_ref_t, args: &[Arg<'_>]) {
2303        let ticks = zx::BootTicks::get();
2304        let thread_ref = self.register_current_thread();
2305        unsafe {
2306            sys::trace_context_write_async_instant_event_record(
2307                self.context.raw,
2308                ticks.into_raw(),
2309                &thread_ref,
2310                &self.category_ref,
2311                &name_ref,
2312                id.into(),
2313                args.as_ptr() as *const sys::trace_arg_t,
2314                args.len(),
2315            );
2316        }
2317    }
2318
2319    fn write_blob(&self, name_ref: sys::trace_string_ref_t, bytes: &[u8], args: &[Arg<'_>]) {
2320        let ticks = zx::BootTicks::get();
2321        let thread_ref = self.register_current_thread();
2322        unsafe {
2323            sys::trace_context_write_blob_event_record(
2324                self.context.raw,
2325                ticks.into_raw(),
2326                &thread_ref,
2327                &self.category_ref,
2328                &name_ref,
2329                bytes.as_ptr() as *const core::ffi::c_void,
2330                bytes.len(),
2331                args.as_ptr() as *const sys::trace_arg_t,
2332                args.len(),
2333            );
2334        }
2335    }
2336
2337    fn write_flow_begin(
2338        &self,
2339        ticks: zx::BootTicks,
2340        name_ref: sys::trace_string_ref_t,
2341        flow_id: Id,
2342        args: &[Arg<'_>],
2343    ) {
2344        let thread_ref = self.register_current_thread();
2345        unsafe {
2346            sys::trace_context_write_flow_begin_event_record(
2347                self.context.raw,
2348                ticks.into_raw(),
2349                &thread_ref,
2350                &self.category_ref,
2351                &name_ref,
2352                flow_id.into(),
2353                args.as_ptr() as *const sys::trace_arg_t,
2354                args.len(),
2355            );
2356        }
2357    }
2358
2359    fn write_flow_end(
2360        &self,
2361        ticks: zx::BootTicks,
2362        name_ref: sys::trace_string_ref_t,
2363        flow_id: Id,
2364        args: &[Arg<'_>],
2365    ) {
2366        let thread_ref = self.register_current_thread();
2367        unsafe {
2368            sys::trace_context_write_flow_end_event_record(
2369                self.context.raw,
2370                ticks.into_raw(),
2371                &thread_ref,
2372                &self.category_ref,
2373                &name_ref,
2374                flow_id.into(),
2375                args.as_ptr() as *const sys::trace_arg_t,
2376                args.len(),
2377            );
2378        }
2379    }
2380
2381    fn write_flow_step(
2382        &self,
2383        ticks: zx::BootTicks,
2384        name_ref: sys::trace_string_ref_t,
2385        flow_id: Id,
2386        args: &[Arg<'_>],
2387    ) {
2388        let thread_ref = self.register_current_thread();
2389        unsafe {
2390            sys::trace_context_write_flow_step_event_record(
2391                self.context.raw,
2392                ticks.into_raw(),
2393                &thread_ref,
2394                &self.category_ref,
2395                &name_ref,
2396                flow_id.into(),
2397                args.as_ptr() as *const sys::trace_arg_t,
2398                args.len(),
2399            );
2400        }
2401    }
2402}
2403
2404/// RAII wrapper for trace contexts without a specific associated category.
2405pub struct Context {
2406    raw: *const sys::trace_context_t,
2407}
2408
2409impl Context {
2410    #[inline]
2411    pub fn acquire() -> Option<Self> {
2412        let context = unsafe { sys::trace_acquire_context() };
2413        if context.is_null() { None } else { Some(Self { raw: context }) }
2414    }
2415
2416    #[inline]
2417    pub fn register_string_literal<T: CategoryString>(&self, s: T) -> sys::trace_string_ref_t {
2418        s.register(self)
2419    }
2420
2421    pub fn write_blob_record(
2422        &self,
2423        type_: sys::trace_blob_type_t,
2424        name_ref: &sys::trace_string_ref_t,
2425        data: &[u8],
2426    ) {
2427        unsafe {
2428            sys::trace_context_write_blob_record(
2429                self.raw,
2430                type_,
2431                name_ref as *const sys::trace_string_ref_t,
2432                data.as_ptr() as *const libc::c_void,
2433                data.len(),
2434            );
2435        }
2436    }
2437
2438    // Write fxt formatted bytes to the trace buffer
2439    //
2440    // returns Ok(num_bytes_written) on success
2441    pub fn copy_record(&self, buffer: &[u64]) -> Option<usize> {
2442        unsafe {
2443            let ptr = sys::trace_context_alloc_record(self.raw, 8 * buffer.len() as libc::size_t);
2444            if ptr == std::ptr::null_mut() {
2445                return None;
2446            }
2447            ptr.cast::<u64>().copy_from(buffer.as_ptr(), buffer.len());
2448        };
2449        Some(buffer.len())
2450    }
2451
2452    /// Records that `count` trace records were dropped before they could be written to the
2453    /// trace buffer.
2454    #[cfg(fuchsia_api_level_at_least = "HEAD")]
2455    pub fn mark_records_dropped(&self, count: usize) {
2456        // SAFETY: `self.raw` is a valid `trace_context_t` pointer acquired from
2457        // `trace_acquire_context` and kept live until `Drop` calls `trace_release_context`.
2458        unsafe { sys::trace_context_mark_records_dropped(self.raw, count) };
2459    }
2460
2461    pub fn buffering_mode(&self) -> BufferingMode {
2462        match unsafe { sys::trace_context_get_buffering_mode(self.raw) } {
2463            sys::TRACE_BUFFERING_MODE_ONESHOT => BufferingMode::OneShot,
2464            sys::TRACE_BUFFERING_MODE_CIRCULAR => BufferingMode::Circular,
2465            sys::TRACE_BUFFERING_MODE_STREAMING => BufferingMode::Streaming,
2466            m => panic!("Unknown trace buffering mode: {:?}", m),
2467        }
2468    }
2469}
2470
2471impl std::ops::Drop for Context {
2472    fn drop(&mut self) {
2473        unsafe { sys::trace_release_context(self.raw) }
2474    }
2475}
2476
2477pub struct ProlongedContext {
2478    context: *const sys::trace_prolonged_context_t,
2479}
2480
2481impl ProlongedContext {
2482    pub fn acquire() -> Option<Self> {
2483        let context = unsafe { sys::trace_acquire_prolonged_context() };
2484        if context.is_null() { None } else { Some(Self { context }) }
2485    }
2486}
2487
2488impl Drop for ProlongedContext {
2489    fn drop(&mut self) {
2490        unsafe { sys::trace_release_prolonged_context(self.context) }
2491    }
2492}
2493
2494unsafe impl Send for ProlongedContext {}
2495
2496mod sys {
2497    #![allow(non_camel_case_types, unused)]
2498    use zx::sys::{zx_handle_t, zx_koid_t, zx_obj_type_t, zx_status_t, zx_ticks_t};
2499
2500    pub type trace_ticks_t = zx_ticks_t;
2501    pub type trace_counter_id_t = u64;
2502    pub type trace_async_id_t = u64;
2503    pub type trace_flow_id_t = u64;
2504    pub type trace_vthread_id_t = u64;
2505    pub type trace_thread_state_t = u32;
2506    pub type trace_cpu_number_t = u32;
2507    pub type trace_string_index_t = u32;
2508    pub type trace_thread_index_t = u32;
2509    pub type trace_context_t = libc::c_void;
2510    pub type trace_prolonged_context_t = libc::c_void;
2511
2512    pub type trace_encoded_string_ref_t = u16;
2513    pub const TRACE_ENCODED_STRING_REF_EMPTY: trace_encoded_string_ref_t = 0;
2514    pub const TRACE_ENCODED_STRING_REF_INLINE_FLAG: trace_encoded_string_ref_t = 0x8000;
2515    pub const TRACE_ENCODED_STRING_REF_LENGTH_MASK: trace_encoded_string_ref_t = 0x7fff;
2516    pub const TRACE_ENCODED_STRING_REF_MAX_LENGTH: usize = 32000;
2517    pub const TRACE_ENCODED_STRING_REF_MIN_INDEX: trace_encoded_string_ref_t = 0x1;
2518    pub const TRACE_ENCODED_STRING_REF_MAX_INDEX: trace_encoded_string_ref_t = 0x7fff;
2519
2520    pub type trace_encoded_thread_ref_t = u32;
2521    pub const TRACE_ENCODED_THREAD_REF_INLINE: trace_encoded_thread_ref_t = 0;
2522    pub const TRACE_ENCODED_THREAD_MIN_INDEX: trace_encoded_thread_ref_t = 0x01;
2523    pub const TRACE_ENCODED_THREAD_MAX_INDEX: trace_encoded_thread_ref_t = 0xff;
2524
2525    pub type trace_state_t = libc::c_int;
2526    pub const TRACE_STOPPED: trace_state_t = 0;
2527    pub const TRACE_STARTED: trace_state_t = 1;
2528    pub const TRACE_STOPPING: trace_state_t = 2;
2529
2530    pub type trace_scope_t = libc::c_int;
2531    pub const TRACE_SCOPE_THREAD: trace_scope_t = 0;
2532    pub const TRACE_SCOPE_PROCESS: trace_scope_t = 1;
2533    pub const TRACE_SCOPE_GLOBAL: trace_scope_t = 2;
2534
2535    pub type trace_blob_type_t = libc::c_int;
2536    pub const TRACE_BLOB_TYPE_DATA: trace_blob_type_t = 1;
2537    pub const TRACE_BLOB_TYPE_LAST_BRANCH: trace_blob_type_t = 2;
2538    pub const TRACE_BLOB_TYPE_PERFETTO: trace_blob_type_t = 3;
2539
2540    pub type trace_buffering_mode_t = libc::c_int;
2541    pub const TRACE_BUFFERING_MODE_ONESHOT: trace_buffering_mode_t = 0;
2542    pub const TRACE_BUFFERING_MODE_CIRCULAR: trace_buffering_mode_t = 1;
2543    pub const TRACE_BUFFERING_MODE_STREAMING: trace_buffering_mode_t = 2;
2544
2545    #[repr(C)]
2546    #[derive(Copy, Clone)]
2547    pub struct trace_string_ref_t {
2548        pub encoded_value: trace_encoded_string_ref_t,
2549        pub inline_string: *const libc::c_char,
2550    }
2551
2552    // trace_site_t is an opaque type that trace-engine uses per callsite to cache if the trace
2553    // point is enabled. Internally, it is a 8 byte allocation accessed with relaxed atomic
2554    // semantics.
2555    pub type trace_site_t = std::sync::atomic::AtomicU64;
2556
2557    // A trace_string_ref_t object is created from a string slice.
2558    // The trace_string_ref_t object is contained inside an Arg object.
2559    // whose lifetime matches the string slice to ensure that the memory
2560    // cannot be de-allocated during the trace.
2561    //
2562    // trace_string_ref_t is safe for Send + Sync because the memory that
2563    // inline_string points to is guaranteed to be valid throughout the trace.
2564    //
2565    // For more information, see the ArgValue implementation for &str in this file.
2566    unsafe impl Send for trace_string_ref_t {}
2567    unsafe impl Sync for trace_string_ref_t {}
2568
2569    #[repr(C)]
2570    pub struct trace_thread_ref_t {
2571        pub encoded_value: trace_encoded_thread_ref_t,
2572        pub inline_process_koid: zx_koid_t,
2573        pub inline_thread_koid: zx_koid_t,
2574    }
2575
2576    #[repr(C)]
2577    pub struct trace_arg_t {
2578        pub name_ref: trace_string_ref_t,
2579        pub value: trace_arg_value_t,
2580    }
2581
2582    #[repr(C)]
2583    pub union trace_arg_union_t {
2584        pub int32_value: i32,
2585        pub uint32_value: u32,
2586        pub int64_value: i64,
2587        pub uint64_value: u64,
2588        pub double_value: libc::c_double,
2589        pub string_value_ref: trace_string_ref_t,
2590        pub pointer_value: libc::uintptr_t,
2591        pub koid_value: zx_koid_t,
2592        pub bool_value: bool,
2593        pub reserved_for_future_expansion: [libc::uintptr_t; 2],
2594    }
2595
2596    pub type trace_arg_type_t = libc::c_int;
2597    pub const TRACE_ARG_NULL: trace_arg_type_t = 0;
2598    pub const TRACE_ARG_INT32: trace_arg_type_t = 1;
2599    pub const TRACE_ARG_UINT32: trace_arg_type_t = 2;
2600    pub const TRACE_ARG_INT64: trace_arg_type_t = 3;
2601    pub const TRACE_ARG_UINT64: trace_arg_type_t = 4;
2602    pub const TRACE_ARG_DOUBLE: trace_arg_type_t = 5;
2603    pub const TRACE_ARG_STRING: trace_arg_type_t = 6;
2604    pub const TRACE_ARG_POINTER: trace_arg_type_t = 7;
2605    pub const TRACE_ARG_KOID: trace_arg_type_t = 8;
2606    pub const TRACE_ARG_BOOL: trace_arg_type_t = 9;
2607
2608    #[repr(C)]
2609    pub struct trace_arg_value_t {
2610        pub type_: trace_arg_type_t,
2611        pub value: trace_arg_union_t,
2612    }
2613
2614    #[repr(C)]
2615    pub struct trace_handler_ops_t {
2616        pub is_category_enabled:
2617            unsafe fn(handler: *const trace_handler_t, category: *const libc::c_char) -> bool,
2618        pub trace_started: unsafe fn(handler: *const trace_handler_t),
2619        pub trace_stopped: unsafe fn(
2620            handler: *const trace_handler_t,
2621            async_ptr: *const (), //async_t,
2622            disposition: zx_status_t,
2623            buffer_bytes_written: libc::size_t,
2624        ),
2625        pub buffer_overflow: unsafe fn(handler: *const trace_handler_t),
2626    }
2627
2628    #[repr(C)]
2629    pub struct trace_handler_t {
2630        pub ops: *const trace_handler_ops_t,
2631    }
2632
2633    // From libtrace-engine.so
2634    unsafe extern "C" {
2635        // From trace-engine/context.h
2636
2637        pub fn trace_context_is_category_enabled(
2638            context: *const trace_context_t,
2639            category_literal: *const libc::c_char,
2640        ) -> bool;
2641
2642        pub fn trace_context_register_string_literal(
2643            context: *const trace_context_t,
2644            string_literal: *const libc::c_char,
2645            out_ref: *mut trace_string_ref_t,
2646        );
2647
2648        #[cfg(fuchsia_api_level_at_least = "27")]
2649        pub fn trace_context_register_bytestring(
2650            context: *const trace_context_t,
2651            string_literal: *const libc::c_char,
2652            length: libc::size_t,
2653            out_ref: *mut trace_string_ref_t,
2654        );
2655
2656        pub fn trace_context_register_category_literal(
2657            context: *const trace_context_t,
2658            category_literal: *const libc::c_char,
2659            out_ref: *mut trace_string_ref_t,
2660        ) -> bool;
2661
2662        pub fn trace_context_register_current_thread(
2663            context: *const trace_context_t,
2664            out_ref: *mut trace_thread_ref_t,
2665        );
2666
2667        pub fn trace_context_register_thread(
2668            context: *const trace_context_t,
2669            process_koid: zx_koid_t,
2670            thread_koid: zx_koid_t,
2671            out_ref: *mut trace_thread_ref_t,
2672        );
2673
2674        #[cfg(fuchsia_api_level_at_least = "31")]
2675        pub fn trace_context_register_vthread_by_ref(
2676            context: *const trace_context_t,
2677            process_koid: zx_koid_t,
2678            vthread_name: *const trace_string_ref_t,
2679            vthread_id: trace_vthread_id_t,
2680            out_ref: *mut trace_thread_ref_t,
2681        );
2682
2683        pub fn trace_context_write_kernel_object_record(
2684            context: *const trace_context_t,
2685            koid: zx_koid_t,
2686            type_: zx_obj_type_t,
2687            args: *const trace_arg_t,
2688            num_args: libc::size_t,
2689        );
2690
2691        pub fn trace_context_write_kernel_object_record_for_handle(
2692            context: *const trace_context_t,
2693            handle: zx_handle_t,
2694            args: *const trace_arg_t,
2695            num_args: libc::size_t,
2696        );
2697
2698        pub fn trace_context_write_process_info_record(
2699            context: *const trace_context_t,
2700            process_koid: zx_koid_t,
2701            process_name_ref: *const trace_string_ref_t,
2702        );
2703
2704        pub fn trace_context_write_thread_info_record(
2705            context: *const trace_context_t,
2706            process_koid: zx_koid_t,
2707            thread_koid: zx_koid_t,
2708            thread_name_ref: *const trace_string_ref_t,
2709        );
2710
2711        pub fn trace_context_write_context_switch_record(
2712            context: *const trace_context_t,
2713            event_time: trace_ticks_t,
2714            cpu_number: trace_cpu_number_t,
2715            outgoing_thread_state: trace_thread_state_t,
2716            outgoing_thread_ref: *const trace_thread_ref_t,
2717            incoming_thread_ref: *const trace_thread_ref_t,
2718        );
2719
2720        pub fn trace_context_write_log_record(
2721            context: *const trace_context_t,
2722            event_time: trace_ticks_t,
2723            thread_ref: *const trace_thread_ref_t,
2724            log_message: *const libc::c_char,
2725            log_message_length: libc::size_t,
2726        );
2727
2728        pub fn trace_context_write_instant_event_record(
2729            context: *const trace_context_t,
2730            event_time: trace_ticks_t,
2731            thread_ref: *const trace_thread_ref_t,
2732            category_ref: *const trace_string_ref_t,
2733            name_ref: *const trace_string_ref_t,
2734            scope: trace_scope_t,
2735            args: *const trace_arg_t,
2736            num_args: libc::size_t,
2737        );
2738
2739        pub fn trace_context_send_alert(context: *const trace_context_t, name: *const libc::c_char);
2740
2741        #[cfg(fuchsia_api_level_at_least = "27")]
2742        pub fn trace_context_send_alert_bytestring(
2743            context: *const trace_context_t,
2744            name: *const u8,
2745            length: usize,
2746        );
2747
2748        pub fn trace_context_write_counter_event_record(
2749            context: *const trace_context_t,
2750            event_time: trace_ticks_t,
2751            thread_ref: *const trace_thread_ref_t,
2752            category_ref: *const trace_string_ref_t,
2753            name_ref: *const trace_string_ref_t,
2754            counter_id: trace_counter_id_t,
2755            args: *const trace_arg_t,
2756            num_args: libc::size_t,
2757        );
2758
2759        pub fn trace_context_write_duration_event_record(
2760            context: *const trace_context_t,
2761            start_time: trace_ticks_t,
2762            end_time: trace_ticks_t,
2763            thread_ref: *const trace_thread_ref_t,
2764            category_ref: *const trace_string_ref_t,
2765            name_ref: *const trace_string_ref_t,
2766            args: *const trace_arg_t,
2767            num_args: libc::size_t,
2768        );
2769
2770        pub fn trace_context_write_blob_event_record(
2771            context: *const trace_context_t,
2772            event_time: trace_ticks_t,
2773            thread_ref: *const trace_thread_ref_t,
2774            category_ref: *const trace_string_ref_t,
2775            name_ref: *const trace_string_ref_t,
2776            blob: *const libc::c_void,
2777            blob_size: libc::size_t,
2778            args: *const trace_arg_t,
2779            num_args: libc::size_t,
2780        );
2781
2782        pub fn trace_context_write_duration_begin_event_record(
2783            context: *const trace_context_t,
2784            event_time: trace_ticks_t,
2785            thread_ref: *const trace_thread_ref_t,
2786            category_ref: *const trace_string_ref_t,
2787            name_ref: *const trace_string_ref_t,
2788            args: *const trace_arg_t,
2789            num_args: libc::size_t,
2790        );
2791
2792        pub fn trace_context_write_duration_end_event_record(
2793            context: *const trace_context_t,
2794            event_time: trace_ticks_t,
2795            thread_ref: *const trace_thread_ref_t,
2796            category_ref: *const trace_string_ref_t,
2797            name_ref: *const trace_string_ref_t,
2798            args: *const trace_arg_t,
2799            num_args: libc::size_t,
2800        );
2801
2802        pub fn trace_context_write_async_begin_event_record(
2803            context: *const trace_context_t,
2804            event_time: trace_ticks_t,
2805            thread_ref: *const trace_thread_ref_t,
2806            category_ref: *const trace_string_ref_t,
2807            name_ref: *const trace_string_ref_t,
2808            async_id: trace_async_id_t,
2809            args: *const trace_arg_t,
2810            num_args: libc::size_t,
2811        );
2812
2813        pub fn trace_context_write_async_instant_event_record(
2814            context: *const trace_context_t,
2815            event_time: trace_ticks_t,
2816            thread_ref: *const trace_thread_ref_t,
2817            category_ref: *const trace_string_ref_t,
2818            name_ref: *const trace_string_ref_t,
2819            async_id: trace_async_id_t,
2820            args: *const trace_arg_t,
2821            num_args: libc::size_t,
2822        );
2823
2824        pub fn trace_context_write_async_end_event_record(
2825            context: *const trace_context_t,
2826            event_time: trace_ticks_t,
2827            thread_ref: *const trace_thread_ref_t,
2828            category_ref: *const trace_string_ref_t,
2829            name_ref: *const trace_string_ref_t,
2830            async_id: trace_async_id_t,
2831            args: *const trace_arg_t,
2832            num_args: libc::size_t,
2833        );
2834
2835        pub fn trace_context_write_flow_begin_event_record(
2836            context: *const trace_context_t,
2837            event_time: trace_ticks_t,
2838            thread_ref: *const trace_thread_ref_t,
2839            category_ref: *const trace_string_ref_t,
2840            name_ref: *const trace_string_ref_t,
2841            flow_id: trace_flow_id_t,
2842            args: *const trace_arg_t,
2843            num_args: libc::size_t,
2844        );
2845
2846        pub fn trace_context_write_flow_step_event_record(
2847            context: *const trace_context_t,
2848            event_time: trace_ticks_t,
2849            thread_ref: *const trace_thread_ref_t,
2850            category_ref: *const trace_string_ref_t,
2851            name_ref: *const trace_string_ref_t,
2852            flow_id: trace_flow_id_t,
2853            args: *const trace_arg_t,
2854            num_args: libc::size_t,
2855        );
2856
2857        pub fn trace_context_write_flow_end_event_record(
2858            context: *const trace_context_t,
2859            event_time: trace_ticks_t,
2860            thread_ref: *const trace_thread_ref_t,
2861            category_ref: *const trace_string_ref_t,
2862            name_ref: *const trace_string_ref_t,
2863            flow_id: trace_flow_id_t,
2864            args: *const trace_arg_t,
2865            num_args: libc::size_t,
2866        );
2867
2868        pub fn trace_context_write_initialization_record(
2869            context: *const trace_context_t,
2870            ticks_per_second: u64,
2871        );
2872
2873        pub fn trace_context_write_string_record(
2874            context: *const trace_context_t,
2875            index: trace_string_index_t,
2876            string: *const libc::c_char,
2877            length: libc::size_t,
2878        );
2879
2880        pub fn trace_context_write_thread_record(
2881            context: *const trace_context_t,
2882            index: trace_thread_index_t,
2883            procss_koid: zx_koid_t,
2884            thread_koid: zx_koid_t,
2885        );
2886
2887        pub fn trace_context_write_blob_record(
2888            context: *const trace_context_t,
2889            type_: trace_blob_type_t,
2890            name_ref: *const trace_string_ref_t,
2891            data: *const libc::c_void,
2892            size: libc::size_t,
2893        );
2894
2895        pub fn trace_context_alloc_record(
2896            context: *const trace_context_t,
2897            num_bytes: libc::size_t,
2898        ) -> *mut libc::c_void;
2899
2900        #[cfg(fuchsia_api_level_at_least = "HEAD")]
2901        pub fn trace_context_mark_records_dropped(
2902            context: *const trace_context_t,
2903            count: libc::size_t,
2904        );
2905
2906        // From trace-engine/instrumentation.h
2907        pub fn trace_state() -> trace_state_t;
2908
2909        #[cfg(fuchsia_api_level_at_least = "27")]
2910        pub fn trace_is_category_bytestring_enabled(
2911            category_literal: *const u8,
2912            length: usize,
2913        ) -> bool;
2914
2915        pub fn trace_is_category_enabled(category_literal: *const libc::c_char) -> bool;
2916
2917        pub fn trace_acquire_context() -> *const trace_context_t;
2918
2919        pub fn trace_acquire_context_for_category(
2920            category_literal: *const libc::c_char,
2921            out_ref: *mut trace_string_ref_t,
2922        ) -> *const trace_context_t;
2923
2924        pub fn trace_acquire_context_for_category_cached(
2925            category_literal: *const libc::c_char,
2926            trace_site: *const u64,
2927            out_ref: *mut trace_string_ref_t,
2928        ) -> *const trace_context_t;
2929
2930        #[cfg(fuchsia_api_level_at_least = "27")]
2931        pub fn trace_acquire_context_for_category_bytestring(
2932            bytes: *const u8,
2933            length: usize,
2934            out_ref: *mut trace_string_ref_t,
2935        ) -> *const trace_context_t;
2936
2937        #[cfg(fuchsia_api_level_at_least = "27")]
2938        pub fn trace_acquire_context_for_category_bytestring_cached(
2939            bytes: *const u8,
2940            length: usize,
2941            trace_site: *const u64,
2942            out_ref: *mut trace_string_ref_t,
2943        ) -> *const trace_context_t;
2944
2945        pub fn trace_release_context(context: *const trace_context_t);
2946
2947        pub fn trace_acquire_prolonged_context() -> *const trace_prolonged_context_t;
2948
2949        pub fn trace_release_prolonged_context(context: *const trace_prolonged_context_t);
2950
2951        pub fn trace_register_observer(event: zx_handle_t) -> zx_status_t;
2952
2953        pub fn trace_unregister_observer(event: zx_handle_t) -> zx_status_t;
2954
2955        pub fn trace_notify_observer_updated(event: zx_handle_t);
2956
2957        pub fn trace_context_get_buffering_mode(
2958            context: *const trace_context_t,
2959        ) -> trace_buffering_mode_t;
2960
2961        #[cfg(fuchsia_api_level_at_least = "31")]
2962        pub fn trace_engine_flush_buffer() -> zx_status_t;
2963    }
2964}
2965
2966/// Arguments for `TraceFuture` and `TraceFutureExt`. Use `trace_future_args!` to construct this
2967/// object.
2968pub struct TraceFutureArgs<'a, C: CategoryString, S: AsTraceStrRef> {
2969    pub category: C,
2970    pub name: S,
2971
2972    /// The trace arguments to appear in every duration event written by the `TraceFuture`. `args`
2973    /// should be empty if `context` is `None`.
2974    pub args: Box<[Arg<'a>]>,
2975
2976    /// The flow id to use in the flow events that connect the duration events together. A flow id
2977    /// will be constructed with `Id::new()` if not provided.
2978    pub flow_id: Option<Id>,
2979
2980    /// Use `trace_future_args!` to construct this object.
2981    pub _use_trace_future_args: (),
2982}
2983
2984#[doc(hidden)]
2985#[macro_export]
2986macro_rules! __impl_trace_future_args {
2987    // This rule is matched when there are no trace arguments. Without arguments, the category
2988    // context doesn't need to be acquired to see if the args should be constructed.
2989    ($category:expr, $name:expr, $flow_id:expr) => {
2990        $crate::TraceFutureArgs {
2991            category: $category,
2992            name: $name,
2993            args: ::std::boxed::Box::new([]),
2994            flow_id: $flow_id,
2995            _use_trace_future_args: (),
2996        }
2997    };
2998    ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)*) => {{
2999        static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
3000        use $crate::AsTraceStrRef;
3001        let context = $crate::TraceCategoryContext::acquire_cached($category, &CACHE);
3002        let args: ::std::boxed::Box<[$crate::Arg<'_>]> = if let Some(context) = context {
3003            ::std::boxed::Box::new(
3004                [$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]
3005            )
3006        } else {
3007            ::std::boxed::Box::new([])
3008        };
3009        $crate::TraceFutureArgs {
3010            category: $category,
3011            name: $name,
3012            args: args,
3013            flow_id: $flow_id,
3014            _use_trace_future_args: (),
3015        }
3016    }};
3017}
3018
3019/// Macro for constructing `TraceFutureArgs`. The trace arguments won't be constructed if the
3020/// category is not enabled. If the category becomes enabled while the `TraceFuture` is still alive
3021/// then the duration events will still be written but without the trace arguments.
3022///
3023/// Example:
3024///
3025/// ```
3026/// async move {
3027///     ....
3028/// }.trace(trace_future_args!("category", "name", "x" => 5, "y" => 10)).await;
3029/// ```
3030#[macro_export]
3031macro_rules! trace_future_args {
3032    ($category:expr, $name:expr $(, $key:expr => $val:expr)* $(,)?) => {
3033        $crate::__impl_trace_future_args!($category, $name, None $(,$key => $val)*)
3034    };
3035    ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)* $(,)?) => {
3036        $crate::__impl_trace_future_args!($category, $name, Some($flow_id) $(,$key => $val)*)
3037    };
3038}
3039
3040/// Extension trait for tracing futures.
3041pub trait TraceFutureExt: Future + Sized {
3042    /// Wraps a `Future` in a `TraceFuture`.
3043    ///
3044    /// Example:
3045    ///
3046    /// ```rust
3047    /// future.trace(trace_future_args!("category", "name")).await;
3048    /// ```
3049    ///
3050    /// Which is equivalent to:
3051    ///
3052    /// ```rust
3053    /// TraceFuture::new(trace_future_args!("category", "name"), future).await;
3054    /// ```
3055    #[inline(always)]
3056    fn trace<'a, C: CategoryString, S: AsTraceStrRef>(
3057        self,
3058        args: TraceFutureArgs<'a, C, S>,
3059    ) -> TraceFuture<'a, Self, C, S> {
3060        TraceFuture::new(args, self)
3061    }
3062}
3063
3064impl<T: Future + Sized> TraceFutureExt for T {}
3065
3066/// Wraps a `Future` and writes duration events every time it's polled. The duration events are
3067/// connected by flow events.
3068#[pin_project]
3069pub struct TraceFuture<'a, Fut: Future, C: CategoryString, S: AsTraceStrRef> {
3070    // LINT.IfChange
3071    #[pin]
3072    future: Fut,
3073    category: C,
3074    name: S,
3075    // LINT.ThenChange(//src/developer/debug/zxdb/console/commands/verb_async_backtrace.cc)
3076    args: Box<[Arg<'a>]>,
3077    flow_id: Option<Id>,
3078    poll_count: u64,
3079}
3080
3081impl<'a, Fut: Future, C: CategoryString, S: AsTraceStrRef> TraceFuture<'a, Fut, C, S> {
3082    #[inline(always)]
3083    pub fn new(args: TraceFutureArgs<'a, C, S>, future: Fut) -> Self {
3084        Self {
3085            future,
3086            category: args.category,
3087            name: args.name,
3088            args: args.args,
3089            flow_id: args.flow_id,
3090            poll_count: 0,
3091        }
3092    }
3093
3094    #[cold]
3095    fn trace_poll(
3096        self: Pin<&mut Self>,
3097        context: &TraceCategoryContext,
3098        cx: &mut std::task::Context<'_>,
3099    ) -> Poll<Fut::Output> {
3100        let start_time = zx::BootTicks::get();
3101        let this = self.project();
3102        *this.poll_count = this.poll_count.saturating_add(1);
3103        let name_ref = this.name.as_trace_str_ref(context);
3104        context.write_duration_begin(start_time, name_ref, &this.args);
3105
3106        let result = this.future.poll(cx);
3107
3108        let flow_id = this.flow_id.get_or_insert_with(Id::new);
3109        let result_str: sys::trace_string_ref_t = if result.is_pending() {
3110            if *this.poll_count == 1 {
3111                context.write_flow_begin(start_time, name_ref, *flow_id, &[]);
3112            } else {
3113                context.write_flow_step(start_time, name_ref, *flow_id, &[]);
3114            }
3115            context.register_str("pending")
3116        } else {
3117            if *this.poll_count != 1 {
3118                context.write_flow_end(start_time, name_ref, *flow_id, &[]);
3119            }
3120            context.register_str("ready")
3121        };
3122        context.write_duration_end(
3123            zx::BootTicks::get(),
3124            name_ref,
3125            &[
3126                ArgValue::of_registered(context.register_str("poll-state"), result_str),
3127                ArgValue::of_registered(context.register_str("poll-count"), *this.poll_count),
3128            ],
3129        );
3130        result
3131    }
3132}
3133
3134impl<Fut: Future, C: CategoryString, S: AsTraceStrRef> Future for TraceFuture<'_, Fut, C, S> {
3135    type Output = Fut::Output;
3136    fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Fut::Output> {
3137        if let Some(context) = TraceCategoryContext::acquire(self.as_ref().get_ref().category) {
3138            self.trace_poll(&context, cx)
3139        } else {
3140            self.project().future.poll(cx)
3141        }
3142    }
3143}
3144
3145#[cfg(test)]
3146mod test {
3147    use super::{Id, trim_to_last_char_boundary};
3148
3149    #[test]
3150    fn trim_to_last_char_boundary_trims_to_last_character_boundary() {
3151        assert_eq!(b"x", trim_to_last_char_boundary("x", 5));
3152        assert_eq!(b"x", trim_to_last_char_boundary("x", 1));
3153        assert_eq!(b"", trim_to_last_char_boundary("x", 0));
3154        assert_eq!(b"xxxxx", trim_to_last_char_boundary("xxxxx", 6));
3155        assert_eq!(b"xxxxx", trim_to_last_char_boundary("xxxxx", 5));
3156        assert_eq!(b"xxxx", trim_to_last_char_boundary("xxxxx", 4));
3157
3158        assert_eq!("💩".as_bytes(), trim_to_last_char_boundary("💩", 5));
3159        assert_eq!("💩".as_bytes(), trim_to_last_char_boundary("💩", 4));
3160        assert_eq!(b"", trim_to_last_char_boundary("💩", 3));
3161    }
3162
3163    // Here, we're looking to make sure that successive calls to the function generate distinct
3164    // values. How those values are distinct is not particularly meaningful; the current
3165    // implementation yields sequential values, but that's not a behavior to rely on.
3166    #[test]
3167    fn test_id_new() {
3168        assert_ne!(Id::new(), Id::new());
3169    }
3170
3171    #[test]
3172    fn test_id_rcu_droppable() {
3173        fn assert_rcu_droppable<T: fuchsia_rcu_traits::RcuDroppable>() {}
3174        assert_rcu_droppable::<Id>();
3175    }
3176}