mirror of
https://github.com/nushell/nushell.git
synced 2024-12-26 00:50:03 +01:00
add a debug info
command to show memory info (#10711)
# Description This PR adds a new command called `debug info`. I'm not sure if the name is right but we can rename it if needed. The purpose of this command is to show a user how much memory nushell is using. This is what the output looks like. I feel like the further we go with nushell, the more we'll need to easily monitor the memory usage. With this command, we should easily be able to do that with scripts or just running the command. ```nushell ❯ debug info | table -e ╭─────────┬──────────────────────────────────────────────────────────────────────╮ │pid │31036 │ │ppid │29388 │ │ │╭─────────────────┬────────────────────────────────────────────────╮ │ │process ││memory │63.5 MB │ │ │ ││virtual_memory │5.6 GB │ │ │ ││status │Runnable │ │ │ ││root │C:\cartar\debug │ │ │ ││cwd │C:\Users\us991808\source\repos\forks\nushell\ │ │ │ ││exe_path │C:\cartar\debug\nu.exe │ │ │ ││command │c:\cartar\debug\nu.exe -l │ │ │ ││name │nu.exe │ │ │ ││environment │{record 110 fields} │ │ │ │╰─────────────────┴────────────────────────────────────────────────╯ │ │ │╭────────────────┬───────╮ │ │system ││total_memory │17.1 GB│ │ │ ││free_memory │5.9 GB │ │ │ ││used_memory │11.3 GB│ │ │ ││available_memory│5.9 GB │ │ │ │╰────────────────┴───────╯ │ ╰─────────┴──────────────────────────────────────────────────────────────────────╯ ``` > [!NOTE] The `process.environment` is not the nushell `$env` but the environment that the process was created with at launch time. # User-Facing Changes <!-- List of all changes that impact the user experience here. This helps us keep track of breaking changes. --> # Tests + Formatting <!-- Don't forget to add tests that cover your changes. Make sure you've run and fixed any issues with these commands: - `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass (on Windows make sure to [enable developer mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging)) - `cargo run -- -c "use std testing; testing run-tests --path crates/nu-std"` to run the tests for the standard library > **Note** > from `nushell` you can also use the `toolkit` as follows > ```bash > use toolkit.nu # or use an `env_change` hook to activate it automatically > toolkit check pr > ``` --> # After Submitting <!-- If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date. -->
This commit is contained in:
parent
6181ea5fc1
commit
1f62024a15
148
crates/nu-command/src/debug/info.rs
Normal file
148
crates/nu-command/src/debug/info.rs
Normal file
@ -0,0 +1,148 @@
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
record, Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
|
||||
Type, Value,
|
||||
};
|
||||
use sysinfo::{Pid, PidExt, ProcessExt, ProcessRefreshKind, RefreshKind, System, SystemExt};
|
||||
// Character used to separate directories in a Path Environment variable on windows is ";"
|
||||
#[cfg(target_family = "windows")]
|
||||
const ENV_PATH_SEPARATOR_CHAR: char = ';';
|
||||
// Character used to separate directories in a Path Environment variable on linux/mac/unix is ":"
|
||||
#[cfg(not(target_family = "windows"))]
|
||||
const ENV_PATH_SEPARATOR_CHAR: char = ':';
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DebugInfo;
|
||||
|
||||
impl Command for DebugInfo {
|
||||
fn name(&self) -> &str {
|
||||
"debug info"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"View process memory info."
|
||||
}
|
||||
|
||||
fn extra_usage(&self) -> &str {
|
||||
"This command is meant for debugging purposes.\nIt shows you the process information and system memory information."
|
||||
}
|
||||
|
||||
fn signature(&self) -> nu_protocol::Signature {
|
||||
Signature::build("debug info")
|
||||
.input_output_types(vec![(Type::Nothing, Type::Record(vec![]))])
|
||||
.category(Category::Debug)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "View process information",
|
||||
example: "debug info",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
_call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let span = Span::unknown();
|
||||
// get the nushell process id
|
||||
let pid = Pid::from(std::process::id() as usize);
|
||||
// only refresh the process and memory information
|
||||
let rk = RefreshKind::new()
|
||||
.with_processes(
|
||||
ProcessRefreshKind::new()
|
||||
.without_cpu()
|
||||
.without_disk_usage()
|
||||
.without_user(),
|
||||
)
|
||||
.with_memory();
|
||||
// only get information requested
|
||||
let system = System::new_with_specifics(rk);
|
||||
// get the process information for the nushell pid
|
||||
let pinfo = system.process(pid);
|
||||
|
||||
if let Some(p) = pinfo {
|
||||
Ok(Value::record(
|
||||
record! {
|
||||
"pid" => Value::int(p.pid().as_u32() as i64, span),
|
||||
"ppid" => Value::int(p.parent().unwrap_or(0.into()).as_u32() as i64, span),
|
||||
"process" => {
|
||||
Value::record(
|
||||
record! {
|
||||
"memory" => Value::filesize(p.memory() as i64, span),
|
||||
"virtual_memory" => Value::filesize(p.virtual_memory() as i64, span),
|
||||
"status" => Value::string(p.status().to_string(), span),
|
||||
// This is a hack to get the "root" since p.root() doesn't work on macos
|
||||
// Would probably puke if nu was on the root of a drive, maybe other ways too.
|
||||
"root" => {
|
||||
if let Some(filename) = p.exe().parent() {
|
||||
Value::string(filename.to_string_lossy().to_string(), span)
|
||||
} else {
|
||||
Value::nothing(span)
|
||||
}
|
||||
},
|
||||
// "root" => Value::string(p.root().to_string_lossy().to_string(), span),
|
||||
"cwd" => Value::string(p.cwd().to_string_lossy().to_string(), span),
|
||||
"exe_path" => Value::string(p.exe().to_string_lossy().to_string(), span),
|
||||
"command" => Value::string(p.cmd().join(" "), span),
|
||||
"name" => Value::string(p.name().to_string(), span),
|
||||
"environment" => {
|
||||
let mut env_rec = Record::new();
|
||||
for val in p.environ() {
|
||||
let (key, value) = val.split_once('=').unwrap_or(("", ""));
|
||||
// Let's make some of the known list-variables into lists
|
||||
if key == "PATH" ||
|
||||
key == "Path" ||
|
||||
key == "DYLD_FALLBACK_LIBRARY_PATH" ||
|
||||
key == "PATHEXT" ||
|
||||
key == "PSMODULEPATH" ||
|
||||
key == "PSModulePath" {
|
||||
let items = value.split(ENV_PATH_SEPARATOR_CHAR).map(|r| Value::string(r.to_string(), span)).collect::<Vec<_>>();
|
||||
env_rec.push(key.to_string(), Value::list(items, span));
|
||||
} else if key == "LS_COLORS" { // LS_COLORS is a special case, it's a colon separated list of key=value pairs
|
||||
let items = value.split(':').map(|r| Value::string(r.to_string(), span)).collect::<Vec<_>>();
|
||||
env_rec.push(key.to_string(), Value::list(items, span));
|
||||
} else {
|
||||
env_rec.push(key.to_string(), Value::string(value.to_string(), span));
|
||||
}
|
||||
}
|
||||
Value::record(env_rec, span)
|
||||
},
|
||||
},
|
||||
span,
|
||||
)
|
||||
},
|
||||
"system" => {
|
||||
Value::record(
|
||||
record! {
|
||||
"total_memory" => Value::filesize(system.total_memory() as i64, span),
|
||||
"free_memory" => Value::filesize(system.free_memory() as i64, span),
|
||||
"used_memory" => Value::filesize(system.used_memory() as i64, span),
|
||||
"available_memory" => Value::filesize(system.available_memory() as i64, span),
|
||||
},
|
||||
span,
|
||||
)
|
||||
}
|
||||
},
|
||||
span,
|
||||
).into_pipeline_data())
|
||||
} else {
|
||||
// If we can't get the process information, just return the system information
|
||||
Ok(Value::record(
|
||||
record! {
|
||||
"total_memory" => Value::filesize(system.total_memory() as i64, span),
|
||||
"free_memory" => Value::filesize(system.free_memory() as i64, span),
|
||||
"used_memory" => Value::filesize(system.used_memory() as i64, span),
|
||||
"available_memory" => Value::filesize(system.available_memory() as i64, span),
|
||||
},
|
||||
span,
|
||||
)
|
||||
.into_pipeline_data())
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
mod ast;
|
||||
mod debug_;
|
||||
mod explain;
|
||||
mod info;
|
||||
mod inspect;
|
||||
mod inspect_table;
|
||||
mod metadata;
|
||||
@ -14,6 +15,7 @@ mod view_span;
|
||||
pub use ast::Ast;
|
||||
pub use debug_::Debug;
|
||||
pub use explain::Explain;
|
||||
pub use info::DebugInfo;
|
||||
pub use inspect::Inspect;
|
||||
pub use inspect_table::build_table;
|
||||
pub use metadata::Metadata;
|
||||
|
@ -134,6 +134,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
||||
bind_command! {
|
||||
Ast,
|
||||
Debug,
|
||||
DebugInfo,
|
||||
Explain,
|
||||
Inspect,
|
||||
Metadata,
|
||||
|
Loading…
Reference in New Issue
Block a user