prax_query/profiling/
mod.rs1pub mod allocation;
53pub mod heap;
54pub mod leak_detector;
55pub mod snapshot;
56
57pub use allocation::{
58 AllocationRecord, AllocationStats, AllocationTracker, GLOBAL_TRACKER, TrackedAllocator,
59};
60pub use heap::{HeapProfiler, HeapReport, HeapStats};
61pub use leak_detector::{LeakDetector, LeakReport, LeakSeverity, PotentialLeak};
62pub use snapshot::{MemorySnapshot, PoolSnapshot, SnapshotDiff};
63
64use std::sync::atomic::{AtomicBool, Ordering};
65
66static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false);
68
69pub fn enable_profiling() {
71 PROFILING_ENABLED.store(true, Ordering::SeqCst);
72 tracing::info!("Memory profiling enabled");
73}
74
75pub fn disable_profiling() {
77 PROFILING_ENABLED.store(false, Ordering::SeqCst);
78 tracing::info!("Memory profiling disabled");
79}
80
81#[inline]
83pub fn is_profiling_enabled() -> bool {
84 PROFILING_ENABLED.load(Ordering::Relaxed)
85}
86
87pub fn with_profiling<F, R>(f: F) -> (R, LeakReport)
89where
90 F: FnOnce() -> R,
91{
92 let detector = LeakDetector::new();
93 let _guard = detector.start();
94
95 let result = f();
96
97 let report = detector.finish();
98 (result, report)
99}
100
101pub async fn with_profiling_async<F, Fut, R>(f: F) -> (R, LeakReport)
103where
104 F: FnOnce() -> Fut,
105 Fut: std::future::Future<Output = R>,
106{
107 let detector = LeakDetector::new();
108 let _guard = detector.start();
109
110 let result = f().await;
111
112 let report = detector.finish();
113 (result, report)
114}
115
116pub struct MemoryProfiler {
118 tracker: AllocationTracker,
119 heap_profiler: HeapProfiler,
120 leak_detector: LeakDetector,
121}
122
123impl MemoryProfiler {
124 pub fn new() -> Self {
126 Self {
127 tracker: AllocationTracker::new(),
128 heap_profiler: HeapProfiler::new(),
129 leak_detector: LeakDetector::new(),
130 }
131 }
132
133 pub fn snapshot(&self) -> MemorySnapshot {
135 MemorySnapshot::capture(&self.tracker)
136 }
137
138 pub fn stats(&self) -> AllocationStats {
140 self.tracker.stats()
141 }
142
143 pub fn heap_stats(&self) -> HeapStats {
145 self.heap_profiler.stats()
146 }
147
148 pub fn detect_leaks(&self) -> LeakReport {
150 self.leak_detector.analyze(&self.tracker)
151 }
152
153 pub fn report(&self) -> MemoryReport {
155 MemoryReport {
156 allocation_stats: self.stats(),
157 heap_stats: self.heap_stats(),
158 leak_report: self.detect_leaks(),
159 pool_stats: self.pool_stats(),
160 }
161 }
162
163 pub fn pool_stats(&self) -> PoolStats {
165 use crate::memory::{GLOBAL_BUFFER_POOL, GLOBAL_STRING_POOL};
166
167 PoolStats {
168 string_pool: GLOBAL_STRING_POOL.stats(),
169 buffer_pool_available: GLOBAL_BUFFER_POOL.available(),
170 }
171 }
172
173 pub fn reset(&self) {
175 self.tracker.reset();
176 }
177}
178
179impl Default for MemoryProfiler {
180 fn default() -> Self {
181 Self::new()
182 }
183}
184
185#[derive(Debug)]
187pub struct MemoryReport {
188 pub allocation_stats: AllocationStats,
190 pub heap_stats: HeapStats,
192 pub leak_report: LeakReport,
194 pub pool_stats: PoolStats,
196}
197
198impl MemoryReport {
199 pub fn has_issues(&self) -> bool {
201 self.leak_report.has_leaks()
202 || self.allocation_stats.net_allocations() > 1000
203 || self.heap_stats.fragmentation_ratio() > 0.3
204 }
205
206 pub fn summary(&self) -> String {
208 let mut s = String::new();
209 s.push_str("=== Memory Profile Report ===\n\n");
210
211 s.push_str("Allocations:\n");
212 s.push_str(&format!(
213 " Total: {} ({} bytes)\n",
214 self.allocation_stats.total_allocations, self.allocation_stats.total_bytes_allocated
215 ));
216 s.push_str(&format!(
217 " Current: {} ({} bytes)\n",
218 self.allocation_stats.current_allocations, self.allocation_stats.current_bytes
219 ));
220 s.push_str(&format!(
221 " Peak: {} bytes\n",
222 self.allocation_stats.peak_bytes
223 ));
224
225 s.push_str("\nHeap:\n");
226 s.push_str(&format!(" Used: {} bytes\n", self.heap_stats.used_bytes));
227 s.push_str(&format!(" RSS: {} bytes\n", self.heap_stats.rss_bytes));
228 s.push_str(&format!(
229 " Fragmentation: {:.1}%\n",
230 self.heap_stats.fragmentation_ratio() * 100.0
231 ));
232
233 s.push_str("\nPools:\n");
234 s.push_str(&format!(
235 " String pool: {} strings ({} bytes)\n",
236 self.pool_stats.string_pool.count, self.pool_stats.string_pool.total_bytes
237 ));
238 s.push_str(&format!(
239 " Buffer pool: {} available\n",
240 self.pool_stats.buffer_pool_available
241 ));
242
243 if self.leak_report.has_leaks() {
244 s.push_str("\n⚠️ POTENTIAL LEAKS DETECTED:\n");
245 s.push_str(&self.leak_report.summary());
246 } else {
247 s.push_str("\n✅ No memory leaks detected\n");
248 }
249
250 s
251 }
252}
253
254impl std::fmt::Display for MemoryReport {
255 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
256 write!(f, "{}", self.summary())
257 }
258}
259
260#[derive(Debug, Clone)]
262pub struct PoolStats {
263 pub string_pool: crate::memory::PoolStats,
265 pub buffer_pool_available: usize,
267}
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272
273 #[test]
274 fn test_profiling_toggle() {
275 disable_profiling();
276 assert!(!is_profiling_enabled());
277
278 enable_profiling();
279 assert!(is_profiling_enabled());
280
281 disable_profiling();
282 assert!(!is_profiling_enabled());
283 }
284
285 #[test]
286 fn test_memory_profiler() {
287 let profiler = MemoryProfiler::new();
288
289 let snapshot = profiler.snapshot();
291 assert!(snapshot.timestamp > 0);
292
293 let stats = profiler.stats();
295 assert!(stats.total_allocations >= 0);
296
297 let report = profiler.report();
299 assert!(!report.summary().is_empty());
300 }
301
302 #[test]
303 fn test_with_profiling() {
304 let (result, report) = with_profiling(|| {
305 let v: Vec<i32> = (0..100).collect();
307 v.len()
308 });
309
310 assert_eq!(result, 100);
311 assert!(report.potential_leaks.is_empty() || !report.potential_leaks.is_empty());
313 }
314}