Quick GDB tutorial
Introduction
Generally, a debugger is a tool which allows programmers to
conveniently test and debug programs. A debugger allows one
- to keep track of the executation of the program (e.g. how value
of certain variable changes during execution) and
- to see what the program was doing when it crashed (e.g. SEG
FAULT).
GDB is a popular debugger for debugging programs written in high-level
languages like C. GDB helps you catch bugs in your program.
Debugging
with GDB
Compiling
your code with GDB
First, you need to compile your C program with GDB. Under linux, it is
done with -g flag.
For example, let us assume that you wrote a program foo.c and you
want to debug with GDB. Compile foo.c with the
following command :
> gcc -g foo.c -o foo
Why do we need to do this? With -g flag, the compiler adds extra
information in the object file which helps debugging. For example, it
adds line number of source with the instructions.
Running
your code with GDB
After successful compilation, you can run your code with GDB and track
bugs. Start running your program with GDB :
>/usr/bin/gdb <progname>
The above command starts the GDB environment.
Next, you can run executable under GDB using the run command :
(gdb)
run <arguments>
You can get out of GDB environment with quit command.
(gdb) q
If your program crashed and dumped a core, you can debug the
core using the following command
>gdb <progname> <core>
How
can you use GDB to track bugs?
Here I will show you a couple of simple examples. GDB is very useful
for capturing bugs with memory issues ( bugs which give you
SEG FAULT most of the time !!). Using GDB, one can find out what the
program was doing when it crashed.
First compile the code foo.c :
>gcc -g foo.c -o foo
This program implements a simple linked-list with employee name and age
for a company. Start running this program and insert employee records:
>./foo
When you try to insert a new employee record, you will get a SEG FAULT.
Now, I will start foo with GDB
>/usr/bin/gdb foo
Next run the program :
(gdb) run
If you try to insert a new employee record, you will find a SEG FAULT.
How would you solve this? You wanna know what the program was doing
when it crashed. The backtrace command shows the stack trace (i.e., the
chain of function calls that leads to the latest state of the program).
Run backtrace as
(gdb) bt
#0 0x00000000004005ee in
insert_to_list (e_age=35, e_name=0x7fbfffe970 "Greg") at foo.c:28
#1 0x0000000000400707 in
add_employee_info () at foo.c:72
#2 0x000000000040081b in main
(argc=1, argv=0x7fbfffeab8) at foo.c:117
(gdb)
It shows that from main the
function add_employee_info is
called followed by insert_to_list .
You got a SEG FAULT at line number 28 of foo.c and inside the function insert_to_list .
Tracking
logical errors
GDB is also very convenient for tracking logical errors. GDB allows you
to create breakpoints anywhere inside your source using the following
command:
> b
<progname>:<linenum>
One can also set a breakpoint at the start of the main function
> b main
Afterwards, when you run your program under GDB, the execution will
stop at the breakpoint(s) specfied. You can check the state of
execution to find possible source of logical errors. Let us take
another example to see how it can be done. The code foo1.c
is another version foo.c code. Let us assume that you want to find out
if there are any logical error in the function remove_employee_info(). Compile
and start the code with gdb as before. Create a breakpoint in the
beginning of remove_employee_info()
(gdb) b foo1.c : 79
Breakpoint 1 at 0x40087b: file
foo1.c, line 79.
(gdb)
Next, run it. When you choose to remove an employee info, it
will stop at the breakpoint.
What do you want? (Insert(1),
Delete(2), List(5)) : 2
Breakpoint 1, remove_employee_info ()
at foo1.c:79
79
printf("\n Employee name : ");
(gdb)
Now you can execute the program step-by-step and monitor the
changes in different variables. There are two commands for step-by-step
execution :
- step : executes the
current statement and stops on the next statement to be executed.
- next : works similar to
step. However, it the current statement is a function call, it will
execute the function call as a whole and stop on the next statement.
These commands will allow you to execute your code step-by-step. While
executing instructions step-by-step, you can check values in local
variables for logical errors. Lets take an example. Lets assume that in
foo1.c, you want to know what data you have on the head node. The print command allows you to do that.
(gdb) p head->name
$4 = "Jack", '\0' <repeats 45 times>
(gdb) p head->age
$5 = 28
(gdb)
Thus, GDB allows you to track down the cause of a crash during
execution or track down which variable makes it go wrong. Often, you
might wanna run the code between two breakpoints as a whole. The
command continue allows you to
do that.
Further
readings
The tutorial above should give you an idea on the capabilities of GDB.
You can use command-line help inside GDB to know more about how the
commands work.
(gdb) help <command>
There are many documents on GDB available online. The original GDB
documentation is a good source to find out all the features of
GDB. GDB also provides some interesting features when used with
Emacs. You can find more about GDB under Emacs here.
Santanu Chatterjee,
Dept. of Computer Science and Engineering,
University of Notre Dame
Date : 1/19/2009