Popular Posts

Thursday, 20 September 2012

DEBUGGING IN C AND C++

Debugging C and C++ programs

Debug Tool commands that resemble C and C++ commands

Debug Tool's command language is a subset of C and C++ commands and has the same syntactical requirements. Debug Tool allows you to work in a language you are familiar with so learning a new set of commands is not necessary.
The table below shows the interpretive subset of C and C++ commands recognized by Debug Tool.
Command Description
block ({}) Composite command grouping
break Termination of loops or switch commands
declarations Declaration of session variables
do/while Iterative looping
expression Any C expression except the conditional (?) operator
for Iterative looping
if Conditional execution
switch Conditional execution
This subset of commands is valid only when the current programming language is C or C++.
In addition to the subset of C and C++ commands that you can use is a list of reserved keywords used and recognized by C and C++ that you cannot abbreviate, use as variable names, or use as any other type of identifier.
Refer to the following topics for more information related to the material discussed in this topic.

Using C and C++ variables with Debug Tool

Debug Tool can process all program variables that are valid in C or C++. You can assign and display the values of variables during your session. You can also declare session variables with the recognized C declarations to suit your testing needs.
Refer to the following topics for more information related to the material discussed in this topic.

Accessing C and C++ program variables

Debug Tool obtains information about a program variable by name using the symbol table built by the compiler. If you specify TEST(SYM) at compile time, the compiler builds a symbol table that allows you to reference any variable in the program.
Note:
There are no suboptions for C++. Symbol information is generated by default when the TEST compiler option is specified.
Refer to the following topics for more information related to the material discussed in this topic.

Displaying values of C and C++ variables or expressions

To display the values of variables or expressions, use the LIST command. The LIST command causes Debug Tool to log and display the current values (and names, if requested) of variables, including the evaluated results of expressions.
Suppose you want to display the program variables X, row[X], and col[X], and their values at line 25. If you issue the following command:
AT 25 LIST ( X, row[X], col[X] ); GO;
Debug Tool sets a breakpoint at line 25 (AT), begins execution of the program (GO), stops at line 25, and displays the variable names and their values.
If you want to see the result of their addition, enter:
AT 25 LIST ( X + row[X] + col[X] ); GO;
Debug Tool sets a breakpoint at line 25 (AT), begins execution of the program (GO), stops at line 25, and displays the result of the expression.
Put commas between the variables when listing more than one. If you do not want to display the variable names when issuing the LIST command, enter LIST UNTITLED.
You can also list variables with the printf function call as follows:
printf ("X=%d, row=%d, col=%d\n", X, row[X], col[X]);
The output from printf, however, does not appear in the Log window and is not recorded in the log file unless you SET INTERCEPT ON FILE stdout.

Assigning values to C and C++ variables

To assign a value to a C and C++ variable, you use an assignment expression. Assignment expressions assign a value to the left operand. The left operand must be a modifiable lvalue. An lvalue is an expression representing a data object that can be examined and altered.
C contains two types of assignment operators: simple and compound. A simple assignment operator gives the value of the right operand to the left operand.
Note:
Only the assignment operators that work for C will work for C++, that is, there is no support for overloaded operators.
The following example demonstrates how to assign the value of number to the member employee of the structure payroll:
payroll.employee = number;
Compound assignment operators perform an operation on both operands and give the result of that operation to the left operand. For example, this expression gives the value of index plus 2 to the variable index:
index += 2
Debug Tool supports all C operators except the tenary operator, as well as any other full C language assignments and function calls to user or C library functions.
Refer to the following topics for more information related to the material discussed in this topic.

%PATHCODE values for C and C++

The table below shows the possible values for the Debug Tool variable %PATHCODE when the current programming language is C and C++.
-1 Debug Tool is not in control as the result of a path or attention situation.
0 Attention function (not ATTENTION condition).
1 A block has been entered.
2 A block is about to be exited.
3 Control has reached a user label.
4 Control is being transferred as a result of a function reference. The invoked routine's parameters, if any, have been prepared.
5 Control is returning from a function reference. Any return code contained in register 15 has not yet been stored.
6 Some logic contained by a conditional do/while, for, or while statement is about to be executed. This can be a single or Null statement and not a block statement.
7 The logic following an if(...) is about to be executed.
8 The logic following an else is about to be executed.
9 The logic following a case within an switch is about to be executed.
10 The logic following a default within a switch is about to be executed.
13 The logic following the end of a switch, do, while, if(...), or for is about to be executed.
17 A goto, break, continue, or return is about to be executed.
Values in the range 3-17 can only be assigned to %PATHCODE if your program was compiled with an option supporting path hooks.

Declaring session variables with C and C++

