The GNU Debugger


The GNU Debugger

Although make automates the process of building a program, that task is the least of your worries when a program does not work correctly or when a program suddenly quits with an error message. You need a debugger to find the cause of program errors. This book's companion CD-ROMs include gdb-the versatile GNU debugger with a command-line interface.

Like any debugger, gdb lets you perform typical debugging tasks, such as the following:

  • Set the breakpoint so that program execution stops at a specified line.

  • Watch the values of variables in the program.

  • Step through the program one line at a time.

  • Change variables in an attempt to fix errors.

The gdb debugger can debug C and C++ programs.

Preparing a Program for Debugging

If you want to debug a program by using gdb, you have to ensure that the compiler generates and places debugging information in the executable. The debugging information contains the names of variables in your program and the mapping of addresses in the executable file to lines of code in the source file. gdb needs this information to perform its functions, such as stopping after executing a specified line of source code.

Insider Insight 

To ensure that the executable is properly prepared for debugging, use the -g option with GCC. You can do this by defining the variable CFLAGS in the makefile as:

CFLAGS= -g

Running gdb

The most common way to debug a program is to run gdb by using the following command:

gdb progname

progname is the name of the program's executable file. After it runs, gdb displays the following message and prompts you for a command:

GNU gdb Red Hat Linux (5.3post-0.20021129.7rh)
Copyright 2002 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-gnu".
(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.
(gdb) q

When you type help, gdb displays the classes of gdb commands. You can get further help on a specific class of commands or a specific command by following the instructions. To see the list of commands you use to run the program you are debugging, type help running at the gdb prompt.

To quit gdb, type q, and press Enter.

gdb has a large number of commands, but you need only a few to find the cause of an error quickly. Table 23-4 lists the commonly used gdb commands.

Table 23-4: Commonly Used gdb Commands

Command

Description

break NUM

Set a breakpoint at the specified line number (the debugger stops at breakpoints)

bt

Display a trace of all stack frames. (This command shows you the sequence of function calls so far.)

clear clear FILENAME:NUM

Delete the breakpoint at a specific line in a source file. For example, xdraw.c:8 clears the breakpoint at line 8 of file xdraw.c.

Continue

Continue running the program being debugged. (Use this command after the program has stopped due to a signal or breakpoint.)

display EXPR

Display value of expression (consisting of variables defined in the program) each time the program stops

file FILE

Load specified executable file for debugging

help NAME

Display help on the command named NAME

info break

Display a list of current breakpoints, including information on how many times each breakpoint has been reached

info files

Display detailed information about the file being debugged

info func

Display all function names

info local

Display information about local variables of current function

info prog

Display the execution status of the program being debugged

info var

Display all global and static variable names

Kill

End the program you are debugging

List

List a section of the source code

Make

Run the make utility to rebuild the executable without leaving gdb

Next

Advance one line of source code in the current function without stepping into other functions

print EXPR

Show the value of the expression EXPR

Quit

Quit gdb

Run

Start running the currently loaded executable

set variable

Set the value of the variable VAR to VALUE VAR=VALUE

shell CMD

Execute a Linux command CMD, without leaving gdb

Step

Advance one line in the current function, stepping into other functions, if any

watch VAR

Show the value of the variable named VAR whenever the value changes

Where

Display the call sequence. Use this command to locate where your program died.

x/F ADDR

Examine the contents of the memory location at address ADDR in the format specified by the letter F, which can be o (octal); x (hex); d (decimal); u (unsigned decimal); t (binary); f (float); a (address); i (instruction); c (char); or s (string). You can append a letter indicating the size of data type to the format letter. Size letters are b (byte); h (halfword, 2 bytes), w (word, 4 bytes); and g (giant, 8 bytes). Typically, ADDR is the name of a variable or pointer.

Finding Bugs by Using gdb

To understand how you can find bugs by using gdb, you need to see an example. The procedure is easiest to show with a simple example, so I start with a rather contrived program that contains a typical bug.

This is the contrived program, which I store in the file dbgtst.c:

#include <stdio.h>

static char buf[256];
void read_input(char *s);

int main(void)
{
  char *input = NULL; /* Just a pointer, no storage for string */

  read_input(input);

/* Process command. */
  printf("You typed: %s\n", input);

/* ... */
  return 0;
}

void read_input(char *s)
{
  printf("Command: ");
  gets(s);
}

This program's main function calls the read_input function to get a line of input from the user. The read_input function expects a character array in which it returns what the user types. In this example, however, main calls read_input with an uninitialized pointer-that's the bug in this simple program.

Build the program by using gcc with the -g option:

gcc -g -o dbgtst dbgtst.c

Ignore the warning message about the gets() function being dangerous; we are trying to use the shortcoming of the gets() function to show how gdb can be used to track down errors.

To see the problem with this program, run it:

./dbgtst
Command: test
Segmentation fault

The program dies after displaying the Segmentation fault message. For this small program, you can find the cause by examining the source code. In a real-world application, however, you may not immediately know what causes the error. That's when you use gdb to find the cause of the problem.

To use gdb to locate a bug, follow these steps:

  1. Load the program under gdb. To load a program named dbgtst in gdb, type the following:

    gdb dbgtst
  2. At the (gdb) prompt, type run. When the program prompts for input, type some input text. The program should fail as it has previously. Here's what happens with the dbgtst program:

    (gdb) run
    Starting program: /home/naba/apps/dbgtst
    Command: test
    
    Program received signal SIGSEGV, Segmentation fault.
    0x420632cc in gets () from /lib/tls/libc.so.6
    (gdb)
  3. Type the where command to determine where the program died. For the dbgtst program, this command yields this output:

    (gdb) where
    #0  0x420632cc in gets () from /lib/tls/libc.so.6
    #1  0x080483bc in read_input (s=0x0) at dbgtst.c:22
    #2  0x0804837e in main () at dbgtst.c:10
    #3  0x420154a0 in __libc_start_main () from /lib/tls/libc.so.6
    (gdb)

    The output shows the sequence of function calls. Function call #0-the most recent one-is to a C library function, gets. The gets call originates in the read_input function, which in turn is called from the main function.

  4. Type the list command with the name of a file and line number to inspect the lines of suspect source code. In dbgtst, you might start with line 22 of dbgtst.c file, as follows:

    (gdb) list dbgtst.c:22
    17      }
    18
    19      void read_input(char *s)
    20      {
    21        printf("Command: ");
    22        gets(s);
    23      }
    24
    (gdb)

    After looking at this listing, if you have some experience writing C programs, you should be able to tell that the problem might be the way read_input is called. Then you list the lines around line 10 in dbgtst.c (where the read_input call originates):

    (gdb) list dbgtst.c:10
    5
    6       int main(void)
    7       {
    8         char *input = NULL; /* Just a pointer, no storage for string */
    9
    10        read_input(input);
    11
    12      /* Process command. */
    13        printf("You typed: %s\n", input);
    14
    (gdb)

    At this point, you should be able to narrow the problem to the variable named input. That variable should be an array, not a NULL pointer.

Fixing Bugs in gdb

Sometimes you can try a bug fix directly in gdb. For the example program in the preceding section, you can try this fix immediately after the program dies after displaying an error message. Because the example is contrived, I have an extra buffer named buf defined in the dbgtst program, as follows:

static char buf[256];

I can fix the problem of the uninitialized pointer by setting the variable input to buf. The following session with gdb corrects the problem of the uninitialized pointer (this example picks up immediately after the program has run and died, due to the segmentation fault):

 (gdb) file dbgtst
A program is being debugged already.  Kill it? (y or n) y

Load new symbol table from "dbgtst"? (y or n) y
Reading symbols from dbgtst...
done.
(gdb) list
1       #include <stdio.h>
2
3       static char buf[256];
4       void read_input(char *s);
5
6       int main(void)
7       {
8         char *input = NULL; /* Just a pointer, no storage for string
*/
9
10        read_input(input);
(gdb) break 9
Breakpoint 1 at 0x8048373: file dbgtst.c, line 9.
(gdb) run
Starting program: /home/naba/apps/dbgtst

Breakpoint 1, main () at dbgtst.c:10
10        read_input(input);
(gdb) set var input=buf
(gdb) cont
Continuing.
Command: test
You typed: test

Program exited normally.
(gdb) q

As the previous listing shows, if I stop the program just before read_input is called and set the variable named input to buf (which is a valid array of characters), the rest of the program runs fine.

After trying in gdb a fix that works, you can make the necessary changes to the source files and can make the fix permanent.