perf: report top-5 processes by CPU and memory
Collect every named process per sample, then send the top-5 by CPU and the top-5 by memory as a `top_processes` object alongside the existing single top-CPU/top-memory scalars (kept for backward compatibility). opsbase shows these as 5-row lists in the device's Live performance card. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+43
-15
@@ -57,6 +57,9 @@ struct PendingSample {
|
|||||||
top_cpu_pct: f64,
|
top_cpu_pct: f64,
|
||||||
top_mem_name: String,
|
top_mem_name: String,
|
||||||
top_mem_mb: i64,
|
top_mem_mb: i64,
|
||||||
|
/// Top-5 processes by CPU (name, normalised %) and by memory (name, MB).
|
||||||
|
top_cpu_procs: Vec<(String, f64)>,
|
||||||
|
top_mem_procs: Vec<(String, i64)>,
|
||||||
attempts: u32,
|
attempts: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,33 +159,48 @@ fn collect_sample(sys: &mut hbb_common::sysinfo::System) -> Option<PendingSample
|
|||||||
// CPU% reading (both 0-100 of the whole machine).
|
// CPU% reading (both 0-100 of the whole machine).
|
||||||
let cpu_count = sys.cpus().len().max(1) as f64;
|
let cpu_count = sys.cpus().len().max(1) as f64;
|
||||||
|
|
||||||
let mut top_cpu: Option<(&str, f32)> = None;
|
// Collect every (named) process once, then derive the top-5 by CPU and by
|
||||||
let mut top_mem: Option<(&str, u64)> = None;
|
// memory. The single top_cpu_*/top_mem_* scalars are kept for backward
|
||||||
|
// compatibility (older servers ignore top_processes).
|
||||||
|
let mut procs: Vec<(&str, f64, i64)> = Vec::new();
|
||||||
let mut proc_count = 0i64;
|
let mut proc_count = 0i64;
|
||||||
for proc in sys.processes().values() {
|
for proc in sys.processes().values() {
|
||||||
proc_count += 1;
|
proc_count += 1;
|
||||||
let name = proc.name();
|
let name = proc.name();
|
||||||
// Some kernel-side rows show up with empty names on Windows;
|
// Some kernel-side rows show up with empty names on Windows;
|
||||||
// skip them so we don't ever render a top-CPU row with no
|
// skip them so we don't ever render a top row with no label.
|
||||||
// label.
|
|
||||||
if name.is_empty() {
|
if name.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let cu = proc.cpu_usage();
|
let cu = proc.cpu_usage();
|
||||||
if cu.is_finite() && cu > top_cpu.map(|(_, v)| v).unwrap_or(0.0) {
|
let cu = if cu.is_finite() { (cu as f64 / cpu_count).min(100.0) } else { 0.0 };
|
||||||
top_cpu = Some((name, cu));
|
let mb = (proc.memory() / 1024 / 1024) as i64;
|
||||||
}
|
procs.push((name, cu, mb));
|
||||||
let mu = proc.memory();
|
|
||||||
if mu > top_mem.map(|(_, v)| v).unwrap_or(0) {
|
|
||||||
top_mem = Some((name, mu));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let (top_cpu_name, top_cpu_pct) = top_cpu
|
let mut by_cpu = procs.clone();
|
||||||
.map(|(n, v)| (n.to_string(), (v as f64 / cpu_count).min(100.0)))
|
by_cpu.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
|
||||||
|
let top_cpu_procs: Vec<(String, f64)> = by_cpu
|
||||||
|
.iter()
|
||||||
|
.take(5)
|
||||||
|
.map(|(n, c, _)| (n.to_string(), *c))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut by_mem = procs;
|
||||||
|
by_mem.sort_by(|a, b| b.2.cmp(&a.2));
|
||||||
|
let top_mem_procs: Vec<(String, i64)> = by_mem
|
||||||
|
.iter()
|
||||||
|
.take(5)
|
||||||
|
.map(|(n, _, m)| (n.to_string(), *m))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let (top_cpu_name, top_cpu_pct) = top_cpu_procs
|
||||||
|
.first()
|
||||||
|
.cloned()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let (top_mem_name, top_mem_mb) = top_mem
|
let (top_mem_name, top_mem_mb) = top_mem_procs
|
||||||
.map(|(n, v)| (n.to_string(), (v / 1024 / 1024) as i64))
|
.first()
|
||||||
|
.cloned()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
Some(PendingSample {
|
Some(PendingSample {
|
||||||
@@ -196,6 +214,8 @@ fn collect_sample(sys: &mut hbb_common::sysinfo::System) -> Option<PendingSample
|
|||||||
top_cpu_pct,
|
top_cpu_pct,
|
||||||
top_mem_name,
|
top_mem_name,
|
||||||
top_mem_mb,
|
top_mem_mb,
|
||||||
|
top_cpu_procs,
|
||||||
|
top_mem_procs,
|
||||||
attempts: 0,
|
attempts: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -277,6 +297,14 @@ async fn post_batch(batch: &[PendingSample]) -> Result<()> {
|
|||||||
"top_cpu_pct": s.top_cpu_pct,
|
"top_cpu_pct": s.top_cpu_pct,
|
||||||
"top_mem_name": s.top_mem_name,
|
"top_mem_name": s.top_mem_name,
|
||||||
"top_mem_mb": s.top_mem_mb,
|
"top_mem_mb": s.top_mem_mb,
|
||||||
|
"top_processes": {
|
||||||
|
"cpu": s.top_cpu_procs.iter()
|
||||||
|
.map(|(n, p)| hbb_common::serde_json::json!({"name": n, "pct": p}))
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
"mem": s.top_mem_procs.iter()
|
||||||
|
.map(|(n, m)| hbb_common::serde_json::json!({"name": n, "mb": m}))
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|||||||
Reference in New Issue
Block a user