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