[an error occurred while processing this directive]

HP OpenVMS Systems Documentation

Content starts here

HP OpenVMS Debugger Manual


Previous Contents Index

13.4.3 Defining Symbols for Values

Use the DEFINE/VALUE command to equate the current value of a language expression to a symbol (the current value is the value at the time the
DEFINE/VALUE command was entered).

The following example shows how you can use the DEFINE/VALUE command to count the number of calls to a routine:



DBG> DEFINE/VALUE COUNT = 0
DBG> SET TRACE/SILENT ROUT DO (DEFINE/VALUE COUNT = COUNT + 1)
DBG> GO
   .
   .
   .
DBG> EVALUATE COUNT
14
DBG>

In the example, the first DEFINE/VALUE command initializes the value of the symbol COUNT to 0. The SET TRACE command sets a silent tracepoint on routine ROUT and (through the DO clause) increments the value of COUNT by 1 every time ROUT is called. After execution is resumed and eventually suspended, the EVALUATE command obtains the current value of COUNT (the number of times that ROUT was called).

13.5 Assigning Commands to Function Keys

To facilitate entering commonly used commands, the function keys on the keypad have predefined debugger functions that are established when you start the debugger. These predefined functions are identified in Appendix A and the debugger's online help (type HELP Keypad). You can modify the functions of the keypad keys to suit your individual needs. If you have a VT200- or VT300-series terminal or a workstation, you can also bind commands to the additional function keys on the LK201 keyboard.

The debugger commands DEFINE/KEY, SHOW KEY, and DELETE/KEY enable you to assign, identify, and delete key definitions, respectively. Before you can use this feature, keypad mode must be enabled with the SET MODE KEYPAD command (keypad mode is enabled by default). Keypad mode also enables you to use the predefined functions of the keypad keys.

To use the keypad keys to enter numbers rather than debugger commands, enter the SET MODE NOKEYPAD command.

13.5.1 Basic Conventions

The debugger DEFINE/KEY command, which is similar to the DCL command DEFINE/KEY, enables you to assign a string to a function key. In the following example, the DEFINE/KEY command defines KP7 (keypad key 7) to enter and execute the SHOW MODULE * command:


DBG> DEFINE/KEY/TERMINATE KP7 "SHOW MODULE *"
%DEBUG-I-DEFKEY, DEFAULT key KP7 has been defined
DBG>

You must use a valid key name (such as KP7) with the commands DEFINE/KEY, SHOW KEY, and DELETE/KEY. See the DEFINE/KEY command for the valid key names that you can use with these commands for VT52 and VT100-series terminals and for LK201 keyboards.

In the previous example, the /TERMINATE qualifier indicates that pressing KP7 executes the command. You do not have to press Return after pressing KP7.

You can assign any number of definitions to the same function key as long as each definition is associated with a different state. The predefined states (DEFAULT, GOLD, BLUE, and so on) are identified in Appendix A and the debugger's online help (type HELP Keypad). In the preceding example, the informational message indicates that KP7 has been defined for the DEFAULT state (which is the default key state).

You can enter key definitions in a debugger initialization file (see Section 13.2) so that these definitions are available whenever you start the debugger.

To display a key definition in the current state, enter the SHOW KEY command. For example:


DBG> SHOW KEY KP7
DEFAULT keypad definitions:
  KP7 = "SHOW MODULE *" (echo,terminate,nolock)
DBG>

To display a key definition in a state other than the current state, specify that state with the /STATE qualifier when entering the SHOW KEY command. To see all key definitions in the current state, enter the SHOW KEY/ALL command.

To delete a key definition, use the DELETE/KEY command. To delete a key definition in a state other than the current state, specify that state with the /STATE qualifier. For example:


DBG> DELETE/KEY/STATE=GOLD KP7
%DEBUG-I-DELKEY, GOLD key KP7 has been deleted
DBG>

13.5.2 Advanced Techniques

This section shows more advanced techniques for defining keys, particularly techniques related to the use of state keys.

The following command line assigns the unterminated command string "SET BREAK %LINE" to KP9, for the BLUE state:


DBG> DEFINE/KEY/IF_STATE=BLUE KP9 "SET BREAK %LINE"

The predefined DEFAULT key state is established by default. The predefined BLUE key state is established by pressing the PF4 key. Enter the command line assigned in the preceding example (SET BREAK %LINE ...) by pressing PF4, pressing KP9, entering a line number, and then pressing the Return key to terminate and process the command line.

