Using GNU's GDB Debugger

Stepping And Resuming

By Peter Jay Salzman


Previous: Breakpoints And WatchpointsNext: Debugging A Running Process



Setting Breakpoints In Single File Programs

There are many ways to set breakpoints. We'll go over each in turn. If you feel up to it, download try5.c and follow my example. Otherwise, you can simply follow the text (I list try5.c in the previous section). It's better if you do this alongside the tutorial, but since I'm showing gdb's output, it's not necessary. First, compile try5.c for debugging.

   $ gcc -Wall -W -g -o try5 try5.c

The first (and easiest) way you can set a breakpoint is by specifying a linenumber. To break at line 6, simply type break 6. From now on, I'm not going to show the copyright when you first run gdb.

	$ gdb try5
	(gdb) break 6
	Breakpoint 1 at 0x80483f6: file try5.c, line 6.

Wasn't that easy? You can also set breakpoints with a function name:

(gdb) break display
	Breakpoint 2 at 0x804841a: file try5.c, line 15.

Disable the 1st breakpoint, and then look at what you've done:

	(gdb) disable 1
	(gdb) info breakpoints 
	Num Type           Disp Enb Address    What
	1   breakpoint     keep n   0x080483f6 in main at try5.c:6
	2   breakpoint     keep y   0x0804841a in display at try5.c:15

Now run the program. Remember, breakpoint 1 is disabled, so it'll stop at line 15.

   (gdb) run
   Starting program: /www/p/linux/gdb/try5 
   
   Breakpoint 2, display (x=3) at try5.c:15
   15              for (i=0; i<x; ++i) {
   (gdb)

We've seen 2 ways to set a breakpoint. Now here's a third. To set a breakpoint 2 lines down from the current line, use break +2. Similarly, you can set a breakpoint 3 lines up from the current line by break -3. Let's set a breakpoint at line 18 and continue the execution.

	(gdb) break +3
	Breakpoint 3 at 0x8048450: file try5.c, line 18.
	(gdb) continue
	Continuing.
	i is 0.
	i is 1.
	i is 2.

	Breakpoint 3, display (x=5) at try5.c:18
	18      }
	(gdb) 

Go ahead and quit gdb to prepare for the next section.



Setting Breakpoints In Multiple File Programs

How do we set breakpoints when a program spans multiple files?

For the form break linenumber, there is an ambiguity when you have a multiple file program. The line number of which file? The answer is that by default, the line number is taken as a line number in whatever file holds main(). That is certainly a reasonable default! But what if we wanted to break on line 5 of a different file? This gives a 4th form for the break command:

	break filename:linenumber

This command will break on line linenumber of the source code file named filename. For example, break MyFuncs.c:102 will break on line 102 of the source code file MyFuncs.c. There's a 5th form:

	break filename:function

For example, break MyFuncs.c:MyPrintFunction. But unless you're using overloaded function names (you've defined a function multiple times), this is superfluous since you're not allowed (in C) to have 2 definitions belonging to the same function name.



Advanced Breaking

If you're trying to debug a program that doesn't have debugging info compiled into the executable, you can't set breakpoints by line number or function name. Instead you have to specify where to break by giving a memory address. This gives us a 6th form:

	break *address

Since I know next to nothing about this, I'll move right along...

