DocumentationEndless PL/I for OpenVMSUser Manual  ·  ESS-PFVUM-001

Chapter 3

Using the OpenVMS Debugger

This chapter is an introduction to using the OpenVMS Debugger with PL/I programs. This chapter provides the following information:

  • An overview of the debugger

  • Information to get you started using the debugger

  • A sample terminal session that demonstrates using the debugger

  • A list of the debugger commands by function

For complete reference information on the OpenVMS Debugger, see the OpenVMS Debugger Manual. Online HELP is available during debugging sessions.

3.1. Overview

A debugger is a tool that helps you locate run-time errors quickly. It is used with a program that has already been compiled and linked successfully, but does not run correctly. For example, the output may be obviously wrong, or the program goes into an infinite loop or terminates prematurely. The debugger enables you to observe and manipulate the program's execution interactively so that you can locate the point at which the program stopped working correctly.

The OpenVMS Debugger is a symbolic debugger, which means that you can refer to program locations by the symbols (names) you used for those locations in your program-the names of variables, routines, labels, and so on. You do not need to use virtual addresses to refer to memory locations.

The debugger recognizes the syntax, expressions, data typing, and other constructs of PL/I, as well as other VAX languages including:

  • VAX Ada

  • VAX BASIC

  • DEC BASIC

  • VAX BLISS

  • VAX C

  • DEC C

  • DEC C++

  • VAX COBOL

  • DEC COBOL

  • VAX DIBOL

  • VAX FORTRAN

  • DEC FORTRAN

  • VAX MACRO-32

  • DEC Pascal

  • VAX Pascal

  • VAX RPG II

  • VAX SCAN

If your program is written in more than one language, you can change from one language to another during a debugging session. The current source language determines the format used for entering and displaying data, as well as other features that have language-specific settings (for example, comment characters, operators and operator precedence, and case sensitivity or insensitivity).

By issuing debugger commands at your terminal, you can perform the following operations:

  • Start, stop, and resume the program's execution

  • Trace the execution path of the program

  • Monitor selected locations, variables, or events

  • Examine and modify the contents of variables, or force events to occur

  • Test the effect of some program modifications without having to edit, recompile, and relink the program

Such techniques allow you to isolate an error in your code much more quickly than you could without the debugger.

Once you have found the error in the program, you can then edit the source code and compile, link, and run the corrected version.

3.2. Features of the Debugger

The OpenVMS Debugger provides the following features to help you debug your programs:

  • Online HELP

    Online HELP is always available during a debugging session and contains information on all the debugger commands and also information on selected topics.

  • Source Code Display

    You can display lines of source code during a debugging session.

  • Screen Mode

    You can capture and display various kinds of information in scrollable windows, which can be moved around the screen and resized. Automatically updated source, instruction, and register displays are available. You can selectively direct debugger input, output, and diagnostic messages to displays. (Screen mode displays work best on VT100-series or VT200-series terminals or MicroVAX workstations.)

  • Keypad Mode

    When you invoke the debugger, several commonly used debugger command sequences are assigned by default to the keys of the numeric keypad (if you have a VT100, VT52, or LK201 keypad).

  • Source Editing

    As you find errors during a debugging session, you can use the EDIT command to invoke any editor available on your system. (You specify the editor you want with the SET EDITOR command.)

  • Command Procedures

    The debugger allows you to execute a command procedure to recreate a debugging session, to continue a previous session, or to avoid typing the same debugger commands many times during a debugging session.

  • Symbol Definitions

    You can define your own symbols to represent lengthy commands, address expressions, or values.

  • Initialization Files

    You can create an initialization file containing commands to set your default debugging modes, screen display definitions, keypad key definitions, symbol definitions, and so on. In addition, you may want to have special initialization files for debugging specific programs.

  • Log Files

    You can record the commands you issue during a debugging session and the debugger's responses to those commands in a log file. You can use log files to keep track of your debugging efforts, or you can use them as command procedures in subsequent debugging sessions.

3.3. Getting Started with the Debugger

This section explains how to use the debugger with PL/I programs. The section focuses on basic debugger functions, to get you started quickly. It also provides any debugger information that is specific to PL/I. For more detailed information that is not specific to a particular language, see the OpenVMS Debugger Manual.

3.3.1. Compiling and Linking a Program to Prepare for Debugging

Before you can use the debugger, you must compile and link your program as explained in this section. The following example shows how to compile and link a PL/I program (consisting of a single compilation unit named INVENTORY) to prepare for using the debugger.

$ PLI/DEBUG/NOOPTIMIZE INVENTORY
$ LINK/DEBUG INVENTORY

The /DEBUG qualifier on the PLI command causes the compiler to write the debug symbol records associated with INVENTORY into the object module, INVENTORY.OBJ. These records allow you to use the names of variables and other symbols declared in INVENTORY in debugger commands. (If your program has several compilation units, you must compile each unit that you want to debug with the /DEBUG qualifier.)

You should use the /NOOPTIMIZE qualifier when you compile a program in preparation for debugging. Otherwise, if the object code is optimized (to reduce the size of the program and make it run faster), the contents of some program locations may be inconsistent with what you might expect from viewing the source code. After debugging the program, you should recompile it without the /NOOPTIMIZE qualifier.

The /DEBUG qualifier on the LINK command causes the linker to include all symbol information that is contained in INVENTORY.OBJ in the executable image. This qualifier also causes the OpenVMS image activator to start the debugger at run time. (If your program has several object modules, you may need to specify the other modules in the LINK command.)

