Copy Link
Add to Bookmark
Report
Chapter 13
You know my methods. Apply them.
Sir Arthur Conan Doyle
Using Breakpoints
Introduction
Types of Breakpoints Supported by SoftICE
Breakpoint Options
Execution Breakpoints
Memory Breakpoints
Interrupt Breakpoints
I/O Breakpoints
Window Message Breakpoints
Understanding Breakpoint Contexts
Virtual Breakpoints
Setting a Breakpoint Action
Conditional Breakpoints
Conditional Breakpoint Count Functions
Using Local Variables in Conditional Expressions
Referencing the Stack in Conditional Breakpoints
Performance
Duplicate Breakpoints
Elapsed Time
Breakpoint Statistics
Referring to Breakpoints in Expressions
Manipulating Breakpoints
Using Embedded Breakpoints
Introduction
You can use SoftICE to set breakpoints on program execution, memory
location reads and writes, interrupts, and reads and writes to I/O
ports. SoftICE assigns a breakpoint index, from 0 to FF, to each
breakpoint. You can use this breakpoint index to identify breakpoints
when you set, delete, disable, enable, or edit them.
All SoftICE breakpoints are sticky, which means that SoftICE tracks
and maintains a breakpoint until you intentionally clear or disable it
using the BC or the BD command. After you clear breakpoints, you can
recall them with the BH command, which displays a breakpoint history.
You can set up to 256 breakpoints at one time in SoftICE. However, the
number of breakpoints you can set on memory location (BPMs) and I/O
ports (BPIOs) is a total of four, due to restrictions of the x86
processors.
Where symbol information is available, you can set breakpoints using
function names. When in source or mixed mode, you can set
point-and-shoot style breakpoints on any source code line. A valuable
feature is that you can set point-and-shoot breakpoints in a module
before it is even loaded.
Types of Breakpoints Supported by SoftICE
SoftICE provides a powerful array of breakpoint capabilities that take
full advantage of the x86 architecture, as follows :
* Execution Breakpoints: SoftICE replaces an existing instruction with
INT 3. You can use the BPX command to set execution breakpoints.
* Memory Breakpoints: SoftICE uses the x86 debug registers to break when
a certain byte / word / dword of memory is read, written, or executed. You
can use the BPM command to set memory breakpoints.
* Interrupt Breakpoints: SoftICE intercepts interrupts by modifying the
IDT (Interrupt Descriptor Table) vectors. You can use the BPINT
command to set interrupt breakpoints.
* I/O Breakpoints: SoftICE uses a debug register extension available on
Pentium and Pentium-Pro CPUs to watch for an IN or OUT instruction
going to a particular port address. You can use the BPIO command to
set I/O breakpoints.
* Window Message Breakpoints: SoftICE traps when a particular message or
range of messages arrives at a window. This is not a fundamental
breakpoint type; it is just a convenient feature built on top of the
other breakpoint primitives. You can use the BMSG command to set
window message breakpoints.
Breakpoint Options
You can qualify each type of breakpoint with the following two
options:
* A conditional expression [IF expression]: The expression must evaluate
to non-zero (TRUE) for the breakpoint to trigger. Refer to Conditional
Breakpoints.
* A breakpoint action [DO "command1;command2;"]: A series of SoftICE
commands can automatically execute when the breakpoint triggers. You
can use this feature in concert with user-defined macros to automate
tasks that would otherwise be tedious. Refer to Setting a Breakpoint
Action on page 114.
Note: For complete information on each breakpoint command, refer to
the SoftICE Command Reference.
Execution Breakpoints
An execution breakpoint traps executing code such as a function call
or language statement. This is the most frequently used type of
breakpoint. By replacing an existing instruction with an INT 3
instruction, SoftICE takes control when execution reaches the INT 3
breakpoint.
SoftICE provides two ways for setting execution breakpoints: using a
mouse and using the BPX command. The following sections describe how
to use these methods for setting breakpoints.
Using a Mouse to Set Breakpoints
If you are using a Pentium processor and a mouse, you can use the
mouse to set or clear point-and-shoot (sticky) and one-shot
breakpoints. To set a sticky breakpoint, double-click the line on
which you want to set the breakpoint. SoftICE highlights the line to
indicate that you set a breakpoint. Double-click the line again to
clear the breakpoint. To set a one-shot breakpoint, click the line on
which you want to set the breakpoint and use the HERE command (F7) to
execute to that line.
Using the BPX Command to Set Breakpoints
Use the BPX command with any of the following parameters to set an
execution breakpoint:
BPX [address] [IF expression] [DO "command1;command2;"]
IF expression:
Refer to Conditional Breakpoints.
DO "command1;command2;":
Refer to Setting a Breakpoint Action.
Example:
To set a breakpoint on your application's WinMain function, use
this command:
BPX WinMain
Use the BPX command without specifying any parameter to set a
point-and-shoot execution breakpoint in the source code. Use Alt-C to
move the cursor into the Code window. Then use the arrow keys to
position the cursor on the line on which you want to set the
breakpoint. Finally, use the BPX command (F9). If you prefer to use
your mouse to set the breakpoint, click the scroll arrows to scroll
the Code window, then double-click the line on which you want to set
the breakpoint.
Memory Breakpoints
A memory breakpoint uses the debug registers found on the 386 CPUs and
later models to monitor access to a certain memory location. This type
of breakpoint is extremely useful for finding out when and where a
program variable is modified, and for setting an execution breakpoint
in read-only memory. You can only set four memory breakpoints at one
time, because the CPU contains only four debug registers.
Use the BPM command to set memory breakpoints:
BPM[B|W|D] address [R|W|RW|X] [ debug register] [IF expression]
[DO "command1;command2;"]
BPM and BPMB:
Set a byte-size breakpoint.
BPMW:
Sets a word (2-byte) size breakpoint.
BPMD:
Sets a dword (4-byte) size breakpoint.
R, W, and RW:
Break on reads, writes, or both.
X:
Breaks on execution; this is more powerful than a BPX-style
breakpoint because memory does not need to be modified, enabling
such options as setting breakpoints in ROM or setting breakpoints
on addresses that are not present.
debug register:
Specifies which debug register to use. SoftICE normally manages
the debug register for you, unless you need to specify it in an
unusual situation.
IF expression:
Refer to Conditional Breakpoints.
DO "command1;command2;":
Refer to Setting a Breakpoint Action.
Example:
The following example sets a memory breakpoint to trigger when a
value of 5 is written to the Dword (4-byte) variable
MyGlobalVariable.
BPMD MyGlobalVariable W IF MyGlobalVariable==5
If the target location of a BPM breakpoint is frequently accessed,
performance can be degraded regardless of whether the conditional
expression evaluates to FALSE.
Interrupt Breakpoints
Use an interrupt breakpoint to trap an interrupt through the IDT. The
breakpoint only triggers when a specified interrupt is dispatched
through the IDT.
Use the BPINT command to set interrupt breakpoints:
BPINT interrupt-number [IF expression] [DO "command1;command2;"]
interrupt-number:
Number ranging from 0 to 255 (0 to FF hex).
IF expression:
Refer to Conditional Breakpoints.
DO "command1;command2;":
Refer to Setting a Breakpoint Action.
If an interrupt is caused by a software INT instruction, the
instruction displayed will be the INT instruction. (SoftICE pops up
when execution reaches the INT instruction responsible for the
breakpoint, but before the instruction actually executes.) Otherwise,
the current instruction will be the first instruction of an interrupt
handler. You can list all interrupts and their handlers by using the
IDT command.
Example:
Use the following command to set a breakpoint to trigger when a
call to the kernel-mode routine NtCreateProcess is made from user
mode:
BPINT 2E IF EAX==1E
Note: The NtCreateProcess is normally called from ZwCreateProcess
in the NTDLL.DLL, which is in turn called from CreateProcessW in
the KERNEL32.DLL. In the conditional expression, 1E is the
service number for NtCreateProcess. Use the NTCALL command to
find this value.
You can use the BPINT command to trap software interrupts, for
example, INT 21 made by 16-bit Windows programs. Note that software
interrupts issued from V86 mode do not pass through the IDT vector
that they specify. INT instructions executed in V86 generate processor
general protection faults (GPF), which are handled by vector 0xD in
the IDT. The Windows GPF handler realizes the cause of the fault and
passes control to a handler dedicated to specific V86 interrupt types.
The types may end up reflecting the interrupt down to V86 mode by
calling the interrupt handler entered in the V86 mode Interrupt Vector
Table (IVT). In some cases, a real-mode interrupt is reflected
(simulated) by calling the real-mode interrupt vector.
In the case where the interrupt is reflected, you can trap it by
placing a BPX breakpoint at the beginning of the real-mode interrupt
handler.
Example:
To set a breakpoint on the real-mode INT 21 handler, use the
following command:
BPX *($0:(21*4))
I/O Breakpoints
An I/O breakpoint monitors reads and writes to a port address. The
breakpoint traps when an IN or OUT instruction accesses the port.
SoftICE implements I/O breakpoints by using the debug register
extensions introduced with the Pentium. As a result, I/O breakpoints
require a Pentium or Pentium-Pro CPU. A maximum of four I/O
breakpoints can be set at one time. The I/O breakpoint is effective in
kernel-level (ring 0) code as well as user (ring 3) code.
Notes: Under Windows 95, SoftICE relies on the I/O permission bitmap,
which restricts I/O trapping to ring 3 code.
Notes: You cannot use I/O breakpoints to trap IN/OUT instructions
executed by MS-DOS programs. The IN/OUT instructions are trapped and
emulated by the operating system, and therefore do not generate real
port I/O, at least not in a 1:1 mapping.
Use the BPIO command to set I/O breakpoints:
BPIO port-number [R|W|RW] [IF expression]
[DO "command1;command2;"]
R, W, and RW :
Break on reads (IN instructions), writes (OUT instructions), or
both, respectively.
IF expression:
Refer to Conditional Breakpoints.
DO "command1;command2;":
Refer to Setting a Breakpoint Action.
When an I/O breakpoint triggers and SoftICE pops up, the current
instruction is the instruction following the IN or OUT that caused the
breakpoint to trigger. Unlike BPM breakpoints, there is no size
specification; any access to the port-number, whether byte, word, or
dword, triggers the breakpoint. Any I/O that spans the I/O breakpoint
will also trigger the breakpoint. For example, if you set an I/O
breakpoint on port 2FF, a word I/O to port 2FE would trigger the
breakpoint.
Example:
Use the following command to set a breakpoint to trigger when a
value is read from port 3FEH with the upper 2 bits set:
BPIO 3FE R IF (AL & C0)==C0
The condition is evaluated after the instruction completes. The
value will be in AL, AX, or EAX because all port I/O, except for
the string I/O instructions (which are rarely used), use the EAX
register.
Window Message Breakpoints
Use a window message breakpoint to trap a certain message or range of
messages delivered to a window procedure. Although you could implement
an equivalent breakpoint yourself using BPX with a conditional
expression, the following BMSG command is easier to use:
BMSG window-handle [L] [ begin-message [ end-message]]
[IF expression] [DO "command1;command2;"]
window-handle:
Value returned when the window was created; you can use the HWND
command to get a list of windows with their handles.
L:
Signifies that the window message should be printed to the
Command window without popping into SoftICE.
begin-message:
Single Windows message or the lower message number in a range of
Windows messages. If you do not specify a range with an
end-message, then only the begin-message will cause a break. For
both begin-message and end-message, the message numbers can be
specified either in hexadecimal or by using the actual ASCII
names of the messages, for example, WM_QUIT.
end-message:
Higher message number in a range of Windows messages.
IF expression:
Refer to Conditional Breakpoints.
DO "command1;command2;":
Refer to Setting a Breakpoint Action.
When specifying a message or a message range, you can use the symbolic
name, for example, WM_NCPAINT. Use the WMSG command to get a list of
the window messages that SoftICE understands. If no message or message
range is specified, any message will trigger the breakpoint.
Example:
To set a window message breakpoint for the window handle 1001E,
use the following command:
BMSG 1001E WM_NCPAINT
SoftICE is smart enough to take into account the address context
of the process that owns the window, so it does not matter what
address context you are in when you use BMSG.
You can construct an equivalent BPX-style breakpoint using a
conditional expression. Use the HWND command to get the address
of the window procedure, then use the following BPX command
(Win32 only):
BPX 5FEBDD12 IF (esp->8)==WM_NCPAINT
Warning: When setting a breakpoint using a raw address (not a symbol),
it is vital to be in the correct address context.
Understanding Breakpoint Contexts
A breakpoint context consists of the address context in which the
breakpoint was set and in what code module the breakpoint is in, if
any. Breakpoint contexts apply to the BPX and BPM commands, and
breakpoint types based on those commands such as BMSG.
For Win32 applications, breakpoints set in the upper 2GB of address
space are global; they break in any context. Breakpoints set in the
lower 2GB are context-sensitive; they trigger according to the
following criteria and SoftICE pops up:
* SoftICE only pops up if the address context matches the context
in which the breakpoint was set.
* If the breakpoint triggers in the same code module in which the
breakpoint was set, then SoftICE disregards the address context
and pops up. This means that a breakpoint set in a shared module
like KERNEL32.DLL breaks in every address context that has the
module loaded, regardless of what address context was selected
when the breakpoint was set.
The exception is if another process mapped the module at a
different base address than the one in which the breakpoint is
set. In this case, the breakpoint does not trigger. Avoid this
situation by basing your DLLs at non-conflicting addresses.
Breakpoints set on MS-DOS and 16-bit Windows programs are
context-sensitive too in the sense that the breakpoint only affects
the NTVDM process in which the breakpoint was set. The breakpoint
never crosses NTVDMs, even if the same program is run multiple times.
Breakpoint contexts are more important for BPM-type breakpoints than
for BPX. BPM sets an x86 hardware breakpoint that triggers on a
certain virtual address. Because the CPU's breakpoint hardware knows
nothing of address spaces, it could potentially trigger on an
unrelated piece of code or data. Breakpoint contexts give SoftICE the
ability to discriminate between false traps and real ones.
Virtual Breakpoints
In SoftICE, you can set breakpoints in Windows modules before they
load, and it is not necessary for a page to be present in physical
memory for a BPX (INT 3) breakpoint to be set. In such cases, the
breakpoint is virtual; it will be automatically armed when the module
loads or the page becomes present. Virtual breakpoints can only be set
on either symbols or source lines.
Setting a Breakpoint Action
You can set a breakpoint to execute a series of SoftICE commands,
including user-defined macros, after the breakpoint is triggered. You
define these breakpoint actions with the DO option, which is available
with every breakpoint type:
DO "command1;command2;"
The body of a breakpoint action definition is a sequence of SoftICE
commands or other macros, separated by semicolons. You need not
terminate the final command with a semicolon.
Breakpoint actions are closely related to macros. Refer to Working
with Persistent Macros on page 162 for more information about macros.
Breakpoint actions are essentially unnamed macros that do not accept
command-line arguments. Breakpoint actions, like macros, can call upon
macros. In fact a prime use of macros is to simplify the creation of
complex breakpoint actions.
If you need to embed a literal quote character (") or a percent sign
(%) within the macro (breakpoint) body, precede the character with a
backslash character (\). To specify a literal backslash character, use
two consecutive backslashes (\\).
If a breakpoint is being logged (refer to the built-in function
BPLOG), the action will not be executed.
The following examples illustrate the basic use of breakpoint actions:
BPX EIP DO "dd eax"
BPX EIP DO "data 1;dd eax"
BPMB dataaddr if (byte(*dataaddr)==1) do "? IRQL"
Conditional Breakpoints
Conditional breakpoints provide a fast and easy way to isolate a
specific condition or state within the system or application you are
debugging. By setting a breakpoint on an instruction or memory address
and supplying a conditional expression, SoftICE will only trigger if
the breakpoint evaluates to non-zero (TRUE). Because the SoftICE
expression evaluator handles complex expressions easily, conditional
expressions take you right to the problem or situation you want to
debug with ease.
All SoftICE breakpoint commands (BPX, BPM, BPIO, BMSG, and BPINT)
accept conditional expressions using the following syntax:
breakpoint-command [ breakpoint options] [IF conditional expression]
[DO "commands"]
The IF keyword, when present, is followed by any expression that you
want to be evaluated when the breakpoint is triggered. The breakpoint
will be ignored if the conditional expression is FALSE (zero). When
the conditional expression is TRUE (non-zero), SoftICE pop ups and
displays the reason for the break, which includes the conditional
expression.
The following examples show conditional expressions used during the
development of SoftICE.
Note: Most of these examples contain system-specific values that vary
depending on the exact version of Windows NT you are running.
* Watch a thread being activated:
bpx ntoskrnl!SwapContext IF (edi==0xFF8B4020)
* Watch a thread being deactivated:
bpx ntoskrnl!SwapContext IF (esi==0xFF8B4020)
* Watch CSRSS HWND objects (type 1) being created:
bpx winsrv!HMAllocObject IF (esp->c == 1)
* Watch CSRSS thread info objects (type 6) being destroyed:
bpx winsrv!HMFreeObject+0x25 IF (byte(esi->8) == 6)
* Watch process object-handle-tables being created:
bpx ntoskrnl!ExAllocatePoolWithTag IF (esp->c == ëObtb')
* Watch a thread state become terminated (enum == 4):
bpmb _thread->29 IF byte(_thread->29) == 4)
* Watch a heap block (230CD8) get freed:
bpx ntddl!RtlFreeHeap IF (esp->c == 230CD8)
* Watch a specific process make a system call:
bpint 2E if (process == _process)
Many of the previous examples use the thread and process intrinsic
functions provided by SoftICE. These functions refer to the active
thread or process in the operating system. In some cases, the examples
precede the function name with an underscore "_". This is a special
feature that makes it easier to refer to a dynamic value such as a
register's contents or the currently running thread or process as a
constant. The following examples should help to clarify this concept:
* This example sets a conditional breakpoint that will be triggered if
the dynamic (run-time) value of the EAX register equals its current
value.
bpx eip IF (eax == _eax)
This is equivalent to:
? EAX
00010022
bpx eip IF (eax == 10022)
* This example sets a conditional breakpoint that will be triggered if
the value of an executing thread's thread-id matches the thread-id of
the currently executing thread.
bpx eip IF (tid == _tid)
This is equivalent to:
? tid
8
bpx eip IF (tid == 8)
When you precede a function name or register with an underscore in an
expression, the function is evaluated immediately and remains constant
throughout the use of that expression.
Conditional Breakpoint Count Functions
SoftICE supports the ability to monitor and control breakpoints based
on the number of times a particular breakpoint has or has not been
triggered. You can use the following count functions in conditional
expressions:
* BPCOUNT
* BPMISS
* BPTOTAL
* BPLOG
* BPINDEX
BPCOUNT
The value for the BPCOUNT function is the current number of times that
the breakpoint has been evaluated as TRUE.
Use this function to control the point at which a triggered breakpoint
causes a popup to occur. Each time the breakpoint is triggered, the
conditional expression associated with the breakpoint is evaluated. If
the condition evaluates to TRUE, the breakpoint instance count
(BPCOUNT) increments by one. If the conditional evaluates to FALSE,
the breakpoint miss instance count (BPMISS) increments by one.
Example:
The fifth time the breakpoint triggers, the BPCOUNT equals 5, so
the conditional expression evaluates to TRUE and SoftICE pops up.
bpx myaddr IF (bpcount==5)
Use BPCOUNT only on the righthand side of compound conditional
expressions for BPCOUNT to increment correctly:
bpx myaddr if (eax==1) && (bpcount==5)
Due to the early-out algorithm employed by the expression evaluator,
the BPCOUNT==5 expression will not be evaluated unless EAX==1. (The C
language works the same way.) Therefore, by the time BPCOUNT==5 gets
evaluated, the expression is TRUE. BPCOUNT will be incremented and if
it equals 5, the full expression evaluates to TRUE and SoftICE pops
up. If BPCOUNT != 5, the expression fails, BPMISS is incremented and
SoftICE will not pop up (although BPCOUNT is now 1 greater).
Once the full expression returns TRUE, SoftICE pops up, and all
instance counts (BPCOUNT and BPMISS) are reset to 0.
Note: Do not use BPCOUNT before the conditional expression, otherwise
BPCOUNT will not increment correctly:
bpx myaddr if (bpcount==5) && (eax==1)
BPMISS
The value for the BPMISS expression function is the current number of
times that the breakpoint was evaluated as FALSE.
The expression function is similar to the BPCOUNT function. Use it to
specify that SoftICE pop up in situations where the breakpoint is
continually evaluating to FALSE. The value of BPMISS will always be
one less than you expect, because it is not updated until the
conditional expression is evaluated. You can use the (>=) operator to
correct this delayed update condition.
Example:
bpx myaddr if (eax==43) || (bpmiss>=5)
Due to the early-out algorithm employed by the expression evaluator,
if the expression eax==43 is ever TRUE, the conditional evaluates to
TRUE and SoftICE pops up. Otherwise, BPMISS is updated each time the
conditional evaluates to FALSE. After 5 consecutive failures, the
expression evaluates to TRUE and SoftICE pops up.
BPTOTAL
The value for the BPTOTAL expression function is the total number of
times that the breakpoint was triggered.
Use this expression function to control the point at which a triggered
breakpoint causes a popup to occur. The value of this expression is
the total number of times the breakpoint was triggered (refer to the
Hits field in the output of the BSTAT command) over its lifetime. This
value is never cleared.
Example:
The first 50 times this breakpoint is triggered, the condition
evaluates to FALSE and SoftICE will not pop up. Every time after
50, the condition evaluates to TRUE, and SoftICE pops up on this
and every subsequent trap.
bpx myaddr if (bptotal > 50)
You can use BPTOTAL to implement functionality identical to that of
BPCOUNT. Use the modulo "%" operator as follows:
if (!(bptotal%COUNT))
The COUNT is the frequency with which you want the breakpoint to
trigger. If COUNT is 4, SoftICE pops up every fourth time the
breakpoint triggers.
BPLOG
Use the BPLOG expression function to log the breakpoint to the history
buffer. SoftICE does not pop up when logged breakpoints trigger.
Note: Actions only execute when SoftICE pops up, so using actions with
the BPLOG function is pointless.
The BPLOG expression function always returns TRUE. It causes SoftICE
to log the breakpoint and relevant information about the breakpoint to
the SoftICE history buffer.
Example:
Any time the breakpoint triggers and the value of EAX equals 1,
SoftICE logs the breakpoint in the history buffer. SoftICE will
not popup.
bpx myaddr if ((eax==1) && bplog)
BPINDEX
Use the BPINDEX expression function to obtain the breakpoint index to
use with breakpoint actions.
This expression function returns the index of the breakpoint that
caused SoftICE to pop up. This index is the same index used by the BL,
BC, BD, BE, BPE, BPT, and BSTAT commands. You can use this value as a
parameter to any command that is being executed as an action.
Example:
This example of a breakpoint action causes the BSTAT command to
be executed with the breakpoint that caused the action to be
executed as its parameter:
bpx myaddr do "bstat bpindex"
This example shows a breakpoint that uses an action to create
another breakpoint:
bpx myaddr do "t;bpx @esp if(tid==_tid) do \"bc bpindex\";g"
Note: BPINDEX is intended to be used with breakpoint actions, and
causes an error if it is used within a conditional expression. Its use
outside of actions is allowed, but the result is unspecified and you
should not rely on it.
Using Local Variables in Conditional Expressions
SoftICE lets you use local variable names in conditional expressions
as long as the type ofbreakpoint is an execution breakpoint (BPX or
BPM X). SoftICE does not recognize local symbols in conditional
expressions for other breakpoint types, such as BPIO or BPMD RW,
because they require an execution scope. This type of breakpoint is
not tied to a specific section of executing code, so local variables
have no meaning.
When using local variables in conditional expressions, functions
typically have a prologue where local variables are created and an
epilogue where they are destroyed. You can access local variables
after the prologue code completes execution and before the epilogue
code begins execution. Function parameters are also temporarily
inaccessible using symbol names during prologue and epilogue
execution, because of adjustments to the stack frame.
To avoid these restrictions, set a breakpoint on either the first or
last source code line within the function body. The following concepts
use the foobar function to explain this concept.
Foobar Function
1:DWORD foobar ( DWORD foo )
2:{
3: DWORD fooTmp=0;
4:
5: if(foo)
6: {
7: fooTmp=foo*2;
8: }else{
9: fooTmp=1;
10: }
11:
12: return fooTmp;
13:}
Source code lines 1 and 2 are outside the function body. These lines
execute the prologue code. If you use a local variable at this point,
you receive the following symbol error:
:BPX foobar if(foo==1)
error: Undefined Symbol (foo)
Set the conditional on the source code line 3 where the local variable
fooTmp is declared and initialized, as follows:
:BPX .3 if(foo==0)
Source code line 13 marks the end of the function body. It also begins
epilogue code execution; thus, local variables and parameters are out
of scope. To set a conditional at the end of the foobar function, use
source line 12, as follows:
:BPX.12 if(fooTmp==1)
Note: Although it is possible to use local variables as the input to a
breakpoint command, such as BPMD RW, you should avoid doing this.
Local variables are relative to the stack, so their absolute address
changes each time the function scope where the variable is declared
executes. When the original function scope exits, the address tied to
the breakpoint no longer refers to the value of the local variable.
Referencing the Stack in Conditional Breakpoints
If you create your symbol file with full symbol information, you can
access function parameters and local variables through their symbolic
names, as described in Using Local Variables in Conditional
Expressions. If, however, you are debugging without full symbol
information, you need to reference function parameters and local
variables on the stack. For example, if you translated a module with
publics only or you want to debug a function for an operating system,
reference function parameters and local variables on the stack.
This section is specific to 32-bit flat application or system code.
Function parameters are passed on the stack, so you need to
de-reference these parameters through the ESP or EBP registers. Which
one you use depends on the function's prologue and where you set the
actual breakpoint in relation to that prologue.
Most 32-bit functions have a prologue of the following form:
PUSH EBP
MOV EBP,ESP
SUB ESP,size (locals)
Which sets up a stack frame as follows:
Stack Top PARAM n ESP+(n*4), or
EBP+(n*4)+4 Pushed by
PARAM #2 ESP+8, or EBP+C Caller
PARAM #1 ESP+4, or EBP+8
RET EIP Stack pointer on
Entry
Current Base Pointer (PUSH
EBP SAVE EBP EBP, MOV EBP,ESP)
LOCALS+SIZE-1 Call prologue
Stack Pointer after
LOCALS+0 prologue (SUB ESP,
size(locals)
SAVE EBX Optional save of "C"
registers Register
SAVE ESI saved by
Stack Current Stack pointer after compiler
Bottom ESP SAVE EDI registers are saved
Use either the ESP or EBP register to address parameters. Using the
EBP register is not valid until the PUSH EBP and MOV EBP, ESP
instructions are executed. Also note that once space for local
variables is created (SUB ESP,size) the position of the parameters
relative to ESP needs to be adjusted by the size of the local
variables and any saved registers.
Typically you set a breakpoint on the function address, for example:
BPX IsWindow
When this breakpoint is triggered, the prologue has not been executed,
and parameters can easily be accessed through the ESP register. At
this point, use of EBP is not valid.
To be sure that de-referencing the stack in a conditional expression
operates as you would expect, use the following guidelines.
Note: This assumes a stack-based calling convention with arguments
pushed right-to-left.
* If you set a breakpoint at the exact function address, for example,
BPX IsWindow, use ESP+(param# * 4) to address parameters, where param#
is 1...n.
* If you set a breakpoint inside a function body (after the full
prologue has been executed), use EBP+(param# * 4)+4 to address
parameters, where param# is 1...n. Be sure that the routine does not
use the EBP register for a purpose other than a stack-frame.
* Functions that are assembly-language based or are optimized for
frame-pointer omission may require that you use the ESP register,
because EBP may not be set up correctly.
Note: Once the space for local variables is allocated on the stack,
the local variables can be addressed using a negative offset from EBP.
The first local variable is at EBP-4. Simple data types are typically
Dword sized, so their offset can be calculated in a manner similar to
function parameters. For example, with two pointer local variables,
one will be at EBP-4 and the other will be at EBP-8.
Performance
Conditional breakpoints have some overhead associated with run-time
evaluation. Under most circumstances you see little or no effect on
performance when using conditional expressions. In situations where
you set a conditional breakpoint on a highly accessed data variable or
code sequence, you may notice slower system performance. This is due
to the fact that every time the breakpoint is triggered, the
conditional expression is evaluated. If a routine is executed hundreds
of times per second (such as ExAllocatePool or SwapContext), the fact
that any type of breakpoint with or without a conditional is trapped
and evaluated with this frequency results in some performance
degradation.
Duplicate Breakpoints
Once a breakpoint is set on an address, you cannot set another
breakpoint on the same address. With conditional expressions, however,
you can create a compound expression using the logical operators (&&)
or (||) to test more than one condition at the same address.
Elapsed Time
SoftICE supports using the time stamp counter (RDTSC instruction) on
all Pentium and Pentium-Pro machines. When SoftICE first starts, it
displays the clock speed of the machine on which it is running. Every
time SoftICE pops up due to a breakpoint, the elapsed time displays
since the last time SoftICE popped up. The time displays after the
break reason in seconds, milliseconds, or microseconds:
Break due to G (ET=23.99 microseconds)
The Pentium cycle counter is highly accurate, but you must keep the
following two issues in mind:
1- There is overhead involved in popping SoftICE up and down. On a
100MHz machine, this takes approximately 5 microseconds. This number
is slightly variable due to caching and privilege level changes.
2- If a hardware interrupt occurs before the breakpoint goes off, all
the interrupt processing time is included. Interrupts are off when
SoftICE pops up, so a hardware interrupt almost always goes off as
soon as Windows NT resumes.
Breakpoint Statistics
SoftICE collects statistical information about each breakpoint,
including the following:
* Total number of hits, breaks, misses, and errors
* Current hits and misses
Use the BSTAT command to display this information. Refer to the
SoftICE Command Reference for more information on the BSTAT command.
Referring to Breakpoints in Expressions
You can combine the prefix "BP" with the breakpoint index to use as a
symbol in an expression. This works for all BPX and BPM breakpoints.
SoftICE uses the actual address of the breakpoint.
Example:
To disassemble code at the address of the breakpoint with index
0, use the command:
U BP0
Manipulating Breakpoints
SoftICE provides a variety of commands for manipulating breakpoints
such as listing, modifying, deleting, enabling, disabling, and
recalling breakpoints. Breakpoints are identified by breakpoint index
numbers, which are numbers ranging from 0 to FF (hex). Breakpoint
index numbers are assigned sequentially as breakpoints are added. The
following table describes the breakpoint manipulation commands:
BD Disable a breakpoint
BE Enable a breakpoint
BL List current breakpoints
BPEEdit a breakpoint
BPTUse breakpoint as template
BC Clear (remove) a breakpoint
BH Display breakpoint history
Note: Refer to the SoftICE Command Reference for more information on
each of these commands.
Using Embedded Breakpoints
It may be helpful for you to embed a breakpoint in your program source
rather than setting a breakpoint with SoftICE. To embed a breakpoint
in your program, do the following:
1 Place an INT 1 or INT 3 instruction at the desired point in the
program source.
2 To enable SoftICE to pop up on such embedded breakpoints, use the
following command:
SET I1HERE ON ; for INT 1 breakpoints
SET I3HERE ON ; for INT 3 breakpoints