By Anthony Umemoto

What is Valgrind?

Valgrind is a diagnostics tool that watches how your program is using memory. Getting comfortable using valgrind will be a useful skill when debugging those dreaded memory leaks.

To install valgrind, use the command:

$ sudo apt install valgrind

To use valgrind, use the command:

$ valgrind ./<executable>

For example:

$ valgrind ./sorter -l 100

You just have to put valgrind in front of the command you’d normally use to run your program!

Valgrind’s Output

You’ll see a lot of different messages from valgrind, since it’s a very robust tool. The end goal is always the same though:

$ valgrind ./example
==4763== Memcheck, a memory error detector
==4763== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==4763== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==4763== Command: ./example
==4763== 
I just used malloc() to make my array!
Filling it up with...
0 1 2 3 4 
And now I use free()
==4763== 
==4763== HEAP SUMMARY:
==4763==     in use at exit: 0 bytes in 0 blocks
==4763==   total heap usage: 2 allocs, 2 frees, 1,044 bytes allocated
==4763== 
==4763== All heap blocks were freed -- no leaks are possible
==4763== 
==4763== For lists of detected and suppressed errors, rerun with: -s
==4763== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

You’ll want to see just your program’s output in the middle (which you can differentiate by the lack of ==####==).

In HEAP SUMMARY, there should be an equal number of allocs and frees.

You should see the message All heap blocks were freed -- no leaks are possible.

And, at the bottom it should say 0 errors from 0 contexts (suppressed: 0 from 0).

If the output looks like this, then you know your program successfully passes valgrind.

DWARF 4

Valgrind looks directly at the executable, and doesn’t actually know what your C code looks like. Because of this, figuring out what valgrind is telling you can be pretty tricky.

To make its messages a little easier to read, we can use the -gdwarf-4 compiler flag when compiling. This will format the executable using DWARF 4, which you can read more about here.

Comparing valgrind’s output with, and without DWARF 4:

dwarf.c:

1  #include <stdlib.h>
2  
3  int main(void) {
4      int *alloc1 = (int *) malloc(sizeof(int) * 10);
5      int *alloc2 = (int *) malloc(sizeof(int) * 10);
6      free(alloc1);
7      return 0;
8  }

This program will cause a memory leak since alloc2 is never free’d.

Compiled with: clang dwarf.c -o dwarf

$ valgrind --leak-check=full ./dwarf
==2586== Memcheck, a memory error detector
==2586== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2586== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==2586== Command: ./dwarf
==2586== 
==2586== 
==2586== HEAP SUMMARY:
==2586==     in use at exit: 40 bytes in 1 blocks
==2586==   total heap usage: 2 allocs, 1 frees, 80 bytes allocated
==2586== 
==2586== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==2586==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2586==    by 0x401166: main (in /home/user/dwarf)
==2586== 
==2586== LEAK SUMMARY:
==2586==    definitely lost: 40 bytes in 1 blocks
==2586==    indirectly lost: 0 bytes in 0 blocks
==2586==      possibly lost: 0 bytes in 0 blocks
==2586==    still reachable: 0 bytes in 0 blocks
==2586==         suppressed: 0 bytes in 0 blocks
==2586== 
==2586== For lists of detected and suppressed errors, rerun with: -s
==2586== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

Here, valgrind is run with --leak-check=full which is useful for figuring out where blocks of memory were lost.

The useful information is:

==2586== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==2586==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2586==    by 0x401166: main (in /home/user/dwarf)

This tells us that the leak is from a block allocated with malloc() in main(). But which malloc()? If your program is large, with lots of memory allocations and frees happening, pinning down which malloc() this is referring to can be difficult.

Compiled with: clang -gdwarf-4 dwarf.c -o dwarf

$ valgrind --leak-check=full ./dwarf
==2742== Memcheck, a memory error detector
==2742== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2742== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==2742== Command: ./dwarf
==2742== 
==2742== 
==2742== HEAP SUMMARY:
==2742==     in use at exit: 40 bytes in 1 blocks
==2742==   total heap usage: 2 allocs, 1 frees, 80 bytes allocated
==2742== 
==2742== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==2742==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2742==    by 0x401166: main (dwarf.c:5)
==2742== 
==2742== LEAK SUMMARY:
==2742==    definitely lost: 40 bytes in 1 blocks
==2742==    indirectly lost: 0 bytes in 0 blocks
==2742==      possibly lost: 0 bytes in 0 blocks
==2742==    still reachable: 0 bytes in 0 blocks
==2742==         suppressed: 0 bytes in 0 blocks
==2742== 
==2742== For lists of detected and suppressed errors, rerun with: -s
==2742== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

Looking at the same section:

==2742== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==2742==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2742==    by 0x401166: main (dwarf.c:5)

Next to main it now says that the leak is from the malloc() on line 5 in dwarf.c, which is the malloc() for the alloc2 array.

Using -gdwarf-4 in Your Makefiles

You’ll want to use -gdwarf-4 as one of the compiler flags in your Makefiles. However, using DWARF 4 results in a larger and slower executable, so it’s best practice to have two targets: one for a debugging executable, and one for a “release” executable.

This can be accomplished by having the compiler flags variable set depending on which target is being compiled.

Makefile:

CC     = clang
BASICS = -Wall -Wextra -Werror -pedantic -Wstrict-prototypes

debug:   CFLAGS = $(BASICS) -gdwarf-4
release: CFLAGS = $(BASICS)

EXE = hello
OBJ = hello.o

debug: $(EXE)
release: $(EXE)

$(EXE): $(OBJ)
	$(CC) -o $@ $^

%.o : %.c
	$(CC) $(CFLAGS) -c $<

Output:

$ make debug
clang -Wall -Wextra -Werror -pedantic -Wstrict-prototypes -gdwarf-4 -c hello.c
clang -o hello hello.o

...

$ make release
clang -Wall -Wextra -Werror -pedantic -Wstrict-prototypes -c hello.c
clang -o hello hello.o