Skip to content

💾 Memory Analysis Tools

🔧 Tools by Platform

╔═══════════════════════════════════════════════════════════════╗ ║ Tool │ Platform │ Detects ║ ╠═══════════════════╪═══════════╪══════════════════════════════╣ ║ Valgrind │ Linux │ Leaks, invalid access ║ ║ AddressSanitizer │ All │ UB, leaks, use-after-free ║ ║ Memory Validator │ Windows │ Commercial, GUI ║ ║ Instruments Leaks │ macOS │ Native, integrated ║ ╚═══════════════════════════════════════════════════════════════╝

Compile flags:

target_compile_options(MyPlugin PRIVATE
    -fsanitize=address
    -fno-omit-frame-pointer
    -O1
)
target_link_options(MyPlugin PRIVATE
    -fsanitize=address
)

Run:

ASAN_OPTIONS=detect_leaks=1 ./plugin_test

Detects: - Heap buffer overflow - Stack buffer overflow - Use after free - Use after return - Use after scope - Memory leaks - Double free - Invalid free

Example Output

=================================================================
==12345==ERROR: AddressSanitizer: heap-use-after-free on address 0x614000000044
READ of size 4 at 0x614000000044 thread T0
    #0 0x4a5b3c in processAudio() plugin.cpp:42
    #1 0x4a5d2e in main() main.cpp:10

0x614000000044 is located 4 bytes inside of 64-byte region
freed by thread T0 here:
    #0 0x4a3f2c in operator delete(void*)
    #1 0x4a5a8e in ~AudioBuffer() buffer.cpp:15

📊 Valgrind

Install:

# Linux
sudo apt install valgrind

# macOS (limited support)
brew install valgrind

Run:

valgrind --tool=memcheck \
         --leak-check=full \
         --show-leak-kinds=all \
         --track-origins=yes \
         --log-file=valgrind.log \
         ./plugin_test

What it checks: - Memory leaks (definitely lost, possibly lost) - Invalid reads/writes - Use of uninitialized values - Invalid frees - Mismatched allocation/deallocation

Example Output

==12345== HEAP SUMMARY:
==12345==     in use at exit: 1,024 bytes in 1 blocks
==12345==   total heap usage: 100 allocs, 99 frees, 10,240 bytes allocated
==12345==
==12345== 1,024 bytes in 1 blocks are definitely lost in loss record 1 of 1
==12345==    at 0x4A5B: malloc (vg_replace_malloc.c:299)
==12345==    by 0x108A2E: AudioBuffer::allocate() (buffer.cpp:23)
==12345==    by 0x108B4C: Plugin::init() (plugin.cpp:15)
==12345==
==12345== LEAK SUMMARY:
==12345==    definitely lost: 1,024 bytes in 1 blocks

🪟 Windows Memory Validator

Commercial tool (~$400) but very powerful GUI

Features: - Real-time memory tracking - Leak detection - Allocation hot spots - Memory fragmentation analysis - Integration with Visual Studio

Download: https://www.softwareverify.com/

🍎 macOS Instruments

Free, built into Xcode

Run:

# From command line
instruments -t Leaks ./plugin_test

# Or use Xcode GUI
# Product → Profile (Cmd+I) → Choose "Leaks" template

Features: - Leaks detection - Allocations tracking - Heap snapshots - Memory graph visualization

🧰 Memory Sanitizers Suite

ThreadSanitizer (TSan)

Detects data races and deadlocks

-fsanitize=thread

UndefinedBehaviorSanitizer (UBSan)

Catches undefined behavior

-fsanitize=undefined

MemorySanitizer (MSan)

Uninitialized memory reads

-fsanitize=memory

🔬 Heap Profiling

Massif (Valgrind)

valgrind --tool=massif ./plugin_test
ms_print massif.out.12345

Shows: - Heap usage over time - Peak memory usage - Allocation call stacks

heaptrack (Linux)

heaptrack ./plugin_test
heaptrack_gui heaptrack.plugin_test.12345.gz

Features: - Flamegraph visualization - Temporary allocation detection - Memory leaks with backtraces

🎯 Common Memory Issues in Audio Plugins

1. Real-time Allocation

Problem:

void processAudio(float* buffer, int size) {
    std::vector<float> temp(size);  // ❌ Allocates in real-time!
    // ...
}

Solution:

class Plugin {
    std::vector<float> temp_;  // Pre-allocated

    void processAudio(float* buffer, int size) {
        temp_.resize(size);  // May allocate, but rare
        // Better: ensure temp_ always large enough
    }
};

2. Memory Leaks

Problem:

float* createBuffer() {
    return new float[1024];  // Who owns this?
}

Solution:

std::unique_ptr<float[]> createBuffer() {
    return std::make_unique<float[]>(1024);  // Clear ownership
}

3. Use After Free

Problem:

auto buffer = std::make_unique<float[]>(size);
processAsync(buffer.get());  // buffer may be deleted before processAsync returns

Solution:

auto buffer = std::make_shared<float[]>(size);
processAsync(buffer);  // Reference counted, safe

📋 Memory Testing Checklist

  • Run with AddressSanitizer
  • Run with Valgrind (Linux)
  • Check for leaks (all platforms)
  • Profile heap usage under load
  • Verify no real-time allocations
  • Test with large sessions (stress test)
  • Check memory usage over time (no gradual leaks)
  • Test plugin load/unload cycles
  • Verify cleanup in destructor

🚀 Best Practices

  1. Use RAII: Let destructors clean up
  2. Pre-allocate: Size buffers in constructor
  3. Smart pointers: Avoid manual memory management
  4. Test early: Run sanitizers during development
  5. CI integration: Sanitizers in automated tests

🔧 Integration with CMake

# Option to enable sanitizers
option(ENABLE_ASAN "Enable AddressSanitizer" OFF)
option(ENABLE_UBSAN "Enable UndefinedBehaviorSanitizer" OFF)

if(ENABLE_ASAN)
    add_compile_options(-fsanitize=address -fno-omit-frame-pointer)
    add_link_options(-fsanitize=address)
endif()

if(ENABLE_UBSAN)
    add_compile_options(-fsanitize=undefined)
    add_link_options(-fsanitize=undefined)
endif()

Usage:

cmake -DENABLE_ASAN=ON ..
cmake --build .
./plugin_test

📝 Memory Bug Report Template

# Memory Issue Report

**Type:** Memory leak / Use-after-free / Buffer overflow
**Severity:** Critical / High / Medium / Low

## Reproduction
1. Load plugin
2. Process 10 seconds of audio
3. Unload plugin
4. Repeat 100 times

## Tool Output
[Paste sanitizer/valgrind output]

## Analysis
Memory is allocated in AudioBuffer::init() but not freed in destructor.

## Fix
Add proper cleanup in ~AudioBuffer()