This tutorial is aimed towards those who want to learn how to use the GNU debugger GDB in Linux. The tutorial assumes that the reader knows what debugging is, has basic knowledge of C programming language, and knows how to compile and execute programs through Linux command line.
Programming and debugging goes hand in hand. Manually debugging a program through print statements can be really cumbersome and in-effective sometimes, especially if the problem is complex. It is in these situations, debuggers are used so that the problem can be debugged easily. Gdb is one of the most popular debugger in Linux. In this article, we will discuss the GNU debugger Gdb along with some practical examples.
Testing Environment
Here are the details of testing environment used for this article
- OS– Ubuntu 13.04
- Shell– Bash 4.2.45
- Compiler- gcc (Ubuntu/Linaro 4.7.3-1ubuntu1) 4.7.3
- Application– GNU gdb (GDB) 7.5.91.20130417-cvs-ubuntu
GDB Examples
If GDB is currently not installed on your system, you can easily install it through your favourite command line download manager like apt-get or yum.
Here is a buggy program that I’ll be using in the examples ahead.
#include<stdio.h> int div(int a, int b) { printf("\n Inside sum() \n"); return (a/b); } int main(void) { int ret = 0; int(*fptr)(int,int); fptr = div; ret = fptr(5, 0); printf("\n The function sum() returned [%d]\n", ret); return 0; }
You can see that the program is fairly simple. It consists of a call to a function div() through function pointer fptr declared in main() function. The arguments sent through the call are divided and the result is returned back to the main() function.
As you can clearly see, the second argument being passed in the call is ‘0’, when 5 will be divided by 0 in the function div(), it is bound to produce divide by zero error and program would crash.
Here is an example of the output, when the program was run on my machine
$ ./func_ptr
Inside sum() Floating point exception (core dumped)
So you can see that the program crashed.
I’ve kept the program very simple so that you can focus on how gdb works and not how the program works. Anyway, the gdb debugger will be able to produce useful information only when the program is compiled with debugging information enabled i.e., by using -g or -ggdb flag with gcc or g++.
I used the -ggdb flag while compiling this program. Here is what the man page has to say about this flag.
-ggdb Produce debugging information for use by GDB. This means to use the most expressive format available (DWARF 2, stabs, or the native format if neither of those are supported), including GDB extensions if at all possible.
Once the program is compiled with debugging information enabled, here is how you can use gdb to debug it
1. Load the program with gdb
To use GDB, the first step is to load the program with it. Here is how it is done
$ gdb func_ptr GNU gdb (GDB) 7.5.91.20130417-cvs-ubuntu Copyright (C) 2013 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". For bug reporting instructions, please see: gdb/bugs/>... Reading symbols from /home/test/practice/func_ptr...done. (gdb)
So you can see that the program can be loaded with the debugger by passing the executable name as a command line argument to the gdb command.
2. Run the program
To run the program, type the ‘run’ command after the program is successfully loaded (as shown in the previous step).
$ gdb func_ptr GNU gdb (GDB) 7.5.91.20130417-cvs-ubuntu Copyright (C) 2013 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". For bug reporting instructions, please see: gdb/bugs/>... Reading symbols from /home/test/practice/func_ptr...done. (gdb) run Starting program: /home/test/practice/func_ptr warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffa000 Inside sum()
Program received signal SIGFPE, Arithmetic exception. 0x00000000004005bc in div (a=5, b=0) at func_ptr.c:6 6 return (a/b);
So you can see that the ‘run’ command made the program execute, which received the much expected arithmetic exception towards the end.
3. Check the call trace
Sometimes, when the program code is huge and complex, it is important to know which function called the last function (where the problem occurred). This can be achieved by the backtrace command.
In our case, the backtrace command produced the following information.
(gdb) backtrace #0 0x00000000004005bc in div (a=5, b=0) at func_ptr.c:6 #1 0x00000000004005e8 in main () at func_ptr.c:17 (gdb)
So you can see that it clearly displayed the name of the function that called the div() function, which in this case is main().
(gdb) break div Breakpoint 1 at 0x4005aa: file func_ptr.c, line 5.
Breakpoints are nothing but halt stations. You can put a breakpoint at some line and when the program hits that line, the execution stops until you ask it to resume. Breakpoints help you to analyse the state of the program, its variables, their values, etc.
Breakpoints in GDB can be applied through the break command. For example, just after loading the program, I did something like
So this way, I put a break point at the entry line of the div() function. You can also mention line number (along with file name) instead of function name, while assigning break points. Now, when you will run the program, it will take a halt at this breakpoint.
(gdb) break div Breakpoint 1 at 0x4005aa: file func_ptr.c, line 5. (gdb) run Starting program: /home/test/practice/func_ptr warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffa000 Breakpoint 1, div (a=5, b=0) at func_ptr.c:5 5 printf("\n Inside sum() \n"); (gdb)
5. Step inside a function or move to next line
Once the program hits a breakpoint, you can make the program move ahead line by line through the next command.
(gdb) run Starting program: /home/test/practice/func_ptr warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffa000 Breakpoint 1, main () at func_ptr.c:12 12 int ret = 0; (gdb) next 15 fptr = div; (gdb) 17 ret = fptr(5, 0); (gdb) Inside sum() ...
You do not have to type ‘next’ every time. Just run it once and use the enter key to repeat the command again and again.
To step into a function, use the step command.
Conclusion
In this article, we studied the very basics of using GDB in Linux. The points covered here should give you a good platform to explore it more and more. You should definitely go through GDB’s man page in Linux and learn about other useful options it provides.