Section 11.8. Debugging a Program: gdb


[Page 419 (continued)]

11.8. Debugging a Program: gdb

The GNU debugger gdb allows you to symbolically debug a program. Although it's not as slick as most commercial professional debuggers on the market, it is a handy utility that comes bundled in Linux distributions. gdb includes the following facilities:

  • running and listing the program

  • setting breakpoints

  • examining variable values

  • tracing execution


[Page 420]

Figure 11-16 is a synopsis of gdb.

Figure 11-16. Description of the gdb command.

Utility: gdb executableFilename

gdb is a standard GNU/Linux debugger. The named executable file is loaded into the debugger and a user prompt is displayed. To obtain information on the various gdb commands, enter help at the prompt.


To demonstrate gdb, let's debug the following recursive version of palindrome ():

1  /* PALINDROME.C */ 2 3  #include "palindrome.h" 4  #include <string.h> 5 6 7  enum { FALSE, TRUE }; 8 9 10  int palindrome (str) 11 12  char *str; 13 14  { 15    return (palinAux (str, 1, strlen (str))); 16  } 17 18  /****************************************************************/ 19 20  int palinAux (str, start, stop) 21 22  char *str; 23  int start; 24  int stop; 25 26  { 27    if (start >= stop) 28      return (TRUE); 29    else if (str[start] != str[stop]) 30      return (FALSE); 31    else 32      return (palinAux (str, start + 1, stop - 1)); 33  } 



[Page 421]

The basic algorithm is that it starts from each end of a string and compares the letters and quits if and when the two letters differ (false) or when the indices "cross" or become equal (true).

11.8.1. Preparing a Program for Debugging

To debug a program, it must have been compiled using the -g option to gcc, which places debugging information into the object module. Using a new palindrome module in palbug.c along with our previous reverse module and a main program to control it all, we compile this way:

$ make gcc -g -c -o main2.o main2.c gcc -g -c -o reverse.o reverse.c gcc -g -c -o palbug.o palbug.c gcc main2.o reverse.o palbug.o -o main2 $ _ 


Now we have a "main2" file containing an executable built from these module files.

11.8.2. Entering the Debugger

Once a program has been compiled correctly, invoke gdb with the name of the executable as the first argument. gdb presents you with a prompt. I recommend that you enter help at the prompt to see a list of all the gdb commands:

$ gdb main2                    ...enter the debugger. GNU gdb 6.0-2mdk (Mandrake Linux) 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 "i586-mandrake-linux-gnu"...Using host libthread_db library "/lib/tls/libthread_db.so.1". (gdb) help                           ...get some 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 
[Page 422]
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) _


Note that gdb prints its copyright and licensing information each time it is started, but in future examples I will omit this in the interest of brevity.

11.8.3. Running a Program

To run your program, enter the run command, which runs the program to completion:

(gdb) run Starting program: /home/ables/main2 palindrome ("cat") = 0 palindrome ("noon") = 0 Program exited with code 030. (gdb) _ 


But there's a problem. The string "noon" is a palindrome, even though my function reports that it isn't.

11.8.4. Listing a Program

In order to know where you're looking in your code, you can use the list command to list sections of the program, ten lines at a time by default, and including line numbers you'll use to reference specific statements:

(gdb) list 2 3      #include <stdio.h> 4      #include "palindrome.h" 5 6      / ****************************************************************/ 7 8      main () 9 10      { 11        printf ("palindrome (\"cat\") = %d\n", palindrome ("cat")); (gdb) _ 



[Page 423]

Our first use of the list command listed the ten lines following our current position (which was line 1). If ten lines aren't enough, you can specify a line range, such as:

(gdb) list 1,99 1      /* MAIN2.C */ 2 3      #include <stdio.h> 4      #include "palindrome.h" 5 6      / ****************************************************************/ 7 8      main () 9 10      { 11        printf ("palindrome (\"cat\") = %d\n", palindrome ("cat")); 12        printf ("palindrome (\"noon\") = %d\n", palindrome ("noon")); 13      } (gdb) _ 


We don't have 99 lines in this file, but it lists what it can. Notice, however, that gdb is listing the contents of main2.c. To list lines from another source file, specify the line range with a file name in the form "filename:firstline,lastline", or to list the ten lines around a specific location, use "filename:linenumber"like this:

(gdb) list palbug.c:10 5 6 7      enum { FALSE, TRUE }; 8 9 10     int palindrome (str) 11 12     char *str; 13 14     { (gdb) list 15       return (palinAux (str, 1, strlen (str))); 16     } 17 18     / ****************************************************************/ 19 20     int palinAux (str, start, stop) 21 22     char *str; 
[Page 424]
23 int start; 24 int stop; (gdb) list 25 26 { 27 if (start >= stop) 28 return (TRUE); 29 else if (str[start] != str[stop]) 30 return (FALSE); 31 else 32 return (palinAux (str, start + 1, stop - 1)); 33 } (gdb) _


Our subsequent list commands began a new ten-line listing from the end of the previous listing.

11.8.5. Setting a Breakpoint

To make gdb stop when it encounters a particular function, use the break command. This allows you to run a program at full speed until the function that you wish to examine more closely is executed:

