#![doc = include_str!("../README.md")]
use metricus::{Counter, CounterOps, Id, PreAllocatedMetric};
use std::alloc::{GlobalAlloc, Layout};
use std::cell::Cell;
use std::sync::LazyLock;
const ALLOC_COUNTER_ID: Id = Id::MAX - 1004;
const ALLOC_BYTES_COUNTER_ID: Id = Id::MAX - 1003;
const DEALLOC_COUNTER_ID: Id = Id::MAX - 1002;
const DEALLOC_BYTES_COUNTER_ID: Id = Id::MAX - 1001;
const fn get_aligned_size(layout: Layout) -> usize {
let alignment_mask: usize = layout.align() - 1;
(layout.size() + alignment_mask) & !alignment_mask
}
pub struct CountingAllocator;
#[allow(static_mut_refs)]
unsafe impl GlobalAlloc for CountingAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
if INSTRUMENTATION_ENABLED.get() {
COUNTERS.alloc_count.increment();
COUNTERS.alloc_bytes.increment_by(get_aligned_size(layout) as u64);
}
#[cfg(all(feature = "jemalloc", not(feature = "mimalloc")))]
{
return unsafe { jemallocator::Jemalloc.alloc(layout) };
}
#[cfg(all(feature = "mimalloc", not(feature = "jemalloc")))]
{
return unsafe { mimalloc::MiMalloc.alloc(layout) };
}
unsafe { std::alloc::System.alloc(layout) }
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
if INSTRUMENTATION_ENABLED.get() {
COUNTERS.dealloc_count.increment();
COUNTERS.dealloc_bytes.increment_by(get_aligned_size(layout) as u64);
}
#[cfg(all(feature = "jemalloc", not(feature = "mimalloc")))]
{
unsafe {
jemallocator::Jemalloc.dealloc(ptr, layout);
}
return;
}
#[cfg(all(feature = "mimalloc", not(feature = "jemalloc")))]
{
unsafe {
mimalloc::MiMalloc.dealloc(ptr, layout);
}
return;
}
unsafe { std::alloc::System.dealloc(ptr, layout) }
}
}
impl CountingAllocator {
pub fn metrics() -> Vec<PreAllocatedMetric> {
vec![
PreAllocatedMetric::counter("global_allocator", ALLOC_COUNTER_ID, &[("fn_name", "alloc")]),
PreAllocatedMetric::counter("global_allocator", ALLOC_BYTES_COUNTER_ID, &[("fn_name", "alloc_bytes")]),
PreAllocatedMetric::counter("global_allocator", DEALLOC_COUNTER_ID, &[("fn_name", "dealloc")]),
PreAllocatedMetric::counter("global_allocator", DEALLOC_BYTES_COUNTER_ID, &[("fn_name", "dealloc_bytes")]),
]
}
}
thread_local! {
static INSTRUMENTATION_ENABLED: Cell<bool> = const { Cell::new(false) };
}
pub fn enable_allocator_instrumentation() {
INSTRUMENTATION_ENABLED.set(true);
}
static COUNTERS: LazyLock<Counters> = LazyLock::new(|| Counters {
alloc_count: Counter::new_with_id(ALLOC_COUNTER_ID),
alloc_bytes: Counter::new_with_id(ALLOC_BYTES_COUNTER_ID),
dealloc_count: Counter::new_with_id(DEALLOC_COUNTER_ID),
dealloc_bytes: Counter::new_with_id(DEALLOC_BYTES_COUNTER_ID),
});
struct Counters {
alloc_count: Counter,
alloc_bytes: Counter,
dealloc_count: Counter,
dealloc_bytes: Counter,
}