Skip to main content

cargo/util/
profile.rs

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}