use std::collections::{HashMap, HashSet};
use std::fs;
use std::io::Read;
use std::time::Duration;
use std::path::PathBuf;
use super::XdnaFdInfoUsage;
use crate::stat::ProcInfo;
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd)]
pub struct XdnaProcUsage {
pub pid: i32,
pub name: String,
pub ids_count: usize,
pub usage: XdnaFdInfoUsage,
}
#[derive(Clone, Default)]
pub struct XdnaFdInfoStat {
pub pre_proc_usage_map: HashMap<i32, XdnaFdInfoUsage>,
pub drm_client_ids: HashSet<usize>,
pub proc_usage: Vec<XdnaProcUsage>,
pub interval: Duration,
}
impl XdnaFdInfoStat {
pub fn get_proc_usage(&mut self, proc_info: &ProcInfo) {
let pid = proc_info.pid;
let name = &proc_info.name;
let mut stat = XdnaFdInfoUsage::default();
let mut buf = String::new();
let mut ids_count = 0usize;
let mut path = PathBuf::with_capacity(24);
path.push("/proc");
path.push(pid.to_string());
if !path.exists() {
self.pre_proc_usage_map.remove(&pid);
return;
}
path.push("fdinfo");
for fd in &proc_info.fds {
buf.clear();
{
path.push(fd.to_string());
let Ok(mut f) = fs::File::open(&path) else { continue };
if f.read_to_string(&mut buf).is_err() { continue }
path.pop(); }
let mut lines = buf.lines().skip_while(|l| !l.starts_with("drm-client-id"));
if let Some(id) = lines.next().and_then(XdnaFdInfoUsage::id_parse) {
ids_count += 1;
if !self.drm_client_ids.insert(id) { continue }
} else {
continue;
}
for l in lines {
let Some(s) = l.get(0..13) else { continue };
match s {
"drm-total-mem" => stat.total_memory_usage_parse(l),
"drm-shared-me" => stat.shared_memory_usage_parse(l),
"drm-active-me" => stat.active_memory_usage_parse(l),
"drm-engine-np" => stat.engine_usage_parse(l),
_ => {},
}
}
}
let diff = if let Some(pre_stat) = self.pre_proc_usage_map.get_mut(&pid) {
let tmp = stat.calc_usage(pre_stat, &self.interval);
*pre_stat = stat;
tmp
} else {
let usage = XdnaFdInfoUsage {
total_memory: stat.total_memory,
shared_memory: stat.shared_memory,
active_memory: stat.active_memory,
..Default::default()
};
self.pre_proc_usage_map.insert(pid, stat);
usage
};
self.proc_usage.push(XdnaProcUsage {
pid,
name: name.to_string(),
ids_count,
usage: diff,
});
}
pub fn update_proc_usage(&mut self, proc_index: &[ProcInfo]) {
self.proc_usage.clear();
self.drm_client_ids.clear();
if proc_index.len() < self.pre_proc_usage_map.len() {
let mut path = PathBuf::with_capacity(16);
path.push("/proc");
self.pre_proc_usage_map.retain(|&pid, _| {
path.push(pid.to_string());
let b = path.exists();
path.pop();
b
});
}
for pu in proc_index {
self.get_proc_usage(pu);
}
}
pub fn fold_fdinfo_usage(&self) -> XdnaFdInfoUsage {
self.proc_usage.iter().fold(XdnaFdInfoUsage::default(), |acc, pu| acc + pu.usage)
}
}