In the process of developing a program, you often need to find errors in the program, which requires debugging tools to help
To help you debug the program, of course, there are many debugging tools currently, and the debugging tool integrated in VC
The powerful functions make you feel at ease. Next we will introduce the use of the debugging tool in VC.
1 VC debugging tool
1.1 establish a debugging environment
Every time a project is created in VC, VC automatically creates two versions: release.
Version, and debug version, as it literally means, the release version is prepared after the program is completed.
The version used for compilation during release, while the debug version is used for debugging during development.
The debug version contains debugging information in Microsoft format without any code optimization.
The binary code of the executable program is optimized in the release version, but it does not contain any
Debugging information.
In the new project, you can see the debug version. To select the release version, you can select
The setting command in a single project. The project setteing dialog box is displayed on the screen.
Select release from the setting for drop-down list, and press OK to exit, 4.1.
Fig 4.1
You must use the debug version when debugging the program. In the project Setting dialog box
Set debugging options on the C/C ++ page.
Fig 4.2
The meanings of each option are as follows:
· Program database indicates a data file (. PDB) that generates information about a stored program. It contains
Types and symbolic debugging information;
· Line numbers only indicates that the. OBJ or. EXE file generated by the program after compilation and link is only
Including global and external symbols and row number information;
· C7 compatible indicates the generation of A. OBJ or. EXE file line number information and symbolic debugging.
Information;
· None indicates that no debugging information is generated.
1.2 General debugging process
Debugging is to observe the state of the program at a certain stage of the program running process.
The program runs continuously, so we must stop the program at a certain location. So what we do
The first task is to set up a breakpoint. Second, run the program again. When the program stops at the set breakpoint
Use various tools to observe the program status. After the program stops at the breakpoint, sometimes we need
To further observe the flow of the program, we will introduce
Settings, how to control program running, and the use of various observation tools.
1.3 How to set breakpoints
In VC, you can set multiple types of breakpoints. We can break these breakpoints in a way that works.
Points are divided into three types: 1. Location-related breakpoints; 2. Logical conditions-related breakpoints; 3. Windows
The following describes the three types of breakpoints.
First, we will introduce the location-related breakpoints.
1. The simplest thing is to set a general position breakpoint. You only need to move the cursor to the position where you want to set the breakpoint,
Of course, this line must contain a valid statement, and then press Add/Remove on the toolbar.
Press the breakpoint button or press the shortcut key F9. Then you will see it on the screen on the left side of this line.
A red dot indicates that the two have a breakpoint.
Fig 4.3
2. Sometimes you may not need to stop the program every time it runs here, but want to satisfy certain requirements.
In this case, you need to set a location-related logical breakpoint. To set this
You only need to select the breakpoint command from the edit menu. Then, the breakpoint dialog box
Will appear on the screen. Select the location tag in the breakpoint dialog box to make the location page
Surface dialog box, 4.4
Fig 4.4
Click Condition to bring up the breakpoint dialog box. In the expression editing box, enter
For example, x> = 3 or a + B> 25. Then, press OK to return.
Fig 4.5
This type of breakpoint mainly works based on its location, but it also integrates logical conditions to make it more flexible.
3. Sometimes we need to debug the program more deeply. We need to enter the assembly code of the program, so we
You need to set a breakpoint on the assembly code: to set this breakpoint, you only need to select debug from the View menu.
Window command,
Fig 4.6
Then select the disassembly sub-command, and the Assembly window will appear on the screen.
Fig 4.7
In the Assembly window shown in Figure 4.7, you will see the assembly code corresponding to the source program, where the source program uses the black
Body text display. The following is the corresponding assembly code. To set a breakpoint, we only need to move the cursor to your desired
Click Insert/remove breakpoints on the toolbar at the breakpoint.
A red dot appears on the right of the assembly code.
Fig 4.8
The breakpoint mentioned above is mainly due to its position, that is, when the program runs to the place where the breakpoint is set
The program will stop. But sometimes we set up a breakpoint that is only related to the logical condition, but not the location.
The following describes the breakpoints related to logical conditions.
(1) set the breakpoint triggered by logical conditions:
L select the breakpoint command from the edit menu. the breakpoint dialog will appear on the screen.
Box.
Fig 4.9
L select the data label in the breakpoint dialog box. The corresponding page will pop up.
Fig 4.10
L write your logical expression in the expression edit box on the data page in Figure 4.10, as shown in figure
(X = 3 );
Fig 4.11
L finally, press OK to return.
The methods for setting other breakpoints are similar. We will describe them one by one.
(2) monitoring Expression Change breakpoint:
L select the breakpoint command from the edit menu. the breakpoint dialog will appear on the screen.
Box.
L select the data label in the breakpoint dialog box. The corresponding page will pop up.
L write the expression you want to monitor in the expression editing box
L press OK to return.
(3) breakpoint for monitoring array changes:
L select the breakpoint command from the edit menu. the breakpoint pair will appear on the screen.
Dialog box.
L select the data label in the breakpoint dialog box. The corresponding page will pop up.
L in the expression editing box, write the name of the array to be monitored;
L in the number of elements edit box, enter the number of elements in the array to be monitored;
L press OK to return.
(4) monitoring the breakpoint that changes the array pointed to by the pointer:
L select the breakpoint command from the edit menu. Then, the breakpoint pair will appear on the screen.
Dialog box.
L select the data label in the breakpoint dialog box;
L in the expression editing box, enter * pointname, where * pointname is the pointer variable name.
;
L in the number of elements edit box, enter the number of elements in the array to be monitored;
L press OK to return.
(5) breakpoint for monitoring changes to external variables:
L select the breakpoint command from the edit menu. the breakpoint dialog box appears on the screen.
;
L select the data label in the breakpoint dialog box;
L enter the variable name in the expression editing box;
L click the drop-down key on the right of the expression editing box;
L select the advanced option. The advanced breakpoint dialog box appears;
L enter the corresponding function name (if needed) in the context box;
L press OK to close the advanced breakpoint dialog box.
L press OK to close the breakpoints dialog box.
(6) After talking about the location breakpoint and logical breakpoint, let's talk about the breakpoint related to Windows messages.
Note: Such breakpoints can only work on x86 or Pentium systems.
L select the breakpoint command from the edit menu. the breakpoint dialog will appear on the screen.
Box;
L select the message tag in the breakpoint dialog box. The corresponding page will pop up;
L enter the name of the Windows function in the break at wndproc edit box;
L in the set one breakpoint from each message to watch drop-down list box, select
Corresponding message;
L press OK to return.
1.4 run the control program
We have discussed how to set various breakpoints. Next we will introduce how to control program running. When we
From the menu build to the sub menu start debuging select go program to start running in the debug state
A small arrow pointing to the code to be executed is displayed after the pause due to the breakpoint.
Fig 4.12
Then, we can control the running of the program as required: There are four commands: Step over,
Step into, step out, run to cursor.
Fig 4.13
In Figure 4.13:
The step over function is to run the code pointed to by the current arrow (only one code is run ).
The step into function is used if the code indicated by the current arrow is a function call.
Enter the function for one-step execution.
The step out function allows the code pointed to by the current arrow to run the program
Function return.
Run to cursor enables the program to run to the code indicated by the cursor.
1.5 view the use of the tool
The most important thing in the debugging process is to observe the state of the program during running so that we can find out
Program errors. The state here includes the value of each variable, the value in storage, and the value in memory,
The value in the stack, so we need to use various tools to help us view the State of the program.
 pop-up debugging information bubble (Data tips pop_up information ).