You might want to declare session variables for use during the course of your session. You cannot initialize session variables in declarations. However, you can use an assignment statement or function call to initialize a session variable.
As in C, keywords can be specified in any order. Variable names up to 255 characters in length can be used. Identifiers are case-sensitive, but if you want to use the session variable when the current programming language changes from C to another HLL, the variable must have an uppercase name and compatible attributes.
To declare a hexadecimal floating-point variable called maximum, enter the following C declaration:
double maximum;
You can only declare scalars, arrays of scalars, structures, and unions in Debug Tool (pointers for the above are allowed as well).
If you declare a session variable with the same name as a programming variable, the session variable hides the programming variable. To reference the programming variable, you must qualify it. For example:
main:>x for the program variable x
x for the session variable x
Session variables remain in effect for the entire debug session, unless they are cleared using the CLEAR command.
Refer to the following topics for more information related to the material discussed in this topic.

C and C++ expressions

Debug Tool allows evaluation of expressions in your test program. All expressions available in C and C++ are also available within Debug Tool except for the conditional expression (? :). That is, all operators such as +, -, %:, and += are fully supported with the exception of the conditional operator.
C and C++ language expressions are arranged in the following groups based on the operators they contain and how you use them:
  • Primary expression
  • Unary expression
  • Binary expression
  • Conditional expression
  • Assignment expression
  • Comma expression
  • lvalue
  • Constant
An lvalue is an expression representing a data object that can be examined and altered. For a more detailed description of expressions and operators, see the C and C++ Program Guides.
The semantics for C and C++ operators are the same as in a compiled C or C++ program. Operands can be a mixture of constants (integer, floating-point, character, string, and enumeration), C and C++ variables, Debug Tool variables, or session variables declared during a Debug Tool session. Language constants are specified as described in the C and C++ Language Reference publications.
The Debug Tool command DESCRIBE ATTRIBUTES can be used to display the resultant type of an expression, without actually evaluating the expression.
The C and C++ language does not specify the order of evaluation for function call arguments. Consequently, it is possible for an expression to have a different execution sequence in compiled code than within Debug Tool. For example, if you enter the following in an interactive session:
int x;
int y;

x = y = 1;

printf ("%d %d %d%" x, y, x=y=0);
the results can differ from results produced by the same statements located in a C or C++ program segment. Any expression containing behavior undefined by ANSI standards can produce different results when evaluated by Debug Tool than when evaluated by the compiler.
The following examples show you various ways Debug Tool supports the use of expressions in your programs:
  • Debug Tool assigns 12 to a (the result of the printf()) function call, as in:
    a = (1,2/3,a++,b++,printf("hello world\n"));
  • Debug Tool supports structure and array referencing and pointer dereferencing, as in:
    league[num].team[1].player[1]++;
    league[num].team[1].total += 1;
    ++(*pleague);
  • Simple and compound assignment is supported, as in:
    v.x = 3;
    a = b = c = d = 0;
    *(pointer++) -= 1;
  • C and C++ language constants in expressions can be used, as in:
    pointer_to_c = "abcdef" + 0x2;
    *pointer_to_long = 3521L = 0x69a1;
    float_val = 3e-11 + 6.6E-10;
    char_val = '7';
  • The comma expression can be used, as in:
    intensity <<= 1, shade * increment, rotate(direction);
    alpha = (y>>3, omega % 4);
  • Debug Tool performs all implicit and explicit C conversions when necessary. Conversion to long double is performed in:
    long_double_val = unsigned_short_val;
    long_double_val = (long double) 3;
Refer to the following topics for more information related to the material discussed in this topic.

Calling C and C++ functions from Debug Tool

You can perform calls to user and C library functions within Debug Tool, unless your program was compiled with the FORMAT(DWARF) suboption of the DEBUG compiler option.
You can make calls to C library functions at any time. In addition, you can use the C library variables stdin, stdout, stderr, __amrc, and errno in expressions including function calls.
The library function ctdli cannot be called unless it is referenced in a compile unit in the program, either main or a function linked to main.
Calls to user functions can be made, provided Debug Tool is able to locate an appropriate definition for the function within the symbol information in the user program. These definitions are created when the program is compiled with TEST(SYM) for C or TEST for C++.
Debug Tool performs parameter conversions and parameter-mismatch checking where possible. Parameter checking is performed if:
  • The function is a library function
  • A prototype for the function exists in the current compile unit
  • Debug Tool is able to locate a prototype for the function in another compile unit, or the function itself was compiled with TEST(SYM) for C or with TEST for C++.
You can turn off this checking by specifying SET WARNING OFF.
Calls can be made to any user functions that have linkage supported by the C or C++ compiler. However, for C++ calls made to any user function, the function must be declared as:
extern "C"
For example, use this declaration if you want to debug an application signal handler. When a condition occurs, control passes to Debug Tool which then passes control to the signal handler.
Debug Tool attempts linkage checking, and does not perform the function call if it determines there is a linkage mismatch. A linkage mismatch occurs when the target program has one linkage but the source program believes it has a different linkage.
It is important to note the following regarding function calls:
  • The evaluation order of function arguments can vary between the C and C++ program and Debug Tool. No discernible difference exists if the evaluation of arguments does not have side effects.
  • Debug Tool knows about the function return value, and all the necessary conversions are performed when the return value is used in an expression.
  • The functions cannot be in XPLINK applications.
  • The functions must have debug information available.
