1pub mod leak_detector;
35pub mod memory;
36pub mod report;
37
38pub use leak_detector::*;
39pub use memory::*;
40pub use report::*;
41
42use std::sync::Arc;
43use std::time::Duration;
44
45use parking_lot::RwLock;
46use serde::{Deserialize, Serialize};
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct ProfilerConfig {
51 #[serde(default = "default_true")]
53 pub memory_enabled: bool,
54
55 #[serde(default = "default_true")]
57 pub leak_detection_enabled: bool,
58
59 #[serde(default = "default_sampling_interval")]
61 pub sampling_interval: Duration,
62
63 #[serde(default = "default_max_snapshots")]
65 pub max_snapshots: usize,
66
67 #[serde(default = "default_allocation_threshold")]
70 pub allocation_threshold: usize,
71
72 #[serde(default)]
74 pub capture_stack_traces: bool,
75
76 #[serde(default = "default_stack_depth")]
78 pub max_stack_depth: usize,
79}
80
81fn default_true() -> bool {
82 true
83}
84
85fn default_sampling_interval() -> Duration {
86 Duration::from_secs(10)
87}
88
89fn default_max_snapshots() -> usize {
90 1000
91}
92
93fn default_allocation_threshold() -> usize {
94 1024 }
96
97fn default_stack_depth() -> usize {
98 16
99}
100
101impl Default for ProfilerConfig {
102 fn default() -> Self {
103 Self {
104 memory_enabled: true,
105 leak_detection_enabled: true,
106 sampling_interval: default_sampling_interval(),
107 max_snapshots: default_max_snapshots(),
108 allocation_threshold: default_allocation_threshold(),
109 capture_stack_traces: false,
110 max_stack_depth: default_stack_depth(),
111 }
112 }
113}
114
115pub struct Profiler {
117 config: ProfilerConfig,
118 memory_profiler: Arc<RwLock<MemoryProfiler>>,
119 leak_detector: Arc<RwLock<LeakDetector>>,
120 started: Arc<std::sync::atomic::AtomicBool>,
121}
122
123impl Profiler {
124 pub fn new(config: ProfilerConfig) -> Self {
126 let memory_config = MemoryProfilerConfig {
127 sampling_interval: config.sampling_interval,
128 max_snapshots: config.max_snapshots,
129 track_allocations: config.memory_enabled,
130 allocation_threshold: config.allocation_threshold,
131 };
132
133 let leak_config = LeakDetectorConfig {
134 enabled: config.leak_detection_enabled,
135 check_interval: config.sampling_interval * 6, growth_threshold_percent: 10.0,
137 min_samples_for_detection: 6,
138 };
139
140 Self {
141 config,
142 memory_profiler: Arc::new(RwLock::new(MemoryProfiler::new(memory_config))),
143 leak_detector: Arc::new(RwLock::new(LeakDetector::new(leak_config))),
144 started: Arc::new(std::sync::atomic::AtomicBool::new(false)),
145 }
146 }
147
148 pub fn with_defaults() -> Self {
150 Self::new(ProfilerConfig::default())
151 }
152
153 pub fn config(&self) -> &ProfilerConfig {
155 &self.config
156 }
157
158 pub fn start(&self) {
160 self.started
161 .store(true, std::sync::atomic::Ordering::SeqCst);
162 self.memory_profiler.write().start();
163 self.leak_detector.write().start();
164 }
165
166 pub fn stop(&self) {
168 self.started
169 .store(false, std::sync::atomic::Ordering::SeqCst);
170 self.memory_profiler.write().stop();
171 self.leak_detector.write().stop();
172 }
173
174 pub fn is_running(&self) -> bool {
176 self.started.load(std::sync::atomic::Ordering::SeqCst)
177 }
178
179 pub fn record_allocation(&self, label: &str, size: usize) {
181 if self.is_running() {
182 self.memory_profiler.write().record_allocation(label, size);
183 self.leak_detector.write().record_allocation(label, size);
184 }
185 }
186
187 pub fn record_deallocation(&self, label: &str, size: usize) {
189 if self.is_running() {
190 self.memory_profiler
191 .write()
192 .record_deallocation(label, size);
193 self.leak_detector.write().record_deallocation(label, size);
194 }
195 }
196
197 pub fn snapshot(&self) -> MemorySnapshot {
199 self.memory_profiler.read().snapshot()
200 }
201
202 pub fn check_leaks(&self) -> Vec<LeakWarning> {
204 self.leak_detector.read().check()
205 }
206
207 pub fn generate_report(&self) -> ProfileReport {
209 let memory_report = self.memory_profiler.read().generate_report();
210 let leak_warnings = self.check_leaks();
211
212 ProfileReport {
213 generated_at: chrono::Utc::now(),
214 config: self.config.clone(),
215 memory: memory_report,
216 leak_warnings,
217 is_running: self.is_running(),
218 }
219 }
220
221 pub fn memory_profiler(&self) -> Arc<RwLock<MemoryProfiler>> {
223 Arc::clone(&self.memory_profiler)
224 }
225
226 pub fn leak_detector(&self) -> Arc<RwLock<LeakDetector>> {
228 Arc::clone(&self.leak_detector)
229 }
230
231 pub fn reset(&self) {
233 self.memory_profiler.write().reset();
234 self.leak_detector.write().reset();
235 }
236}
237
238impl std::fmt::Debug for Profiler {
239 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
240 f.debug_struct("Profiler")
241 .field("config", &self.config)
242 .field("is_running", &self.is_running())
243 .finish()
244 }
245}
246
247#[derive(Debug, Clone, Serialize, Deserialize)]
249pub struct ProfileReport {
250 pub generated_at: chrono::DateTime<chrono::Utc>,
252
253 pub config: ProfilerConfig,
255
256 pub memory: MemoryReport,
258
259 pub leak_warnings: Vec<LeakWarning>,
261
262 pub is_running: bool,
264}
265
266impl ProfileReport {
267 pub fn has_leak_warnings(&self) -> bool {
269 !self.leak_warnings.is_empty()
270 }
271
272 pub fn current_memory_bytes(&self) -> u64 {
274 self.memory.current_bytes
275 }
276
277 pub fn peak_memory_bytes(&self) -> u64 {
279 self.memory.peak_bytes
280 }
281
282 pub fn to_json(&self) -> Result<String, serde_json::Error> {
284 serde_json::to_string_pretty(self)
285 }
286
287 pub fn to_summary(&self) -> String {
289 let mut summary = String::new();
290 summary.push_str("=== TRAP Simulator Profiling Report ===\n\n");
291
292 summary.push_str(&format!(
293 "Generated: {}\n",
294 self.generated_at.format("%Y-%m-%d %H:%M:%S UTC")
295 ));
296 summary.push_str(&format!(
297 "Status: {}\n",
298 if self.is_running {
299 "Running"
300 } else {
301 "Stopped"
302 }
303 ));
304
305 summary.push_str("\n--- Memory Usage ---\n");
306 summary.push_str(&format!(
307 "Current: {} MB\n",
308 self.memory.current_bytes / 1024 / 1024
309 ));
310 summary.push_str(&format!(
311 "Peak: {} MB\n",
312 self.memory.peak_bytes / 1024 / 1024
313 ));
314 summary.push_str(&format!(
315 "Total Allocated: {} MB\n",
316 self.memory.total_allocated / 1024 / 1024
317 ));
318 summary.push_str(&format!(
319 "Total Deallocated: {} MB\n",
320 self.memory.total_deallocated / 1024 / 1024
321 ));
322 summary.push_str(&format!(
323 "Allocation Count: {}\n",
324 self.memory.allocation_count
325 ));
326
327 if !self.memory.regions.is_empty() {
328 summary.push_str("\n--- Memory Regions ---\n");
329 for (name, region) in &self.memory.regions {
330 summary.push_str(&format!(
331 " {}: {} KB (peak: {} KB, allocs: {})\n",
332 name,
333 region.current_bytes / 1024,
334 region.peak_bytes / 1024,
335 region.allocation_count
336 ));
337 }
338 }
339
340 if !self.leak_warnings.is_empty() {
341 summary.push_str("\n--- Leak Warnings ---\n");
342 for warning in &self.leak_warnings {
343 summary.push_str(&format!(
344 " [{}] {}: {} (growth: {:.1}%/min)\n",
345 warning.severity,
346 warning.region,
347 warning.message,
348 warning.growth_rate_per_minute
349 ));
350 }
351 } else {
352 summary.push_str("\n--- No leak warnings detected ---\n");
353 }
354
355 summary
356 }
357}
358
359#[cfg(test)]
360mod tests {
361 use super::*;
362
363 #[test]
364 fn test_profiler_config_default() {
365 let config = ProfilerConfig::default();
366 assert!(config.memory_enabled);
367 assert!(config.leak_detection_enabled);
368 assert_eq!(config.sampling_interval, Duration::from_secs(10));
369 }
370
371 #[test]
372 fn test_profiler_lifecycle() {
373 let profiler = Profiler::with_defaults();
374 assert!(!profiler.is_running());
375
376 profiler.start();
377 assert!(profiler.is_running());
378
379 profiler.stop();
380 assert!(!profiler.is_running());
381 }
382
383 #[test]
384 fn test_profiler_record_allocation() {
385 let profiler = Profiler::with_defaults();
386 profiler.start();
387
388 profiler.record_allocation("test_region", 1024);
389 profiler.record_allocation("test_region", 2048);
390
391 let snapshot = profiler.snapshot();
392 assert!(snapshot.current_bytes >= 3072);
393
394 profiler.record_deallocation("test_region", 1024);
395
396 let snapshot = profiler.snapshot();
397 assert!(snapshot.current_bytes >= 2048);
398 }
399
400 #[test]
401 fn test_profiler_report() {
402 let profiler = Profiler::with_defaults();
403 profiler.start();
404
405 profiler.record_allocation("devices", 1024 * 1024);
406 profiler.record_allocation("registers", 512 * 1024);
407
408 let report = profiler.generate_report();
409 assert!(report.is_running);
410 assert!(!report.memory.regions.is_empty());
411
412 let summary = report.to_summary();
413 assert!(summary.contains("Memory Usage"));
414 }
415
416 #[test]
417 fn test_profiler_reset() {
418 let profiler = Profiler::with_defaults();
419 profiler.start();
420
421 profiler.record_allocation("test", 1024);
422 profiler.reset();
423
424 let snapshot = profiler.snapshot();
425 assert_eq!(snapshot.current_bytes, 0);
426 }
427}