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