fuchsia_trace/
lib.rs

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