Memory leak workflow¶
The C core uses careful ownership conventions (see Memory ownership). When a leak shows up, the workflow below catches it.
Valgrind (Linux)¶
cd python
MSCOMPRESS_DEBUG=1 uv sync --all-extras --reinstall
valgrind --tool=memcheck --leak-check=full \
--log-file=leak-check.txt \
$(uv run which python) -m pytest
The debug build (MSCOMPRESS_DEBUG=1) adds -g -O0, which makes
Valgrind's stack traces map cleanly to Cython source lines.
macOS leaks¶
cd python
MSCOMPRESS_DEBUG=1 uv sync --all-extras --reinstall
MallocStackLogging=1 $(uv run which python) -m pytest &
PID=$!
sleep 5
leaks $PID > leaks-report.txt
kill $PID
For richer per-call analysis, the repo ships a
cpython-memory-leak-detector workflow that wraps leaks. See the
project root.
CLI¶
The CLI binary itself can be run under Valgrind:
cmake -S . -B . -DCMAKE_BUILD_TYPE=Debug
cmake --build .
valgrind --tool=memcheck --leak-check=full ./cli/mscompress in.mzML out.msz
Common leak sources¶
- Mismatched division dealloc — using
dealloc_division()on an mmap-backed division (or vice versa). See Memory ownership. - Double-free on
encode_base64()input — the function frees itszlib_block_tparameter; do not free it after the call. zlib_pop_header()return value — the 4-byte header is malloc'd; free it after use.
When you find a new failure mode worth documenting, add it to Memory ownership.