memscope_rs/query/
engine.rs1use crate::query::types::{
7 AllocationQueryResult, QueryResult, SummaryQueryResult, ThreadQueryResult,
8};
9use crate::snapshot::{MemorySnapshot, SharedSnapshotEngine};
10use std::sync::Arc;
11
12pub struct QueryEngine {
22 snapshot_engine: SharedSnapshotEngine,
24}
25
26pub type SharedQueryEngine = Arc<QueryEngine>;
28
29impl QueryEngine {
30 pub fn new(snapshot_engine: SharedSnapshotEngine) -> Self {
32 Self { snapshot_engine }
33 }
34
35 fn get_snapshot(&self) -> MemorySnapshot {
37 self.snapshot_engine.build_snapshot()
38 }
39
40 pub fn top_allocations(&self, limit: usize) -> QueryResult {
45 let snapshot = self.get_snapshot();
46 let mut allocations: Vec<_> = snapshot.active_allocations.values().cloned().collect();
47
48 allocations.sort_by(|a, b| b.size.cmp(&a.size));
50
51 allocations.truncate(limit);
53
54 let total_bytes = allocations.iter().map(|a| a.size).sum();
55
56 QueryResult::Allocations(AllocationQueryResult {
57 count: allocations.len(),
58 total_bytes,
59 allocations,
60 })
61 }
62
63 pub fn allocations_by_thread(&self, thread_id: u64) -> QueryResult {
68 let snapshot = self.get_snapshot();
69 let allocations: Vec<_> = snapshot
70 .active_allocations
71 .values()
72 .filter(|a| a.thread_id == thread_id)
73 .cloned()
74 .collect();
75
76 let total_bytes = allocations.iter().map(|a| a.size).sum();
77
78 QueryResult::Allocations(AllocationQueryResult {
79 count: allocations.len(),
80 total_bytes,
81 allocations,
82 })
83 }
84
85 pub fn thread_stats(&self) -> QueryResult {
87 let snapshot = self.get_snapshot();
88 let threads: Vec<_> = snapshot.thread_stats.values().cloned().collect();
89 let total_bytes = threads.iter().map(|t| t.current_memory).sum();
90
91 QueryResult::Threads(ThreadQueryResult {
92 count: threads.len(),
93 total_bytes,
94 threads,
95 })
96 }
97
98 pub fn summary(&self) -> QueryResult {
100 let snapshot = self.get_snapshot();
101
102 QueryResult::Summary(SummaryQueryResult {
103 total_allocations: snapshot.stats.total_allocations,
104 total_deallocations: snapshot.stats.total_deallocations,
105 active_allocations: snapshot.stats.active_allocations,
106 total_allocated: snapshot.stats.total_allocated,
107 total_deallocated: snapshot.stats.total_deallocated,
108 current_memory: snapshot.stats.current_memory,
109 peak_memory: snapshot.stats.peak_memory,
110 thread_count: snapshot.thread_stats.len(),
111 })
112 }
113
114 pub fn allocations_by_variable(&self, var_name: &str) -> QueryResult {
119 let snapshot = self.get_snapshot();
120 let allocations: Vec<_> = snapshot
121 .active_allocations
122 .values()
123 .filter(|a| a.var_name.as_ref().map(|n| n == var_name).unwrap_or(false))
124 .cloned()
125 .collect();
126
127 let total_bytes = allocations.iter().map(|a| a.size).sum();
128
129 QueryResult::Allocations(AllocationQueryResult {
130 count: allocations.len(),
131 total_bytes,
132 allocations,
133 })
134 }
135
136 pub fn allocations_larger_than(&self, min_size: usize) -> QueryResult {
141 let snapshot = self.get_snapshot();
142 let allocations: Vec<_> = snapshot
143 .active_allocations
144 .values()
145 .filter(|a| a.size > min_size)
146 .cloned()
147 .collect();
148
149 let total_bytes = allocations.iter().map(|a| a.size).sum();
150
151 QueryResult::Allocations(AllocationQueryResult {
152 count: allocations.len(),
153 total_bytes,
154 allocations,
155 })
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162 use crate::event_store::EventStore;
163 use crate::snapshot::SnapshotEngine;
164
165 #[test]
166 fn test_query_engine_creation() {
167 let event_store = Arc::new(EventStore::new());
168 let snapshot_engine = Arc::new(SnapshotEngine::new(event_store));
169 let query_engine = QueryEngine::new(snapshot_engine);
170
171 let result = query_engine.summary();
172 match result {
173 QueryResult::Summary(summary) => {
174 assert_eq!(summary.total_allocations, 0);
175 }
176 _ => panic!("Expected summary result"),
177 }
178 }
179
180 #[test]
181 fn test_top_allocations() {
182 let event_store = Arc::new(EventStore::new());
183 event_store.record(crate::event_store::MemoryEvent::allocate(0x1000, 1024, 1));
184 event_store.record(crate::event_store::MemoryEvent::allocate(0x2000, 2048, 1));
185 event_store.record(crate::event_store::MemoryEvent::allocate(0x3000, 512, 1));
186
187 let snapshot_engine = Arc::new(SnapshotEngine::new(event_store));
188 let query_engine = QueryEngine::new(snapshot_engine);
189
190 let result = query_engine.top_allocations(2);
191 match result {
192 QueryResult::Allocations(allocations) => {
193 assert_eq!(allocations.count, 2);
194 assert_eq!(allocations.allocations[0].size, 2048);
195 assert_eq!(allocations.allocations[1].size, 1024);
196 }
197 _ => panic!("Expected allocations result"),
198 }
199 }
200
201 #[test]
202 fn test_allocations_by_thread() {
203 let event_store = Arc::new(EventStore::new());
204 event_store.record(crate::event_store::MemoryEvent::allocate(0x1000, 1024, 1));
205 event_store.record(crate::event_store::MemoryEvent::allocate(0x2000, 2048, 2));
206
207 let snapshot_engine = Arc::new(SnapshotEngine::new(event_store));
208 let query_engine = QueryEngine::new(snapshot_engine);
209
210 let result = query_engine.allocations_by_thread(1);
211 match result {
212 QueryResult::Allocations(allocations) => {
213 assert_eq!(allocations.count, 1);
214 assert_eq!(allocations.total_bytes, 1024);
215 }
216 _ => panic!("Expected allocations result"),
217 }
218 }
219
220 #[test]
221 fn test_thread_stats() {
222 let event_store = Arc::new(EventStore::new());
223 event_store.record(crate::event_store::MemoryEvent::allocate(0x1000, 1024, 1));
224 event_store.record(crate::event_store::MemoryEvent::allocate(0x2000, 2048, 2));
225
226 let snapshot_engine = Arc::new(SnapshotEngine::new(event_store));
227 let query_engine = QueryEngine::new(snapshot_engine);
228
229 let result = query_engine.thread_stats();
230 match result {
231 QueryResult::Threads(threads) => {
232 assert_eq!(threads.count, 2);
233 }
234 _ => panic!("Expected threads result"),
235 }
236 }
237
238 #[test]
239 fn test_summary() {
240 let event_store = Arc::new(EventStore::new());
241 event_store.record(crate::event_store::MemoryEvent::allocate(0x1000, 1024, 1));
242 event_store.record(crate::event_store::MemoryEvent::deallocate(0x1000, 1024, 1));
243
244 let snapshot_engine = Arc::new(SnapshotEngine::new(event_store));
245 let query_engine = QueryEngine::new(snapshot_engine);
246
247 let result = query_engine.summary();
248 match result {
249 QueryResult::Summary(summary) => {
250 assert_eq!(summary.total_allocations, 1);
251 assert_eq!(summary.total_deallocations, 1);
252 assert_eq!(summary.active_allocations, 0);
253 }
254 _ => panic!("Expected summary result"),
255 }
256 }
257}