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