Benchmark each pipeline element (#7854)

# Description

Adds a `profile` command that profiles each pipeline element of a block
and can also recursively step into child blocks.

# Limitations
* It is implemented using pipeline metadata which currently get lost in
some circumstances (e.g.,
https://github.com/nushell/nushell/issues/4501). This means that the
profiler will lose data coming from subexpressions. This issue will
hopefully be solved in the future.
* It also does not step into individual loop iteration which I'm not
sure why but maybe that's a good thing.

# User-Facing Changes

Shouldn't change any existing behavior.

# 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.

---------

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
This commit is contained in:
Jakub Žádník
2023-02-11 23:35:48 +02:00
committed by GitHub
parent 64b6c02a22
commit 58529aa0b2
12 changed files with 328 additions and 25 deletions

View File

@ -7,6 +7,44 @@ use crate::{ShellError, Span, Value, VarId};
/// Environment variables per overlay
pub type EnvVars = HashMap<String, HashMap<String, Value>>;
#[derive(Debug, Clone)]
pub struct ProfilingConfig {
pub max_depth: i64,
pub depth: i64,
pub collect_source: bool,
pub collect_values: bool,
}
impl ProfilingConfig {
pub fn new(max_depth: i64, collect_source: bool, collect_values: bool) -> Self {
ProfilingConfig {
max_depth,
depth: 0,
collect_source,
collect_values,
}
}
pub fn enter_block(&mut self) {
self.depth += 1;
}
pub fn leave_block(&mut self) {
self.depth -= 1;
}
pub fn should_debug(&self) -> bool {
self.depth <= self.max_depth
}
pub fn reset(&mut self) {
self.max_depth = 0;
self.depth = 0;
self.collect_source = false;
self.collect_values = false;
}
}
/// A runtime value stack used during evaluation
///
/// A note on implementation:
@ -35,6 +73,7 @@ pub struct Stack {
/// List of active overlays
pub active_overlays: Vec<String>,
pub recursion_count: Box<u64>,
pub profiling_config: ProfilingConfig,
}
impl Stack {
@ -45,6 +84,7 @@ impl Stack {
env_hidden: HashMap::new(),
active_overlays: vec![DEFAULT_OVERLAY_NAME.to_string()],
recursion_count: Box::new(0),
profiling_config: ProfilingConfig::new(0, false, false),
}
}
@ -126,6 +166,7 @@ impl Stack {
env_hidden: HashMap::new(),
active_overlays: self.active_overlays.clone(),
recursion_count: self.recursion_count.to_owned(),
profiling_config: self.profiling_config.clone(),
}
}
@ -151,6 +192,7 @@ impl Stack {
env_hidden: HashMap::new(),
active_overlays: self.active_overlays.clone(),
recursion_count: self.recursion_count.to_owned(),
profiling_config: self.profiling_config.clone(),
}
}

View File

@ -40,14 +40,16 @@ const LINE_ENDING_PATTERN: &[char] = &['\r', '\n'];
/// Nushell.
#[derive(Debug)]
pub enum PipelineData {
Value(Value, Option<PipelineMetadata>),
ListStream(ListStream, Option<PipelineMetadata>),
// Note: the PipelineMetadata is boxed everywhere because the DataSource::Profiling caused
// stack overflow on Windows CI when testing virtualenv
Value(Value, Option<Box<PipelineMetadata>>),
ListStream(ListStream, Option<Box<PipelineMetadata>>),
ExternalStream {
stdout: Option<RawStream>,
stderr: Option<RawStream>,
exit_code: Option<ListStream>,
span: Span,
metadata: Option<PipelineMetadata>,
metadata: Option<Box<PipelineMetadata>>,
trim_end_newline: bool,
},
Empty,
@ -62,10 +64,11 @@ pub struct PipelineMetadata {
pub enum DataSource {
Ls,
HtmlThemes,
Profiling(Vec<Value>),
}
impl PipelineData {
pub fn new_with_metadata(metadata: Option<PipelineMetadata>, span: Span) -> PipelineData {
pub fn new_with_metadata(metadata: Option<Box<PipelineMetadata>>, span: Span) -> PipelineData {
PipelineData::Value(Value::Nothing { span }, metadata)
}
@ -73,7 +76,7 @@ impl PipelineData {
PipelineData::Empty
}
pub fn metadata(&self) -> Option<PipelineMetadata> {
pub fn metadata(&self) -> Option<Box<PipelineMetadata>> {
match self {
PipelineData::ListStream(_, x) => x.clone(),
PipelineData::ExternalStream { metadata: x, .. } => x.clone(),
@ -82,7 +85,7 @@ impl PipelineData {
}
}
pub fn set_metadata(mut self, metadata: Option<PipelineMetadata>) -> Self {
pub fn set_metadata(mut self, metadata: Option<Box<PipelineMetadata>>) -> Self {
match &mut self {
PipelineData::ListStream(_, x) => *x = metadata,
PipelineData::ExternalStream { metadata: x, .. } => *x = metadata,
@ -284,7 +287,7 @@ impl PipelineData {
pub fn collect_string_strict(
self,
span: Span,
) -> Result<(String, Span, Option<PipelineMetadata>), ShellError> {
) -> Result<(String, Span, Option<Box<PipelineMetadata>>), ShellError> {
match self {
PipelineData::Empty => Ok((String::new(), span, None)),
PipelineData::Value(Value::String { val, span }, metadata) => Ok((val, span, metadata)),
@ -809,9 +812,10 @@ impl Iterator for PipelineIterator {
pub trait IntoPipelineData {
fn into_pipeline_data(self) -> PipelineData;
fn into_pipeline_data_with_metadata(
self,
metadata: impl Into<Option<PipelineMetadata>>,
metadata: impl Into<Option<Box<PipelineMetadata>>>,
) -> PipelineData;
}
@ -822,9 +826,10 @@ where
fn into_pipeline_data(self) -> PipelineData {
PipelineData::Value(self.into(), None)
}
fn into_pipeline_data_with_metadata(
self,
metadata: impl Into<Option<PipelineMetadata>>,
metadata: impl Into<Option<Box<PipelineMetadata>>>,
) -> PipelineData {
PipelineData::Value(self.into(), metadata.into())
}
@ -834,7 +839,7 @@ pub trait IntoInterruptiblePipelineData {
fn into_pipeline_data(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineData;
fn into_pipeline_data_with_metadata(
self,
metadata: impl Into<Option<PipelineMetadata>>,
metadata: impl Into<Option<Box<PipelineMetadata>>>,
ctrlc: Option<Arc<AtomicBool>>,
) -> PipelineData;
}
@ -857,7 +862,7 @@ where
fn into_pipeline_data_with_metadata(
self,
metadata: impl Into<Option<PipelineMetadata>>,
metadata: impl Into<Option<Box<PipelineMetadata>>>,
ctrlc: Option<Arc<AtomicBool>>,
) -> PipelineData {
PipelineData::ListStream(