CS 270
CS 270 Documentation

Recitation R7: Debug and Valgrind


What to turn in

You will get credit for this recitation by completing the Recitation 7 Quiz in Canvas

About The Recitation

This recitation will be helpful for assignment P4A and P4B. It has two objectives.
  1. You will learn about a debugging framework you can use to turn on and off debugging messages in your code without having to make source changes and recompile to eliminate the messages.
  2. You will learn to use Valgrind to find dynamic memory use issues.
  3. You can use the remaing time to work on P4A.

Debugging

We encourage you to use Fritz Sieker's debugging framework. He has provided a function called debug(). You don't have to include any extra header files. You can use this function like a printf. The difference is that the debug() function will only print output to the console if you run your program with the -debug option as the first argument. This way, if you don't want to see the debugging output, you don't have to remove the calls to debug(). You can simply remove the -debug option from the command line. We have provided you with an example of how to call the debug() function in the symbol_init function. To see the output, run the program as follows:

./testSymbol -debug 20
DEBUG symbol.c[59] symbol_init() symbol_init was called with table_size = 20

You don't have to remove the debug() calls before submitting your program because we will run it without the -debug option. However, if you choose not to use this framework, make sure to remove all statements that print something before submitting. In other words, if I were to run your program without the -debug option, I should not see any extra output. Otherwise, the autograder won't be able to match your output to the expected output.


Using Valgrind

Valgrind is a popular program that allows you to track memory errors (such as segmentation faults) and memory leaks. To run your program through Valgrind and check for memory leaks, use the following command:

valgrind --leak-check=yes ./testSymbol

Here is an example. I ran my program and got the following:

./testSymbol
init 20
add askbjs 22
Segmentation fault (core dumped)

A segmentation fault usually indicates that you tried to access memory you weren't supposed to access. I ran my program using Valgrind and got the following (only part of the output is shown):

valgrind --leak-check=yes ./testSymbol
==28069== Memcheck, a memory error detector
==28069== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==28069== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==28069== Command: ./testSymbol
==28069==
init 20
add askbjs 22
==28069== Invalid read of size 8
==28069==    at 0x400C93: symbol_search (symbol.c:106)
==28069==    by 0x400D0D: symbol_add (symbol.c:118)
==28069==    by 0x401165: main (testSymbol.c:154)
==28069==  Address 0x51fa0b8 is 16 bytes after a block of size 8 alloc'd
==28069==    at 0x4C2DA60: calloc (vg_replace_malloc.c:711)
==28069==    by 0x400A81: symbol_init (symbol.c:60)
==28069==    by 0x4010C7: main (testSymbol.c:138)
==28069== 
==28069== Invalid read of size 8
==28069==    at 0x400B31: symbol_add_unique (symbol.c:75)
==28069==    by 0x400D28: symbol_add (symbol.c:119)
==28069==    by 0x401165: main (testSymbol.c:154)
==28069==  Address 0x51fa0b8 is 16 bytes after a block of size 8 alloc'd
==28069==    at 0x4C2DA60: calloc (vg_replace_malloc.c:711)
==28069==    by 0x400A81: symbol_init (symbol.c:60)
==28069==    by 0x4010C7: main (testSymbol.c:138)
==28069== 
==28069== Invalid write of size 8
==28069==    at 0x400B54: symbol_add_unique (symbol.c:78)
==28069==    by 0x400D28: symbol_add (symbol.c:119)
==28069==    by 0x401165: main (testSymbol.c:154)
==28069==  Address 0x51fa0b8 is 16 bytes after a block of size 8 alloc'd
==28069==    at 0x4C2DA60: calloc (vg_replace_malloc.c:711)
==28069==    by 0x400A81: symbol_init (symbol.c:60)
==28069==    by 0x4010C7: main (testSymbol.c:138)
==28069== 
OK

(You can quit from Valgrind prematurely using Ctrl-C)

It gave me a bunch of invalid reads and writes. I started by looking into the first invalid read. It shows me the stack trace at the point where the invalid read occurred: main called symbol_add and symbol_add called symbol_search. So, the invalid read occurred on line 106 of my symbol.c file. It also shows me the cause for the invalid read. It says: "Address 0x51fa0b8 is 16 bytes after a block of size 8 alloc'd." This is basically saying that I previously allocated a block of 8 bytes and I'm trying to access something out of bounds. It even tells me where in my code I allocated that block: line 60 of symbol.c. I went to that line and, sure enough, I realized that I hadn't allocated enough space for my hash table. I corrected the error and re-ran with Valgrind:

valgrind --leak-check=yes ./testSymbol
==28136== Memcheck, a memory error detector
==28136== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==28136== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==28136== Command: ./testSymbol 20
==28136== 
init 20
add askbjs 22
OK
quit
==28136== 
==28136== HEAP SUMMARY:
==28136==     in use at exit: 0 bytes in 0 blocks
==28136==   total heap usage: 6 allocs, 6 frees, 525,535 bytes allocated
==28136== 
==28136== All heap blocks were freed -- no leaks are possible
==28136== 
==28136== For counts of detected and suppressed errors, rerun with: -v
==28136== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

I no longer get memory errors. Also, note that it says "All heap blocks were freed -- no leaks are possible." This means that my symbol_term function freed all dynamically allocated memory correctly. It's important to note that you have to try your program with different test cases. One single run of Valgrind does not guarantee that your program is correct. Also, note that the program must terminate before Valgrind shows you information about memory leaks.

Valgrind will come in handy when you get a segmentation fault or when you want to test your symbol_reset and symbol_term functions.

Here's what a run with memory leaks would look like:

valgrind --leak-check=yes ./testSymbol
==28299== Memcheck, a memory error detector
==28299== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==28299== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==28299== Command: ./testSymbol 20
==28299== 
init 20
add test 1
OK
add test2 2
OK
quit
==28299== 
==28299== HEAP SUMMARY:
==28299==     in use at exit: 11 bytes in 2 blocks
==28299==   total heap usage: 8 allocs, 6 frees, 525,571 bytes allocated
==28299== 
==28299== 11 bytes in 2 blocks are definitely lost in loss record 1 of 1
==28299==    at 0x4C2BBAD: malloc (vg_replace_malloc.c:299)
==28299==    by 0x4EC0079: strdup (in /usr/lib64/libc-2.23.so)
==28299==    by 0x400AFF: symbol_add_unique (symbol.c:72)
==28299==    by 0x400D2B: symbol_add (symbol.c:119)
==28299==    by 0x401158: main (testSymbol.c:154)
==28299== 
==28299== LEAK SUMMARY:
==28299==    definitely lost: 11 bytes in 2 blocks
==28299==    indirectly lost: 0 bytes in 0 blocks
==28299==      possibly lost: 0 bytes in 0 blocks
==28299==    still reachable: 0 bytes in 0 blocks
==28299==         suppressed: 0 bytes in 0 blocks
==28299== 
==28299== For counts of detected and suppressed errors, rerun with: -v
==28299== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

This is telling me that I didn't free something that I allocated in line 72 of my symbol.c.