nushell/crates/nu-command/src/debug/profile.rs
Darren Schroeder 4c787af26d
relocate debug commands (#8071)
# Description

Now that we've landed the debug commands we were working on, let's
relocate them to an easier place to find all of them. That's what this
PR does.

The only actual code change was changing the `timeit` command to a
`Category::Debug` command. The rest is just moving things around and
hooking them up.

# User-Facing 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 -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass

# 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.
2023-02-13 16:39:07 +00:00

114 lines
3.6 KiB
Rust

use nu_engine::{eval_block, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Closure, Command, EngineState, ProfilingConfig, Stack};
use nu_protocol::{
Category, DataSource, Example, IntoPipelineData, PipelineData, PipelineMetadata, Signature,
Spanned, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct Profile;
impl Command for Profile {
fn name(&self) -> &str {
"profile"
}
fn usage(&self) -> &str {
"Profile each pipeline element in a closure."
}
fn extra_usage(&self) -> &str {
r#"The command collects run time of every pipeline element, recursively stepping into child closures
until a maximum depth. Optionally, it also collects the source code and intermediate values.
Current known limitations are:
* profiling data from subexpressions is not tracked
* it does not step into loop iterations"#
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("profile")
.required(
"closure",
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
"the closure to run",
)
.switch("source", "Collect source code in the report", None)
.switch("values", "Collect values in the report", None)
.named(
"max-depth",
SyntaxShape::Int,
"How many levels of blocks to step into (default: 1)",
Some('d'),
)
.input_output_types(vec![(Type::Any, Type::Table(vec![]))])
.allow_variants_without_examples(true)
.category(Category::Debug)
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let capture_block: Spanned<Closure> = call.req(engine_state, stack, 0)?;
let block = engine_state.get_block(capture_block.item.block_id);
let redirect_stdout = call.redirect_stdout;
let redirect_stderr = call.redirect_stderr;
let mut stack = stack.captures_to_stack(&capture_block.item.captures);
let input_val = input.into_value(call.head);
if let Some(var) = block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id {
stack.add_var(*var_id, input_val.clone());
}
}
stack.profiling_config = ProfilingConfig::new(
call.get_flag::<i64>(engine_state, &mut stack, "max-depth")?
.unwrap_or(1),
call.has_flag("source"),
call.has_flag("values"),
);
let profiling_metadata = Box::new(PipelineMetadata {
data_source: DataSource::Profiling(vec![]),
});
let result = if let Some(PipelineMetadata {
data_source: DataSource::Profiling(values),
}) = eval_block(
engine_state,
&mut stack,
block,
input_val.into_pipeline_data_with_metadata(profiling_metadata),
redirect_stdout,
redirect_stderr,
)?
.metadata()
.map(|m| *m)
{
Value::list(values, call.head)
} else {
Value::nothing(call.head)
};
Ok(result.into_pipeline_data())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description:
"Profile some code, stepping into the `spam` command and collecting source.",
example: r#"def spam [] { "spam" }; profile { spam | str length } -d 2 --source"#,
result: None,
}]
}
}