Debugging Tools for Windows |
Conditional breakpoints can be very useful when you are trying to find bugs in your code. They cause a break to occur only if a specific condition is satisfied.
A conditional breakpoint is created by combining a breakpoint command with either the j (Execute If - Else) command or the .if token, followed by the gc (Go from Conditional Breakpoint) command. This breakpoint causes a break to occur only if a specific condition is satisfied.
The basic syntax for a conditional breakpoint using the j command is as follows:
The basic syntax for a conditional breakpoint using the .if token is as follows:
Conditional breakpoints are best illustrated with an example. The following command sets a breakpoint at line 143 of the Mysource.cpp source file. When this breakpoint is hit, the variable MyVar is tested. If this variable is less than or equal to 20, execution continues; if it is greater than 20, execution stops.
0:000> bp `mysource.cpp:143` ".if (poi(MyVar)>0n20) {} .else {gc}"
The preceding command has a fairly complicated syntax that contains the following elements:
If you want to see a message each time the breakpoint is passed or when it is finally hit, you can use additional commands in the single quotation marks or curly brackets. For example:
0:000> bp `:143` ".if (poi(MyVar)>5) {.echo MyVar Too Big} .else {.echo MyVar Acceptable; gc} "
These comments are especially useful if you have several such breakpoints running at the same time, because the debugger does not display its standard "Breakpoint n Hit" messages when you are using a command string in the bp command.
You can set a breakpoint that is conditional on a register value.
The following command will break at the beginning of the myFunction function if the eax register is equal to 0xA3:
0:000> bp mydriver!myFunction ".if @eax = 0xa3 {} .else {gc}"
However, the following similar command will not necessarily break when eax equals 0xC0004321:
0:000> bp mydriver!myFunction ".if @eax = 0xc0004321 {} .else {gc}"
The reason the preceding command will fail is that the MASM expression evaluator sign-extends registers whose high bit equals one. When eax has the value 0xC0004321, it will be treated as 0xFFFFFFFF`C0004321 in computations—even though eax will still be displayed as 0xC0004321. However, the numeral 0xc0004321 is sign-extended in kernel mode, but not in user mode. Therefore, the preceding command will not work properly in user mode. If you mask the high bits of eax, the command will work properly in kernel mode—but now it will fail in user mode.
You should formulate your commands defensively against sign extension in both modes. In the preceding command, you can make the command defensive by masking the high bits of a 32-bit register by using an AND operation to combine it with 0x00000000`FFFFFFFF and by masking the high bits of a numeric constant by including a grave accent ( ` ) in its syntax.
The following command will work properly in user mode and kernel mode:
0:000> bp mydriver!myFunction ".if (@eax & 0x0`ffffffff) = 0x0`c0004321 {} .else {gc}"
For more information about which numbers are sign-extended by the debugger, see Sign Extension.
In WinDbg, you can create a conditional breakpoint by clicking Breakpoints from the Edit menu, entering a new breakpoint address into the Command box, and entering a condition into the Condition box.
For example, typing mymod!myFunc+0x3A into the Command box and myVar < 7 into the Condition box is equivalent to issuing the following command:
0:000> bu mymod!myFunc+0x3A ".if(myVar<7) {.echo "Breakpoint hit, condition myVar<7"} .else {gc}"
If you are controlling the user-mode debugger from the kernel debugger, you cannot use conditional breakpoints or any other breakpoint command string that contains the gc (Go from Conditional Breakpoint) or g (Go) commands. If you use these commands, the serial interface might not be able to keep up with the number of breakpoint passes, and you will be unable to break back into CDB.