The break command without any argument gives a 7th form (only one more to go). It sets a break point at the very next instruction. Look at try5 again (having one eye on the source code will help here).

	1    $ gdb try5
	2    (gdb) break display
	3    Breakpoint 1 at 0x804841a: file try5.c, line 15.
	4    (gdb) run
	5    Starting program: /www/p/linux/gdb/try5 
	6
	7    Breakpoint 1, display (x=5) at try5.c:15
	8    15              for (i=0; i<x; ++i) {
	9    (gdb) next
	10   16                 printf("i is %d.\n", i);
	11   (gdb) print i
	12   $1 = 0
	13   (gdb) break
	14   Breakpoint 2 at 0x8048430: file try5.c, line 16.
	15   (gdb) continue
	16   Continuing.
	17   i is 0.
	18
	19   Breakpoint 2, display (x=5) at try5.c:16
	20   16                 printf("i is %d.\n", i);
	21   (gdb) print i
	22   $2 = 1

The astute reader will wonder why, on line 22, i has the value of 1 and not 0. We set the breakpoint on line 13 when i had the value of 0. But the very next instruction (which is where we set the breakpoint) was just a printf statement (source code line 16). How in blazes did the printf increment the value of i?

Here's the answer. Once gdb stops at a breakpoint, it will ignore all other breakpoints until one line of instruction has executed. Why does it do this? If this weren't the case, everytime you stopped at a breakpoint, you'd have to disable that breakpoint to resume execution--you wouldn't be able to get past that breakpoint! If this doesn't make sense to you, think about it for awhile. If you still can't get it, don't worry. It's a minor point.

There's one more use for breakpoint form 7, the break command with no arguments. If you change to a higher frame, use break and then continue, the b

	$ gdb try5
	(gdb) break display
	Breakpoint 1 at 0x804841a: file try5.c, line 15.
	15              for (i=0; i<x; ++i) {
	(gdb) backtrace
	#0  display (x=3) at try5.c:15
	#1  0x8048409 in main () at try5.c:8
	#2  0x4003e46b in __libc_start_main () from /lib/libc.so.6
	(gdb) frame 1
	#1  0x8048409 in main () at try5.c:8
	8          display(x);
	(gdb) break
	Breakpoint 2 at 0x8048409: file try5.c, line 8.
	(gdb) continue
	Continuing.
	i is 0.
	i is 1.
	i is 2.

	Breakpoint 2, 0x8048409 in main () at try5.c:8
	8          display(x);
	(gdb) 

Can you see what happened here? We stopped at the top of display(), frame 0. We then switched to the frame 1 (main()) and issued the break command. This set a breakpoint at the very next instruction after the call to display(). We then continued execution, and the program ran until it hit the very next instruction after display(). In essence, we set the breakpoint so that execution would halt after display() returned. Make sense?

To reiterate, the 7th form of breakpoint is used for loops when you're in the top most frame and returns from functions when you're not in the top most frame. Frankly, I don't find this terribly useful. When in a loop, I think the break +offset or break linenumber is more convenient. For returning from functions, I find the finish command more useful (which you'll learn about shortly).

The 8th and last form of break command is the conditional breakpoint. They are quite useful but little understood. Perhaps part of the reason is that the gdb User Manual does a really poor job explaining them. Here is the form:

	break ... if cond

where ... represents any one of the previous 7 forms of breakpoints we've learned about already and cond is any conditional in the language you're using. Here is an example:

	$ gdb try5
	(gdb) break 16 if i==2
	Breakpoint 1 at 0x8048430: file try5.c, line 16.
	(gdb) r
	Starting program: /www/p/linux/gdb/try5 
	i is 0.
	i is 1.

	Breakpoint 1, display (x=3) at try5.c:16
	16                 printf("i is %d.\n", i);

We used the 1st form of break with the conditional i==2. We could've also used a test for inequality, like i!=2 or i>2. This is mega useful when you're inside of a loop that's going to repeat a million times. The 8th form of break is your friend!



Summary Of Breakpoints

Form 1:break linenumberSet a breakpoint at line number linenumber
Form 2:break functionSet a breakpoint at function function.
Form 3:break filename:linenumberSet a breakpoint at line linenum in source file filename.
Form 4:break *addressSet a breakpoint at address <address>. Use this to set breakpoints in parts of a program that doesn't have debugging information or source files.


Deleting Breakpoints

Here are the commands used to delete breakpoints you've set when they've outlived their usefulness.

clear <function> Clear any breakpoints set at the entry to the function <function>.
clear <filename><function> Clear any breakpoints set at the entry to the function <function> defined in the source code file <filename>.
clear <linenum> Clear any breakpoints set at line <linenum> of the current source file. The current source file is the last file whose text was printed.
clear <filename:linenum> Clear any breakpoints at line <linenum> in file <filename>.
delete Clear all breakpoints.
delete n Each breakpoint is assigned a number starting with 1. This clears breakpoint n.




Inspecting Variables

Note to Fortran users: All Fortran variables must be in lowercase, regardless of how they were capitalized in your source code. This is because the Fortran standard specifies case independence when it comes to variables. Yes, variable C is variable c in the Fortran standard. There are compilers out there that allow you to use case dependent variables, but this is non-standard, and gcc mandates all lowercase variables. This was done to support legacy code. Sigh.

The whole purpose of setting a breakpoint or watchpoint is to see what's going on with your variables, so let's take a look at inspecting your variables. You can print the data type of a variable using the ptype command. Here are some examples:

   (gdb) ptype argc
   type = int
   (gdb) ptype myfloat
   type = float
   (gdb) ptype argv 
   type = char **
   (gdb) ptype mystring
   type = unsigned char *
   (gdb) pt myIntArray
   type = int [10]

You can even use ptype to look at structures. Take, for example, the fstat structure defined in sys/stat.h.

   (gdb) ptype fstat
   type = struct stat {
       __dev_t st_dev;
       short unsigned int __pad1;
       __ino_t st_ino;
       __mode_t st_mode;
       __nlink_t st_nlink;
       __uid_t st_uid;
       __gid_t st_gid;
       __dev_t st_rdev;
       short unsigned int __pad2;
       __off_t st_size;
       long unsigned int st_blksize;
       __blkcnt_t st_blocks;
       __time_t st_atime;
       long unsigned int __unused1;
       __time_t st_mtime;
       long unsigned int __unused2;
       __time_t st_ctime;
       long unsigned int __unused3;
       long unsigned int __unused4;
       long unsigned int __unused5;
   }

That's quite a structure! You can abbreviate ptype by pt.

	(gdb) pt mydouble
type = double

Remember, you can only print the data type of a variable which is defined in the currently selected frame.

Now that you know how to print the data type of your variables, you may want to print their values. Consider the following program (which will be compiled via gcc -g filename):

1   #include<stdio.h>
2   #include<string.h>
3   
4   int main( int argc, char *argv[] )
5   {
6        double mydouble = 3.14 / 3;
7        float  myfloat  = 3.3;
8        char   mychar   = 'A';
9        int    myIntArray[10];
10       int    MyNegativeInt = -1;
11       char   myString[20];
12  
13       struct foo {
14            char *name;
15            int  EyeColour;
16       } myStruct;
17  
18       strncpy(myString, "hello", 19);
19  
20       for ( int i = 0; i < 10; i++ )
21            myIntArray[i] = i;
22  
23       return 0;
24  }

You can view the value of a variable using the print command.

   (gdb) print i
   $4 = -1073744780

I stopped the program right before the for loop, so this is what variable i is before it gets initialized. gdb prints the value of the variable which is most `comfortable' (to borrow fortran 99 lingo) with the datatype. In other words, floats get printed as floats:

	(gdb) print myfloat
	$1 = 3.29999995

and doubles get printed as doubles:

	(gdb) print mydouble
	$1 = 1.0466666666666666

and chars get printed as chars:

	(gdb) print mychar
	$1 = 65 'A'

By the way, you can use the abbreviation p for print:

	(gdb) p argc
	$1 = 1

You may be wondering what the numbers preceeded by $ (like $1 or $3) mean. They're kind of like a variable history. Everytime you print any variable, the $n gets incremented by 1. $ by itself refers to the last variable you printed and $n refers to the n'th variable you printed. Look at the following example to see this:

	(gdb) p mychar
	$26 = 65 'A'
	(gdb) p mydouble
	$27 = 1.0466666666666666
	(gdb) p $
	$28 = 1.0466666666666666
	(gdb) p $27
	$29 = 1.0466666666666666
	(gdb) p $26
	$30 = 65 'A'

You can even typecast a variable when you print it! Here's MyNegativeInt as an int, char and double respectively:

	(gdb) p MyNegativeInt
	$41 = -1
	(gdb) p (char) MyNegativeInt
	$42 = -1 'ÿ'
	(gdb) p (double) MyNegativeInt
	$43 = -1

The possibilities are endless. But wait, there's more!



Inspecting Arrays And Structures

Printing array values is much the same as printing other variables. gdb still uses the concept of being `comfortable'. In other words, when you print an array, that's exactly what you get! From the code snippet of the previous section:

   (gdb) p myIntArray
   $46 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

Of course, gdb knows how to access elements of an array:

	(gdb) pt myIntArray
	type = int [10]
	(gdb) pt myIntArray[3]
	type = int
	(gdb) p myIntArray[3]
	$48 = 3

You can do kind of advanced stuff too -- things that you'd expect from only Perl :-). Here's how you print 5 elements of myIntArray, starting at element 3:

	(gdb) p myIntArray[3]@5
	$49 = {3, 4, 5, 6, 7}

GDB will not, however, check bounds of the array. Previously we defined myIntArray as an array of 10 ints. Let's see what happens when we try printing 4 ints past the end of the array:

	(gdb) p myIntArray[3]@11
	$54 = {3, 4, 5, 6, 7, 8, 9, 10, 1107293224, 1079194419, -1947051841}

Doh! Hopefully, that's not someone's password. :-). You can also print structures:

	(gdb) p myStruct
	$2 = {name = 0x40014978 "Miles Davis", EyeColour = 1}