3.3.2. Starting and Terminating a Debugging Session

To invoke the debugger, issue the DCL command RUN. The following message will appear on your screen:

$ RUN INVENTORY

                     OpenVMS DEBUG Version 5.4

%DEBUG-I-INITIAL, language is PL/I, module set to 'INVENTORY'
DBG>

The INITIAL message indicates that the debugging session is initialized for a PL/I program and that the name of the main program unit is INVENTORY. The DBG> prompt indicates that you can now type debugger commands. At this point, if you type the GO command, program execution begins and continues until it is forced to pause or stop (for example, if the program prompts you for input, or an error occurs).

If you have a mixed-language program that includes an Ada package, the following message will appear on your screen instead of the previous one when you invoke the debugger:

$ RUN INVENTORY

                     OpenVMS DEBUG Version <VMS_VERSION>

%DEBUG-I-INITIAL, language is PL/I, module set to 'INVENTORY'
%DEBUG-I-NOTATMAIN, type GO to get to start of main program
DBG>

The NOTATMAIN message indicates that execution is suspended before the start of the main program, so that you can execute initialization code under debugger control. Typing the GO command places you at the start of the main program. At that point, type the GO command again to start program execution. Execution continues until it is forced to pause or stop (for example, if the program prompts you for input, or an error occurs).

The following message indicates that your program has completed successfully:

%DEBUG-I-EXITSTATUS, is '%SYSTEM-S-NORMAL, normal successful completion'
DBG>

To interrupt a debugging session and return to the DCL level, press Ctrl/y. This is useful if, for example, your program loops or you want to interrupt a debugger command that is still in progress.

To resume the debugging session after a Ctrl/y interruption, type either the CONTINUE or the DEBUG command at the DCL level. Use the CONTINUE command to return to the point at which you interrupted the debugging session. If you interrupted the session because of an infinite loop, use the DEBUG command instead. The DEBUG command returns you to the debugger prompt so that you can type another command. For example:

DBG> GO
⋮
Ctrl/y
Interrupt

$ DEBUG
DBG>

To end a debugging session, type the EXIT command or press Ctrl/z:

DBG> EXIT
$

3.3.3. Issuing Debugger Commands

You can issue debugger commands anytime you see the debugger prompt (DBG>). Type the command at the keyboard and press the Return key. You can issue several commands on a line by separating the command strings with semicolons (;). As with DCL commands, you can continue a command string on a new line by ending the previous line with a hyphen (-).

Alternatively, you can use the numeric keypad to issue certain commands. Figure 3.1, “Debugger Keypad Key Functions” identifies the predefined key functions. You can also redefine key functions with the DEFINE/KEY command.

Most keypad keys have three predefined functions-default, GOLD, and BLUE. (The PF1 key is known as the GOLD key; the PF4 key is known as the BLUE key.) To obtain a key's default function, press the key. To obtain its GOLD function, first press the PF1 key, and then the key. To obtain its BLUE function, first press the PF4 key, and then the key.

In Figure 3.1, “Debugger Keypad Key Functions”, the default, GOLD, and BLUE functions are listed within each key's outline, from top to bottom, respectively. For example, pressing keypad key 0 issues the STEP command; pressing key PF1 and then key 0 issues the STEP/INTO command; pressing key PF4 and then key 0 issues the STEP/OVER command.

Type the command HELP KEYPAD to get help on the keypad key definitions.

Figure 3.1. Debugger Keypad Key Functions
Debugger Keypad Key Functions

3.3.4. Viewing Your Source Code

The debugger provides two modes for displaying information: noscreen mode and screen mode. By default, when you invoke the debugger, you are in noscreen mode, but you may find that it is easier to view your source code in screen mode. Both modes are briefly described in the following sections.

3.3.4.1. Noscreen Mode

Noscreen mode is the default, line-oriented mode of displaying input and output. To invoke noscreen mode from screen mode, press the keypad key sequence GOLD-PF3. See the sample debugging session in Section 3.4, “Sample Debugging Session” for a demonstration of noscreen mode.

In noscreen mode, you can use the TYPE command to display one or more source lines. For example, the following command displays line 3 of the module whose code is currently executing:

DBG> TYPE 3
3:    I = 7;
DBG>

The display of source lines is independent of program execution. To display source code from a module other than the one whose code is currently executing, use the TYPE command with a path name to specify the module. For example, the following command displays lines 16 through 21 of module TEST:

DBG> TYPE TEST\16:21

3.3.4.2. Screen Mode

To invoke screen mode, press keypad key PF3. In screen mode, the debugger splits the screen into three displays named SRC, OUT, and PROMPT, by default. The following example shows how your screen will appear in screen mode.

- SRC: module MAIN -scroll-source------------------------
     1: MAIN:  PROCEDURE OPTIONS (MAIN);
     2: DECLARE (I,J,K) FIXED BINARY;
     3:   I = 7;
->   4:   J = 4;
     5:   K = I + J;
     6: END;







- OUT -output---------------------------------------------
stepped to MAIN\%LINE 4
MAIN\I: 7
MAIN\J: 50331649




- PROMPT -error-program-prompt----------------------------
DBG> STEP 2
DBG> EXAMINE I,J
DBG>

The SRC display, at the top of the screen, shows the source code of the module (compilation unit) whose code is currently executing. An arrow in the left column points to the next line to be executed, which corresponds to the current value of the program counter (PC). The line numbers, which are assigned by the compiler, match those in a listing file.

