HP OpenVMS Systems Documentation

Content starts here

OpenVMS Debugger Manual

Previous Contents Index

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

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


$ DEBUG/KEEP   (3)
           Debugger Banner and Version Number
DBG> STEP 4   (5)
stepped to SQUARES$MAIN\%LINE 9
     9:         DO 10 I = 1, N
DBG> STEP 2   (7)
stepped to SQUARES$MAIN\%LINE 11
    11:                 OUTARR(K) = INARR(I)**2
DBG> DEPOSIT K = 1   (9)
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)
10:       IF(INARR(I) .NE. 0) THEN
11:           K = K + 1
12:           OUTARR(K) = INARR(I)**2
13:       ENDIF
$ LOGOUT   (15)
DBG> GO   (18)
SQUARES$MAIN\I:        1
SQUARES$MAIN\K:        1
SQUARES$MAIN\I:        2
SQUARES$MAIN\K:        2
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.

  1. The /DEBUG qualifier on the DCL FORTRAN command directs the compiler to write the symbol information associated with SQUARES into the object module, SQUARES.OBJ, in addition to the code and data for the program.
    The /NOOPTIMIZE qualifier disables optimization by the Fortran compiler, which ensures that the executable code matches the source code of the program. Debugging optimized code can be confusing because the contents of some program locations might be inconsistent with what you would expect from viewing the source code.
  2. The /DEBUG qualifier on the DCL command LINK causes the linker to include all symbol information that is contained in SQUARES.OBJ in the executable image.
  3. The DCL command DEBUG/KEEP starts the debugger, which displays its banner and the debugger prompt, DBG>. You can now enter debugger commands.
  4. The debugger command RUN SQUARES brings the program SQUARES under debugger control. The informational message identifies the source language of the program and the name of the main program unit (FORTRAN and SQUARES, respectively, in this example).
    Execution is initially paused at the start of the main program unit (line 1 of SQUARES, in this example).
  5. You decide to test the values of variables N and K after the READ statement has been executed and the value 0 has been assigned to K.
    The command STEP 4 executes 4 source lines of the program. Execution is now paused at line 9. Note that the STEP command ignores source lines that do not result in executable code; also, by default, the debugger identifies the source line at which execution is paused.
  6. The command EXAMINE N, K displays the current values of N and K. Their values are correct at this point in the execution of the program.
  7. The command STEP 2 executes the program into the loop that copies and squares all nonzero elements of INARR into OUTARR.
  8. The command EXAMINE I,K displays the current values of I and K.
    I has the expected value 1, but K has the value 0 instead of 1, which is the expected value. Now you can see the error in the program: K should be incremented in the loop just before it is used in line 11.
  9. The DEPOSIT command assigns K the value it should have now: 1.
  10. The SET TRACE command is now used to patch the program so that the value of K is incremented automatically in the loop. The command sets a tracepoint that triggers every time execution reaches line 11:
    • The /SILENT qualifier suppresses the "trace at" message that would otherwise appear each time line 11 is executed.
    • The DO clause issues the DEPOSIT K = K + 1 command every time the tracepoint is triggered.
  11. To test the patch, the GO command starts execution from the current location.
    The program output shows that the patched program works properly. The EXITSTATUS message shows that the program executed to completion.
  12. The SPAWN command spawns a subprocess to return control temporarily to DCL level (without ending the debugging session) so that you can correct the source file and recompile and relink the program.
  13. The EDIT command invokes an editor and the source file is edited to addK = K + 1 after line 10, as shown. (Compiler-assigned line numbers have been added to clarify the example.)
  14. The revised program is compiled and linked.
  15. The LOGOUT command terminates the spawned subprocess and returns control to the debugger.
  16. The (debugger) command RUN SQUARES brings the revised program under debugger control so that its correct execution can be verified.
  17. The SET BREAK command sets a breakpoint that triggers every time line 12 is executed. The DO clause displays the values of I and K automatically when the breakpoint triggers.
  18. The GO command starts execution.
    At the first breakpoint, the value of K is 1, indicating that the program is running correctly so far. Each additional GO command shows the current values of I and K. After two more GO commands, K is now 3, as expected, but note that I is 4. The reason is that one of the INARR elements was 0 so that lines 11 and 12 were not executed (and K was not incremented) for that iteration of the DO loop. This confirms that the program is running correctly.
  19. The EXIT command ends the debugging session and returns control to DCL level.

