Valgrind tutorial

Table of Contents

Valgrind (http://valgrind.org/) is a fantastic tool for automatically detecting memory management bugs in programs (and for performing all sorts of other dynamic analyses).

Though it's no substitute for careful programming, you may find Valgrind useful for helping to identify memory leaks in your programs before submission (remember that we will be docking points for flagrantly leaking memory!)

Valgrind is already installed on ada, and using it is simply a matter of (a) knowing how to invoke it and (b) grokking its output.

Using Valgrind for the graph lab

For the graph lab, you're provided with a main.c file that dynamically allocates some vertex_t and adj_vertex_t structures to demonstrate the construction of an adjacency list. Problem is, the structures are never freed and so result in memory leaks.

Let's run the program through Valgrind to see how it can help use detect the leaks. After building the graph binary (make will do it), run the following command:

valgrind --leak-check=yes ./graph

This should produce results that look like the following:

==23076== Memcheck, a memory error detector
==23076== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==23076== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==23076== Command: ./graph
==23076==
Adjacency list:
  A: B(10)
  B: A(10) C(5)
  C: B(5)
==23076==
==23076== HEAP SUMMARY:
==23076==     in use at exit: 168 bytes in 7 blocks
==23076==   total heap usage: 7 allocs, 0 frees, 168 bytes allocated
==23076==
==23076== 168 (24 direct, 144 indirect) bytes in 1 blocks are definitely lost in loss record 7 of 7
==23076==    at 0x4A0515D: malloc (vg_replace_malloc.c:195)
==23076==    by 0x4005BC: main (main.c:17)
==23076==
==23076== LEAK SUMMARY:
==23076==    definitely lost: 24 bytes in 1 blocks
==23076==    indirectly lost: 144 bytes in 6 blocks
==23076==      possibly lost: 0 bytes in 0 blocks
==23076==    still reachable: 0 bytes in 0 blocks
==23076==         suppressed: 0 bytes in 0 blocks
==23076==
==23076== For counts of detected and suppressed errors, rerun with: -v
==23076== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 6 from 6)

The important bit here is that Valgrind reports that memory is "definitely lost". It also provides an estimate of how many bytes are leaked, and notes that there are 7 allocs and 0 frees (which is another indication that there is a memory leak). Finally, it also tells you that leaked memory was allocated by the main function (at line 17, to be specific) — if you leak memory in a deeply nested function, look for the top function in the stack trace.

To illustrate another common type of memory management bug Valgrind can detect for us, let us consider the following, faulty implementation of free_adj_list that a student implements in an attempt to free up the allocated vertices.

void free_adj_list (vertex_t *v) {
    adj_vertex_t *adjv = v->adj_list;
    while (adjv != NULL) {
        free(adjv); 
        adjv = adjv->next; 
    } 
}

After implementing this function, a student then proceeds to free the three vertices allocated in main as follows:

free_adj_list(v1); 
free(v1);
free_adj_list(v2); 
free(v2); 
free_adj_list(v3); 
free(v3);

Before reading on, try to figure out on your own what's wrong with the implementation above.

Here's the Valgrind report for the newly compiled binary:

==32242== Memcheck, a memory error detector
==32242== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==32242== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==32242== Command: ./graph
==32242==
Adjacency list:
  A: B(10)
  B: A(10) C(5)
  C: B(5)
==32242== Invalid read of size 8
==32242==    at 0x40060E: free_adj_list (main.c:9)
==32242==    by 0x400844: main (main.c:65)
==32242==  Address 0x4c1d170 is 16 bytes inside a block of size 24 free'd
==32242==    at 0x4A04D72: free (vg_replace_malloc.c:325)
==32242==    by 0x400609: free_adj_list (main.c:8)
==32242==    by 0x400844: main (main.c:65)
==32242==
==32242== Invalid read of size 8
==32242==    at 0x40060E: free_adj_list (main.c:9)
==32242==    by 0x40085C: main (main.c:69)
==32242==  Address 0x4c1d1d0 is 16 bytes inside a block of size 24 free'd
==32242==    at 0x4A04D72: free (vg_replace_malloc.c:325)
==32242==    by 0x400609: free_adj_list (main.c:8)
==32242==    by 0x40085C: main (main.c:69)
==32242==
==32242== Invalid read of size 8
==32242==    at 0x40060E: free_adj_list (main.c:9)
==32242==    by 0x400874: main (main.c:71)
==32242==  Address 0x4c1d290 is 16 bytes inside a block of size 24 free'd
==32242==    at 0x4A04D72: free (vg_replace_malloc.c:325)
==32242==    by 0x400609: free_adj_list (main.c:8)
==32242==    by 0x400874: main (main.c:71)
==32242==
==32242==
==32242== HEAP SUMMARY:
==32242==     in use at exit: 0 bytes in 0 blocks
==32242==   total heap usage: 7 allocs, 7 frees, 168 bytes allocated
==32242==
==32242== All heap blocks were freed -- no leaks are possible
==32242==
==32242== For counts of detected and suppressed errors, rerun with: -v
==32242== ERROR SUMMARY: 4 errors from 3 contexts (suppressed: 6 from 6)

Note that there aren't any leaks ("no leaks are possible"). There are, however, 4 errors — one for each of the adj_vertex_t structures we freed. Turns out that line 9 in main.c (the line where Valgrind reports an "Invalid read") is the following one:

adjv = adjv->next;

We're trying to access a pointer in a region of memory that was already freed! Valgrind also tells us where it was freed — at line 8 in main.c (which happens to be the previous line of code). What you might find interesting is that the program doesn't crash — it's certainly not good practice to write code like this, though.

Here's a version of free_adj_list that uses a temporary pointer to fix the problem (make sure you understand how it works):

void free_adj_list (vertex_t *v) {
    adj_vertex_t *adjv, *tmp; 
    adjv = v->adj_list;
    while (adjv != NULL) {
        tmp = adjv->next; 
        free(adjv);
        adjv = tmp;
    }
}

Recompiling and running Valgrind again gives us the following report:

==32606== Memcheck, a memory error detector
==32606== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==32606== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==32606== Command: ./graph
==32606== 
Adjacency list:
  A: B(10) 
  B: A(10) C(5) 
  C: B(5) 
==32606== 
==32606== HEAP SUMMARY:
==32606==     in use at exit: 0 bytes in 0 blocks
==32606==   total heap usage: 7 allocs, 7 frees, 168 bytes allocated
==32606== 
==32606== All heap blocks were freed -- no leaks are possible
==32606== 
==32606== For counts of detected and suppressed errors, rerun with: -v
==32606== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 6)

Finally — 0 errors and no memory leaks.

Now go forth and Valgrind away your memory management woes!