Skip to main content

MemoryTracker

Struct MemoryTracker 

Source
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

Source

pub fn new() -> Self

Create a new memory tracker.

Source

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_reallocation may 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 address
  • size - Allocation size in bytes
Source

pub fn live_binding_count_at(&self, ptr: usize) -> usize

Number of live bindings currently recorded at ptr.

Source

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 closed
  • Ok(false) if the pointer was not tracked (possible double-free or untracked allocation)
Source

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.

Source

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.

Source

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.

Source

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.

Source

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 reallocation
  • new_ptr - Address returned by the allocator
  • old_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
Source

pub fn get_allocation_size(&self, ptr: usize) -> Option<usize>

Get the size of an active allocation.

§Arguments
  • ptr - Memory pointer address
§Returns
  • Some(size) if the allocation exists
  • None if the pointer is not tracked
Source

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 address
  • var_name - Variable name
  • type_name - Type name
  • source_file - Source file (optional)
  • source_line - Source line (optional)
Source

pub fn fast_track_allocation( &self, ptr: usize, size: usize, var_name: String, ) -> TrackingResult<()>

Fast track allocation with variable name.

§Arguments
  • ptr - Memory pointer address
  • size - Allocation size in bytes
  • var_name - Variable name
Source

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).

Source

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.

Source

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.

Source

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.

Source

pub fn get_stats(&self) -> TrackingResult<MemoryStats>

Get current memory statistics.

Source

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.

Source

pub fn get_active_allocations(&self) -> TrackingResult<Vec<AllocationInfo>>

Get all currently active allocations.

Source

pub fn get_memory_by_type(&self) -> TrackingResult<HashMap<String, usize>>

Get memory grouped by type.

Source

pub fn set_fast_mode(&self, enabled: bool)

Enable or disable fast mode.

Source

pub fn is_fast_mode(&self) -> bool

Check if fast mode is enabled.

Source

pub fn enable_fast_mode(&self)

Enable fast mode for testing.

Source

pub fn ensure_memory_analysis_path<P: AsRef<Path>>(&self, path: P) -> PathBuf

Ensure memory analysis path exists and return the full path.

Source

pub fn ensure_memscope_path<P: AsRef<Path>>(&self, path: P) -> PathBuf

Ensure path uses .memscope extension and is in MemoryAnalysis directory.

Source

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.

Source

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

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl Drop for MemoryTracker

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more