[an error occurred while processing this directive]

HP OpenVMS Systems

C++ Programming Language
Content starts here

Compaq C++
Using Compaq C++ for OpenVMS Alpha


Previous Contents Index


Chapter 8
Using the OpenVMS Debugger

A debugger helps you find run-time errors by letting you observe and interactively manipulate program execution step by step, until you discover where the program functions incorrectly. The OpenVMS Debugger is symbolic, meaning that you can refer to symbolic names for the memory addresses allocated to variables, routines, labels, and so on. You need not use virtual addresses.

The language of the source program you are currently debugging determines the format you use to enter and display data. The language also determines the format used for features, such as comment characters, operators, and operator precedence, which have language-specific settings. However, if you have modules written in another language, you can switch from one language to another during your debugging session.

8.1 Debugging C++ Programs

The OpenVMS Debugger supports the language constructs of C++ and other debugger-supported programming languages. This section describes features specific to debugging C++ programs. For more information on the OpenVMS Debugger, see the OpenVMS Debugger Manual.

8.1.1 Compiling and Linking in Preparation for Debugging

To use the debugger, compile and link your program with the /debug qualifier on both commands. On the compiler command, the /debug qualifier writes into the object module the debug symbol records declared in the program source file. These records make the names of variables and other declared symbols accessible to debugger commands. If your program has several compilation units, make sure you use the /debug qualifier to compile each unit you want to debug.

Additionally, use the /nooptimize qualifier with the compiler command. Optimized code can reduce program size and increase execution speed, but can also create inconsistencies in memory content that adversely affects debugging. Use the default /optimize qualifier only with programs that have been completely debugged.

8.1.2 Debugger Support

Additionally, compilation with normal (full) optimization will have the following noticeable effects on OpenVMS Alpha systems:

  • Stepping by line will generally seem to bounce forward and back, due to the effects of code scheduling. The general drift will definitely be forward, but initial experience indicates that the effect will be very close to stepping by instruction.
  • Variables that are "split" (so that they are allocated in more than one location during different parts of their lifetimes) are not described at all.
    Although not handled quite like normal split variables, formal parameters that are passed in registers share many of the same problems as split variables. Even with the /nooptimize qualifier, such a parameter often will be copied immediately to a "permanent home" (either on the stack or in some other register) during the routine prolog. The debugger symbol table description of such parameters encodes this permanent home location and not the physical register in which the parameter is passed. The end-of-prolog location is recorded in the debugger symbol tables and will be used as the preferred breakpoint location when a breakpoint is set in the context of an appropriately set module (so that symbol table information is available to the debugger).

On the linker command, the /debug qualifier incorporates into the executable image all the symbol information contained in the object modules. Using the /debug qualifier on the linker command also starts the debugger at run time.

Debugger Command-Line Options

The compiler provides a set of debugger options that you can specify to the /debug qualifier on the compiler command line. These options determine the kind of information that the compiler places in the object module for use by the OpenVMS Debugger. These debugger options include using traceback records and using the debugger symbol table. For more information, see Section 1.2.

8.1.3 Starting and Ending a Debugging Session

When you enter the DCL run command and specify your executable image file, the OpenVMS Debugger takes control. The debugger displays a message indicating its version, the programming language the source code is written in, and the name of the image file. When the DBG> prompt appears, you can enter debugger commands.

To execute the program, enter the debugger go command. Execution proceeds until the debugger pauses or stops the program (for example, to prompt you for user input, to signal an error, or to inform you that your program completed successfully).

To interrupt the debugging session in progress, press Ctrl/C. The DBG> prompt displays and you can again enter debugger commands.

To end a debugging session, enter the debugger exit command or press Ctrl/Z.

8.1.4 Features Basic to Debugging C++ Programs

This section describes features essential for debugging C++ programs.

8.1.4.1 Determining Language Mode

The OpenVMS Debugger is in C++ language mode when invoked against a main program or routine written in C++. If you are debugging an application with modules written in some language other than C++, you may switch back to C++ language mode by using the command set language c_plus_plus .

You can use the show language command to determine the language mode set. For example:


DBG> show language
language: C_PLUS_PLUS
DBG>

8.1.4.2 Built-In Operators

This section describes the built-in operators that you can use in debugger commands. The operators in C++ language expressions are as follows:

Symbol Function Kind
* Indirection Prefix
& Address of Prefix
sizeof size of Prefix
-- Unary minus (negation) Prefix
+ Addition Infix
-- Subtraction Infix
* Multiplication Infix
/ Division Infix
% Remainder Infix
<< Left shift Infix
>> Right shift Infix
== Equal to Infix
!= Not equal to Infix
> Greater than Infix
>= Greater than or equal to Infix
< Less than Infix
<= Less than or equal to Infix
~ (tilde) Bit-wise NOT Prefix
& Bit-wise AND Infix
| Bit-wise OR Infix
^ Bit-wise exclusive OR Infix
! Logical NOT Prefix
&& Logical AND Infix
|| Logical OR Infix

