💾 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 ║ ╚═══════════════════════════════════════════════════════════════╝
🔍 AddressSanitizer (Recommended)¶
Compile flags:
target_compile_options(MyPlugin PRIVATE
-fsanitize=address
-fno-omit-frame-pointer
-O1
)
target_link_options(MyPlugin PRIVATE
-fsanitize=address
)
Run:
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:
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
UndefinedBehaviorSanitizer (UBSan)¶
Catches undefined behavior
MemorySanitizer (MSan)¶
Uninitialized memory reads
🔬 Heap Profiling¶
Massif (Valgrind)¶
Shows: - Heap usage over time - Peak memory usage - Allocation call stacks
heaptrack (Linux)¶
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:
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:
📋 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¶
- Use RAII: Let destructors clean up
- Pre-allocate: Size buffers in constructor
- Smart pointers: Avoid manual memory management
- Test early: Run sanitizers during development
- 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:
📝 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()