OpenVMS Debugger Manual


Previous Contents Index

2.3.4 Suspending Program Execution with Breakpoints

The SET BREAK command enables you to select locations at which to suspend program execution (breakpoints). You can then enter commands to check the call stack, examine the current values of variables, and so on. You resume execution from a breakpoint with the GO or STEP commands.

The following example shows a typical use of the SET BREAK command:


DBG> SET BREAK COUNT
DBG> GO
   .
   .
   .
break at routine PROG2\COUNT 
     54:  procedure COUNT(X,Y:INTEGER);
DBG>

In the example, the SET BREAK command sets a breakpoint on routine COUNT (at the beginning of the routine's code); the GO command starts execution. When routine COUNT is encountered, the following occurs:

At this breakpoint, you can use the STEP command to step through routine COUNT and then use the EXAMINE command (discussed in Section 2.4.1) to check on the values of X and Y.

When using the SET BREAK command, you can specify program locations using various kinds of address expressions (for example, line numbers, routine names, memory addresses, byte offsets). With high-level languages, you typically use routine names, labels, or line numbers, possibly with path names to ensure uniqueness.

Specify routine names and labels as they appear in the source code. Line numbers can be derived from either a source code display or a listing file. When specifying a line number, use the prefix %LINE; otherwise, the debugger interprets the line number as a memory location. For example, the following command sets a breakpoint at line 41 of the module in which execution is paused. The breakpoint causes the debugger to suspend execution at the beginning of line 41.


DBG> SET BREAK %LINE 41

Note that you can set breakpoints only on lines that resulted in machine-code instructions. The debugger warns you if you try to do otherwise (for example, on a comment line). To pick a line number in a module other than the one in which execution is paused, you must specify the module's name in a path name. For example:


DBG> SET BREAK SCREEN_IO\%LINE 58

You can also use the SET BREAK command with a qualifier, but no parameter, to break on every line, or on every CALL instruction, and so on. For example:


DBG> SET BREAK/LINE
DBG> SET BREAK/CALL

You can set breakpoints on events, such as exceptions, or state transitions in tasking programs.

You can conditionalize a breakpoint (with a WHEN clause) or specify that a list of commands be executed at the breakpoint (with a DO clause).

To display the current breakpoints, enter the SHOW BREAK command.

To deactivate a breakpoint, enter the DEACTIVATE BREAK command, and specify the program location exactly as you did when setting the breakpoint. This causes the debugger to ignore the breakpoint during program execution. However, you can activate it at a later time, for example, when you rerun the program (see Section 1.3.3). A deactivated breakpoint is listed as such in a SHOW BREAK display.

To activate a breakpoint, use the ACTIVATE BREAK command. Activating a breakpoint causes it to take effect during program execution.

The commands DEACTIVATE BREAK/ALL and ACTIVATE BREAK/ALL operate on all breakpoints and are particularly useful when rerunning a program.

To cancel a breakpoint, use the CANCEL BREAK command. A canceled breakpoint is no longer listed in a SHOW BREAK display.

2.3.5 Tracing Program Execution with Tracepoints

The SET TRACE command enables you to select locations for tracing the execution of your program (tracepoints), without stopping its execution. After setting a tracepoint, you can start execution with the GO command and then monitor the path of execution, checking for unexpected behavior. By setting a tracepoint on a routine, you can also monitor the number of times it is called.

As with breakpoints, every time a tracepoint is reached, the debugger issues a message and displays the source line. But the program continues executing, and the debugger prompt is not displayed. For example:


DBG> SET TRACE COUNT
DBG> GO
trace at routine PROG2\COUNT 
     54:  procedure COUNT(X,Y:INTEGER);
   .
   .
   .

This is the only difference between a breakpoint and a tracepoint. When using the SET TRACE command, you specify address expressions, qualifiers, and optional clauses exactly as with the SET BREAK command. The commands SHOW TRACE, ACTIVATE TRACE, DEACTIVATE TRACE, and CANCEL TRACE operate on tracepoints in a manner similar to the corresponding commands for breakpoints (see Section 2.3.4).

2.3.6 Monitoring Changes in Variables with Watchpoints

The SET WATCH command enables you to specify program variables that the debugger monitors as your program executes. This process is called setting watchpoints. If the program modifies the value of a watched variable, the debugger suspends execution and displays information. The debugger monitors watchpoints continuously during program execution. (Note that you can also use the SET WATCH command to monitor arbitrary program locations, not just variables.)

You can set a watchpoint on a variable by specifying the variable's name with the SET WATCH command. For example, the following command sets a watchpoint on the variable TOTAL:


DBG> SET WATCH TOTAL

Subsequently, every time the program modifies the value of TOTAL, the watchpoint is triggered.

Note

The technique you use to set watchpoints depends on your system (Alpha or Integrity servers) and the type of variable, static or nonstatic. On Alpha systems, for example, a static variable is associated with the same memory address throughout program execution.

The following example shows what happens when your program modifies the contents of this watched variable:


DBG> SET WATCH TOTAL
DBG> GO
   .
   .
   .
watch of SCREEN_IO\TOTAL at SCREEN_IO\%LINE 13 
     13:   TOTAL = TOTAL + 1; 
    old value: 16 
    new value: 17 
break at SCREEN_IO\%LINE 14 
     14:   POP(TOTAL);
DBG>

In this example, a watchpoint is set on the variable TOTAL and execution is started. When the value of TOTAL changes, execution is paused. The debugger announces the event ("watch of..."), identifying where TOTAL changed (the beginning of line 13) and the associated source line. The debugger then displays the old and new values and announces that execution has been paused at the beginning of the next line (14). Finally, the debugger prompts for another command. When a change in a variable occurs at a point other than the beginning of a source line, the debugger gives the line number plus the byte offset from the beginning of the line.

On Alpha processors, you can set a watchpoint on a nonstatic variable by setting a tracepoint on the defining routine and specifying a DO clause to set the watchpoint whenever execution reaches the tracepoint. Since a nonstatic variable is allocated on the stack or in a register and exists only when its defining routine is active (on the call stack), the variable name is not always meaningful in the way that a static variable name is.

In the following example, a watchpoint is set on the nonstatic variable Y in routine ROUT3. After the tracepoint is triggered, the WPTTRACE message indicates that the nonstatic watchpoint is set, and the watchpoint is triggered when the value of Y changes. For example:


DBG> SET TRACE/NOSOURCE ROUT3 DO (SET WATCH Y)
DBG> GO
   .
   .
   .
trace at routine MOD4\ROUT3
%DEBUG-I-WPTTRACE, nonstatic watchpoint, tracing every 
                   instruction
   .
   .
   .
watch of MOD4\ROUT3\Y at MOD4\ROUT3\%LINE 16
   16:     Y := 4
   old value:    3
   new value:    4
break at MOD4\ROUT3\%LINE 17
   17:     SWAP(X,Y);
DBG>

When execution returns to the calling routine, the nonstatic variable is no longer active, so the debugger automatically cancels the watchpoint and issues a message to that effect.

On Alpha processors and Integrity server, the debugger treats all watchpoints as nonstaticwatchpoints.

The commands SHOW WATCH, ACTIVATE WATCH, DEACTIVATE WATCH, and CANCEL WATCH operate on watchpoints in a manner similar to the corresponding commands for breakpoints (see Section 2.3.4). However, a nonstatic watchpoint exists only as long as execution remains within the scope of the variable being watched.

2.4 Examining and Manipulating Program Data

This section explains how to use the EXAMINE, DEPOSIT, and EVALUATE commands to display and modify the contents of variables and evaluate expressions. Before you can examine or deposit into a nonstatic variable, as defined in Section 2.3.6, its defining routine must be active.

2.4.1 Displaying the Value of a Variable

To display the current value of a variable, use the EXAMINE command. It has the following syntax:


EXAMINE address-expression

The debugger recognizes the compiler-generated data type of the variable you specify and retrieves and formats the data accordingly. The following examples show some uses of the EXAMINE command.

Examine a string variable:


DBG> EXAMINE EMPLOYEE_NAME
PAYROLL\EMPLOYEE_NAME:    "Peter C. Lombardi"
DBG>

Examine three integer variables:


DBG> EXAMINE WIDTH, LENGTH, AREA
SIZE\WIDTH:   4 
SIZE\LENGTH:  7 
SIZE\AREA:   28
DBG>

Examine a two-dimensional array of real numbers (three per dimension):


DBG> EXAMINE REAL_ARRAY
PROG2\REAL_ARRAY 
   (1,1):       27.01000 
   (1,2):       31.00000 
   (1,3):       12.48000 
   (2,1):       15.08000 
   (2,2):       22.30000 
   (2,3):       18.73000
DBG>

Examine element 4 of a one-dimensional array of characters:


DBG> EXAMINE CHAR_ARRAY(4)
PROG2\CHAR_ARRAY(4): 'm'
DBG>

Examine a record variable (COBOL example):


DBG> EXAMINE PART
INVENTORY\PART: 
    ITEM:     "WF-1247" 
    PRICE:     49.95 
    IN_STOCK:  24
DBG>

Examine a record component (COBOL example):


DBG> EXAMINE IN_STOCK OF PART
INVENTORY\IN-STOCK of PART: 
    IN_STOCK:  24
DBG>

You can use the EXAMINE command with any kind of address expression (not just a variable name) to display the contents of a program location. The debugger associates certain default data types with untyped locations. If you want the data interpreted and displayed in some other data format you can override the defaults for typed and untyped locations.

2.4.2 Assigning a Value to a Variable

To assign a new value to a variable, use the DEPOSIT command. It has the following syntax:


DEPOSIT address-expression = language-expression

The DEPOSIT command is like an assignment statement in most programming languages.

In the following examples, the DEPOSIT command assigns new values to different variables. The debugger checks that the value assigned, which can be a language expression, is consistent with the data type and dimensional constraints of the variable.

Deposit a string value (it must be enclosed in quotation marks (") or apostrophes ('):


DBG> DEPOSIT PART_NUMBER = "WG-7619.3-84"

Deposit an integer expression:


DBG> DEPOSIT WIDTH = CURRENT_WIDTH + 10

Deposit element 12 of an array of characters (you cannot deposit an entire array aggregate with a single DEPOSIT command, only an element):


DBG> DEPOSIT C_ARRAY(12) := 'K'

Deposit a record component (you cannot deposit an entire record aggregate with a single DEPOSIT command, only a component):


DBG> DEPOSIT EMPLOYEE.ZIPCODE = 02172

Deposit an out-of-bounds value (X was declared as a positive integer):


DBG> DEPOSIT X = -14
%DEBUG-I-IVALOUTBNDS, value assigned is out of bounds 
        at or near DEPOSIT

As with the EXAMINE command, you can specify any kind of address expression (not just a variable name) with the DEPOSIT command. You can override the defaults for typed and untyped locations if you want the data interpreted in some other data format.

2.4.3 Evaluating Language Expressions

To evaluate a language expression, use the EVALUATE command. It has the following syntax:


EVALUATE language-expression

The debugger recognizes the operators and expression syntax of the currently set language. In the following example, the value 45 is assigned to the integer variable WIDTH; the EVALUATE command then obtains the sum of the current value of WIDTH and 7:


DBG> DEPOSIT WIDTH := 45
DBG> EVALUATE WIDTH + 7
52
DBG>

In the next example, the values TRUE and FALSE are assigned to the Boolean variables WILLING and ABLE, respectively; the EVALUATE command then obtains the logical conjunction of these values:


DBG> DEPOSIT WILLING := TRUE
DBG> DEPOSIT ABLE := FALSE
DBG> EVALUATE WILLING AND ABLE
False
DBG>

2.5 Controlling Access to Symbols in Your Program

To have full access to the symbols that are associated with your program (variable names, routine names, source code, line numbers, and so on), you must compile and link the program using the /DEBUG qualifier, as explained in Section 1.2.

Under these conditions, the way in which the debugger handles these symbols is transparent to you in most cases. However, the following two areas might require action:

2.5.1 Setting and Canceling Modules

To facilitate symbol searches, the debugger loads symbol information from the executable image into a run-time symbol table (RST), where that information can be accessed efficiently. Unless symbol information is in the RST, the debugger does not recognize or properly interpret the associated symbols.

Because the RST takes up memory, the debugger loads it dynamically, anticipating what symbols you might want to reference in the course of program execution. The loading process is called module setting, because all symbol information for a given module is loaded into the RST at one time.

Initially, only the module containing the image transfer address is set. Subsequently, whenever execution of the program is interrupted, the debugger sets the module that contains the routine in which execution is paused. This enables you to reference the symbols that should be visible at that location.

If you try to reference a symbol in a module that has not been set, the debugger warns you that the symbol is not in the RST. For example:


DBG> EXAMINE K
%DEBUG-W-NOSYMBOL, symbol 'K' is not in symbol table
DBG>

You must use the SET MODULE command to set the module containing that symbol explicitly. For example:


DBG> SET MODULE MOD3
DBG> EXAMINE K
MOD3\ROUT2\K: 26
DBG>

The SHOW MODULE command lists the modules of your program and identifies which modules are set.

Dynamic module setting can slow the debugger down as more and more modules are set. If performance becomes a problem, you can use the CANCEL MODULE command to reduce the number of set modules, or you can disable dynamic module setting by entering the SET MODE NODYNAMIC command (SET MODE DYNAMIC enables dynamic module setting).

2.5.2 Resolving Symbol Ambiguities

Symbol ambiguities can occur when a symbol (for example, a variable name X) is defined in more than one routine or other program unit.

In most cases, the debugger resolves symbol ambiguities automatically. First, it uses the scope and visibility rules of the currently set language. In addition, because the debugger permits you to specify symbols in arbitrary modules (to set breakpoints and so on), the debugger uses the ordering of routine calls on the call stack to resolve symbol ambiguities.

If the debugger cannot resolve a symbol ambiguity, it issues a message. For example:


DBG> EXAMINE Y
%DEBUG-W-NOUNIQUE, symbol 'Y' is not unique
DBG>

You can then use a path-name prefix to uniquely specify a declaration of the given symbol. First, use the SHOW SYMBOL command to identify all path names associated with the given symbol (corresponding to all declarations of that symbol) that are currently loaded in the RST. Then use the desired path-name prefix when referencing the symbol. For example:


DBG> SHOW SYMBOL Y
data MOD7\ROUT3\BLOCK1\Y
data MOD4\ROUT2\Y
DBG> EXAMINE MOD4\ROUT2\Y
MOD4\ROUT2\Y: 12
DBG>

If you need to refer to a particular declaration of Y repeatedly, use the SET SCOPE command to establish a new default scope for symbol lookup. Then, references to Y without a path-name prefix specify the declaration of Y that is visible in the new scope. For example:


DBG> SET SCOPE MOD4\ROUT2
DBG> EXAMINE Y
MOD4\ROUT2\Y: 12
DBG>

To display the current scope for symbol lookup, use the SHOW SCOPE command. To restore the default scope, use the CANCEL SCOPE command.

2.6 Sample Debugging Session

This section walks you through a debugging session with a simple Fortran program that contains a logic error (see Example 2-1). Compiler-assigned line numbers have been added in the example so that you can identify the source lines referenced in the discussion.

The program, called SQUARES, performs the following functions:

  1. Reads a sequence of integer numbers from a data file and saves these numbers in the array INARR (lines 4 and 5).
  2. Enters a loop in which it copies the square of each nonzero integer into another array OUTARR (lines 8 through 13).
  3. Prints the number of nonzero elements in the original sequence and the square of each such element (lines 16 through 21).

Example 2-1 Sample Program SQUARES

 1:       INTEGER INARR(20), OUTARR(20) 
 2: C 
 3: C     ---Read the input array from the data file. 
 4:       OPEN(UNIT=8, FILE='DATAFILE.DAT', STATUS='OLD') 
 5:       READ(8,*) N, (INARR(I), I=1,N) 
 6: C 
 7: C     ---Square all nonzero elements and store in OUTARR. 
 8:       K = 0 
 9:       DO 10 I = 1, N 
10:       IF(INARR(I) .NE. 0) THEN 
11:           OUTARR(K) = INARR(I)**2 
12:       ENDIF 
13:    10 CONTINUE 
14: C 
15: C     ---Print the squared output values.  Then stop. 
16:       PRINT 20, K 
17:    20 FORMAT(' Number of nonzero elements is',I4) 
18:       DO 40 I = 1, K 
19:       PRINT 30, I, OUTARR(I) 
20:    30 FORMAT(' Element',I4,' has value',I6) 
21:    40 CONTINUE 
22:       END 

When you run SQUARES, it produces the following output, regardless of the number of nonzero elements in the data file:


$ RUN SQUARES
Number of nonzero elements is   0

The error in the program is that variable K, which keeps track of the current index into OUTARR, is not incremented in the loop on lines 9 through 13. The statement K = K + 1 should be inserted just before line 11.

Example 2-2 shows how to start the debugging session and then how to use the debugger to find the error. Comments, keyed to the callouts, follow the example.

Example 2-2 Sample Debugging Session Using Program SQUARES

$ FORTRAN/DEBUG/NOOPTIMIZE SQUARES   (1)
$ LINK/DEBUG SQUARES   (2)
 
 
 
$ DEBUG/KEEP   (3)
           Debugger Banner and Version Number
DBG> RUN SQUARES   (4)
Language: FORTRAN, Module: SQUARES$MAIN
DBG> STEP 4   (5)
stepped to SQUARES$MAIN\%LINE 9
     9:         DO 10 I = 1, N
DBG> EXAMINE N,K   (6)
SQUARES$MAIN\N:       9
SQUARES$MAIN\K:       0
DBG> STEP 2   (7)
stepped to SQUARES$MAIN\%LINE 11
    11:                 OUTARR(K) = INARR(I)**2
DBG> EXAMINE I,K   (8)
SQUARES$MAIN\I:       1
SQUARES$MAIN\K:       0
DBG> DEPOSIT K = 1   (9)
DBG> SET TRACE/SILENT %LINE 11 DO (DEPOSIT K = K + 1)   (10)
DBG> GO   (11)
Number of nonzero elements is   4
Element   1 has value    16
Element   2 has value    36
Element   3 has value     9
Element   4 has value    49
'Normal successful completion'
DBG> SPAWN   (12)
$ EDIT SQUARES.FOR   (13)
   .
   .
   .
10:       IF(INARR(I) .NE. 0) THEN
11:           K = K + 1
12:           OUTARR(K) = INARR(I)**2
13:       ENDIF
   .
   .
   .
$ FORTRAN/DEBUG/NOOPTIMIZE SQUARES   (14)
$ LINK/DEBUG SQUARES
$ LOGOUT   (15)
DBG> RUN SQUARES   (16)
Language: FORTRAN, Module: SQUARES$MAIN
DBG> SET BREAK %LINE 12 DO (EXAMINE I,K)   (17)
DBG> GO   (18)
SQUARES$MAIN\I:        1
SQUARES$MAIN\K:        1
DBG> GO
SQUARES$MAIN\I:        2
SQUARES$MAIN\K:        2
DBG> GO
SQUARES$MAIN\I:        4
SQUARES$MAIN\K:        3
DBG> EXIT   (19)
$ 

The following comments apply to the callouts in Example 2-2. Example 2-1 shows the program that is being debugged.


Previous Next Contents Index