diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-04-14 23:21:12 +0100 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-04-14 23:21:12 +0100 |
commit | 546d9be2a7bf7b3942c125f922a01321aea6ad26 (patch) | |
tree | 7323905a2e0efe7fefb029c8d049987df6a6462f | |
parent | e1a2649aff0a9387fb14646a56cb652061bc42ec (diff) | |
parent | 247ac265f1e7f2d812e6f9174d48ceef45465b91 (diff) |
Merge #1146
1146: Moar profiling r=matklad a=matklad
Co-authored-by: Aleksey Kladov <[email protected]>
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | crates/ra_ide_api/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_ide_api/src/change.rs | 2 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main.rs | 19 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 3 | ||||
-rw-r--r-- | crates/ra_prof/src/lib.rs | 93 | ||||
-rw-r--r-- | docs/dev/README.md | 14 |
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" } | |||
23 | ra_text_edit = { path = "../ra_text_edit" } | 23 | ra_text_edit = { path = "../ra_text_edit" } |
24 | ra_db = { path = "../ra_db" } | 24 | ra_db = { path = "../ra_db" } |
25 | ra_fmt = { path = "../ra_fmt" } | 25 | ra_fmt = { path = "../ra_fmt" } |
26 | ra_prof = { path = "../ra_prof" } | ||
26 | hir = { path = "../ra_hir", package = "ra_hir" } | 27 | hir = { path = "../ra_hir", package = "ra_hir" } |
27 | test_utils = { path = "../test_utils" } | 28 | test_utils = { path = "../test_utils" } |
28 | ra_assists = { path = "../ra_assists" } | 29 | ra_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 | }; |
11 | use ra_syntax::SourceFile; | 11 | use ra_syntax::SourceFile; |
12 | use ra_prof::profile; | ||
12 | use relative_path::RelativePathBuf; | 13 | use relative_path::RelativePathBuf; |
13 | use rayon::prelude::*; | 14 | use rayon::prelude::*; |
14 | 15 | ||
@@ -153,6 +154,7 @@ const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100); | |||
153 | 154 | ||
154 | impl RootDatabase { | 155 | impl 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}; | |||
6 | use ra_prof; | 6 | use ra_prof; |
7 | 7 | ||
8 | fn main() -> Result<()> { | 8 | fn 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 | ||
37 | fn main_inner() -> Result<()> { | 32 | fn 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 | }; |
14 | use ra_syntax::{AstNode, SyntaxKind, TextUnit}; | 14 | use ra_syntax::{AstNode, SyntaxKind, TextUnit}; |
15 | use ra_prof::profile; | ||
15 | use rustc_hash::FxHashMap; | 16 | use rustc_hash::FxHashMap; |
16 | use serde::{Serialize, Deserialize}; | 17 | use serde::{Serialize, Deserialize}; |
17 | use serde_json::to_value; | 18 | use 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; | |||
6 | use std::collections::{HashSet}; | 6 | use std::collections::{HashSet}; |
7 | use std::default::Default; | 7 | use std::default::Default; |
8 | use std::iter::FromIterator; | 8 | use std::iter::FromIterator; |
9 | use std::sync::RwLock; | 9 | use std::sync::{RwLock, atomic::{AtomicBool, Ordering}}; |
10 | use lazy_static::lazy_static; | 10 | use 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 | /// | ||
26 | pub fn set_filter(f: Filter) { | 22 | pub 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 | /// | ||
64 | pub fn profile(desc: &str) -> Profiler { | 62 | pub 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 { | |||
95 | pub struct Filter { | 98 | pub struct Filter { |
96 | depth: usize, | 99 | depth: usize, |
97 | allowed: Vec<String>, | 100 | allowed: Vec<String>, |
101 | longer_than: Duration, | ||
98 | } | 102 | } |
99 | 103 | ||
100 | impl Filter { | 104 | impl 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 | ||
165 | static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false); | ||
166 | |||
131 | lazy_static! { | 167 | lazy_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)] |
176 | mod tests { | 214 | mod 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. | |||
105 | Inside rust-analyzer, we use the standard `log` crate for logging, and | 105 | Inside 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 |
107 | with `env_logger`), but the stderr itself is processed by VS Code. To mirror | 107 | with `env_logger`), but the stderr itself is processed by VS Code. To mirror |
108 | logs to a `./log` directory, set `RA_INTERNAL_MODE=1` environmental variable. | 108 | logs to a `./log` directory, set `RA_LOG_DIR=1` environmental variable. |
109 | 109 | ||
110 | To see stderr in the running VS Code instance, go to the "Output" tab of the | 110 | To see stderr in the running VS Code instance, go to the "Output" tab of the |
111 | panel and select `rust-analyzer`. This shows `eprintln!` as well. Note that | 111 | 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: | |||
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 | |||
141 | We have a built-in hierarchical profiler, you can enable it by using `RA_PROF` env-var: | ||
142 | |||
143 | ``` | ||
144 | RA_PROFILE=* // dump everything | ||
145 | RA_PROFILE=foo|bar|baz // enabled only selected entries | ||
146 | RA_PROFILE=*@3>10 // dump everything, up to depth 3, if it takes more than 10 ms | ||
147 | ``` | ||
148 | |||
149 | In particular, I have `export RA_PROFILE='*>10' in my shell profile. | ||