Debugging with gdb:-
Lets take an example and debug it.
Below program is giving a count of number which divides a given number in given range.
Ex. if passed parameter to function checkDivisible is 100(num), 1(startRange) and 14(endRange). then it will return count = 5 because 100 is divisible by 1,2,4,5 and 10 in range 1 to 14.
//countDivisible.c
#include<stdio.h>
int checkDivisible(int num,int startRange,int endRange)
{
int count=0,i=0;
for (i = startRange ; i <=endRange; i++)
{
if (num%i == 0)
count++;
}
return count;
}
int main()
{
int number = 1000,count=0,startRange=0,endRange=10;
count = checkDivisible(number,startRange,endRange);
printf("Total no count which divide number[%d] in range:[%d] to [%d] are:%d",number,startRange,endRange,count);
return 0;
}
Recompile the program for debugging and start debugging.
$cc -g -o countDivisible countDivisible.c
-g is used to compile it in debugging mode.
now start gdb:-
$gdb countDivisible
GNU gdb Red Hat Linux (6.3.0.0-1.143.el4rh)
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".
(gdb)
Set breakpoints and run a program:-(Step by step explanation is given in coming sections)
(gdb) break main
Breakpoint 1 at 0x80483cc: file countDivisible.c, line 17.
(gdb) run
Starting program: /iiidb/work/ram/hackerEarth/countDivisible
Breakpoint 1, main () at countDivisible.c:17
17 int number = 1000,count=0,startRange=0,endRange=10;
(gdb) n
18 count = checkDivisible(number,startRange,endRange);
(gdb) s
checkDivisible (num=1000, startRange=0, endRange=10) at countDivisible.c:5
5 int count=0,i=0;
(gdb) p startRange
$1 = 0
(gdb) p endRange
$2 = 10
(gdb) p num
$3 = 1000
(gdb) list
1 #include<stdio.h>
2
3 int checkDivisible(int num,int startRange,int endRange)
4 {
5 int count=0,i=0;
6 for (i = startRange ; i <=endRange; i++)
7 {
8 if (num%i == 0)
9 count++;
10 }
(gdb) n
6 for (i = startRange ; i <=endRange; i++)
(gdb) n
8 if (num%i == 0)
(gdb) n
Program received signal SIGFPE, Arithmetic exception.
0x08048399 in checkDivisible (num=1000, startRange=0, endRange=10) at countDivisible.c:8
8 if (num%i == 0)
(gdb) bt
#0 0x08048399 in checkDivisible (num=1000, startRange=0, endRange=10) at countDivisible.c:8
#1 0x080483f6 in main () at countDivisible.c:18
(gdb) cont
Continuing.
Program terminated with signal SIGFPE, Arithmetic exception.
The program no longer exists.
(gdb) quit
$
gdb online help:-
gdb has extensive online help
(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 all” for the list of all commands.
Type “help” followed by command name for full documentation.
Type “apropos word” to search for commands related to “word”.
Command name abbreviations are allowed if unambiguous.
(gdb)
Setting breakpoints:-
You can stop the program at any point by setting breakpoints. These cause the program to stop and return control to the debugger. You’ll be able to inspect variables and then allow the program to continue.
A number of commands are used for setting breakpoints. These are listed by gdb with help breakpoint :
(gdb)help breakpoint
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
delete breakpoints -- Delete some breakpoints or auto-display expressions
delete checkpoint -- Delete a fork/checkpoint (experimental)
delete mem -- Delete memory region
delete tracepoints -- Delete specified tracepoints
disable -- Disable some breakpoints
disable breakpoints -- Disable some breakpoints
disable display -- Disable some expressions to be displayed when program stops
disable mem -- Disable memory region
disable tracepoints -- Disable specified tracepoints
enable -- Enable some breakpoints
enable delete -- Enable breakpoints and delete when hit
enable display -- Enable some expressions to be displayed when program stops
enable mem -- Enable memory region
enable once -- Enable breakpoints for one hit
enable tracepoints -- Enable specified tracepoints
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
Type “help” followed by command name for full documentation.
Type “apropos word” to search for commands related to “word”.
Command name abbreviations are allowed if unambiguous.
you can set break point at any function of source file. In our example we have set break point at main function. That's why when we run it, it get stop at main.
(gdb) b main
Breakpoint 1 at 0x80483cc: file countDivisible.c, line 17.
(gdb) r
Starting program: /iiidb/work/ram/hackerEarth/countDivisible
Breakpoint 1, main () at countDivisible.c:17
***17 int number = 1000,count=0,startRange=0,endRange=10;***
(gdb)
you can set break point at any line in source code.
in our example, we can set break point at main as countDivisible.c : 14 (source file name> :
(gdb) break <fileName>:<line#>
(gdb) break countDivisible.c :14
So here both breakpoints are pointing to same location.
Running a program:-
You can execute the program with the run command. Any arguments that you give to the run command are passed to the program as its arguments.
In our example:
(gdb) run
Starting program: /iiidb/work/ram/hackerEarth/countDivisible
The program runs incorrectly as before. When the program faults,gdb shows the reason and the location. You can now investigate the underlying cause of the problem.
8 if (num%i == 0)
(gdb) n
Program received signal SIGFPE, Arithmetic exception.
0x08048399 in checkDivisible (num=1000, startRange=0, endRange=10) at countDivisible.c:8 //shows fault reason: the exact line number of source file
8 if (num%i == 0)
Here a segmentation fault occured (signal SIGFPE). Here we can see that gdb is giving us fault information (exact line number and source file).
Stack Trace:-
You can see the function tree, how you got to this position by using the backtrace command:
In our example:
(gdb) backtrace
#0 0x08048399 in checkDivisible (num=1000, startRange=0, endRange=10) at countDivisible.c:8
#1 0x080483f6 in main () at countDivisible.c:18
(gdb)
its showing tree structure of function call.
1) function call is main() at countDivisible.c : 8
2) then second funcion call is countDivisible.c : 18
This is a very simple program, and the trace is short because you haven’t called many functions from within other functions. This can be very useful when debugging functions that are called from many different places.
The backtrace command may be abbreviated bt , and, for compatibility with other debuggers, the where command has the same function.
Go to next line:-
you can go to next line to debug by using 'n' command.
Examining variables:-
print command shows the content of the variable.
(gdb) print $var
$1 10
set command is used to set a value in variable.
(gdb) set var=50
(gdb) print var
$1 = 50
here value of var has been changed from 10 to 50.
Go inside the function definition:-
While debugging if a contro reaches to function call and you want to go inside the function definition for debug, they you can go using 's' command. Like in our program we went inside the function.
(gdb) n
18 count = checkDivisible(number,startRange,endRange);
(gdb) s
checkDivisible (num=1000, startRange=0, endRange=10) at countDivisible.c:5
5 int count=0,i=0;
(gdb)
Empty command:-
All versions support an “empty command”; hitting Enter executes the last command again. This is especially useful when stepping through a program line by line with the step or next commands.
Listing a program:-
You can view the source code of the program from within gdb by using the list command. This prints out a portion of the code around the current position. Subsequent uses of list will print out more. You can also give list a function name as an argument and it will show the code at that position, or a pair of line numbers and it will list the code between those line numbers.
(gdb) list
1 #include<stdio.h>
2
3 int checkDivisible(int num,int startRange,int endRange)
4 {
5 int count=0,i=0;
6 for (i = startRange ; i <=endRange; i++)
7 {
8 if (num%i == 0)
9 count++;
10 }
(gdb)
Continue:-
If you want to allow continue, use command cont or c. Control will exit from that break points and it runs to compilation of program ahead. In our example:-
(gdb) cont
Continuing.
Program terminated with signal SIGFPE, Arithmetic exception.
The program no longer exists.
(gdb)
Exit from gdb:-
To exit from gdb, use quit command.
$(gdb)quit
Set print elements 0:-
Set a limit on how many elements of an array GDB will print. If GDB is printing a large array, it stops printing after it has printed the number of elements set by the set print elements command. This limit also applies to the display of strings. When GDB starts, this limit is set to 200. Setting number-of-elements to zero means that the printing is unlimited.
$(gdb) set print elements 0
Debugging programs with multiple processes:-
GDB provides support for debugging programs that create additional processes using the fork or vfork function. By default, when a program forks, GDB will continue to debug the parent process and the child process will run unimpeded.
If you want to follow the child process instead of the parent process, use the command set follow-fork-mode.
Here mode can be:- parent In this mode GDB will continue debugging the parent process if a fork() or vfork() is called. This is the default mode. child In this mode GDB will switch to the child process if a fork() or vfork() is called.
Syntax:-
set follow-fork-mode parent
set follow-fork-mode child
show follow-fork-mode
Default mode:-
The default value for the follow-fork-mode setting is 'parent'.
Remarks:-
If you have set detach-on-fork to on, GDB will debug both the parent and the child process. Use the info inferiors command to show details and the inferior command to switch between them.
Example:-
//In this example we will debug the following C++ program:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
void func(int pid, int ret)
{
printf("My PID is %d, fork() returned %d\n", pid, ret);
if (ret)
printf("We are in the parent process\n");
else
printf("We are in the child process\n");
}
int main()
{
int r = fork();
func(getpid(), r);
return 0;
}
First we will debug the program with the default setting for follow-fork-mode:
(gdb) break main
Breakpoint 1 at 0x804848f: file forktest.cpp, line 17.
(gdb) run
Starting program: /home/testuser/forktest
Breakpoint 1, main () at forktest.cpp:17
17 int r = fork();
(gdb) show follow-fork-mode
Debugger response to a program call of fork or vfork is "parent".
(gdb) break func
Breakpoint 2 at 0x804844a: file forktest.cpp, line 7.
(gdb) continue
Continuing.
Breakpoint 2, func (pid=7975, ret=7980) at forktest.cpp:7
7 printf("My PID is %d, fork() returned %dpid, ret);
(gdb)
My PID is 7980, fork() returned 0
We are in the child process
(gdb) continue
Continuing.
My PID is 7975, fork() returned 7980
We are in the parent process
[Inferior 1 (process 7975) exited normally]
As GDB was configured to continue debugging the parent process, the child process produced the 'We are in the child process' text while GDB was stopped at a breakpoint in the parent process. When we issued the continue command, the parent process printed its message and exited.
Now we will see what happens when GDB is configured to switch to the child process:
(gdb) break main
Breakpoint 1 at 0x804848f: file forktest.cpp, line 17.
(gdb) run
Starting program: /home/testuser/forktest
Breakpoint 1, main () at forktest.cpp:17
17 int r = fork();
(gdb) set follow-fork-mode child
(gdb) break func
Breakpoint 2 at 0x804844a: file forktest.cpp, line 7.
(gdb) continue
Continuing.
My PID is 8025, fork() returned 8029
We are in the parent process[New process 8029]
[Switching to process 8029]
Breakpoint 2, func (pid=8029, ret=0) at forktest.cpp:7
7 printf("My PID is %d, fork() returned %dpid, ret);
(gdb) continue
Continuing.
My PID is 8029, fork() returned 0
We are in the child process
[Inferior 2 (process 8029) exited normally]
GDB has now switched to the child process, keeping the parent process run in the background. The value of ret in the breakpoint message was 0 indicating that we are in the child process.