The SET KEY command enables you to change the default state for key definitions. For example, after entering the SET KEY/STATE=BLUE command, you do not need to press PF4 to enter the command line in the previous example. Also, the SHOW KEY command will show key definitions in the BLUE state, by default, and the DELETE/KEY command will delete key definitions in the BLUE state by default.

You can create additional key states. For example:


DBG> SET KEY/STATE=DEFAULT
DBG> DEFINE/KEY/SET_STATE=RED/LOCK_STATE F12 ""

In this example, the SET KEY command establishes DEFAULT as the current state. The DEFINE/KEY command makes F12 (LK201 keyboard) a state key. As a result, pressing F12 while in the DEFAULT state causes the current state to become RED. The key definition is not terminated and has no other effect (a null string is assigned to F12). After pressing F12, you can enter RED commands by pressing keys that have definitions associated with the RED state.

13.6 Using Control Structures to Enter Commands

The FOR, IF, REPEAT, and WHILE commands enable you to create looping and conditional constructs for entering debugger commands. The associated command EXITLOOP is used to exit a FOR, REPEAT, or WHILE loop. The following sections describe these commands.

See Section 4.1.6 and Section 14.3.2.2 for information about evaluating language expressions.

13.6.1 FOR Command

The FOR command executes a sequence of commands while incrementing a variable a specified number of times. It has the following syntax:


FOR name=expression1 TO expression2 [BY expression3] DO(command[; ...])

For example, the following command line sets up a loop that initializes the first 10 elements of an array to 0:


DBG> FOR I = 1 TO 10 DO (DEPOSIT A(I) = 0)

13.6.2 IF Command

The IF command executes a sequence of commands if a language expression (Boolean expression) is evaluated as true. It has the following syntax:


IF boolean-expression THEN (command[; ...]) [ELSE (command[;...])]

The following Fortran example sets up a condition that issues the command EXAMINE X2 if X1 is not equal to - 9.9, and issues the command EXAMINE Y1 otherwise:


DBG> IF X1 .NE. -9.9 THEN (EXAMINE X2) ELSE (EXAMINE Y1)

The following Pascal example combines a FOR loop and a condition test. The STEP command is issued if X1 is not equal to - 9.9. The test is made four times:


DBG> FOR COUNT = 1 TO 4 DO (IF X1 <> -9.9 THEN (STEP))

13.6.3 REPEAT Command

The REPEAT command executes a sequence of commands a specified number of times. It has the following syntax:


REPEAT language-expression DO (command[; ...])

For example, the following command line sets up a loop that issues a sequence of two commands (EXAMINE Y then STEP) 10 times:


DBG> REPEAT 10 DO (EXAMINE Y; STEP)

13.6.4 WHILE Command

The WHILE command executes a sequence of commands while the language expression (Boolean expression) you have specified evaluates as true. It has the following syntax:


WHILE boolean-expression DO (command[; ...])

The following Pascal example sets up a loop that repetitively tests X1 and X2 and issues the two commands EXAMINE X2 and STEP if X2 is less than X1:


DBG> WHILE X2 < X1 DO (EX X2;STEP)

13.6.5 EXITLOOP Command

The EXITLOOP command exits one or more enclosing FOR, REPEAT, or WHILE loops. It has the following syntax:


EXITLOOP [integer]

The integer n specifies the number of nested loops to exit from.

The following Pascal example sets up an endless loop that issues a STEP command with each iteration. After each step, the value of X is tested. If X is greater than 3, the EXITLOOP command terminates the loop.


DBG> WHILE TRUE DO (STEP; IF X > 3 THEN EXITLOOP)

13.7 Calling Routines Independently of Program Execution

The CALL command enables you to execute a routine independently of the normal execution of your program. It is one of the four debugger commands that you can use to execute your program (the others are GO, STEP, and EXIT).

The CALL command executes a routine whether or not your program actually includes a call to that routine, as long as the routine was linked with your program. Thus, you can use the CALL command to execute routines for any purpose (for example, to debug a routine out of the context of program execution, call a run-time library procedure, call a routine that dumps debugging information, and so on).

You can debug unrelated routines by linking them with a dummy main program that has a transfer address, and then using the CALL command to execute them.

