1use crate::lockfree::aggregator::LockfreeAggregator;
6use std::sync::Arc;
7use thiserror::Error;
8use tracing::{debug, info, warn};
9
10pub struct UnifiedBackend {
13 environment: RuntimeEnvironment,
15 active_strategy: TrackingStrategy,
17 config: BackendConfig,
19 aggregator: Arc<LockfreeAggregator>,
21}
22
23#[derive(Debug, Clone, PartialEq)]
26pub enum RuntimeEnvironment {
27 SingleThreaded,
29 MultiThreaded { thread_count: usize },
31 AsyncRuntime { runtime_type: AsyncRuntimeType },
33 Hybrid {
35 thread_count: usize,
36 async_task_count: usize,
37 },
38}
39
40#[derive(Debug, Clone, PartialEq)]
42pub enum AsyncRuntimeType {
43 Tokio,
45 AsyncStd,
47 Custom,
49}
50
51#[derive(Debug, Clone, PartialEq)]
53pub enum TrackingStrategy {
54 GlobalDirect,
56 ThreadLocal,
58 TaskLocal,
60 HybridTracking,
62}
63
64#[derive(Debug, Clone)]
66pub struct BackendConfig {
67 pub auto_detect: bool,
69 pub force_strategy: Option<TrackingStrategy>,
71 pub sample_rate: f64,
73 pub max_overhead_percent: f64,
75}
76
77pub struct TrackingSession {
79 session_id: String,
81 backend: Arc<UnifiedBackend>,
83 start_time: std::time::Instant,
85}
86
87#[derive(Debug)]
89pub struct MemoryAnalysisData {
90 pub raw_data: Vec<u8>,
92 pub statistics: MemoryStatistics,
94 pub environment: RuntimeEnvironment,
96 pub session_metadata: SessionMetadata,
98}
99
100#[derive(Debug)]
102pub struct MemoryStatistics {
103 pub total_allocations: usize,
105 pub peak_memory_bytes: usize,
107 pub avg_allocation_size: f64,
109 pub session_duration_ms: u64,
111}
112
113#[derive(Debug)]
115pub struct SessionMetadata {
116 pub session_id: String,
118 pub detected_environment: RuntimeEnvironment,
120 pub strategy_used: TrackingStrategy,
122 pub overhead_percent: f64,
124}
125
126#[derive(Error, Debug)]
128pub enum BackendError {
129 #[error("Failed to detect runtime environment: {reason}")]
131 EnvironmentDetectionFailed { reason: String },
132
133 #[error("Cannot select appropriate tracking strategy for environment: {environment:?}")]
135 StrategySelectionFailed { environment: RuntimeEnvironment },
136
137 #[error("Failed to initialize tracking session: {reason}")]
139 TrackingInitializationFailed { reason: String },
140
141 #[error("Error collecting tracking data: {reason}")]
143 DataCollectionError { reason: String },
144
145 #[error("Invalid backend configuration: {reason}")]
147 ConfigurationError { reason: String },
148}
149
150impl Default for BackendConfig {
151 fn default() -> Self {
153 Self {
154 auto_detect: true,
155 force_strategy: None,
156 sample_rate: 1.0,
157 max_overhead_percent: 5.0,
158 }
159 }
160}
161
162impl UnifiedBackend {
163 pub fn initialize(config: BackendConfig) -> Result<Self, BackendError> {
166 if config.sample_rate < 0.0 || config.sample_rate > 1.0 {
168 return Err(BackendError::ConfigurationError {
169 reason: "Sample rate must be between 0.0 and 1.0".to_string(),
170 });
171 }
172
173 if config.max_overhead_percent < 0.0 || config.max_overhead_percent > 100.0 {
174 return Err(BackendError::ConfigurationError {
175 reason: "Max overhead percent must be between 0.0 and 100.0".to_string(),
176 });
177 }
178
179 info!("Initializing unified backend with config: {:?}", config);
180
181 let environment = if config.auto_detect {
183 Self::detect_environment()?
184 } else {
185 RuntimeEnvironment::SingleThreaded };
187
188 debug!("Detected environment: {:?}", environment);
189
190 let active_strategy = if let Some(forced) = config.force_strategy.clone() {
192 warn!("Using forced strategy: {:?}", forced);
193 forced
194 } else {
195 Self::select_strategy(&environment)?
196 };
197
198 info!("Selected tracking strategy: {:?}", active_strategy);
199
200 let output_dir = std::env::temp_dir().join("memscope_unified");
202 let aggregator = Arc::new(LockfreeAggregator::new(output_dir));
203
204 Ok(Self {
205 environment,
206 active_strategy,
207 config,
208 aggregator,
209 })
210 }
211
212 pub fn detect_environment() -> Result<RuntimeEnvironment, BackendError> {
215 debug!("Starting environment detection");
216
217 let async_runtime = Self::detect_async_runtime();
219
220 let thread_count = std::thread::available_parallelism()
222 .map(|p| p.get())
223 .unwrap_or(1);
224
225 let environment = match (async_runtime, thread_count) {
227 (Some(runtime_type), 0) => {
228 RuntimeEnvironment::AsyncRuntime { runtime_type }
230 }
231 (Some(runtime_type), 1) => RuntimeEnvironment::AsyncRuntime { runtime_type },
232 (Some(_runtime_type), threads) => {
233 RuntimeEnvironment::Hybrid {
234 thread_count: threads,
235 async_task_count: 0, }
237 }
238 (None, 1) => RuntimeEnvironment::SingleThreaded,
239 (None, threads) => RuntimeEnvironment::MultiThreaded {
240 thread_count: threads,
241 },
242 };
243
244 debug!("Environment detection completed: {:?}", environment);
245 Ok(environment)
246 }
247
248 fn detect_async_runtime() -> Option<AsyncRuntimeType> {
251 if Self::is_tokio_present() {
253 debug!("Tokio runtime detected");
254 return Some(AsyncRuntimeType::Tokio);
255 }
256
257 if Self::is_async_std_present() {
259 debug!("async-std runtime detected");
260 return Some(AsyncRuntimeType::AsyncStd);
261 }
262
263 debug!("No async runtime detected");
265 None
266 }
267
268 fn is_tokio_present() -> bool {
270 std::env::var("TOKIO_WORKER_THREADS").is_ok()
273 }
275
276 fn is_async_std_present() -> bool {
278 false
281 }
282
283 fn select_strategy(environment: &RuntimeEnvironment) -> Result<TrackingStrategy, BackendError> {
285 let strategy = match environment {
286 RuntimeEnvironment::SingleThreaded => TrackingStrategy::GlobalDirect,
287 RuntimeEnvironment::MultiThreaded { .. } => TrackingStrategy::ThreadLocal,
288 RuntimeEnvironment::AsyncRuntime { .. } => TrackingStrategy::TaskLocal,
289 RuntimeEnvironment::Hybrid { .. } => TrackingStrategy::HybridTracking,
290 };
291
292 debug!(
293 "Selected strategy {:?} for environment {:?}",
294 strategy, environment
295 );
296 Ok(strategy)
297 }
298
299 pub fn start_tracking(&mut self) -> Result<TrackingSession, BackendError> {
302 let session_id = format!(
303 "session_{}",
304 std::time::SystemTime::now()
305 .duration_since(std::time::UNIX_EPOCH)
306 .map_err(|e| BackendError::TrackingInitializationFailed {
307 reason: format!("Failed to generate session ID: {}", e),
308 })?
309 .as_millis()
310 );
311
312 info!("Starting tracking session: {}", session_id);
313
314 match self.active_strategy {
316 TrackingStrategy::GlobalDirect => {
317 self.initialize_global_tracking()?;
318 }
319 TrackingStrategy::ThreadLocal => {
320 self.initialize_thread_local_tracking()?;
321 }
322 TrackingStrategy::TaskLocal => {
323 self.initialize_task_local_tracking()?;
324 }
325 TrackingStrategy::HybridTracking => {
326 self.initialize_hybrid_tracking()?;
327 }
328 }
329
330 let session = TrackingSession {
331 session_id: session_id.clone(),
332 backend: Arc::new(self.clone()),
333 start_time: std::time::Instant::now(),
334 };
335
336 debug!("Tracking session {} started successfully", session_id);
337 Ok(session)
338 }
339
340 fn initialize_global_tracking(&mut self) -> Result<(), BackendError> {
342 debug!("Initializing global direct tracking");
343 Ok(())
345 }
346
347 fn initialize_thread_local_tracking(&mut self) -> Result<(), BackendError> {
349 debug!("Initializing thread-local tracking");
350 Ok(())
352 }
353
354 fn initialize_task_local_tracking(&mut self) -> Result<(), BackendError> {
356 debug!("Initializing task-local tracking");
357 Ok(())
359 }
360
361 fn initialize_hybrid_tracking(&mut self) -> Result<(), BackendError> {
363 debug!("Initializing hybrid tracking");
364 Ok(())
366 }
367
368 pub fn collect_data(&self) -> Result<MemoryAnalysisData, BackendError> {
371 debug!("Collecting tracking data");
372
373 let raw_data = vec![];
376
377 let statistics = self.calculate_statistics(&raw_data)?;
379
380 let session_metadata = SessionMetadata {
382 session_id: "current_session".to_string(), detected_environment: self.environment.clone(),
384 strategy_used: self.active_strategy.clone(),
385 overhead_percent: self.measure_overhead(),
386 };
387
388 let analysis_data = MemoryAnalysisData {
389 raw_data,
390 statistics,
391 environment: self.environment.clone(),
392 session_metadata,
393 };
394
395 info!(
396 "Data collection completed, {} allocations tracked",
397 analysis_data.statistics.total_allocations
398 );
399
400 Ok(analysis_data)
401 }
402
403 fn calculate_statistics(&self, _raw_data: &[u8]) -> Result<MemoryStatistics, BackendError> {
405 Ok(MemoryStatistics {
408 total_allocations: 0,
409 peak_memory_bytes: 0,
410 avg_allocation_size: 0.0,
411 session_duration_ms: 0,
412 })
413 }
414
415 fn measure_overhead(&self) -> f64 {
417 self.config.max_overhead_percent
420 }
421
422 pub fn shutdown(self) -> Result<MemoryAnalysisData, BackendError> {
424 info!("Shutting down unified backend");
425
426 let final_data = self.collect_data()?;
428
429 debug!("Backend shutdown completed successfully");
430 Ok(final_data)
431 }
432}
433
434impl Clone for UnifiedBackend {
436 fn clone(&self) -> Self {
437 Self {
438 environment: self.environment.clone(),
439 active_strategy: self.active_strategy.clone(),
440 config: self.config.clone(),
441 aggregator: Arc::clone(&self.aggregator),
442 }
443 }
444}
445
446impl TrackingSession {
447 pub fn session_id(&self) -> &str {
449 &self.session_id
450 }
451
452 pub fn elapsed_time(&self) -> std::time::Duration {
454 self.start_time.elapsed()
455 }
456
457 pub fn collect_data(&self) -> Result<MemoryAnalysisData, BackendError> {
459 self.backend.collect_data()
460 }
461
462 pub fn end_session(self) -> Result<MemoryAnalysisData, BackendError> {
464 info!("Ending tracking session: {}", self.session_id);
465
466 let final_data = self.backend.collect_data()?;
467
468 debug!(
469 "Session {} ended after {:?}",
470 self.session_id,
471 self.start_time.elapsed()
472 );
473
474 Ok(final_data)
475 }
476}
477
478#[cfg(test)]
479mod tests {
480 use super::*;
481
482 #[test]
483 fn test_backend_initialization() {
484 let config = BackendConfig::default();
485 let backend = UnifiedBackend::initialize(config);
486 assert!(backend.is_ok());
487 }
488
489 #[test]
490 fn test_environment_detection() {
491 let env = UnifiedBackend::detect_environment();
492 assert!(env.is_ok());
493 }
494
495 #[test]
496 fn test_invalid_config_sample_rate() {
497 let config = BackendConfig {
498 sample_rate: 1.5, ..Default::default()
500 };
501 let result = UnifiedBackend::initialize(config);
502 assert!(matches!(
503 result,
504 Err(BackendError::ConfigurationError { .. })
505 ));
506 }
507
508 #[test]
509 fn test_strategy_selection() {
510 let env = RuntimeEnvironment::SingleThreaded;
511 let strategy = UnifiedBackend::select_strategy(&env);
512 assert!(matches!(strategy, Ok(TrackingStrategy::GlobalDirect)));
513 }
514}