Because the exclamation point (!) is an operator, it cannot be used in C++ programs as a comment delimiter. However, to permit debugger log files to be used as debugger input, the debugger still recognizes the exclamation point as a comment delimiter if it is the first nonspace character on a line. In C++ language mode, the debugger accepts a forward slash immediately followed by an asterisk (/*) as the comment delimiter. The comment continues to the end of the current line. A matching asterisk immediately followed by a slash (*/) is neither needed nor recognized.

The debugger accepts the asterisk (*) prefix as an indirection operator in both C++ language expressions and debugger address expressions. In address expressions, the asterisk prefix is synonymous to the period (.) prefix or the at sign (@) prefix when the language is set to C++.

To prevent unintended modifications to the program being debugged, the debugger does not support any of the assignment operators in C++ (or any other language). Thus, such operators as =, +=, --=, ++, and -- are not recognized. To alter the contents of a memory location, you must do so with an explicit deposit command.

8.1.4.3 Constructs in Language and Address Expressions

The supported constructs in language and address expressions for C++ are as follows:

Symbol Construct
[ ] Subscripting
. (period) Structure component selection
-> Pointer dereferencing

8.1.4.4 Data Types

Predefined data types supported in the debugger are as follows:

C++ Data Type OpenVMS Data Type Name
int Longword Integer (L)
unsigned int Longword Unsigned (LU)
short int Word Integer (W)
unsigned short int Word Unsigned (WU)
char Byte Integer (B)
unsigned char Byte Unsigned (BU)
float F_Floating (F)
double D_Floating (D)
enum None
struct None
union None
class None
Pointer type None
Array type None

Uppercase letters in parentheses represent standard data type mnemonics in the OpenVMS common language environment. For more information, see OpenVMS Programming Interfaces: Calling a System Routine.

Supported data types specific to OpenVMS Alpha systems are as follows:

C++ Data Type OpenVMS Alpha Data Type Name
__int16 Word Integer (W)
unsigned __int16 Word Unsigned (WU)
__int32 Longword Integer (L)
unsigned __int32 Longword Unsigned (LU)
__int64 Quadword Integer (Q)
unsigned __int64 Quadword Unsigned (QU)
__g_float G_Floating (G)
__f_float F_Floating (F)
__s_float IEEE S_Floating (FS)
__t_float IEEE T_Floating (FT)

8.1.4.5 Qualified Class Names

Discussions in Section 8.2 use the term qualified class names to describe how to compose the names of class members when using the debugger. If a class is not defined within another class, the qualified class name is merely the name of the class itself. However, if a class is nested within another class, the name of the immediately containing class must precede it separated with a pair of colons (::). If the containing class is itself nested, its name must be prefixed, and so on.

The following are examples of properly qualified class names:


DBG> set break C::f             ! f is a member of class C
DBG> set break FOO::BAR::BAZ::g ! g is a member of BAZ,
                                        ! which is nested in BAR,
                                        ! which is nested in FOO

8.2 Using the OpenVMS Debugger with C++ Data

This section describes how to use the OpenVMS Debugger with C++ data.

8.2.1 Nonstatic Data Members

This section describes how to refer to data members that are not declared static.

8.2.1.1 Noninherited Data Members

To refer to a nonstatic data member that is defined directly in a C++ class (or a struct or union ), use its name just as with a C language struct or union member. The following example shows the correct use of a nonstatic data member reference:


DBG> examine x.m, p->m

8.2.1.2 Inherited Data Members

Currently, debugger support distinguishes nonstatic data members inherited from various base classes by prefixing their names with a sequence of significant base class names on the inheritance path to the member, and then the class that the member is declared in. A base class on a path from an object to a member is significant if the base class in question is derived from using multiple inheritance. Thus, a base class is significant if it is mentioned in a base list containing more than one base specifier.

This notation generates the minimum number of base class prefixes necessary to describe the inheritance path to a base class, because it involves naming only those base classes where one must choose where to proceed next when traversing the path. When no multiple inheritance is involved, the reference has the following syntax:


CLASS::member

Specify the sequence of significant base classes in the order from the object's most derived significant class, to the significant base class closest to the object.

For example, consider the inheritance graph for the following declarations:


struct A { int a_member; };
struct B : A { int b_member; };
struct C { int c_member; };
struct D : B, C { int d_member; };
struct E : D { int e_member; };
struct F { int f_member; };
struct G : F { int g_member; };
struct H : E, G { int h_member; };
struct I : H { int i_member; };
struct J : I { int j_member; };

static J j_object;