The OUT display, in the middle of the screen, captures the debugger's output in response to the commands that you issue.

The PROMPT display, at the bottom of the screen, shows the debugger prompt (DBG>), your input, debugger diagnostic messages, and program output. In the example, the two debugger commands that have been issued (STEP 2 and EXAMINE I,J) are displayed.

(The unpredictable value reported by the debugger for J indicates that line 4 has not been executed yet; line 4 will subsequently assign the value 4 to J.)

The SRC and OUT displays can be scrolled to display information beyond the window's edge. Press keypad key 8 to scroll up and keypad key 2 to scroll down. Use keypad key 3 to change the display to be scrolled (by default, the SRC display is scrolled). Scrolling a display does not affect program execution.

If the debugger cannot locate source lines for the routine that is currently executing, it attempts to display source lines in the next routine down on the call stack for which source lines are available and issues the following message:

%DEBUG-I-SOURCESCOPE, Source lines not available for .0\%PC.
        Displaying source in a caller of the current routine.

Source lines may not be available for the following reasons:

  • The PC value is within a system routine or a shareable image routine for which no source code is available.

  • The PC value is within a routine that was compiled without the /DEBUG compiler command qualifier (or with /NODEBUG).

  • The PC value is within a routine whose module is not set (module setting is explained in Section 3.3.7.1, “Module Setting”).

  • The source file was moved to a different directory after it was compiled (the location of source files is embedded in the object modules).

3.3.5. Controlling and Monitoring Program Execution

This section discusses the following topics:

  • Starting and resuming program execution with the GO command

  • Stepping through the program's code with the STEP command

  • Determining the current value of the program counter (PC) with the SHOW CALLS command

  • Suspending program execution with breakpoints

  • Tracing program execution with tracepoints

  • Monitoring changes in variables with watchpoints

3.3.5.1. Starting and Resuming Program Execution

There are two commands for starting or resuming program execution: GO and STEP. The GO command starts execution. The STEP command executes a specified number of source lines or instructions.

The GO Command

The GO command starts program execution, which continues until forced to stop. The GO command is used most often in conjunction with breakpoints, tracepoints, and watchpoints (described in Sections 3.3.5.3, 3.3.5.4, and 3.3.5.5). If you set a breakpoint in the path of execution and then issue the GO command, execution is suspended at that breakpoint. If you set a tracepoint, the path of execution through that tracepoint is monitored. If you set a watchpoint, execution is suspended when the value of the watched variable changes.

You can also use the GO command to test for an exception condition or an infinite loop. If an exception condition that is not handled by your program occurs, the debugger takes control and displays the DBG> prompt so that you can issue commands. If you are using screen mode, the pointer in the source display indicates where execution stopped. You can use the SHOW CALLS command (explained in Section 3.3.5.2, “Determining the Current Value of the Program Counter”) to identify the currently active routine calls (the call stack).

If an infinite loop occurs, the program does not terminate, so the debugger prompt does not reappear. To obtain the prompt, interrupt the program by pressing Ctrl/y and then issue the DCL command DEBUG. You can then look at the source display and invoke a SHOW CALLS display to obtain the current PC value.

The STEP Command

The STEP command allows you to execute a specified number of source lines or instructions, or to execute the program to the next instruction of a particular kind, for example, to the next CALL instruction.

By default, the STEP command executes a single source line at a time. 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:

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

The PC value is now at the first machine code instruction for line 27 of the module TEST; line 27 is in COUNT, a routine within the module TEST. TEST\COUNT\%LINE 27 is a path name. The debugger uses path names to refer to symbols. (You do not need to use a path name in referring to a symbol, however, unless the symbol is not unique. If the symbol is not unique, the debugger issues an error message. See Section 3.3.7.2, “Resolving Multiply Defined Symbols” for more information on resolving multiply defined symbols.)

The STEP command can execute a number of lines at a time. In the following example, the STEP command executes three lines:

DBG> STEP 3

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 contains more than one statement, the debugger executes all the statements on that line as part of the single step.

You can specify different stepping modes, such as stepping by instruction rather than by line (SET STEP INSTRUCTION). Also, by default, the debugger steps over called routines; execution is not suspended within a called routine, although the routine is executed. Issuing the SET STEP INTO command causes the debugger to suspend execution within called routines, as well as within the routine that is currently executing.

3.3.5.2. Determining the Current Value of the Program Counter

The SHOW CALLS command lets you determine the current value of the program counter (PC) (for example, after returning to the debugger following a Ctrl/y interruption).

The SHOW CALLS command displays a traceback that lists the sequence of calls leading to the currently executing routine. For each routine (beginning with the currently executing routine), the debugger displays the following information:

  • The name of the module that contains the routine

  • The name of the routine

  • The line number at which the call was made (or at which execution is suspended, in the case of the current routine)

  • The corresponding PC addresses (the relative PC address from the start of the routine, and the absolute PC address of the program)

For example:

DBG> SHOW CALLS
 module name      routine name       line     rel PC    abs PC

*TEST             PRODUCT              18    00000009  0000063C
*TEST             COUNT                47    00000009  00000647
*MY_PROG          MY_PROG              21    0000000D  00000653
DBG>

This example indicates that execution is currently at line 18 of routine PRODUCT (in module TEST), which was called from line 47 of routine COUNT (in module TEST), which was called from line 21 of routine MY_PROG (in module MY_PROG).

3.3.5.3. Suspending Program Execution

