M.2. Breakpoints and the run, stop, continue and print Commands
We begin our study of the debugger by investigating breakpoints, which are markers that can be set at any executable line of code. When program execution reaches a breakpoint, execution pauses, allowing you to examine the values of variables to help determine whether logic errors exist. For example, you can examine the value of a variable that stores the result of a calculation to determine whether the calculation was performed correctly. Note that attempting to set a breakpoint at a line of code that is not executable (such as a comment) will actually set the breakpoint at the next executable line of code in that function.
To illustrate the features of the debugger, we use the program listed in Fig. M.3, which creates and manipulates an object of class Account (Figs. M.1M.2). Execution begins in main (lines 1230 of Fig. M.3). Line 14 creates an Account object with an initial balance of $50.00. Account's constructor (lines 1022 of Fig. M.2) accepts one argument, which specifies the Account's initial balance. Line 17 of Fig. M.3 outputs the initial account balance using Account member function getBalance. Line 19 declares a local variable withdrawalAmount which stores a withdrawal amount read from the user. Line 21 prompts the user for the withdrawal amount; line 22 inputs the amount into withdrawalAmount. Line 25 subtracts the withdrawal from the Account's balance using its debit member function. Finally, line 28 displays the new balance.
Figure M.1. Header file for the Account class.
1 // Fig. M.1: Account.h 2 // Definition of Account class. 3 4 class Account 5 { 6 public: 7 Account( int ); // constructor initializes balance 8 void credit( int ); // add an amount to the account balance 9 void debit( int ); // subtract an amount from the account balance 10 int getBalance(); // return the account balance 11 private: 12 int balance; // data member that stores the balance 13 }; // end class Account |
Figure M.2. Definition for the Account class.
(This item is displayed on pages 1383 - 1384 in the print version)
1 // Fig. M.2: Account.cpp 2 // Member-function definitions for class Account. 3 #include 4 using std::cout; 5 using std::endl; 6 7 #include "Account.h" // include definition of class Account 8 9 // Account constructor initializes data member balance 10 Account::Account( int initialBalance ) 11 { 12 balance = 0; // assume that the balance begins at 0 13 14 // if initialBalance is greater than 0, set this value as the 15 // balance of the Account; otherwise, balance remains 0 16 if ( initialBalance > 0 ) 17 balance = initialBalance; 18 19 // if initialBalance is negative, print error message 20 if ( initialBalance < 0 ) 21 cout << "Error: Initial balance cannot be negative. " << endl; 22 } // end Account constructor 23 24 // credit (add) an amount to the account balance 25 void Account::credit( int amount ) 26 { 27 balance = balance + amount; // add amount to balance 28 } // end function credit 29 30 // debit (subtract) an amount from the account balance 31 void Account::debit( int amount ) 32 { 33 if ( amount <= balance ) // debit amount does not exceed balance 34 balance = balance - amount; 35 36 else // debit amount exceeds balance 37 cout << "Debit amount exceeded account balance. " << endl; 38 } // end function debit 39 40 // return the account balance 41 int Account::getBalance() 42 { 43 return balance; // gives the value of balance to the calling function 44 } // end function getBalance |
Figure M.3. Test class for debugging.
1 // Fig. M.3: figM_03.cpp 2 // Create and manipulate Account objects. 3 #include 4 using std::cin; 5 using std::cout; 6 using std::endl; 7 8 // include definition of class Account from Account.h 9 #include "Account.h" 10 11 // function main begins program execution 12 int main() 13 { 14 Account account1( 50 ); // create Account object 15 16 // display initial balance of each object 17 cout << "account1 balance: $" << account1.getBalance() << endl; 18 19 int withdrawalAmount; // stores withdrawal amount read from user 20 21 cout << " Enter withdrawal amount for account1: "; // prompt 22 cin >> withdrawalAmount; // obtain user input 23 cout << " attempting to subtract " << withdrawalAmount 24 << " from account1 balance "; 25 account1.debit( withdrawalAmount ); // try to subtract from account1 26 27 // display balances 28 cout << "account1 balance: $" << account1.getBalance() << endl; 29 return 0; // indicate successful termination 30 } // end main |
In the following steps, you will use breakpoints and various debugger commands to examine the value of the variable withdrawalAmount declared in line 19 of Fig. M.3.
1. |
Compiling the program for debugging. The GNU debugger works only with executable files that were compiled with the -g compiler option, which generates information that is used by the debugger to help you debug your programs. Compile the program with the -g command-line option by typing g++ -g -o figM_03 figM_03.cpp Account.cpp.
|
||
|
|||
2. |
Starting the debugger. Type gdb figM_03 (Fig. M.4). This command will start the GNU debugger and display the (gdb) prompt at which you can enter commands.
Figure M.4. Starting the debugger to run the program.
|
||
3. |
Running a program in the debugger. Run the program through the debugger by typing run (Fig. M.5). If you do not set any breakpoints before running your program in the debugger, the program will run to completion.
Figure M.5. Running the program with no breakpoints set.
|
||
4. |
Inserting breakpoints using the GNU debugger. You set a breakpoint at a specific line of code in your program. The line numbers used in these steps are from the source code in Fig. M.3. Set a breakpoint at line 17 in the source code by typing break 17. The break command inserts a breakpoint at the line number specified after the command. You can set as many breakpoints as necessary. Each breakpoint is identified in terms of the order in which it was created. The first breakpoint created is known as Breakpoint 1. Set another breakpoint at line 25 by typing break 25 (Fig. M.6). When the program runs, it suspends execution at any line that contains a breakpoint and the program is said to be in break mode. Breakpoints can be set even after the debugging process has begun. [Note: If you do not have a numbered listing for your code, you can use the list command to output your code with line numbers. For more information about the list command type help list from the gdb prompt.]
Figure M.6. Setting two breakpoints in the program.
|
||
5. |
Running the program and beginning the debugging process. Type run to execute your program and begin the debugging process (Fig. M.7). The program pauses when execution reaches the breakpoint at line 17. At this point, the debugger notifies you that a breakpoint has been reached and displays the source code at that line (17). That line of code contains the next statement that will execute.
Figure M.7. Running the program until it reaches the first breakpoint.
|
||
6. |
Using the continue command to resume execution. Type continue. The continue command causes the program to continue running until the next breakpoint is reached (line 25). Enter 13 at the prompt. The debugger notifies you when execution reaches the second breakpoint (Fig. M.8). Note that figM_03's normal output appears between messages from the debugger.
Figure M.8. Continuing execution until the second breakpoint is reached.
|
||
|
|||
7. |
Examining a variable's value. Type print withdrawalAmount to display the current value stored in the withdrawalAmount variable (Fig. M.9). The print command allows you to peek inside the computer at the value of one of your variables. This command will help you find and eliminate logic errors in your code. Note that the value displayed is 13the value read in and assigned to withdrawalAmount in line 22 of Fig. M.3. Use the print command to output the contents of the account1 object. When an object is output through the debugger with the print command, the object is output with braces surrounding the object's data members. In this case, there is a single data memberbalancewhich has a value of 50.
Figure M.9. Printing the values of variables.
|
||
8. |
Using convenience variables. When the print command is used, the result is stored in a convenience variable such as $1. Convenience variables, which are temporary variables, named using a dollar sign followed by an integer, are created by the debugger as you print values during your debugging session. A convenience variable can be used in the debugging process to perform arithmetic and evaluate boolean expressions. Type print $1. The debugger displays the value of $1 (Fig. M.10), which contains the value of withdrawalAmount. Note that printing the value of $1 creates a new convenience variable$3.
Figure M.10. Printing a convenience variable.
|
||
9. |
Continuing program execution. Type continue to continue the program's execution. The debugger encounters no additional breakpoints, so it continues executing and eventually terminates (Fig. M.11).
Figure M.11. Finishing execution of the program.
|
||
|
|||
10. |
Removing a breakpoint. You can display a list of all of the breakpoints in the program by typing info break. To remove a breakpoint, type delete, followed by a space and the number of the breakpoint to remove. Remove the first breakpoint by typing delete 1. Remove the second breakpoint as well. Now type info break to list the remaining breakpoints in the program. The debugger should indicate that no breakpoints are set (Fig. M.12).
Figure M.12. Viewing and removing breakpoints.
|
||
11. |
Executing the program without breakpoints. Type run to execute the program. Enter the value 13 at the prompt. Because you successfully removed the two breakpoints, the program's output is displayed without the debugger entering break mode (Fig. M.13).
Figure M.13. Program executing with no breakpoints set.
|
||
12. |
Using the quit command. Use the quit command to end the debugging session (Fig. M.14). This command causes the debugger to terminate.
Figure M.14. Exiting the debugger using the quit command.
|
In this section, you learned how to enable the debugger using the gdb command and run a program with the run command. You saw how to set a breakpoint at a particular line number in the main function. The break command can also be used to set a breakpoint at a line number in another file or at a particular function. Typing break, then the filename, a colon and the line number will set a breakpoint at a line in another file. Typing break, then a function name will cause the debugger to enter the break mode whenever that function is called.
Also in this section, you saw how the help list command will provide more information on the list command. If you have any questions about the debugger or any of his commands, type help or help followed by the command name for more information.
Finally, you learned to examine variables with the print command and remove breakpoints with the delete command. You learned how to use the continue command to continue execution after a breakpoint is reached and the quit command to end the debugger.