1use crate::capture::system_monitor;
41use crate::core::tracker::MemoryTracker;
42use crate::event_store::{EventStore, MemoryEvent};
43use crate::render_engine::dashboard::renderer::rebuild_allocations_from_events;
44use crate::render_engine::export::{export_snapshot_to_json, ExportJsonOptions};
45use crate::snapshot::MemorySnapshot;
46
47use std::collections::HashMap;
48use std::sync::{Arc, Mutex};
49use std::time::{Duration, Instant};
50
51#[derive(Debug, Clone)]
52pub struct SamplingConfig {
53 pub sample_rate: f64,
54 pub capture_call_stack: bool,
55 pub max_stack_depth: usize,
56}
57
58impl Default for SamplingConfig {
59 fn default() -> Self {
60 Self {
61 sample_rate: 1.0,
62 capture_call_stack: false,
63 max_stack_depth: 10,
64 }
65 }
66}
67
68impl SamplingConfig {
69 pub fn demo() -> Self {
70 Self {
71 sample_rate: 0.1,
72 capture_call_stack: false,
73 max_stack_depth: 5,
74 }
75 }
76
77 pub fn full() -> Self {
78 Self {
79 sample_rate: 1.0,
80 capture_call_stack: true,
81 max_stack_depth: 20,
82 }
83 }
84
85 pub fn high_performance() -> Self {
86 Self {
87 sample_rate: 0.01,
88 capture_call_stack: false,
89 max_stack_depth: 0,
90 }
91 }
92}
93
94#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
95pub struct SystemSnapshot {
96 pub timestamp: u64,
97 pub cpu_usage_percent: f64,
98 pub memory_usage_bytes: u64,
99 pub memory_usage_percent: f64,
100 pub thread_count: usize,
101 pub disk_read_bps: u64,
102 pub disk_write_bps: u64,
103 pub network_rx_bps: u64,
104 pub network_tx_bps: u64,
105 pub gpu_usage_percent: f64,
106 pub gpu_memory_used: u64,
107 pub gpu_memory_total: u64,
108}
109
110#[derive(Debug, Clone)]
111pub struct AnalysisReport {
112 pub total_allocations: usize,
113 pub total_deallocations: usize,
114 pub active_allocations: usize,
115 pub peak_memory_bytes: u64,
116 pub current_memory_bytes: u64,
117 pub allocation_rate_per_sec: f64,
118 pub deallocation_rate_per_sec: f64,
119 pub hotspots: Vec<AllocationHotspot>,
120 pub system_snapshots: Vec<SystemSnapshot>,
121}
122
123#[derive(Debug, Clone)]
124pub struct AllocationHotspot {
125 pub var_name: String,
126 pub type_name: String,
127 pub total_size: usize,
128 pub allocation_count: usize,
129 pub location: Option<String>,
130}
131
132pub struct Tracker {
133 inner: Arc<MemoryTracker>,
134 event_store: Arc<EventStore>,
135 config: Arc<Mutex<TrackerConfig>>,
136 start_time: Instant,
137 system_snapshots: Arc<Mutex<Vec<SystemSnapshot>>>,
138}
139
140impl Clone for Tracker {
141 fn clone(&self) -> Self {
142 Tracker {
143 inner: self.inner.clone(),
144 event_store: self.event_store.clone(),
145 config: self.config.clone(),
146 start_time: Instant::now(), system_snapshots: self.system_snapshots.clone(),
148 }
149 }
150}
151
152#[derive(Debug, Clone)]
153struct TrackerConfig {
154 sampling: SamplingConfig,
155 auto_export_on_drop: bool,
156 export_path: Option<String>,
157}
158
159impl Tracker {
160 pub fn new() -> Self {
161 Self {
162 inner: Arc::new(MemoryTracker::new()),
163 event_store: Arc::new(EventStore::new()),
164 config: Arc::new(Mutex::new(TrackerConfig {
165 sampling: SamplingConfig::default(),
166 auto_export_on_drop: false,
167 export_path: None,
168 })),
169 start_time: Instant::now(),
170 system_snapshots: Arc::new(Mutex::new(Vec::new())),
171 }
172 }
173
174 pub fn global() -> Self {
175 use crate::core::tracker::get_tracker;
176 static GLOBAL_EVENT_STORE: std::sync::OnceLock<Arc<EventStore>> =
177 std::sync::OnceLock::new();
178 static GLOBAL_CONFIG: std::sync::OnceLock<Arc<Mutex<TrackerConfig>>> =
179 std::sync::OnceLock::new();
180 static GLOBAL_SYSTEM_SNAPSHOTS: std::sync::OnceLock<Arc<Mutex<Vec<SystemSnapshot>>>> =
181 std::sync::OnceLock::new();
182
183 Self {
184 inner: get_tracker(),
185 event_store: GLOBAL_EVENT_STORE
186 .get_or_init(|| Arc::new(EventStore::new()))
187 .clone(),
188 config: GLOBAL_CONFIG
189 .get_or_init(|| {
190 Arc::new(Mutex::new(TrackerConfig {
191 sampling: SamplingConfig::default(),
192 auto_export_on_drop: false,
193 export_path: None,
194 }))
195 })
196 .clone(),
197 start_time: Instant::now(),
198 system_snapshots: GLOBAL_SYSTEM_SNAPSHOTS
199 .get_or_init(|| Arc::new(Mutex::new(Vec::new())))
200 .clone(),
201 }
202 }
203
204 pub fn with_system_monitoring(self) -> Self {
205 self.capture_system_snapshot();
206 self
207 }
208
209 pub fn with_sampling(self, config: SamplingConfig) -> Self {
210 if let Ok(mut cfg) = self.config.lock() {
211 cfg.sampling = config;
212 }
213 self
214 }
215
216 pub fn with_auto_export(self, path: &str) -> Self {
217 if let Ok(mut cfg) = self.config.lock() {
218 cfg.auto_export_on_drop = true;
219 cfg.export_path = Some(path.to_string());
220 }
221 self
222 }
223
224 pub fn track_as<T: crate::Trackable>(&self, var: &T, name: &str, file: &str, line: u32) {
225 if let Ok(cfg) = self.config.lock() {
226 if cfg.sampling.sample_rate < 1.0 {
227 use std::collections::hash_map::DefaultHasher;
228 use std::hash::{Hash, Hasher};
229 let mut hasher = DefaultHasher::new();
230 let timestamp = std::time::SystemTime::now()
233 .duration_since(std::time::UNIX_EPOCH)
234 .unwrap_or_default()
235 .as_nanos();
236 timestamp.hash(&mut hasher);
237 std::thread::current().id().hash(&mut hasher);
238 name.hash(&mut hasher);
239 file.hash(&mut hasher);
240 line.hash(&mut hasher);
241 let hash = hasher.finish();
242 let threshold = (cfg.sampling.sample_rate * 1000.0) as u64;
243 if (hash % 1000) > threshold {
244 return;
245 }
246 }
247 }
248
249 self.track_inner(var, name, file, line);
250 }
251
252 fn track_inner<T: crate::Trackable>(&self, var: &T, name: &str, file: &str, line: u32) {
253 let type_name = var.get_type_name().to_string();
254 let kind = var.track_kind();
255
256 let thread_id_u64 = crate::utils::current_thread_id_u64();
257
258 match kind {
259 crate::core::types::TrackKind::HeapOwner { ptr, size } => {
260 if let Err(e) = self.inner.track_allocation(ptr, size) {
262 tracing::error!("Failed to track allocation at ptr {:x}: {}", ptr, e);
263 return;
264 }
265
266 let mut event = MemoryEvent::allocate(ptr, size, thread_id_u64);
267 event.var_name = Some(name.to_string());
268 event.type_name = Some(type_name.clone());
269 event.source_file = Some(file.to_string());
270 event.source_line = Some(line);
271 self.event_store.record(event);
272
273 if let Err(e) = self.inner.associate_var(
274 ptr,
275 name.to_string(),
276 type_name,
277 Some(file),
278 Some(line),
279 ) {
280 tracing::error!("Failed to associate var '{}' at ptr {:x}: {}", name, ptr, e);
281 }
282 }
283 crate::core::types::TrackKind::Container | crate::core::types::TrackKind::Value => {
284 let mut event = MemoryEvent::metadata(
287 name.to_string(),
288 type_name,
289 thread_id_u64,
290 var.get_size_estimate(),
291 );
292 event.source_file = Some(file.to_string());
293 event.source_line = Some(line);
294 self.event_store.record(event);
295 }
296 }
297 }
298
299 pub fn track_deallocation(&self, ptr: usize) -> crate::TrackingResult<bool> {
300 let size = self.inner.get_allocation_size(ptr).unwrap_or(0);
301
302 let result = self.inner.track_deallocation(ptr)?;
303
304 if result {
306 let thread_id_u64 = crate::utils::current_thread_id_u64();
307
308 let event = MemoryEvent::deallocate(ptr, size, thread_id_u64);
309 self.event_store.record(event);
310 }
311
312 Ok(result)
313 }
314
315 pub fn events(&self) -> Vec<MemoryEvent> {
316 self.event_store.snapshot()
317 }
318
319 pub fn event_store(&self) -> &Arc<EventStore> {
320 &self.event_store
321 }
322
323 fn capture_system_snapshot(&self) {
324 let snapshot = SystemSnapshot {
325 timestamp: std::time::SystemTime::now()
326 .duration_since(std::time::UNIX_EPOCH)
327 .unwrap_or_default()
328 .as_millis() as u64,
329 cpu_usage_percent: system_monitor::cpu_usage(),
330 memory_usage_bytes: system_monitor::memory_used(),
331 memory_usage_percent: system_monitor::memory_usage_percent(),
332 thread_count: system_monitor::thread_count(),
333 disk_read_bps: system_monitor::disk_read_bps(),
334 disk_write_bps: system_monitor::disk_write_bps(),
335 network_rx_bps: system_monitor::network_rx_bps(),
336 network_tx_bps: system_monitor::network_tx_bps(),
337 gpu_usage_percent: system_monitor::gpu_memory_usage_percent(),
338 gpu_memory_used: system_monitor::gpu_memory_used(),
339 gpu_memory_total: system_monitor::gpu_memory_total(),
340 };
341
342 if let Ok(mut snapshots) = self.system_snapshots.lock() {
343 snapshots.push(snapshot);
344 }
345 }
346
347 pub fn stats(&self) -> crate::core::types::MemoryStats {
348 let stats = self.inner.get_stats().unwrap_or_default();
349 crate::core::types::MemoryStats {
350 total_allocations: stats.total_allocations as usize,
351 total_allocated: stats.total_allocated as usize,
352 active_allocations: stats.active_allocations,
353 active_memory: stats.active_memory as usize,
354 peak_allocations: stats.peak_allocations,
355 peak_memory: stats.peak_memory as usize,
356 total_deallocations: stats.total_deallocations as usize,
357 total_deallocated: stats.total_deallocated as usize,
358 leaked_allocations: stats.leaked_allocations,
359 leaked_memory: stats.leaked_memory as usize,
360 ..Default::default()
361 }
362 }
363
364 pub fn analyze(&self) -> AnalysisReport {
365 let stats = self.stats();
366 let events = self.event_store().snapshot();
367 let allocations = rebuild_allocations_from_events(&events);
368 let elapsed = self.start_time.elapsed().as_secs_f64();
369
370 let current_memory: usize = allocations.iter().map(|a| a.size).sum();
371 let peak_memory = stats.peak_memory.max(current_memory);
372
373 let mut hotspot_map: HashMap<String, (String, usize, usize)> = HashMap::new();
374 for alloc in &allocations {
375 if let Some(ref var_name) = alloc.var_name {
376 let key = var_name.clone();
377 let entry = hotspot_map.entry(key).or_insert((
378 alloc.type_name.clone().unwrap_or_default(),
379 0,
380 0,
381 ));
382 entry.1 += alloc.size;
383 entry.2 += 1;
384 }
385 }
386
387 let hotspots: Vec<AllocationHotspot> = hotspot_map
388 .into_iter()
389 .map(
390 |(var_name, (type_name, total_size, count))| AllocationHotspot {
391 var_name,
392 type_name,
393 total_size,
394 allocation_count: count,
395 location: None,
396 },
397 )
398 .collect();
399
400 let system_snapshots = self
401 .system_snapshots
402 .lock()
403 .unwrap_or_else(|e| e.into_inner())
404 .clone();
405
406 AnalysisReport {
407 total_allocations: stats.total_allocations,
408 total_deallocations: stats.total_deallocations,
409 active_allocations: allocations.len(),
410 peak_memory_bytes: peak_memory as u64,
411 current_memory_bytes: current_memory as u64,
412 allocation_rate_per_sec: if elapsed > 0.0 {
413 stats.total_allocations as f64 / elapsed
414 } else {
415 0.0
416 },
417 deallocation_rate_per_sec: if elapsed > 0.0 {
418 stats.total_deallocations as f64 / elapsed
419 } else {
420 0.0
421 },
422 hotspots,
423 system_snapshots,
424 }
425 }
426
427 pub fn inner(&self) -> &Arc<MemoryTracker> {
428 &self.inner
429 }
430
431 pub fn elapsed(&self) -> Duration {
432 self.start_time.elapsed()
433 }
434
435 pub fn system_snapshots(&self) -> Vec<SystemSnapshot> {
436 self.system_snapshots
437 .lock()
438 .unwrap_or_else(|e| e.into_inner())
439 .clone()
440 }
441
442 pub fn current_system_snapshot(&self) -> SystemSnapshot {
443 SystemSnapshot {
444 timestamp: std::time::SystemTime::now()
445 .duration_since(std::time::UNIX_EPOCH)
446 .unwrap_or_default()
447 .as_millis() as u64,
448 cpu_usage_percent: system_monitor::cpu_usage(),
449 memory_usage_bytes: system_monitor::memory_used(),
450 memory_usage_percent: system_monitor::memory_usage_percent(),
451 thread_count: system_monitor::thread_count(),
452 disk_read_bps: system_monitor::disk_read_bps(),
453 disk_write_bps: system_monitor::disk_write_bps(),
454 network_rx_bps: system_monitor::network_rx_bps(),
455 network_tx_bps: system_monitor::network_tx_bps(),
456 gpu_usage_percent: system_monitor::gpu_memory_usage_percent(),
457 gpu_memory_used: system_monitor::gpu_memory_used(),
458 gpu_memory_total: system_monitor::gpu_memory_total(),
459 }
460 }
461}
462
463impl Default for Tracker {
464 fn default() -> Self {
465 Self::new()
466 }
467}
468
469impl Drop for Tracker {
470 fn drop(&mut self) {
471 if let Ok(cfg) = self.config.lock() {
472 if cfg.auto_export_on_drop {
473 if let Some(ref path) = cfg.export_path {
474 let events = self.event_store().snapshot();
476 let allocations = rebuild_allocations_from_events(&events);
477 let snapshot = MemorySnapshot::from_allocation_infos(allocations);
478 let options = ExportJsonOptions::default();
479 if let Err(e) =
480 export_snapshot_to_json(&snapshot, std::path::Path::new(path), &options)
481 {
482 tracing::error!("Failed to auto-export on drop: {}", e);
483 }
484 }
485 }
486 }
487 }
488}
489
490impl serde::Serialize for AnalysisReport {
491 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
492 where
493 S: serde::Serializer,
494 {
495 use serde::ser::SerializeStruct;
496 let mut state = serializer.serialize_struct("AnalysisReport", 9)?;
497 state.serialize_field("total_allocations", &self.total_allocations)?;
498 state.serialize_field("total_deallocations", &self.total_deallocations)?;
499 state.serialize_field("active_allocations", &self.active_allocations)?;
500 state.serialize_field("peak_memory_bytes", &self.peak_memory_bytes)?;
501 state.serialize_field("current_memory_bytes", &self.current_memory_bytes)?;
502 state.serialize_field("allocation_rate_per_sec", &self.allocation_rate_per_sec)?;
503 state.serialize_field("deallocation_rate_per_sec", &self.deallocation_rate_per_sec)?;
504 state.serialize_field("hotspots", &self.hotspots)?;
505 state.serialize_field("system_snapshots", &self.system_snapshots)?;
506 state.end()
507 }
508}
509
510impl serde::Serialize for AllocationHotspot {
511 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
512 where
513 S: serde::Serializer,
514 {
515 use serde::ser::SerializeStruct;
516 let mut state = serializer.serialize_struct("AllocationHotspot", 5)?;
517 state.serialize_field("var_name", &self.var_name)?;
518 state.serialize_field("type_name", &self.type_name)?;
519 state.serialize_field("total_size", &self.total_size)?;
520 state.serialize_field("allocation_count", &self.allocation_count)?;
521 state.serialize_field("location", &self.location)?;
522 state.end()
523 }
524}
525
526#[macro_export]
527macro_rules! tracker {
528 () => {
529 $crate::tracker::Tracker::new()
530 };
531}
532
533#[macro_export]
534macro_rules! track {
535 ($tracker:expr, $var:expr) => {{
536 let var_name = stringify!($var);
537 $tracker.track_as(&$var, var_name, file!(), line!());
538 }};
539}
540
541#[cfg(test)]
542mod tests {
543 use super::*;
544
545 #[test]
546 fn test_tracker_creation() {
547 let tracker = Tracker::new();
548 let _ = tracker;
549 }
550
551 #[test]
552 fn test_tracker_with_config() {
553 let tracker = Tracker::new()
554 .with_sampling(SamplingConfig::demo())
555 .with_system_monitoring();
556 let _ = tracker;
557 }
558
559 #[test]
560 fn test_track_macro() {
561 let tracker = tracker!();
562 let my_vec = vec![1, 2, 3];
563 track!(tracker, my_vec);
564 }
565
566 #[test]
567 fn test_analyze() {
568 let tracker = tracker!();
569 let data = vec![1, 2, 3];
570 track!(tracker, data);
571 let report = tracker.analyze();
572 assert!(report.total_allocations > 0);
573 }
574
575 #[test]
576 #[cfg(target_os = "macos")]
577 fn test_system_monitoring() {
578 std::thread::sleep(std::time::Duration::from_millis(200));
579
580 let cpu = system_monitor::cpu_usage();
581 let mem = system_monitor::memory_used();
582 let total = system_monitor::memory_total();
583
584 println!("CPU: {:.2}%", cpu);
585 println!("Memory: {} / {} bytes", mem, total);
586
587 assert!((0.0..=100.0).contains(&cpu));
588 assert!(total > 0);
589 }
590
591 #[test]
592 fn test_current_system_snapshot() {
593 std::thread::sleep(std::time::Duration::from_millis(150));
594
595 let tracker = tracker!();
596 let snapshot = tracker.current_system_snapshot();
597
598 println!(
599 "Snapshot: CPU={:.2}%, Mem={:.2}%",
600 snapshot.cpu_usage_percent, snapshot.memory_usage_percent
601 );
602
603 assert!(snapshot.cpu_usage_percent >= 0.0 && snapshot.cpu_usage_percent <= 100.0);
604 }
605
606 #[test]
607 fn test_sampling_config_default() {
608 let config = SamplingConfig::default();
609 assert_eq!(config.sample_rate, 1.0);
610 assert!(!config.capture_call_stack);
611 assert_eq!(config.max_stack_depth, 10);
612 }
613
614 #[test]
615 fn test_sampling_config_demo() {
616 let config = SamplingConfig::demo();
617 assert_eq!(config.sample_rate, 0.1);
618 assert!(!config.capture_call_stack);
619 assert_eq!(config.max_stack_depth, 5);
620 }
621
622 #[test]
623 fn test_sampling_config_full() {
624 let config = SamplingConfig::full();
625 assert_eq!(config.sample_rate, 1.0);
626 assert!(config.capture_call_stack);
627 assert_eq!(config.max_stack_depth, 20);
628 }
629
630 #[test]
631 fn test_sampling_config_high_performance() {
632 let config = SamplingConfig::high_performance();
633 assert_eq!(config.sample_rate, 0.01);
634 assert!(!config.capture_call_stack);
635 assert_eq!(config.max_stack_depth, 0);
636 }
637
638 #[test]
639 fn test_sampling_config_clone() {
640 let config = SamplingConfig::full();
641 let cloned = config.clone();
642 assert_eq!(cloned.sample_rate, config.sample_rate);
643 assert_eq!(cloned.capture_call_stack, config.capture_call_stack);
644 }
645
646 #[test]
647 fn test_sampling_config_debug() {
648 let config = SamplingConfig::default();
649 let debug_str = format!("{:?}", config);
650 assert!(debug_str.contains("SamplingConfig"));
651 assert!(debug_str.contains("sample_rate"));
652 }
653
654 #[test]
655 fn test_system_snapshot_default() {
656 let snapshot = SystemSnapshot::default();
657 assert_eq!(snapshot.timestamp, 0);
658 assert_eq!(snapshot.cpu_usage_percent, 0.0);
659 assert_eq!(snapshot.memory_usage_bytes, 0);
660 assert_eq!(snapshot.thread_count, 0);
661 }
662
663 #[test]
664 fn test_system_snapshot_clone() {
665 let snapshot = SystemSnapshot {
666 timestamp: 1000,
667 cpu_usage_percent: 50.0,
668 memory_usage_bytes: 1024 * 1024,
669 memory_usage_percent: 25.0,
670 thread_count: 4,
671 disk_read_bps: 1000,
672 disk_write_bps: 500,
673 network_rx_bps: 2000,
674 network_tx_bps: 1000,
675 gpu_usage_percent: 30.0,
676 gpu_memory_used: 512 * 1024 * 1024,
677 gpu_memory_total: 2 * 1024 * 1024 * 1024,
678 };
679
680 let cloned = snapshot.clone();
681 assert_eq!(cloned.timestamp, 1000);
682 assert_eq!(cloned.cpu_usage_percent, 50.0);
683 }
684
685 #[test]
686 fn test_system_snapshot_debug() {
687 let snapshot = SystemSnapshot::default();
688 let debug_str = format!("{:?}", snapshot);
689 assert!(debug_str.contains("SystemSnapshot"));
690 }
691
692 #[test]
693 fn test_analysis_report_creation() {
694 let report = AnalysisReport {
695 total_allocations: 100,
696 total_deallocations: 50,
697 active_allocations: 50,
698 peak_memory_bytes: 1024 * 1024,
699 current_memory_bytes: 512 * 1024,
700 allocation_rate_per_sec: 10.0,
701 deallocation_rate_per_sec: 5.0,
702 hotspots: vec![],
703 system_snapshots: vec![],
704 };
705
706 assert_eq!(report.total_allocations, 100);
707 assert_eq!(report.active_allocations, 50);
708 }
709
710 #[test]
711 fn test_analysis_report_clone() {
712 let report = AnalysisReport {
713 total_allocations: 10,
714 total_deallocations: 5,
715 active_allocations: 5,
716 peak_memory_bytes: 1024,
717 current_memory_bytes: 512,
718 allocation_rate_per_sec: 1.0,
719 deallocation_rate_per_sec: 0.5,
720 hotspots: vec![],
721 system_snapshots: vec![],
722 };
723
724 let cloned = report.clone();
725 assert_eq!(cloned.total_allocations, 10);
726 }
727
728 #[test]
729 fn test_analysis_report_debug() {
730 let report = AnalysisReport {
731 total_allocations: 0,
732 total_deallocations: 0,
733 active_allocations: 0,
734 peak_memory_bytes: 0,
735 current_memory_bytes: 0,
736 allocation_rate_per_sec: 0.0,
737 deallocation_rate_per_sec: 0.0,
738 hotspots: vec![],
739 system_snapshots: vec![],
740 };
741
742 let debug_str = format!("{:?}", report);
743 assert!(debug_str.contains("AnalysisReport"));
744 }
745
746 #[test]
747 fn test_allocation_hotspot_creation() {
748 let hotspot = AllocationHotspot {
749 var_name: "my_vec".to_string(),
750 type_name: "Vec<u8>".to_string(),
751 total_size: 1024,
752 allocation_count: 10,
753 location: Some("main.rs:42".to_string()),
754 };
755
756 assert_eq!(hotspot.var_name, "my_vec");
757 assert_eq!(hotspot.total_size, 1024);
758 }
759
760 #[test]
761 fn test_allocation_hotspot_clone() {
762 let hotspot = AllocationHotspot {
763 var_name: "data".to_string(),
764 type_name: "String".to_string(),
765 total_size: 100,
766 allocation_count: 5,
767 location: None,
768 };
769
770 let cloned = hotspot.clone();
771 assert_eq!(cloned.var_name, "data");
772 }
773
774 #[test]
775 fn test_allocation_hotspot_debug() {
776 let hotspot = AllocationHotspot {
777 var_name: "test".to_string(),
778 type_name: "i32".to_string(),
779 total_size: 4,
780 allocation_count: 1,
781 location: None,
782 };
783
784 let debug_str = format!("{:?}", hotspot);
785 assert!(debug_str.contains("AllocationHotspot"));
786 }
787
788 #[test]
789 fn test_tracker_clone() {
790 let tracker = Tracker::new();
791 let cloned = tracker.clone();
792
793 let report1 = tracker.analyze();
794 let report2 = cloned.analyze();
795
796 assert_eq!(report1.total_allocations, report2.total_allocations);
798 }
799
800 #[test]
801 fn test_tracker_with_sampling() {
802 let tracker = Tracker::new().with_sampling(SamplingConfig::high_performance());
803 let data = vec![1, 2, 3];
804 tracker.track_as(&data, "data", "test.rs", 1);
805 }
806
807 #[test]
808 fn test_tracker_elapsed() {
809 let tracker = Tracker::new();
810 std::thread::sleep(std::time::Duration::from_millis(10));
811 let elapsed = tracker.elapsed();
812 assert!(elapsed >= std::time::Duration::from_millis(10));
813 }
814
815 #[test]
816 fn test_tracker_with_system_monitoring() {
817 let tracker = Tracker::new().with_system_monitoring();
818 let _ = tracker.current_system_snapshot();
819 }
820
821 #[test]
822 fn test_tracker_track_as_multiple() {
823 let tracker = Tracker::new();
824 let data = vec![1, 2, 3, 4, 5];
825
826 tracker.track_as(&data, "my_vec", "test.rs", 10);
827 tracker.track_as(&data, "my_vec", "test.rs", 20);
828
829 let report = tracker.analyze();
830 let _ = report.total_allocations;
831 }
832
833 #[test]
834 fn test_sampling_config_custom() {
835 let config = SamplingConfig {
836 sample_rate: 0.5,
837 capture_call_stack: true,
838 max_stack_depth: 15,
839 };
840
841 assert!((config.sample_rate - 0.5).abs() < 0.001);
842 assert!(config.capture_call_stack);
843 assert_eq!(config.max_stack_depth, 15);
844 }
845
846 #[test]
847 fn test_analysis_report_with_hotspots() {
848 let report = AnalysisReport {
849 total_allocations: 100,
850 total_deallocations: 50,
851 active_allocations: 50,
852 peak_memory_bytes: 1024 * 1024,
853 current_memory_bytes: 512 * 1024,
854 allocation_rate_per_sec: 10.0,
855 deallocation_rate_per_sec: 5.0,
856 hotspots: vec![AllocationHotspot {
857 var_name: "test".to_string(),
858 type_name: "Vec<u8>".to_string(),
859 total_size: 1024,
860 allocation_count: 10,
861 location: Some("test.rs:1".to_string()),
862 }],
863 system_snapshots: vec![],
864 };
865
866 assert_eq!(report.hotspots.len(), 1);
867 }
868
869 #[test]
870 fn test_tracker_with_sampling_and_monitoring() {
871 let tracker = Tracker::new()
872 .with_sampling(SamplingConfig::demo())
873 .with_system_monitoring();
874
875 let data = vec![1, 2, 3];
876 tracker.track_as(&data, "data", "test.rs", 1);
877
878 let snapshot = tracker.current_system_snapshot();
879 assert!(snapshot.cpu_usage_percent >= 0.0);
880 }
881
882 #[test]
883 fn test_tracker_events() {
884 let tracker = Tracker::new();
885 let data = vec![1, 2, 3];
886 tracker.track_as(&data, "test_data", "test.rs", 1);
887
888 let events = tracker.events();
889 assert!(!events.is_empty());
890 }
891
892 #[test]
893 fn test_tracker_event_store() {
894 let tracker = Tracker::new();
895 let _store = tracker.event_store();
896 }
897
898 #[test]
899 fn test_tracker_stats() {
900 let tracker = Tracker::new();
901 let stats = tracker.stats();
902
903 assert_eq!(stats.total_allocations, 0);
904 assert_eq!(stats.active_allocations, 0);
905 }
906
907 #[test]
908 fn test_tracker_stats_with_data() {
909 let tracker = Tracker::new();
910 let data = vec![1u8; 1024];
911 tracker.track_as(&data, "buffer", "test.rs", 1);
912
913 let stats = tracker.stats();
914 assert!(
915 stats.total_allocations >= 1,
916 "Should have at least one allocation after tracking data"
917 );
918 }
919
920 #[test]
921 fn test_tracker_system_snapshots() {
922 let tracker = Tracker::new().with_system_monitoring();
923 let snapshots = tracker.system_snapshots();
924 assert!(!snapshots.is_empty());
925 }
926
927 #[test]
928 fn test_tracker_inner() {
929 let tracker = Tracker::new();
930 let _inner = tracker.inner();
931 }
932
933 #[test]
934 fn test_tracker_with_auto_export() {
935 let tracker = Tracker::new().with_auto_export("/tmp/test_export");
936 let data = vec![1, 2, 3];
937 tracker.track_as(&data, "test", "test.rs", 1);
938 }
939
940 #[test]
941 fn test_sampling_config_zero_rate() {
942 let config = SamplingConfig {
943 sample_rate: 0.0,
944 capture_call_stack: false,
945 max_stack_depth: 0,
946 };
947
948 let tracker = Tracker::new().with_sampling(config);
949 let data = vec![1, 2, 3];
950 tracker.track_as(&data, "test", "test.rs", 1);
951 }
952
953 #[test]
954 fn test_analysis_report_serialization() {
955 let report = AnalysisReport {
956 total_allocations: 100,
957 total_deallocations: 50,
958 active_allocations: 50,
959 peak_memory_bytes: 1024,
960 current_memory_bytes: 512,
961 allocation_rate_per_sec: 10.0,
962 deallocation_rate_per_sec: 5.0,
963 hotspots: vec![],
964 system_snapshots: vec![],
965 };
966
967 let json = serde_json::to_string(&report);
968 assert!(json.is_ok());
969 }
970
971 #[test]
972 fn test_allocation_hotspot_serialization() {
973 let hotspot = AllocationHotspot {
974 var_name: "test".to_string(),
975 type_name: "Vec<u8>".to_string(),
976 total_size: 1024,
977 allocation_count: 10,
978 location: Some("test.rs:1".to_string()),
979 };
980
981 let json = serde_json::to_string(&hotspot);
982 assert!(json.is_ok());
983 }
984
985 #[test]
986 fn test_tracker_multiple_system_snapshots() {
987 let tracker = Tracker::new().with_system_monitoring();
988 std::thread::sleep(std::time::Duration::from_millis(10));
989 tracker.current_system_snapshot();
990
991 let snapshots = tracker.system_snapshots();
992 assert!(
993 !snapshots.is_empty(),
994 "Should have at least one system snapshot"
995 );
996 }
997
998 #[test]
999 fn test_tracker_analyze_with_hotspots() {
1000 let tracker = Tracker::new();
1001 let data1 = vec![1u8; 100];
1002 let data2 = vec![2u8; 200];
1003 let data3 = vec![3u8; 300];
1004
1005 tracker.track_as(&data1, "buffer1", "test.rs", 1);
1006 tracker.track_as(&data2, "buffer2", "test.rs", 2);
1007 tracker.track_as(&data3, "buffer3", "test.rs", 3);
1008
1009 let report = tracker.analyze();
1010 assert!(
1011 report.total_allocations >= 3,
1012 "Should have at least 3 allocations after tracking"
1013 );
1014 }
1015
1016 #[test]
1017 fn test_tracker_default() {
1018 let tracker = Tracker::default();
1019 let report = tracker.analyze();
1020 assert_eq!(report.total_allocations, 0);
1021 }
1022}