Skip to main content

memscope_rs/timeline/
query.rs

1//! Timeline Query - Time-based event queries
2//!
3//! This module provides functionality for querying events based on
4//! time ranges and other temporal criteria.
5
6use crate::event_store::event::{MemoryEvent, MemoryEventType};
7use crate::event_store::EventStore;
8use std::sync::Arc;
9
10/// Timeline query engine
11///
12/// Provides functionality to query events based on time ranges
13/// and other temporal criteria.
14pub struct TimelineQuery {
15    /// Reference to the event store
16    event_store: Arc<EventStore>,
17}
18
19impl TimelineQuery {
20    /// Create a new timeline query
21    pub fn new(event_store: Arc<EventStore>) -> Self {
22        Self { event_store }
23    }
24
25    /// Get events in a time range
26    ///
27    /// # Arguments
28    /// * `start` - Start timestamp (inclusive)
29    /// * `end` - End timestamp (exclusive)
30    pub fn get_events_in_range(&self, start: u64, end: u64) -> Vec<MemoryEvent> {
31        self.event_store
32            .snapshot()
33            .into_iter()
34            .filter(|e| e.timestamp >= start && e.timestamp < end)
35            .collect()
36    }
37
38    /// Get allocation events in a time range
39    ///
40    /// # Arguments
41    /// * `start` - Start timestamp (inclusive)
42    /// * `end` - End timestamp (exclusive)
43    pub fn get_allocations_in_range(&self, start: u64, end: u64) -> Vec<MemoryEvent> {
44        self.get_events_in_range(start, end)
45            .into_iter()
46            .filter(|e| e.event_type == MemoryEventType::Allocate)
47            .collect()
48    }
49
50    /// Get deallocation events in a time range
51    ///
52    /// # Arguments
53    /// * `start` - Start timestamp (inclusive)
54    /// * `end` - End timestamp (exclusive)
55    pub fn get_deallocations_in_range(&self, start: u64, end: u64) -> Vec<MemoryEvent> {
56        self.get_events_in_range(start, end)
57            .into_iter()
58            .filter(|e| e.event_type == MemoryEventType::Deallocate)
59            .collect()
60    }
61
62    /// Get events for a specific thread in a time range
63    ///
64    /// # Arguments
65    /// * `thread_id` - The thread ID to filter by
66    /// * `start` - Start timestamp (inclusive)
67    /// * `end` - End timestamp (exclusive)
68    pub fn get_thread_events_in_range(
69        &self,
70        thread_id: u64,
71        start: u64,
72        end: u64,
73    ) -> Vec<MemoryEvent> {
74        self.get_events_in_range(start, end)
75            .into_iter()
76            .filter(|e| e.thread_id == thread_id)
77            .collect()
78    }
79
80    /// Get memory usage over time with cumulative tracking.
81    ///
82    /// # Arguments
83    /// * `start` - Start timestamp (inclusive)
84    /// * `end` - End timestamp (exclusive)
85    /// * `interval_ms` - Interval between snapshots in milliseconds
86    ///
87    /// # Returns
88    /// Vector of (timestamp, cumulative_memory_bytes) tuples showing
89    /// the actual memory usage at each point in time.
90    pub fn get_memory_usage_over_time(
91        &self,
92        start: u64,
93        end: u64,
94        interval_ms: u64,
95    ) -> Vec<(u64, usize)> {
96        let mut result = Vec::new();
97        let interval_ns = interval_ms * 1_000_000;
98
99        // Get all events in the time range, sorted by timestamp
100        let mut all_events: Vec<(u64, MemoryEvent)> = Vec::new();
101
102        for event in self.get_events_in_range(start, end) {
103            all_events.push((event.timestamp, event));
104        }
105
106        // Sort by timestamp
107        all_events.sort_by_key(|(ts, _)| *ts);
108
109        // Sample at each interval
110        let mut current = start;
111        let mut event_idx = 0;
112        let mut running_memory: usize = 0;
113
114        while current < end {
115            // Process all events up to current timestamp
116            while event_idx < all_events.len() {
117                let (ts, event) = &all_events[event_idx];
118                if *ts > current {
119                    break;
120                }
121
122                match &event.event_type {
123                    MemoryEventType::Allocate => {
124                        running_memory += event.size;
125                    }
126                    MemoryEventType::Deallocate => {
127                        running_memory = running_memory.saturating_sub(event.size);
128                    }
129                    MemoryEventType::Reallocate => {
130                        let old_size = event.old_size.unwrap_or(0);
131                        running_memory = running_memory
132                            .saturating_sub(old_size)
133                            .saturating_add(event.size);
134                    }
135                    _ => {}
136                }
137
138                event_idx += 1;
139            }
140
141            result.push((current, running_memory));
142            current += interval_ns;
143        }
144
145        result
146    }
147
148    /// Get peak memory usage in a time range
149    ///
150    /// # Arguments
151    /// * `start` - Start timestamp (inclusive)
152    /// * `end` - End timestamp (exclusive)
153    ///
154    /// # Note
155    /// This function processes all events in chronological order to find
156    /// the true peak memory usage, not just the maximum interval delta.
157    pub fn get_peak_memory_in_range(&self, start: u64, end: u64) -> usize {
158        // Get all events sorted by timestamp
159        let mut all_events: Vec<MemoryEvent> = Vec::new();
160
161        for event in self.get_events_in_range(start, end) {
162            all_events.push(event);
163        }
164
165        all_events.sort_by_key(|e| e.timestamp);
166
167        // Track running memory and find peak
168        let mut running_memory: usize = 0;
169        let mut peak_memory: usize = 0;
170
171        for event in all_events {
172            match &event.event_type {
173                MemoryEventType::Allocate => {
174                    running_memory += event.size;
175                }
176                MemoryEventType::Deallocate => {
177                    running_memory = running_memory.saturating_sub(event.size);
178                }
179                MemoryEventType::Reallocate => {
180                    let old_size = event.old_size.unwrap_or(0);
181                    running_memory = running_memory
182                        .saturating_sub(old_size)
183                        .saturating_add(event.size);
184                }
185                _ => {}
186            }
187            peak_memory = peak_memory.max(running_memory);
188        }
189
190        peak_memory
191    }
192
193    /// Get event rate (events per second) in a time range
194    ///
195    /// # Arguments
196    /// * `start` - Start timestamp (inclusive)
197    /// * `end` - End timestamp (exclusive)
198    pub fn get_event_rate(&self, start: u64, end: u64) -> f64 {
199        let events = self.get_events_in_range(start, end);
200        let duration_ns = end.saturating_sub(start) as f64;
201        if duration_ns > 0.0 {
202            (events.len() as f64) / (duration_ns / 1_000_000_000.0)
203        } else {
204            0.0
205        }
206    }
207}
208
209#[cfg(test)]
210mod tests {
211    use super::*;
212
213    #[test]
214    fn test_timeline_query_creation() {
215        let event_store = Arc::new(EventStore::new());
216        let query = TimelineQuery::new(event_store);
217        let events = query.get_events_in_range(0, 1000);
218        assert!(events.is_empty());
219    }
220
221    #[test]
222    fn test_get_events_in_range() {
223        let event_store = Arc::new(EventStore::new());
224        let event1 = MemoryEvent::allocate(0x1000, 1024, 123);
225        event_store.record(event1);
226        let event2 = MemoryEvent::deallocate(0x1000, 1024, 456);
227        event_store.record(event2);
228
229        let query = TimelineQuery::new(event_store);
230        // Use a large time range to capture all events
231        let events = query.get_events_in_range(0, u64::MAX);
232        assert_eq!(events.len(), 2);
233    }
234
235    #[test]
236    fn test_memory_usage_over_time() {
237        let event_store = Arc::new(EventStore::new());
238        let event = MemoryEvent::allocate(0x1000, 1024, 123);
239        event_store.record(event);
240
241        let query = TimelineQuery::new(event_store);
242        let usage = query.get_memory_usage_over_time(0, 1000, 100);
243        assert!(!usage.is_empty());
244    }
245}