After preparing the executable file that contains the debugging information and symbols, let's run GDB to read all the symbols from the file and debug it. Run the following command to start the debugging process:
gdb rangen_boost_gdb
Our output will be as follows:
C:CPP>gdb rangen_boost_gdb GNU gdb (GDB) 7.8.1 Copyright (C) 2014 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-w64-mingw32". 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 rangen_boost_gdb...done. (gdb)_
We got the same output as the previous GDB output, except for the last line before (gdb)
. This line tells us that GDB has successfully read all the debugging symbols and is ready to initiate the debugging process. In this step, we can also specify the arguments, if our program needs any. Since our program does not need to specify any argument, we can ignore it for now.
To start the debugging process, we can call either the run
or start
command. The former will start our program under GDB, while the latter will behave similarly but will execute the code line-by-line. The difference is that if we have not yet set the breakpoint, the program will run as usual if we call the run
command, whereas the debugger will automatically set the breakpoint in the main block of code, stopping the program if it reaches that point, if we start with the start
command.
For now, let's use the start
command for the debugging process. Just type start
in the GDB prompt, and the console will append the following output:
(gdb) start Temporary breakpoint 1 at 0x401506: file rangen_boost.cpp, line 10. Starting program: C:CPP angen_boost_gdb.exe [New Thread 10856.0x213c] Temporary breakpoint 1, main () at rangen_boost.cpp:10 10 std::cout << "Select number among 0 to 10: ";
The debugging process is started. From the output, we can find that one breakpoint is created automatically inside the main
block which is in line 10. When there is no breakpoint, the debugger will choose the first statement inside the main block. That is why we get line 10
as our automatic breakpoint.
After we successfully start our program under GDB, the next step is to continue and step. We can use one of the following commands to continue and step the debugging process:
continue
: This command will resume the execution of the program until our program completes normally. If it finds a breakpoint, the execution will stop at the line where the breakpoint is set.step
: This command will execute just one more step of our program. The step might mean either one line of source code or one machine instruction. If it finds the invocation of function, it will come into the function and run one more step inside the function.next
: This command behaves similar to the step
command, but it only continues to the next line in the current stack frame. In other words, if the next
command finds the invocation of a function, it will not come into the function.For now, let's use the next
command. Type the next
command in the GDB prompt just after we call the start
command. We should get the following output:
(gdb) next Select number among 0 to 10: 11 std::cin >> guessNumber;
The GDB executes the 10th line and then continues to the 11th line. We will call the next
command again to continue the debugging process. However, if we just press the Enter key, the GDB will execute our previous command. This is why we now just need to press the Enter key, which will give us a blinking cursor. Now, we have to input the number that we guessed to be stored in the guessNumber
variable. I will input the number 4
, but you may enter your favorite number. Press the Enter key again to continue debugging as many times as needed to exit the program normally. The following output will be appended:
(gdb) 4 12 if(guessNumber < 0 || guessNumber > 10) (gdb) 17 boost::random::mt19937 rng; (gdb) 19 boost::random::uniform_int_distribution<> ten(0,10); (gdb) 20 int randomNumber = ten(rng); (gdb) 22 if(guessNumber == randomNumber) (gdb) 28 std::cout << "Sorry, I'm thinking about number " << randomNumber << " "; (gdb) Sorry, I'm thinking about number 8 30 return 0; (gdb) 31 }(gdb) 0x00000000004013b5 in __tmainCRTStartup () (gdb) Single stepping until exit from function __tmainCRTStartup, which has no line number information. [Inferior 1 (process 11804) exited normally]
As we can see in the preceding output, after we enter the number guessed, the program executes the if
statement to ensure that the number we entered is not out of range. If our guessing number is valid, the program continues to generate a random number. Our guessing number is then compared with a random number generated by our program. The program will give a different output irrespective of the two numbers being same or not. Unfortunately, my guessing number is different than the random number. You might obtain a different output if you are able to guess the number correctly.
Sometimes, we may want to examine our source file while we run the debugging process. Since the debugging information and symbol are recorded in our program, GDB can print the source code even if it is an executable file. To print the source code, we can type list
(or the l
command for the shortcut) in the GDB prompt. By default, GDB will print ten lines at every invocation of the command. However, we can change this setting using the set listsize
command. Also, to know the number of lines that will be displayed by the list
command, we can invoke the show listsize
command. Let's see the following command line output:
(gdb) show listsize Number of source lines gdb will list by default is 10. (gdb) set listsize 20 (gdb) show listsize Number of source lines gdb will list by default is 20. (gdb)_
We increase the number of lines to be displayed using the list
command. Now, every time the list
command is invoked, the output will display twenty lines of source code.
The following are several forms of the list
command, which are the most common:
list
: This command will show the source code for as many lines as the list size defines. If we call it again, it will display the remaining lines as many as the list size defines.list [linenumber]
: This command will display the lines centered on linenumber
. The command list 10
will display line 5 to line 14 since line 10 is at the center.list [functionname]
: This command will display lines centered on the beginning of the functionname
variable. The command list main
will display the int main(void)
function at the center of list.list [first,last]
: This command will display lines from first to last. The command list 15,16
will display line 15 and line 16 only.list [,last]
: This command will display lines ending with the last
. The command list ,5
will display line 1 to line 5.list [first,]
: This command will display all the lines starting with the specified line as the first. The command list 5,
will display line 5 to the rest of line if the number of the lines is more than the specified line number. Otherwise, it will display as many lines as the list size setting.list +
: This command will display all the lines following the lines last displayed.list -
: This command will display all the lines preceding the lines last displayed.If we suspect that a line makes an error, we can set a breakpoint in that line so that the debugger stops the debugging process at that line. To set a breakpoint, we can call the break [linenumber]
command. Consider that we want to stop at line 20, which contains the following code:
int randomNumber = ten(rng);
Here, we will have to call the break 20
command just after we load our program under GDB to set a breakpoint at line 20. The following output console illustrates this:
(gdb) break 20 Breakpoint 1 at 0x401574: file rangen_boost.cpp, line 20. (gdb) run Starting program: C:CPP angen_boost_gdb.exe [New Thread 1428.0x13f4] Select number among 0 to 10: 2 Breakpoint 1, main () at rangen_boost.cpp:20 20 int randomNumber = ten(rng); (gdb) next 22 if(guessNumber == randomNumber) (gdb) 28 std::cout << "Sorry, I'm thinking about number " << randomNumber << " "; (gdb) Sorry, I'm thinking about number 8 30 return 0; (gdb) 31 }(gdb) 0x00000000004013b5 in __tmainCRTStartup () (gdb) Single stepping until exit from function __tmainCRTStartup, which has no line number information. [Inferior 1 (process 1428) exited normally] (gdb)_
In the preceding output console, just after our program is loaded under GDB, we call the break 20
command. The debugger then sets a new breakpoint at line 20. Instead of calling the start
command as we previously did, we call the run
command to execute the program and let it stop when it finds a breakpoint. After we enter our guessing number, 2
for example, the debugger stops at line 20, the line at which we expected it to stop. Then, we call the next
command to continue the debugger and press the Enter key several times until the program exits.
If we want to delete a breakpoint, simply use the delete N
command, in which N
is the order in which all the breakpoints are set. If we do not memorize all the locations of the breakpoints that we set, we can call the info break
command to get a list of all breakpoints. We can also use the delete
command (without N
), which will delete all breakpoints.
We were already able to stop at our desired line. We can also discover the value of the variable that we use in our program. We can call the print [variablename]
command to print the value of any variable. Using the previous breakpoint, we will print the value of the variable randomNumber
. Just after the debugger hits the breakpoint in line 20, we will call the print randomNumber
command. Then, we call the next
command and print the randomNumber
variable again. Look at the following illustration of the command invocation:
(gdb) break 20 Breakpoint 1 at 0x401574: file rangen_boost.cpp, line 20. (gdb) run Starting program: C:CPP angen_boost_gdb.exe [New Thread 5436.0x1b04] Select number among 0 to 10: 3 Breakpoint 1, main () at rangen_boost.cpp:20 20 int randomNumber = ten(rng); (gdb) print randomNumber $1 = 0 (gdb) next 22 if(guessNumber == randomNumber) (gdb) print randomNumber $2 = 8 (gdb)_
As we can see in the preceding output, the following line is where the breakpoint is set:
int randomNumber = ten(rng);
Before the line is executed, we peek the value of randomNumber
variable. The value of the variable is 0
. Then, we call the next
command to instruct debugger to execute the line. After that, we peek at the value of the variable again, and this time it is 8
. Of course, in this experiment, you might get the different value rather than 8.
We will cheat our program by modifying the value of one of the variables. The value of a variable can be reassigned using the set var [variablename]=[newvalue]
command. To ensure the type of the variable that we want to modify, we can call the whatis [variablename]
command to get the required type of variable.
Now, let's change the value of the randomNumber
variable after the program assigns a random number to the variable. We will restart the debugging process, delete all the breakpoints we set already, set a new breakpoint at line 22, and continue the debugging process by typing the continue
command until the debugger hits the breakpoint in line 22. On this condition, we can reassign the value of the randomNumber
variable to be exactly the same as the value of the guessNumber
variable. Now, call the continue
command again. After this, we will be congratulated for guessing the correct number.
For more details, let's take a look at the following output console, which will illustrate the preceding step:
(gdb) start The program being debugged has been started already. Start it from the beginning? (y or n) y Temporary breakpoint 2 at 0x401506: file rangen_boost.cpp, line 10. Starting program: C:CPP angen_boost_gdb.exe [New Thread 6392.0x1030] Temporary breakpoint 2, main () at rangen_boost.cpp:10 10 std::cout << "Select number among 0 to 10: "; (gdb) info break Num Type Disp Enb Address What 1 breakpoint keep y 0x0000000000401574 in main() at rangen_boost.cpp:20 (gdb) delete 1 (gdb) info break No breakpoints or watchpoints. (gdb) break 22 Breakpoint 3 at 0x40158d: file rangen_boost.cpp, line 22. (gdb) continue Continuing. Select number among 0 to 10: 5 Breakpoint 3, main () at rangen_boost.cpp:22 22 if(guessNumber == randomNumber) (gdb) whatis randomNumber type = int (gdb) print randomNumber $3 = 8 (gdb) set var randomNumber=5 (gdb) print randomNumber $4 = 5 (gdb) continue Continuing. Congratulation, 5 is your lucky number. [Inferior 1 (process 6392) exited normally] (gdb)_
As we can see in the preceding output, when we call the start
command, the debugger asks us to stop the previous debugging process since it is still running. Just type the Y key and press the Enter key to answer the query. We can list all the available breakpoints using the info break
command and then delete the desired breakpoint based on the order we get from the info break
command. We call the continue
command to resume the debugging process, and when the debugger hits the breakpoint, we reassign the randomNumber
variable with the value of the guessNumber
variable. We continue the debugging process and successfully modify the value of the randomNumber
variable at runtime since we are congratulated by the program.
If we have many variables in the program, instead of printing all of the variables one-by-one, we can print the values of all the variables using the info locals
command.
I occasionally call the Windows shell command inside the GDB prompt, such as the cls
command to clear the screen, the dir
command to list the content of the active directory, and even the compiling command. If you also want to execute the Windows shell command, the GDB command that you can use is shell [Windows shell command]
. It actually just adds the shell
command before the Windows shell command and argument when needed. Let's see the following console output to understand executing the Windows shell command inside the GDB prompt. Let's take a look at the following output:
C:CPP>gdb GNU gdb (GDB) 7.8.1 Copyright (C) 2014 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-w64-mingw32". 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". (gdb) shell dir rangen_boost* /w Volume in drive C is SYSTEM Volume Serial Number is 8EA6-1DBE Directory of C:CPP rangen_boost.cpp rangen_boost.exe rangen_boost_gdb.exe 3 File(s) 190,379 bytes 0 Dir(s) 141,683,314,688 bytes free (gdb) shell g++ -Wall -ansi -I ../boost_1_58_0 rangen_boost.cpp -o rangen_boost_gdb_2 -g (gdb) shell dir rangen_boost* /w Volume in drive C is SYSTEM Volume Serial Number is 8EA6-1DBE Directory of C:CPP rangen_boost.cpp rangen_boost.exe rangen_boost_gdb.exe rangen_boost_gdb_2.exe 4 File(s) 259,866 bytes 0 Dir(s) 141,683,249,152 bytes free
In the preceding console output, we invoke the dir
command to list all files that begin with the rangen_boost
name within the active directory. Then, we invoke the compiling command to produce the rangen_boost_gdb_2.exe
executable file in the active directory. Then, we call the dir
command again to ensure that the rangen_boost_gdb_2.exe
executable file has been successfully created.