xref: /aosp_15_r20/external/crosvm/perfetto/src/lib.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2023 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 mod bindings;
6 mod hashes;
7 
8 pub mod protos {
9     include!(concat!(env!("OUT_DIR"), "/perfetto_protos/generated.rs"));
10 }
11 
12 use std::ffi::c_void;
13 use std::ffi::CString;
14 use std::mem::size_of;
15 use std::path::Path;
16 use std::slice;
17 use std::time::Duration;
18 
19 pub use bindings::*;
20 pub use cros_tracing_types::static_strings::StaticString;
21 use cros_tracing_types::TraceDuration;
22 use protobuf::Message;
23 use protos::perfetto_config::trace_config::BufferConfig;
24 use protos::perfetto_config::trace_config::DataSource;
25 use protos::perfetto_config::trace_config::IncrementalStateConfig;
26 use protos::perfetto_config::DataSourceConfig;
27 use protos::perfetto_config::TraceConfig;
28 use protos::perfetto_config::TrackEventConfig;
29 use zerocopy::AsBytes;
30 use zerocopy::FromBytes;
31 use zerocopy::FromZeroes;
32 
33 /// Randomly generated GUID to help locate the AOT header.
34 const HEADER_MAGIC: &[u8; 16] = b"\x8d\x10\xa3\xee\x79\x1f\x47\x25\xb2\xb8\xb8\x9f\x85\xe7\xd6\x7c";
35 
36 /// The optional header written ahead of the trace data.
37 #[repr(C)]
38 #[derive(Copy, Clone, AsBytes, FromZeroes, FromBytes)]
39 struct TraceHeader {
40     magic: [u8; 16],
41     data_size: u64,
42     data_checksum_sha256: [u8; 32],
43 }
44 
45 #[macro_export]
46 macro_rules! zero {
47     ($x:ident) => {
48         0
49     };
50 }
51 
52 /// Helper macro for perfetto_tags
53 #[macro_export]
54 macro_rules! tag_or_empty_string {
55     () => {
56         "\0".as_ptr() as *const std::ffi::c_char
57     };
58     ($tag:expr) => {
59         concat!($tag, "\0").as_ptr() as *const std::ffi::c_char
60     };
61 }
62 
63 /// Macro for creating an array of const char * for perfetto tags.
64 #[macro_export]
65 macro_rules! perfetto_tags {
66     () => {
67         [
68             tag_or_empty_string!(),
69             tag_or_empty_string!(),
70             tag_or_empty_string!(),
71             tag_or_empty_string!(),
72         ]
73     };
74     ($tag0:expr) => {
75         [
76             tag_or_empty_string!($tag0),
77             tag_or_empty_string!(),
78             tag_or_empty_string!(),
79             tag_or_empty_string!(),
80         ]
81     };
82     ($tag0:expr, $tag1:expr) => {
83         [
84             tag_or_empty_string!($tag0),
85             tag_or_empty_string!($tag1),
86             tag_or_empty_string!(),
87             tag_or_empty_string!(),
88         ]
89     };
90     ($tag0:expr, $tag1:expr, $tag2:expr) => {
91         [
92             tag_or_empty_string!($tag0),
93             tag_or_empty_string!($tag1),
94             tag_or_empty_string!($tag2),
95             tag_or_empty_string!(),
96         ]
97     };
98     ($tag0:expr, $tag1:expr, $tag2:expr, $tag3:expr) => {
99         [
100             tag_or_empty_string!($tag0),
101             tag_or_empty_string!($tag1),
102             tag_or_empty_string!($tag2),
103             tag_or_empty_string!($tag3),
104         ]
105     };
106 }
107 
108 /// Main macro to be called by any crate wanting to use perfetto tracing. It
109 /// should be called once in your crate outside of any function.
110 ///
111 /// # Arguments
112 ///  * `module_path` - is the module path where this
113 ///  * The remaining arguments are an arbitrary list of triples that describe the tracing
114 ///    categories. They are supplied flattened (e.g. ((a, b, c), (d, e, f)) => (a, b, c, d, e, f).
115 ///    Each triple contains:
116 ///         - the category name (this is the same name/ident that will be passed to trace point
117 ///           macros).
118 ///         - a free form text description of the category.
119 ///         - the tag set for this category (generated by calling perfetto_tags).
120 ///
121 /// # Examples
122 /// ```no_run
123 /// setup_perfetto!(
124 ///     tracing,
125 ///     mycrate,
126 ///     "General trace points for my crate",
127 ///     perfetto_tags!(),
128 ///     debug,
129 ///     "Debug trace points",
130 ///     perfetto_tags!("debug"))
131 /// ```
132 #[macro_export]
133 macro_rules! setup_perfetto {
134     ($mod:ident, $($cat:ident, $description:expr, $tags:expr),+) => {
135         #[allow(non_camel_case_types)]
136         #[derive(Copy, Clone)]
137         pub enum PerfettoCategory {
138             $($cat,)+
139             // Hacky way to get the count of the perfetto categories.
140             CATEGORY_COUNT,
141         }
142 
143         /// Const array of perfetto categories that will be passed to perfetto api.
144         pub const CATEGORIES: [&ctrace_category; PerfettoCategory::CATEGORY_COUNT as usize] = [
145             $(
146                 &ctrace_category {
147                     client_index: PerfettoCategory::$cat as u64,
148                     instances_callback: Some(instances_callback),
149                     name: concat!(stringify!($cat), "\0").as_ptr() as *const std::ffi::c_char,
150                     description: concat!($description, "\0").as_ptr() as *const std::ffi::c_char,
151                     tags: $tags
152                 },
153             )+
154         ];
155 
156         /// Base offset into the global list of categories where our categories live.
157         pub static PERFETTO_CATEGORY_BASE: std::sync::atomic::AtomicU64 =
158             std::sync::atomic::AtomicU64::new(0);
159 
160         /// Active trace instance bitmaps for each of our categories. We use a u32 because the
161         /// cperfetto API uses a u32, but really only 8 traces can be active at a time.
162         pub static PERFETTO_CATEGORY_INSTANCES:
163             [std::sync::atomic::AtomicU32; PerfettoCategory::CATEGORY_COUNT as usize] = [
164                 $(
165                     // Note, we pass $cat to the zero! macro here, which always just returns
166                     // 0, because it's impossible to iterate over $cat unless $cat is used.
167                     std::sync::atomic::AtomicU32::new($crate::zero!($cat)),
168                 )+
169         ];
170 
171         /// Register the perfetto categories defined by this macro with the perfetto shared
172         /// library. This should be called once at process startup.
173         pub fn register_categories() {
174             PERFETTO_CATEGORY_BASE.store(
175                 unsafe {
176                     ctrace_register_categories(
177                         CATEGORIES.as_ptr() as *const *const ctrace_category,
178                         CATEGORIES.len() as u64,
179                     )
180                 },
181                 std::sync::atomic::Ordering::SeqCst,
182             );
183         }
184 
185         /// Callback from the perfetto shared library when the set of active trace instances for
186         /// a given category has changed. Index is the client index of one of our registered
187         /// categories.
188         extern "C" fn instances_callback(instances: u32, index: u64) {
189             PERFETTO_CATEGORY_INSTANCES[index as usize].store(
190                 instances, std::sync::atomic::Ordering::SeqCst);
191 
192             for cb in PERFETTO_PER_TRACE_CALLBACKS.lock().iter() {
193                 cb();
194             }
195         }
196 
197 
198         static PERFETTO_PER_TRACE_CALLBACKS: sync::Mutex<Vec<fn()>> = sync::Mutex::new(Vec::new());
199 
200 
201         pub fn add_per_trace_callback(callback: fn()) {
202             PERFETTO_PER_TRACE_CALLBACKS.lock().push(callback);
203         }
204 
205         /// Create and return a scoped named trace event, which will start at construction and end when
206         /// the event goes out of scope and is dropped. Will return None if tracing is disabled for
207         /// this category.
208         ///
209         /// # Examples
210         /// ```no_run
211         /// {
212         ///     let _trace = trace_event!(my_category, "trace_point_name");
213         ///     do_some_work();
214         /// } // _trace dropped here & records the span.
215         /// ```
216         #[macro_export]
217         macro_rules! trace_event {
218             ($category:ident, $name:literal) => {
219                 {
220                     let instances = $mod::PERFETTO_CATEGORY_INSTANCES
221                         [$mod::PerfettoCategory::$category as usize]
222                         .load(std::sync::atomic::Ordering::SeqCst);
223 
224                     if instances != 0 {
225                         let category_index = $mod::PERFETTO_CATEGORY_BASE
226                             .load(std::sync::atomic::Ordering::SeqCst)
227                             + $mod::PerfettoCategory::$category as u64;
228                         Some($crate::TraceEvent::new(
229                             category_index,
230                             instances,
231                             concat!($name, "\0").as_ptr() as *const std::ffi::c_char,
232                         ))
233                     } else {
234                         None
235                     }
236                 }
237             };
238             ($category:ident, $name:expr $(,$t:expr)+) => {
239                 // Perfetto doesn't support extra detail arguments, so drop
240                 // them.
241                 trace_event!($category, $name)
242             };
243         }
244 
245         /// Internal macro used to begin a trace event. Not intended for direct
246         /// use by library consumers.
247         #[macro_export]
248         macro_rules! trace_event_begin {
249             ($category:ident, $name:expr) => {
250                 let instances = $mod::PERFETTO_CATEGORY_INSTANCES
251                     [$mod::PerfettoCategory::$category as usize]
252                     .load(std::sync::atomic::Ordering::SeqCst);
253 
254                 if instances != 0 {
255                     let category_index = $mod::PERFETTO_CATEGORY_BASE
256                         .load(std::sync::atomic::Ordering::SeqCst)
257                         + $mod::PerfettoCategory::$category as u64;
258 
259                     unsafe {
260                         $crate::trace_event_begin(
261                             category_index,
262                             instances,
263                             concat!($name, "\0").as_ptr() as *const std::ffi::c_char,
264                         )
265                     };
266                 }
267             };
268         }
269 
270         /// Ends the currently active trace event. Not intended for direct use
271         /// by library consumers.
272         #[macro_export]
273         macro_rules! trace_event_end {
274             ($category:ident) => {
275                 let instances = $mod::PERFETTO_CATEGORY_INSTANCES
276                     [$mod::PerfettoCategory::$category as usize]
277                         .load(std::sync::atomic::Ordering::SeqCst);
278 
279                 if instances != 0 {
280                     let category_index = $mod::PERFETTO_CATEGORY_BASE
281                         .load(std::sync::atomic::Ordering::SeqCst)
282                         + $mod::PerfettoCategory::$category as u64;
283                     unsafe { $crate::trace_event_end(category_index, instances) }
284                 }
285             };
286         }
287 
288         /// Creates an async flow but does not start it.
289         ///
290         /// Internal wrapper for use by cros_async_trace.
291         #[macro_export]
292         macro_rules! trace_create_async {
293             ($category:expr, $name:expr) => {
294                 {
295                     let instances = $mod::PERFETTO_CATEGORY_INSTANCES
296                         [$category as usize]
297                             .load(std::sync::atomic::Ordering::SeqCst);
298 
299                     if instances != 0 {
300                         let category_index = $mod::PERFETTO_CATEGORY_BASE
301                             .load(std::sync::atomic::Ordering::SeqCst)
302                             + $category as u64;
303 
304                         let trace_point_name: $crate::StaticString = $name;
305                         unsafe {
306                             Some($crate::trace_create_async(
307                                 category_index,
308                                 instances,
309                                 trace_point_name.as_ptr(),
310                             ))
311                         }
312                     } else {
313                         None
314                     }
315                 }
316             }
317         }
318 
319         /// Starts an existing async flow.
320         ///
321         /// Internal wrapper for use by cros_async_trace.
322         #[macro_export]
323         macro_rules! trace_begin_async {
324             ($category:expr, $name:expr, $optional_terminating_flow_id:expr) => {
325                 let instances = $mod::PERFETTO_CATEGORY_INSTANCES
326                     [$category as usize]
327                         .load(std::sync::atomic::Ordering::SeqCst);
328 
329                 if instances != 0 {
330                     let category_index = $mod::PERFETTO_CATEGORY_BASE
331                         .load(std::sync::atomic::Ordering::SeqCst)
332                         + $category as u64;
333 
334                     if let Some(terminating_flow_id) = $optional_terminating_flow_id {
335                         let trace_point_name: $crate::StaticString = $name;
336                         // Safe because we guarantee $name is a StaticString (which enforces static
337                         // a lifetime for the underlying CString).
338                         unsafe {
339                             $crate::trace_begin_async(
340                                 category_index,
341                                 instances,
342                                 trace_point_name.as_ptr(),
343                                 terminating_flow_id,
344                             )
345                         };
346                     }
347                 }
348             }
349         }
350 
351         /// Pauses a running async flow.
352         ///
353         /// Internal wrapper for use by cros_async_trace.
354         #[macro_export]
355         macro_rules! trace_pause_async {
356             ($category:expr) => {
357                 {
358                     let instances = $mod::PERFETTO_CATEGORY_INSTANCES
359                         [$category as usize]
360                             .load(std::sync::atomic::Ordering::SeqCst);
361 
362                     if instances != 0 {
363                         let category_index = $mod::PERFETTO_CATEGORY_BASE
364                             .load(std::sync::atomic::Ordering::SeqCst)
365                             + $category as u64;
366 
367                         unsafe {
368                             // Safe because we are only passing primitives in.
369                             Some($crate::trace_pause_async(
370                                 category_index,
371                                 instances,
372                             ))
373                         }
374                     } else {
375                         None
376                     }
377                 }
378             }
379         }
380 
381         /// Ends a running async flow.
382         ///
383         /// Internal wrapper for use by cros_async_trace.
384         #[macro_export]
385         macro_rules! trace_end_async {
386             ($category:expr) => {
387                 let instances = $mod::PERFETTO_CATEGORY_INSTANCES
388                     [$category as usize]
389                         .load(std::sync::atomic::Ordering::SeqCst);
390 
391                 if instances != 0 {
392                     let category_index = $mod::PERFETTO_CATEGORY_BASE
393                         .load(std::sync::atomic::Ordering::SeqCst)
394                         + $category as u64;
395 
396                     // Safe because we are only passing primitives in.
397                     unsafe {
398                         $crate::trace_end_async(
399                             category_index,
400                             instances,
401                         )
402                     };
403                 }
404             }
405         }
406 
407         /// Emits a counter with the specified name and value. Note that
408         /// Perfetto does NOT average or sample this data, so a high volume of
409         /// calls will very quickly fill the trace buffer.
410         ///
411         /// # Examples
412         /// ```no_run
413         /// trace_counter!(my_category, "counter_name", 500);
414         /// ```
415         #[macro_export]
416         macro_rules! trace_counter {
417             ($category:ident, $name:literal, $value:expr) => {
418                 let instances = $mod::PERFETTO_CATEGORY_INSTANCES
419                     [$mod::PerfettoCategory::$category as usize]
420                         .load(std::sync::atomic::Ordering::SeqCst);
421 
422                 if instances != 0 {
423                     let category_index = $mod::PERFETTO_CATEGORY_BASE
424                         .load(std::sync::atomic::Ordering::SeqCst)
425                         + $mod::PerfettoCategory::$category as u64;
426 
427                     // Safe because the counter name is a 'static string.
428                     unsafe {
429                         $crate::trace_counter(
430                             category_index,
431                             instances,
432                             concat!($name, "\0").as_ptr() as *const std::ffi::c_char,
433                             $value,
434                         )
435                     };
436                 }
437             };
438 
439             ($category:ident, $name:expr, $value:expr) => {
440                 // Required for safety when calling trace_counter.
441                 let trace_point_name: $crate::StaticString = $name;
442 
443                 let instances = $mod::PERFETTO_CATEGORY_INSTANCES
444                     [$mod::PerfettoCategory::$category as usize]
445                         .load(std::sync::atomic::Ordering::SeqCst);
446 
447                 if instances != 0 {
448                     let category_index = $mod::PERFETTO_CATEGORY_BASE
449                         .load(std::sync::atomic::Ordering::SeqCst)
450                         + $mod::PerfettoCategory::$category as u64;
451 
452                     // Safe because we guarantee $name is a StaticString (which enforces static a
453                     // lifetime for the underlying CString).
454                     unsafe {
455                         $crate::trace_counter(
456                             category_index,
457                             instances,
458                             trace_point_name.as_ptr(),
459                             $value,
460                         )
461                     };
462                 }
463             };
464         }
465     };
466 }
467 
468 /// Perfetto supports two backends, a system backend that runs in a dedicated
469 /// process, and an in process backend. These are selected using this enum.
470 pub enum BackendType {
471     InProcess = BackendType_CTRACE_IN_PROCESS_BACKEND as isize,
472     System = BackendType_CTRACE_SYSTEM_BACKEND as isize,
473 }
474 
475 /// Initializes the tracing system. Should not be called directly (use
476 /// `setup_perfetto` instead).
init_tracing(backend: BackendType)477 pub fn init_tracing(backend: BackendType) {
478     let args = ctrace_init_args {
479         api_version: 1,
480         backend: backend as u32,
481         shmem_size_hint_kb: 0,
482         shmem_page_size_hint_kb: 0,
483         shmem_batch_commits_duration_ms: 0,
484     };
485     unsafe { ctrace_init(&args) }
486 }
487 
488 /// Rust wrapper for running traces.
489 pub struct Trace {
490     session: ctrace_trace_session_handle,
491     trace_stopped: bool,
492 }
493 
494 // Safe because the trace session handle can be sent between threads without ill effect.
495 unsafe impl Sync for Trace {}
496 unsafe impl Send for Trace {}
497 
498 impl Trace {
499     /// Starts a trace.
start( duration: TraceDuration, buffer_size_kb: u32, clear_period: Duration, categories: Option<Vec<String>>, ) -> anyhow::Result<Self>500     pub fn start(
501         duration: TraceDuration,
502         buffer_size_kb: u32,
503         clear_period: Duration,
504         categories: Option<Vec<String>>,
505     ) -> anyhow::Result<Self> {
506         let mut config = TraceConfig::new();
507         let mut incremental_state_config = IncrementalStateConfig::new();
508         incremental_state_config.set_clear_period_ms(clear_period.as_millis().try_into()?);
509         config.incremental_state_config = Some(incremental_state_config).into();
510 
511         let mut buffer = BufferConfig::new();
512         buffer.set_size_kb(buffer_size_kb);
513         config.buffers.push(buffer);
514 
515         let mut data_source = DataSource::new();
516         let mut data_source_config = DataSourceConfig::new();
517         data_source_config.name = Some("track_event".to_owned());
518 
519         if let Some(categories) = categories {
520             let mut track_event_config = TrackEventConfig::new();
521             track_event_config.enabled_categories = categories;
522             track_event_config.disabled_categories.push("*".to_string());
523             data_source_config.track_event_config = Some(track_event_config).into();
524         }
525 
526         data_source.config = Some(data_source_config).into();
527 
528         if let TraceDuration::StopIn(trace_duration) = duration {
529             config.set_duration_ms(trace_duration.as_millis().try_into()?);
530         }
531 
532         config.data_sources.push(data_source);
533 
534         Ok(Self {
535             session: start_trace_from_proto(config)?,
536             trace_stopped: false,
537         })
538     }
539 
540     /// Ends a trace and writes the results to the provided file path.
end(mut self, output: &Path)541     pub fn end(mut self, output: &Path) {
542         // Safe because the session is guaranteed to be valid by self.
543         unsafe { end_trace(self.session, output) }
544         self.trace_stopped = true;
545     }
546 
547     /// Ends a trace and returns the trace data. Prepends a magic value & data length to the trace
548     /// data.
end_to_buffer(mut self) -> Vec<u8>549     pub fn end_to_buffer(mut self) -> Vec<u8> {
550         // Safe because the session is guaranteed to be valid by self, and trace_data is disposed
551         // by later calling ctrace_free_trace_buffer.
552         let mut trace_data = unsafe { end_trace_to_buffer(self.session) };
553 
554         // Safe because:
555         // 1. trace_data is valid from 0..size.
556         // 2. trace_data lives as long as this slice.
557         let trace_data_slice =
558             unsafe { slice::from_raw_parts(trace_data.data as *mut u8, trace_data.size as usize) };
559 
560         let header = TraceHeader {
561             magic: *HEADER_MAGIC,
562             data_size: trace_data.size,
563             data_checksum_sha256: hashes::sha256(trace_data_slice),
564         };
565         let mut trace_vec: Vec<u8> =
566             Vec::with_capacity(size_of::<TraceHeader>() + trace_data.size as usize);
567         trace_vec.extend_from_slice(header.as_bytes());
568         trace_vec.extend_from_slice(trace_data_slice);
569 
570         // Safe because trace data is a valid buffer created by ctrace_stop_trace_to_buffer and
571         // there are no other references to it.
572         unsafe { ctrace_free_trace_buffer(&mut trace_data) };
573 
574         self.trace_stopped = true;
575         trace_vec
576     }
577 }
578 
579 impl Drop for Trace {
drop(&mut self)580     fn drop(&mut self) {
581         if !self.trace_stopped {
582             panic!("Trace must be stopped before it is dropped.")
583         }
584     }
585 }
586 
587 /// Start a perfetto trace of duration `duration` and write the output to `output`.
run_trace(duration: Duration, buffer_size: u32, output: &Path)588 pub fn run_trace(duration: Duration, buffer_size: u32, output: &Path) {
589     let output = output.to_owned();
590     std::thread::spawn(move || {
591         let session = start_trace(duration, buffer_size);
592         std::thread::sleep(duration);
593         unsafe { end_trace(session, output.as_path()) };
594     });
595 }
596 
597 /// Starts a Perfetto trace with the provided config.
start_trace_from_proto(config: TraceConfig) -> anyhow::Result<ctrace_trace_session_handle>598 pub fn start_trace_from_proto(config: TraceConfig) -> anyhow::Result<ctrace_trace_session_handle> {
599     let mut config_bytes = config.write_to_bytes()?;
600 
601     // Safe because config_bytes points to valid memory & we pass its size as required.
602     Ok(unsafe {
603         ctrace_trace_start_from_config_proto(
604             config_bytes.as_mut_ptr() as *mut c_void,
605             config_bytes.len() as u64,
606         )
607     })
608 }
609 
610 /// Starts a trace with the given "duration", where duration specifies how much history to hold in
611 /// the ring buffer; in other words, duration is the lookback period when the trace results are
612 /// dumped.
start_trace(duration: Duration, buffer_size_kb: u32) -> ctrace_trace_session_handle613 pub fn start_trace(duration: Duration, buffer_size_kb: u32) -> ctrace_trace_session_handle {
614     unsafe {
615         ctrace_trace_start(&ctrace_trace_config {
616             duration_ms: duration.as_millis() as u32,
617             buffer_size_kb,
618         })
619     }
620 }
621 
622 /// End the given trace session and write the results to `output`.
623 /// Safety: trace_session must be a valid trace session from `start_trace`.
end_trace(trace_session: ctrace_trace_session_handle, output: &Path)624 pub unsafe fn end_trace(trace_session: ctrace_trace_session_handle, output: &Path) {
625     let path_c_str = CString::new(output.as_os_str().to_str().unwrap()).unwrap();
626     ctrace_trace_stop(trace_session, path_c_str.as_ptr());
627 }
628 
629 /// End the given trace session returns the trace data.
630 ///
631 /// Safety: trace_session must be a valid trace session from `start_trace`.
end_trace_to_buffer( trace_session: ctrace_trace_session_handle, ) -> ctrace_trace_buffer632 pub unsafe fn end_trace_to_buffer(
633     trace_session: ctrace_trace_session_handle,
634 ) -> ctrace_trace_buffer {
635     ctrace_trace_stop_to_buffer(trace_session)
636 }
637 
638 /// Add a clock snapshot to the current trace.
639 ///
640 /// This function does not not do any inline checking if a trace is active,
641 /// and thus should only be called in a per-trace callback registered via
642 /// the add_per_trace_callback! macro.
snapshot_clock(mut snapshot: ClockSnapshot)643 pub fn snapshot_clock(mut snapshot: ClockSnapshot) {
644     unsafe { ctrace_add_clock_snapshot(&mut snapshot.snapshot) };
645 }
646 
647 /// Represents a Perfetto trace span.
648 pub struct TraceEvent {
649     category_index: u64,
650     instances: u32,
651 }
652 
653 impl TraceEvent {
654     #[allow(clippy::not_unsafe_ptr_arg_deref)]
new(category_index: u64, instances: u32, name: *const std::ffi::c_char) -> Self655     pub fn new(category_index: u64, instances: u32, name: *const std::ffi::c_char) -> Self {
656         unsafe {
657             trace_event_begin(
658                 category_index,
659                 instances,
660                 #[allow(clippy::not_unsafe_ptr_arg_deref)]
661                 name,
662             )
663         };
664         Self {
665             category_index,
666             instances,
667         }
668     }
669 }
670 
671 impl Drop for TraceEvent {
drop(&mut self)672     fn drop(&mut self) {
673         unsafe { trace_event_end(self.category_index, self.instances) }
674     }
675 }
676 
677 /// Extension of the Perfetto enum (protos::perfetto_config::BuiltinClock) which
678 /// includes the proposed (but not yet official) TSC clock item.
679 pub enum BuiltinClock {
680     Unknown = 0,
681     Realtime = 1,
682     Coarse = 2,
683     Monotonic = 3,
684     MonotonicCoarse = 4,
685     MonotonicRaw = 5,
686     Boottime = 6,
687     Tsc = 9,
688 }
689 
690 /// Wrapper struct around a ctrace_clock_snapshot.
691 pub struct ClockSnapshot {
692     pub snapshot: ctrace_clock_snapshot,
693 }
694 
695 impl ClockSnapshot {
new(first: &Clock, second: &Clock) -> ClockSnapshot696     pub fn new(first: &Clock, second: &Clock) -> ClockSnapshot {
697         ClockSnapshot {
698             snapshot: ctrace_clock_snapshot {
699                 clocks: [first.clock, second.clock],
700             },
701         }
702     }
703 }
704 
705 /// Builder wrapper for a ctrace_clock.
706 pub struct Clock {
707     clock: ctrace_clock,
708 }
709 
710 impl Clock {
new(clock_id: u32, timestamp: u64) -> Clock711     pub fn new(clock_id: u32, timestamp: u64) -> Clock {
712         Clock {
713             clock: ctrace_clock {
714                 clock_id,
715                 timestamp,
716                 is_incremental: false,
717                 unit_multiplier_ns: 0,
718             },
719         }
720     }
721 
set_multiplier(&mut self, multiplier: u64) -> &mut Clock722     pub fn set_multiplier(&mut self, multiplier: u64) -> &mut Clock {
723         self.clock.unit_multiplier_ns = multiplier;
724         self
725     }
726 
set_is_incremental(&mut self, is_incremental: bool) -> &mut Clock727     pub fn set_is_incremental(&mut self, is_incremental: bool) -> &mut Clock {
728         self.clock.is_incremental = is_incremental;
729         self
730     }
731 }
732 
733 // If running tests in debug mode, ie. `cargo test -p perfetto`,
734 // the cperfetto.dll needs to be imported into the `target` directory.
735 #[cfg(test)]
736 mod tests {
737     #![allow(dead_code)]
738 
739     use std::ffi::c_char;
740 
741     use cros_tracing_types::static_strings::StaticString;
742 
743     use super::*;
744 
745     const AOT_BUFFER_SIZE_KB: u32 = 1024;
746     const AOT_BUFFER_CLEAR_PERIOD: Duration = Duration::from_secs(1);
747     setup_perfetto!(tests, future, "Async ftrace points", perfetto_tags!());
748     #[test]
test_async_trace_builds_and_runs()749     fn test_async_trace_builds_and_runs() {
750         tests::register_categories();
751         init_tracing(BackendType::InProcess);
752         let trace = Trace::start(
753             TraceDuration::AlwaysOn,
754             AOT_BUFFER_SIZE_KB,
755             AOT_BUFFER_CLEAR_PERIOD,
756             Some(vec!["future".to_string()]),
757         )
758         .expect("Failed to start trace");
759 
760         let static_name = StaticString::register("future_1");
761         let future_category = PerfettoCategory::future;
762 
763         let flow_id = tests::trace_create_async!(future_category, static_name);
764         assert!(flow_id.is_some());
765 
766         tests::trace_begin_async!(future_category, static_name, flow_id);
767 
768         let flow_id = tests::trace_pause_async!(future_category);
769         assert!(flow_id.is_some());
770 
771         tests::trace_begin_async!(future_category, static_name, flow_id);
772 
773         tests::trace_end_async!(future_category);
774 
775         trace.end_to_buffer();
776     }
777 
778     #[test]
test_tags_macro_all_empty()779     fn test_tags_macro_all_empty() {
780         let all_tags_empty = perfetto_tags!();
781 
782         // SAFETY: strings from perfetto_tags have static lifetime.
783         unsafe {
784             assert_eq!(*(all_tags_empty[0] as *const char), '\0');
785             assert_eq!(*(all_tags_empty[1] as *const char), '\0');
786             assert_eq!(*(all_tags_empty[2] as *const char), '\0');
787             assert_eq!(*(all_tags_empty[3] as *const char), '\0');
788         }
789     }
790 
791     #[test]
test_tags_macro_two_used()792     fn test_tags_macro_two_used() {
793         let two_used_tags = perfetto_tags!("tag0", "tag1");
794 
795         // SAFETY: strings from perfetto_tags have static lifetime.
796         let tag0 = unsafe { CStr::from_ptr(two_used_tags[0] as *mut c_char) };
797         // SAFETY: strings from perfetto_tags have static lifetime.
798         let tag1 = unsafe { CStr::from_ptr(two_used_tags[1] as *mut c_char) };
799         assert_eq!(tag0.to_str().unwrap(), "tag0");
800         assert_eq!(tag1.to_str().unwrap(), "tag1");
801 
802         // SAFETY: strings have static lifetime.
803         unsafe {
804             assert_eq!(*(two_used_tags[2] as *const char), '\0');
805             assert_eq!(*(two_used_tags[3] as *const char), '\0');
806         }
807     }
808 }
809