Skip to main content

memscope_rs/timeline/
index.rs

1//! Timeline Index - Index for efficient timeline queries
2//!
3//! This module provides indexing structures for efficient timeline queries.
4
5use crate::event_store::MemoryEvent;
6use serde::{Deserialize, Serialize};
7use std::collections::{BTreeMap, HashMap};
8
9/// Timeline index for efficient event lookup
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct TimelineIndex {
12    /// Index by pointer address
13    pub by_ptr: HashMap<usize, Vec<usize>>,
14    /// Index by thread ID
15    pub by_thread: HashMap<u64, Vec<usize>>,
16    /// Index by scope name
17    pub by_scope: HashMap<String, Vec<usize>>,
18    /// Index by task ID
19    pub by_task: HashMap<u64, Vec<usize>>,
20    /// Index by timestamp (ordered)
21    pub by_time: BTreeMap<u64, Vec<usize>>,
22}
23
24impl TimelineIndex {
25    /// Create a new timeline index
26    pub fn new() -> Self {
27        Self {
28            by_ptr: HashMap::new(),
29            by_thread: HashMap::new(),
30            by_scope: HashMap::new(),
31            by_task: HashMap::new(),
32            by_time: BTreeMap::new(),
33        }
34    }
35
36    /// Index an event
37    ///
38    /// # Arguments
39    /// * `event_index` - The index of the event in the event list
40    /// * `event` - The event to index
41    pub fn index_event(&mut self, event_index: usize, event: &MemoryEvent) {
42        // Index by pointer
43        self.by_ptr.entry(event.ptr).or_default().push(event_index);
44
45        // Index by thread
46        self.by_thread
47            .entry(event.thread_id)
48            .or_default()
49            .push(event_index);
50
51        // Index by time
52        self.by_time
53            .entry(event.timestamp)
54            .or_default()
55            .push(event_index);
56
57        // Index by variable name if available
58        if let Some(ref var_name) = event.var_name {
59            self.by_scope
60                .entry(var_name.clone())
61                .or_default()
62                .push(event_index);
63        }
64    }
65
66    /// Index multiple events
67    ///
68    /// # Arguments
69    /// * `events` - The events to index
70    pub fn index_events(&mut self, events: &[MemoryEvent]) {
71        for (i, event) in events.iter().enumerate() {
72            self.index_event(i, event);
73        }
74    }
75
76    /// Get event indices by pointer
77    pub fn get_by_ptr(&self, ptr: usize) -> Option<&Vec<usize>> {
78        self.by_ptr.get(&ptr)
79    }
80
81    /// Get event indices by thread
82    pub fn get_by_thread(&self, thread_id: u64) -> Option<&Vec<usize>> {
83        self.by_thread.get(&thread_id)
84    }
85
86    /// Get event indices by scope
87    pub fn get_by_scope(&self, scope: &str) -> Option<&Vec<usize>> {
88        self.by_scope.get(scope)
89    }
90
91    /// Get event indices by time range
92    pub fn get_by_time_range(&self, start: u64, end: u64) -> Vec<usize> {
93        let mut result = Vec::new();
94        for (_timestamp, indices) in self.by_time.range(start..=end) {
95            result.extend(indices);
96        }
97        result
98    }
99
100    /// Clear the index
101    pub fn clear(&mut self) {
102        self.by_ptr.clear();
103        self.by_thread.clear();
104        self.by_scope.clear();
105        self.by_task.clear();
106        self.by_time.clear();
107    }
108}
109
110impl Default for TimelineIndex {
111    fn default() -> Self {
112        Self::new()
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119    use crate::event_store::MemoryEventType;
120
121    #[test]
122    fn test_timeline_index_creation() {
123        let index = TimelineIndex::new();
124        assert!(index.by_ptr.is_empty());
125        assert!(index.by_thread.is_empty());
126    }
127
128    #[test]
129    fn test_index_event() {
130        let mut index = TimelineIndex::new();
131        let event = MemoryEvent {
132            timestamp: 1000,
133            event_type: MemoryEventType::Allocate,
134            ptr: 0x1000,
135            size: 1024,
136            old_size: None,
137            thread_id: 1,
138            var_name: Some("test_var".to_string()),
139            type_name: Some("i32".to_string()),
140            call_stack_hash: None,
141            thread_name: None,
142            source_file: None,
143            source_line: None,
144            module_path: None,
145            clone_source_ptr: None,
146            clone_target_ptr: None,
147            stack_ptr: None,
148            task_id: None,
149        };
150
151        index.index_event(0, &event);
152
153        assert!(index.get_by_ptr(0x1000).is_some());
154        assert!(index.get_by_thread(1).is_some());
155        assert!(index.get_by_scope("test_var").is_some());
156    }
157
158    #[test]
159    fn test_index_multiple_events() {
160        let mut index = TimelineIndex::new();
161        let events = vec![
162            MemoryEvent::allocate(0x1000, 1024, 1),
163            MemoryEvent::allocate(0x2000, 2048, 1),
164            MemoryEvent::deallocate(0x1000, 1024, 1),
165        ];
166
167        index.index_events(&events);
168
169        assert_eq!(index.get_by_ptr(0x1000).unwrap().len(), 2);
170        assert_eq!(index.get_by_ptr(0x2000).unwrap().len(), 1);
171    }
172
173    #[test]
174    fn test_get_by_time_range() {
175        let mut index = TimelineIndex::new();
176        let events = vec![
177            MemoryEvent::allocate(0x1000, 1024, 1),
178            MemoryEvent::allocate(0x2000, 2048, 1),
179            MemoryEvent::deallocate(0x1000, 1024, 1),
180        ];
181
182        index.index_events(&events);
183
184        let range_events = index.get_by_time_range(0, u64::MAX);
185        assert_eq!(range_events.len(), 3);
186    }
187
188    #[test]
189    fn test_timeline_index_default() {
190        let index = TimelineIndex::default();
191        assert!(index.by_ptr.is_empty());
192        assert!(index.by_thread.is_empty());
193        assert!(index.by_scope.is_empty());
194        assert!(index.by_task.is_empty());
195        assert!(index.by_time.is_empty());
196    }
197
198    #[test]
199    fn test_timeline_index_clear() {
200        let mut index = TimelineIndex::new();
201        let event = MemoryEvent::allocate(0x1000, 1024, 1);
202        index.index_event(0, &event);
203
204        assert!(!index.by_ptr.is_empty());
205
206        index.clear();
207
208        assert!(index.by_ptr.is_empty());
209        assert!(index.by_thread.is_empty());
210        assert!(index.by_scope.is_empty());
211        assert!(index.by_task.is_empty());
212        assert!(index.by_time.is_empty());
213    }
214
215    #[test]
216    fn test_get_by_ptr_not_found() {
217        let index = TimelineIndex::new();
218        assert!(index.get_by_ptr(0xDEADBEEF).is_none());
219    }
220
221    #[test]
222    fn test_get_by_thread_not_found() {
223        let index = TimelineIndex::new();
224        assert!(index.get_by_thread(999).is_none());
225    }
226
227    #[test]
228    fn test_get_by_scope_not_found() {
229        let index = TimelineIndex::new();
230        assert!(index.get_by_scope("nonexistent").is_none());
231    }
232
233    #[test]
234    fn test_index_event_without_var_name() {
235        let mut index = TimelineIndex::new();
236        let mut event = MemoryEvent::allocate(0x1000, 1024, 1);
237        event.var_name = None;
238
239        index.index_event(0, &event);
240
241        assert!(index.get_by_ptr(0x1000).is_some());
242        assert!(index.by_scope.is_empty());
243    }
244
245    #[test]
246    fn test_get_by_time_range_empty() {
247        let index = TimelineIndex::new();
248        let range_events = index.get_by_time_range(0, 1000);
249        assert!(range_events.is_empty());
250    }
251
252    #[test]
253    fn test_get_by_time_range_partial() {
254        let mut index = TimelineIndex::new();
255        let mut event1 = MemoryEvent::allocate(0x1000, 1024, 1);
256        event1.timestamp = 100;
257        let mut event2 = MemoryEvent::allocate(0x2000, 2048, 1);
258        event2.timestamp = 200;
259        let mut event3 = MemoryEvent::allocate(0x3000, 4096, 1);
260        event3.timestamp = 300;
261
262        index.index_event(0, &event1);
263        index.index_event(1, &event2);
264        index.index_event(2, &event3);
265
266        let range_events = index.get_by_time_range(150, 250);
267        assert_eq!(range_events.len(), 1);
268    }
269
270    #[test]
271    fn test_timeline_index_clone() {
272        let mut index = TimelineIndex::new();
273        let event = MemoryEvent::allocate(0x1000, 1024, 1);
274        index.index_event(0, &event);
275
276        let cloned = index.clone();
277        assert!(cloned.get_by_ptr(0x1000).is_some());
278    }
279
280    #[test]
281    fn test_timeline_index_debug() {
282        let index = TimelineIndex::new();
283        let debug_str = format!("{:?}", index);
284        assert!(debug_str.contains("TimelineIndex"));
285    }
286
287    #[test]
288    fn test_index_multiple_threads() {
289        let mut index = TimelineIndex::new();
290        let event1 = MemoryEvent::allocate(0x1000, 1024, 1);
291        let event2 = MemoryEvent::allocate(0x2000, 2048, 2);
292        let event3 = MemoryEvent::allocate(0x3000, 4096, 1);
293
294        index.index_events(&[event1, event2, event3]);
295
296        assert_eq!(index.get_by_thread(1).unwrap().len(), 2);
297        assert_eq!(index.get_by_thread(2).unwrap().len(), 1);
298    }
299
300    #[test]
301    fn test_index_same_ptr_multiple_times() {
302        let mut index = TimelineIndex::new();
303        let event1 = MemoryEvent::allocate(0x1000, 1024, 1);
304        let event2 = MemoryEvent::deallocate(0x1000, 1024, 1);
305
306        index.index_events(&[event1, event2]);
307
308        let ptr_events = index.get_by_ptr(0x1000).unwrap();
309        assert_eq!(ptr_events.len(), 2);
310        assert_eq!(ptr_events[0], 0);
311        assert_eq!(ptr_events[1], 1);
312    }
313}