pub struct ModAlloc { /* private fields */ }Expand description
Global allocator wrapper that tracks allocations.
Install as #[global_allocator] to enable tracking. The wrapper
forwards every allocation, deallocation, reallocation, and
zero-initialised allocation to std::alloc::System and records
the event in four lock-free AtomicU64 counters.
§Example
use mod_alloc::ModAlloc;
#[global_allocator]
static GLOBAL: ModAlloc = ModAlloc::new();
fn main() {
let v: Vec<u8> = vec![0; 1024];
let stats = GLOBAL.snapshot();
assert!(stats.alloc_count >= 1);
drop(v);
}Implementations§
Source§impl ModAlloc
impl ModAlloc
Sourcepub const fn new() -> Self
pub const fn new() -> Self
Construct a new ModAlloc allocator wrapper.
All counters start at zero. This function is const, which
allows construction in a static for use as
#[global_allocator].
§Example
use mod_alloc::ModAlloc;
static GLOBAL: ModAlloc = ModAlloc::new();
let stats = GLOBAL.snapshot();
assert_eq!(stats.alloc_count, 0);Examples found in repository?
More examples
Sourcepub fn snapshot(&self) -> AllocStats
pub fn snapshot(&self) -> AllocStats
Snapshot the current counter values.
Each counter is read independently with Relaxed ordering;
the resulting AllocStats is a coherent best-effort view
but does not represent a single atomic moment in time. For
scoped measurement, prefer Profiler.
§Example
use mod_alloc::ModAlloc;
let alloc = ModAlloc::new();
let stats = alloc.snapshot();
assert_eq!(stats.alloc_count, 0);Examples found in repository?
39fn main() -> std::io::Result<()> {
40 for _ in 0..500 {
41 alloc_small();
42 }
43 for _ in 0..100 {
44 alloc_medium();
45 }
46 for _ in 0..10 {
47 alloc_large();
48 }
49
50 let snap = GLOBAL.snapshot();
51 println!(
52 "captured {} allocations, {} total bytes, peak {} bytes",
53 snap.alloc_count, snap.total_bytes, snap.peak_bytes
54 );
55
56 let path = std::path::Path::new("dhat-heap.json");
57 GLOBAL.write_dhat_json(path)?;
58
59 let abs = std::fs::canonicalize(path).unwrap_or_else(|_| path.to_path_buf());
60 println!("wrote {}", abs.display());
61 println!("open it in dh_view.html to inspect.");
62 Ok(())
63}More examples
26fn main() {
27 for _ in 0..WARMUP {
28 let v: Vec<u8> = Vec::with_capacity(SIZE);
29 std::hint::black_box(&v);
30 }
31
32 GLOBAL.reset();
33 let start = Instant::now();
34 for _ in 0..N {
35 let v: Vec<u8> = Vec::with_capacity(SIZE);
36 std::hint::black_box(&v);
37 }
38 let elapsed = start.elapsed();
39
40 let snap = GLOBAL.snapshot();
41 let per_cycle_ns = elapsed.as_nanos() as f64 / N as f64;
42
43 println!("bench_overhead:");
44 println!(" iterations: {N}");
45 println!(" allocation size: {SIZE} bytes");
46 println!(" elapsed: {elapsed:?}");
47 println!(" per alloc+dealloc: {per_cycle_ns:.1} ns");
48 println!();
49 println!("counter snapshot after run:");
50 println!(" alloc_count: {}", snap.alloc_count);
51 println!(" total_bytes: {}", snap.total_bytes);
52 println!(" current_bytes: {}", snap.current_bytes);
53 println!(" peak_bytes: {}", snap.peak_bytes);
54}10fn main() {
11 let p = Profiler::start();
12
13 let v: Vec<u64> = (0..1_000).collect();
14 let sum: u64 = v.iter().sum();
15 drop(v);
16
17 let mut owned: Vec<String> = Vec::with_capacity(100);
18 for i in 0..100 {
19 owned.push(format!("item-{i}"));
20 }
21 drop(owned);
22
23 let delta = p.stop();
24 println!("Profiler delta (alloc/total/current = delta; peak = absolute):");
25 println!(" alloc_count: {}", delta.alloc_count);
26 println!(" total_bytes: {}", delta.total_bytes);
27 println!(" current_bytes: {}", delta.current_bytes);
28 println!(" peak_bytes: {}", delta.peak_bytes);
29
30 let snap = GLOBAL.snapshot();
31 println!();
32 println!("Process-wide snapshot:");
33 println!(" alloc_count: {}", snap.alloc_count);
34 println!(" total_bytes: {}", snap.total_bytes);
35 println!(" current_bytes: {}", snap.current_bytes);
36 println!(" peak_bytes: {}", snap.peak_bytes);
37
38 println!();
39 println!("(workload checksum: {sum})");
40}37fn main() {
38 for _ in 0..1_000 {
39 alloc_small();
40 }
41 for _ in 0..100 {
42 alloc_medium();
43 }
44 for _ in 0..10 {
45 alloc_large();
46 }
47
48 let snap = GLOBAL.snapshot();
49 println!("Process-wide snapshot:");
50 println!(" alloc_count: {}", snap.alloc_count);
51 println!(" total_bytes: {}", snap.total_bytes);
52 println!(" current_bytes: {}", snap.current_bytes);
53 println!(" peak_bytes: {}", snap.peak_bytes);
54 println!();
55
56 let mut sites = GLOBAL.call_sites();
57 sites.sort_by_key(|s| std::cmp::Reverse(s.total_bytes));
58
59 println!("Top 10 call sites by total bytes:");
60 println!(
61 "{:>10} {:>14} {:>4} {:>18}",
62 "count", "total_bytes", "frm", "top frame"
63 );
64 for (rank, site) in sites.iter().take(10).enumerate() {
65 println!(
66 "{rank:>2}: {count:>6} {bytes:>14} {frm:>4} {top:#018x}",
67 rank = rank,
68 count = site.count,
69 bytes = site.total_bytes,
70 frm = site.frame_count,
71 top = site.frames[0],
72 );
73 }
74}38fn main() {
39 for _ in 0..500 {
40 alloc_small();
41 }
42 for _ in 0..100 {
43 alloc_medium();
44 }
45 for _ in 0..10 {
46 alloc_large();
47 }
48
49 let mut report = GLOBAL.symbolicated_report();
50 report.sort_by_key(|s| std::cmp::Reverse(s.total_bytes));
51
52 let snap = GLOBAL.snapshot();
53 println!("Process-wide:");
54 println!(
55 " {} allocations, {} total bytes, peak {} bytes",
56 snap.alloc_count, snap.total_bytes, snap.peak_bytes
57 );
58 println!();
59
60 println!("Top call sites by total bytes:");
61 println!(
62 "{:>2} {:>10} {:>14} top frame",
63 "#", "count", "total_bytes"
64 );
65 for (rank, site) in report.iter().take(10).enumerate() {
66 let top = &site.frames[0];
67 let name = top.function.as_deref().unwrap_or("<unresolved>");
68 let loc = match (top.file.as_ref(), top.line) {
69 (Some(f), Some(l)) => format!(" {}:{}", f.display(), l),
70 _ => String::new(),
71 };
72 println!(
73 "{rank:>2} {count:>10} {bytes:>14} {name}{loc}",
74 rank = rank,
75 count = site.count,
76 bytes = site.total_bytes,
77 name = name,
78 loc = loc,
79 );
80 // Show inlined expansions if any.
81 for inlined in site.frames.iter().skip(1).take_while(|f| f.inlined) {
82 let n = inlined.function.as_deref().unwrap_or("<unresolved>");
83 println!(" inlined: {n}");
84 }
85 }
86}Sourcepub fn reset(&self)
pub fn reset(&self)
Reset all counters to zero.
Intended for use at the start of a profile run, before any
outstanding allocations exist. Calling reset while
allocations are live can cause current_bytes to wrap on
subsequent deallocations; the other counters are unaffected.
§Example
use mod_alloc::ModAlloc;
let alloc = ModAlloc::new();
alloc.reset();
let stats = alloc.snapshot();
assert_eq!(stats.alloc_count, 0);Examples found in repository?
26fn main() {
27 for _ in 0..WARMUP {
28 let v: Vec<u8> = Vec::with_capacity(SIZE);
29 std::hint::black_box(&v);
30 }
31
32 GLOBAL.reset();
33 let start = Instant::now();
34 for _ in 0..N {
35 let v: Vec<u8> = Vec::with_capacity(SIZE);
36 std::hint::black_box(&v);
37 }
38 let elapsed = start.elapsed();
39
40 let snap = GLOBAL.snapshot();
41 let per_cycle_ns = elapsed.as_nanos() as f64 / N as f64;
42
43 println!("bench_overhead:");
44 println!(" iterations: {N}");
45 println!(" allocation size: {SIZE} bytes");
46 println!(" elapsed: {elapsed:?}");
47 println!(" per alloc+dealloc: {per_cycle_ns:.1} ns");
48 println!();
49 println!("counter snapshot after run:");
50 println!(" alloc_count: {}", snap.alloc_count);
51 println!(" total_bytes: {}", snap.total_bytes);
52 println!(" current_bytes: {}", snap.current_bytes);
53 println!(" peak_bytes: {}", snap.peak_bytes);
54}Sourcepub fn call_sites(&self) -> Vec<CallSiteStats>
Available on crate feature backtraces only.
pub fn call_sites(&self) -> Vec<CallSiteStats>
backtraces only.Drain the per-call-site aggregation table into a Vec.
Available only with the backtraces cargo feature. The
returned vector contains one CallSiteStats per unique
call site observed since the table was first written. Each
row carries up to 8 raw return addresses (top of stack
first), the number of allocations attributed to that site,
and the total bytes.
Symbolication (resolving addresses to function names)
lands in v0.9.2. This method exposes raw addresses only.
§Example
use mod_alloc::ModAlloc;
#[global_allocator]
static GLOBAL: ModAlloc = ModAlloc::new();
let _v: Vec<u8> = vec![0; 1024];
for site in GLOBAL.call_sites() {
println!("{} allocs, {} bytes at {:#x}",
site.count, site.total_bytes, site.frames[0]);
}Examples found in repository?
37fn main() {
38 for _ in 0..1_000 {
39 alloc_small();
40 }
41 for _ in 0..100 {
42 alloc_medium();
43 }
44 for _ in 0..10 {
45 alloc_large();
46 }
47
48 let snap = GLOBAL.snapshot();
49 println!("Process-wide snapshot:");
50 println!(" alloc_count: {}", snap.alloc_count);
51 println!(" total_bytes: {}", snap.total_bytes);
52 println!(" current_bytes: {}", snap.current_bytes);
53 println!(" peak_bytes: {}", snap.peak_bytes);
54 println!();
55
56 let mut sites = GLOBAL.call_sites();
57 sites.sort_by_key(|s| std::cmp::Reverse(s.total_bytes));
58
59 println!("Top 10 call sites by total bytes:");
60 println!(
61 "{:>10} {:>14} {:>4} {:>18}",
62 "count", "total_bytes", "frm", "top frame"
63 );
64 for (rank, site) in sites.iter().take(10).enumerate() {
65 println!(
66 "{rank:>2}: {count:>6} {bytes:>14} {frm:>4} {top:#018x}",
67 rank = rank,
68 count = site.count,
69 bytes = site.total_bytes,
70 frm = site.frame_count,
71 top = site.frames[0],
72 );
73 }
74}Sourcepub fn symbolicated_report(&self) -> Vec<SymbolicatedCallSite>
Available on crate feature symbolicate only.
pub fn symbolicated_report(&self) -> Vec<SymbolicatedCallSite>
symbolicate only.Drain the per-call-site table and symbolicate each frame against the running binary’s own debug info.
Available only with the symbolicate cargo feature, which
also implies backtraces. Returns one
SymbolicatedCallSite per unique call site, each
carrying resolved function names plus (where available)
source file and line.
Allocates. Safe to call from non-allocator contexts only (ordinary user code outside the global-allocator hook).
Results are cached per-address across calls.
§Example
use mod_alloc::ModAlloc;
#[global_allocator]
static GLOBAL: ModAlloc = ModAlloc::new();
let _v: Vec<u8> = vec![0; 1024];
for site in GLOBAL.symbolicated_report() {
let top = &site.frames[0];
println!("{} allocs / {} bytes at {}",
site.count,
site.total_bytes,
top.function.as_deref().unwrap_or("<unresolved>"));
}Examples found in repository?
38fn main() {
39 for _ in 0..500 {
40 alloc_small();
41 }
42 for _ in 0..100 {
43 alloc_medium();
44 }
45 for _ in 0..10 {
46 alloc_large();
47 }
48
49 let mut report = GLOBAL.symbolicated_report();
50 report.sort_by_key(|s| std::cmp::Reverse(s.total_bytes));
51
52 let snap = GLOBAL.snapshot();
53 println!("Process-wide:");
54 println!(
55 " {} allocations, {} total bytes, peak {} bytes",
56 snap.alloc_count, snap.total_bytes, snap.peak_bytes
57 );
58 println!();
59
60 println!("Top call sites by total bytes:");
61 println!(
62 "{:>2} {:>10} {:>14} top frame",
63 "#", "count", "total_bytes"
64 );
65 for (rank, site) in report.iter().take(10).enumerate() {
66 let top = &site.frames[0];
67 let name = top.function.as_deref().unwrap_or("<unresolved>");
68 let loc = match (top.file.as_ref(), top.line) {
69 (Some(f), Some(l)) => format!(" {}:{}", f.display(), l),
70 _ => String::new(),
71 };
72 println!(
73 "{rank:>2} {count:>10} {bytes:>14} {name}{loc}",
74 rank = rank,
75 count = site.count,
76 bytes = site.total_bytes,
77 name = name,
78 loc = loc,
79 );
80 // Show inlined expansions if any.
81 for inlined in site.frames.iter().skip(1).take_while(|f| f.inlined) {
82 let n = inlined.function.as_deref().unwrap_or("<unresolved>");
83 println!(" inlined: {n}");
84 }
85 }
86}Sourcepub fn dhat_json_string(&self) -> String
Available on crate feature dhat-compat only.
pub fn dhat_json_string(&self) -> String
dhat-compat only.Render the per-call-site report as a DHAT-compatible JSON string.
Available only with the dhat-compat cargo feature (which
implies backtraces). When the symbolicate feature is
also active, frame strings carry function names and
(where available) source file and line; otherwise they
carry raw hex addresses.
Allocates. Safe to call from non-allocator contexts only (ordinary user code outside the global-allocator hook).
The output schema (dhatFileVersion: 2, mode: "rust-heap")
matches the format consumed by the upstream
dh_view.html viewer shipped with Valgrind.
§Example
use mod_alloc::ModAlloc;
#[global_allocator]
static GLOBAL: ModAlloc = ModAlloc::new();
let _v: Vec<u8> = vec![0; 1024];
let json = GLOBAL.dhat_json_string();
assert!(json.contains("\"dhatFileVersion\":2"));Sourcepub fn write_dhat_json<P: AsRef<Path>>(&self, path: P) -> Result<()>
Available on crate feature dhat-compat only.
pub fn write_dhat_json<P: AsRef<Path>>(&self, path: P) -> Result<()>
dhat-compat only.Render the per-call-site report and write it to path as
DHAT-compatible JSON.
Available only with the dhat-compat cargo feature.
Mirrors dhat-rs’s convention of writing
dhat-heap.json to the current directory; pass that path
to drop a file the upstream viewer will load directly.
Allocates. Safe to call from non-allocator contexts only.
§Example
use mod_alloc::ModAlloc;
#[global_allocator]
static GLOBAL: ModAlloc = ModAlloc::new();
let _v: Vec<u8> = vec![0; 1024];
GLOBAL.write_dhat_json("dhat-heap.json")?;Examples found in repository?
39fn main() -> std::io::Result<()> {
40 for _ in 0..500 {
41 alloc_small();
42 }
43 for _ in 0..100 {
44 alloc_medium();
45 }
46 for _ in 0..10 {
47 alloc_large();
48 }
49
50 let snap = GLOBAL.snapshot();
51 println!(
52 "captured {} allocations, {} total bytes, peak {} bytes",
53 snap.alloc_count, snap.total_bytes, snap.peak_bytes
54 );
55
56 let path = std::path::Path::new("dhat-heap.json");
57 GLOBAL.write_dhat_json(path)?;
58
59 let abs = std::fs::canonicalize(path).unwrap_or_else(|_| path.to_path_buf());
60 println!("wrote {}", abs.display());
61 println!("open it in dh_view.html to inspect.");
62 Ok(())
63}Trait Implementations§
Source§impl GlobalAlloc for ModAlloc
impl GlobalAlloc for ModAlloc
Source§unsafe fn alloc(&self, layout: Layout) -> *mut u8
unsafe fn alloc(&self, layout: Layout) -> *mut u8
layout. Read moreSource§unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8
alloc, but also ensures that the contents
are set to zero before being returned. Read more