1use chrono::{DateTime, Utc};
4use parking_lot::RwLock;
5use std::collections::HashMap;
6use std::sync::Arc;
7use std::time::Instant;
8
9pub struct Profiler {
11 profiles: Arc<RwLock<HashMap<String, Vec<ProfileSample>>>>,
12}
13
14#[derive(Debug, Clone)]
16pub struct ProfileSample {
17 pub operation: String,
19 pub duration_ms: f64,
21 pub timestamp: DateTime<Utc>,
23 pub metadata: HashMap<String, String>,
25}
26
27impl Profiler {
28 pub fn new() -> Self {
30 Self {
31 profiles: Arc::new(RwLock::new(HashMap::new())),
32 }
33 }
34
35 pub fn start(&self, operation: impl Into<String>) -> ProfileGuard {
37 ProfileGuard {
38 operation: operation.into(),
39 start: Instant::now(),
40 profiler: self.profiles.clone(),
41 metadata: HashMap::new(),
42 }
43 }
44
45 pub fn get_samples(&self, operation: &str) -> Vec<ProfileSample> {
47 self.profiles
48 .read()
49 .get(operation)
50 .cloned()
51 .unwrap_or_default()
52 }
53
54 pub fn get_stats(&self, operation: &str) -> Option<ProfileStats> {
56 let samples = self.get_samples(operation);
57 if samples.is_empty() {
58 return None;
59 }
60
61 let durations: Vec<f64> = samples.iter().map(|s| s.duration_ms).collect();
62 let count = durations.len();
63 let sum: f64 = durations.iter().sum();
64 let mean = sum / count as f64;
65
66 let variance: f64 =
67 durations.iter().map(|d| (d - mean).powi(2)).sum::<f64>() / count as f64;
68
69 let std_dev = variance.sqrt();
70
71 let mut sorted = durations.clone();
72 sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
73
74 let min = sorted[0];
75 let max = sorted[count - 1];
76 let p50 = sorted[count / 2];
77 let p95 = sorted[(count * 95) / 100];
78 let p99 = sorted[(count * 99) / 100];
79
80 Some(ProfileStats {
81 operation: operation.to_string(),
82 count,
83 mean,
84 std_dev,
85 min,
86 max,
87 p50,
88 p95,
89 p99,
90 })
91 }
92
93 pub fn clear(&self) {
95 self.profiles.write().clear();
96 }
97}
98
99impl Default for Profiler {
100 fn default() -> Self {
101 Self::new()
102 }
103}
104
105pub struct ProfileGuard {
107 operation: String,
108 start: Instant,
109 profiler: Arc<RwLock<HashMap<String, Vec<ProfileSample>>>>,
110 metadata: HashMap<String, String>,
111}
112
113impl ProfileGuard {
114 pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
116 self.metadata.insert(key.into(), value.into());
117 self
118 }
119}
120
121impl Drop for ProfileGuard {
122 fn drop(&mut self) {
123 let duration = self.start.elapsed();
124 let sample = ProfileSample {
125 operation: self.operation.clone(),
126 duration_ms: duration.as_secs_f64() * 1000.0,
127 timestamp: Utc::now(),
128 metadata: self.metadata.clone(),
129 };
130
131 let mut profiles = self.profiler.write();
132 profiles
133 .entry(self.operation.clone())
134 .or_default()
135 .push(sample);
136 }
137}
138
139#[derive(Debug, Clone)]
141pub struct ProfileStats {
142 pub operation: String,
144 pub count: usize,
146 pub mean: f64,
148 pub std_dev: f64,
150 pub min: f64,
152 pub max: f64,
154 pub p50: f64,
156 pub p95: f64,
158 pub p99: f64,
160}
161
162impl std::fmt::Display for ProfileStats {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 write!(
165 f,
166 "Operation: {}\n\
167 Count: {}\n\
168 Mean: {:.2}ms\n\
169 StdDev: {:.2}ms\n\
170 Min: {:.2}ms\n\
171 Max: {:.2}ms\n\
172 P50: {:.2}ms\n\
173 P95: {:.2}ms\n\
174 P99: {:.2}ms",
175 self.operation,
176 self.count,
177 self.mean,
178 self.std_dev,
179 self.min,
180 self.max,
181 self.p50,
182 self.p95,
183 self.p99
184 )
185 }
186}
187
188pub struct MemoryProfiler {
190 snapshots: Arc<RwLock<Vec<MemorySnapshot>>>,
191}
192
193#[derive(Debug, Clone)]
195pub struct MemorySnapshot {
196 pub timestamp: DateTime<Utc>,
198 pub allocated_bytes: u64,
200 pub deallocated_bytes: u64,
202 pub active_allocations: u64,
204 pub operation: String,
206}
207
208impl MemoryProfiler {
209 pub fn new() -> Self {
211 Self {
212 snapshots: Arc::new(RwLock::new(Vec::new())),
213 }
214 }
215
216 pub fn snapshot(&self, operation: impl Into<String>) {
218 let snapshot = MemorySnapshot {
220 timestamp: Utc::now(),
221 allocated_bytes: 0,
222 deallocated_bytes: 0,
223 active_allocations: 0,
224 operation: operation.into(),
225 };
226
227 self.snapshots.write().push(snapshot);
228 }
229
230 pub fn get_snapshots(&self) -> Vec<MemorySnapshot> {
232 self.snapshots.read().clone()
233 }
234
235 pub fn clear(&self) {
237 self.snapshots.write().clear();
238 }
239}
240
241impl Default for MemoryProfiler {
242 fn default() -> Self {
243 Self::new()
244 }
245}
246
247pub struct CpuProfiler {
249 samples: Arc<RwLock<Vec<CpuSample>>>,
250}
251
252#[derive(Debug, Clone)]
254pub struct CpuSample {
255 pub timestamp: DateTime<Utc>,
257 pub cpu_percent: f64,
259 pub operation: String,
261}
262
263impl CpuProfiler {
264 pub fn new() -> Self {
266 Self {
267 samples: Arc::new(RwLock::new(Vec::new())),
268 }
269 }
270
271 pub fn record(&self, operation: impl Into<String>, cpu_percent: f64) {
273 let sample = CpuSample {
274 timestamp: Utc::now(),
275 cpu_percent,
276 operation: operation.into(),
277 };
278
279 self.samples.write().push(sample);
280 }
281
282 pub fn get_samples(&self) -> Vec<CpuSample> {
284 self.samples.read().clone()
285 }
286
287 pub fn get_average(&self, operation: &str) -> Option<f64> {
289 let samples: Vec<CpuSample> = self
290 .samples
291 .read()
292 .iter()
293 .filter(|s| s.operation == operation)
294 .cloned()
295 .collect();
296
297 if samples.is_empty() {
298 return None;
299 }
300
301 let sum: f64 = samples.iter().map(|s| s.cpu_percent).sum();
302 Some(sum / samples.len() as f64)
303 }
304}
305
306impl Default for CpuProfiler {
307 fn default() -> Self {
308 Self::new()
309 }
310}
311
312#[cfg(test)]
313mod tests {
314 use super::*;
315 use std::thread;
316 use std::time::Duration;
317
318 #[test]
319 fn test_profiler() {
320 let profiler = Profiler::new();
321
322 {
323 let _guard = profiler.start("test_operation");
324 thread::sleep(Duration::from_millis(10));
325 }
326
327 let samples = profiler.get_samples("test_operation");
328 assert_eq!(samples.len(), 1);
329 assert!(samples[0].duration_ms >= 10.0);
330 }
331
332 #[test]
333 fn test_profiler_stats() {
334 let profiler = Profiler::new();
335
336 for _ in 0..10 {
337 let _guard = profiler.start("test_op");
338 thread::sleep(Duration::from_millis(1));
339 }
340
341 let stats = profiler.get_stats("test_op");
342 assert!(stats.is_some());
343
344 let stats = stats.expect("No stats");
345 assert_eq!(stats.count, 10);
346 assert!(stats.mean > 0.0);
347 }
348
349 #[test]
350 fn test_memory_profiler() {
351 let profiler = MemoryProfiler::new();
352 profiler.snapshot("test_operation");
353
354 assert_eq!(profiler.get_snapshots().len(), 1);
355 }
356
357 #[test]
358 fn test_cpu_profiler() {
359 let profiler = CpuProfiler::new();
360 profiler.record("test_op", 50.0);
361 profiler.record("test_op", 60.0);
362
363 let avg = profiler.get_average("test_op");
364 assert_eq!(avg, Some(55.0));
365 }
366}