1: 1024 bytes

October 25, 2025 · View on GitHub

= mem-trace: A memory tracer and profiler

mem-trace can be used for 2 purposes:

  • trace memory, e.g. find the stacktrace leading to the allocation of a specific pointer.
  • profile memory, e.g. find the stacktraces where most bytes of memory are allocated.

== Usage

=== Trace memory

Build with the tracer feature enabled:


cargo build --features tracer

Tracing memory allows you to get the stacktrace leading to the allocation of a specific pointer.

Start your program with gdb and use LD_PRELOAD to load mem-trace:


gdb --args env LD_PRELOAD=path/to/libmem_trace.so ./program

Inspect your program in gdb to get the address of the pointer you want to trace. Then, call mem_trace::print_trace in the debugger to get the stacktrace leading to the allocation of an address:


call mem_trace::print_trace(0x7fffebac2010)

You should get an output that looks like:


PID: 227045 Len: 8 0: malloc at /home/user/projects/mem-trace/src/lib.rs:37:21 1: alloc::raw_vec::RawVecInner::try_allocate_in 2: alloc::raw_vec::RawVecInner::with_capacity_in at /home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec/mod.rs:419:15 3: alloc::raw_vec::RawVec<T,A>::with_capacity_in at /home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec/mod.rs:187:20 alloc::vec::Vec<T,A>::with_capacity_in at /home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/vec/mod.rs:929:20 ::from_elem at /home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/vec/spec_from_elem.rs:26:21 4: alloc::vec::from_elem at /home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/vec/mod.rs:3475:5 5: memory_usage::func at src/main.rs:4:15 6: memory_usage::call_func at src/main.rs:15:9 7: memory_usage::main at src/main.rs:20:5 8: core::ops::function::FnOnce::call_once at /home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5 9: std::sys::backtrace::__rust_begin_short_backtrace at /home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/backtrace.rs:158:18 10: std::rt::lang_start::{{closure}} at /home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:206:18 11: core::ops::function::impls::<impl core::ops::function::FnOnce for &F>::call_once at /rustc/9725c4baacef19345e13f91b27e66e10ef5592ae/library/core/src/ops/function.rs:287:21 std::panicking::catch_unwind::do_call at /rustc/9725c4baacef19345e13f91b27e66e10ef5592ae/library/std/src/panicking.rs:590:40 std::panicking::catch_unwind at /rustc/9725c4baacef19345e13f91b27e66e10ef5592ae/library/std/src/panicking.rs:553:19 std::panic::catch_unwind at /rustc/9725c4baacef19345e13f91b27e66e10ef5592ae/library/std/src/panic.rs:359:14 std::rt::lang_start_internal::{{closure}} at /rustc/9725c4baacef19345e13f91b27e66e10ef5592ae/library/std/src/rt.rs:175:24 std::panicking::catch_unwind::do_call at /rustc/9725c4baacef19345e13f91b27e66e10ef5592ae/library/std/src/panicking.rs:590:40 std::panicking::catch_unwind at /rustc/9725c4baacef19345e13f91b27e66e10ef5592ae/library/std/src/panicking.rs:553:19 std::panic::catch_unwind at /rustc/9725c4baacef19345e13f91b27e66e10ef5592ae/library/std/src/panic.rs:359:14 std::rt::lang_start_internal at /rustc/9725c4baacef19345e13f91b27e66e10ef5592ae/library/std/src/rt.rs:171:5 12: std::rt::lang_start at /home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:205:5 13: main 14: 15: __libc_start_main 16: _start

This shows that the allocation happened in the function memory_usage::func in file src/main.rs at line 4, column 15:

=== Profile memory

Build with the profiler feature enabled:


cargo build --features profiler

Run your program using LD_PRELOAD to load mem-trace:


LD_PRELOAD=path/to/libmem_trace.so ./program

This will print at regular interval the 10 top locations for memory usage, in reverse order (that is, the location where most memory is allocated will be printed last):


[…]

1: 1024 bytes

[…] 5: memory_usage::func at src/main.rs:5:5 6: memory_usage::call_func at src/main.rs:15:9 7: memory_usage::main at src/main.rs:20:5 […]

0: 1600000000 bytes

[…] 5: memory_usage::func at src/main.rs:4:15 6: memory_usage::call_func at src/main.rs:15:9 7: memory_usage::main at src/main.rs:20:5 […]