Aller au contenu

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

CommandDescription
valgrind ./programRun with Memcheck (default)
valgrind --leak-check=full ./programDetailed leak analysis
valgrind --tool=callgrind ./programCPU profiling
valgrind --tool=cachegrind ./programCache profiling
valgrind --tool=helgrind ./programThread error detection
valgrind --tool=massif ./programHeap profiling
valgrind --tool=drd ./programData 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

IssueSolution
Very slow executionNormal (10-50x slowdown); use smaller test inputs
Too many errorsUse suppressions for library issues
No line numbersCompile with -g -O0 flags
Unsupported instructionUpdate Valgrind; check CPU instruction set
False positivesCreate suppression entries
macOS issuesValgrind 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