5.7 The Visual C++ Debugger
The Visual C++ development environment contains a powerful debugger that allows the developer to monitor and control the program's execution as well as inspect the program's state. Described here are the basic commands needed for many simple debugging situations.
To use the debugger, it is important that a debug version of the program be built. The debug version contains information and special code that allows the debugger to examine and control the program during execution. The default configuration for the environment is to build both a debug version, containing the extra information and code, and a release version, which does not. The current configuration is shown in a small selection box above the view area. The debug configuration is named Win32 Debug. If the debug version is not currently selected, use the selection box controls to select it as the current configuration. The debug version can be disabled or enabled using the Configurations dialog box under the Build menu.
Starting and Stopping the Debugger
Three commands control the overall starting and stopping of the debugger. The Go command is used to begin the execution of the program under the debugger. When the execution under the debugger begins, the arrangement of the areas in the Visual C++ window and the contents of some menus changes to the debugger format, which is explained below. Typically, the developer has set some initial breakpoints, so that after the Go command, the program runs until a breakpoint is reached (whose condition is true if it is a conditional breakpoint), the end of the program is reached, or the program abnormally terminates due to a failure. The Go command is also used to resume execution after a breakpoint. The debugging session is ended using the Stop Debugging command. This command immediately terminates the program and the debugger, returning the window arrangement and the menu contents to that of the original configuration for building and editing the system. The developer may realize that the program has executed past a point that he or she wanted to examine. Since there is no general way to undo the program's execution, the Restart command can be used to begin the execution of the program again without leaving the debugger environment.
When the program is being executed under the debugger, the format of the Visual C++ display is changed to show three horizontal areas: the view area, the edit area, and the debugging-information area. This format of the Visual C++ display is designed to make the debugging information more prominent and easily accessible. The view area and the edit area have the same contents and purpose as previously described in section 5.4.
During debugging, the edit area is managed by the debugger which uses it to display the file containing the current line of code.. This aids the developer in seeing the code as it is executed. An arrow is drawn in the left margin to point to the line of code being executed. The postion of the cursuor in the edit area also has significance for some of the debugger commands: by placing the cursor at a particular line or at a particular variable, the developer is able to focus the debugger on that item. Using the cursor in this way makes it convenient for the developer to specify a line of code at which a breakpoint is to be set or indicate a variable whose value should be displayed.
The debugging-information area typically has two side-by-side windows: on the left is displayed information about the current execution context and, on the right, the state of variables that are being watched. The current execution context contains a scrollable list that shows the current invocation sequence. By selecting different entries in this list, the developer is able to examine the current sequence of method invocations and determine the variables currently being used by each of these methods. Variables of interest can be added to the watched area and their current values will be displayed and updated as the execution progresses.
The current execution-context window has three tabs at the bottom that allow the developer to select what information about the selected method invocation is displayed. The Auto tab displays the value of variables that are close to the line of code being executed by the method. The Locals tab displays all of the local variables of the method being executed. Finally, the this tab displays the current state of the object in which the method is executing.
A breakpoint is a debugging annotation added to a line of executable code that makes the program stop immeditately before executing that line of code and turns control over to the debugger. When the program is stopped at a breakpoint, the debugger commands can be used to examine and change the values of variables and objects, set new breakpoints or remove existing breakpoints, and control the subsequent execution of the program. It is often necessary to eliminate a breakpoint once it has done its job and the developer no longer wants the program to stop at the breakpoint. This is called deleting the breakpoint. A breakpoint that has been deleted no longer exists; it is forgotten.
Breakpoints may be set in two different ways; both begin by placing the edit area's cursor in the line of code where the breakpoint is to be added. When this is done, one way of adding a breakpoint is to follow the steps shown in the table below using the Edit menu. The Breakpoints dialogue box shows the list of breakpoints that are currently set. An alternative method of setting a breakpoint is to use the breakpoint button, whose icon is a hand held up in a "stop" gesture. This button is located immediately above the editing area. To set a breakpoint in this way, position the cursor as before in the edit area and then click the breakpoint button.
Breakpoints can be removed in either of two ways. One way is to use the list of current breakpoints shown in the botton part of the Breakpoints dialogue box. Next to this list is a Remove button. To remove a breakpoint ,simply select the breakpoint from the list and press the Remove button. A second way to remove a breakpoint is to position the edit cursor in a line containing a breakpoint (the colored-circle visual cue indicates which lines have breakpoints) and click the breakpoint button (the upheld hand icon). Once a breakpoint has been removed, it no longer has any effect on the program's execution.
A conditional breakpoint is a breakpoint that will stop the program's execution only when a given boolean condition is true. The condition can be attached to the breakpoint when the breakpoint is created or it can be attached to an already existing breakpoint. To attach the condition when creating the breakpoint, create the breakpoint using the Breakpoints dialogue. After the breakpoint has been entered in the Break at box, press the Condition button. To attach the condition to an existing breakpoint, select the breakpoint from the list of breakpoints at the botton of the Breakpoints dialogue box and then press the Condition button. Pressing the Condition button causes a Breakpoint Condition dialogue box to appear. Near the top of this box is an editable line where an arbitrary boolean condition can be placed. This boolean condition is written in the same syntax as normal C++ conditions (e.g., x == 10 && y < z ). The condition will be part of the breakpoint information shown in the breakpoint list in the Breakpoints dialogue box. Also, when a conditional breakpoint is reached in the execution, a pop-up dialogue box will appear showing the condition.
There are four commands for controlling the execution of the program
when the program is halted (typically via a breakpoint) in the
debugger: the three variations of the Step command and the Run to cursor command. These commands are shown in the table below. Each of
these four commands is similar in that they resume the execution
of the program and specify a "nearby" stopping point; they differ
in how the stopping point is defined. The Step In command begins executing the current line of code and stops again
when a different source line has been reached. If the code is
a sequence of simple statements (without method calls or branches)
then the Step In command has the effect of executing one line after another. The
developer can execute the Step In command repeatedly to reach a desired point in the execution.
If the line of code being executed has a method call, then the
execution will stop at the first line of code in the invoked method.
It is this case that gives meaning to the In part of the command's name. The Step Over command is similar to Step In, except that with it the debugger does not step into method calls.
With the Step Over command, the debugger will execute until control reaches another
line of code in the current method. The Step Over command is useful when the focus of debugging is on the current
method and not the methods that it uses. It is also useful in
stepping over system calls and library method (e.g., stream I/O
methods or windowing-system methods). The Step Out command completes the execution of the current method and stops
again when control has returned to the invoking method. This command
is useful when a method has been stepped into and the developer
wants to quickly finish the invoked method and continue debugging
at the point of the invocation. Finally, the Run to Cursor command allows the user to directly specify how far the execution
should proceed. The developer places the cursor at a line of code
to specify where the executio should stop again. The developer
must be careful that the specified line will actually be executed
and not branched around.
Each time your program invokes a method, the information about where in your program the invocation was made from is saved in a block of data called a stack frame. The frame also contains the arguments of the call and the local variables of the function that was called. All the stack frames are allocated in a region of memory called the run-time stack. When your program stops, the Visual C++ debugger has commands for examining the stack that allow you to see all of this information. The debugger automatically selects the stack frame for the currently executing method and describes it briefly.
The current sequence of method invocations can be examined via the scrollable Context list in the debugging information area (the leftmost part of the debugging information area). The scrollable list is organized in a stack-like fashiong: the current method being executed is at the top of this list, the method that invoked the current method is the next item in the list, and so on. By selecting different entries in this list, the developer can view the information about each method comprising the current execution sequence. For the currently selected method, the developer can view the most recently used data items (Auto tab), all of the local data of the method (Local tab), and the current values of all of the data of the object performing the method (this tab).
There are three ways to review the values of variables and objects. Each way of displaying this information has its own best use depending on how much context is associated with the information, over what time span is the information useful to the developer, and how much manipulation the developer must perform to obtain the information.
The first means of displaying the value of variables and objects is by holding the cursor stationary anywhere in the name of a variable or object in the edit area, which prompts the debugger to display adjacent to the cursor the current value of the variable or object. This technique is the simplest and easiest way to examine the value of variables and objects. However, the information is transitory in that when the cursor moves, the information disappears.
The second means of displaying the value of variables and objects applies to any variable or object included in the current execution sequence. Information about these variables and objects can be obtained by using the context portion (the left region) of the debugging-information area. The variables and objects that are part of the selected context are displayed in this area. It is useful to examine variables and objects by this method to gain a deeper understanding of the sequence of events underlying the current invocation sequence. The variables in this display are organized by the methods or objects of which they are a part and are ordered in time (the most recent invocation at the top of the Context selection list). The information in this display also changes over time as the execution sequence changes.
Watching the variable or object provides a thrid means to review their values. The value of such variables and objects is displayed in the watch part (the right part) of the debugging-information area. To create a watched variable, use the QuickWatch dialogue box, accessible from either the Debug menu or from the debug toolbar. In the QuickWatch dialogue box ,the name of the variable or object to be watch is entered in the Expression field. The Current value portion of the dialogue box displays the current value of the named variable or object. To maintain a continuing watch on the variable or object, click the AddWatch button. The value of the watched variable is then continuously displayed in the watch area. Watching a variable or object is useful when its value is frequently needed during a particular debugging operation. Having the value in the watch area makes the value conveniently accessible at all times.