1use crate::analysis::memory_passport_tracker::{MemoryPassportTracker, PassportTrackerConfig};
26use crate::capture::backends::async_tracker::AsyncTracker;
27use crate::core::{MemScopeError, MemScopeResult};
28use crate::tracker::{AnalysisReport, Tracker};
29use std::path::Path;
30use std::sync::Arc;
31use std::time::Instant;
32use tracing::info;
33
34static GLOBAL_TRACKER: std::sync::RwLock<Option<Arc<GlobalTracker>>> = std::sync::RwLock::new(None);
35
36#[derive(Debug, Clone)]
37pub struct TrackerConfig {
38 pub max_allocations: usize,
39 pub enable_statistics: bool,
40}
41
42impl Default for TrackerConfig {
43 fn default() -> Self {
44 Self {
45 max_allocations: 1_000_000,
46 enable_statistics: true,
47 }
48 }
49}
50
51#[derive(Debug, Clone, Default)]
52pub struct GlobalTrackerConfig {
53 pub tracker: TrackerConfig,
54 pub passport: PassportTrackerConfig,
55}
56
57pub struct GlobalTracker {
58 tracker: Tracker,
59 passport_tracker: Arc<MemoryPassportTracker>,
60 async_tracker: Arc<AsyncTracker>,
61 start_time: Instant,
62}
63
64impl std::fmt::Debug for GlobalTracker {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 f.debug_struct("GlobalTracker")
67 .field("start_time", &self.start_time)
68 .finish()
69 }
70}
71
72impl GlobalTracker {
73 pub fn new() -> Self {
74 Self::with_config(GlobalTrackerConfig::default())
75 }
76
77 pub fn with_config(config: GlobalTrackerConfig) -> Self {
78 let tracker = Tracker::new();
79 let passport_tracker = Arc::new(MemoryPassportTracker::new(config.passport));
80 let async_tracker = Arc::new(AsyncTracker::new());
81 async_tracker.set_initialized();
82
83 Self {
84 tracker,
85 passport_tracker,
86 async_tracker,
87 start_time: Instant::now(),
88 }
89 }
90
91 pub fn tracker(&self) -> &Tracker {
92 &self.tracker
93 }
94
95 pub fn passport_tracker(&self) -> &Arc<MemoryPassportTracker> {
96 &self.passport_tracker
97 }
98
99 pub fn async_tracker(&self) -> &Arc<AsyncTracker> {
100 &self.async_tracker
101 }
102
103 pub fn elapsed(&self) -> std::time::Duration {
104 self.start_time.elapsed()
105 }
106
107 pub fn track<T: crate::Trackable>(&self, var: &T) {
108 self.track_as(var, "unknown", "", 0, "");
109 }
110
111 pub fn track_as<T: crate::Trackable>(
112 &self,
113 var: &T,
114 name: &str,
115 file: &str,
116 line: u32,
117 module_path: &str,
118 ) {
119 self.tracker.track_as(var, name, file, line, module_path);
120
121 if let Some(task_id) = AsyncTracker::get_current_task() {
122 let kind = var.track_kind();
123 if let crate::core::types::TrackKind::HeapOwner { ptr, size } = kind {
124 let type_name = var.get_type_name().to_string();
125 self.async_tracker.track_allocation_with_location(
126 ptr,
127 size,
128 task_id,
129 Some(name.to_string()),
130 Some(type_name),
131 None,
132 );
133 }
134 }
135
136 let kind = var.track_kind();
137 if let crate::core::types::TrackKind::HeapOwner { ptr, size: _ } = kind {
138 let borrow_analyzer = crate::analysis::borrow_analysis::get_global_borrow_analyzer();
139 let type_name = var.get_type_name();
140 let borrow_type = if type_name.contains("Mutex") || type_name.contains("Cell") {
141 crate::analysis::borrow_analysis::BorrowType::Mutable
142 } else {
143 crate::analysis::borrow_analysis::BorrowType::Immutable
144 };
145 let _ = borrow_analyzer.track_borrow(ptr, borrow_type, name);
146 }
147 }
148
149 pub fn create_passport(
150 &self,
151 ptr: usize,
152 size: usize,
153 context: String,
154 ) -> Result<String, crate::capture::types::TrackingError> {
155 self.passport_tracker
156 .create_passport_simple(ptr, size, context)
157 }
158
159 pub fn record_handover(&self, ptr: usize, context: String, function: String) {
160 let _ = self
161 .passport_tracker
162 .record_handover_to_ffi(ptr, context, function);
163 }
164
165 pub fn record_free(&self, ptr: usize, context: String, function: String) {
166 let _ = self
167 .passport_tracker
168 .record_freed_by_foreign(ptr, context, function);
169 }
170
171 pub fn detect_leaks(&self) -> crate::analysis::memory_passport_tracker::LeakDetectionResult {
172 self.passport_tracker.detect_leaks_at_shutdown()
173 }
174
175 pub fn analyze(&self) -> AnalysisReport {
176 self.tracker.analyze()
177 }
178
179 pub fn get_stats(&self) -> GlobalTrackerStats {
180 let report = self.tracker.analyze();
181 let passport_stats = self.passport_tracker.get_stats();
182 let async_stats = self.async_tracker.get_stats();
183
184 GlobalTrackerStats {
185 total_allocations: report.total_allocations,
186 active_allocations: report.active_allocations,
187 peak_memory_bytes: report.peak_memory_bytes,
188 current_memory_bytes: report.current_memory_bytes,
189 passport_count: passport_stats.total_passports_created,
190 active_passports: passport_stats.active_passports,
191 leaks_detected: passport_stats.leaks_detected,
192 async_task_count: async_stats.total_tasks,
193 active_async_tasks: async_stats.active_tasks,
194 uptime: self.elapsed(),
195 }
196 }
197
198 pub fn export_json<P: AsRef<Path>>(&self, path: P) -> MemScopeResult<()> {
199 use crate::render_engine::export::export_all_json;
200
201 let path = path.as_ref();
202 export_all_json(
203 path,
204 &self.tracker,
205 &self.passport_tracker,
206 &self.async_tracker,
207 )
208 .map_err(|e| MemScopeError::error("global_tracking", "export_json", e.to_string()))?;
209 Ok(())
210 }
211
212 pub fn export_html<P: AsRef<Path>>(&self, path: P) -> MemScopeResult<()> {
213 use crate::render_engine::export::export_dashboard_html_with_async;
214
215 let path = path.as_ref();
216 export_dashboard_html_with_async(
217 path,
218 &self.tracker,
219 &self.passport_tracker,
220 &self.async_tracker,
221 )
222 .map_err(|e| MemScopeError::error("global_tracking", "export_html", e.to_string()))?;
223 Ok(())
224 }
225
226 pub fn export_html_with_template<P: AsRef<Path>>(
227 &self,
228 path: P,
229 template: crate::render_engine::export::DashboardTemplate,
230 ) -> MemScopeResult<()> {
231 use crate::render_engine::export::export_dashboard_html_with_template;
232
233 let path = path.as_ref();
234 export_dashboard_html_with_template(
235 path,
236 &self.tracker,
237 &self.passport_tracker,
238 template,
239 Some(&self.async_tracker),
240 )
241 .map_err(|e| {
242 MemScopeError::error(
243 "global_tracking",
244 "export_html_with_template",
245 e.to_string(),
246 )
247 })?;
248 Ok(())
249 }
250}
251
252impl Default for GlobalTracker {
253 fn default() -> Self {
254 Self::new()
255 }
256}
257
258#[derive(Debug, Clone)]
259pub struct GlobalTrackerStats {
260 pub total_allocations: usize,
261 pub active_allocations: usize,
262 pub peak_memory_bytes: u64,
263 pub current_memory_bytes: u64,
264 pub passport_count: usize,
265 pub active_passports: usize,
266 pub leaks_detected: usize,
267 pub async_task_count: usize,
268 pub active_async_tasks: usize,
269 pub uptime: std::time::Duration,
270}
271
272pub fn init_global_tracking() -> MemScopeResult<()> {
273 let mut guard = GLOBAL_TRACKER.write().map_err(|_| {
274 MemScopeError::error(
275 "global_tracking",
276 "init_global_tracking",
277 "Failed to acquire global tracker lock",
278 )
279 })?;
280
281 if guard.is_some() {
282 return Err(MemScopeError::error(
283 "global_tracking",
284 "init_global_tracking",
285 "Global tracking already initialized",
286 ));
287 }
288
289 *guard = Some(Arc::new(GlobalTracker::new()));
290 info!("Global tracking initialized");
291 Ok(())
292}
293
294pub fn init_global_tracking_with_config(config: GlobalTrackerConfig) -> MemScopeResult<()> {
295 let mut guard = GLOBAL_TRACKER.write().map_err(|_| {
296 MemScopeError::error(
297 "global_tracking",
298 "init_global_tracking_with_config",
299 "Failed to acquire global tracker lock",
300 )
301 })?;
302
303 if guard.is_some() {
304 return Err(MemScopeError::error(
305 "global_tracking",
306 "init_global_tracking_with_config",
307 "Global tracking already initialized",
308 ));
309 }
310
311 *guard = Some(Arc::new(GlobalTracker::with_config(config)));
312 info!("Global tracking initialized with config");
313 Ok(())
314}
315
316pub fn reset_global_tracking() {
317 if let Ok(mut guard) = GLOBAL_TRACKER.write() {
318 *guard = None;
319 }
320}
321
322pub fn is_initialized() -> bool {
323 GLOBAL_TRACKER
324 .read()
325 .map(|guard| guard.is_some())
326 .unwrap_or(false)
327}
328
329pub fn global_tracker() -> MemScopeResult<Arc<GlobalTracker>> {
330 GLOBAL_TRACKER
331 .read()
332 .map(|guard| {
333 guard.as_ref().cloned().ok_or_else(|| {
334 MemScopeError::error(
335 "global_tracking",
336 "global_tracker",
337 "Global tracking not initialized",
338 )
339 })
340 })
341 .map_err(|_| {
342 MemScopeError::error(
343 "global_tracking",
344 "global_tracker",
345 "Failed to acquire global tracker lock",
346 )
347 })?
348}
349
350#[cfg(test)]
351mod tests {
352 use super::*;
353 use std::time::Duration;
354
355 fn reset_global_state() {
356 reset_global_tracking();
357 }
358
359 #[test]
360 fn test_unified_tracker() {
361 let tracker = GlobalTracker::new();
362 assert!(tracker.tracker().analyze().total_allocations == 0);
363
364 let stats = tracker.get_stats();
365 assert_eq!(stats.total_allocations, 0);
366 }
367
368 #[test]
369 fn test_tracker_config_default() {
370 let config = TrackerConfig::default();
371 assert_eq!(config.max_allocations, 1_000_000);
372 assert!(config.enable_statistics);
373 }
374
375 #[test]
376 fn test_tracker_config_clone() {
377 let config = TrackerConfig {
378 max_allocations: 500,
379 enable_statistics: false,
380 };
381 let cloned = config.clone();
382 assert_eq!(cloned.max_allocations, 500);
383 assert!(!cloned.enable_statistics);
384 }
385
386 #[test]
387 fn test_tracker_config_debug() {
388 let config = TrackerConfig::default();
389 let debug_str = format!("{:?}", config);
390 assert!(debug_str.contains("TrackerConfig"));
391 assert!(debug_str.contains("max_allocations"));
392 assert!(debug_str.contains("enable_statistics"));
393 }
394
395 #[test]
396 fn test_global_tracker_config_default() {
397 let config = GlobalTrackerConfig::default();
398 assert_eq!(config.tracker.max_allocations, 1_000_000);
399 assert!(config.tracker.enable_statistics);
400 }
401
402 #[test]
403 fn test_global_tracker_config_clone() {
404 let config = GlobalTrackerConfig {
405 tracker: TrackerConfig {
406 max_allocations: 100,
407 enable_statistics: false,
408 },
409 passport: PassportTrackerConfig::default(),
410 };
411 let cloned = config.clone();
412 assert_eq!(cloned.tracker.max_allocations, 100);
413 assert!(!cloned.tracker.enable_statistics);
414 }
415
416 #[test]
417 fn test_global_tracker_config_debug() {
418 let config = GlobalTrackerConfig::default();
419 let debug_str = format!("{:?}", config);
420 assert!(debug_str.contains("GlobalTrackerConfig"));
421 }
422
423 #[test]
424 fn test_global_tracker_new() {
425 let tracker = GlobalTracker::new();
426 assert!(tracker.elapsed() < Duration::from_secs(1));
427 assert!(tracker.tracker().analyze().total_allocations == 0);
428 }
429
430 #[test]
431 fn test_global_tracker_with_config() {
432 let config = GlobalTrackerConfig {
433 tracker: TrackerConfig {
434 max_allocations: 100,
435 enable_statistics: true,
436 },
437 passport: PassportTrackerConfig::default(),
438 };
439 let tracker = GlobalTracker::with_config(config);
440 assert!(tracker.tracker().analyze().total_allocations == 0);
441 }
442
443 #[test]
444 fn test_global_tracker_default() {
445 let tracker = GlobalTracker::default();
446 assert!(tracker.tracker().analyze().total_allocations == 0);
447 }
448
449 #[test]
450 fn test_global_tracker_debug() {
451 let tracker = GlobalTracker::new();
452 let debug_str = format!("{:?}", tracker);
453 assert!(debug_str.contains("GlobalTracker"));
454 assert!(debug_str.contains("start_time"));
455 }
456
457 #[test]
458 fn test_global_tracker_elapsed() {
459 let tracker = GlobalTracker::new();
460 std::thread::sleep(Duration::from_millis(10));
461 let elapsed = tracker.elapsed();
462 assert!(elapsed >= Duration::from_millis(10));
463 }
464
465 #[test]
466 fn test_global_tracker_tracker_accessor() {
467 let tracker = GlobalTracker::new();
468 let inner_tracker = tracker.tracker();
469 assert!(inner_tracker.analyze().total_allocations == 0);
470 }
471
472 #[test]
473 fn test_global_tracker_passport_tracker_accessor() {
474 let tracker = GlobalTracker::new();
475 let passport = tracker.passport_tracker();
476 let stats = passport.get_stats();
477 assert_eq!(stats.total_passports_created, 0);
478 }
479
480 #[test]
481 fn test_global_tracker_async_tracker_accessor() {
482 let tracker = GlobalTracker::new();
483 let async_tracker = tracker.async_tracker();
484 let stats = async_tracker.get_stats();
485 assert_eq!(stats.total_tasks, 0);
486 }
487
488 #[test]
489 fn test_global_tracker_analyze() {
490 let tracker = GlobalTracker::new();
491 let report = tracker.analyze();
492 assert_eq!(report.total_allocations, 0);
493 assert_eq!(report.active_allocations, 0);
494 }
495
496 #[test]
497 fn test_global_tracker_stats() {
498 let tracker = GlobalTracker::new();
499 let stats = tracker.get_stats();
500 assert_eq!(stats.total_allocations, 0);
501 assert_eq!(stats.active_allocations, 0);
502 assert_eq!(stats.peak_memory_bytes, 0);
503 assert_eq!(stats.current_memory_bytes, 0);
504 assert_eq!(stats.passport_count, 0);
505 assert_eq!(stats.active_passports, 0);
506 assert_eq!(stats.leaks_detected, 0);
507 assert_eq!(stats.async_task_count, 0);
508 assert_eq!(stats.active_async_tasks, 0);
509 assert!(stats.uptime < Duration::from_secs(1));
510 }
511
512 #[test]
513 fn test_global_tracker_stats_debug() {
514 let tracker = GlobalTracker::new();
515 let stats = tracker.get_stats();
516 let debug_str = format!("{:?}", stats);
517 assert!(debug_str.contains("GlobalTrackerStats"));
518 assert!(debug_str.contains("total_allocations"));
519 }
520
521 #[test]
522 fn test_global_tracker_stats_clone() {
523 let tracker = GlobalTracker::new();
524 let stats = tracker.get_stats();
525 let cloned = stats.clone();
526 assert_eq!(cloned.total_allocations, stats.total_allocations);
527 assert_eq!(cloned.active_allocations, stats.active_allocations);
528 }
529
530 #[test]
531 fn test_init_global_tracking_double_init() {
532 reset_global_state();
533 let result1 = init_global_tracking();
534 assert!(result1.is_ok(), "First init should succeed");
535 let result2 = init_global_tracking();
536 assert!(result2.is_err(), "Second init should fail");
537 reset_global_state();
538 }
539
540 #[test]
541 fn test_init_global_tracking_with_config_double_init() {
542 reset_global_state();
543 let config = GlobalTrackerConfig::default();
544 let result1 = init_global_tracking_with_config(config);
545 assert!(result1.is_ok(), "First init should succeed");
546 let config2 = GlobalTrackerConfig::default();
547 let result2 = init_global_tracking_with_config(config2);
548 assert!(result2.is_err(), "Second init should fail");
549 reset_global_state();
550 }
551
552 #[test]
553 fn test_global_tracker_accessor_not_initialized() {
554 reset_global_state();
555 let result = global_tracker();
556 assert!(result.is_err(), "Should fail when not initialized");
557 reset_global_state();
558 }
559
560 #[test]
561 fn test_reset_global_tracking() {
562 reset_global_state();
563 init_global_tracking().unwrap();
564 assert!(is_initialized());
565 reset_global_tracking();
566 assert!(!is_initialized());
567 }
568
569 #[test]
570 fn test_global_tracker_create_passport() {
571 let tracker = GlobalTracker::new();
572 let result = tracker.create_passport(0x1000, 64, "test_context".to_string());
573 assert!(result.is_ok());
574 let passport_id = result.unwrap();
575 assert!(!passport_id.is_empty());
576 }
577
578 #[test]
579 fn test_global_tracker_detect_leaks() {
580 let tracker = GlobalTracker::new();
581 let result = tracker.detect_leaks();
582 assert_eq!(result.total_leaks, 0);
583 }
584
585 #[test]
586 fn test_global_tracker_record_handover() {
587 let tracker = GlobalTracker::new();
588 tracker
589 .create_passport(0x2000, 128, "test".to_string())
590 .unwrap();
591 tracker.record_handover(0x2000, "context".to_string(), "function".to_string());
592 }
593
594 #[test]
595 fn test_global_tracker_record_free() {
596 let tracker = GlobalTracker::new();
597 tracker
598 .create_passport(0x3000, 256, "test".to_string())
599 .unwrap();
600 tracker.record_free(0x3000, "context".to_string(), "function".to_string());
601 }
602
603 #[test]
604 fn test_global_tracker_track_simple() {
605 let tracker = GlobalTracker::new();
606 let value = String::from("test");
607 tracker.track(&value);
608 }
609
610 #[test]
611 fn test_global_tracker_track_as() {
612 let tracker = GlobalTracker::new();
613 let value = String::from("test_value");
614 tracker.track_as(&value, "test_var", "test.rs", 10, "test_module");
615 }
616}