aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_prof
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_prof')
-rw-r--r--crates/ra_prof/Cargo.toml9
-rw-r--r--crates/ra_prof/src/lib.rs153
2 files changed, 162 insertions, 0 deletions
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]
2edition = "2018"
3name = "ra_prof"
4version = "0.1.0"
5authors = ["rust-analyzer developers"]
6publish = false
7
8[dependencies]
9lazy_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 @@
1use std::cell::RefCell;
2use std::time::{Duration, Instant};
3use std::mem;
4use std::io::{stderr, Write};
5use std::iter::repeat;
6use std::collections::{HashSet};
7use std::default::Default;
8use std::iter::FromIterator;
9use std::sync::RwLock;
10use lazy_static::lazy_static;
11
12pub 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
19pub 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
46pub struct Profiler {
47 desc: Option<String>,
48}
49
50pub struct Filter {
51 depth: usize,
52 allowed: Vec<String>,
53}
54
55impl Filter {
56 pub fn new(depth: usize, allowed: Vec<String>) -> Filter {
57 Filter { depth, allowed }
58 }
59}
60
61struct ProfileStack {
62 starts: Vec<Instant>,
63 messages: Vec<Message>,
64 filter_data: FilterData,
65}
66
67struct Message {
68 level: usize,
69 duration: Duration,
70 message: String,
71}
72
73impl ProfileStack {
74 fn new() -> ProfileStack {
75 ProfileStack { starts: Vec::new(), messages: Vec::new(), filter_data: Default::default() }
76 }
77}
78
79#[derive(Default, Clone)]
80struct FilterData {
81 depth: usize,
82 version: usize,
83 allowed: HashSet<String>,
84}
85
86lazy_static! {
87 static ref FILTER: RwLock<FilterData> = RwLock::new(Default::default());
88}
89
90thread_local!(static PROFILE_STACK: RefCell<ProfileStack> = RefCell::new(ProfileStack::new()));
91
92impl 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
115fn 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)]
131mod 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}