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