Chapter 3
Controlling and Monitoring Program Execution

This chapter describes how to control and monitor program execution while debugging by using the following techniques:

  • Executing the program by step unit
  • Suspending and tracing execution with breakpoints and tracepoints
  • Monitoring changes in variables and other program locations with watchpoints

The following related functions are discussed in Chapter 2:

  • Starting or resuming program execution with the GO command ( Section 2.3.1)
  • Monitoring where execution is currently paused with the SHOW CALLS command ( Section 2.3.3)

This chapter includes information that is common to all programs. For more information:

  • See Chapter 15 for additional information specific to multiprocess programs.
  • See Chapter 16 for additional information specific to vectorized programs (VAX only).
  • See Chapter 17 for additional information specific to tasking (multithread) programs.

For information about rerunning your program or running another program from the current debugging session, see Section 1.3.3 and Section 1.3.4.

3.1 Commands Used to Execute the Program

Only four debugger commands are directly associated with program execution:

EXIT (if your program has exit handlers)

As explained in Section 2.3.1 and Section 2.3.2, GO and STEP are the basic commands for starting and resuming program execution. The STEP command is discussed further in Section 3.2.

During a debugging session, routines are executed as they are called during the execution of a program. The CALL command enables you to arbitrarily call and execute a routine that was linked with your program. This command is discussed in Section 13.7.

The EXIT command was discussed in Section 1.8, in conjunction with ending a debugging session. Because it executes any exit handlers in your program, it is also useful for debugging exit handlers (see Section 14.6).

When using any of these four commands, note that program execution can be interrupted or stopped by any of the following events:

  • The program terminates
  • A breakpoint is reached
  • A watchpoint is triggered
  • An exception is signaled
  • You press Ctrl/C

3.2 Executing the Program by Step Unit

The STEP command (probably the most frequently used debugger command) enables you to execute your program in small increments called step units.

By default, a step unit is an executable line of source code. In the following example, the STEP command executes one line, reports the action ("stepped to..."), and displays the line number (27) and source code of the next line to be executed:

stepped to TEST\COUNT\%LINE 27
     27:   X := X + 1;

Execution is now paused at the first machine-code instruction for line 27 of module TEST. Line 27 is in COUNT, a routine within module TEST.

The STEP command can also execute several source lines at a time. If you specify a positive integer as a parameter, the STEP command executes that number of lines. In the following example, the STEP command executes the next three lines:

stepped to TEST\COUNT\%LINE 34
     34:   SWAP(X,Y);

Note that only those source lines for which code instructions were generated by the compiler are recognized as executable lines by the debugger. The debugger skips over any other lines---for example, comment lines. Also, if a line has more than one statement on it, the debugger executes all the statements on that line as part of the single step.

Source lines are displayed by default after stepping if they are available for the module being debugged. Source lines are not available if you are stepping in code that has not been compiled or linked with the /DEBUG qualifier (for example, a shareable image routine). If source lines are available, you can control their display with the SET STEP [NO]SOURCE command and the /[NO]SOURCE qualifier of the STEP command. For information about how to control the display of source code in general and in particular after stepping, see Chapter 6.

3.2.1 Changing the STEP Command Behavior

You can change the default behavior of the STEP command in two ways:

  • By specifying a STEP command qualifier---for example, STEP/INTO
  • By establishing a new default qualifier with the SET STEP command---for example, SET STEP INTO

In the following example, the STEP/INTO command steps into a called routine when the program counter (PC) is at a call statement. The debugger displays the source line identifying the routine PRODUCT, which is called from routine COUNT of module TEST:

stepped to routine TEST\PRODUCT
     6:    function PRODUCT(X,Y : INTEGER) return INTEGER is

After the STEP/INTO command executes, subsequent STEP commands revert to the default behavior.

