1use crate::core::{MemScopeError, MemScopeResult};
6use std::sync::Arc;
7use tracing::{debug, info, warn};
8
9#[derive(Debug, Clone, PartialEq, Eq)]
11pub enum RuntimeEnvironment {
12 SingleThreaded,
14 MultiThreaded { thread_count: usize },
16 AsyncRuntime { runtime_type: AsyncRuntimeType },
18 Hybrid {
20 thread_count: usize,
21 async_task_count: usize,
22 },
23}
24
25#[derive(Debug, Clone, PartialEq, Eq)]
27pub enum AsyncRuntimeType {
28 Tokio,
30 AsyncStd,
32 Custom,
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum TrackingStrategy {
39 GlobalDirect,
41 ThreadLocal,
43 TaskLocal,
45 HybridTracking,
47}
48
49#[derive(Debug, Clone)]
51pub struct BackendConfig {
52 pub auto_detect: bool,
54 pub force_strategy: Option<TrackingStrategy>,
56 pub sample_rate: f64,
58 pub max_overhead_percent: f64,
60}
61
62impl Default for BackendConfig {
63 fn default() -> Self {
64 Self {
65 auto_detect: true,
66 force_strategy: None,
67 sample_rate: 1.0,
68 max_overhead_percent: 5.0,
69 }
70 }
71}
72
73#[derive(Debug, Clone)]
75pub struct DetectionConfig {
76 pub deep_async_detection: bool,
78 pub analysis_period_ms: u64,
80 pub multi_thread_threshold: usize,
82 pub max_detection_time_ms: u64,
84 pub confidence_level: f64,
86}
87
88impl Default for DetectionConfig {
89 fn default() -> Self {
90 Self {
91 deep_async_detection: true,
92 analysis_period_ms: 100,
93 multi_thread_threshold: 2,
94 max_detection_time_ms: 500,
95 confidence_level: 1.0,
96 }
97 }
98}
99
100#[derive(Debug, Clone)]
102pub struct EnvironmentDetection {
103 pub environment: RuntimeEnvironment,
105 pub recommended_strategy: TrackingStrategy,
107 pub thread_count: usize,
109 pub memory_usage: f64,
111 pub confidence: f64,
113}
114
115pub struct TrackingSession {
117 session_id: String,
119 backend: Arc<UnifiedBackend>,
121 start_time: std::time::Instant,
123}
124
125#[derive(Debug)]
127pub struct MemoryAnalysisData {
128 pub raw_data: Vec<u8>,
130 pub statistics: MemoryStatistics,
132 pub environment: RuntimeEnvironment,
134 pub session_metadata: SessionMetadata,
136}
137
138#[derive(Debug)]
140pub struct MemoryStatistics {
141 pub total_allocations: usize,
143 pub peak_memory_bytes: usize,
145 pub avg_allocation_size: f64,
147 pub session_duration_ms: u64,
149}
150
151#[derive(Debug)]
153pub struct SessionMetadata {
154 pub session_id: String,
156 pub detected_environment: RuntimeEnvironment,
158 pub strategy_used: TrackingStrategy,
160 pub overhead_percent: f64,
162}
163
164#[derive(Debug)]
166pub struct UnifiedBackend {
167 environment: RuntimeEnvironment,
169 active_strategy: TrackingStrategy,
171 config: BackendConfig,
173}
174
175impl Clone for UnifiedBackend {
176 fn clone(&self) -> Self {
177 Self {
178 environment: self.environment.clone(),
179 active_strategy: self.active_strategy,
180 config: self.config.clone(),
181 }
182 }
183}
184
185impl UnifiedBackend {
186 pub fn initialize(config: BackendConfig) -> MemScopeResult<Self> {
188 if config.sample_rate < 0.0 || config.sample_rate > 1.0 {
189 return Err(MemScopeError::error(
190 "unified_tracker",
191 "initialize",
192 "Sample rate must be between 0.0 and 1.0",
193 ));
194 }
195
196 if config.max_overhead_percent < 0.0 || config.max_overhead_percent > 100.0 {
197 return Err(MemScopeError::error(
198 "unified_tracker",
199 "initialize",
200 "Max overhead percent must be between 0.0 and 100.0",
201 ));
202 }
203
204 info!("Initializing unified backend");
205
206 let environment = if config.auto_detect {
207 Self::detect_environment()?
208 } else {
209 RuntimeEnvironment::SingleThreaded
210 };
211
212 let active_strategy = if let Some(forced) = config.force_strategy {
213 warn!("Using forced strategy: {:?}", forced);
214 forced
215 } else {
216 Self::select_strategy(&environment)?
217 };
218
219 info!("Selected tracking strategy: {:?}", active_strategy);
220
221 Ok(Self {
222 environment,
223 active_strategy,
224 config,
225 })
226 }
227
228 pub fn new() -> Self {
230 Self::initialize(BackendConfig::default()).unwrap_or_else(|_| Self {
231 environment: RuntimeEnvironment::SingleThreaded,
232 active_strategy: TrackingStrategy::GlobalDirect,
233 config: BackendConfig::default(),
234 })
235 }
236
237 pub fn with_config(config: BackendConfig) -> MemScopeResult<Self> {
239 Self::initialize(config)
240 }
241
242 pub fn strategy(&self) -> &TrackingStrategy {
244 &self.active_strategy
245 }
246
247 pub fn environment(&self) -> &RuntimeEnvironment {
249 &self.environment
250 }
251
252 pub fn config(&self) -> &BackendConfig {
254 &self.config
255 }
256
257 pub fn detect_environment() -> MemScopeResult<RuntimeEnvironment> {
259 debug!("Starting environment detection");
260
261 let async_runtime = Self::detect_async_runtime();
262 let thread_count = std::thread::available_parallelism()
263 .map(|p| p.get())
264 .unwrap_or(1);
265
266 let environment = match (async_runtime, thread_count) {
267 (Some(runtime_type), 1) => RuntimeEnvironment::AsyncRuntime { runtime_type },
268 (Some(_runtime_type), threads) => RuntimeEnvironment::Hybrid {
269 thread_count: threads,
270 async_task_count: 0,
271 },
272 (None, 1) => RuntimeEnvironment::SingleThreaded,
273 (None, threads) => RuntimeEnvironment::MultiThreaded {
274 thread_count: threads,
275 },
276 };
277
278 debug!("Environment detection completed: {:?}", environment);
279 Ok(environment)
280 }
281
282 fn detect_async_runtime() -> Option<AsyncRuntimeType> {
284 if Self::is_tokio_present() {
285 debug!("Tokio runtime detected");
286 return Some(AsyncRuntimeType::Tokio);
287 }
288
289 if Self::is_async_std_present() {
290 debug!("async-std runtime detected");
291 return Some(AsyncRuntimeType::AsyncStd);
292 }
293
294 None
295 }
296
297 fn is_tokio_present() -> bool {
302 std::env::var("TOKIO_WORKER_THREADS").is_ok()
303 }
304
305 fn is_async_std_present() -> bool {
308 std::env::var("ASYNC_STD_THREAD_COUNT").is_ok()
309 }
310
311 fn select_strategy(environment: &RuntimeEnvironment) -> MemScopeResult<TrackingStrategy> {
313 let strategy = match environment {
314 RuntimeEnvironment::SingleThreaded => TrackingStrategy::GlobalDirect,
315 RuntimeEnvironment::MultiThreaded { .. } => TrackingStrategy::ThreadLocal,
316 RuntimeEnvironment::AsyncRuntime { .. } => TrackingStrategy::TaskLocal,
317 RuntimeEnvironment::Hybrid { .. } => TrackingStrategy::HybridTracking,
318 };
319
320 debug!(
321 "Selected strategy {:?} for environment {:?}",
322 strategy, environment
323 );
324 Ok(strategy)
325 }
326
327 pub fn start_tracking(&mut self) -> MemScopeResult<TrackingSession> {
329 let session_id = format!(
330 "session_{}",
331 std::time::SystemTime::now()
332 .duration_since(std::time::UNIX_EPOCH)
333 .map_err(|e| MemScopeError::error(
334 "unified_tracker",
335 "start_tracking",
336 format!("Failed to generate session ID: {}", e)
337 ))?
338 .as_millis()
339 );
340
341 info!("Starting tracking session: {}", session_id);
342
343 let session = TrackingSession {
344 session_id: session_id.clone(),
345 backend: Arc::new(self.clone()),
346 start_time: std::time::Instant::now(),
347 };
348
349 debug!("Tracking session {} started", session_id);
350 Ok(session)
351 }
352
353 pub fn collect_data(&self) -> MemScopeResult<MemoryAnalysisData> {
355 debug!("Collecting tracking data");
356
357 let statistics = MemoryStatistics {
358 total_allocations: 0,
359 peak_memory_bytes: 0,
360 avg_allocation_size: 0.0,
361 session_duration_ms: 0,
362 };
363
364 let session_metadata = SessionMetadata {
365 session_id: "current_session".to_string(),
366 detected_environment: self.environment.clone(),
367 strategy_used: self.active_strategy,
368 overhead_percent: self.config.max_overhead_percent,
369 };
370
371 Ok(MemoryAnalysisData {
372 raw_data: vec![],
373 statistics,
374 environment: self.environment.clone(),
375 session_metadata,
376 })
377 }
378
379 pub fn shutdown(self) -> MemScopeResult<MemoryAnalysisData> {
381 info!("Shutting down unified backend");
382 self.collect_data()
383 }
384}
385
386impl Default for UnifiedBackend {
387 fn default() -> Self {
388 Self::new()
389 }
390}
391
392impl TrackingSession {
393 pub fn session_id(&self) -> &str {
395 &self.session_id
396 }
397
398 pub fn elapsed_time(&self) -> std::time::Duration {
400 self.start_time.elapsed()
401 }
402
403 pub fn collect_data(&self) -> MemScopeResult<MemoryAnalysisData> {
405 self.backend.collect_data()
406 }
407
408 pub fn end_session(self) -> MemScopeResult<MemoryAnalysisData> {
410 info!("Ending tracking session: {}", self.session_id);
411 self.backend.collect_data()
412 }
413}
414
415#[derive(Debug)]
417pub struct EnvironmentDetector {
418 config: DetectionConfig,
419}
420
421impl EnvironmentDetector {
422 pub fn new(config: DetectionConfig) -> Self {
424 Self { config }
425 }
426
427 pub fn detect(&self) -> MemScopeResult<EnvironmentDetection> {
429 let environment = UnifiedBackend::detect_environment()?;
430 let recommended_strategy = UnifiedBackend::select_strategy(&environment)?;
431
432 let thread_count = match &environment {
433 RuntimeEnvironment::SingleThreaded => 1,
434 RuntimeEnvironment::MultiThreaded { thread_count } => *thread_count,
435 RuntimeEnvironment::AsyncRuntime { .. } => 1,
436 RuntimeEnvironment::Hybrid { thread_count, .. } => *thread_count,
437 };
438
439 let confidence = self.config.confidence_level;
440
441 Ok(EnvironmentDetection {
442 environment,
443 recommended_strategy,
444 thread_count,
445 memory_usage: 0.0,
446 confidence,
447 })
448 }
449
450 pub fn config(&self) -> &DetectionConfig {
452 &self.config
453 }
454}
455
456impl Default for EnvironmentDetector {
457 fn default() -> Self {
458 Self::new(DetectionConfig::default())
459 }
460}
461
462pub fn initialize() -> MemScopeResult<UnifiedBackend> {
464 UnifiedBackend::initialize(BackendConfig::default())
465}
466
467pub fn get_backend() -> UnifiedBackend {
469 UnifiedBackend::new()
470}
471
472pub fn detect_environment() -> MemScopeResult<RuntimeEnvironment> {
474 UnifiedBackend::detect_environment()
475}
476
477#[derive(Debug, Clone)]
479pub struct DispatcherConfig {
480 pub auto_switch_strategies: bool,
482 pub max_concurrent_trackers: usize,
484 pub metrics_interval_ms: u64,
486 pub memory_threshold_mb: usize,
488}
489
490impl Default for DispatcherConfig {
491 fn default() -> Self {
492 Self {
493 auto_switch_strategies: true,
494 max_concurrent_trackers: 4,
495 metrics_interval_ms: 1000,
496 memory_threshold_mb: 100,
497 }
498 }
499}
500
501#[derive(Debug, Clone, Default)]
503pub struct DispatcherMetrics {
504 pub total_dispatches: u64,
506 pub strategy_switches: u64,
508 pub avg_dispatch_latency_us: f64,
510 pub memory_overhead_percent: f64,
512 pub active_trackers: usize,
514}
515
516#[derive(Debug, Clone)]
518pub enum TrackingOperation {
519 StartTracking,
521 StopTracking,
523 CollectData,
525}
526
527#[derive(Debug, Clone, Copy, PartialEq, Eq)]
529pub enum TrackerType {
530 SingleThread,
532 MultiThread,
534 AsyncTracker,
536 HybridTracker,
538}
539
540#[derive(Debug, Clone)]
542pub struct TrackerConfig {
543 pub sample_rate: f64,
545 pub max_overhead_mb: usize,
547}
548
549impl Default for TrackerConfig {
550 fn default() -> Self {
551 Self {
552 sample_rate: 1.0,
553 max_overhead_mb: 50,
554 }
555 }
556}
557
558#[derive(Debug, Clone, Default)]
560pub struct TrackerStatistics {
561 pub allocations_tracked: u64,
563 pub memory_tracked_bytes: u64,
565 pub overhead_bytes: u64,
567 pub tracking_duration_ms: u64,
569}
570
571#[derive(Debug)]
573pub enum TrackerError {
574 InitializationFailed { reason: String },
576 StartFailed { reason: String },
578 DataCollectionFailed { reason: String },
580 InvalidConfiguration { reason: String },
582}
583
584impl std::fmt::Display for TrackerError {
585 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
586 match self {
587 TrackerError::InitializationFailed { reason } => {
588 write!(f, "Tracker initialization failed: {}", reason)
589 }
590 TrackerError::StartFailed { reason } => {
591 write!(f, "Failed to start tracking: {}", reason)
592 }
593 TrackerError::DataCollectionFailed { reason } => {
594 write!(f, "Failed to collect tracking data: {}", reason)
595 }
596 TrackerError::InvalidConfiguration { reason } => {
597 write!(f, "Invalid tracker configuration: {}", reason)
598 }
599 }
600 }
601}
602
603impl std::error::Error for TrackerError {}
604
605pub trait MemoryTracker: Send + Sync {
607 fn initialize(&mut self, config: TrackerConfig) -> Result<(), TrackerError>;
609 fn start_tracking(&mut self) -> Result<(), TrackerError>;
611 fn stop_tracking(&mut self) -> Result<Vec<u8>, TrackerError>;
613 fn get_statistics(&self) -> TrackerStatistics;
615 fn is_active(&self) -> bool;
617 fn tracker_type(&self) -> TrackerType;
619}
620
621pub struct TrackingDispatcher {
626 active_strategy: Option<TrackingStrategy>,
627 config: DispatcherConfig,
628 metrics: DispatcherMetrics,
629}
630
631impl TrackingDispatcher {
632 pub fn new(config: DispatcherConfig) -> Self {
634 Self {
635 active_strategy: None,
636 config,
637 metrics: DispatcherMetrics::default(),
638 }
639 }
640
641 pub fn select_strategy(&mut self, environment: &RuntimeEnvironment) -> TrackingStrategy {
643 let strategy = match environment {
644 RuntimeEnvironment::SingleThreaded => TrackingStrategy::GlobalDirect,
645 RuntimeEnvironment::MultiThreaded { thread_count } => {
646 if *thread_count <= 2 {
647 TrackingStrategy::GlobalDirect
648 } else {
649 TrackingStrategy::ThreadLocal
650 }
651 }
652 RuntimeEnvironment::AsyncRuntime { .. } => TrackingStrategy::TaskLocal,
653 RuntimeEnvironment::Hybrid {
654 thread_count,
655 async_task_count,
656 } => {
657 if *thread_count > 1 && *async_task_count > 0 {
658 TrackingStrategy::HybridTracking
659 } else if *async_task_count > 0 {
660 TrackingStrategy::TaskLocal
661 } else {
662 TrackingStrategy::ThreadLocal
663 }
664 }
665 };
666
667 self.active_strategy = Some(strategy);
668 strategy
669 }
670
671 pub fn active_strategy(&self) -> Option<&TrackingStrategy> {
673 self.active_strategy.as_ref()
674 }
675
676 pub fn metrics(&self) -> &DispatcherMetrics {
678 &self.metrics
679 }
680
681 pub fn config(&self) -> &DispatcherConfig {
683 &self.config
684 }
685}
686
687impl Default for TrackingDispatcher {
688 fn default() -> Self {
689 Self::new(DispatcherConfig::default())
690 }
691}
692
693#[cfg(test)]
694mod tests {
695 use super::*;
696
697 #[test]
698 fn test_unified_backend_creation() {
699 let backend = UnifiedBackend::new();
700 assert!(matches!(
701 backend.environment(),
702 RuntimeEnvironment::SingleThreaded | RuntimeEnvironment::MultiThreaded { .. }
703 ));
704 }
705
706 #[test]
707 fn test_backend_initialization() {
708 let config = BackendConfig::default();
709 let backend = UnifiedBackend::initialize(config);
710 assert!(backend.is_ok());
711 }
712
713 #[test]
714 fn test_environment_detection() {
715 let env = UnifiedBackend::detect_environment();
716 assert!(env.is_ok());
717 }
718
719 #[test]
720 fn test_invalid_config_sample_rate() {
721 let config = BackendConfig {
722 sample_rate: 1.5,
723 ..Default::default()
724 };
725 let result = UnifiedBackend::initialize(config);
726 assert!(result.is_err());
727 }
728
729 #[test]
730 fn test_strategy_selection() {
731 let env = RuntimeEnvironment::SingleThreaded;
732 let strategy = UnifiedBackend::select_strategy(&env);
733 assert!(matches!(strategy, Ok(TrackingStrategy::GlobalDirect)));
734 }
735
736 #[test]
737 fn test_runtime_environment_variants() {
738 let single = RuntimeEnvironment::SingleThreaded;
739 let multi = RuntimeEnvironment::MultiThreaded { thread_count: 4 };
740 let async_env = RuntimeEnvironment::AsyncRuntime {
741 runtime_type: AsyncRuntimeType::Tokio,
742 };
743 let hybrid = RuntimeEnvironment::Hybrid {
744 thread_count: 2,
745 async_task_count: 4,
746 };
747
748 assert_ne!(single, multi);
749 assert_ne!(multi, async_env);
750 assert_ne!(async_env, hybrid);
751 }
752
753 #[test]
754 fn test_tracking_strategy_variants() {
755 let global = TrackingStrategy::GlobalDirect;
756 let thread_local = TrackingStrategy::ThreadLocal;
757 let task_local = TrackingStrategy::TaskLocal;
758 let hybrid = TrackingStrategy::HybridTracking;
759
760 assert_ne!(global, thread_local);
761 assert_ne!(thread_local, task_local);
762 assert_ne!(task_local, hybrid);
763 }
764
765 #[test]
766 fn test_tracking_session() {
767 let mut backend = UnifiedBackend::new();
768 let session = backend.start_tracking();
769 assert!(session.is_ok());
770
771 let session = session.unwrap();
772 assert!(!session.session_id().is_empty());
773 let _elapsed = session.elapsed_time();
774 }
775
776 #[test]
777 fn test_environment_detector() {
778 let detector = EnvironmentDetector::default();
779 let result = detector.detect();
780 assert!(result.is_ok());
781 }
782
783 #[test]
784 fn test_initialize_function() {
785 let result = initialize();
786 assert!(result.is_ok());
787 }
788
789 #[test]
790 fn test_get_backend_function() {
791 let backend = get_backend();
792 assert!(matches!(
793 backend.environment(),
794 RuntimeEnvironment::SingleThreaded | RuntimeEnvironment::MultiThreaded { .. }
795 ));
796 }
797}