From 7623db11061f70dd654405a0da91bc3ad1abc53a Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 25 Apr 2020 14:52:23 +0200 Subject: [PATCH 1/7] minor clenup --- crates/ra_prof/src/lib.rs | 25 +++++++++++-------- .../tests/heavy_tests/support.rs | 6 +---- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/crates/ra_prof/src/lib.rs b/crates/ra_prof/src/lib.rs index 2d4f68f5e0..d95ad3107b 100644 --- a/crates/ra_prof/src/lib.rs +++ b/crates/ra_prof/src/lib.rs @@ -26,11 +26,18 @@ pub use crate::memory_usage::{Bytes, MemoryUsage}; #[global_allocator] static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; +/// 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() { - set_filter(match std::env::var("RA_PROFILE") { - Ok(spec) => Filter::from_spec(&spec), - Err(_) => Filter::disabled(), - }); + 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) }; + set_filter(filter); } /// Set profiling filter. It specifies descriptions allowed to profile. @@ -43,7 +50,7 @@ pub fn init() { /// let f = Filter::from_spec("profile1|profile2@2"); /// set_filter(f); /// ``` -pub fn set_filter(f: Filter) { +fn set_filter(f: Filter) { PROFILING_ENABLED.store(f.depth > 0, Ordering::SeqCst); let set: HashSet<_> = f.allowed.iter().cloned().collect(); let mut old = FILTER.write().unwrap(); @@ -127,18 +134,14 @@ impl Profiler { } } -pub struct Filter { +struct Filter { depth: usize, allowed: Vec, longer_than: Duration, } impl Filter { - // 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 from_spec(mut spec: &str) -> Filter { + fn from_spec(mut spec: &str) -> Filter { let longer_than = if let Some(idx) = spec.rfind('>') { let longer_than = spec[idx + 1..].parse().expect("invalid profile longer_than"); spec = &spec[..idx]; diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs index 7eebedff7a..e4fe3411aa 100644 --- a/crates/rust-analyzer/tests/heavy_tests/support.rs +++ b/crates/rust-analyzer/tests/heavy_tests/support.rs @@ -62,11 +62,7 @@ impl<'a> Project<'a> { static INIT: Once = Once::new(); INIT.call_once(|| { env_logger::builder().is_test(true).try_init().unwrap(); - ra_prof::set_filter(if crate::PROFILE.is_empty() { - ra_prof::Filter::disabled() - } else { - ra_prof::Filter::from_spec(&crate::PROFILE) - }); + ra_prof::init_from(crate::PROFILE); }); let mut paths = vec![]; From b3e9f3d143b0fae970449b7c49a2daf6f966a068 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 25 Apr 2020 15:02:09 +0200 Subject: [PATCH 2/7] Move hprof to a separate file --- crates/ra_prof/src/hprof.rs | 391 +++++++++++++++++++++++++++++++++++ crates/ra_prof/src/lib.rs | 400 +----------------------------------- 2 files changed, 398 insertions(+), 393 deletions(-) create mode 100644 crates/ra_prof/src/hprof.rs diff --git a/crates/ra_prof/src/hprof.rs b/crates/ra_prof/src/hprof.rs new file mode 100644 index 0000000000..79268513d8 --- /dev/null +++ b/crates/ra_prof/src/hprof.rs @@ -0,0 +1,391 @@ +//! Simple hierarchical profiler +use std::{ + cell::RefCell, + collections::{BTreeMap, HashSet}, + io::{stderr, Write}, + sync::{ + atomic::{AtomicBool, Ordering}, + RwLock, + }, + time::{Duration, Instant}, +}; + +use once_cell::sync::Lazy; + +/// 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) }; + set_filter(filter); +} + +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()); + if !PROFILING_ENABLED.load(Ordering::Relaxed) { + return Profiler { label: None, detail: None }; + } + + PROFILE_STACK.with(|stack| { + let mut stack = stack.borrow_mut(); + if stack.starts.is_empty() { + if let Ok(f) = FILTER.try_read() { + if f.version > stack.filter_data.version { + stack.filter_data = f.clone(); + } + }; + } + if stack.starts.len() > stack.filter_data.depth { + return Profiler { label: None, detail: None }; + } + let allowed = &stack.filter_data.allowed; + if stack.starts.is_empty() && !allowed.is_empty() && !allowed.contains(label) { + return Profiler { label: None, detail: None }; + } + + stack.starts.push(Instant::now()); + Profiler { label: Some(label), detail: None } + }) +} + +pub struct Profiler { + label: Option