In contrast, the SET STEP command enables you to establish new defaults for the STEP command. These defaults remain in effect until another SET STEP command is entered. For example, the SET STEP INTO command causes subsequent STEP commands to behave like STEP/INTO (SET STEP LINE causes subsequent STEP commands to behave like STEP/LINE).

There is a SET STEP command parameter for each STEP command qualifier.

You can override the current STEP command defaults for the duration of a single STEP command by specifying other qualifiers. Use the SHOW STEP command to identify the current STEP command defaults.

3.2.2 Stepping Into and Over Routines

By default, when the PC is at a call statement and you enter the STEP command, the debugger steps over the called routine. Although the routine is executed, execution is not paused within the routine but, rather, on the beginning of the line that follows the call statement. When stepping by instruction, execution is paused on the instruction that follows a called routine's return instruction.

To step into a called routine when the PC is at a call statement, enter the STEP/INTO command. The following example shows how to step into the routine PRODUCT, which is called from routine COUNT of module TEST:

stepped to TEST\COUNT\%LINE 18
    18:       AREA := PRODUCT(LENGTH, WIDTH);
stepped to routine TEST\PRODUCT
     6:    function PRODUCT(X,Y : INTEGER) return INTEGER is

To return to the calling routine from any point within the called routine, use the STEP/RETURN command. It causes the debugger to step to the return instruction of the routine being executed. A subsequent STEP command brings you back to the statement that follows the routine call. For example:

stepped on return from TEST\PRODUCT\%LINE 11 to TEST\PRODUCT\%LINE 15+4
    15:    end PRODUCT;
stepped to TEST\COUNT\%LINE 19
    19:       LENGTH := LENGTH + 1;

To step into several routines, enter the SET STEP INTO command to change the default behavior of the STEP command from STEP/OVER to STEP/INTO:


As a result of this command, when the PC is at a call statement, a STEP command suspends execution within the called routine. If you later want to step over routine calls, enter the SET STEP OVER command.

When SET STEP INTO is in effect, you can qualify the kinds of called routines that the debugger is stepping into by specifying any of the following parameters with the SET STEP command:

  • [NO]JSB---Controls whether to step into routines called by JSB instructions. (VAX processors only)
  • [NO]SHARE---Controls whether to step into called routines in shareable images.
  • [NO]SYSTEM---Controls whether to step into called system routines.

These parameters make it possible to step into application-defined routines and automatically step over system routines, and so on. For example, the following command directs the debugger to step into called routines in user space only. The debugger steps over routines in system space and in shareable images.


3.3 Suspending and Tracing Execution with Breakpoints and Tracepoints

This section discusses using the SET BREAK and SET TRACE commands to, respectively, suspend and trace program execution. The commands are discussed together because of their similarities.

SET BREAK Command Overview

The SET BREAK command lets you specify program locations or events at which to suspend program execution (breakpoints). After setting a breakpoint, you can start or resume program execution with the GO command, letting the program run until the specified location or condition is reached. When the breakpoint is triggered, the debugger suspends execution, identifies the breakpoint, and displays the DBG> prompt. You can then enter debugger commands---for example, to determine where you are (with the SHOW CALLS command), step into a routine, examine or modify variables, and so on.

The syntax of the SET BREAK command is as follows:

SET BREAK[/qualifier[...]] [address-expression[, ...]]
    [WHEN (conditional-expression)]
    [DO (command[; ...])]

The following example shows a typical use of the SET BREAK command and shows the general default behavior of the debugger at a breakpoint.

In this 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, execution is paused, the debugger announces that the breakpoint at COUNT has been reached ("break at ..."), displays the source line (54) where execution is paused, and prompts for another command:

break at routine PROG2\COUNT
     54:  procedure COUNT(X,Y:INTEGER);

SET TRACE Command Overview

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

The debugger's default behavior at a tracepoint is identical to that at a breakpoint, except that program execution continues past a tracepoint. Thus, the DBG> prompt is not displayed when a tracepoint is reached and announced by the debugger.

Except for the command name, the syntax of the SET TRACE command is identical to that of the SET BREAK command:

