The following GDB quick tips chart will help you find the GDB commands for debugging your applications:
| Command | Short command | Description |
| gdb yourappln.exe | - | Opening an application in GDB for debugging |
| break main | b main | Set the breakpoint to the main function |
| run | r | Executes the program till it reaches the breakpoint for step-by-step execution |
| next | n | Executes the program one step at a time |
| step | s | Steps into the function to execute the function step by step |
| continue | c | Continues the execution of the program until the next breakpoint; if no breakpoints are set, it will continue the execution of the application normally |
| backtrace | bt | Prints the entire call stack |
| quit |
q or Ctrl + d |
Exits GDB |
| -help | -h | Displays the available options and briefly displays their use |
With the preceding basic GDB quick reference, let's start debugging our faulty application to detect the bug. Let's first start GDB with the following command:
gdb ./a.out
Then, let's add a breakpoint at main() to perform step-by-step execution:
jegan@ubuntu:~/MasteringC++Programming/Debugging/Ex1$ g++ main.cpp -g
jegan@ubuntu:~/MasteringC++Programming/Debugging/Ex1$ ls
a.out main.cpp
jegan@ubuntu:~/MasteringC++Programming/Debugging/Ex1$ gdb ./a.out
GNU gdb (Ubuntu 7.12.50.20170314-0ubuntu1.1) 7.12.50.20170314-git
Copyright (C) 2017 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 ./a.out...done.
(gdb) b main
Breakpoint 1 at 0xba4: file main.cpp, line 46.
(gdb) l
32
33 bool operator > (const MyInteger &rhsObject) {
34 return this->number < rhsObject.number;
35 }
36
37 friend ostream& operator << ( ostream &output, const MyInteger &object );
38
39 };
40
41 ostream& operator << (ostream &o, const MyInteger& object) {
(gdb)
After launching our application with gdb, the b main command will add a breakpoint to the first line of the main() function. Now let's try to execute the application:
(gdb) run
Starting program: /home/jegan/MasteringC++Programming/Debugging/Ex1/a.out
Breakpoint 1, main () at main.cpp:46
46 int main ( ) {
(gdb)
As you may have observed, the program execution has paused at line number 46 in our main() function, since we added a breakpoint to the main() function.
At this point, let's execute the application step by step, as follows:
(gdb) run
Starting program: /home/jegan/MasteringC++Programming/Debugging/Ex1/a.out
Breakpoint 1, main () at main.cpp:46
46 int main ( ) {
(gdb) next
48 vector<MyInteger> v = { 10, 100, 40, 20, 80, 70, 50, 30, 60, 90 };
(gdb) next
50 cout << "\nVectors entries before sorting are ..." << endl;
(gdb) n
Vectors entries before sorting are ...51 copy ( v.begin(), v.end() , ostream_iterator<MyInteger>( cout, "\t" ) );
(gdb) n
52 cout << endl;
(gdb) n
10 100 40 20 80 70 50 30 60 90
54 sort ( v.begin(), v.end() );
(gdb)
Now, let's add two more breakpoints at line numbers 29 and 33, as follows:
Breakpoint 1 at 0xba4: file main.cpp, line 46.Breakpoint 1 at 0xba4: file main.cpp, line 46.(gdb) run
Starting program: /home/jegan/Downloads/MasteringC++Programming/Debugging/Ex1/a.out
Breakpoint 1, main () at main.cpp:46
46 int main ( ) {
(gdb) l
41 ostream& operator << (ostream &o, const MyInteger& object) {
42 o << object.number;
43 }
44
45
46
int main ( ) {
47
48 vector<MyInteger> v = { 10, 100, 40, 20, 80, 70, 50, 30, 60, 90 };
49
50 cout << "\nVectors entries before sorting are ..." << endl;
(gdb) n
48 vector<MyInteger> v = { 10, 100, 40, 20, 80, 70, 50, 30, 60, 90 };
(gdb) n
50 cout << "\nVectors entries before sorting are ..." << endl;
(gdb) n
Vectors entries before sorting are ...
51 copy ( v.begin(), v.end() , ostream_iterator<MyInteger>( cout, "\t" ) );
(gdb) break 29
Breakpoint 2 at 0x555555554f88: file main.cpp, line 29.
(gdb) break 33
Breakpoint 3 at 0x555555554b80: file main.cpp, line 33.
(gdb)
From this, you will understand that the breakpoints can be added by the function name or by the line numbers as well. Let's now let the program continue its execution until it reaches one of the breakpoints that we have set:
(gdb) break 29
Breakpoint 2 at 0x555555554f88: file main.cpp, line 29.
(gdb) break 33
Breakpoint 3 at 0x555555554b80: file main.cpp, line 33.
(gdb) continue
Continuing.
Breakpoint 2, MyInteger::operator< (this=0x55555576bc24, rhsObject=...) at main.cpp:30
30 return this->number > rhsObject.number;
(gdb)
As you can see, the program execution paused at line number 29, as it gets invoked whenever the sort function needs to decide whether the two items must be swapped in the process of sorting the vector entries in an ascending order.
Let's explore how to inspect or print the variables, this->number and rhsObject.number:
(gdb) break 29
Breakpoint 2 at 0x400ec6: file main.cpp, line 29.
(gdb) break 33
Breakpoint 3 at 0x400af6: file main.cpp, line 33.
(gdb) continue
Continuing.
Breakpoint 2, MyInteger::operator< (this=0x617c24, rhsObject=...) at main.cpp:30
30 return this->number > rhsObject.number;
(gdb) print this->number
$1 = 100
(gdb) print rhsObject.number
$2 = 10
(gdb)
Did you see the way the < and > operators are implemented? The operator checks for the less than operation, while the actual implementation checks for the greater than operation, and a similar bug is observed in the > operator-overloaded method as well. Please check the following code:
bool operator < ( const MyInteger &rhsObject ) {
return this->number > rhsObject.number;
}
bool operator > ( const MyInteger &rhsObject ) {
return this->number < rhsObject.number;
}
While the sort() function is supposed to be sorting the vector entries in an ascending order, the output shows that it is sorting them in a descending order, and the preceding code is the root cause of the issue. Hence, let's fix the issue, as follows:
bool operator < ( const MyInteger &rhsObject ) {
return this->number < rhsObject.number;
}
bool operator > ( const MyInteger &rhsObject ) {
return this->number > rhsObject.number;
}
With these changes, let's compile and run the program:
g++ main.cpp -std=c++17 -g
./a.out
This is the output that you'll get:
Vectors entries before sorting are ...
10 100 40 20 80 70 50 30 60 90
Vectors entries after sorting are ...
10 20 30 40 50 60 70 80 90 100
Cool, we fixed the bug! Needless to say, you will have recognized how useful the GDB debugging tool is. While we have only scratched the surface of the GDB tool's capability, it offers many powerful debugging features. However, in this chapter, it would be impractical to cover every single feature the GDB tool supports; hence, I would strongly recommend you explore GDB documentation for further learning at https://sourceware.org/gdb/documentation/.