use super::*;
macro_rules! fold_optionals {
($left:expr, $right:expr) => {
fold_optionals!($left, $right, |l, r| l + r)
};
($left:expr, $right:expr, $f:expr) => {
match ($left, $right) {
(Some(l), Some(r)) => Some($f(l, r)),
(Some(l), None) => Some(l.clone()),
(None, Some(r)) => Some(r.clone()),
(None, None) => None,
}
};
}
#[::below_derive::queriable_derives]
pub struct ProcessModel {
#[queriable(subquery)]
pub processes: BTreeMap<i32, SingleProcessModel>,
}
impl ProcessModel {
pub fn new(sample: &procfs::PidMap, last: Option<(&procfs::PidMap, Duration)>) -> ProcessModel {
let mut processes: BTreeMap<i32, SingleProcessModel> = BTreeMap::new();
for (pid, pidinfo) in sample.iter() {
processes.insert(
*pid,
SingleProcessModel::new(
pidinfo,
last.and_then(|(p, d)| p.get(pid).map(|p| (p, d))),
),
);
}
ProcessModel { processes }
}
}
impl Nameable for ProcessModel {
fn name() -> &'static str {
"process"
}
}
#[::below_derive::queriable_derives]
pub struct SingleProcessModel {
pub pid: Option<i32>,
pub ppid: Option<i32>,
pub ns_tgid: Option<Vec<u32>>,
pub comm: Option<String>,
pub state: Option<procfs::PidState>,
pub uptime_secs: Option<u64>,
pub cgroup: Option<String>,
#[queriable(subquery)]
pub io: Option<ProcessIoModel>,
#[queriable(subquery)]
pub mem: Option<ProcessMemoryModel>,
#[queriable(subquery)]
pub cpu: Option<ProcessCpuModel>,
pub cmdline: Option<String>,
pub exe_path: Option<String>,
}
impl SingleProcessModel {
fn new(
sample: &procfs::PidInfo,
last: Option<(&procfs::PidInfo, Duration)>,
) -> SingleProcessModel {
SingleProcessModel {
pid: sample.stat.pid,
ppid: sample.stat.ppid,
ns_tgid: sample
.status
.ns_tgid
.as_ref()
.map(|v| v.iter().skip(1).cloned().collect()),
comm: sample.stat.comm.clone(),
state: sample.stat.state.clone(),
uptime_secs: sample.stat.running_secs,
cgroup: Some(sample.cgroup.clone()),
io: last.map(|(l, d)| ProcessIoModel::new(&l.io, &sample.io, d)),
mem: last.map(|(l, d)| ProcessMemoryModel::new(l, sample, d)),
cpu: last.map(|(l, d)| ProcessCpuModel::new(&l.stat, &sample.stat, d)),
cmdline: if let Some(cmd_vec) = sample.cmdline_vec.as_ref() {
Some(cmd_vec.join(" "))
} else {
Some("?".into())
},
exe_path: sample.exe_path.clone(),
}
}
pub fn fold(left: &SingleProcessModel, right: &SingleProcessModel) -> SingleProcessModel {
SingleProcessModel {
pid: None,
ppid: None,
ns_tgid: None,
comm: None,
state: None,
uptime_secs: None,
cgroup: None,
io: fold_optionals!(&left.io, &right.io, ProcessIoModel::fold),
mem: fold_optionals!(&left.mem, &right.mem, ProcessMemoryModel::fold),
cpu: fold_optionals!(&left.cpu, &right.cpu, ProcessCpuModel::fold),
cmdline: None,
exe_path: None,
}
}
}
impl Nameable for SingleProcessModel {
fn name() -> &'static str {
"process"
}
}
#[::below_derive::queriable_derives]
pub struct ProcessIoModel {
pub rbytes_per_sec: Option<f64>,
pub wbytes_per_sec: Option<f64>,
pub rwbytes_per_sec: Option<f64>,
}
impl ProcessIoModel {
fn new(begin: &procfs::PidIo, end: &procfs::PidIo, delta: Duration) -> ProcessIoModel {
let rbytes_per_sec = count_per_sec!(begin.rbytes, end.rbytes, delta);
let wbytes_per_sec = count_per_sec!(begin.wbytes, end.wbytes, delta);
let rwbytes_per_sec =
Some(rbytes_per_sec.unwrap_or_default() + wbytes_per_sec.unwrap_or_default());
ProcessIoModel {
rbytes_per_sec,
wbytes_per_sec,
rwbytes_per_sec,
}
}
pub fn fold(left: &ProcessIoModel, right: &ProcessIoModel) -> ProcessIoModel {
ProcessIoModel {
rbytes_per_sec: fold_optionals!(left.rbytes_per_sec, right.rbytes_per_sec),
wbytes_per_sec: fold_optionals!(left.wbytes_per_sec, right.wbytes_per_sec),
rwbytes_per_sec: fold_optionals!(left.rwbytes_per_sec, right.rwbytes_per_sec),
}
}
}
#[::below_derive::queriable_derives]
pub struct ProcessCpuModel {
pub usage_pct: Option<f64>,
pub user_pct: Option<f64>,
pub system_pct: Option<f64>,
pub num_threads: Option<u64>,
}
impl ProcessCpuModel {
fn new(begin: &procfs::PidStat, end: &procfs::PidStat, delta: Duration) -> ProcessCpuModel {
let user_pct = usec_pct!(begin.user_usecs, end.user_usecs, delta);
let system_pct = usec_pct!(begin.system_usecs, end.system_usecs, delta);
let usage_pct = collector::opt_add(user_pct, system_pct);
ProcessCpuModel {
usage_pct,
user_pct,
system_pct,
num_threads: end.num_threads,
}
}
pub fn fold(left: &ProcessCpuModel, right: &ProcessCpuModel) -> ProcessCpuModel {
ProcessCpuModel {
usage_pct: fold_optionals!(left.usage_pct, right.usage_pct),
user_pct: fold_optionals!(left.user_pct, right.user_pct),
system_pct: fold_optionals!(left.system_pct, right.system_pct),
num_threads: fold_optionals!(left.num_threads, right.num_threads),
}
}
}
#[::below_derive::queriable_derives]
pub struct ProcessMemoryModel {
pub minorfaults_per_sec: Option<f64>,
pub majorfaults_per_sec: Option<f64>,
pub rss_bytes: Option<u64>,
pub vm_size: Option<u64>,
pub lock: Option<u64>,
pub pin: Option<u64>,
pub anon: Option<u64>,
pub file: Option<u64>,
pub shmem: Option<u64>,
pub pte: Option<u64>,
pub swap: Option<u64>,
pub huge_tlb: Option<u64>,
}
impl ProcessMemoryModel {
fn new(begin: &procfs::PidInfo, end: &procfs::PidInfo, delta: Duration) -> ProcessMemoryModel {
ProcessMemoryModel {
minorfaults_per_sec: count_per_sec!(begin.stat.minflt, end.stat.minflt, delta),
majorfaults_per_sec: count_per_sec!(begin.stat.majflt, end.stat.majflt, delta),
rss_bytes: end.stat.rss_bytes,
vm_size: end.status.vm_size,
lock: end.status.lock,
pin: end.status.pin,
anon: end.status.anon,
file: end.status.file,
shmem: end.status.shmem,
pte: end.status.pte,
swap: end.status.swap,
huge_tlb: end.status.huge_tlb,
}
}
pub fn fold(left: &ProcessMemoryModel, right: &ProcessMemoryModel) -> ProcessMemoryModel {
ProcessMemoryModel {
minorfaults_per_sec: fold_optionals!(
left.minorfaults_per_sec,
right.minorfaults_per_sec
),
majorfaults_per_sec: fold_optionals!(
left.majorfaults_per_sec,
right.majorfaults_per_sec
),
rss_bytes: fold_optionals!(left.rss_bytes, right.rss_bytes),
vm_size: fold_optionals!(left.vm_size, right.vm_size),
lock: fold_optionals!(left.lock, right.lock),
pin: fold_optionals!(left.pin, right.pin),
anon: fold_optionals!(left.anon, right.anon),
file: fold_optionals!(left.file, right.file),
shmem: fold_optionals!(left.shmem, right.shmem),
pte: fold_optionals!(left.pte, right.pte),
swap: fold_optionals!(left.swap, right.swap),
huge_tlb: fold_optionals!(left.huge_tlb, right.huge_tlb),
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn query_model() {
let model_json = r#"
{
"processes": {
"1": {
"pid": 1,
"comm": "systemd"
}
}
}
"#;
let model: ProcessModel = serde_json::from_str(model_json).unwrap();
assert_eq!(
model.query(&ProcessModelFieldId::from_str("processes.1.comm").unwrap()),
Some(Field::Str("systemd".to_owned()))
);
}
}