However, this might get out of hand for very large structs. You can set pretty printing of structures by set print pretty:

	(gdb) set print pretty
	(gdb) p myStruct
	$4 = {
	  name = 0x40014978 "Miles Davis", 
	  EyeColour = 1
	}
	(gdb) 

or, if you only want one of the elements of the structure, you can print it in the way that would seem obvious:

	(gdb) print myStruct.name
	$6 = 0x40014978 "Miles Davis"

this works too, but why is a mystery to me:

	(gdb) print myStruct->name 
	$15 = 0x40014978 "Miles Davis"


Advanced Inspection

You can print things using a format specifier:

	print /FMT variable

Where FMT is:

ooctal xhex ddecimal uunsigned decimal
tbinaryffloataaddress cchar

Here's some examples of printing some of our variables using a format specifier:

	(gdb) p mychar
	$33 = 65 'A'
	(gdb) p /o mychar
	$34 = 0101
	(gdb) p /x mychar 
	$35 = 0x41
	(gdb) p /d mychar 
	$36 = 65
	(gdb) p /u mychar 
	$37 = 65
	(gdb) p /t mychar 
	$38 = 1000001
	(gdb) p /f mychar 
	$39 = 65
	(gdb) p /a mychar 
	$40 = 0x41

By the way, memory addresses in gdb are printed in hex by default. Therefore, p /a mychar prints mychar interpreted as an address, the hexidecimal representation of 65. This is very different from the address of mychar!

