use std::alloc::{GlobalAlloc, Layout, System};
use std::cell::Cell;
use std::hint::black_box;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct AllocStats {
pub allocs: u64,
pub deallocs: u64,
pub reallocs: u64,
pub bytes_allocated: u64,
}
impl AllocStats {
pub const ZERO: Self = Self {
allocs: 0,
deallocs: 0,
reallocs: 0,
bytes_allocated: 0,
};
pub fn is_zero(&self) -> bool {
self.allocs == 0 && self.deallocs == 0 && self.reallocs == 0
}
pub fn since(self, earlier: Self) -> Self {
Self {
allocs: self.allocs.wrapping_sub(earlier.allocs),
deallocs: self.deallocs.wrapping_sub(earlier.deallocs),
reallocs: self.reallocs.wrapping_sub(earlier.reallocs),
bytes_allocated: self.bytes_allocated.wrapping_sub(earlier.bytes_allocated),
}
}
}
thread_local! {
static STATS: Cell<AllocStats> = const { Cell::new(AllocStats::ZERO) };
}
#[inline]
fn record(update: impl FnOnce(&mut AllocStats)) {
let _ = STATS.try_with(|cell| {
let mut stats = cell.get();
update(&mut stats);
cell.set(stats);
});
}
pub fn thread_stats() -> AllocStats {
STATS.try_with(Cell::get).unwrap_or(AllocStats::ZERO)
}
pub fn measure<R>(f: impl FnOnce() -> R) -> (R, AllocStats) {
let before = thread_stats();
let result = f();
(result, thread_stats().since(before))
}
pub fn is_active() -> bool {
let ((), stats) = measure(|| drop(black_box(Box::new(0u8))));
stats.allocs > 0
}
#[derive(Debug, Default)]
pub struct CountingAllocator<A = System> {
inner: A,
}
impl CountingAllocator<System> {
pub const fn system() -> Self {
Self { inner: System }
}
}
impl<A> CountingAllocator<A> {
pub const fn new(inner: A) -> Self {
Self { inner }
}
}
#[inline]
fn bytes(size: usize) -> u64 {
size as u64
}
unsafe impl<A: GlobalAlloc> GlobalAlloc for CountingAllocator<A> {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
record(|s| {
s.allocs += 1;
s.bytes_allocated += bytes(layout.size());
});
unsafe { self.inner.alloc(layout) }
}
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
record(|s| {
s.allocs += 1;
s.bytes_allocated += bytes(layout.size());
});
unsafe { self.inner.alloc_zeroed(layout) }
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
record(|s| s.deallocs += 1);
unsafe { self.inner.dealloc(ptr, layout) }
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
record(|s| {
s.reallocs += 1;
s.bytes_allocated += bytes(new_size);
});
unsafe { self.inner.realloc(ptr, layout, new_size) }
}
}