Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/cros_tracing/src/trace_marker.rs
5392 views
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
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
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.
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
302
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 {
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
319