Document revision date: 19 July 1999 | |
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 |
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:
$ ALLOCATE TTD2: |
$ DEFINE DBG$INPUT TTD2: $ DEFINE DBG$OUTPUT TTD2: |
$ SHOW DEVICE/FULL TTD2: |
$ SET TERMINAL/PERMANENT/DEVICE=VT200 TTD2: |
$ DEBUG/KEEP . . . DBG> RUN prog-name |
$ DEALLOCATE TTD2: |
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.
$ SET PROCESS/PRIV=LOG_IO $ SET TERMINAL/NOHANG/PERMANENT $ LOGOUT/NOHANG |
$ SET ACL/OBJECT_TYPE=DEVICE/ACL=(IDENT=[PROJ,JONES],ACCESS=READ+WRITE) TTD2: (1) $ SET PROTECTION=WORLD:RW/DEVICE TTD2: (2) |
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:
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_PLUS_PLUS | COBOL | DIBOL | FORTRAN |
MACRO | PASCAL | PLI | RPG |
SCAN | UNKNOWN |
On Alpha processors, the SET LANGUAGE command takes the following keywords:
ADA | AMACRO | BASIC | BLISS |
C | C_PLUS_PLUS | COBOL | FORTRAN |
MACRO | MACRO64 | PASCAL | PLI |
UNKNOWN |
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 |
privacy and legal statement | ||
4538PRO_028.HTML |