When a program stops at a breakpoint, the easiest way to observe the value of a variable or expression is to use
Test Information bubble. To view the value of a variable, you only need to place the mouse over the variable in the source program window,
You will see an information bubble popped up, showing the value of this variable.
Fig 4.14
To view the value of an expression, select the expression first, and then place the cursor over the selected expression.
A message bubble pops up to display the expression value 4.15.
Fig 4.15
The Variable Window ).
In the View menu, select variables window in debug window. The variable window appears on the screen.
The variable name and its corresponding value are displayed. You will see three labels in the lower part of the variable observation window.
: Auto, local, and this select different labels. Different types of variables are displayed in this window.
Fig 4.16
○ Watch window ):
In the View menu, select the debug window command and the watch window sub-command. Variable Window
Will appear on the screen.
Fig 4.17
Double-click an empty row in the name column in the observation window in Figure 4.17 and enter the variable name or expression you want to view.
.
Fig 4.18
After you press enter, you will see the corresponding value. The observation window can contain multiple pages, which correspond to labels respectively.
Watch2, watch3, and so on. Assume that the expression you entered is a structure or an object.
You can click + on the right side of the expression to further observe the value of the member variable.
4.19.
Fig 4.19
○ Quick watch );
In the quick view variable dialog box, you can view the value of a variable or expression like in the observation window.
But we can also use it to change the variables during the running process. The specific operations are as follows:
(1) In the Debug menu, select the quick watch command. The Quick watch command will appear on the screen.
Watch dialog box;
Fig 4.20
(2) enter the variable name in the expression editing box and press Enter;
Fig 4.21
(3) The variable name and its current value 4.22 will appear in the current value grid:
Fig 4.22
(4) To change the value of a variable, double-click the name column corresponding to the variable and enter
Value;
(5) to add the variable to the observation window, click Add watch;
(6) Click the close button to return;
We can also directly view the values in the memory.
(1) Select debug windows and memory sub-commands from the View menu. Memory window appears
;
Fig 4.23
(2) enter the memory address you want to view in the address edit box and press Enter. In the corresponding memory address
The value is displayed in the memory window.
Fig 4.24
During debugging, sometimes we need to view or change the value in the register. We only need:
(1) Select the debug window and registers sub-options from the View menu. Registers
Window appears. In the registers window, the information is displayed in the form of register = value, where
Register indicates the register name, and value indicates the value in the register.
Fig 4.25
(2) If you want to modify the value of a register, use the tab key or mouse to move the cursor
Right of the changed value, and enter the value you want. Press enter to return.
In registers, there is a special type of register called a flag Register, which has eight flag bits:
OV indicates overflow;
Up indicates the direction;
EI indicates the interruption enable;
Sign is a symbol,
Zero indicates zero.
Parity indicates the parity check.
Carry is the carry sign.
2. Advanced debugging technology
We have discussed the use of the debugging tool before. You can use it to perform regular debugging, even if the program is located somewhere.
Stop and observe the current status of the program. These tools are also available in the debugger. But we know that
We know that these tools are not enough in the Development of VC programs. For faster and better development
Program development, we also need to use more advanced debugging tools. We know that in the development process using VC,
The use of MFC will greatly facilitate application development, so developers often use MFC to develop applications.
This is the reason why Microsoft provides some features in MFC to help you program
Debugging.
We know that in MFC, most classes are inherited from a class called cobject.
Is a virtual base class, but it defines many member functions, many of which are used to support programs
Debug member functions, such as dump and assertvalid. In addition, they all support trace, assert, and so on.
Macro, and supports checking memory vulnerabilities. We know that to support debugging, the class library must be in the performance
For this reason, Microsoft provides two different version libraries: Win32 debug.
Version and Win32 release version. As we have mentioned earlier, whenever we build a project,
We also have two corresponding versions. In your debug version project, the compiler connects to the debug version
In your release project, the compiler connects the release version of the MFC class library
Get as fast as possible. The following describes the use of these tools.
2.1 use of trace macros
The trace macro is like the printf function we used in the C language before, so that the program is running
Output some debugging information so that we can understand some program states. But the difference is that trace
Macros are output only in the debugging status, and the printf function used previously has an output in any situation.
Output. Like the printf function, the trace function can accept multiple parameters, for example:
Int x = 1;
Int y = 16;
Float z = 32.0;
Trace ("this is a trace statement \ n ");
Trace ("the value of X is % d \ n", X );
Trace ("x = % d and Y = % d \ n", x, y );
Trace ("x = % d and Y = % X and Z = % F \ n", x, y, z );
Note that the trace macro only plays a role in the project of the debug version.
, Trace Macro will be ignored.
2.2 Use of assert macros
In the development process, we can assume that a certain condition must be true as long as the program runs correctly. If not
So we can assert that the program is definitely wrong. In this case, we can use assert to set the disconnection.
Statement. The assert macro parameter is a logical expression. If the logical expression is
If the expression is false, a dialog box is displayed to warn you and
Stop program execution. At the same time, you are required to make a choice: abort, ignore, retry. If you choose
The system stops program execution. If you choose Ignore, the system ignores this error and continues to execute
If you select retry, the system recalculates the expression and activates the debugger. Same as trace
Macro. The assert macro only works in the debug version. In the release version, the assert macro is ignored.
2.3 use of assert_valid macro and overload of assertvalid () member Letter of class
The assert_valid macro is used to check the internal validity of an object at runtime. For example
For a student, we know that the age of each student must be greater than zero. If the age is less than zero, the student
The object must be faulty. In fact, the assert_valid macro is a member function converted to an object.
Assertvalid () is called, but this method is safer. Its parameter is an object pointer, through
This pointer is used to call its assertvalid () member function.
When we create a new class inherited from the cobject class
To perform a specific validity check.
2.4 use of dump functions of Objects
The dump function is used to output the member variables of an object in the specified format to help you diagnose
The internal status of the object. Like the assertvalid member function, dump is also a member function of the cobject class.
Number. The dump function parameter is a cdumpcontext object.
Input data. When you create a new class inherited from cobject, you can repeat the following steps:
Load your own dump function:
(1) Call the dump function of the base class to output the content of the base class;
(2) output data of this class to the cdumpcontest object.
For example, a typical dump function is defined as follows:
# Ifdef _ debug
Void cperson: dump (cdumpcontext & DC) const
{
// Call base class function first
Cobject: dump (DC );
// Now do the stuff for our specific class
DC <"Last Name:" <m_lastname <"\ n"
<"First name:" <m_firstname <"\ n ";
}
# Endif
You may have noticed that the entire function definition is included in # ifdef _ debug and # endif.
So that the dump member function only works in the debug version, but does not work in the release version.
3. Check for memory Vulnerabilities
You may already know that in C ++ and C, the pointer problem, that is, memory application and release, is a header.
If you applied for memory but didn't release it, and your program needs to run for a long time,
Then, the system resources will gradually decrease. When all the system resources are used up, the system will crash. Institute
In order to ensure the full release of resources in the development process. Next we will introduce the memory Vulnerability
Check.
Maybe you will ask, how does the system support the memory vulnerability check? In fact, in your debug version, all
The memory allocation function is overloaded. The specific process is as follows:
When memory is stored, it first calls the general memory allocation function to allocate a slightly larger memory block. In this memory block
There are four small blocks: heap information, buffer, user memory block, and buffer. The
A block is information about the heap. For example, the location where the memory is applied (file name, row number) and the class of the memory block.
Type (such as integer, floating point, or a class of objects. The second block is a buffer zone used to intercept users.
Out-of-bounds memory usage. The third block is the memory actually provided to the user, and the returned pointer also refers
Here. The fourth block is also a buffer, which acts the same as the second block.
After the applied memory is recorded, it is easier to check for memory vulnerabilities.
If you want to check whether a program segment has a memory vulnerability, you only need to request the system at the beginning of the program segment.
Create a memory usage image for you, record the memory usage at the beginning of the program, and then
At the end of the sequence segment, let the system make a memory image for you and compare the two images to check whether they are released.
Memory, if there is unreleased memory, according to the allocation information in this section to tell the user in
The Applied memory is not released.
Specifically, the following steps are required to check for memory vulnerabilities:
L create a cmemorystate object at the beginning of the program segment you are detecting and call its member letter
Checkpoint to get a snapshot of the current memory usage;
L create a cmemorystate object at the end of the program segment you are detecting and call its members.
Function checkpoint to get a snapshot of the current memory usage;
L create the third cmemorystate object, call its member function difference, and set the first
The cmemorystate object and the second cmemeorystate object are used as their parameters.
Otherwise, the function returns a non-zero value, indicating that the program segment has a memory vulnerability. Next let's look at
Typical Example:
// Declare the variables needed
# Ifdef _ debug
Cmemorystate oldmemstate, newmemstate, diffmemstate;
Oldmemstate. Checkpoint ();
# Endif
// Do your memory allocations and deallocations...
Cstring S = "This is a frame variable ";
// The next object is a heap object
Cperson * P = new cperson ("Smith", "Alan", "582130215 ");
# Ifdef _ debug
Newmemstate. Checkpoint ();
If (diffmemstate. Difference (oldmemstate, newmemstate ))
{
Trace ("memory leaked! \ N ");
}
# Endif