Loading SoftICE

If you are running SoftICE under Windows 95 or under Windows NT in System Startup mode, SoftICE automatically loads when you start or reboot your PC. If you are running SoftICE in Manual Startup mode under Windows NT, SoftICE does not load automatically.

To load SoftICE for Windows NT, do one of the following:

Select Start SoftICE

Enter the command:

NET START NTICE

Note:  Once you load SoftICE, you cannot deactivate it until you reboot your PC.

To verify that SoftICE is loaded, press the SoftICE hot key sequence Ctrl-D. The SoftICE screen should appear. To return to the Windows operating system, use the X (exit) or G (go to) command (F5).

Building the GDIDEMO Sample Application

The first step in preparing to debug a Windows application is to build it with debug information. The makefile for the sample application GDIDEMO is already set up for this purpose.

Open a DOS shell.

Change to the directory that contains the sample code.

If you are using Visual C++, execute the NMAKE command as follows:

C:\MSTOOLS\SAMPLES\WIN32\GDIDEMO>NMAKE

If you are using a different compiler, see you compiler documentation for instructions about making the file.

If GDIDEMO is located in another directory, change the path as appropriate.

Loading the GDIDEMO Sample Application

Loading an application entails creating a symbol file from the application's debug information and loading the symbol and source files into SoftICE. To Load the GDIDEMO application:

Start Symbol Loader.

In the Symbol Loader window, either choose Open Module from the File menu or click the Open button.

Locate GDIDEMO.EXE and click Open.

Either choose Load from the Module menu or click the Load button to load GDIDEMO.

Symbol Loader translates the debug information into a .NMS symbol file, loads the symbol and source files, starts GDIDEMO, pops up the SoftICE screen, and displays the source code for the file GDIDEMO.C.

Controlling the SoftICE Screen

The SoftICE screen is your central location for viewing and debugging code. It provides up to seven windows and one help line to let you view and control various aspects of your debugging session. By default, it displays:

Locals window  Displays and expands variables allocated on the stack.

Code window  Displays source code or unassembled instructions.

Command window  Enters user commands and display information.

Help line  Provides information about SoftICE commands and shows the active address context.

Look at the contents of the Code window. Note that SoftICE displays the WinMain routine at line 34. By default, SoftICE creates a breakpoint and stops at the first main module it encounters when loading your application.

To see all the source files that SoftICE loaded, enter the FILE command with the wild card character:

:FILE *

SoftICE displays the source files for GDIDEMO: draw.c, maze.c, xform.c, poly.c, wininfo.c, dialog.c, init.c, bounce.c, and gdidemo.c. The Command window varies in size depending upon the number of lines used by open windows, so you might not see all these file names. To display the remaining file names, press any key.

Many SoftICE windows can be scrolled. If you have a mouse, you can click on the scroll arrows. If not, SoftICE provides key sequences that let you scroll specific windows. Try these methods for scrolling the Code window:

Scroll the Code Window	Key Sequence	Mouse Action
Scroll to the previous page.	(Ctrl)PageUp	Click the innermost up scroll arrow
Scroll to the next page.	(Ctrl)PageDown	Click the innermost down scroll arrow
Scroll to the previous line.	(Ctrl)UpArrow	Click the outermost up scroll arrow
Scroll to the next line.	(Ctrl)DownArrow	Click the outermost down scroll arrow
Scroll left one character.	Ctrl-LeftArrow	Click the left scroll arrow
Scroll right one character.	Ctrl-RightArrow	Click the right scroll arrow

Enter the U command followed by EIP to disassemble the instructions for the current instruction pointer.

:U EIP

You can also use the . (dot) command to accomplish the same thing:

:.

Tracing and Stepping through Source Code

The following steps show you how to use SoftICE to trace through source code:

Enter the T (trace) command or press the F8 key to trace one instruction.

:T

Execution proceeds to the next source line and highlights it.

Enter the T (trace) command again.

:T

At this point, the following source line should be highlighted:

if(!hPrevInst)

The Code window is currently displaying source code. However, it can also display disassembled code or mixed (both source and disassembled) code. To view mixed code, use the SRC command (F3).

:SRC

Note that each source line is followed by its assembler instructions.

Press F3 once to see disassembled code, then again to return to source code.

Enter the T command (F8) to trace one instruction.

Execution proceeds until it reaches the line that executes the RegisterAppClass function.

As demonstrated in these steps, the T command executes one source statement or assembly language instruction. You can also use the P command (F10) to execute one program step. Stepping differs from tracing in one crucial way. If you are stepping and the statement or instruction is a function call, control is not returned until the function call is complete.

Hint:  The T command does not trace into a function call if the source code is not available. A good example of this is Win32 API calls. To trace into a function call when source code is not available, use the SRC command (F3) to switch into mixed or assembly mode.

