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