Prev - Index - Next

A bug's life

Lubarsky's Law of Cybernetic Entomology: There's always one more bug.

An inevitable part of software development is debugging. Many languages offer inbuilt debugging tools, such as Python's debugger, while other languages rely on external debugging tools.

The GNU debugger, GDB, is designed to work with a variety of languages, but is primarily targeted at C and C++ developers. GDB is a command line interface, but it can also be run over serial links for embedded debugging and there are a number of graphical interfaces to GDB, such as DDD, Code Medic and xxgdb:

DDD screenshot xxgdb screenshot

A brief introduction to using GDB

1. Finding the bug

Assume we have a program called cipher that accepts a file and performs simple encryption on the file. The problem is that the program dumps core with a segmentation fault, which simple means that the program is attempting to write into memory that it does not own:

        [~/work/]> ./cipher                  

        CIPHER syntax: cipher file password

        Segmentation fault (core dumped)

Note that we have not supplied a filename or a password. A core file is generated, which will help us track down the problem...

        [~/work/]> ls -l c*
	-rwxrwxr-x   1 graeme   graeme       8469 Mar  2 11:35 cipher
	-rw-rw-r--   1 graeme   graeme       1108 Mar  2 11:35 cipher.c
	-rw-------   1 graeme   graeme     110592 Mar  2 11:35 core

2. Starting GDB

Assuming that cipher was compiled with the -g flag, we can load the executable into GDB:

[~/work/]> gdb cipher
GNU gdb with Linux/x86 hardware watchpoint and FPU support
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...

3. Listing the program

The list command will (by default) list the first 10 lines of the source code for the current function, and subsequent calls to list will display the next 10 lines, and so on:

(gdb) list
16      int
17      main(int argc, char *argv[]) {
19        char fileName[BUFSIZE];
20        char password[BUFSIZE];
21        char buf[BUFSIZE];
22        FILE *f;
24        if (argc != 3) {

4. Running the program

Firstly, let's run the program and ensure that the bug is reproduced:

(gdb) run
Starting program: /home/graeme/work/cipher 

CIPHER syntax: cipher file password

Program received signal SIGSEGV, Segmentation fault.

Using the backtrace command, we can see a stack trace, which reveals that the problem is the strcpy() call at line 30:

(gdb) backtrace
#0  strcpy (dest=0xbffff830 "", src=0x0) at ../sysdeps/generic/strcpy.c:34
#1  0x80485f6 in main (argc=1, argv=0xbffffc4c) at cipher.c:30

Using the list command again, we have now identified the location of the problem:

(gdb) list cipher.c:30
25          fprintf(stderr, "\nCIPHER syntax: cipher file password\n\n");
26          /* we should be exiting here */
27        }
29        /* what happens if argv[1] or argv[2] is greater than BUFSIZE? */
30        strcpy(fileName, argv[1]);
31        strcpy(password, argv[2]);
33        if ((f = fopen(fileName, "r")) == NULL) {
34          fprintf(stderr, "Error: could not open %s for reading!\n\n", fileName);

5. Tracking variables

Clearly, argv[1] is somehow corrupting the call to strcpy. We can check the value of argv[1] by setting a breakpoint and then printing the value of argv[1]:

(gdb) break 30
Breakpoint 1 at 0x80485e1: file cipher.c, line 30.
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/graeme/work/cipher 

CIPHER syntax: cipher file password

Breakpoint 1, main (argc=1, argv=0xbffffc4c) at cipher.c:30
30        strcpy(fileName, argv[1]);
(gdb) print argv[1]
$1 = 0x0

Hmmm... seems argv[1] equals 0x0 (or NULL), which is not good!

Checking back, there were no command line arguments, which means that argv[1] is not defined. The routine starting at line 24 checks for this condition, but fails to exit! If we insert the following line at line 26, then the bug is fixed:


Getting help with GDB

GDB has a set of info pages and also has inbuilt help, which can be accessed via the help command:

(gdb) help
List of classes of commands:

aliases -- Aliases of other commands
breakpoints -- Making program stop at certain points
data -- Examining data
files -- Specifying and examining files
internals -- Maintenance commands
obscure -- Obscure features
running -- Running the program
stack -- Examining the stack
status -- Status inquiries
support -- Support facilities
tracepoints -- Tracing of program execution without stopping the program
user-defined -- User-defined commands

Type "help" followed by a class name for a list of commands in that class.
Type "help" followed by command name for full documentation.
Command name abbreviations are allowed if unambiguous.

Prev - Index - Next