Refer to the following topics for more information related to the material discussed in this topic.
  • Related references
  • z/OS XL C/C++ Language Reference

C reserved keywords

The table below lists all keywords reserved by the C language. When the current programming language is C or C++, these keywords cannot be abbreviated, used as variable names, or used as any other type of identifiers.
auto else long switch
break enum register typedef
case extern return union
char float short unsigned
const for signed void
continue goto sizeof volatile
default if static while
do int struct _Packed
double      

C operators and operands

The table below lists the C language operators in order of precedence and shows the direction of associativity for each operator. The primary operators have the highest precedence. The comma operator has the lowest precedence. Operators in the same group have the same precedence.
Precedence level Associativity Operators
Primary left to right  ()  [ ]  .  ->
Unary right to left  ++  --  -  +  !   ~  &   *  (typename)  sizeof
Multiplicative left to right  *  /  %
Additive left to right  +  -
Bitwise shift left to right  <<  >>
Relational left to right  <  >  <=  >=
Equality left to right  ++  !=
Bitwise logical AND left to right  &
Bitwise exclusive OR left to right  ^ or ¬
Bitwise inclusive OR left to right  |
Logical AND left to right  &&
Logical OR left to right  ||
Assignment right to left  =  +=  -=  *=  /=   <<=  >>=  %=   &=  ^=   |=
Comma left to right  ,

Language Environment conditions and their C and C++ equivalents

Language Environment condition names (the symbolic feedback codes CEExxx) can be used interchangeably with the equivalent C and C++ conditions listed in the following table. For example, AT OCCURRENCE CEE341 is equivalent to AT OCCURRENCE SIGILL. Raising a CEE341 condition triggers an AT OCCURRENCE SIGILL breakpoint and vice versa.
Language Environment condition Description Equivalent C/C++ condition
CEE341 Operation exception SIGILL
CEE342 Privileged operation exception SIGILL
CEE343 Execute exception SIGILL
CEE344 Protection exception SIGSEGV
CEE345 Addressing exception SIGSEGV
CEE346 Specification exception SIGILL
CEE347 Data exception SIGFPE
CEE348 Fixed point overflow exception SIGFPE
CEE349 Fixed point divide exception SIGFPE
CEE34A Decimal overflow exception SIGFPE
CEE34B Decimal divide exception SIGFPE
CEE34C Exponent overflow exception SIGFPE
CEE34D Exponent underflow exception SIGFPE
CEE34E Significance exception SIGFPE
CEE34F Floating-point divide exception SIGFPE

Debug Tool evaluation of C and C++ expressions

Debug Tool interprets most input as a collection of one or more expressions. You can use expressions to alter a program variable or to extend the program by adding expressions at points that are governed by AT breakpoints.
Debug Tool evaluates C and C++ expressions following the rules presented in z/OS XL C/C++ Language Reference. The result of an expression is equal to the result that would have been produced if the same expression had been part of your compiled program.
Implicit string concatenation is supported. For example, "abc" "def" is accepted for "abcdef" and treated identically. Concatenation of wide string literals to string literals is not accepted. For example, L"abc"L"def" is valid and equivalent to L"abcdef", but "abc" L"def" is not valid.
Expressions you use during your session are evaluated with the same sensitivity to enablement as are compiled expressions. Conditions that are enabled are the same ones that exist for program statements.
During a Debug Tool session, if the current setting for WARNING is ON, the occurrence in your C or C++ program of any one of the conditions listed below causes the display of a diagnostic message.
  • Division by zero
  • Remainder (%) operator for a zero value in the second operand
  • Array subscript out of bounds for a defined array
  • Bit shifting by a number that is either negative or greater than 32
  • Incorrect number of parameters, or parameter type mismatches for a function call
  • Differing linkage calling conventions for a function call
  • Assignment of an integer value to a variable of enumeration data type where the integer value does not correspond to an integer value of one of the enumeration constants of the enumeration data type
  • Assignment to an lvalue that has the const attribute
  • Attempt to take the address of an object with register storage class
  • A signed integer constant not in the range -2**31 to 2**31
  • A real constant not having an exponent of 3 or fewer digits
  • A float constant not larger than 5.39796053469340278908664699142502496E-79 or smaller than 7.2370055773322622139731865630429929E+75
  • A hex escape sequence that does not contain at least one hexadecimal digit
  • An octal escape sequence with an integer value of 256 or greater
  • An unsigned integer constant greater than the maximum value of 4294967295.
Refer to the following topics for more information related to the material discussed in this topic.

Intercepting files when debugging C and C++ programs

