diff --git a/Cargo.lock b/Cargo.lock index 20e6cd5403..367ff3f828 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1139,6 +1139,7 @@ dependencies = [ "jemalloc-ctl", "jemallocator", "once_cell", + "ra_arena", ] [[package]] diff --git a/crates/ra_arena/src/lib.rs b/crates/ra_arena/src/lib.rs index ea98d54441..441fbb3cbe 100644 --- a/crates/ra_arena/src/lib.rs +++ b/crates/ra_arena/src/lib.rs @@ -96,6 +96,9 @@ impl Arena { pub const fn new() -> Arena { Arena { data: Vec::new() } } + pub fn clear(&mut self) { + self.data.clear(); + } pub fn len(&self) -> usize { self.data.len() diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 6a53be621d..bd4ef69a07 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -667,7 +667,7 @@ impl Expectation { } mod diagnostics { - use hir_def::{expr::ExprId, src::HasSource, FunctionId, Lookup}; + use hir_def::{expr::ExprId, FunctionId}; use hir_expand::diagnostics::DiagnosticSink; use crate::{db::HirDatabase, diagnostics::NoSuchField}; @@ -686,10 +686,9 @@ mod diagnostics { ) { match self { InferenceDiagnostic::NoSuchField { expr, field } => { - let source = owner.lookup(db.upcast()).source(db.upcast()); let (_, source_map) = db.body_with_source_map(owner.into()); let field = source_map.field_syntax(*expr, *field); - sink.push(NoSuchField { file: source.file_id, field: field.value }) + sink.push(NoSuchField { file: field.file_id, field: field.value }) } } } diff --git a/crates/ra_prof/Cargo.toml b/crates/ra_prof/Cargo.toml index d15b089925..c33b5121ae 100644 --- a/crates/ra_prof/Cargo.toml +++ b/crates/ra_prof/Cargo.toml @@ -9,6 +9,7 @@ publish = false doctest = false [dependencies] +ra_arena = { path = "../ra_arena" } once_cell = "1.3.1" backtrace = { version = "0.3.44", optional = true } diff --git a/crates/ra_prof/src/hprof.rs b/crates/ra_prof/src/hprof.rs new file mode 100644 index 0000000000..2b8a903636 --- /dev/null +++ b/crates/ra_prof/src/hprof.rs @@ -0,0 +1,246 @@ +//! Simple hierarchical profiler +use once_cell::sync::Lazy; +use std::{ + cell::RefCell, + collections::{BTreeMap, HashSet}, + io::{stderr, Write}, + sync::{ + atomic::{AtomicBool, Ordering}, + RwLock, + }, + time::{Duration, Instant}, +}; + +use crate::tree::{Idx, Tree}; + +/// Filtering syntax +/// env RA_PROFILE=* // dump everything +/// env RA_PROFILE=foo|bar|baz // enabled only selected entries +/// env RA_PROFILE=*@3>10 // dump everything, up to depth 3, if it takes more than 10 ms +pub fn init() { + let spec = std::env::var("RA_PROFILE").unwrap_or_default(); + init_from(&spec); +} + +pub fn init_from(spec: &str) { + let filter = if spec.is_empty() { Filter::disabled() } else { Filter::from_spec(spec) }; + filter.install(); +} + +pub type Label = &'static str; + +/// This function starts a profiling scope in the current execution stack with a given description. +/// It returns a Profile structure and measure elapsed time between this method invocation and Profile structure drop. +/// It supports nested profiling scopes in case when this function invoked multiple times at the execution stack. In this case the profiling information will be nested at the output. +/// Profiling information is being printed in the stderr. +/// +/// # Example +/// ``` +/// use ra_prof::{profile, set_filter, Filter}; +/// +/// let f = Filter::from_spec("profile1|profile2@2"); +/// set_filter(f); +/// profiling_function1(); +/// +/// fn profiling_function1() { +/// let _p = profile("profile1"); +/// profiling_function2(); +/// } +/// +/// fn profiling_function2() { +/// let _p = profile("profile2"); +/// } +/// ``` +/// This will print in the stderr the following: +/// ```text +/// 0ms - profile +/// 0ms - profile2 +/// ``` +pub fn profile(label: Label) -> Profiler { + assert!(!label.is_empty()); + let enabled = PROFILING_ENABLED.load(Ordering::Relaxed) + && PROFILE_STACK.with(|stack| stack.borrow_mut().push(label)); + let label = if enabled { Some(label) } else { None }; + Profiler { label, detail: None } +} + +pub struct Profiler { + label: Option