1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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,
}]
}
}