Document revision date: 30 March 2001

## OpenVMS Alpha System Analysis Tools Manual

Previous | Contents | Index

## 9.9 Access to Symbols in OpenVMS Executive Images

For a discussion and explanation of how the OpenVMS debugger accesses symbols in OpenVMS executive images, see Section 8.11.

## 9.10 Sample System Dump Debugging Session

This section provides a sample session that shows the use of some OpenVMS debugger commands as they apply to the system dump debugger. The examples in this section show how to work with a dump created as follows:

1. Follow the steps in Section 8.12, up to and including Example 8-9 (Breakpoint Display).
2. When the breakpoint at line 146 is reached, enter the OpenVMS debugger command to clear R27 and then continue:

       DBG> DEPOSIT R27=0
       DBG> GO

3. The system then crashes and a dump is written.
4. When the system reboots, copy the contents of SYS$SYSTEM:SYSDUMP.DMP to the build system with SDA:

       $ analyze/crash sys$system:sysdump.dmp

        OpenVMS (TM) Alpha system dump analyzer
        ...analyzing a selective memory dump...

        %SDA-W-NOTSAVED, global pages not saved in the dump file
        Dump taken on  1-JAN-1998 00:00:00.00
        INVEXCEPTN, Exception while above ASTDEL

        SDA> copy hstsys::sysdump.dmp
        SDA>

To reproduce this sample session, you need access to the SYSTEM_DEBUG.DSF matching the SYSTEM_DEBUG.EXE file on your test system and to the source file C_TEST_ROUTINES.C, which is available in SYS$EXAMPLES.

The example begins by invoking the system dump debugger's character cell interface on the build system.

Example 9-1 Invoking the System Dump Debugger

    $ define dbg$decw$display " "
    $ debug/keep

             OpenVMS Alpha Debug64 Version V7.2-019

    DBG>

Use the ANALYZE/CRASH_DUMP command to open the system dump. In this example, the logical name DBGHK$IMAGE_PATH is used for the image path, so the command qualifier /IMAGE_PATH is not being used. You may need to use it.

When you have opened the dump file, the DEBUG prompt is displayed. You should now do the following:

1. Set the language to be C, the language of the module that was active at the time of the system crash.
2. Set the source directory to the location of the source of the module. Use the debugger's SET SOURCE command, which takes a search list as a parameter so you can make it point to many places.

Example 9-2 Accessing the System Dump

    DBG> analyze/crash_dump sysdump.dmp
    %SDA-W-NOTSAVED, global pages not saved in the dump file
    %DEBUG-I-INIBRK, target system interrupted
    %DEBUG-I-DYNIMGSET, setting image SYSTEM_DEBUG
    %DEBUG-I-DYNMODSET, setting module C_TEST_ROUTINES
    DBG> set language c
    DBG> set source/latest sys$examples,sys$library
    DBG>

Now that the debugger has access to the source, you can put the debugger into screen mode to see exactly where you are and the code surrounding it.

