use std::alloc::{GlobalAlloc, Layout};
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use crate::{SaveToStatsFolder, Sensor};
#[derive(Default)]
pub struct AllocationSensor {
start_allocs: AllocationsStats,
end_allocs: AllocationsStats,
}
impl SaveToStatsFolder for AllocationSensor {
#[coverage(off)]
fn save_to_stats_folder(&self) -> Vec<(std::path::PathBuf, Vec<u8>)> {
vec![]
}
}
impl Sensor for AllocationSensor {
type Observations = (u64, u64);
#[coverage(off)]
fn start_recording(&mut self) {
self.start_allocs = get_allocation_stats();
}
#[coverage(off)]
fn stop_recording(&mut self) {
self.end_allocs = get_allocation_stats();
}
#[coverage(off)]
fn get_observations(&mut self) -> Self::Observations {
let blocks = self.end_allocs.total_blocks - self.start_allocs.total_blocks;
let bytes = self.end_allocs.total_bytes - self.start_allocs.total_bytes;
(blocks, bytes)
}
}
static mut ALLOC_STATS: InternalAllocationStats = InternalAllocationStats::new();
#[derive(Default)]
struct InternalAllocationStats {
total_blocks: AtomicU64,
total_bytes: AtomicU64,
curr_blocks: AtomicUsize,
curr_bytes: AtomicUsize,
}
impl InternalAllocationStats {
#[coverage(off)]
const fn new() -> Self {
Self {
total_blocks: AtomicU64::new(0),
total_bytes: AtomicU64::new(0),
curr_blocks: AtomicUsize::new(0),
curr_bytes: AtomicUsize::new(0),
}
}
}
impl InternalAllocationStats {
#[coverage(off)]
fn realloc(&mut self, size: usize, shrink: bool, delta: usize) {
self.total_blocks.fetch_add(1, Ordering::Relaxed);
self.total_bytes.fetch_add(size as u64, Ordering::Relaxed);
if shrink {
self.curr_bytes.fetch_sub(delta, Ordering::Relaxed);
} else {
self.curr_bytes.fetch_add(delta, Ordering::Relaxed);
}
}
#[coverage(off)]
fn alloc(&mut self, size: usize) {
self.total_blocks.fetch_add(1, Ordering::Relaxed);
self.total_bytes.fetch_add(size as u64, Ordering::Relaxed);
self.curr_blocks.fetch_add(1, Ordering::Relaxed);
self.curr_bytes.fetch_add(size, Ordering::Relaxed);
}
#[coverage(off)]
fn dealloc(&mut self, size: usize) {
self.curr_blocks.fetch_sub(1, Ordering::Relaxed);
self.curr_bytes.fetch_sub(size, Ordering::Relaxed);
}
}
#[derive(Debug)]
pub struct CountingAllocator<A>(pub A)
where
A: GlobalAlloc;
unsafe impl<A> GlobalAlloc for CountingAllocator<A>
where
A: GlobalAlloc,
{
#[coverage(off)]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let ptr = self.0.alloc(layout);
if ptr.is_null() {
return ptr;
}
let size = layout.size();
ALLOC_STATS.alloc(size);
ptr
}
#[coverage(off)]
unsafe fn realloc(&self, old_ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
let new_ptr = self.0.realloc(old_ptr, layout, new_size);
if new_ptr.is_null() {
return new_ptr;
}
let old_size = layout.size();
let (shrink, delta) = if new_size < old_size {
(true, old_size - new_size)
} else {
(false, new_size - old_size)
};
ALLOC_STATS.realloc(new_size, shrink, delta);
new_ptr
}
#[coverage(off)]
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
self.0.dealloc(ptr, layout);
let size = layout.size();
ALLOC_STATS.dealloc(size);
}
}
#[derive(Default)]
struct AllocationsStats {
total_blocks: u64,
total_bytes: u64,
}
#[coverage(off)]
fn get_allocation_stats() -> AllocationsStats {
unsafe {
AllocationsStats {
total_blocks: ALLOC_STATS.total_blocks.load(Ordering::SeqCst),
total_bytes: ALLOC_STATS.total_bytes.load(Ordering::SeqCst),
}
}
}