diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-04-03 10:09:11 +0100 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-04-03 10:09:11 +0100 |
commit | c6c88070c4f25cd3710f03b7461cb277de8d3cc5 (patch) | |
tree | cfe6ec2fb43dcfb9a7f1c5698aaac0d17b2bf78a | |
parent | b8e58d4a2f317fe300f13416858f33e860138c4d (diff) | |
parent | b74449e9952846a8ea66c3507e52c24348d6dbc9 (diff) |
Merge #1068
1068: profiling crate first draft r=matklad a=pasa
I've made this first draft for #961
Could you look at it? Is this something what you are looking for?
It has lack of tests. I can't figure out how to test stderr output in rust right now. Do you have some clues?
Additionally I'm thinking about to implement procedural macros to annotate methods with this profiler. Will it be helpful?
Co-authored-by: Sergey Parilin <[email protected]>
-rw-r--r-- | Cargo.lock | 10 | ||||
-rw-r--r-- | crates/ra_cli/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_cli/src/main.rs | 7 | ||||
-rw-r--r-- | crates/ra_hir/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres.rs | 4 | ||||
-rw-r--r-- | crates/ra_lsp_server/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main.rs | 10 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop.rs | 7 | ||||
-rw-r--r-- | crates/ra_prof/Cargo.toml | 9 | ||||
-rw-r--r-- | crates/ra_prof/src/lib.rs | 153 |
10 files changed, 193 insertions, 10 deletions
diff --git a/Cargo.lock b/Cargo.lock index ddd508b0a..06117ceaf 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -931,6 +931,7 @@ dependencies = [ | |||
931 | "ra_db 0.1.0", | 931 | "ra_db 0.1.0", |
932 | "ra_hir 0.1.0", | 932 | "ra_hir 0.1.0", |
933 | "ra_ide_api 0.1.0", | 933 | "ra_ide_api 0.1.0", |
934 | "ra_prof 0.1.0", | ||
934 | "ra_syntax 0.1.0", | 935 | "ra_syntax 0.1.0", |
935 | "tools 0.1.0", | 936 | "tools 0.1.0", |
936 | ] | 937 | ] |
@@ -970,6 +971,7 @@ dependencies = [ | |||
970 | "ra_arena 0.1.0", | 971 | "ra_arena 0.1.0", |
971 | "ra_db 0.1.0", | 972 | "ra_db 0.1.0", |
972 | "ra_mbe 0.1.0", | 973 | "ra_mbe 0.1.0", |
974 | "ra_prof 0.1.0", | ||
973 | "ra_syntax 0.1.0", | 975 | "ra_syntax 0.1.0", |
974 | "ra_tt 0.1.0", | 976 | "ra_tt 0.1.0", |
975 | "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | 977 | "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", |
@@ -1018,6 +1020,7 @@ dependencies = [ | |||
1018 | "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", | 1020 | "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", |
1019 | "ra_arena 0.1.0", | 1021 | "ra_arena 0.1.0", |
1020 | "ra_ide_api 0.1.0", | 1022 | "ra_ide_api 0.1.0", |
1023 | "ra_prof 0.1.0", | ||
1021 | "ra_project_model 0.1.0", | 1024 | "ra_project_model 0.1.0", |
1022 | "ra_syntax 0.1.0", | 1025 | "ra_syntax 0.1.0", |
1023 | "ra_text_edit 0.1.0", | 1026 | "ra_text_edit 0.1.0", |
@@ -1051,6 +1054,13 @@ dependencies = [ | |||
1051 | ] | 1054 | ] |
1052 | 1055 | ||
1053 | [[package]] | 1056 | [[package]] |
1057 | name = "ra_prof" | ||
1058 | version = "0.1.0" | ||
1059 | dependencies = [ | ||
1060 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
1061 | ] | ||
1062 | |||
1063 | [[package]] | ||
1054 | name = "ra_project_model" | 1064 | name = "ra_project_model" |
1055 | version = "0.1.0" | 1065 | version = "0.1.0" |
1056 | dependencies = [ | 1066 | dependencies = [ |
diff --git a/crates/ra_cli/Cargo.toml b/crates/ra_cli/Cargo.toml index 467628236..328b2436f 100644 --- a/crates/ra_cli/Cargo.toml +++ b/crates/ra_cli/Cargo.toml | |||
@@ -18,3 +18,4 @@ tools = { path = "../tools" } | |||
18 | ra_batch = { path = "../ra_batch" } | 18 | ra_batch = { path = "../ra_batch" } |
19 | ra_hir = { path = "../ra_hir" } | 19 | ra_hir = { path = "../ra_hir" } |
20 | ra_db = { path = "../ra_db" } | 20 | ra_db = { path = "../ra_db" } |
21 | ra_prof = { path = "../ra_prof" } | ||
diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs index 1f2750d89..45555be6e 100644 --- a/crates/ra_cli/src/main.rs +++ b/crates/ra_cli/src/main.rs | |||
@@ -1,12 +1,13 @@ | |||
1 | mod analysis_stats; | 1 | mod analysis_stats; |
2 | 2 | ||
3 | use std::{fs, io::Read, path::Path, time::Instant}; | 3 | use std::{fs, io::Read, path::Path}; |
4 | 4 | ||
5 | use clap::{App, Arg, SubCommand}; | 5 | use clap::{App, Arg, SubCommand}; |
6 | use ra_ide_api::file_structure; | 6 | use ra_ide_api::file_structure; |
7 | use ra_syntax::{SourceFile, TreeArc, AstNode}; | 7 | use ra_syntax::{SourceFile, TreeArc, AstNode}; |
8 | use tools::collect_tests; | 8 | use tools::collect_tests; |
9 | use flexi_logger::Logger; | 9 | use flexi_logger::Logger; |
10 | use ra_prof::profile; | ||
10 | 11 | ||
11 | type Result<T> = ::std::result::Result<T, failure::Error>; | 12 | type Result<T> = ::std::result::Result<T, failure::Error>; |
12 | 13 | ||
@@ -27,13 +28,11 @@ fn main() -> Result<()> { | |||
27 | .get_matches(); | 28 | .get_matches(); |
28 | match matches.subcommand() { | 29 | match matches.subcommand() { |
29 | ("parse", Some(matches)) => { | 30 | ("parse", Some(matches)) => { |
30 | let start = Instant::now(); | 31 | let _p = profile("parsing"); |
31 | let file = file()?; | 32 | let file = file()?; |
32 | let elapsed = start.elapsed(); | ||
33 | if !matches.is_present("no-dump") { | 33 | if !matches.is_present("no-dump") { |
34 | println!("{}", file.syntax().debug_dump()); | 34 | println!("{}", file.syntax().debug_dump()); |
35 | } | 35 | } |
36 | eprintln!("parsing: {:?}", elapsed); | ||
37 | ::std::mem::forget(file); | 36 | ::std::mem::forget(file); |
38 | } | 37 | } |
39 | ("symbols", _) => { | 38 | ("symbols", _) => { |
diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml index 501308acc..a2858dad9 100644 --- a/crates/ra_hir/Cargo.toml +++ b/crates/ra_hir/Cargo.toml | |||
@@ -19,6 +19,7 @@ ra_db = { path = "../ra_db" } | |||
19 | mbe = { path = "../ra_mbe", package = "ra_mbe" } | 19 | mbe = { path = "../ra_mbe", package = "ra_mbe" } |
20 | tt = { path = "../ra_tt", package = "ra_tt" } | 20 | tt = { path = "../ra_tt", package = "ra_tt" } |
21 | test_utils = { path = "../test_utils" } | 21 | test_utils = { path = "../test_utils" } |
22 | ra_prof = {path = "../ra_prof" } | ||
22 | 23 | ||
23 | [dev-dependencies] | 24 | [dev-dependencies] |
24 | flexi_logger = "0.11.0" | 25 | flexi_logger = "0.11.0" |
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 6f049acfc..4ae04514a 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs | |||
@@ -60,6 +60,7 @@ use ra_arena::{Arena, RawId, impl_arena_id}; | |||
60 | use ra_db::{FileId, Edition}; | 60 | use ra_db::{FileId, Edition}; |
61 | use test_utils::tested_by; | 61 | use test_utils::tested_by; |
62 | use ra_syntax::ast; | 62 | use ra_syntax::ast; |
63 | use ra_prof::profile; | ||
63 | 64 | ||
64 | use crate::{ | 65 | use crate::{ |
65 | ModuleDef, Name, Crate, Module, | 66 | ModuleDef, Name, Crate, Module, |
@@ -181,7 +182,7 @@ enum ReachedFixedPoint { | |||
181 | 182 | ||
182 | impl CrateDefMap { | 183 | impl CrateDefMap { |
183 | pub(crate) fn crate_def_map_query(db: &impl DefDatabase, krate: Crate) -> Arc<CrateDefMap> { | 184 | pub(crate) fn crate_def_map_query(db: &impl DefDatabase, krate: Crate) -> Arc<CrateDefMap> { |
184 | let start = std::time::Instant::now(); | 185 | let _p = profile("crate_def_map_query"); |
185 | let def_map = { | 186 | let def_map = { |
186 | let edition = krate.edition(db); | 187 | let edition = krate.edition(db); |
187 | let mut modules: Arena<CrateModuleId, ModuleData> = Arena::default(); | 188 | let mut modules: Arena<CrateModuleId, ModuleData> = Arena::default(); |
@@ -198,7 +199,6 @@ impl CrateDefMap { | |||
198 | } | 199 | } |
199 | }; | 200 | }; |
200 | let def_map = collector::collect_defs(db, def_map); | 201 | let def_map = collector::collect_defs(db, def_map); |
201 | log::info!("crate_def_map_query: {:?}", start.elapsed()); | ||
202 | Arc::new(def_map) | 202 | Arc::new(def_map) |
203 | } | 203 | } |
204 | 204 | ||
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml index d82410700..bc181e4eb 100644 --- a/crates/ra_lsp_server/Cargo.toml +++ b/crates/ra_lsp_server/Cargo.toml | |||
@@ -27,6 +27,7 @@ ra_ide_api = { path = "../ra_ide_api" } | |||
27 | ra_arena = { path = "../ra_arena" } | 27 | ra_arena = { path = "../ra_arena" } |
28 | gen_lsp_server = { path = "../gen_lsp_server" } | 28 | gen_lsp_server = { path = "../gen_lsp_server" } |
29 | ra_project_model = { path = "../ra_project_model" } | 29 | ra_project_model = { path = "../ra_project_model" } |
30 | ra_prof = { path = "../ra_prof" } | ||
30 | 31 | ||
31 | [dev-dependencies] | 32 | [dev-dependencies] |
32 | tempfile = "3" | 33 | tempfile = "3" |
diff --git a/crates/ra_lsp_server/src/main.rs b/crates/ra_lsp_server/src/main.rs index 5a2905207..eb4091a3d 100644 --- a/crates/ra_lsp_server/src/main.rs +++ b/crates/ra_lsp_server/src/main.rs | |||
@@ -3,6 +3,7 @@ use flexi_logger::{Duplicate, Logger}; | |||
3 | use gen_lsp_server::{run_server, stdio_transport}; | 3 | use gen_lsp_server::{run_server, stdio_transport}; |
4 | 4 | ||
5 | use ra_lsp_server::{Result, InitializationOptions}; | 5 | use ra_lsp_server::{Result, InitializationOptions}; |
6 | use ra_prof; | ||
6 | 7 | ||
7 | fn main() -> Result<()> { | 8 | fn main() -> Result<()> { |
8 | ::std::env::set_var("RUST_BACKTRACE", "short"); | 9 | ::std::env::set_var("RUST_BACKTRACE", "short"); |
@@ -11,6 +12,15 @@ fn main() -> Result<()> { | |||
11 | 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()?, |
12 | _ => logger.start()?, | 13 | _ => logger.start()?, |
13 | }; | 14 | }; |
15 | let prof_depth = match ::std::env::var("RA_PROFILE_DEPTH") { | ||
16 | Ok(ref d) => d.parse()?, | ||
17 | _ => 0, | ||
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)); | ||
14 | log::info!("lifecycle: server started"); | 24 | log::info!("lifecycle: server started"); |
15 | match ::std::panic::catch_unwind(main_inner) { | 25 | match ::std::panic::catch_unwind(main_inner) { |
16 | Ok(res) => { | 26 | Ok(res) => { |
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index eecf278a8..82410bee3 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs | |||
@@ -24,6 +24,7 @@ use crate::{ | |||
24 | Result, | 24 | Result, |
25 | InitializationOptions, | 25 | InitializationOptions, |
26 | }; | 26 | }; |
27 | use ra_prof::profile; | ||
27 | 28 | ||
28 | #[derive(Debug, Fail)] | 29 | #[derive(Debug, Fail)] |
29 | #[fail(display = "Language Server request failed with {}. ({})", code, message)] | 30 | #[fail(display = "Language Server request failed with {}. ({})", code, message)] |
@@ -181,7 +182,7 @@ fn main_loop_inner( | |||
181 | recv(libdata_receiver) -> data => Event::Lib(data.unwrap()) | 182 | recv(libdata_receiver) -> data => Event::Lib(data.unwrap()) |
182 | }; | 183 | }; |
183 | log::info!("loop_turn = {:?}", event); | 184 | log::info!("loop_turn = {:?}", event); |
184 | let start = std::time::Instant::now(); | 185 | let _p = profile("loop_turn"); |
185 | let mut state_changed = false; | 186 | let mut state_changed = false; |
186 | match event { | 187 | match event { |
187 | Event::Task(task) => on_task(task, msg_sender, pending_requests), | 188 | Event::Task(task) => on_task(task, msg_sender, pending_requests), |
@@ -235,10 +236,9 @@ fn main_loop_inner( | |||
235 | in_flight_libraries += 1; | 236 | in_flight_libraries += 1; |
236 | let sender = libdata_sender.clone(); | 237 | let sender = libdata_sender.clone(); |
237 | pool.execute(move || { | 238 | pool.execute(move || { |
238 | let start = ::std::time::Instant::now(); | ||
239 | log::info!("indexing {:?} ... ", root); | 239 | log::info!("indexing {:?} ... ", root); |
240 | let _p = profile(&format!("indexed {:?}", root)); | ||
240 | let data = LibraryData::prepare(root, files); | 241 | let data = LibraryData::prepare(root, files); |
241 | log::info!("indexed {:?} {:?}", start.elapsed(), root); | ||
242 | sender.send(data).unwrap(); | 242 | sender.send(data).unwrap(); |
243 | }); | 243 | }); |
244 | } | 244 | } |
@@ -266,7 +266,6 @@ fn main_loop_inner( | |||
266 | subs.subscriptions(), | 266 | subs.subscriptions(), |
267 | ) | 267 | ) |
268 | } | 268 | } |
269 | log::info!("loop_turn = {:?}", start.elapsed()); | ||
270 | } | 269 | } |
271 | } | 270 | } |
272 | 271 | ||
diff --git a/crates/ra_prof/Cargo.toml b/crates/ra_prof/Cargo.toml new file mode 100644 index 000000000..19ce21783 --- /dev/null +++ b/crates/ra_prof/Cargo.toml | |||
@@ -0,0 +1,9 @@ | |||
1 | [package] | ||
2 | edition = "2018" | ||
3 | name = "ra_prof" | ||
4 | version = "0.1.0" | ||
5 | authors = ["rust-analyzer developers"] | ||
6 | publish = false | ||
7 | |||
8 | [dependencies] | ||
9 | lazy_static = "1.3.0" \ No newline at end of file | ||
diff --git a/crates/ra_prof/src/lib.rs b/crates/ra_prof/src/lib.rs new file mode 100644 index 000000000..abddff960 --- /dev/null +++ b/crates/ra_prof/src/lib.rs | |||
@@ -0,0 +1,153 @@ | |||
1 | use std::cell::RefCell; | ||
2 | use std::time::{Duration, Instant}; | ||
3 | use std::mem; | ||
4 | use std::io::{stderr, Write}; | ||
5 | use std::iter::repeat; | ||
6 | use std::collections::{HashSet}; | ||
7 | use std::default::Default; | ||
8 | use std::iter::FromIterator; | ||
9 | use std::sync::RwLock; | ||
10 | use lazy_static::lazy_static; | ||
11 | |||
12 | pub fn set_filter(f: Filter) { | ||
13 | let set = HashSet::from_iter(f.allowed.iter().cloned()); | ||
14 | let mut old = FILTER.write().unwrap(); | ||
15 | let filter_data = FilterData { depth: f.depth, allowed: set, version: old.version + 1 }; | ||
16 | *old = filter_data; | ||
17 | } | ||
18 | |||
19 | pub fn profile(desc: &str) -> Profiler { | ||
20 | PROFILE_STACK.with(|stack| { | ||
21 | let mut stack = stack.borrow_mut(); | ||
22 | if stack.starts.len() == 0 { | ||
23 | match FILTER.try_read() { | ||
24 | Ok(f) => { | ||
25 | if f.version > stack.filter_data.version { | ||
26 | stack.filter_data = f.clone(); | ||
27 | } | ||
28 | } | ||
29 | Err(_) => (), | ||
30 | }; | ||
31 | } | ||
32 | let desc_str = desc.to_string(); | ||
33 | if desc_str.is_empty() { | ||
34 | Profiler { desc: None } | ||
35 | } else if stack.starts.len() < stack.filter_data.depth | ||
36 | && stack.filter_data.allowed.contains(&desc_str) | ||
37 | { | ||
38 | stack.starts.push(Instant::now()); | ||
39 | Profiler { desc: Some(desc_str) } | ||
40 | } else { | ||
41 | Profiler { desc: None } | ||
42 | } | ||
43 | }) | ||
44 | } | ||
45 | |||
46 | pub struct Profiler { | ||
47 | desc: Option<String>, | ||
48 | } | ||
49 | |||
50 | pub struct Filter { | ||
51 | depth: usize, | ||
52 | allowed: Vec<String>, | ||
53 | } | ||
54 | |||
55 | impl Filter { | ||
56 | pub fn new(depth: usize, allowed: Vec<String>) -> Filter { | ||
57 | Filter { depth, allowed } | ||
58 | } | ||
59 | } | ||
60 | |||
61 | struct ProfileStack { | ||
62 | starts: Vec<Instant>, | ||
63 | messages: Vec<Message>, | ||
64 | filter_data: FilterData, | ||
65 | } | ||
66 | |||
67 | struct Message { | ||
68 | level: usize, | ||
69 | duration: Duration, | ||
70 | message: String, | ||
71 | } | ||
72 | |||
73 | impl ProfileStack { | ||
74 | fn new() -> ProfileStack { | ||
75 | ProfileStack { starts: Vec::new(), messages: Vec::new(), filter_data: Default::default() } | ||
76 | } | ||
77 | } | ||
78 | |||
79 | #[derive(Default, Clone)] | ||
80 | struct FilterData { | ||
81 | depth: usize, | ||
82 | version: usize, | ||
83 | allowed: HashSet<String>, | ||
84 | } | ||
85 | |||
86 | lazy_static! { | ||
87 | static ref FILTER: RwLock<FilterData> = RwLock::new(Default::default()); | ||
88 | } | ||
89 | |||
90 | thread_local!(static PROFILE_STACK: RefCell<ProfileStack> = RefCell::new(ProfileStack::new())); | ||
91 | |||
92 | impl Drop for Profiler { | ||
93 | fn drop(&mut self) { | ||
94 | match self { | ||
95 | Profiler { desc: Some(desc) } => { | ||
96 | PROFILE_STACK.with(|stack| { | ||
97 | let mut stack = stack.borrow_mut(); | ||
98 | let start = stack.starts.pop().unwrap(); | ||
99 | let duration = start.elapsed(); | ||
100 | let level = stack.starts.len(); | ||
101 | let message = mem::replace(desc, String::new()); | ||
102 | stack.messages.push(Message { level, duration, message }); | ||
103 | if level == 0 { | ||
104 | let stdout = stderr(); | ||
105 | print(0, &stack.messages, &mut stdout.lock()); | ||
106 | stack.messages.clear(); | ||
107 | } | ||
108 | }); | ||
109 | } | ||
110 | Profiler { desc: None } => (), | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | |||
115 | fn print(lvl: usize, msgs: &[Message], out: &mut impl Write) { | ||
116 | let mut last = 0; | ||
117 | let indent = repeat(" ").take(lvl + 1).collect::<String>(); | ||
118 | for (i, &Message { level: l, duration: dur, message: ref msg }) in msgs.iter().enumerate() { | ||
119 | if l != lvl { | ||
120 | continue; | ||
121 | } | ||
122 | writeln!(out, "{} {:6}ms - {}", indent, dur.as_millis(), msg) | ||
123 | .expect("printing profiling info to stdout"); | ||
124 | |||
125 | print(lvl + 1, &msgs[last..i], out); | ||
126 | last = i; | ||
127 | } | ||
128 | } | ||
129 | |||
130 | #[cfg(test)] | ||
131 | mod tests { | ||
132 | |||
133 | use super::profile; | ||
134 | use super::set_filter; | ||
135 | use super::Filter; | ||
136 | |||
137 | #[test] | ||
138 | fn test_basic_profile() { | ||
139 | let s = vec!["profile1".to_string(), "profile2".to_string()]; | ||
140 | let f = Filter { depth: 2, allowed: s }; | ||
141 | set_filter(f); | ||
142 | profiling_function1(); | ||
143 | } | ||
144 | |||
145 | fn profiling_function1() { | ||
146 | let _p = profile("profile1"); | ||
147 | profiling_function2(); | ||
148 | } | ||
149 | |||
150 | fn profiling_function2() { | ||
151 | let _p = profile("profile2"); | ||
152 | } | ||
153 | } | ||