Valgrind Cheat Sheet
Overview
Valgrind is a programming tool suite for memory debugging, memory leak detection, and profiling on Linux and macOS. Its most popular tool, Memcheck, detects memory-management problems such as use of uninitialized values, reading/writing freed memory, buffer overflows, and memory leaks. Valgrind works by running programs on a synthetic CPU, providing detailed diagnostics without requiring source code modification.
Beyond Memcheck, Valgrind includes Callgrind (call-graph profiler), Cachegrind (cache profiler), Helgrind (thread error detector), DRD (data race detector), and Massif (heap profiler). These tools are essential for C/C++ development, especially for systems programming and performance-critical applications.
Installation
# Ubuntu/Debian
sudo apt install valgrind
# Fedora
sudo dnf install valgrind
# macOS (limited support)
brew install valgrind
# From source
wget https://sourceware.org/pub/valgrind/valgrind-3.23.0.tar.bz2
tar xjf valgrind-3.23.0.tar.bz2
cd valgrind-3.23.0
./configure && make && sudo make install
# Verify
valgrind --version
Core Commands
| Command | Description |
|---|---|
valgrind ./program | Run with Memcheck (default) |
valgrind --leak-check=full ./program | Detailed leak analysis |
valgrind --tool=callgrind ./program | CPU profiling |
valgrind --tool=cachegrind ./program | Cache profiling |
valgrind --tool=helgrind ./program | Thread error detection |
valgrind --tool=massif ./program | Heap profiling |
valgrind --tool=drd ./program | Data race detection |
Memcheck (Memory Errors)
Basic Usage
# Compile with debug info (essential)
gcc -g -O0 -o myapp main.c
# Run with Memcheck
valgrind ./myapp
# Full leak check with origins
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./myapp
# Write output to file
valgrind --log-file=valgrind.log --leak-check=full ./myapp
# Suppress known issues
valgrind --suppressions=my.supp --leak-check=full ./myapp
Common Error Types
# Invalid read/write (buffer overflow)
# ==1234== Invalid read of size 4
# ==1234== at 0x401234: main (test.c:10)
# Use of uninitialized value
# ==1234== Conditional jump depends on uninitialised value(s)
# ==1234== at 0x401234: main (test.c:15)
# Invalid free / double free
# ==1234== Invalid free() / delete / delete[] / realloc()
# Memory leak
# ==1234== 40 bytes in 1 blocks are definitely lost
# ==1234== at 0x4C2FB0F: malloc
# ==1234== by 0x401234: main (test.c:5)
Leak Summary Categories
# definitely lost - memory with no pointers (real leak)
# indirectly lost - memory reachable only through lost blocks
# possibly lost - interior pointer found (fragile reference)
# still reachable - pointer exists but wasn't freed (usually OK)
Generating Suppressions
# Generate suppression entries for known issues
valgrind --gen-suppressions=all --leak-check=full ./myapp 2>&1 | grep -A 10 "^{"
# Save suppressions
valgrind --gen-suppressions=all ./myapp 2> raw.supp
# Edit raw.supp to create clean my.supp file
Callgrind (CPU Profiling)
# Profile execution
valgrind --tool=callgrind ./myapp
# Produces callgrind.out.<pid>
# Profile specific functions
valgrind --tool=callgrind --toggle-collect=hot_function ./myapp
# Visualize with KCachegrind
kcachegrind callgrind.out.12345
# Or with callgrind_annotate
callgrind_annotate callgrind.out.12345
# Annotate specific source file
callgrind_annotate --inclusive=yes callgrind.out.12345 main.c
Cachegrind (Cache Profiling)
# Cache simulation
valgrind --tool=cachegrind ./myapp
# Annotate results
cg_annotate cachegrind.out.12345
# Compare two runs
cg_diff cachegrind.out.before cachegrind.out.after
Helgrind (Thread Errors)
# Detect data races and lock issues
valgrind --tool=helgrind ./threaded_app
# Common issues detected:
# - Data races (concurrent read/write without synchronization)
# - Lock ordering violations (potential deadlocks)
# - Misuse of POSIX pthread API
Configuration
Common Options
# Increase stack trace depth
valgrind --num-callers=30 --leak-check=full ./myapp
# Track file descriptors
valgrind --track-fds=yes ./myapp
# Child process tracking
valgrind --trace-children=yes ./myapp
# Error limit (default: 10000)
valgrind --error-limit=no ./myapp
# XML output for tools
valgrind --xml=yes --xml-file=report.xml --leak-check=full ./myapp
Suppression File
# my.supp
{
ignore_libc_leak
Memcheck:Leak
match-leak-kinds: reachable
fun:malloc
obj:/usr/lib/x86_64-linux-gnu/libc.so.*
}
{
ignore_dl_open
Memcheck:Leak
fun:calloc
fun:_dlerror_run
}
Massif (Heap Profiler)
# Profile heap usage
valgrind --tool=massif ./myapp
# Analyze results
ms_print massif.out.12345
# Profile stack too
valgrind --tool=massif --stacks=yes ./myapp
# Set snapshot frequency
valgrind --tool=massif --time-unit=B ./myapp
Advanced Usage
CI Integration
#!/bin/bash
# ci-memcheck.sh
valgrind --leak-check=full \
--error-exitcode=1 \
--errors-for-leak-kinds=definite,indirect \
--suppressions=ci.supp \
./test_suite
if [ $? -ne 0 ]; then
echo "Memory errors detected!"
exit 1
fi
Valgrind with GDB
# Start program under Valgrind with GDB server
valgrind --vgdb=yes --vgdb-error=0 ./myapp
# In another terminal, attach GDB
gdb ./myapp
(gdb) target remote | vgdb
(gdb) continue
Troubleshooting
| Issue | Solution |
|---|---|
| Very slow execution | Normal (10-50x slowdown); use smaller test inputs |
| Too many errors | Use suppressions for library issues |
| No line numbers | Compile with -g -O0 flags |
| Unsupported instruction | Update Valgrind; check CPU instruction set |
| False positives | Create suppression entries |
| macOS issues | Valgrind has limited macOS support; consider ASan |
# Quick check (most useful flags)
valgrind --leak-check=full --show-leak-kinds=definite --track-origins=yes --error-exitcode=1 ./myapp
# Check Valgrind itself
valgrind --tool=none ./myapp
# Alternative: AddressSanitizer (compile-time, faster)
gcc -fsanitize=address -g -O1 -o myapp main.c
./myapp