Valgrind is a dynamic analysis framework that instruments your program to detect memory bugs, threading races, and performance issues. It runs your code on a synthetic CPU, observing every memory access and checking for errors: buffer overflows, use-after-free, memory leaks, uninitialized values, data races. Catches bugs at the moment they happen, not when they cause a crash elsewhere.
The trade-off: 10-50x slowdown due to instrumentation. Fine for testing and debugging; use perf for production profiling. Valgrind's memcheck tool is what most people use—compile with -g -O0, run valgrind ./program, and read the error report pointing to the exact line and function.
$ gcc -g -O0 -o program program.c $ valgrind ./program ==12345== Invalid read of size 4 ==12345== at 0x109179: process (program.c:22) ==12345== Address 0x4a4b080 is 0 bytes after a block of size 40 alloc'd ==12345== at 0x483DD99: malloc (vg_replace_malloc.c:307) ==12345== by 0x109150: process (program.c:15)
Invaluable for finding memory bugs that don't crash or leave obvious symptoms.