SET TRACE[/qualifier[...]] [address-expression[, ...]]
[WHEN (conditional-expression)]
[DO (command[; ...])]

The SET TRACE and SET BREAK commands have similar syntax. When using the SET TRACE command, specify address expressions, qualifiers, and the optional WHEN and DO clauses exactly as with the SET BREAK command.

Unless you use the /TEMPORARY qualifier on the SET BREAK or SET TRACE command, breakpoints and tracepoints remain in effect until you:

To identify all of the breakpoints or tracepoints that are currently set, use the SHOW BREAK or SHOW TRACE command.

To deactivate, activate, or cancel breakpoints or tracepoints, use the following commands (see Section 3.3.7):


The following sections describe how to specify program locations and events with the SET BREAK and SET TRACE commands.

3.3.1 Setting Breakpoints or Tracepoints on Individual Program Locations

To set a breakpoint or a tracepoint on a particular program location, specify an address expression with the SET BREAK or SET TRACE command.

Fundamentally, an address expression specifies a memory address or a register. Because the debugger understands the symbols associated with your program, the address expressions you typically use with the SET BREAK or SET TRACE command are routine names, labels, or source line numbers rather than memory addresses---the debugger converts these symbols to addresses. Specifying Symbolic Addresses


In some cases, when using the SET BREAK or SET TRACE command with a symbolic address expression, you might need to set a module or specify a scope or a path name. Those concepts are described in detail in Chapter 5. The examples in this section assume that all modules are set and that all symbols referenced are uniquely defined, unless otherwise indicated.

The following examples show how to set a breakpoint on a routine (SWAP) and a tracepoint on a label (LOOP1):


The next command sets a breakpoint on the return instruction of routine SWAP. Breaking on the return instruction of a routine lets you inspect the local environment (for example, to obtain the values of local variables) while the routine is still active.


Some languages, for example Fortran, use numeric labels. To set a breakpoint or a tracepoint on a numeric label, you must precede the number with the built-in symbol %LABEL. Otherwise, the debugger interprets the number as a memory address. For example, the following command sets a tracepoint on label 20:


You can set a breakpoint or a tracepoint on a line of source code by specifying the line number preceded by the built-in symbol %LINE. The following command sets a breakpoint on line 14:


The previous breakpoint causes execution to pause on the first instruction of line 14. You can set a breakpoint or a tracepoint only on lines for which the compiler generated instructions (lines that resulted in executable code). If you specify a line number that is not associated with an instruction, such as a comment line or a statement that declares but does not initialize a variable, the debugger issues a diagnostic message. For example:

%DEBUG-I-LINEINFO, no line 6, previous line is 5, next line is 8
%DEBUG-E-NOSYMBOL, symbol '%LINE 6' is not in the symbol table

The previous messages indicate that the compiler did not generate instructions for lines 6 or 7 in this case.

Some languages allow more than one statement on a line. In such cases, you can use statement numbers to differentiate among statements on the same line. A statement number consists of a line number, followed by a period (.), and a number indicating the statement. The syntax is as follows:

%LINE line-number.statement-number

For example, the following command sets a tracepoint on the second statement of line 38:


When searching for symbols that you reference in commands, the debugger uses the conventions described in Section 5.3.1. That is, it first looks within the module where execution is currently paused, then in other scopes associated with routines on the call stack, and so on. Therefore, to specify a symbol that is defined in more than one module, such as a line number, you might need to use a path name. For example, the following command sets a tracepoint on line 27 of module MOD4:


Remember the symbol lookup conventions when specifying a line number in debugger commands. If that line number is not defined in the module where execution is paused (because it is not associated with an instruction), the debugger uses the symbol lookup conventions to locate another module where the line number is defined.

When specifying address expressions, you can combine symbolic addresses with byte offsets. Thus, you can set a breakpoint or a tracepoint on a particular instruction by specifying its line number and the byte offset from the beginning of that line to the first byte of the instruction. For example, the next command sets a breakpoint on the address that is five bytes beyond the beginning of line 23:


Previous Next Contents Index