π¦ 1. What is Valgrind?
Section titled βπ¦ 1. What is Valgrind?β# Valgrind is a dynamic analysis tool for Linux/macOS programs.# It detects memory errors, leaks, threading bugs, and performance issues.# It runs your program inside a virtual CPU β no recompilation needed.# (But compile with -g for useful line numbers in reports)
# Install on Debian/Ubuntusudo apt install valgrind
# Install on macOS (limited support β Linux preferred)brew install valgrind
# Verify installationvalgrind --version# β valgrind-3.22.0π’ BEGINNER
Section titled βπ’ BEGINNERβ2. Compiling for Valgrind
Section titled β2. Compiling for Valgrindβ# Always compile with -g (debug symbols) for readable output# Optionally use -O0 to disable optimizations (more accurate results)gcc -g -O0 -o myprogram myprogram.c
# For C++g++ -g -O0 -o myprogram myprogram.cpp3. Basic Run (Memcheck β Default Tool)
Section titled β3. Basic Run (Memcheck β Default Tool)β# Simplest usage β Memcheck tool runs by defaultvalgrind ./myprogram
# With command-line arguments for your programvalgrind ./myprogram arg1 arg24. Understanding the Output Format
Section titled β4. Understanding the Output Formatβ# Every Valgrind line is prefixed with ==PID==# Example output:
==12345== Memcheck, a memory error detector==12345== Invalid read of size 4 β type of error==12345== at 0x401234: main (foo.c:10) β where it happened==12345== Address 0x... is 0 bytes after β what memory was touched==12345====12345== LEAK SUMMARY:==12345== definitely lost: 40 bytes in 1 blocks β real leak==12345== indirectly lost: 0 bytes in 0 blocks==12345== possibly lost: 0 bytes in 0 blocks β maybe leak==12345== still reachable: 0 bytes in 0 blocks β pointer exists but not freed==12345== suppressed: 0 bytes in 0 blocks β ignored (by suppression rules)5. Detecting Memory Leaks
Section titled β5. Detecting Memory Leaksβ// myprogram.c β intentional leak#include <stdlib.h>int main() { int* p = malloc(40); // Allocated but never freed β leak! return 0;}# Run with leak checking enabledvalgrind --leak-check=full ./myprogram# β Reports "definitely lost: 40 bytes in 1 blocks"# --leak-check=full shows each leak's allocation stack trace# --leak-check=yes summary only# --leak-check=no disable leak check6. Detecting Invalid Memory Access
Section titled β6. Detecting Invalid Memory Accessβ// Reading beyond array boundsint main() { int arr[5]; return arr[10]; // Invalid read β out of bounds!}valgrind ./myprogram# β "Invalid read of size 4"# β Shows the exact file and line number with -g7. Detecting Use-After-Free
Section titled β7. Detecting Use-After-Freeβint main() { int* p = malloc(sizeof(int)); free(p); *p = 42; // Use after free β undefined behavior! return 0;}valgrind ./myprogram# β "Invalid write of size 4"# β "Address ... is 0 bytes inside a block of size 4 free'd"8. Detecting Uninitialized Memory
Section titled β8. Detecting Uninitialized Memoryβint main() { int x; // Declared but never initialized return x + 1; // Using uninitialized value!}valgrind --track-origins=yes ./myprogram# β "Use of uninitialised value of size 4"# β --track-origins=yes tells you WHERE the uninit value came fromπ‘ INTERMEDIATE
Section titled βπ‘ INTERMEDIATEβ9. Commonly Used Flags
Section titled β9. Commonly Used Flagsβ# Show full leak details with allocation stack tracesvalgrind --leak-check=full ./myprogram
# Also show reachable and indirectly lost blocksvalgrind --leak-check=full --show-leak-kinds=all ./myprogram
# Track where uninitialized values originatevalgrind --track-origins=yes ./myprogram
# Show error details verboselyvalgrind -v ./myprogram
# Limit number of errors reported (default: 10 per unique spot)valgrind --error-limit=no ./myprogram
# Combine all useful flags for thorough checkingvalgrind --leak-check=full \\ --show-leak-kinds=all \\ --track-origins=yes \\ --error-limit=no \\ -v \\ ./myprogram10. Saving Output to a File
Section titled β10. Saving Output to a Fileβ# Redirect Valgrind output to a log filevalgrind --log-file=valgrind_report.txt ./myprogram
# Append PID to filename (useful for multi-process apps)valgrind --log-file=report_%p.txt ./myprogram# β Creates report_12345.txt etc.
# Redirect to a file descriptor (e.g., stderr = 2)valgrind --log-fd=2 ./myprogram11. Suppression Files (Ignore Known False Positives)
Section titled β11. Suppression Files (Ignore Known False Positives)β# Generate a suppression file from current errorsvalgrind --gen-suppressions=all ./myprogram 2> my.supp# β Prints suppression blocks you can copy into a .supp file
# Use a suppression file to ignore known issuesvalgrind --suppressions=my.supp ./myprogram
# Use multiple suppression filesvalgrind --suppressions=sys.supp --suppressions=my.supp ./myprogram# Example suppression block in a .supp file:{ ignore_openssl_leak # Name (can be anything) Memcheck:Leak # Tool:ErrorType fun:malloc # Stack frame pattern fun:CRYPTO_malloc fun:*openssl*}12. Callgrind β CPU Profiling
Section titled β12. Callgrind β CPU Profilingβ# Run with Callgrind to collect call graph + instruction countsvalgrind --tool=callgrind ./myprogram# β Creates callgrind.out.<PID> file
# View the report in terminalcallgrind_annotate callgrind.out.12345
# Open visually with KCachegrind (GUI)kcachegrind callgrind.out.12345
# Focus on specific function only (reduce noise)valgrind --tool=callgrind --fn-skip=pthread_* ./myprogram
# Start profiling paused β toggle with callgrind_controlvalgrind --tool=callgrind --instr-atstart=no ./myprogramcallgrind_control -i on # Start collectingcallgrind_control -i off # Stop collecting13. Helgrind β Thread Error Detector
Section titled β13. Helgrind β Thread Error Detectorβ# Detect race conditions, lock order violations, pthread API misusevalgrind --tool=helgrind ./myprogram
# Common errors reported:# β "Possible data race during read/write of size N"# β "Lock order violated" (deadlock risk)# β "Mutex is already locked by this thread"// Example race condition Helgrind catches:int counter = 0; // Shared variable β no lock!
void* increment(void* arg) { counter++; // β Helgrind flags this as a data race return NULL;}14. DRD β Another Thread Checker
Section titled β14. DRD β Another Thread Checkerβ# DRD is an alternative to Helgrind β sometimes catches different issuesvalgrind --tool=drd ./myprogram
# Key differences from Helgrind:# β Lower memory overhead# β Better support for custom synchronization primitives# β Reports "Conflicting accesses" for races
# Show stack trace for both conflicting accessesvalgrind --tool=drd --show-stack-usage=yes ./myprogram15. Massif β Heap Profiler
Section titled β15. Massif β Heap Profilerβ# Profile heap memory usage over timevalgrind --tool=massif ./myprogram# β Creates massif.out.<PID>
# View the report as a text chartms_print massif.out.12345# β ASCII chart of heap usage over time
# Open visually with Massif-Visualizer (GUI)massif-visualizer massif.out.12345
# Include stack memory in profilingvalgrind --tool=massif --pages-as-heap=yes ./myprogram
# Take snapshots every N allocations (default: 10)valgrind --tool=massif --detailed-freq=1 ./myprogram16. Cachegrind β Cache & Branch Profiler
Section titled β16. Cachegrind β Cache & Branch Profilerβ# Simulate CPU cache behavior β find cache missesvalgrind --tool=cachegrind ./myprogram# β Creates cachegrind.out.<PID>
# View the annotated reportcg_annotate cachegrind.out.12345
# Diff two runs (to compare optimizations)cg_diff cachegrind.out.111 cachegrind.out.222
# Output includes:# Ir = Instructions read# I1mr = L1 instruction cache misses# D1mr = L1 data cache read misses# DLmr = Last-level cache read missesπ΄ ADVANCED
Section titled βπ΄ ADVANCEDβ17. Valgrind Client Requests (In-Code API)
Section titled β17. Valgrind Client Requests (In-Code API)β#include <valgrind/valgrind.h>#include <valgrind/memcheck.h>
int main() { // Check if we're running under Valgrind if (RUNNING_ON_VALGRIND) { printf("Running under Valgrind\\n"); }
char buf[10]; // Mark memory as defined (suppress false positives for custom allocators) VALGRIND_MAKE_MEM_DEFINED(buf, 10);
// Mark memory as undefined (trigger errors if accessed) VALGRIND_MAKE_MEM_UNDEFINED(buf, 10);
// Mark memory as inaccessible (like freed memory) VALGRIND_MAKE_MEM_NOACCESS(buf, 10);
// Print current memory error count to Valgrind log unsigned errs = VALGRIND_COUNT_ERRORS;
// Force Valgrind to do a full leak check at this point VALGRIND_DO_LEAK_CHECK;
return 0;}# Compile with Valgrind headersgcc -g -I/usr/include/valgrind -o myprogram myprogram.c18. Custom Memory Pool / Allocator Support
Section titled β18. Custom Memory Pool / Allocator Supportβ#include <valgrind/memcheck.h>
// Tell Valgrind about a custom memory poolvoid* pool = malloc(1024); // Your pool buffer
// Register the poolVALGRIND_CREATE_MEMPOOL(pool, 0, 0);
// When you hand out a block from your pool:void* block = pool; // Your allocation logicVALGRIND_MEMPOOL_ALLOC(pool, block, 64); // Mark 64 bytes as allocated
// When you "free" a block back to your pool:VALGRIND_MEMPOOL_FREE(pool, block);
// When destroying the whole pool:VALGRIND_DESTROY_MEMPOOL(pool);free(pool);19. Multi-Process and Child Process Tracing
Section titled β19. Multi-Process and Child Process Tracingβ# Also trace child processes spawned by your programvalgrind --trace-children=yes ./myprogram
# Filter which children to trace by executable namevalgrind --trace-children=yes \\ --trace-children-skip=*bash*,*sh \\ ./myprogram
# Log each child to its own file (using %p = PID placeholder)valgrind --trace-children=yes \\ --log-file=vg_%p.txt \\ ./myprogram20. Attaching to a Running Process (Vgdb)
Section titled β20. Attaching to a Running Process (Vgdb)β# Start program under Valgrind with GDB server enabledvalgrind --vgdb=yes --vgdb-error=0 ./myprogram# β Valgrind pauses and waits for GDB to connect
# In another terminal, attach GDBgdb ./myprogram(gdb) target remote | vgdb # Connect to the Valgrind GDB server
# Now you can set breakpoints, inspect memory, etc.(gdb) monitor leak_check full # Run Valgrind commands via GDB monitor(gdb) monitor who_points_at 0xADDRESS # Find what points to an address(gdb) continue21. XML Output (for CI/CD Integration)
Section titled β21. XML Output (for CI/CD Integration)β# Output errors as XML β great for parsing in CI pipelinesvalgrind --xml=yes --xml-file=valgrind_out.xml ./myprogram
# Parse in Python (example)# import xml.etree.ElementTree as ET# tree = ET.parse('valgrind_out.xml')# errors = tree.findall('.//error')
# Use with tools like:# β valgrind-xml-parser (Python)# β Jenkins Valgrind Plugin# β GitLab CI artifact parsing22. Valgrind in CI/CD (GitHub Actions Example)
Section titled β22. Valgrind in CI/CD (GitHub Actions Example)βname: Valgrind Memory Check
on: [push, pull_request]
jobs: memcheck: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3
- name: Install Valgrind run: sudo apt-get install -y valgrind
- name: Build with debug symbols run: gcc -g -O0 -o myprogram myprogram.c
- name: Run Valgrind run: | valgrind --error-exitcode=1 \\ # Exit with code 1 on any error --leak-check=full \\ --show-leak-kinds=all \\ ./myprogram # CI fails automatically if Valgrind finds errors23. Exit Code Control (for Scripting)
Section titled β23. Exit Code Control (for Scripting)β# Exit with a non-zero code if Valgrind finds errors (great for CI)valgrind --error-exitcode=1 ./myprogramecho $? # β 1 if errors found, 0 if clean
# Set a specific exit code valuevalgrind --error-exitcode=42 ./myprogram24. Performance Tips
Section titled β24. Performance Tipsβ# Valgrind slows programs by 10xβ50x β reduce with these:
# Reduce detail level (faster but less info)valgrind --leak-check=summary ./myprogram
# Skip unneeded checksvalgrind --undef-value-errors=no ./myprogram # Skip uninit checks
# Use --fair-sched to improve threading simulation accuracyvalgrind --tool=helgrind --fair-sched=yes ./myprogram
# For large programs β limit stack trace depth (faster)valgrind --num-callers=5 ./myprogram # Default is 12 frames
# Cache results between runs (Cachegrind only)valgrind --tool=cachegrind --cache-sim=no ./myprogram # Branch onlyπ Quick Tool Reference
Section titled βπ Quick Tool Referenceβ| Tool | Command | Purpose |
|---|---|---|
| Memcheck | --tool=memcheck (default) | Memory leaks, invalid access, uninit use |
| Callgrind | --tool=callgrind | CPU profiling, call graph |
| Helgrind | --tool=helgrind | Thread race conditions |
| DRD | --tool=drd | Thread errors (alternative to Helgrind) |
| Massif | --tool=massif | Heap memory profiling |
| Cachegrind | --tool=cachegrind | CPU cache & branch prediction |
| DHAT | --tool=dhat | Dynamic heap analysis |
| BBV | --tool=exp-bbv | Basic block vectors for simulation |
π Quick Flags Reference
Section titled βπ Quick Flags Referenceβ| Flag | Meaning |
|---|---|
--leak-check=full | Full leak report with stack traces |
--show-leak-kinds=all | Show all leak types |
--track-origins=yes | Show origin of uninitialized values |
--error-exitcode=N | Return exit code N if errors found |
--log-file=file.txt | Save output to file |
--suppressions=file | Load suppression rules |
--gen-suppressions=all | Generate suppression entries |
--trace-children=yes | Follow child processes |
--vgdb=yes | Enable GDB server |
--num-callers=N | Stack trace depth (default: 12) |
--error-limit=no | Report all errors (no cap) |
-v | Verbose output |
π Exit Code Cheat Sheet
Section titled βπ Exit Code Cheat Sheetβ# Clean run (no errors): β returns your program's exit code# Errors found + --error-exitcode=1 set: β returns 1# Valgrind internal error: β returns 1# Program killed by signal: β returns 128 + signal numberValgrind runs on Linux (best), macOS (limited), and not on Windows (use WSL or AddressSanitizer instead).
For Windows memory checking, consider: AddressSanitizer (-fsanitize=address) or Dr. Memory.