valgrind overview

Valgrind is a command line tool used to debug memory errors and to detect memory leaks.

Memory errors include referencing an index outside the bounds of an array, referencing an uninitialized variable, or dereferencing a null pointer. Many such memory errors might manifest as a segmentation fault, but many may not.

A memory leak occurs when heap allocated memory is not deallocated when the space is no longer needed.

Valgrind can help detect these issues and might even tell you where in your program these errors occur.

Beginning with Homework 1, you must run valgrind on your solution before submitting your work. Your programs will be tested with valgrindand should not produce any memory leaks or errors when tested. (There is one exception that will be discussed below.)

Running valgrind

In general, you can use the following to run valgrind:
valgrind [optional flags] <path to your program>
  

If the program you are testing is named a.out and it is located in the current directory, you would run valgrind thus:

  
valgrind ./a.out
  

The output might look something like this:

==6725== Memcheck, a memory error detector
==6725== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==6725== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==6725== Command: ./a.out
==6725==
<Your Program's Output> Hello World!
==6725==
==6725== HEAP SUMMARY:
==6725== in use at exit: 72,704 bytes in 1 blocks
==6725== total heap usage: 1 allocs, 0 frees, 72,704 bytes allocated
==6725==
==6725== LEAK SUMMARY:
==6725== definitely lost: 0 bytes in 0 blocks
==6725== indirectly lost: 0 bytes in 0 blocks
==6725== possibly lost: 0 bytes in 0 blocks
==6725== still reachable: 72,704 bytes in 1 blocks
==6725== suppressed: 0 bytes in 0 blocks
==6725== Rerun with --leak-check=full to see details of leaked memory
==6725==
==6725== For counts of detected and suppressed errors, rerun with: -v
==6725== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 3 from 3)
  

Interpreting valgrind's Output

There are 3 main parts to valgrind’s output:
  1. The Heap Summary tells you the number of bytes in use when the program exits, the number of memory allocations (anytime the new operator is used), the number of frees (anytime the delete operator is used), and the total number of bytes allocated.
  2. The Leak Summary tells you what memory your program might have leaked. Anything lost means that some heap allocated memory can no longer be reached by your program. In general, you do not want to lose track of any memory.
  3. The Error Summary tells you how many errors occurred during the execution of your program. Read more about what a memory error is below.

The One Exception to Memory Leaks

In the above program’s valgrind report, everything seems fine except those 72,704 bytes that were not deallocated. This specific memory leak occurs in our implementations of the C++ standard libraries, in this case the implementation of iostream that was used. This is difficult/impossible to avoid and thus you will not be penalized for having these 72,704 bytes still reachable on the Halligan server. To be clear, it is okay to have 72,704 bytes “still reachable” in the leak summary and exactly 1 more alloc than free in the heap summary. Anything else will result in point deductions on homeworks.

What a Memory Leak Looks Like

Let's see what happens when we allocate space for an array of 5 integers on the heap but forget to delete it:
int *arr_ptr = new int[5];  // this storage is never deleted
  

valgrind shows the following:

==13147== Memcheck, a memory error detector
==13147== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==13147== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==13147== Command: ./a.out
==13147==
==13147==
==13147== HEAP SUMMARY:
==13147== in use at exit: 72,724 bytes in 2 blocks
==13147== total heap usage: 2 allocs, 0 frees, 72,724 bytes allocated
==13147==
==13147== LEAK SUMMARY:
==13147== definitely lost: 20 bytes in 1 blocks
==13147== indirectly lost: 0 bytes in 0 blocks
==13147== possibly lost: 0 bytes in 0 blocks
==13147== still reachable: 72,704 bytes in 1 blocks
==13147== suppressed: 0 bytes in 0 blocks
==13147== Rerun with --leak-check=full to see details of leaked memory
==13147==
==13147== For counts of detected and suppressed errors, rerun with: -v
==13147== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 3 from 3)
  

In the Heap Summary, we make 1 allocation for the array on the heap (the other 1 allocation is from the C++ libraries), but we make 0 frees (deletes). These should always be equal to each other; for every new there should be a corresponding delete! In the Leak Summary, valgrind shows us we definitely leaked 20 bytes of memory, which is from our dynamically allocated array (5 integers of 4 bytes each).

valgrind also suggests we rerun with the flag: --leak-check=full

The command would look like this: valgrind --leak-check=full ./a.out

Running this command with the flag produces this output:

==13221== Memcheck, a memory error detector
==13221== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==13221== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==13221== Command: ./a.out
==13221==
==13221==
==13221== HEAP SUMMARY:
==13221== in use at exit: 72,724 bytes in 2 blocks
==13221== total heap usage: 2 allocs, 0 frees, 72,724 bytes allocated
==13221==
==13221== 20 bytes in 1 blocks are definitely lost in loss record 1 of 2
==13221== at 0x4C2A73D: operator new[](unsigned long) (vg_replace_malloc.c:422)
==13221== by 0x4008F3: main (in /h/ssong03/comp/test/a.out)
==13221==
==13221== LEAK SUMMARY:
==13221== definitely lost: 20 bytes in 1 blocks
==13221== indirectly lost: 0 bytes in 0 blocks
==13221== possibly lost: 0 bytes in 0 blocks
==13221== still reachable: 72,704 bytes in 1 blocks
==13221== suppressed: 0 bytes in 0 blocks
==13221== Reachable blocks (those to which a pointer was found) are not shown.
==13221== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==13221==
==13221== For counts of detected and suppressed errors, rerun with: -v
==13221== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 3 from 3)
  