Because classes B, C, E, and G are mentioned in base lists, which involve multiple inheritance, they are the significant classes that appear as prefixes. The following examples are references to all the members through debugger deposit commands. Note that the class of the inherited member itself appears before the member name, regardless of whether or not the member belongs to a significant class.


DBG> deposit j_object.E::B::A::a_member = 1
DBG> deposit j_object.E::B::b_member = 2
DBG> deposit j_object.E::C::c_member = 3
DBG> deposit j_object.E::D::d_member = 4
DBG> deposit j_object.E::e_member = 5
DBG> deposit j_object.G::F::f_member = 6
DBG> deposit j_object.G::g_member = 7
DBG> deposit j_object.H::h_member = 8
DBG> deposit j_object.I::i_member = 9
DBG> deposit j_object.j_member = 10
DBG> examine j_object
6212\j_object
    E::B::A::a_member:  1
    E::B::b_member:     2
    E::C::c_member:     3
    E::D::d_member:     4
    E::e_member:        5
    G::F::f_member:     6
    G::g_member:        7
    H::h_member:        8
    I::i_member:        9
    j_member:   10
DBG>

8.2.1.3 Inherited Virtual Data Members

In the OpenVMS Debugger, symbolic access to data members of virtual base classes is currently not supported. Objects of classes with virtual base classes have a pointer member named __bptr .

8.2.2 Static Data Members

The following examples show the correct use of static data member references:


DBG> examine C::s
DBG> examine FOO::BAR::BAZ::sdm

8.2.3 Reference Objects and Reference Members

To access the values of objects declared with a reference, use the name of the object.

The OpenVMS Debugger treats data members declared with a reference type as though they were pointer variables; thus, you must use the * or -> dereference operators on their names.

For example, consider the following code:


class C {
public:
    int &ref_mem;
    C(int &arg) : ref_mem(arg) {}
};

main()
{
    auto int obj = 5;
    auto int &ref_obj = obj;
    auto C c(obj);
    obj = 23;
}
...

The following sequence shows the correct way to use the debugger to examine the members:


stepped on return from routine REF\main to REF\main\%LINE 13+16
    13: }
DBG> examine obj, ref_obj
REF\main\obj:   23
REF\main\ref_obj:       23
DBG> examine c
REF\main\c
    ref_mem:    2144211292
DBG> symbolize c.ref_mem
address 7FCE1154:
    REF\main\c
DBG> examine *c.ref_mem
*REF\main\c.ref_mem:    23

8.2.4 Pointers to Members

Values that are pointers to members are represented as a struct with the members __thunk and __offset . The __thunk member contains the address of the member pointed to. The __offset member contains an offset adjustment when the pointer to member refers to a multiply inherited base class. For example:


struct A {
    int mem0;
};

struct B {
    int mem1;
    int mem2;
};

struct C : public A, public B {
    int mem3;
    int mem4;
};

/* pointer to member initalized with pointer to member
 * address of the same class.
 */
int C::*pmc = &C::mem2;

/* pointer to member initialized with pointer to member
 * address of one of the  * base classses.  An implicit
 *  conversion occurs.
 */
int C::*pmbc = &B::mem2;

extern "C" printf (const char *,...);

main()
{
 C *cinst = new C;
 cinst->*pmc = 7;
 printf("cinst pointer to member value is %d\n",cinst->mem2);
}

If you compile this program with the /nooptimize/debug qualifiers, from the last line in the program, you can use the pointer to member to display the following information:


DBG>set break %line 31
DBG>go
DBG>go
cinst pointer to member value is 7
DBG> Set Mode Noscreen; Set Step Source
DBG> ex cinst
PTRMEMBER\main\cinst:   004136F8
DBG> ex *cinst
*PTRMEMBER\main\cinst
    A::mem0:    00000000
    B::mem1:    00000000
    B::mem2:    00000007
    mem3:       00000000
    mem4:       00000000
DBG> ex pmc
PTRMEMBER\pmc
    __thunk:    00010060
    __offset:   00000000
DBG> call 00010060(004136F8)
value returned is 00413700
DBG> ex 00413700
00413700:       00000007
DBG> ex pmbc
PTRMEMBER\pmbc
    __thunk:    00010098
    __offset:   00000004
DBG> call 00010098(004136F8+4)
value returned is 00413700
DBG> ex 00413700
00413700:       00000007
DBG> exit

8.2.5 Referencing Entities by Type

To examine and display the value of an object or member by type, use the command examine/type . Similarly, you can modify the value of an expression to be deposited to a type you specify by using the command deposit/type . With the /type qualifier, the syntax for these commands is as follows:


deposit/type=(name)
examine/type=(name)

The type denoted by name must be the name of a variable or data type declared in the program. The /type qualifier is particularly useful for referencing C++ objects that have been declared with more than one type.

