Skip to main content

memscope_rs/metadata/
scope.rs

1//! Scope Tracker - Scope metadata management
2//!
3//! This module provides scope tracking and metadata management
4//! for the MetadataEngine.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::sync::{Arc, Mutex};
9
10/// Unique identifier for scopes
11pub type ScopeId = u64;
12
13/// Scope information
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct ScopeInfo {
16    /// Name of the scope
17    pub name: String,
18    /// Parent scope name (if any)
19    pub parent: Option<String>,
20    /// Depth of this scope in the hierarchy
21    pub depth: usize,
22    /// Variables in this scope
23    pub variables: Vec<String>,
24    /// Total memory usage in this scope
25    pub total_memory: usize,
26    /// Peak memory usage in this scope
27    pub peak_memory: usize,
28    /// Number of allocations in this scope
29    pub allocation_count: usize,
30    /// When this scope was created
31    pub lifetime_start: Option<u64>,
32    /// When this scope was destroyed
33    pub lifetime_end: Option<u64>,
34    /// Whether this scope is currently active
35    pub is_active: bool,
36}
37
38/// Scope hierarchy information
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct ScopeHierarchy {
41    /// Root scopes
42    pub root_scopes: Vec<String>,
43    /// Scope tree relationships
44    pub scope_tree: HashMap<String, Vec<String>>,
45    /// Maximum depth observed
46    pub max_depth: usize,
47    /// Total number of scopes
48    pub total_scopes: usize,
49}
50
51/// Scope Tracker - manages scope hierarchy and metadata
52#[derive(Debug)]
53pub struct ScopeTracker {
54    /// Active scopes
55    active_scopes: Arc<Mutex<HashMap<ScopeId, ScopeInfo>>>,
56    /// Scope stack per thread
57    scope_stack: Arc<Mutex<HashMap<String, Vec<ScopeId>>>>,
58    /// Next available scope ID
59    next_scope_id: Arc<Mutex<u64>>,
60    /// Scope hierarchy
61    hierarchy: Arc<Mutex<ScopeHierarchy>>,
62}
63
64impl ScopeTracker {
65    /// Create a new ScopeTracker
66    pub fn new() -> Self {
67        Self {
68            active_scopes: Arc::new(Mutex::new(HashMap::new())),
69            scope_stack: Arc::new(Mutex::new(HashMap::new())),
70            next_scope_id: Arc::new(Mutex::new(1)),
71            hierarchy: Arc::new(Mutex::new(ScopeHierarchy {
72                root_scopes: Vec::new(),
73                scope_tree: HashMap::new(),
74                max_depth: 0,
75                total_scopes: 0,
76            })),
77        }
78    }
79
80    /// Enter a new scope
81    pub fn enter_scope(&self, name: String) -> ScopeId {
82        let scope_id = {
83            let mut id = self
84                .next_scope_id
85                .lock()
86                .expect("Failed to acquire next_scope_id lock");
87            let id_val = *id;
88            *id += 1;
89            id_val
90        };
91
92        let thread_id = format!("{:?}", std::thread::current().id());
93        let timestamp = std::time::SystemTime::now()
94            .duration_since(std::time::UNIX_EPOCH)
95            .unwrap_or_default()
96            .as_nanos() as u64;
97
98        // Determine parent scope and depth
99        let (parent, depth) = {
100            let stack = self
101                .scope_stack
102                .lock()
103                .expect("Failed to acquire scope_stack lock");
104            if let Some(thread_stack) = stack.get(&thread_id) {
105                if let Some(&parent_id) = thread_stack.last() {
106                    if let Some(active) = self
107                        .active_scopes
108                        .lock()
109                        .expect("Failed to acquire active_scopes lock")
110                        .get(&parent_id)
111                    {
112                        (Some(active.name.clone()), active.depth + 1)
113                    } else {
114                        (None, 0)
115                    }
116                } else {
117                    (None, 0)
118                }
119            } else {
120                (None, 0)
121            }
122        };
123
124        let scope_info = ScopeInfo {
125            name: name.clone(),
126            parent: parent.clone(),
127            depth,
128            variables: Vec::new(),
129            total_memory: 0,
130            peak_memory: 0,
131            allocation_count: 0,
132            lifetime_start: Some(timestamp),
133            lifetime_end: None,
134            is_active: true,
135        };
136
137        // Add to active scopes
138        self.active_scopes
139            .lock()
140            .expect("Failed to acquire active_scopes lock")
141            .insert(scope_id, scope_info.clone());
142
143        // Push to scope stack
144        self.scope_stack
145            .lock()
146            .expect("Failed to acquire scope_stack lock")
147            .entry(thread_id)
148            .or_default()
149            .push(scope_id);
150
151        // Update hierarchy
152        {
153            let mut hierarchy = self
154                .hierarchy
155                .lock()
156                .expect("Failed to acquire hierarchy lock");
157            if let Some(parent_name) = &parent {
158                hierarchy
159                    .scope_tree
160                    .entry(parent_name.clone())
161                    .or_default()
162                    .push(name.clone());
163            } else {
164                if !hierarchy.root_scopes.contains(&name) {
165                    hierarchy.root_scopes.push(name);
166                }
167            }
168            hierarchy.total_scopes += 1;
169            if depth > hierarchy.max_depth {
170                hierarchy.max_depth = depth;
171            }
172        }
173
174        scope_id
175    }
176
177    /// Exit the current scope
178    pub fn exit_scope(&self) -> Option<ScopeId> {
179        let thread_id = format!("{:?}", std::thread::current().id());
180        let timestamp = std::time::SystemTime::now()
181            .duration_since(std::time::UNIX_EPOCH)
182            .unwrap_or_default()
183            .as_nanos() as u64;
184
185        let scope_id = {
186            let mut stack = self
187                .scope_stack
188                .lock()
189                .expect("Failed to acquire scope_stack lock");
190            if let Some(thread_stack) = stack.get_mut(&thread_id) {
191                thread_stack.pop()
192            } else {
193                None
194            }
195        };
196
197        if let Some(scope_id) = scope_id {
198            if let Some(scope) = self
199                .active_scopes
200                .lock()
201                .expect("Failed to acquire active_scopes lock")
202                .get_mut(&scope_id)
203            {
204                scope.lifetime_end = Some(timestamp);
205                scope.is_active = false;
206            }
207            Some(scope_id)
208        } else {
209            None
210        }
211    }
212
213    /// Get scope information by ID
214    pub fn get_scope(&self, scope_id: ScopeId) -> Option<ScopeInfo> {
215        self.active_scopes
216            .lock()
217            .expect("Failed to acquire active_scopes lock")
218            .get(&scope_id)
219            .cloned()
220    }
221
222    /// Get all scopes
223    pub fn get_all_scopes(&self) -> Vec<(ScopeId, ScopeInfo)> {
224        self.active_scopes
225            .lock()
226            .expect("Failed to acquire active_scopes lock")
227            .iter()
228            .map(|(k, v)| (*k, v.clone()))
229            .collect()
230    }
231
232    /// Get scope hierarchy
233    pub fn get_hierarchy(&self) -> ScopeHierarchy {
234        self.hierarchy
235            .lock()
236            .expect("Failed to acquire hierarchy lock")
237            .clone()
238    }
239
240    /// Clear all scopes
241    pub fn clear(&self) {
242        self.active_scopes
243            .lock()
244            .expect("Failed to acquire active_scopes lock")
245            .clear();
246        self.scope_stack
247            .lock()
248            .expect("Failed to acquire scope_stack lock")
249            .clear();
250        *self
251            .next_scope_id
252            .lock()
253            .expect("Failed to acquire next_scope_id lock") = 1;
254    }
255}
256
257impl Default for ScopeTracker {
258    fn default() -> Self {
259        Self::new()
260    }
261}
262
263#[cfg(test)]
264mod tests {
265    use super::*;
266
267    #[test]
268    fn test_scope_tracker_creation() {
269        let tracker = ScopeTracker::new();
270        assert_eq!(tracker.get_all_scopes().len(), 0);
271    }
272
273    #[test]
274    fn test_enter_exit_scope() {
275        let tracker = ScopeTracker::new();
276        let scope_id = tracker.enter_scope("test_scope".to_string());
277        assert!(scope_id > 0);
278
279        let scope = tracker.get_scope(scope_id);
280        assert!(scope.is_some());
281        assert!(scope.unwrap().is_active);
282
283        let exited = tracker.exit_scope();
284        assert!(exited.is_some());
285
286        let scope = tracker.get_scope(scope_id);
287        assert!(scope.is_some());
288        assert!(!scope.unwrap().is_active);
289    }
290
291    #[test]
292    fn test_nested_scopes() {
293        let tracker = ScopeTracker::new();
294        let outer_id = tracker.enter_scope("outer".to_string());
295        let inner_id = tracker.enter_scope("inner".to_string());
296
297        let outer = tracker.get_scope(outer_id).unwrap();
298        let inner = tracker.get_scope(inner_id).unwrap();
299
300        assert_eq!(inner.depth, outer.depth + 1);
301        assert_eq!(inner.parent, Some("outer".to_string()));
302
303        tracker.exit_scope();
304        tracker.exit_scope();
305    }
306
307    #[test]
308    fn test_hierarchy() {
309        let tracker = ScopeTracker::new();
310        tracker.enter_scope("root".to_string());
311        tracker.enter_scope("child1".to_string());
312        tracker.exit_scope();
313        tracker.enter_scope("child2".to_string());
314        tracker.exit_scope();
315        tracker.exit_scope();
316
317        let hierarchy = tracker.get_hierarchy();
318        assert_eq!(hierarchy.root_scopes.len(), 1);
319        assert_eq!(hierarchy.total_scopes, 3);
320    }
321}