
GDB Tutorial
Debugging Under Unix: How to use GDB
Who should read this?
This tutorial is aimed at a programmer who is new to the Unix command line
environment, to aid them with getting started using the gdb
debugger.
This tutorial assumes that you already know how to program in C, and that you are able to compile and execute programs.
What is GDB?
GDB is the GNU Debugger which can be used to debug programs wittten in several languages including C and C++. It allows you to inspect what a program is doing at certain points during execution. Errors such as Segmentation Faults may be easier to find with the help of a debugger.
Learning Objectives
After this tutorial you will be able to use GDB to help debug your programs by:- Setting breakpoints
- Printing variable values
- Navigate the function call stack at points during execution
- Analyse coredumps for issues
Source Code
To demonstrate some of the debugger commands, we will run some sample buggy programs. As we progress through the tutorial, we will use the debugger to locate and fix errors in the code. The code can be downloaded from the UWE Gitlab - GDB Tutorial repository.
Prerequisites
You will require a C development environment with the following tools installed:
- GCC
- Git
- GDB
Download the Source Code
Download the source code from the UWE Gitlab repository
git clone https://gitlab.uwe.ac.uk/jd7-white/GDB_Tutorial.git
Code Compilation
GDB is most effective when debugging programs that have been compiled with debug
symbols linked in to it. With the gcc
compiler, this is achieved
using the -g
flag.
It is also a good idea to use the -Og
(Capital 'oh', not a zero) flag to optimise the program
for debugging.
gcc -Wall -g -Og -o <output executable name> <input C file name>
Download and compile the example source code from the repository.
Debugging Programs - buggy_bubble.c
A broken bubble sort program
Compile the buggy_bubble.c source file using either the makefile or manually. The '-g' and '-Og' flags add debug information to the executable for use in GDB.Makefile:
make buggy_bubble
Manually:
gcc -g -Og -Wall -o buggy_bubble buggy_bubble.c
Run the program and invoke GDB
After compiling the program, run the executable and provide it with some sample input. The program will print a lot of junk and finally exit with a Segmentation fault.jon@Jon-Mint-19-VM:~/UWE/GDB_Tutorial$ ./buggy_bubble
Bubble sort program
Enter Number of Elements : 3
Enter elements (press enter between each entry)
Element 1 : 3
Element 2 : 2
Element 3 : 1
Press Enter to continue...
Before Sorting
3 2 1
Sorting
3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3
0 0 0 0 0 0 0 0 0 0 0 0 032628 461222368 0
:
0 0 0 0 0 0Segmentation fault (core dumped)
To start debugging, at the command line prompt, type: gdb buggy_bubble
Basic commands
You should now see the GDB command prompt waiting for your input. Typerun
to execute the program. The program will crash again, but GDB will tell you the
location of the crash, in this case, this is the function bsort() at line 70 of the
buggy_bubble.c file,.Program received signal SIGSEGV, Segmentation fault.
0x00005555555548ec in bsort (list=0x555555756040 <list>, numElements=3) at buggy_bubble.c:70
70 printf("%5d", list[j]);
We can look at the stack to see the function invocations at the time of the program
crash using the command bt
, which stands for 'backtrace'.(gdb) bt
#0 0x00005555555548ec in bsort (list=0x555555756040 <list>, numElements=3) at buggy_bubble.c:70
#1 0x0000555555554a67 in main (argc=<optimised out>, argv=<optimised out>) at buggy_bubble.c:116
From here we can see that bsort() is the cause of the crash and that it was called
from the main() function. We can get some more context about the code around this line
by issuing the list
command.(gdb) list
65 }
66 }
67
68 /* Print out the array as it stands now */
69 for (short j = 0; j < numElements; j--) {
70 printf("%5d", list[j]);
71 }
72
73 putchar('\n');
74
This shows that the program died within a loop that prints out the array.Analyse the fault
We can stop code execution when we enter bsort() and step through the code line by line. We can set breakpoints using theb
command or break
.(gdb) b bsort
Breakpoint 1 at 0x555555554867: file buggy_bubble.c, line 48.
Typing info break
will show the current list of breakpoints and their status.(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000555555554867 in bsort at buggy_bubble.c:48
'info' is a generic command that can be used to get information about a variety of
different things. issue the command info
on its own to see the types of
information that can be dumped.
Some useful commands are info locals
to
dump all local variables and their values and info registers
to dump the
value of the CPU registers.
To delete a breakpoint you can use the command delete breakpoint <n>
,
or to temporarily enable or disable them, use the enable
and disable
commands.
Now restart the program by typing run
and the program will halt its
execution at the start of the bsort() function. When execution is halted, you can
print variables and pointers using the command p
, examine memory x
, or
control the program execution flow. The c
or continue
command will continue the program execution until it hits another breakpoint or the
program crashes.
Pressing n
or next
executes the next line of code in its entirety. If the
line is a call to another function, the whole function call will be executed.
The s
or step
commands executes the next line of code, but
if the line is a function call, the execution steps into the new function and halts
at the next line
Step through the code, or continue until the program crashes./p>
Breakpoint 1, bsort (list=0x555555756040 <list>, numElements=3) at buggy_bubble.c:48
48 void bsort(short *list, short numElements) {
(gdb) n
51 printf("Sorting\n\n");
(gdb) n
Sorting
52 ncomp = 0;
(gdb) n
53 nswap = 0;
(gdb) c
Continuing.
2 0 0 0 0 0 0 0 0 0 0 0 0 032767-2083 1888
:
Program received signal SIGSEGV, Segmentation fault.
0x00005555555548ec in bsort (list=0x555555756040 <list>, numElements=3) at buggy_bubble.c:70
70 printf("%5d", list[j]);
We know from our previous analysis that the the code crashed whilst printing out
our list in a for loop. Lets look at some of the local variables.
Program received signal SIGSEGV, Segmentation fault.
0x00005555555548ec in bsort (list=0x555555756040 <list>, numElements=3) at buggy_bubble.c:70
70 printf("%5d", list[j]);
(gdb) print list[0]
$1 = 2
(gdb) print list[1]
$2 = 1
(gdb) print list[2]
$3 = 3
(gdb) print j
$4 = -2081
Our loop counter 'j' is set to a negative value, so we are printing out the index
into the list array incorrectly. Examining the code we can see an error with our for loop.
/* Print out the array as it stands now */
for (short j = 0; j < numElements; j--) {
printf("%5d", list[j]);
}
We are decrementing 'j' in the loop. Change the 'j--'
to 'j++'
, recompile and retest
the program, it should now work correctly.
Debugging a coredump file
During execution of a program, you may see a segmentation fault and a statement that a coredump has been generated.
$ ./buggy_bubble
Segmentation fault (core dumped)
However, in most Unix distributions, the generation
of coredumps for user processes is disabled. If a coredump is not generated in the
local directory, you can enable the generation of coredumps.
Enabling coredumps
You can verify the coredump settings with theulimit
command. This shows
the maximum size of coredump created. A value of 0
shows that
coredumps are disabled.
$ ulimit -c
0
To enable the coredump, you can modify this setting using the ulimit -c
command.
$ ulimit -c unlimited
Reading a coredump
Start GDB as per normal, but also provide the name of the coredump as a argument to gdb. GDB will start at the point of the crash enabling you to explore the memory and variables at the time of the crash.jon@Jon-Mint-19-VM:~/UWE/GDB_Tutorial$ gdb buggy_bubble core
GNU gdb (Ubuntu 8.1-0ubuntu3.2) 8.1.0.20180409-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
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".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from buggy_bubble...done.
[New LWP 25063]
Core was generated by `./buggy_bubble'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x000055811a7098ec in bsort (list=0x55811a90b040 <list>, numElements=3) at buggy_bubble.c:70
70 printf("%5d", list[j]);
(gdb) print j
$1 = -2081
Tips
Breakpoints
It is possible to set breakpoints in a variety of ways.Break into a line or a Function.
(gdb) break (b as shortcut) linenum
# Note: Break will take place at line <linenum> in the current source file.
# The current file is the last file whose code appeared in the debug console.
(gdb) b function
Break into a line which is relative to the current line.
(gdb) b +linenum
Break into a Function in a given file.
(gdb) b filename:function
Break on to a line in a given file.
(gdb) b filename:linenum
Break upon matching memory address.
If you have a program without debug symbols, then you can’t use any of the above options. Instead, the gdb allows you to specify a break point for memory addresses.
(gdb) b *(memory address)
Break after a condition.
(gdb) b <...> if condition
# Note:
#1: The symbol <...> implies that you can use any form of breakpoints.
#2: Some of them you've already seen in the previous tips.
Example: break linenum if variable==1