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/// Flow events must be enclosed in a duration event.
1479///
1480/// Example:
1481///
1482/// ```rust
1483/// let flow_id = 1234;
1484/// {
1485///     duration!("foo", "step_1");
1486///     flow_begin!("foo", "bar", flow_id, "x" => 5, "y" => "boo");
1487/// }
1488/// ```
1489///
1490/// ```rust
1491/// const FOO: &'static str = "foo";
1492/// const BAR: &'static str = "bar";
1493/// let flow_id = 1234;
1494/// {
1495///     duration!("foo", "step_1");
1496///     flow_begin!("foo", "bar", flow_id);
1497/// }
1498/// ```
1499#[macro_export]
1500macro_rules! flow_begin {
1501    ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)*) => {
1502        {
1503            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1504            use $crate::AsTraceStrRef;
1505            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1506                $crate::flow_begin(&context, $name, $flow_id,
1507                                   &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
1508            }
1509        }
1510    }
1511}
1512
1513/// Convenience macro for the `flow_step` function.
1514///
1515/// Flow events must be enclosed in a duration event.
1516///
1517/// Example:
1518///
1519/// ```rust
1520/// let flow_id = 1234;
1521/// {
1522///     duration!("foo", "step_2");
1523///     flow_step!("foo", "bar", flow_id, "x" => 5, "y" => "boo");
1524/// }
1525/// ```
1526///
1527/// ```rust
1528/// const FOO: &'static str = "foo";
1529/// const BAR: &'static str = "bar";
1530/// let flow_id = 1234;
1531/// {
1532///     duration!("foo", "step_2");
1533///     flow_step!("foo", "bar", flow_id);
1534/// }
1535/// ```
1536#[macro_export]
1537macro_rules! flow_step {
1538    ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)*) => {
1539        {
1540            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1541            use $crate::AsTraceStrRef;
1542            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1543                $crate::flow_step(&context, $name, $flow_id,
1544                                  &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
1545            }
1546        }
1547    }
1548}
1549
1550/// Convenience macro for the `flow_end` function.
1551///
1552/// Flow events must be enclosed in a duration event.
1553///
1554/// Example:
1555///
1556/// ```rust
1557/// let flow_id = 1234;
1558/// {
1559///     duration!("foo", "step_3");
1560///     flow_end!("foo", "bar", flow_id, "x" => 5, "y" => "boo");
1561/// }
1562/// ```
1563///
1564/// ```rust
1565/// const FOO: &'static str = "foo";
1566/// const BAR: &'static str = "bar";
1567/// let flow_id = 1234;
1568/// {
1569///     duration!("foo", "step_3");
1570///     flow_end!("foo", "bar", flow_id);
1571/// }
1572/// ```
1573#[macro_export]
1574macro_rules! flow_end {
1575    ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)*) => {
1576        {
1577            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1578            use $crate::AsTraceStrRef;
1579            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1580                $crate::flow_end(&context, $name, $flow_id,
1581                                 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
1582            }
1583        }
1584    }
1585}
1586
1587/// Writes a flow begin event with the specified id.
1588/// This event may be followed by flow steps events and must be matched by
1589/// a flow end event with the same category, name, and id.
1590///
1591/// Flow events describe control flow handoffs between threads or across processes.
1592/// They are typically represented as arrows in a visualizer.  Flow arrows are
1593/// from the end of the duration event which encloses the beginning of the flow
1594/// to the beginning of the duration event which encloses the next step or the
1595/// end of the flow.  The id serves to correlate flows across processes. Note
1596/// that flow IDs are global in Perfetto and not scoped to the category and
1597/// name; using non-unique IDs concurrently for different flows will lead to
1598/// ambiguous lines in the trace viewer.
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_begin<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_begin(ticks, name_ref, flow_id, args);
1618}
1619
1620/// Writes a flow end 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 across processes. Note
1627/// that flow IDs are global in Perfetto and not scoped to the category and
1628/// name; using non-unique IDs concurrently for different flows will lead to
1629/// ambiguous lines in the trace viewer.
1630///
1631/// This event must be enclosed in a duration event which represents where
1632/// the flow handoff occurs.
1633///
1634/// 0 to 15 arguments can be associated with the event, each of which is used
1635/// to annotate the flow with additional information.  The arguments provided
1636/// to matching flow begin, flow step, and flow end events are combined together
1637/// in the trace; it is not necessary to repeat them.
1638pub fn flow_end<S: AsTraceStrRef>(
1639    context: &TraceCategoryContext,
1640    name: S,
1641    flow_id: Id,
1642    args: &[Arg<'_>],
1643) {
1644    let ticks = zx::BootTicks::get();
1645    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1646
1647    let name_ref = name.as_trace_str_ref(context);
1648    context.write_flow_end(ticks, name_ref, flow_id, args);
1649}
1650
1651/// Writes a flow step event with the specified id.
1652///
1653/// Flow events describe control flow handoffs between threads or across processes.
1654/// They are typically represented as arrows in a visualizer.  Flow arrows are
1655/// from the end of the duration event which encloses the beginning of the flow
1656/// to the beginning of the duration event which encloses the next step or the
1657/// end of the flow.  The id serves to correlate flows across processes. Note
1658/// that flow IDs are global in Perfetto and not scoped to the category and
1659/// name; using non-unique IDs concurrently for different flows will lead to
1660/// ambiguous lines in the trace viewer.
1661///
1662/// This event must be enclosed in a duration event which represents where
1663/// the flow handoff occurs.
1664///
1665/// 0 to 15 arguments can be associated with the event, each of which is used
1666/// to annotate the flow with additional information.  The arguments provided
1667/// to matching flow begin, flow step, and flow end events are combined together
1668/// in the trace; it is not necessary to repeat them.
1669pub fn flow_step<S: AsTraceStrRef>(
1670    context: &TraceCategoryContext,
1671    name: S,
1672    flow_id: Id,
1673    args: &[Arg<'_>],
1674) {
1675    let ticks = zx::BootTicks::get();
1676    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1677
1678    let name_ref = name.as_trace_str_ref(context);
1679    context.write_flow_step(ticks, name_ref, flow_id, args);
1680}
1681
1682/// Convenience macro to emit the beginning of a flow attached to an instant event.
1683///
1684/// Flows must be attached to a duration event. This can be awkward when there isn't an obvious
1685/// duration event to attach to, or the relevant duration is very small, which makes visualizing
1686/// difficult. This emits a flow event wrapped in a self contained instant event that is also easy
1687/// to see in the tracing UI.
1688///
1689/// Example:
1690///
1691/// ```rust
1692/// let flow_id = 1234;
1693/// instaflow_begin!("category", "flow", "step", flow_id, "x" => 5, "y" => "boo");
1694/// ```
1695#[macro_export]
1696macro_rules! instaflow_begin {
1697    (
1698        $category:expr,
1699        $flow_name:expr,
1700        $step_name:expr,
1701        $flow_id:expr
1702        $(, $key:expr => $val:expr)*
1703    ) => {
1704        {
1705            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1706            use $crate::AsTraceStrRef;
1707            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1708                $crate::instaflow_begin(
1709                    &context,
1710                    $flow_name,
1711                    $step_name,
1712                    $flow_id,
1713                    &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*],
1714                )
1715            }
1716        }
1717    }
1718}
1719
1720/// Convenience macro to emit the end of a flow attached to an instant event.
1721///
1722/// Flows must be attached to a duration event. This can be awkward when there isn't an obvious
1723/// duration event to attach to, or the relevant duration is very small, which makes visualizing
1724/// difficult. This emits a flow event wrapped in a self contained instant event that is also easy
1725/// to see in the tracing UI.
1726///
1727/// Example:
1728///
1729/// ```rust
1730/// let flow_id = 1234;
1731/// instaflow_end!("category", "flow", "step", flow_id, "x" => 5, "y" => "boo");
1732/// ```
1733#[macro_export]
1734macro_rules! instaflow_end {
1735    (
1736        $category:expr,
1737        $flow_name:expr,
1738        $step_name:expr,
1739        $flow_id:expr
1740        $(, $key:expr => $val:expr)*
1741    ) => {
1742        {
1743            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1744            use $crate::AsTraceStrRef;
1745            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1746                $crate::instaflow_end(
1747                    &context,
1748                    $flow_name,
1749                    $step_name,
1750                    $flow_id,
1751                    &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*],
1752                )
1753            }
1754        }
1755    }
1756}
1757
1758/// Convenience macro to emit a step in a flow attached to an instant event.
1759///
1760/// Flows must be attached to a duration event. This can be awkward when there isn't an obvious
1761/// duration event to attach to, or the relevant duration is very small, which makes visualizing
1762/// difficult. This emits a flow event wrapped in a self contained instant event that is also easy
1763/// to see in the tracing UI.
1764///
1765/// Example:
1766///
1767/// ```rust
1768/// let flow_id = 1234;
1769/// instaflow_step!("category", "flow", "step", flow_id, "x" => 5, "y" => "boo");
1770/// ```
1771#[macro_export]
1772macro_rules! instaflow_step {
1773    (
1774        $category:expr,
1775        $flow_name:expr,
1776        $step_name:expr,
1777        $flow_id:expr
1778        $(, $key:expr => $val:expr)*
1779    ) => {
1780        {
1781            static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1782            use $crate::AsTraceStrRef;
1783            if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1784                $crate::instaflow_step(
1785                    &context,
1786                    $flow_name,
1787                    $step_name,
1788                    $flow_id,
1789                    &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*],
1790                )
1791            }
1792        }
1793    }
1794}
1795
1796/// Convenience function to emit the beginning of a flow attached to an instant event.
1797///
1798/// Flow events describe control flow handoffs between threads or across processes. They are
1799/// typically represented as arrows in a visualizer. Flow arrows are from the end of the duration
1800/// event which encloses the beginning of the flow to the beginning of the duration event which
1801/// encloses the next step or the end of the flow. The id serves to correlate flows across
1802/// processes. Note that flow IDs are global in Perfetto and not scoped to the category and
1803/// name; using non-unique IDs concurrently for different flows will lead to ambiguous lines
1804/// in the trace viewer.
1805///
1806/// 0 to 15 arguments can be associated with the event, each of which is used to annotate the flow
1807/// with additional information. The arguments provided to matching flow begin, flow step, and flow
1808/// end events are combined together in the trace; it is not necessary to repeat them.
1809pub fn instaflow_begin<S1: AsTraceStrRef, S2: AsTraceStrRef>(
1810    context: &TraceCategoryContext,
1811    flow_name: S1,
1812    step_name: S2,
1813    flow_id: Id,
1814    args: &[Arg<'_>],
1815) {
1816    let ticks = zx::BootTicks::get();
1817    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1818
1819    let flow_name_ref = flow_name.as_trace_str_ref(context);
1820    let step_name_ref = step_name.as_trace_str_ref(context);
1821
1822    context.write_duration_begin(ticks, step_name_ref, args);
1823    context.write_flow_begin(ticks, flow_name_ref, flow_id, args);
1824    context.write_duration_end(ticks, step_name_ref, args);
1825}
1826
1827/// Convenience function to the end of a flow attached to an instant event.
1828///
1829/// Flow events describe control flow handoffs between threads or across processes. They are
1830/// typically represented as arrows in a visualizer. Flow arrows are from the end of the duration
1831/// event which encloses the beginning of the flow to the beginning of the duration event which
1832/// encloses the next step or the end of the flow. The id serves to correlate flows across
1833/// processes. Note that flow IDs are global in Perfetto and not scoped to the category and
1834/// name; using non-unique IDs concurrently for different flows will lead to ambiguous lines
1835/// in the trace viewer.
1836///
1837/// 0 to 15 arguments can be associated with the event, each of which is used to annotate the flow
1838/// with additional information. The arguments provided to matching flow begin, flow step, and flow
1839/// end events are combined together in the trace; it is not necessary to repeat them.
1840pub fn instaflow_end<S1: AsTraceStrRef, S2: AsTraceStrRef>(
1841    context: &TraceCategoryContext,
1842    flow_name: S1,
1843    step_name: S2,
1844    flow_id: Id,
1845    args: &[Arg<'_>],
1846) {
1847    let ticks = zx::BootTicks::get();
1848    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1849
1850    let flow_name_ref = flow_name.as_trace_str_ref(context);
1851    let step_name_ref = step_name.as_trace_str_ref(context);
1852
1853    context.write_duration_begin(ticks, step_name_ref, args);
1854    context.write_flow_end(ticks, flow_name_ref, flow_id, args);
1855    context.write_duration_end(ticks, step_name_ref, args);
1856}
1857
1858/// Convenience function to emit a step in a flow attached to an instant event.
1859///
1860/// Flow events describe control flow handoffs between threads or across processes. They are
1861/// typically represented as arrows in a visualizer. Flow arrows are from the end of the duration
1862/// event which encloses the beginning of the flow to the beginning of the duration event which
1863/// encloses the next step or the end of the flow. The id serves to correlate flows across
1864/// processes. Note that flow IDs are global in Perfetto and not scoped to the category and
1865/// name; using non-unique IDs concurrently for different flows will lead to ambiguous lines
1866/// in the trace viewer.
1867///
1868/// 0 to 15 arguments can be associated with the event, each of which is used to annotate the flow
1869/// with additional information. The arguments provided to matching flow begin, flow step, and flow
1870/// end events are combined together in the trace; it is not necessary to repeat them.
1871pub fn instaflow_step<S1: AsTraceStrRef, S2: AsTraceStrRef>(
1872    context: &TraceCategoryContext,
1873    flow_name: S1,
1874    step_name: S2,
1875    flow_id: Id,
1876    args: &[Arg<'_>],
1877) {
1878    let ticks = zx::BootTicks::get();
1879    assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1880
1881    let flow_name_ref = flow_name.as_trace_str_ref(context);
1882    let step_name_ref = step_name.as_trace_str_ref(context);
1883
1884    context.write_duration_begin(ticks, step_name_ref, args);
1885    context.write_flow_step(ticks, flow_name_ref, flow_id, args);
1886    context.write_duration_end(ticks, step_name_ref, args);
1887}
1888
1889// translated from trace-engine/types.h for inlining
1890const fn trace_make_empty_string_ref() -> sys::trace_string_ref_t {
1891    sys::trace_string_ref_t {
1892        encoded_value: sys::TRACE_ENCODED_STRING_REF_EMPTY,
1893        inline_string: ptr::null(),
1894    }
1895}
1896
1897#[inline]
1898fn trim_to_last_char_boundary(string: &str, max_len: usize) -> &[u8] {
1899    let mut len = string.len();
1900    if string.len() > max_len {
1901        // Trim to the last unicode character that fits within the max length.
1902        // We search for the last character boundary that is immediately followed
1903        // by another character boundary (end followed by beginning).
1904        len = max_len;
1905        while len > 0 {
1906            if string.is_char_boundary(len - 1) && string.is_char_boundary(len) {
1907                break;
1908            }
1909            len -= 1;
1910        }
1911    }
1912    &string.as_bytes()[0..len]
1913}
1914
1915// translated from trace-engine/types.h for inlining
1916// The resulting `trace_string_ref_t` only lives as long as the input `string`.
1917#[inline]
1918fn trace_make_inline_string_ref(string: &str) -> sys::trace_string_ref_t {
1919    let len = string.len() as u16;
1920    if len == 0 {
1921        return trace_make_empty_string_ref();
1922    }
1923
1924    let string = trim_to_last_char_boundary(string, sys::TRACE_ENCODED_STRING_REF_MAX_LENGTH);
1925
1926    sys::trace_string_ref_t {
1927        encoded_value: sys::TRACE_ENCODED_STRING_REF_INLINE_FLAG | len,
1928        inline_string: string.as_ptr() as *const libc::c_char,
1929    }
1930}
1931
1932/// RAII wrapper for a trace context for a specific category.
1933pub struct TraceCategoryContext {
1934    context: Context,
1935    category_ref: sys::trace_string_ref_t,
1936}
1937
1938impl TraceCategoryContext {
1939    #[inline]
1940    pub fn acquire_cached<C: CategoryString>(
1941        category: C,
1942        site: &sys::trace_site_t,
1943    ) -> Option<TraceCategoryContext> {
1944        category.acquire_context_cached(site)
1945    }
1946
1947    pub fn acquire<C: CategoryString>(category: C) -> Option<TraceCategoryContext> {
1948        category.acquire_context()
1949    }
1950
1951    #[inline]
1952    pub fn register_string_literal<T: CategoryString>(&self, name: T) -> sys::trace_string_ref_t {
1953        name.register(&self.context)
1954    }
1955
1956    #[inline]
1957    #[cfg(fuchsia_api_level_at_least = "27")]
1958    pub fn register_str(&self, name: &'static str) -> sys::trace_string_ref_t {
1959        unsafe {
1960            let mut name_ref = mem::MaybeUninit::<sys::trace_string_ref_t>::uninit();
1961            sys::trace_context_register_bytestring(
1962                self.context.raw,
1963                name.as_ptr().cast::<libc::c_char>(),
1964                name.len(),
1965                name_ref.as_mut_ptr(),
1966            );
1967            name_ref.assume_init()
1968        }
1969    }
1970    #[inline]
1971    #[cfg(not(fuchsia_api_level_at_least = "27"))]
1972    pub fn register_str(&self, name: &'static str) -> sys::trace_string_ref_t {
1973        trace_make_inline_string_ref(name)
1974    }
1975
1976    #[inline]
1977    fn register_current_thread(&self) -> sys::trace_thread_ref_t {
1978        unsafe {
1979            let mut thread_ref = mem::MaybeUninit::<sys::trace_thread_ref_t>::uninit();
1980            sys::trace_context_register_current_thread(self.context.raw, thread_ref.as_mut_ptr());
1981            thread_ref.assume_init()
1982        }
1983    }
1984
1985    #[cfg(fuchsia_api_level_at_least = "31")]
1986    #[inline]
1987    fn register_vthread<S: AsTraceStrRef>(
1988        &self,
1989        name: &S,
1990        id: sys::trace_vthread_id_t,
1991        process_koid: zx::sys::zx_koid_t,
1992    ) -> sys::trace_thread_ref_t {
1993        let name_ref = name.as_trace_str_ref(self);
1994        unsafe {
1995            let mut thread_ref = mem::MaybeUninit::<sys::trace_thread_ref_t>::uninit();
1996            sys::trace_context_register_vthread_by_ref(
1997                self.context.raw,
1998                process_koid,
1999                &name_ref,
2000                id,
2001                thread_ref.as_mut_ptr(),
2002            );
2003            thread_ref.assume_init()
2004        }
2005    }
2006
2007    #[inline]
2008    pub fn write_instant(&self, name_ref: sys::trace_string_ref_t, scope: Scope, args: &[Arg<'_>]) {
2009        let ticks = zx::BootTicks::get();
2010        let thread_ref = self.register_current_thread();
2011        unsafe {
2012            sys::trace_context_write_instant_event_record(
2013                self.context.raw,
2014                ticks.into_raw(),
2015                &thread_ref,
2016                &self.category_ref,
2017                &name_ref,
2018                scope.into_raw(),
2019                args.as_ptr() as *const sys::trace_arg_t,
2020                args.len(),
2021            );
2022        }
2023    }
2024
2025    pub fn write_instant_with_inline_name(&self, name: &str, scope: Scope, args: &[Arg<'_>]) {
2026        let name_ref = trace_make_inline_string_ref(name);
2027        self.write_instant(name_ref, scope, args)
2028    }
2029
2030    fn write_counter(&self, name_ref: sys::trace_string_ref_t, counter_id: u64, args: &[Arg<'_>]) {
2031        let ticks = zx::BootTicks::get();
2032        let thread_ref = self.register_current_thread();
2033        unsafe {
2034            sys::trace_context_write_counter_event_record(
2035                self.context.raw,
2036                ticks.into_raw(),
2037                &thread_ref,
2038                &self.category_ref,
2039                &name_ref,
2040                counter_id,
2041                args.as_ptr() as *const sys::trace_arg_t,
2042                args.len(),
2043            );
2044        }
2045    }
2046
2047    pub fn write_counter_with_inline_name(&self, name: &str, counter_id: u64, args: &[Arg<'_>]) {
2048        let name_ref = trace_make_inline_string_ref(name);
2049        self.write_counter(name_ref, counter_id, args);
2050    }
2051
2052    fn write_duration(
2053        &self,
2054        name_ref: sys::trace_string_ref_t,
2055        start_time: zx::BootTicks,
2056        args: &[Arg<'_>],
2057    ) {
2058        let ticks = zx::BootTicks::get();
2059        let thread_ref = self.register_current_thread();
2060        unsafe {
2061            sys::trace_context_write_duration_event_record(
2062                self.context.raw,
2063                start_time.into_raw(),
2064                ticks.into_raw(),
2065                &thread_ref,
2066                &self.category_ref,
2067                &name_ref,
2068                args.as_ptr() as *const sys::trace_arg_t,
2069                args.len(),
2070            );
2071        }
2072    }
2073
2074    pub fn write_duration_with_inline_name(
2075        &self,
2076        name: &str,
2077        start_time: zx::BootTicks,
2078        args: &[Arg<'_>],
2079    ) {
2080        let name_ref = trace_make_inline_string_ref(name);
2081        self.write_duration(name_ref, start_time, args);
2082    }
2083
2084    fn write_duration_begin(
2085        &self,
2086        ticks: zx::BootTicks,
2087        name_ref: sys::trace_string_ref_t,
2088        args: &[Arg<'_>],
2089    ) {
2090        let thread_ref = self.register_current_thread();
2091        unsafe {
2092            sys::trace_context_write_duration_begin_event_record(
2093                self.context.raw,
2094                ticks.into_raw(),
2095                &thread_ref,
2096                &self.category_ref,
2097                &name_ref,
2098                args.as_ptr() as *const sys::trace_arg_t,
2099                args.len(),
2100            );
2101        }
2102    }
2103
2104    #[cfg(fuchsia_api_level_at_least = "31")]
2105    fn write_vthread_duration_begin<S: AsTraceStrRef>(
2106        &self,
2107        ticks: zx::BootTicks,
2108        name_ref: sys::trace_string_ref_t,
2109        vthread: &VThread<S>,
2110        args: &[Arg<'_>],
2111    ) {
2112        let thread_ref = self.register_vthread(&vthread.name, vthread.id, vthread.process_koid);
2113        unsafe {
2114            sys::trace_context_write_duration_begin_event_record(
2115                self.context.raw,
2116                ticks.into_raw(),
2117                &thread_ref,
2118                &self.category_ref,
2119                &name_ref,
2120                args.as_ptr() as *const sys::trace_arg_t,
2121                args.len(),
2122            );
2123        }
2124    }
2125
2126    pub fn write_duration_begin_with_inline_name(&self, name: &str, args: &[Arg<'_>]) {
2127        let name_ref = trace_make_inline_string_ref(name);
2128        self.write_duration_begin(zx::BootTicks::get(), name_ref, args);
2129    }
2130
2131    fn write_duration_end(
2132        &self,
2133        ticks: zx::BootTicks,
2134        name_ref: sys::trace_string_ref_t,
2135        args: &[Arg<'_>],
2136    ) {
2137        let thread_ref = self.register_current_thread();
2138        unsafe {
2139            sys::trace_context_write_duration_end_event_record(
2140                self.context.raw,
2141                ticks.into_raw(),
2142                &thread_ref,
2143                &self.category_ref,
2144                &name_ref,
2145                args.as_ptr() as *const sys::trace_arg_t,
2146                args.len(),
2147            );
2148        }
2149    }
2150
2151    #[cfg(fuchsia_api_level_at_least = "31")]
2152    fn write_vthread_duration_end<S: AsTraceStrRef>(
2153        &self,
2154        ticks: zx::BootTicks,
2155        name_ref: sys::trace_string_ref_t,
2156        vthread: &VThread<S>,
2157        args: &[Arg<'_>],
2158    ) {
2159        let thread_ref = self.register_vthread(&vthread.name, vthread.id, vthread.process_koid);
2160        unsafe {
2161            sys::trace_context_write_duration_end_event_record(
2162                self.context.raw,
2163                ticks.into_raw(),
2164                &thread_ref,
2165                &self.category_ref,
2166                &name_ref,
2167                args.as_ptr() as *const sys::trace_arg_t,
2168                args.len(),
2169            );
2170        }
2171    }
2172
2173    #[cfg(fuchsia_api_level_at_least = "31")]
2174    fn write_vthread_instant<S: AsTraceStrRef>(
2175        &self,
2176        ticks: zx::BootTicks,
2177        name_ref: sys::trace_string_ref_t,
2178        vthread: &VThread<S>,
2179        scope: Scope,
2180        args: &[Arg<'_>],
2181    ) {
2182        let thread_ref = self.register_vthread(&vthread.name, vthread.id, vthread.process_koid);
2183        // SAFETY: The trace context is live, and all pointers (including string refs and args)
2184        // point to memory whose lifetimes are guaranteed to outlive this function call stack.
2185        unsafe {
2186            sys::trace_context_write_instant_event_record(
2187                self.context.raw,
2188                ticks.into_raw(),
2189                &thread_ref,
2190                &self.category_ref,
2191                &name_ref,
2192                scope.into_raw(),
2193                args.as_ptr() as *const sys::trace_arg_t,
2194                args.len(),
2195            );
2196        }
2197    }
2198
2199    #[cfg(fuchsia_api_level_at_least = "31")]
2200    fn write_vthread_duration<S: AsTraceStrRef>(
2201        &self,
2202        start_time: zx::BootTicks,
2203        ticks: zx::BootTicks,
2204        name_ref: sys::trace_string_ref_t,
2205        vthread: &VThread<S>,
2206        args: &[Arg<'_>],
2207    ) {
2208        let thread_ref = self.register_vthread(&vthread.name, vthread.id, vthread.process_koid);
2209        // SAFETY: The trace context is live, and all pointers (including string refs and args)
2210        // point to memory whose lifetimes are guaranteed to outlive this function call stack.
2211        unsafe {
2212            sys::trace_context_write_duration_event_record(
2213                self.context.raw,
2214                start_time.into_raw(),
2215                ticks.into_raw(),
2216                &thread_ref,
2217                &self.category_ref,
2218                &name_ref,
2219                args.as_ptr() as *const sys::trace_arg_t,
2220                args.len(),
2221            );
2222        }
2223    }
2224
2225    pub fn write_duration_end_with_inline_name(&self, name: &str, args: &[Arg<'_>]) {
2226        let name_ref = trace_make_inline_string_ref(name);
2227        self.write_duration_end(zx::BootTicks::get(), name_ref, args);
2228    }
2229
2230    fn write_async_begin(&self, id: Id, name_ref: sys::trace_string_ref_t, args: &[Arg<'_>]) {
2231        let ticks = zx::BootTicks::get();
2232        let thread_ref = self.register_current_thread();
2233        unsafe {
2234            sys::trace_context_write_async_begin_event_record(
2235                self.context.raw,
2236                ticks.into_raw(),
2237                &thread_ref,
2238                &self.category_ref,
2239                &name_ref,
2240                id.into(),
2241                args.as_ptr() as *const sys::trace_arg_t,
2242                args.len(),
2243            );
2244        }
2245    }
2246
2247    pub fn write_async_begin_with_inline_name(&self, id: Id, name: &str, args: &[Arg<'_>]) {
2248        let name_ref = trace_make_inline_string_ref(name);
2249        self.write_async_begin(id, name_ref, args);
2250    }
2251
2252    fn write_async_end(&self, id: Id, name_ref: sys::trace_string_ref_t, args: &[Arg<'_>]) {
2253        let ticks = zx::BootTicks::get();
2254        let thread_ref = self.register_current_thread();
2255        unsafe {
2256            sys::trace_context_write_async_end_event_record(
2257                self.context.raw,
2258                ticks.into_raw(),
2259                &thread_ref,
2260                &self.category_ref,
2261                &name_ref,
2262                id.into(),
2263                args.as_ptr() as *const sys::trace_arg_t,
2264                args.len(),
2265            );
2266        }
2267    }
2268
2269    pub fn write_async_end_with_inline_name(&self, id: Id, name: &str, args: &[Arg<'_>]) {
2270        let name_ref = trace_make_inline_string_ref(name);
2271        self.write_async_end(id, name_ref, args);
2272    }
2273
2274    fn write_async_instant(&self, id: Id, name_ref: sys::trace_string_ref_t, args: &[Arg<'_>]) {
2275        let ticks = zx::BootTicks::get();
2276        let thread_ref = self.register_current_thread();
2277        unsafe {
2278            sys::trace_context_write_async_instant_event_record(
2279                self.context.raw,
2280                ticks.into_raw(),
2281                &thread_ref,
2282                &self.category_ref,
2283                &name_ref,
2284                id.into(),
2285                args.as_ptr() as *const sys::trace_arg_t,
2286                args.len(),
2287            );
2288        }
2289    }
2290
2291    fn write_blob(&self, name_ref: sys::trace_string_ref_t, bytes: &[u8], args: &[Arg<'_>]) {
2292        let ticks = zx::BootTicks::get();
2293        let thread_ref = self.register_current_thread();
2294        unsafe {
2295            sys::trace_context_write_blob_event_record(
2296                self.context.raw,
2297                ticks.into_raw(),
2298                &thread_ref,
2299                &self.category_ref,
2300                &name_ref,
2301                bytes.as_ptr() as *const core::ffi::c_void,
2302                bytes.len(),
2303                args.as_ptr() as *const sys::trace_arg_t,
2304                args.len(),
2305            );
2306        }
2307    }
2308
2309    fn write_flow_begin(
2310        &self,
2311        ticks: zx::BootTicks,
2312        name_ref: sys::trace_string_ref_t,
2313        flow_id: Id,
2314        args: &[Arg<'_>],
2315    ) {
2316        let thread_ref = self.register_current_thread();
2317        unsafe {
2318            sys::trace_context_write_flow_begin_event_record(
2319                self.context.raw,
2320                ticks.into_raw(),
2321                &thread_ref,
2322                &self.category_ref,
2323                &name_ref,
2324                flow_id.into(),
2325                args.as_ptr() as *const sys::trace_arg_t,
2326                args.len(),
2327            );
2328        }
2329    }
2330
2331    fn write_flow_end(
2332        &self,
2333        ticks: zx::BootTicks,
2334        name_ref: sys::trace_string_ref_t,
2335        flow_id: Id,
2336        args: &[Arg<'_>],
2337    ) {
2338        let thread_ref = self.register_current_thread();
2339        unsafe {
2340            sys::trace_context_write_flow_end_event_record(
2341                self.context.raw,
2342                ticks.into_raw(),
2343                &thread_ref,
2344                &self.category_ref,
2345                &name_ref,
2346                flow_id.into(),
2347                args.as_ptr() as *const sys::trace_arg_t,
2348                args.len(),
2349            );
2350        }
2351    }
2352
2353    fn write_flow_step(
2354        &self,
2355        ticks: zx::BootTicks,
2356        name_ref: sys::trace_string_ref_t,
2357        flow_id: Id,
2358        args: &[Arg<'_>],
2359    ) {
2360        let thread_ref = self.register_current_thread();
2361        unsafe {
2362            sys::trace_context_write_flow_step_event_record(
2363                self.context.raw,
2364                ticks.into_raw(),
2365                &thread_ref,
2366                &self.category_ref,
2367                &name_ref,
2368                flow_id.into(),
2369                args.as_ptr() as *const sys::trace_arg_t,
2370                args.len(),
2371            );
2372        }
2373    }
2374}
2375
2376/// RAII wrapper for trace contexts without a specific associated category.
2377pub struct Context {
2378    raw: *const sys::trace_context_t,
2379}
2380
2381impl Context {
2382    #[inline]
2383    pub fn acquire() -> Option<Self> {
2384        let context = unsafe { sys::trace_acquire_context() };
2385        if context.is_null() { None } else { Some(Self { raw: context }) }
2386    }
2387
2388    #[inline]
2389    pub fn register_string_literal<T: CategoryString>(&self, s: T) -> sys::trace_string_ref_t {
2390        s.register(self)
2391    }
2392
2393    pub fn write_blob_record(
2394        &self,
2395        type_: sys::trace_blob_type_t,
2396        name_ref: &sys::trace_string_ref_t,
2397        data: &[u8],
2398    ) {
2399        unsafe {
2400            sys::trace_context_write_blob_record(
2401                self.raw,
2402                type_,
2403                name_ref as *const sys::trace_string_ref_t,
2404                data.as_ptr() as *const libc::c_void,
2405                data.len(),
2406            );
2407        }
2408    }
2409
2410    // Write fxt formatted bytes to the trace buffer
2411    //
2412    // returns Ok(num_bytes_written) on success
2413    pub fn copy_record(&self, buffer: &[u64]) -> Option<usize> {
2414        unsafe {
2415            let ptr = sys::trace_context_alloc_record(self.raw, 8 * buffer.len() as libc::size_t);
2416            if ptr == std::ptr::null_mut() {
2417                return None;
2418            }
2419            ptr.cast::<u64>().copy_from(buffer.as_ptr(), buffer.len());
2420        };
2421        Some(buffer.len())
2422    }
2423
2424    pub fn buffering_mode(&self) -> BufferingMode {
2425        match unsafe { sys::trace_context_get_buffering_mode(self.raw) } {
2426            sys::TRACE_BUFFERING_MODE_ONESHOT => BufferingMode::OneShot,
2427            sys::TRACE_BUFFERING_MODE_CIRCULAR => BufferingMode::Circular,
2428            sys::TRACE_BUFFERING_MODE_STREAMING => BufferingMode::Streaming,
2429            m => panic!("Unknown trace buffering mode: {:?}", m),
2430        }
2431    }
2432}
2433
2434impl std::ops::Drop for Context {
2435    fn drop(&mut self) {
2436        unsafe { sys::trace_release_context(self.raw) }
2437    }
2438}
2439
2440pub struct ProlongedContext {
2441    context: *const sys::trace_prolonged_context_t,
2442}
2443
2444impl ProlongedContext {
2445    pub fn acquire() -> Option<Self> {
2446        let context = unsafe { sys::trace_acquire_prolonged_context() };
2447        if context.is_null() { None } else { Some(Self { context }) }
2448    }
2449}
2450
2451impl Drop for ProlongedContext {
2452    fn drop(&mut self) {
2453        unsafe { sys::trace_release_prolonged_context(self.context) }
2454    }
2455}
2456
2457unsafe impl Send for ProlongedContext {}
2458
2459mod sys {
2460    #![allow(non_camel_case_types, unused)]
2461    use zx::sys::{zx_handle_t, zx_koid_t, zx_obj_type_t, zx_status_t, zx_ticks_t};
2462
2463    pub type trace_ticks_t = zx_ticks_t;
2464    pub type trace_counter_id_t = u64;
2465    pub type trace_async_id_t = u64;
2466    pub type trace_flow_id_t = u64;
2467    pub type trace_vthread_id_t = u64;
2468    pub type trace_thread_state_t = u32;
2469    pub type trace_cpu_number_t = u32;
2470    pub type trace_string_index_t = u32;
2471    pub type trace_thread_index_t = u32;
2472    pub type trace_context_t = libc::c_void;
2473    pub type trace_prolonged_context_t = libc::c_void;
2474
2475    pub type trace_encoded_string_ref_t = u16;
2476    pub const TRACE_ENCODED_STRING_REF_EMPTY: trace_encoded_string_ref_t = 0;
2477    pub const TRACE_ENCODED_STRING_REF_INLINE_FLAG: trace_encoded_string_ref_t = 0x8000;
2478    pub const TRACE_ENCODED_STRING_REF_LENGTH_MASK: trace_encoded_string_ref_t = 0x7fff;
2479    pub const TRACE_ENCODED_STRING_REF_MAX_LENGTH: usize = 32000;
2480    pub const TRACE_ENCODED_STRING_REF_MIN_INDEX: trace_encoded_string_ref_t = 0x1;
2481    pub const TRACE_ENCODED_STRING_REF_MAX_INDEX: trace_encoded_string_ref_t = 0x7fff;
2482
2483    pub type trace_encoded_thread_ref_t = u32;
2484    pub const TRACE_ENCODED_THREAD_REF_INLINE: trace_encoded_thread_ref_t = 0;
2485    pub const TRACE_ENCODED_THREAD_MIN_INDEX: trace_encoded_thread_ref_t = 0x01;
2486    pub const TRACE_ENCODED_THREAD_MAX_INDEX: trace_encoded_thread_ref_t = 0xff;
2487
2488    pub type trace_state_t = libc::c_int;
2489    pub const TRACE_STOPPED: trace_state_t = 0;
2490    pub const TRACE_STARTED: trace_state_t = 1;
2491    pub const TRACE_STOPPING: trace_state_t = 2;
2492
2493    pub type trace_scope_t = libc::c_int;
2494    pub const TRACE_SCOPE_THREAD: trace_scope_t = 0;
2495    pub const TRACE_SCOPE_PROCESS: trace_scope_t = 1;
2496    pub const TRACE_SCOPE_GLOBAL: trace_scope_t = 2;
2497
2498    pub type trace_blob_type_t = libc::c_int;
2499    pub const TRACE_BLOB_TYPE_DATA: trace_blob_type_t = 1;
2500    pub const TRACE_BLOB_TYPE_LAST_BRANCH: trace_blob_type_t = 2;
2501    pub const TRACE_BLOB_TYPE_PERFETTO: trace_blob_type_t = 3;
2502
2503    pub type trace_buffering_mode_t = libc::c_int;
2504    pub const TRACE_BUFFERING_MODE_ONESHOT: trace_buffering_mode_t = 0;
2505    pub const TRACE_BUFFERING_MODE_CIRCULAR: trace_buffering_mode_t = 1;
2506    pub const TRACE_BUFFERING_MODE_STREAMING: trace_buffering_mode_t = 2;
2507
2508    #[repr(C)]
2509    #[derive(Copy, Clone)]
2510    pub struct trace_string_ref_t {
2511        pub encoded_value: trace_encoded_string_ref_t,
2512        pub inline_string: *const libc::c_char,
2513    }
2514
2515    // trace_site_t is an opaque type that trace-engine uses per callsite to cache if the trace
2516    // point is enabled. Internally, it is a 8 byte allocation accessed with relaxed atomic
2517    // semantics.
2518    pub type trace_site_t = std::sync::atomic::AtomicU64;
2519
2520    // A trace_string_ref_t object is created from a string slice.
2521    // The trace_string_ref_t object is contained inside an Arg object.
2522    // whose lifetime matches the string slice to ensure that the memory
2523    // cannot be de-allocated during the trace.
2524    //
2525    // trace_string_ref_t is safe for Send + Sync because the memory that
2526    // inline_string points to is guaranteed to be valid throughout the trace.
2527    //
2528    // For more information, see the ArgValue implementation for &str in this file.
2529    unsafe impl Send for trace_string_ref_t {}
2530    unsafe impl Sync for trace_string_ref_t {}
2531
2532    #[repr(C)]
2533    pub struct trace_thread_ref_t {
2534        pub encoded_value: trace_encoded_thread_ref_t,
2535        pub inline_process_koid: zx_koid_t,
2536        pub inline_thread_koid: zx_koid_t,
2537    }
2538
2539    #[repr(C)]
2540    pub struct trace_arg_t {
2541        pub name_ref: trace_string_ref_t,
2542        pub value: trace_arg_value_t,
2543    }
2544
2545    #[repr(C)]
2546    pub union trace_arg_union_t {
2547        pub int32_value: i32,
2548        pub uint32_value: u32,
2549        pub int64_value: i64,
2550        pub uint64_value: u64,
2551        pub double_value: libc::c_double,
2552        pub string_value_ref: trace_string_ref_t,
2553        pub pointer_value: libc::uintptr_t,
2554        pub koid_value: zx_koid_t,
2555        pub bool_value: bool,
2556        pub reserved_for_future_expansion: [libc::uintptr_t; 2],
2557    }
2558
2559    pub type trace_arg_type_t = libc::c_int;
2560    pub const TRACE_ARG_NULL: trace_arg_type_t = 0;
2561    pub const TRACE_ARG_INT32: trace_arg_type_t = 1;
2562    pub const TRACE_ARG_UINT32: trace_arg_type_t = 2;
2563    pub const TRACE_ARG_INT64: trace_arg_type_t = 3;
2564    pub const TRACE_ARG_UINT64: trace_arg_type_t = 4;
2565    pub const TRACE_ARG_DOUBLE: trace_arg_type_t = 5;
2566    pub const TRACE_ARG_STRING: trace_arg_type_t = 6;
2567    pub const TRACE_ARG_POINTER: trace_arg_type_t = 7;
2568    pub const TRACE_ARG_KOID: trace_arg_type_t = 8;
2569    pub const TRACE_ARG_BOOL: trace_arg_type_t = 9;
2570
2571    #[repr(C)]
2572    pub struct trace_arg_value_t {
2573        pub type_: trace_arg_type_t,
2574        pub value: trace_arg_union_t,
2575    }
2576
2577    #[repr(C)]
2578    pub struct trace_handler_ops_t {
2579        pub is_category_enabled:
2580            unsafe fn(handler: *const trace_handler_t, category: *const libc::c_char) -> bool,
2581        pub trace_started: unsafe fn(handler: *const trace_handler_t),
2582        pub trace_stopped: unsafe fn(
2583            handler: *const trace_handler_t,
2584            async_ptr: *const (), //async_t,
2585            disposition: zx_status_t,
2586            buffer_bytes_written: libc::size_t,
2587        ),
2588        pub buffer_overflow: unsafe fn(handler: *const trace_handler_t),
2589    }
2590
2591    #[repr(C)]
2592    pub struct trace_handler_t {
2593        pub ops: *const trace_handler_ops_t,
2594    }
2595
2596    // From libtrace-engine.so
2597    unsafe extern "C" {
2598        // From trace-engine/context.h
2599
2600        pub fn trace_context_is_category_enabled(
2601            context: *const trace_context_t,
2602            category_literal: *const libc::c_char,
2603        ) -> bool;
2604
2605        pub fn trace_context_register_string_literal(
2606            context: *const trace_context_t,
2607            string_literal: *const libc::c_char,
2608            out_ref: *mut trace_string_ref_t,
2609        );
2610
2611        #[cfg(fuchsia_api_level_at_least = "27")]
2612        pub fn trace_context_register_bytestring(
2613            context: *const trace_context_t,
2614            string_literal: *const libc::c_char,
2615            length: libc::size_t,
2616            out_ref: *mut trace_string_ref_t,
2617        );
2618
2619        pub fn trace_context_register_category_literal(
2620            context: *const trace_context_t,
2621            category_literal: *const libc::c_char,
2622            out_ref: *mut trace_string_ref_t,
2623        ) -> bool;
2624
2625        pub fn trace_context_register_current_thread(
2626            context: *const trace_context_t,
2627            out_ref: *mut trace_thread_ref_t,
2628        );
2629
2630        pub fn trace_context_register_thread(
2631            context: *const trace_context_t,
2632            process_koid: zx_koid_t,
2633            thread_koid: zx_koid_t,
2634            out_ref: *mut trace_thread_ref_t,
2635        );
2636
2637        #[cfg(fuchsia_api_level_at_least = "31")]
2638        pub fn trace_context_register_vthread_by_ref(
2639            context: *const trace_context_t,
2640            process_koid: zx_koid_t,
2641            vthread_name: *const trace_string_ref_t,
2642            vthread_id: trace_vthread_id_t,
2643            out_ref: *mut trace_thread_ref_t,
2644        );
2645
2646        pub fn trace_context_write_kernel_object_record(
2647            context: *const trace_context_t,
2648            koid: zx_koid_t,
2649            type_: zx_obj_type_t,
2650            args: *const trace_arg_t,
2651            num_args: libc::size_t,
2652        );
2653
2654        pub fn trace_context_write_kernel_object_record_for_handle(
2655            context: *const trace_context_t,
2656            handle: zx_handle_t,
2657            args: *const trace_arg_t,
2658            num_args: libc::size_t,
2659        );
2660
2661        pub fn trace_context_write_process_info_record(
2662            context: *const trace_context_t,
2663            process_koid: zx_koid_t,
2664            process_name_ref: *const trace_string_ref_t,
2665        );
2666
2667        pub fn trace_context_write_thread_info_record(
2668            context: *const trace_context_t,
2669            process_koid: zx_koid_t,
2670            thread_koid: zx_koid_t,
2671            thread_name_ref: *const trace_string_ref_t,
2672        );
2673
2674        pub fn trace_context_write_context_switch_record(
2675            context: *const trace_context_t,
2676            event_time: trace_ticks_t,
2677            cpu_number: trace_cpu_number_t,
2678            outgoing_thread_state: trace_thread_state_t,
2679            outgoing_thread_ref: *const trace_thread_ref_t,
2680            incoming_thread_ref: *const trace_thread_ref_t,
2681        );
2682
2683        pub fn trace_context_write_log_record(
2684            context: *const trace_context_t,
2685            event_time: trace_ticks_t,
2686            thread_ref: *const trace_thread_ref_t,
2687            log_message: *const libc::c_char,
2688            log_message_length: libc::size_t,
2689        );
2690
2691        pub fn trace_context_write_instant_event_record(
2692            context: *const trace_context_t,
2693            event_time: trace_ticks_t,
2694            thread_ref: *const trace_thread_ref_t,
2695            category_ref: *const trace_string_ref_t,
2696            name_ref: *const trace_string_ref_t,
2697            scope: trace_scope_t,
2698            args: *const trace_arg_t,
2699            num_args: libc::size_t,
2700        );
2701
2702        pub fn trace_context_send_alert(context: *const trace_context_t, name: *const libc::c_char);
2703
2704        #[cfg(fuchsia_api_level_at_least = "27")]
2705        pub fn trace_context_send_alert_bytestring(
2706            context: *const trace_context_t,
2707            name: *const u8,
2708            length: usize,
2709        );
2710
2711        pub fn trace_context_write_counter_event_record(
2712            context: *const trace_context_t,
2713            event_time: trace_ticks_t,
2714            thread_ref: *const trace_thread_ref_t,
2715            category_ref: *const trace_string_ref_t,
2716            name_ref: *const trace_string_ref_t,
2717            counter_id: trace_counter_id_t,
2718            args: *const trace_arg_t,
2719            num_args: libc::size_t,
2720        );
2721
2722        pub fn trace_context_write_duration_event_record(
2723            context: *const trace_context_t,
2724            start_time: trace_ticks_t,
2725            end_time: trace_ticks_t,
2726            thread_ref: *const trace_thread_ref_t,
2727            category_ref: *const trace_string_ref_t,
2728            name_ref: *const trace_string_ref_t,
2729            args: *const trace_arg_t,
2730            num_args: libc::size_t,
2731        );
2732
2733        pub fn trace_context_write_blob_event_record(
2734            context: *const trace_context_t,
2735            event_time: trace_ticks_t,
2736            thread_ref: *const trace_thread_ref_t,
2737            category_ref: *const trace_string_ref_t,
2738            name_ref: *const trace_string_ref_t,
2739            blob: *const libc::c_void,
2740            blob_size: libc::size_t,
2741            args: *const trace_arg_t,
2742            num_args: libc::size_t,
2743        );
2744
2745        pub fn trace_context_write_duration_begin_event_record(
2746            context: *const trace_context_t,
2747            event_time: trace_ticks_t,
2748            thread_ref: *const trace_thread_ref_t,
2749            category_ref: *const trace_string_ref_t,
2750            name_ref: *const trace_string_ref_t,
2751            args: *const trace_arg_t,
2752            num_args: libc::size_t,
2753        );
2754
2755        pub fn trace_context_write_duration_end_event_record(
2756            context: *const trace_context_t,
2757            event_time: trace_ticks_t,
2758            thread_ref: *const trace_thread_ref_t,
2759            category_ref: *const trace_string_ref_t,
2760            name_ref: *const trace_string_ref_t,
2761            args: *const trace_arg_t,
2762            num_args: libc::size_t,
2763        );
2764
2765        pub fn trace_context_write_async_begin_event_record(
2766            context: *const trace_context_t,
2767            event_time: trace_ticks_t,
2768            thread_ref: *const trace_thread_ref_t,
2769            category_ref: *const trace_string_ref_t,
2770            name_ref: *const trace_string_ref_t,
2771            async_id: trace_async_id_t,
2772            args: *const trace_arg_t,
2773            num_args: libc::size_t,
2774        );
2775
2776        pub fn trace_context_write_async_instant_event_record(
2777            context: *const trace_context_t,
2778            event_time: trace_ticks_t,
2779            thread_ref: *const trace_thread_ref_t,
2780            category_ref: *const trace_string_ref_t,
2781            name_ref: *const trace_string_ref_t,
2782            async_id: trace_async_id_t,
2783            args: *const trace_arg_t,
2784            num_args: libc::size_t,
2785        );
2786
2787        pub fn trace_context_write_async_end_event_record(
2788            context: *const trace_context_t,
2789            event_time: trace_ticks_t,
2790            thread_ref: *const trace_thread_ref_t,
2791            category_ref: *const trace_string_ref_t,
2792            name_ref: *const trace_string_ref_t,
2793            async_id: trace_async_id_t,
2794            args: *const trace_arg_t,
2795            num_args: libc::size_t,
2796        );
2797
2798        pub fn trace_context_write_flow_begin_event_record(
2799            context: *const trace_context_t,
2800            event_time: trace_ticks_t,
2801            thread_ref: *const trace_thread_ref_t,
2802            category_ref: *const trace_string_ref_t,
2803            name_ref: *const trace_string_ref_t,
2804            flow_id: trace_flow_id_t,
2805            args: *const trace_arg_t,
2806            num_args: libc::size_t,
2807        );
2808
2809        pub fn trace_context_write_flow_step_event_record(
2810            context: *const trace_context_t,
2811            event_time: trace_ticks_t,
2812            thread_ref: *const trace_thread_ref_t,
2813            category_ref: *const trace_string_ref_t,
2814            name_ref: *const trace_string_ref_t,
2815            flow_id: trace_flow_id_t,
2816            args: *const trace_arg_t,
2817            num_args: libc::size_t,
2818        );
2819
2820        pub fn trace_context_write_flow_end_event_record(
2821            context: *const trace_context_t,
2822            event_time: trace_ticks_t,
2823            thread_ref: *const trace_thread_ref_t,
2824            category_ref: *const trace_string_ref_t,
2825            name_ref: *const trace_string_ref_t,
2826            flow_id: trace_flow_id_t,
2827            args: *const trace_arg_t,
2828            num_args: libc::size_t,
2829        );
2830
2831        pub fn trace_context_write_initialization_record(
2832            context: *const trace_context_t,
2833            ticks_per_second: u64,
2834        );
2835
2836        pub fn trace_context_write_string_record(
2837            context: *const trace_context_t,
2838            index: trace_string_index_t,
2839            string: *const libc::c_char,
2840            length: libc::size_t,
2841        );
2842
2843        pub fn trace_context_write_thread_record(
2844            context: *const trace_context_t,
2845            index: trace_thread_index_t,
2846            procss_koid: zx_koid_t,
2847            thread_koid: zx_koid_t,
2848        );
2849
2850        pub fn trace_context_write_blob_record(
2851            context: *const trace_context_t,
2852            type_: trace_blob_type_t,
2853            name_ref: *const trace_string_ref_t,
2854            data: *const libc::c_void,
2855            size: libc::size_t,
2856        );
2857
2858        pub fn trace_context_alloc_record(
2859            context: *const trace_context_t,
2860            num_bytes: libc::size_t,
2861        ) -> *mut libc::c_void;
2862
2863        // From trace-engine/instrumentation.h
2864        pub fn trace_state() -> trace_state_t;
2865
2866        #[cfg(fuchsia_api_level_at_least = "27")]
2867        pub fn trace_is_category_bytestring_enabled(
2868            category_literal: *const u8,
2869            length: usize,
2870        ) -> bool;
2871
2872        pub fn trace_is_category_enabled(category_literal: *const libc::c_char) -> bool;
2873
2874        pub fn trace_acquire_context() -> *const trace_context_t;
2875
2876        pub fn trace_acquire_context_for_category(
2877            category_literal: *const libc::c_char,
2878            out_ref: *mut trace_string_ref_t,
2879        ) -> *const trace_context_t;
2880
2881        pub fn trace_acquire_context_for_category_cached(
2882            category_literal: *const libc::c_char,
2883            trace_site: *const u64,
2884            out_ref: *mut trace_string_ref_t,
2885        ) -> *const trace_context_t;
2886
2887        #[cfg(fuchsia_api_level_at_least = "27")]
2888        pub fn trace_acquire_context_for_category_bytestring(
2889            bytes: *const u8,
2890            length: usize,
2891            out_ref: *mut trace_string_ref_t,
2892        ) -> *const trace_context_t;
2893
2894        #[cfg(fuchsia_api_level_at_least = "27")]
2895        pub fn trace_acquire_context_for_category_bytestring_cached(
2896            bytes: *const u8,
2897            length: usize,
2898            trace_site: *const u64,
2899            out_ref: *mut trace_string_ref_t,
2900        ) -> *const trace_context_t;
2901
2902        pub fn trace_release_context(context: *const trace_context_t);
2903
2904        pub fn trace_acquire_prolonged_context() -> *const trace_prolonged_context_t;
2905
2906        pub fn trace_release_prolonged_context(context: *const trace_prolonged_context_t);
2907
2908        pub fn trace_register_observer(event: zx_handle_t) -> zx_status_t;
2909
2910        pub fn trace_unregister_observer(event: zx_handle_t) -> zx_status_t;
2911
2912        pub fn trace_notify_observer_updated(event: zx_handle_t);
2913
2914        pub fn trace_context_get_buffering_mode(
2915            context: *const trace_context_t,
2916        ) -> trace_buffering_mode_t;
2917    }
2918}
2919
2920/// Arguments for `TraceFuture` and `TraceFutureExt`. Use `trace_future_args!` to construct this
2921/// object.
2922pub struct TraceFutureArgs<'a, C: CategoryString, S: AsTraceStrRef> {
2923    pub category: C,
2924    pub name: S,
2925
2926    /// The trace arguments to appear in every duration event written by the `TraceFuture`. `args`
2927    /// should be empty if `context` is `None`.
2928    pub args: Box<[Arg<'a>]>,
2929
2930    /// The flow id to use in the flow events that connect the duration events together. A flow id
2931    /// will be constructed with `Id::new()` if not provided.
2932    pub flow_id: Option<Id>,
2933
2934    /// Use `trace_future_args!` to construct this object.
2935    pub _use_trace_future_args: (),
2936}
2937
2938#[doc(hidden)]
2939#[macro_export]
2940macro_rules! __impl_trace_future_args {
2941    // This rule is matched when there are no trace arguments. Without arguments, the category
2942    // context doesn't need to be acquired to see if the args should be constructed.
2943    ($category:expr, $name:expr, $flow_id:expr) => {
2944        $crate::TraceFutureArgs {
2945            category: $category,
2946            name: $name,
2947            args: ::std::boxed::Box::new([]),
2948            flow_id: $flow_id,
2949            _use_trace_future_args: (),
2950        }
2951    };
2952    ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)*) => {{
2953        static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
2954        use $crate::AsTraceStrRef;
2955        let context = $crate::TraceCategoryContext::acquire_cached($category, &CACHE);
2956        let args: ::std::boxed::Box<[$crate::Arg<'_>]> = if let Some(context) = context {
2957            ::std::boxed::Box::new(
2958                [$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]
2959            )
2960        } else {
2961            ::std::boxed::Box::new([])
2962        };
2963        $crate::TraceFutureArgs {
2964            category: $category,
2965            name: $name,
2966            args: args,
2967            flow_id: $flow_id,
2968            _use_trace_future_args: (),
2969        }
2970    }};
2971}
2972
2973/// Macro for constructing `TraceFutureArgs`. The trace arguments won't be constructed if the
2974/// category is not enabled. If the category becomes enabled while the `TraceFuture` is still alive
2975/// then the duration events will still be written but without the trace arguments.
2976///
2977/// Example:
2978///
2979/// ```
2980/// async move {
2981///     ....
2982/// }.trace(trace_future_args!("category", "name", "x" => 5, "y" => 10)).await;
2983/// ```
2984#[macro_export]
2985macro_rules! trace_future_args {
2986    ($category:expr, $name:expr $(, $key:expr => $val:expr)* $(,)?) => {
2987        $crate::__impl_trace_future_args!($category, $name, None $(,$key => $val)*)
2988    };
2989    ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)* $(,)?) => {
2990        $crate::__impl_trace_future_args!($category, $name, Some($flow_id) $(,$key => $val)*)
2991    };
2992}
2993
2994/// Extension trait for tracing futures.
2995pub trait TraceFutureExt: Future + Sized {
2996    /// Wraps a `Future` in a `TraceFuture`.
2997    ///
2998    /// Example:
2999    ///
3000    /// ```rust
3001    /// future.trace(trace_future_args!("category", "name")).await;
3002    /// ```
3003    ///
3004    /// Which is equivalent to:
3005    ///
3006    /// ```rust
3007    /// TraceFuture::new(trace_future_args!("category", "name"), future).await;
3008    /// ```
3009    #[inline(always)]
3010    fn trace<'a, C: CategoryString, S: AsTraceStrRef>(
3011        self,
3012        args: TraceFutureArgs<'a, C, S>,
3013    ) -> TraceFuture<'a, Self, C, S> {
3014        TraceFuture::new(args, self)
3015    }
3016}
3017
3018impl<T: Future + Sized> TraceFutureExt for T {}
3019
3020/// Wraps a `Future` and writes duration events every time it's polled. The duration events are
3021/// connected by flow events.
3022#[pin_project]
3023pub struct TraceFuture<'a, Fut: Future, C: CategoryString, S: AsTraceStrRef> {
3024    // LINT.IfChange
3025    #[pin]
3026    future: Fut,
3027    category: C,
3028    name: S,
3029    // LINT.ThenChange(//src/developer/debug/zxdb/console/commands/verb_async_backtrace.cc)
3030    args: Box<[Arg<'a>]>,
3031    flow_id: Option<Id>,
3032    poll_count: u64,
3033}
3034
3035impl<'a, Fut: Future, C: CategoryString, S: AsTraceStrRef> TraceFuture<'a, Fut, C, S> {
3036    #[inline(always)]
3037    pub fn new(args: TraceFutureArgs<'a, C, S>, future: Fut) -> Self {
3038        Self {
3039            future,
3040            category: args.category,
3041            name: args.name,
3042            args: args.args,
3043            flow_id: args.flow_id,
3044            poll_count: 0,
3045        }
3046    }
3047
3048    #[cold]
3049    fn trace_poll(
3050        self: Pin<&mut Self>,
3051        context: &TraceCategoryContext,
3052        cx: &mut std::task::Context<'_>,
3053    ) -> Poll<Fut::Output> {
3054        let start_time = zx::BootTicks::get();
3055        let this = self.project();
3056        *this.poll_count = this.poll_count.saturating_add(1);
3057        let name_ref = this.name.as_trace_str_ref(context);
3058        context.write_duration_begin(start_time, name_ref, &this.args);
3059
3060        let result = this.future.poll(cx);
3061
3062        let flow_id = this.flow_id.get_or_insert_with(Id::new);
3063        let result_str: sys::trace_string_ref_t = if result.is_pending() {
3064            if *this.poll_count == 1 {
3065                context.write_flow_begin(start_time, name_ref, *flow_id, &[]);
3066            } else {
3067                context.write_flow_step(start_time, name_ref, *flow_id, &[]);
3068            }
3069            context.register_str("pending")
3070        } else {
3071            if *this.poll_count != 1 {
3072                context.write_flow_end(start_time, name_ref, *flow_id, &[]);
3073            }
3074            context.register_str("ready")
3075        };
3076        context.write_duration_end(
3077            zx::BootTicks::get(),
3078            name_ref,
3079            &[
3080                ArgValue::of_registered(context.register_str("poll-state"), result_str),
3081                ArgValue::of_registered(context.register_str("poll-count"), *this.poll_count),
3082            ],
3083        );
3084        result
3085    }
3086}
3087
3088impl<Fut: Future, C: CategoryString, S: AsTraceStrRef> Future for TraceFuture<'_, Fut, C, S> {
3089    type Output = Fut::Output;
3090    fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Fut::Output> {
3091        if let Some(context) = TraceCategoryContext::acquire(self.as_ref().get_ref().category) {
3092            self.trace_poll(&context, cx)
3093        } else {
3094            self.project().future.poll(cx)
3095        }
3096    }
3097}
3098
3099#[cfg(test)]
3100mod test {
3101    use super::{Id, trim_to_last_char_boundary};
3102
3103    #[test]
3104    fn trim_to_last_char_boundary_trims_to_last_character_boundary() {
3105        assert_eq!(b"x", trim_to_last_char_boundary("x", 5));
3106        assert_eq!(b"x", trim_to_last_char_boundary("x", 1));
3107        assert_eq!(b"", trim_to_last_char_boundary("x", 0));
3108        assert_eq!(b"xxxxx", trim_to_last_char_boundary("xxxxx", 6));
3109        assert_eq!(b"xxxxx", trim_to_last_char_boundary("xxxxx", 5));
3110        assert_eq!(b"xxxx", trim_to_last_char_boundary("xxxxx", 4));
3111
3112        assert_eq!("💩".as_bytes(), trim_to_last_char_boundary("💩", 5));
3113        assert_eq!("💩".as_bytes(), trim_to_last_char_boundary("💩", 4));
3114        assert_eq!(b"", trim_to_last_char_boundary("💩", 3));
3115    }
3116
3117    // Here, we're looking to make sure that successive calls to the function generate distinct
3118    // values. How those values are distinct is not particularly meaningful; the current
3119    // implementation yields sequential values, but that's not a behavior to rely on.
3120    #[test]
3121    fn test_id_new() {
3122        assert_ne!(Id::new(), Id::new());
3123    }
3124}