memscope_rs/metadata/
scope.rs1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::sync::{Arc, Mutex};
9
10pub type ScopeId = u64;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct ScopeInfo {
16 pub name: String,
18 pub parent: Option<String>,
20 pub depth: usize,
22 pub variables: Vec<String>,
24 pub total_memory: usize,
26 pub peak_memory: usize,
28 pub allocation_count: usize,
30 pub lifetime_start: Option<u64>,
32 pub lifetime_end: Option<u64>,
34 pub is_active: bool,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct ScopeHierarchy {
41 pub root_scopes: Vec<String>,
43 pub scope_tree: HashMap<String, Vec<String>>,
45 pub max_depth: usize,
47 pub total_scopes: usize,
49}
50
51#[derive(Debug)]
53pub struct ScopeTracker {
54 active_scopes: Arc<Mutex<HashMap<ScopeId, ScopeInfo>>>,
56 scope_stack: Arc<Mutex<HashMap<String, Vec<ScopeId>>>>,
58 next_scope_id: Arc<Mutex<u64>>,
60 hierarchy: Arc<Mutex<ScopeHierarchy>>,
62}
63
64impl ScopeTracker {
65 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 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 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 self.active_scopes
139 .lock()
140 .expect("Failed to acquire active_scopes lock")
141 .insert(scope_id, scope_info.clone());
142
143 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 {
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 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 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 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 pub fn get_hierarchy(&self) -> ScopeHierarchy {
234 self.hierarchy
235 .lock()
236 .expect("Failed to acquire hierarchy lock")
237 .clone()
238 }
239
240 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}