                                We will now discuss in a little more detail
                                                the struggle for existence.
                                                             Charles Darwin

SoftICE Tutorial

     Introduction
     Loading SoftICE
     Building the GDIDEMO Sample Application
     Loading the GDIDEMO Sample Application
     Controlling the SoftICE Screen
     Tracing and Stepping through Source Code
     Viewing Local Data
     Setting Point-and-Shoot Breakpoints
          Setting a One-Shot Breakpoint
          Setting a Sticky Breakpoint
     Using SoftICE Informational Commands
     Using Symbols and Symbol Tables
     Setting a Conditional Breakpoint
          Setting a BPX Breakpoint
          Editing a Breakpoint
     Setting a Read-Write Memory Breakpoint

Introduction

     This tutorial gives you hands-on experience debugging a Windows
     application to teach you the fundamental steps for debugging
     applications and drivers. During this debugging session, you will
     learn how to do the following:

   * Load SoftICE
   * Build an application
   * Load the application source and symbol files
   * Trace and step through source code and assembly language
   * View local data and structures
   * Set point-and-shoot breakpoints
   * Use SoftICE informational commands to explore the state of the
     application
   * Work with symbols and symbol tables
   * Modify a breakpoint to use a conditional expression

     Each section in the tutorial builds upon the previous sections, so you
     should perform them in order.

     This tutorial uses the GDIDEMO application as its basis. GDIDEMO
     provides a demonstration of GDI functionality. GDIDEMO is located in
     the \EXAMPLES\GDIDEMO directory on your CDROM. GDIDEMO is also
     available under \mstools\samples\win32\GDIDEMO. If you use the GDIDEMO
     on the CDROM, copy it to your hard drive.

     You can substitute a different sample application or an application of
     your own design. The debugging principles and features of SoftICE used
     in this tutorial apply to most applications.

     Note: The examples is this tutorial are based on Windows NT. If you
     are using Windows 95, your output may vary slightly.

Loading SoftICE

     If you are running SoftICE under Windows 95 or under Windows NT in
     Boot, System, or Automatic 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 95, enter the command WINICE. 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.

     To build the sample program, perform the following steps:

     1. Open a DOS shell.

     2. Change to the directory that contains the sample code.

     3. Execute the NMAKE command:

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

     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
     applications debug information and loading the symbol and source
     files into SoftICE. To Load the GDIDEMO application, perform the
     following steps:

     1. Start Symbol Loader : The Symbol Loader window appears.

     2. Either choose OPEN MODULE from the File menu or click the OPEN
     button : The Open window appears.

     3. Locate GDIDEMO.EXE and click Open.

     4. 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 the following:

     Locals window: Displays and expand 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.

     1. Look at the contents of the Code window. Note that SoftICE is
     displaying 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.

     2. 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. (Refer to Chapter 5:
     Navigating Through SoftICE on page 69 for information about resizing
     windows.)

     3. 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                   Click the innermost up
      page.                    PageUp          scroll arrow

      Scroll to the next                       Click the innermost down
      page.                    PageDown        scroll arrow

      Scroll to the previous                   Click the outermost up
      line.                    UpArrow         scroll arrow

      Scroll to the next                       Click the outermost down
      line.                    DownArrow       scroll arrow

      Scroll left one                          Click the left scroll
      character.               Ctrl-LeftArrow  arrow

      Scroll right one                         Click the right scroll
      character.               Ctrl-RightArrow arrow

     4. 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:

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

     :T

     The F8 key is the default key for the T (trace) command.

     Execution proceeds to the next source line and highlights it. At this
     point, the following source line should be highlighted:

     if(!hPrevInst)

     2. 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.

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

     4. 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:

     1. 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.

     2. 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.

     3. 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.

     4. 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.

     1. 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. Setting Point-and-Shoot Breakpoints

     2. 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.

     3. 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(&wndClass))

     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.

     The F9 key is the default key for the BPX command.

     1. 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.

     2. 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.

     3. 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.

     4. 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.

     5. You can use the SoftICE expression evaluator to translate a line
     number into an address. To find the address for line 74, use the ?
     command:

     :? .74
     void * = 0x00402442

     6. 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;

     7. 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.

     1. 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.

     2. 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.

     3. 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

     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
     Class Name  Handle    Owner    WndwProc  Styles
     ---------------Application Private---------------
     BOUNCEDEMO  A018A3B0  GDIDEMO  004015A4  00000003
     DRAWDEMO    A018A318  GDIDEMO  00403CE4  00000003
     MAZEDEMO    A018A280  GDIDEMO  00403A94  00000003
     XFORMDEMO   A018A1E8  GDIDEMO  00403764  00000003
     POLYDEMO    A018A150  GDIDEMO  00402F34  00000003
     GDIDEMO     A018A0C0  GDIDEMO  004010B5  00000003

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.

     1. 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

     2. 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.

   * Set a memory breakpoint to monitor access to a key piece of
     information, as described in Setting a Read-Write Memory Breakpoint on
     page 39.

Setting a BPX Breakpoint

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

     1. 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.

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

     3. 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.

     1. 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.

     2. 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.

     3. 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). For more information on the
     SoftICE expression evaluator refer to Chapter 8: Using Expressions on
     page 125 and for referencing the stack in conditional expressions
     refer to Conditional Breakpoints on page 114.

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

     5. 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 windows 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 windows
     instance data.

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

     :G .57

     2. 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.

     3. 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.

     4. 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.

     5. 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

     6. 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.

     7. 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.

     8. 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. Refer to the SoftICE Command Reference for a
     complete explanation of all the SoftICE commands.
