C Debugging
Personal Software Engineering – SE350

 

Summary

This activity will provide experience using the debugging tool gdb. Perform all of the in-class exercises with your partner.

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; absolutely no mystery in that, and hence all problems are solvable. There are many ways people go about debugging, from printing out messages to the screen, using a debugger, or just thinking about what the program is doing and making an educated guess as to what the problem is. 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 in hopes that it will start to work again. The key to successful debugging is to develop a methodical approach to locating a defect, understanding what the cause of the defect is – then applying a fix. Before applying a fix, a new unit test should be added to the test suite that recreates the defect. 

Sources: Debugging Under Unix: gdb Tutorial
              Learn the essentials of debugging
             gdb Tutorial use in this activity

Exercises

With your partner, walk through the following script. The numbered lines explain what is happening. The unnumbered lines are what you should type.

 

Download the C source file prime.c and the makefile. 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 complier 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!

This activity has no submittable information. Instead, as your team finishes the experiment, call over your instructor and show him your work; this is the basis for assigning credit.