Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/bus_stats.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
use std::cmp::Reverse;
6
use std::fmt;
7
use std::sync::Arc;
8
use std::time::Duration;
9
use std::time::Instant;
10
11
use sync::Mutex;
12
13
/// Helper enum to distinguish between read stats and write stats.
14
#[derive(Clone, Copy)]
15
pub(crate) enum BusOperation {
16
Read,
17
Write,
18
}
19
20
/// Identifying information about a device on the Bus, used for statistics.
21
#[derive(Clone, Default, Eq, PartialEq, Debug)]
22
struct DeviceStatisticsIdentifier {
23
/// Name of the device
24
name: String,
25
/// Id of the device
26
id: u32,
27
/// Base address where the device was added to the bus.
28
base: u64,
29
/// Length of address range this device entry covers.
30
len: u64,
31
}
32
33
impl DeviceStatisticsIdentifier {
34
fn new(name: String, id: u32, base: u64, len: u64) -> DeviceStatisticsIdentifier {
35
DeviceStatisticsIdentifier {
36
name,
37
id,
38
base,
39
len,
40
}
41
}
42
43
/// Get a json representation of `self`.
44
fn json(&self) -> serde_json::Value {
45
serde_json::json!({
46
"name": self.name,
47
"id": self.id,
48
"base": self.base,
49
"len": self.len})
50
}
51
}
52
53
/// Statistics about how a device has been accessed via a Bus.
54
#[derive(Clone, Default, Eq, PartialEq, Debug)]
55
struct DeviceStatistics {
56
/// Counter of the number of reads performed.
57
read_counter: u64,
58
/// Total duration of reads performed.
59
read_duration: Duration,
60
/// Counter of the number of writes performed.
61
write_counter: u64,
62
/// Total duration of writes performed.
63
write_duration: Duration,
64
}
65
66
impl DeviceStatistics {
67
/// Increment either a read counter or a write counter, depending on `stat`. Also add the
68
/// time elapsed since `start` to read_duration or write_duration respectively.
69
fn increment(&mut self, stat: BusOperation, start: Instant) {
70
let (counter, duration) = match stat {
71
BusOperation::Read => (&mut self.read_counter, &mut self.read_duration),
72
BusOperation::Write => (&mut self.write_counter, &mut self.write_duration),
73
};
74
75
// We use saturating_add because we don't want any disruptions to emulator running due to
76
// statistics
77
*counter = counter.saturating_add(1);
78
*duration = duration
79
.checked_add(start.elapsed())
80
.unwrap_or(Duration::new(0, 0)); // If we overflow, reset to 0
81
}
82
83
/// Get the accumulated count and duration of a particular Operation
84
fn get(&self, stat: BusOperation) -> (u64, Duration) {
85
match stat {
86
BusOperation::Read => (self.read_counter, self.read_duration),
87
BusOperation::Write => (self.write_counter, self.write_duration),
88
}
89
}
90
91
/// Merge another DeviceStat into this one.
92
fn merge(&mut self, other: &DeviceStatistics) {
93
self.read_counter = self.read_counter.saturating_add(other.read_counter);
94
self.read_duration = self
95
.read_duration
96
.checked_add(other.read_duration)
97
.unwrap_or(Duration::new(0, 0)); // If we overflow, reset to 0
98
99
self.write_counter = self.write_counter.saturating_add(other.write_counter);
100
self.write_duration = self
101
.write_duration
102
.checked_add(other.write_duration)
103
.unwrap_or(Duration::new(0, 0)); // If we overflow, reset to 0
104
}
105
106
/// Get a json representation of `self`.
107
fn json(&self) -> serde_json::Value {
108
serde_json::json!({
109
"reads": self.read_counter,
110
"read_duration": {
111
"seconds": self.read_duration.as_secs(),
112
"subsecond_nanos": self.read_duration.subsec_nanos(),
113
},
114
"writes": self.write_counter,
115
"write_duration": {
116
"seconds": self.write_duration.as_secs(),
117
"subsecond_nanos": self.write_duration.subsec_nanos(),
118
},
119
})
120
}
121
}
122
123
/// Statistics about how a bus has been accessed.
124
#[derive(Clone, Default, Debug)]
125
pub struct BusStatistics {
126
/// Whether or not statistics have been enabled to measure Bus Reads/Writes.
127
enabled: bool,
128
/// Vec of per-device statistics, indexed by BusEntry.index.
129
device_stats: Vec<DeviceStatistics>,
130
/// Global information about all devices inserted into any bus.
131
device_identifiers: Arc<Mutex<Vec<DeviceStatisticsIdentifier>>>,
132
}
133
134
impl BusStatistics {
135
pub fn new() -> BusStatistics {
136
BusStatistics::default()
137
}
138
139
/// Enable or disable statistics gathering.
140
pub fn set_enabled(&mut self, enabled: bool) {
141
self.enabled = enabled;
142
}
143
144
/// Get the start time of the stat that is to be recorded.
145
///
146
/// If the BusStatistics instance is not enabled this will return None.
147
pub(crate) fn start_stat(&self) -> Option<Instant> {
148
if !self.enabled {
149
return None;
150
}
151
Some(Instant::now())
152
}
153
154
/// Record the end of the stat.
155
///
156
/// The start value return from start_stat should be passed as `start`. If `start` is None or
157
/// if the BusStatistics instance is not enabled this will do nothing. The counters and
158
/// durations will silently overflow to prevent interference with vm operation.
159
pub(crate) fn end_stat(
160
&mut self,
161
stat: BusOperation,
162
start: Option<Instant>,
163
device_index: usize,
164
) {
165
if !self.enabled {
166
return;
167
}
168
169
if let Some(start) = start {
170
// Make sure the device_stats is large enough
171
if self.device_stats.len() < device_index + 1 {
172
self.device_stats
173
.resize(device_index + 1, DeviceStatistics::default());
174
}
175
176
self.device_stats[device_index].increment(stat, start);
177
}
178
}
179
180
/// Get the next available device index.
181
///
182
/// When adding a BusEntry to the bus, the Bus should call this function to get the index for
183
/// the entry. This BusStatistics will then save the device `name` and `id` associated with the
184
/// device index for displaying statistics later.
185
pub(crate) fn next_device_index(&self, name: String, id: u32, base: u64, len: u64) -> usize {
186
let mut device_identifiers = self.device_identifiers.lock();
187
let idx = device_identifiers.len();
188
device_identifiers.push(DeviceStatisticsIdentifier::new(name, id, base, len));
189
idx
190
}
191
192
/// Merge several BusStatistics into one.
193
pub fn merged(stats: &[Arc<Mutex<BusStatistics>>]) -> BusStatistics {
194
if stats.is_empty() {
195
return BusStatistics::new();
196
}
197
198
let device_count = stats[0].lock().device_identifiers.lock().len();
199
200
let mut merged = BusStatistics {
201
enabled: stats[0].lock().enabled,
202
device_stats: Vec::with_capacity(device_count),
203
device_identifiers: stats[0].lock().device_identifiers.clone(),
204
};
205
206
for idx in 0..device_count {
207
let mut device_stat = DeviceStatistics::default();
208
// Merge all DeviceStatistics
209
for other in stats {
210
let other = other.lock();
211
// Not all vcpu Buses may have stats for all devices.
212
if let Some(other_stats) = other.device_stats.get(idx) {
213
device_stat.merge(other_stats);
214
}
215
}
216
217
merged.device_stats.push(device_stat);
218
}
219
220
merged
221
}
222
223
/// Get a json representation of `self`. Returns an array of maps, where each map contains the
224
/// read an write statistics for a particular device.
225
pub fn json(&self) -> serde_json::Value {
226
let mut devices = serde_json::json!([]);
227
let devices_vec = devices.as_array_mut().unwrap();
228
for (device_identifier, device_stat) in self
229
.device_identifiers
230
.lock()
231
.iter()
232
.zip(self.device_stats.iter())
233
{
234
devices_vec.push(
235
serde_json::json!({"info": device_identifier.json(), "stats": device_stat.json()}),
236
);
237
}
238
devices
239
}
240
}
241
242
impl std::fmt::Display for BusStatistics {
243
/// BusStatistics' Display is split into two tables, Reads and Writes.
244
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
245
for (opname, op) in &[("Read", BusOperation::Read), ("Write", BusOperation::Write)] {
246
writeln!(
247
f,
248
"Device Name Device Id Address Range {opname:<15}s{opname:<15} Duration"
249
)?;
250
251
let mut device_indices: Vec<usize> = (0..self.device_stats.len()).collect();
252
// Sort indices by op duration
253
device_indices.sort_by_key(|i| Reverse(self.device_stats[*i].get(*op).1));
254
255
for i in device_indices.iter() {
256
let device_identifier = &self.device_identifiers.lock()[*i];
257
let (count, duration) = self.device_stats[*i].get(*op);
258
#[allow(clippy::format_in_format_args)]
259
writeln!(
260
f,
261
"{:<30}0x{:<13x}{:<25}{:<15}{:<15}",
262
device_identifier.name,
263
device_identifier.id,
264
format!(
265
"0x{:x}-0x{:x}",
266
device_identifier.base,
267
device_identifier.base + device_identifier.len
268
),
269
count,
270
// Alignment not implemented by Debug
271
format!("{:?}", duration),
272
)?;
273
}
274
writeln!(f)?;
275
}
276
277
Ok(())
278
}
279
}
280
281