Debugging Programs with gdb in C Programming Language

gdb is a powerful interactive debugger that is frequently used to debug programs com- piled with GNU’s gcc compiler. It allows you to run your program, stop at a predeter- mined location, display and/or set variables, and continue execution. It allows you to trace your program’s execution and even execute it one line at a time. gdb also has a facility for determining where core dumps  occur. A core dump occurs due to some abnor-mal event, possibly division by zero or attempts to access past the end of an array. This results in the creation of a file named core that contains a snapshot of the contents of the process’s memory at the time it terminated.

Your C program must be compiled with the gcc compiler using the -g option to make full use of gdb’s features. The -g option causes the C compiler to add extra infor- mation to the output file, including variable and structure types, source filenames, and C statement to machine code mappings.

Program 18.4 shows a program that attempts to access elements past the end of an array.

Program 18.4   A Simple  Program for Use with gdb

#include <stdio.h>

int main (void)

{

const int data[5] = {1, 2, 3, 4, 5};

int i, sum;

for (i = 0; i >= 0; ++i)

sum += data[i];

printf (“sum = %i\n”, sum);

return 0;

}

Here’s what happens when the program is run on a Mac OS X system from a terminal window (on other systems you might get a different message displayed when you run the program):

$ a.out

Segmentation fault

Use gdb to try to track down the error. This is certainly a contrived example; neverthe- less, it is illustrative.

First, make sure you compile the program with the –g option. Then, you can start up gdb on the executable file, which is a.out by default. This might result in lines of intro- ductory messages being displayed on your system:

$ gcc –g p18.4.c     Recompile with debugging information for gdb

$ gdb a.out          Start up gdb on the executable file

GNU gdb 5.3-20030128 (Apple version gdb-309) (Thu Dec 4 15:41:30 GMT 2003) Copyright 2003 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 “powerpc-apple-darwin”.

Reading symbols for shared libraries .. done

When gdb is ready to accept commands, it displays a (gdb) prompt. In our simple example, you’re simply telling it to run your program by entering the run command. This causes gdb to start execution of your program until it finishes or an abnormal event occurs:

(gdb) run

Starting program: /Users/stevekochan/MySrc/c/a.out

Reading symbols for shared libraries . done

Program received signal EXC_BAD_ACCESS, Could not access memory.

0x00001d7c in main () at p18-4.c:9

9             sum += data[i];

(gdb)

So your program receives an error (as it did before), but it still remains under the control of gdb. That’s the nice part because now you can see what it was doing when the error occurred as well as look at the values of variables at the time of the error.

As you can see from the previous display, the program tried to perform an invalid memory access at line 9 in your program. The actual line in question is automatically displayed from your source file. To gain a little context around that line, you can use the list command, which displays a window of 10 lines around the specified line (5 lines before and 4 lines after):

(gdb) list 9

