The gdb Debugger


Most programs have bugs. You can attempt to prevent them by developing your code iteratively, and you can attempt to track them down by code inspection, using printf(), or log files. Often times, however, you have no option besides looking at a crash or stepping through your code in a debugger. gdb is a command-line debugger that allows you to do just that.

If you want to be able to debug your code, you need to compile your code in cc, gcc, or g++ with the -g command line option. (If you’re using a makefile, edit the CFLAGS or CXXFLAGS variable to include -g.) Without the -g option, the compiler will not include the debugging symbols that gdb needs to map your executable program back to your source code. The -g option makes your binary much larger and much easier to reverse-engineer, so it’s highly recommended that you not use it when compiling your final executable.

sdb and dbx are two alternate UNIX debuggers that you might find on your system. They are both less frequently used, so we focus here on gdb.

Launching gdb

You have several options for debugging your programs using gdb. You can launch gdb and run your program from inside it, you can attach gdb to a currently running version of your program, and you can use gdb to debug a core file from a crash.

Launching Programs Inside gdb

You can launch gdb with your application name as a command-line parameter. gdb will load up the program’s symbol information and allow you to launch the program. For example, the command

 $ gdb debugprogram

will launch gdb and load debugprogram. It will give you a (gdb) prompt to indicate that it is ready for input. If you forgot to compile debugprogram with the -g flag, then you will see (no debugging symbols found) as part of your output.

If you type

 (gdb) run

then gdb will launch your application. Where possible, gdb tries to save you some typing; you can abbreviate commands as long as the text is not ambiguous. In the case of run, you could type either r or ru and it would still launch your program.

You can also launch gdb without a command-line argument and load your program with the command

 (gdb) file debugprogram

This will attempt to load debugprogram out of the current working directory You can load a file from a separate directory by including the path with the filename.

Attaching to a Process Using gdb

You can also attach gdb to a running process. You may want to do this if you notice a problem while your program is running. You can get a list of your running processes with ps -u username. It should look something like

 $ ps -u nate   PID TTY          TIME CMD  7240 ptS/34    00:00:00 bash  9033 pts/34    00:00:04 debugprogram  9238 pts/34    00:00:00 ps

The first column tells you that debugprogram is process ID 9033. You can then attach to your process using gdb by running it with the program name and the process ID. For example,

 $ gdb debugprogram 9033

As long as there isn’t a file named 9033, this command will attach to the running process. It will halt the execution of the process at the current line of source. When you quit out of gdb, as long as you haven’t killed the program, gdb will detach the debugger from the process.

Using gdb to Debug a Core File

You can configure the UNIX environment to dump a core when an application crashes. The core is a core image-a file containing an image of the failed process, including its stack and heap at the moment of failure. (The term “core image” dates back to a time when the main memory of most computers was known as core memory, because it was built from donutshaped magnets called inductor cores.) The core image can be used by a debugger such as gdb to determine where the program was when it dropped core, and how-that is, by what sequence of function calls-it got there. gdb can also determine the values of variables at the moment the program failed, the statements and operations being executed at the time, and the argument(s) each function was called with.

Because core files can take up a lot of disk space, most UNIX systems by default will not dump a core when a program crashes. You can change this by running a shell command to increase or remove the limit on core dump sizes. In csh or tcsh, the shell command

 % unlimit coredumpsize

will remove the core size restriction. In bash or ksh, this can be achieved with the command

 $ ulimit -c unlimited

If you want to generate cores, you will need to add this line to a start-up script (such as your .bashrc) or run it each time you create a shell.

When you have core dumping enabled, if your program crashes through a bad pointer access, you should see a line of output that says

 Segmentation fault (core dumped)

If you run ls in the program’s working directory then you should see a core file. On some systems, the core will have a name like core.20472, where 20472 is the number of the process that crashed.

You can debug a core file in gdb by specifying the application name followed by the name of the core file. For example,

 $ gdb debugprogram core.20472

will launch gdb with the executable debugprogram and the core file core.20472. gdb will show the line of code that the program was executing when it dumped core.

Common gdb Commands

Table 24–3 lists some of the most commonly used gdb commands. As mentioned earlier, you can also enter abbreviated commands, such as p for print.

Table 24–3: gdb Commands

Command

Description

break

Sets a breakpoint in a file, function, or method.

bt

Shows the backtrace (calling stack) of your program.

c

Continue running the program.

delete

Remove breakpoints.

file

Sets the executable that you would like to debug.

help

Displays help information and help subtopics.

kill

Stop the process being debugged.

list

Show the source code around the line that the debugger is stopped on.

next

