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 {
crate::ffi::value_boundary(
KgMemStats {
current_bytes: 0,
peak_bytes: 0,
total_allocs: 0,
},
|| {
let current_bytes = CURRENT.load(Ordering::Relaxed);
let peak_bytes = PEAK.fetch_max(current_bytes, Ordering::Relaxed);
KgMemStats {
current_bytes,
peak_bytes: peak_bytes.max(current_bytes),
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);
}
}