mirror of
https://github.com/nushell/nushell.git
synced 2025-02-16 18:41:44 +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",
|
"percent-encoding",
|
||||||
"reedline",
|
"reedline",
|
||||||
"rstest",
|
"rstest",
|
||||||
"sysinfo 0.28.0",
|
"sysinfo 0.28.2",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2819,7 +2819,7 @@ dependencies = [
|
|||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"sha2",
|
"sha2",
|
||||||
"sqlparser",
|
"sqlparser",
|
||||||
"sysinfo 0.28.0",
|
"sysinfo 0.28.2",
|
||||||
"tabled",
|
"tabled",
|
||||||
"terminal_size 0.2.1",
|
"terminal_size 0.2.1",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@ -2848,7 +2848,7 @@ dependencies = [
|
|||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
"nu-utils",
|
"nu-utils",
|
||||||
"serde",
|
"serde",
|
||||||
"sysinfo 0.28.0",
|
"sysinfo 0.28.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -5134,9 +5134,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sysinfo"
|
name = "sysinfo"
|
||||||
version = "0.28.0"
|
version = "0.28.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "727220a596b4ca0af040a07091e49f5c105ec8f2592674339a5bf35be592f76e"
|
checksum = "d3e847e2de7a137c8c2cede5095872dbb00f4f9bf34d061347e36b43322acd56"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
|
@ -36,7 +36,7 @@ once_cell = "1.17.0"
|
|||||||
log = "0.4"
|
log = "0.4"
|
||||||
miette = { version = "5.5.0", features = ["fancy-no-backtrace"] }
|
miette = { version = "5.5.0", features = ["fancy-no-backtrace"] }
|
||||||
percent-encoding = "2"
|
percent-encoding = "2"
|
||||||
sysinfo = "0.28.0"
|
sysinfo = "0.28.2"
|
||||||
thiserror = "1.0.31"
|
thiserror = "1.0.31"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
@ -85,7 +85,7 @@ percent-encoding = "2.2.0"
|
|||||||
reedline = { version = "0.16.0", features = ["bashisms", "sqlite"] }
|
reedline = { version = "0.16.0", features = ["bashisms", "sqlite"] }
|
||||||
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
|
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
|
||||||
sqlparser = { version = "0.30.0", features = ["serde"], optional = true }
|
sqlparser = { version = "0.30.0", features = ["serde"], optional = true }
|
||||||
sysinfo = "0.28.0"
|
sysinfo = "0.28.2"
|
||||||
tabled = "0.10.0"
|
tabled = "0.10.0"
|
||||||
terminal_size = "0.2.1"
|
terminal_size = "0.2.1"
|
||||||
thiserror = "1.0.31"
|
thiserror = "1.0.31"
|
||||||
|
@ -3,8 +3,10 @@ use chrono::Local;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
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 std::time::{Duration, UNIX_EPOCH};
|
||||||
use sysinfo::{
|
use sysinfo::{
|
||||||
ComponentExt, CpuExt, CpuRefreshKind, DiskExt, NetworkExt, System, SystemExt, UserExt,
|
ComponentExt, CpuExt, CpuRefreshKind, DiskExt, NetworkExt, System, SystemExt, UserExt,
|
||||||
@ -36,7 +38,13 @@ impl Command for Sys {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> 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> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -60,51 +68,53 @@ impl Command for Sys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_sys(call: &Call) -> Result<PipelineData, ShellError> {
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
let span = call.head;
|
pub struct SysResult {
|
||||||
let mut sys = System::new();
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
let mut headers = vec![];
|
impl LazyRecord for SysResult {
|
||||||
let mut values = vec![];
|
fn column_names(&self) -> Vec<&'static str> {
|
||||||
|
vec!["host", "cpu", "disks", "mem", "temp", "net"]
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Record {
|
fn get_column_value(&self, column: &str) -> Result<Value, ShellError> {
|
||||||
cols: headers,
|
let span = self.span;
|
||||||
vals: values,
|
|
||||||
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 {
|
pub fn trim_cstyle_null(s: String) -> String {
|
||||||
s.trim_matches(char::from(0)).to_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();
|
||||||
sys.refresh_disks_list();
|
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 });
|
output.push(Value::Record { cols, vals, span });
|
||||||
}
|
}
|
||||||
if !output.is_empty() {
|
Value::List { vals: output, span }
|
||||||
Some(Value::List { vals: output, span })
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
sys.refresh_networks_list();
|
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 });
|
output.push(Value::Record { cols, vals, span });
|
||||||
}
|
}
|
||||||
if !output.is_empty() {
|
Value::List { vals: output, span }
|
||||||
Some(Value::List { vals: output, span })
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cpu(sys: &mut System, span: Span) -> Option<Value> {
|
pub fn cpu(span: Span) -> Value {
|
||||||
// FIXME: we must refresh the CPU *twice* System::MINIMUM_CPU_UPDATE_INTERVAL apart to
|
let mut sys = System::new();
|
||||||
// get valid usage data, we're currently only doing it once. Consider using a LazyRecord
|
|
||||||
// to avoid slowing down all calls to `sys`
|
|
||||||
sys.refresh_cpu_specifics(CpuRefreshKind::everything());
|
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![];
|
let mut output = vec![];
|
||||||
for cpu in sys.cpus() {
|
for cpu in sys.cpus() {
|
||||||
@ -230,8 +236,12 @@ pub fn cpu(sys: &mut System, span: Span) -> Option<Value> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
cols.push("cpu_usage".into());
|
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 {
|
vals.push(Value::Float {
|
||||||
val: cpu.cpu_usage() as f64,
|
val: rounded_usage as f64,
|
||||||
span,
|
span,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -254,14 +264,11 @@ pub fn cpu(sys: &mut System, span: Span) -> Option<Value> {
|
|||||||
output.push(Value::Record { cols, vals, span });
|
output.push(Value::Record { cols, vals, span });
|
||||||
}
|
}
|
||||||
|
|
||||||
if !output.is_empty() {
|
Value::List { vals: output, span }
|
||||||
Some(Value::List { vals: output, span })
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mem(sys: &mut System, span: Span) -> Option<Value> {
|
pub fn mem(span: Span) -> Value {
|
||||||
|
let mut sys = System::new();
|
||||||
sys.refresh_memory();
|
sys.refresh_memory();
|
||||||
|
|
||||||
let mut cols = vec![];
|
let mut cols = vec![];
|
||||||
@ -318,10 +325,11 @@ pub fn mem(sys: &mut System, span: Span) -> Option<Value> {
|
|||||||
span,
|
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();
|
sys.refresh_users_list();
|
||||||
|
|
||||||
let mut cols = vec![];
|
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 });
|
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();
|
||||||
sys.refresh_components_list();
|
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 });
|
output.push(Value::Record { cols, vals, span });
|
||||||
}
|
}
|
||||||
|
|
||||||
if !output.is_empty() {
|
Value::List { vals: output, span }
|
||||||
Some(Value::List { vals: output, span })
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ nu-utils = { path = "../nu-utils", version = "0.76.1" }
|
|||||||
|
|
||||||
chrono = { version="0.4.23", features = ["std"], default-features = false }
|
chrono = { version="0.4.23", features = ["std"], default-features = false }
|
||||||
serde = {version = "1.0.143", default-features = false }
|
serde = {version = "1.0.143", default-features = false }
|
||||||
sysinfo ="0.28.0"
|
sysinfo ="0.28.2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
plugin = []
|
plugin = []
|
||||||
|
Loading…
Reference in New Issue
Block a user