diff options
Diffstat (limited to 'crates/ra_prof/src/lib.rs')
-rw-r--r-- | crates/ra_prof/src/lib.rs | 153 |
1 files changed, 153 insertions, 0 deletions
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 | } | ||