#![no_std]
extern crate alloc;
#[cfg(any(feature = "heap", feature = "cpu"))]
mod profiling;
#[cfg(feature = "cpu")]
pub use profiling::{start_cpu_profiling, stop_cpu_profiling};
#[cfg(not(feature = "cpu"))]
#[inline]
pub fn start_cpu_profiling(_freq_hz: u32) {}
#[cfg(not(feature = "cpu"))]
#[inline]
pub fn stop_cpu_profiling() {}
pub struct ProfilingAllocator<const CPU_FREQ: u32 = 99>;
impl<const CPU_FREQ: u32> ProfilingAllocator<CPU_FREQ> {
pub const fn new() -> Self {
Self
}
}
impl<const CPU_FREQ: u32> Default for ProfilingAllocator<CPU_FREQ> {
fn default() -> Self {
Self::new()
}
}
pub type HeapProfiler = ProfilingAllocator<99>;
#[cfg(not(feature = "heap"))]
mod disabled {
use super::ProfilingAllocator;
use core::alloc::{GlobalAlloc, Layout};
unsafe impl<const CPU_FREQ: u32> GlobalAlloc for ProfilingAllocator<CPU_FREQ> {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
unsafe { libc::malloc(layout.size()) as *mut u8 }
}
#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
unsafe { libc::free(ptr as *mut libc::c_void) }
}
#[inline]
unsafe fn realloc(&self, ptr: *mut u8, _layout: Layout, new_size: usize) -> *mut u8 {
unsafe { libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 }
}
#[inline]
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
unsafe { libc::calloc(1, layout.size()) as *mut u8 }
}
}
}
#[cfg(feature = "heap")]
mod enabled {
use super::ProfilingAllocator;
#[cfg(feature = "cpu")]
use super::profiling::start_cpu_profiling;
use super::profiling::{record_alloc, record_dealloc};
use core::alloc::{GlobalAlloc, Layout};
use core::sync::atomic::{AtomicBool, Ordering};
static CPU_INITIALIZED: AtomicBool = AtomicBool::new(false);
#[inline]
fn maybe_init_cpu<const FREQ: u32>() {
#[cfg(feature = "cpu")]
{
if FREQ > 0 && !CPU_INITIALIZED.swap(true, Ordering::SeqCst) {
start_cpu_profiling(FREQ);
}
}
}
const MIN_ALIGN: usize = core::mem::size_of::<usize>() * 2;
#[inline(never)]
unsafe fn aligned_malloc(size: usize, align: usize) -> *mut u8 {
if align <= MIN_ALIGN {
unsafe { libc::malloc(size) as *mut u8 }
} else {
let mut ptr: *mut libc::c_void = core::ptr::null_mut();
let ret = unsafe { libc::posix_memalign(&mut ptr, align, size) };
if ret == 0 {
ptr as *mut u8
} else {
core::ptr::null_mut()
}
}
}
unsafe impl<const CPU_FREQ: u32> GlobalAlloc for ProfilingAllocator<CPU_FREQ> {
#[inline(never)]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
maybe_init_cpu::<CPU_FREQ>();
let ptr = unsafe { aligned_malloc(layout.size(), layout.align()) };
if !ptr.is_null() {
record_alloc(ptr, layout.size());
}
ptr
}
#[inline(never)]
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
record_dealloc(ptr, layout.size());
unsafe { libc::free(ptr as *mut libc::c_void) }
}
#[inline(never)]
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
if layout.align() > MIN_ALIGN {
let new_ptr = unsafe { aligned_malloc(new_size, layout.align()) };
if !new_ptr.is_null() {
let copy_size = if new_size < layout.size() {
new_size
} else {
layout.size()
};
unsafe { core::ptr::copy_nonoverlapping(ptr, new_ptr, copy_size) };
record_dealloc(ptr, layout.size());
unsafe { libc::free(ptr as *mut libc::c_void) };
record_alloc(new_ptr, new_size);
}
new_ptr
} else {
record_dealloc(ptr, layout.size());
let new_ptr =
unsafe { libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 };
if !new_ptr.is_null() {
record_alloc(new_ptr, new_size);
}
new_ptr
}
}
#[inline(never)]
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
maybe_init_cpu::<CPU_FREQ>();
if layout.align() <= MIN_ALIGN {
let ptr = unsafe { libc::calloc(1, layout.size()) as *mut u8 };
if !ptr.is_null() {
record_alloc(ptr, layout.size());
}
ptr
} else {
let ptr = unsafe { aligned_malloc(layout.size(), layout.align()) };
if !ptr.is_null() {
unsafe { core::ptr::write_bytes(ptr, 0, layout.size()) };
record_alloc(ptr, layout.size());
}
ptr
}
}
}
}
#[macro_export]
#[cfg(feature = "heap")]
macro_rules! profiler {
() => {
$crate::profiler!(cpu = 99);
};
(cpu = $freq:expr) => {
#[global_allocator]
static __RSPROF_ALLOC: $crate::ProfilingAllocator<$freq> =
$crate::ProfilingAllocator::<$freq>::new();
};
}
#[macro_export]
#[cfg(not(feature = "heap"))]
macro_rules! profiler {
() => {};
(cpu = $freq:expr) => {};
}