Quick GDB tutorial

Introduction

Generally, a debugger is a tool which allows programmers to conveniently test and debug programs. A debugger allows one
  1. to keep track of the executation of the program (e.g. how value of certain variable changes during execution) and
  2. 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 :
  1. step : executes the current statement and stops on the next statement to be executed.
  2. 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