Skip to main content

fuchsia_trace/
lib.rs

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