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