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 /crates/ra_prof | |
parent | e1a2649aff0a9387fb14646a56cb652061bc42ec (diff) | |
parent | 247ac265f1e7f2d812e6f9174d48ceef45465b91 (diff) |
Merge #1146
1146: Moar profiling r=matklad a=matklad
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_prof')
-rw-r--r-- | crates/ra_prof/src/lib.rs | 93 |
1 files changed, 64 insertions, 29 deletions
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 | } |