1pub mod memory;
35pub mod leak_detector;
36pub mod report;
37
38pub use memory::*;
39pub use leak_detector::*;
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!("Status: {}\n", if self.is_running { "Running" } else { "Stopped" }));
297
298 summary.push_str("\n--- Memory Usage ---\n");
299 summary.push_str(&format!(
300 "Current: {} MB\n",
301 self.memory.current_bytes / 1024 / 1024
302 ));
303 summary.push_str(&format!(
304 "Peak: {} MB\n",
305 self.memory.peak_bytes / 1024 / 1024
306 ));
307 summary.push_str(&format!(
308 "Total Allocated: {} MB\n",
309 self.memory.total_allocated / 1024 / 1024
310 ));
311 summary.push_str(&format!(
312 "Total Deallocated: {} MB\n",
313 self.memory.total_deallocated / 1024 / 1024
314 ));
315 summary.push_str(&format!(
316 "Allocation Count: {}\n",
317 self.memory.allocation_count
318 ));
319
320 if !self.memory.regions.is_empty() {
321 summary.push_str("\n--- Memory Regions ---\n");
322 for (name, region) in &self.memory.regions {
323 summary.push_str(&format!(
324 " {}: {} KB (peak: {} KB, allocs: {})\n",
325 name,
326 region.current_bytes / 1024,
327 region.peak_bytes / 1024,
328 region.allocation_count
329 ));
330 }
331 }
332
333 if !self.leak_warnings.is_empty() {
334 summary.push_str("\n--- Leak Warnings ---\n");
335 for warning in &self.leak_warnings {
336 summary.push_str(&format!(
337 " [{}] {}: {} (growth: {:.1}%/min)\n",
338 warning.severity,
339 warning.region,
340 warning.message,
341 warning.growth_rate_per_minute
342 ));
343 }
344 } else {
345 summary.push_str("\n--- No leak warnings detected ---\n");
346 }
347
348 summary
349 }
350}
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355
356 #[test]
357 fn test_profiler_config_default() {
358 let config = ProfilerConfig::default();
359 assert!(config.memory_enabled);
360 assert!(config.leak_detection_enabled);
361 assert_eq!(config.sampling_interval, Duration::from_secs(10));
362 }
363
364 #[test]
365 fn test_profiler_lifecycle() {
366 let profiler = Profiler::with_defaults();
367 assert!(!profiler.is_running());
368
369 profiler.start();
370 assert!(profiler.is_running());
371
372 profiler.stop();
373 assert!(!profiler.is_running());
374 }
375
376 #[test]
377 fn test_profiler_record_allocation() {
378 let profiler = Profiler::with_defaults();
379 profiler.start();
380
381 profiler.record_allocation("test_region", 1024);
382 profiler.record_allocation("test_region", 2048);
383
384 let snapshot = profiler.snapshot();
385 assert!(snapshot.current_bytes >= 3072);
386
387 profiler.record_deallocation("test_region", 1024);
388
389 let snapshot = profiler.snapshot();
390 assert!(snapshot.current_bytes >= 2048);
391 }
392
393 #[test]
394 fn test_profiler_report() {
395 let profiler = Profiler::with_defaults();
396 profiler.start();
397
398 profiler.record_allocation("devices", 1024 * 1024);
399 profiler.record_allocation("registers", 512 * 1024);
400
401 let report = profiler.generate_report();
402 assert!(report.is_running);
403 assert!(!report.memory.regions.is_empty());
404
405 let summary = report.to_summary();
406 assert!(summary.contains("Memory Usage"));
407 }
408
409 #[test]
410 fn test_profiler_reset() {
411 let profiler = Profiler::with_defaults();
412 profiler.start();
413
414 profiler.record_allocation("test", 1024);
415 profiler.reset();
416
417 let snapshot = profiler.snapshot();
418 assert_eq!(snapshot.current_bytes, 0);
419 }
420}