Debugging is the process of identifying and removing errors from software systems. The GNU/Linux operating system has a standard de facto tool (that is, not part of any standard, but used by almost anybody in the Linux world) called GDB. The GDB version installed on this book's Docker is version 8.2.91. Of course, there are graphical tools that can use GDB under the hood, but GDB on Linux is the way to go for its reliability, simplicity, and speed. In this recipe, we will debug the software we've written in the previous recipe.
Using GDB to debug a program
How to do it...
In order to use some of the GDB commands, we need to modify the previous program and add some variables in it:
- Open a shell and modify the hello.cpp file by typing in the following code:
#include <iostream>
int main()
{
int x = 10;
x += 2;
std::cout << "Hello World! x = " << x << std::endl;
return 0;
}
This is a very simple program: take a variable, add 2 to it, and print the result.
- Let's make sure that the program is compiled by typing the following command:
root@bffd758254f8:~/Chapter1# make
g++ -c hello.cpp
g++ -o hello hello.o
- Now that we have the executable, we will debug it. From the command line, type gdb hello:
root@bffd758254f8:~/Chapter1# gdb hello
GNU gdb (Ubuntu 8.2.91.20190405-0ubuntu3) 8.2.91.20190405-git
Copyright (C) 2019 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 hello...
(No debugging symbols found in hello)
(gdb)
- As you can see, the last line says (No debugging symbols found in hello). GDB doesn't have to debug symbols to debug the program, so we have to communicate to the compiler that the debug symbols are to be included during the compilation. We have to quit the current session; to do this, type q (Enter]. Then, edit the makefile, and add the -g option to the g++ compiler section (the hello.o target):
CC = g++
all: hello
hello: hello.o
${CC} -o hello hello.o
hello.o: hello.cpp
$(CC) -c -g hello.cpp
clean:
rm hello.o hello
- Let's run it again, but, first, we have to rebuild the application with the make command:
root@bcec6ff72b3c:/BOOK/chapter1# gdb hello
GNU gdb (Ubuntu 8.2.91.20190405-0ubuntu3) 8.2.91.20190405-git
Copyright (C) 2019 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 hello...
(No debugging symbols found in hello)
(gdb)
We're ready to debug it. A debug session typically includes setting breakpoints, watching the content of variables, setting watchpoints, and many others. The next section will show the most common debug commands.
How it works...
In the previous section, we have seen the steps necessary to create a program and a makefile. In this section, we'll learn how to debug the Hello World! program we developed.
Let's start by visualizing the code we're going to debug. We do this by running the l command (short for list):
(gdb) l
1 #include <iostream>
2 int main()
3 {
4 int x = 10;
5 x += 2;
6 std::cout << "Hello World! x = " << x << std::endl;
7 return 0;
8 }
We have to set a breakpoint. To set a breakpoint, we run the b 5 command. This sets a breakpoint to the code line number 5 in the current module:
(gdb) b 5
Breakpoint 1 at 0x1169: file hello.cpp, line 5.
(gdb)
It's time to run the program now. To run a program, we type the r command. This runs the hello program we started with GDB:
(gdb) r
Starting program: /root/Chapter1/hello
Once started, GDB will automatically stop at any breakpoint hit by the process flow. In this case, the process runs, and then stops at line 5 of the hello.cpp file:
Breakpoint 1, main () at hello.cpp:5
5 x += 2;
To proceed step by step, we run the n command (that is, step over) on GDB. This executes the current visualized line of code. A similar command is s (step into). If the current command is a function, it steps into the function:
(gdb) n
6 std::cout << "Hello World! x = " << x << std::endl;
the 'n' command (short for next) execute one line. Now we may want to check the content of the variable x after the increment:
If we need to know the content of a variable, we run the p command (short for print), which prints the content of a variable. In this case, as expected, x = 12 gets printed:
(gdb) p x
$1 = 12
Now, let's run the program until the end (or until the next breakpoint, if set). This is done with the c command (short for continue):
(gdb) c
Continuing.
Hello World! x = 12
[Inferior 1 (process 101) exited normally]
(gdb)
GDB really acts as an interpreter by letting the programmer step the program line by line. This helps the developer to troubleshoot problems, see the content of variables at runtime, change the status of variables, and more.
There's more...
GDB has a lot of very useful commands. In the following chapters, GDB will be explored more. There are four more commands to show here:
- s: Short for step. If called on a method, it steps into it.
- bt: Short for backtrace. Prints the call stack.
- q: Short for quit. Use to exit GDB.
- d: Short for delete. It removes a breakpoint. For example, d 1 removes the first breakpoint set.