Viewing Local Data

The Locals window displays the current stack frame. In this case, it contains the local data for the WinMain function. The following steps illustrate how to use the Locals window:

Enter the T command to enter the RegisterAppClass function. The Locals window is now empty because local data is not yet allocated for the function.

The RegisterAppClass function is implemented in the source file INIT.C. SoftICE displays the current source file in the upper left corner of the Code window.

Enter the T command again.

The Locals window contains the parameter passed to the RegisterAppClass (hInstance) and a local structure wndClass. The structure tag wndClass is marked with a plus sign (+). This plus sign indicates that you can expand the structure to view its contents.

Note:  You can also expand character strings and arrays.

If you have a Pentium-class processor and a mouse, double-click the structure WNDCLASSA to expand it. To collapse the structure wndClass, double-click its contents.

To use the keyboard to expand the structure: press Alt-L to move the cursor to the Locals window, use the UpArrow or DownArrow to move the highlight bar to the structure, and press Enter. Press Enter again to collapse it.

Setting Point-and-Shoot Breakpoints

This section shows you how to set two handy types of point-and-shoot breakpoints: one-shot and sticky breakpoints.

Setting a One-Shot Breakpoint

The following steps demonstrate how to set a one-shot breakpoint. A one-shot breakpoint clears after the breakpoint is triggered.

To shift focus to the Code window, either use your mouse to click in the window or press Alt-C.

If you wanted to shift focus back to the Command window you could press Alt-C again.

Either use the Down arrow key, the down scroll arrow, or the U command to place the cursor on line 61, the first call to the Win32 API function RegisterClass. If you use the U command, specify the source line 61 as follows:

:U .61

SoftICE places source line 61 at the top of the Code window.

Use the HERE command (F7) to execute to line 61.

The HERE command executes from the current instruction to the instruction that contains the cursor. The HERE command sets a one-shot breakpoint on the specified address or source line and continues execution until that breakpoint triggers. When the breakpoint is triggered, SoftICE automatically clears the breakpoint so that it does not trigger again.

The following current source line should be highlighted:

