diff options
-rw-r--r-- | crates/ra_prof/src/lib.rs | 150 |
1 files changed, 68 insertions, 82 deletions
diff --git a/crates/ra_prof/src/lib.rs b/crates/ra_prof/src/lib.rs index 55ad45fb7..0107f7de9 100644 --- a/crates/ra_prof/src/lib.rs +++ b/crates/ra_prof/src/lib.rs | |||
@@ -1,22 +1,50 @@ | |||
1 | extern crate lazy_static; | ||
2 | |||
3 | use std::cell::RefCell; | 1 | use std::cell::RefCell; |
4 | use std::time; | 2 | use std::time::{Duration, Instant}; |
5 | use std::fmt; | ||
6 | use std::mem; | 3 | use std::mem; |
7 | use std::io::{stderr, StderrLock, Write}; | 4 | use std::io::{stderr, Write}; |
8 | use std::iter::repeat; | 5 | use std::iter::repeat; |
9 | use std::collections::{HashSet}; | 6 | use std::collections::{HashSet}; |
10 | use std::default::Default; | 7 | use std::default::Default; |
11 | use std::iter::FromIterator; | 8 | use std::iter::FromIterator; |
12 | use std::sync::RwLock; | 9 | use std::sync::RwLock; |
13 | |||
14 | use lazy_static::lazy_static; | 10 | use lazy_static::lazy_static; |
15 | 11 | ||
16 | type Message = (usize, u64, String); | 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 | } | ||
17 | 45 | ||
18 | pub struct Profiler { | 46 | pub struct Profiler { |
19 | desc: String, | 47 | desc: Option<String>, |
20 | } | 48 | } |
21 | 49 | ||
22 | pub struct Filter { | 50 | pub struct Filter { |
@@ -25,112 +53,70 @@ pub struct Filter { | |||
25 | } | 53 | } |
26 | 54 | ||
27 | struct ProfileStack { | 55 | struct ProfileStack { |
28 | starts: Vec<time::Instant>, | 56 | starts: Vec<Instant>, |
29 | messages: Vec<Message>, | 57 | messages: Vec<Message>, |
30 | filter_data: FilterData, | 58 | filter_data: FilterData, |
31 | } | 59 | } |
32 | 60 | ||
61 | struct Message { | ||
62 | level: usize, | ||
63 | duration: Duration, | ||
64 | message: String, | ||
65 | } | ||
66 | |||
33 | impl ProfileStack { | 67 | impl ProfileStack { |
34 | fn new() -> ProfileStack { | 68 | fn new() -> ProfileStack { |
35 | ProfileStack { starts: Vec::new(), messages: Vec::new(), filter_data: Default::default() } | 69 | ProfileStack { starts: Vec::new(), messages: Vec::new(), filter_data: Default::default() } |
36 | } | 70 | } |
37 | } | 71 | } |
38 | 72 | ||
39 | #[derive(Default)] | 73 | #[derive(Default, Clone)] |
40 | struct FilterData { | 74 | struct FilterData { |
41 | depth: usize, | 75 | depth: usize, |
42 | version: usize, | 76 | version: usize, |
43 | allowed: HashSet<String>, | 77 | allowed: HashSet<String>, |
44 | } | 78 | } |
45 | 79 | ||
46 | impl Clone for FilterData { | ||
47 | fn clone(&self) -> FilterData { | ||
48 | let set = HashSet::from_iter(self.allowed.iter().cloned()); | ||
49 | FilterData { depth: self.depth, allowed: set, version: self.version } | ||
50 | } | ||
51 | } | ||
52 | |||
53 | lazy_static! { | 80 | lazy_static! { |
54 | static ref FILTER: RwLock<FilterData> = RwLock::new(Default::default()); | 81 | static ref FILTER: RwLock<FilterData> = RwLock::new(Default::default()); |
55 | } | 82 | } |
56 | 83 | ||
57 | thread_local!(static PROFILE_STACK: RefCell<ProfileStack> = RefCell::new(ProfileStack::new())); | 84 | thread_local!(static PROFILE_STACK: RefCell<ProfileStack> = RefCell::new(ProfileStack::new())); |
58 | 85 | ||
59 | pub fn set_filter(f: Filter) { | ||
60 | let mut old = FILTER.write().unwrap(); | ||
61 | let set = HashSet::from_iter(f.allowed.iter().cloned()); | ||
62 | let filter_data = FilterData { depth: f.depth, allowed: set, version: old.version + 1 }; | ||
63 | *old = filter_data; | ||
64 | } | ||
65 | |||
66 | pub fn profile<T: fmt::Display>(desc: T) -> Profiler { | ||
67 | PROFILE_STACK.with(|stack| { | ||
68 | let mut stack = stack.borrow_mut(); | ||
69 | if stack.starts.len() == 0 { | ||
70 | match FILTER.try_read() { | ||
71 | Ok(f) => { | ||
72 | if f.version > stack.filter_data.version { | ||
73 | stack.filter_data = f.clone(); | ||
74 | } | ||
75 | } | ||
76 | Err(_) => (), | ||
77 | }; | ||
78 | } | ||
79 | let desc_str = desc.to_string(); | ||
80 | if desc_str.is_empty() { | ||
81 | Profiler { desc: desc_str } | ||
82 | } else if stack.starts.len() < stack.filter_data.depth | ||
83 | && stack.filter_data.allowed.contains(&desc_str) | ||
84 | { | ||
85 | stack.starts.push(time::Instant::now()); | ||
86 | Profiler { desc: desc_str } | ||
87 | } else { | ||
88 | Profiler { desc: String::new() } | ||
89 | } | ||
90 | }) | ||
91 | } | ||
92 | |||
93 | impl Drop for Profiler { | 86 | impl Drop for Profiler { |
94 | fn drop(&mut self) { | 87 | fn drop(&mut self) { |
95 | if self.desc.is_empty() { | 88 | match self { |
96 | return; | 89 | Profiler { desc: Some(desc) } => { |
97 | } | 90 | PROFILE_STACK.with(|stack| { |
98 | PROFILE_STACK.with(|stack| { | 91 | let mut stack = stack.borrow_mut(); |
99 | let mut stack = stack.borrow_mut(); | 92 | let start = stack.starts.pop().unwrap(); |
100 | let start = stack.starts.pop().unwrap(); | 93 | let duration = start.elapsed(); |
101 | let duration = start.elapsed(); | 94 | let level = stack.starts.len(); |
102 | let duration_ms = duration.as_secs() * 1000 + u64::from(duration.subsec_millis()); | 95 | let message = mem::replace(desc, String::new()); |
103 | let stack_len = stack.starts.len(); | 96 | stack.messages.push(Message { level, duration, message }); |
104 | let msg = (stack_len, duration_ms, mem::replace(&mut self.desc, String::new())); | 97 | if level == 0 { |
105 | stack.messages.push(msg); | 98 | let stdout = stderr(); |
106 | if stack_len == 0 { | 99 | print(0, &stack.messages, &mut stdout.lock()); |
107 | let stdout = stderr(); | 100 | stack.messages.clear(); |
108 | print(0, &stack.messages, 1, &mut stdout.lock()); | 101 | } |
109 | stack.messages.clear(); | 102 | }); |
110 | } | 103 | } |
111 | }); | 104 | Profiler { desc: None } => (), |
105 | } | ||
112 | } | 106 | } |
113 | } | 107 | } |
114 | 108 | ||
115 | fn print(lvl: usize, msgs: &[Message], enabled: usize, stdout: &mut StderrLock<'_>) { | 109 | fn print(lvl: usize, msgs: &[Message], out: &mut impl Write) { |
116 | if lvl > enabled { | ||
117 | return; | ||
118 | } | ||
119 | let mut last = 0; | 110 | let mut last = 0; |
120 | for (i, &(l, time, ref msg)) in msgs.iter().enumerate() { | 111 | let indent = repeat(" ").take(lvl + 1).collect::<String>(); |
112 | for (i, &Message { level: l, duration: dur, message: ref msg }) in msgs.iter().enumerate() { | ||
121 | if l != lvl { | 113 | if l != lvl { |
122 | continue; | 114 | continue; |
123 | } | 115 | } |
124 | writeln!( | 116 | writeln!(out, "{} {:6}ms - {}", indent, dur.as_millis(), msg) |
125 | stdout, | 117 | .expect("printing profiling info to stdout"); |
126 | "{} {:6}ms - {}", | 118 | |
127 | repeat(" ").take(lvl + 1).collect::<String>(), | 119 | print(lvl + 1, &msgs[last..i], out); |
128 | time, | ||
129 | msg | ||
130 | ) | ||
131 | .expect("printing profiling info to stdout"); | ||
132 | |||
133 | print(lvl + 1, &msgs[last..i], enabled, stdout); | ||
134 | last = i; | 120 | last = i; |
135 | } | 121 | } |
136 | } | 122 | } |