Several considerations must be kept in mind when using the SET INTERCEPT command to intercept files while you are debugging a C application.
For CICS(R) only: SET INTERCEPT is not supported for CICS.
For C++, there is no specific support for intercepting IOStreams. IOStreams is implemented using C I/O which implies that:
  • If you intercept I/O for a C standard stream, this implicitly intercepts I/O for the corresponding IOStreams' standard stream.
  • If you intercept I/O for a file, by name, and define an IOStream object associated with the same file, IOStream I/O to that file will be intercepted.
Note:
Although you can intercept IOStreams indirectly via C/370(TM) I/O, the behaviors might be different or undefined in C++.
You can use the following names with the SET INTERCEPT command during a debug session:
  • stdout, stderr, and stdin (lowercase only)
  • any valid fopen() file specifier.
The behavior of I/O interception across system() call boundaries is global. This implies that the setting of INTERCEPT ON for xx in Program A is also in effect for Program B (when Program A system() calls to Program B). Correspondingly, setting INTERCEPT OFF for xx in Program B turns off interception in Program A when Program B returns to A. This is also true if a file is intercepted in Program B and returns to Program A. This model applies to disk files, memory files, and standard streams.
When a stream is intercepted, it inherits the text/binary attribute specified on the fopen statement. The output to and input from the Debug Tool log file behaves like terminal I/O, with the following considerations:
  • Intercepted input behaves as though the terminal was opened for record I/O. Intercepted input is truncated if the data is longer than the record size and the truncated data is not available to subsequent reads.
  • Intercepted output is not truncated. Data is split across multiple lines.
  • Some situations causing an error with the real file might not cause an error when the file is intercepted (for example, truncation errors do not occur). Files expecting specific error conditions do not make good candidates for interception.
  • Only sequential I/O can be performed on an intercepted stream, but file positioning functions are tolerated and the real file position is not changed. fseek, rewind, ftell, fgetpos, and fsetpos do not cause an error, but have no effect.
  • The logical record length of an intercepted stream reflects the logical record length of the real file.
  • When an unintercepted memory file is opened, the record format is always fixed and the open mode is always binary. These attributes are reflected in the intercepted stream.
  • Files opened to the terminal for write are flushed before an input operation occurs from the terminal. This is not supported for intercepted files.
Other characteristics of intercepted files are:
  • When an fclose() occurs or INTERCEPT is set OFF for a file that was intercepted, the data is flushed to the session log file before the file is closed or the SET INTERCEPT OFF command is processed.
  • When an fopen() occurs for an intercepted file, an open occurs on the real file before the interception takes effect. If the fopen() fails, no interception occurs for that file and any assumptions about the real file, such as the ddname allocation and data set defaults, take effect.
  • The behavior of the ASIS suboption on the fopen() statement is not supported for intercepted files.
  • When the clrmemf() function is invoked and memory files have been intercepted, the buffers are flushed to the session log file before the files are removed.
  • If the fldata() function is invoked for an intercepted file, the characteristics of the real file are returned.
  • If stderr is intercepted, the interception overrides the Language Environment message file (the default destination for stderr). A subsequent SET INTERCEPT OFF command returns stderr to its MSGFILE destination.
  • If a file is opened with a ddname, interception occurs only if the ddname is specified on the INTERCEPT command. Intercepting the underlying file name does not cause interception of the stream.
  • User prefix qualifications are included in MVS(TM) data set names entered in the INTERCEPT command, using the same rules as defined for the fopen() function.
  • If library functions are invoked when Debug Tool is waiting for input for an intercepted file (for example, if you interactively enter fwrite(..) when Debug Tool is waiting for input), subsequent behavior is undefined.
  • I/O intercepts remain in effect for the entire debug session, unless you terminate them by selecting SET INTERCEPT OFF.
Command line redirection of the standard streams is supported under Debug Tool, as shown below.
1>&2
If stderr is the target of the interception command, stdout is also intercepted. If stdout is the target of the INTERCEPT command, stderr is not intercepted. When INTERCEPT is set OFF for stdout, the stream is redirected to stderr.
2>&1
If stdout is the target of the INTERCEPT command, stderr is also intercepted. If stderr is the target of the INTERCEPT command, stdout is not intercepted. When INTERCEPT is set OFF for stderr, the stream is redirected to stdout again.
1>file.name
stdout is redirected to file.name. For interception of stdout to occur, stdout or file.name can be specified on the interception request. This also applies to 1>>file.name
2>file.name
stderr is redirected to file.name. For interception of stderr to occur, stderr or file.name can be specified on the interception request. This also applies to 2>>file.name
2>&1 1>file.name
stderr is redirected to stdout, and both are redirected to file.name. If file.name is specified on the interception command, both stderr and stdout are intercepted. If you specify stderr or stdout on the INTERCEPT command, the behavior follows rule 1b above.
1>&2 2>file.name
stdout is redirected to stderr, and both are redirected to file.name. If you specify file.name on the INTERCEPT command, both stderr and stdout are intercepted. If you specify stdout or stderr on the INTERCEPT command, the behavior follows rule 1a above.
The same standard stream cannot be redirected twice on the command line. Interception is undefined if this is violated, as shown below.
2>&1 2>file.name
Behavior of stderr is undefined.
1>&2 1>file.name
Behavior of stdout is undefined.
Refer to the following topics for more information related to the material discussed in this topic.
  • Related references
  • z/OS XL C/C++ Programming Guide