if(!RegisterClass(

Note:  You can do the same thing by using the G (go) command and specifying the line number or address to which to execute:

:G .61

Setting a Sticky Breakpoint

The following steps demonstrate another type of point-and-shoot breakpoint: the sticky breakpoint, which does not clear until you explicitly clear it.

Find the next call to RegisterClass that appears on source line 74. With the cursor on line 74, enter the BPX command (F9) to set an execution breakpoint. The BPX command sets an execution breakpoint by inserting an INT3 instruction into the code. Note that the line is highlighted when you set a breakpoint.

Press the F9 key to clear the breakpoint.

If you are using a Pentium-class processor and you have a mouse, you can double-click on a line in the Code window to set or clear a breakpoint.

Set a breakpoint on line 74, then use the G or X command (F5) to execute the instructions until the breakpoint triggers:

:G

When the INT3 instruction is executed, SoftICE pops up.

Unlike the HERE command, which sets a one-shot breakpoint, the BPX command sets a sticky breakpoint. A sticky breakpoint remains until you clear it.

To view information about breakpoints that are currently set, use the BL command:

:BL

00)   BPX #0137:00402442

Note:  The address you see might be different.

From the output of the BL command, one breakpoint is set on code address 0x402442. This address equates to source line 74 in the current file INIT.C.

The RegisterAppClass function has a relatively straightforward implementation, so it is unnecessary to trace every single source line. Use the P command with the RET parameter (F12) to return to the point where this function was called:

:P RET

The RET parameter to the P command causes SoftICE to execute instructions until the function call returns. Because RegisterAppClass was called from within WinMain, SoftICE pops up in WinMain on the statement after the RegisterAppClass function call. The following source line in WinMain should be highlighted:

msg.wParam = 1;

Enter the BC command with the wild card parameter to clear all the breakpoints:

BC *

Using SoftICE Informational Commands

SoftICE provides a wide variety of informational commands that detail the state of an application or the system. This section teaches you about two of them: H (help) and CLASS.

The H and Class commands work best when you have more room to display information, so use the WL command to close the Locals window. Closing this window automatically increases the size of the Command window.

The H command provides general help on all the SoftICE commands or detailed help on a specific command. To view detailed help about the CLASS command, enter CLASS as the parameter to the H command.

:H CLASS

Display window class information

CLASS [-x] [process | thread | module | class-name]

ex: CLASS USER

The first line of help provides a description of the command. The second line is the detailed use, including any options and/or parameters the command accepts. The third line is an example of the command.

The purpose of the RegisterAppClass function is to register window class templates that are used by the GDIDEMO application to create windows. Use the CLASS command to examine the classes registered by GDIDEMO.

:CLASS GDIDEMO

Class Name     Handle     Owner     Wndw Proc     Styles

------------------Application Private------------------

BOUNCEDEMO    A018A3B0    GDIDEMO   004015A4      00000003

DRAWDEMO        A018A318     GDIDEMO   00403CE4     00000003

MAZDEMO            A018A280    GDIDEMO   00403A94      00000003

XFORMDEMO      A018A1E8    GDIDEMO   00403764       00000003

POLYDEMO          A018A150    GDIDEMO   00402F34       00000003

GDIDEMO             A018A0C0    GDIDEMO   004010B5       00000003

Note:  This example shows only those classes specifically registered by the GDIDEMO application. Classes registered by other Windows modules, such as USER32, are omitted.

The output of the CLASS command provides summary information for each window class registered on behalf of the GDIDEMO process. This includes the class name, the address of the internal WINCLASS data structure, the module which registered the class, the address of the default window procedure for the class, and the value of the class style flags.

Note:  For more specific information on window class definitions, use the CLASS command with the -X option, as follows:

:CLASS -X

Using Symbols and Symbol Tables

Now that you are familiar with using SoftICE to step, trace, and create point-and-shoot style breakpoints, it is time to explore symbols and tables. When you load symbols for an application, SoftICE creates a symbol table that contains all the symbols defined for that module.

Use the TABLE command to see all the symbol tables that are loaded:

:TABLE

GDIDEMO [NM32]

964657 Bytes Of Symbol Memory Available

The currently active symbol table is listed in bold. This is the symbol table used to resolve symbol names. If the current table is not the table from which you want to reference symbols, use the TABLE command and specify the name of the table to make active:

:TABLE GDIDEMO

Use the SYM command to display the symbols from the current symbol table. With the current table set to GDIDEMO, the SYM command produces output similar to the following abbreviated output:

:SYM

.text(001B)

001B:00401000 WinMain

001B:004010B5 WndProc

001B:004011DB CreateProc

001B:00401270 CommandProc

001B:00401496 PaintProc

001B:004014D2 DestroyProc

001B:004014EA lRandom

001B:00401530 CreateBounceWindow

001B:004015A4 BounceProc

001B:004016A6 BounceCreateProc

001B:00401787 BounceCommandProc

001B:0040179C BouncePaintProc

This list of symbol names is from the .text section of the executable. The .text section is typically used for procedures and functions. The symbols displayed in this example are all functions of GDIDEMO.

Setting a Conditional Breakpoint

One of the symbols defined for the GDIDEMO application is the LockWindowInfo function. The purpose of this routine is to retrieve a pointer value that is specific to a particular instance of a window.

To learn about conditional and memory breakpoints, you will perform the following steps:

Set a BPX breakpoint on the LockWindowInfo function.

Edit the breakpoint to use a conditional expression, thus setting a conditional breakpoint.

Setting a BPX Breakpoint

Before setting the conditional breakpoint, you need to set a BPX-style breakpoint on LockWindowInfo.

Set a BPX-style breakpoint on the LockWindowInfo function:

:BPX LockWindowInfo

When one of the GDIDEMO windows needs to draw information in its client area, it calls the LockWindowInfo function. Every time the LockWindowInfo function is called, SoftICE pops up to let you debug the function. The GDIDEMO windows continually updates, so this breakpoint goes off quite frequently.

Use the BL command to verify that the breakpoint is set.

Use either the X or G command to exit SoftICE.

SoftICE should pop up almost immediately on the LockWindowInfo function.

Editing a Breakpoint

From the LockWindowInfo function prototype on source line 47, you can see that the function accepts one parameter of type HWND and returns a void pointer type. The HWND parameter is the handle to the window that is attempting to draw information within its client area. At this point, you want to modify the existing breakpoint, adding a conditional breakpoint to isolate a specific HWND value.

Before you can set the conditional expression, you need to obtain the HWND value for the POLYDEMO window. The HWND command provides information about application windows. Use the HWND command and specify the GDIDEMO process:

:HWND GDIDEMO

The following example illustrates what you should see if you are using Windows NT. If you are using Windows 95, your output will vary.

Handle     Class                         WinProc     TID     Module

07019C    GDIDEMO             004010B5     2D      GDIDEMO

100160     MDIClient              77E7F2F5     2D      GDIDEMO

09017E     BOUNCEDEMO   004015A4     2D      GDIDEMO

100172     POLYDEMO          00402F34     2D      GDIDEMO

11015C     DRAWDEMO        00403CE4    2D      GDIDEMO

The POLYDEMO window handle is bold and underlined. This is the window handle you want to use to form a conditional expression. If the POLYDEMO window does not appear in the HWND output, exit SoftICE using the G or X commands (F5) and repeat Step 1 until the window is created.

The value used in this example is probably not the same value that appears in your output. For the exercise to work correctly, you must use the HWND command to obtain the actual HWND value on your system. Using the POLYDEMO window handle, you can set a conditional expression to monitor calls to LockWindowInfo looking for a matching handle value. When the LockWindowInfo function is called with the POLYDEMO window handle, SoftICE pops up.

Because you already have a breakpoint set on LockWindowInfo, use the BPE command (Breakpoint Edit) to modify the existing breakpoint:

:BPE 0

When you use the BPE command to modify an existing breakpoint, SoftICE places the definition of that breakpoint onto the command line so that it can be easily edited. The output of the BPE command appears:

:BPX LockWindowInfo

The cursor appears at the end of the command line and is ready for you to type in the conditional expression.

Remember to substitute the POLYDEMO window handle value that you found using the HWND command instead of the value (100172) used in this example. Your conditional expression should appear similar to the following example. The conditional expression appears in bold type.

:BPX LockWindowInfo IF ESP->4 == 100172

Note:  Win32 applications pass parameters on the stack and at the entry point of a function; the first parameter has a positive offset of 4 from the ESP register. Using the SoftICE expression evaluator, this is expressed in the following form: ESP->4. ESP is the CPU stack pointer register and the "->" operator causes the lefthand side of the expression (ESP) to be indirected at the offset specified on the righthand side of the expression (4).

Verify that the breakpoint and conditional expression are correctly set by using the BL command.

Exit SoftICE using the G or X command (F5).

When SoftICE pops up, the conditional expression will be TRUE.

Setting a Read-Write Memory Breakpoint

We set the original breakpoint and subsequently the conditional expression so that we could obtain the address of a data structure specific to this instance of the POLYDEMO window. This value is stored in the window's extra data and is a global handle. The LockWindowInfo function retrieves this global handle and uses the Win32 API LocalLock to translate it into a pointer that can be used to access the window's instance data.

Obtain the pointer value for the windows instance data by executing up to the return statement on source line 57:

:G .57

Win32 API functions return 32-bit values in the EAX register, so you can use the BPMD command and specify the EAX register to set a memory breakpoint on the instance data pointer.

:BPMD EAX

The BPMD command uses the hardware debug registers provided by Intel CPUs to monitor reads and writes to the Dword value at a linear address. In this case, you are using BPMD to trap read and write accesses to the first Dword of the window instance data.

Use the BL command to verify that the memory breakpoint is set. Your output should look similar to the following:

:BL

00)   BPX LockWindowInfo IF ((ESP->4)==0x100172)

