1#![no_std]
2#![doc = include_str!("../README.md")]
3
4#[cfg(feature = "std")]
5extern crate std;
6
7use core::alloc::GlobalAlloc;
8
9use derive_more::{Add, AddAssign, Sub, SubAssign, Neg};
10
11#[cfg(test)]
12mod test;
13
14#[cfg(feature = "thread")]
15std::thread_local! {
16 static THREAD_METRICS: core::cell::Cell<Metrics> = core::cell::Cell::new(Metrics { allocated_bytes: 0, allocations: 0 });
17}
18#[cfg(feature = "global")]
19static GLOBAL_METRICS: spin::Mutex<Metrics> = spin::Mutex::new(Metrics { allocated_bytes: 0, allocations: 0 });
20
21fn add_assign_metrics(_metrics: Metrics) {
22 #[cfg(feature = "thread")]
23 { THREAD_METRICS.set(THREAD_METRICS.get() + _metrics) }
24 #[cfg(feature = "global")]
25 { *GLOBAL_METRICS.lock() += _metrics }
26}
27
28#[cfg_attr(feature = "thread", doc = r#"
32```rust
33let before = alloc_metrics::thread_metrics();
34// ... do some work ...
35let after = alloc_metrics::thread_metrics();
36let delta = after - before;
37```
38"#)]
39#[derive(Clone, Copy, Debug, PartialEq, Eq, Add, AddAssign, Sub, SubAssign, Neg)]
40pub struct Metrics {
41 pub allocated_bytes: isize,
43 pub allocations: isize,
45}
46
47pub struct MetricAlloc<A: GlobalAlloc> {
52 wrapped: A,
53}
54impl<A: GlobalAlloc> MetricAlloc<A> {
55 pub const fn new(wrapped: A) -> Self {
57 Self { wrapped }
58 }
59}
60unsafe impl<A: GlobalAlloc> GlobalAlloc for MetricAlloc<A> {
61 unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
62 let res = self.wrapped.alloc(layout);
63 if !res.is_null() {
64 add_assign_metrics(Metrics {
65 allocated_bytes: layout.size() as isize,
66 allocations: 1,
67 });
68 }
69 res
70 }
71 unsafe fn alloc_zeroed(&self, layout: core::alloc::Layout) -> *mut u8 {
72 let res = self.wrapped.alloc_zeroed(layout);
73 if !res.is_null() {
74 add_assign_metrics(Metrics {
75 allocated_bytes: layout.size() as isize,
76 allocations: 1,
77 });
78 }
79 res
80 }
81 unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) {
82 self.wrapped.dealloc(ptr, layout);
83 if !ptr.is_null() {
84 add_assign_metrics(Metrics {
85 allocated_bytes: -(layout.size() as isize),
86 allocations: -1,
87 });
88 }
89 }
90 unsafe fn realloc(&self, ptr: *mut u8, layout: core::alloc::Layout, new_size: usize) -> *mut u8 {
91 let res = self.wrapped.realloc(ptr, layout, new_size);
92 if !res.is_null() {
93 add_assign_metrics(Metrics {
94 allocated_bytes: new_size as isize - layout.size() as isize,
95 allocations: 0,
96 })
97 }
98 res
99 }
100}
101
102#[cfg(feature = "thread")]
106pub fn thread_metrics() -> Metrics {
107 THREAD_METRICS.get()
108}
109
110#[cfg(feature = "global")]
114pub fn global_metrics() -> Metrics {
115 *GLOBAL_METRICS.lock()
116}