pub mod allocation;
pub mod heap;
pub mod leak_detector;
pub mod snapshot;
pub use allocation::{
AllocationRecord, AllocationStats, AllocationTracker, GLOBAL_TRACKER, TrackedAllocator,
};
pub use heap::{HeapProfiler, HeapReport, HeapStats};
pub use leak_detector::{LeakDetector, LeakReport, LeakSeverity, PotentialLeak};
pub use snapshot::{MemorySnapshot, PoolSnapshot, SnapshotDiff};
use std::sync::atomic::{AtomicBool, Ordering};
static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false);
pub fn enable_profiling() {
PROFILING_ENABLED.store(true, Ordering::SeqCst);
tracing::info!("Memory profiling enabled");
}
pub fn disable_profiling() {
PROFILING_ENABLED.store(false, Ordering::SeqCst);
tracing::info!("Memory profiling disabled");
}
#[inline]
pub fn is_profiling_enabled() -> bool {
PROFILING_ENABLED.load(Ordering::Relaxed)
}
pub fn with_profiling<F, R>(f: F) -> (R, LeakReport)
where
F: FnOnce() -> R,
{
let detector = LeakDetector::new();
let _guard = detector.start();
let result = f();
let report = detector.finish();
(result, report)
}
pub async fn with_profiling_async<F, Fut, R>(f: F) -> (R, LeakReport)
where
F: FnOnce() -> Fut,
Fut: std::future::Future<Output = R>,
{
let detector = LeakDetector::new();
let _guard = detector.start();
let result = f().await;
let report = detector.finish();
(result, report)
}
pub struct MemoryProfiler {
tracker: AllocationTracker,
heap_profiler: HeapProfiler,
leak_detector: LeakDetector,
}
impl MemoryProfiler {
pub fn new() -> Self {
Self {
tracker: AllocationTracker::new(),
heap_profiler: HeapProfiler::new(),
leak_detector: LeakDetector::new(),
}
}
pub fn snapshot(&self) -> MemorySnapshot {
MemorySnapshot::capture(&self.tracker)
}
pub fn stats(&self) -> AllocationStats {
self.tracker.stats()
}
pub fn heap_stats(&self) -> HeapStats {
self.heap_profiler.stats()
}
pub fn detect_leaks(&self) -> LeakReport {
self.leak_detector.analyze(&self.tracker)
}
pub fn report(&self) -> MemoryReport {
MemoryReport {
allocation_stats: self.stats(),
heap_stats: self.heap_stats(),
leak_report: self.detect_leaks(),
pool_stats: self.pool_stats(),
}
}
pub fn pool_stats(&self) -> PoolStats {
use crate::memory::{GLOBAL_BUFFER_POOL, GLOBAL_STRING_POOL};
PoolStats {
string_pool: GLOBAL_STRING_POOL.stats(),
buffer_pool_available: GLOBAL_BUFFER_POOL.available(),
}
}
pub fn reset(&self) {
self.tracker.reset();
}
}
impl Default for MemoryProfiler {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct MemoryReport {
pub allocation_stats: AllocationStats,
pub heap_stats: HeapStats,
pub leak_report: LeakReport,
pub pool_stats: PoolStats,
}
impl MemoryReport {
pub fn has_issues(&self) -> bool {
self.leak_report.has_leaks()
|| self.allocation_stats.net_allocations() > 1000
|| self.heap_stats.fragmentation_ratio() > 0.3
}
pub fn summary(&self) -> String {
let mut s = String::new();
s.push_str("=== Memory Profile Report ===\n\n");
s.push_str("Allocations:\n");
s.push_str(&format!(
" Total: {} ({} bytes)\n",
self.allocation_stats.total_allocations, self.allocation_stats.total_bytes_allocated
));
s.push_str(&format!(
" Current: {} ({} bytes)\n",
self.allocation_stats.current_allocations, self.allocation_stats.current_bytes
));
s.push_str(&format!(
" Peak: {} bytes\n",
self.allocation_stats.peak_bytes
));
s.push_str("\nHeap:\n");
s.push_str(&format!(" Used: {} bytes\n", self.heap_stats.used_bytes));
s.push_str(&format!(" RSS: {} bytes\n", self.heap_stats.rss_bytes));
s.push_str(&format!(
" Fragmentation: {:.1}%\n",
self.heap_stats.fragmentation_ratio() * 100.0
));
s.push_str("\nPools:\n");
s.push_str(&format!(
" String pool: {} strings ({} bytes)\n",
self.pool_stats.string_pool.count, self.pool_stats.string_pool.total_bytes
));
s.push_str(&format!(
" Buffer pool: {} available\n",
self.pool_stats.buffer_pool_available
));
if self.leak_report.has_leaks() {
s.push_str("\n⚠️ POTENTIAL LEAKS DETECTED:\n");
s.push_str(&self.leak_report.summary());
} else {
s.push_str("\n✅ No memory leaks detected\n");
}
s
}
}
impl std::fmt::Display for MemoryReport {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.summary())
}
}
#[derive(Debug, Clone)]
pub struct PoolStats {
pub string_pool: crate::memory::PoolStats,
pub buffer_pool_available: usize,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_profiling_toggle() {
disable_profiling();
assert!(!is_profiling_enabled());
enable_profiling();
assert!(is_profiling_enabled());
disable_profiling();
assert!(!is_profiling_enabled());
}
#[test]
fn test_memory_profiler() {
let profiler = MemoryProfiler::new();
let snapshot = profiler.snapshot();
assert!(snapshot.timestamp > 0);
let stats = profiler.stats();
assert!(stats.total_allocations >= 0);
let report = profiler.report();
assert!(!report.summary().is_empty());
}
#[test]
fn test_with_profiling() {
let (result, report) = with_profiling(|| {
let v: Vec<i32> = (0..100).collect();
v.len()
});
assert_eq!(result, 100);
assert!(report.potential_leaks.is_empty() || !report.potential_leaks.is_empty());
}
}