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