aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--crates/ra_ide_api/Cargo.toml1
-rw-r--r--crates/ra_ide_api/src/change.rs2
-rw-r--r--crates/ra_lsp_server/src/main.rs19
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs3
-rw-r--r--crates/ra_prof/src/lib.rs93
-rw-r--r--docs/dev/README.md14
7 files changed, 91 insertions, 42 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 5a5edc3e2..779580762 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -996,6 +996,7 @@ dependencies = [
996 "ra_db 0.1.0", 996 "ra_db 0.1.0",
997 "ra_fmt 0.1.0", 997 "ra_fmt 0.1.0",
998 "ra_hir 0.1.0", 998 "ra_hir 0.1.0",
999 "ra_prof 0.1.0",
999 "ra_syntax 0.1.0", 1000 "ra_syntax 0.1.0",
1000 "ra_text_edit 0.1.0", 1001 "ra_text_edit 0.1.0",
1001 "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 1002 "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/crates/ra_ide_api/Cargo.toml b/crates/ra_ide_api/Cargo.toml
index 45bab4e28..333706c1a 100644
--- a/crates/ra_ide_api/Cargo.toml
+++ b/crates/ra_ide_api/Cargo.toml
@@ -23,6 +23,7 @@ ra_syntax = { path = "../ra_syntax" }
23ra_text_edit = { path = "../ra_text_edit" } 23ra_text_edit = { path = "../ra_text_edit" }
24ra_db = { path = "../ra_db" } 24ra_db = { path = "../ra_db" }
25ra_fmt = { path = "../ra_fmt" } 25ra_fmt = { path = "../ra_fmt" }
26ra_prof = { path = "../ra_prof" }
26hir = { path = "../ra_hir", package = "ra_hir" } 27hir = { path = "../ra_hir", package = "ra_hir" }
27test_utils = { path = "../test_utils" } 28test_utils = { path = "../test_utils" }
28ra_assists = { path = "../ra_assists" } 29ra_assists = { path = "../ra_assists" }
diff --git a/crates/ra_ide_api/src/change.rs b/crates/ra_ide_api/src/change.rs
index a4a086931..5bfdbe7e9 100644
--- a/crates/ra_ide_api/src/change.rs
+++ b/crates/ra_ide_api/src/change.rs
@@ -9,6 +9,7 @@ use ra_db::{
9 salsa::{Database, SweepStrategy}, 9 salsa::{Database, SweepStrategy},
10}; 10};
11use ra_syntax::SourceFile; 11use ra_syntax::SourceFile;
12use ra_prof::profile;
12use relative_path::RelativePathBuf; 13use relative_path::RelativePathBuf;
13use rayon::prelude::*; 14use rayon::prelude::*;
14 15
@@ -153,6 +154,7 @@ const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100);
153 154
154impl RootDatabase { 155impl RootDatabase {
155 pub(crate) fn apply_change(&mut self, change: AnalysisChange) { 156 pub(crate) fn apply_change(&mut self, change: AnalysisChange) {
157 let _p = profile("RootDatabase::apply_change");
156 log::info!("apply_change {:?}", change); 158 log::info!("apply_change {:?}", change);
157 if !change.new_roots.is_empty() { 159 if !change.new_roots.is_empty() {
158 let mut local_roots = Vec::clone(&self.local_roots()); 160 let mut local_roots = Vec::clone(&self.local_roots());
diff --git a/crates/ra_lsp_server/src/main.rs b/crates/ra_lsp_server/src/main.rs
index 82f52a6e8..b0b70df5c 100644
--- a/crates/ra_lsp_server/src/main.rs
+++ b/crates/ra_lsp_server/src/main.rs
@@ -6,21 +6,16 @@ use ra_lsp_server::{Result, InitializationOptions};
6use ra_prof; 6use ra_prof;
7 7
8fn main() -> Result<()> { 8fn main() -> Result<()> {
9 ::std::env::set_var("RUST_BACKTRACE", "short"); 9 std::env::set_var("RUST_BACKTRACE", "short");
10 let logger = Logger::with_env_or_str("error").duplicate_to_stderr(Duplicate::All); 10 let logger = Logger::with_env_or_str("error").duplicate_to_stderr(Duplicate::All);
11 match ::std::env::var("RA_INTERNAL_MODE") { 11 match std::env::var("RA_LOG_DIR") {
12 Ok(ref v) if v == "1" => logger.log_to_file().directory("log").start()?, 12 Ok(ref v) if v == "1" => logger.log_to_file().directory("log").start()?,
13 _ => logger.start()?, 13 _ => logger.start()?,
14 }; 14 };
15 let prof_depth = match ::std::env::var("RA_PROFILE_DEPTH") { 15 ra_prof::set_filter(match std::env::var("RA_PROFILE") {
16 Ok(ref d) => d.parse()?, 16 Ok(spec) => ra_prof::Filter::from_spec(&spec),
17 _ => 0, 17 Err(_) => ra_prof::Filter::disabled(),
18 }; 18 });
19 let profile_allowed = match ::std::env::var("RA_PROFILE") {
20 Ok(ref p) => p.split(";").map(String::from).collect(),
21 _ => Vec::new(),
22 };
23 ra_prof::set_filter(ra_prof::Filter::new(prof_depth, profile_allowed));
24 log::info!("lifecycle: server started"); 19 log::info!("lifecycle: server started");
25 match ::std::panic::catch_unwind(main_inner) { 20 match ::std::panic::catch_unwind(main_inner) {
26 Ok(res) => { 21 Ok(res) => {
@@ -36,7 +31,7 @@ fn main() -> Result<()> {
36 31
37fn main_inner() -> Result<()> { 32fn main_inner() -> Result<()> {
38 let (receiver, sender, threads) = stdio_transport(); 33 let (receiver, sender, threads) = stdio_transport();
39 let cwd = ::std::env::current_dir()?; 34 let cwd = std::env::current_dir()?;
40 run_server(ra_lsp_server::server_capabilities(), receiver, sender, |params, r, s| { 35 run_server(ra_lsp_server::server_capabilities(), receiver, sender, |params, r, s| {
41 let root = params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd); 36 let root = params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd);
42 37
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 41d1f759f..eb8a53545 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -12,6 +12,7 @@ use ra_ide_api::{
12 AssistId, 12 AssistId,
13}; 13};
14use ra_syntax::{AstNode, SyntaxKind, TextUnit}; 14use ra_syntax::{AstNode, SyntaxKind, TextUnit};
15use ra_prof::profile;
15use rustc_hash::FxHashMap; 16use rustc_hash::FxHashMap;
16use serde::{Serialize, Deserialize}; 17use serde::{Serialize, Deserialize};
17use serde_json::to_value; 18use serde_json::to_value;
@@ -328,6 +329,7 @@ pub fn handle_completion(
328 world: ServerWorld, 329 world: ServerWorld,
329 params: req::CompletionParams, 330 params: req::CompletionParams,
330) -> Result<Option<req::CompletionResponse>> { 331) -> Result<Option<req::CompletionResponse>> {
332 let _p = profile("handle_completion");
331 let position = { 333 let position = {
332 let file_id = params.text_document.try_conv_with(&world)?; 334 let file_id = params.text_document.try_conv_with(&world)?;
333 let line_index = world.analysis().file_line_index(file_id); 335 let line_index = world.analysis().file_line_index(file_id);
@@ -564,6 +566,7 @@ pub fn handle_code_action(
564 world: ServerWorld, 566 world: ServerWorld,
565 params: req::CodeActionParams, 567 params: req::CodeActionParams,
566) -> Result<Option<CodeActionResponse>> { 568) -> Result<Option<CodeActionResponse>> {
569 let _p = profile("handle_code_action");
567 let file_id = params.text_document.try_conv_with(&world)?; 570 let file_id = params.text_document.try_conv_with(&world)?;
568 let line_index = world.analysis().file_line_index(file_id); 571 let line_index = world.analysis().file_line_index(file_id);
569 let range = params.range.conv_with(&line_index); 572 let range = params.range.conv_with(&line_index);
diff --git a/crates/ra_prof/src/lib.rs b/crates/ra_prof/src/lib.rs
index 1cc8e361d..402c719b1 100644
--- a/crates/ra_prof/src/lib.rs
+++ b/crates/ra_prof/src/lib.rs
@@ -6,7 +6,7 @@ use std::iter::repeat;
6use std::collections::{HashSet}; 6use std::collections::{HashSet};
7use std::default::Default; 7use std::default::Default;
8use std::iter::FromIterator; 8use std::iter::FromIterator;
9use std::sync::RwLock; 9use std::sync::{RwLock, atomic::{AtomicBool, Ordering}};
10use lazy_static::lazy_static; 10use lazy_static::lazy_static;
11 11
12/// Set profiling filter. It specifies descriptions allowed to profile. 12/// Set profiling filter. It specifies descriptions allowed to profile.
@@ -15,18 +15,20 @@ use lazy_static::lazy_static;
15/// 15///
16/// #Example 16/// #Example
17/// ``` 17/// ```
18/// use ra_prof::set_filter; 18/// use ra_prof::{set_filter, Filter};
19/// use ra_prof::Filter; 19/// let f = Filter::from_spec("profile1|profile2@2");
20/// let max_depth = 2;
21/// let allowed = vec!["profile1".to_string(), "profile2".to_string()];
22/// let f = Filter::new( max_depth, allowed );
23/// set_filter(f); 20/// set_filter(f);
24/// ``` 21/// ```
25///
26pub fn set_filter(f: Filter) { 22pub fn set_filter(f: Filter) {
23 PROFILING_ENABLED.store(f.depth > 0, Ordering::SeqCst);
27 let set = HashSet::from_iter(f.allowed.iter().cloned()); 24 let set = HashSet::from_iter(f.allowed.iter().cloned());
28 let mut old = FILTER.write().unwrap(); 25 let mut old = FILTER.write().unwrap();
29 let filter_data = FilterData { depth: f.depth, allowed: set, version: old.version + 1 }; 26 let filter_data = FilterData {
27 depth: f.depth,
28 allowed: set,
29 longer_than: f.longer_than,
30 version: old.version + 1,
31 };
30 *old = filter_data; 32 *old = filter_data;
31} 33}
32 34
@@ -37,12 +39,9 @@ pub fn set_filter(f: Filter) {
37/// 39///
38/// #Example 40/// #Example
39/// ``` 41/// ```
40/// use ra_prof::profile; 42/// use ra_prof::{profile, set_filter, Filter};
41/// use ra_prof::set_filter;
42/// use ra_prof::Filter;
43/// 43///
44/// let allowed = vec!["profile1".to_string(), "profile2".to_string()]; 44/// let f = Filter::from_spec("profile1|profile2@2");
45/// let f = Filter::new(2, allowed);
46/// set_filter(f); 45/// set_filter(f);
47/// profiling_function1(); 46/// profiling_function1();
48/// 47///
@@ -60,8 +59,12 @@ pub fn set_filter(f: Filter) {
60/// 0ms - profile 59/// 0ms - profile
61/// 0ms - profile2 60/// 0ms - profile2
62/// ``` 61/// ```
63///
64pub fn profile(desc: &str) -> Profiler { 62pub fn profile(desc: &str) -> Profiler {
63 assert!(!desc.is_empty());
64 if !PROFILING_ENABLED.load(Ordering::Relaxed) {
65 return Profiler { desc: None };
66 }
67
65 PROFILE_STACK.with(|stack| { 68 PROFILE_STACK.with(|stack| {
66 let mut stack = stack.borrow_mut(); 69 let mut stack = stack.borrow_mut();
67 if stack.starts.len() == 0 { 70 if stack.starts.len() == 0 {
@@ -74,14 +77,14 @@ pub fn profile(desc: &str) -> Profiler {
74 Err(_) => (), 77 Err(_) => (),
75 }; 78 };
76 } 79 }
77 let desc_str = desc.to_string(); 80
78 if desc_str.is_empty() { 81 if stack.starts.len() > stack.filter_data.depth {
79 Profiler { desc: None } 82 return Profiler { desc: None };
80 } else if stack.starts.len() < stack.filter_data.depth 83 }
81 && stack.filter_data.allowed.contains(&desc_str) 84
82 { 85 if stack.filter_data.allowed.is_empty() || stack.filter_data.allowed.contains(desc) {
83 stack.starts.push(Instant::now()); 86 stack.starts.push(Instant::now());
84 Profiler { desc: Some(desc_str) } 87 Profiler { desc: Some(desc.to_string()) }
85 } else { 88 } else {
86 Profiler { desc: None } 89 Profiler { desc: None }
87 } 90 }
@@ -95,11 +98,41 @@ pub struct Profiler {
95pub struct Filter { 98pub struct Filter {
96 depth: usize, 99 depth: usize,
97 allowed: Vec<String>, 100 allowed: Vec<String>,
101 longer_than: Duration,
98} 102}
99 103
100impl Filter { 104impl Filter {
101 pub fn new(depth: usize, allowed: Vec<String>) -> Filter { 105 // Filtering syntax
102 Filter { depth, allowed } 106 // env RA_PROFILE=* // dump everything
107 // env RA_PROFILE=foo|bar|baz // enabled only selected entries
108 // env RA_PROFILE=*@3>10 // dump everything, up to depth 3, if it takes more than 10 ms
109 pub fn from_spec(mut spec: &str) -> Filter {
110 let longer_than = if let Some(idx) = spec.rfind(">") {
111 let longer_than = spec[idx + 1..].parse().expect("invalid profile longer_than");
112 spec = &spec[..idx];
113 Duration::from_millis(longer_than)
114 } else {
115 Duration::new(0, 0)
116 };
117
118 let depth = if let Some(idx) = spec.rfind("@") {
119 let depth: usize = spec[idx + 1..].parse().expect("invalid profile depth");
120 spec = &spec[..idx];
121 depth
122 } else {
123 999
124 };
125 let allowed =
126 if spec == "*" { Vec::new() } else { spec.split("|").map(String::from).collect() };
127 Filter::new(depth, allowed, longer_than)
128 }
129
130 pub fn disabled() -> Filter {
131 Filter::new(0, Vec::new(), Duration::new(0, 0))
132 }
133
134 pub fn new(depth: usize, allowed: Vec<String>, longer_than: Duration) -> Filter {
135 Filter { depth, allowed, longer_than }
103 } 136 }
104} 137}
105 138
@@ -126,8 +159,11 @@ struct FilterData {
126 depth: usize, 159 depth: usize,
127 version: usize, 160 version: usize,
128 allowed: HashSet<String>, 161 allowed: HashSet<String>,
162 longer_than: Duration,
129} 163}
130 164
165static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false);
166
131lazy_static! { 167lazy_static! {
132 static ref FILTER: RwLock<FilterData> = RwLock::new(Default::default()); 168 static ref FILTER: RwLock<FilterData> = RwLock::new(Default::default());
133} 169}
@@ -147,7 +183,9 @@ impl Drop for Profiler {
147 stack.messages.push(Message { level, duration, message }); 183 stack.messages.push(Message { level, duration, message });
148 if level == 0 { 184 if level == 0 {
149 let stdout = stderr(); 185 let stdout = stderr();
150 print(0, &stack.messages, &mut stdout.lock()); 186 if duration >= stack.filter_data.longer_than {
187 print(0, &stack.messages, &mut stdout.lock());
188 }
151 stack.messages.clear(); 189 stack.messages.clear();
152 } 190 }
153 }); 191 });
@@ -174,15 +212,12 @@ fn print(lvl: usize, msgs: &[Message], out: &mut impl Write) {
174 212
175#[cfg(test)] 213#[cfg(test)]
176mod tests { 214mod tests {
177 215 use super::*;
178 use super::profile;
179 use super::set_filter;
180 use super::Filter;
181 216
182 #[test] 217 #[test]
183 fn test_basic_profile() { 218 fn test_basic_profile() {
184 let s = vec!["profile1".to_string(), "profile2".to_string()]; 219 let s = vec!["profile1".to_string(), "profile2".to_string()];
185 let f = Filter::new(2, s); 220 let f = Filter::new(2, s, Duration::new(0, 0));
186 set_filter(f); 221 set_filter(f);
187 profiling_function1(); 222 profiling_function1();
188 } 223 }
diff --git a/docs/dev/README.md b/docs/dev/README.md
index 7bb323f3f..d34ff96c8 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -105,7 +105,7 @@ figure out where logs go.
105Inside rust-analyzer, we use the standard `log` crate for logging, and 105Inside rust-analyzer, we use the standard `log` crate for logging, and
106`flexi_logger` for logging frotend. By default, log goes to stderr (the same as 106`flexi_logger` for logging frotend. By default, log goes to stderr (the same as
107with `env_logger`), but the stderr itself is processed by VS Code. To mirror 107with `env_logger`), but the stderr itself is processed by VS Code. To mirror
108logs to a `./log` directory, set `RA_INTERNAL_MODE=1` environmental variable. 108logs to a `./log` directory, set `RA_LOG_DIR=1` environmental variable.
109 109
110To see stderr in the running VS Code instance, go to the "Output" tab of the 110To see stderr in the running VS Code instance, go to the "Output" tab of the
111panel and select `rust-analyzer`. This shows `eprintln!` as well. Note that 111panel 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:
135 There's an alias for this: `cargo jinstall-lsp`. 135 There's an alias for this: `cargo jinstall-lsp`.
136 136
137* `Rust Analyzer: Syntax Tree` shows syntax tree of the current file/selection. 137* `Rust Analyzer: Syntax Tree` shows syntax tree of the current file/selection.
138
139# Profiling
140
141We have a built-in hierarchical profiler, you can enable it by using `RA_PROF` env-var:
142
143```
144RA_PROFILE=* // dump everything
145RA_PROFILE=foo|bar|baz // enabled only selected entries
146RA_PROFILE=*@3>10 // dump everything, up to depth 3, if it takes more than 10 ms
147```
148
149In particular, I have `export RA_PROFILE='*>10' in my shell profile.