M1: core engine, config, plugin model and core plugins
- Cargo workspace (core, plugins, tui, cli) with drop-in compatible CLI - Python-compatible config file handling, limits, views, rates, MMM, history - process collection via sysinfo + procfs, filters, thresholds, events, actions - 20 core plugins (cpu, percpu, mem, memswap, load, fs, network, diskio, ...) - stdout, stdout-csv, stdout-json, --issue, --modules-list output modes - differential test harness against the Python implementation
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
//! CPU plugin.
|
||||
|
||||
use crate::collectors::{CpuTimes, read_cpu_times};
|
||||
use crate::cpu_percent::cpu_percent;
|
||||
use crate::fields;
|
||||
use glances_core::plugin::{Field, HistoryItem, Plugin, PluginModel, UpdateContext};
|
||||
use glances_core::{Args, Config};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub struct CpuPlugin {
|
||||
pub model: PluginModel,
|
||||
previous_times: Option<CpuTimes>,
|
||||
pub nb_log_core: usize,
|
||||
}
|
||||
|
||||
impl CpuPlugin {
|
||||
pub fn new(config: &Config, args: &Args) -> Self {
|
||||
let fields = fields! {
|
||||
"total" => Field::new("Sum of all CPU percentages (except idle).").unit("percent").log().mmm(),
|
||||
"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").log(),
|
||||
"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").log(),
|
||||
"iowait" => Field::new("*(Linux)*: percent time spent by the CPU waiting for I/O operations to complete.").unit("percent").log(),
|
||||
"dpc" => Field::new("*(Windows)*: time spent servicing deferred procedure calls (DPCs)").unit("percent").log(),
|
||||
"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").optional(),
|
||||
"irq" => Field::new("*(Linux and BSD)*: percent time spent servicing/handling hardware/software interrupts. Time servicing interrupts (hardware + software).").unit("percent").optional(),
|
||||
"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").optional(),
|
||||
"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").alert().optional(),
|
||||
"guest" => Field::new("*(Linux)*: time spent running a virtual CPU for guest operating systems under the control of the Linux kernel.").unit("percent").optional(),
|
||||
"ctx_switches" => Field::new("number of context switches (voluntary + involuntary) per second. A context switch is a procedure that a computer's CPU (central processing unit) follows to change from one task to another while ensuring that the tasks do not conflict.").unit("number").rate().min_symbol("K").short_name("ctx_sw").optional(),
|
||||
"interrupts" => Field::new("number of interrupts per second.").unit("number").rate().min_symbol("K").short_name("inter").optional(),
|
||||
"soft_interrupts" => Field::new("number of software interrupts per second. Always set to 0 on Windows and SunOS.").unit("number").rate().min_symbol("K").short_name("sw_int").optional(),
|
||||
"syscalls" => Field::new("number of system calls per second. Always 0 on Linux OS.").unit("number").rate().min_symbol("K").short_name("sys_call").optional(),
|
||||
"cpucore" => Field::new("Total number of CPU core.").unit("number"),
|
||||
"time_since_update" => Field::new("Number of seconds since last update.").unit("seconds"),
|
||||
};
|
||||
let items_history = vec![
|
||||
HistoryItem::new("user", "User CPU usage"),
|
||||
HistoryItem::new("system", "System CPU usage"),
|
||||
];
|
||||
let model = PluginModel::new(
|
||||
"cpu",
|
||||
fields,
|
||||
items_history,
|
||||
json!({}),
|
||||
config,
|
||||
args.time.unwrap_or(2.0),
|
||||
args.disable_history,
|
||||
args.disable_config_exec,
|
||||
);
|
||||
let nb_log_core = crate::collectors::logical_core_count();
|
||||
Self { model, previous_times: None, nb_log_core }
|
||||
}
|
||||
|
||||
fn times_percent(&mut self) -> CpuTimes {
|
||||
let current = read_cpu_times().unwrap_or_default();
|
||||
match self.previous_times {
|
||||
Some(previous) => {
|
||||
self.previous_times = Some(current);
|
||||
current.times_percent(&previous)
|
||||
}
|
||||
None => {
|
||||
let second = read_cpu_times().unwrap_or(current);
|
||||
self.previous_times = Some(second);
|
||||
second.times_percent(¤t)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Plugin for CpuPlugin {
|
||||
fn name(&self) -> &str {
|
||||
"cpu"
|
||||
}
|
||||
|
||||
fn model(&self) -> &PluginModel {
|
||||
&self.model
|
||||
}
|
||||
|
||||
fn model_mut(&mut self) -> &mut PluginModel {
|
||||
&mut self.model
|
||||
}
|
||||
|
||||
fn collect(&mut self, _ctx: &mut UpdateContext) -> anyhow::Result<Value> {
|
||||
let percent = self.times_percent();
|
||||
let total = {
|
||||
let mut state = cpu_percent().lock().unwrap();
|
||||
state.get_cpu()
|
||||
};
|
||||
|
||||
let mut stats = serde_json::Map::new();
|
||||
stats.insert("total".to_string(), json!(total));
|
||||
stats.insert("user".to_string(), json!(percent.user));
|
||||
stats.insert("nice".to_string(), json!(percent.nice));
|
||||
stats.insert("system".to_string(), json!(percent.system));
|
||||
stats.insert("idle".to_string(), json!(percent.idle));
|
||||
stats.insert("iowait".to_string(), json!(percent.iowait));
|
||||
stats.insert("irq".to_string(), json!(percent.irq));
|
||||
stats.insert("steal".to_string(), json!(percent.steal));
|
||||
stats.insert("guest".to_string(), json!(percent.guest));
|
||||
|
||||
// Additional CPU stats from /proc/stat.
|
||||
if let Ok(content) = std::fs::read_to_string("/proc/stat") {
|
||||
let mut ctx_switches = 0u64;
|
||||
let mut interrupts = 0u64;
|
||||
let mut soft_interrupts = 0u64;
|
||||
for line in content.lines() {
|
||||
if let Some(rest) = line.strip_prefix("ctxt ") {
|
||||
ctx_switches = rest.split_whitespace().next().and_then(|v| v.parse().ok()).unwrap_or(0);
|
||||
} else if let Some(rest) = line.strip_prefix("intr ") {
|
||||
interrupts = rest.split_whitespace().next().and_then(|v| v.parse().ok()).unwrap_or(0);
|
||||
} else if let Some(rest) = line.strip_prefix("softirq ") {
|
||||
soft_interrupts = rest.split_whitespace().next().and_then(|v| v.parse().ok()).unwrap_or(0);
|
||||
}
|
||||
}
|
||||
stats.insert("ctx_switches".to_string(), json!(ctx_switches));
|
||||
stats.insert("interrupts".to_string(), json!(interrupts));
|
||||
stats.insert("soft_interrupts".to_string(), json!(soft_interrupts));
|
||||
}
|
||||
stats.insert("syscalls".to_string(), json!(0));
|
||||
stats.insert("cpucore".to_string(), json!(self.nb_log_core));
|
||||
|
||||
Ok(Value::Object(stats))
|
||||
}
|
||||
|
||||
fn update_views(&mut self) {
|
||||
let key = self.get_key();
|
||||
self.model.update_views(key);
|
||||
if self.model.stats.get("time_since_update").and_then(Value::as_f64) != Some(0.0) {
|
||||
if let Some(ctx_switches) = self.model.stats.get("ctx_switches").and_then(Value::as_f64) {
|
||||
let maximum = 100.0 * self.nb_log_core as f64;
|
||||
let decoration =
|
||||
self.model.get_alert(ctx_switches, 0.0, maximum, true, false, Some("ctx_switches"), None, false);
|
||||
self.model.set_view_decoration(None, "ctx_switches", decoration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn display_curse(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user