1use crate::profiling::entries::{MemoryEntry, TimingEntry};
4use crate::profiling::memory::MemoryTracker;
5use crate::profiling::timer::Timer;
6use std::collections::HashMap;
7use std::sync::Mutex;
8use std::time::{Duration, Instant};
9
10#[derive(Debug)]
12pub struct Profiler {
13 timings: HashMap<String, TimingEntry>,
15 memory: HashMap<String, MemoryEntry>,
17 active_timers: HashMap<String, Instant>,
19 running: bool,
21}
22
23impl Profiler {
24 pub fn new() -> Self {
26 Self {
27 timings: HashMap::new(),
28 memory: HashMap::new(),
29 active_timers: HashMap::new(),
30 running: false,
31 }
32 }
33
34 pub fn global() -> &'static Mutex<Profiler> {
36 static GLOBAL_PROFILER: once_cell::sync::Lazy<Mutex<Profiler>> =
37 once_cell::sync::Lazy::new(|| Mutex::new(Profiler::new()));
38 &GLOBAL_PROFILER
39 }
40
41 pub fn start(&mut self) {
43 self.running = true;
44 self.timings.clear();
45 self.memory.clear();
46 self.active_timers.clear();
47 }
48
49 pub fn stop(&mut self) {
51 self.running = false;
52 }
53
54 pub fn reset(&mut self) {
56 self.timings.clear();
57 self.memory.clear();
58 self.active_timers.clear();
59 }
60
61 pub fn register_timer_start(&mut self, timer: &Timer) {
63 if !self.running {
64 return;
65 }
66
67 self.active_timers
68 .insert(timer.name().to_string(), timer.start_time());
69
70 if let Some(parent) = timer.parent() {
72 if let Some(entry) = self.timings.get_mut(parent) {
73 entry.add_child(timer.name());
74 }
75 }
76 }
77
78 pub fn register_timer_stop(&mut self, name: &str, duration: Duration, parent: Option<&str>) {
80 if !self.running {
81 return;
82 }
83
84 self.active_timers.remove(name);
86
87 match self.timings.get_mut(name) {
89 Some(entry) => {
90 entry.add_measurement(duration);
91 }
92 None => {
93 let entry = TimingEntry::new(duration, parent);
94 self.timings.insert(name.to_string(), entry);
95 }
96 }
97
98 if let Some(parent) = parent {
100 if let Some(entry) = self.timings.get_mut(parent) {
101 entry.add_child(name);
102 }
103 }
104 }
105
106 pub fn register_memory_tracker_start(&mut self, _tracker: &MemoryTracker) {
108 if !self.running {
109 }
111 }
112
113 pub fn register_memory_tracker_stop(&mut self, name: &str, delta: usize) {
115 if !self.running {
116 return;
117 }
118
119 match self.memory.get_mut(name) {
121 Some(entry) => {
122 entry.add_measurement(delta);
123 }
124 None => {
125 let entry = MemoryEntry::new(delta);
126 self.memory.insert(name.to_string(), entry);
127 }
128 }
129 }
130
131 pub fn print_report(&self) {
133 if self.timings.is_empty() && self.memory.is_empty() {
134 println!("No profiling data collected.");
135 return;
136 }
137
138 if !self.timings.is_empty() {
139 println!("\n=== Timing Report ===");
140 println!(
141 "{:<30} {:<10} {:<15} {:<15} {:<15}",
142 "Operation", "Calls", "Total (ms)", "Average (ms)", "Max (ms)"
143 );
144 println!("{}", "-".repeat(90));
145
146 let mut entries: Vec<(&String, &TimingEntry)> = self.timings.iter().collect();
148 entries.sort_by(|a, b| b.1.total_duration().cmp(&a.1.total_duration()));
149
150 for (name, entry) in entries {
151 println!(
152 "{:<30} {:<10} {:<15.2} {:<15.2} {:<15.2}",
153 name,
154 entry.calls(),
155 entry.total_duration().as_secs_f64() * 1000.0,
156 entry.average_duration().as_secs_f64() * 1000.0,
157 entry.max_duration().as_secs_f64() * 1000.0
158 );
159 }
160 }
161
162 if !self.memory.is_empty() {
163 println!("\n=== Memory Report ===");
164 println!(
165 "{:<30} {:<10} {:<15} {:<15}",
166 "Operation", "Counts", "Total (KB)", "Max (KB)"
167 );
168 println!("{}", "-".repeat(75));
169
170 let mut entries: Vec<(&String, &MemoryEntry)> = self.memory.iter().collect();
172 entries.sort_by(|a, b| b.1.total_delta().abs().cmp(&a.1.total_delta().abs()));
173
174 for (name, entry) in entries {
175 println!(
176 "{:<30} {:<10} {:<15.2} {:<15.2}",
177 name,
178 entry.allocations(),
179 entry.total_delta() as f64 / 1024.0,
180 entry.max_delta() as f64 / 1024.0
181 );
182 }
183 }
184 }
185
186 pub fn get_report(&self) -> String {
188 use std::fmt::Write;
189 let mut report = String::new();
190
191 if self.timings.is_empty() && self.memory.is_empty() {
192 writeln!(report, "No profiling data collected.").expect("Operation failed");
193 return report;
194 }
195
196 if !self.timings.is_empty() {
197 writeln!(report, "\n=== Timing Report ===").expect("Operation failed");
198 writeln!(
199 report,
200 "{:<30} {:<10} {:<15} {:<15} {:<15}",
201 "Operation", "Calls", "Total (ms)", "Average (ms)", "Max (ms)"
202 )
203 .expect("Operation failed");
204 writeln!(report, "{}", "-".repeat(90)).expect("Operation failed");
205
206 let mut entries: Vec<(&String, &TimingEntry)> = self.timings.iter().collect();
208 entries.sort_by(|a, b| b.1.total_duration().cmp(&a.1.total_duration()));
209
210 for (name, entry) in entries {
211 writeln!(
212 report,
213 "{:<30} {:<10} {:<15.2} {:<15.2} {:<15.2}",
214 name,
215 entry.calls(),
216 entry.total_duration().as_secs_f64() * 1000.0,
217 entry.average_duration().as_secs_f64() * 1000.0,
218 entry.max_duration().as_secs_f64() * 1000.0
219 )
220 .expect("Operation failed");
221 }
222 }
223
224 if !self.memory.is_empty() {
225 writeln!(report, "\n=== Memory Report ===").expect("Operation failed");
226 writeln!(
227 report,
228 "{:<30} {:<10} {:<15} {:<15}",
229 "Operation", "Counts", "Total (KB)", "Max (KB)"
230 )
231 .expect("Operation failed");
232 writeln!(report, "{}", "-".repeat(75)).expect("Operation failed");
233
234 let mut entries: Vec<(&String, &MemoryEntry)> = self.memory.iter().collect();
236 entries.sort_by(|a, b| b.1.total_delta().abs().cmp(&a.1.total_delta().abs()));
237
238 for (name, entry) in entries {
239 writeln!(
240 report,
241 "{:<30} {:<10} {:<15.2} {:<15.2}",
242 name,
243 entry.allocations(),
244 entry.total_delta() as f64 / 1024.0,
245 entry.max_delta() as f64 / 1024.0
246 )
247 .expect("Operation failed");
248 }
249 }
250
251 report
252 }
253
254 pub fn get_timing_stats(&self, name: &str) -> Option<(usize, Duration, Duration, Duration)> {
256 self.timings.get(name).map(|entry| {
257 (
258 entry.calls(),
259 entry.total_duration(),
260 entry.average_duration(),
261 entry.max_duration(),
262 )
263 })
264 }
265
266 pub fn get_memory_stats(&self, name: &str) -> Option<(usize, isize, usize)> {
268 self.memory
269 .get(name)
270 .map(|entry| (entry.allocations(), entry.total_delta(), entry.max_delta()))
271 }
272
273 pub fn is_running(&self) -> bool {
275 self.running
276 }
277
278 pub fn timings(&self) -> &HashMap<String, TimingEntry> {
280 &self.timings
281 }
282
283 pub fn memory(&self) -> &HashMap<String, MemoryEntry> {
285 &self.memory
286 }
287}
288
289impl Default for Profiler {
290 fn default() -> Self {
291 Self::new()
292 }
293}