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#[cfg(fuchsia_api_level_at_least = "NEXT")]
833#[macro_export]
834macro_rules! vthread_duration_begin {
835 ($category:expr, $name:expr, $vthread_name:expr, $vthread_id:expr $(, $key:expr => $val:expr)* $(,)?) => {
836 {
837 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
838 let vthread = $crate::VThread::new($vthread_name, $vthread_id);
839 use $crate::AsTraceStrRef;
840 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
841 $crate::vthread_duration_begin(
842 &context,
843 $name,
844 &vthread,
845 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]
846 )
847 }
848 }
849 };
850}
851
852#[macro_export]
866macro_rules! duration_end {
867 ($category:expr, $name:expr $(, $key:expr => $val:expr)* $(,)?) => {
868 {
869 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
870 use $crate::AsTraceStrRef;
871 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
872 $crate::duration_end(&context, $name, &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
873 }
874 }
875 };
876}
877
878#[cfg(fuchsia_api_level_at_least = "NEXT")]
894#[macro_export]
895macro_rules! vthread_duration_end {
896 ($category:expr, $name:expr, $vthread_name:expr, $vthread_id:expr $(, $key:expr => $val:expr)* $(,)?) => {
897 {
898 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
899 let vthread = $crate::VThread::new($vthread_name, $vthread_id);
900 use $crate::AsTraceStrRef;
901 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
902 $crate::vthread_duration_end(
903 &context,
904 $name,
905 &vthread,
906 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]
907 )
908 }
909 }
910 };
911}
912
913pub fn duration_begin<S: AsTraceStrRef>(context: &TraceCategoryContext, name: S, args: &[Arg<'_>]) {
924 let ticks = zx::BootTicks::get();
925 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
926
927 let name_ref = name.as_trace_str_ref(&context);
928 context.write_duration_begin(ticks, name_ref, args);
929}
930
931#[cfg(fuchsia_api_level_at_least = "NEXT")]
932pub struct VThread<S: AsTraceStrRef = &'static str> {
933 name: S,
934 id: sys::trace_vthread_id_t,
935 process_koid: zx::sys::zx_koid_t,
936}
937
938#[cfg(fuchsia_api_level_at_least = "NEXT")]
939impl<S: AsTraceStrRef> VThread<S> {
940 pub fn new(name: S, id: sys::trace_vthread_id_t) -> Self {
941 Self { name, id, process_koid: zx::sys::ZX_KOID_INVALID }
942 }
943
944 pub fn new_with_process_koid(
945 name: S,
946 id: sys::trace_vthread_id_t,
947 process_koid: zx::sys::zx_koid_t,
948 ) -> Self {
949 Self { name, id, process_koid }
950 }
951}
952
953#[cfg(fuchsia_api_level_at_least = "NEXT")]
955pub fn vthread_duration_begin<S1: AsTraceStrRef, S2: AsTraceStrRef>(
956 context: &TraceCategoryContext,
957 name: S1,
958 vthread: &VThread<S2>,
959 args: &[Arg<'_>],
960) {
961 let ticks = zx::BootTicks::get();
962 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
963
964 let name_ref = name.as_trace_str_ref(context);
965 context.write_vthread_duration_begin(ticks, name_ref, vthread, args);
966}
967
968pub fn duration_end<S: AsTraceStrRef>(context: &TraceCategoryContext, name: S, args: &[Arg<'_>]) {
978 let ticks = zx::BootTicks::get();
979 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
980
981 let name_ref = name.as_trace_str_ref(&context);
982 context.write_duration_end(ticks, name_ref, args);
983}
984
985#[cfg(fuchsia_api_level_at_least = "NEXT")]
987pub fn vthread_duration_end<S1: AsTraceStrRef, S2: AsTraceStrRef>(
988 context: &TraceCategoryContext,
989 name: S1,
990 vthread: &VThread<S2>,
991 args: &[Arg<'_>],
992) {
993 let ticks = zx::BootTicks::get();
994 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
995
996 let name_ref = name.as_trace_str_ref(context);
997 context.write_vthread_duration_end(ticks, name_ref, vthread, args);
998}
999
1000#[must_use = "emits an end event when dropped, so if dropped immediately creates an essentially \
1003 zero length duration that should just be an instant instead"]
1004pub struct AsyncScope<C: CategoryString = &'static CStr, S: AsTraceStrRef = &'static CStr> {
1005 id: Id,
1008 category: C,
1009 name: S,
1010}
1011
1012impl<C: CategoryString, S: AsTraceStrRef> AsyncScope<C, S> {
1013 pub fn begin(id: Id, category: C, name: S, args: &[Arg<'_>]) -> Self {
1016 async_begin(id, category, &name, args);
1017 Self { id, category, name }
1018 }
1019
1020 pub fn end(self, args: &[Arg<'_>]) {
1023 async_end(self.id, self.category, &self.name, args);
1024 std::mem::forget(self);
1025 }
1026}
1027
1028impl<C: CategoryString, S: AsTraceStrRef> Drop for AsyncScope<C, S> {
1029 fn drop(&mut self) {
1030 async_end(self.id, self.category, &self.name, &[]);
1034 }
1035}
1036
1037pub fn async_enter<C: CategoryString, S: AsTraceStrRef>(
1046 id: Id,
1047 category: C,
1048 name: S,
1049 args: &[Arg<'_>],
1050) -> AsyncScope<C, S> {
1051 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1052 AsyncScope::begin(id, category, name, args)
1053}
1054
1055#[macro_export]
1086macro_rules! async_enter {
1087 ($id:expr, $category:expr, $name:expr $(, $key:expr => $val:expr)*) => {
1088 {
1089 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1090 use $crate::AsTraceStrRef;
1091 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1092 Some($crate::AsyncScope::begin($id, $category, $name, &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]))
1093 } else {
1094 None
1095 }
1096 }
1097 }
1098}
1099
1100#[macro_export]
1124macro_rules! async_instant {
1125 ($id:expr, $category:expr, $name:expr $(, $key:expr => $val:expr)*) => {
1126 {
1127 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1128 use $crate::AsTraceStrRef;
1129 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1130 $crate::async_instant($id, &context, $name, &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]);
1131 }
1132 }
1133 }
1134}
1135
1136pub fn async_begin<C: CategoryString, S: AsTraceStrRef>(
1148 id: Id,
1149 category: C,
1150 name: S,
1151 args: &[Arg<'_>],
1152) {
1153 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1154
1155 if let Some(context) = TraceCategoryContext::acquire(category) {
1156 let name_ref = name.as_trace_str_ref(&context);
1157 context.write_async_begin(id, name_ref, args);
1158 }
1159}
1160
1161pub fn async_end<C: CategoryString, S: AsTraceStrRef>(
1173 id: Id,
1174 category: C,
1175 name: S,
1176 args: &[Arg<'_>],
1177) {
1178 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1179
1180 if let Some(context) = TraceCategoryContext::acquire(category) {
1181 let name_ref = name.as_trace_str_ref(&context);
1182 context.write_async_end(id, name_ref, args);
1183 }
1184}
1185
1186pub fn async_instant<S: AsTraceStrRef>(
1198 id: Id,
1199 context: &TraceCategoryContext,
1200 name: S,
1201 args: &[Arg<'_>],
1202) {
1203 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1204
1205 let name_ref = name.as_trace_str_ref(context);
1206 context.write_async_instant(id, name_ref, args);
1207}
1208
1209#[macro_export]
1210macro_rules! blob {
1211 ($category:expr, $name:expr, $bytes:expr $(, $key:expr => $val:expr)*) => {
1212 {
1213 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1214 use $crate::AsTraceStrRef;
1215 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1216 $crate::blob_fn(&context, $name, $bytes, &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
1217 }
1218 }
1219 }
1220}
1221pub fn blob_fn<S: AsTraceStrRef>(
1222 context: &TraceCategoryContext,
1223 name: S,
1224 bytes: &[u8],
1225 args: &[Arg<'_>],
1226) {
1227 let name_ref = name.as_trace_str_ref(context);
1228 context.write_blob(name_ref, bytes, args);
1229}
1230
1231#[macro_export]
1246macro_rules! flow_begin {
1247 ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)*) => {
1248 {
1249 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1250 use $crate::AsTraceStrRef;
1251 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1252 $crate::flow_begin(&context, $name, $flow_id,
1253 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
1254 }
1255 }
1256 }
1257}
1258
1259#[macro_export]
1274macro_rules! flow_step {
1275 ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)*) => {
1276 {
1277 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1278 use $crate::AsTraceStrRef;
1279 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1280 $crate::flow_step(&context, $name, $flow_id,
1281 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
1282 }
1283 }
1284 }
1285}
1286
1287#[macro_export]
1302macro_rules! flow_end {
1303 ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)*) => {
1304 {
1305 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1306 use $crate::AsTraceStrRef;
1307 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1308 $crate::flow_end(&context, $name, $flow_id,
1309 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
1310 }
1311 }
1312 }
1313}
1314
1315pub fn flow_begin<S: AsTraceStrRef>(
1334 context: &TraceCategoryContext,
1335 name: S,
1336 flow_id: Id,
1337 args: &[Arg<'_>],
1338) {
1339 let ticks = zx::BootTicks::get();
1340 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1341
1342 let name_ref = name.as_trace_str_ref(context);
1343 context.write_flow_begin(ticks, name_ref, flow_id, args);
1344}
1345
1346pub fn flow_end<S: AsTraceStrRef>(
1363 context: &TraceCategoryContext,
1364 name: S,
1365 flow_id: Id,
1366 args: &[Arg<'_>],
1367) {
1368 let ticks = zx::BootTicks::get();
1369 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1370
1371 let name_ref = name.as_trace_str_ref(context);
1372 context.write_flow_end(ticks, name_ref, flow_id, args);
1373}
1374
1375pub fn flow_step<S: AsTraceStrRef>(
1392 context: &TraceCategoryContext,
1393 name: S,
1394 flow_id: Id,
1395 args: &[Arg<'_>],
1396) {
1397 let ticks = zx::BootTicks::get();
1398 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1399
1400 let name_ref = name.as_trace_str_ref(context);
1401 context.write_flow_step(ticks, name_ref, flow_id, args);
1402}
1403
1404#[macro_export]
1418macro_rules! instaflow_begin {
1419 (
1420 $category:expr,
1421 $flow_name:expr,
1422 $step_name:expr,
1423 $flow_id:expr
1424 $(, $key:expr => $val:expr)*
1425 ) => {
1426 {
1427 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1428 use $crate::AsTraceStrRef;
1429 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1430 $crate::instaflow_begin(
1431 &context,
1432 $flow_name,
1433 $step_name,
1434 $flow_id,
1435 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*],
1436 )
1437 }
1438 }
1439 }
1440}
1441
1442#[macro_export]
1456macro_rules! instaflow_end {
1457 (
1458 $category:expr,
1459 $flow_name:expr,
1460 $step_name:expr,
1461 $flow_id:expr
1462 $(, $key:expr => $val:expr)*
1463 ) => {
1464 {
1465 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1466 use $crate::AsTraceStrRef;
1467 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1468 $crate::instaflow_end(
1469 &context,
1470 $flow_name,
1471 $step_name,
1472 $flow_id,
1473 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*],
1474 )
1475 }
1476 }
1477 }
1478}
1479
1480#[macro_export]
1494macro_rules! instaflow_step {
1495 (
1496 $category:expr,
1497 $flow_name:expr,
1498 $step_name:expr,
1499 $flow_id:expr
1500 $(, $key:expr => $val:expr)*
1501 ) => {
1502 {
1503 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1504 use $crate::AsTraceStrRef;
1505 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1506 $crate::instaflow_step(
1507 &context,
1508 $flow_name,
1509 $step_name,
1510 $flow_id,
1511 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*],
1512 )
1513 }
1514 }
1515 }
1516}
1517
1518pub fn instaflow_begin<S1: AsTraceStrRef, S2: AsTraceStrRef>(
1530 context: &TraceCategoryContext,
1531 flow_name: S1,
1532 step_name: S2,
1533 flow_id: Id,
1534 args: &[Arg<'_>],
1535) {
1536 let ticks = zx::BootTicks::get();
1537 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1538
1539 let flow_name_ref = flow_name.as_trace_str_ref(context);
1540 let step_name_ref = step_name.as_trace_str_ref(context);
1541
1542 context.write_duration_begin(ticks, step_name_ref, args);
1543 context.write_flow_begin(ticks, flow_name_ref, flow_id, args);
1544 context.write_duration_end(ticks, step_name_ref, args);
1545}
1546
1547pub fn instaflow_end<S1: AsTraceStrRef, S2: AsTraceStrRef>(
1559 context: &TraceCategoryContext,
1560 flow_name: S1,
1561 step_name: S2,
1562 flow_id: Id,
1563 args: &[Arg<'_>],
1564) {
1565 let ticks = zx::BootTicks::get();
1566 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1567
1568 let flow_name_ref = flow_name.as_trace_str_ref(context);
1569 let step_name_ref = step_name.as_trace_str_ref(context);
1570
1571 context.write_duration_begin(ticks, step_name_ref, args);
1572 context.write_flow_end(ticks, flow_name_ref, flow_id, args);
1573 context.write_duration_end(ticks, step_name_ref, args);
1574}
1575
1576pub fn instaflow_step<S1: AsTraceStrRef, S2: AsTraceStrRef>(
1588 context: &TraceCategoryContext,
1589 flow_name: S1,
1590 step_name: S2,
1591 flow_id: Id,
1592 args: &[Arg<'_>],
1593) {
1594 let ticks = zx::BootTicks::get();
1595 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1596
1597 let flow_name_ref = flow_name.as_trace_str_ref(context);
1598 let step_name_ref = step_name.as_trace_str_ref(context);
1599
1600 context.write_duration_begin(ticks, step_name_ref, args);
1601 context.write_flow_step(ticks, flow_name_ref, flow_id, args);
1602 context.write_duration_end(ticks, step_name_ref, args);
1603}
1604
1605const fn trace_make_empty_string_ref() -> sys::trace_string_ref_t {
1607 sys::trace_string_ref_t {
1608 encoded_value: sys::TRACE_ENCODED_STRING_REF_EMPTY,
1609 inline_string: ptr::null(),
1610 }
1611}
1612
1613#[inline]
1614fn trim_to_last_char_boundary(string: &str, max_len: usize) -> &[u8] {
1615 let mut len = string.len();
1616 if string.len() > max_len {
1617 len = max_len;
1621 while len > 0 {
1622 if string.is_char_boundary(len - 1) && string.is_char_boundary(len) {
1623 break;
1624 }
1625 len -= 1;
1626 }
1627 }
1628 &string.as_bytes()[0..len]
1629}
1630
1631#[inline]
1634fn trace_make_inline_string_ref(string: &str) -> sys::trace_string_ref_t {
1635 let len = string.len() as u16;
1636 if len == 0 {
1637 return trace_make_empty_string_ref();
1638 }
1639
1640 let string = trim_to_last_char_boundary(string, sys::TRACE_ENCODED_STRING_REF_MAX_LENGTH);
1641
1642 sys::trace_string_ref_t {
1643 encoded_value: sys::TRACE_ENCODED_STRING_REF_INLINE_FLAG | len,
1644 inline_string: string.as_ptr() as *const libc::c_char,
1645 }
1646}
1647
1648pub struct TraceCategoryContext {
1650 context: Context,
1651 category_ref: sys::trace_string_ref_t,
1652}
1653
1654impl TraceCategoryContext {
1655 #[inline]
1656 pub fn acquire_cached<C: CategoryString>(
1657 category: C,
1658 site: &sys::trace_site_t,
1659 ) -> Option<TraceCategoryContext> {
1660 category.acquire_context_cached(site)
1661 }
1662
1663 pub fn acquire<C: CategoryString>(category: C) -> Option<TraceCategoryContext> {
1664 category.acquire_context()
1665 }
1666
1667 #[inline]
1668 pub fn register_string_literal<T: CategoryString>(&self, name: T) -> sys::trace_string_ref_t {
1669 name.register(&self.context)
1670 }
1671
1672 #[inline]
1673 #[cfg(fuchsia_api_level_at_least = "27")]
1674 pub fn register_str(&self, name: &'static str) -> sys::trace_string_ref_t {
1675 unsafe {
1676 let mut name_ref = mem::MaybeUninit::<sys::trace_string_ref_t>::uninit();
1677 sys::trace_context_register_bytestring(
1678 self.context.raw,
1679 name.as_ptr().cast::<libc::c_char>(),
1680 name.len(),
1681 name_ref.as_mut_ptr(),
1682 );
1683 name_ref.assume_init()
1684 }
1685 }
1686 #[inline]
1687 #[cfg(not(fuchsia_api_level_at_least = "27"))]
1688 pub fn register_str(&self, name: &'static str) -> sys::trace_string_ref_t {
1689 trace_make_inline_string_ref(name)
1690 }
1691
1692 #[inline]
1693 fn register_current_thread(&self) -> sys::trace_thread_ref_t {
1694 unsafe {
1695 let mut thread_ref = mem::MaybeUninit::<sys::trace_thread_ref_t>::uninit();
1696 sys::trace_context_register_current_thread(self.context.raw, thread_ref.as_mut_ptr());
1697 thread_ref.assume_init()
1698 }
1699 }
1700
1701 #[cfg(fuchsia_api_level_at_least = "NEXT")]
1702 #[inline]
1703 fn register_vthread<S: AsTraceStrRef>(
1704 &self,
1705 name: &S,
1706 id: sys::trace_vthread_id_t,
1707 process_koid: zx::sys::zx_koid_t,
1708 ) -> sys::trace_thread_ref_t {
1709 let name_ref = name.as_trace_str_ref(self);
1710 unsafe {
1711 let mut thread_ref = mem::MaybeUninit::<sys::trace_thread_ref_t>::uninit();
1712 sys::trace_context_register_vthread_by_ref(
1713 self.context.raw,
1714 process_koid,
1715 &name_ref,
1716 id,
1717 thread_ref.as_mut_ptr(),
1718 );
1719 thread_ref.assume_init()
1720 }
1721 }
1722
1723 #[inline]
1724 pub fn write_instant(&self, name_ref: sys::trace_string_ref_t, scope: Scope, args: &[Arg<'_>]) {
1725 let ticks = zx::BootTicks::get();
1726 let thread_ref = self.register_current_thread();
1727 unsafe {
1728 sys::trace_context_write_instant_event_record(
1729 self.context.raw,
1730 ticks.into_raw(),
1731 &thread_ref,
1732 &self.category_ref,
1733 &name_ref,
1734 scope.into_raw(),
1735 args.as_ptr() as *const sys::trace_arg_t,
1736 args.len(),
1737 );
1738 }
1739 }
1740
1741 pub fn write_instant_with_inline_name(&self, name: &str, scope: Scope, args: &[Arg<'_>]) {
1742 let name_ref = trace_make_inline_string_ref(name);
1743 self.write_instant(name_ref, scope, args)
1744 }
1745
1746 fn write_counter(&self, name_ref: sys::trace_string_ref_t, counter_id: u64, args: &[Arg<'_>]) {
1747 let ticks = zx::BootTicks::get();
1748 let thread_ref = self.register_current_thread();
1749 unsafe {
1750 sys::trace_context_write_counter_event_record(
1751 self.context.raw,
1752 ticks.into_raw(),
1753 &thread_ref,
1754 &self.category_ref,
1755 &name_ref,
1756 counter_id,
1757 args.as_ptr() as *const sys::trace_arg_t,
1758 args.len(),
1759 );
1760 }
1761 }
1762
1763 pub fn write_counter_with_inline_name(&self, name: &str, counter_id: u64, args: &[Arg<'_>]) {
1764 let name_ref = trace_make_inline_string_ref(name);
1765 self.write_counter(name_ref, counter_id, args);
1766 }
1767
1768 fn write_duration(
1769 &self,
1770 name_ref: sys::trace_string_ref_t,
1771 start_time: zx::BootTicks,
1772 args: &[Arg<'_>],
1773 ) {
1774 let ticks = zx::BootTicks::get();
1775 let thread_ref = self.register_current_thread();
1776 unsafe {
1777 sys::trace_context_write_duration_event_record(
1778 self.context.raw,
1779 start_time.into_raw(),
1780 ticks.into_raw(),
1781 &thread_ref,
1782 &self.category_ref,
1783 &name_ref,
1784 args.as_ptr() as *const sys::trace_arg_t,
1785 args.len(),
1786 );
1787 }
1788 }
1789
1790 pub fn write_duration_with_inline_name(
1791 &self,
1792 name: &str,
1793 start_time: zx::BootTicks,
1794 args: &[Arg<'_>],
1795 ) {
1796 let name_ref = trace_make_inline_string_ref(name);
1797 self.write_duration(name_ref, start_time, args);
1798 }
1799
1800 fn write_duration_begin(
1801 &self,
1802 ticks: zx::BootTicks,
1803 name_ref: sys::trace_string_ref_t,
1804 args: &[Arg<'_>],
1805 ) {
1806 let thread_ref = self.register_current_thread();
1807 unsafe {
1808 sys::trace_context_write_duration_begin_event_record(
1809 self.context.raw,
1810 ticks.into_raw(),
1811 &thread_ref,
1812 &self.category_ref,
1813 &name_ref,
1814 args.as_ptr() as *const sys::trace_arg_t,
1815 args.len(),
1816 );
1817 }
1818 }
1819
1820 #[cfg(fuchsia_api_level_at_least = "NEXT")]
1821 fn write_vthread_duration_begin<S: AsTraceStrRef>(
1822 &self,
1823 ticks: zx::BootTicks,
1824 name_ref: sys::trace_string_ref_t,
1825 vthread: &VThread<S>,
1826 args: &[Arg<'_>],
1827 ) {
1828 let thread_ref = self.register_vthread(&vthread.name, vthread.id, vthread.process_koid);
1829 unsafe {
1830 sys::trace_context_write_duration_begin_event_record(
1831 self.context.raw,
1832 ticks.into_raw(),
1833 &thread_ref,
1834 &self.category_ref,
1835 &name_ref,
1836 args.as_ptr() as *const sys::trace_arg_t,
1837 args.len(),
1838 );
1839 }
1840 }
1841
1842 pub fn write_duration_begin_with_inline_name(&self, name: &str, args: &[Arg<'_>]) {
1843 let name_ref = trace_make_inline_string_ref(name);
1844 self.write_duration_begin(zx::BootTicks::get(), name_ref, args);
1845 }
1846
1847 fn write_duration_end(
1848 &self,
1849 ticks: zx::BootTicks,
1850 name_ref: sys::trace_string_ref_t,
1851 args: &[Arg<'_>],
1852 ) {
1853 let thread_ref = self.register_current_thread();
1854 unsafe {
1855 sys::trace_context_write_duration_end_event_record(
1856 self.context.raw,
1857 ticks.into_raw(),
1858 &thread_ref,
1859 &self.category_ref,
1860 &name_ref,
1861 args.as_ptr() as *const sys::trace_arg_t,
1862 args.len(),
1863 );
1864 }
1865 }
1866
1867 #[cfg(fuchsia_api_level_at_least = "NEXT")]
1868 fn write_vthread_duration_end<S: AsTraceStrRef>(
1869 &self,
1870 ticks: zx::BootTicks,
1871 name_ref: sys::trace_string_ref_t,
1872 vthread: &VThread<S>,
1873 args: &[Arg<'_>],
1874 ) {
1875 let thread_ref = self.register_vthread(&vthread.name, vthread.id, vthread.process_koid);
1876 unsafe {
1877 sys::trace_context_write_duration_end_event_record(
1878 self.context.raw,
1879 ticks.into_raw(),
1880 &thread_ref,
1881 &self.category_ref,
1882 &name_ref,
1883 args.as_ptr() as *const sys::trace_arg_t,
1884 args.len(),
1885 );
1886 }
1887 }
1888
1889 pub fn write_duration_end_with_inline_name(&self, name: &str, args: &[Arg<'_>]) {
1890 let name_ref = trace_make_inline_string_ref(name);
1891 self.write_duration_end(zx::BootTicks::get(), name_ref, args);
1892 }
1893
1894 fn write_async_begin(&self, id: Id, name_ref: sys::trace_string_ref_t, args: &[Arg<'_>]) {
1895 let ticks = zx::BootTicks::get();
1896 let thread_ref = self.register_current_thread();
1897 unsafe {
1898 sys::trace_context_write_async_begin_event_record(
1899 self.context.raw,
1900 ticks.into_raw(),
1901 &thread_ref,
1902 &self.category_ref,
1903 &name_ref,
1904 id.into(),
1905 args.as_ptr() as *const sys::trace_arg_t,
1906 args.len(),
1907 );
1908 }
1909 }
1910
1911 pub fn write_async_begin_with_inline_name(&self, id: Id, name: &str, args: &[Arg<'_>]) {
1912 let name_ref = trace_make_inline_string_ref(name);
1913 self.write_async_begin(id, name_ref, args);
1914 }
1915
1916 fn write_async_end(&self, id: Id, name_ref: sys::trace_string_ref_t, args: &[Arg<'_>]) {
1917 let ticks = zx::BootTicks::get();
1918 let thread_ref = self.register_current_thread();
1919 unsafe {
1920 sys::trace_context_write_async_end_event_record(
1921 self.context.raw,
1922 ticks.into_raw(),
1923 &thread_ref,
1924 &self.category_ref,
1925 &name_ref,
1926 id.into(),
1927 args.as_ptr() as *const sys::trace_arg_t,
1928 args.len(),
1929 );
1930 }
1931 }
1932
1933 pub fn write_async_end_with_inline_name(&self, id: Id, name: &str, args: &[Arg<'_>]) {
1934 let name_ref = trace_make_inline_string_ref(name);
1935 self.write_async_end(id, name_ref, args);
1936 }
1937
1938 fn write_async_instant(&self, id: Id, name_ref: sys::trace_string_ref_t, args: &[Arg<'_>]) {
1939 let ticks = zx::BootTicks::get();
1940 let thread_ref = self.register_current_thread();
1941 unsafe {
1942 sys::trace_context_write_async_instant_event_record(
1943 self.context.raw,
1944 ticks.into_raw(),
1945 &thread_ref,
1946 &self.category_ref,
1947 &name_ref,
1948 id.into(),
1949 args.as_ptr() as *const sys::trace_arg_t,
1950 args.len(),
1951 );
1952 }
1953 }
1954
1955 fn write_blob(&self, name_ref: sys::trace_string_ref_t, bytes: &[u8], args: &[Arg<'_>]) {
1956 let ticks = zx::BootTicks::get();
1957 let thread_ref = self.register_current_thread();
1958 unsafe {
1959 sys::trace_context_write_blob_event_record(
1960 self.context.raw,
1961 ticks.into_raw(),
1962 &thread_ref,
1963 &self.category_ref,
1964 &name_ref,
1965 bytes.as_ptr() as *const core::ffi::c_void,
1966 bytes.len(),
1967 args.as_ptr() as *const sys::trace_arg_t,
1968 args.len(),
1969 );
1970 }
1971 }
1972
1973 fn write_flow_begin(
1974 &self,
1975 ticks: zx::BootTicks,
1976 name_ref: sys::trace_string_ref_t,
1977 flow_id: Id,
1978 args: &[Arg<'_>],
1979 ) {
1980 let thread_ref = self.register_current_thread();
1981 unsafe {
1982 sys::trace_context_write_flow_begin_event_record(
1983 self.context.raw,
1984 ticks.into_raw(),
1985 &thread_ref,
1986 &self.category_ref,
1987 &name_ref,
1988 flow_id.into(),
1989 args.as_ptr() as *const sys::trace_arg_t,
1990 args.len(),
1991 );
1992 }
1993 }
1994
1995 fn write_flow_end(
1996 &self,
1997 ticks: zx::BootTicks,
1998 name_ref: sys::trace_string_ref_t,
1999 flow_id: Id,
2000 args: &[Arg<'_>],
2001 ) {
2002 let thread_ref = self.register_current_thread();
2003 unsafe {
2004 sys::trace_context_write_flow_end_event_record(
2005 self.context.raw,
2006 ticks.into_raw(),
2007 &thread_ref,
2008 &self.category_ref,
2009 &name_ref,
2010 flow_id.into(),
2011 args.as_ptr() as *const sys::trace_arg_t,
2012 args.len(),
2013 );
2014 }
2015 }
2016
2017 fn write_flow_step(
2018 &self,
2019 ticks: zx::BootTicks,
2020 name_ref: sys::trace_string_ref_t,
2021 flow_id: Id,
2022 args: &[Arg<'_>],
2023 ) {
2024 let thread_ref = self.register_current_thread();
2025 unsafe {
2026 sys::trace_context_write_flow_step_event_record(
2027 self.context.raw,
2028 ticks.into_raw(),
2029 &thread_ref,
2030 &self.category_ref,
2031 &name_ref,
2032 flow_id.into(),
2033 args.as_ptr() as *const sys::trace_arg_t,
2034 args.len(),
2035 );
2036 }
2037 }
2038}
2039
2040pub struct Context {
2042 raw: *const sys::trace_context_t,
2043}
2044
2045impl Context {
2046 #[inline]
2047 pub fn acquire() -> Option<Self> {
2048 let context = unsafe { sys::trace_acquire_context() };
2049 if context.is_null() { None } else { Some(Self { raw: context }) }
2050 }
2051
2052 #[inline]
2053 pub fn register_string_literal<T: CategoryString>(&self, s: T) -> sys::trace_string_ref_t {
2054 s.register(self)
2055 }
2056
2057 pub fn write_blob_record(
2058 &self,
2059 type_: sys::trace_blob_type_t,
2060 name_ref: &sys::trace_string_ref_t,
2061 data: &[u8],
2062 ) {
2063 unsafe {
2064 sys::trace_context_write_blob_record(
2065 self.raw,
2066 type_,
2067 name_ref as *const sys::trace_string_ref_t,
2068 data.as_ptr() as *const libc::c_void,
2069 data.len(),
2070 );
2071 }
2072 }
2073
2074 pub fn copy_record(&self, buffer: &[u64]) -> Option<usize> {
2078 unsafe {
2079 let ptr = sys::trace_context_alloc_record(self.raw, 8 * buffer.len() as libc::size_t);
2080 if ptr == std::ptr::null_mut() {
2081 return None;
2082 }
2083 ptr.cast::<u64>().copy_from(buffer.as_ptr(), buffer.len());
2084 };
2085 Some(buffer.len())
2086 }
2087
2088 pub fn buffering_mode(&self) -> BufferingMode {
2089 match unsafe { sys::trace_context_get_buffering_mode(self.raw) } {
2090 sys::TRACE_BUFFERING_MODE_ONESHOT => BufferingMode::OneShot,
2091 sys::TRACE_BUFFERING_MODE_CIRCULAR => BufferingMode::Circular,
2092 sys::TRACE_BUFFERING_MODE_STREAMING => BufferingMode::Streaming,
2093 m => panic!("Unknown trace buffering mode: {:?}", m),
2094 }
2095 }
2096}
2097
2098impl std::ops::Drop for Context {
2099 fn drop(&mut self) {
2100 unsafe { sys::trace_release_context(self.raw) }
2101 }
2102}
2103
2104pub struct ProlongedContext {
2105 context: *const sys::trace_prolonged_context_t,
2106}
2107
2108impl ProlongedContext {
2109 pub fn acquire() -> Option<Self> {
2110 let context = unsafe { sys::trace_acquire_prolonged_context() };
2111 if context.is_null() { None } else { Some(Self { context }) }
2112 }
2113}
2114
2115impl Drop for ProlongedContext {
2116 fn drop(&mut self) {
2117 unsafe { sys::trace_release_prolonged_context(self.context) }
2118 }
2119}
2120
2121unsafe impl Send for ProlongedContext {}
2122
2123mod sys {
2124 #![allow(non_camel_case_types, unused)]
2125 use zx::sys::{zx_handle_t, zx_koid_t, zx_obj_type_t, zx_status_t, zx_ticks_t};
2126
2127 pub type trace_ticks_t = zx_ticks_t;
2128 pub type trace_counter_id_t = u64;
2129 pub type trace_async_id_t = u64;
2130 pub type trace_flow_id_t = u64;
2131 pub type trace_vthread_id_t = u64;
2132 pub type trace_thread_state_t = u32;
2133 pub type trace_cpu_number_t = u32;
2134 pub type trace_string_index_t = u32;
2135 pub type trace_thread_index_t = u32;
2136 pub type trace_context_t = libc::c_void;
2137 pub type trace_prolonged_context_t = libc::c_void;
2138
2139 pub type trace_encoded_string_ref_t = u16;
2140 pub const TRACE_ENCODED_STRING_REF_EMPTY: trace_encoded_string_ref_t = 0;
2141 pub const TRACE_ENCODED_STRING_REF_INLINE_FLAG: trace_encoded_string_ref_t = 0x8000;
2142 pub const TRACE_ENCODED_STRING_REF_LENGTH_MASK: trace_encoded_string_ref_t = 0x7fff;
2143 pub const TRACE_ENCODED_STRING_REF_MAX_LENGTH: usize = 32000;
2144 pub const TRACE_ENCODED_STRING_REF_MIN_INDEX: trace_encoded_string_ref_t = 0x1;
2145 pub const TRACE_ENCODED_STRING_REF_MAX_INDEX: trace_encoded_string_ref_t = 0x7fff;
2146
2147 pub type trace_encoded_thread_ref_t = u32;
2148 pub const TRACE_ENCODED_THREAD_REF_INLINE: trace_encoded_thread_ref_t = 0;
2149 pub const TRACE_ENCODED_THREAD_MIN_INDEX: trace_encoded_thread_ref_t = 0x01;
2150 pub const TRACE_ENCODED_THREAD_MAX_INDEX: trace_encoded_thread_ref_t = 0xff;
2151
2152 pub type trace_state_t = libc::c_int;
2153 pub const TRACE_STOPPED: trace_state_t = 0;
2154 pub const TRACE_STARTED: trace_state_t = 1;
2155 pub const TRACE_STOPPING: trace_state_t = 2;
2156
2157 pub type trace_scope_t = libc::c_int;
2158 pub const TRACE_SCOPE_THREAD: trace_scope_t = 0;
2159 pub const TRACE_SCOPE_PROCESS: trace_scope_t = 1;
2160 pub const TRACE_SCOPE_GLOBAL: trace_scope_t = 2;
2161
2162 pub type trace_blob_type_t = libc::c_int;
2163 pub const TRACE_BLOB_TYPE_DATA: trace_blob_type_t = 1;
2164 pub const TRACE_BLOB_TYPE_LAST_BRANCH: trace_blob_type_t = 2;
2165 pub const TRACE_BLOB_TYPE_PERFETTO: trace_blob_type_t = 3;
2166
2167 pub type trace_buffering_mode_t = libc::c_int;
2168 pub const TRACE_BUFFERING_MODE_ONESHOT: trace_buffering_mode_t = 0;
2169 pub const TRACE_BUFFERING_MODE_CIRCULAR: trace_buffering_mode_t = 1;
2170 pub const TRACE_BUFFERING_MODE_STREAMING: trace_buffering_mode_t = 2;
2171
2172 #[repr(C)]
2173 #[derive(Copy, Clone)]
2174 pub struct trace_string_ref_t {
2175 pub encoded_value: trace_encoded_string_ref_t,
2176 pub inline_string: *const libc::c_char,
2177 }
2178
2179 pub type trace_site_t = std::sync::atomic::AtomicU64;
2183
2184 unsafe impl Send for trace_string_ref_t {}
2194 unsafe impl Sync for trace_string_ref_t {}
2195
2196 #[repr(C)]
2197 pub struct trace_thread_ref_t {
2198 pub encoded_value: trace_encoded_thread_ref_t,
2199 pub inline_process_koid: zx_koid_t,
2200 pub inline_thread_koid: zx_koid_t,
2201 }
2202
2203 #[repr(C)]
2204 pub struct trace_arg_t {
2205 pub name_ref: trace_string_ref_t,
2206 pub value: trace_arg_value_t,
2207 }
2208
2209 #[repr(C)]
2210 pub union trace_arg_union_t {
2211 pub int32_value: i32,
2212 pub uint32_value: u32,
2213 pub int64_value: i64,
2214 pub uint64_value: u64,
2215 pub double_value: libc::c_double,
2216 pub string_value_ref: trace_string_ref_t,
2217 pub pointer_value: libc::uintptr_t,
2218 pub koid_value: zx_koid_t,
2219 pub bool_value: bool,
2220 pub reserved_for_future_expansion: [libc::uintptr_t; 2],
2221 }
2222
2223 pub type trace_arg_type_t = libc::c_int;
2224 pub const TRACE_ARG_NULL: trace_arg_type_t = 0;
2225 pub const TRACE_ARG_INT32: trace_arg_type_t = 1;
2226 pub const TRACE_ARG_UINT32: trace_arg_type_t = 2;
2227 pub const TRACE_ARG_INT64: trace_arg_type_t = 3;
2228 pub const TRACE_ARG_UINT64: trace_arg_type_t = 4;
2229 pub const TRACE_ARG_DOUBLE: trace_arg_type_t = 5;
2230 pub const TRACE_ARG_STRING: trace_arg_type_t = 6;
2231 pub const TRACE_ARG_POINTER: trace_arg_type_t = 7;
2232 pub const TRACE_ARG_KOID: trace_arg_type_t = 8;
2233 pub const TRACE_ARG_BOOL: trace_arg_type_t = 9;
2234
2235 #[repr(C)]
2236 pub struct trace_arg_value_t {
2237 pub type_: trace_arg_type_t,
2238 pub value: trace_arg_union_t,
2239 }
2240
2241 #[repr(C)]
2242 pub struct trace_handler_ops_t {
2243 pub is_category_enabled:
2244 unsafe fn(handler: *const trace_handler_t, category: *const libc::c_char) -> bool,
2245 pub trace_started: unsafe fn(handler: *const trace_handler_t),
2246 pub trace_stopped: unsafe fn(
2247 handler: *const trace_handler_t,
2248 async_ptr: *const (), disposition: zx_status_t,
2250 buffer_bytes_written: libc::size_t,
2251 ),
2252 pub buffer_overflow: unsafe fn(handler: *const trace_handler_t),
2253 }
2254
2255 #[repr(C)]
2256 pub struct trace_handler_t {
2257 pub ops: *const trace_handler_ops_t,
2258 }
2259
2260 unsafe extern "C" {
2262 pub fn trace_context_is_category_enabled(
2265 context: *const trace_context_t,
2266 category_literal: *const libc::c_char,
2267 ) -> bool;
2268
2269 pub fn trace_context_register_string_literal(
2270 context: *const trace_context_t,
2271 string_literal: *const libc::c_char,
2272 out_ref: *mut trace_string_ref_t,
2273 );
2274
2275 #[cfg(fuchsia_api_level_at_least = "27")]
2276 pub fn trace_context_register_bytestring(
2277 context: *const trace_context_t,
2278 string_literal: *const libc::c_char,
2279 length: libc::size_t,
2280 out_ref: *mut trace_string_ref_t,
2281 );
2282
2283 pub fn trace_context_register_category_literal(
2284 context: *const trace_context_t,
2285 category_literal: *const libc::c_char,
2286 out_ref: *mut trace_string_ref_t,
2287 ) -> bool;
2288
2289 pub fn trace_context_register_current_thread(
2290 context: *const trace_context_t,
2291 out_ref: *mut trace_thread_ref_t,
2292 );
2293
2294 pub fn trace_context_register_thread(
2295 context: *const trace_context_t,
2296 process_koid: zx_koid_t,
2297 thread_koid: zx_koid_t,
2298 out_ref: *mut trace_thread_ref_t,
2299 );
2300
2301 #[cfg(fuchsia_api_level_at_least = "NEXT")]
2302 pub fn trace_context_register_vthread_by_ref(
2303 context: *const trace_context_t,
2304 process_koid: zx_koid_t,
2305 vthread_name: *const trace_string_ref_t,
2306 vthread_id: trace_vthread_id_t,
2307 out_ref: *mut trace_thread_ref_t,
2308 );
2309
2310 pub fn trace_context_write_kernel_object_record(
2311 context: *const trace_context_t,
2312 koid: zx_koid_t,
2313 type_: zx_obj_type_t,
2314 args: *const trace_arg_t,
2315 num_args: libc::size_t,
2316 );
2317
2318 pub fn trace_context_write_kernel_object_record_for_handle(
2319 context: *const trace_context_t,
2320 handle: zx_handle_t,
2321 args: *const trace_arg_t,
2322 num_args: libc::size_t,
2323 );
2324
2325 pub fn trace_context_write_process_info_record(
2326 context: *const trace_context_t,
2327 process_koid: zx_koid_t,
2328 process_name_ref: *const trace_string_ref_t,
2329 );
2330
2331 pub fn trace_context_write_thread_info_record(
2332 context: *const trace_context_t,
2333 process_koid: zx_koid_t,
2334 thread_koid: zx_koid_t,
2335 thread_name_ref: *const trace_string_ref_t,
2336 );
2337
2338 pub fn trace_context_write_context_switch_record(
2339 context: *const trace_context_t,
2340 event_time: trace_ticks_t,
2341 cpu_number: trace_cpu_number_t,
2342 outgoing_thread_state: trace_thread_state_t,
2343 outgoing_thread_ref: *const trace_thread_ref_t,
2344 incoming_thread_ref: *const trace_thread_ref_t,
2345 );
2346
2347 pub fn trace_context_write_log_record(
2348 context: *const trace_context_t,
2349 event_time: trace_ticks_t,
2350 thread_ref: *const trace_thread_ref_t,
2351 log_message: *const libc::c_char,
2352 log_message_length: libc::size_t,
2353 );
2354
2355 pub fn trace_context_write_instant_event_record(
2356 context: *const trace_context_t,
2357 event_time: trace_ticks_t,
2358 thread_ref: *const trace_thread_ref_t,
2359 category_ref: *const trace_string_ref_t,
2360 name_ref: *const trace_string_ref_t,
2361 scope: trace_scope_t,
2362 args: *const trace_arg_t,
2363 num_args: libc::size_t,
2364 );
2365
2366 pub fn trace_context_send_alert(context: *const trace_context_t, name: *const libc::c_char);
2367
2368 #[cfg(fuchsia_api_level_at_least = "27")]
2369 pub fn trace_context_send_alert_bytestring(
2370 context: *const trace_context_t,
2371 name: *const u8,
2372 length: usize,
2373 );
2374
2375 pub fn trace_context_write_counter_event_record(
2376 context: *const trace_context_t,
2377 event_time: trace_ticks_t,
2378 thread_ref: *const trace_thread_ref_t,
2379 category_ref: *const trace_string_ref_t,
2380 name_ref: *const trace_string_ref_t,
2381 counter_id: trace_counter_id_t,
2382 args: *const trace_arg_t,
2383 num_args: libc::size_t,
2384 );
2385
2386 pub fn trace_context_write_duration_event_record(
2387 context: *const trace_context_t,
2388 start_time: trace_ticks_t,
2389 end_time: trace_ticks_t,
2390 thread_ref: *const trace_thread_ref_t,
2391 category_ref: *const trace_string_ref_t,
2392 name_ref: *const trace_string_ref_t,
2393 args: *const trace_arg_t,
2394 num_args: libc::size_t,
2395 );
2396
2397 pub fn trace_context_write_blob_event_record(
2398 context: *const trace_context_t,
2399 event_time: trace_ticks_t,
2400 thread_ref: *const trace_thread_ref_t,
2401 category_ref: *const trace_string_ref_t,
2402 name_ref: *const trace_string_ref_t,
2403 blob: *const libc::c_void,
2404 blob_size: libc::size_t,
2405 args: *const trace_arg_t,
2406 num_args: libc::size_t,
2407 );
2408
2409 pub fn trace_context_write_duration_begin_event_record(
2410 context: *const trace_context_t,
2411 event_time: trace_ticks_t,
2412 thread_ref: *const trace_thread_ref_t,
2413 category_ref: *const trace_string_ref_t,
2414 name_ref: *const trace_string_ref_t,
2415 args: *const trace_arg_t,
2416 num_args: libc::size_t,
2417 );
2418
2419 pub fn trace_context_write_duration_end_event_record(
2420 context: *const trace_context_t,
2421 event_time: trace_ticks_t,
2422 thread_ref: *const trace_thread_ref_t,
2423 category_ref: *const trace_string_ref_t,
2424 name_ref: *const trace_string_ref_t,
2425 args: *const trace_arg_t,
2426 num_args: libc::size_t,
2427 );
2428
2429 pub fn trace_context_write_async_begin_event_record(
2430 context: *const trace_context_t,
2431 event_time: trace_ticks_t,
2432 thread_ref: *const trace_thread_ref_t,
2433 category_ref: *const trace_string_ref_t,
2434 name_ref: *const trace_string_ref_t,
2435 async_id: trace_async_id_t,
2436 args: *const trace_arg_t,
2437 num_args: libc::size_t,
2438 );
2439
2440 pub fn trace_context_write_async_instant_event_record(
2441 context: *const trace_context_t,
2442 event_time: trace_ticks_t,
2443 thread_ref: *const trace_thread_ref_t,
2444 category_ref: *const trace_string_ref_t,
2445 name_ref: *const trace_string_ref_t,
2446 async_id: trace_async_id_t,
2447 args: *const trace_arg_t,
2448 num_args: libc::size_t,
2449 );
2450
2451 pub fn trace_context_write_async_end_event_record(
2452 context: *const trace_context_t,
2453 event_time: trace_ticks_t,
2454 thread_ref: *const trace_thread_ref_t,
2455 category_ref: *const trace_string_ref_t,
2456 name_ref: *const trace_string_ref_t,
2457 async_id: trace_async_id_t,
2458 args: *const trace_arg_t,
2459 num_args: libc::size_t,
2460 );
2461
2462 pub fn trace_context_write_flow_begin_event_record(
2463 context: *const trace_context_t,
2464 event_time: trace_ticks_t,
2465 thread_ref: *const trace_thread_ref_t,
2466 category_ref: *const trace_string_ref_t,
2467 name_ref: *const trace_string_ref_t,
2468 flow_id: trace_flow_id_t,
2469 args: *const trace_arg_t,
2470 num_args: libc::size_t,
2471 );
2472
2473 pub fn trace_context_write_flow_step_event_record(
2474 context: *const trace_context_t,
2475 event_time: trace_ticks_t,
2476 thread_ref: *const trace_thread_ref_t,
2477 category_ref: *const trace_string_ref_t,
2478 name_ref: *const trace_string_ref_t,
2479 flow_id: trace_flow_id_t,
2480 args: *const trace_arg_t,
2481 num_args: libc::size_t,
2482 );
2483
2484 pub fn trace_context_write_flow_end_event_record(
2485 context: *const trace_context_t,
2486 event_time: trace_ticks_t,
2487 thread_ref: *const trace_thread_ref_t,
2488 category_ref: *const trace_string_ref_t,
2489 name_ref: *const trace_string_ref_t,
2490 flow_id: trace_flow_id_t,
2491 args: *const trace_arg_t,
2492 num_args: libc::size_t,
2493 );
2494
2495 pub fn trace_context_write_initialization_record(
2496 context: *const trace_context_t,
2497 ticks_per_second: u64,
2498 );
2499
2500 pub fn trace_context_write_string_record(
2501 context: *const trace_context_t,
2502 index: trace_string_index_t,
2503 string: *const libc::c_char,
2504 length: libc::size_t,
2505 );
2506
2507 pub fn trace_context_write_thread_record(
2508 context: *const trace_context_t,
2509 index: trace_thread_index_t,
2510 procss_koid: zx_koid_t,
2511 thread_koid: zx_koid_t,
2512 );
2513
2514 pub fn trace_context_write_blob_record(
2515 context: *const trace_context_t,
2516 type_: trace_blob_type_t,
2517 name_ref: *const trace_string_ref_t,
2518 data: *const libc::c_void,
2519 size: libc::size_t,
2520 );
2521
2522 pub fn trace_context_alloc_record(
2523 context: *const trace_context_t,
2524 num_bytes: libc::size_t,
2525 ) -> *mut libc::c_void;
2526
2527 pub fn trace_generate_nonce() -> u64;
2530
2531 pub fn trace_state() -> trace_state_t;
2532
2533 #[cfg(fuchsia_api_level_at_least = "27")]
2534 pub fn trace_is_category_bytestring_enabled(
2535 category_literal: *const u8,
2536 length: usize,
2537 ) -> bool;
2538
2539 pub fn trace_is_category_enabled(category_literal: *const libc::c_char) -> bool;
2540
2541 pub fn trace_acquire_context() -> *const trace_context_t;
2542
2543 pub fn trace_acquire_context_for_category(
2544 category_literal: *const libc::c_char,
2545 out_ref: *mut trace_string_ref_t,
2546 ) -> *const trace_context_t;
2547
2548 pub fn trace_acquire_context_for_category_cached(
2549 category_literal: *const libc::c_char,
2550 trace_site: *const u64,
2551 out_ref: *mut trace_string_ref_t,
2552 ) -> *const trace_context_t;
2553
2554 #[cfg(fuchsia_api_level_at_least = "27")]
2555 pub fn trace_acquire_context_for_category_bytestring(
2556 bytes: *const u8,
2557 length: usize,
2558 out_ref: *mut trace_string_ref_t,
2559 ) -> *const trace_context_t;
2560
2561 #[cfg(fuchsia_api_level_at_least = "27")]
2562 pub fn trace_acquire_context_for_category_bytestring_cached(
2563 bytes: *const u8,
2564 length: usize,
2565 trace_site: *const u64,
2566 out_ref: *mut trace_string_ref_t,
2567 ) -> *const trace_context_t;
2568
2569 pub fn trace_release_context(context: *const trace_context_t);
2570
2571 pub fn trace_acquire_prolonged_context() -> *const trace_prolonged_context_t;
2572
2573 pub fn trace_release_prolonged_context(context: *const trace_prolonged_context_t);
2574
2575 pub fn trace_register_observer(event: zx_handle_t) -> zx_status_t;
2576
2577 pub fn trace_unregister_observer(event: zx_handle_t) -> zx_status_t;
2578
2579 pub fn trace_notify_observer_updated(event: zx_handle_t);
2580
2581 pub fn trace_context_get_buffering_mode(
2582 context: *const trace_context_t,
2583 ) -> trace_buffering_mode_t;
2584 }
2585}
2586
2587pub struct TraceFutureArgs<'a, C: CategoryString, S: AsTraceStrRef> {
2590 pub category: C,
2591 pub name: S,
2592
2593 pub args: Box<[Arg<'a>]>,
2596
2597 pub flow_id: Option<Id>,
2600
2601 pub _use_trace_future_args: (),
2603}
2604
2605#[doc(hidden)]
2606#[macro_export]
2607macro_rules! __impl_trace_future_args {
2608 ($category:expr, $name:expr, $flow_id:expr) => {
2611 $crate::TraceFutureArgs {
2612 category: $category,
2613 name: $name,
2614 args: ::std::boxed::Box::new([]),
2615 flow_id: $flow_id,
2616 _use_trace_future_args: (),
2617 }
2618 };
2619 ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)*) => {{
2620 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
2621 use $crate::AsTraceStrRef;
2622 let context = $crate::TraceCategoryContext::acquire_cached($category, &CACHE);
2623 let args: ::std::boxed::Box<[$crate::Arg<'_>]> = if let Some(context) = context {
2624 ::std::boxed::Box::new(
2625 [$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]
2626 )
2627 } else {
2628 ::std::boxed::Box::new([])
2629 };
2630 $crate::TraceFutureArgs {
2631 category: $category,
2632 name: $name,
2633 args: args,
2634 flow_id: $flow_id,
2635 _use_trace_future_args: (),
2636 }
2637 }};
2638}
2639
2640#[macro_export]
2652macro_rules! trace_future_args {
2653 ($category:expr, $name:expr $(, $key:expr => $val:expr)* $(,)?) => {
2654 $crate::__impl_trace_future_args!($category, $name, None $(,$key => $val)*)
2655 };
2656 ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)* $(,)?) => {
2657 $crate::__impl_trace_future_args!($category, $name, Some($flow_id) $(,$key => $val)*)
2658 };
2659}
2660
2661pub trait TraceFutureExt: Future + Sized {
2663 #[inline(always)]
2677 fn trace<'a, C: CategoryString, S: AsTraceStrRef>(
2678 self,
2679 args: TraceFutureArgs<'a, C, S>,
2680 ) -> TraceFuture<'a, Self, C, S> {
2681 TraceFuture::new(args, self)
2682 }
2683}
2684
2685impl<T: Future + Sized> TraceFutureExt for T {}
2686
2687#[pin_project]
2690pub struct TraceFuture<'a, Fut: Future, C: CategoryString, S: AsTraceStrRef> {
2691 #[pin]
2692 future: Fut,
2693 category: C,
2694 name: S,
2695 args: Box<[Arg<'a>]>,
2696 flow_id: Option<Id>,
2697 poll_count: u64,
2698}
2699
2700impl<'a, Fut: Future, C: CategoryString, S: AsTraceStrRef> TraceFuture<'a, Fut, C, S> {
2701 #[inline(always)]
2702 pub fn new(args: TraceFutureArgs<'a, C, S>, future: Fut) -> Self {
2703 Self {
2704 future,
2705 category: args.category,
2706 name: args.name,
2707 args: args.args,
2708 flow_id: args.flow_id,
2709 poll_count: 0,
2710 }
2711 }
2712
2713 #[cold]
2714 fn trace_poll(
2715 self: Pin<&mut Self>,
2716 context: &TraceCategoryContext,
2717 cx: &mut std::task::Context<'_>,
2718 ) -> Poll<Fut::Output> {
2719 let start_time = zx::BootTicks::get();
2720 let this = self.project();
2721 *this.poll_count = this.poll_count.saturating_add(1);
2722 let name_ref = this.name.as_trace_str_ref(context);
2723 context.write_duration_begin(start_time, name_ref, &this.args);
2724
2725 let result = this.future.poll(cx);
2726
2727 let flow_id = this.flow_id.get_or_insert_with(Id::new);
2728 let result_str: sys::trace_string_ref_t = if result.is_pending() {
2729 if *this.poll_count == 1 {
2730 context.write_flow_begin(start_time, name_ref, *flow_id, &[]);
2731 } else {
2732 context.write_flow_step(start_time, name_ref, *flow_id, &[]);
2733 }
2734 context.register_str("pending")
2735 } else {
2736 if *this.poll_count != 1 {
2737 context.write_flow_end(start_time, name_ref, *flow_id, &[]);
2738 }
2739 context.register_str("ready")
2740 };
2741 context.write_duration_end(
2742 zx::BootTicks::get(),
2743 name_ref,
2744 &[
2745 ArgValue::of_registered(context.register_str("poll-state"), result_str),
2746 ArgValue::of_registered(context.register_str("poll-count"), *this.poll_count),
2747 ],
2748 );
2749 result
2750 }
2751}
2752
2753impl<Fut: Future, C: CategoryString, S: AsTraceStrRef> Future for TraceFuture<'_, Fut, C, S> {
2754 type Output = Fut::Output;
2755 fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Fut::Output> {
2756 if let Some(context) = TraceCategoryContext::acquire(self.as_ref().get_ref().category) {
2757 self.trace_poll(&context, cx)
2758 } else {
2759 self.project().future.poll(cx)
2760 }
2761 }
2762}
2763
2764#[cfg(test)]
2765mod test {
2766 use super::{Id, trim_to_last_char_boundary};
2767
2768 #[test]
2769 fn trim_to_last_char_boundary_trims_to_last_character_boundary() {
2770 assert_eq!(b"x", trim_to_last_char_boundary("x", 5));
2771 assert_eq!(b"x", trim_to_last_char_boundary("x", 1));
2772 assert_eq!(b"", trim_to_last_char_boundary("x", 0));
2773 assert_eq!(b"xxxxx", trim_to_last_char_boundary("xxxxx", 6));
2774 assert_eq!(b"xxxxx", trim_to_last_char_boundary("xxxxx", 5));
2775 assert_eq!(b"xxxx", trim_to_last_char_boundary("xxxxx", 4));
2776
2777 assert_eq!("💩".as_bytes(), trim_to_last_char_boundary("💩", 5));
2778 assert_eq!("💩".as_bytes(), trim_to_last_char_boundary("💩", 4));
2779 assert_eq!(b"", trim_to_last_char_boundary("💩", 3));
2780 }
2781
2782 #[test]
2786 fn test_id_new() {
2787 assert_ne!(Id::new(), Id::new());
2788 }
2789}