hp.com home products and services support and drivers solutions how to buy
cd-rom home
End of Jump to page title
HP OpenVMS systems
documentation

Jump to content


HP OpenVMS Debugger Manual

HP OpenVMS Debugger Manual


Previous Contents Index


$ doct2:=$sys$disk:[]doct2 
$ doct2 6 
 
         Debugger Banner and Version Number
 
Language:: Module: Doct2: GO to reach DBG> set step semantic_event 
DBG> go 
break at routine DOCT2\main 
   654:     x = atoi(argv[1]); 
DBG> step 
stepped to DOCT2\main\%LINE 654+8 
   654:     x = atoi(argv[1]); 
DBG> step 
stepped to DOCT2\main\%LINE 655+12 
   655:     printf("%d\n", x); 
DBG> step 
6 
stepped to DOCT2\main\%LINE 661+16 
   661:         printf("y > 2"); 
DBG> step 
y > 2 
stepped to DOCT2\main\%LINE 671+16 
   671:         printf("not z"); 
DBG> step 
not z 
stepped to DOCT2\main\%LINE 674+16 
   674:     printf("\n"); 
DBG> step 
stepped to DOCT2\main\%LINE 675+24 
   675:     } 
DBG> step 
stepped to DOCT2\__main+104 
DBG> step 
'Normal successful completion' 
DBG> 

Notice that the semantic stepping behavior is much smoother and more straightforward than the stepping-by-line example. Further, semantic stepping results in stopping at significant points of the program. In general, semantic stepping significantly reduces or eliminates the confusion of "bouncing" around the code nonsequentially, which characteristically happens with stepping by line through optimized code. Although some reordering of the source program may be done to take advantage of better execution characteristics, generally the flow is from top to bottom.

The granularity of stepping is different between stepping by line and stepping semantically. Sometimes it is greater, sometimes smaller. For example, a statement that would by its semantic nature constitute a semantic event will not show up with semantic stepping if it has been optimized away. Thus, the semantic region will span across several lines, skipping the line that has been optimized away.

14.1.4 Use of Registers

A compiler might determine that the value of an expression does not change between two given occurrences and might save the value in a register. In such cases, the compiler does not recompute the value for the next occurrence, but assumes the value saved in the register is valid.

If, while debugging a program, you use the DEPOSIT command to change the value of the variable in the expression, the corresponding value stored in the register might not be changed. Thus, when execution continues, the value in the register might be used instead of the changed value in the expression, which will cause unexpected results.

In addition, when the value of a nonstatic variable (see Section 3.4.3) is held in a register, its value in memory is generally invalid; therefore, a spurious value might be displayed if you enter the EXAMINE command for a variable under these circumstances.

14.1.5 Use of Condition Codes (VAX Only)

On VAX processors, one optimization technique takes advantage of the way in which the VAX processor condition codes are set. For example, consider the following Pascal source code:


X := X + 2.5; 
IF X < 0 
THEN 
    ...

Rather than test the new value of X to determine whether to branch, the optimized code bases its decision on the condition code setting after 2.5 is added to X. Thus, if you attempt to set a breakpoint on the IF statement and deposit a different value into X, you do not achieve the intended result because the condition codes no longer reflect the value of X. In other words, the decision to branch is being made without regard to the deposited value of the variable.

Again, you can use the EXAMINE/OPERAND .%PC command to determine the correct location for depositing so as to achieve the results you expect.

14.1.6 Split-Lifetime Variables

In compiling with optimization, the compiler sometimes performs split-lifetime analysis on a variable, "splitting" it into several independent subvariables that can be independently allocated. The effect is that the original variable can be thought to reside in different locations at different points in time --- sometimes in a register, sometimes in memory, and sometimes nowhere. It is even possible for the different subvariables to be simultaneously active.

On Alpha processors, in response to the EXAMINE command, the debugger tells you at which locations in the program the variable was defined. When the variable has an inappropriate value, this location information can help you determine where the value of the variable was assigned. (The /DEFINITIONS qualifier enables you to specify more or fewer than the default five locations.)

Split-lifetime analysis applies only to scalar variables and parameters. It does not apply to arrays, records, structures, or other aggregates.