Scope of objects in C and C++

An object is visible in a block or source file if its data type and declared name are known within the block or source file. The region where an object is visible is referred to as its scope. In Debug Tool, an object can be a variable or function and is also used to refer to line numbers.
Note:
The use of an object here is not to be confused with a C++ object. Any reference to C++ will be qualified as such.
In ANSI C, the four kinds of scope are:
  • Block
  • File
  • Function
  • Function prototype
For C++, in addition to the scopes defined for C, it also has the class scope.
An object has block scope if its declaration is located inside a block. An object with block scope is visible from the point where it is declared to the closing brace (}) that terminates the block.
An object has file scope if its definition appears outside of any block. Such an object is visible from the point where it is declared to the end of the source file. In Debug Tool, if you are qualified to the compilation unit with the file static variables, file static and global variables are always visible.
The only type of object with function scope is a label name.
An object has function prototype scope if its declaration appears within the list of parameters in a function prototype.
A class member has class scope if its declaration is located inside a class.
You cannot reference objects that are visible at function prototype scope, but you can reference ones that are visible at file or block scope if:
  • For C variables and functions, the source file was compiled with TEST(SYM) and the object was referenced somewhere within the source.
  • For C variables declared in a block that is nested in another block, the source file was compiled with TEST(SYM, BLOCK).
  • For line numbers, the source file was compiled with TEST(LINE) GONUMBER.
  • For labels, the source file was compiled with TEST(SYM, PATH). In some cases (for example, when using GOTO), labels can be referenced if the source file was compiled with TEST(SYM, NOPATH).
Debug Tool follows the same scoping rules as ANSI, except that it handles objects at file scope differently. An object at file scope can be referenced from within Debug Tool at any point in the source file, not just from the point in the source file where it is declared. Debug Tool session variables always have a higher scope than program variables, and consequently have higher precedence than a program variable with the same name. The program variable can always be accessed through qualification.
In addition, Debug Tool supports the referencing of variables in multiple load modules. Multiple load modules are managed through the C library functions dllload(), dllfree(), fetch(), and release().
Example: referencing variables and setting breakpoints in C and C++ blocks

Storage classes in C and C++

Debug Tool supports the change and reference of all objects declared with the following storage classes:
  • auto
  • register
  • static
  • extern
Session variables declared during the Debug Tool session are also available for reference and change.
An object with auto storage class is available for reference or change in Debug Tool, provided the block where it is defined is active. Once a block finishes executing, the auto variables within this block are no longer available for change, but can still be examined using DESCRIBE ATTRIBUTES.
An object with register storage class might be available for reference or change in Debug Tool, provided the variable has not been optimized to a register.
An object with static storage class is always available for change or reference in Debug Tool. If it is not located in the currently qualified compile unit, you must specifically qualify it.
An object with extern storage class is always available for change or reference in Debug Tool. It might also be possible to reference such a variable in a program even if it is not defined or referenced from within this source file. This is possible provided Debug Tool can locate another compile unit (compiled with TEST(SYM)) with the appropriate definition.

Blocks and block identifiers for C

It is often necessary to set breakpoints on entry into or exit from a given block or to reference variables that are not immediately visible from the current block. Debug Tool can do this, provided that all blocks are named. It uses the following naming convention:
  • The outermost block of a function has the same name as the function.
  • Blocks enclosed in this outermost block are sequentially named: %BLOCK2, %BLOCK3, %BLOCK4, and so on in order of their appearance in the function.
When these block names are used in the Debug Tool commands, you might need to distinguish between nested blocks in different functions within the same source file. This can be done by naming the blocks in one of two ways:
Short form
function_name:>%BLOCKzzz
Long form
function_name:>%BLOCKxxx :>%BLOCKyyy: ... :>%BLOCKzzz
%BLOCKzzz is contained in %BLOCKyyy, which is contained in %BLOCKxxx. The short form is always allowed; it is never necessary to specify the long form.
The currently active block name can be retrieved from the Debug Tool variable %BLOCK. You can display the names of blocks by entering:
DESCRIBE CU;

Blocks and block identifiers for C++

Block Identifiers tend to be longer for C++ than C because C++ functions can be overloaded. In order to distinguish one function name from the other, each block identifier is like a prototype. For example, a function named shapes(int,int) in C would have a block named shapes; however, in C++ the block would be called shapes(int,int).
You must always refer to a C++ block identifier in its entirety, even if the function is not overloaded. That is, you cannot refer to shapes(int,int) as shapes only.
Note:
The block name for main() is always main (without the qualifying parameters after it) even when compiled with C++ because main() has extern C linkage.
Since block names can be quite long, it is not unusual to see the name truncated in the LOCATION field on the first line of the screen. If you want to find out where you are, enter:
QUERY LOCATION
and the name will be shown in its entirety (wrapped) in the session log.
Block identifiers are restricted to a length of 255 characters. Any name longer than 255 characters is truncated.