The SET BREAK command lets you select breakpoints, which are locations at which program execution is suspended. When you reach a breakpoint, you can issue commands to check the call stack, examine the current values of variables, and so on.

In the following example, the SET BREAK command sets a breakpoint on the procedure COUNT. The GO command then starts execution. When the procedure COUNT is encountered, execution is suspended. The debugger reports that the breakpoint at COUNT has been reached (break at ...), displays the source line (54) where execution is suspended, and prompts you for another command. At this breakpoint, you could step through the procedure COUNT, using the STEP command, and use the EXAMINE command (discussed in Section 3.3.6.1, “Displaying the Values of Variables”) to check on the current values of X and Y.

DBG> SET BREAK COUNT
DBG> GO
⋮
break at PROG2\COUNT
     54:  COUNT: PROCEDURE (X,Y);
DBG>

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

Routine names and labels should be specified as they appear in the source code. Line numbers may 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 next command sets a breakpoint at line 41 of the module whose code is currently executing; the debugger suspends execution when the PC value is at the start 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, if you try to set a breakpoint on a comment line). To set a breakpoint on a line number in a module other than the one whose code is currently executing, specify the module's name in a path name. For example:

DBG> SET BREAK SCREEN_IO\%LINE 58

You do not always need to specify a particular program location, such as line 58 or COUNT, to set a breakpoint. You can set breakpoints on events, such as exceptions. You can also use the SET BREAK command with the /LINE qualifier (but no parameter) to break on every line, or with the /CALL qualifier to break on every CALL instruction, and so on. For example:

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

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). For example, the next command sets a breakpoint on the label LOOP3. The DO (EXAMINE TEMP) clause causes the value of the variable TEMP to be displayed whenever the breakpoint is triggered.

DBG> SET BREAK LOOP3 DO (EXAMINE TEMP)
DBG> GO
⋮
break at COUNTER\LOOP3
     37:    LOOP3: DO I = 1 TO 10
COUNTER\TEMP:   284.19
DBG>

To display the currently active breakpoints, issue the SHOW BREAK command:

DBG> SHOW BREAK
breakpoint at SCREEN_IO\%LINE 58
breakpoint at COUNTER\LOOP3
   do (EXAMINE TEMP)
⋮
DBG>

If any portion of your program was written in Ada, two breakpoints that are associated with Ada tasking exception events are automatically established when you invoke the debugger. When you issue a SHOW BREAK command under these conditions, the following breakpoints are displayed:

DBG> SHOW BREAK
Breakpoint on ADA event "DEPENDENTS_EXCEPTION" for any value
Breakpoint on ADA event "EXCEPTION_TERMINATED" for any value

These breakpoints are equivalent to issuing the following commands:

DBG> SET BREAK/EVENT=DEPENDENTS_EXCEPTION
DBG> SET BREAK/EVENT=EXCEPTION_TERMINATED

To cancel a breakpoint, issue the CANCEL BREAK command, specifying the program location or event exactly as you did when setting the breakpoint. The CANCEL BREAK/ALL command cancels all breakpoints.

3.3.5.4. Tracing Program Execution

The SET TRACE command lets you select tracepoints, which are locations for tracing the execution of your program 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 the routine is called.

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

DBG> SET TRACE COUNT
DBG> GO
⋮
trace at PROG2\COUNT
     54:  COUNT: PROCEDURE (X,Y);
⋮

When using the SET TRACE command, specify address expressions, qualifiers, and optional clauses exactly as with the SET BREAK command.

The /LINE qualifier causes the SET TRACE command to trace every line and is a convenient means of checking the execution path. By default, lines are traced within all called routines, as well as the currently executing routine. If you do not want to trace through system routines or through routines in shareable images, use the /NOSYSTEM or /NOSHARE qualifiers. For example:

DBG> SET TRACE/LINE/NOSYSTEM/NOSHARE

The /SILENT qualifier suppresses the trace message and the display of source code. This is useful when you want to use the SET TRACE command to execute a debugger command at the tracepoint. For example:

DBG> SET TRACE/SILENT %LINE 83 DO (EXAMINE STATUS)
DBG> GO
⋮
SCREEN_IO\CLEAR\STATUS:   'OFF'
⋮

3.3.5.5. Monitoring Changes in Variables

The SET WATCH command lets you set watchpoints that will be monitored continuously as your program executes. With high-level languages, you typically set watchpoints on variables that have been declared in your program. In addition, you can set watchpoints on arbitrary program locations. If the program modifies the value of a watched variable, the debugger suspends execution and displays the old and new values.

To set a watchpoint on a variable, specify 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.

The following example shows the effect on program execution when your program modifies the contents of a 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 the GO command is issued to start execution. When the value of TOTAL changes, execution is suspended. The debugger reports the event watch of ... and identifies where TOTAL changed (line 13) and the associated source line. The debugger then displays the old and new values and reports that execution has been suspended at the start of the next line (14). (The debugger reports break at ..., but this is not a breakpoint; it is the effect of the watchpoint.) Finally, the debugger prompts for another command.

When a change in a variable occurs at a point other than at the start of a source line, the debugger gives the line number plus the byte offset from the start of the line.

Note that this general technique for setting watchpoints always applies to static variables. A static variable is associated with the same virtual memory location throughout program execution. In PL/I, only the following kinds of variables are statically allocated:

  • STATIC

  • EXTERNAL

  • GLOBALDEF

  • GLOBALREF

