1use 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#[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#[inline]
38pub fn is_enabled() -> bool {
39 unsafe { sys::trace_state() != sys::TRACE_STOPPED }
41}
42
43pub 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#[repr(transparent)]
74#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
75pub struct Id(u64);
76
77impl Id {
78 pub fn new() -> Self {
85 Self(unsafe { sys::trace_generate_nonce() })
87 }
88
89 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 fn register(&self, context: &Context) -> sys::trace_string_ref_t;
121
122 fn acquire_context(&self) -> Option<TraceCategoryContext>;
125
126 fn acquire_context_cached(&self, site: &sys::trace_site_t) -> Option<TraceCategoryContext>;
128
129 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 fn acquire_context_cached(&self, site: &sys::trace_site_t) -> Option<TraceCategoryContext> {
163 unsafe {
164 let mut category_ref = mem::MaybeUninit::<sys::trace_string_ref_t>::uninit();
169 let raw = sys::trace_acquire_context_for_category_cached(
170 self.as_ptr(),
171 site.as_ptr(),
172 category_ref.as_mut_ptr(),
173 );
174 if raw != ptr::null() {
175 Some(TraceCategoryContext {
176 context: Context { raw },
177 category_ref: category_ref.assume_init(),
178 })
179 } else {
180 None
181 }
182 }
183 }
184
185 fn is_category_enabled(&self) -> bool {
186 unsafe { sys::trace_is_category_enabled(self.as_ptr()) }
187 }
188}
189
190#[cfg(fuchsia_api_level_at_least = "27")]
191impl CategoryString for &'static str {
192 fn register(&self, context: &Context) -> sys::trace_string_ref_t {
193 unsafe {
194 let mut self_ref = mem::MaybeUninit::<sys::trace_string_ref_t>::uninit();
195 sys::trace_context_register_bytestring(
196 context.raw,
197 self.as_ptr().cast::<libc::c_char>(),
198 self.len(),
199 self_ref.as_mut_ptr(),
200 );
201 self_ref.assume_init()
202 }
203 }
204
205 fn acquire_context(&self) -> Option<TraceCategoryContext> {
206 unsafe {
207 let mut category_ref = mem::MaybeUninit::<sys::trace_string_ref_t>::uninit();
208 let raw = sys::trace_acquire_context_for_category_bytestring(
209 self.as_ptr(),
210 self.len(),
211 category_ref.as_mut_ptr(),
212 );
213 if raw != ptr::null() {
214 Some(TraceCategoryContext {
215 context: Context { raw },
216 category_ref: category_ref.assume_init(),
217 })
218 } else {
219 None
220 }
221 }
222 }
223
224 fn acquire_context_cached(&self, site: &sys::trace_site_t) -> Option<TraceCategoryContext> {
225 unsafe {
226 let mut category_ref = mem::MaybeUninit::<sys::trace_string_ref_t>::uninit();
231 let raw = sys::trace_acquire_context_for_category_bytestring_cached(
232 self.as_ptr(),
233 self.len(),
234 site.as_ptr(),
235 category_ref.as_mut_ptr(),
236 );
237 if raw != ptr::null() {
238 Some(TraceCategoryContext {
239 context: Context { raw },
240 category_ref: category_ref.assume_init(),
241 })
242 } else {
243 None
244 }
245 }
246 }
247
248 fn is_category_enabled(&self) -> bool {
249 unsafe { sys::trace_is_category_bytestring_enabled(self.as_ptr(), self.len()) }
250 }
251}
252
253pub trait AlertString {
254 fn send_alert(&self, context: &Context);
256}
257
258impl AlertString for &CStr {
259 fn send_alert(&self, context: &Context) {
260 unsafe {
261 sys::trace_context_send_alert(context.raw, self.as_ptr());
262 }
263 }
264}
265
266#[cfg(fuchsia_api_level_at_least = "27")]
267impl AlertString for &str {
268 fn send_alert(&self, context: &Context) {
269 unsafe {
270 sys::trace_context_send_alert_bytestring(context.raw, self.as_ptr(), self.len());
271 }
272 }
273}
274
275#[cfg(fuchsia_api_level_at_least = "27")]
276impl AlertString for &String {
277 fn send_alert(&self, context: &Context) {
278 unsafe {
279 sys::trace_context_send_alert_bytestring(context.raw, self.as_ptr(), self.len());
280 }
281 }
282}
283
284pub trait AsTraceStrRef {
285 fn as_trace_str_ref(&self, context: &TraceCategoryContext) -> sys::trace_string_ref_t;
286}
287
288impl AsTraceStrRef for &'static CStr {
289 fn as_trace_str_ref(&self, context: &TraceCategoryContext) -> sys::trace_string_ref_t {
290 context.register_string_literal(*self)
291 }
292}
293
294impl AsTraceStrRef for &'static str {
298 fn as_trace_str_ref(&self, context: &TraceCategoryContext) -> sys::trace_string_ref_t {
299 context.register_str(self)
300 }
301}
302
303impl AsTraceStrRef for String {
304 fn as_trace_str_ref(&self, _context: &TraceCategoryContext) -> sys::trace_string_ref_t {
305 trace_make_inline_string_ref(self.as_str())
306 }
307}
308
309impl<T: AsTraceStrRef> AsTraceStrRef for &T {
311 fn as_trace_str_ref(&self, context: &TraceCategoryContext) -> sys::trace_string_ref_t {
312 (*self).as_trace_str_ref(context)
313 }
314}
315
316#[repr(transparent)]
318pub struct Arg<'a>(sys::trace_arg_t, PhantomData<&'a ()>);
319
320pub trait ArgValue {
326 fn of<'a>(key: &'a str, value: Self) -> Arg<'a>
327 where
328 Self: 'a;
329 fn of_registered<'a>(name_ref: sys::trace_string_ref_t, value: Self) -> Arg<'a>
330 where
331 Self: 'a;
332}
333
334macro_rules! arg_from {
340 ($valname:ident, $(($type:ty, $tag:expr, $value:expr))*) => {
341 $(
342 impl ArgValue for $type {
343 #[inline]
344 fn of<'a>(key: &'a str, $valname: Self) -> Arg<'a>
345 where Self: 'a
346 {
347 #[allow(unused)]
348 let $valname = $valname;
349
350 Arg(sys::trace_arg_t {
351 name_ref: trace_make_inline_string_ref(key),
352 value: sys::trace_arg_value_t {
353 type_: $tag,
354 value: $value,
355 },
356 }, PhantomData)
357 }
358 #[inline]
359 fn of_registered<'a>(name_ref: sys::trace_string_ref_t, $valname: Self) -> Arg<'a>
360 where Self: 'a
361 {
362 #[allow(unused)]
363 let $valname = $valname;
364
365 Arg(sys::trace_arg_t {
366 name_ref,
367 value: sys::trace_arg_value_t {
368 type_: $tag,
369 value: $value,
370 },
371 }, PhantomData)
372 }
373 }
374 )*
375 }
376}
377
378#[rustfmt::skip]
380arg_from!(val,
381 ((), sys::TRACE_ARG_NULL, sys::trace_arg_union_t { int32_value: 0 })
382 (bool, sys::TRACE_ARG_BOOL, sys::trace_arg_union_t { bool_value: val })
383 (i32, sys::TRACE_ARG_INT32, sys::trace_arg_union_t { int32_value: val })
384 (u32, sys::TRACE_ARG_UINT32, sys::trace_arg_union_t { uint32_value: val })
385 (i64, sys::TRACE_ARG_INT64, sys::trace_arg_union_t { int64_value: val })
386 (u64, sys::TRACE_ARG_UINT64, sys::trace_arg_union_t { uint64_value: val })
387 (isize, sys::TRACE_ARG_INT64, sys::trace_arg_union_t { int64_value: val as i64 })
388 (usize, sys::TRACE_ARG_UINT64, sys::trace_arg_union_t { uint64_value: val as u64 })
389 (f64, sys::TRACE_ARG_DOUBLE, sys::trace_arg_union_t { double_value: val })
390 (zx::Koid, sys::TRACE_ARG_KOID, sys::trace_arg_union_t { koid_value: val.raw_koid() })
391);
392
393impl<T> ArgValue for *const T {
394 #[inline]
395 fn of<'a>(key: &'a str, val: Self) -> Arg<'a>
396 where
397 Self: 'a,
398 {
399 Arg(
400 sys::trace_arg_t {
401 name_ref: trace_make_inline_string_ref(key),
402 value: sys::trace_arg_value_t {
403 type_: sys::TRACE_ARG_POINTER,
404 value: sys::trace_arg_union_t { pointer_value: val as usize },
405 },
406 },
407 PhantomData,
408 )
409 }
410 #[inline]
411 fn of_registered<'a>(name_ref: sys::trace_string_ref_t, val: Self) -> Arg<'a>
412 where
413 Self: 'a,
414 {
415 Arg(
416 sys::trace_arg_t {
417 name_ref,
418 value: sys::trace_arg_value_t {
419 type_: sys::TRACE_ARG_POINTER,
420 value: sys::trace_arg_union_t { pointer_value: val as usize },
421 },
422 },
423 PhantomData,
424 )
425 }
426}
427
428impl<T> ArgValue for *mut T {
429 #[inline]
430 fn of<'a>(key: &'a str, val: Self) -> Arg<'a>
431 where
432 Self: 'a,
433 {
434 Arg(
435 sys::trace_arg_t {
436 name_ref: trace_make_inline_string_ref(key),
437 value: sys::trace_arg_value_t {
438 type_: sys::TRACE_ARG_POINTER,
439 value: sys::trace_arg_union_t { pointer_value: val as usize },
440 },
441 },
442 PhantomData,
443 )
444 }
445 #[inline]
446 fn of_registered<'a>(name_ref: sys::trace_string_ref_t, val: Self) -> Arg<'a>
447 where
448 Self: 'a,
449 {
450 Arg(
451 sys::trace_arg_t {
452 name_ref,
453 value: sys::trace_arg_value_t {
454 type_: sys::TRACE_ARG_POINTER,
455 value: sys::trace_arg_union_t { pointer_value: val as usize },
456 },
457 },
458 PhantomData,
459 )
460 }
461}
462
463impl<'a> ArgValue for &'a str {
464 #[inline]
465 fn of<'b>(key: &'b str, val: Self) -> Arg<'b>
466 where
467 Self: 'b,
468 {
469 Arg(
470 sys::trace_arg_t {
471 name_ref: trace_make_inline_string_ref(key),
472 value: sys::trace_arg_value_t {
473 type_: sys::TRACE_ARG_STRING,
474 value: sys::trace_arg_union_t {
475 string_value_ref: trace_make_inline_string_ref(val),
476 },
477 },
478 },
479 PhantomData,
480 )
481 }
482 #[inline]
483 fn of_registered<'b>(name_ref: sys::trace_string_ref_t, val: Self) -> Arg<'b>
484 where
485 Self: 'b,
486 {
487 Arg(
488 sys::trace_arg_t {
489 name_ref,
490 value: sys::trace_arg_value_t {
491 type_: sys::TRACE_ARG_STRING,
492 value: sys::trace_arg_union_t {
493 string_value_ref: trace_make_inline_string_ref(val),
494 },
495 },
496 },
497 PhantomData,
498 )
499 }
500}
501
502impl<'a> ArgValue for sys::trace_string_ref_t {
503 #[inline]
504 fn of<'b>(key: &'b str, val: Self) -> Arg<'b>
505 where
506 Self: 'b,
507 {
508 Arg(
509 sys::trace_arg_t {
510 name_ref: trace_make_inline_string_ref(key),
511 value: sys::trace_arg_value_t {
512 type_: sys::TRACE_ARG_STRING,
513 value: sys::trace_arg_union_t { string_value_ref: val },
514 },
515 },
516 PhantomData,
517 )
518 }
519 #[inline]
520 fn of_registered<'b>(name_ref: sys::trace_string_ref_t, val: Self) -> Arg<'b>
521 where
522 Self: 'b,
523 {
524 Arg(
525 sys::trace_arg_t {
526 name_ref,
527 value: sys::trace_arg_value_t {
528 type_: sys::TRACE_ARG_STRING,
529 value: sys::trace_arg_union_t { string_value_ref: val },
530 },
531 },
532 PhantomData,
533 )
534 }
535}
536
537#[macro_export]
559macro_rules! instant {
560 ($category:expr, $name:expr, $scope:expr $(, $key:expr => $val:expr)*) => {
561 {
562 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
563 use $crate::AsTraceStrRef;
564 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
565 $crate::instant(&context, $name, $scope, &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]);
566 }
567 }
568 }
569}
570
571#[inline]
574pub fn instant<S: AsTraceStrRef>(
575 context: &TraceCategoryContext,
576 name: S,
577 scope: Scope,
578 args: &[Arg<'_>],
579) {
580 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
581
582 let name_ref = name.as_trace_str_ref(context);
583 context.write_instant(name_ref, scope, args);
584}
585
586#[macro_export]
600macro_rules! alert {
601 ($category:expr, $name:expr) => {
602 $crate::alert($category, $name)
603 };
604}
605
606pub fn alert<C: CategoryString, S: AlertString>(category: C, name: S) {
608 if let Some(context) = category.acquire_context() {
609 name.send_alert(&context.context);
610 }
611}
612
613#[macro_export]
630macro_rules! counter {
631 ($category:expr, $name:expr, $counter_id:expr $(, $key:expr => $val:expr)*) => {
632 {
633 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
634 use $crate::AsTraceStrRef;
635 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
636 $crate::counter(&context, $name, $counter_id,
637 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
638 }
639 }
640 }
641}
642
643pub fn counter<S: AsTraceStrRef>(
653 context: &TraceCategoryContext,
654 name: S,
655 counter_id: u64,
656 args: &[Arg<'_>],
657) {
658 assert!(args.len() >= 1, "trace counter args must include at least one numeric argument");
659 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
660
661 let name_ref = name.as_trace_str_ref(context);
662 context.write_counter(name_ref, counter_id, args);
663}
664
665#[must_use = "DurationScope must be `end`ed to be recorded"]
668pub struct DurationScope<'a, C: CategoryString, S: AsTraceStrRef> {
669 category: C,
670 name: S,
671 args: &'a [Arg<'a>],
672 start_time: zx::BootTicks,
673}
674
675impl<'a, C: CategoryString, S: AsTraceStrRef> DurationScope<'a, C, S> {
676 pub fn begin(category: C, name: S, args: &'a [Arg<'_>]) -> Self {
679 let start_time = zx::BootTicks::get();
680 Self { category, name, args, start_time }
681 }
682}
683
684impl<'a, C: CategoryString, S: AsTraceStrRef> Drop for DurationScope<'a, C, S> {
685 fn drop(&mut self) {
686 if let Some(context) = TraceCategoryContext::acquire(self.category) {
687 let name_ref = self.name.as_trace_str_ref(&context);
688 context.write_duration(name_ref, self.start_time, self.args);
689 }
690 }
691}
692
693pub fn complete_duration<C: CategoryString, S: AsTraceStrRef>(
695 category: C,
696 name: S,
697 start_time: zx::BootTicks,
698 args: &[Arg<'_>],
699) {
700 if let Some(context) = TraceCategoryContext::acquire(category) {
701 let name_ref = name.as_trace_str_ref(&context);
702 context.write_duration(name_ref, start_time, args);
703 }
704}
705
706#[macro_export]
741macro_rules! duration {
742 ($category:expr, $name:expr $(, $key:expr => $val:expr)* $(,)?) => {
743 let mut args;
744 let _scope = {
745 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
746 use $crate::AsTraceStrRef;
753 if let Some(context) =
754 $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
755 args = [$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*];
756 Some($crate::duration($category, $name, &args))
757 } else {
758 None
759 }
760 };
761 }
762}
763
764pub fn duration<'a, C: CategoryString, S: AsTraceStrRef>(
776 category: C,
777 name: S,
778 args: &'a [Arg<'_>],
779) -> DurationScope<'a, C, S> {
780 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
781 DurationScope::begin(category, name, args)
782}
783
784#[macro_export]
798macro_rules! duration_begin {
799 ($category:expr, $name:expr $(, $key:expr => $val:expr)* $(,)?) => {
800 {
801 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
802 use $crate::AsTraceStrRef;
803 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
804 $crate::duration_begin(&context, $name,
805 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
806 }
807 }
808 };
809}
810
811#[macro_export]
825macro_rules! duration_end {
826 ($category:expr, $name:expr $(, $key:expr => $val:expr)* $(,)?) => {
827 {
828 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
829 use $crate::AsTraceStrRef;
830 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
831 $crate::duration_end(&context, $name, &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
832 }
833 }
834 };
835}
836
837pub fn duration_begin<S: AsTraceStrRef>(context: &TraceCategoryContext, name: S, args: &[Arg<'_>]) {
848 let ticks = zx::BootTicks::get();
849 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
850
851 let name_ref = name.as_trace_str_ref(&context);
852 context.write_duration_begin(ticks, name_ref, args);
853}
854
855pub fn duration_end<S: AsTraceStrRef>(context: &TraceCategoryContext, name: S, args: &[Arg<'_>]) {
865 let ticks = zx::BootTicks::get();
866 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
867
868 let name_ref = name.as_trace_str_ref(&context);
869 context.write_duration_end(ticks, name_ref, args);
870}
871
872#[must_use = "emits an end event when dropped, so if dropped immediately creates an essentially \
875 zero length duration that should just be an instant instead"]
876pub struct AsyncScope<C: CategoryString = &'static CStr, S: AsTraceStrRef = &'static CStr> {
877 id: Id,
880 category: C,
881 name: S,
882}
883
884impl<C: CategoryString, S: AsTraceStrRef> AsyncScope<C, S> {
885 pub fn begin(id: Id, category: C, name: S, args: &[Arg<'_>]) -> Self {
888 async_begin(id, category, &name, args);
889 Self { id, category, name }
890 }
891
892 pub fn end(self, args: &[Arg<'_>]) {
895 async_end(self.id, self.category, &self.name, args);
896 std::mem::forget(self);
897 }
898}
899
900impl<C: CategoryString, S: AsTraceStrRef> Drop for AsyncScope<C, S> {
901 fn drop(&mut self) {
902 async_end(self.id, self.category, &self.name, &[]);
906 }
907}
908
909pub fn async_enter<C: CategoryString, S: AsTraceStrRef>(
918 id: Id,
919 category: C,
920 name: S,
921 args: &[Arg<'_>],
922) -> AsyncScope<C, S> {
923 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
924 AsyncScope::begin(id, category, name, args)
925}
926
927#[macro_export]
958macro_rules! async_enter {
959 ($id:expr, $category:expr, $name:expr $(, $key:expr => $val:expr)*) => {
960 {
961 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
962 use $crate::AsTraceStrRef;
963 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
964 Some($crate::AsyncScope::begin($id, $category, $name, &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]))
965 } else {
966 None
967 }
968 }
969 }
970}
971
972#[macro_export]
996macro_rules! async_instant {
997 ($id:expr, $category:expr, $name:expr $(, $key:expr => $val:expr)*) => {
998 {
999 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1000 use $crate::AsTraceStrRef;
1001 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1002 $crate::async_instant($id, &context, $name, &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]);
1003 }
1004 }
1005 }
1006}
1007
1008pub fn async_begin<C: CategoryString, S: AsTraceStrRef>(
1020 id: Id,
1021 category: C,
1022 name: S,
1023 args: &[Arg<'_>],
1024) {
1025 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1026
1027 if let Some(context) = TraceCategoryContext::acquire(category) {
1028 let name_ref = name.as_trace_str_ref(&context);
1029 context.write_async_begin(id, name_ref, args);
1030 }
1031}
1032
1033pub fn async_end<C: CategoryString, S: AsTraceStrRef>(
1045 id: Id,
1046 category: C,
1047 name: S,
1048 args: &[Arg<'_>],
1049) {
1050 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1051
1052 if let Some(context) = TraceCategoryContext::acquire(category) {
1053 let name_ref = name.as_trace_str_ref(&context);
1054 context.write_async_end(id, name_ref, args);
1055 }
1056}
1057
1058pub fn async_instant<S: AsTraceStrRef>(
1070 id: Id,
1071 context: &TraceCategoryContext,
1072 name: S,
1073 args: &[Arg<'_>],
1074) {
1075 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1076
1077 let name_ref = name.as_trace_str_ref(context);
1078 context.write_async_instant(id, name_ref, args);
1079}
1080
1081#[macro_export]
1082macro_rules! blob {
1083 ($category:expr, $name:expr, $bytes:expr $(, $key:expr => $val:expr)*) => {
1084 {
1085 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1086 use $crate::AsTraceStrRef;
1087 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1088 $crate::blob_fn(&context, $name, $bytes, &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
1089 }
1090 }
1091 }
1092}
1093pub fn blob_fn<S: AsTraceStrRef>(
1094 context: &TraceCategoryContext,
1095 name: S,
1096 bytes: &[u8],
1097 args: &[Arg<'_>],
1098) {
1099 let name_ref = name.as_trace_str_ref(context);
1100 context.write_blob(name_ref, bytes, args);
1101}
1102
1103#[macro_export]
1118macro_rules! flow_begin {
1119 ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)*) => {
1120 {
1121 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1122 use $crate::AsTraceStrRef;
1123 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1124 $crate::flow_begin(&context, $name, $flow_id,
1125 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
1126 }
1127 }
1128 }
1129}
1130
1131#[macro_export]
1146macro_rules! flow_step {
1147 ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)*) => {
1148 {
1149 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1150 use $crate::AsTraceStrRef;
1151 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1152 $crate::flow_step(&context, $name, $flow_id,
1153 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
1154 }
1155 }
1156 }
1157}
1158
1159#[macro_export]
1174macro_rules! flow_end {
1175 ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)*) => {
1176 {
1177 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1178 use $crate::AsTraceStrRef;
1179 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1180 $crate::flow_end(&context, $name, $flow_id,
1181 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
1182 }
1183 }
1184 }
1185}
1186
1187pub fn flow_begin<S: AsTraceStrRef>(
1206 context: &TraceCategoryContext,
1207 name: S,
1208 flow_id: Id,
1209 args: &[Arg<'_>],
1210) {
1211 let ticks = zx::BootTicks::get();
1212 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1213
1214 let name_ref = name.as_trace_str_ref(context);
1215 context.write_flow_begin(ticks, name_ref, flow_id, args);
1216}
1217
1218pub fn flow_end<S: AsTraceStrRef>(
1235 context: &TraceCategoryContext,
1236 name: S,
1237 flow_id: Id,
1238 args: &[Arg<'_>],
1239) {
1240 let ticks = zx::BootTicks::get();
1241 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1242
1243 let name_ref = name.as_trace_str_ref(context);
1244 context.write_flow_end(ticks, name_ref, flow_id, args);
1245}
1246
1247pub fn flow_step<S: AsTraceStrRef>(
1264 context: &TraceCategoryContext,
1265 name: S,
1266 flow_id: Id,
1267 args: &[Arg<'_>],
1268) {
1269 let ticks = zx::BootTicks::get();
1270 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1271
1272 let name_ref = name.as_trace_str_ref(context);
1273 context.write_flow_step(ticks, name_ref, flow_id, args);
1274}
1275
1276#[macro_export]
1290macro_rules! instaflow_begin {
1291 (
1292 $category:expr,
1293 $flow_name:expr,
1294 $step_name:expr,
1295 $flow_id:expr
1296 $(, $key:expr => $val:expr)*
1297 ) => {
1298 {
1299 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1300 use $crate::AsTraceStrRef;
1301 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1302 $crate::instaflow_begin(
1303 &context,
1304 $flow_name,
1305 $step_name,
1306 $flow_id,
1307 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*],
1308 )
1309 }
1310 }
1311 }
1312}
1313
1314#[macro_export]
1328macro_rules! instaflow_end {
1329 (
1330 $category:expr,
1331 $flow_name:expr,
1332 $step_name:expr,
1333 $flow_id:expr
1334 $(, $key:expr => $val:expr)*
1335 ) => {
1336 {
1337 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1338 use $crate::AsTraceStrRef;
1339 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1340 $crate::instaflow_end(
1341 &context,
1342 $flow_name,
1343 $step_name,
1344 $flow_id,
1345 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*],
1346 )
1347 }
1348 }
1349 }
1350}
1351
1352#[macro_export]
1366macro_rules! instaflow_step {
1367 (
1368 $category:expr,
1369 $flow_name:expr,
1370 $step_name:expr,
1371 $flow_id:expr
1372 $(, $key:expr => $val:expr)*
1373 ) => {
1374 {
1375 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1376 use $crate::AsTraceStrRef;
1377 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1378 $crate::instaflow_step(
1379 &context,
1380 $flow_name,
1381 $step_name,
1382 $flow_id,
1383 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*],
1384 )
1385 }
1386 }
1387 }
1388}
1389
1390pub fn instaflow_begin<S1: AsTraceStrRef, S2: AsTraceStrRef>(
1402 context: &TraceCategoryContext,
1403 flow_name: S1,
1404 step_name: S2,
1405 flow_id: Id,
1406 args: &[Arg<'_>],
1407) {
1408 let ticks = zx::BootTicks::get();
1409 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1410
1411 let flow_name_ref = flow_name.as_trace_str_ref(context);
1412 let step_name_ref = step_name.as_trace_str_ref(context);
1413
1414 context.write_duration_begin(ticks, step_name_ref, args);
1415 context.write_flow_begin(ticks, flow_name_ref, flow_id, args);
1416 context.write_duration_end(ticks, step_name_ref, args);
1417}
1418
1419pub fn instaflow_end<S1: AsTraceStrRef, S2: AsTraceStrRef>(
1431 context: &TraceCategoryContext,
1432 flow_name: S1,
1433 step_name: S2,
1434 flow_id: Id,
1435 args: &[Arg<'_>],
1436) {
1437 let ticks = zx::BootTicks::get();
1438 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1439
1440 let flow_name_ref = flow_name.as_trace_str_ref(context);
1441 let step_name_ref = step_name.as_trace_str_ref(context);
1442
1443 context.write_duration_begin(ticks, step_name_ref, args);
1444 context.write_flow_end(ticks, flow_name_ref, flow_id, args);
1445 context.write_duration_end(ticks, step_name_ref, args);
1446}
1447
1448pub fn instaflow_step<S1: AsTraceStrRef, S2: AsTraceStrRef>(
1460 context: &TraceCategoryContext,
1461 flow_name: S1,
1462 step_name: S2,
1463 flow_id: Id,
1464 args: &[Arg<'_>],
1465) {
1466 let ticks = zx::BootTicks::get();
1467 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1468
1469 let flow_name_ref = flow_name.as_trace_str_ref(context);
1470 let step_name_ref = step_name.as_trace_str_ref(context);
1471
1472 context.write_duration_begin(ticks, step_name_ref, args);
1473 context.write_flow_step(ticks, flow_name_ref, flow_id, args);
1474 context.write_duration_end(ticks, step_name_ref, args);
1475}
1476
1477const fn trace_make_empty_string_ref() -> sys::trace_string_ref_t {
1479 sys::trace_string_ref_t {
1480 encoded_value: sys::TRACE_ENCODED_STRING_REF_EMPTY,
1481 inline_string: ptr::null(),
1482 }
1483}
1484
1485#[inline]
1486fn trim_to_last_char_boundary(string: &str, max_len: usize) -> &[u8] {
1487 let mut len = string.len();
1488 if string.len() > max_len {
1489 len = max_len;
1493 while len > 0 {
1494 if string.is_char_boundary(len - 1) && string.is_char_boundary(len) {
1495 break;
1496 }
1497 len -= 1;
1498 }
1499 }
1500 &string.as_bytes()[0..len]
1501}
1502
1503#[inline]
1506fn trace_make_inline_string_ref(string: &str) -> sys::trace_string_ref_t {
1507 let len = string.len() as u16;
1508 if len == 0 {
1509 return trace_make_empty_string_ref();
1510 }
1511
1512 let string = trim_to_last_char_boundary(string, sys::TRACE_ENCODED_STRING_REF_MAX_LENGTH);
1513
1514 sys::trace_string_ref_t {
1515 encoded_value: sys::TRACE_ENCODED_STRING_REF_INLINE_FLAG | len,
1516 inline_string: string.as_ptr() as *const libc::c_char,
1517 }
1518}
1519
1520pub struct TraceCategoryContext {
1522 context: Context,
1523 category_ref: sys::trace_string_ref_t,
1524}
1525
1526impl TraceCategoryContext {
1527 #[inline]
1528 pub fn acquire_cached<C: CategoryString>(
1529 category: C,
1530 site: &sys::trace_site_t,
1531 ) -> Option<TraceCategoryContext> {
1532 category.acquire_context_cached(site)
1533 }
1534
1535 pub fn acquire<C: CategoryString>(category: C) -> Option<TraceCategoryContext> {
1536 category.acquire_context()
1537 }
1538
1539 #[inline]
1540 pub fn register_string_literal<T: CategoryString>(&self, name: T) -> sys::trace_string_ref_t {
1541 name.register(&self.context)
1542 }
1543
1544 #[inline]
1545 #[cfg(fuchsia_api_level_at_least = "27")]
1546 pub fn register_str(&self, name: &'static str) -> sys::trace_string_ref_t {
1547 unsafe {
1548 let mut name_ref = mem::MaybeUninit::<sys::trace_string_ref_t>::uninit();
1549 sys::trace_context_register_bytestring(
1550 self.context.raw,
1551 name.as_ptr().cast::<libc::c_char>(),
1552 name.len(),
1553 name_ref.as_mut_ptr(),
1554 );
1555 name_ref.assume_init()
1556 }
1557 }
1558 #[inline]
1559 #[cfg(not(fuchsia_api_level_at_least = "27"))]
1560 pub fn register_str(&self, name: &'static str) -> sys::trace_string_ref_t {
1561 trace_make_inline_string_ref(name)
1562 }
1563
1564 #[inline]
1565 fn register_current_thread(&self) -> sys::trace_thread_ref_t {
1566 unsafe {
1567 let mut thread_ref = mem::MaybeUninit::<sys::trace_thread_ref_t>::uninit();
1568 sys::trace_context_register_current_thread(self.context.raw, thread_ref.as_mut_ptr());
1569 thread_ref.assume_init()
1570 }
1571 }
1572
1573 #[inline]
1574 pub fn write_instant(&self, name_ref: sys::trace_string_ref_t, scope: Scope, args: &[Arg<'_>]) {
1575 let ticks = zx::BootTicks::get();
1576 let thread_ref = self.register_current_thread();
1577 unsafe {
1578 sys::trace_context_write_instant_event_record(
1579 self.context.raw,
1580 ticks.into_raw(),
1581 &thread_ref,
1582 &self.category_ref,
1583 &name_ref,
1584 scope.into_raw(),
1585 args.as_ptr() as *const sys::trace_arg_t,
1586 args.len(),
1587 );
1588 }
1589 }
1590
1591 pub fn write_instant_with_inline_name(&self, name: &str, scope: Scope, args: &[Arg<'_>]) {
1592 let name_ref = trace_make_inline_string_ref(name);
1593 self.write_instant(name_ref, scope, args)
1594 }
1595
1596 fn write_counter(&self, name_ref: sys::trace_string_ref_t, counter_id: u64, args: &[Arg<'_>]) {
1597 let ticks = zx::BootTicks::get();
1598 let thread_ref = self.register_current_thread();
1599 unsafe {
1600 sys::trace_context_write_counter_event_record(
1601 self.context.raw,
1602 ticks.into_raw(),
1603 &thread_ref,
1604 &self.category_ref,
1605 &name_ref,
1606 counter_id,
1607 args.as_ptr() as *const sys::trace_arg_t,
1608 args.len(),
1609 );
1610 }
1611 }
1612
1613 pub fn write_counter_with_inline_name(&self, name: &str, counter_id: u64, args: &[Arg<'_>]) {
1614 let name_ref = trace_make_inline_string_ref(name);
1615 self.write_counter(name_ref, counter_id, args);
1616 }
1617
1618 fn write_duration(
1619 &self,
1620 name_ref: sys::trace_string_ref_t,
1621 start_time: zx::BootTicks,
1622 args: &[Arg<'_>],
1623 ) {
1624 let ticks = zx::BootTicks::get();
1625 let thread_ref = self.register_current_thread();
1626 unsafe {
1627 sys::trace_context_write_duration_event_record(
1628 self.context.raw,
1629 start_time.into_raw(),
1630 ticks.into_raw(),
1631 &thread_ref,
1632 &self.category_ref,
1633 &name_ref,
1634 args.as_ptr() as *const sys::trace_arg_t,
1635 args.len(),
1636 );
1637 }
1638 }
1639
1640 pub fn write_duration_with_inline_name(
1641 &self,
1642 name: &str,
1643 start_time: zx::BootTicks,
1644 args: &[Arg<'_>],
1645 ) {
1646 let name_ref = trace_make_inline_string_ref(name);
1647 self.write_duration(name_ref, start_time, args);
1648 }
1649
1650 fn write_duration_begin(
1651 &self,
1652 ticks: zx::BootTicks,
1653 name_ref: sys::trace_string_ref_t,
1654 args: &[Arg<'_>],
1655 ) {
1656 let thread_ref = self.register_current_thread();
1657 unsafe {
1658 sys::trace_context_write_duration_begin_event_record(
1659 self.context.raw,
1660 ticks.into_raw(),
1661 &thread_ref,
1662 &self.category_ref,
1663 &name_ref,
1664 args.as_ptr() as *const sys::trace_arg_t,
1665 args.len(),
1666 );
1667 }
1668 }
1669
1670 pub fn write_duration_begin_with_inline_name(&self, name: &str, args: &[Arg<'_>]) {
1671 let name_ref = trace_make_inline_string_ref(name);
1672 self.write_duration_begin(zx::BootTicks::get(), name_ref, args);
1673 }
1674
1675 fn write_duration_end(
1676 &self,
1677 ticks: zx::BootTicks,
1678 name_ref: sys::trace_string_ref_t,
1679 args: &[Arg<'_>],
1680 ) {
1681 let thread_ref = self.register_current_thread();
1682 unsafe {
1683 sys::trace_context_write_duration_end_event_record(
1684 self.context.raw,
1685 ticks.into_raw(),
1686 &thread_ref,
1687 &self.category_ref,
1688 &name_ref,
1689 args.as_ptr() as *const sys::trace_arg_t,
1690 args.len(),
1691 );
1692 }
1693 }
1694
1695 pub fn write_duration_end_with_inline_name(&self, name: &str, args: &[Arg<'_>]) {
1696 let name_ref = trace_make_inline_string_ref(name);
1697 self.write_duration_end(zx::BootTicks::get(), name_ref, args);
1698 }
1699
1700 fn write_async_begin(&self, id: Id, name_ref: sys::trace_string_ref_t, args: &[Arg<'_>]) {
1701 let ticks = zx::BootTicks::get();
1702 let thread_ref = self.register_current_thread();
1703 unsafe {
1704 sys::trace_context_write_async_begin_event_record(
1705 self.context.raw,
1706 ticks.into_raw(),
1707 &thread_ref,
1708 &self.category_ref,
1709 &name_ref,
1710 id.into(),
1711 args.as_ptr() as *const sys::trace_arg_t,
1712 args.len(),
1713 );
1714 }
1715 }
1716
1717 pub fn write_async_begin_with_inline_name(&self, id: Id, name: &str, args: &[Arg<'_>]) {
1718 let name_ref = trace_make_inline_string_ref(name);
1719 self.write_async_begin(id, name_ref, args);
1720 }
1721
1722 fn write_async_end(&self, id: Id, name_ref: sys::trace_string_ref_t, args: &[Arg<'_>]) {
1723 let ticks = zx::BootTicks::get();
1724 let thread_ref = self.register_current_thread();
1725 unsafe {
1726 sys::trace_context_write_async_end_event_record(
1727 self.context.raw,
1728 ticks.into_raw(),
1729 &thread_ref,
1730 &self.category_ref,
1731 &name_ref,
1732 id.into(),
1733 args.as_ptr() as *const sys::trace_arg_t,
1734 args.len(),
1735 );
1736 }
1737 }
1738
1739 pub fn write_async_end_with_inline_name(&self, id: Id, name: &str, args: &[Arg<'_>]) {
1740 let name_ref = trace_make_inline_string_ref(name);
1741 self.write_async_end(id, name_ref, args);
1742 }
1743
1744 fn write_async_instant(&self, id: Id, name_ref: sys::trace_string_ref_t, args: &[Arg<'_>]) {
1745 let ticks = zx::BootTicks::get();
1746 let thread_ref = self.register_current_thread();
1747 unsafe {
1748 sys::trace_context_write_async_instant_event_record(
1749 self.context.raw,
1750 ticks.into_raw(),
1751 &thread_ref,
1752 &self.category_ref,
1753 &name_ref,
1754 id.into(),
1755 args.as_ptr() as *const sys::trace_arg_t,
1756 args.len(),
1757 );
1758 }
1759 }
1760
1761 fn write_blob(&self, name_ref: sys::trace_string_ref_t, bytes: &[u8], args: &[Arg<'_>]) {
1762 let ticks = zx::BootTicks::get();
1763 let thread_ref = self.register_current_thread();
1764 unsafe {
1765 sys::trace_context_write_blob_event_record(
1766 self.context.raw,
1767 ticks.into_raw(),
1768 &thread_ref,
1769 &self.category_ref,
1770 &name_ref,
1771 bytes.as_ptr() as *const core::ffi::c_void,
1772 bytes.len(),
1773 args.as_ptr() as *const sys::trace_arg_t,
1774 args.len(),
1775 );
1776 }
1777 }
1778
1779 fn write_flow_begin(
1780 &self,
1781 ticks: zx::BootTicks,
1782 name_ref: sys::trace_string_ref_t,
1783 flow_id: Id,
1784 args: &[Arg<'_>],
1785 ) {
1786 let thread_ref = self.register_current_thread();
1787 unsafe {
1788 sys::trace_context_write_flow_begin_event_record(
1789 self.context.raw,
1790 ticks.into_raw(),
1791 &thread_ref,
1792 &self.category_ref,
1793 &name_ref,
1794 flow_id.into(),
1795 args.as_ptr() as *const sys::trace_arg_t,
1796 args.len(),
1797 );
1798 }
1799 }
1800
1801 fn write_flow_end(
1802 &self,
1803 ticks: zx::BootTicks,
1804 name_ref: sys::trace_string_ref_t,
1805 flow_id: Id,
1806 args: &[Arg<'_>],
1807 ) {
1808 let thread_ref = self.register_current_thread();
1809 unsafe {
1810 sys::trace_context_write_flow_end_event_record(
1811 self.context.raw,
1812 ticks.into_raw(),
1813 &thread_ref,
1814 &self.category_ref,
1815 &name_ref,
1816 flow_id.into(),
1817 args.as_ptr() as *const sys::trace_arg_t,
1818 args.len(),
1819 );
1820 }
1821 }
1822
1823 fn write_flow_step(
1824 &self,
1825 ticks: zx::BootTicks,
1826 name_ref: sys::trace_string_ref_t,
1827 flow_id: Id,
1828 args: &[Arg<'_>],
1829 ) {
1830 let thread_ref = self.register_current_thread();
1831 unsafe {
1832 sys::trace_context_write_flow_step_event_record(
1833 self.context.raw,
1834 ticks.into_raw(),
1835 &thread_ref,
1836 &self.category_ref,
1837 &name_ref,
1838 flow_id.into(),
1839 args.as_ptr() as *const sys::trace_arg_t,
1840 args.len(),
1841 );
1842 }
1843 }
1844}
1845
1846pub struct Context {
1848 raw: *const sys::trace_context_t,
1849}
1850
1851impl Context {
1852 #[inline]
1853 pub fn acquire() -> Option<Self> {
1854 let context = unsafe { sys::trace_acquire_context() };
1855 if context.is_null() { None } else { Some(Self { raw: context }) }
1856 }
1857
1858 #[inline]
1859 pub fn register_string_literal<T: CategoryString>(&self, s: T) -> sys::trace_string_ref_t {
1860 s.register(self)
1861 }
1862
1863 pub fn write_blob_record(
1864 &self,
1865 type_: sys::trace_blob_type_t,
1866 name_ref: &sys::trace_string_ref_t,
1867 data: &[u8],
1868 ) {
1869 unsafe {
1870 sys::trace_context_write_blob_record(
1871 self.raw,
1872 type_,
1873 name_ref as *const sys::trace_string_ref_t,
1874 data.as_ptr() as *const libc::c_void,
1875 data.len(),
1876 );
1877 }
1878 }
1879
1880 pub fn copy_record(&self, buffer: &[u64]) -> Option<usize> {
1884 unsafe {
1885 let ptr = sys::trace_context_alloc_record(self.raw, 8 * buffer.len() as libc::size_t);
1886 if ptr == std::ptr::null_mut() {
1887 return None;
1888 }
1889 ptr.cast::<u64>().copy_from(buffer.as_ptr(), buffer.len());
1890 };
1891 Some(buffer.len())
1892 }
1893
1894 pub fn buffering_mode(&self) -> BufferingMode {
1895 match unsafe { sys::trace_context_get_buffering_mode(self.raw) } {
1896 sys::TRACE_BUFFERING_MODE_ONESHOT => BufferingMode::OneShot,
1897 sys::TRACE_BUFFERING_MODE_CIRCULAR => BufferingMode::Circular,
1898 sys::TRACE_BUFFERING_MODE_STREAMING => BufferingMode::Streaming,
1899 m => panic!("Unknown trace buffering mode: {:?}", m),
1900 }
1901 }
1902}
1903
1904impl std::ops::Drop for Context {
1905 fn drop(&mut self) {
1906 unsafe { sys::trace_release_context(self.raw) }
1907 }
1908}
1909
1910pub struct ProlongedContext {
1911 context: *const sys::trace_prolonged_context_t,
1912}
1913
1914impl ProlongedContext {
1915 pub fn acquire() -> Option<Self> {
1916 let context = unsafe { sys::trace_acquire_prolonged_context() };
1917 if context.is_null() { None } else { Some(Self { context }) }
1918 }
1919}
1920
1921impl Drop for ProlongedContext {
1922 fn drop(&mut self) {
1923 unsafe { sys::trace_release_prolonged_context(self.context) }
1924 }
1925}
1926
1927unsafe impl Send for ProlongedContext {}
1928
1929mod sys {
1930 #![allow(non_camel_case_types, unused)]
1931 use zx::sys::{zx_handle_t, zx_koid_t, zx_obj_type_t, zx_status_t, zx_ticks_t};
1932
1933 pub type trace_ticks_t = zx_ticks_t;
1934 pub type trace_counter_id_t = u64;
1935 pub type trace_async_id_t = u64;
1936 pub type trace_flow_id_t = u64;
1937 pub type trace_thread_state_t = u32;
1938 pub type trace_cpu_number_t = u32;
1939 pub type trace_string_index_t = u32;
1940 pub type trace_thread_index_t = u32;
1941 pub type trace_context_t = libc::c_void;
1942 pub type trace_prolonged_context_t = libc::c_void;
1943
1944 pub type trace_encoded_string_ref_t = u16;
1945 pub const TRACE_ENCODED_STRING_REF_EMPTY: trace_encoded_string_ref_t = 0;
1946 pub const TRACE_ENCODED_STRING_REF_INLINE_FLAG: trace_encoded_string_ref_t = 0x8000;
1947 pub const TRACE_ENCODED_STRING_REF_LENGTH_MASK: trace_encoded_string_ref_t = 0x7fff;
1948 pub const TRACE_ENCODED_STRING_REF_MAX_LENGTH: usize = 32000;
1949 pub const TRACE_ENCODED_STRING_REF_MIN_INDEX: trace_encoded_string_ref_t = 0x1;
1950 pub const TRACE_ENCODED_STRING_REF_MAX_INDEX: trace_encoded_string_ref_t = 0x7fff;
1951
1952 pub type trace_encoded_thread_ref_t = u32;
1953 pub const TRACE_ENCODED_THREAD_REF_INLINE: trace_encoded_thread_ref_t = 0;
1954 pub const TRACE_ENCODED_THREAD_MIN_INDEX: trace_encoded_thread_ref_t = 0x01;
1955 pub const TRACE_ENCODED_THREAD_MAX_INDEX: trace_encoded_thread_ref_t = 0xff;
1956
1957 pub type trace_state_t = libc::c_int;
1958 pub const TRACE_STOPPED: trace_state_t = 0;
1959 pub const TRACE_STARTED: trace_state_t = 1;
1960 pub const TRACE_STOPPING: trace_state_t = 2;
1961
1962 pub type trace_scope_t = libc::c_int;
1963 pub const TRACE_SCOPE_THREAD: trace_scope_t = 0;
1964 pub const TRACE_SCOPE_PROCESS: trace_scope_t = 1;
1965 pub const TRACE_SCOPE_GLOBAL: trace_scope_t = 2;
1966
1967 pub type trace_blob_type_t = libc::c_int;
1968 pub const TRACE_BLOB_TYPE_DATA: trace_blob_type_t = 1;
1969 pub const TRACE_BLOB_TYPE_LAST_BRANCH: trace_blob_type_t = 2;
1970 pub const TRACE_BLOB_TYPE_PERFETTO: trace_blob_type_t = 3;
1971
1972 pub type trace_buffering_mode_t = libc::c_int;
1973 pub const TRACE_BUFFERING_MODE_ONESHOT: trace_buffering_mode_t = 0;
1974 pub const TRACE_BUFFERING_MODE_CIRCULAR: trace_buffering_mode_t = 1;
1975 pub const TRACE_BUFFERING_MODE_STREAMING: trace_buffering_mode_t = 2;
1976
1977 #[repr(C)]
1978 #[derive(Copy, Clone)]
1979 pub struct trace_string_ref_t {
1980 pub encoded_value: trace_encoded_string_ref_t,
1981 pub inline_string: *const libc::c_char,
1982 }
1983
1984 pub type trace_site_t = std::sync::atomic::AtomicU64;
1988
1989 unsafe impl Send for trace_string_ref_t {}
1999 unsafe impl Sync for trace_string_ref_t {}
2000
2001 #[repr(C)]
2002 pub struct trace_thread_ref_t {
2003 pub encoded_value: trace_encoded_thread_ref_t,
2004 pub inline_process_koid: zx_koid_t,
2005 pub inline_thread_koid: zx_koid_t,
2006 }
2007
2008 #[repr(C)]
2009 pub struct trace_arg_t {
2010 pub name_ref: trace_string_ref_t,
2011 pub value: trace_arg_value_t,
2012 }
2013
2014 #[repr(C)]
2015 pub union trace_arg_union_t {
2016 pub int32_value: i32,
2017 pub uint32_value: u32,
2018 pub int64_value: i64,
2019 pub uint64_value: u64,
2020 pub double_value: libc::c_double,
2021 pub string_value_ref: trace_string_ref_t,
2022 pub pointer_value: libc::uintptr_t,
2023 pub koid_value: zx_koid_t,
2024 pub bool_value: bool,
2025 pub reserved_for_future_expansion: [libc::uintptr_t; 2],
2026 }
2027
2028 pub type trace_arg_type_t = libc::c_int;
2029 pub const TRACE_ARG_NULL: trace_arg_type_t = 0;
2030 pub const TRACE_ARG_INT32: trace_arg_type_t = 1;
2031 pub const TRACE_ARG_UINT32: trace_arg_type_t = 2;
2032 pub const TRACE_ARG_INT64: trace_arg_type_t = 3;
2033 pub const TRACE_ARG_UINT64: trace_arg_type_t = 4;
2034 pub const TRACE_ARG_DOUBLE: trace_arg_type_t = 5;
2035 pub const TRACE_ARG_STRING: trace_arg_type_t = 6;
2036 pub const TRACE_ARG_POINTER: trace_arg_type_t = 7;
2037 pub const TRACE_ARG_KOID: trace_arg_type_t = 8;
2038 pub const TRACE_ARG_BOOL: trace_arg_type_t = 9;
2039
2040 #[repr(C)]
2041 pub struct trace_arg_value_t {
2042 pub type_: trace_arg_type_t,
2043 pub value: trace_arg_union_t,
2044 }
2045
2046 #[repr(C)]
2047 pub struct trace_handler_ops_t {
2048 pub is_category_enabled:
2049 unsafe fn(handler: *const trace_handler_t, category: *const libc::c_char) -> bool,
2050 pub trace_started: unsafe fn(handler: *const trace_handler_t),
2051 pub trace_stopped: unsafe fn(
2052 handler: *const trace_handler_t,
2053 async_ptr: *const (), disposition: zx_status_t,
2055 buffer_bytes_written: libc::size_t,
2056 ),
2057 pub buffer_overflow: unsafe fn(handler: *const trace_handler_t),
2058 }
2059
2060 #[repr(C)]
2061 pub struct trace_handler_t {
2062 pub ops: *const trace_handler_ops_t,
2063 }
2064
2065 unsafe extern "C" {
2067 pub fn trace_context_is_category_enabled(
2070 context: *const trace_context_t,
2071 category_literal: *const libc::c_char,
2072 ) -> bool;
2073
2074 pub fn trace_context_register_string_literal(
2075 context: *const trace_context_t,
2076 string_literal: *const libc::c_char,
2077 out_ref: *mut trace_string_ref_t,
2078 );
2079
2080 #[cfg(fuchsia_api_level_at_least = "27")]
2081 pub fn trace_context_register_bytestring(
2082 context: *const trace_context_t,
2083 string_literal: *const libc::c_char,
2084 length: libc::size_t,
2085 out_ref: *mut trace_string_ref_t,
2086 );
2087
2088 pub fn trace_context_register_category_literal(
2089 context: *const trace_context_t,
2090 category_literal: *const libc::c_char,
2091 out_ref: *mut trace_string_ref_t,
2092 ) -> bool;
2093
2094 pub fn trace_context_register_current_thread(
2095 context: *const trace_context_t,
2096 out_ref: *mut trace_thread_ref_t,
2097 );
2098
2099 pub fn trace_context_register_thread(
2100 context: *const trace_context_t,
2101 process_koid: zx_koid_t,
2102 thread_koid: zx_koid_t,
2103 out_ref: *mut trace_thread_ref_t,
2104 );
2105
2106 pub fn trace_context_write_kernel_object_record(
2107 context: *const trace_context_t,
2108 koid: zx_koid_t,
2109 type_: zx_obj_type_t,
2110 args: *const trace_arg_t,
2111 num_args: libc::size_t,
2112 );
2113
2114 pub fn trace_context_write_kernel_object_record_for_handle(
2115 context: *const trace_context_t,
2116 handle: zx_handle_t,
2117 args: *const trace_arg_t,
2118 num_args: libc::size_t,
2119 );
2120
2121 pub fn trace_context_write_process_info_record(
2122 context: *const trace_context_t,
2123 process_koid: zx_koid_t,
2124 process_name_ref: *const trace_string_ref_t,
2125 );
2126
2127 pub fn trace_context_write_thread_info_record(
2128 context: *const trace_context_t,
2129 process_koid: zx_koid_t,
2130 thread_koid: zx_koid_t,
2131 thread_name_ref: *const trace_string_ref_t,
2132 );
2133
2134 pub fn trace_context_write_context_switch_record(
2135 context: *const trace_context_t,
2136 event_time: trace_ticks_t,
2137 cpu_number: trace_cpu_number_t,
2138 outgoing_thread_state: trace_thread_state_t,
2139 outgoing_thread_ref: *const trace_thread_ref_t,
2140 incoming_thread_ref: *const trace_thread_ref_t,
2141 );
2142
2143 pub fn trace_context_write_log_record(
2144 context: *const trace_context_t,
2145 event_time: trace_ticks_t,
2146 thread_ref: *const trace_thread_ref_t,
2147 log_message: *const libc::c_char,
2148 log_message_length: libc::size_t,
2149 );
2150
2151 pub fn trace_context_write_instant_event_record(
2152 context: *const trace_context_t,
2153 event_time: trace_ticks_t,
2154 thread_ref: *const trace_thread_ref_t,
2155 category_ref: *const trace_string_ref_t,
2156 name_ref: *const trace_string_ref_t,
2157 scope: trace_scope_t,
2158 args: *const trace_arg_t,
2159 num_args: libc::size_t,
2160 );
2161
2162 pub fn trace_context_send_alert(context: *const trace_context_t, name: *const libc::c_char);
2163
2164 #[cfg(fuchsia_api_level_at_least = "27")]
2165 pub fn trace_context_send_alert_bytestring(
2166 context: *const trace_context_t,
2167 name: *const u8,
2168 length: usize,
2169 );
2170
2171 pub fn trace_context_write_counter_event_record(
2172 context: *const trace_context_t,
2173 event_time: trace_ticks_t,
2174 thread_ref: *const trace_thread_ref_t,
2175 category_ref: *const trace_string_ref_t,
2176 name_ref: *const trace_string_ref_t,
2177 counter_id: trace_counter_id_t,
2178 args: *const trace_arg_t,
2179 num_args: libc::size_t,
2180 );
2181
2182 pub fn trace_context_write_duration_event_record(
2183 context: *const trace_context_t,
2184 start_time: trace_ticks_t,
2185 end_time: trace_ticks_t,
2186 thread_ref: *const trace_thread_ref_t,
2187 category_ref: *const trace_string_ref_t,
2188 name_ref: *const trace_string_ref_t,
2189 args: *const trace_arg_t,
2190 num_args: libc::size_t,
2191 );
2192
2193 pub fn trace_context_write_blob_event_record(
2194 context: *const trace_context_t,
2195 event_time: trace_ticks_t,
2196 thread_ref: *const trace_thread_ref_t,
2197 category_ref: *const trace_string_ref_t,
2198 name_ref: *const trace_string_ref_t,
2199 blob: *const libc::c_void,
2200 blob_size: libc::size_t,
2201 args: *const trace_arg_t,
2202 num_args: libc::size_t,
2203 );
2204
2205 pub fn trace_context_write_duration_begin_event_record(
2206 context: *const trace_context_t,
2207 event_time: trace_ticks_t,
2208 thread_ref: *const trace_thread_ref_t,
2209 category_ref: *const trace_string_ref_t,
2210 name_ref: *const trace_string_ref_t,
2211 args: *const trace_arg_t,
2212 num_args: libc::size_t,
2213 );
2214
2215 pub fn trace_context_write_duration_end_event_record(
2216 context: *const trace_context_t,
2217 event_time: trace_ticks_t,
2218 thread_ref: *const trace_thread_ref_t,
2219 category_ref: *const trace_string_ref_t,
2220 name_ref: *const trace_string_ref_t,
2221 args: *const trace_arg_t,
2222 num_args: libc::size_t,
2223 );
2224
2225 pub fn trace_context_write_async_begin_event_record(
2226 context: *const trace_context_t,
2227 event_time: trace_ticks_t,
2228 thread_ref: *const trace_thread_ref_t,
2229 category_ref: *const trace_string_ref_t,
2230 name_ref: *const trace_string_ref_t,
2231 async_id: trace_async_id_t,
2232 args: *const trace_arg_t,
2233 num_args: libc::size_t,
2234 );
2235
2236 pub fn trace_context_write_async_instant_event_record(
2237 context: *const trace_context_t,
2238 event_time: trace_ticks_t,
2239 thread_ref: *const trace_thread_ref_t,
2240 category_ref: *const trace_string_ref_t,
2241 name_ref: *const trace_string_ref_t,
2242 async_id: trace_async_id_t,
2243 args: *const trace_arg_t,
2244 num_args: libc::size_t,
2245 );
2246
2247 pub fn trace_context_write_async_end_event_record(
2248 context: *const trace_context_t,
2249 event_time: trace_ticks_t,
2250 thread_ref: *const trace_thread_ref_t,
2251 category_ref: *const trace_string_ref_t,
2252 name_ref: *const trace_string_ref_t,
2253 async_id: trace_async_id_t,
2254 args: *const trace_arg_t,
2255 num_args: libc::size_t,
2256 );
2257
2258 pub fn trace_context_write_flow_begin_event_record(
2259 context: *const trace_context_t,
2260 event_time: trace_ticks_t,
2261 thread_ref: *const trace_thread_ref_t,
2262 category_ref: *const trace_string_ref_t,
2263 name_ref: *const trace_string_ref_t,
2264 flow_id: trace_flow_id_t,
2265 args: *const trace_arg_t,
2266 num_args: libc::size_t,
2267 );
2268
2269 pub fn trace_context_write_flow_step_event_record(
2270 context: *const trace_context_t,
2271 event_time: trace_ticks_t,
2272 thread_ref: *const trace_thread_ref_t,
2273 category_ref: *const trace_string_ref_t,
2274 name_ref: *const trace_string_ref_t,
2275 flow_id: trace_flow_id_t,
2276 args: *const trace_arg_t,
2277 num_args: libc::size_t,
2278 );
2279
2280 pub fn trace_context_write_flow_end_event_record(
2281 context: *const trace_context_t,
2282 event_time: trace_ticks_t,
2283 thread_ref: *const trace_thread_ref_t,
2284 category_ref: *const trace_string_ref_t,
2285 name_ref: *const trace_string_ref_t,
2286 flow_id: trace_flow_id_t,
2287 args: *const trace_arg_t,
2288 num_args: libc::size_t,
2289 );
2290
2291 pub fn trace_context_write_initialization_record(
2292 context: *const trace_context_t,
2293 ticks_per_second: u64,
2294 );
2295
2296 pub fn trace_context_write_string_record(
2297 context: *const trace_context_t,
2298 index: trace_string_index_t,
2299 string: *const libc::c_char,
2300 length: libc::size_t,
2301 );
2302
2303 pub fn trace_context_write_thread_record(
2304 context: *const trace_context_t,
2305 index: trace_thread_index_t,
2306 procss_koid: zx_koid_t,
2307 thread_koid: zx_koid_t,
2308 );
2309
2310 pub fn trace_context_write_blob_record(
2311 context: *const trace_context_t,
2312 type_: trace_blob_type_t,
2313 name_ref: *const trace_string_ref_t,
2314 data: *const libc::c_void,
2315 size: libc::size_t,
2316 );
2317
2318 pub fn trace_context_alloc_record(
2319 context: *const trace_context_t,
2320 num_bytes: libc::size_t,
2321 ) -> *mut libc::c_void;
2322
2323 pub fn trace_generate_nonce() -> u64;
2326
2327 pub fn trace_state() -> trace_state_t;
2328
2329 #[cfg(fuchsia_api_level_at_least = "27")]
2330 pub fn trace_is_category_bytestring_enabled(
2331 category_literal: *const u8,
2332 length: usize,
2333 ) -> bool;
2334
2335 pub fn trace_is_category_enabled(category_literal: *const libc::c_char) -> bool;
2336
2337 pub fn trace_acquire_context() -> *const trace_context_t;
2338
2339 pub fn trace_acquire_context_for_category(
2340 category_literal: *const libc::c_char,
2341 out_ref: *mut trace_string_ref_t,
2342 ) -> *const trace_context_t;
2343
2344 pub fn trace_acquire_context_for_category_cached(
2345 category_literal: *const libc::c_char,
2346 trace_site: *const u64,
2347 out_ref: *mut trace_string_ref_t,
2348 ) -> *const trace_context_t;
2349
2350 #[cfg(fuchsia_api_level_at_least = "27")]
2351 pub fn trace_acquire_context_for_category_bytestring(
2352 bytes: *const u8,
2353 length: usize,
2354 out_ref: *mut trace_string_ref_t,
2355 ) -> *const trace_context_t;
2356
2357 #[cfg(fuchsia_api_level_at_least = "27")]
2358 pub fn trace_acquire_context_for_category_bytestring_cached(
2359 bytes: *const u8,
2360 length: usize,
2361 trace_site: *const u64,
2362 out_ref: *mut trace_string_ref_t,
2363 ) -> *const trace_context_t;
2364
2365 pub fn trace_release_context(context: *const trace_context_t);
2366
2367 pub fn trace_acquire_prolonged_context() -> *const trace_prolonged_context_t;
2368
2369 pub fn trace_release_prolonged_context(context: *const trace_prolonged_context_t);
2370
2371 pub fn trace_register_observer(event: zx_handle_t) -> zx_status_t;
2372
2373 pub fn trace_unregister_observer(event: zx_handle_t) -> zx_status_t;
2374
2375 pub fn trace_notify_observer_updated(event: zx_handle_t);
2376
2377 pub fn trace_context_get_buffering_mode(
2378 context: *const trace_context_t,
2379 ) -> trace_buffering_mode_t;
2380 }
2381}
2382
2383pub struct TraceFutureArgs<'a, C: CategoryString, S: AsTraceStrRef> {
2386 pub category: C,
2387 pub name: S,
2388
2389 pub args: Box<[Arg<'a>]>,
2392
2393 pub flow_id: Option<Id>,
2396
2397 pub _use_trace_future_args: (),
2399}
2400
2401#[doc(hidden)]
2402#[macro_export]
2403macro_rules! __impl_trace_future_args {
2404 ($category:expr, $name:expr, $flow_id:expr) => {
2407 $crate::TraceFutureArgs {
2408 category: $category,
2409 name: $name,
2410 args: ::std::boxed::Box::new([]),
2411 flow_id: $flow_id,
2412 _use_trace_future_args: (),
2413 }
2414 };
2415 ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)*) => {{
2416 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
2417 use $crate::AsTraceStrRef;
2418 let context = $crate::TraceCategoryContext::acquire_cached($category, &CACHE);
2419 let args: ::std::boxed::Box<[$crate::Arg<'_>]> = if let Some(context) = context {
2420 ::std::boxed::Box::new(
2421 [$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]
2422 )
2423 } else {
2424 ::std::boxed::Box::new([])
2425 };
2426 $crate::TraceFutureArgs {
2427 category: $category,
2428 name: $name,
2429 args: args,
2430 flow_id: $flow_id,
2431 _use_trace_future_args: (),
2432 }
2433 }};
2434}
2435
2436#[macro_export]
2448macro_rules! trace_future_args {
2449 ($category:expr, $name:expr $(, $key:expr => $val:expr)* $(,)?) => {
2450 $crate::__impl_trace_future_args!($category, $name, None $(,$key => $val)*)
2451 };
2452 ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)* $(,)?) => {
2453 $crate::__impl_trace_future_args!($category, $name, Some($flow_id) $(,$key => $val)*)
2454 };
2455}
2456
2457pub trait TraceFutureExt: Future + Sized {
2459 #[inline(always)]
2473 fn trace<'a, C: CategoryString, S: AsTraceStrRef>(
2474 self,
2475 args: TraceFutureArgs<'a, C, S>,
2476 ) -> TraceFuture<'a, Self, C, S> {
2477 TraceFuture::new(args, self)
2478 }
2479}
2480
2481impl<T: Future + Sized> TraceFutureExt for T {}
2482
2483#[pin_project]
2486pub struct TraceFuture<'a, Fut: Future, C: CategoryString, S: AsTraceStrRef> {
2487 #[pin]
2488 future: Fut,
2489 category: C,
2490 name: S,
2491 args: Box<[Arg<'a>]>,
2492 flow_id: Option<Id>,
2493 poll_count: u64,
2494}
2495
2496impl<'a, Fut: Future, C: CategoryString, S: AsTraceStrRef> TraceFuture<'a, Fut, C, S> {
2497 #[inline(always)]
2498 pub fn new(args: TraceFutureArgs<'a, C, S>, future: Fut) -> Self {
2499 Self {
2500 future,
2501 category: args.category,
2502 name: args.name,
2503 args: args.args,
2504 flow_id: args.flow_id,
2505 poll_count: 0,
2506 }
2507 }
2508
2509 #[cold]
2510 fn trace_poll(
2511 self: Pin<&mut Self>,
2512 context: &TraceCategoryContext,
2513 cx: &mut std::task::Context<'_>,
2514 ) -> Poll<Fut::Output> {
2515 let start_time = zx::BootTicks::get();
2516 let this = self.project();
2517 *this.poll_count = this.poll_count.saturating_add(1);
2518 let name_ref = this.name.as_trace_str_ref(context);
2519 context.write_duration_begin(start_time, name_ref, &this.args);
2520
2521 let result = this.future.poll(cx);
2522
2523 let flow_id = this.flow_id.get_or_insert_with(Id::new);
2524 let result_str: sys::trace_string_ref_t = if result.is_pending() {
2525 if *this.poll_count == 1 {
2526 context.write_flow_begin(start_time, name_ref, *flow_id, &[]);
2527 } else {
2528 context.write_flow_step(start_time, name_ref, *flow_id, &[]);
2529 }
2530 context.register_str("pending")
2531 } else {
2532 if *this.poll_count != 1 {
2533 context.write_flow_end(start_time, name_ref, *flow_id, &[]);
2534 }
2535 context.register_str("ready")
2536 };
2537 context.write_duration_end(
2538 zx::BootTicks::get(),
2539 name_ref,
2540 &[
2541 ArgValue::of_registered(context.register_str("poll-state"), result_str),
2542 ArgValue::of_registered(context.register_str("poll-count"), *this.poll_count),
2543 ],
2544 );
2545 result
2546 }
2547}
2548
2549impl<Fut: Future, C: CategoryString, S: AsTraceStrRef> Future for TraceFuture<'_, Fut, C, S> {
2550 type Output = Fut::Output;
2551 fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Fut::Output> {
2552 if let Some(context) = TraceCategoryContext::acquire(self.as_ref().get_ref().category) {
2553 self.trace_poll(&context, cx)
2554 } else {
2555 self.project().future.poll(cx)
2556 }
2557 }
2558}
2559
2560#[cfg(test)]
2561mod test {
2562 use super::{Id, trim_to_last_char_boundary};
2563
2564 #[test]
2565 fn trim_to_last_char_boundary_trims_to_last_character_boundary() {
2566 assert_eq!(b"x", trim_to_last_char_boundary("x", 5));
2567 assert_eq!(b"x", trim_to_last_char_boundary("x", 1));
2568 assert_eq!(b"", trim_to_last_char_boundary("x", 0));
2569 assert_eq!(b"xxxxx", trim_to_last_char_boundary("xxxxx", 6));
2570 assert_eq!(b"xxxxx", trim_to_last_char_boundary("xxxxx", 5));
2571 assert_eq!(b"xxxx", trim_to_last_char_boundary("xxxxx", 4));
2572
2573 assert_eq!("💩".as_bytes(), trim_to_last_char_boundary("💩", 5));
2574 assert_eq!("💩".as_bytes(), trim_to_last_char_boundary("💩", 4));
2575 assert_eq!(b"", trim_to_last_char_boundary("💩", 3));
2576 }
2577
2578 #[test]
2582 fn test_id_new() {
2583 assert_ne!(Id::new(), Id::new());
2584 }
2585}