use std::io::Read;
use std::fs;
use std::time::Duration;
use std::path::{Path, PathBuf};
use crate::DevicePath;
#[derive(Debug, Default, Clone)]
pub struct ProcInfo {
pub pid: i32,
pub name: String,
pub fds: Vec<i32>,
pub is_kfd_proc: bool,
}
fn get_fds<T: AsRef<Path>>(fd_dir_path: &mut PathBuf, device_path: &[T]) -> (Vec<i32>, bool) {
let Ok(fd_list) = fs::read_dir(&fd_dir_path) else { return (Vec::new(), false) };
let mut fds: Vec<i32> = Vec::with_capacity(16);
let mut is_kfd_proc = false;
for dir_entry in fd_list {
let Ok(dir_entry) = dir_entry else { continue };
let fd = dir_entry.file_name();
let link = {
fd_dir_path.push(fd.clone());
let Ok(link) = fs::read_link(&fd_dir_path) else { continue };
fd_dir_path.pop();
link
};
if device_path.iter().any(|path| link.starts_with(path)) {
let Some(fd) = fd.to_str().and_then(|s| s.parse::<i32>().ok()) else { continue };
fds.push(fd);
}
if link.starts_with("/dev/kfd") {
is_kfd_proc = true;
}
}
(fds, is_kfd_proc)
}
pub fn get_process_list() -> Vec<i32> {
const SYSTEMD_CMDLINE: &[&[u8]] = &[ b"/lib/systemd", b"/usr/lib/systemd" ];
let Ok(proc_dir) = fs::read_dir("/proc") else { return Vec::new() };
let mut proc_list: Vec<i32> = Vec::with_capacity(128);
fn filter_proc(dir_entry: fs::DirEntry) -> Option<i32> {
let mut buf_cmdline = [0u8; 16];
if !dir_entry.metadata().ok()?.is_dir() {
return None;
}
let pid = dir_entry.file_name().to_str()?.parse::<i32>().ok()?;
if pid == 1 { return None }
{
let mut f = fs::File::open(format!("/proc/{pid}/cmdline")).ok()?;
let _ = f.read_exact(&mut buf_cmdline);
if SYSTEMD_CMDLINE.iter().any(|path| buf_cmdline.starts_with(path)) {
return None;
}
}
Some(pid)
}
for dir_entry in proc_dir {
let Ok(dir_entry) = dir_entry else { continue };
if let Some(pid) = filter_proc(dir_entry) {
proc_list.push(pid);
}
}
proc_list
}
pub fn update_index_by_all_proc<T: AsRef<Path>>(
vec_info: &mut Vec<ProcInfo>,
device_path: &[T],
all_proc: &[i32],
) {
vec_info.clear();
let mut buf_path = PathBuf::with_capacity(32);
let mut buf_name = String::with_capacity(16);
for p in all_proc {
buf_path.clear();
buf_name.clear();
let pid = *p;
buf_path.push("/proc");
buf_path.push(pid.to_string());
let (fds, is_kfd_proc) = get_fds(&mut buf_path.join("fd/"), device_path);
if fds.is_empty() { continue }
buf_path.push("comm");
let Ok(mut f) = fs::File::open(&buf_path) else { continue };
if f.read_to_string(&mut buf_name).is_err() { continue }
buf_name.pop(); let name = buf_name.clone();
vec_info.push(ProcInfo { pid, name, fds, is_kfd_proc });
}
}
pub fn spawn_update_index_thread(
device_paths: Vec<DevicePath>,
interval: u64,
) {
let mut buf_index: Vec<ProcInfo> = Vec::new();
let interval = Duration::from_secs(interval);
std::thread::spawn(move || loop {
let all_proc = get_process_list();
for device_path in &device_paths {
let paths: &[&PathBuf] = if device_path.is_amdgpu() {
&[&device_path.render, &device_path.card]
} else {
&[&device_path.accel]
};
update_index_by_all_proc(
&mut buf_index,
paths,
&all_proc,
);
let lock = device_path.arc_proc_index.lock();
if let Ok(mut index) = lock {
index.clone_from(&buf_index);
}
}
std::thread::sleep(interval);
});
}
pub fn diff_usage(pre_usage_ns: i64, cur_usage_ns: i64, interval: &Duration) -> i64 {
let diff_ns = if pre_usage_ns == 0 || cur_usage_ns < pre_usage_ns {
return 0;
} else {
cur_usage_ns.saturating_sub(pre_usage_ns) as u128
};
(diff_ns * 100)
.checked_div(interval.as_nanos())
.unwrap_or(0) as i64
}