Step over the next program line. Executes functions and moves on.

print

Displays the value of a variable or expression.

quit

Exits the program.

set

Sets program or environment variables.

step

Step into the next program instruction. Will step into functions.

run

Runs your program. You can provide it command line arguments as well.

You can get more information and a description of the parameters that you can use for any of these commands with the help command. For example,

 (gdb) help step Step program until it reaches a different source line. Argument N means do this N times (or till program stops for another reason).

Debugging with gdb

Once you launch gdb, it will print some copyright information to your terminal. If you attached to a process or are debugging a core file, it will print the symbols that it read, the function and line that it is currently debugging, and finally a (gdb) command prompt. The output should look something like this:

 #0 0x080483b2 in main () at myfile.c:7 7        *x=80; (gdb)

In this example, the debugger is stopped in the function main() on line 7 of myfile.c, and that line contains an assignment to *x.

If you aren’t attaching to a process or debugging a core, you will start out with a (gdb) command prompt. As described earlier, you can start a program with the run command. If you would like to get a command prompt while the program is running, then you can interrupt the program’s execution by typing CTRL-C. (Holding the CTRL key and pressing the c key) This should give you the file and line that you stopped the program at as just shown. If you would like to continue the program’s execution, you can enter the c command.

Text editing for the gdb command prompt works like the emacs editor by default. It will allow you to cycle through the list of past commands using the arrow keys, cut the text to the end of the current line with CTRL-K, and paste the copied text with CTRL-Y. See Chapter 5 for details on how to use emacs.

A gdb Example

This section provides a short walkthrough of gdb. It ‘s probably most helpful if you are able to try it out on your machine while you read through the material. Start off by entering the following text into a file called debugtest.c:

 #include <stdio.h> void foo() {   unsigned int i = 0;   unsigned int * p = &i;   while(p)     i++;   } } int main() {   foo() ;   return 0;  }

Then compile it with the command

 $ gcc -Wall -g -o debugtest debugtest.c

and launch gdb with the command

 $ gdb debugtest

You should get a (gdb) command prompt. Enter the run (or r) command. It should look like this:

 (gdb) run Starting program: /home/nate/debugtest Reading symbols from shared object read from target memory...done. Loaded system supplied DSO at 0xfc8000

The preceding code will go into an infinite loop. You should interrupt the program with CTRL-C. You may then see

 Program received signal SIGINT, Interrupt. 0x0804835f in foo () at debugtest.c:9 9            i++; (gdb)

It may also will stop on the line above it. You can look at the value of the variables i and p with the command print (or p):

 (gdb) print i $1 = 2 3 4 0 5 1 6 3 4 8 (gdb) print p $2 = (unsigned int *) 0xbfa563d0 (gdb) p *p $3 = 2 3 4 0 5 1 6 3 4 8

You can also get a listing of the source code near this line with the command list (or 1) and the call stack with the command bt:

 (gdb) list 4       { 5         unsigned int i=0; 6         unsigned int * p=&i; 7 8         while(p) { 9           i++; 10        } 11      } 12 13      int main() (gdb) bt #0 Ox0804835f in foo () at debugtest.c:9 #1 0x08048385 in main () at debugtest.c:15

You can break out of the loop by changing the value of p with the set command. Try entering the following three commands:

 (gdb) set variable p = 0 (gdb) p *p Cannot access memory at address 0x0 (gdb) c Continuing. Program exited normally.

Using CTRL-C to interrupt your program works well in the case of an infinite loop, but often you want to look at why a specific piece of code isn’t working. You can force the debugger to stop on a particular line by setting a breakpoint using the break (or b) command. Next enter

 (gdb) b debugtest.c:6 Breakpoint 1 at 0x8048355: file debugtest.c, line 6.

Now if you run the program again, you will see

 (gdb) r Starting program: /home/nate/debugtest Reading symbols from shared object read from target memory...done. Loaded system supplied DSO at Oxa48000 Breakpoint 1, foo () at debugtest.c:6 6         unsigned int * p = &i; (gdb)

You can remove the breakpoint with the delete (or d) command:

 (gdb) d 1

You can then step (or s) through program execution one line at a time:

 (gdb) s 8          while(p) { (gdb) s 9          i++; (gdb) s 8          while(p) {

When you are done with your debugging session, run the quit (or q) command. If you launched the program inside the debugger and it’s still running, you will get a prompt:

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

Saying y will kill the program.




UNIX. The Complete Reference
UNIX: The Complete Reference, Second Edition (Complete Reference Series)
ISBN: 0072263369
EAN: 2147483647
Year: 2006
Pages: 316

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