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:
- Recognize that a bug exists
- Isolate the source of the bug
- Identify the cause of the bug
- Write a test case to reproduce the bug
- Determine a fix for the bug
- Apply the fix and test it
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.
- Run the Makefile
and note any compiler errors or warnings
make
- Run prime in debug
mode by invoking gdb:
gdb
prime
- Check out the help
system:
h
- Run the program,
providing an upper bound when asked:
r
20
- 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.
- 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
- Fix the first bug
and recompile:
<edit your code>
make
- Now invoke gdb again:
gdb
prime
- Run again:
r
20
- 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
- Now we can run the
program again, and it should stop at the beginning of CheckPrime:
r
20
- 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
- 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)
- 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
- Now change the
code and recompile it:
<edit your code>
make
- Now start up the
debugger again:
gdb
prime
- Run it again:
r
20
- Another invalid
memory access, but now at line 26. List the code to see what is going
on:
l 26
- J must be out of
bounds for array Prime. Print the value of J:
p J
- 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>
- 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
- Change the code
and recompile it:
<edit our
code>
make
- Now start up the debugger again:
gdb
prime
- Run it again:
r
20
- 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.
- Start your ActivityJournal.txt.
- Copy prime.c to prime_standard.c.
- Edit Makefile to compile the new version by changing prime.c to prime_standard.c.
- Using our programming standards incrementally update the code while testing it periodically to see that it still compiles and runs.
- You should commit and push your changes as you are doing this -- you may want to go back to a prior version!
- Submit these files named exactly prime_standard.c, ActivityJournal.txt, and the updated Makefile to your repository.
Grading
- 10 points complete submission per instructions
- 20 points Part 1 completion including running program
- 10 points Part 2 ActivityJournal.txt
- 10 points Part 2 Updated Makefile
- 30 points Part 2 coding standards followed
- 20 points Part 2 updated program runs (no credit if no significant changes made)