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 #[inline]
163 fn acquire_context_cached(&self, site: &sys::trace_site_t) -> Option<TraceCategoryContext> {
164 unsafe {
165 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 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 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
297impl AsTraceStrRef for &'static str {
301 #[inline]
302 fn as_trace_str_ref(&self, context: &TraceCategoryContext) -> sys::trace_string_ref_t {
303 context.register_str(self)
304 }
305}
306
307impl AsTraceStrRef for String {
308 #[inline]
309 fn as_trace_str_ref(&self, _context: &TraceCategoryContext) -> sys::trace_string_ref_t {
310 trace_make_inline_string_ref(self.as_str())
311 }
312}
313
314impl<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#[repr(transparent)]
324pub struct Arg<'a>(sys::trace_arg_t, PhantomData<&'a ()>);
325
326pub 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
340macro_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#[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#[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#[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#[macro_export]
606macro_rules! alert {
607 ($category:expr, $name:expr) => {
608 $crate::alert($category, $name)
609 };
610}
611
612pub 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#[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
649pub 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#[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 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
699pub 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#[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 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
770pub 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#[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#[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
843pub 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
861pub 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#[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 id: Id,
886 category: C,
887 name: S,
888}
889
890impl<C: CategoryString, S: AsTraceStrRef> AsyncScope<C, S> {
891 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 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 async_end(self.id, self.category, &self.name, &[]);
912 }
913}
914
915pub 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#[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#[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
1014pub 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
1039pub 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
1064pub 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#[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#[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#[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
1193pub 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
1224pub 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
1253pub 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#[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#[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#[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
1396pub 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
1425pub 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
1454pub 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
1483const 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 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#[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
1526pub 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
1852pub 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 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 pub type trace_site_t = std::sync::atomic::AtomicU64;
1994
1995 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 (), 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 unsafe extern "C" {
2073 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 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
2389pub struct TraceFutureArgs<'a, C: CategoryString, S: AsTraceStrRef> {
2392 pub category: C,
2393 pub name: S,
2394
2395 pub args: Box<[Arg<'a>]>,
2398
2399 pub flow_id: Option<Id>,
2402
2403 pub _use_trace_future_args: (),
2405}
2406
2407#[doc(hidden)]
2408#[macro_export]
2409macro_rules! __impl_trace_future_args {
2410 ($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_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
2463pub trait TraceFutureExt: Future + Sized {
2465 #[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#[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 #[test]
2588 fn test_id_new() {
2589 assert_ne!(Id::new(), Id::new());
2590 }
2591}