CMSC 15400 - GDB lab - Wednesday, April 11
In this lab, you will complete a tutorial on the GDB debugger.
Then you will complete an exercise where you will debug a simple linked list.
Email the results of this exercise to the TA; details are provided below.
References
As you go through the exercises today, reference the following for help on GDB:
We will also look at the tool objdump in this lab. You can find help on objdump here:
Discussion
- What is a debugger?
- What is GDB? Which languages does GDB support?
- What is a segmentation fault?
- What is objdump?
Tutorial
Follow the steps of this tutorial.
You will use the following two programs:
- A C program that gives an incorrect result: prog1.c
- A C program that ends with a segmentation fault: prog2.c
You are encouraged to discuss GDB commands and the steps of this Tutorial section
with other students.
You can find the C files used in the lab at /home/bomb154/maclabs/gdblab.tar.
You can unpack these files to you current directory with the command:
tar -xvf /home/bomb154/maclabs/gdblab.tar
You do not have to sumbit your work from this Tutorial section of the lab.
Compiling for GDB: The -g flag
In order to debug a C program, you must compile with the '-g' option.
This option generates debugging information,
such as datatype and line number information, and stores this in the executable.
- Compile prog1.c and prog2.c with debugging information on:
% gcc -g prog1.c -o prog1
% gcc -g prog2.c -o prog2
- Execute each of these programs (without GDB), and observe the results.
Starting GDB
After you have compiled a C source file with -g, you can run this program with GDB.
Use the command gdb <executable> at the command line.
- Start debugging prog1. The GDB prompt should appear:
% gdb prog1
GNU gdb 6.1-debian
...
(gdb)
-
Note that at any time, you can exit GDB with the command quit.
Running a program in GDB:
run
You can execute a program within GDB with the command run
- Execute prog1. The program should run normally.
(gdb) run
Starting program: /home/evtimov/cs154/gdblab/prog1
Counting occurrences of 's' in string Jackson, Mississippi
Count: 20
run args
sets the arguments of your program (i.e., arguments the main function).
Of course, just running the program isn't enough to find our error.
We now look at some of the GDB commands used to examine the program as it executes.
Setting a breakpoint:
break
You can set a breakpoint so that GDB will pause execution at a certain line in the source code.
Use the command break, or the shortcut b, as follows:
break <function>
break <linenumber>
break <filename:function>
break <filename:linenumber>
You can also set a breakpoint at the memory address of an instruction:
break *<address>
You can display all active breakpoints with the command info break.
To delete a breakpoint, use one of the following:
delete <breakpoint number>
where number is obtained from info break, or
clear <...>
similar to break <...> above.
-
Set a breakpoint in the first line of the main function in prog1 and execute this program:
(gdb) break main
Breakpoint 1 at 0x80483b4: file prog1.c, line 7.
(gdb) run
Starting program: /home/evtimov/cs154/gdblab/prog1
Breakpoint 1, main () at prog1.c:7
7 char str[] = "Jackson, Mississippi";
(gdb)
You can add a breakpoint at any point before or after you have called run.
We can use the command next, step, and continue to examine a program as it executes.
next executes one line of source code in the current function.
step is similar to next, but will step inside a function that is called on the current line.
next N and step N increment N lines, rather than just one line.
The list command displays source code around your most recent listing.
list <number> displays code around a particular line.
At any time, you can call continue to proceed automatically
to the next breakpoint or until the program terminates.
- Execute prog1 line-by-line with the next and step
until you are within the function char_cnt.
Note that you need to use step when you reach the call to char_cnt in main:
int cnt = char_cnt('s',str);
- Call list to display the char_cnt function.
- Call continue so that the program terminates.
Displaying data:
print
and
watch
You can use the print command, or its shortcut p,
to display the value of a variable in the current function.
print is very flexible; you can dereference pointers, display addresses of variables, and index into arrays with the
*, &, and [] operators.
A watchpoint
is a breakpoint that stops execution whenever the value of a variable
or expression changes and displays the old and new values of that
expression.
You can set a watchpoint with the command watch <expr>
- Start prog1 from the beginning (call run).
- Step until you reach the char_cnt function.
Or, you can also set a breakpoint at the char_cnt function.
- Use the print command to display the values of str,*str, and str[0].
- Set a watchpoint on the local variable cnt, and continue execution until the program terminates.
- Do you see why this char_cnt does not give the correct result?
Note that you can use the print command to examine the members of a struct:
(gdb) print *mylist
$2 = {first = 0x0, last = 0x0}
Examining the call stack
The
backtrace command can be called at any time in GDB, and shows which function is
associated with the current point of execution.
You can call backtrace
to see how the function got to where it is, that is, through what function calls. backtrace will display the current call stack, with one line per frame, where each frame effectively corresponds to a function call.
The commands
up and down
move "up" and "down" the call stack, that is, they will reset the current frame that is visible to you in GDB.
The commands where and info stack are equivalent to backtrace.
With no arguments,
frame
displays information about the current stack frame.
You will find that these commands are valuable for debugging segmentation faults, such as the one in prog2.c
- Note that in this subsection, we use prog2.c instead of prog1.c
- Compile the program prog2.c so that debugging information is included.
- Run this program from the command line (without GDB).
- Now, run this program with GDB. You do not have to set a breakpoint in main, just call run when GDB starts:
% gdb prog2
Starting program: ...
...
(gdb) run
- Which line does this segmentation fault occur on?
- Now call
up to examine local variables in the main function
at the point where char_cnt was called.
(You may want to call frame and list next.)
- What are the values of the arguments to main?
- Do you see why this segmentation fault occured?
You can use the command x to examine the contents of a
memory address.
For example, to display instructions at address 0x8048422, you can call:
(gdb) x/4i 0x8048422
0x8048422 : mov $0x0,%eax
0x8048427 : leave
0x8048428 : ret
0x8048429 : push %ebp
Here, /4i is an optional parameter, indicating that GDB should display 4 lines of machine instruction.
Other formats are available,
such as x/d for decimal, x/x for hex, and x/s for strings.
- Use the '&' operator to find the address of variable mystr in the function main.
- Now, use the x/s operator and format command to diplay the contents of this address as a null-terminated string.
You can call the command print/x $reg to display the value of
register $reg. Again, /x indicates that GDB should display the output in hex format.
To display the contents of every registers, use the command info registers.
CSPP51081: You can probably get by with skipping the next few topics. Don't forget the Exercise at the end. -Sonjia
Displaying machine instructions:
disas
The command disas, or disas <function>, displays machine instructions for a given function (default is current frame).
To find the instruction address that corresponds to a line of source code, use info <line>.
To go in the reverse direction info *<address>
- Find the source code line where the char_cnt is called in main.
- What is the range of instructions associated with this line of source code? Display these instructions with disas.
Stepping through and into a function, by instruction:
nexti, and stepi
We saw above how we can use next and step to step through
our program, line by line. The commands nexti and stepi are similar, but execute one instruction rather than one line our source code.
These commands can be useful for lines that contain several operations or function calls.
- Find the line in char_cnt where iter is incremented:
iter++;
- Using disas and info line, review the
machine instructions for this line of source code. What register is
used, and what value is stored in this register?
- Set a breakpoint at in char_cnt, where iter is incremented
- Execute this line by instruction using the command stepi. Before you start, and after each instruction, display the value of this register and the variable iter. (You can also set a watchpoint to display these values)
Using
objdump
to examine binary files.
In addition to GDB, you can also use the program objdump to display a range of information about a binary file.
objdump -d disassembles a file (similar to disas in gdb).
objdump -t displays symbol table information
- Open a new shell, which you will use to run disas from the command line.
- Disassemble the executable prog1. What information do you see, that you did not get from GDB's disas command?
- Use objdump and grep to find symbol table entry for the function char_cnt.
Exercise
You will now debug a C program that implements a simple linked list.
Note that there are two driver files included in the tar file for this lab: driver1.c and driver2.c.
Each uses the file linkedlist.c, a dynamically-allocated linked list with two bugs. driver1.c will find one bug, and driver2.c will find the other.
- Select either driver1.c or driver2.c and use that driver file to complete this exercise.
- Compile linkedlist.c and driver1.c (or driver2.c):
gcc -g -Wall driver1.c linkedlist.c -o listtest
- Run listtest, and notice that this program ends with a segmentation fault.
- Using GDB, find the bug in this linked list, and fix the code so that the test passes:
% ./listtest
Test OK.
(If you get stuck, you can ask the TA for a hint).
You are encouraged to discuss GDB with other students.
However, you should try to find the bug on your own,
and let others using the same driver to find it on their own as well.
Submitting this lab
Email your revised version of linkedlist.c
as an attachment to Josh Grochow (joshuag@cs).
In the body of your email briefly answer the following questions:
- Did you use driver1.c or driver2.c?
- Which line of source code resulted in a segmentation fault?
You may include the output of a GDB command.
- Which instruction resulted in this segmentation fault? Do not
submit the memory address, instead display this instruction as assembly
code. You may include the output of a GDB command.
-
Where does the error occur in linkedlist.c? That is, which function is written incorrectly,
and why is it incorrect?