1use std::cell::RefCell;
2use std::env;
3use std::fmt;
4use std::io::{stdout, StdoutLock, Write};
5use std::iter::repeat;
6use std::mem;
7use std::time;
8
9thread_local!(static PROFILE_STACK: RefCell<Vec<time::Instant>> = RefCell::new(Vec::new()));
10thread_local!(static MESSAGES: RefCell<Vec<Message>> = RefCell::new(Vec::new()));
11
12type Message = (usize, u64, String);
13
14pub struct Profiler {
15 desc: String,
16}
17
18fn enabled_level() -> Option<usize> {
19 env::var("CARGO_PROFILE").ok().and_then(|s| s.parse().ok())
20}
21
22pub fn start<T: fmt::Display>(desc: T) -> Profiler {
23 if enabled_level().is_none() {
24 return Profiler {
25 desc: String::new(),
26 };
27 }
28
29 PROFILE_STACK.with(|stack| stack.borrow_mut().push(time::Instant::now()));
30
31 Profiler {
32 desc: desc.to_string(),
33 }
34}
35
36impl Drop for Profiler {
37 fn drop(&mut self) {
38 let enabled = match enabled_level() {
39 Some(i) => i,
40 None => return,
41 };
42
43 let (start, stack_len) = PROFILE_STACK.with(|stack| {
44 let mut stack = stack.borrow_mut();
45 let start = stack.pop().unwrap();
46 (start, stack.len())
47 });
48 let duration = start.elapsed();
49 let duration_ms = duration.as_secs() * 1000 + u64::from(duration.subsec_millis());
50
51 let msg = (
52 stack_len,
53 duration_ms,
54 mem::replace(&mut self.desc, String::new()),
55 );
56 MESSAGES.with(|msgs| msgs.borrow_mut().push(msg));
57
58 if stack_len == 0 {
59 fn print(lvl: usize, msgs: &[Message], enabled: usize, stdout: &mut StdoutLock<'_>) {
60 if lvl > enabled {
61 return;
62 }
63 let mut last = 0;
64 for (i, &(l, time, ref msg)) in msgs.iter().enumerate() {
65 if l != lvl {
66 continue;
67 }
68 writeln!(
69 stdout,
70 "{} {:6}ms - {}",
71 repeat(" ").take(lvl + 1).collect::<String>(),
72 time,
73 msg
74 )
75 .expect("printing profiling info to stdout");
76
77 print(lvl + 1, &msgs[last..i], enabled, stdout);
78 last = i;
79 }
80 }
81 let stdout = stdout();
82 MESSAGES.with(|msgs| {
83 let mut msgs = msgs.borrow_mut();
84 print(0, &msgs, enabled, &mut stdout.lock());
85 msgs.clear();
86 });
87 }
88 }
89}