01)   BPMD #0023:001421F8 RW DR3

Breakpoint index 0 is the execution breakpoint on LockWindowInfo and breakpoint index 1 is the BPMD on the window instance data.

Use the BD command to disable the breakpoint on the LockWindowInfo.

:BD 0

SoftICE provides the BC (breakpoint clear) and BD (breakpoint disable) commands to clear or disable a breakpoint. Disabling a breakpoint is useful if you want to re-enable the breakpoint later in your debugging session. If you are not interested in using the breakpoint again, then it makes more sense to clear it.

Use the BL command to verify that the breakpoint on LockWindowInfo is disabled. SoftICE indicates that a breakpoint is disabled by placing an asterisk (*) after the breakpoint index. Your output should appear similar to the following:

:BL

00) * BPX _LockWindowInfo IF ((ESP->4)==0x100172)

01)   BPMD #0023:001421F8 RW DR3

Note:  You can use the BE command to re-enable a breakpoint:

:BE breakpoint-index-number

Exit SoftICE using the G or X command.

When the POLYDEMO window accesses the first Dword of its window instance data, the breakpoint triggers and SoftICE pops up.

When SoftICE pops up due to the memory breakpoint, you are in the PolyRedraw or PolyDrawBez function. Both functions access the nBezTotal field at offset 0 of the POLYDRAW window instance data.

Note:  The Intel CPU architecture defines memory breakpoints as traps, which means that the breakpoint triggers after the memory has been accessed. In SoftICE, the instruction or source line that is highlighted is the one after the instruction or source line that accessed the memory.

Clear the breakpoints you set in this section by using the BC command:

:BC *

Note:  You can use the wildcard character (*) with the BC, BD, and BE commands to clear, disable, and enable all breakpoints.

Exit SoftICE using the G or X command.

The operating system terminates the application.

Congratulations on completing your first SoftICE debugging session. In this session, you traced through source code, viewed locals and structures, and set point-and-shoot, conditional, and read-write memory breakpoints. SoftICE provides many more advanced features. The SoftICE commands ADDR, HEAP, LOCALS, QUERY, THREAD, TYPES, WATCH, and WHAT are just a few of the many SoftICE commands that help you debug smarter and faster.