mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-15 17:28:09 +00:00
Merge #1146
1146: Moar profiling r=matklad a=matklad Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
commit
546d9be2a7
7 changed files with 91 additions and 42 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -996,6 +996,7 @@ dependencies = [
|
|||
"ra_db 0.1.0",
|
||||
"ra_fmt 0.1.0",
|
||||
"ra_hir 0.1.0",
|
||||
"ra_prof 0.1.0",
|
||||
"ra_syntax 0.1.0",
|
||||
"ra_text_edit 0.1.0",
|
||||
"rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -23,6 +23,7 @@ ra_syntax = { path = "../ra_syntax" }
|
|||
ra_text_edit = { path = "../ra_text_edit" }
|
||||
ra_db = { path = "../ra_db" }
|
||||
ra_fmt = { path = "../ra_fmt" }
|
||||
ra_prof = { path = "../ra_prof" }
|
||||
hir = { path = "../ra_hir", package = "ra_hir" }
|
||||
test_utils = { path = "../test_utils" }
|
||||
ra_assists = { path = "../ra_assists" }
|
||||
|
|
|
@ -9,6 +9,7 @@ use ra_db::{
|
|||
salsa::{Database, SweepStrategy},
|
||||
};
|
||||
use ra_syntax::SourceFile;
|
||||
use ra_prof::profile;
|
||||
use relative_path::RelativePathBuf;
|
||||
use rayon::prelude::*;
|
||||
|
||||
|
@ -153,6 +154,7 @@ const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100);
|
|||
|
||||
impl RootDatabase {
|
||||
pub(crate) fn apply_change(&mut self, change: AnalysisChange) {
|
||||
let _p = profile("RootDatabase::apply_change");
|
||||
log::info!("apply_change {:?}", change);
|
||||
if !change.new_roots.is_empty() {
|
||||
let mut local_roots = Vec::clone(&self.local_roots());
|
||||
|
|
|
@ -6,21 +6,16 @@ use ra_lsp_server::{Result, InitializationOptions};
|
|||
use ra_prof;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
::std::env::set_var("RUST_BACKTRACE", "short");
|
||||
std::env::set_var("RUST_BACKTRACE", "short");
|
||||
let logger = Logger::with_env_or_str("error").duplicate_to_stderr(Duplicate::All);
|
||||
match ::std::env::var("RA_INTERNAL_MODE") {
|
||||
match std::env::var("RA_LOG_DIR") {
|
||||
Ok(ref v) if v == "1" => logger.log_to_file().directory("log").start()?,
|
||||
_ => logger.start()?,
|
||||
};
|
||||
let prof_depth = match ::std::env::var("RA_PROFILE_DEPTH") {
|
||||
Ok(ref d) => d.parse()?,
|
||||
_ => 0,
|
||||
};
|
||||
let profile_allowed = match ::std::env::var("RA_PROFILE") {
|
||||
Ok(ref p) => p.split(";").map(String::from).collect(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
ra_prof::set_filter(ra_prof::Filter::new(prof_depth, profile_allowed));
|
||||
ra_prof::set_filter(match std::env::var("RA_PROFILE") {
|
||||
Ok(spec) => ra_prof::Filter::from_spec(&spec),
|
||||
Err(_) => ra_prof::Filter::disabled(),
|
||||
});
|
||||
log::info!("lifecycle: server started");
|
||||
match ::std::panic::catch_unwind(main_inner) {
|
||||
Ok(res) => {
|
||||
|
@ -36,7 +31,7 @@ fn main() -> Result<()> {
|
|||
|
||||
fn main_inner() -> Result<()> {
|
||||
let (receiver, sender, threads) = stdio_transport();
|
||||
let cwd = ::std::env::current_dir()?;
|
||||
let cwd = std::env::current_dir()?;
|
||||
run_server(ra_lsp_server::server_capabilities(), receiver, sender, |params, r, s| {
|
||||
let root = params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd);
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ use ra_ide_api::{
|
|||
AssistId,
|
||||
};
|
||||
use ra_syntax::{AstNode, SyntaxKind, TextUnit};
|
||||
use ra_prof::profile;
|
||||
use rustc_hash::FxHashMap;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde_json::to_value;
|
||||
|
@ -328,6 +329,7 @@ pub fn handle_completion(
|
|||
world: ServerWorld,
|
||||
params: req::CompletionParams,
|
||||
) -> Result<Option<req::CompletionResponse>> {
|
||||
let _p = profile("handle_completion");
|
||||
let position = {
|
||||
let file_id = params.text_document.try_conv_with(&world)?;
|
||||
let line_index = world.analysis().file_line_index(file_id);
|
||||
|
@ -564,6 +566,7 @@ pub fn handle_code_action(
|
|||
world: ServerWorld,
|
||||
params: req::CodeActionParams,
|
||||
) -> Result<Option<CodeActionResponse>> {
|
||||
let _p = profile("handle_code_action");
|
||||
let file_id = params.text_document.try_conv_with(&world)?;
|
||||
let line_index = world.analysis().file_line_index(file_id);
|
||||
let range = params.range.conv_with(&line_index);
|
||||
|
|
|
@ -6,7 +6,7 @@ use std::iter::repeat;
|
|||
use std::collections::{HashSet};
|
||||
use std::default::Default;
|
||||
use std::iter::FromIterator;
|
||||
use std::sync::RwLock;
|
||||
use std::sync::{RwLock, atomic::{AtomicBool, Ordering}};
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
/// Set profiling filter. It specifies descriptions allowed to profile.
|
||||
|
@ -15,18 +15,20 @@ use lazy_static::lazy_static;
|
|||
///
|
||||
/// #Example
|
||||
/// ```
|
||||
/// use ra_prof::set_filter;
|
||||
/// use ra_prof::Filter;
|
||||
/// let max_depth = 2;
|
||||
/// let allowed = vec!["profile1".to_string(), "profile2".to_string()];
|
||||
/// let f = Filter::new( max_depth, allowed );
|
||||
/// use ra_prof::{set_filter, Filter};
|
||||
/// let f = Filter::from_spec("profile1|profile2@2");
|
||||
/// set_filter(f);
|
||||
/// ```
|
||||
///
|
||||
pub fn set_filter(f: Filter) {
|
||||
PROFILING_ENABLED.store(f.depth > 0, Ordering::SeqCst);
|
||||
let set = HashSet::from_iter(f.allowed.iter().cloned());
|
||||
let mut old = FILTER.write().unwrap();
|
||||
let filter_data = FilterData { depth: f.depth, allowed: set, version: old.version + 1 };
|
||||
let filter_data = FilterData {
|
||||
depth: f.depth,
|
||||
allowed: set,
|
||||
longer_than: f.longer_than,
|
||||
version: old.version + 1,
|
||||
};
|
||||
*old = filter_data;
|
||||
}
|
||||
|
||||
|
@ -37,12 +39,9 @@ pub fn set_filter(f: Filter) {
|
|||
///
|
||||
/// #Example
|
||||
/// ```
|
||||
/// use ra_prof::profile;
|
||||
/// use ra_prof::set_filter;
|
||||
/// use ra_prof::Filter;
|
||||
/// use ra_prof::{profile, set_filter, Filter};
|
||||
///
|
||||
/// let allowed = vec!["profile1".to_string(), "profile2".to_string()];
|
||||
/// let f = Filter::new(2, allowed);
|
||||
/// let f = Filter::from_spec("profile1|profile2@2");
|
||||
/// set_filter(f);
|
||||
/// profiling_function1();
|
||||
///
|
||||
|
@ -60,8 +59,12 @@ pub fn set_filter(f: Filter) {
|
|||
/// 0ms - profile
|
||||
/// 0ms - profile2
|
||||
/// ```
|
||||
///
|
||||
pub fn profile(desc: &str) -> Profiler {
|
||||
assert!(!desc.is_empty());
|
||||
if !PROFILING_ENABLED.load(Ordering::Relaxed) {
|
||||
return Profiler { desc: None };
|
||||
}
|
||||
|
||||
PROFILE_STACK.with(|stack| {
|
||||
let mut stack = stack.borrow_mut();
|
||||
if stack.starts.len() == 0 {
|
||||
|
@ -74,14 +77,14 @@ pub fn profile(desc: &str) -> Profiler {
|
|||
Err(_) => (),
|
||||
};
|
||||
}
|
||||
let desc_str = desc.to_string();
|
||||
if desc_str.is_empty() {
|
||||
Profiler { desc: None }
|
||||
} else if stack.starts.len() < stack.filter_data.depth
|
||||
&& stack.filter_data.allowed.contains(&desc_str)
|
||||
{
|
||||
|
||||
if stack.starts.len() > stack.filter_data.depth {
|
||||
return Profiler { desc: None };
|
||||
}
|
||||
|
||||
if stack.filter_data.allowed.is_empty() || stack.filter_data.allowed.contains(desc) {
|
||||
stack.starts.push(Instant::now());
|
||||
Profiler { desc: Some(desc_str) }
|
||||
Profiler { desc: Some(desc.to_string()) }
|
||||
} else {
|
||||
Profiler { desc: None }
|
||||
}
|
||||
|
@ -95,11 +98,41 @@ pub struct Profiler {
|
|||
pub struct Filter {
|
||||
depth: usize,
|
||||
allowed: Vec<String>,
|
||||
longer_than: Duration,
|
||||
}
|
||||
|
||||
impl Filter {
|
||||
pub fn new(depth: usize, allowed: Vec<String>) -> Filter {
|
||||
Filter { depth, allowed }
|
||||
// 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 {
|
||||
let longer_than = if let Some(idx) = spec.rfind(">") {
|
||||
let longer_than = spec[idx + 1..].parse().expect("invalid profile longer_than");
|
||||
spec = &spec[..idx];
|
||||
Duration::from_millis(longer_than)
|
||||
} else {
|
||||
Duration::new(0, 0)
|
||||
};
|
||||
|
||||
let depth = if let Some(idx) = spec.rfind("@") {
|
||||
let depth: usize = spec[idx + 1..].parse().expect("invalid profile depth");
|
||||
spec = &spec[..idx];
|
||||
depth
|
||||
} else {
|
||||
999
|
||||
};
|
||||
let allowed =
|
||||
if spec == "*" { Vec::new() } else { spec.split("|").map(String::from).collect() };
|
||||
Filter::new(depth, allowed, longer_than)
|
||||
}
|
||||
|
||||
pub fn disabled() -> Filter {
|
||||
Filter::new(0, Vec::new(), Duration::new(0, 0))
|
||||
}
|
||||
|
||||
pub fn new(depth: usize, allowed: Vec<String>, longer_than: Duration) -> Filter {
|
||||
Filter { depth, allowed, longer_than }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,8 +159,11 @@ struct FilterData {
|
|||
depth: usize,
|
||||
version: usize,
|
||||
allowed: HashSet<String>,
|
||||
longer_than: Duration,
|
||||
}
|
||||
|
||||
static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
lazy_static! {
|
||||
static ref FILTER: RwLock<FilterData> = RwLock::new(Default::default());
|
||||
}
|
||||
|
@ -147,7 +183,9 @@ impl Drop for Profiler {
|
|||
stack.messages.push(Message { level, duration, message });
|
||||
if level == 0 {
|
||||
let stdout = stderr();
|
||||
print(0, &stack.messages, &mut stdout.lock());
|
||||
if duration >= stack.filter_data.longer_than {
|
||||
print(0, &stack.messages, &mut stdout.lock());
|
||||
}
|
||||
stack.messages.clear();
|
||||
}
|
||||
});
|
||||
|
@ -174,15 +212,12 @@ fn print(lvl: usize, msgs: &[Message], out: &mut impl Write) {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::profile;
|
||||
use super::set_filter;
|
||||
use super::Filter;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_basic_profile() {
|
||||
let s = vec!["profile1".to_string(), "profile2".to_string()];
|
||||
let f = Filter::new(2, s);
|
||||
let f = Filter::new(2, s, Duration::new(0, 0));
|
||||
set_filter(f);
|
||||
profiling_function1();
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ figure out where logs go.
|
|||
Inside rust-analyzer, we use the standard `log` crate for logging, and
|
||||
`flexi_logger` for logging frotend. By default, log goes to stderr (the same as
|
||||
with `env_logger`), but the stderr itself is processed by VS Code. To mirror
|
||||
logs to a `./log` directory, set `RA_INTERNAL_MODE=1` environmental variable.
|
||||
logs to a `./log` directory, set `RA_LOG_DIR=1` environmental variable.
|
||||
|
||||
To see stderr in the running VS Code instance, go to the "Output" tab of the
|
||||
panel and select `rust-analyzer`. This shows `eprintln!` as well. Note that
|
||||
|
@ -135,3 +135,15 @@ There's also two VS Code commands which might be of interest:
|
|||
There's an alias for this: `cargo jinstall-lsp`.
|
||||
|
||||
* `Rust Analyzer: Syntax Tree` shows syntax tree of the current file/selection.
|
||||
|
||||
# Profiling
|
||||
|
||||
We have a built-in hierarchical profiler, you can enable it by using `RA_PROF` env-var:
|
||||
|
||||
```
|
||||
RA_PROFILE=* // dump everything
|
||||
RA_PROFILE=foo|bar|baz // enabled only selected entries
|
||||
RA_PROFILE=*@3>10 // dump everything, up to depth 3, if it takes more than 10 ms
|
||||
```
|
||||
|
||||
In particular, I have `export RA_PROFILE='*>10' in my shell profile.
|
||||
|
|
Loading…
Reference in a new issue