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