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>(
225 &self,
226 var: &T,
227 name: &str,
228 file: &str,
229 line: u32,
230 module_path: &str,
231 ) {
232 if let Ok(cfg) = self.config.lock() {
233 if cfg.sampling.sample_rate < 1.0 {
234 use std::collections::hash_map::DefaultHasher;
235 use std::hash::{Hash, Hasher};
236 let mut hasher = DefaultHasher::new();
237 let timestamp = std::time::SystemTime::now()
240 .duration_since(std::time::UNIX_EPOCH)
241 .unwrap_or_default()
242 .as_nanos();
243 timestamp.hash(&mut hasher);
244 std::thread::current().id().hash(&mut hasher);
245 name.hash(&mut hasher);
246 file.hash(&mut hasher);
247 line.hash(&mut hasher);
248 let hash = hasher.finish();
249 let threshold = (cfg.sampling.sample_rate * 1000.0) as u64;
250 if (hash % 1000) > threshold {
251 return;
252 }
253 }
254 }
255
256 self.track_inner(var, name, file, line, module_path);
257 }
258
259 #[allow(clippy::too_many_arguments)]
260 pub fn track_clone(
262 &self,
263 source_ptr: usize,
264 target_ptr: usize,
265 size: usize,
266 var_name: Option<String>,
267 type_name: Option<String>,
268 file: &str,
269 line: u32,
270 module_path: &str,
271 ) {
272 let thread_id_u64 = crate::utils::current_thread_id_u64();
273
274 let mut event = crate::event_store::MemoryEvent::clone_event(
275 source_ptr,
276 target_ptr,
277 size,
278 thread_id_u64,
279 var_name,
280 type_name,
281 );
282 event.source_file = Some(file.to_string());
283 event.source_line = Some(line);
284 event.module_path = Some(module_path.to_string());
285
286 self.event_store.record(event);
287 }
288
289 fn track_inner<T: crate::Trackable>(
290 &self,
291 var: &T,
292 name: &str,
293 file: &str,
294 line: u32,
295 module_path: &str,
296 ) {
297 let type_name = var.get_type_name().to_string();
298 let kind = var.track_kind();
299
300 let thread_id_u64 = crate::utils::current_thread_id_u64();
301
302 match kind {
303 crate::core::types::TrackKind::HeapOwner { ptr, size } => {
304 if let Err(e) = self.inner.track_allocation(ptr, size) {
306 tracing::error!("Failed to track allocation at ptr {:x}: {}", ptr, e);
307 return;
308 }
309
310 let mut event = MemoryEvent::allocate(ptr, size, thread_id_u64);
311 event.var_name = Some(name.to_string());
312 event.type_name = Some(type_name.clone());
313 event.source_file = Some(file.to_string());
314 event.source_line = Some(line);
315 event.module_path = Some(module_path.to_string());
316 self.event_store.record(event);
317
318 if let Err(e) = self.inner.associate_var(
319 ptr,
320 name.to_string(),
321 type_name,
322 Some(file),
323 Some(line),
324 ) {
325 tracing::error!("Failed to associate var '{}' at ptr {:x}: {}", name, ptr, e);
326 }
327 }
328 crate::core::types::TrackKind::StackOwner {
329 ptr: stack_ptr,
330 heap_ptr,
331 size,
332 } => {
333 if let Err(e) = self.inner.track_allocation(stack_ptr, size) {
338 tracing::error!("Failed to track allocation at ptr {:x}: {}", stack_ptr, e);
339 return;
340 }
341
342 let mut event = MemoryEvent::allocate(heap_ptr, size, thread_id_u64);
343 event.var_name = Some(name.to_string());
344 event.type_name = Some(type_name.clone());
345 event.source_file = Some(file.to_string());
346 event.source_line = Some(line);
347 event.module_path = Some(module_path.to_string());
348 event.stack_ptr = Some(stack_ptr);
350 self.event_store.record(event);
351
352 if let Err(e) = self.inner.associate_var(
353 heap_ptr,
354 name.to_string(),
355 type_name,
356 Some(file),
357 Some(line),
358 ) {
359 tracing::error!(
360 "Failed to associate var '{}' at ptr {:x}: {}",
361 name,
362 heap_ptr,
363 e
364 );
365 }
366 }
367 crate::core::types::TrackKind::Container | crate::core::types::TrackKind::Value => {
368 let mut event = MemoryEvent::metadata(
371 name.to_string(),
372 type_name,
373 thread_id_u64,
374 var.get_size_estimate(),
375 );
376 event.source_file = Some(file.to_string());
377 event.source_line = Some(line);
378 event.module_path = Some(module_path.to_string());
379 self.event_store.record(event);
380 }
381 }
382 }
383
384 pub fn track_deallocation(&self, ptr: usize) -> crate::TrackingResult<bool> {
385 let size = self.inner.get_allocation_size(ptr).unwrap_or(0);
386
387 let result = self.inner.track_deallocation(ptr)?;
388
389 if result {
391 let thread_id_u64 = crate::utils::current_thread_id_u64();
392
393 let event = MemoryEvent::deallocate(ptr, size, thread_id_u64);
394 self.event_store.record(event);
395 }
396
397 Ok(result)
398 }
399
400 pub fn events(&self) -> Vec<MemoryEvent> {
401 self.event_store.snapshot()
402 }
403
404 pub fn event_store(&self) -> &Arc<EventStore> {
405 &self.event_store
406 }
407
408 fn capture_system_snapshot(&self) {
409 let snapshot = SystemSnapshot {
410 timestamp: std::time::SystemTime::now()
411 .duration_since(std::time::UNIX_EPOCH)
412 .unwrap_or_default()
413 .as_millis() as u64,
414 cpu_usage_percent: system_monitor::cpu_usage(),
415 memory_usage_bytes: system_monitor::memory_used(),
416 memory_usage_percent: system_monitor::memory_usage_percent(),
417 thread_count: system_monitor::thread_count(),
418 disk_read_bps: system_monitor::disk_read_bps(),
419 disk_write_bps: system_monitor::disk_write_bps(),
420 network_rx_bps: system_monitor::network_rx_bps(),
421 network_tx_bps: system_monitor::network_tx_bps(),
422 gpu_usage_percent: system_monitor::gpu_memory_usage_percent(),
423 gpu_memory_used: system_monitor::gpu_memory_used(),
424 gpu_memory_total: system_monitor::gpu_memory_total(),
425 };
426
427 if let Ok(mut snapshots) = self.system_snapshots.lock() {
428 snapshots.push(snapshot);
429 }
430 }
431
432 pub fn stats(&self) -> crate::core::types::MemoryStats {
433 let stats = self.inner.get_stats().unwrap_or_default();
434 crate::core::types::MemoryStats {
435 total_allocations: stats.total_allocations as usize,
436 total_allocated: stats.total_allocated as usize,
437 active_allocations: stats.active_allocations,
438 active_memory: stats.active_memory as usize,
439 peak_allocations: stats.peak_allocations,
440 peak_memory: stats.peak_memory as usize,
441 total_deallocations: stats.total_deallocations as usize,
442 total_deallocated: stats.total_deallocated as usize,
443 leaked_allocations: stats.leaked_allocations,
444 leaked_memory: stats.leaked_memory as usize,
445 ..Default::default()
446 }
447 }
448
449 pub fn analyze(&self) -> AnalysisReport {
450 let stats = self.stats();
451 let events = self.event_store().snapshot();
452 let allocations = rebuild_allocations_from_events(&events);
453 let elapsed = self.start_time.elapsed().as_secs_f64();
454
455 let current_memory: usize = allocations.iter().map(|a| a.size).sum();
456 let peak_memory = stats.peak_memory.max(current_memory);
457
458 let mut hotspot_map: HashMap<String, (String, usize, usize)> = HashMap::new();
459 for alloc in &allocations {
460 if let Some(ref var_name) = alloc.var_name {
461 let key = var_name.clone();
462 let entry = hotspot_map.entry(key).or_insert((
463 alloc.type_name.clone().unwrap_or_default(),
464 0,
465 0,
466 ));
467 entry.1 += alloc.size;
468 entry.2 += 1;
469 }
470 }
471
472 let hotspots: Vec<AllocationHotspot> = hotspot_map
473 .into_iter()
474 .map(
475 |(var_name, (type_name, total_size, count))| AllocationHotspot {
476 var_name,
477 type_name,
478 total_size,
479 allocation_count: count,
480 location: None,
481 },
482 )
483 .collect();
484
485 let system_snapshots = self
486 .system_snapshots
487 .lock()
488 .unwrap_or_else(|e| e.into_inner())
489 .clone();
490
491 AnalysisReport {
492 total_allocations: stats.total_allocations,
493 total_deallocations: stats.total_deallocations,
494 active_allocations: allocations.len(),
495 peak_memory_bytes: peak_memory as u64,
496 current_memory_bytes: current_memory as u64,
497 allocation_rate_per_sec: if elapsed > 0.0 {
498 stats.total_allocations as f64 / elapsed
499 } else {
500 0.0
501 },
502 deallocation_rate_per_sec: if elapsed > 0.0 {
503 stats.total_deallocations as f64 / elapsed
504 } else {
505 0.0
506 },
507 hotspots,
508 system_snapshots,
509 }
510 }
511
512 pub fn inner(&self) -> &Arc<MemoryTracker> {
513 &self.inner
514 }
515
516 pub fn elapsed(&self) -> Duration {
517 self.start_time.elapsed()
518 }
519
520 pub fn system_snapshots(&self) -> Vec<SystemSnapshot> {
521 self.system_snapshots
522 .lock()
523 .unwrap_or_else(|e| e.into_inner())
524 .clone()
525 }
526
527 pub fn current_system_snapshot(&self) -> SystemSnapshot {
528 SystemSnapshot {
529 timestamp: std::time::SystemTime::now()
530 .duration_since(std::time::UNIX_EPOCH)
531 .unwrap_or_default()
532 .as_millis() as u64,
533 cpu_usage_percent: system_monitor::cpu_usage(),
534 memory_usage_bytes: system_monitor::memory_used(),
535 memory_usage_percent: system_monitor::memory_usage_percent(),
536 thread_count: system_monitor::thread_count(),
537 disk_read_bps: system_monitor::disk_read_bps(),
538 disk_write_bps: system_monitor::disk_write_bps(),
539 network_rx_bps: system_monitor::network_rx_bps(),
540 network_tx_bps: system_monitor::network_tx_bps(),
541 gpu_usage_percent: system_monitor::gpu_memory_usage_percent(),
542 gpu_memory_used: system_monitor::gpu_memory_used(),
543 gpu_memory_total: system_monitor::gpu_memory_total(),
544 }
545 }
546}
547
548impl Default for Tracker {
549 fn default() -> Self {
550 Self::new()
551 }
552}
553
554impl Drop for Tracker {
555 fn drop(&mut self) {
556 let events = self.event_store().snapshot();
558 let allocations = rebuild_allocations_from_events(&events);
559
560 let thread_id_u64 = crate::utils::current_thread_id_u64();
562
563 for alloc in &allocations {
564 let event = MemoryEvent::deallocate(alloc.ptr, alloc.size, thread_id_u64);
565 self.event_store().record(event);
566 }
567
568 if let Ok(cfg) = self.config.lock() {
569 if cfg.auto_export_on_drop {
570 if let Some(ref path) = cfg.export_path {
571 let events = self.event_store().snapshot();
573 let allocations = rebuild_allocations_from_events(&events);
574 let snapshot = MemorySnapshot::from_allocation_infos(allocations);
575 let options = ExportJsonOptions::default();
576 if let Err(e) =
577 export_snapshot_to_json(&snapshot, std::path::Path::new(path), &options)
578 {
579 tracing::error!("Failed to auto-export on drop: {}", e);
580 }
581 }
582 }
583 }
584 }
585}
586
587impl serde::Serialize for AnalysisReport {
588 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
589 where
590 S: serde::Serializer,
591 {
592 use serde::ser::SerializeStruct;
593 let mut state = serializer.serialize_struct("AnalysisReport", 9)?;
594 state.serialize_field("total_allocations", &self.total_allocations)?;
595 state.serialize_field("total_deallocations", &self.total_deallocations)?;
596 state.serialize_field("active_allocations", &self.active_allocations)?;
597 state.serialize_field("peak_memory_bytes", &self.peak_memory_bytes)?;
598 state.serialize_field("current_memory_bytes", &self.current_memory_bytes)?;
599 state.serialize_field("allocation_rate_per_sec", &self.allocation_rate_per_sec)?;
600 state.serialize_field("deallocation_rate_per_sec", &self.deallocation_rate_per_sec)?;
601 state.serialize_field("hotspots", &self.hotspots)?;
602 state.serialize_field("system_snapshots", &self.system_snapshots)?;
603 state.end()
604 }
605}
606
607impl serde::Serialize for AllocationHotspot {
608 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
609 where
610 S: serde::Serializer,
611 {
612 use serde::ser::SerializeStruct;
613 let mut state = serializer.serialize_struct("AllocationHotspot", 5)?;
614 state.serialize_field("var_name", &self.var_name)?;
615 state.serialize_field("type_name", &self.type_name)?;
616 state.serialize_field("total_size", &self.total_size)?;
617 state.serialize_field("allocation_count", &self.allocation_count)?;
618 state.serialize_field("location", &self.location)?;
619 state.end()
620 }
621}
622
623#[macro_export]
624macro_rules! tracker {
625 () => {
626 $crate::tracker::Tracker::new()
627 };
628}
629
630#[macro_export]
631macro_rules! track {
632 ($tracker:expr, $var:expr) => {{
633 let var_name = stringify!($var);
634 $tracker.track_as(&$var, var_name, file!(), line!(), module_path!());
635 }};
636}
637
638#[macro_export]
639macro_rules! track_clone {
640 ($tracker:expr, $source:expr, $target:expr) => {{
641 let source_name = stringify!($source);
642 let target_name = stringify!($target);
643 let source_ptr = &$source as *const _ as usize;
644 let target_ptr = &$target as *const _ as usize;
645 let type_name = $crate::utils::type_of(&$target);
646 $tracker.track_clone(
647 source_ptr,
648 target_ptr,
649 std::mem::size_of_val(&$target),
650 Some(target_name.to_string()),
651 Some(type_name.to_string()),
652 file!(),
653 line!(),
654 module_path!(),
655 );
656 }};
657}
658
659#[cfg(test)]
660mod tests {
661 use super::*;
662
663 #[test]
664 fn test_tracker_creation() {
665 let tracker = Tracker::new();
666 let _ = tracker;
667 }
668
669 #[test]
670 fn test_tracker_with_config() {
671 let tracker = Tracker::new()
672 .with_sampling(SamplingConfig::demo())
673 .with_system_monitoring();
674 let _ = tracker;
675 }
676
677 #[test]
678 fn test_track_macro() {
679 let tracker = tracker!();
680 let my_vec = vec![1, 2, 3];
681 track!(tracker, my_vec);
682 }
683
684 #[test]
685 fn test_analyze() {
686 let tracker = tracker!();
687 let data = vec![1, 2, 3];
688 track!(tracker, data);
689 let report = tracker.analyze();
690 assert!(report.total_allocations > 0);
691 }
692
693 #[test]
694 #[cfg(target_os = "macos")]
695 fn test_system_monitoring() {
696 std::thread::sleep(std::time::Duration::from_millis(200));
697
698 let cpu = system_monitor::cpu_usage();
699 let mem = system_monitor::memory_used();
700 let total = system_monitor::memory_total();
701
702 println!("CPU: {:.2}%", cpu);
703 println!("Memory: {} / {} bytes", mem, total);
704
705 assert!((0.0..=100.0).contains(&cpu));
706 assert!(total > 0);
707 }
708
709 #[test]
710 fn test_current_system_snapshot() {
711 std::thread::sleep(std::time::Duration::from_millis(150));
712
713 let tracker = tracker!();
714 let snapshot = tracker.current_system_snapshot();
715
716 println!(
717 "Snapshot: CPU={:.2}%, Mem={:.2}%",
718 snapshot.cpu_usage_percent, snapshot.memory_usage_percent
719 );
720
721 assert!(snapshot.cpu_usage_percent >= 0.0 && snapshot.cpu_usage_percent <= 100.0);
722 }
723
724 #[test]
725 fn test_sampling_config_default() {
726 let config = SamplingConfig::default();
727 assert_eq!(config.sample_rate, 1.0);
728 assert!(!config.capture_call_stack);
729 assert_eq!(config.max_stack_depth, 10);
730 }
731
732 #[test]
733 fn test_sampling_config_demo() {
734 let config = SamplingConfig::demo();
735 assert_eq!(config.sample_rate, 0.1);
736 assert!(!config.capture_call_stack);
737 assert_eq!(config.max_stack_depth, 5);
738 }
739
740 #[test]
741 fn test_sampling_config_full() {
742 let config = SamplingConfig::full();
743 assert_eq!(config.sample_rate, 1.0);
744 assert!(config.capture_call_stack);
745 assert_eq!(config.max_stack_depth, 20);
746 }
747
748 #[test]
749 fn test_sampling_config_high_performance() {
750 let config = SamplingConfig::high_performance();
751 assert_eq!(config.sample_rate, 0.01);
752 assert!(!config.capture_call_stack);
753 assert_eq!(config.max_stack_depth, 0);
754 }
755
756 #[test]
757 fn test_sampling_config_clone() {
758 let config = SamplingConfig::full();
759 let cloned = config.clone();
760 assert_eq!(cloned.sample_rate, config.sample_rate);
761 assert_eq!(cloned.capture_call_stack, config.capture_call_stack);
762 }
763
764 #[test]
765 fn test_sampling_config_debug() {
766 let config = SamplingConfig::default();
767 let debug_str = format!("{:?}", config);
768 assert!(debug_str.contains("SamplingConfig"));
769 assert!(debug_str.contains("sample_rate"));
770 }
771
772 #[test]
773 fn test_system_snapshot_default() {
774 let snapshot = SystemSnapshot::default();
775 assert_eq!(snapshot.timestamp, 0);
776 assert_eq!(snapshot.cpu_usage_percent, 0.0);
777 assert_eq!(snapshot.memory_usage_bytes, 0);
778 assert_eq!(snapshot.thread_count, 0);
779 }
780
781 #[test]
782 fn test_system_snapshot_clone() {
783 let snapshot = SystemSnapshot {
784 timestamp: 1000,
785 cpu_usage_percent: 50.0,
786 memory_usage_bytes: 1024 * 1024,
787 memory_usage_percent: 25.0,
788 thread_count: 4,
789 disk_read_bps: 1000,
790 disk_write_bps: 500,
791 network_rx_bps: 2000,
792 network_tx_bps: 1000,
793 gpu_usage_percent: 30.0,
794 gpu_memory_used: 512 * 1024 * 1024,
795 gpu_memory_total: 2 * 1024 * 1024 * 1024,
796 };
797
798 let cloned = snapshot.clone();
799 assert_eq!(cloned.timestamp, 1000);
800 assert_eq!(cloned.cpu_usage_percent, 50.0);
801 }
802
803 #[test]
804 fn test_system_snapshot_debug() {
805 let snapshot = SystemSnapshot::default();
806 let debug_str = format!("{:?}", snapshot);
807 assert!(debug_str.contains("SystemSnapshot"));
808 }
809
810 #[test]
811 fn test_analysis_report_creation() {
812 let report = AnalysisReport {
813 total_allocations: 100,
814 total_deallocations: 50,
815 active_allocations: 50,
816 peak_memory_bytes: 1024 * 1024,
817 current_memory_bytes: 512 * 1024,
818 allocation_rate_per_sec: 10.0,
819 deallocation_rate_per_sec: 5.0,
820 hotspots: vec![],
821 system_snapshots: vec![],
822 };
823
824 assert_eq!(report.total_allocations, 100);
825 assert_eq!(report.active_allocations, 50);
826 }
827
828 #[test]
829 fn test_analysis_report_clone() {
830 let report = AnalysisReport {
831 total_allocations: 10,
832 total_deallocations: 5,
833 active_allocations: 5,
834 peak_memory_bytes: 1024,
835 current_memory_bytes: 512,
836 allocation_rate_per_sec: 1.0,
837 deallocation_rate_per_sec: 0.5,
838 hotspots: vec![],
839 system_snapshots: vec![],
840 };
841
842 let cloned = report.clone();
843 assert_eq!(cloned.total_allocations, 10);
844 }
845
846 #[test]
847 fn test_analysis_report_debug() {
848 let report = AnalysisReport {
849 total_allocations: 0,
850 total_deallocations: 0,
851 active_allocations: 0,
852 peak_memory_bytes: 0,
853 current_memory_bytes: 0,
854 allocation_rate_per_sec: 0.0,
855 deallocation_rate_per_sec: 0.0,
856 hotspots: vec![],
857 system_snapshots: vec![],
858 };
859
860 let debug_str = format!("{:?}", report);
861 assert!(debug_str.contains("AnalysisReport"));
862 }
863
864 #[test]
865 fn test_allocation_hotspot_creation() {
866 let hotspot = AllocationHotspot {
867 var_name: "my_vec".to_string(),
868 type_name: "Vec<u8>".to_string(),
869 total_size: 1024,
870 allocation_count: 10,
871 location: Some("main.rs:42".to_string()),
872 };
873
874 assert_eq!(hotspot.var_name, "my_vec");
875 assert_eq!(hotspot.total_size, 1024);
876 }
877
878 #[test]
879 fn test_allocation_hotspot_clone() {
880 let hotspot = AllocationHotspot {
881 var_name: "data".to_string(),
882 type_name: "String".to_string(),
883 total_size: 100,
884 allocation_count: 5,
885 location: None,
886 };
887
888 let cloned = hotspot.clone();
889 assert_eq!(cloned.var_name, "data");
890 }
891
892 #[test]
893 fn test_allocation_hotspot_debug() {
894 let hotspot = AllocationHotspot {
895 var_name: "test".to_string(),
896 type_name: "i32".to_string(),
897 total_size: 4,
898 allocation_count: 1,
899 location: None,
900 };
901
902 let debug_str = format!("{:?}", hotspot);
903 assert!(debug_str.contains("AllocationHotspot"));
904 }
905
906 #[test]
907 fn test_tracker_clone() {
908 let tracker = Tracker::new();
909 let cloned = tracker.clone();
910
911 let report1 = tracker.analyze();
912 let report2 = cloned.analyze();
913
914 assert_eq!(report1.total_allocations, report2.total_allocations);
916 }
917
918 #[test]
919 fn test_tracker_with_sampling() {
920 let tracker = Tracker::new().with_sampling(SamplingConfig::high_performance());
921 let data = vec![1, 2, 3];
922 tracker.track_as(&data, "data", "test.rs", 1, "test_module");
923 }
924
925 #[test]
926 fn test_tracker_elapsed() {
927 let tracker = Tracker::new();
928 std::thread::sleep(std::time::Duration::from_millis(10));
929 let elapsed = tracker.elapsed();
930 assert!(elapsed >= std::time::Duration::from_millis(10));
931 }
932
933 #[test]
934 fn test_tracker_with_system_monitoring() {
935 let tracker = Tracker::new().with_system_monitoring();
936 let _ = tracker.current_system_snapshot();
937 }
938
939 #[test]
940 fn test_tracker_track_as_multiple() {
941 let tracker = Tracker::new();
942 let data = vec![1, 2, 3, 4, 5];
943
944 tracker.track_as(&data, "my_vec", "test.rs", 10, "test_module");
945 tracker.track_as(&data, "my_vec", "test.rs", 20, "test_module");
946
947 let report = tracker.analyze();
948 let _ = report.total_allocations;
949 }
950
951 #[test]
952 fn test_sampling_config_custom() {
953 let config = SamplingConfig {
954 sample_rate: 0.5,
955 capture_call_stack: true,
956 max_stack_depth: 15,
957 };
958
959 assert!((config.sample_rate - 0.5).abs() < 0.001);
960 assert!(config.capture_call_stack);
961 assert_eq!(config.max_stack_depth, 15);
962 }
963
964 #[test]
965 fn test_analysis_report_with_hotspots() {
966 let report = AnalysisReport {
967 total_allocations: 100,
968 total_deallocations: 50,
969 active_allocations: 50,
970 peak_memory_bytes: 1024 * 1024,
971 current_memory_bytes: 512 * 1024,
972 allocation_rate_per_sec: 10.0,
973 deallocation_rate_per_sec: 5.0,
974 hotspots: vec![AllocationHotspot {
975 var_name: "test".to_string(),
976 type_name: "Vec<u8>".to_string(),
977 total_size: 1024,
978 allocation_count: 10,
979 location: Some("test.rs:1".to_string()),
980 }],
981 system_snapshots: vec![],
982 };
983
984 assert_eq!(report.hotspots.len(), 1);
985 }
986
987 #[test]
988 fn test_tracker_with_sampling_and_monitoring() {
989 let tracker = Tracker::new()
990 .with_sampling(SamplingConfig::demo())
991 .with_system_monitoring();
992
993 let data = vec![1, 2, 3];
994 tracker.track_as(&data, "data", "test.rs", 1, "test_module");
995
996 let snapshot = tracker.current_system_snapshot();
997 assert!(snapshot.cpu_usage_percent >= 0.0);
998 }
999
1000 #[test]
1001 fn test_tracker_events() {
1002 let tracker = Tracker::new();
1003 let data = vec![1, 2, 3];
1004 tracker.track_as(&data, "test_data", "test.rs", 1, "test_module");
1005
1006 let events = tracker.events();
1007 assert!(!events.is_empty());
1008 }
1009
1010 #[test]
1011 fn test_tracker_event_store() {
1012 let tracker = Tracker::new();
1013 let _store = tracker.event_store();
1014 }
1015
1016 #[test]
1017 fn test_tracker_stats() {
1018 let tracker = Tracker::new();
1019 let stats = tracker.stats();
1020
1021 assert_eq!(stats.total_allocations, 0);
1022 assert_eq!(stats.active_allocations, 0);
1023 }
1024
1025 #[test]
1026 fn test_tracker_stats_with_data() {
1027 let tracker = Tracker::new();
1028 let data = vec![1u8; 1024];
1029 tracker.track_as(&data, "buffer", "test.rs", 1, "test_module");
1030
1031 let stats = tracker.stats();
1032 assert!(
1033 stats.total_allocations >= 1,
1034 "Should have at least one allocation after tracking data"
1035 );
1036 }
1037
1038 #[test]
1039 fn test_tracker_system_snapshots() {
1040 let tracker = Tracker::new().with_system_monitoring();
1041 let snapshots = tracker.system_snapshots();
1042 assert!(!snapshots.is_empty());
1043 }
1044
1045 #[test]
1046 fn test_tracker_inner() {
1047 let tracker = Tracker::new();
1048 let _inner = tracker.inner();
1049 }
1050
1051 #[test]
1052 fn test_tracker_with_auto_export() {
1053 let tracker = Tracker::new().with_auto_export("/tmp/test_export");
1054 let data = vec![1, 2, 3];
1055 tracker.track_as(&data, "test", "test.rs", 1, "test_module");
1056 }
1057
1058 #[test]
1059 fn test_sampling_config_zero_rate() {
1060 let config = SamplingConfig {
1061 sample_rate: 0.0,
1062 capture_call_stack: false,
1063 max_stack_depth: 0,
1064 };
1065
1066 let tracker = Tracker::new().with_sampling(config);
1067 let data = vec![1, 2, 3];
1068 tracker.track_as(&data, "test", "test.rs", 1, "test_module");
1069 }
1070
1071 #[test]
1072 fn test_analysis_report_serialization() {
1073 let report = AnalysisReport {
1074 total_allocations: 100,
1075 total_deallocations: 50,
1076 active_allocations: 50,
1077 peak_memory_bytes: 1024,
1078 current_memory_bytes: 512,
1079 allocation_rate_per_sec: 10.0,
1080 deallocation_rate_per_sec: 5.0,
1081 hotspots: vec![],
1082 system_snapshots: vec![],
1083 };
1084
1085 let json = serde_json::to_string(&report);
1086 assert!(json.is_ok());
1087 }
1088
1089 #[test]
1090 fn test_allocation_hotspot_serialization() {
1091 let hotspot = AllocationHotspot {
1092 var_name: "test".to_string(),
1093 type_name: "Vec<u8>".to_string(),
1094 total_size: 1024,
1095 allocation_count: 10,
1096 location: Some("test.rs:1".to_string()),
1097 };
1098
1099 let json = serde_json::to_string(&hotspot);
1100 assert!(json.is_ok());
1101 }
1102
1103 #[test]
1104 fn test_tracker_multiple_system_snapshots() {
1105 let tracker = Tracker::new().with_system_monitoring();
1106 std::thread::sleep(std::time::Duration::from_millis(10));
1107 tracker.current_system_snapshot();
1108
1109 let snapshots = tracker.system_snapshots();
1110 assert!(
1111 !snapshots.is_empty(),
1112 "Should have at least one system snapshot"
1113 );
1114 }
1115
1116 #[test]
1117 fn test_tracker_analyze_with_hotspots() {
1118 let tracker = Tracker::new();
1119 let data1 = vec![1u8; 100];
1120 let data2 = vec![2u8; 200];
1121 let data3 = vec![3u8; 300];
1122
1123 tracker.track_as(&data1, "buffer1", "test.rs", 1, "test_module");
1124 tracker.track_as(&data2, "buffer2", "test.rs", 2, "test_module");
1125 tracker.track_as(&data3, "buffer3", "test.rs", 3, "test_module");
1126
1127 let report = tracker.analyze();
1128 assert!(
1129 report.total_allocations >= 3,
1130 "Should have at least 3 allocations after tracking"
1131 );
1132 }
1133
1134 #[test]
1135 fn test_tracker_default() {
1136 let tracker = Tracker::default();
1137 let report = tracker.analyze();
1138 assert_eq!(report.total_allocations, 0);
1139 }
1140}