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 AsTraceStrRef for std::borrow::Cow<'static, str> {
315 #[inline]
316 fn as_trace_str_ref(&self, context: &TraceCategoryContext) -> sys::trace_string_ref_t {
317 match self {
318 std::borrow::Cow::Borrowed(s) => s.as_trace_str_ref(context),
319 std::borrow::Cow::Owned(s) => s.as_trace_str_ref(context),
320 }
321 }
322}
323
324impl<T: AsTraceStrRef> AsTraceStrRef for &T {
326 #[inline]
327 fn as_trace_str_ref(&self, context: &TraceCategoryContext) -> sys::trace_string_ref_t {
328 (*self).as_trace_str_ref(context)
329 }
330}
331
332#[repr(transparent)]
334pub struct Arg<'a>(sys::trace_arg_t, PhantomData<&'a ()>);
335
336pub trait ArgValue {
342 fn of<'a>(key: &'a str, value: Self) -> Arg<'a>
343 where
344 Self: 'a;
345 fn of_registered<'a>(name_ref: sys::trace_string_ref_t, value: Self) -> Arg<'a>
346 where
347 Self: 'a;
348}
349
350macro_rules! arg_from {
356 ($valname:ident, $(($type:ty, $tag:expr, $value:expr))*) => {
357 $(
358 impl ArgValue for $type {
359 #[inline]
360 fn of<'a>(key: &'a str, $valname: Self) -> Arg<'a>
361 where Self: 'a
362 {
363 #[allow(unused)]
364 let $valname = $valname;
365
366 Arg(sys::trace_arg_t {
367 name_ref: trace_make_inline_string_ref(key),
368 value: sys::trace_arg_value_t {
369 type_: $tag,
370 value: $value,
371 },
372 }, PhantomData)
373 }
374 #[inline]
375 fn of_registered<'a>(name_ref: sys::trace_string_ref_t, $valname: Self) -> Arg<'a>
376 where Self: 'a
377 {
378 #[allow(unused)]
379 let $valname = $valname;
380
381 Arg(sys::trace_arg_t {
382 name_ref,
383 value: sys::trace_arg_value_t {
384 type_: $tag,
385 value: $value,
386 },
387 }, PhantomData)
388 }
389 }
390 )*
391 }
392}
393
394#[rustfmt::skip]
396arg_from!(val,
397 ((), sys::TRACE_ARG_NULL, sys::trace_arg_union_t { int32_value: 0 })
398 (bool, sys::TRACE_ARG_BOOL, sys::trace_arg_union_t { bool_value: val })
399 (i32, sys::TRACE_ARG_INT32, sys::trace_arg_union_t { int32_value: val })
400 (u32, sys::TRACE_ARG_UINT32, sys::trace_arg_union_t { uint32_value: val })
401 (i64, sys::TRACE_ARG_INT64, sys::trace_arg_union_t { int64_value: val })
402 (u64, sys::TRACE_ARG_UINT64, sys::trace_arg_union_t { uint64_value: val })
403 (isize, sys::TRACE_ARG_INT64, sys::trace_arg_union_t { int64_value: val as i64 })
404 (usize, sys::TRACE_ARG_UINT64, sys::trace_arg_union_t { uint64_value: val as u64 })
405 (f64, sys::TRACE_ARG_DOUBLE, sys::trace_arg_union_t { double_value: val })
406 (zx::Koid, sys::TRACE_ARG_KOID, sys::trace_arg_union_t { koid_value: val.raw_koid() })
407);
408
409impl<T> ArgValue for *const T {
410 #[inline]
411 fn of<'a>(key: &'a str, val: Self) -> Arg<'a>
412 where
413 Self: 'a,
414 {
415 Arg(
416 sys::trace_arg_t {
417 name_ref: trace_make_inline_string_ref(key),
418 value: sys::trace_arg_value_t {
419 type_: sys::TRACE_ARG_POINTER,
420 value: sys::trace_arg_union_t { pointer_value: val as usize },
421 },
422 },
423 PhantomData,
424 )
425 }
426 #[inline]
427 fn of_registered<'a>(name_ref: sys::trace_string_ref_t, val: Self) -> Arg<'a>
428 where
429 Self: 'a,
430 {
431 Arg(
432 sys::trace_arg_t {
433 name_ref,
434 value: sys::trace_arg_value_t {
435 type_: sys::TRACE_ARG_POINTER,
436 value: sys::trace_arg_union_t { pointer_value: val as usize },
437 },
438 },
439 PhantomData,
440 )
441 }
442}
443
444impl<T> ArgValue for *mut T {
445 #[inline]
446 fn of<'a>(key: &'a str, val: Self) -> Arg<'a>
447 where
448 Self: 'a,
449 {
450 Arg(
451 sys::trace_arg_t {
452 name_ref: trace_make_inline_string_ref(key),
453 value: sys::trace_arg_value_t {
454 type_: sys::TRACE_ARG_POINTER,
455 value: sys::trace_arg_union_t { pointer_value: val as usize },
456 },
457 },
458 PhantomData,
459 )
460 }
461 #[inline]
462 fn of_registered<'a>(name_ref: sys::trace_string_ref_t, val: Self) -> Arg<'a>
463 where
464 Self: 'a,
465 {
466 Arg(
467 sys::trace_arg_t {
468 name_ref,
469 value: sys::trace_arg_value_t {
470 type_: sys::TRACE_ARG_POINTER,
471 value: sys::trace_arg_union_t { pointer_value: val as usize },
472 },
473 },
474 PhantomData,
475 )
476 }
477}
478
479impl<'a> ArgValue for &'a str {
480 #[inline]
481 fn of<'b>(key: &'b str, val: Self) -> Arg<'b>
482 where
483 Self: 'b,
484 {
485 Arg(
486 sys::trace_arg_t {
487 name_ref: trace_make_inline_string_ref(key),
488 value: sys::trace_arg_value_t {
489 type_: sys::TRACE_ARG_STRING,
490 value: sys::trace_arg_union_t {
491 string_value_ref: trace_make_inline_string_ref(val),
492 },
493 },
494 },
495 PhantomData,
496 )
497 }
498 #[inline]
499 fn of_registered<'b>(name_ref: sys::trace_string_ref_t, val: Self) -> Arg<'b>
500 where
501 Self: 'b,
502 {
503 Arg(
504 sys::trace_arg_t {
505 name_ref,
506 value: sys::trace_arg_value_t {
507 type_: sys::TRACE_ARG_STRING,
508 value: sys::trace_arg_union_t {
509 string_value_ref: trace_make_inline_string_ref(val),
510 },
511 },
512 },
513 PhantomData,
514 )
515 }
516}
517
518impl<'a> ArgValue for sys::trace_string_ref_t {
519 #[inline]
520 fn of<'b>(key: &'b str, val: Self) -> Arg<'b>
521 where
522 Self: 'b,
523 {
524 Arg(
525 sys::trace_arg_t {
526 name_ref: trace_make_inline_string_ref(key),
527 value: sys::trace_arg_value_t {
528 type_: sys::TRACE_ARG_STRING,
529 value: sys::trace_arg_union_t { string_value_ref: val },
530 },
531 },
532 PhantomData,
533 )
534 }
535 #[inline]
536 fn of_registered<'b>(name_ref: sys::trace_string_ref_t, val: Self) -> Arg<'b>
537 where
538 Self: 'b,
539 {
540 Arg(
541 sys::trace_arg_t {
542 name_ref,
543 value: sys::trace_arg_value_t {
544 type_: sys::TRACE_ARG_STRING,
545 value: sys::trace_arg_union_t { string_value_ref: val },
546 },
547 },
548 PhantomData,
549 )
550 }
551}
552
553#[macro_export]
575macro_rules! instant {
576 ($category:expr, $name:expr, $scope:expr $(, $key:expr => $val:expr)*) => {
577 {
578 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
579 use $crate::AsTraceStrRef;
580 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
581 $crate::instant(&context, $name, $scope, &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]);
582 }
583 }
584 }
585}
586
587#[inline]
590pub fn instant<S: AsTraceStrRef>(
591 context: &TraceCategoryContext,
592 name: S,
593 scope: Scope,
594 args: &[Arg<'_>],
595) {
596 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
597
598 let name_ref = name.as_trace_str_ref(context);
599 context.write_instant(name_ref, scope, args);
600}
601
602#[macro_export]
616macro_rules! alert {
617 ($category:expr, $name:expr) => {
618 $crate::alert($category, $name)
619 };
620}
621
622pub fn alert<C: CategoryString, S: AlertString>(category: C, name: S) {
624 if let Some(context) = category.acquire_context() {
625 name.send_alert(&context.context);
626 }
627}
628
629#[macro_export]
646macro_rules! counter {
647 ($category:expr, $name:expr, $counter_id:expr $(, $key:expr => $val:expr)*) => {
648 {
649 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
650 use $crate::AsTraceStrRef;
651 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
652 $crate::counter(&context, $name, $counter_id,
653 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
654 }
655 }
656 }
657}
658
659pub fn counter<S: AsTraceStrRef>(
669 context: &TraceCategoryContext,
670 name: S,
671 counter_id: u64,
672 args: &[Arg<'_>],
673) {
674 assert!(args.len() >= 1, "trace counter args must include at least one numeric argument");
675 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
676
677 let name_ref = name.as_trace_str_ref(context);
678 context.write_counter(name_ref, counter_id, args);
679}
680
681#[must_use = "DurationScope must be `end`ed to be recorded"]
684pub struct DurationScope<'a, C: CategoryString, S: AsTraceStrRef> {
685 category: C,
686 name: S,
687 args: &'a [Arg<'a>],
688 start_time: zx::BootTicks,
689}
690
691impl<'a, C: CategoryString, S: AsTraceStrRef> DurationScope<'a, C, S> {
692 pub fn begin(category: C, name: S, args: &'a [Arg<'_>]) -> Self {
695 let start_time = zx::BootTicks::get();
696 Self { category, name, args, start_time }
697 }
698}
699
700impl<'a, C: CategoryString, S: AsTraceStrRef> Drop for DurationScope<'a, C, S> {
701 fn drop(&mut self) {
702 if let Some(context) = TraceCategoryContext::acquire(self.category) {
703 let name_ref = self.name.as_trace_str_ref(&context);
704 context.write_duration(name_ref, self.start_time, self.args);
705 }
706 }
707}
708
709pub fn complete_duration<C: CategoryString, S: AsTraceStrRef>(
711 category: C,
712 name: S,
713 start_time: zx::BootTicks,
714 args: &[Arg<'_>],
715) {
716 if let Some(context) = TraceCategoryContext::acquire(category) {
717 let name_ref = name.as_trace_str_ref(&context);
718 context.write_duration(name_ref, start_time, args);
719 }
720}
721
722#[macro_export]
757macro_rules! duration {
758 ($category:expr, $name:expr $(, $key:expr => $val:expr)* $(,)?) => {
759 let mut args;
760 let _scope = {
761 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
762 use $crate::AsTraceStrRef;
769 if let Some(context) =
770 $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
771 args = [$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*];
772 Some($crate::duration($category, $name, &args))
773 } else {
774 None
775 }
776 };
777 }
778}
779
780pub fn duration<'a, C: CategoryString, S: AsTraceStrRef>(
792 category: C,
793 name: S,
794 args: &'a [Arg<'_>],
795) -> DurationScope<'a, C, S> {
796 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
797 DurationScope::begin(category, name, args)
798}
799
800#[macro_export]
814macro_rules! duration_begin {
815 ($category:expr, $name:expr $(, $key:expr => $val:expr)* $(,)?) => {
816 {
817 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
818 use $crate::AsTraceStrRef;
819 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
820 $crate::duration_begin(&context, $name,
821 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
822 }
823 }
824 };
825}
826
827#[cfg(fuchsia_api_level_at_least = "NEXT")]
843#[macro_export]
844macro_rules! vthread_duration_begin {
845 ($category:expr, $name:expr, $vthread_name:expr, $vthread_id:expr $(, $key:expr => $val:expr)* $(,)?) => {
846 {
847 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
848 let vthread = $crate::VThread::new($vthread_name, $vthread_id);
849 use $crate::AsTraceStrRef;
850 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
851 $crate::vthread_duration_begin(
852 &context,
853 $name,
854 &vthread,
855 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]
856 )
857 }
858 }
859 };
860}
861
862#[macro_export]
876macro_rules! duration_end {
877 ($category:expr, $name:expr $(, $key:expr => $val:expr)* $(,)?) => {
878 {
879 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
880 use $crate::AsTraceStrRef;
881 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
882 $crate::duration_end(&context, $name, &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
883 }
884 }
885 };
886}
887
888#[cfg(fuchsia_api_level_at_least = "NEXT")]
904#[macro_export]
905macro_rules! vthread_duration_end {
906 ($category:expr, $name:expr, $vthread_name:expr, $vthread_id:expr $(, $key:expr => $val:expr)* $(,)?) => {
907 {
908 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
909 let vthread = $crate::VThread::new($vthread_name, $vthread_id);
910 use $crate::AsTraceStrRef;
911 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
912 $crate::vthread_duration_end(
913 &context,
914 $name,
915 &vthread,
916 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]
917 )
918 }
919 }
920 };
921}
922
923pub fn duration_begin<S: AsTraceStrRef>(context: &TraceCategoryContext, name: S, args: &[Arg<'_>]) {
934 let ticks = zx::BootTicks::get();
935 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
936
937 let name_ref = name.as_trace_str_ref(&context);
938 context.write_duration_begin(ticks, name_ref, args);
939}
940
941#[cfg(fuchsia_api_level_at_least = "NEXT")]
942pub struct VThread<S: AsTraceStrRef = &'static str> {
943 name: S,
944 id: sys::trace_vthread_id_t,
945 process_koid: zx::sys::zx_koid_t,
946}
947
948#[cfg(fuchsia_api_level_at_least = "NEXT")]
949impl<S: AsTraceStrRef> VThread<S> {
950 pub fn new(name: S, id: sys::trace_vthread_id_t) -> Self {
951 Self { name, id, process_koid: zx::sys::ZX_KOID_INVALID }
952 }
953
954 pub fn new_with_process_koid(
955 name: S,
956 id: sys::trace_vthread_id_t,
957 process_koid: zx::sys::zx_koid_t,
958 ) -> Self {
959 Self { name, id, process_koid }
960 }
961}
962
963#[cfg(fuchsia_api_level_at_least = "NEXT")]
965pub fn vthread_duration_begin<S1: AsTraceStrRef, S2: AsTraceStrRef>(
966 context: &TraceCategoryContext,
967 name: S1,
968 vthread: &VThread<S2>,
969 args: &[Arg<'_>],
970) {
971 let ticks = zx::BootTicks::get();
972 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
973
974 let name_ref = name.as_trace_str_ref(context);
975 context.write_vthread_duration_begin(ticks, name_ref, vthread, args);
976}
977
978pub fn duration_end<S: AsTraceStrRef>(context: &TraceCategoryContext, name: S, args: &[Arg<'_>]) {
988 let ticks = zx::BootTicks::get();
989 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
990
991 let name_ref = name.as_trace_str_ref(&context);
992 context.write_duration_end(ticks, name_ref, args);
993}
994
995#[cfg(fuchsia_api_level_at_least = "NEXT")]
997pub fn vthread_duration_end<S1: AsTraceStrRef, S2: AsTraceStrRef>(
998 context: &TraceCategoryContext,
999 name: S1,
1000 vthread: &VThread<S2>,
1001 args: &[Arg<'_>],
1002) {
1003 let ticks = zx::BootTicks::get();
1004 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1005
1006 let name_ref = name.as_trace_str_ref(context);
1007 context.write_vthread_duration_end(ticks, name_ref, vthread, args);
1008}
1009
1010#[must_use = "emits an end event when dropped, so if dropped immediately creates an essentially \
1013 zero length duration that should just be an instant instead"]
1014pub struct AsyncScope<C: CategoryString = &'static CStr, S: AsTraceStrRef = &'static CStr> {
1015 id: Id,
1018 category: C,
1019 name: S,
1020}
1021
1022impl<C: CategoryString, S: AsTraceStrRef> AsyncScope<C, S> {
1023 pub fn begin(id: Id, category: C, name: S, args: &[Arg<'_>]) -> Self {
1026 async_begin(id, category, &name, args);
1027 Self { id, category, name }
1028 }
1029
1030 pub fn end(self, args: &[Arg<'_>]) {
1033 async_end(self.id, self.category, &self.name, args);
1034 std::mem::forget(self);
1035 }
1036}
1037
1038impl<C: CategoryString, S: AsTraceStrRef> Drop for AsyncScope<C, S> {
1039 fn drop(&mut self) {
1040 async_end(self.id, self.category, &self.name, &[]);
1044 }
1045}
1046
1047pub fn async_enter<C: CategoryString, S: AsTraceStrRef>(
1056 id: Id,
1057 category: C,
1058 name: S,
1059 args: &[Arg<'_>],
1060) -> AsyncScope<C, S> {
1061 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1062 AsyncScope::begin(id, category, name, args)
1063}
1064
1065#[macro_export]
1096macro_rules! async_enter {
1097 ($id:expr, $category:expr, $name:expr $(, $key:expr => $val:expr)*) => {
1098 {
1099 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1100 use $crate::AsTraceStrRef;
1101 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1102 Some($crate::AsyncScope::begin($id, $category, $name, &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]))
1103 } else {
1104 None
1105 }
1106 }
1107 }
1108}
1109
1110#[macro_export]
1134macro_rules! async_instant {
1135 ($id:expr, $category:expr, $name:expr $(, $key:expr => $val:expr)*) => {
1136 {
1137 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1138 use $crate::AsTraceStrRef;
1139 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1140 $crate::async_instant($id, &context, $name, &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]);
1141 }
1142 }
1143 }
1144}
1145
1146pub fn async_begin<C: CategoryString, S: AsTraceStrRef>(
1158 id: Id,
1159 category: C,
1160 name: S,
1161 args: &[Arg<'_>],
1162) {
1163 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1164
1165 if let Some(context) = TraceCategoryContext::acquire(category) {
1166 let name_ref = name.as_trace_str_ref(&context);
1167 context.write_async_begin(id, name_ref, args);
1168 }
1169}
1170
1171pub fn async_end<C: CategoryString, S: AsTraceStrRef>(
1183 id: Id,
1184 category: C,
1185 name: S,
1186 args: &[Arg<'_>],
1187) {
1188 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1189
1190 if let Some(context) = TraceCategoryContext::acquire(category) {
1191 let name_ref = name.as_trace_str_ref(&context);
1192 context.write_async_end(id, name_ref, args);
1193 }
1194}
1195
1196pub fn async_instant<S: AsTraceStrRef>(
1208 id: Id,
1209 context: &TraceCategoryContext,
1210 name: S,
1211 args: &[Arg<'_>],
1212) {
1213 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1214
1215 let name_ref = name.as_trace_str_ref(context);
1216 context.write_async_instant(id, name_ref, args);
1217}
1218
1219#[macro_export]
1220macro_rules! blob {
1221 ($category:expr, $name:expr, $bytes:expr $(, $key:expr => $val:expr)*) => {
1222 {
1223 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1224 use $crate::AsTraceStrRef;
1225 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1226 $crate::blob_fn(&context, $name, $bytes, &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
1227 }
1228 }
1229 }
1230}
1231pub fn blob_fn<S: AsTraceStrRef>(
1232 context: &TraceCategoryContext,
1233 name: S,
1234 bytes: &[u8],
1235 args: &[Arg<'_>],
1236) {
1237 let name_ref = name.as_trace_str_ref(context);
1238 context.write_blob(name_ref, bytes, args);
1239}
1240
1241#[macro_export]
1256macro_rules! flow_begin {
1257 ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)*) => {
1258 {
1259 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1260 use $crate::AsTraceStrRef;
1261 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1262 $crate::flow_begin(&context, $name, $flow_id,
1263 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
1264 }
1265 }
1266 }
1267}
1268
1269#[macro_export]
1284macro_rules! flow_step {
1285 ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)*) => {
1286 {
1287 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1288 use $crate::AsTraceStrRef;
1289 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1290 $crate::flow_step(&context, $name, $flow_id,
1291 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
1292 }
1293 }
1294 }
1295}
1296
1297#[macro_export]
1312macro_rules! flow_end {
1313 ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)*) => {
1314 {
1315 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1316 use $crate::AsTraceStrRef;
1317 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1318 $crate::flow_end(&context, $name, $flow_id,
1319 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*])
1320 }
1321 }
1322 }
1323}
1324
1325pub fn flow_begin<S: AsTraceStrRef>(
1344 context: &TraceCategoryContext,
1345 name: S,
1346 flow_id: Id,
1347 args: &[Arg<'_>],
1348) {
1349 let ticks = zx::BootTicks::get();
1350 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1351
1352 let name_ref = name.as_trace_str_ref(context);
1353 context.write_flow_begin(ticks, name_ref, flow_id, args);
1354}
1355
1356pub fn flow_end<S: AsTraceStrRef>(
1373 context: &TraceCategoryContext,
1374 name: S,
1375 flow_id: Id,
1376 args: &[Arg<'_>],
1377) {
1378 let ticks = zx::BootTicks::get();
1379 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1380
1381 let name_ref = name.as_trace_str_ref(context);
1382 context.write_flow_end(ticks, name_ref, flow_id, args);
1383}
1384
1385pub fn flow_step<S: AsTraceStrRef>(
1402 context: &TraceCategoryContext,
1403 name: S,
1404 flow_id: Id,
1405 args: &[Arg<'_>],
1406) {
1407 let ticks = zx::BootTicks::get();
1408 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1409
1410 let name_ref = name.as_trace_str_ref(context);
1411 context.write_flow_step(ticks, name_ref, flow_id, args);
1412}
1413
1414#[macro_export]
1428macro_rules! instaflow_begin {
1429 (
1430 $category:expr,
1431 $flow_name:expr,
1432 $step_name:expr,
1433 $flow_id:expr
1434 $(, $key:expr => $val:expr)*
1435 ) => {
1436 {
1437 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1438 use $crate::AsTraceStrRef;
1439 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1440 $crate::instaflow_begin(
1441 &context,
1442 $flow_name,
1443 $step_name,
1444 $flow_id,
1445 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*],
1446 )
1447 }
1448 }
1449 }
1450}
1451
1452#[macro_export]
1466macro_rules! instaflow_end {
1467 (
1468 $category:expr,
1469 $flow_name:expr,
1470 $step_name:expr,
1471 $flow_id:expr
1472 $(, $key:expr => $val:expr)*
1473 ) => {
1474 {
1475 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1476 use $crate::AsTraceStrRef;
1477 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1478 $crate::instaflow_end(
1479 &context,
1480 $flow_name,
1481 $step_name,
1482 $flow_id,
1483 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*],
1484 )
1485 }
1486 }
1487 }
1488}
1489
1490#[macro_export]
1504macro_rules! instaflow_step {
1505 (
1506 $category:expr,
1507 $flow_name:expr,
1508 $step_name:expr,
1509 $flow_id:expr
1510 $(, $key:expr => $val:expr)*
1511 ) => {
1512 {
1513 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
1514 use $crate::AsTraceStrRef;
1515 if let Some(context) = $crate::TraceCategoryContext::acquire_cached($category, &CACHE) {
1516 $crate::instaflow_step(
1517 &context,
1518 $flow_name,
1519 $step_name,
1520 $flow_id,
1521 &[$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*],
1522 )
1523 }
1524 }
1525 }
1526}
1527
1528pub fn instaflow_begin<S1: AsTraceStrRef, S2: AsTraceStrRef>(
1540 context: &TraceCategoryContext,
1541 flow_name: S1,
1542 step_name: S2,
1543 flow_id: Id,
1544 args: &[Arg<'_>],
1545) {
1546 let ticks = zx::BootTicks::get();
1547 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1548
1549 let flow_name_ref = flow_name.as_trace_str_ref(context);
1550 let step_name_ref = step_name.as_trace_str_ref(context);
1551
1552 context.write_duration_begin(ticks, step_name_ref, args);
1553 context.write_flow_begin(ticks, flow_name_ref, flow_id, args);
1554 context.write_duration_end(ticks, step_name_ref, args);
1555}
1556
1557pub fn instaflow_end<S1: AsTraceStrRef, S2: AsTraceStrRef>(
1569 context: &TraceCategoryContext,
1570 flow_name: S1,
1571 step_name: S2,
1572 flow_id: Id,
1573 args: &[Arg<'_>],
1574) {
1575 let ticks = zx::BootTicks::get();
1576 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1577
1578 let flow_name_ref = flow_name.as_trace_str_ref(context);
1579 let step_name_ref = step_name.as_trace_str_ref(context);
1580
1581 context.write_duration_begin(ticks, step_name_ref, args);
1582 context.write_flow_end(ticks, flow_name_ref, flow_id, args);
1583 context.write_duration_end(ticks, step_name_ref, args);
1584}
1585
1586pub fn instaflow_step<S1: AsTraceStrRef, S2: AsTraceStrRef>(
1598 context: &TraceCategoryContext,
1599 flow_name: S1,
1600 step_name: S2,
1601 flow_id: Id,
1602 args: &[Arg<'_>],
1603) {
1604 let ticks = zx::BootTicks::get();
1605 assert!(args.len() <= 15, "no more than 15 trace arguments are supported");
1606
1607 let flow_name_ref = flow_name.as_trace_str_ref(context);
1608 let step_name_ref = step_name.as_trace_str_ref(context);
1609
1610 context.write_duration_begin(ticks, step_name_ref, args);
1611 context.write_flow_step(ticks, flow_name_ref, flow_id, args);
1612 context.write_duration_end(ticks, step_name_ref, args);
1613}
1614
1615const fn trace_make_empty_string_ref() -> sys::trace_string_ref_t {
1617 sys::trace_string_ref_t {
1618 encoded_value: sys::TRACE_ENCODED_STRING_REF_EMPTY,
1619 inline_string: ptr::null(),
1620 }
1621}
1622
1623#[inline]
1624fn trim_to_last_char_boundary(string: &str, max_len: usize) -> &[u8] {
1625 let mut len = string.len();
1626 if string.len() > max_len {
1627 len = max_len;
1631 while len > 0 {
1632 if string.is_char_boundary(len - 1) && string.is_char_boundary(len) {
1633 break;
1634 }
1635 len -= 1;
1636 }
1637 }
1638 &string.as_bytes()[0..len]
1639}
1640
1641#[inline]
1644fn trace_make_inline_string_ref(string: &str) -> sys::trace_string_ref_t {
1645 let len = string.len() as u16;
1646 if len == 0 {
1647 return trace_make_empty_string_ref();
1648 }
1649
1650 let string = trim_to_last_char_boundary(string, sys::TRACE_ENCODED_STRING_REF_MAX_LENGTH);
1651
1652 sys::trace_string_ref_t {
1653 encoded_value: sys::TRACE_ENCODED_STRING_REF_INLINE_FLAG | len,
1654 inline_string: string.as_ptr() as *const libc::c_char,
1655 }
1656}
1657
1658pub struct TraceCategoryContext {
1660 context: Context,
1661 category_ref: sys::trace_string_ref_t,
1662}
1663
1664impl TraceCategoryContext {
1665 #[inline]
1666 pub fn acquire_cached<C: CategoryString>(
1667 category: C,
1668 site: &sys::trace_site_t,
1669 ) -> Option<TraceCategoryContext> {
1670 category.acquire_context_cached(site)
1671 }
1672
1673 pub fn acquire<C: CategoryString>(category: C) -> Option<TraceCategoryContext> {
1674 category.acquire_context()
1675 }
1676
1677 #[inline]
1678 pub fn register_string_literal<T: CategoryString>(&self, name: T) -> sys::trace_string_ref_t {
1679 name.register(&self.context)
1680 }
1681
1682 #[inline]
1683 #[cfg(fuchsia_api_level_at_least = "27")]
1684 pub fn register_str(&self, name: &'static str) -> sys::trace_string_ref_t {
1685 unsafe {
1686 let mut name_ref = mem::MaybeUninit::<sys::trace_string_ref_t>::uninit();
1687 sys::trace_context_register_bytestring(
1688 self.context.raw,
1689 name.as_ptr().cast::<libc::c_char>(),
1690 name.len(),
1691 name_ref.as_mut_ptr(),
1692 );
1693 name_ref.assume_init()
1694 }
1695 }
1696 #[inline]
1697 #[cfg(not(fuchsia_api_level_at_least = "27"))]
1698 pub fn register_str(&self, name: &'static str) -> sys::trace_string_ref_t {
1699 trace_make_inline_string_ref(name)
1700 }
1701
1702 #[inline]
1703 fn register_current_thread(&self) -> sys::trace_thread_ref_t {
1704 unsafe {
1705 let mut thread_ref = mem::MaybeUninit::<sys::trace_thread_ref_t>::uninit();
1706 sys::trace_context_register_current_thread(self.context.raw, thread_ref.as_mut_ptr());
1707 thread_ref.assume_init()
1708 }
1709 }
1710
1711 #[cfg(fuchsia_api_level_at_least = "NEXT")]
1712 #[inline]
1713 fn register_vthread<S: AsTraceStrRef>(
1714 &self,
1715 name: &S,
1716 id: sys::trace_vthread_id_t,
1717 process_koid: zx::sys::zx_koid_t,
1718 ) -> sys::trace_thread_ref_t {
1719 let name_ref = name.as_trace_str_ref(self);
1720 unsafe {
1721 let mut thread_ref = mem::MaybeUninit::<sys::trace_thread_ref_t>::uninit();
1722 sys::trace_context_register_vthread_by_ref(
1723 self.context.raw,
1724 process_koid,
1725 &name_ref,
1726 id,
1727 thread_ref.as_mut_ptr(),
1728 );
1729 thread_ref.assume_init()
1730 }
1731 }
1732
1733 #[inline]
1734 pub fn write_instant(&self, name_ref: sys::trace_string_ref_t, scope: Scope, args: &[Arg<'_>]) {
1735 let ticks = zx::BootTicks::get();
1736 let thread_ref = self.register_current_thread();
1737 unsafe {
1738 sys::trace_context_write_instant_event_record(
1739 self.context.raw,
1740 ticks.into_raw(),
1741 &thread_ref,
1742 &self.category_ref,
1743 &name_ref,
1744 scope.into_raw(),
1745 args.as_ptr() as *const sys::trace_arg_t,
1746 args.len(),
1747 );
1748 }
1749 }
1750
1751 pub fn write_instant_with_inline_name(&self, name: &str, scope: Scope, args: &[Arg<'_>]) {
1752 let name_ref = trace_make_inline_string_ref(name);
1753 self.write_instant(name_ref, scope, args)
1754 }
1755
1756 fn write_counter(&self, name_ref: sys::trace_string_ref_t, counter_id: u64, args: &[Arg<'_>]) {
1757 let ticks = zx::BootTicks::get();
1758 let thread_ref = self.register_current_thread();
1759 unsafe {
1760 sys::trace_context_write_counter_event_record(
1761 self.context.raw,
1762 ticks.into_raw(),
1763 &thread_ref,
1764 &self.category_ref,
1765 &name_ref,
1766 counter_id,
1767 args.as_ptr() as *const sys::trace_arg_t,
1768 args.len(),
1769 );
1770 }
1771 }
1772
1773 pub fn write_counter_with_inline_name(&self, name: &str, counter_id: u64, args: &[Arg<'_>]) {
1774 let name_ref = trace_make_inline_string_ref(name);
1775 self.write_counter(name_ref, counter_id, args);
1776 }
1777
1778 fn write_duration(
1779 &self,
1780 name_ref: sys::trace_string_ref_t,
1781 start_time: zx::BootTicks,
1782 args: &[Arg<'_>],
1783 ) {
1784 let ticks = zx::BootTicks::get();
1785 let thread_ref = self.register_current_thread();
1786 unsafe {
1787 sys::trace_context_write_duration_event_record(
1788 self.context.raw,
1789 start_time.into_raw(),
1790 ticks.into_raw(),
1791 &thread_ref,
1792 &self.category_ref,
1793 &name_ref,
1794 args.as_ptr() as *const sys::trace_arg_t,
1795 args.len(),
1796 );
1797 }
1798 }
1799
1800 pub fn write_duration_with_inline_name(
1801 &self,
1802 name: &str,
1803 start_time: zx::BootTicks,
1804 args: &[Arg<'_>],
1805 ) {
1806 let name_ref = trace_make_inline_string_ref(name);
1807 self.write_duration(name_ref, start_time, args);
1808 }
1809
1810 fn write_duration_begin(
1811 &self,
1812 ticks: zx::BootTicks,
1813 name_ref: sys::trace_string_ref_t,
1814 args: &[Arg<'_>],
1815 ) {
1816 let thread_ref = self.register_current_thread();
1817 unsafe {
1818 sys::trace_context_write_duration_begin_event_record(
1819 self.context.raw,
1820 ticks.into_raw(),
1821 &thread_ref,
1822 &self.category_ref,
1823 &name_ref,
1824 args.as_ptr() as *const sys::trace_arg_t,
1825 args.len(),
1826 );
1827 }
1828 }
1829
1830 #[cfg(fuchsia_api_level_at_least = "NEXT")]
1831 fn write_vthread_duration_begin<S: AsTraceStrRef>(
1832 &self,
1833 ticks: zx::BootTicks,
1834 name_ref: sys::trace_string_ref_t,
1835 vthread: &VThread<S>,
1836 args: &[Arg<'_>],
1837 ) {
1838 let thread_ref = self.register_vthread(&vthread.name, vthread.id, vthread.process_koid);
1839 unsafe {
1840 sys::trace_context_write_duration_begin_event_record(
1841 self.context.raw,
1842 ticks.into_raw(),
1843 &thread_ref,
1844 &self.category_ref,
1845 &name_ref,
1846 args.as_ptr() as *const sys::trace_arg_t,
1847 args.len(),
1848 );
1849 }
1850 }
1851
1852 pub fn write_duration_begin_with_inline_name(&self, name: &str, args: &[Arg<'_>]) {
1853 let name_ref = trace_make_inline_string_ref(name);
1854 self.write_duration_begin(zx::BootTicks::get(), name_ref, args);
1855 }
1856
1857 fn write_duration_end(
1858 &self,
1859 ticks: zx::BootTicks,
1860 name_ref: sys::trace_string_ref_t,
1861 args: &[Arg<'_>],
1862 ) {
1863 let thread_ref = self.register_current_thread();
1864 unsafe {
1865 sys::trace_context_write_duration_end_event_record(
1866 self.context.raw,
1867 ticks.into_raw(),
1868 &thread_ref,
1869 &self.category_ref,
1870 &name_ref,
1871 args.as_ptr() as *const sys::trace_arg_t,
1872 args.len(),
1873 );
1874 }
1875 }
1876
1877 #[cfg(fuchsia_api_level_at_least = "NEXT")]
1878 fn write_vthread_duration_end<S: AsTraceStrRef>(
1879 &self,
1880 ticks: zx::BootTicks,
1881 name_ref: sys::trace_string_ref_t,
1882 vthread: &VThread<S>,
1883 args: &[Arg<'_>],
1884 ) {
1885 let thread_ref = self.register_vthread(&vthread.name, vthread.id, vthread.process_koid);
1886 unsafe {
1887 sys::trace_context_write_duration_end_event_record(
1888 self.context.raw,
1889 ticks.into_raw(),
1890 &thread_ref,
1891 &self.category_ref,
1892 &name_ref,
1893 args.as_ptr() as *const sys::trace_arg_t,
1894 args.len(),
1895 );
1896 }
1897 }
1898
1899 pub fn write_duration_end_with_inline_name(&self, name: &str, args: &[Arg<'_>]) {
1900 let name_ref = trace_make_inline_string_ref(name);
1901 self.write_duration_end(zx::BootTicks::get(), name_ref, args);
1902 }
1903
1904 fn write_async_begin(&self, id: Id, name_ref: sys::trace_string_ref_t, args: &[Arg<'_>]) {
1905 let ticks = zx::BootTicks::get();
1906 let thread_ref = self.register_current_thread();
1907 unsafe {
1908 sys::trace_context_write_async_begin_event_record(
1909 self.context.raw,
1910 ticks.into_raw(),
1911 &thread_ref,
1912 &self.category_ref,
1913 &name_ref,
1914 id.into(),
1915 args.as_ptr() as *const sys::trace_arg_t,
1916 args.len(),
1917 );
1918 }
1919 }
1920
1921 pub fn write_async_begin_with_inline_name(&self, id: Id, name: &str, args: &[Arg<'_>]) {
1922 let name_ref = trace_make_inline_string_ref(name);
1923 self.write_async_begin(id, name_ref, args);
1924 }
1925
1926 fn write_async_end(&self, id: Id, name_ref: sys::trace_string_ref_t, args: &[Arg<'_>]) {
1927 let ticks = zx::BootTicks::get();
1928 let thread_ref = self.register_current_thread();
1929 unsafe {
1930 sys::trace_context_write_async_end_event_record(
1931 self.context.raw,
1932 ticks.into_raw(),
1933 &thread_ref,
1934 &self.category_ref,
1935 &name_ref,
1936 id.into(),
1937 args.as_ptr() as *const sys::trace_arg_t,
1938 args.len(),
1939 );
1940 }
1941 }
1942
1943 pub fn write_async_end_with_inline_name(&self, id: Id, name: &str, args: &[Arg<'_>]) {
1944 let name_ref = trace_make_inline_string_ref(name);
1945 self.write_async_end(id, name_ref, args);
1946 }
1947
1948 fn write_async_instant(&self, id: Id, name_ref: sys::trace_string_ref_t, args: &[Arg<'_>]) {
1949 let ticks = zx::BootTicks::get();
1950 let thread_ref = self.register_current_thread();
1951 unsafe {
1952 sys::trace_context_write_async_instant_event_record(
1953 self.context.raw,
1954 ticks.into_raw(),
1955 &thread_ref,
1956 &self.category_ref,
1957 &name_ref,
1958 id.into(),
1959 args.as_ptr() as *const sys::trace_arg_t,
1960 args.len(),
1961 );
1962 }
1963 }
1964
1965 fn write_blob(&self, name_ref: sys::trace_string_ref_t, bytes: &[u8], args: &[Arg<'_>]) {
1966 let ticks = zx::BootTicks::get();
1967 let thread_ref = self.register_current_thread();
1968 unsafe {
1969 sys::trace_context_write_blob_event_record(
1970 self.context.raw,
1971 ticks.into_raw(),
1972 &thread_ref,
1973 &self.category_ref,
1974 &name_ref,
1975 bytes.as_ptr() as *const core::ffi::c_void,
1976 bytes.len(),
1977 args.as_ptr() as *const sys::trace_arg_t,
1978 args.len(),
1979 );
1980 }
1981 }
1982
1983 fn write_flow_begin(
1984 &self,
1985 ticks: zx::BootTicks,
1986 name_ref: sys::trace_string_ref_t,
1987 flow_id: Id,
1988 args: &[Arg<'_>],
1989 ) {
1990 let thread_ref = self.register_current_thread();
1991 unsafe {
1992 sys::trace_context_write_flow_begin_event_record(
1993 self.context.raw,
1994 ticks.into_raw(),
1995 &thread_ref,
1996 &self.category_ref,
1997 &name_ref,
1998 flow_id.into(),
1999 args.as_ptr() as *const sys::trace_arg_t,
2000 args.len(),
2001 );
2002 }
2003 }
2004
2005 fn write_flow_end(
2006 &self,
2007 ticks: zx::BootTicks,
2008 name_ref: sys::trace_string_ref_t,
2009 flow_id: Id,
2010 args: &[Arg<'_>],
2011 ) {
2012 let thread_ref = self.register_current_thread();
2013 unsafe {
2014 sys::trace_context_write_flow_end_event_record(
2015 self.context.raw,
2016 ticks.into_raw(),
2017 &thread_ref,
2018 &self.category_ref,
2019 &name_ref,
2020 flow_id.into(),
2021 args.as_ptr() as *const sys::trace_arg_t,
2022 args.len(),
2023 );
2024 }
2025 }
2026
2027 fn write_flow_step(
2028 &self,
2029 ticks: zx::BootTicks,
2030 name_ref: sys::trace_string_ref_t,
2031 flow_id: Id,
2032 args: &[Arg<'_>],
2033 ) {
2034 let thread_ref = self.register_current_thread();
2035 unsafe {
2036 sys::trace_context_write_flow_step_event_record(
2037 self.context.raw,
2038 ticks.into_raw(),
2039 &thread_ref,
2040 &self.category_ref,
2041 &name_ref,
2042 flow_id.into(),
2043 args.as_ptr() as *const sys::trace_arg_t,
2044 args.len(),
2045 );
2046 }
2047 }
2048}
2049
2050pub struct Context {
2052 raw: *const sys::trace_context_t,
2053}
2054
2055impl Context {
2056 #[inline]
2057 pub fn acquire() -> Option<Self> {
2058 let context = unsafe { sys::trace_acquire_context() };
2059 if context.is_null() { None } else { Some(Self { raw: context }) }
2060 }
2061
2062 #[inline]
2063 pub fn register_string_literal<T: CategoryString>(&self, s: T) -> sys::trace_string_ref_t {
2064 s.register(self)
2065 }
2066
2067 pub fn write_blob_record(
2068 &self,
2069 type_: sys::trace_blob_type_t,
2070 name_ref: &sys::trace_string_ref_t,
2071 data: &[u8],
2072 ) {
2073 unsafe {
2074 sys::trace_context_write_blob_record(
2075 self.raw,
2076 type_,
2077 name_ref as *const sys::trace_string_ref_t,
2078 data.as_ptr() as *const libc::c_void,
2079 data.len(),
2080 );
2081 }
2082 }
2083
2084 pub fn copy_record(&self, buffer: &[u64]) -> Option<usize> {
2088 unsafe {
2089 let ptr = sys::trace_context_alloc_record(self.raw, 8 * buffer.len() as libc::size_t);
2090 if ptr == std::ptr::null_mut() {
2091 return None;
2092 }
2093 ptr.cast::<u64>().copy_from(buffer.as_ptr(), buffer.len());
2094 };
2095 Some(buffer.len())
2096 }
2097
2098 pub fn buffering_mode(&self) -> BufferingMode {
2099 match unsafe { sys::trace_context_get_buffering_mode(self.raw) } {
2100 sys::TRACE_BUFFERING_MODE_ONESHOT => BufferingMode::OneShot,
2101 sys::TRACE_BUFFERING_MODE_CIRCULAR => BufferingMode::Circular,
2102 sys::TRACE_BUFFERING_MODE_STREAMING => BufferingMode::Streaming,
2103 m => panic!("Unknown trace buffering mode: {:?}", m),
2104 }
2105 }
2106}
2107
2108impl std::ops::Drop for Context {
2109 fn drop(&mut self) {
2110 unsafe { sys::trace_release_context(self.raw) }
2111 }
2112}
2113
2114pub struct ProlongedContext {
2115 context: *const sys::trace_prolonged_context_t,
2116}
2117
2118impl ProlongedContext {
2119 pub fn acquire() -> Option<Self> {
2120 let context = unsafe { sys::trace_acquire_prolonged_context() };
2121 if context.is_null() { None } else { Some(Self { context }) }
2122 }
2123}
2124
2125impl Drop for ProlongedContext {
2126 fn drop(&mut self) {
2127 unsafe { sys::trace_release_prolonged_context(self.context) }
2128 }
2129}
2130
2131unsafe impl Send for ProlongedContext {}
2132
2133mod sys {
2134 #![allow(non_camel_case_types, unused)]
2135 use zx::sys::{zx_handle_t, zx_koid_t, zx_obj_type_t, zx_status_t, zx_ticks_t};
2136
2137 pub type trace_ticks_t = zx_ticks_t;
2138 pub type trace_counter_id_t = u64;
2139 pub type trace_async_id_t = u64;
2140 pub type trace_flow_id_t = u64;
2141 pub type trace_vthread_id_t = u64;
2142 pub type trace_thread_state_t = u32;
2143 pub type trace_cpu_number_t = u32;
2144 pub type trace_string_index_t = u32;
2145 pub type trace_thread_index_t = u32;
2146 pub type trace_context_t = libc::c_void;
2147 pub type trace_prolonged_context_t = libc::c_void;
2148
2149 pub type trace_encoded_string_ref_t = u16;
2150 pub const TRACE_ENCODED_STRING_REF_EMPTY: trace_encoded_string_ref_t = 0;
2151 pub const TRACE_ENCODED_STRING_REF_INLINE_FLAG: trace_encoded_string_ref_t = 0x8000;
2152 pub const TRACE_ENCODED_STRING_REF_LENGTH_MASK: trace_encoded_string_ref_t = 0x7fff;
2153 pub const TRACE_ENCODED_STRING_REF_MAX_LENGTH: usize = 32000;
2154 pub const TRACE_ENCODED_STRING_REF_MIN_INDEX: trace_encoded_string_ref_t = 0x1;
2155 pub const TRACE_ENCODED_STRING_REF_MAX_INDEX: trace_encoded_string_ref_t = 0x7fff;
2156
2157 pub type trace_encoded_thread_ref_t = u32;
2158 pub const TRACE_ENCODED_THREAD_REF_INLINE: trace_encoded_thread_ref_t = 0;
2159 pub const TRACE_ENCODED_THREAD_MIN_INDEX: trace_encoded_thread_ref_t = 0x01;
2160 pub const TRACE_ENCODED_THREAD_MAX_INDEX: trace_encoded_thread_ref_t = 0xff;
2161
2162 pub type trace_state_t = libc::c_int;
2163 pub const TRACE_STOPPED: trace_state_t = 0;
2164 pub const TRACE_STARTED: trace_state_t = 1;
2165 pub const TRACE_STOPPING: trace_state_t = 2;
2166
2167 pub type trace_scope_t = libc::c_int;
2168 pub const TRACE_SCOPE_THREAD: trace_scope_t = 0;
2169 pub const TRACE_SCOPE_PROCESS: trace_scope_t = 1;
2170 pub const TRACE_SCOPE_GLOBAL: trace_scope_t = 2;
2171
2172 pub type trace_blob_type_t = libc::c_int;
2173 pub const TRACE_BLOB_TYPE_DATA: trace_blob_type_t = 1;
2174 pub const TRACE_BLOB_TYPE_LAST_BRANCH: trace_blob_type_t = 2;
2175 pub const TRACE_BLOB_TYPE_PERFETTO: trace_blob_type_t = 3;
2176
2177 pub type trace_buffering_mode_t = libc::c_int;
2178 pub const TRACE_BUFFERING_MODE_ONESHOT: trace_buffering_mode_t = 0;
2179 pub const TRACE_BUFFERING_MODE_CIRCULAR: trace_buffering_mode_t = 1;
2180 pub const TRACE_BUFFERING_MODE_STREAMING: trace_buffering_mode_t = 2;
2181
2182 #[repr(C)]
2183 #[derive(Copy, Clone)]
2184 pub struct trace_string_ref_t {
2185 pub encoded_value: trace_encoded_string_ref_t,
2186 pub inline_string: *const libc::c_char,
2187 }
2188
2189 pub type trace_site_t = std::sync::atomic::AtomicU64;
2193
2194 unsafe impl Send for trace_string_ref_t {}
2204 unsafe impl Sync for trace_string_ref_t {}
2205
2206 #[repr(C)]
2207 pub struct trace_thread_ref_t {
2208 pub encoded_value: trace_encoded_thread_ref_t,
2209 pub inline_process_koid: zx_koid_t,
2210 pub inline_thread_koid: zx_koid_t,
2211 }
2212
2213 #[repr(C)]
2214 pub struct trace_arg_t {
2215 pub name_ref: trace_string_ref_t,
2216 pub value: trace_arg_value_t,
2217 }
2218
2219 #[repr(C)]
2220 pub union trace_arg_union_t {
2221 pub int32_value: i32,
2222 pub uint32_value: u32,
2223 pub int64_value: i64,
2224 pub uint64_value: u64,
2225 pub double_value: libc::c_double,
2226 pub string_value_ref: trace_string_ref_t,
2227 pub pointer_value: libc::uintptr_t,
2228 pub koid_value: zx_koid_t,
2229 pub bool_value: bool,
2230 pub reserved_for_future_expansion: [libc::uintptr_t; 2],
2231 }
2232
2233 pub type trace_arg_type_t = libc::c_int;
2234 pub const TRACE_ARG_NULL: trace_arg_type_t = 0;
2235 pub const TRACE_ARG_INT32: trace_arg_type_t = 1;
2236 pub const TRACE_ARG_UINT32: trace_arg_type_t = 2;
2237 pub const TRACE_ARG_INT64: trace_arg_type_t = 3;
2238 pub const TRACE_ARG_UINT64: trace_arg_type_t = 4;
2239 pub const TRACE_ARG_DOUBLE: trace_arg_type_t = 5;
2240 pub const TRACE_ARG_STRING: trace_arg_type_t = 6;
2241 pub const TRACE_ARG_POINTER: trace_arg_type_t = 7;
2242 pub const TRACE_ARG_KOID: trace_arg_type_t = 8;
2243 pub const TRACE_ARG_BOOL: trace_arg_type_t = 9;
2244
2245 #[repr(C)]
2246 pub struct trace_arg_value_t {
2247 pub type_: trace_arg_type_t,
2248 pub value: trace_arg_union_t,
2249 }
2250
2251 #[repr(C)]
2252 pub struct trace_handler_ops_t {
2253 pub is_category_enabled:
2254 unsafe fn(handler: *const trace_handler_t, category: *const libc::c_char) -> bool,
2255 pub trace_started: unsafe fn(handler: *const trace_handler_t),
2256 pub trace_stopped: unsafe fn(
2257 handler: *const trace_handler_t,
2258 async_ptr: *const (), disposition: zx_status_t,
2260 buffer_bytes_written: libc::size_t,
2261 ),
2262 pub buffer_overflow: unsafe fn(handler: *const trace_handler_t),
2263 }
2264
2265 #[repr(C)]
2266 pub struct trace_handler_t {
2267 pub ops: *const trace_handler_ops_t,
2268 }
2269
2270 unsafe extern "C" {
2272 pub fn trace_context_is_category_enabled(
2275 context: *const trace_context_t,
2276 category_literal: *const libc::c_char,
2277 ) -> bool;
2278
2279 pub fn trace_context_register_string_literal(
2280 context: *const trace_context_t,
2281 string_literal: *const libc::c_char,
2282 out_ref: *mut trace_string_ref_t,
2283 );
2284
2285 #[cfg(fuchsia_api_level_at_least = "27")]
2286 pub fn trace_context_register_bytestring(
2287 context: *const trace_context_t,
2288 string_literal: *const libc::c_char,
2289 length: libc::size_t,
2290 out_ref: *mut trace_string_ref_t,
2291 );
2292
2293 pub fn trace_context_register_category_literal(
2294 context: *const trace_context_t,
2295 category_literal: *const libc::c_char,
2296 out_ref: *mut trace_string_ref_t,
2297 ) -> bool;
2298
2299 pub fn trace_context_register_current_thread(
2300 context: *const trace_context_t,
2301 out_ref: *mut trace_thread_ref_t,
2302 );
2303
2304 pub fn trace_context_register_thread(
2305 context: *const trace_context_t,
2306 process_koid: zx_koid_t,
2307 thread_koid: zx_koid_t,
2308 out_ref: *mut trace_thread_ref_t,
2309 );
2310
2311 #[cfg(fuchsia_api_level_at_least = "NEXT")]
2312 pub fn trace_context_register_vthread_by_ref(
2313 context: *const trace_context_t,
2314 process_koid: zx_koid_t,
2315 vthread_name: *const trace_string_ref_t,
2316 vthread_id: trace_vthread_id_t,
2317 out_ref: *mut trace_thread_ref_t,
2318 );
2319
2320 pub fn trace_context_write_kernel_object_record(
2321 context: *const trace_context_t,
2322 koid: zx_koid_t,
2323 type_: zx_obj_type_t,
2324 args: *const trace_arg_t,
2325 num_args: libc::size_t,
2326 );
2327
2328 pub fn trace_context_write_kernel_object_record_for_handle(
2329 context: *const trace_context_t,
2330 handle: zx_handle_t,
2331 args: *const trace_arg_t,
2332 num_args: libc::size_t,
2333 );
2334
2335 pub fn trace_context_write_process_info_record(
2336 context: *const trace_context_t,
2337 process_koid: zx_koid_t,
2338 process_name_ref: *const trace_string_ref_t,
2339 );
2340
2341 pub fn trace_context_write_thread_info_record(
2342 context: *const trace_context_t,
2343 process_koid: zx_koid_t,
2344 thread_koid: zx_koid_t,
2345 thread_name_ref: *const trace_string_ref_t,
2346 );
2347
2348 pub fn trace_context_write_context_switch_record(
2349 context: *const trace_context_t,
2350 event_time: trace_ticks_t,
2351 cpu_number: trace_cpu_number_t,
2352 outgoing_thread_state: trace_thread_state_t,
2353 outgoing_thread_ref: *const trace_thread_ref_t,
2354 incoming_thread_ref: *const trace_thread_ref_t,
2355 );
2356
2357 pub fn trace_context_write_log_record(
2358 context: *const trace_context_t,
2359 event_time: trace_ticks_t,
2360 thread_ref: *const trace_thread_ref_t,
2361 log_message: *const libc::c_char,
2362 log_message_length: libc::size_t,
2363 );
2364
2365 pub fn trace_context_write_instant_event_record(
2366 context: *const trace_context_t,
2367 event_time: trace_ticks_t,
2368 thread_ref: *const trace_thread_ref_t,
2369 category_ref: *const trace_string_ref_t,
2370 name_ref: *const trace_string_ref_t,
2371 scope: trace_scope_t,
2372 args: *const trace_arg_t,
2373 num_args: libc::size_t,
2374 );
2375
2376 pub fn trace_context_send_alert(context: *const trace_context_t, name: *const libc::c_char);
2377
2378 #[cfg(fuchsia_api_level_at_least = "27")]
2379 pub fn trace_context_send_alert_bytestring(
2380 context: *const trace_context_t,
2381 name: *const u8,
2382 length: usize,
2383 );
2384
2385 pub fn trace_context_write_counter_event_record(
2386 context: *const trace_context_t,
2387 event_time: trace_ticks_t,
2388 thread_ref: *const trace_thread_ref_t,
2389 category_ref: *const trace_string_ref_t,
2390 name_ref: *const trace_string_ref_t,
2391 counter_id: trace_counter_id_t,
2392 args: *const trace_arg_t,
2393 num_args: libc::size_t,
2394 );
2395
2396 pub fn trace_context_write_duration_event_record(
2397 context: *const trace_context_t,
2398 start_time: trace_ticks_t,
2399 end_time: trace_ticks_t,
2400 thread_ref: *const trace_thread_ref_t,
2401 category_ref: *const trace_string_ref_t,
2402 name_ref: *const trace_string_ref_t,
2403 args: *const trace_arg_t,
2404 num_args: libc::size_t,
2405 );
2406
2407 pub fn trace_context_write_blob_event_record(
2408 context: *const trace_context_t,
2409 event_time: trace_ticks_t,
2410 thread_ref: *const trace_thread_ref_t,
2411 category_ref: *const trace_string_ref_t,
2412 name_ref: *const trace_string_ref_t,
2413 blob: *const libc::c_void,
2414 blob_size: libc::size_t,
2415 args: *const trace_arg_t,
2416 num_args: libc::size_t,
2417 );
2418
2419 pub fn trace_context_write_duration_begin_event_record(
2420 context: *const trace_context_t,
2421 event_time: trace_ticks_t,
2422 thread_ref: *const trace_thread_ref_t,
2423 category_ref: *const trace_string_ref_t,
2424 name_ref: *const trace_string_ref_t,
2425 args: *const trace_arg_t,
2426 num_args: libc::size_t,
2427 );
2428
2429 pub fn trace_context_write_duration_end_event_record(
2430 context: *const trace_context_t,
2431 event_time: trace_ticks_t,
2432 thread_ref: *const trace_thread_ref_t,
2433 category_ref: *const trace_string_ref_t,
2434 name_ref: *const trace_string_ref_t,
2435 args: *const trace_arg_t,
2436 num_args: libc::size_t,
2437 );
2438
2439 pub fn trace_context_write_async_begin_event_record(
2440 context: *const trace_context_t,
2441 event_time: trace_ticks_t,
2442 thread_ref: *const trace_thread_ref_t,
2443 category_ref: *const trace_string_ref_t,
2444 name_ref: *const trace_string_ref_t,
2445 async_id: trace_async_id_t,
2446 args: *const trace_arg_t,
2447 num_args: libc::size_t,
2448 );
2449
2450 pub fn trace_context_write_async_instant_event_record(
2451 context: *const trace_context_t,
2452 event_time: trace_ticks_t,
2453 thread_ref: *const trace_thread_ref_t,
2454 category_ref: *const trace_string_ref_t,
2455 name_ref: *const trace_string_ref_t,
2456 async_id: trace_async_id_t,
2457 args: *const trace_arg_t,
2458 num_args: libc::size_t,
2459 );
2460
2461 pub fn trace_context_write_async_end_event_record(
2462 context: *const trace_context_t,
2463 event_time: trace_ticks_t,
2464 thread_ref: *const trace_thread_ref_t,
2465 category_ref: *const trace_string_ref_t,
2466 name_ref: *const trace_string_ref_t,
2467 async_id: trace_async_id_t,
2468 args: *const trace_arg_t,
2469 num_args: libc::size_t,
2470 );
2471
2472 pub fn trace_context_write_flow_begin_event_record(
2473 context: *const trace_context_t,
2474 event_time: trace_ticks_t,
2475 thread_ref: *const trace_thread_ref_t,
2476 category_ref: *const trace_string_ref_t,
2477 name_ref: *const trace_string_ref_t,
2478 flow_id: trace_flow_id_t,
2479 args: *const trace_arg_t,
2480 num_args: libc::size_t,
2481 );
2482
2483 pub fn trace_context_write_flow_step_event_record(
2484 context: *const trace_context_t,
2485 event_time: trace_ticks_t,
2486 thread_ref: *const trace_thread_ref_t,
2487 category_ref: *const trace_string_ref_t,
2488 name_ref: *const trace_string_ref_t,
2489 flow_id: trace_flow_id_t,
2490 args: *const trace_arg_t,
2491 num_args: libc::size_t,
2492 );
2493
2494 pub fn trace_context_write_flow_end_event_record(
2495 context: *const trace_context_t,
2496 event_time: trace_ticks_t,
2497 thread_ref: *const trace_thread_ref_t,
2498 category_ref: *const trace_string_ref_t,
2499 name_ref: *const trace_string_ref_t,
2500 flow_id: trace_flow_id_t,
2501 args: *const trace_arg_t,
2502 num_args: libc::size_t,
2503 );
2504
2505 pub fn trace_context_write_initialization_record(
2506 context: *const trace_context_t,
2507 ticks_per_second: u64,
2508 );
2509
2510 pub fn trace_context_write_string_record(
2511 context: *const trace_context_t,
2512 index: trace_string_index_t,
2513 string: *const libc::c_char,
2514 length: libc::size_t,
2515 );
2516
2517 pub fn trace_context_write_thread_record(
2518 context: *const trace_context_t,
2519 index: trace_thread_index_t,
2520 procss_koid: zx_koid_t,
2521 thread_koid: zx_koid_t,
2522 );
2523
2524 pub fn trace_context_write_blob_record(
2525 context: *const trace_context_t,
2526 type_: trace_blob_type_t,
2527 name_ref: *const trace_string_ref_t,
2528 data: *const libc::c_void,
2529 size: libc::size_t,
2530 );
2531
2532 pub fn trace_context_alloc_record(
2533 context: *const trace_context_t,
2534 num_bytes: libc::size_t,
2535 ) -> *mut libc::c_void;
2536
2537 pub fn trace_generate_nonce() -> u64;
2540
2541 pub fn trace_state() -> trace_state_t;
2542
2543 #[cfg(fuchsia_api_level_at_least = "27")]
2544 pub fn trace_is_category_bytestring_enabled(
2545 category_literal: *const u8,
2546 length: usize,
2547 ) -> bool;
2548
2549 pub fn trace_is_category_enabled(category_literal: *const libc::c_char) -> bool;
2550
2551 pub fn trace_acquire_context() -> *const trace_context_t;
2552
2553 pub fn trace_acquire_context_for_category(
2554 category_literal: *const libc::c_char,
2555 out_ref: *mut trace_string_ref_t,
2556 ) -> *const trace_context_t;
2557
2558 pub fn trace_acquire_context_for_category_cached(
2559 category_literal: *const libc::c_char,
2560 trace_site: *const u64,
2561 out_ref: *mut trace_string_ref_t,
2562 ) -> *const trace_context_t;
2563
2564 #[cfg(fuchsia_api_level_at_least = "27")]
2565 pub fn trace_acquire_context_for_category_bytestring(
2566 bytes: *const u8,
2567 length: usize,
2568 out_ref: *mut trace_string_ref_t,
2569 ) -> *const trace_context_t;
2570
2571 #[cfg(fuchsia_api_level_at_least = "27")]
2572 pub fn trace_acquire_context_for_category_bytestring_cached(
2573 bytes: *const u8,
2574 length: usize,
2575 trace_site: *const u64,
2576 out_ref: *mut trace_string_ref_t,
2577 ) -> *const trace_context_t;
2578
2579 pub fn trace_release_context(context: *const trace_context_t);
2580
2581 pub fn trace_acquire_prolonged_context() -> *const trace_prolonged_context_t;
2582
2583 pub fn trace_release_prolonged_context(context: *const trace_prolonged_context_t);
2584
2585 pub fn trace_register_observer(event: zx_handle_t) -> zx_status_t;
2586
2587 pub fn trace_unregister_observer(event: zx_handle_t) -> zx_status_t;
2588
2589 pub fn trace_notify_observer_updated(event: zx_handle_t);
2590
2591 pub fn trace_context_get_buffering_mode(
2592 context: *const trace_context_t,
2593 ) -> trace_buffering_mode_t;
2594 }
2595}
2596
2597pub struct TraceFutureArgs<'a, C: CategoryString, S: AsTraceStrRef> {
2600 pub category: C,
2601 pub name: S,
2602
2603 pub args: Box<[Arg<'a>]>,
2606
2607 pub flow_id: Option<Id>,
2610
2611 pub _use_trace_future_args: (),
2613}
2614
2615#[doc(hidden)]
2616#[macro_export]
2617macro_rules! __impl_trace_future_args {
2618 ($category:expr, $name:expr, $flow_id:expr) => {
2621 $crate::TraceFutureArgs {
2622 category: $category,
2623 name: $name,
2624 args: ::std::boxed::Box::new([]),
2625 flow_id: $flow_id,
2626 _use_trace_future_args: (),
2627 }
2628 };
2629 ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)*) => {{
2630 static CACHE: $crate::trace_site_t = $crate::trace_site_t::new(0);
2631 use $crate::AsTraceStrRef;
2632 let context = $crate::TraceCategoryContext::acquire_cached($category, &CACHE);
2633 let args: ::std::boxed::Box<[$crate::Arg<'_>]> = if let Some(context) = context {
2634 ::std::boxed::Box::new(
2635 [$($crate::ArgValue::of_registered($key.as_trace_str_ref(&context), $val)),*]
2636 )
2637 } else {
2638 ::std::boxed::Box::new([])
2639 };
2640 $crate::TraceFutureArgs {
2641 category: $category,
2642 name: $name,
2643 args: args,
2644 flow_id: $flow_id,
2645 _use_trace_future_args: (),
2646 }
2647 }};
2648}
2649
2650#[macro_export]
2662macro_rules! trace_future_args {
2663 ($category:expr, $name:expr $(, $key:expr => $val:expr)* $(,)?) => {
2664 $crate::__impl_trace_future_args!($category, $name, None $(,$key => $val)*)
2665 };
2666 ($category:expr, $name:expr, $flow_id:expr $(, $key:expr => $val:expr)* $(,)?) => {
2667 $crate::__impl_trace_future_args!($category, $name, Some($flow_id) $(,$key => $val)*)
2668 };
2669}
2670
2671pub trait TraceFutureExt: Future + Sized {
2673 #[inline(always)]
2687 fn trace<'a, C: CategoryString, S: AsTraceStrRef>(
2688 self,
2689 args: TraceFutureArgs<'a, C, S>,
2690 ) -> TraceFuture<'a, Self, C, S> {
2691 TraceFuture::new(args, self)
2692 }
2693}
2694
2695impl<T: Future + Sized> TraceFutureExt for T {}
2696
2697#[pin_project]
2700pub struct TraceFuture<'a, Fut: Future, C: CategoryString, S: AsTraceStrRef> {
2701 #[pin]
2703 future: Fut,
2704 category: C,
2705 name: S,
2706 args: Box<[Arg<'a>]>,
2708 flow_id: Option<Id>,
2709 poll_count: u64,
2710}
2711
2712impl<'a, Fut: Future, C: CategoryString, S: AsTraceStrRef> TraceFuture<'a, Fut, C, S> {
2713 #[inline(always)]
2714 pub fn new(args: TraceFutureArgs<'a, C, S>, future: Fut) -> Self {
2715 Self {
2716 future,
2717 category: args.category,
2718 name: args.name,
2719 args: args.args,
2720 flow_id: args.flow_id,
2721 poll_count: 0,
2722 }
2723 }
2724
2725 #[cold]
2726 fn trace_poll(
2727 self: Pin<&mut Self>,
2728 context: &TraceCategoryContext,
2729 cx: &mut std::task::Context<'_>,
2730 ) -> Poll<Fut::Output> {
2731 let start_time = zx::BootTicks::get();
2732 let this = self.project();
2733 *this.poll_count = this.poll_count.saturating_add(1);
2734 let name_ref = this.name.as_trace_str_ref(context);
2735 context.write_duration_begin(start_time, name_ref, &this.args);
2736
2737 let result = this.future.poll(cx);
2738
2739 let flow_id = this.flow_id.get_or_insert_with(Id::new);
2740 let result_str: sys::trace_string_ref_t = if result.is_pending() {
2741 if *this.poll_count == 1 {
2742 context.write_flow_begin(start_time, name_ref, *flow_id, &[]);
2743 } else {
2744 context.write_flow_step(start_time, name_ref, *flow_id, &[]);
2745 }
2746 context.register_str("pending")
2747 } else {
2748 if *this.poll_count != 1 {
2749 context.write_flow_end(start_time, name_ref, *flow_id, &[]);
2750 }
2751 context.register_str("ready")
2752 };
2753 context.write_duration_end(
2754 zx::BootTicks::get(),
2755 name_ref,
2756 &[
2757 ArgValue::of_registered(context.register_str("poll-state"), result_str),
2758 ArgValue::of_registered(context.register_str("poll-count"), *this.poll_count),
2759 ],
2760 );
2761 result
2762 }
2763}
2764
2765impl<Fut: Future, C: CategoryString, S: AsTraceStrRef> Future for TraceFuture<'_, Fut, C, S> {
2766 type Output = Fut::Output;
2767 fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Fut::Output> {
2768 if let Some(context) = TraceCategoryContext::acquire(self.as_ref().get_ref().category) {
2769 self.trace_poll(&context, cx)
2770 } else {
2771 self.project().future.poll(cx)
2772 }
2773 }
2774}
2775
2776#[cfg(test)]
2777mod test {
2778 use super::{Id, trim_to_last_char_boundary};
2779
2780 #[test]
2781 fn trim_to_last_char_boundary_trims_to_last_character_boundary() {
2782 assert_eq!(b"x", trim_to_last_char_boundary("x", 5));
2783 assert_eq!(b"x", trim_to_last_char_boundary("x", 1));
2784 assert_eq!(b"", trim_to_last_char_boundary("x", 0));
2785 assert_eq!(b"xxxxx", trim_to_last_char_boundary("xxxxx", 6));
2786 assert_eq!(b"xxxxx", trim_to_last_char_boundary("xxxxx", 5));
2787 assert_eq!(b"xxxx", trim_to_last_char_boundary("xxxxx", 4));
2788
2789 assert_eq!("💩".as_bytes(), trim_to_last_char_boundary("💩", 5));
2790 assert_eq!("💩".as_bytes(), trim_to_last_char_boundary("💩", 4));
2791 assert_eq!(b"", trim_to_last_char_boundary("💩", 3));
2792 }
2793
2794 #[test]
2798 fn test_id_new() {
2799 assert_ne!(Id::new(), Id::new());
2800 }
2801}