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