A variable that is allocated on the stack or in a register (a nonstatic variable) exists only when its defining routine is active (on the call stack). In PL/I nonstatic variables include the following:

  • AUTOMATIC

  • BASED

  • CONTROLLED

  • DEFINED

  • PARAMETER

If you try to set a watchpoint on a nonstatic variable when its defining routine is not active, the debugger issues a warning like the following:

DBG> SET WATCH Y
%DEBUG-W-SYMNOTACT, nonstatic variable 'Y' is not active

A convenient technique for setting a watchpoint on a nonstatic variable is to set a breakpoint on the defining routine, also specifying a DO clause to set the watchpoint whenever execution reaches the breakpoint. In the following example, a watchpoint is set on the nonstatic variable Y in routine COUNTER:

DBG> SET BREAK COUNTER DO (SET WATCH Y)
DBG> GO
⋮
break at routine MOD4\COUNTER
%DEBUG-I-WPTTRACE, nonstatic watchpoint, tracing every instruction
DBG> SHOW WATCH
watchpoint of MOD4\COUNTER\Y [tracing every instruction]
DBG>

The debugger monitors nonstatic watchpoints by tracing every instruction. Because this slows execution speed compared to monitoring static watchpoints, the debugger informs you when it is monitoring nonstatic watchpoints.

When execution eventually 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.

3.3.6. Examining and Manipulating Data

This section explains how to use the EXAMINE, DEPOSIT, and EVALUATE commands to display and modify the contents of variables and to evaluate expressions. It also notes restrictions on the use of these commands with PL/I programs.

Note that, before you can examine or deposit into a nonstatic variable (as defined in the previous section), its defining routine must be active (on the call stack).

3.3.6.1. Displaying the Values of Variables

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

EXAMINE variable-name

The debugger recognizes the compiler-generated data type of the specified variable 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 integers (three per dimension):

DBG> EXAMINE INTEGER_ARRAY
PROG2\INTEGER_ARRAY
   (1,1):       27
   (1,2):       31
   (1,3):       12
   (2,1):       15
   (2,2):       22
   (2,3):       18
DBG>

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

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

Examine the value of a variable declared as FIXED DECIMAL (10,5):

DBG> EXAMINE X
PROG4\X:   540.02700
DBG>

Examine the value of a structure variable:

DBG> EXAMINE PART
MAIN_PROG\INVENTORY_PROG.PART
    ITEM:      "WF-1247"
    PRICE:       49.95
    IN_STOCK:    24
DBG>

Examine the value of a pictured variable (note that the debugger displays the value in quotation marks):

DBG> EXAMINE Q
MAINP\Q:    "666.3330"
DBG>

Examine the value of a pointer (which is the virtual address of the variable it accesses) and display the value in hexadecimal radix instead of decimal (the default):

DBG> EXAMINE/HEXADECIMAL P
PROG4\SAMPLE.P: 0000B2A4
DBG>

Examine the value of a variable with the BASED attribute; in this case, the variable X has been declared as BASED(PTR), with PTR its pointer:

DBG> EXAMINE X
PROG5\X:    "A"
DBG>

Examine the value of a variable X declared as BASED with a variable PTR declared as POINTER; here, PTR is associated with X by the following line of PL/I code (instead of X having been declared as BASED(PTR) as in the preceding example):

ALLOCATE X SET (PTR);

In this case, you examine the value of X as follows:

DBG> EXAMINE PTR->X
PROG6\PTR->X:    "A"
DBG>

The EXAMINE command can be used 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. You can override the defaults for typed and untyped locations if you want the data to be interpreted and displayed in some other data format.

See Section 3.3.6.3, “Evaluating Expressions” for a comparison of the EXAMINE and EVALUATE commands.

3.3.6.2. Changing the Values of Variables

To change the value of a variable, use the DEPOSIT command. The DEPOSIT command has the following form:

DEPOSIT variable-name = value

The DEPOSIT command is like an assignment statement in PL/I.

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 PARTNUMBER = "WG-7619.3-84"

Deposit an integer expression:

DBG> DEPOSIT WIDTH = CURRENTWIDTH + 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'

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

3.3.6.3. Evaluating Expressions

To evaluate a language expression, use the EVALUATE command. The EVALUATE command has the following form:

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

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

The following example shows how the EVALUATE and EXAMINE commands are similar. When the expression following the command is a variable name, the value reported by the debugger is the same for either command.

DBG> DEPOSIT WIDTH = 45
DBG> EVALUATE WIDTH
45
DBG> EXAMINE WIDTH
SIZE\WIDTH:   45

The following example shows an important difference between the EVALUATE and EXAMINE commands:

DBG> EVALUATE WIDTH + 7
52
DBG> EXAMINE WIDTH + 7
SIZE\WIDTH:   131584

With the EVALUATE command, WIDTH + 7 is interpreted as a language expression, which evaluates to 45 + 7, or 52. With the EXAMINE command, WIDTH + 7 is interpreted as an address expression: 7 bytes are added to the address of WIDTH, and whatever value is in the resulting address is reported (in this instance, 131584).

3.3.6.4. Notes on Debugger Support for PL/I

In general, the debugger supports the data types and operators of PL/I and of the other debugger-supported languages. However, there are certain language-specific limitations or other differences. (For information on the supported data types and operators of any of the languages, type the HELP LANGUAGE command at the DBG> prompt.)

You cannot use the DEPOSIT command with entry or label variables or formats, or with entire arrays or structures. You cannot use the EXAMINE command with entry or label variables or formats; use the EVALUATE/ADDRESS command instead.

