use std::cmp::Reverse;
use std::fmt;
use std::sync::Arc;
use std::time::Duration;
use std::time::Instant;
use sync::Mutex;
#[derive(Clone, Copy)]
pub(crate) enum BusOperation {
Read,
Write,
}
#[derive(Clone, Default, Eq, PartialEq, Debug)]
struct DeviceStatisticsIdentifier {
name: String,
id: u32,
base: u64,
len: u64,
}
impl DeviceStatisticsIdentifier {
fn new(name: String, id: u32, base: u64, len: u64) -> DeviceStatisticsIdentifier {
DeviceStatisticsIdentifier {
name,
id,
base,
len,
}
}
fn json(&self) -> serde_json::Value {
serde_json::json!({
"name": self.name,
"id": self.id,
"base": self.base,
"len": self.len})
}
}
#[derive(Clone, Default, Eq, PartialEq, Debug)]
struct DeviceStatistics {
read_counter: u64,
read_duration: Duration,
write_counter: u64,
write_duration: Duration,
}
impl DeviceStatistics {
fn increment(&mut self, stat: BusOperation, start: Instant) {
let (counter, duration) = match stat {
BusOperation::Read => (&mut self.read_counter, &mut self.read_duration),
BusOperation::Write => (&mut self.write_counter, &mut self.write_duration),
};
*counter = counter.saturating_add(1);
*duration = duration
.checked_add(start.elapsed())
.unwrap_or(Duration::new(0, 0));
}
fn get(&self, stat: BusOperation) -> (u64, Duration) {
match stat {
BusOperation::Read => (self.read_counter, self.read_duration),
BusOperation::Write => (self.write_counter, self.write_duration),
}
}
fn merge(&mut self, other: &DeviceStatistics) {
self.read_counter = self.read_counter.saturating_add(other.read_counter);
self.read_duration = self
.read_duration
.checked_add(other.read_duration)
.unwrap_or(Duration::new(0, 0));
self.write_counter = self.write_counter.saturating_add(other.write_counter);
self.write_duration = self
.write_duration
.checked_add(other.write_duration)
.unwrap_or(Duration::new(0, 0));
}
fn json(&self) -> serde_json::Value {
serde_json::json!({
"reads": self.read_counter,
"read_duration": {
"seconds": self.read_duration.as_secs(),
"subsecond_nanos": self.read_duration.subsec_nanos(),
},
"writes": self.write_counter,
"write_duration": {
"seconds": self.write_duration.as_secs(),
"subsecond_nanos": self.write_duration.subsec_nanos(),
},
})
}
}
#[derive(Clone, Default, Debug)]
pub struct BusStatistics {
enabled: bool,
device_stats: Vec<DeviceStatistics>,
device_identifiers: Arc<Mutex<Vec<DeviceStatisticsIdentifier>>>,
}
impl BusStatistics {
pub fn new() -> BusStatistics {
BusStatistics::default()
}
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
pub(crate) fn start_stat(&self) -> Option<Instant> {
if !self.enabled {
return None;
}
Some(Instant::now())
}
pub(crate) fn end_stat(
&mut self,
stat: BusOperation,
start: Option<Instant>,
device_index: usize,
) {
if !self.enabled {
return;
}
if let Some(start) = start {
if self.device_stats.len() < device_index + 1 {
self.device_stats
.resize(device_index + 1, DeviceStatistics::default());
}
self.device_stats[device_index].increment(stat, start);
}
}
pub(crate) fn next_device_index(&self, name: String, id: u32, base: u64, len: u64) -> usize {
let mut device_identifiers = self.device_identifiers.lock();
let idx = device_identifiers.len();
device_identifiers.push(DeviceStatisticsIdentifier::new(name, id, base, len));
idx
}
pub fn merged(stats: &[Arc<Mutex<BusStatistics>>]) -> BusStatistics {
if stats.is_empty() {
return BusStatistics::new();
}
let device_count = stats[0].lock().device_identifiers.lock().len();
let mut merged = BusStatistics {
enabled: stats[0].lock().enabled,
device_stats: Vec::with_capacity(device_count),
device_identifiers: stats[0].lock().device_identifiers.clone(),
};
for idx in 0..device_count {
let mut device_stat = DeviceStatistics::default();
for other in stats {
let other = other.lock();
if let Some(other_stats) = other.device_stats.get(idx) {
device_stat.merge(other_stats);
}
}
merged.device_stats.push(device_stat);
}
merged
}
pub fn json(&self) -> serde_json::Value {
let mut devices = serde_json::json!([]);
let devices_vec = devices.as_array_mut().unwrap();
for (device_identifier, device_stat) in self
.device_identifiers
.lock()
.iter()
.zip(self.device_stats.iter())
{
devices_vec.push(
serde_json::json!({"info": device_identifier.json(), "stats": device_stat.json()}),
);
}
devices
}
}
impl std::fmt::Display for BusStatistics {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (opname, op) in &[("Read", BusOperation::Read), ("Write", BusOperation::Write)] {
writeln!(
f,
"Device Name Device Id Address Range {opname:<15}s{opname:<15} Duration"
)?;
let mut device_indices: Vec<usize> = (0..self.device_stats.len()).collect();
device_indices.sort_by_key(|i| Reverse(self.device_stats[*i].get(*op).1));
for i in device_indices.iter() {
let device_identifier = &self.device_identifiers.lock()[*i];
let (count, duration) = self.device_stats[*i].get(*op);
#[allow(clippy::format_in_format_args)]
writeln!(
f,
"{:<30}0x{:<13x}{:<25}{:<15}{:<15}",
device_identifier.name,
device_identifier.id,
format!(
"0x{:x}-0x{:x}",
device_identifier.base,
device_identifier.base + device_identifier.len
),
count,
format!("{:?}", duration),
)?;
}
writeln!(f)?;
}
Ok(())
}
}