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 CEE
xxx) 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:
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 ;