Example: referencing variables and setting breakpoints in C and C++ blocks

The program below is used as the basis for several examples, described after the program listing.
#pragma runopts(EXECOPS)
#include <stdlib.h>

main()
{
      >>> Debug Tool is given <<<
      >>> control here.    <<<
  init();
  sort();
}

short length = 40;
static long *table;

init()
{
  table = malloc(sizeof(long)*length);

  ·
  ·
  ·

}

sort ()
{                                     /* Block sort */
  int i;
  for (i = 0; i < length-1; i++) {    /* Block %BLOCK2 */
    int j;
    for (j = i+1; j < length; j++) {  /* Block %BLOCK3 */
      static int temp;
      temp = table[i];
      table[i] = table[j];
      table[j] = temp;
    }
  }
}

Scope and visibility of objects

Let's assume the program shown above is compiled with TEST(SYM). When Debug Tool gains control, the file scope variables length and table are available for change, as in:
length = 60;
The block scope variables i, j, and temp are not visible in this scope and cannot be directly referenced from within Debug Tool at this time. You can list the line numbers in the current scope by entering:
LIST LINE NUMBERS;
Now let's assume the program is compiled with TEST(SYM, NOBLOCK). Since the program is explicitly compiled using NOBLOCK, Debug Tool will never know about the variables j and temp because they are defined in a block that is nested in another block. Debug Tool does know about the variable i since it is not in a scope that is nested.

Blocks and block identifiers

In the program above, the function sort has three blocks:
  • sort
  • %BLOCK2
  • %BLOCK3
The following example sets a breakpoint on entry to the second block of sort:
at entry sort:>%BLOCK2;
The following example sets a breakpoint on exit of the first block of main and lists the entries of the sorted table.
at exit main {
  for (i = 0; i < length; i++)
    printf("table entry %d is %d\n", i, table[i]);
}
The following example lists the variable temp in the third block of sort. This is possible since temp has the static storage class.
LIST sort:>%BLOCK3:temp;

Displaying environmental information

You can also use the DESCRIBE command to display a list of attributes applicable to the current run-time environment. The type of information displayed varies from language to language.
Issuing DESCRIBE ENVIRONMENT displays a list of open files and conditions being monitored by the run-time environment. For example, if you enter DESCRIBE ENVIRONMENT while debugging a C or C++ program, you might get the following output:
Currently open files
    stdout
    sysprint
The following conditions are enabled:
    SIGFPE
    SIGILL
    SIGSEGV
    SIGTERM
    SIGINT
    SIGABRT
    SIGUSR1
    SIGUSR2
    SIGABND

Qualifying variables and changing the point of view in C and C++

Qualification is a method of:
  • Specifying an object through the use of qualifiers
  • Changing the point of view
Qualification is often necessary due to name conflicts, or when a program consists of multiple load modules, compile units, and/or functions.
When program execution is suspended and Debug Tool receives control, the default, or implicit qualification is the active block at the point of program suspension. All objects visible to the C or C++ program in this block are also visible to Debug Tool. Such objects can be specified in commands without the use of qualifiers. All others must be specified using explicit qualification.
Qualifiers depend, of course, upon the naming convention of the system where you are working.
Example: using qualification in C

Qualifying variables in C and C++

You can precisely specify an object, provided you know the following:
  • Load module or DLL name
  • Source file (compilation unit) name
  • Block name (must include function prototype for C++ block qualification).