4     {

5          const int data[5] = {1, 2, 3, 4, 5};

6          int i, sum;

7

8          for (i = 0; i >= 0; ++i)

9             sum += data[i];

10

11         printf (“sum = %i\n”, sum);

12

13         return 0;

(gdb)

You can look at variables by using the print command. Take a look at what the value of sum was at the time the program stopped with an error:

(gdb) print sum

$1 = -1089203864

This value for sum is obviously out of whack (and on your system might differ from what’s shown here). The $n notation is used by gdb to keep track of the values you’ve previously displayed, so you can easily reference them again later.

Take a look at what the index variable i is set to:

(gdb) print i

$2 = 232

Oops! That’s not good.You have only five elements in the array and you tried to access the 233rd element when the error occurred. On your system, the error might occur ear- lier or later. But eventually, you should get an error.

Before you exit from gdb, look at another variable. See how nicely gdb deals with variables like arrays and structures:

(gdb) print data        Show the contents of the data array

$3 = {1, 2, 3, 4, 5}

(gdb) print data[0]     Show the value of the first element

$4 = 1

You’ll see an example of a structure a little later. To complete this first example with gdb, you need to learn how to get out.You can do that with the quit command:

(gdb) quit

The program is running. Exit anyway? (y or n) y

$

Even though the program had an error, technically speaking, it was still active inside gdb; the error merely caused your program’s execution to be suspended, but not terminated. That’s the reason gdb asked for confirmation about quitting.

1. Working with Variables

gdb has two basic commands that allow you to work with variables in your program. One you’ve seen already is print. The other allows you to set the value of a variable. This is done with the set var command. The set command actually takes a number of different options, but var is the one you want to use to assign a value to a variable:

(gdb) set var i=5

(gdb) print i

$1 = 5

(gdb) set var i=i*2     You can write any valid expression

(gdb) print i

$2 = 10

(gdb) set var i=$1+20   You can use so-called “convenience variables”

(gdb) print i

$3 = 25

A variable must be accessible by the current function, and the process must be active, that is, running. gdb maintains an idea of a current line (like an editor), a current file (the source file of the program), and a current function. When gdb starts up without a core file, the current function is main, the current file is the one that contains main, and the current line is the first executable line in main; otherwise, the current line, file, and pro- cedure are set to the location where the program aborted.

If a local variable with the specified name doesn’t exist, gdb looks for an external variable of the same name. In the previous example, the function executing at the time the invalid access occurred was main, and i was a variable local to main.

A function can be specified  as part of the variable name in the form function::variable to reference a variable local to a specific routine, for example,

(gdb) print main::i      Display contents of i in main

$4 = 25

(gdb) set var main::i=0  Set value of i in main

Note that attempting to set a variable in an inactive function (that is, a function that is not either currently executing or waiting for another function to return to continue its own execution) is an error and results in the following message:

No symbol “var” in current context.

Global variables can be directly referenced as ‘file’::var. This forces gdb to access an external variable  as defined in the file file and ignore any local variable of the same name in the current function.

Structure and union members can be accessed using standard C syntax. If datePtr is a pointer to a date structure, print datePtr->year prints the year member of the structure pointed to by datePtr.

Referencing a structure or union without a member causes the contents of the entire structure or union to be displayed.

You can force gdb to display a variable in a different format, for example hexadeci- mal, by following the print command with a / and a letter that specifies the format to use. Many gdb commands can be abbreviated with a single letter. In the following exam- ple, the abbreviation for the print command, which is p, is used:

(gdb) set var i=35   Set i to 35

(gdb) p /x i        Display i in hexadecimal

$1 = 0x23

2. Source File Display

gdb provides several commands that give you access to the source files. This enables you to debug the program without having to reference a source listing or open your source files in other windows.

As mentioned earlier, gdb maintains an idea of what the current line and file are. You’ve seen how you can display the area around the current line with the list com- mand, which can be abbreviated  as l. Each time you subsequently type the list com- mand (or more simply, just press the Enter or Return key), the next 10 lines from the file are displayed. This value of 10 is the default and can be set to any value by using the listsize command.

If you want to display a range of lines, you can specify the starting and ending line numbers, separated by a comma, as follows:

(gdb) list 10,15     List lines 10 through 15

Lines from a function can be listed by specifying the function’s name to the list com- mand:

(gdb) list foo      Display lines for function foo

If the function is in another source file, gdb automatically switches to that file.You can find the name of the current source file being displayed with gdb by typing in the com- mand info source.

Typing a + after the list command causes the next 10 lines from the current file to be displayed, which is the same action that occurs if just list is typed. Typing a – causes the previous 10 lines to be displayed. Both the + and – options can also be followed by a number to specify a relative offset to be added or subtracted from the current line.

3. Controlling  Program Execution

Displaying lines from a file doesn’t modify the way a program is executed.You must use other commands for that.You’ve seen two commands that control the execution of a program in gdb: run, which runs the program from the beginning, and quit, which ter- minates execution of the current program.

The run command can be followed by command-line arguments and/or redirection (< or >), and gdb handles them properly. Subsequent use of the run command without any arguments reuses the previous arguments and redirection.You can display the current arguments with the command show args.

3.1. Inserting Breakpoints

The break command can be used to set breakpoints in your program. A breakpoint is just as its name implies—a point in your program that, when reached during execution, caus- es the program to “break” or pause. The program’s execution is suspended, which allows you to do things such as look at variables and determine precisely what’s going on at the point.

A breakpoint can be set at any line in your program by simply specifying the line number to the command. If you specify a line number but no function or filename, the breakpoint is set on that line in the current file; if you specify a function, the breakpoint is set on the first executable line in that function.

(gdb) break 12                 Set breakpoint on line 12

Breakpoint 1 at 0x1da4: file mod1.c, line 12.

(gdb) break main               Set breakpoint at start of main

Breakpoint 2 at 0x1d6c: file mod1.c, line 3.

(gdb) break mod2.c:foo         Breakpoint in function foo in file mod2.c

Breakpoint 3 at 0x1dd8: file mod2.c, line 4.

When a breakpoint is reached during program execution, gdb suspends execution of your program, returns control to you, and identifies the breakpoint and the line of your pro- gram at which it stopped.You can do anything you want at that point:You can display or set variables, set or unset breakpoints, and so on. To resume execution of the program, you can simply use the continue command, which can be abbreviated  as simply c.

3.2. Single Stepping

Another useful command for controlling program execution is the step command, which can be abbreviated  as s. This command single steps your program, meaning that one line of C code in your program is executed for each step command you enter. If you follow the step command with a number, then that many lines are executed. Note that a line might contain several C statements; however, gdb is line oriented, and exe- cutes all statements on a line as a single step. If a statement spans several lines, single step- ping the first line of the statement causes all the lines of the statement to be executed. You can single step your program at any time that a continue is appropriate (after a sig- nal or breakpoint).

If the statement contains a function call and you step, gdb takes you into the func- tion (provided it’s not a system library function; these are typically not entered). If you use the next command instead of step, gdb makes the function call and does not step you into it.

Try some of gdb’s features on Program 18.5, which otherwise serves no useful pur- pose.

Program 18.5   Working with gdb

#include <stdio.h>

#include <stdlib.h>

struct date {

int month;

int day;

int year;

};

struct date foo (struct date x)

{

++x.day;

return x;

}

int main (void)

{

struct date today = {10, 11, 2004};

int       array[5] = {1, 2, 3, 4, 5};

struct date *newdate, foo ();

char      *string = “test string”;

int       i = 3;

newdate = (struct date *) malloc (sizeof (struct date));

newdate->month = 11;

newdate->day = 15;

newdate->year = 2004;

today = foo (today);

free (newdate);

return 0;

}

In the sample session for Program 18.5, your output might be slightly different, depend- ing on which version and on what system you are running gdb.

Program 18.5   gdb Session

$ gcc -g p18-5.c

$ gdb a.out

GNU gdb 5.3-20030128 (Apple version gdb-309) (Thu Dec 4 15:41:30 GMT 2003) Copyright 2003 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 “powerpc-apple-darwin”.

Reading symbols for shared libraries .. done

(gdb) list main

14

15       return x;

16     }

