Debugging with gdb
Starting gdb
Recall from today's reading that you may run GDB from the terminal or within Emacs. While we hope you might settle into using GDB from within Emacs (one of its great strengths as a development environment), here you will make sure you can do both.
-
The program
array-bug.ccontains two simple functions for doing array manipulations.- Copy the program to your account.
-
Read over the program (particularly the function documentation
and the
mainprocedure) to be sure you understand what it is supposed to do, but do not make any changes to it. -
Compile and run the program to confirm its (mis)behavior. For
this exercise, we'll use the system default compiler:
cc -g -o array-bug array-bug.c
-
Use the terminal to start GDB on your program and then run your
program within GDB. (You will give one terminal command, and one
GDB command within terminal.) You may then exit GDB with
the
quitcommand. - Start GDB within Emacs and then run your program within the GDB buffer window. Leave GDB running after the program completes.
Setting break points
You should have noticed that your program does not yield the "expected" results. To isolate the bug (in case you haven't spotted it already), we will want to probe some values while the program is running. (That way we don't have to decide before we compile which values may be interesting to look at.) Thus we will practice setting break points in the code to get a better handle on the action.
-
Set a breakpoint at the line just after the definition of the
variable
expected, (i.e., the line with the call tocompute_average, then run your program.-
Use GDB to inspect the value of
expectedbefore thecompute_averageprocedure runs. Does it have the value intended? (You may also want to verify the value oflength.) -
Use GDB to "step over" the call to
compute_average(that is, use thenextcommand). -
Inspect the value of
expected(i.e., just before the call to thehalveprocedure). Did it change? -
Inspect the value of the
meanvariable after callingcompute_average. Is it correct? -
Step over the call to
halveand inspect the value ofexpectedonce again. Did it change? -
Corruptions are often caused by mismanaging
references. Determine the memory addresses of the four variables
declared in
main. -
Use
killto release the current execution of the program.
-
Use GDB to inspect the value of
Setting watchpoints
By this point you should have identified two problematic behaviors in
the program: (1) mean is computed incorrectly
within compute_average, and (2) expected is
somehow corrupted within halve. We will now use
additional GDB tools to help isolate the cause(s).
-
Run the program again. While stopped at the first breakpoint
(within
main) set a write watchpoint for theexpectedvariable.-
continueexecuting the program. That is, don't use thestepornextcommands; instead, GDB should pause the program whereverexpectedchanges value. -
Where does the corruption occur? What are the old and new values?
While GDB shows you the code context, issue a
backtracerequest to see the execution context as well. (This particular stackframe is very short, but when a function has many possible entry points, understanding the execution context is a critical step in the debugging process.) -
Determine the values and addresses of the several local
variables available where the program is currently paused:
i,length, andvalues, and evenvalues[i]. Compare these addresses to those you identified earlier. - Construct a hypothesis about how (not why) the variable gets corrupted.
-
Stepping the source
Chances are very good now you've not only determined the nature of the bug, but also its source. Bear with us as we walk through one more important aspect of using the debugger, stepping through code line by line to monitor behavior.
-
Set a breakpoint within the
halveprocedure. (Recall you may simply give a function name as an argument tobreak—a rather pleasing command in this instance.) Temporarilydisablethe first breakpoint you had set earlier and then re-run the program.- Where does the program actually break? Is it where you expected? Why do you suppose it stops there?
-
Inspect the value of the relevant variables (all those listed
in Exercise 4c) in a single line using a printf statement. Here
is a starting example:
printf "i=\t%p\t=\t%d\n",&i,i
Are any of these values surprising? Construct a hypothesis to explain them. -
Execute the
stepcommand once, note the output produced, and then re-issue (via command history) theprintfcommand to help identify the side-effect and execution context. -
Repeat the previous instruction
(
step,printf, inspect) several times as you traverse the loop.
If you've been paying attention to the values and the addresses, by now you should be able to hypothesize the precise cause for the bug(s), which you may now attempt to fix. Be sure to confirm your fixes. If they do not fix the problem, restore your program to its original state and continue to use the debugger to explore the behavior while also studying the source code carefully.
Optional: Über-advanced GDB
For those with extra time, explore one or more of the following sections of the GDB Manual, identify some more advanced command(s) we have not yet discussed or used in the lab, and try them out on this or another program.
