- 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
137 lines
6.8 KiB
Rust
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
|
|
}
|
|
}
|