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