Examples of Split-Lifetime Processing

The following examples illustrate the use of split-lifetime processing. For the first example, a small C program, the numbers in the left column are listing line numbers.


385 doct8 () { 
386 
387     int i, j, k; 
388 
389     i = 1; 
390     j = 2; 
391     k = 3; 
392 
393     if (foo(i)) { 
394         j = 17; 
395         } 
396     else { 
397         k = 18; 
398         } 
399 
400     printf("%d, %d, %d\n", i, j, k); 
401 
402     } 

When compiled, linked, and executed for debugging, the optimized program results in this dialogue:


$ run doct8


   .
   .
   .
     DBG> step/into 
     stepped to DOCT8\doct8\%LINE 391 
        391:     k = 3; 
     DBG> examine i 
     %W, entity 'i' was not allocated in memory (was optimized away) 
     DBG> examine j 
     %W, entity 'j' does not have a value at the current PC 
     DBG> examine k 
     %W, entity 'k' does not have a value at the current PC 

Note the difference in the message for the variable i compared to j or k. The variable i was not allocated in memory (registers, core, or otherwise) at all, so there is no point in ever trying to examine its value again. By contrast, j and k do not have a value "at the current PC" here; somewhere later in the program they will.

Stepping one more line results in this:


     DBG> step 
     stepped to DOCT8\doct8\%LINE 385 
        385: doct8 () { 

This looks like a step backward --- a common phenomenon in optimized (scheduled) code. (This problem is dealt with by "semantic stepping mode," discussed in Section 14.1.2.) Continuing to step results in this:


     DBG> step 5 
     stepped to DOCT8\doct8\%LINE 391 
        391:     k = 3; 
     DBG> examine k 
     %W, entity 'k' does not have a value at the current PC 
     DBG> step 
     stepped to DOCT8\doct8\%LINE 393 
        393:     if (foo(i)) { 
     DBG> examine j 
     %W, entity 'j' does not have a value at the current PC 
     DBG> examine k 
     DOCT8\doct8\k:  3 
         value defined at DOCT8\doct8\%LINE 391 

Here j is still undefined, but k now has a value, namely 3. That value was assigned at line 391.

Recall from the source that j was assigned a value before k (at line 390), but that has yet to show up. Again, this is common with optimized (scheduled) code.


     DBG> step 
     stepped to DOCT8\doct8\%LINE 390 
        390:     j = 2; 

Here the value of j appears. Thus:


     DBG> examine j 
     %W, entity 'j' does not have a value at the current PC 
     DBG> step 
     stepped to DOCT8\doct8\%LINE 393 
        393:     if (foo(i)) { 
     DBG> examine j 
     DOCT8\doct8\j:  2 
         value defined at DOCT8\doct8\%LINE 390 

Skipping ahead to the print statement at line 400, examine j again.


     DBG> set break %line 400 
     DBG> g 
     break at DOCT8\doct8\%LINE 400 
        400:     printf("%d, %d, %d\n", i, j, k); 
     DBG> examine j 
     DOCT8\doct8\j:  2 
         value defined at DOCT8\doct8\%LINE 390 
         value defined at DOCT8\doct8\%LINE 394 

Here there is more than one definition location given for j. Which applies depends on which path was taken in the IF clause. If a variable has an apparently inappropriate value, this mechanism provides a means to take a closer look at those places, and only those, where that value might have come from.

You can use the SHOW SYMBOL/ADDRESS command to display the split-lifetime information for a symbol, as in the following example:


DBG> show symbol/address j
     data DOCT8\doct8\j 
       between PC 131128 and 131140                      
         PC definition locations are at: 131124          
         address: %R3 
       between PC 131144 and 131148                      
         PC definition locations are at: 131140          
         address: %R3 
       between PC 131152 and 131156                      
         PC definition locations are at: 131124          
         address: %R3 
       between PC 131160 and 131208                      
         PC definition locations are at: 131124, 131140  
         address: %R3

The variable j has four lifetime segments. The PC addresses are the result of linking the image, and the comments relate them to line numbers in the source program.

There is one major conceptual difference between the split-lifetime support on OpenVMS Alpha systems and what is available on OpenVMS VAX systems. On Alpha systems, the debugger tracks and reports which assignments and definitions might have provided the displayed value of a variable. This additional information can help you cope with some of the effects of code motion and other optimizations --- effects that cause a variable to have a value coming from an unexpected place in the program.

EXAMINE/DEFINITIONS Command (Alpha Only)

For a split-lifetime variable, the EXAMINE command not only displays the value of the active lifetime, it also displays the lifetime's definition points. The definition points are places where the lifetime could have received an initial value (if there is only one definition point, then that is the only place.)

There is more than one definition point if a lifetime's initial value can come from more than one place. In the previous example when the program is suspended at the printf, examining j results in the following:


DBG> examine j
DOCT8\doct8\j:  2 
    value defined at DOCT8\doct8\%LINE 390 
    value defined at DOCT8\doct8\%LINE 394

Here, the lifetime of j has two definition points, because the value could have come from either line 390 or line 394, depending on whether or not the expression at line 393 was TRUE.

By default, up to five definition locations are displayed when the contents of a variable are examined. You can specify the number of definition locations to display with the /DEFINITIONS=n qualifier, as in the following example:


DBG> EXAMINE/DEFINITIONS=74 FOO

Note that the minimum abbreviation is /DEFI.

If you want a default number of definitions other than five, you can use a command definition like the following:


DBG> DEFINE/COMMAND E = "EXAMINE/DEFINITIONS=100"

If the /DEFINITIONS qualifier is set to 100, and the split-lifetime variable examined has 120 definition points, the debugger displays the 100 as specified, and then reports:


there are 20 more definition points   
 

14.2 Debugging Screen-Oriented Programs

The debugger uses the terminal screen for input and output (I/O) during a debugging session. If you use a single terminal to debug a screen-oriented program that uses most or all of the screen, debugger I/O can overwrite, or can be overwritten by, program I/O.

Using one terminal for both program I/O and debugger I/O is even more complicated if you are debugging in screen mode and your screen-oriented program calls any Run-Time Library (RTL) Screen Management (SMG$) routines. This is because the debugger's screen mode also calls SMG routines. In such cases, the debugger and your program share the same SMG pasteboard, which causes further interference.

To avoid these problems when debugging a screen-oriented program, use one of the following techniques to separate debugger I/O from program I/O:

Assume that TTD1: is your current terminal from which you plan to start the debugger. You want to display debugger I/O on terminal TTD2: so that TTD1: is devoted exclusively to program I/O.

Follow these steps:

  1. Provide the necessary protection to TTD2: so that you can allocate that terminal from TTD1: (see Section 14.2.1).
    The remaining steps are all performed from TTD1:.
  2. Allocate TTD2:. This provides your process on TTD1: with exclusive access to TTD2: as follows:


    $ ALLOCATE TTD2:
    

  3. Assign the debugger logical names DBG$INPUT and DBG$OUTPUT to TTD2: as follows:


    $ DEFINE DBG$INPUT TTD2:
    $ DEFINE DBG$OUTPUT TTD2:
    

    DBG$INPUT and DBG$OUTPUT specify the debugger input device and output device, respectively. By default, these logical names are equated to SYS$INPUT and SYS$OUTPUT, respectively. Assigning DBG$INPUT and DBG$OUTPUT to TTD2: enables you to display debugger commands and debugger output on TTD2:.

  4. Make sure that the terminal type is known to the system. Enter the following command:


    $ SHOW DEVICE/FULL TTD2:
    

    If the device type is unknown, your system manager (or a user with LOG_IO or PHY_IO privilege) must make it known to the system as shown in the following example. In the example, the terminal is assumed to be a VT200:


    $ SET TERMINAL/PERMANENT/DEVICE=VT200 TTD2:
    

  5. Run the program to be debugged:


    $ DEBUG/KEEP
       .
       .
       .
    DBG> RUN prog-name
    

    You can now observe debugger I/O on TTD2:

  6. When finished with the debugging session, deallocate TTD2: as follows (or log out):


    $ DEALLOCATE TTD2:
    

14.2.1 Setting the Protection to Allocate a Terminal

On a properly secured system, terminals are protected so that you cannot allocate a terminal from another terminal.

To set the necessary protection, your system manager (or a user with the privileges indicated) should follow the steps shown in the following example.

In the example, TTD1: is your current terminal (from which you plan to start the debugger), and TTD2: is the terminal to be allocated so that it can display debugger I/O.

  1. If both TTD1: and TTD2: are hardwired to the system, go to Step 4.
    If TTD1: and TTD2: are connected to the system over a LAT (local area transport), go to Step 2.
  2. Log in to TTD2:.
  3. Enter these commands (you need LOG_IO or PHY_IO privilege):


    $ SET PROCESS/PRIV=LOG_IO
    $ SET TERMINAL/NOHANG/PERMANENT
    $ LOGOUT/NOHANG
    

  4. Enter one of the following commands (you need OPER privilege):


    $ SET ACL/OBJECT_TYPE=DEVICE/ACL=(IDENT=[PROJ,JONES],ACCESS=READ+WRITE) TTD2:   (1)
    $ SET PROTECTION=WORLD:RW/DEVICE TTD2:   (2)
    

    1. The SET ACL command line is preferred because it uses an access control list (ACL). In the example, access is restricted to user identification code (UIC) [PROJ,JONES].
    2. The SET PROTECTION command line provides world read/write access, which allows any user to allocate and perform I/O to TTD2:.

14.3 Debugging Multilanguage Programs

The debugger enables you to debug modules whose source code is written in different languages within the same debugging session. This section highlights some language-specific behavior that you should be aware of to minimize possible confusion.

When debugging in any language, be sure to consult:

14.3.1 Controlling the Current Debugger Language

When you bring a program under debugger control, the debugger sets the current language to that in which the module containing the main program (usually the routine containing the image transfer address) is written. The current language is identified at that point. For example:


$ DEBUG/KEEP
           Debugger Banner and Version Number
DBG> RUN prog-name
Language: PASCAL, Module: FORMS
DBG>

The current language setting determines how the debugger parses and interprets the names, operators, and expressions you specify in debugger commands, including things like the typing of variables, array and record syntax, the default radix for integer data, case sensitivity, and so on. The language setting also determines how the debugger displays data associated with your program.

Many programs include modules that are written in languages other than that of the main program. To minimize confusion, by default the debugger language remains set to the language of the main program throughout a debugging session, even if execution is paused within a module written in another language.

To take full advantage of symbolic debugging with such modules, use the SET LANGUAGE command to set the debugging context to that of another language. For example, the following command causes the debugger to interpret any symbols, expressions, and so on according to the rules of the COBOL language:


DBG> SET LANGUAGE COBOL

On VAX processors, the SET LANGUAGE command takes the following keywords:
Ada BASIC BLISS C
C++ COBOL DIBOL Fortran
MACRO-32 Pascal PL/I RPG II
SCAN      

On Alpha processors, the SET LANGUAGE command takes the following keywords:
Ada BASIC BLISS C
C++ COBOL Fortran MACRO-32 1
MACRO-64 Pascal PL/I  


1Note that MACRO-32 must be compiled with the AMACRO compiler.

On I64 processors, the SET LANGUAGE command takes the following keywords:
Assembler (IAS) BASIC BLISS C
C++ COBOL Fortran MACRO-32 1
IMACRO PASCAL    
Assembler (IAS) BASIC BLISS C
C++ COBOL Fortran MACRO-32 1
IMACRO PASCAL    


1Note that MACRO-32 must be compiled with the AMACRO compiler.
1Note that MACRO-32 must be compiled with the AMACRO compiler.

In addition, when debugging a program that is written in an unsupported language, you can specify the SET LANGUAGE UNKNOWN command. To maximize the usability of the debugger with unsupported languages, the SET LANGUAGE UNKNOWN command causes the debugger to accept a large set of data formats and operators, including some that might be specific to only a few supported languages. The operators and constructs that are recognized when the language is set to UNKNOWN are identified in the debugger's online help (type HELP Language).


Previous Next Contents Index