aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_prof
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-04-14 23:21:12 +0100
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-04-14 23:21:12 +0100
commit546d9be2a7bf7b3942c125f922a01321aea6ad26 (patch)
tree7323905a2e0efe7fefb029c8d049987df6a6462f /crates/ra_prof
parente1a2649aff0a9387fb14646a56cb652061bc42ec (diff)
parent247ac265f1e7f2d812e6f9174d48ceef45465b91 (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.rs93
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;
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 }