(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 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 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 (gdb)  break palindrome Breakpoint 1 at 0x804843e: file palbug.c, line 15. (gdb) run Starting program: /home/ables/main2 
[Page 425]
Breakpoint 1, palindrome (str=0x80485b8 "cat") at palbug.c:15 15 return (palinAux (str, 1, strlen (str))); (gdb) list 10 int palindrome (str) 11 12 char *str; 13 14 { 15 return (palinAux (str, 1, strlen (str))); 16 } 17 18 / ****************************************************************/ 19 (gdb) _


If you want to set a breakpoint at a specific line number, you can use a line number instead of a function name, like this:

(gdb) list 20     int palinAux (str, start, stop) 21 22     char *str; 23     int start; 24     int stop; 25 26     { 27       if (start >= stop) 28         return (TRUE); 29       else if (str[start] != str[stop]) (gdb) break 27 Breakpoint 2 at 0x8048465: file palbug.c, line 27. (gdb) run The program being debugged has been started already. Start it from the beginning? (y or n) n Program not restarted. (gdb) continue Continuing. Breakpoint 2, palinAux (str=0x80485b8 "cat", start=1, stop=3) at palbug.c:27 27        if (start >= stop) (gdb) _ 


Notice that after I set the new breakpoint, I typed "run", but this instructs gdb to start the program from the beginning. Since I was in the middle of execution already, it asked me if I knew what I was doing (which, of course, I did not!). To resume execution of a program from the same place, use the continue command. If you type "y" in answer to the question, the program will be restarted from the beginning with all the same breakpoints that have already been set.


[Page 426]

As with the list command, you can specify a line number or function name within a file with the syntax "break file:linenumber" and "break file:functionname," respectively.

(gdb) quit The program is running. Exit anyway? (y or n) y $ gdb main2                                 ...begin again. (gdb) break palbug.c:27 Breakpoint 1 at 0x8048465: file palbug.c, line 27. (gdb) run Starting program: /home/ables/main2 Breakpoint 1, palinAux (str=0x80485b8 "cat", start=1, stop=3) at palbug.c:27 27       if (start >= stop) (gdb) _ 


11.8.6. Stepping Through the Code

To step through a program one line at a time, use the step command. This command causes gdb to redisplay its prompt immediately after the next line of program has been executed, and is useful for high-resolution interrogation of a function. In the following example, I entered step after my program stopped at line 27.

(gdb)  step 29        else if (str[start] != str[stop]) (gdb)  step 30          return (FALSE); (gdb) _ 


You can also step multiple lines at a time:

(gdb) step 5 palindrome ("cat") = 0 Breakpoint 1, palinAux (str=0x80485d5 "noon", start=1, stop=4) at palbug.c:27 27       if (start >= stop) (gdb) step 5 palindrome ("noon") = 0 main () at main2.c:13 13      } (gdb) _ 



[Page 427]

11.8.7. Examining Variable Values

The power of gdb is the ability to examine and change the state of the program at any point during its execution. You can stop execution and examine the value of any variable to see if they match your expectation. To print the value of a particular variable at any time, use the print command.

(gdb) help data Examining data. List of commands: append -- Append target code/data to a local file call -- Call a function in the program delete display -- Cancel some expressions to be displayed when program stops delete mem -- Delete memory region disable display -- Disable some expressions to be displayed when program stops disable mem -- Disable memory region disassemble -- Disassemble a specified section of memory display -- Print value of expression EXP each time the program stops dump -- Dump target code/data to a local file enable display -- Enable some expressions to be displayed when program stops enable mem -- Enable memory region inspect -- Same as "print" command mem -- Define attributes for memory region output -- Like "print" but don't put in value history and don't print newline print -- Print value of expression EXP print-object -- Ask an Objective-C object to print itself printf -- Printf "printf format string" ptype -- Print definition of type TYPE restore -- Restore the contents of FILE to target memory set -- Evaluate expression EXP and assign result to variable VAR set variable -- Evaluate expression EXP and assign result to variable VAR undisplay -- Cancel some expressions to be displayed when program stops whatis -- Print data type of expression EXP x -- Examine memory: x/FMT ADDRESS (gdb) run The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/ables/main2 
[Page 428]
Breakpoint 1, palinAux (str=0x80485b8 "cat", start=1, stop=3) at palbug.c:27 27 if (start >= stop) (gdb) continue Continuing. palindrome ("cat") = 0 Breakpoint 1, palinAux (str=0x80485d5 "noon", start=1, stop=4) at palbug.c:27 27 if (start >= stop) (gdb) print str $1 = 0x80485d5 "noon" (gdb) print start $2 = 1 (gdb) print stop $3 = 4 (gdb) quit The program is running. Exit anyway? (y or n) y $ _


11.8.8. The Epiphany

If we continue to step through the program while processing the "noon" string, we see we come to line 30 where we return FALSE (i.e., that "noon" is not a palindrome). Here's a good place to look at some variables, because something clearly isn't right.

(gdb) step 30         return (FALSE); (gdb) print str[1] $1 = 111 'o' (gdb) print str[2] $2 = 111 'o' (gdb) print str[3] $3 = 110 'n' (gdb) print str[4] $4 = 0 '\0' (gdb) print str[0] $5 = 110 'n' (gdb) quit The program is running. Exit anyway? (y or n) y $ 


Do you see the problem? When we entered the palinAux () function, the start and stop variables were 1 and 4, respectively, which seemed reasonable since there are four characters in the string "noon". But those values are not correct, they are both 1 greater than they should be. It's a very common error to forget that C array indices begin at zero rather than one. We should be testing locations 03 of our string, not 14!


[Page 429]

The bug is on line 15 in the main program. Rather than starting to index into the string at element one, we should be starting at zero. The last location is not actually the same number as the length of the string, it is should be one less than the length of the string. If we correct line 15 to read:

return (palinAux (str, 0, strlen (str) - 1));


and recompile the program, it should work:

$ make cc -g -c -o palbug.o palbug.c gcc main2.o reverse.o palbug.o -o main2 $ ./main2 palindrome ("cat") = 0 palindrome ("noon") = 1                 ...whoo hoo! $ _ 





Linux for Programmers and Users
Linux for Programmers and Users
ISBN: 0131857487
EAN: 2147483647
Year: 2007
Pages: 339

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net