Skip to main content

fuchsia_trace/
lib.rs

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