You cannot use the EXAMINE command to determine the values or attributes of global literals (such as GLOBALDEF VALUE literals) because they are static expressions. Use the EVALUATE command instead.

You cannot use the EXAMINE, EVALUATE, and DEPOSIT commands with compile-time variables and procedures. You can, however, use EVALUATE and DEPOSIT (but not EXAMINE) with a compile-time constant, as long as the constant is the source and not the destination.

Note that an uninitialized automatic variable does not have valid contents until after a value has been assigned to it. If you examine it before that point, the value displayed is unpredictable.

You can deposit a value into a pointer variable either by depositing another pointer's value into it, thus making symbolic reference to both pointers, or by depositing a virtual address into it. (You can find out the virtual address of a variable by using the EVALUATE/ADDRESS command, and then deposit that address into the pointer.) When you examine a pointer, the debugger displays its value in the form of the virtual address of the variable that the pointer points to.

Because the default representation of decimal constants in PL/I is packed decimal rather than fixed binary, debugger commands such as EVALUATE/HEXADECIMAL 53 display the number's packed decimal representation. You can cause conversion to take place, however, either by specifying a fixed binary variable in the expression, or by using a constant in a radix other than decimal (because nondecimal radix constants are assumed to be fixed binary), as in the command:

EVALUATE/HEXADECIMAL 53 + %HEX 0.

3.3.7. Controlling Symbol References

In most cases, the way the debugger handles symbols (variable names and so on) is transparent to you. However, the following two areas may require action on your part:

  • Module setting

  • Multiply defined symbols

3.3.7.1. Module Setting

To facilitate symbol searches, the debugger loads symbol records from the executable image into a run-time symbol table (RST), where they can be accessed efficiently. Unless a symbol record is in the RST, the debugger cannot recognize or properly interpret that symbol.

Because the RST uses memory, the debugger loads it dynamically, anticipating what symbols you might want to reference during execution. The loading process is called module setting, because all of the symbol records of a given module are loaded into the RST at one time.

At debugger startup, only the module containing the image transfer address is set. As your program executes, whenever the debugger interrupts execution, it sets the module surrounding the current PC value. This lets you 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 issues a warning. For example:

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

You must then use the SET MODULE command to set the module containing that symbol manually:

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 have been set.

Note that dynamic module setting may slow down the debugger 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 issuing the SET MODE NODYNAMIC command. (The SET MODE DYNAMIC command enables dynamic module setting.)

3.3.7.2. Resolving Multiply Defined Symbols

The debugger finds the symbols that you reference in commands according to the scope and visibility rules of the currently set language. In general, the debugger first searches for a symbol within the block or routine surrounding the current PC value. If the symbol is not found in that scope region, the debugger searches the nesting program unit, then its nesting unit, and so on. (The precise order of search depends on the currently set language and guarantees that the proper declaration of a multiply defined symbol is selected.)

The debugger allows you to reference symbols throughout your program, not just those that are visible at the current PC value, so that you can set breakpoints in arbitrary areas, examine arbitrary variables, and so on. Therefore, if the symbol is not visible at the current PC value, the debugger also searches other scope regions. First, it searches within the currently executing routine, then the caller of that routine, then its caller, and so on, until the symbol is found. Symbolically, this search list is denoted 0,1,2,..., n, where n is the number of calls in the call stack. Within each of these scope regions, the debugger uses the visibility rules of the currently set language to locate symbols.

If the debugger cannot resolve a symbol ambiguity, it issues a warning. 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; then use the desired path name 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 will specify the declaration of Y that is visible in the new scope region. 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.

3.4. Sample Debugging Session

This section shows a sample debugging session with a PL/I program, SUM, which contains a logic error. Line numbers have been added to facilitate the discussion.

    1             SUM: PROCEDURE OPTIONS(MAIN);
    2    1             DECLARE (I,HIGHEST,TOTAL) FIXED;
    3    1                TOTAL=0;
    4    1                HIGHEST=1;
    5    1                DO WHILE (HIGHEST>0);
    6    2                    GET LIST (HIGHEST) OPTIONS (PROMPT
    7    2                     ('Type a number greater than 0, or 0 to quit: '));
    8    2                    IF HIGHEST <=0
    9    2                     THEN
   10    2                        STOP;
   11    2                    DO I=1 TO HIGHEST;
   12    3                        TOTAL=TOTAL+I;
   13    3                        END;
   14    2                    PUT SKIP EDIT
   15    2                     ('The sum of integers from 1 through',
   16    2                     HIGHEST,' is',TOTAL)
   17    2                     (A,F(10),A,F(10));
   18    2                    PUT SKIP;
   19    2                    END;  /* DO WHILE */
   20    1        END SUM;

This program prompts for a number and prints the sum of the integers from 1 through the number entered. The problem in the program occurs because the variable TOTAL is not reinitialized when a new number is entered; the statement assigning the value 0 to TOTAL occurs before the loop instead of within it.

Initially, you might compile, link, and run the program as follows:

$ PLI SUM
$ LINK SUM
$ RUN SUM
Type a number greater than 0, or 0 to quit: 5

The sum of integers from 1 through         5 is        15
Enter a number: 4
The sum of integers from 1 through         4 is        25
Type a number greater than 0, or 0 to quit: 0
$

The program returns a correct sum for the first number you enter, but the sum for the second number is obviously too high.

