Skip to main content

memscope_rs/core/
scope_tracker.rs

1//! Scope tracking functionality for memory analysis
2
3use crate::core::types::*;
4use crate::core::{MemScopeError, MemScopeResult, SystemErrorType};
5use std::collections::HashMap;
6use std::sync::atomic::{AtomicU64, Ordering};
7use std::sync::{Arc, Mutex, OnceLock, RwLock};
8use std::time::{SystemTime, UNIX_EPOCH};
9
10/// Global scope tracker instance
11static GLOBAL_SCOPE_TRACKER: OnceLock<Arc<ScopeTracker>> = OnceLock::new();
12
13/// Get the global scope tracker instance
14pub fn get_global_scope_tracker() -> Arc<ScopeTracker> {
15    GLOBAL_SCOPE_TRACKER
16        .get_or_init(|| Arc::new(ScopeTracker::new()))
17        .clone()
18}
19
20/// Unique identifier for scopes
21pub type ScopeId = u64;
22
23/// Core scope tracking functionality
24pub struct ScopeTracker {
25    /// Active scopes
26    pub active_scopes: RwLock<HashMap<ScopeId, ScopeInfo>>,
27    /// Completed scopes for analysis
28    pub completed_scopes: Mutex<Vec<ScopeInfo>>,
29    /// Scope hierarchy relationships
30    pub scope_hierarchy: Mutex<ScopeHierarchy>,
31    /// Next available scope ID using atomic counter
32    next_scope_id: AtomicU64,
33    /// Current scope stack per thread
34    pub scope_stack: RwLock<HashMap<String, Vec<ScopeId>>>,
35}
36
37impl ScopeTracker {
38    /// Create a new scope tracker
39    pub fn new() -> Self {
40        Self {
41            active_scopes: RwLock::new(HashMap::new()),
42            completed_scopes: Mutex::new(Vec::new()),
43            scope_hierarchy: Mutex::new(ScopeHierarchy {
44                root_scopes: Vec::new(),
45                scope_tree: HashMap::new(),
46                max_depth: 0,
47                total_scopes: 0,
48                relationships: HashMap::new(),
49                depth_map: HashMap::new(),
50            }),
51            next_scope_id: AtomicU64::new(1),
52            scope_stack: RwLock::new(HashMap::new()),
53        }
54    }
55
56    /// Enter a new scope
57    pub fn enter_scope(&self, name: String) -> MemScopeResult<ScopeId> {
58        let scope_id = self.allocate_scope_id();
59        let thread_id = format!("{:?}", std::thread::current().id());
60        let timestamp = current_timestamp();
61
62        let (parent_scope, depth) = {
63            let stack = self.scope_stack.read().map_err(|e| {
64                MemScopeError::system(
65                    SystemErrorType::Locking,
66                    format!("Failed to acquire scope_stack read lock: {}", e),
67                )
68            })?;
69            if let Some(thread_stack) = stack.get(&thread_id) {
70                if let Some(&parent_id) = thread_stack.last() {
71                    let active = self.active_scopes.read().map_err(|e| {
72                        MemScopeError::system(
73                            SystemErrorType::Locking,
74                            format!("Failed to acquire active_scopes read lock: {}", e),
75                        )
76                    })?;
77                    if let Some(parent) = active.get(&parent_id) {
78                        (Some(parent.name.clone()), parent.depth + 1)
79                    } else {
80                        (None, 0)
81                    }
82                } else {
83                    (None, 0)
84                }
85            } else {
86                (None, 0)
87            }
88        };
89
90        let scope_info = ScopeInfo {
91            name: name.clone(),
92            parent: parent_scope.clone(),
93            children: Vec::new(),
94            depth,
95            variables: Vec::new(),
96            total_memory: 0,
97            peak_memory: 0,
98            allocation_count: 0,
99            lifetime_start: Some(timestamp as u64),
100            lifetime_end: None,
101            is_active: true,
102            start_time: timestamp as u64,
103            end_time: None,
104            memory_usage: 0,
105            child_scopes: Vec::new(),
106            parent_scope: parent_scope.clone(),
107        };
108
109        self.active_scopes
110            .write()
111            .map_err(|e| {
112                MemScopeError::system(
113                    SystemErrorType::Locking,
114                    format!("Failed to acquire active_scopes write lock: {}", e),
115                )
116            })?
117            .insert(scope_id, scope_info);
118
119        self.scope_stack
120            .write()
121            .map_err(|e| {
122                MemScopeError::system(
123                    SystemErrorType::Locking,
124                    format!("Failed to acquire scope_stack write lock: {}", e),
125                )
126            })?
127            .entry(thread_id.clone())
128            .or_default()
129            .push(scope_id);
130
131        if let Ok(mut hierarchy) = self.scope_hierarchy.lock() {
132            hierarchy.depth_map.insert(name.clone(), depth);
133
134            if let Some(parent) = parent_scope.clone() {
135                hierarchy
136                    .relationships
137                    .entry(parent)
138                    .or_default()
139                    .push(name.clone());
140            } else {
141                hierarchy.root_scopes.push(name);
142            }
143        }
144
145        Ok(scope_id)
146    }
147
148    /// Exit a scope
149    pub fn exit_scope(&self, scope_id: ScopeId) -> MemScopeResult<()> {
150        let thread_id = format!("{:?}", std::thread::current().id());
151        let timestamp = current_timestamp();
152
153        let mut scope_info = self
154            .active_scopes
155            .write()
156            .map_err(|e| {
157                MemScopeError::system(
158                    SystemErrorType::Locking,
159                    format!("Failed to acquire active_scopes write lock: {}", e),
160                )
161            })?
162            .remove(&scope_id)
163            .ok_or_else(|| MemScopeError::internal(format!("Invalid scope ID: {scope_id}")))?;
164
165        scope_info.end_time = Some(timestamp as u64);
166        scope_info.lifetime_end = Some(timestamp as u64);
167
168        if let Ok(mut stack) = self.scope_stack.write() {
169            if let Some(thread_stack) = stack.get_mut(&thread_id) {
170                if let Some(pos) = thread_stack.iter().position(|&id| id == scope_id) {
171                    thread_stack.remove(pos);
172                }
173            }
174        }
175
176        if let Ok(mut completed_scopes) = self.completed_scopes.lock() {
177            completed_scopes.push(scope_info);
178        }
179
180        Ok(())
181    }
182
183    pub fn associate_variable(
184        &self,
185        variable_name: String,
186        memory_size: usize,
187    ) -> MemScopeResult<()> {
188        let thread_id = format!("{:?}", std::thread::current().id());
189
190        let current_scope_id = self
191            .scope_stack
192            .read()
193            .map_err(|e| {
194                MemScopeError::system(
195                    SystemErrorType::Locking,
196                    format!("Failed to acquire scope_stack read lock: {}", e),
197                )
198            })?
199            .get(&thread_id)
200            .and_then(|stack| stack.last().copied());
201
202        if let Some(scope_id) = current_scope_id {
203            if let Ok(mut active) = self.active_scopes.write() {
204                if let Some(scope) = active.get_mut(&scope_id) {
205                    scope.variables.push(variable_name);
206                    scope.memory_usage += memory_size;
207                    scope.peak_memory = scope.peak_memory.max(scope.memory_usage);
208                    scope.allocation_count += 1;
209                }
210            }
211        }
212
213        Ok(())
214    }
215
216    pub fn get_scope_analysis(&self) -> MemScopeResult<ScopeAnalysis> {
217        let mut all_scopes: Vec<ScopeInfo> = self
218            .active_scopes
219            .read()
220            .map_err(|e| {
221                MemScopeError::system(
222                    SystemErrorType::Locking,
223                    format!("Failed to acquire active_scopes read lock: {}", e),
224                )
225            })?
226            .values()
227            .cloned()
228            .collect();
229
230        if let Ok(completed_scopes) = self.completed_scopes.lock() {
231            all_scopes.extend(completed_scopes.iter().cloned());
232        }
233
234        let hierarchy = if let Ok(hierarchy) = self.scope_hierarchy.lock() {
235            hierarchy.clone()
236        } else {
237            ScopeHierarchy {
238                root_scopes: Vec::new(),
239                scope_tree: HashMap::new(),
240                max_depth: 0,
241                total_scopes: 0,
242                relationships: HashMap::new(),
243                depth_map: HashMap::new(),
244            }
245        };
246
247        Ok(ScopeAnalysis {
248            total_scopes: all_scopes.len(),
249            active_scopes: all_scopes.iter().filter(|s| s.is_active).count(),
250            max_depth: hierarchy.max_depth,
251            average_lifetime: 1000.0,
252            memory_efficiency: 0.8,
253            scopes: all_scopes,
254            scope_hierarchy: hierarchy,
255            cross_scope_references: Vec::new(),
256        })
257    }
258
259    pub fn get_scope_lifecycle_metrics(&self) -> MemScopeResult<Vec<ScopeLifecycleMetrics>> {
260        let metrics = if let Ok(completed_scopes) = self.completed_scopes.lock() {
261            completed_scopes
262                .iter()
263                .map(|scope| {
264                    let lifetime =
265                        scope.end_time.unwrap_or(current_timestamp() as u64) - scope.start_time;
266                    let efficiency_score = if scope.peak_memory > 0 {
267                        scope.memory_usage as f64 / scope.peak_memory as f64
268                    } else {
269                        1.0
270                    };
271
272                    ScopeLifecycleMetrics {
273                        scope_name: scope.name.clone(),
274                        variable_count: scope.variables.len(),
275                        average_lifetime_ms: lifetime as f64,
276                        total_memory_usage: scope.memory_usage,
277                        peak_memory_usage: scope.peak_memory,
278                        allocation_frequency: 1.0,
279                        deallocation_efficiency: efficiency_score,
280                        completed_allocations: scope.allocation_count,
281                        memory_growth_events: 0,
282                        peak_concurrent_variables: scope.variables.len(),
283                        memory_efficiency_ratio: if scope.peak_memory > 0 {
284                            scope.memory_usage as f64 / scope.peak_memory as f64
285                        } else {
286                            1.0
287                        },
288                        ownership_transfer_events: 0,
289                        fragmentation_score: 0.0,
290                        instant_allocations: 0,
291                        short_term_allocations: 0,
292                        medium_term_allocations: 0,
293                        long_term_allocations: 0,
294                        suspected_leaks: 0,
295                        risk_distribution: crate::core::types::RiskDistribution::default(),
296                        scope_metrics: Vec::new(),
297                        type_lifecycle_patterns: Vec::new(),
298                    }
299                })
300                .collect()
301        } else {
302            Vec::new()
303        };
304
305        Ok(metrics)
306    }
307
308    pub fn get_all_scopes(&self) -> Vec<ScopeInfo> {
309        let mut all_scopes: Vec<ScopeInfo> = match self.active_scopes.read() {
310            Ok(active) => active.values().cloned().collect(),
311            Err(_) => Vec::new(),
312        };
313
314        if let Ok(completed_scopes) = self.completed_scopes.lock() {
315            all_scopes.extend(completed_scopes.iter().cloned());
316        }
317
318        all_scopes
319    }
320
321    /// Allocate a new unique scope ID using atomic operations
322    fn allocate_scope_id(&self) -> ScopeId {
323        self.next_scope_id.fetch_add(1, Ordering::Relaxed)
324    }
325}
326
327impl Default for ScopeTracker {
328    fn default() -> Self {
329        Self::new()
330    }
331}
332
333/// RAII scope guard for automatic scope management
334pub struct ScopeGuard {
335    scope_id: ScopeId,
336    tracker: Arc<ScopeTracker>,
337}
338
339impl ScopeGuard {
340    pub fn enter(name: &str) -> MemScopeResult<Self> {
341        let tracker = get_global_scope_tracker();
342        let scope_id = tracker.enter_scope(name.to_string())?;
343
344        Ok(Self { scope_id, tracker })
345    }
346
347    pub fn scope_id(&self) -> ScopeId {
348        self.scope_id
349    }
350}
351
352impl Drop for ScopeGuard {
353    fn drop(&mut self) {
354        let _ = self.tracker.exit_scope(self.scope_id);
355    }
356}
357
358fn current_timestamp() -> u128 {
359    SystemTime::now()
360        .duration_since(UNIX_EPOCH)
361        .map(|d| d.as_millis())
362        .unwrap_or(0)
363}
364
365/// Macro for tracking scopes with automatic cleanup
366#[macro_export]
367macro_rules! track_scope {
368    ($scope_name:expr) => {
369        let _scope_guard = $crate::scope_tracker::ScopeGuard::enter($scope_name)?;
370    };
371    ($scope_name:expr, $block:block) => {{
372        let _scope_guard = $crate::scope_tracker::ScopeGuard::enter($scope_name)?;
373        $block
374    }};
375}
376
377/// Enhanced track_var macro that also associates with current scope
378#[macro_export]
379macro_rules! track_var_with_scope {
380    ($var:ident) => {{
381        // Track the variable normally
382        let result = $crate::_track_var_impl(&$var, stringify!($var));
383
384        // Also associate with current scope
385        if result.is_ok() {
386            if let Some(size) = $crate::Trackable::get_heap_ptr(&$var) {
387                let scope_tracker = $crate::scope_tracker::get_global_scope_tracker();
388                let _ = scope_tracker
389                    .associate_variable(stringify!($var).to_string(), std::mem::size_of_val(&$var));
390            }
391        }
392
393        result
394    }};
395}
396
397#[cfg(test)]
398mod tests {
399    use super::*;
400
401    #[test]
402    fn test_scope_tracker_creation() {
403        let tracker = ScopeTracker::new();
404        assert_eq!(tracker.next_scope_id.load(Ordering::SeqCst), 1);
405    }
406
407    #[test]
408    fn test_enter_and_exit_scope() {
409        let tracker = ScopeTracker::new();
410        let scope_id = tracker.enter_scope("test_scope".to_string()).unwrap();
411
412        // Check scope is active
413        assert!(tracker
414            .active_scopes
415            .read()
416            .unwrap()
417            .get(&scope_id)
418            .is_some());
419
420        // Exit scope
421        tracker.exit_scope(scope_id).unwrap();
422
423        // Check scope is no longer active
424        assert!(tracker
425            .active_scopes
426            .read()
427            .unwrap()
428            .get(&scope_id)
429            .is_none());
430    }
431}