Debugging with GDB
GDB is a useful, easy to use debugging tool. Using GDB you can debug programs written in different programming languages like C, C++, Ada etc. and compiled for different architectures like VxWorks, Power PC etc. In this article, we will discuss basic GDB commands and a real-life example which will help you getting started with GDB.
Debugging is a part and parcel of a programmer's life. The purpose of a debugger is to allow you to peek inside a program while it executes or the moment it crashed. Using a debugger you can find the exact point in the code where the program is misbehaving, and then fix it appropriately. Debugging a piece of code can be real challenging, especially if it has been written by somebody else and spans multiple source files. Various debugging tools are available for different languages and different platforms.
In this article, we will discuss a very powerful command line based tool: GDB.
Introduction
GDB is free software, protected by the GNU General Public License (GPL). GDB or The GNU Source-Level Debugger can be used to debug programs written in C, C++, Fortran, Objective-C and many other languages. GDB can do the following things to help you catch bugs in your programs:
- ·Start your program, specifying anything that might affect its behavior.
- ·Make your program stop at specified locations or specified conditions.
- ·Examine what has happened, when your program stopped.
- ·Change things in your program, so you can experiment with correcting the effects of one bug and go on to learn about another.
Playing with GDB
To start GDB, simply fire up a Linux terminal and type 'gdb'. Once started GDB reads commands from the terminal until you exit from it.
The most usual way to start GDB is:
$ gdb program
where program is an executable file.
If you want to debug a running process, then type:
where pid is the process-id of the running process.$ gdb program pid
To display help and all available options and brief description of their use, type:
$ gdb --help or $ gdb -h
If you need to execute shell commands during your debugging session, there is no need to quit GDB; you can just use the shell command:
(gdb) shell command-string
For e.g. (gdb) shell ls
This command will display all the files in the current working directory within the GDB environment.
Here, (gdb) is the GDB prompt.
For running your program, simply type 'run':
For quitting GDB, type 'quit':(gdb) run
Logging GDB's output(gdb) quit
You can save the output of GDB commands to a file. Several commands are available to control GDB's logging:
Enable logging.
(gdb) set logging on
Disable logging.
(gdb) set logging off
Change the name of the current logfile.
(gdb) set logging file <filename>
Show the current values of logging settings.
(gdb) show logging
Getting help in GDB
GDB has a very comprehensive and easy to use help:
You can use help with no arguments to display a short list of named classes of commands.(gdb) help
Using one of the help classes as an argument, you can get a list of individual commands in that class.(gdb) help class
With a command name as an argument, GDB displays a short summary about the command.(gdb) help command
In addition to help, you can use the GDB commands ‘show’ and ‘info’ to inquire about the state of GDB or the program being debugged.
This command describes the state of your program. For example, you can list the breakpoints you have set with info breakpoints. You can get a complete list of info sub-commands with help info.(gdb) info
This command describes the state of GDB itself. For example, you can list the history of commands you have typed using show commands. You can get a complete list of show sub-commands with help show.(gdb) show
Getting Debugging Information
In order to debug a program effectively, you need to generate debugging information when the program is compiled. This debugging information is stored in the object file.
To generate debugging information, specify the '-g' option when the program is compiled. For example:
$ gcc -g -o <output-file> <source-file>
Breakpoints, watchpoints and catchpoints
A breakpoint makes your program stop whenever a certain point in the program is reached. You can set breakpoints with the break command and its variants, to specify the place where your program should stop by line number, function name or exact address in the program.
A watchpoint is a special breakpoint that stops your program when the value of an expression changes.
A catchpoint is another special breakpoint that stops your program when a certain kind of event occurs, such as loading of a library.
GDB assigns a number to each breakpoint, watchpoint, or catchpoint when you create it. Each breakpoint may be enabled or disabled. A disabled breakpoint has no effect on your program until enabled. There are several ways of setting a breakpoint:
Sets a breakpoint at the specified line number in the current source file.(gdb) break line-number
Sets a breakpoint at entry to the function function(gdb) break function
Sets a breakpoint at the specified line-number in the source file filename.(gdb) break filename:line-number
Sets a breakpoint at entry to the function function found in file filename.(gdb) break filename:function
Sets a breakpoint at the specified address.(gdb) break *address
Sets a breakpoint with condition cond; evaluate the expression cond each time the breakpoint is reached, and stop only if cond evaluates as true.(gdb) break ... if cond
Note: To check the status of user-set breakpoints, type info breakpoints.
To delete a breakpoint, use the delete or clear commands.
To delete a breakpoint, use the delete or clear commands.
Stepping and Continuing
Stepping means executing one more 'step' of your program, where 'step' can be a line of source code or a machine instruction. Continuing means resuming program execution until your program completes normally. When your program execution stops due to a breakpoint, you can either step one instruction at a time or continue the program execution normally.
Continue running the program until control reaches a different source line, then stop it and return control to GDB.(gdb) step
Continue program being debugged, after signal or breakpoint.(gdb) continue
Step program, proceeding through subroutine calls. This is similar to step, but function calls that appear within the line of code are executed without stopping.(gdb) next
Stacks and Backtraces
Stacks
When your program stops, either due to a breakpoint or a signal, the first thing you need to know is where exactly it stopped and how it got there. Each time your program performs a function call; information about the call is generated, which includes the location of the call in your program, the arguments of the call and the local variables of the function being called. The information is saved in a block of data called a stack frame. The stack frames are allocated in a region of memory called the call stack.
GDB offers several commands for examining the stack when your program stops.
Backtraces
A backtrace is a summary of how your program got where it is. Each line in the backtrace shows the frame number and the function name. The backtrace also shows the source file name and line number, as well as the arguments to the function.
Printing source lines
To print source lines from a file, use the list command. By default, ten lines are printed. Here are the forms of most commonly used list commands:
Print lines centered around line number linenum in the current source file.(gdb) list linenum
Print lines centered around the beginning of function function.(gdb) list function
Display the number of lines that list prints.(gdb) show listsize
Make the list display count source lines.(gdb) set listsize count
Note: For more options about the list command, type:
(gdb) help list
Printing and Examining Data
To examine data in your program you can use the print and the x command. It evaluates and prints the value of an expression you have typed.
(gdb) print expr
Evaluate and print the value of the specified expression expr.
If you omit expr, last printed value is displayed again.(gdb) print
Do not print addresses when displaying their contents.(gdb) set print address off
Show whether or not addresses are to be printed.(gdb) show print address
You can use the command x (for examine) to examine memory in any of the several formats:
Use the x command to examine memory using various formats (/fmt).(gdb) x /fmt addr
Tip: If you want to print the value of an expression frequently, you might want to add it to the automatic display list so that GDB prints its value each time the program execution stops.
Display the current values of the expressions on the list.(gdb) display
(gdb) display expr
Add the expression expr to the list of expressions to be displayed each time your program stops.
[BREAK=A Real Life Example & Summary]
A real-life example
Consider the following example which compares the length of two strings and displays which one is longer (Figure1 and Figure2):
___________
_________Figure1 _________________________________ Figure2
Compile the program as follows:
$ gcc -Wall -g -o compare compare.c
Now, run the program:
String str1=linux is longer$ ./compare
Though the program executed successfully, the output is not what we expected. The output should have been as follows:
String str2=linuxforyou is longer
Let’s debug the program using GDB.
GNU gdb Red Hat Linux (6.1post-1.20040607.43rh)$ gdb ./compare
Copyright 2004 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"...Using host libthread_db library "/lib/libthread_db.so.1".
# Enable logging
Copying output to gdb.txt.(gdb) set logging on
(gdb) set listsize 15
1 /*(gdb) list
2 * Program to compare two strings
3 * Filename: compare.c
4 */
5 #include "compare.h"
6 void compare(char*, char*);
7
8 int main()
9 {
10 char *str1 = "linux";
11 char *str2 = "linuxforyou";
12 compare(str1, str2);
13 return 0;
14 }
15
Starting program: /home/raman/gdb_progs/compare(gdb) run
Reading symbols from shared object read from target memory...done.
Loaded system supplied DSO at 0xffffe000
String str1=linux is longer
Program exited normally.
# Set a breakpoint at the string compare function
Breakpoint 1 at 0x804847a: file compare.h, line 11.(gdb) break compare
Num Type Disp Enb Address What(gdb) info breakpoints
1 breakpoint keep y 0x0804847a in compare at compare.h:11
warning: cannot close "shared object read from target memory": File in wrong format(gdb) run
Starting program: /home/raman/gdb_progs/compare
Reading symbols from shared object read from target memory...done.
Loaded system supplied DSO at 0xffffe000
Breakpoint 1, compare (str1=0x804863c "linux", str2=0x8048642 "linuxforyou")
at compare.h:11
11 len1 = strlen(str2);
# If you don't want to display addresses, then type:
# Automate the display of str1, str2, len1, len2(gdb) set print address off
1: str1 = "linux"(gdb) display str1
2: str2 = "linuxforyou"(gdb) display str2
3: len1 = 134518588(gdb) display len1
4: len2 = 134513936(gdb) display len2
# Step to the next line
12 len2 = strlen(str1);(gdb) next
4: len2 = 134513936
3: len1 = 11
2: str2 = "linuxforyou"
1: str1 = "linux"
13 if(len1 > len2)(gdb) next
4: len2 = 5
3: len1 = 11
2: str2 = "linuxforyou"
1: str1 = "linux"
From the above values we can notice that the values of len1 and len2 are incorrect, so we can set them to correct values as follows:
$1 = 5(gdb) print len1=strlen(str1)
$2 = 11(gdb) print len2=strlen(str2)
16 printf("String str2=%s is longer\n", str2);(gdb) next
4: len2 = 11
3: len1 = 5
2: str2 = "linuxforyou"
1: str1 = "linux"
(gdb) continue
Continuing.
String str2=linuxforyou is longer
Program exited normally.
Bingo!!! This time the output is correct.
So, by debugging the program using GDB we found where the problem was.
The problem can now be fixed by making appropriate changes in the source file.
# Quit GDB as follows:
(gdb) quit
Summary
In this article we discussed about some of the commands which can help you getting started in GDB and debug programs spanning multiple source files. Debugging can be a nightmare for new programmers on *nix platforms. GDB is a very powerful and easy to use tool if you have the source code of the application. Using GDB along with other tools like strace, purify etc. can really make a programmer's life easy.
Happy Debugging!!!
Note: This article includes references from the GDB Manual and is licensed under the GNU Free Documentation License. Visit GNU Free Documentation License - GNU Project - Free Software Foundation (FSF) for more details.