pub struct MemoryTracker { /* private fields */ }Expand description
Core memory tracking functionality.
The MemoryTracker maintains records of all memory allocations and deallocations, provides statistics, and supports exporting data in various formats.
§Performance Characteristics
- DashMap: Lock-free concurrent access for allocations
- Atomic counters: Lock-free statistics updates
- Thread-local: Zero-contention for per-thread tracking
Implementations§
Source§impl MemoryTracker
impl MemoryTracker
Sourcepub fn track_allocation(&self, ptr: usize, size: usize) -> TrackingResult<()>
pub fn track_allocation(&self, ptr: usize, size: usize) -> TrackingResult<()>
Track a memory allocation or a typed observation of one.
§Re-observation vs. address reuse
The same address can be reported more than once: a track! on a buffer the
allocator hook already recorded, two track! calls on the same variable, or a
different block that landed on a reused address whose free was lost.
- Identical size → duplicate observation: metadata refresh only, no accounting.
- Different size → new binding. An observation cannot prove that the old
block is still the same block, so the previous row leaves the live set and the
new one gets its own identity. Reusing the row (“in-place realloc”) would merge
two allocations, and a later release by raw address would then delete the wrong
one. Only
MemoryTracker::track_reallocationmay claim identity continuity, because only the allocator can observe it.
This keeps total_allocated - total_deallocated == Σ live sizes (plan A06) while
never inventing a generation merge.
§Arguments
ptr- Memory pointer addresssize- Allocation size in bytes
Sourcepub fn live_binding_count_at(&self, ptr: usize) -> usize
pub fn live_binding_count_at(&self, ptr: usize) -> usize
Number of live bindings currently recorded at ptr.
Sourcepub fn track_deallocation(&self, ptr: usize) -> TrackingResult<bool>
pub fn track_deallocation(&self, ptr: usize) -> TrackingResult<bool>
Track a memory deallocation by address.
This is the degraded path: an address alone cannot name a binding, so when
the address holds more than one live binding the oldest one is closed and the
guess is counted in Self::degraded_releases. Callers that know which block
they are releasing must use Self::track_deallocation_of (plan PR-02).
§Returns
Ok(true)if a binding was found and closedOk(false)if the pointer was not tracked (possible double-free or untracked allocation)
Sourcepub fn close_and_report(
&self,
ptr: usize,
serial: Option<u64>,
) -> TrackingResult<Option<ClosedBinding>>
pub fn close_and_report( &self, ptr: usize, serial: Option<u64>, ) -> TrackingResult<Option<ClosedBinding>>
Close a binding and report exactly which one was closed.
The facade needs the identity that was actually released: with several live
bindings at one address the newest one is not the one a degraded release closes,
so publishing binding_serial(ptr) there would describe the wrong block.
Sourcepub fn binding_at(&self, ptr: usize) -> Option<ClosedBinding>
pub fn binding_at(&self, ptr: usize) -> Option<ClosedBinding>
The live binding recorded at exactly ptr (newest binding at that address).
O(1): one map lookup. Use this for deduplication and charging decisions instead of the range scan.
Sourcepub fn binding_containing(&self, addr: usize) -> Option<ClosedBinding>
pub fn binding_containing(&self, addr: usize) -> Option<ClosedBinding>
The live binding whose block contains addr, if the candidate is unique.
A smart pointer’s pointee sits inside its allocation (an Arc puts the value
after the two reference counts), so matching by address or size cannot find the
block. The containing range can.
Sourcepub fn track_deallocation_of(
&self,
ptr: usize,
serial: u64,
) -> TrackingResult<bool>
pub fn track_deallocation_of( &self, ptr: usize, serial: u64, ) -> TrackingResult<bool>
Track a memory deallocation of one specific binding.
serial is the identity returned by Self::binding_serial; only that binding
may be closed, so a release that arrives after the address was reused cannot
delete the new block.
Sourcepub fn track_reallocation(
&self,
old_ptr: usize,
new_ptr: usize,
_old_size: usize,
new_size: usize,
) -> TrackingResult<()>
pub fn track_reallocation( &self, old_ptr: usize, new_ptr: usize, _old_size: usize, new_size: usize, ) -> TrackingResult<()>
Track a reallocation of a tracked block.
The block keeps its identity (and its binding serial): the new size replaces the
old one and the allocation counter does not change, whether the allocator kept the
address (old_ptr == new_ptr) or moved the block. Metadata recorded by track! is
carried over to the new address.
§Arguments
old_ptr- Address before the reallocationnew_ptr- Address returned by the allocatorold_size- Size the caller asked to reallocate. Kept for API symmetry; the cache trusts the size it recorded, and an unobserved block books nothing.new_size- Size of the resulting block
Sourcepub fn get_allocation_size(&self, ptr: usize) -> Option<usize>
pub fn get_allocation_size(&self, ptr: usize) -> Option<usize>
Sourcepub fn associate_var(
&self,
ptr: usize,
var_name: String,
type_name: String,
source_file: Option<&str>,
source_line: Option<u32>,
) -> TrackingResult<()>
pub fn associate_var( &self, ptr: usize, var_name: String, type_name: String, source_file: Option<&str>, source_line: Option<u32>, ) -> TrackingResult<()>
Associate a variable name and type with an allocation.
§Arguments
ptr- Memory pointer addressvar_name- Variable nametype_name- Type namesource_file- Source file (optional)source_line- Source line (optional)
Sourcepub fn fast_track_allocation(
&self,
ptr: usize,
size: usize,
var_name: String,
) -> TrackingResult<()>
pub fn fast_track_allocation( &self, ptr: usize, size: usize, var_name: String, ) -> TrackingResult<()>
Fast track allocation with variable name.
§Arguments
ptr- Memory pointer addresssize- Allocation size in bytesvar_name- Variable name
Sourcepub fn binding_serial(&self, ptr: usize) -> Option<u64>
pub fn binding_serial(&self, ptr: usize) -> Option<u64>
Identity of the live binding at ptr, if any.
Changes whenever a new block is recorded at that address, so callers can detect address reuse instead of assuming the same allocation (plan A06/PR-02).
Sourcepub fn duplicate_observations(&self) -> u64
pub fn duplicate_observations(&self) -> u64
Number of duplicate sightings of already tracked addresses.
A non-zero value is expected (hook registration plus typed observation of the same buffer); it is reported separately so it can never be mistaken for allocated bytes.
Sourcepub fn replaced_bindings(&self) -> u64
pub fn replaced_bindings(&self) -> u64
Number of bindings superseded at the same address without an observed release.
Non-zero means the capture missed a free, or two different blocks shared an
address: the bytes stay conserved, but the two allocations are not the same one.
Sourcepub fn degraded_releases(&self) -> u64
pub fn degraded_releases(&self) -> u64
Number of releases that could only be matched by address.
Non-zero means a release arrived for an address that held several live bindings,
so the closed binding was a guess. Callers that know the identity should use
track_deallocation_of instead.
Sourcepub fn get_stats(&self) -> TrackingResult<MemoryStats>
pub fn get_stats(&self) -> TrackingResult<MemoryStats>
Get current memory statistics.
Sourcepub fn detect_leaks(&self) -> (usize, u64)
pub fn detect_leaks(&self) -> (usize, u64)
Detect memory leaks at program shutdown.
This should be called when the program is shutting down to detect allocations that were never freed. Returns the count and total size of allocations that are still active.
Sourcepub fn get_active_allocations(&self) -> TrackingResult<Vec<AllocationInfo>>
pub fn get_active_allocations(&self) -> TrackingResult<Vec<AllocationInfo>>
Get all currently active allocations.
Sourcepub fn get_memory_by_type(&self) -> TrackingResult<HashMap<String, usize>>
pub fn get_memory_by_type(&self) -> TrackingResult<HashMap<String, usize>>
Get memory grouped by type.
Sourcepub fn set_fast_mode(&self, enabled: bool)
pub fn set_fast_mode(&self, enabled: bool)
Enable or disable fast mode.
Sourcepub fn is_fast_mode(&self) -> bool
pub fn is_fast_mode(&self) -> bool
Check if fast mode is enabled.
Sourcepub fn enable_fast_mode(&self)
pub fn enable_fast_mode(&self)
Enable fast mode for testing.
Sourcepub fn ensure_memory_analysis_path<P: AsRef<Path>>(&self, path: P) -> PathBuf
pub fn ensure_memory_analysis_path<P: AsRef<Path>>(&self, path: P) -> PathBuf
Ensure memory analysis path exists and return the full path.
Sourcepub fn ensure_memscope_path<P: AsRef<Path>>(&self, path: P) -> PathBuf
pub fn ensure_memscope_path<P: AsRef<Path>>(&self, path: P) -> PathBuf
Ensure path uses .memscope extension and is in MemoryAnalysis directory.
Sourcepub fn export_to_memscope<P: AsRef<Path>>(&self, path: P) -> TrackingResult<()>
pub fn export_to_memscope<P: AsRef<Path>>(&self, path: P) -> TrackingResult<()>
Export memory tracking data to .memscope file format (JSON content).
This method exports memory tracking data with a .memscope file extension. The content is serialized as JSON for human readability and interoperability.
Sourcepub fn export_to_json<P: AsRef<Path>>(&self, path: P) -> TrackingResult<()>
pub fn export_to_json<P: AsRef<Path>>(&self, path: P) -> TrackingResult<()>
Export memory tracking data to JSON format.
Trait Implementations§
Source§impl Default for MemoryTracker
impl Default for MemoryTracker
Source§impl Drop for MemoryTracker
impl Drop for MemoryTracker
Auto Trait Implementations§
impl !Freeze for MemoryTracker
impl !RefUnwindSafe for MemoryTracker
impl Forget for MemoryTracker
impl Send for MemoryTracker
impl Sync for MemoryTracker
impl Unpin for MemoryTracker
impl UnsafeUnpin for MemoryTracker
impl UnwindSafe for MemoryTracker
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more