xref: /aosp_15_r20/external/crosvm/cros_tracing/src/trace_marker.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2022 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 #![deny(missing_docs)]
6 
7 use std::fs::File;
8 use std::fs::OpenOptions;
9 use std::io::Write;
10 use std::os::unix::io::AsRawFd;
11 use std::path::Path;
12 
13 use base::error;
14 use base::RawDescriptor;
15 use sync::Mutex;
16 
17 static TRACE_MARKER_FILE: Mutex<Option<File>> = Mutex::new(None);
18 
19 #[macro_export]
20 /// This macro is used as a placeholder to let us iterate over the compile-time
21 /// allocated vector of categories when we statically initialize it.
22 /// This macro should only be used internally to this crate.
23 macro_rules! zero_internal {
24     ($x:ident) => {
25         0
26     };
27 }
28 
29 #[macro_export]
30 /// This macro expands an expression with its value for easier printing.
31 /// `expand_fmt_internal!(my_var)` becomes `"(my_var: {:?})"`.
32 macro_rules! expand_fmt_internal {
33     ($x:expr) => {
34         std::concat!("(", std::stringify!($x), ": {:?})")
35     };
36 }
37 
38 #[macro_export]
39 /// Macro used to handle fd permanency across jailed devices.
40 /// If we run crosvm without `--disable-sandbox`, we need to add the `trace_marker`
41 /// file descriptor to the list of file descriptors allowed to be accessed by the
42 /// sandboxed process. We call this macro to add the file descriptor to the list
43 /// of `keep_rds` that the process is allowed to access every time we jail.
44 macro_rules! push_descriptors {
45     ($fd_vec:expr) => {
46         $crate::push_descriptors_internal($fd_vec);
47     };
48 }
49 
50 #[macro_export]
51 /// Prints a single non-scoped message without creating a trace context.
52 /// The tagged variant lets us enable or disable individual categories.
53 macro_rules! trace_simple_print {
54     ($category: ident, $($t:tt)+) => {{
55         if($crate::ENABLED_CATEGORIES[$crate::TracedCategories::$category as usize].load(std::sync::atomic::Ordering::Relaxed)) {
56             $crate::trace_simple_print!($($t)*);
57         }
58     }};
59     ($($t:tt)*) => {{
60         $crate::trace_simple_print_internal(std::format!($($t)*));
61     }};
62 }
63 
64 /// Platform-specific implementation of the `push_descriptors!` macro. If the
65 /// `trace_marker` file has been initialized properly, it adds its file descriptor
66 /// to the list of file descriptors that are allowed to be accessed when the process
67 /// is jailed in the sandbox.
68 ///
69 /// # Arguments
70 ///
71 /// * `keep_rds` - List of file descriptors that will be accessible after jailing
push_descriptors_internal(keep_rds: &mut Vec<RawDescriptor>)72 pub fn push_descriptors_internal(keep_rds: &mut Vec<RawDescriptor>) {
73     if let Some(file) = TRACE_MARKER_FILE.lock().as_ref() {
74         let fd = file.as_raw_fd();
75         if !keep_rds.contains(&fd) {
76             keep_rds.push(fd);
77         }
78     }
79 }
80 
81 #[macro_export]
82 /// Macro used to set up the trace environment categories. It takes a variable
83 /// number of arguments in pairs of category, boolean value on whether or not the
84 /// tracing category is enabled at compile time.
85 ///
86 /// # Example usage
87 ///
88 /// ```ignore
89 /// setup_trace_marker!(
90 ///     (Category1, true),
91 ///     (Category2, false)
92 /// );
93 /// ```
94 ///
95 /// Categories that are enabled will have their events traced at runtime via
96 /// `trace_event_begin!()`, `trace_event_end!()`, or `trace_event!()` scoped tracing.
97 /// The categories that are marked as false will have their events skipped.
98 macro_rules! setup_trace_marker {
99  ($(($cat:ident, $enabled:literal)),+) => {
100      #[allow(non_camel_case_types, missing_docs)]
101      /// The tracing categories that the trace_marker backend supports.
102      pub enum TracedCategories {
103          $($cat,)+
104          /// Hacky way to get the count of how many tracing categories we have in total.
105          CATEGORY_COUNT,
106      }
107 
108      /// Vector counting how many tracing event contexts are running for each category.
109      pub static CATEGORY_COUNTER: [std::sync::atomic::AtomicI64; TracedCategories::CATEGORY_COUNT as usize] = [
110          $(
111              // Note, we pass $cat to the zero_internal! macro here, which always just returns
112              // 0, because it's impossible to iterate over $cat unless $cat is used.
113              std::sync::atomic::AtomicI64::new(zero_internal!($cat)),
114          )+
115      ];
116 
117      /// Vector used to test if a category is enabled or not for tracing.
118      pub static ENABLED_CATEGORIES: [std::sync::atomic::AtomicBool; TracedCategories::CATEGORY_COUNT as usize] = [
119          $(
120              std::sync::atomic::AtomicBool::new($enabled),
121          )+
122      ];
123 
124      /// Sequential identifier for scoped trace events. This unique identifier is incremented
125      /// for each new `trace_event()` call.
126      pub static EVENT_COUNTER: std::sync::atomic::AtomicU64 =
127          std::sync::atomic::AtomicU64::new(0);
128  }
129 }
130 
131 #[macro_export]
132 /// Returns a Trace object with a new name and index and traces its enter state, if the
133 /// given category identifier is enabled. Extra args can be provided for easier debugging.
134 /// Upon exiting its scope, it is automatically collected and its exit gets traced.
135 ///
136 /// If the category identifier is not enabled for this event, nothing happens and the trace
137 /// event is skipped.
138 ///
139 /// # Example usage
140 ///
141 /// ```ignore
142 /// {
143 ///    let _trace = trace_event!(Category, "exec", param1, param2);
144 ///
145 ///    // ... Rest of code ...
146 ///
147 ///    // End of `_trace`'s lifetime so `trace_event_end` is called.
148 /// }
149 /// ```
150 ///
151 /// This will output in `trace_marker`:
152 ///
153 ///   - `$uid: $category Enter: exec - (param1: ...)(param2: ...)`
154 ///
155 /// and when _trace runs out of scope, it will output:
156 ///
157 ///   - `$uid: Exit: exec`
158 ///
159 /// where `$uid` will be the same unique value across those two events.
160 macro_rules! trace_event {
161     ($category:ident, $name:literal, $($arg:expr),+) => {{
162         if($crate::ENABLED_CATEGORIES[$crate::TracedCategories::$category as usize].load(std::sync::atomic::Ordering::Relaxed)) {
163             $crate::trace_event_begin!($category);
164             let index = $crate::EVENT_COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
165             $crate::trace_simple_print!($category,
166                                         "{} {} Enter: {} - {}",
167                                         index,
168                                         std::stringify!($category),
169                                         $name,
170                                         // Creates a formatted list for each argument and their
171                                         // values.
172                                         std::format_args!(std::concat!($($crate::expand_fmt_internal!($arg),)*), $($arg),*));
173             Some($crate::Trace::new(index, $name, std::stringify!($category), $crate::TracedCategories::$category as usize))
174         } else {
175             None
176         }
177     }};
178     ($category:ident, $name:expr) => {{
179         if($crate::ENABLED_CATEGORIES[$crate::TracedCategories::$category as usize].load(std::sync::atomic::Ordering::Relaxed)) {
180             $crate::trace_event_begin!($category);
181             let index = $crate::EVENT_COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
182             $crate::trace_simple_print!($category,
183                                         "{} {} Enter: {}", index, std::stringify!($category), $name);
184             Some($crate::Trace::new(index, $name, std::stringify!($category), $crate::TracedCategories::$category as usize))
185         } else {
186             None
187         }
188     }};
189 }
190 
191 #[macro_export]
192 /// Begins a tracing event context in the given category, it increases the counter
193 /// of the currently traced events for that category by one.
194 ///
195 /// # Arguments
196 ///
197 /// * `category` - Identifier name of the category.
198 macro_rules! trace_event_begin {
199     ($category:ident) => {
200         $crate::CATEGORY_COUNTER[$crate::TracedCategories::$category as usize]
201             .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
202     };
203 }
204 
205 #[macro_export]
206 /// Ends a tracing event context in the given category, it decreases the counter
207 /// of the currently traced events for that category by one.
208 ///
209 /// # Arguments
210 ///
211 /// * `category` - Identifier name of the category.
212 macro_rules! trace_event_end {
213     ($category:ident) => {
214         if ($crate::ENABLED_CATEGORIES[$crate::TracedCategories::$category as usize]
215             .load(std::sync::atomic::Ordering::Relaxed))
216         {
217             $crate::CATEGORY_COUNTER[$crate::TracedCategories::$category as usize]
218                 .fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
219         }
220     };
221     ($category_id:expr) => {
222         if ($crate::ENABLED_CATEGORIES[$category_id as usize]
223             .load(std::sync::atomic::Ordering::Relaxed))
224         {
225             $crate::CATEGORY_COUNTER[$category_id as usize]
226                 .fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
227         }
228     };
229 }
230 
231 // List of categories that can be enabled.
232 // If a category is marked as disabled here, no events will be processed for it.
233 setup_trace_marker!(
234     (VirtioFs, true),
235     (VirtioNet, true),
236     (USB, true),
237     (gpu_display, true),
238     (VirtioBlk, true),
239     (VirtioScsi, true)
240 );
241 
242 /// Platform-specific implementation of the `trace_simple_print!` macro. If tracing
243 /// is enabled on the system, it writes the given message to the `trace_marker` file.
244 ///
245 /// # Arguments
246 ///
247 /// * `message` - The message to be written
trace_simple_print_internal(message: String)248 pub fn trace_simple_print_internal(message: String) {
249     // In case tracing is not working or the trace marker file is None we can
250     // just ignore this. We don't need to handle the error here.
251     if let Some(file) = TRACE_MARKER_FILE.lock().as_mut() {
252         // We ignore the error here in case write!() fails, because the trace
253         // marker file would be normally closed by the system unless we are
254         // actively tracing the runtime. It is not an error.
255         let _ = write!(file, "{}", message);
256     };
257 }
258 
259 /// Initializes the trace_marker backend. It attepts to open the `trace_marker`
260 /// file and keep a reference that can be shared throughout the lifetime of the
261 /// crosvm process.
262 ///
263 /// If tracefs is not available on the system or the file cannot be opened,
264 /// tracing will not work but the crosvm process will still continue execution
265 /// without tracing.
init()266 pub fn init() {
267     let mut trace_marker_file = TRACE_MARKER_FILE.lock();
268     if trace_marker_file.is_some() {
269         return;
270     }
271 
272     let path = Path::new("/sys/kernel/tracing/trace_marker");
273     let file = match OpenOptions::new().read(false).write(true).open(path) {
274         Ok(f) => f,
275         Err(e) => {
276             error!(
277                 "Failed to open {}: {}. Tracing will not work.",
278                 path.display(),
279                 e
280             );
281             return;
282         }
283     };
284 
285     *trace_marker_file = Some(file);
286 }
287 
288 /// A trace context obtained from a `trace_event!()` call.
289 pub struct Trace {
290     /// Unique identifier for the specific event.
291     identifier: u64,
292     /// Name of the trace event.
293     name: String,
294     /// Category name to which the event belongs.
295     category: String,
296     /// Category ID to which the event belongs.
297     category_id: usize,
298 }
299 
300 impl Trace {
301     /// Returns a Trace object with the given name, id, and category
new(identifier: u64, name: &str, category: &str, category_id: usize) -> Self302     pub fn new(identifier: u64, name: &str, category: &str, category_id: usize) -> Self {
303         Trace {
304             identifier,
305             name: name.to_string(),
306             category: category.to_string(),
307             category_id,
308         }
309     }
310 }
311 
312 impl Drop for Trace {
drop(&mut self)313     fn drop(&mut self) {
314         trace_simple_print!("{} {} Exit: {}", self.identifier, self.category, self.name);
315         trace_event_end!(self.category_id);
316     }
317 }
318