Speaking of the address of mychar, one would expect that since C loves pointers, gdb would love pointers too. And in fact, it does! Printing the address of mychar is obvious to C programmers (sorry, Fortran users!):

	(gdb) p &mychar
	$42 = 0xbffff41b "A33S@¿Xò\213%¿ð?Hôÿ¿\023â\003@\001"

gdb even knows about the dereference operator. How's this for being perverse?

	(gdb) p *(&mychar)
	$43 = 65 'A'

This is the perfect vehicle for teaching students what a pointer is. We're dereferencing the address of mychar. Of course, there's more to this than just coolness (although it's worth it for the coolness factor alone!). I was writing a curses program once and it kept segfaulting on me whenever I tried drawing to a WINDOW object. By looking at the address of a WINDOW that I was passing to a function, I determined that I was passing a WINDOW by value, drawing to a local copy of the WINDOW and returning. Of course, the local copy of the WINDOW wasn't anything initialized by curses so drawing to it was causing a segmentation violation. Looking at the code, it was highly non-obvious what was was going on; it looked just swell! It wasn't until I compared the address of the passed WINDOW with the address of the received WINDOW that I discovered the big oops!

Furthermore, who here is guilty of buffer overruns? Be truthful! It's very easy to fall into the `off by one' error when you initialize, write to or read from a C array. How many times have you used strcpy when you should've used strncpy? These errors are insidious because they usually don't crash the program, but manifest themselves in wierd behavior in certain rare cases that are hard to track down. Looking at the addresses of what's going on is a sure fire way of finding out the details of what's going on.



Changing Variables

There are two ways you can change the value of a variable in gdb. Let's change the value of double myvariable to 10.0. Firstly, you can use the set command:

	set myvariable = 10.0

which is the `quiet' way. gdb will simply set myvariable to 10 without printing anything. Then there's the `noisy' way using the print command:

	print myvariable = 10.0

which will set myvariable to 10.0 and then print this new value to the screen. The print command ends up being less keystrokes because you can use the abbreviation p for print.

Remember, you can only change the value of a variable which is defined within the current context. Make sure the variable you want to change is defined in the currently selected frame. If it's not, you need to set the frame before you can change the variable.



Stepping through your program

One thing that is good to know is the exact sequence of execution of your program, especially through loops and conditional branches. If the program is not too large, you can follow it easily by executing one line at a time.

There are two commands used to step through your program:

step:
Execute a single line in the program. If the current statement calls a function, the function is single stepped.
next:
Execute a single line in the program but treat function calls as a single line. This command is used to skip over function calls.

Since C statements like printf() and scanf() are functions themselves, if you step through all your program (as opposed to next, you'll find yourself stepping through glibc, the standard C library (which is probably not what you want!). Good debugging makes use of next mostly. If you really want to step through a function call, it's best to set a breakpoint there and then you can use next from inside the function.

To execute the next statement, type:

	step

Each time you type a step command, gdb will then list the line that it is about to execute, with the line number on the left, so you can see what's about to happen before it happens.



Finding out where you are and listing source code

To find out where you are at any time, type the command:

	where

This will show you the current line number. For example, a line like this:

	#0  foo () at foo.f:12

shows that the execution of our program is currently at a location that corresponds to line 12 in the Fortran source file, foo.f.

You can display a few lines of your source program around the current location by using the command:

	list

This will list 10 lines of source roughly centred on your current line number. If you haven't started to debug yet, it will list the first 10 lines of source code. If you type list again, it'll print the next 10 lines of source code. You can also type:

	list 25

and this will list 10 lines of source code centred on line 25. Typing list again will list the next 10 lines of source code. You can also specify a range of lines to be listed. For example, to list lines 10 through 24 in the current program, you'd type:

	list 10,24

If there is a function in your program named endpoints(), you can list 10 lines centred on the start of endpoints() by:

	list endpoints

If you're listing lines and decide you want to see the 10 lines previous to the 10 lines you just displayed:

	list -

Suppose you set a breakpoint:

	break 55

and gdb responds with:

	Breakpoint 1 at 0x8048540: file program3.c, line 55.

You can list the lines centred around that address by specifying the asterisk (for address). It will list the 10 lines centred around the source code line containing that address.

	list *0x8048540

 



back   Back: Breakpoints And Watchpointsup  Up to the TOCNext: Debugging A Running Process   next
email  Email comments and corrections
printable  Printable version