use std::alloc::{GlobalAlloc, Layout, System};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering::Relaxed};
use crate::scope::ACTIVE;
static COUNT: AtomicU64 = AtomicU64::new(0);
static BYTES: AtomicU64 = AtomicU64::new(0);
static SEEN: AtomicBool = AtomicBool::new(false);
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct AllocCounts {
pub count: u64,
pub bytes: u64,
}
pub struct CountingAllocator;
#[inline]
fn note(bytes: usize) {
if ACTIVE.load(Relaxed) > 0 {
if !SEEN.load(Relaxed) {
SEEN.store(true, Relaxed);
}
COUNT.fetch_add(1, Relaxed);
BYTES.fetch_add(bytes as u64, Relaxed);
}
}
unsafe impl GlobalAlloc for CountingAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
note(layout.size());
unsafe { System.alloc(layout) }
}
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
note(layout.size());
unsafe { System.alloc_zeroed(layout) }
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe { System.dealloc(ptr, layout) }
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
note(new_size);
unsafe { System.realloc(ptr, layout, new_size) }
}
}
pub fn note_allocation(bytes: usize) {
note(bytes);
}
pub fn alloc_counts() -> AllocCounts {
AllocCounts {
count: COUNT.load(Relaxed),
bytes: BYTES.load(Relaxed),
}
}
pub(crate) fn counting() -> bool {
SEEN.load(Relaxed)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn notes_count_only_while_profiling() {
let before = alloc_counts();
crate::scope::enable();
note_allocation(100);
crate::scope::disable();
let after = alloc_counts();
assert!(after.count > before.count && after.bytes >= before.bytes + 100);
assert!(counting());
}
}