The following example shows how you can use the CALL command to display some process statistics without having to include the necessary code in your program. The example consists of calls to run-time library routines that initialize a timer (LIB$INIT_TIMER) and display the elapsed time and various statistics (LIB$SHOW_TIMER). (Note that the presence of the debugger affects the timings and counts.)


DBG> SET MODULE SHARE$LIBRTL   (1)
DBG> CALL LIB$INIT_TIMER   (2)
value returned is 1   (3)
DBG> [ enter various debugger commands ]
   .
   .
   .
DBG> CALL LIB$SHOW_TIMER   (4)
 ELAPSED: 0 00:00:21.65  CPU: 0:14:00.21  BUFIO: 16  DIRIO: 0  FAULTS: 3
value returned is 1
DBG>

The comments that follow refer to the callouts in the previous example:

  1. The routines LIB$INIT_TIMER and LIB$SHOW_TIMER are in the shareable image LIBRTL. This image must be set by setting its module, because only its universal symbols are accessible during a debugging session (see Section 5.4.2.3).
  2. This CALL command executes the routine LIB$INIT_TIMER.
  3. The value returned message indicates the value returned in register R0 after the CALL command has been executed.
    By convention, after a called routine has executed, register R0 contains the function return value (if the routine is a function) or the procedure completion status (if the routine is a procedure that returns a status value). If a called procedure does not return a status value or function value, the value in R0 might be meaningless, and the value returned message can be ignored.
  4. This CALL command executes the routine LIB$SHOW_TIMER.

The following example shows how to call LIB$SHOW_VM (also in LIBRTL) to display memory statistics. Again, note that the presence of the debugger affects the counts.


DBG> SET MODULE SHARE$LIBRTL
DBG> CALL LIB$SHOW_VM
 1785 calls to LIB$GET_VM, 284 calls to LIB$FREE_VM,
 122216 bytes still allocated value returned is 1
DBG>

You can pass parameters to routines with the CALL command. See the CALL command description for details and examples.


Chapter 14
Debugging Special Cases

This chapter presents debugging techniques for special cases that are not covered elsewhere in this manual:

  • Optimized code
  • Screen-oriented programs
  • Multilanguage programs
  • Stack corruption
  • Exceptions and condition handlers
  • Exit handlers
  • AST-driven programs
  • Translated images

14.1 Debugging Optimized Code

By default, many compilers optimize the code they produce so that the program executes faster. With optimization, invariant expressions are removed from DO loops so that they are evaluated only once at run time; some memory locations might be allocated to different variables at different points in the program, and some variables might be eliminated so that you no longer have access to them while debugging.

The net result is that the code that is executing as you debug might not match the source code displayed in a screen-mode source display (see Section 7.4.1) or in a source listing file.

To avoid the problems of debugging optimized code, many compilers allow you to specify the /NOOPTIMIZE (or equivalent) command qualifier at compile time. Specifying this qualifier inhibits most compiler optimization and thereby reduces discrepancies between the source code and executable code caused by optimization.

If this option is not available to you, or if you have a definite need to debug optimized code, read this section. It describes the techniques for debugging optimized code and gives some typical examples of optimized code to show the potential causes of confusion. It also describes some features you can use to reduce the confusion inherent in debugging optimized code.

In order to take advantage of the features that improve the ability to debug optimized code, you need an up-to-date version of your language compiler. For definitive information about the necessary version of your compiler, please see your compiler release notes or other compiler documentation.

Note that about one-third more disk space is needed for debugging optimized code, to accommodate the increased image size.

When debugging optimized code, use a screen-mode instruction display, such as the predefined display INST, to show the decoded instruction stream of your program (see Section 7.4.4). An instruction display shows the exact code that is executing.

In screen mode, pressing KP7 places the SRC and INST displays side by side for easy comparison. Alternatively, you can inspect a compiler-generated machine-code listing.

In addition, to execute the program at the instruction level and examine instructions, use the techniques described in Section 4.3.

Using these methods, you should be able to determine what is happening at the executable code level and be able to resolve the discrepancy between source display and program behavior.

14.1.1 Eliminated Variables

A compiler might optimize code by eliminating variables, either permanently or temporarily at various points during execution. For example, if you try to examine a variable X that no longer is accessible because of optimization, the debugger might display one of the following messages:


%DEBUG-W-UNALLOCATED, entity X was not allocated in memory
                      (was optimized away)

%DEBUG-W-NOVALATPC, entity X does not have a value at the
                    current PC

The following Pascal example shows how this could happen:


