use std::alloc::{GlobalAlloc, Layout, System};
use std::sync::atomic::{AtomicU64, Ordering};
static CURRENT: AtomicU64 = AtomicU64::new(0);
static PEAK: AtomicU64 = AtomicU64::new(0);
static TOTAL_ALLOCS: AtomicU64 = AtomicU64::new(0);
struct TrackingAllocator;
unsafe impl GlobalAlloc for TrackingAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let ptr = unsafe { System.alloc(layout) };
if !ptr.is_null() {
let size = layout.size() as u64;
TOTAL_ALLOCS.fetch_add(1, Ordering::Relaxed);
let now = CURRENT.fetch_add(size, Ordering::Relaxed) + size;
PEAK.fetch_max(now, Ordering::Relaxed);
}
ptr
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe { System.dealloc(ptr, layout) };
CURRENT.fetch_sub(layout.size() as u64, Ordering::Relaxed);
}
}
#[global_allocator]
static GLOBAL: TrackingAllocator = TrackingAllocator;
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct KgMemStats {
pub current_bytes: u64,
pub peak_bytes: u64,
pub total_allocs: u64,
}
#[no_mangle]
pub extern "C" fn kglite_memory_stats() -> KgMemStats {
KgMemStats {
current_bytes: CURRENT.load(Ordering::Relaxed),
peak_bytes: PEAK.load(Ordering::Relaxed),
total_allocs: TOTAL_ALLOCS.load(Ordering::Relaxed),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn memory_stats_track_allocations() {
let before = kglite_memory_stats();
let v: Vec<u64> = (0..10_000).collect();
let after = kglite_memory_stats();
assert!(after.total_allocs >= before.total_allocs);
assert!(after.peak_bytes >= after.current_bytes);
assert_eq!(v.len(), 10_000);
}
}