8.3 Using the OpenVMS Debugger with C++ Functions

This section describes how to reference the various kinds of functions and function arguments.

8.3.1 Referring to Overloaded Functions

To find the symbolic names of functions in your code, use the show symbol command. If the function is overloaded, use the wildcard character (*) in the name specification to display the overloaded symbol names.

For example, consider the following code:


class base
  {
  public:
    base();
    base( int );

    ~base();

    int base_f1();

    void base_f2();
    void base_f2( int );
    void base_f2( char );
   };

The following sequence shows how to display overloaded symbols and determine the appropriate function reference:


DBG> set break base::base_f2
%DEBUG-E-NOTUNQOVR, symbol 'base::base_f2' is overloaded
        use SHOW SYMBOL to find the unique symbol names
DBG> show symbol *base_f2
overloaded symbol CXX_T10_179\base::base_f2
  overloaded instance CXX_T10_179\base::base_f2__1
  overloaded instance CXX_T10_179\base::base_f2__2
  overloaded instance CXX_T10_179\base::base_f2__3
DBG> set break base::base_f2__2
DBG> step
stepped to CXX_T10_179\main\%LINE 20
    20:     x.base_f2();
DBG> step
stepped to CXX_T10_179\main\%LINE 21
    21:     x.base_f2(5);
DBG> step
break at routine CXX_T10_179\base::base_f2__2
    12:     void base_f2( int ) {}
DBG> step
stepped to CXX_T10_179\main\%LINE 22
    22:     x.base_f2('W');
stepped to CXX_T10_179\main\%LINE 22
DBG> go
%DEBUG-I-EXITSTATUS, is '%SYSTEM-S-NORMAL, normal successful completion'
DBG> ^Z

8.3.2 Referring to Static and Nonstatic Member Functions

If the member function is overloaded, append the suffix __ integer-number. The following examples show the correct use of member function references:


DBG> set break MYSTRING::length
DBG> set break MYCOMPLEX::format__1, MYCOMPLEX::format__2

8.3.3 Referring to Constructors

To refer to a constructor, state the name. If a constructor is overloaded, append the suffix __ integer-number. The following examples show the correct use of constructor references:


DBG> set break FOO
DBG> set break MYSTRING__1

8.3.4 Referring to Destructors

The following example shows the correct use of a destructor reference:


DBG> set break ~FOO

8.3.5 Referring to Conversions

The set of atomic types are drawn from the following set of names:


void              char       signed_char     unsigned_char   signed_short
unsigned_short    int        signed_int      unsigned_int    signed_long
unsigned_long     float      double          long_double

Pointer types are named (type)* . Reference types are named (type)& . The types struct , union , class , and enum are named by their tags, and the qualifiers const and volatile precede their types with a space in between. For example:


DBG> set break C::int, C::(const S)&

8.3.6 Referring to User-Defined Operators

The following operators may be overloaded by user-defined functions:


+       -       *       /       %       ^
&       |       ~       !       =       <
>       +=      -=      *=      /=      %=
^=      &=      |=      <<      >>      >>=
<<=     ==      !=      <=      >=      &&
|       ++      --      ->*     ,       ->
[]      ()      delete  new

If the function is overloaded, append the suffix __ integer_number to the operator characters. In particular, this suffix is necessary if both unary and binary instances of an operator such as + are defined, or if prefix instances of ++ or -- are defined.

The following examples show the correct use of user-defined function references:


DBG> set break MYSTRING::+
DBG> set break COUNTER::++__1, COUNTER::++__2

See §7.2 of The C++ Programming Language, 3nd Edition for details.

8.3.7 Referring to Function Arguments

In OpenVMS Debugger referencing, you use this , *this , and this->m as follows:

  • All nonstatic member functions have a pointer parameter available named this . For example:


    DBG> examine this
    
  • Use *this to examine the prefix object that a member function is invoked against. For example:


    DBG> examine *this
    
  • Use the this parameter to refer to a data member m of the prefix argument to a member function. For example:


    DBG> examine this->m
    

8.3.8 Calling C++ Member Functions from the Debugger

When calling C++ member functions from the debugger, you cannot make the call using the same syntax that you would use in a C++ source file. You must call the class qualified member function name with the object as the first argument. For example:


extern "C" void printf(const char *,...);

class C12 {
        int i;
        int j;
public:
        C12() : i(1), j(2) {}
        method();
};

C12::method()
{
        i = i + j;
        printf("C12::method called: i=%d, j=%d\n",i,j);
}

main()
{
        C12 cinst;
        cinst.method();
        printf("End of example.\n");
}

When you compile this example with /debug/noopt , you can call the member function with the following command:


DBG> call C12::method(cinst)


Previous Next Contents Index