PROGRAM DOC(OUTPUT);
   VAR
      X,Y: INTEGER;
   BEGIN
      X := 5;
      Y := 2;
      WRITELN(X*Y);
   END.

If you compile this program with the /NOOPTIMIZE (or equivalent) qualifier, you obtain the following (normal) behavior when debugging:


$ PASCAL/DEBUG/NOOPTIMIZE DOC
$ LINK/DEBUG DOC
$ DEBUG/KEEP
   .
   .
   .
DBG> RUN DOC
   .
   .
   .
DBG> STEP
stepped to DOC\%LINE 5
     5:         X := 5;
DBG> STEP
stepped to DOC\%LINE 6
     6:         Y := 2;
DBG> STEP
stepped to DOC\%LINE 7
     7:         WRITELN(X*Y);
DBG> EXAMINE X,Y
DOC\X:  5
DOC\Y:  2
DBG>

If you compile the program with the /OPTIMIZE (or equivalent) qualifier, because the values of X and Y are not changed after the initial assignment, the compiler calculates X*Y, stores that value (10), and does not allocate storage for X or Y. Therefore, after you start debugging, a STEP command takes you directly to line 7 rather than line 5. Moreover, you cannot examine X or Y:


$ PASCAL/DEBUG/OPTIMIZE DOC
$ LINK/DEBUG DOC
$ DEBUG/KEEP
   .
   .
   .
DBG> RUN DOC
   .
   .
   .
DBG> EXAMINE X,Y
%DEBUG-W-UNALLOCATED, entity X was not allocated in memory
                      (was optimized away)
DBG> STEP
stepped to DOC\%LINE 7
     7:         WRITELN(X*Y);
DBG>

On VAX processors, to see what values are being used in your optimized program, use the EXAMINE/OPERAND .%PC command to display the machine code at the current PC value, including the values and symbolization of all of the operands. For example, the following lines show the optimized code when the PC value is at the WRITELN statement:


DBG> STEP
stepped to DOC\%LINE 7
     7:         WRITELN(X*Y);
DBG> EXAMINE/OPERAND .%PC
DOC\%LINE 7:    PUSHL   S^#10
DBG>

In contrast, the following lines show the unoptimized code at the WRITELN statement:


DBG> STEP
stepped to DOC\%LINE 7
     7:         WRITELN(X*Y);
DBG> EXAMINE/OPERAND .%PC
DOC\%LINE 7:    MOVL    S^#10,B^-4(FP)
     B^-4(FP)   2146279292 contains 62914576
DBG>

14.1.2 Changes in Coding Order

Several methods of optimizing consist of performing operations in a sequence different from the sequence specified in the source code. Sometimes code is eliminated altogether.

As a result, the source code displayed by the debugger does not correspond exactly to the actual code being executed.

The following example depicts a segment of source code from a Fortran program as it might appear on a compiler listing or in a screen-mode source display. This code segment sets the first ten elements of array A to the value 1/X.


Line          Source Code
----          -----------
  5            DO 100 I=1,10
  6            A(I) = 1/X
  7        100 CONTINUE

Optimization may produce the following scenario: As the compiler processes the source program, it determines that the reciprocal of X need only be computed once, not 10 times as the source code specifies, because the value of X never changes in the DO loop. The compiler thus may generate optimized code equivalent to the following code segment:


Line          Optimized Code Equivalent
----          -------------------------
  5            TEMP = 1/X
               DO 100 I=1,10
  6            A(I) = TEMP
  7        100 CONTINUE

Depending on the compiler implementation, the moved code may be associated with the first line of the loop (common on VAX systems) or may retain its original line number (common on Alpha systems).

If a discrepancy occurs, it is not obvious from looking at the displayed source line. Furthermore, if the computation of 1/X were to fail because X is 0, it would appear from inspecting the source display that a division by 0 had occurred on a source line that contains no division at all.

This kind of apparent mismatch between source code and executable code should be expected from time to time when you debug optimized programs. It can be caused not only by code motions out of loops, as in the previous example, but by a number of other optimization methods as well.

14.1.3 Semantic Stepping (Alpha Only)

Semantic stepping (available only on Alpha systems) makes stepping through optimized code less confusing. The semantic-stepping mode complements the traditional step-by-line and step-by-instruction modes. There are two commands for semantic stepping: SET STEP SEMANTIC_EVENT and STEP/SEMANTIC_EVENT.

Semantic Events