These are known as qualifiers and some, or all, might be required when referencing an object in a command. Qualifiers are separated by a combination of greater than signs (>) and colons and precede the object they qualify. For example, the following is a fully qualified object:
load_name::>cu_name:>block_name:>object
If required, load_name is the name of the load module. It is required only when the program consists of multiple load modules and when you want to change the qualification to other than the current load module. load_name is enclosed in quotation marks ("). If it is not, it must be a valid identifier in the C or C++ programming language. load_name can also be the Debug Tool variable %LOAD.
If required, CU_NAME is the name of the compilation unit or source file. The cu_name must be the fully qualified source file name or an absolute pathname. It is required only when you want to change the qualification to other than the currently qualified compilation unit. It can be the Debug Tool variable %CU. If there appears to be an ambiguity between the compilation unit name, and (for example), a block name, you must enclose the compilation unit name in quotation marks (").
If required, block_name is the name of the block. block_name can be the Debug Tool variable %BLOCK.
Example: using qualification in C
Refer to the following topics for more information related to the material discussed in this topic.

Changing the point of view in C and C++

To change the point of view from the command line or a commands file, use qualifiers in conjunction with the SET QUALIFY command. This can be necessary to get to data that is inaccessible from the current point of view, or can simplify debugging when a number of objects are being referenced.
It is possible to change the point of view to another load module or DLL, to another compilation unit, to a nested block, or to a block that is not nested. The SET keyword is optional.
Example: using qualification in C

Example: using qualification in C

The examples below use the following program.
LOAD MODULE NAME:  MAINMOD
SOURCE FILE  NAME:  MVSID.SORTMAIN.C

short length = 40;
main ()
{
  long *table;
  void (*pf)();

  table = malloc(sizeof(long)*length);

  ·
  ·
  ·

  pf = fetch("SORTMOD");
  (*pf)(table);

  ·
  ·
  ·

  release(pf);

  ·
  ·
  ·

}

LOAD MODULE NAME:  SORTMOD
SOURCE FILE  NAME:  MVSID.SORTSUB.C

short length = 40;
short sn = 3;
void (long table[])
{
  short i;
  for (i = 0; i < length-1; i++) {
    short j;
    for (j = i+1; j < length; j++) {
      float sn = 3.0;
      short temp;
      temp = table[i];

  ·
  ·
  ·

      >>> Debug Tool is given <<<
      >>> control here.    <<<

  ·
  ·
  ·

      table[i] = table[j];
      table[j] = temp;
    }
  }
}
When Debug Tool receives control, variables i, j, temp, table, and length can be specified without qualifiers in a command. If variable sn is referenced, Debug Tool uses the variable that is a float. However, the names of the blocks and compile units differ, maintaining compatibility with the operating system.

Qualifying variables

  • Change the file scope variable length defined in the compilation unit MVSID.SORTSUB.C in the load module SORTMOD:
    "SORTMOD"::>"MVSID.SORTSUB.C":>length = 20;
  • Assume Debug Tool gained control from main(). The following changes the variable length:
    %LOAD::>"MVSID.SORTMAIN.C":>length = 20;
    Because length is in the current load module and compilation unit, it can also be changed by:
    length = 20;
  • Assume Debug Tool gained control as shown in the example program above. You can break whenever the variable temp in load module SORTMOD changes in any of the following ways:
    AT CHANGE temp;
    AT CHANGE %BLOCK3:>temp;
    AT CHANGE sort:%BLOCK3:>temp;
    AT CHANGE %BLOCK:>temp;
    AT CHANGE %CU:>sort:>%BLOCK3:>temp;
    AT CHANGE "MVSID.SORTSUB.C":>sort:>%BLOCK3:>temp;
    AT CHANGE "SORTMOD"::>"MVSID.SORTSUB.C":>sort:>%BLOCK3:>temp;

Changing the point of view

  • Qualify to the second nested block in the function sort() in sort.
    SET QUALIFY BLOCK %BLOCK2;
    You can do this in a number of other ways, including:
    QUALIFY BLOCK sort:>%BLOCK2;
    Once the point of view changes, Debug Tool has access to objects accessible from this point of view. You can specify these objects in commands without qualifiers, as in:
    j = 3;
    temp = 4;
  • Qualify to the function main in the load module MAINMOD in the compilation unit MVSID.SORTMAIN.C and list the entries of table.
    QUALIFY BLOCK "MAINMOD"::>"MVSID.SORTMAIN.C":>main;
    LIST table[i];

Stepping through C++ programs

You can step through methods as objects are constructed and destructed. In addition, you can step through static constructors and destructors. These are methods of objects that are executed before and after main() respectively.
If you are debugging a program that calls a function that resides in a header file, the cursor moves to the applicable header file. You can then view the function source as you step through it. Once the function returns, debugging continues at the line following the original function call.
You can step around a header file function by issuing the STEP OVER command. This is useful in stepping over Library functions (for example, string functions defined in string.h) that you cannot debug anyway.

Setting breakpoints in C++

The differences between setting breakpoints in C++ and C are described below.

Setting breakpoints in C++ using AT ENTRY/EXIT

AT ENTRY/EXIT sets a breakpoint in the specified block. You can set a breakpoint on methods, methods within nested classes, templates, and overloaded operators. An example is given for each below.
A block identifier can be quite long, especially with templates, nested classes, or class with many levels of inheritance. In fact, it might not even be obvious at first as to the block name for a particular function. To set a breakpoint for these nontrivial blocks can be quite cumbersome. Therefore, it is recommended that you make use of DESCRIBE CU and retrieve the block identifier from the session log.
When you do a DESCRIBE CU, the methods are always shown qualified by their class. If a method is unique, you can set a breakpoint by using just the method name. Otherwise, you must qualify the method with its class name. The following two examples are equivalent:
AT ENTRY method()

AT ENTRY classname::method()
The following examples are valid:
AT ENTRY square(int,int) 'simple' method square
AT ENTRY shapes::square(int) Method square qualified by its class shapes.
AT EXIT outer::inner::func() Nested classes. Outer and inner are classes. func() is within class inner.
AT EXIT Stack<int,5>::Stack() Templates.
AT ENTRY Plus::operator++(int) Overloaded operator.
AT ENTRY ::fail() Functions defined at file scope must be referenced by the global scope operator ::
The following examples are invalid:
AT ENTRY shapes Where shapes is a class. Cannot set breakpoint on a class. (There is no block identifier for a class.)
AT ENTRY shapes::square Invalid since method square must be followed by its parameter list.
AT ENTRY shapes:>square(int) Invalid since shapes is a class name, not a block name.

Setting breakpoints in C++ using AT CALL

AT CALL gives Debug Tool control when the application code attempts to call the specified entry point. The entry name must be a fully qualified name. That is, the name shown in DESCRIBE CU must be used. Using the example
AT ENTRY shapes::square(int)
to set a breakpoint on the method square, you must enter:
AT CALL shapes::square(int)
even if square is uniquely identified.
Refer to the following topics for more information related to the material discussed in this topic.

Examining C++ objects

When displaying an C++ object, only the local member variables are shown. Access types (public, private, protected) are not distinguished among the variables. The member functions are not displayed. If you want to see their attributes, you can display them individually, but not in the context of a class. When displaying a derived class, the base class within it is shown as type class and will not be expanded.
Refer to the following topics for more information related to the material discussed in this topic.

Example: displaying attributes of C++ objects

The examples below use the following definitions.
class shape { ... };

class line : public shape {
  member variables of class line...
}

line edge;

Displaying object attributes

To describe the attributes of the object edge, enter the following command.
DESCRIBE ATTRIBUTES edge;
The Log window displays the following output.
DESCRIBE ATTRIBUTES edge;
ATTRIBUTES for edge
  Its address is yyyyyyyy and its length is xx
  class line
     class shape
     member variables of class shape....
Note that the base class is shown as class shape _shape.

Displaying class attributes

To display the attributes of class shape, enter the following command.
DESCRIBE ATTRIBUTES class shape;
The Log window displays the following output.
DESCRIBE ATTRIBUTES class shape ;
 ATTRIBUTES for class shape
   const class shape...

Displaying static data

If a class contains static data, the static data will be shown as part of the class when displayed. For example:
class A {
  int x;
  static int y;
}

A obj;
You can also display the static member by referencing it as A::y since each object of class A has the same value.

Displaying global data

To avoid ambiguity, variables declared at file scope can be referenced using the global scope operator ::. For example:
int x;
class A {
   int x;

  ·
  ·
  ·

   }
}
If you are within a member function of A and want to display the value of x at file scope, enter LIST ::x. If you do not use ::, entering LIST x will display the value of x for the current object (i.e., this->x).

Monitoring storage in C++

You might find it useful to monitor registers (general-purpose and floating-point) while stepping through your code and assembly listing by using the LIST REGISTERS command. The compiler listing displays the pseudo assembly code, including Debug Tool hooks. You can watch the hooks that you stop on and watch expected changes in register values step by step in accordance with the pseudo assembly instructions between the hooks. You can also modify the value of machine registers while stepping through your code.
You can list the contents of storage in various ways. Using the LIST REGISTERS command, you can receive a list of the contents of the General Purpose Registers or the floating-point registers.
You can also monitor the contents of storage by specifying a dump-format display of storage. To accomplish this, use the LIST STORAGE command. You can specify the address of the storage that you want to view, as well as the number of bytes.

Example: monitoring and modifying registers and storage in C

The examples below use the following C program to demonstrate how to monitor and modify registers and storage.
int dbl(int j)            /* line 1 */
{                         /* line 2 */
  return 2*j;             /* line 3 */
}                         /* line 4 */
int main(void)
{
  int i;
  i = 10;
  return dbl(i);
}
If you compile the program above using the compiler options TEST(ALL),LIST, then your pseudo assembly listing will be similar to the listing shown below.
*  int dbl(int j)
           ST    r1,152(,r13)
*  {
           EX    r0,HOOK..PGM-ENTRY
*    return 2*j;
           EX    r0,HOOK..STMT
           L     r15,152(,r13)
           L     r15,0(,r15)
           SLL   r15,1
           B     @5L2
           DC    A@5L2-ep)
           NOPR
 @5L1      DS    0D
*  }
 @5L2      DS    0D
           EX    r0,HOOK..PGM-EXIT
To display a continuously updated view of the registers in the Monitor window, enter the following command:
MONITOR LIST REGISTERS
After a few steps, Debug Tool halts on line 1 (the program entry hook, shown in the listing above). Another STEP takes you to line 3, and halts on the statement hook. The next STEP takes you to line 4, and halts on the program exit hook. As indicated by the pseudo assembly listing, only register 15 has changed during this STEP, and it contains the return value of the function. In the Monitor window, register 15 now has the value 0x00000014 (decimal 20), as expected.
You can change the value from 20 to 8 just before returning from dbl() by issuing the command:
%GPR15 = 8 ;

 

No comments:

Post a Comment