Files
glances-rs/crates/glances-plugins/src/plugins/percpu.rs
T
glances-rs 8385900ac1 M2: terminal UI (ratatui)
- layout: header, top menu (quicklook, cpu, mem, swap, load), left sidebar,
  process list and alert footer
- decorations/colors, bars, per-plugin render implementations
- hotkeys: quit, help, modes, sorting, cursor, nice, kill, filters, toggles
- TestBackend layout test
2026-10-09 23:35:23 +00:00

137 lines
6.8 KiB
Rust

//! Per-CPU plugin.
use crate::cpu_percent::cpu_percent;
use crate::fields;
use glances_core::plugin::{Field, Plugin, PluginModel, UpdateContext};
use glances_core::{Args, Config};
use serde_json::Value;
pub struct PerCpuPlugin {
pub model: PluginModel,
pub max_cpu_display: usize,
}
impl PerCpuPlugin {
pub fn new(config: &Config, args: &Args) -> Self {
let fields = fields! {
"cpu_number" => Field::new("CPU number"),
"total" => Field::new("Sum of CPU percentages (except idle) for current CPU number.").unit("percent"),
"system" => Field::new("Percent time spent in kernel space. System CPU time is the time spent running code in the Operating System kernel.").unit("percent"),
"user" => Field::new("CPU percent time spent in user space. User CPU time is the time spent on the processor running your program's code (or code in libraries).").unit("percent"),
"iowait" => Field::new("*(Linux)*: percent time spent by the CPU waiting for I/O operations to complete.").unit("percent"),
"idle" => Field::new("percent of CPU used by any program. Every program or task that runs on a computer system occupies a certain amount of processing time on the CPU. If the CPU has completed all tasks it is idle.").unit("percent"),
"irq" => Field::new("*(Linux and BSD)*: percent time spent servicing/handling hardware/software interrupts. Time servicing interrupts (hardware + software).").unit("percent"),
"nice" => Field::new("*(Unix)*: percent time occupied by user level processes with a positive nice value. The time the CPU has spent running users' processes that are *niced*.").unit("percent"),
"steal" => Field::new("*(Linux)*: percentage of time a virtual CPU waits for a real CPU while the hypervisor is servicing another virtual processor.").unit("percent"),
"guest" => Field::new("*(Linux)*: percent of time spent running a virtual CPU for guest operating systems under the control of the Linux kernel.").unit("percent"),
"guest_nice" => Field::new("*(Linux)*: percent of time spent running a niced guest (virtual CPU).").unit("percent"),
"softirq" => Field::new("*(Linux)*: percent of time spent handling software interrupts.").unit("percent"),
"dpc" => Field::new("*(Windows)*: percent of time spent handling deferred procedure calls.").unit("percent"),
"interrupt" => Field::new("*(Windows)*: percent of time spent handling software interrupts.").unit("percent"),
};
let mut model = PluginModel::new(
"percpu",
fields,
Vec::new(),
Value::Array(Vec::new()),
config,
args.time.unwrap_or(2.0),
args.disable_history,
args.disable_config_exec,
);
model.set_item_key("cpu_number");
let max_cpu_display = config.get_int_value("percpu", "max_cpu_display", 4).max(1) as usize;
Self { model, max_cpu_display }
}
}
impl Plugin for PerCpuPlugin {
fn name(&self) -> &str {
"percpu"
}
fn model(&self) -> &PluginModel {
&self.model
}
fn model_mut(&mut self) -> &mut PluginModel {
&mut self.model
}
fn get_key(&self) -> Option<&'static str> {
Some("cpu_number")
}
fn collect(&mut self, _ctx: &mut UpdateContext) -> anyhow::Result<Value> {
let mut state = cpu_percent().lock().unwrap();
Ok(Value::Array(state.get_percpu().to_vec()))
}
fn render(&mut self, ctx: &glances_core::RenderContext) -> Vec<glances_core::StatLine> {
let mut ret = Vec::new();
let items: Vec<Value> = self.model.stats.as_array().cloned().unwrap_or_default();
if items.is_empty() || !ctx.runtime.percpu {
return ret;
}
let items = &items;
let quicklook_disabled = !ctx.runtime.is_plugin_enabled("quicklook");
let mut header = vec!["user", "system", "iowait", "idle", "irq", "nice", "steal", "guest"];
if quicklook_disabled {
ret.push(self.model.curse_add_line(format!("{:5}", "CPU"), glances_core::Decoration::Title, false, false, false));
header.insert(0, "total");
}
for stat in &header {
ret.push(self.model.curse_add_line(format!("{stat:>7}"), glances_core::Decoration::Default, false, false, false));
}
// Sort by total and keep the top max_cpu_display cores.
let mut sorted: Vec<Value> = items.clone();
if sorted.len() > self.max_cpu_display {
sorted.sort_by(|a, b| {
b.get("total")
.and_then(Value::as_f64)
.unwrap_or(0.0)
.partial_cmp(&a.get("total").and_then(Value::as_f64).unwrap_or(0.0))
.unwrap_or(std::cmp::Ordering::Equal)
});
}
let displayed: Vec<Value> = sorted.iter().take(self.max_cpu_display).cloned().collect();
for cpu in &displayed {
ret.push(self.model.curse_new_line());
if quicklook_disabled {
let cpu_id = cpu.get("cpu_number").and_then(Value::as_i64).unwrap_or(0);
let msg = if cpu_id < 10 { format!("CPU{cpu_id:1} ") } else { format!("{cpu_id:4} ") };
ret.push(self.model.curse_add_line(msg, glances_core::Decoration::Default, false, false, false));
}
for stat in &header {
let value = cpu.get(*stat).and_then(Value::as_f64);
let decoration = match value {
Some(value) => self.model.get_alert(value, 0.0, 100.0, true, false, Some(stat), None, false),
None => glances_core::Decoration::Default,
};
let msg = match value {
Some(value) => format!("{value:6.1}%"),
None => format!("{:>6}%", "?"),
};
ret.push(self.model.curse_add_line(msg, decoration, false, false, false));
}
}
if items.len() > self.max_cpu_display {
ret.push(self.model.curse_new_line());
if quicklook_disabled {
ret.push(self.model.curse_add_line("CPU* ", glances_core::Decoration::Default, false, false, false));
}
for stat in &header {
let values: Vec<f64> = sorted
.iter()
.skip(self.max_cpu_display)
.filter_map(|cpu| cpu.get(*stat).and_then(Value::as_f64))
.collect();
let mean = if values.is_empty() { 0.0 } else { values.iter().sum::<f64>() / values.len() as f64 };
let decoration = self.model.get_alert(mean, 0.0, 100.0, true, false, Some(stat), None, false);
ret.push(self.model.curse_add_line(format!("{mean:6.1}%"), decoration, false, false, false));
}
}
ret
}
}