One problem of stepping through optimized code is that the apparent source program location "bounces" back and forth with the same line often appearing again and again. Indeed, sometimes the forward progress in STEP LINE mode averages barely more than one instruction per STEP command.

This problem is addressed through annotating instructions that are semantic events. Semantic events are important for two reasons:

  • They represent the points in the program where the effects of the program actually happen.
  • These effects tend to happen in an order that remains close to the source order of events in the program.

A semantic event is one of the following:

  • Data event --- An assignment to a user variable
  • Control event --- A control flow decision, with a conditional or unconditional transfer of control, other than a call
  • Call event --- A call (to a routine that is not stepped over) or a return from a call

It is important to understand that not every assignment, transfer of control, or call is necessarily a semantic event. The major exceptions are as follows:

  • When two instructions are required to assign to a complex or X_floating value, only the first instruction is treated as a semantic event.
  • When there are multiple branches that are part of a single higher-level construct, such as a decision tree of branches that implement a case or select construct, then only the first is treated as a semantic event.
  • When a call is made to a routine that is a compiler-specific helper routine, such as a call to OTS$MOVE, which handles certain kinds of string or storage copy operations, the call is not considered a semantic event. This means that control will not stop at the call.
    To step into such a routine, you must do either of the following:
    • Set a breakpoint at the routine entry point
    • Use a series of STEP/INSTRUCTION commands to reach the call of interest and then use STEP/INSTRUCTION/INTO to enter the called routine.
  • When there is more than one semantic event in a row with the same line number, then only the first is used.

SET STEP SEMANTIC_EVENT Command

The SET STEP SEMANTIC_EVENT command establishes the default stepping mode as semantic.

STEP/SEMANTIC_EVENT Command

STEP/SEMANTIC_EVENT, or simply STEP when semantic mode is in effect, causes a breakpoint to be set at the next semantic event, whether an assignment, a transfer of control, or a call. Execution proceeds to that next event. Parts of any number of different lines/statements may be executed along the way without interfering with progress. When the semantic event is reached (that is, when the instruction associated with that event is reached but not yet executed), execution is suspended (similar to reaching the next line when STEP/LINE is used).

Example of Semantic Stepping

The comments in the following C program, doct2, point out some considerations for optimization:


#include <stdio.h>
#include <stdlib.h>

int main(unsigned argc, char **argv) {
    int w, x, y, z=0;

    x = atoi(argv[1]);
    printf("%d\n", x);

    x = 5;
    y = x;

    if (y > 2) {               /* always true */
        printf("y > 2");
        }
    else {
        printf("y <= 2");
        }

    if (z) {                   /* always false */
        printf("z");
        }
    else {
        printf("not z");
        }

    printf("\n");
    }

Contrast the following two examples, which show stepping by line and stepping by semantic event through the optimized doct2 program:

  • Stepping by line:


    $ doct2:=$sys$disk:[]doct2
    $ doct2 6
    
             Debugger Banner and Version Number
    
    Language:: Module: Doct2: GO to reach DBG> go
    break at routine DOCT2\main
       654:     x = atoi(argv[1]);
    DBG> step
    stepped to DOCT2\main\%LINE 651
       651: int main(unsigned argc, char **argv) {
    DBG> step
    stepped to DOCT2\main\%LINE 654
       654:     x = atoi(argv[1]);
    DBG> step
    stepped to DOCT2\main\%LINE 651
       651: int main(unsigned argc, char **argv) {
    DBG> step
    stepped to DOCT2\main\%LINE 654
       654:     x = atoi(argv[1]);
    DBG> step
    stepped to DOCT2\main\%LINE 655
       655:     printf("%d\n", x);
    DBG> step
    stepped to DOCT2\main\%LINE 654
       654:     x = atoi(argv[1]);
    DBG> step
    stepped to DOCT2\main\%LINE 655
       655:     printf("%d\n", x);
    DBG> step
    6
    stepped to DOCT2\main\%LINE 661
       661:         printf("y > 2");
    DBG> step
    y > 2
    stepped to DOCT2\main\%LINE 671
       671:         printf("not z");
    DBG> step
    not z
    stepped to DOCT2\main\%LINE 674
       674:     printf("\n");
    DBG> step
    stepped to DOCT2\main\%LINE 675
       675:     }
    DBG> step
    'Normal successful completion'
    DBG>
    
  • Stepping by semantic event:


    Previous Next Contents Index