C/C++ Debugging using Microsoft Visual C++
Version 5.0
Winter Term, 1998
This write-up describes the basic use of the how to use the Microsoft Visual C++ 5.0 IDE to debug C/C++ programs interactively. The write-up assumes you are running MSVC++ 5.0 under Win95/Win NT.
Interactive debugging involves essentially the following approach: a program is run and its behavior is examined with the goal of detecting logical errors while the program is in execution. Potential errors include a flow of control which is not as desired or the setting of incorrect values to variables. The program can be stopped at critical points (statements at which you believe an error may occur or is about to occur). At these critical points variable values may be checked and even reset to new values so that you may check, and correct, your program's actions. In this document we explore the basic use of the interactive MSVC++ debugger.
Preparing the Project for Debugging
When you start up the MSVC++ 5.0 IDE, the IDE puts all the C++ code in a mode, which by default ensures that the project is built in the debug mode. You may want to refer to the document "Steps to get started with Visual C++ development environment (IDE)" to get to know how to create a project using MSVC++ 5.0 in the EECS 280 Web Page.
On the left side of the MSVC Window, you will see a window ( looking like notebook) with three pages in it. The First Page is called "ClassView", the next "FileView" and the final "InfoView". The "ClassView" has the list of all the classes used in the project. "FileView" contains the lost of all the files used in the project and "InfoView" contains any helpful information, that you may need like some help on C/C++ standard library functions.
In the FileView section, if you double click an item (ie one of the files) then it takes the cursor to that file. Similarly, in the Class View section, if you double click an item (ie. one of the classes) then the IDE takes the cursor to that particular class in that file.
Starting the MSVC++ Debugger:
Once you have compiled and linked ( which automatically buts the necessary debug information in your executable), you can start the Debugger by doing the following: From the main pull-down menu, click on Build, then select Start Debug and then click on Step Into. This gets the control to the main () function in the file containing the main () function. You can see this by a little yellow arrow pointing to the right near the function. At any point, if you wanted to know where the control is, you should look out for this yellow pointed cursor.
There also appears a small toolbar of icons, which help you do the debug operation you want to very easily. Note that you might want to re-size and/or re-arrange these
windows so that you can easily see each of them during debugging.
The toolbar looks like the above picture. Each of the toolbar icon has some function. The most important icons are the ones appearing in braces. These are Step Into, Step Over, Step Out and Run-To-Cursor. Similarly, the other important one is Stop-Debgging which is the second icon from left. We discuss about these later.
Debugger Windows:
The Program window contains three panes:
These panes can be resized by clicking and dragging the boundary between them.
In the Class-view page of the classes pane, you will see a set of functions under the Globals item. This gives you a list of all global functions in the project. (At any time if you click on the ‘+’ it expands to give a list of all the functions and a ‘-‘ appears in it place. If you want to shrink, then click on the ‘-‘ sign, a ‘+’ appears back. Double-Clicking on any of the function takes you to the function, even if the function is in a different file and the current control is in a different file.
If the project has got classes in them, then this pane contains the list of all the classes and if you click on the ‘+’ to the left of each of the class , you get a list of all the functions for that class under the class name. Double-clicking on any of the functions take you to the source code for that function.
Similarly, the File-View pane contains a list of all the files used in the project. As before, you can expand or shrink.
The LVD pane shows, top down, the current function calling chain, from the main function to the currently executing function; the function name which is selected in this pane is currently displayed in the source pane above. If you click on any function name in the call-chain pane, then the source for that function will be displayed in the source pane; the display will show the point in the source code at which execution currently resides. The LVD pane also shows a list of the currently executing function's local variables, along with any global variables, which this function references; the variable names are on the left of the pane with their current values on the right. If the variable is an array or a structure, with multiple values or a pointer, then it has a ‘+’ sign to the left of its name; clicking on this ‘+’ shows the values stored in this variable. (Option-clicking on the arrow will dereference multiple levels of pointers and directly displays data members of the variable.)
If you wish to change a variable's value, then double click on the value shown in the pane and type in a new value. If you double-click on an array or variable name, it will then be displayed in its own Data window; this is convenient for viewing variables with multiple values within them. Option-double-clicking a pointer variable opens an array Data window using the pointer value. The source pane displays the source code of the currently executing function, or the source code of the function which you selected in the call-chain pane. There is a solid yellow arrow to the left of the statement, which will be next executed in the function. To the far left of each executable statement, you can see a special column, which indicates if any kind of control operation has been placed (like break point) on this statement.
The UVD pane as the name suggests, contains the values of the variable, which the user explicitly specified to view. These may be certain global variables or variables in the main () function or can be anything. This also provides a way in which the user can view variables that are not necessarily in the local stack. In order to add any variable to the UVD pane, select the Debug option from the main pull-down menu and then click on Quickwatch. This brings up a little box with the current value of the variable and a button to add the variable to the UVD pane. If needed, the user can add it to the UVD pane.
Controlling Program Execution:
The debugger toolbar and the debugger control buttons tool pane (if it is showing) each contain six buttons which are used to control the execution of your program. You may also use the Debug menu or type appropriate keyboard sequences to perform the same operations. The debugger buttons (from left to right on the toolbar or on the control pane) function as follows:
Please also refer to the diagram above that gives all the icons for the functions described below.
Restart: This is the first button appearing on the left in the toolbar. If you are in the middle of debugging and you want to start all over again (when you missed something and you cannot reproduce it), then you can restart the debugging process.
Stop Debugging: The second button from the left, containing a square, suspends program execution. The Stop button works only when the Program running under the debugger. The Stop command in the Control menu will work if any debugger window is active. By stopping the debugging and running it again is equal to restarting the whole process of debugging.
Run: This button has a right pointing arrow on it and is the fourth button on the left. It starts your program, runs it until either (1) you stop it with the Stop button (see below), (2) a breakpoint is encountered (see below), (3) a run-time error occurs, or (4) program execution is complete. Unfortunately, there is one other way to stop execution - the program crashes. Selecting Run from the Control menu also executes the Run command.
Step Into: The fifth button from the left in the toolbar, you may also select Step Into in the Control menu to execute this command. Step Into executes the current source statement and follows (steps into) any function called from that source statement. If the current source statement contains no function call, then Step Into is the same as Step Over. If the current source statement contains a function call, then the debugger steps into the called function and stops with the current statement arrow on the first executable statement in the called function. Note that if the current source statement contains more than one function call, then the debugger will step into the first of the functions which is called, which may not be the first function in the source statement itself. You may not want to step into the system defined functions or library functions (like strcpy, strcat etc) as it will lead to some assembly code which is not exactly readable.
Step Over: The sixth button from the left, it executes the current source statement (the one with the arrow in the source pane) and moves the current statement arrow to the next statement to be executed in your source code, regardless of whether the current statement contains one or more function calls. The Step Over button allows you to single step through your source code independent of function boundaries. The entire current source statement is completely executed, including any function calls it contains; if the source statement is the last statement in a function, then control returns to the next source statement in the calling function. Step Over can also selected from the Control menu.
Step Out: The seventh button from the left, Step Out command can also be selected from the Control menu. Step Out executes the rest of the source statements in the currently executing function, including any function calls in those source statements. When the current function returns to its caller, the debugger stops the program with the current statement arrow on the next source statement in the calling function. Step Out allows you to finish executing the current function, then stop, which is useful if you have Stepped Into a function and are through observing it.
Run-To-Cursor: If you want to just run through a specific portion of the code and stop right after that, the run-to-cursor command can be used. For example if you want to stop right after a 10,000 count for-loop you may not want to step-over the statement each of the time. Instead, you just move the cursor to the statement right after the for loop and execute the run-to-cursor command. You can also select the run-to-cursor command from the control menu.
The six debugger control buttons may be used to control execution of your program to a large degree. You can follow the order of execution as well as display the values of variables whenever execution is interrupted. The variable shown in the debugger window panes are always updated to their current values when execution stops and control returns to the debugger.
However, it is often desirable to obtain more direct control over program execution by utilizing breakpoints. Breakpoints are specific statements chosen by you in advance at which you want program execution to stop.
Breakpoints:
In the source pane of the Program window, position your cursor anywhere in the statement and use your right mouse button to click once. This brings up a context sensitive menu. If you select the Insert/Remove Breakpoint item from the context sensitive menu, at the left most end of the statement you will see a red circle and you have set a breakpoint on this source statement. When you run your program under the debugger, execution will stop at any source statement at which a breakpoint is set - the current statement arrow will point to the breakpoint statement, which will be the next statement executed in your program. Note that you can only set a breakpoint on a line of source code; therefore, it is good practice to put one source statement per source line. A breakpoint may be turned off (cleared) by clicking on the circle.. Breakpoints can be set or cleared during program execution whenever you are in the debugger proper.
By using breakpoints effectively, you can examine variable values at critical points in your program run. By setting a breakpoint at the beginning of each function in the source code, you can quickly check program flow and look for program loops. If you option-click a breakpoint marker, then a temporary breakpoint is set on that source statement; the temporary breakpoint will remain in effect until the next time that execution stops at this source statement, at which time the breakpoint is removed. Such temporary breakpoints allow you to execute your program until it reaches a particular statement once.
To see a list of all of the currently set breakpoints in your program, select Edit+Breakpoints from the Window menu; this produces a Breakpoint window which lists the source file and line number of each breakpoint currently set in the program. Clicking a breakpoint marker in this window turns the breakpoint on or off ; Selecting an entry in this window and clicking on the Edit Code button takes the cursor to the actual piece of code, where the breakpoint was set.
To clear all breakpoints in a program, select Edit+Breakpoint option from the main control menu. This brings up a dialog, which has a push button named "Remove All". Clicking on the push button clears all breakpoints from the Control menu.
It is also possible to set conditional breakpoints; a conditional breakpoint is a breakpoint, which has an attached condition - the breakpoint stops execution only if the condition is true when the breakpoint statement is reached. To create a conditional breakpoint:
Conditional breakpoints are useful for stopping when a variable has (or does not have) a certain value or for stopping inside a loop after a certain number of iterations. Note that the conditional expression should only involve variables which are defined within the scope containing the breakpoint statement.
Viewing and Changing Program Variables:
Now you know how to control program execution utilizing both the debugger control buttons and breakpoints; this allows you both to direct and to examine the flow of control in your program. This ability in itself will enable you to find many program errors. However, it is often necessary to check the values of program variables while your program is running, and perhaps to reset them, in order to find logical program errors.
The Program window displays all local variables and their values in the variables pane; these are the variables which are defined in the function containing the current statement pointer. If the current function references global variables, then these global variables will be displayed at the bottom of the variables pane below a dotted line. The value of a variable will be gibberish until the current function places a value in the variable; if the variable is a pointer (array) or structure, then you can click on the ‘+’ to the left of the variable name to see its component members. If you wish to see all of the global variables in the project, they are displayed under the global section discussed above. ( in the classes pane )
To change the value of a variable at any time (in any window or in any pane), simply double-click on the value and type in a new value. You can also do this by selecting the value (single-click) and then hitting either the Return or Enter keys. A variable value may be entered as a decimal/hexadecimal integer, a floating point number, a C or Pascal string, or a character constant. For strings and character constants, the appropriate quotes must be typed around the value; for a Pascal string, use /p as the first character in the string.
You can also view the variables by simply moving your mouse cursor near the variable for a couple of seconds and you will notice that the value of the variable pops up. However, this would out work always especially if you want to take a look at the contents of a pointer. In such cases, use your mouse cursor to explicitly select the expression ( eg., *p) and use your right-mouse button to bring up a pop-up menu and select quickwatch. This shows the value of the variable if it is simple or else, it shows the values of individual fields of the structure/class.
Quitting the Debugger - Runtime Errors
In MSVC++, the most frequent error is one of the following picture: Similarly, there are also messages but they are not of very importance to use, since they also exactly similar to this.