1use crate::core::bounded_memory_stats::{
7 AllocationHistoryManager, BoundedMemoryStats, BoundedStatsConfig,
8};
9use crate::core::ownership_history::{HistoryConfig, OwnershipHistoryRecorder};
10use crate::core::safe_operations::SafeLock;
11use crate::core::types::{
12 AllocationInfo, DropChainNode, DropChainPerformanceMetrics, EnhancedPotentialLeak,
13 LeakEvidence, LeakEvidenceType, LeakImpact, LeakRiskLevel, LeakType, MemoryStats,
14 ResourceLeakAnalysis, TrackingError::LockError, TrackingResult,
15};
16
17use std::collections::HashMap;
18use std::sync::atomic::{AtomicU8, Ordering};
19use std::sync::{Arc, Mutex, OnceLock};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum BinaryExportMode {
24 UserOnly,
27 Full,
30}
31
32impl Default for BinaryExportMode {
33 fn default() -> Self {
35 BinaryExportMode::UserOnly
36 }
37}
38
39const STRATEGY_GLOBAL_SINGLETON: u8 = 0;
41const STRATEGY_THREAD_LOCAL: u8 = 1;
42
43static TRACKING_STRATEGY: AtomicU8 = AtomicU8::new(STRATEGY_GLOBAL_SINGLETON);
45
46static GLOBAL_TRACKER: OnceLock<Arc<MemoryTracker>> = OnceLock::new();
48
49thread_local! {
51 static THREAD_LOCAL_TRACKER: Arc<MemoryTracker> = {
52 let tracker = Arc::new(MemoryTracker::new());
53 crate::core::thread_registry::register_current_thread_tracker(&tracker);
55 tracker
56 };
57}
58
59pub fn configure_tracking_strategy(is_concurrent: bool) {
67 let strategy = if is_concurrent {
68 STRATEGY_THREAD_LOCAL
69 } else {
70 STRATEGY_GLOBAL_SINGLETON
71 };
72
73 TRACKING_STRATEGY.store(strategy, Ordering::Relaxed);
74
75 tracing::info!(
76 "Configured tracking strategy: {}",
77 if is_concurrent {
78 "thread-local"
79 } else {
80 "global-singleton"
81 }
82 );
83}
84
85pub fn get_tracker() -> Arc<MemoryTracker> {
91 match TRACKING_STRATEGY.load(Ordering::Relaxed) {
92 STRATEGY_GLOBAL_SINGLETON => GLOBAL_TRACKER
93 .get_or_init(|| Arc::new(MemoryTracker::new()))
94 .clone(),
95 STRATEGY_THREAD_LOCAL => THREAD_LOCAL_TRACKER.with(|tracker| tracker.clone()),
96 _ => {
97 tracing::warn!("Unknown tracking strategy, falling back to global singleton");
99 GLOBAL_TRACKER
100 .get_or_init(|| Arc::new(MemoryTracker::new()))
101 .clone()
102 }
103 }
104}
105
106#[deprecated(note = "Use get_tracker() instead for dual-mode support")]
111pub fn get_global_tracker() -> Arc<MemoryTracker> {
112 get_tracker()
113}
114
115pub struct MemoryTracker {
120 pub(crate) active_allocations: Mutex<HashMap<usize, AllocationInfo>>,
122 pub(crate) bounded_stats: Mutex<BoundedMemoryStats>,
124 pub(crate) history_manager: Mutex<AllocationHistoryManager>,
126 pub(crate) ownership_history: Mutex<OwnershipHistoryRecorder>,
128 pub(crate) stats: Mutex<MemoryStats>,
130 pub(crate) fast_mode: std::sync::atomic::AtomicBool,
132}
133
134impl MemoryTracker {
135 pub fn new() -> Self {
137 let fast_mode =
138 std::env::var("MEMSCOPE_TEST_MODE").is_ok() || cfg!(test) || cfg!(feature = "test");
139
140 let config = if fast_mode {
142 BoundedStatsConfig {
144 max_recent_allocations: 1_000,
145 max_historical_summaries: 100,
146 enable_auto_cleanup: true,
147 cleanup_threshold: 0.8,
148 }
149 } else {
150 BoundedStatsConfig::default()
152 };
153
154 let history_config = if fast_mode {
156 HistoryConfig {
157 max_events_per_allocation: 10,
158 track_borrowing: false,
159 track_cloning: true,
160 track_ownership_transfers: false,
161 }
162 } else {
163 HistoryConfig::default()
164 };
165
166 Self {
167 active_allocations: Mutex::new(HashMap::new()),
168 bounded_stats: Mutex::new(BoundedMemoryStats::with_config(config.clone())),
169 history_manager: Mutex::new(AllocationHistoryManager::with_config(config)),
170 ownership_history: Mutex::new(OwnershipHistoryRecorder::with_config(history_config)),
171 stats: Mutex::new(MemoryStats::default()),
172 fast_mode: std::sync::atomic::AtomicBool::new(fast_mode),
173 }
174 }
175
176 pub fn get_stats(&self) -> TrackingResult<MemoryStats> {
178 let bounded_stats = self
180 .bounded_stats
181 .safe_lock()
182 .map(|stats| stats.clone())
183 .unwrap_or_else(|_| crate::core::bounded_memory_stats::BoundedMemoryStats::default());
184
185 let _history = self
187 .history_manager
188 .safe_lock()
189 .map(|manager| manager.get_history_vec())
190 .unwrap_or_else(|_| Vec::new());
191
192 let legacy_stats = MemoryStats {
194 total_allocations: bounded_stats.total_allocations,
195 total_allocated: bounded_stats.total_allocated,
196 active_allocations: bounded_stats.active_allocations,
197 active_memory: bounded_stats.active_memory,
198 peak_allocations: bounded_stats.peak_allocations,
199 peak_memory: bounded_stats.peak_memory,
200 total_deallocations: bounded_stats.total_deallocations,
201 total_deallocated: bounded_stats.total_deallocated,
202 leaked_allocations: bounded_stats.leaked_allocations,
203 leaked_memory: bounded_stats.leaked_memory,
204 fragmentation_analysis: bounded_stats.fragmentation_analysis.clone(),
205 lifecycle_stats: bounded_stats.lifecycle_stats.clone(),
206 system_library_stats: bounded_stats.system_library_stats.clone(),
207 concurrency_analysis: bounded_stats.concurrency_analysis.clone(),
208 allocations: bounded_stats.get_all_allocations(),
210 };
211
212 if let Ok(mut stats) = self.stats.safe_lock() {
214 *stats = legacy_stats.clone();
215 }
216
217 Ok(legacy_stats)
218 }
219
220 pub fn get_active_allocations(&self) -> TrackingResult<Vec<AllocationInfo>> {
222 self.active_allocations
223 .safe_lock()
224 .map(|active| active.values().cloned().collect())
225 .map_err(|e| LockError(format!("Failed to get active allocations: {e}",)))
226 }
227
228 pub fn get_allocation_history(&self) -> TrackingResult<Vec<AllocationInfo>> {
230 self.history_manager
231 .safe_lock()
232 .map(|manager| manager.get_history_vec())
233 .map_err(|e| LockError(format!("Failed to get allocation history: {e}",)))
234 }
235
236 pub fn set_fast_mode(&self, enabled: bool) {
238 self.fast_mode
239 .store(enabled, std::sync::atomic::Ordering::Relaxed);
240 }
241
242 pub fn is_fast_mode(&self) -> bool {
244 self.fast_mode.load(std::sync::atomic::Ordering::Relaxed)
245 }
246
247 pub fn enable_fast_mode(&self) {
249 self.fast_mode
250 .store(true, std::sync::atomic::Ordering::Relaxed);
251 }
252
253 pub fn export_memory_analysis<P: AsRef<std::path::Path>>(&self, path: P) -> TrackingResult<()> {
259 let output_path = self.ensure_memory_analysis_path(path);
260 crate::export::visualization::export_memory_analysis(self, output_path)
261 }
262
263 pub fn ensure_memory_analysis_path<P: AsRef<std::path::Path>>(
265 &self,
266 path: P,
267 ) -> std::path::PathBuf {
268 let path = path.as_ref();
269 let memory_analysis_dir = std::path::Path::new("MemoryAnalysis");
270
271 if let Err(e) = std::fs::create_dir_all(memory_analysis_dir) {
273 tracing::warn!("Failed to create MemoryAnalysis directory: {}", e);
274 }
275
276 memory_analysis_dir.join(path)
277 }
278
279 pub fn export_to_binary<P: AsRef<std::path::Path>>(&self, path: P) -> TrackingResult<()> {
293 self.export_user_binary(path)
295 }
296
297 pub fn export_to_binary_with_mode<P: AsRef<std::path::Path>>(
316 &self,
317 path: P,
318 mode: BinaryExportMode,
319 ) -> TrackingResult<()> {
320 match mode {
321 BinaryExportMode::UserOnly => {
322 tracing::info!("Using strict filtering for user-only binary export");
323 self.export_user_binary(path)
324 }
325 BinaryExportMode::Full => {
326 tracing::info!("Using loose filtering for full binary export");
327 self.export_full_binary(path)
328 }
329 }
330 }
331
332 pub fn export_user_binary<P: AsRef<std::path::Path>>(&self, path: P) -> TrackingResult<()> {
347 let output_path = self.ensure_memscope_path(path);
348
349 tracing::info!("Starting user binary export to: {}", output_path.display());
350
351 let all_allocations = self.get_active_allocations()?;
352
353 let user_allocations: Vec<_> = all_allocations
356 .into_iter()
357 .filter(|allocation| allocation.var_name.is_some())
358 .collect();
359
360 tracing::info!(
361 "Filtered {} user allocations for export (excluding system allocations)",
362 user_allocations.len()
363 );
364
365 crate::export::binary::export_to_binary_with_mode(
366 &user_allocations,
367 output_path,
368 crate::export::binary::format::BinaryExportMode::UserOnly,
369 &crate::export::binary::BinaryExportConfig::default(),
370 )
371 .map_err(|e| crate::core::types::TrackingError::ExportError(e.to_string()))?;
372
373 tracing::info!("User binary export completed successfully");
374 Ok(())
375 }
376
377 pub fn export_full_binary<P: AsRef<std::path::Path>>(&self, path: P) -> TrackingResult<()> {
391 let output_path = self.ensure_memscope_path(path);
392
393 tracing::info!("Starting full binary export to: {}", output_path.display());
394
395 let all_allocations = self.get_active_allocations()?;
396
397 tracing::info!(
398 "Exporting {} total allocations (user + system)",
399 all_allocations.len()
400 );
401
402 crate::export::binary::export_to_binary_with_mode(
405 &all_allocations,
406 output_path,
407 crate::export::binary::format::BinaryExportMode::Full,
408 &crate::export::binary::BinaryExportConfig::default(),
409 )
410 .map_err(|e| crate::core::types::TrackingError::ExportError(e.to_string()))?;
411
412 tracing::info!("Full binary export completed successfully");
413 Ok(())
414 }
415
416 fn ensure_memscope_path<P: AsRef<std::path::Path>>(&self, path: P) -> std::path::PathBuf {
418 let mut output_path = self.ensure_memory_analysis_path(path);
419
420 if output_path.extension().is_none()
422 || output_path.extension() != Some(std::ffi::OsStr::new("memscope"))
423 {
424 output_path.set_extension("memscope");
425 }
426
427 output_path
428 }
429
430 pub fn parse_binary_to_standard_json<P: AsRef<std::path::Path>>(
446 binary_path: P,
447 base_name: &str,
448 ) -> TrackingResult<()> {
449 crate::export::binary::BinaryParser::to_standard_json_files(binary_path, base_name)
450 .map_err(|e| crate::core::types::TrackingError::ExportError(e.to_string()))
451 }
452
453 pub fn parse_binary_to_json<P: AsRef<std::path::Path>>(
461 binary_path: P,
462 json_path: P,
463 ) -> TrackingResult<()> {
464 crate::export::binary::parse_binary_to_json(binary_path, json_path)
465 .map_err(|e| crate::core::types::TrackingError::ExportError(e.to_string()))
466 }
467
468 pub fn parse_binary_to_html<P: AsRef<std::path::Path>>(
484 binary_path: P,
485 html_path: P,
486 ) -> TrackingResult<()> {
487 crate::export::binary::parse_binary_to_html(binary_path, html_path)
488 .map_err(|e| crate::core::types::TrackingError::ExportError(e.to_string()))
489 }
490
491 pub fn export_binary_to_html<P: AsRef<std::path::Path>>(
493 binary_path: P,
494 html_path: P,
495 ) -> TrackingResult<()> {
496 Self::parse_binary_to_html(binary_path, html_path)
497 }
498
499 pub fn export_lifecycle_timeline<P: AsRef<std::path::Path>>(
506 &self,
507 path: P,
508 ) -> TrackingResult<()> {
509 let output_path = self.ensure_memory_analysis_path(path);
510 crate::export::visualization::export_lifecycle_timeline(self, output_path)
511 }
512
513 pub fn analyze_drop_chain(
515 &self,
516 ptr: usize,
517 type_name: &str,
518 ) -> Option<crate::core::types::DropChainAnalysis> {
519 let start_time = std::time::SystemTime::now()
520 .duration_since(std::time::UNIX_EPOCH)
521 .unwrap_or_default()
522 .as_nanos() as u64;
523
524 let root_node = self.create_drop_chain_node(ptr, type_name, start_time);
526
527 let ownership_hierarchy = self.analyze_ownership_hierarchy(ptr, type_name);
529
530 let drop_sequence = self.build_drop_sequence(ptr, type_name, &ownership_hierarchy);
532
533 let performance_metrics = self.calculate_drop_chain_performance(&drop_sequence);
535
536 let leak_detection = self.detect_resource_leaks(ptr, type_name, &ownership_hierarchy);
538
539 let end_time = std::time::SystemTime::now()
540 .duration_since(std::time::UNIX_EPOCH)
541 .unwrap_or_default()
542 .as_nanos() as u64;
543
544 Some(crate::core::types::DropChainAnalysis {
545 root_object: root_node,
546 drop_sequence,
547 total_duration_ns: end_time - start_time,
548 performance_metrics,
549 ownership_hierarchy,
550 leak_detection,
551 })
552 }
553
554 fn create_drop_chain_node(
556 &self,
557 ptr: usize,
558 type_name: &str,
559 timestamp: u64,
560 ) -> crate::core::types::DropChainNode {
561 let drop_impl_type = self.determine_drop_implementation_type(type_name);
562 let cleanup_actions = self.analyze_cleanup_actions(type_name);
563 let performance_characteristics = self.analyze_drop_performance_characteristics(type_name);
564
565 let drop_duration_ns = self.estimate_drop_duration(type_name);
567
568 let children = self.find_child_objects_for_drop(ptr, type_name);
570
571 crate::core::types::DropChainNode {
572 object_id: ptr,
573 type_name: type_name.to_string(),
574 drop_timestamp: timestamp,
575 drop_duration_ns,
576 children,
577 drop_impl_type,
578 cleanup_actions,
579 performance_characteristics,
580 }
581 }
582
583 fn determine_drop_implementation_type(
585 &self,
586 type_name: &str,
587 ) -> crate::core::types::DropImplementationType {
588 use crate::core::types::DropImplementationType;
589
590 if type_name.starts_with("Box<")
591 || type_name.starts_with("Rc<")
592 || type_name.starts_with("Arc<")
593 {
594 DropImplementationType::SmartPointer
595 } else if type_name.starts_with("Vec<")
596 || type_name.starts_with("HashMap<")
597 || type_name.starts_with("BTreeMap<")
598 || type_name.starts_with("HashSet<")
599 {
600 DropImplementationType::Collection
601 } else if type_name.contains("File")
602 || type_name.contains("Socket")
603 || type_name.contains("Handle")
604 || type_name.contains("Stream")
605 {
606 DropImplementationType::ResourceHandle
607 } else if self.is_copy_type(type_name) {
608 DropImplementationType::NoOp
609 } else if self.has_custom_drop_impl(type_name) {
610 DropImplementationType::Custom
611 } else {
612 DropImplementationType::Automatic
613 }
614 }
615
616 fn is_copy_type(&self, type_name: &str) -> bool {
618 matches!(
619 type_name,
620 "i8" | "i16"
621 | "i32"
622 | "i64"
623 | "i128"
624 | "isize"
625 | "u8"
626 | "u16"
627 | "u32"
628 | "u64"
629 | "u128"
630 | "usize"
631 | "f32"
632 | "f64"
633 | "bool"
634 | "char"
635 | "&str"
636 | "&[u8]"
637 ) || type_name.starts_with("&")
638 || type_name.starts_with("*")
639 }
640
641 fn has_custom_drop_impl(&self, type_name: &str) -> bool {
643 type_name.contains("Guard")
646 || type_name.contains("Lock")
647 || type_name.contains("Mutex")
648 || type_name.contains("RwLock")
649 || type_name.contains("Channel")
650 || type_name.contains("Receiver")
651 || type_name.contains("Sender")
652 }
653
654 fn analyze_cleanup_actions(&self, type_name: &str) -> Vec<crate::core::types::CleanupAction> {
656 use crate::core::types::{CleanupAction, CleanupActionType};
657
658 let mut actions = Vec::new();
659 let timestamp = std::time::SystemTime::now()
660 .duration_since(std::time::UNIX_EPOCH)
661 .unwrap_or_default()
662 .as_nanos() as u64;
663
664 if type_name.starts_with("Box<") || type_name.starts_with("Vec<") {
665 actions.push(CleanupAction {
666 action_type: CleanupActionType::MemoryDeallocation,
667 timestamp,
668 duration_ns: 100, resource_description: format!("Heap memory for {type_name}"),
670 success: true,
671 });
672 }
673
674 if type_name.contains("File") {
675 actions.push(CleanupAction {
676 action_type: CleanupActionType::FileHandleClosure,
677 timestamp,
678 duration_ns: 1000, resource_description: "File handle closure".to_string(),
680 success: true,
681 });
682 }
683
684 if type_name.contains("Socket") || type_name.contains("TcpStream") {
685 actions.push(CleanupAction {
686 action_type: CleanupActionType::NetworkConnectionClosure,
687 timestamp,
688 duration_ns: 5000, resource_description: "Network connection closure".to_string(),
690 success: true,
691 });
692 }
693
694 if type_name.contains("Mutex") || type_name.contains("RwLock") {
695 actions.push(CleanupAction {
696 action_type: CleanupActionType::LockRelease,
697 timestamp,
698 duration_ns: 50, resource_description: format!("Lock release for {type_name}"),
700 success: true,
701 });
702 }
703
704 if type_name.starts_with("Rc<") || type_name.starts_with("Arc<") {
705 actions.push(CleanupAction {
706 action_type: CleanupActionType::ReferenceCountDecrement,
707 timestamp,
708 duration_ns: 20, resource_description: "Reference count decrement".to_string(),
710 success: true,
711 });
712 }
713
714 actions
715 }
716
717 fn analyze_drop_performance_characteristics(
719 &self,
720 type_name: &str,
721 ) -> crate::core::types::DropPerformanceCharacteristics {
722 use crate::core::types::{DropPerformanceCharacteristics, ImpactLevel};
723
724 let (execution_time_ns, cpu_usage, memory_ops, io_ops, syscalls, impact) =
725 if type_name.starts_with("Vec<") || type_name.starts_with("HashMap<") {
726 (1000, 5.0, 10, 0, 1, ImpactLevel::Low)
727 } else if type_name.contains("File") || type_name.contains("Socket") {
728 (10000, 2.0, 1, 5, 3, ImpactLevel::Medium)
729 } else if type_name.contains("Mutex") || type_name.contains("RwLock") {
730 (100, 1.0, 0, 0, 1, ImpactLevel::Low)
731 } else if self.has_custom_drop_impl(type_name) {
732 (5000, 10.0, 5, 2, 2, ImpactLevel::Medium)
733 } else {
734 (50, 0.5, 1, 0, 0, ImpactLevel::Low)
735 };
736
737 DropPerformanceCharacteristics {
738 execution_time_ns,
739 cpu_usage_percent: cpu_usage,
740 memory_operations: memory_ops,
741 io_operations: io_ops,
742 system_calls: syscalls,
743 impact_level: impact,
744 }
745 }
746
747 fn estimate_drop_duration(&self, type_name: &str) -> u64 {
749 if type_name.starts_with("Vec<") {
750 1000 } else if type_name.starts_with("HashMap<") {
753 2000 } else if type_name.contains("File") {
755 10000 } else if type_name.contains("Socket") {
757 15000 } else if type_name.starts_with("Box<") {
759 500 } else if self.has_custom_drop_impl(type_name) {
761 5000 } else {
763 100 }
765 }
766
767 fn find_child_objects_for_drop(
769 &self,
770 ptr: usize,
771 type_name: &str,
772 ) -> Vec<crate::core::types::DropChainNode> {
773 let mut children = Vec::new();
774 let timestamp = std::time::SystemTime::now()
775 .duration_since(std::time::UNIX_EPOCH)
776 .unwrap_or_default()
777 .as_nanos() as u64;
778
779 if type_name.starts_with("Vec<") {
781 if let Some(element_type) = self.extract_generic_type(type_name, "Vec") {
783 for i in 0..3 {
785 let child_ptr = ptr + (i * 8); children.push(self.create_drop_chain_node(
787 child_ptr,
788 &element_type,
789 timestamp + i as u64 * 100,
790 ));
791 }
792 }
793 }
794
795 children
796 }
797
798 fn analyze_ownership_hierarchy(
800 &self,
801 ptr: usize,
802 type_name: &str,
803 ) -> crate::core::types::OwnershipHierarchy {
804 use crate::core::types::{OwnershipHierarchy, OwnershipNode};
805
806 let ownership_type = self.determine_ownership_type(type_name);
808 let root_node = OwnershipNode {
809 object_id: ptr,
810 type_name: type_name.to_string(),
811 ownership_type,
812 owned_objects: self.find_owned_objects(ptr, type_name),
813 reference_count: self.get_reference_count_for_type(type_name),
814 weak_reference_count: self.get_weak_reference_count_for_type(type_name),
815 };
816
817 OwnershipHierarchy {
818 root_owners: vec![root_node],
819 max_depth: self.calculate_ownership_depth(ptr, type_name),
820 total_objects: self.count_owned_objects(ptr, type_name),
821 transfer_events: self.collect_ownership_transfer_events(ptr),
822 weak_references: self.collect_weak_references(ptr),
823 circular_references: self.detect_circular_references(ptr),
824 }
825 }
826
827 fn build_drop_sequence(
829 &self,
830 ptr: usize,
831 type_name: &str,
832 _hierarchy: &crate::core::types::OwnershipHierarchy,
833 ) -> Vec<crate::core::types::DropChainNode> {
834 let mut sequence = Vec::new();
835 let timestamp = std::time::SystemTime::now()
836 .duration_since(std::time::UNIX_EPOCH)
837 .unwrap_or_default()
838 .as_nanos() as u64;
839
840 sequence.push(self.create_drop_chain_node(ptr, type_name, timestamp));
842
843 if type_name.starts_with("Vec<") {
845 if let Some(element_type) = self.extract_generic_type(type_name, "Vec") {
846 for i in 0..3 {
847 let child_ptr = ptr + (i * 8);
848 sequence.push(self.create_drop_chain_node(
849 child_ptr,
850 &element_type,
851 timestamp + (i as u64 + 1) * 100,
852 ));
853 }
854 }
855 }
856
857 sequence
858 }
859
860 fn calculate_drop_chain_performance(
862 &self,
863 drop_sequence: &[DropChainNode],
864 ) -> DropChainPerformanceMetrics {
865 let total_objects = drop_sequence.len();
866 let max_depth = self.calculate_drop_chain_depth(drop_sequence);
867 let total_time: u64 = drop_sequence.iter().map(|node| node.drop_duration_ns).sum();
868 let avg_drop_time = if total_objects > 0 {
869 total_time as f64 / total_objects as f64
870 } else {
871 0.0
872 };
873 let slowest_drop = drop_sequence
874 .iter()
875 .map(|node| node.drop_duration_ns)
876 .max()
877 .unwrap_or(0);
878
879 let efficiency_score = self.calculate_drop_efficiency_score(drop_sequence);
881
882 let bottlenecks = self.identify_drop_bottlenecks(drop_sequence);
884
885 DropChainPerformanceMetrics {
886 total_objects,
887 max_depth,
888 avg_drop_time_ns: avg_drop_time,
889 slowest_drop_ns: slowest_drop,
890 efficiency_score,
891 bottlenecks,
892 }
893 }
894
895 fn detect_resource_leaks(
897 &self,
898 ptr: usize,
899 type_name: &str,
900 _hierarchy: &crate::core::types::OwnershipHierarchy,
901 ) -> ResourceLeakAnalysis {
902 let mut potential_leaks = Vec::new();
903
904 if type_name.contains("Rc<") && self.has_potential_cycle(ptr) {
906 let evidence = vec![LeakEvidence {
907 evidence_type: LeakEvidenceType::CircularReference,
908 description: "Potential circular reference in Rc structure".to_string(),
909 strength: 75.0,
910 timestamp: std::time::SystemTime::now()
911 .duration_since(std::time::UNIX_EPOCH)
912 .unwrap_or_default()
913 .as_nanos() as u64,
914 }];
915
916 potential_leaks.push(EnhancedPotentialLeak {
917 object_id: ptr,
918 leak_type: LeakType::ReferenceCycle,
919 risk_level: LeakRiskLevel::High,
920 evidence,
921 estimated_impact: LeakImpact {
922 memory_bytes: self.estimate_type_size(type_name),
923 performance_impact_percent: 5.0,
924 resource_count: 1,
925 time_to_critical_hours: Some(24.0),
926 },
927 });
928 }
929
930 if type_name.contains("File") && !self.has_explicit_close(ptr) {
931 let evidence = vec![LeakEvidence {
932 evidence_type: LeakEvidenceType::ResourceNotClosed,
933 description: "File handle may not be explicitly closed".to_string(),
934 strength: 60.0,
935 timestamp: std::time::SystemTime::now()
936 .duration_since(std::time::UNIX_EPOCH)
937 .unwrap_or_default()
938 .as_nanos() as u64,
939 }];
940
941 potential_leaks.push(EnhancedPotentialLeak {
942 object_id: ptr,
943 leak_type: LeakType::FileHandle,
944 risk_level: LeakRiskLevel::Medium,
945 evidence,
946 estimated_impact: LeakImpact {
947 memory_bytes: 1024, performance_impact_percent: 2.0,
949 resource_count: 1,
950 time_to_critical_hours: Some(72.0),
951 },
952 });
953 }
954
955 ResourceLeakAnalysis {
956 potential_leaks,
957 detection_confidence: 0.7, usage_patterns: Vec::new(), prevention_recommendations: self.generate_leak_prevention_recommendations(type_name),
960 }
961 }
962
963 fn has_potential_cycle(&self, _ptr: usize) -> bool {
965 rand::random::<f64>() < 0.1 }
968
969 fn extract_generic_type(&self, type_name: &str, _container: &str) -> Option<String> {
973 if let Some(start) = type_name.find('<') {
974 if let Some(end) = type_name.rfind('>') {
975 let inner = &type_name[start + 1..end];
976 Some(inner.to_string())
977 } else {
978 None
979 }
980 } else {
981 None
982 }
983 }
984
985 fn determine_ownership_type(&self, type_name: &str) -> crate::core::types::OwnershipType {
987 use crate::core::types::OwnershipType;
988
989 if type_name.starts_with("Box<") {
990 OwnershipType::Unique
991 } else if type_name.starts_with("Rc<") {
992 OwnershipType::SharedSingleThreaded
993 } else if type_name.starts_with("Arc<") {
994 OwnershipType::SharedMultiThreaded
995 } else if type_name.starts_with("&") {
996 OwnershipType::Borrowed
997 } else if type_name.contains("Weak") {
998 OwnershipType::Weak
999 } else if type_name.starts_with("*") {
1000 OwnershipType::Raw
1001 } else {
1002 OwnershipType::Unique
1003 }
1004 }
1005
1006 fn find_owned_objects(
1008 &self,
1009 _ptr: usize,
1010 type_name: &str,
1011 ) -> Vec<crate::core::types::OwnershipNode> {
1012 use crate::core::types::{OwnershipNode, OwnershipType};
1013
1014 let mut owned = Vec::new();
1015
1016 if type_name.starts_with("Vec<") {
1018 if let Some(element_type) = self.extract_generic_type(type_name, "Vec") {
1019 for i in 0..2 {
1021 owned.push(OwnershipNode {
1022 object_id: 1000 + i,
1023 type_name: element_type.clone(),
1024 ownership_type: OwnershipType::Unique,
1025 owned_objects: Vec::new(),
1026 reference_count: None,
1027 weak_reference_count: None,
1028 });
1029 }
1030 }
1031 }
1032
1033 owned
1034 }
1035
1036 fn get_reference_count_for_type(&self, type_name: &str) -> Option<usize> {
1038 if type_name.starts_with("Rc<") || type_name.starts_with("Arc<") {
1039 Some(1) } else {
1041 None
1042 }
1043 }
1044
1045 fn get_weak_reference_count_for_type(&self, type_name: &str) -> Option<usize> {
1047 if type_name.starts_with("Rc<") || type_name.starts_with("Arc<") {
1048 Some(0) } else {
1050 None
1051 }
1052 }
1053
1054 fn calculate_ownership_depth(&self, _ptr: usize, type_name: &str) -> usize {
1056 if type_name.starts_with("Vec<")
1057 || type_name.starts_with("HashMap<")
1058 || type_name.starts_with("Box<")
1059 {
1060 2 } else {
1062 1 }
1064 }
1065
1066 fn count_owned_objects(&self, _ptr: usize, type_name: &str) -> usize {
1068 if type_name.starts_with("Vec<") {
1069 5 } else if type_name.starts_with("HashMap<") {
1071 8 } else if type_name.starts_with("Box<") {
1073 2 } else {
1075 1
1076 }
1077 }
1078
1079 fn collect_ownership_transfer_events(
1081 &self,
1082 _ptr: usize,
1083 ) -> Vec<crate::core::types::OwnershipTransferEvent> {
1084 Vec::new()
1087 }
1088
1089 fn collect_weak_references(&self, _ptr: usize) -> Vec<crate::core::types::WeakReferenceInfo> {
1091 Vec::new()
1093 }
1094
1095 fn detect_circular_references(
1097 &self,
1098 _ptr: usize,
1099 ) -> Vec<crate::core::types::CircularReferenceInfo> {
1100 Vec::new()
1102 }
1103
1104 fn calculate_drop_chain_depth(
1106 &self,
1107 drop_sequence: &[crate::core::types::DropChainNode],
1108 ) -> usize {
1109 drop_sequence
1110 .iter()
1111 .map(|node| self.calculate_node_depth(node, 0))
1112 .max()
1113 .unwrap_or(0)
1114 }
1115
1116 #[allow(clippy::only_used_in_recursion)]
1118 fn calculate_node_depth(
1119 &self,
1120 node: &crate::core::types::DropChainNode,
1121 current_depth: usize,
1122 ) -> usize {
1123 if node.children.is_empty() {
1124 current_depth + 1
1125 } else {
1126 node.children
1127 .iter()
1128 .map(|child| self.calculate_node_depth(child, current_depth + 1))
1129 .max()
1130 .unwrap_or(current_depth + 1)
1131 }
1132 }
1133
1134 fn calculate_drop_efficiency_score(
1136 &self,
1137 drop_sequence: &[crate::core::types::DropChainNode],
1138 ) -> f64 {
1139 if drop_sequence.is_empty() {
1140 return 100.0;
1141 }
1142
1143 let total_time: u64 = drop_sequence.iter().map(|node| node.drop_duration_ns).sum();
1144 let object_count = drop_sequence.len() as u64;
1145
1146 let avg_time_per_object = total_time / object_count;
1150 let efficiency = if avg_time_per_object < 1000 {
1151 100.0
1152 } else if avg_time_per_object > 10000 {
1153 0.0
1154 } else {
1155 100.0 - ((avg_time_per_object - 1000) as f64 / 9000.0) * 100.0
1156 };
1157
1158 efficiency.clamp(0.0, 100.0)
1159 }
1160
1161 fn identify_drop_bottlenecks(
1163 &self,
1164 drop_sequence: &[crate::core::types::DropChainNode],
1165 ) -> Vec<crate::core::types::DropPerformanceBottleneck> {
1166 use crate::core::types::{DropBottleneckType, DropPerformanceBottleneck, ImpactLevel};
1167
1168 let mut bottlenecks = Vec::new();
1169 let avg_time = if !drop_sequence.is_empty() {
1170 drop_sequence
1171 .iter()
1172 .map(|node| node.drop_duration_ns)
1173 .sum::<u64>()
1174 / drop_sequence.len() as u64
1175 } else {
1176 0
1177 };
1178
1179 for node in drop_sequence {
1180 if node.drop_duration_ns > avg_time * 3 {
1182 let severity = if node.drop_duration_ns > 50000 {
1183 ImpactLevel::High
1184 } else if node.drop_duration_ns > 10000 {
1185 ImpactLevel::Medium
1186 } else {
1187 ImpactLevel::Low
1188 };
1189
1190 let bottleneck_type = if node.type_name.contains("File")
1191 || node.type_name.contains("Socket")
1192 {
1193 DropBottleneckType::ResourceHandleDelay
1194 } else if node.type_name.starts_with("Vec<")
1195 || node.type_name.starts_with("HashMap<")
1196 {
1197 DropBottleneckType::LargeCollectionCleanup
1198 } else if node.drop_impl_type == crate::core::types::DropImplementationType::Custom
1199 {
1200 DropBottleneckType::SlowCustomDrop
1201 } else {
1202 DropBottleneckType::DeepOwnershipHierarchy
1203 };
1204
1205 bottlenecks.push(DropPerformanceBottleneck {
1206 object_id: node.object_id,
1207 bottleneck_type: bottleneck_type.clone(),
1208 severity,
1209 description: format!(
1210 "Drop of {} took {}ns, significantly above average of {}ns",
1211 node.type_name, node.drop_duration_ns, avg_time
1212 ),
1213 optimization_suggestion: self
1214 .get_drop_optimization_suggestion(&bottleneck_type),
1215 });
1216 }
1217 }
1218
1219 bottlenecks
1220 }
1221
1222 fn get_drop_optimization_suggestion(
1224 &self,
1225 bottleneck_type: &crate::core::types::DropBottleneckType,
1226 ) -> String {
1227 use crate::core::types::DropBottleneckType;
1228
1229 match bottleneck_type {
1230 DropBottleneckType::SlowCustomDrop => {
1231 "Consider optimizing custom Drop implementation or using async cleanup".to_string()
1232 }
1233 DropBottleneckType::DeepOwnershipHierarchy => {
1234 "Consider flattening ownership hierarchy or using weak references".to_string()
1235 }
1236 DropBottleneckType::LargeCollectionCleanup => {
1237 "Consider using Vec::clear() before drop or implementing custom cleanup".to_string()
1238 }
1239 DropBottleneckType::ResourceHandleDelay => {
1240 "Consider async resource cleanup or connection pooling".to_string()
1241 }
1242 DropBottleneckType::LockContention => {
1243 "Consider reducing lock scope or using lock-free data structures".to_string()
1244 }
1245 DropBottleneckType::MemoryFragmentation => {
1246 "Consider using memory pools or custom allocators".to_string()
1247 }
1248 }
1249 }
1250
1251 fn generate_leak_prevention_recommendations(
1253 &self,
1254 type_name: &str,
1255 ) -> Vec<crate::core::types::LeakPreventionRecommendation> {
1256 use crate::core::types::{LeakPreventionRecommendation, LeakPreventionType, Priority};
1257
1258 let mut recommendations = Vec::new();
1259
1260 if type_name.contains("Rc<") {
1261 recommendations.push(LeakPreventionRecommendation {
1262 recommendation_type: LeakPreventionType::UseWeakReferences,
1263 priority: Priority::High,
1264 description: "Use Weak references to break potential cycles in Rc structures"
1265 .to_string(),
1266 implementation_guidance: "Replace some Rc references with Weak where appropriate"
1267 .to_string(),
1268 expected_effectiveness: 0.9,
1269 });
1270 }
1271
1272 if type_name.contains("File") || type_name.contains("Socket") {
1273 recommendations.push(LeakPreventionRecommendation {
1274 recommendation_type: LeakPreventionType::UseRAII,
1275 priority: Priority::High,
1276 description: "Ensure proper RAII patterns for resource cleanup".to_string(),
1277 implementation_guidance: "Use Drop trait or scoped guards for automatic cleanup"
1278 .to_string(),
1279 expected_effectiveness: 0.95,
1280 });
1281 }
1282
1283 recommendations.push(LeakPreventionRecommendation {
1284 recommendation_type: LeakPreventionType::ResourceMonitoring,
1285 priority: Priority::Medium,
1286 description: "Implement resource usage monitoring".to_string(),
1287 implementation_guidance: "Add metrics and alerts for resource usage patterns"
1288 .to_string(),
1289 expected_effectiveness: 0.7,
1290 });
1291
1292 recommendations
1293 }
1294
1295 fn has_explicit_close(&self, _ptr: usize) -> bool {
1297 rand::random::<f64>() < 0.8 }
1300}
1301
1302impl Default for MemoryTracker {
1303 fn default() -> Self {
1304 Self::new()
1305 }
1306}
1307
1308impl Drop for MemoryTracker {
1309 fn drop(&mut self) {
1310 if std::env::var("MEMSCOPE_VERBOSE").is_ok() {
1312 tracing::info!("💡 Tip: Use tracker.export_to_json() or tracker.export_interactive_dashboard() before drop to save analysis results");
1313 }
1314
1315 if let Ok(mut active) = self.active_allocations.lock() {
1317 active.clear();
1318 }
1319 }
1320}
1321
1322#[cfg(test)]
1323mod tests {
1324 use super::*;
1325 use std::sync::Arc;
1326
1327 #[test]
1328 fn test_memory_tracker_creation() {
1329 let tracker = MemoryTracker::new();
1330
1331 assert!(
1333 !tracker.is_fast_mode() || std::env::var("MEMSCOPE_TEST_MODE").is_ok() || cfg!(test)
1334 );
1335
1336 let stats_result = tracker.get_stats();
1338 assert!(stats_result.is_ok());
1339 }
1340
1341 #[test]
1342 fn test_fast_mode_toggle() {
1343 let tracker = MemoryTracker::new();
1344
1345 tracker.set_fast_mode(true);
1347 assert!(tracker.is_fast_mode());
1348
1349 tracker.set_fast_mode(false);
1351 assert!(!tracker.is_fast_mode());
1352
1353 tracker.enable_fast_mode();
1355 assert!(tracker.is_fast_mode());
1356 }
1357
1358 #[test]
1359 fn test_get_active_allocations() {
1360 let tracker = MemoryTracker::new();
1361 tracker.enable_fast_mode();
1362
1363 let allocations = tracker.get_active_allocations();
1365 assert!(allocations.is_ok());
1366 assert_eq!(allocations.unwrap().len(), 0);
1367 }
1368
1369 #[test]
1370 fn test_get_allocation_history() {
1371 let tracker = MemoryTracker::new();
1372 tracker.enable_fast_mode();
1373
1374 let history = tracker.get_allocation_history();
1376 assert!(history.is_ok());
1377 assert_eq!(history.unwrap().len(), 0);
1378 }
1379
1380 #[test]
1381 fn test_memory_analysis_path_creation() {
1382 let tracker = MemoryTracker::new();
1383
1384 let path = tracker.ensure_memory_analysis_path("test.svg");
1385 assert!(path.to_string_lossy().contains("MemoryAnalysis"));
1386 assert!(path.to_string_lossy().ends_with("test.svg"));
1387 }
1388
1389 #[test]
1390 fn test_memscope_path_creation() {
1391 let tracker = MemoryTracker::new();
1392
1393 let path = tracker.ensure_memscope_path("test");
1394 assert!(path.to_string_lossy().contains("MemoryAnalysis"));
1395 assert!(path.to_string_lossy().ends_with(".memscope"));
1396
1397 let path_with_ext = tracker.ensure_memscope_path("test.memscope");
1398 assert!(path_with_ext.to_string_lossy().ends_with(".memscope"));
1399 }
1400
1401 #[test]
1402 fn test_binary_export_mode_default() {
1403 let mode = BinaryExportMode::default();
1404 assert_eq!(mode, BinaryExportMode::UserOnly);
1405 }
1406
1407 #[test]
1408 fn test_binary_export_mode_variants() {
1409 assert_ne!(
1411 std::mem::discriminant(&BinaryExportMode::UserOnly),
1412 std::mem::discriminant(&BinaryExportMode::Full)
1413 );
1414 }
1415
1416 #[test]
1417 fn test_global_tracker_singleton() {
1418 let tracker1 = get_tracker();
1419 let tracker2 = get_tracker();
1420
1421 assert!(Arc::ptr_eq(&tracker1, &tracker2));
1423 }
1424
1425 #[test]
1426 fn test_drop_chain_analysis_basic() {
1427 let tracker = MemoryTracker::new();
1428 tracker.enable_fast_mode();
1429
1430 let analysis = tracker.analyze_drop_chain(0x1000, "Vec<i32>");
1431 assert!(analysis.is_some());
1432
1433 let analysis = analysis.unwrap();
1434 assert_eq!(analysis.root_object.object_id, 0x1000);
1435 assert_eq!(analysis.root_object.type_name, "Vec<i32>");
1436 assert!(analysis.total_duration_ns > 0);
1437 }
1438
1439 #[test]
1440 fn test_drop_implementation_type_detection() {
1441 let tracker = MemoryTracker::new();
1442
1443 let box_type = tracker.determine_drop_implementation_type("Box<i32>");
1445 assert_eq!(
1446 box_type,
1447 crate::core::types::DropImplementationType::SmartPointer
1448 );
1449
1450 let vec_type = tracker.determine_drop_implementation_type("Vec<i32>");
1452 assert_eq!(
1453 vec_type,
1454 crate::core::types::DropImplementationType::Collection
1455 );
1456
1457 let file_type = tracker.determine_drop_implementation_type("File");
1459 assert_eq!(
1460 file_type,
1461 crate::core::types::DropImplementationType::ResourceHandle
1462 );
1463
1464 let int_type = tracker.determine_drop_implementation_type("i32");
1466 assert_eq!(int_type, crate::core::types::DropImplementationType::NoOp);
1467 }
1468
1469 #[test]
1470 fn test_copy_type_detection() {
1471 let tracker = MemoryTracker::new();
1472
1473 assert!(tracker.is_copy_type("i32"));
1474 assert!(tracker.is_copy_type("u64"));
1475 assert!(tracker.is_copy_type("f32"));
1476 assert!(tracker.is_copy_type("bool"));
1477 assert!(tracker.is_copy_type("char"));
1478 assert!(tracker.is_copy_type("&str"));
1479
1480 assert!(!tracker.is_copy_type("String"));
1481 assert!(!tracker.is_copy_type("Vec<i32>"));
1482 assert!(!tracker.is_copy_type("HashMap<String, i32>"));
1483 }
1484
1485 #[test]
1486 fn test_custom_drop_detection() {
1487 let tracker = MemoryTracker::new();
1488
1489 assert!(tracker.has_custom_drop_impl("MutexGuard"));
1490 assert!(tracker.has_custom_drop_impl("RwLockWriteGuard"));
1491 assert!(tracker.has_custom_drop_impl("Receiver<i32>"));
1492 assert!(tracker.has_custom_drop_impl("Sender<String>"));
1493
1494 assert!(!tracker.has_custom_drop_impl("i32"));
1495 assert!(!tracker.has_custom_drop_impl("String"));
1496 }
1497
1498 #[test]
1499 fn test_drop_duration_estimation() {
1500 let tracker = MemoryTracker::new();
1501
1502 let vec_duration = tracker.estimate_drop_duration("Vec<i32>");
1504 assert!(vec_duration > 0);
1505 assert!(vec_duration < 10000); let file_duration = tracker.estimate_drop_duration("File");
1509 assert!(file_duration > vec_duration);
1510
1511 let simple_duration = tracker.estimate_drop_duration("i32");
1513 assert!(simple_duration < vec_duration);
1514 }
1515
1516 #[test]
1517 fn test_ownership_type_determination() {
1518 let tracker = MemoryTracker::new();
1519
1520 assert_eq!(
1521 tracker.determine_ownership_type("Box<i32>"),
1522 crate::core::types::OwnershipType::Unique
1523 );
1524 assert_eq!(
1525 tracker.determine_ownership_type("Rc<i32>"),
1526 crate::core::types::OwnershipType::SharedSingleThreaded
1527 );
1528 assert_eq!(
1529 tracker.determine_ownership_type("Arc<i32>"),
1530 crate::core::types::OwnershipType::SharedMultiThreaded
1531 );
1532 assert_eq!(
1533 tracker.determine_ownership_type("&i32"),
1534 crate::core::types::OwnershipType::Borrowed
1535 );
1536 assert_eq!(
1537 tracker.determine_ownership_type("Weak<i32>"),
1538 crate::core::types::OwnershipType::Weak
1539 );
1540 }
1541
1542 #[test]
1543 fn test_ownership_depth_calculation() {
1544 let tracker = MemoryTracker::new();
1545
1546 assert!(tracker.calculate_ownership_depth(0x1000, "Vec<i32>") > 1);
1548 assert!(tracker.calculate_ownership_depth(0x1000, "HashMap<String, i32>") > 1);
1549 assert!(tracker.calculate_ownership_depth(0x1000, "Box<i32>") > 1);
1550
1551 assert_eq!(tracker.calculate_ownership_depth(0x1000, "i32"), 1);
1553 }
1554
1555 #[test]
1556 fn test_owned_objects_counting() {
1557 let tracker = MemoryTracker::new();
1558
1559 assert!(tracker.count_owned_objects(0x1000, "Vec<i32>") > 1);
1561 assert!(tracker.count_owned_objects(0x1000, "HashMap<String, i32>") > 1);
1562
1563 assert_eq!(tracker.count_owned_objects(0x1000, "i32"), 1);
1565 }
1566
1567 #[test]
1568 fn test_reference_count_detection() {
1569 let tracker = MemoryTracker::new();
1570
1571 assert!(tracker.get_reference_count_for_type("Rc<i32>").is_some());
1573 assert!(tracker.get_reference_count_for_type("Arc<i32>").is_some());
1574
1575 assert!(tracker.get_reference_count_for_type("Box<i32>").is_none());
1577 assert!(tracker.get_reference_count_for_type("i32").is_none());
1578 }
1579
1580 #[test]
1581 fn test_generic_type_extraction() {
1582 let tracker = MemoryTracker::new();
1583
1584 assert_eq!(
1585 tracker.extract_generic_type("Vec<i32>", "Vec"),
1586 Some("i32".to_string())
1587 );
1588 assert_eq!(
1589 tracker.extract_generic_type("HashMap<String, i32>", "HashMap"),
1590 Some("String, i32".to_string())
1591 );
1592 assert_eq!(
1593 tracker.extract_generic_type("Box<Vec<String>>", "Box"),
1594 Some("Vec<String>".to_string())
1595 );
1596
1597 assert_eq!(tracker.extract_generic_type("i32", ""), None);
1599 }
1600}