Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions crates/nu-command/src/core_commands/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl Command for Metadata {
None => {
let mut cols = vec![];
let mut vals = vec![];
if let Some(x) = &input.metadata() {
if let Some(x) = input.metadata().as_deref() {
match x {
PipelineMetadata {
data_source: DataSource::Ls,
Expand All @@ -89,6 +89,12 @@ impl Command for Metadata {
cols.push("source".into());
vals.push(Value::string("into html --list", head))
}
PipelineMetadata {
data_source: DataSource::Profiling(values),
} => {
cols.push("profiling".into());
vals.push(Value::list(values.clone(), head))
}
}
}

Expand Down Expand Up @@ -118,7 +124,11 @@ impl Command for Metadata {
}
}

fn build_metadata_record(arg: &Value, metadata: &Option<PipelineMetadata>, head: Span) -> Value {
fn build_metadata_record(
arg: &Value,
metadata: &Option<Box<PipelineMetadata>>,
head: Span,
) -> Value {
let mut cols = vec![];
let mut vals = vec![];

Expand All @@ -140,7 +150,7 @@ fn build_metadata_record(arg: &Value, metadata: &Option<PipelineMetadata>, head:
});
}

if let Some(x) = &metadata {
if let Some(x) = metadata.as_deref() {
match x {
PipelineMetadata {
data_source: DataSource::Ls,
Expand All @@ -154,6 +164,12 @@ fn build_metadata_record(arg: &Value, metadata: &Option<PipelineMetadata>, head:
cols.push("source".into());
vals.push(Value::string("into html --list", head))
}
PipelineMetadata {
data_source: DataSource::Profiling(values),
} => {
cols.push("profiling".into());
vals.push(Value::list(values.clone(), head))
}
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/nu-command/src/default_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,7 @@ pub fn create_default_context() -> EngineState {
// Experimental
bind_command! {
IsAdmin,
Profile,
View,
ViewFiles,
ViewSource,
Expand Down
2 changes: 2 additions & 0 deletions crates/nu-command/src/experimental/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
mod is_admin;
mod profile;
mod view;
mod view_files;
mod view_source;
mod view_span;

pub use is_admin::IsAdmin;
pub use profile::Profile;
pub use view::View;
pub use view_files::ViewFiles;
pub use view_source::ViewSource;
Expand Down
113 changes: 113 additions & 0 deletions crates/nu-command/src/experimental/profile.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,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,
}]
}
}
4 changes: 2 additions & 2 deletions crates/nu-command/src/filesystem/ls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,9 +264,9 @@ impl Command for Ls {
_ => Some(Value::Nothing { span: call_span }),
})
.into_pipeline_data_with_metadata(
PipelineMetadata {
Box::new(PipelineMetadata {
data_source: DataSource::Ls,
},
}),
engine_state.ctrlc.clone(),
))
}
Expand Down
2 changes: 1 addition & 1 deletion crates/nu-command/src/filters/uniq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ pub fn uniq(
call: &Call,
input: Vec<Value>,
item_mapper: Box<dyn Fn(ItemMapperState) -> ValueCounter>,
metadata: Option<PipelineMetadata>,
metadata: Option<Box<PipelineMetadata>>,
) -> Result<PipelineData, ShellError> {
let ctrlc = engine_state.ctrlc.clone();
let head = call.head;
Expand Down
4 changes: 2 additions & 2 deletions crates/nu-command/src/formats/to/html.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,9 +320,9 @@ fn to_html(
vals: result,
span: head,
}
.into_pipeline_data_with_metadata(PipelineMetadata {
.into_pipeline_data_with_metadata(Box::new(PipelineMetadata {
data_source: DataSource::HtmlThemes,
}));
})));
} else {
let theme_span = match &theme {
Some(v) => v.span,
Expand Down
4 changes: 2 additions & 2 deletions crates/nu-command/src/viewers/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -612,9 +612,9 @@ fn handle_row_stream(
call: &Call,
row_offset: usize,
ctrlc: Option<Arc<AtomicBool>>,
metadata: Option<PipelineMetadata>,
metadata: Option<Box<PipelineMetadata>>,
) -> Result<PipelineData, ShellError> {
let stream = match metadata {
let stream = match metadata.as_deref() {
// First, `ls` sources:
Some(PipelineMetadata {
data_source: DataSource::Ls,
Expand Down
Loading