mirror of
https://github.com/nushell/nushell.git
synced 2024-11-07 09:04:18 +01:00
Fix CPU usage info in sys
(#8321)
Closes #8264. This PR does a few things to fix the `usage` column in `sys.cpu`: 1. Sleep a while (~400ms) between calls to `sys.refresh_cpu()`, [as required by `sysinfo`](https://docs.rs/sysinfo/latest/sysinfo/trait.SystemExt.html#method.refresh_cpu) 2. Change `sys` to return a `LazyRecord` (so you can do things like `sys | get host` instantly without waiting for CPU info) 3. Update our `sysinfo` dependency to [fix CPU usage calculations on Linux](https://github.com/GuillaumeGomez/sysinfo/pull/946) CPU usage is no longer always reported as zero: ![image](https://user-images.githubusercontent.com/26268125/222929775-5e9cbe18-95d9-4ecb-baf8-1e843f5c7086.png)
This commit is contained in:
parent
33fb17776a
commit
f93033c20b
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -2700,7 +2700,7 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"reedline",
|
||||
"rstest",
|
||||
"sysinfo 0.28.0",
|
||||
"sysinfo 0.28.2",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@ -2819,7 +2819,7 @@ dependencies = [
|
||||
"serde_yaml",
|
||||
"sha2",
|
||||
"sqlparser",
|
||||
"sysinfo 0.28.0",
|
||||
"sysinfo 0.28.2",
|
||||
"tabled",
|
||||
"terminal_size 0.2.1",
|
||||
"thiserror",
|
||||
@ -2848,7 +2848,7 @@ dependencies = [
|
||||
"nu-protocol",
|
||||
"nu-utils",
|
||||
"serde",
|
||||
"sysinfo 0.28.0",
|
||||
"sysinfo 0.28.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5134,9 +5134,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.28.0"
|
||||
version = "0.28.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "727220a596b4ca0af040a07091e49f5c105ec8f2592674339a5bf35be592f76e"
|
||||
checksum = "d3e847e2de7a137c8c2cede5095872dbb00f4f9bf34d061347e36b43322acd56"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"core-foundation-sys",
|
||||
|
@ -36,7 +36,7 @@ once_cell = "1.17.0"
|
||||
log = "0.4"
|
||||
miette = { version = "5.5.0", features = ["fancy-no-backtrace"] }
|
||||
percent-encoding = "2"
|
||||
sysinfo = "0.28.0"
|
||||
sysinfo = "0.28.2"
|
||||
thiserror = "1.0.31"
|
||||
|
||||
[features]
|
||||
|
@ -85,7 +85,7 @@ percent-encoding = "2.2.0"
|
||||
reedline = { version = "0.16.0", features = ["bashisms", "sqlite"] }
|
||||
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
|
||||
sqlparser = { version = "0.30.0", features = ["serde"], optional = true }
|
||||
sysinfo = "0.28.0"
|
||||
sysinfo = "0.28.2"
|
||||
tabled = "0.10.0"
|
||||
terminal_size = "0.2.1"
|
||||
thiserror = "1.0.31"
|
||||
|
@ -3,8 +3,10 @@ use chrono::Local;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||
Category, Example, IntoPipelineData, LazyRecord, PipelineData, ShellError, Signature, Span,
|
||||
Type, Value,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::{Duration, UNIX_EPOCH};
|
||||
use sysinfo::{
|
||||
ComponentExt, CpuExt, CpuRefreshKind, DiskExt, NetworkExt, System, SystemExt, UserExt,
|
||||
@ -36,7 +38,13 @@ impl Command for Sys {
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
run_sys(call)
|
||||
let span = call.span();
|
||||
let ret = Value::LazyRecord {
|
||||
val: Box::new(SysResult { span }),
|
||||
span,
|
||||
};
|
||||
|
||||
Ok(ret.into_pipeline_data())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -60,51 +68,53 @@ impl Command for Sys {
|
||||
}
|
||||
}
|
||||
|
||||
fn run_sys(call: &Call) -> Result<PipelineData, ShellError> {
|
||||
let span = call.head;
|
||||
let mut sys = System::new();
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct SysResult {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
let mut headers = vec![];
|
||||
let mut values = vec![];
|
||||
|
||||
if let Some(value) = host(&mut sys, span) {
|
||||
headers.push("host".into());
|
||||
values.push(value);
|
||||
}
|
||||
if let Some(value) = cpu(&mut sys, span) {
|
||||
headers.push("cpu".into());
|
||||
values.push(value);
|
||||
}
|
||||
if let Some(value) = disks(&mut sys, span) {
|
||||
headers.push("disks".into());
|
||||
values.push(value);
|
||||
}
|
||||
if let Some(value) = mem(&mut sys, span) {
|
||||
headers.push("mem".into());
|
||||
values.push(value);
|
||||
}
|
||||
if let Some(value) = temp(&mut sys, span) {
|
||||
headers.push("temp".into());
|
||||
values.push(value);
|
||||
}
|
||||
if let Some(value) = net(&mut sys, span) {
|
||||
headers.push("net".into());
|
||||
values.push(value);
|
||||
impl LazyRecord for SysResult {
|
||||
fn column_names(&self) -> Vec<&'static str> {
|
||||
vec!["host", "cpu", "disks", "mem", "temp", "net"]
|
||||
}
|
||||
|
||||
Ok(Value::Record {
|
||||
cols: headers,
|
||||
vals: values,
|
||||
span,
|
||||
fn get_column_value(&self, column: &str) -> Result<Value, ShellError> {
|
||||
let span = self.span;
|
||||
|
||||
match column {
|
||||
"host" => Ok(host(span)),
|
||||
"cpu" => Ok(cpu(span)),
|
||||
"disks" => Ok(disks(span)),
|
||||
"mem" => Ok(mem(span)),
|
||||
"temp" => Ok(temp(span)),
|
||||
"net" => Ok(net(span)),
|
||||
_ => Err(ShellError::LazyRecordAccessFailed {
|
||||
message: format!("Could not find column '{column}'"),
|
||||
column_name: column.to_string(),
|
||||
span,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
|
||||
fn typetag_name(&self) -> &'static str {
|
||||
"sys"
|
||||
}
|
||||
|
||||
fn typetag_deserialize(&self) {
|
||||
unimplemented!("typetag_deserialize")
|
||||
}
|
||||
.into_pipeline_data())
|
||||
}
|
||||
|
||||
pub fn trim_cstyle_null(s: String) -> String {
|
||||
s.trim_matches(char::from(0)).to_string()
|
||||
}
|
||||
|
||||
pub fn disks(sys: &mut System, span: Span) -> Option<Value> {
|
||||
pub fn disks(span: Span) -> Value {
|
||||
let mut sys = System::new();
|
||||
sys.refresh_disks();
|
||||
sys.refresh_disks_list();
|
||||
|
||||
@ -157,14 +167,11 @@ pub fn disks(sys: &mut System, span: Span) -> Option<Value> {
|
||||
|
||||
output.push(Value::Record { cols, vals, span });
|
||||
}
|
||||
if !output.is_empty() {
|
||||
Some(Value::List { vals: output, span })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
Value::List { vals: output, span }
|
||||
}
|
||||
|
||||
pub fn net(sys: &mut System, span: Span) -> Option<Value> {
|
||||
pub fn net(span: Span) -> Value {
|
||||
let mut sys = System::new();
|
||||
sys.refresh_networks();
|
||||
sys.refresh_networks_list();
|
||||
|
||||
@ -193,18 +200,17 @@ pub fn net(sys: &mut System, span: Span) -> Option<Value> {
|
||||
|
||||
output.push(Value::Record { cols, vals, span });
|
||||
}
|
||||
if !output.is_empty() {
|
||||
Some(Value::List { vals: output, span })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
Value::List { vals: output, span }
|
||||
}
|
||||
|
||||
pub fn cpu(sys: &mut System, span: Span) -> Option<Value> {
|
||||
// FIXME: we must refresh the CPU *twice* System::MINIMUM_CPU_UPDATE_INTERVAL apart to
|
||||
// get valid usage data, we're currently only doing it once. Consider using a LazyRecord
|
||||
// to avoid slowing down all calls to `sys`
|
||||
pub fn cpu(span: Span) -> Value {
|
||||
let mut sys = System::new();
|
||||
sys.refresh_cpu_specifics(CpuRefreshKind::everything());
|
||||
// We must refresh the CPU twice a while apart to get valid usage data.
|
||||
// In theory we could just sleep MINIMUM_CPU_UPDATE_INTERVAL, but I've noticed that
|
||||
// that gives poor results (error of ~5%). Decided to wait 2x that long, somewhat arbitrarily
|
||||
std::thread::sleep(System::MINIMUM_CPU_UPDATE_INTERVAL * 2);
|
||||
sys.refresh_cpu_specifics(CpuRefreshKind::new().with_cpu_usage());
|
||||
|
||||
let mut output = vec![];
|
||||
for cpu in sys.cpus() {
|
||||
@ -230,8 +236,12 @@ pub fn cpu(sys: &mut System, span: Span) -> Option<Value> {
|
||||
});
|
||||
|
||||
cols.push("cpu_usage".into());
|
||||
|
||||
// sysinfo CPU usage numbers are not very precise unless you wait a long time between refreshes.
|
||||
// Round to 1DP (chosen somewhat arbitrarily) so people aren't misled by high-precision floats.
|
||||
let rounded_usage = (cpu.cpu_usage() * 10.0).round() / 10.0;
|
||||
vals.push(Value::Float {
|
||||
val: cpu.cpu_usage() as f64,
|
||||
val: rounded_usage as f64,
|
||||
span,
|
||||
});
|
||||
|
||||
@ -254,14 +264,11 @@ pub fn cpu(sys: &mut System, span: Span) -> Option<Value> {
|
||||
output.push(Value::Record { cols, vals, span });
|
||||
}
|
||||
|
||||
if !output.is_empty() {
|
||||
Some(Value::List { vals: output, span })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
Value::List { vals: output, span }
|
||||
}
|
||||
|
||||
pub fn mem(sys: &mut System, span: Span) -> Option<Value> {
|
||||
pub fn mem(span: Span) -> Value {
|
||||
let mut sys = System::new();
|
||||
sys.refresh_memory();
|
||||
|
||||
let mut cols = vec![];
|
||||
@ -318,10 +325,11 @@ pub fn mem(sys: &mut System, span: Span) -> Option<Value> {
|
||||
span,
|
||||
});
|
||||
|
||||
Some(Value::Record { cols, vals, span })
|
||||
Value::Record { cols, vals, span }
|
||||
}
|
||||
|
||||
pub fn host(sys: &mut System, span: Span) -> Option<Value> {
|
||||
pub fn host(span: Span) -> Value {
|
||||
let mut sys = System::new();
|
||||
sys.refresh_users_list();
|
||||
|
||||
let mut cols = vec![];
|
||||
@ -414,10 +422,11 @@ pub fn host(sys: &mut System, span: Span) -> Option<Value> {
|
||||
vals.push(Value::List { vals: users, span });
|
||||
}
|
||||
|
||||
Some(Value::Record { cols, vals, span })
|
||||
Value::Record { cols, vals, span }
|
||||
}
|
||||
|
||||
pub fn temp(sys: &mut System, span: Span) -> Option<Value> {
|
||||
pub fn temp(span: Span) -> Value {
|
||||
let mut sys = System::new();
|
||||
sys.refresh_components();
|
||||
sys.refresh_components_list();
|
||||
|
||||
@ -455,9 +464,5 @@ pub fn temp(sys: &mut System, span: Span) -> Option<Value> {
|
||||
output.push(Value::Record { cols, vals, span });
|
||||
}
|
||||
|
||||
if !output.is_empty() {
|
||||
Some(Value::List { vals: output, span })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
Value::List { vals: output, span }
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ nu-utils = { path = "../nu-utils", version = "0.76.1" }
|
||||
|
||||
chrono = { version="0.4.23", features = ["std"], default-features = false }
|
||||
serde = {version = "1.0.143", default-features = false }
|
||||
sysinfo ="0.28.0"
|
||||
sysinfo ="0.28.2"
|
||||
|
||||
[features]
|
||||
plugin = []
|
||||
|
Loading…
Reference in New Issue
Block a user