To debug the program, you must compile and link with the debugger. (If you want a listing with line numbers to refer to during the debugging session, include the /LIST qualifier with the PLI command, and then print the listing file that results; you need not specify the file type because the PRINT command searches for the LIS file type by default.) For example:

$ PLI/DEBUG/LIST/NOOPTIMIZE SUM
$ LINK/DEBUG SUM
$ PRINT SUM

You are now ready to begin a debugging session. The terminal session is keyed to the numbered notes that follow.

$ RUN SUM

                    VAX DEBUG Version <VMS_VERSION>

%DEBUG-I-INITIAL, language is PLI, module set to 'SUM'   ❶
DBG> SET BREAK %LINE 7   ❷
DBG> GO    ❸
break at SUM\%LINE 7    ❹
     7:              ('Type a number greater than 0, or 0 to quit: '));
DBG> EXAMINE TOTAL
SUM\TOTAL:      0    ❺
DBG> GO
Type a number greater than 0, or 0 to quit: 5

The sum of integers from 1 through         5 is        15    ❻

break at SUM\%LINE 7
     7:              ('Type a number greater than 0, or 0 to quit: '));   ❼
DBG> EXAMINE TOTAL
SUM\TOTAL:     15    ❽
DBG> DEPOSIT TOTAL=0   ❾
DBG> GO
Type a number greater than 0, or 0 to quit: 4
The sum of integers from 1 through         4 is        10    ❿

break at SUM\%LINE 7
     7:              ('Type a number greater than 0, or 0 to quit: '));
DBG> GO
Type a number greater than 0, or 0 to quit: 0  (11)
%DEBUG-I-EXITSTATUS, is '%SYSTEM-S-NORMAL, normal successful completion'
DBG> EXIT    (12)
$
  1. When you issue the RUN command, the debugger displays an informational message and the DBG> prompt. You are now in the default noscreen mode. The lines of source code are displayed as they are executed, by default.

  2. You decide that the problem may lie with the initialization of the variable TOTAL. You can test this hypothesis by examining the value of TOTAL each time you enter a new number. To stop the program at the point at which you can do this, you set a breakpoint at line 7.

  3. The GO command starts program execution.

  4. When line 7 is reached, the debugger interrupts program execution and displays the source line at which the breakpoint was set.

  5. You use the EXAMINE command to determine the current value of the variable TOTAL. Its value is 0, as expected at this point.

  6. The GO command resumes program execution. The program now prompts you for a number. You type 5. The program's response is correct.

  7. The debugger again reaches the breakpoint at line 7 and displays the source line.

  8. You examine the variable TOTAL with the EXAMINE command. Its value is 15, not 0 as it should be. This indicates that the assignment statement that initializes TOTAL is misplaced.

  9. The DEPOSIT command replaces the contents of TOTAL with 0, thus allowing the program to return a correct result the next time through the loop.

  10. The GO command resumes program execution. The result is correct.

  11. When you enter a 0 in response to the prompt, the program exits, causing the debugger to display a message that indicates the termination status.

  12. The EXIT command terminates the debugging session.

You can now correct the program so that it reinitializes the variable TOTAL correctly.

3.5. Debugger Command Summary

This section lists all of the debugger commands and any related DCL commands in functional groupings, along with brief descriptions.

During a debugging session, you can get online HELP on any command and its qualifiers by typing the HELP command followed by the name of the command in question. The HELP command has the following form:

HELP debugger command

3.5.1. Starting and Terminating a Debugging Session

($) RUN[a] Invokes the debugger if LINK/DEBUG was used
($) RUN/[NO]DEBUG[a]Controls whether the debugger is invoked when the program is executed
Ctrl/z or EXITEnds a debugging session, executing all exit handlers
QUITEnds a debugging session without executing any exit handlers declared in the program
Ctrl/yInterrupts a debugging session and returns you to the DCL level
Ctrl/cHas the same effect as Ctrl/y, unless the program has a Ctrl/c service routine
($) CONTINUE[a]Resumes a debugging session after a Ctrl/y interruption
($) DEBUG[a]Resumes a debugging session after a Ctrl/y interruption but returns you to the debugger prompt
ATTACHPasses control of your terminal from the current process to another process (similar to the DCL command ATTACH)
SPAWNCreates a subprocess; lets you issue DCL commands without interrupting your debugging context (similar to the DCL command SPAWN)

[a] This is a DCL command, not a debugger command.

3.5.2. Controlling and Monitoring Program Execution

GOStarts or resumes program execution
STEPExecutes the program up to the next line, instruction, or specified instruction
{ SET  }
{ SHOW }
 STEP
Establishes or displays the default qualifiers for the STEP command
{ SET    }
{ SHOW   }
{ CANCEL }
 BREAK
Sets, displays, or cancels breakpoints
{ SET    }
{ SHOW   }
{ CANCEL }
 TRACE
Sets, displays, or cancels tracepoints
{ SET    }
{ SHOW   }
{ CANCEL }
 WATCH
Sets, displays, or cancels watchpoints
{ SET    }
{ CANCEL }
EXCEPTION
BREAK
Sets or cancels exception breakpoints
SHOW CALLSIdentifies the currently active routine calls
SHOW STACKGives additional information about the currently active routine calls
CALLCalls a routine

3.5.3. Examining and Manipulating Data

EXAMINEDisplays the value of a variable or the contents of a program location
DEPOSITChanges the value of a variable or the contents of a program location
EVALUATEEvaluates a language or address expression

3.5.4. Controlling Type Selection and Symbolization