Example 9-3 Displaying the Source Code

    DBG> Set Mode Screen; Set Step Nosource

    - SRC: module C_TEST_ROUTINES -scroll-source------------------------------------
       135:     int i,k;
       136:     for(k=0;k<1000;k++)
       137:       {
       138:          test_c_code5(&i);
       139:       }
       140:     return;
       141: }
       142: int test_c_code3(int subrtnCount)
       143: {
       144:     subrtnCount = subrtnCount - 1;
       145:     if (subrtnCount != 0)
    -> 146:         subrtnCount = test_c_code3(subrtnCount);
       147:     return subrtnCount;
       148: }
       149: int test_c_code2(int64 in64,int in32, int64 test, int64* pVar)
       150: {
       151:     xdt$fregsav[5] = in64;
       152:     xdt$fregsav[6] = in32;
       153:     if (xdt$fregsav[9] > 0)
       154:         *pVar =  (*pVar + xdt$fregsav[17])%xdt$fregsav[9];
       155:     else
       156:         *pVar = (*pVar + xdt$fregsav[17]);
       157:     xdt$fregsav[7] = test_c_code3(10);
    - OUT -output-------------------------------------------------------------------

    - PROMPT -error-program-prompt--------------------------------------------------

    DBG>

Now, you try a couple of other commands, EXAMINE and SHOW CALLS. The EXAMINE command allows you to look at all the C variables. Note that the C_TEST_ROUTINES module is compiled with the /NOOPTIMIZE switch which allows access to all variables. The SHOW CALLS command shows you the call sequence from the beginning of the stack. In this case, you started out in the image EXEC_INIT. (The debugger prefixes all images other than the main image with SHARE$ so it shows up as SHARE$EXEC_INIT.)

Example 9-4 Using the Examine and Show Calls Commands

    DBG> Set Mode Screen; Set Step Nosource

    - SRC: module C_TEST_ROUTINES -scroll-source------------------------------------
       135:     int i,k;
       136:     for(k=0;k<1000;k++)
       137:       {
       138:          test_c_code5(&i);
       139:       }
       140:     return;
       141: }
       142: int test_c_code3(int subrtnCount)
       143: {
       144:     subrtnCount = subrtnCount - 1;
       145:     if (subrtnCount != 0)
    -> 146:         subrtnCount = test_c_code3(subrtnCount);
       147:     return subrtnCount;
       148: }
       149: int test_c_code2(int64 in64,int in32, int64 test, int64* pVar)
       150: {
       151:     xdt$fregsav[5] = in64;
       152:     xdt$fregsav[6] = in32;
       153:     if (xdt$fregsav[9] > 0)
       154:         *pVar =  (*pVar + xdt$fregsav[17])%xdt$fregsav[9];
       155:     else
       156:         *pVar = (*pVar + xdt$fregsav[17]);
       157:     xdt$fregsav[7] = test_c_code3(10);
    - OUT -output-------------------------------------------------------------------
    C_TEST_ROUTINES\test_c_code3\subrtnCount:       9
     module name     routine name      line           rel PC           abs PC
    *C_TEST_ROUTINES test_c_code3       146       00000000000000CC FFFFFFFF83002D6C
    *C_TEST_ROUTINES test_c_code2       157       00000000000001A0 FFFFFFFF83002E40
    *C_TEST_ROUTINES test_c_code        170       0000000000000260 FFFFFFFF83002F00
    *XDELTA          XDT$SYSDBG_INIT   9371       0000000000000058 FFFFFFFF83052238
    *SYS$DOINIT      INI$DOINIT        1488       0000000000000098 FFFFFFFF830520B8
     SHARE$EXEC_INIT                              0000000000018C74 FFFFFFFF83086C74
     SHARE$EXEC_INIT                              0000000000014BD0 FFFFFFFF83082BD0

    - PROMPT -error-program-prompt--------------------------------------------------

    DBG> e subrtnCount
    DBG> show calls
    DBG>

## Part 3
OpenVMS Watchpoint Utility

Part 3 describes the Watchpoint utility. It presents how to use the Watchpoint utility by doing the following:

- Loading the watchpoint driver
- Creating and deleting watchpoints
- Looking at watchpoint driver data
- Acquiring collected watchpoint data
- Looking at the protection attributes and access fault mechanism
- Looking at some watchpoint restrictions

## Chapter 10
The Watchpoint Utility

This chapter describes the Watchpoint utility (WP), which enables you to monitor write access to user-specified locations. The chapter contains the following sections:

Section 10.1 presents an introduction of the Watchpoint utility.

Section 10.2 describes how to load the watchpoint driver.

Section 10.3 describes the creation and deletion of watchpoints and the constraints upon watchpoint locations.

Section 10.4 contains detailed descriptions of the watchpoint driver data structures, knowledge of which may be required to analyze collected watchpoint data.

Section 10.5 discusses acquiring collected watchpoint data.

Section 10.6 describes the watchpoint protection facility.

Section 10.7 describes its restrictions.

## 10.1 Introduction

A watchpoint is a data field to which write access is monitored. The field is from 1 to 8 bytes long and must be contained within a single page. Typically, watchpoints are in nonpaged pool. However, subject to certain constraints (see Section 10.3.1), they can be defined in other areas of system space. The Watchpoint facility can simultaneously monitor a large number (50 or more) watchpoints.

The utility is implemented in the WPDRIVER device driver and the utility program WP. This document concentrates on the device driver, which can be invoked directly or through the WP utility.

For information on the WP utility, see its help files, which can be displayed with the following DCL command:

               $ HELP/LIBRARY=SYS$HELP:WP

Once the driver has been loaded, a suitably privileged user can designate a watchpoint in system space. Any write to a location designated as a watchpoint is trapped. Information is recorded about the write, including its time, the register contents, and the program counter (PC) and processor status longword (PSL) of the writing instruction. Optionally, one or both of the following user-specified actions can be taken:

- An XDELTA breakpoint1 or SCD breakpoint which occurs just after the write to the watchpoint
- A fatal watchpoint bugcheck which occurs just after the write to the watchpoint

You define a watchpoint by issuing QIO requests to the watchpoint driver; entering commands to the WP utility, which issues requests to the driver; or, from kernel mode code, invoking a routine within the watchpoint driver.

The WPDRIVER data structures store information about writes to a watchpoint. This information can be obtained either through QIO requests to the WPDRIVER, commands to the WP utility, XDELTA commands issued during a requested breakpoint, or SDA commands issued during the analysis of a requested crashdump.

Note ##### 1 For simplicity, this chapter only mentions XDELTA. Any reference to XDELTA breakpoints also implies SCD breakpoints.

## 10.2 Initializing the Watchpoint Utility

From a process with CMKRNL privilege, run the SYSMAN utility to load the watchpoint driver, SYS$WPDRIVER.EXE. Enter the following commands:

    $ RUN SYS$SYSTEM:SYSMAN
    SYSMAN> IO CONNECT WPA0:/NOADAPTER/DRIVER=SYS$WPDRIVER
    SYSMAN> EXIT

SYSMAN creates system I/O data structures for the pseudo-device WPA0, loads WPDRIVER, and invokes its initialization routines. WPDRIVER initialization includes the following actions:

- Allocating nonpaged pool and physical memory for WPDRIVER data structures
- Appropriating the SCB vector specific to access violations
- Recording in system space the addresses of the WPDRIVER routines invoked by kernel mode code to create and delete watchpoints

Memory requirements for WPDRIVER and its data structures are:

- Device driver and UCB---approximately 3K bytes of nonpaged pool
- Trace table and a related array---36 bytes for each of system parameter WPTTE_SIZE trace table entries
- Watchpoint restore entries---system parameter WPRE_SIZE pages of physically contiguous memory
- Each watchpoint---176 bytes of nonpaged pool

It is advisable to load the watchpoint driver relatively soon after system initialization to ensure its allocation of physically contiguous memory. If the driver cannot allocate enough physically contiguous memory, it does not set WPA0: online. If the unit is offline, you will not be able to use the watchpoint utility.

## 10.3 Creating and Deleting Watchpoints

There are three different ways to create and delete watchpoints:

- An image can assign a channel to device WPA0: and then request the Queue I/O Request ($QIO) system service to create or delete a watchpoint.
- Code running in kernel mode can dispatch directly to routines within the WPDRIVER to create and delete watchpoints.
- You can enter commands to the WP utility.

The first two methods are described in detail in the sections that follow.

### 10.3.1 Using the $QIO Interface

An image first assigns a channel to the pseudo-device WPA0: and then issues a $QIO request on that channel. The process must have the privilege PHY_IO; otherwise, the $QIO request is rejected with the error SS$_NOPRIV.

Table 10-1 shows the functions that the driver supports.

Table 10-1 Driver Supported Functions
Function | Activity
--- | ---
IO$_ACCESS | Creates a watchpoint
IO$_DEACCESS | Deletes a watchpoint
IO$_RDSTATS | Receives trace information on a watchpoint

The IO$_ACCESS function requires the following device/function dependent arguments:

- P2---Length of the watchpoint. A number larger than 8 is reduced to 8.
- P3---Starting address of the watchpoint area.

The following are the constraints on the watchpoint area. It must be:

- Nonpageable system space.
- Write-accessible from kernel mode.
- Within one page. If it is not, the requested length is reduced to what will fit within the page containing the starting address.
- Within a page accessed only from kernel mode and by instructions that incur no pagefaults.
- Within a page whose protection is not altered while the watchpoint is in place.
- Outside of certain address ranges. These are the WPDRIVER code, its data structures, and the system page table.

Because of the current behavior of the driver, there is an additional requirement that there be no "unexpected" access violations referencing a page containing a watchpoint. See Section 10.7 for further details.

To specify that an XDELTA breakpoint or a fatal bugcheck occur if the watchpoint is written, use the following I/O function code modifiers:

- IO$M_CTRL to request an XDELTA breakpoint
- IO$M_ABORT to request a fatal bugcheck

For an XDELTA breakpoint to be taken, OpenVMS must have been booted specifying that XDELTA and/or the SCD be resident (bit 1 or bit 15 in the boot flags must be set). If both watchpoint options are requested, the XDELTA breakpoint is taken first. At exit from the breakpoint, the driver crashes the system.

A request to create a watchpoint can succeed completely, succeed partially, or fail. Table 10-2 shows the status codes that can be returned in the I/O status block.

Table 10-2 Returned Status Codes
Status Code | Meaning
--- | ---
SS$_NORMAL | Success.
SS$_BUFFEROVF | A watchpoint was established, but its length is less than was requested because the requested watchpoint would have straddled a page boundary.
SS$_EXQUOTA | The watchpoint could not be created because too many watchpoints already exist.
SS$_INSFMEM | The watchpoint could not be created because there was insufficient nonpaged pool to create data structures specific to this watchpoint.
SS$_IVADDR | The requested watchpoint resides in one of the areas in which the WPDRIVER is unable to create watchpoints.
SS$_WASSET | An existing watchpoint either coincides or overlaps with the requested watchpoint.

The following example MACRO program assigns a channel to the WPA0 device and creates a watchpoint of 4 bytes, at starting address 80001068. The program requests neither an XDELTA breakpoint nor a system crash for that watchpoint.

                      $IODEF
                      .PSECT   RWDATA,NOEXE,RD,WRT,LONG
                                              ;
            WP_IOSB:  .BLKL    2              ; I/O status block.
            WP_ADDR:  .LONG    ^X80001068     ; Address of watchpoint to create.
            WP_NAM:   .ASCID   /WPA0:/        ; Device to which to assign channel.
            WP_CHAN:  .BLKW    1              ; Channel number.
                      .PSECT   PROG,EXE,NOWRT
                                              ;
            START:    .CALL_ENTRY

                       $ASSIGN_S DEVNAM=WP_NAM,CHAN=WP_CHAN
                       BLBC    R0,RETURN

                       $QIOW_S CHAN=WP_CHAN,-
                               FUNC=#IO$_ACCESS,-
                               IOSB=WP_IOSB,-
                               P2=#4,-
                               P3=WP_ADDR
                       BLBC    R0,RETURN
                       MOVL    WP_IOSB,R0     ; Move status to R0.
            RETURN:    RET                    ; Return to caller.
                      .END     START

A watchpoint remains in effect until it is explicitly deleted. (Note, however, that watchpoint definitions do not persist across system reboots.) To delete an existing watchpoint, issue an IO$_DEACCESS QIO request.

The IO$_DEACCESS function requires the following device/function dependent argument: P3 - Starting address of the watchpoint to be deleted.

Table 10-3 shows the status values that are returned in the I/O status block.

Table 10-3 Returned Status Values
Status Value | Meaning
--- | ---
SS$_NORMAL | Success.
SS$_IVADDR | The specified watchpoint does not exist.

Section 10.5 describes the use of the IO$_RDSTATS QIO request.

### 10.3.2 Invoking WPDRIVER Entry Points from System Routines

When the WPDRIVER is loaded, it initializes two locations in system space with the addresses of routines within the driver. These locations, WP$CREATE_WATCHPOINT and WP$DELETE_WATCHPOINT, enable dispatch to create and delete watchpoint routines within the loaded driver. Input arguments for both routines are passed in registers.

Code running in kernel mode can execute the following instructions:

                      JSB    @G^WP$CREATE_WATCHPOINT ; create a watchpoint

and

                     JSB    @G^WP$DELETE_WATCHPOINT ; delete a watchpoint

Both these routines save IPL at entry and set it to the fork IPL of the WPDRIVER, IPL 11. Thus, they should not be invoked by code threads running above IPL 11. At exit, the routines restore the entry IPL.

These two locations contain an RSB instruction prior to the loading of the driver. As a result, if a system routine tries to create or delete a watchpoint before the WPDRIVER is loaded, control immediately returns.

WP$CREATE_WATCHPOINT has the following register arguments:

- R0---User-specified watchpoint options
  - Bit 1 equal to 1 specifies that a fatal OPERCRASH bugcheck should occur after a write to the watchpoint area.
  - Bit 2 equal to 1 specifies that an XDELTA breakpoint should occur after a write to the watchpoint area.
- R1---Length of the watchpoint area
- R2---Starting address of the watchpoint area

Status is returned in R0. The status values and their interpretations are identical to those for the QIO interface to create a watchpoint. The only difference is that the SS$_NOPRIV status cannot be returned with this interface.

WPS$DELETE_WATCHPOINT has the following register argument:

- R2---Starting address of the watchpoint area

Status is returned in R0. The status values and their interpretations are identical to those for the QIO interface.

## 10.4 Data Structures

The WPDRIVER uses three different kinds of data structures:

- One watchpoint restore entry (WPRE) for each page of system space in which one or more active watchpoints are located
- One watchpoint control block (WPCB) for each active watchpoint
- Trace table entries (WPTTEs) in a circular trace buffer which maintains a history of watchpoint writes

These data structures are described in detail and illustrated in the sections that follow.

### 10.4.1 Watchpoint Restore Entry (WPRE)

There is one WPRE for each system page that contains a watchpoint. That is, if nine watchpoints are defined which are in four different system pages, four WPREs are required to describe those pages. When WPDRIVER is loaded, its initialization routine allocates physically contiguous memory for the maximum number of WPREs. The number of pages to be allocated is specified by system parameter WPRE_SIZE.

The WPDRIVER allocates WPREs starting at the beginning of the table and maintains a tightly packed list. That is, when a WPRE in the middle of those in use is "deallocated," its current contents are replaced with the contents of the last WPRE in use. The number in use at any given time is in the driver variable WP$L_WP_COUNT. The system global EXE$GA_WP_WPRE points to the beginning of the WPRE table.

The WPRE for a page contains information useful for:

- Determining whether a given access violation refers to an address in the page associated with this WPRE
- Restoring the original SPTE value for the associated page
- Reestablishing the modified SPTE value when watchpoints are reenabled
- Invalidating the translation buffer when the SPTE is modified
- Locating the data structures associated with individual watchpoints defined in this system page

Previous | Next | Contents | Index

| privacy and legal statement |
6549PRO_033.HTML