1use std::time::{Duration, Instant};
4use std::rc::{Rc, Weak};
5use std::cell::RefCell;
6
7
8thread_local! {
9 static STACK: RefCell<Stack> = RefCell::new(Stack::new());
10}
11
12static mut ENABLED: bool = false;
13
14struct Stack {
25 thread_name: String,
26 root_frame: Rc<RefCell<Frame>>,
27 current_frame: Rc<RefCell<Frame>>,
28}
29
30impl Stack {
38
39 fn new() -> Self {
40
41 let thread = std::thread::current();
42 let root_frame = Frame::new("root", Weak::new());
43
44 Self {
50 thread_name: thread.name().unwrap_or("").to_string(),
51 root_frame: Rc::clone(&root_frame),
52 current_frame: root_frame
53 }
54
55 }
56
57 fn push(&mut self, name: &'static str) {
58 let frame = (*self.current_frame).borrow_mut().get_or_create_child(name, &self.current_frame);
59 (*frame).borrow_mut().start = Instant::now();
60 self.current_frame = frame;
61 }
62
63 fn pop(&mut self) {
64
65 let parent_frame = {
66 let mut current_frame = (*self.current_frame).borrow_mut();
67 let duration = current_frame.start.elapsed();
68 current_frame.duration_sum += duration;
69 current_frame.duration_count += 1;
70 Weak::upgrade(¤t_frame.parent)
71 };
72
73 self.current_frame = parent_frame.expect("You can't pop the root frame.");
74
75 }
76
77 fn debug(&self) {
78 println!("----< thread: {} >----", self.thread_name);
79 Self::debug_frame(&self.root_frame, 0);
80 }
81
82 fn debug_frame(frame: &Rc<RefCell<Frame>>, tab: u16) {
83
84 let guard = (**frame).borrow();
85 for _ in 0..tab {
86 print!(" ");
87 }
88
89 if guard.duration_count > 0 {
90 println!("- {} (x{}, total: {:.2?}, avg: {:.2?})", guard.name, guard.duration_count, guard.duration_sum, guard.duration_sum / guard.duration_count);
91 } else {
92 println!("- {}", guard.name);
93 }
94
95 for child in &guard.children {
96 Self::debug_frame(&child.frame, tab + 1);
97 }
98
99 }
100
101}
102
103
104struct Frame {
105 name: &'static str,
107 children: Vec<FrameChild>,
109 parent: Weak<RefCell<Frame>>,
111 start: Instant,
113 duration_sum: Duration,
115 duration_count: u32
117}
118
119struct FrameChild {
120 name: &'static str,
121 frame: Rc<RefCell<Frame>>
122}
123
124impl Frame {
125
126 fn new(name: &'static str, parent: Weak<RefCell<Frame>>) -> Rc<RefCell<Self>> {
127 Rc::new(RefCell::new(Self {
128 name,
129 children: Vec::new(),
130 parent,
131 start: Instant::now(),
132 duration_sum: Duration::from_secs(0),
133 duration_count: 0
134 }))
135 }
136
137 fn push_child(&mut self, name: &'static str, parent: &Rc<RefCell<Frame>>) -> Rc<RefCell<Frame>> {
138 let child = Self::new(name, Rc::downgrade(parent));
139 self.children.push(FrameChild {
140 name,
141 frame: Rc::clone(&child)
142 });
143 child
144 }
145
146 fn get_or_create_child(&mut self, name: &'static str, parent: &Rc<RefCell<Frame>>) -> Rc<RefCell<Frame>> {
147
148 let len = self.children.len();
152
153 let end = if len == 0 {
154 return self.push_child(name, parent);
155 } else if len > 1 {
156 let last_child = &self.children[len - 1];
157 if last_child.name == name {
158 return Rc::clone(&last_child.frame);
159 }
160 len - 1
161 } else {
162 len
163 };
164
165 let first_child = self.children[..end].iter()
167 .find(move |frame| frame.name == name);
168
169 if let Some(child) = first_child {
170 Rc::clone(&child.frame)
171 } else {
172 self.push_child(name, parent)
173 }
174
175 }
176
177}
178
179
180pub fn push(name: &'static str) {
181 if is_enabled() {
182 STACK.with(move |stack| stack.borrow_mut().push(name));
183 }
184}
185
186pub fn pop() {
187 if is_enabled() {
188 STACK.with(move |stack| stack.borrow_mut().pop());
189 }
190}
191
192pub fn pop_push(name: &'static str) {
193 if is_enabled() {
194 STACK.with(move |stack| {
195 let mut guard = stack.borrow_mut();
196 guard.pop();
197 guard.push(name);
198 });
199 }
200}
201
202pub fn frame<F: FnOnce()>(name: &'static str, func: F) {
203 if is_enabled() {
204 STACK.with(move |stack| {
205 let mut stack = stack.borrow_mut();
206 stack.push(name);
207 func();
208 stack.pop();
209 });
210 }
211}
212
213pub fn debug() {
215 STACK.with(|stack| {
216 stack.borrow().debug();
217 });
218}
219
220#[inline]
221fn is_enabled() -> bool {
222 unsafe { ENABLED }
223}
224
225#[inline]
228pub unsafe fn enable() {
229 ENABLED = true;
230}
231
232#[inline]
235pub unsafe fn disable() {
236 ENABLED = false;
237}