{ SET  }
{ SHOW }
 RADIX
Establishes the radix for data entry and display, displays the radix, or restores the radix
{ SET  }
{ SHOW }
 TYPE
Establishes the type to be associated with untyped program locations, displays the type, or restores the type
SET MODE [NO]G_FLOATControls whether double-precision floating-point constants are interpreted as G_FLOAT or D_FLOAT
SET MODE [NO]LINEControls whether code locations are displayed in terms of line numbers or routine-name + byte offset
SET MODE [NO]SYMBOLICControls whether code locations are displayed symbolically or in terms of numeric addresses
SYMBOLIZEConverts a virtual address to a symbolic address

3.5.5. Controlling Symbol Lookup

SHOW SYMBOLDisplays symbols in your program
{ SET  }
{ SHOW }
 MODULE
Sets a module by loading its symbol records into the debugger's symbol table, identifies a set module, or cancels a set module
{ SET  }
{ SHOW }
 IMAGE
Sets a shareable image by loading data structures into the debugger's symbol table, identifies a set image, or cancels a set image
SET MODE [NO]DYNAMICControls whether modules and shareable images are set automatically when the debugger interrupts execution
{ SET    }
{ SHOW   }
{ CANCEL }
SCOPE
Establishes, displays, or restores the scope for symbol lookup

3.5.6. Displaying Source Code

TYPEDisplays lines of source code
EXAMINE/SOURCEDisplays the source code at the location specified by the address expression
{ SET  }
{ SHOW }
 SOURCE
Creates, displays, or cancels a source directory search list
SEARCHSearches the source code for the specified string
{ SET  }
{ SHOW }
 SEARCH
Establishes or displays the default qualifiers for the SEARCH command
{ SET  }
{ SHOW }
MAX_SOURCE_
FILES
Establishes or displays the maximum number of source files that may be kept open at one time
{ SET  }
{ SHOW }
 MARGINS
Establishes or displays the left and right margin settings for displaying source code

3.5.7. Using Screen Mode

SET MODE [NO]SCREENEnables or disables screen mode
SET MODE [NO]SCROLLControls whether an output display is updated line by line or once per command
DISPLAYModifies an existing display
{ SET  }
{ SHOW }
 DISPLAY
Creates, identifies, or deletes a display
{ SET    }
{ SHOW   }
{ CANCEL }
 WINDOW
Creates, identifies, or deletes a window definition
SELECTSelects a display for a display attribute
SHOW SELECTIdentifies the displays selected for each of the display attributes
SCROLLScrolls a display
SAVESaves the current contents of a display and writes it to another display
EXTRACTSaves a display or the current screen state and writes it to a file
EXPANDExpands or contracts a display
MOVEMoves a display across the screen
{ SET  }
{ SHOW }
 TERMINAL
Establishes or displays the height and width of the screen

Ctrl/w or

DISPLAY/REFRESH

Refreshes the screen

3.5.8. Editing Source Code

EDITInvokes an editor during a debugging session
{ SET  }
{ SHOW }
 EDITOR
Establishes or identifies the editor invoked by the EDIT command

3.5.9. Defining Symbols

DEFINEDefines a symbol as an address, command, or value
DELETE or UNDEFINEDeletes symbol definitions
{ SET  }
{ SHOW }
 DEFINE
Establishes or displays the default qualifier for the DEFINE command
SHOW SYMBOL/DEFINEDIdentifies symbols that have been defined

3.5.10. Using Keypad Mode

SET MODE [NO]KEYPADEnables or disables keypad mode
DEFINE/KEYCreates key definitions

DELETE/KEY or

UNDEFINE/KEY

Deletes key definitions
SET KEYEstablishes the key definition state
SHOW KEYDisplays key definitions

3.5.11. Using Command Procedures and Log Files

DECLAREDefines parameters to be passed to command procedures
{ SET  }
{ SHOW }
 LOG
Specifies or identifies the debugger log file
SET OUTPUT [NO]LOGControls whether a debugging session is logged
SET OUTPUT [NO]SCREEN_LOGControls whether, in screen mode, the screen contents are logged as the screen is updated
SET OUTPUT [NO]VERIFYControls whether debugger commands are displayed as a command procedure is executed
SHOW OUTPUTDisplays the current output options established by the SET OUTPUT command
{ SET  }
{ SHOW }
 ATSIGN
Establishes or displays the default file specification that the debugger uses to search for command procedures
@file-specExecutes a command procedure

3.5.12. Using Control Structures

IFExecutes a list of commands conditionally
FORExecutes a list of commands repetitively
REPEATExecutes a list of commands repetitively
WHILEExecutes a list of commands conditionally
EXITLOOPExits an enclosing WHILE, REPEAT, or FOR loop

3.5.13. Additional Commands

SET PROMPTSpecifies the debugger prompt
SET OUTPUT [NO]TERMINALControls whether debugger output is displayed or suppressed, except for diagnostic messages
{ SET  }
{ SHOW }
 LANGUAGE
Establishes or displays the current language
{ SET  }
{ SHOW }
EVENT_FACILITY
Establishes or identifies the current run-time facility for language-specific events
SHOW EXIT_HANDLERSIdentifies the exit handlers declared in the program
{ SET  }
{ SHOW }
 TASK
Modifies the tasking environment or displays task information
{ DISABLE }
{ ENABLE  }
 AST
Disables the delivery of ASTs in the program, enables the delivery of ASTs, or identifies whether delivery is enabled or disabled