Using different flags when running valgrind can give you more precise information on the nature of the leak or error and maybe even where the error occurred. In this case, valgrind gives us a message that tells us the memory lost was allocated in main. When debugging, the error messages can give you a general idea of where to start looking for your leak.

What a Memory Error Looks Like

Now let's look at an example of a memory error. Say you realize you never freed that dynamic array so you add the appropriate delete statement. But instead of typing it out, you copy paste the code from online and accidentally hit paste twice. (Copy and paste is evil!)

int *arr_ptr = new int[5];
delete [] arr_ptr;
delete [] arr_ptr;
  

this attempts to free memory twice, and error known as a double free. valgrind reports the error:

==14801== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==14801== Command: ./a.out
==14801==
==14801== Invalid free() / delete / delete[] / realloc()
==14801== at 0x4C2B574: operator delete[](void*) (vg_replace_malloc.c:620)
==14801== by 0x4008DA: main (in /h/ssong03/comp/test/a.out)
==14801== Address 0x5a9ec80 is 0 bytes inside a block of size 20 free'd
==14801== at 0x4C2B574: operator delete[](void*) (vg_replace_malloc.c:620)
==14801== by 0x4008BC: main (in /h/ssong03/comp/test/a.out)
==14801== Block was alloc'd at
==14801== at 0x4C2A73D: operator new[](unsigned long) (vg_replace_malloc.c:422)
==14801== by 0x40089A: main (in /h/ssong03/comp/test/a.out)
==14801==
==14801==
==14801== HEAP SUMMARY:
==14801== in use at exit: 72,704 bytes in 1 blocks
==14801== total heap usage: 2 allocs, 2 frees, 72,724 bytes allocated
==14801==
==14801== LEAK SUMMARY:
==14801== definitely lost: 0 bytes in 0 blocks
==14801== indirectly lost: 0 bytes in 0 blocks
==14801== possibly lost: 0 bytes in 0 blocks
==14801== still reachable: 72,704 bytes in 1 blocks
==14801== suppressed: 0 bytes in 0 blocks
==14801== Rerun with --leak-check=full to see details of leaked memory
==14801==
==14801== For counts of detected and suppressed errors, rerun with: -v
==14801== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 3 from 3)
  

Looking at Heap and Leak Summary, we no longer have any memory leaks! But now we have another error that says something about an invalid free? This is telling us that we freed the same piece of memory twice, a double free. valgrind again shows us that the error occurred in main so we ought to try looking there for the error. For other typical valgrind errors, we recommend that you read http://cs.ecs.baylor.edu/~donahoo/tools/valgrind/messages.html

Understanding valgrind's Error Messages

Another example of a memory error arises when some conditional logic (an if statement or loop test) uses uninitialized memory:

==32216== Conditional jump or move depends on uninitialised value(s)
==32216== at 0x4027E5: PirateList::pirateIndex(int) (PirateList.cpp:151)
==32216== by 0x402A21: PirateList::addFriend(int, int) (PirateList.cpp:115)
==32216== by 0x401D90: Hookbook::addFriend(int, int)
==32216== by 0x401A03: run_final_lab_test() (test_hookbook.cpp:46)
==32216== by 0x401253: main (test_hookbook.cpp:27)
  

When reading valgrind error messages, we start from the bottom and work upwards. The flow of the program looks like this:

  1. In the function main, at line 27 in the file test_hookbook.cpp
  2. In the funciton run_final_lab_test, at line 46 in the file test_hookbook.cpp
  3. Function addFriend in the Hookbook class
  4. In the function addFriend of the PirateList at line 115 in the file PirateList.cpp
  5. In the function pirateIndex in the PirateList at line 151 in the file PirateList.cpp

Line 151 of PirateList.cpp is:

        if (priateArray[i].memberID == memID)
  

The above valgrind message is telling us that we are accessing an uninitialize value in that line of code. Either pirateArray[i] itself, or the memberID field, or the local variable memID is uninitialized. You can now do some debugging to figure out which one is causing the problem. valgrind is a very helpful tool, but you still have to use other methods of debugging alongside it.

The terminology valgrind is using comes from the fact that it doesn't know what the source code doing (if, while, for, whatever). It is seeing the machine language code, which is either a conditional jump instruction or a conditional move instruction. What you need to know is that these are coming from conditionals or loop tests in your code. The line number will usually give you sufficient detail.

Summary

In short, valgrind is a powerful tool at your disposal to help you find errors. Learn how to use it and it will help you become a better programmer. Just a word of caution, though: the fact that you run valgrind and get no errors is not proof that your program is bug free. It is a very good step in that direction, though.