Skip to main content

memscope_rs/analyzer/
timeline.rs

1//! Timeline analysis module.
2
3use crate::event_store::{MemoryEvent, MemoryEventType};
4use crate::view::MemoryView;
5use tracing::debug;
6
7/// Timeline analysis module.
8///
9/// Provides time-based memory analysis.
10pub struct TimelineAnalysis {
11    view: MemoryView,
12}
13
14impl TimelineAnalysis {
15    /// Create from view.
16    pub fn from_view(view: &MemoryView) -> Self {
17        debug!(
18            "Creating TimelineAnalysis with {} events",
19            view.events().len()
20        );
21        Self { view: view.clone() }
22    }
23
24    /// Query events in time range.
25    pub fn query(&self, start: u64, end: u64) -> TimelineResult {
26        debug!("Querying events in time range [{}, {}]", start, end);
27        let events: Vec<&MemoryEvent> = self
28            .view
29            .events()
30            .iter()
31            .filter(|e| e.timestamp >= start && e.timestamp <= end)
32            .collect();
33
34        let result = TimelineResult {
35            event_count: events.len(),
36            start,
37            end,
38        };
39        debug!("Query returned {} events", result.event_count);
40        result
41    }
42
43    /// Get event timeline.
44    pub fn timeline(&self) -> Vec<TimelineEvent> {
45        let events: Vec<TimelineEvent> = self
46            .view
47            .events()
48            .iter()
49            .map(|e| TimelineEvent {
50                timestamp: e.timestamp,
51                event_type: format!("{:?}", e.event_type),
52                ptr: e.ptr,
53                size: e.size,
54                thread_id: e.thread_id,
55            })
56            .collect();
57        debug!("Timeline has {} events", events.len());
58        events
59    }
60
61    /// Get allocation rate over time.
62    ///
63    /// Groups allocations into time buckets and calculates the rate
64    /// for each bucket. Returns a vector of allocation rates per bucket.
65    pub fn allocation_rate(&self, bucket_ms: u64) -> Vec<AllocationRate> {
66        debug!("Computing allocation rate with {}ms buckets", bucket_ms);
67        let events = self.view.events();
68        if events.is_empty() || bucket_ms == 0 {
69            debug!("Allocation rate: no events or invalid bucket size");
70            return vec![];
71        }
72
73        // Find time range
74        let min_time = events.iter().map(|e| e.timestamp).min().unwrap_or(0);
75        let max_time = events.iter().map(|e| e.timestamp).max().unwrap_or(0);
76
77        // Calculate number of buckets
78        let time_range = max_time.saturating_sub(min_time);
79        let bucket_ns = bucket_ms * 1_000_000; // Convert ms to ns
80        let num_buckets = ((time_range / bucket_ns) + 1) as usize;
81
82        if num_buckets == 0 || num_buckets > 10000 {
83            debug!(
84                "Allocation rate: bucket count {} exceeds limit or is zero",
85                num_buckets
86            );
87            return vec![];
88        }
89
90        // Initialize buckets
91        let mut buckets: Vec<AllocationRate> = (0..num_buckets)
92            .map(|i| AllocationRate {
93                start: min_time + (i as u64 * bucket_ns),
94                end: min_time + ((i + 1) as u64 * bucket_ns),
95                count: 0,
96                bytes: 0,
97            })
98            .collect();
99
100        // Fill buckets
101        let mut skipped_count = 0usize;
102        for event in events {
103            if event.event_type == MemoryEventType::Allocate {
104                let bucket_idx = ((event.timestamp.saturating_sub(min_time)) / bucket_ns) as usize;
105                if bucket_idx < buckets.len() {
106                    buckets[bucket_idx].count += 1;
107                    buckets[bucket_idx].bytes += event.size;
108                } else {
109                    skipped_count += 1;
110                }
111            }
112        }
113
114        if skipped_count > 0 {
115            debug!(
116                "Allocation rate: {} events skipped due to bucket index out of range",
117                skipped_count
118            );
119        }
120
121        // Filter out empty buckets at the end
122        while buckets.last().map(|b| b.count == 0).unwrap_or(false) {
123            buckets.pop();
124        }
125
126        debug!("Allocation rate: {} buckets computed", buckets.len());
127        buckets
128    }
129}
130
131/// Timeline query result.
132#[derive(Debug, Clone)]
133pub struct TimelineResult {
134    /// Number of events in range
135    pub event_count: usize,
136    /// Start timestamp
137    pub start: u64,
138    /// End timestamp
139    pub end: u64,
140}
141
142/// A timeline event.
143#[derive(Debug, Clone)]
144pub struct TimelineEvent {
145    /// Event timestamp
146    pub timestamp: u64,
147    /// Event type
148    pub event_type: String,
149    /// Memory pointer
150    pub ptr: usize,
151    /// Allocation size
152    pub size: usize,
153    /// Thread ID
154    pub thread_id: u64,
155}
156
157/// Allocation rate over a time period.
158#[derive(Debug, Clone)]
159pub struct AllocationRate {
160    /// Start of period
161    pub start: u64,
162    /// End of period
163    pub end: u64,
164    /// Number of allocations
165    pub count: usize,
166    /// Total bytes allocated
167    pub bytes: usize,
168}
169
170#[cfg(test)]
171mod tests {
172    use super::*;
173    use crate::event_store::MemoryEvent;
174
175    #[test]
176    fn test_timeline_query() {
177        let events = vec![
178            MemoryEvent::allocate(0x1000, 64, 1),
179            MemoryEvent::allocate(0x2000, 128, 2),
180        ];
181        let view = MemoryView::from_events(events);
182        let analysis = TimelineAnalysis::from_view(&view);
183        let result = analysis.query(0, u64::MAX);
184        assert_eq!(result.event_count, 2);
185    }
186
187    #[test]
188    fn test_allocation_rate() {
189        let events = vec![
190            MemoryEvent::allocate(0x1000, 64, 1),
191            MemoryEvent::allocate(0x2000, 128, 1),
192            MemoryEvent::allocate(0x3000, 256, 1),
193        ];
194        let view = MemoryView::from_events(events);
195        let analysis = TimelineAnalysis::from_view(&view);
196        let rates = analysis.allocation_rate(1); // 1ms buckets
197        assert!(!rates.is_empty());
198        let total_count: usize = rates.iter().map(|r| r.count).sum();
199        assert_eq!(total_count, 3);
200    }
201
202    #[test]
203    fn test_allocation_rate_empty() {
204        let events: Vec<MemoryEvent> = vec![];
205        let view = MemoryView::from_events(events);
206        let analysis = TimelineAnalysis::from_view(&view);
207        let rates = analysis.allocation_rate(1);
208        assert!(rates.is_empty());
209    }
210}