The Watcom Debugger is capable of handling a wide variety of expressions. An expression is a combination of operators and operands selected from application variables and names, debugger variables, and constants. Expressions can be used in a large number of debugger commands and dialogs. For example, the evaluated result of an expression may be displayed by choosing New in the Watches window or by using the Print command.
The appropriate syntax of an expression, that is, the valid sequence of operators and operands, depends on the grammar of the language that's currently established. The Watcom Debugger supports the grammars of the C, C++, and FORTRAN 77 languages.
QNX doesn't support FORTRAN 77. You need to use it in the debugger only if you're remotely debugging an application that's written in FORTRAN 77 on a platform that supports it. |
A grammar is selected automatically by the debugger when tracing the execution of modules in an application. For example, part of an application might be written in C, another part in C++, and another part in FORTRAN 77. The modules must have been compiled by one of the WATCOM C, C++ or FORTRAN 77 compilers. When tracing into a module written in one of these languages, the debugger automatically selects the appropriate grammar.
In addition to this automatic selection, a particular grammar may be selected using the debugger Set LAnguage command. The language currently selected can be determined using the SHow Set LAnguage command.
The debugger handles two types of expressions. The difference between the two types of expressions is quite subtle:
If the notation for a particular command argument is address, it is an address expression. If the argument is given as expr then it's a normal expression. The difference between the two forms lies in how they treat symbol names:
Consider the following case. You have a symbol sam at offset 100, and the word at that location contains the value 15. If you enter sam into the Watches window, you expect the value 15 to be printed, and since the Watches window takes a normal expression, that's what you get.
Now let's try it with the Breakpoint dialog. Enter sam in the address field. The Breakpoint dialog uses the result of its expression as the address at which to set a breakpoint. The Breakpoint dialog takes an address expression, and an implicit unary & operator appears in front of symbols. The debugger has a set of heuristics that it applies to determine whether it should use the rvalue or lvalue of a symbol.
Regardless of the programming language used to code the modules of an application, the names of variables and routines are available to the debugger (provided that the appropriate symbolic debugging information has been included with the application's execution module). The debugger doesn't restrict the way in which names are used in expressions. A name could represent a variable, but it could also represent the entry point into a routine.
The syntax of a symbol name reference is quite complicated:
[[[image]@][module]@][routine_name.]symbol_name
If any part of the symbol name reference contains a character that could
be interpreted as an operator (for example, a period or hyphen), enclose it
in back quotes (`) to force the debugger to interpret it as a
symbol name. For example:
`aaa.bbb`@@ccc |
Generally, an application consists of many modules that were compiled separately. The current image is the one containing the module that's currently executing. The current module is the one containing the source lines currently under examination in the Source or Assembly window. By default, the Source window's title line contains the current module name. The current routine is the one containing the source line at which execution is currently paused.
The following are examples of references to symbol names.
If the symbol doesn't exist in the current scope then it must be qualified with its routine name. Generally, these are variables that are local to a particular routine. For example:
If the symbol isn't externally defined and it doesn't exist in the current module, then it may be qualified with its module name. In the C and C++ programming languages, we can define a variable that is global to a module but known only to that module (static storage class). For example:
static char *NarrowTitle = { "Su Mo Tu We Th Fr Sa" };
In the above example, NarrowTitle is global to the module calendar. If the current module isn't calendar, then the module name can be used to qualify the symbol as shown in the following example.
calendar@NarrowTitle
If the symbol is local to a routine that's not in the current module, then it must be qualified with its module name and routine name. For example:
module_name@routine_name.symbol_name calendar@main.curr_time calendar@main.tyme subs@SUB1.X subs@SUB2.X
If the symbol is local to an image that's not in the current executable, then it must be fully qualified with the image name. For example:
prog_name@@routine_name prog_name@module_name@routine_name prog_name@module_name@routine_name.symbol_name dll_name@calendar@main.curr_time dll_name@calendar@main.tyme program@subs@SUB1.X program@subs@SUB2.X
There is a special case for the primary executable image. This is the name of the program you specified when you started the debugger. You can refer to it by omitting the image name. The following examples all refer to symbols in the primary executable image:
@@WinMain @module@WinMain @@routine.symbol
In FORTRAN 77, all variables (arguments, local variables, COMMON block variables) are available to the subprogram they're defined or referenced in. The same symbol name can be used in more than one subprogram. If it's a local variable, it represents a different variable in each subprogram. If it's an argument, it might represent a different variable in each subprogram. If it's a variable in a COMMON block, it represents the same variable in each subprogram where the COMMON block is defined. For example:
SUBROUTINE SUB1( X ) REAL Y COMMON /BLK/ Z . . . END SUBROUTINE SUB2( X ) REAL Y COMMON /BLK/ Z . . . END
In the above example, X is an argument and doesn't need to refer to the same variable in the calling subprogram. For example:
CALL SUB1( A ) CALL SUB2( B )
The variable Y is a different variable in each of SUB1() and SUB2(). The COMMON block variable Z refers to the same variable in each of SUB1() and SUB2() (different names for Z could have been used). To refer to X, Y, or Z in the subprogram SUB2(), you would specify SUB2.X, SUB2.Y, or SUB2.Z. If SUB2() is in the module MOD, and MOD isn't the current module, you would specify MOD@SUB2.X, MOD@SUB2.Y, or MOD@SUB2.Z.
Global and local symbol name debugging information is included in an executable image if you request it of the linker. However, local symbol information must be present in your object files. The WATCOM C, C++ and FORTRAN 77 compilers can include local symbol debugging information in object files by specifying the appropriate compiler option. See the chapter Preparing a Program to be Debugged. |
Regardless of the programming language used to code the modules of an application, line number information identifying the start of executable statements is available to the debugger (provided the appropriate symbolic debugging information has been included with the application's execution module). The debugger doesn't restrict how line number references are used in expressions. A line number represents the code address of an executable statement in a routine. Not all line numbers represent executable statements; thus some line numbers might not be valid in an expression. For example, source lines consisting of comments don't represent executable statements.
The general format for a line number reference is:
[ [image]@ ] [module_name] @ decimal_digits
The following are examples of references to executable statements:
@36 @@45 @51 @125 hello@9 @hello@9 prog@hello@9 otherprg@goodbye@9 puzzle@50 calendar@20 SUB1@30
If the line number doesn't exist in the current module, it must be qualified with its module name. If it doesn't exist in the current image, it must be qualified with the image name. Line numbers aren't necessarily unique. For example, an executable statement could occur at line number 20 in several modules. The module name can always be used to uniquely identify the line 20 we're interested in. In the above examples, we explicitly refer to line 20 in the module calendar. When the module name is omitted, the current module is assumed.
Line number debugging information is included in an executable image only if you compile and link your application with the appropriate options. See the chapter Preparing a Program to be Debugged. |
The debugger supports arithmetic and character constants. Each constant has a data type associated with it. Arithmetic constants consist of those constants whose data type is integer, real, or complex (FORTRAN only). C treats character constants like arithmetic constants so they can be used in arithmetic expressions. FORTRAN treats character constants as constants of type CHARACTER so they can't be used in arithmetic expressions.
An integer constant is formed by a non-empty string of digits preceded by an optional radix specifier. The digits are taken from the set of digits valid for the given radix, if specified, or the current default radix. If the current radix is 10, then the digits are 0 through 9. If the current radix is 16, then the digits are 0 through 9 and A through F or a through f. See the section ``Options Dialog'' in the Debugger Environment chapter.
The following are examples of integer constants:
123 57DE 1423 345 34565788
You can define radix specifiers, but two are predefined by the debugger:
For example:
0x1234 hexadecimal 0n1234 decimal 255 decimal 0xff hexadecimal 0x1ADB hexadecimal 0n200 decimal 0x12fc0 hexadecimal
We first define a simple real constant as follows: an optional sign, followed by an integer part, followed by a decimal point, followed by a fractional part. The integer and fractional parts are non-empty strings of digits. The fractional part can be omitted.
A real constant has one of the following forms:
The optionally signed integer constant that follows the E is called the exponent. The value of a real constant that contains an exponent is the value of the constant preceding the E multiplied by the power of ten determined by the exponent.
The following are examples of real constants:
123.764 0.4352344 1423.34E12 +345.E-4 -0.4565788E3 2.E6 1234.
The accepted forms of floating-point constants are a subset of that supported by the FORTRAN 77 programming language. The debugger doesn't support floating-point constants that begin with a decimal point (for example, .4352344) or have no decimal point (for example, 2E6). However, both forms would be acceptable to a FORTRAN compiler. Also, the debugger doesn't support double-precision floating-point constants where ``D'' is used instead of ``E'' for the exponent part (for example, 2D6, 2.4352344D6). All floating-point constants are stored internally by the debugger in double-precision format. |
A complex constant consists of the following parts:
The following are examples of complex constants:
( 1423.34E12, 3 ) ( +345, 4 )
Complex constants are accepted when the debugger's currently established language is FORTRAN. The language currently selected can be determined using the SHow Set LAnguage command.
In the C and C++ programming languages, a character constant consists of an apostrophe followed by a single character followed by an apostrophe. The apostrophes aren't part of the data. An apostrophe in character data represents one character, namely the apostrophe. A character constant must have length 1.
The following are examples of character constants.
'A' 'e' '''
The C/C++ form of a character constant is accepted when the debugger's currently established language is C or C++. The language currently selected can be determined using the SHow Set LAnguage command.
In the FORTRAN 77 programming language, a character constant consists of an apostrophe followed by any string of characters followed by an apostrophe. The apostrophes aren't part of the data. If an apostrophe is to appear as part of the data, it must be followed immediately by another apostrophe.
Blanks are significant. |
The length of the character constant is the number of characters appearing between the delimiting apostrophes. Consecutive apostrophes in a character data represent one character, namely the apostrophe. A character constant must not have length 0.
The following are examples of character constants:
'ABCDEFG1234567' 'There''s always tomorrow'
The FORTRAN form of a character constant is accepted only when the debugger's currently established language is FORTRAN.
In addition to referring to memory locations by symbolic name or line number, you can also refer to them using a combination of constants, register names, and symbol names. In the Intel 80x86 architecture, a memory reference requires a segment and offset specification. When symbol names are used, these are implicit. The general form of a memory reference is:
[segment:]offset
When an offset is specified alone, the default segment value is taken from the CS, DS or SS register, depending on the circumstances.
The debugger defines a number of symbols that have special meaning. These symbols are used to refer to the computer's registers and other special variables. For more information, see Appendix B: Predefined Symbols.
The debugger permits the manipulation of register contents and special debugger variables (for example, dbg$32) using any of the operators described in this chapter. By default, these predefined names are accessed just like any other variables that you or the application defined.
Should the situation ever arise where the application defines a variable whose name conflicts with that of one of these debugger variables, the module specifier _dbg may be used to resolve the ambiguity. For example, if the application defines a variable called cs then _dbg@cs can be specified to resolve the ambiguity. The _dbg@ prefix indicates that we are referring to a debugger-defined symbol rather than an application-defined symbol.
Sometimes a value may be stored in more than one register. For example, a 32-bit long integer value may be stored in the register pair DX:AX. We require a mechanism for grouping registers to represent a single quantity for use in expressions.
We define the term register aggregate as any grouping of registers to form a single unit. An aggregate is specified by placing register names in brackets, in order from most significant to least significant. Any aggregate may be specified as long as it forms an 8-, 16-, 32- or 64-bit quantity. The following table gives examples of some of the many aggregates that can be formed:
Size | Register aggregate |
---|---|
8 bits | [al] |
16 bits | [ah al] |
[bl ah] | |
[ax] | |
32 bits | [dx ax] |
[dh dl ax] | |
[dh dl ah al] | |
[ds di] | |
64 bits | [ax bx cx dx] |
[edx eax] (386/486/Pentium only) |
In some cases, the specified aggregate may be equivalent to a register. For example, the aggregates [ah al] and [ax] are equivalent to ax.
The default type for 8-bit, 16-bit, and 32-bit aggregates is integer. The default type for 64-bit aggregates is double-precision floating-point. To force the debugger into treating a 32-bit aggregate as single-precision floating-point, the type coercion operator [float] may be used.
The debugger supports most C operators and includes an additional set of operators for convenience. The WATCOM C Language Reference manual describes many of these operators.
The syntax for debugger expressions is similar to that of the C programming language. Operators are presented in order of precedence, from lowest to highest. Operators on the same line have the same priority.
Assignment Operators (lowest priority):
= += -= *= /= %= &= |= ^= <<= >>=
Logical Operators:
|| &&
Bit Operators:
| ^ &
Relational Operators:
== != > <= < >=
Shift Operators:
<< >>
Arithmetic Operators:
+ - * / %
Unary Operators:
+ - ~ ! ++ -- & * % sizeof unary_expr sizeof(type_name) (type_name) unary_expr [type_name] unary_expr ?
Binary Address Operator (highest priority):
:
Parentheses can be used to order the evaluation of an expression.
In addition to the operators listed above, a number of primary expression operators are supported. These operators are used in identifying the object to be operated upon.
The following sections describe the operators presented above.
If the value on the left is non-zero, then the expression on the right isn't evaluated. This is known as short-circuit expression evaluation. |
(SS:00FE) = FFFF var: (SS:0100) = 0152 (SS:0102) = 1240 (SS:0104) = EEEE
(SS:00FE) = FFFF var: (SS:0100) = 0152 (SS:0102) = 1240 (SS:0104) = EEEE
This operator isn't found in the C or C++ programming languages. |
sizeof tyme sizeof (*tyme)
sizeof( struct tm )
type_name ::= type_spec { [ "near" | "far" | "huge" ] "*" } type_spec ::= typedef_name | "struct" structure_tag | "union" union_tag | "enum" enum_tag | scalar_type { scalar_type } scalar_type ::= "char" | "int" | "float" | "double" | "short" | "long" | "signed" | "unsigned"
For example:
(float) 4 (int) 3.1415926
Unless qualified by the short or long keyword, the int type is 16 bits in 16-bit applications and 32 bits in 32-bit applications (386, 486, and Pentium systems). The character, short integer, and long integer types may be treated as signed or unsigned items. The default for the character type is unsigned. The default for the integer types is signed. For example:
[char] (default is unsigned) [signed char] [unsigned char] [int] (default is signed) [short] (default is signed) [short int] (default is signed) [signed short int] [long] (default is signed) [long int] (default is signed) [signed long] [unsigned long int] [float] [double]
Note that it's unnecessary to specify the int keyword when short or long is specified.
?id
The result of this expression is 1 if id is a symbol known to the debugger, and 0 otherwise. If the symbol doesn't exist in the current scope, then it must be qualified with its module name. Automatic symbols exist only in the current function.
segment:offset
The elements segment and offset can be expressions. For example:
(ES):(DI+100) (SS):(SP-20)
char *ProcessorType[2][4][2] = { { { "Intel 8086", "Intel 8088" }, { "Intel 80186", "Intel 80188" }, { "Intel 80286", "unknown" }, { "Intel 80386", "unknown" } }, { { "NEC V30", "NEC V20" }, { "unknown", "unknown" }, { "unknown", "unknown" }, { "unknown", "unknown" } } };
This array can be viewed as two layers of rectangular matrices of 4 rows by 2 columns. The array elements are all pointers to string values.
By using a subscript expression, specific slices of an array can be displayed. To see only the values of the first layer, the following expression can be issued:
processortype[0]
To see only the first row of the first layer, the following expression can be issued:
processortype[0][0]
To see the second row of the first layer, the following command can be issued:
processortype[0][1]
To see the value of a specific entry in a matrix, all the indexes can be specified:
processortype[0][0][0] processortype[0][0][1] processortype[0][1][0]
ClearScreen() PosCursor( 10, 20 ) Line( 15, 1, 30, '-', '+', '-' )
tyme2.tm_year
tyme->tm_year
Debugger support for the C++ grammar includes all of the C operators described in the previous section ``Operators for the C Grammar.'' In addition to this, the debugger supports a variety of C++ operators in the C++ Programming Language manual.
Perhaps the best way to illustrate the additional capabilities of the debugger's support for the C++ grammar is by way of an example. The following C++ program encompasses the features of C++ that we use in our debugging example:
// DBG_EXAM.C: C++ debugging example program struct BASE { int a; BASE() : a(0) {} ~BASE(){} BASE & operator =( BASE const &s ) { a = s.a; return *this; } virtual void foo() { a = 1; } }; struct DERIVED : BASE { int b; DERIVED() : b(0) {} ~DERIVED() {} DERIVED & operator =( DERIVED const &s ) { a = s.a; b = s.b; return *this; } virtual void foo() { a = 2; b = 3; } virtual void foo( int ) { } }; void use( BASE *p ) { p->foo(); } void main() { DERIVED x; DERIVED y; use( &x ); y = x; }
Compile and link this program so that the most comprehensive debugging information is included in the executable file.
Continuing with the example of the previous section, we can step into the call to use() and up to the p->foo() function call. Try to set a breakpoint at foo.
You'll be presented with a window containing a list of foo functions to choose from since the reference to foo at this point is ambiguous. Select the one that interests you.
You may also have observed that, in this instance, p is really a pointer to the variable x, which is a DERIVED type. To display all the fields of x, you can typecast it as follows.
*(DERIVED *)p
Continuing with the example of the previous sections, we can step into the call to f->foo()() and up to the b=3 statement. You can use the this operator as illustrated in the following example.
this->a *this
Continuing with the example of the previous sections, we can set breakpoints at C++ operators using expressions similar to the following:
operator =
For example:
DERIVED & operator =( DERIVED const &s ) { a = s.a; b = s.b; return *this; }
We can use the scope operator :: to identify what it is that we wish to examine. Continuing with the example of the previous sections, we can enter an address like:
base::foo
In some cases, this also helps resolve any ambiguity. The example above permits us to set a breakpoint at the source code for the function foo() in the class BASE.
virtual void foo() { a = 1; }
Here are some more interesting examples:
derived::foo derived::operator =
The first of these two examples contains an ambiguous reference, so a prompt window is displayed to resolve the ambiguity.
We can also examine the constructor and destructor functions of an object or class. Continuing with the example of the previous sections, we can enter expressions like:
base::base base::~base
The examples above permit us to refer to the source code for the constructor and destructor functions in the class BASE.
The debugger supports most FORTRAN 77 operators and includes an additional set of operators for convenience. The additional operators are patterned after those available in the C programming language.
QNX doesn't support FORTRAN 77. The operators described in this section are useful only if you're remotely debugging an application on a platform that does support it. |
The grammar that the debugger supports is close to that of the FORTRAN 77 language, but there are a few instances where space characters must be used to clear up any ambiguities. For example, the expression:
1.eq.x
results in an error, since the debugger forms a floating-point constant from the 1., leaving the string eq.x. If you introduce a space after the 1, then you clear up the ambiguity:
1 .eq.x
Unlike FORTRAN, the parser in the debugger treats spaces as significant characters. Thus spaces must not be introduced in the middle of symbol names, constants, multi-character operators like .EQ. or //, and so on.
Operators are presented in order of precedence, from lowest to highest. Operators on the same line have the same priority.
Assignment Operators (lowest priority):
= += -= *= /= %= &= |= ^= <<= >>=
Logical Operators:
.EQV. .NEQV. .OR. .AND. .NOT.
Bit Operators:
| ^ &
Relational Operators:
.EQ. .NE. .LT. .LE. .GT. .GE.
Shift and Concatenation Operators:
<< >> //
Arithmetic Operators:
+ - * / % ** (unsupported)
Unary Operators:
+ - ~ ++ -- & * % [type_name] unary_expr ?
Binary Address Operator (highest priority):
:
Parentheses can be used to order the evaluation of an expression.
In addition to the operators listed above, a number of primary expression operators are supported. These operators are used in identifying the object to be operated upon:
The following built-in functions may be used to convert the specified argument to a particular type:
The sections that follow describe the operators presented above.
The debugger treats registers as unsigned items. |
The debugger treats registers as unsigned items. |
Exponentiation isn't supported by the debugger. |
(SS:00FE) = FFFF var: (SS:0100) = 0152 (SS:0102) = 1240 (SS:0104) = EEEE
(SS:00FE) = FFFF var: (SS:0100) = 0152 (SS:0102) = 1240 (SS:0104) = EEEE
The % operator isn't found in the FORTRAN 77 programming language. |
?id
The result of this expression is 1 if id is a symbol known to the debugger; 0 if not. If the symbol doesn't exist in the current scope, then it must be qualified with its module name. Automatic symbols exist only in the current subprogram.
segment:offset
The elements segment and offset can be expressions. For example:
(ES):(DI+100) (SS):(SP-20)
tyme2.tm_year
tyme->tm_year