17

18     int main (void)

19     {

20        struct date today = {10, 11, 2004};

21        int       array[5] = {1, 2, 3, 4, 5};

22        struct date *newdate, foo ();

23        char      *string = “test string”;

(gdb) break main           Set breakpoint in main

Breakpoint 1 at 0x1ce8: file p18-5.c, line 20.

(gdb) run                 Start program execution

Starting program: /Users/stevekochan/MySrc/c/a.out

Reading symbols for shared libraries . done

Breakpoint 1, main () at p18-5.c:20

20        struct date today = {10, 11, 2004};

(gdb) step           Execute line 20

21        int       array[5] = {1, 2, 3, 4, 5};

(gdb) print today

$1 = {

month = 10,

day = 11,

year = 2004

}

(gdb) print array          This array hasn’t been initialized yet

$2 = {-1881069176, -1880816132, -1880815740, -1880816132, -1880846287}

(gdb) step    Run another line

23        char      *string = “test string”;

(gdb) print array              Now try it

$3 = {1, 2, 3, 4, 5}       That’s better

(gdb) list 23,28

23        char      *string = “test string”;

24        int       i = 3;

25

26        newdate = (struct date *) malloc (sizeof (struct date));

27        newdate->month = 11;

28        newdate->day = 15;

(gdb) step 5              Execute 5 lines

29        newdate->year = 2004;

(gdb) print string

$4 = 0x1fd4 “test string”

(gdb) print string[1]

$5 = 101 ‘e’

(gdb) print array[i]       The program set i to 3

$6 = 3

(gdb) print newdate        This is a pointer variable

$7 = (struct date *) 0x100140

(gdb) print newdate->month

$8 = 11

(gdb) print newdate->day + i Arbitrary C expression

$9 = 18

(gdb) print $7            Access previous value

$10 = (struct date *) 0x100140

(gdb) info locals         Show the value of all local variables

today = {

month = 10,

day = 11,

year = 2004

}

array = {1, 2, 3, 4, 5}

newdate = (struct date *) 0x100140

string = 0x1fd4 “test string”

i = 3

(gdb) break foo           Put a breakpoint at the start of foo

Breakpoint 2 at 0x1c98: file p18-5.c, line 13.

(gdb) continue      Continue execution

Continuing.

Breakpoint 2, foo (x={month = 10, day = 11, year = 2004}) at p18-5.c:13

13       ++x.day; 0x8e in foo:25: {

(gdb) print today        Display value of today

No symbol “today” in current context

(gdb) print main::today   Display value of today from main

$11 = {

month = 10,

day = 11,

year = 2004

}

(gdb) step

15       return x;

(gdb) print x.day

$12 = 12

(gdb) continue

Continuing.

Program exited normally.

(gdb)

Note one feature of gdb: After a breakpoint is reached or after single stepping, it lists the line that will be executed next when you resume execution of your program, and not the last executed line. That’s why array was still not initialized the first time it was dis-

played. Single stepping one line caused it to be initialized. Also note that declarations that initialize automatic variables are considered executable lines (they actually do cause the compiler to produce executable code).

3.3. Listing and Deleting  Breakpoints

Once set, breakpoints remain in a program until gdb exits or until you delete them.You can see all the breakpoints that you have set by using the info break command, as follows:

(gdb) info break

Num Type      Disp Enb  Address     What

1 breakpoint   keep y   0x00001c9c in main at p18-5.c:20

2 breakpoint   keep y   0x00001c4c in foo at p18-5.c:13

You can delete a breakpoint at a particular line with the clear command followed by the line number.You can delete a breakpoint at the start of a function by specifying the function’s name to the clear command instead:

(gdb) clear 20    Remove breakpoint from line 20

Deleted breakpoint 1

(gdb) info break

Num Type        Disp Enb Address  What

2  breakpoint  keep y  0x00001c4c in foo at p18-5.c:13

(gdb) clear foo    Remove breakpoint on entry into foo

Deleted breakpoint 2

(gdb) info break

No breakpoints or watchpoints.

(gdb)

4. Getting a Stack Trace

Sometimes, you’ll want to know exactly where you are in terms of the hierarchy of function calls when a program gets interrupted. This is useful information when examin- ing a core file.You can take a look at the call stack by using the backtrace command, which can be abbreviated  as bt. The following is an example use of Program 18.5.

(gdb) break foo

Breakpoint 1 at 0x1c4c: file p18-5.c, line 13. (gdb) run

Starting program: /Users/stevekochan/MySrc/c/a.out

Reading symbols for shared libraries . done

Breakpoint 1, foo (x={month = 10, day = 11, year = 2004}) at p18-5.c:13

13       ++x.day;

(gdb) bt          Print stack trace

#0 foo (x={month = 10, day = 11, year = 2004}) at p18-5.c:13

#1 0x00001d48 in main () at p18-5.c:31 (gdb)

When the break is taken on entry to foo, the backtrace command is entered. The out- put shows two functions on the call stack: foo and main. As you can see, the arguments to the functions are also listed.Various commands (such as up, down, frame, and info args) that are not covered here allow you to work your way around in the stack so that you can more easily examine arguments passed to a particular function or work with its local variables.

5. Calling Functions and Setting Arrays and Structures

You can use function calls in gdb expressions  as follows:

(gdb) print foo(*newdate) Call foo with date structure pointed to by newdate

$13 = {

month = 11,

day = 16,

year = 2004

}

(gdb)

Here, the function foo is as defined in Program 18.5.

You can assign values to an array or structure by listing them inside a set of curly braces, as follows:

(gdb) print array

$14 = {1, 2, 3, 4, 5}

(gdb) set var array = {100, 200}

(gdb) print array

$15 = {100, 200, 0, 0}     Unspecified values set to zero

(gdb) print today

$16 = {

month = 10,

day = 11,

year = 2004

}

(gdb) set var today={8, 8, 2004}

(gdb) print today

$17 = {

month = 8,

day = 8,

year = 2004

} (gdb)

6. Getting Help with gdb Commands

You can use the built-in help command to get information about various commands or types of commands (called classes by gdb).

The command help, without any arguments,  lists all the available classes:

(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.

Now, you can give the help command one of those listed classes, as follows:

(gdb) help breakpoints

Making program stop at certain points.

List of commands:

awatch — Set a watchpoint for an expression

break — Set breakpoint at specified line or function

catch — Set catchpoints to catch events

clear — Clear breakpoint at specified line or function

commands — Set commands to be executed when a breakpoint is hit

condition — Specify breakpoint number N to break only if COND is true

delete — Delete some breakpoints or auto-display expressions

disable — Disable some breakpoints enable — Enable some breakpoints

future-break — Set breakpoint at expression

hbreak — Set a hardware assisted breakpoint

ignore — Set ignore-count of breakpoint number N to COUNT

rbreak — Set a breakpoint for all functions matching REGEXP

rwatch — Set a read watchpoint for an expression

save-breakpoints — Save current breakpoint definitions as a script

set exception-catch-type-regexp –

Set a regexp to match against the exception type of a caughtobject

set exception-throw-type-regexp –

Set a regexp to match against the exception type of a thrownobject

show exception-catch-type-regexp –

Show a regexp to match against the exception type of a caughtobject

show exception-throw-type-regexp –

Show a regexp to match against the exception type of a thrownobject

tbreak — Set a temporary breakpoint

tcatch — Set temporary catchpoints to catch events

thbreak — Set a temporary hardware assisted breakpoint

watch — Set a watchpoint for an expression

Type “help” followed by command name for full documentation.

Command name abbreviations are allowed if unambiguous.

(gdb)

Alternatively, you can specify a command, such as one from the previous list:

(gdb_ help break

Set breakpoint at specified line or function.

Argument may be line number, function name, or “*” and an address.

If line number is specified, break at start of code for that line.

If function is specified, break at start of code for that function.

If an address is specified, break at that exact address.

With no arg, uses current execution address of selected stack frame.

This is useful for breaking on return to a stack frame.

Multiple breakpoints at one place are permitted, and useful if conditional.

break … if <cond> sets condition <cond> on the breakpoint as it is created.

Do “help breakpoints” for info on other commands dealing with breakpoints.

(gdb)

So, you can see that you have a lot of help information built right in to the gdb debug- ger. Be sure to take advantage of it!

7. Odds and Ends

Many other features are available with gdb that can’t be covered here for space reasons. These include the ability to

  • Set temporary breakpoints that are automatically removed when they are reached.
  • Enable and disable breakpoints without having to clear them.
  • Dump memory locations in a specified format.
  • Set a watchpoint that allows for your program’s execution to be stopped whenever the value of a specified expression changes (for example, when a variable changes its value).
  • Specify a list of values to be displayed whenever the program stops.
  • Set your own “convenience variables” by name.

Table 18.1 lists the gdb commands covered in this chapter. A leading bold character for a command name shows how the command can be abbreviated.

Source: Kochan Stephen G. (2004), Programming in C: A Complete Introduction to the C Programming Language, Sams; Subsequent edition.

Leave a Reply

Your email address will not be published. Required fields are marked *