Skip to main content

memscope_rs/view/
filters.rs

1//! View filters for data filtering.
2
3use crate::snapshot::ActiveAllocation;
4use std::collections::HashSet;
5
6use super::MemoryView;
7
8/// View filter types.
9#[derive(Debug, Clone)]
10pub enum ViewFilter {
11    /// Filter by thread ID
12    ByThread(u64),
13    /// Filter by type name (contains)
14    ByType(String),
15    /// Filter by size range [min, max]
16    BySizeRange(usize, usize),
17    /// Filter by address range [min, max]
18    ByAddressRange(u64, u64),
19    /// Filter by minimum size
20    ByMinSize(usize),
21    /// Filter by maximum size
22    ByMaxSize(usize),
23}
24
25/// Filter builder for MemoryView.
26pub struct FilterBuilder<'a> {
27    view: &'a MemoryView,
28    filters: Vec<ViewFilter>,
29}
30
31impl<'a> FilterBuilder<'a> {
32    /// Create new filter builder.
33    pub fn new(view: &'a MemoryView) -> Self {
34        Self {
35            view,
36            filters: Vec::new(),
37        }
38    }
39
40    /// Filter by thread ID.
41    pub fn by_thread(mut self, thread_id: u64) -> Self {
42        self.filters.push(ViewFilter::ByThread(thread_id));
43        self
44    }
45
46    /// Filter by type name (contains match).
47    pub fn by_type(mut self, type_name: &str) -> Self {
48        self.filters.push(ViewFilter::ByType(type_name.to_string()));
49        self
50    }
51
52    /// Filter by size range [min, max].
53    pub fn by_size_range(mut self, min: usize, max: usize) -> Self {
54        self.filters.push(ViewFilter::BySizeRange(min, max));
55        self
56    }
57
58    /// Filter by address range [min, max].
59    pub fn by_address_range(mut self, min: u64, max: u64) -> Self {
60        self.filters.push(ViewFilter::ByAddressRange(min, max));
61        self
62    }
63
64    /// Filter by minimum size.
65    pub fn by_min_size(mut self, min: usize) -> Self {
66        self.filters.push(ViewFilter::ByMinSize(min));
67        self
68    }
69
70    /// Filter by maximum size.
71    pub fn by_max_size(mut self, max: usize) -> Self {
72        self.filters.push(ViewFilter::ByMaxSize(max));
73        self
74    }
75
76    /// Push a filter to the filter chain.
77    pub fn push(mut self, filter: ViewFilter) -> Self {
78        self.filters.push(filter);
79        self
80    }
81
82    /// Apply filters and return filtered allocations.
83    pub fn apply(&self) -> Vec<&ActiveAllocation> {
84        let mut result: Vec<_> = self.view.allocations();
85
86        for filter in &self.filters {
87            result = match filter {
88                ViewFilter::ByThread(tid) => {
89                    result.into_iter().filter(|a| a.thread_id == *tid).collect()
90                }
91                ViewFilter::ByType(t) => result
92                    .into_iter()
93                    .filter(|a| a.type_name.as_ref().map(|n| n.contains(t)).unwrap_or(false))
94                    .collect(),
95                ViewFilter::BySizeRange(min, max) => result
96                    .into_iter()
97                    .filter(|a| a.size >= *min && a.size <= *max)
98                    .collect(),
99                ViewFilter::ByAddressRange(min, max) => result
100                    .into_iter()
101                    .filter(|a| {
102                        a.ptr
103                            .map(|p| p as u64 >= *min && p as u64 <= *max)
104                            .unwrap_or(false)
105                    })
106                    .collect(),
107                ViewFilter::ByMinSize(min) => {
108                    result.into_iter().filter(|a| a.size >= *min).collect()
109                }
110                ViewFilter::ByMaxSize(max) => {
111                    result.into_iter().filter(|a| a.size <= *max).collect()
112                }
113            };
114        }
115
116        result
117    }
118
119    /// Apply filters and return count.
120    pub fn count(&self) -> usize {
121        self.apply().len()
122    }
123
124    /// Apply filters and return total size.
125    pub fn total_size(&self) -> usize {
126        self.apply().iter().map(|a| a.size).sum()
127    }
128
129    /// Get unique thread IDs after filtering.
130    pub fn thread_ids(&self) -> Vec<u64> {
131        let allocs = self.apply();
132        let ids: HashSet<u64> = allocs.iter().map(|a| a.thread_id).collect();
133        ids.into_iter().collect()
134    }
135
136    /// Get unique type names after filtering.
137    pub fn type_names(&self) -> Vec<String> {
138        let allocs = self.apply();
139        let types: HashSet<String> = allocs.iter().filter_map(|a| a.type_name.clone()).collect();
140        types.into_iter().collect()
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147    use crate::event_store::MemoryEvent;
148
149    #[test]
150    fn test_filter_by_thread() {
151        let events = vec![
152            MemoryEvent::allocate(0x1000, 64, 1),
153            MemoryEvent::allocate(0x2000, 128, 2),
154        ];
155        let view = MemoryView::from_events(events);
156        let builder = view.filter().by_thread(1);
157        let filtered = builder.apply();
158        assert_eq!(filtered.len(), 1);
159    }
160
161    #[test]
162    fn test_filter_by_size() {
163        let events = vec![
164            MemoryEvent::allocate(0x1000, 64, 1),
165            MemoryEvent::allocate(0x2000, 128, 1),
166            MemoryEvent::allocate(0x3000, 256, 1),
167        ];
168        let view = MemoryView::from_events(events);
169        let builder = view.filter().by_size_range(100, 200);
170        let filtered = builder.apply();
171        assert_eq!(filtered.len(), 1);
172    }
173
174    #[test]
175    fn test_filter_chain() {
176        let events = vec![
177            MemoryEvent::allocate(0x1000, 64, 1).with_type_name("Vec<i32>".to_string()),
178            MemoryEvent::allocate(0x2000, 128, 1).with_type_name("String".to_string()),
179            MemoryEvent::allocate(0x3000, 64, 2).with_type_name("Vec<i32>".to_string()),
180        ];
181        let view = MemoryView::from_events(events);
182        let builder = view.filter().by_thread(1).by_type("Vec");
183        let filtered = builder.apply();
184        assert_eq!(filtered.len(), 1);
185    }
186
187    #[test]
188    fn test_filter_by_type_no_match() {
189        let events =
190            vec![MemoryEvent::allocate(0x1000, 64, 1).with_type_name("Vec<i32>".to_string())];
191        let view = MemoryView::from_events(events);
192        let builder = view.filter().by_type("HashMap");
193        let filtered = builder.apply();
194        assert_eq!(filtered.len(), 0);
195    }
196
197    #[test]
198    fn test_filter_by_type_none_type_name() {
199        let events = vec![MemoryEvent::allocate(0x1000, 64, 1)];
200        let view = MemoryView::from_events(events);
201        let builder = view.filter().by_type("Vec");
202        let filtered = builder.apply();
203        assert_eq!(filtered.len(), 0);
204    }
205
206    #[test]
207    fn test_filter_by_address_range() {
208        let events = vec![
209            MemoryEvent::allocate(0x1000, 64, 1),
210            MemoryEvent::allocate(0x2000, 128, 1),
211            MemoryEvent::allocate(0x3000, 256, 1),
212        ];
213        let view = MemoryView::from_events(events);
214        let builder = view.filter().by_address_range(0x1500, 0x2500);
215        let filtered = builder.apply();
216        assert_eq!(filtered.len(), 1);
217    }
218
219    #[test]
220    fn test_filter_by_min_size() {
221        let events = vec![
222            MemoryEvent::allocate(0x1000, 64, 1),
223            MemoryEvent::allocate(0x2000, 128, 1),
224            MemoryEvent::allocate(0x3000, 256, 1),
225        ];
226        let view = MemoryView::from_events(events);
227        let builder = view.filter().by_min_size(100);
228        let filtered = builder.apply();
229        assert_eq!(filtered.len(), 2);
230    }
231
232    #[test]
233    fn test_filter_by_max_size() {
234        let events = vec![
235            MemoryEvent::allocate(0x1000, 64, 1),
236            MemoryEvent::allocate(0x2000, 128, 1),
237            MemoryEvent::allocate(0x3000, 256, 1),
238        ];
239        let view = MemoryView::from_events(events);
240        let builder = view.filter().by_max_size(100);
241        let filtered = builder.apply();
242        assert_eq!(filtered.len(), 1);
243    }
244
245    #[test]
246    fn test_filter_push() {
247        let events = vec![
248            MemoryEvent::allocate(0x1000, 64, 1),
249            MemoryEvent::allocate(0x2000, 128, 2),
250        ];
251        let view = MemoryView::from_events(events);
252        let builder = view.filter().push(ViewFilter::ByThread(1));
253        let filtered = builder.apply();
254        assert_eq!(filtered.len(), 1);
255    }
256
257    #[test]
258    fn test_filter_count() {
259        let events = vec![
260            MemoryEvent::allocate(0x1000, 64, 1),
261            MemoryEvent::allocate(0x2000, 128, 2),
262        ];
263        let view = MemoryView::from_events(events);
264        let count = view.filter().by_thread(1).count();
265        assert_eq!(count, 1);
266    }
267
268    #[test]
269    fn test_filter_total_size() {
270        let events = vec![
271            MemoryEvent::allocate(0x1000, 64, 1),
272            MemoryEvent::allocate(0x2000, 128, 1),
273        ];
274        let view = MemoryView::from_events(events);
275        let total = view.filter().by_thread(1).total_size();
276        assert_eq!(total, 192);
277    }
278
279    #[test]
280    fn test_filter_thread_ids() {
281        let events = vec![
282            MemoryEvent::allocate(0x1000, 64, 1),
283            MemoryEvent::allocate(0x2000, 128, 2),
284            MemoryEvent::allocate(0x3000, 256, 1),
285        ];
286        let view = MemoryView::from_events(events);
287        let ids = view.filter().thread_ids();
288        assert_eq!(ids.len(), 2);
289        assert!(ids.contains(&1));
290        assert!(ids.contains(&2));
291    }
292
293    #[test]
294    fn test_filter_type_names() {
295        let events = vec![
296            MemoryEvent::allocate(0x1000, 64, 1).with_type_name("Vec<i32>".to_string()),
297            MemoryEvent::allocate(0x2000, 128, 2).with_type_name("String".to_string()),
298        ];
299        let view = MemoryView::from_events(events);
300        let types = view.filter().type_names();
301        assert_eq!(types.len(), 2);
302        assert!(types.contains(&"Vec<i32>".to_string()));
303        assert!(types.contains(&"String".to_string()));
304    }
305
306    #[test]
307    fn test_filter_empty_view() {
308        let events: Vec<MemoryEvent> = vec![];
309        let view = MemoryView::from_events(events);
310        let binding = view.filter().by_thread(1);
311        let filtered = binding.apply();
312        assert_eq!(filtered.len(), 0);
313    }
314
315    #[test]
316    fn test_filter_no_filters() {
317        let events = vec![
318            MemoryEvent::allocate(0x1000, 64, 1),
319            MemoryEvent::allocate(0x2000, 128, 2),
320        ];
321        let view = MemoryView::from_events(events);
322        let binding = view.filter();
323        let filtered = binding.apply();
324        assert_eq!(filtered.len(), 2);
325    }
326
327    #[test]
328    fn test_filter_multiple_chained() {
329        let events = vec![
330            MemoryEvent::allocate(0x1000, 64, 1).with_type_name("Vec<i32>".to_string()),
331            MemoryEvent::allocate(0x2000, 128, 1).with_type_name("String".to_string()),
332            MemoryEvent::allocate(0x3000, 256, 1).with_type_name("Vec<i32>".to_string()),
333            MemoryEvent::allocate(0x4000, 64, 2).with_type_name("Vec<i32>".to_string()),
334        ];
335        let view = MemoryView::from_events(events);
336        let binding = view.filter().by_thread(1).by_type("Vec").by_max_size(100);
337        let filtered = binding.apply();
338        assert_eq!(filtered.len(), 1);
339    }
340}