C Debugging Introduction with Rewrite

Summary

This activity will provide experience using the debugging tool gdb. In addition you will rewrite the prime.c code to conform to the course C coding standards.

The basic steps in debugging are:

Debugging software is challenging, but keep in mind that all bugs are caused by computers doing exactly what they are told. Therefore, all problems are solvable.

There are many approaches to debugging including printing out messages to the screen, using a debugger, or just thinking about what the program is doing and making an educated guess. Without a process to follow, resolving problems can seem impossible. Most inexperienced programmers find themselves in precisely that situation when confronted with a bug.

The most troubling thing is that many students -- and even commercial coders -- seem to freeze when unexpected problems arise. Some literally try making random changes or, worse, try to run the program a few more times hoping that it will start to work again. The key to successful debugging is to develop a methodical approach to locating a defect, understanding the cause, and then applying a fix. Before applying a fix, a new unit test should be added to the test suite that recreates the defect.

Here is a reference card of gdb commands: gdb Quick Reference

Part 1

Walk through the following script. The numbered lines explain what is happening. The unnumbered lines are what you should type.

Create a directory in your git repository: c-debug
Download the C source file prime.c and the Makefile into the c-debug folder.
Use the wget utility to get the file into your hamilton account.
Review the comments in prime.c and examine the Makefile.
Note the –g compiler option. You must compile your code with this option in order to run in debug mode with gdb.

  1. Run the Makefile and note any compiler errors or warnings

make

  1. Run prime in debug mode by invoking gdb:

gdb prime

  1. Check out the help system:

h

  1. Run the program, providing an upper bound when asked:

r

20

  1. The program halted with an invalid memory access. Use Backtrace to find the point of error:

bt

Backtrace will typically identify the source line where the error occurred. In the event backtrace does not identify the error, look for other clues - starting with suspicious compiler warnings, as is the case in prime.c at line 44.

 

  1. In the call to scanf the second argument should be an address of an integer, not an integer value. Quit to fix this bug, missing "&" before "UpperBound":

q

y

  1. Fix the first bug and recompile:

<edit your code>

make

 

  1. Now invoke gdb again:

gdb prime

  1. Run again:

r

20

  1. This time no prime numbers were found. Maybe there is something wrong with the CheckPrime function. We can set a breakpoint there to see what is going on:

b CheckPrime

 

  1. Now we can run the program again, and it should stop at the beginning of CheckPrime:

r

20

  1. We can see from the Breakpoint line that we are testing 3 for primeness. By stepping through the function we might see what is wrong. We will use the Next command 3 times:

n

n

n

  1. Wait a minute! We are about to set Prime[3] to 0, indicating that it is not prime. Why did the second if test pass? Print the test:

p (K % J == 0)

  1. The answer is 1, which means "true". Well, K is 3, what is J:

p J

Aha! We are starting our tests of divisibility with 1. Every integer is divisible by 1, so none will be considered prime. We need to change the initialization of J to 2 in line 24. Quit the debugger:

q

y

  1. Now change the code and recompile it:

<edit your code>

make

  1. Now start up the debugger again:

gdb prime

  1. Run it again:

r

20

  1. Another invalid memory access, but now at line 26. List the code to see what is going on:

l 26

  1. J must be out of bounds for array Prime. Print the value of J:

p J

  1. J is clearly too big. We need to see all of the CheckPrime function to figure out what is going on, so use the List function and hit ENTER twice to continue listing:

l CheckPrime

<ENTER>

<ENTER>

  1. The comment says that we should only test values of J that are less than or equal to the square root of K. But, the loop never tests for this, it just keeps going indefinitely. We need to change the while condition to (J * J <= K). Quit the debugger:

q

y

  1. Change the code and recompile it:

<edit our code>

make

  1. Now start up the debugger again:

gdb prime

  1. Run it again:

r

20

  1. Success!

For Part 1 push your corrected prime.c file to your remote repository. Complete this activity by the end of class today. 

Part 2

The objective is to create a rewritten version of prime.c that conforms to our programming standards.

Grading