10 Debugging DEC C++ Programs

10.1 Significant Supported Features

The Digital Ladebug debugger debugs programs compiled with the DEC C++ compiler on the Digital UNIX operating system. The following features are supported:

Some of these features are discussed further in this chapter. This chapter also explains the limitations on Ladebug support for the C++ language.

10.2 DEC C++ Flags for Debugging

To prepare to use the Ladebug debugger on a C++ program, invoke the compiler with the appropriate debugging flag: -g, -g2, or -g3. For example:

% cxx -g sample.cxx -o sample

The -g flag on the compiler command instructs the compiler to write the program's debugger symbol table into the executable image. This flag also turns off the default optimization, which could cause a confusing debugging session.

Refer to the cxx(1) reference page or other compiler documentation for information about the various -gn flags and their relationship to optimization.

Traceback information and symbol table information are both necessary for debugging. They:

Traceback and symbol table information result in a larger object file. When you have finished debugging your program, you can remove traceback information with the the strip command (see strip(1) ). To remove symbol table information, you can compile and link again with -g0 or -g1 to create a new executable program.

Typical uses of the debugging flags at the various stages of program development are as follows:


+ g1 results in a larger object file than -g0 but smaller than the other gn flags.

10.3 Calling Overloaded Functions

To call overloaded functions from the debugger, you must set $overloadmenu to 1 (the default), as follows:

(ladebug) set $overloadmenu = 1

Then, use the following call command syntax:

call  function ([parameter[, . . . ]])

For example:

(ladebug) call foo(15)

The debugger will call the function that you select from the menu of overloaded names.

10.4 Setting the Class Scope

The debugger maintains the concept of a current context in which to perform lookup of program variable names. The current context includes a file scope and either a function scope or a class scope. The debugger automatically updates the current context when program execution suspends.

The class command lets you set the scope to a class in the program you are debugging. The syntax for the class command is as follows:

class  class_name

Explicitly setting the debugger's current context to a class allows for visibility into a class to:

After the class scope is set, you can set breakpoints in the class's member functions and examine data without explicitly mentioning the class name. If you do not want to affect the current context, you can use the scope resolution operator (::) to access a class whose members are not currently visible.

To display the current class scope (if one exists), enter the class command with no argument.

Example 10-1 shows the use of the class command to set the class scope to S in order to make member function foo visible so a breakpoint can be set in foo.

Example 10-1 Setting the Class Scope

(ladebug) stop in main; run
[#1: stop in main ]
[1] stopped at [int main(void):26 0x120000744]
     26      int result = s.bar();

(ladebug) stop in foo
Symbol foo not visible in current scope.
foo has no valid breakpoint address
Warning: Breakpoint not set

(ladebug)  class S
class S  {
  int i;
  int j;
  S (void);
  ~S (void);
  int foo (void);
  virtual int bar (void);
}

(ladebug) stop in foo
[#2: stop in foo (void) ]
(ladebug)

10.5 Displaying Class Information

The whatis and print commands display information on a class. Use the whatis command to display static information about the classes. Use the print command to view dynamic information about class objects.

The whatis command displays the class type declaration, including:


Data members
Member functions
Constructors
Destructors
Static data members
Static member functions

For classes that are derived from other classes, the data members and member functions inherited from the base class are not displayed. Any member functions that are redefined from the base class are displayed.

The whatis command used on a class name displays all class information, including constructors. To use this command on a constructor only, use the following syntax:

whatis  class_name::class_name

Constructors and destructors of nested classes must be accessed with the class command. The following syntax is for a constructor:

class  class_name::(type signature)

The following syntax is for a destructor:

class  class_name~::(type signature)

The print command lets you display the value of data members and static members. Information regarding the public, private, or protected status of class members is not provided, since the debugger relaxes the related status rules to be more helpful to users.

The type signatures of member functions, constructors, and destructors are displayed in a form that is appropriate for later use in resolving references to overloaded functions.

Example 10-2 shows the whatis and print commands in conjunction with a class.

Example 10-2 Displaying Class Information

(ladebug) list 1, 9
      1 class S {
      2 public:
      3      int i;
      4      int j;
      5      S() { i = 1; j = 2; }
      6      ~S() { }
      7      int foo ();
      8      virtual int bar();
      9 };

(ladebug) whatis S
class S  {
  int i;
  int j;
  S (void);
  ~S (void);
  int foo (void);
  virtual int bar (void);
} S

(ladebug) whatis S :: bar
int bar (void)

(ladebug) stop in S :: foo
[#2: stop in S :: foo ]

(ladebug) run
[2] stopped at [int S::foo(void):13 0x120000648]
     13      return i;

(ladebug) print S :: i
1
(ladebug)

10.6 Displaying Object Information

The print and whatis commands display information on instances of classes (objects). Use the whatis command to display the class type of an object. Use the print command to display the current value of an object. You can print an object's contents all at one time by using the following print command syntax:

print  object

You can also display individual object members using the member access operators, period (.) and right arrow (->), in a print command.

You can use the scope resolution operator (::) to reference global variables, to reference hidden members in base classes, to explicitly reference a member that is inherited, or otherwise to name a member hidden by the current context.

When you are in the context of a nested class, you must use the scope resolution operator to access members of the enclosing class.

Example 10-3 shows how to use the print and whatis commands to display object information.

Example 10-3 Displaying Object Information

(ladebug) whatis s
class S  {
  int i;
  int j;
  S (void);
  ~S (void);
  int foo (void);
  virtual int bar (void);
} s

(ladebug) stop in S::foo; run
[#1: stop in s.foo ]
[1] stopped at [int S::foo(void):13 0x120000638]
     35      return i;

(ladebug) print *this
class {
        i = 1;
        j = 2;
    }

(ladebug) print i, j
1 2

(ladebug) print this->i, this->j
1 2
(ladebug)

10.7 Displaying Virtual and Inherited Class Information

When you use the print command to display information on an instance of a derived class, Ladebug displays both the new class members as well as the members inherited from a base class. Base class member information is nested within the inherited class information. Ladebug displays members that are inherited from a base class, using the following notation:

baseclass:{var1:value1,var2:value2, . . . varN:valueN}

Pointers to members of a class are not supported.

If you have two members in an object with the same name but different base class types (multiple inheritance), you can refer to the members using the following syntax:

object.class::member

This syntax is more effective than using the object.member and object->member syntaxes, which can be ambiguous. In all cases, the Digital Ladebug debugger uses the C++ language rules as defined in The Annotated C++ Reference Manual to determine which member you are specifying.

Example 10-4 shows a case where the expanded syntax is necessary.

Example 10-4 Resolving References to Objects of Multiple Inherited Classes

(ladebug) print dinst.ambig
Ambiguous reference
Selecting 'ambig' failed!
Error: no value for dinst.ambig

(ladebug) print dinst.B::ambig
2
(ladebug)

Trying to examine an inlined member function that is not called results in the following error:

Member function has been inlined.

Ladebug will report this error regardless of the setting of the -noinline_auto compilation flag. As a workaround, include a call to the given member function somewhere in your program. (The call does not need to be executed.)

If a program is not compiled with the -g flag, a breakpoint set on an inline member function may confuse the debugger.

10.8 Member Functions on the Stack Trace

The implicit this pointer, which is a part of all nonstatic member functions, is displayed as the address on the stack trace. The class type of the object is also given.

Sometimes the debugger does not see class type names with internal linkage. When this happens, the debugger issues the following error message:

Name is overloaded.

The stack trace in Figure 10-1 displays a member function foo of an object declared with class type S.

Figure 10-1 A Stack Trace Displaying a Member Function

10.9 Resolving Ambiguous References to Overloaded Functions

In most cases, the debugger works with one specific function at a time. In the case of overloaded function names, you must specify the desired overloaded function. There are two ways to resolve references to overloaded function names, both under the the control of the $overloadmenu debugger variable (the default setting of this debugger variable is 1):

10.10 Setting Breakpoints

When you set a breakpoint in a C function, the debugger confirms it by echoing the breakpoint command along with the status number for the breakpoint. When you set a breakpoint in a C++ function, the debugger also prints the type signature of the function in which the breakpoint was set.

The following sections describe setting breakpoints in member functions, in overloaded functions, and in constructors and destructors. See Section 10.12.1 for information on setting breakpoints in exception handlers.

10.10.1 Setting Breakpoints in Member Functions

To set a breakpoint that stops in a member function, use one of the following stop in commands:

stop in  function

stop in  class_name::function

These forms of specifying a breakpoint in a function use the static class type information to determine the address of the function at which to set the breakpoint, and presume that no run-time information from an object is needed.

In Example 10-7, a breakpoint is set for the bar member function of class S.

Example 10-7 Setting Breakpoints in Member Functions

(ladebug) stop in S :: bar
[#1: stop in S::bar(void) ]

(ladebug) status
#1 PC==0x120000658 in S::bar(void) "c++ex.C":18 { break }

(ladebug) run
[1] stopped at [S::bar(void):18 0x120000658]
     18      return j;

(ladebug) where
>0  0x120000658 in ((S*)0x120000658)->bar() c++ex.C:18
#1  0x120000750 in main() c++ex.C:26
(ladebug)

If you need run-time information from the object to determine the correct virtual function at which to set a breakpoint, qualify the function name with the object, using one of the following stop in commands:

stop in  object.function

stop in  objectpointer->function

These forms of setting the breakpoint cause the debugger to stop at the member function in all objects declared with the same class type as the specified object. In Example 10-8, objects s and t are both declared to be of class type S. A breakpoint is set for the bar member function. The first time the debugger stops at bar() is for object s. The second time the debugger stops in bar() is for object t.

Example 10-8 Setting Breakpoints in Virtual Member Functions

(ladebug) stop in main
[#1: stop in main(void) ]

(ladebug) run
[1] stopped at [main(void):26 0x120000744]
     26      int result = s.bar();

(ladebug) stop in s.bar
[#2: stop in S::bar(void) ]
(ladebug) status
#1 PC==0x120000744 in main(void) "c++ex.C":26 { break }
#2 PC==0x120000658 in S::bar(void) "c++ex.C":18 { break }

(ladebug) print &s
0x140000000

(ladebug) print &t
0x140000008

(ladebug) cont
[2] stopped at [S::bar(void):18 0x120000658]
     18      return j;

(ladebug) where
>0  0x120000658 in ((S*)0x140000000)->bar() c++ex.C:18
#1  0x120000750 in main() c++ex.C:26

(ladebug) cont
[2] stopped at [S::bar(void):18 0x120000658]
     18      return j;

(ladebug) where
>0  0x120000658 in ((S*)0x140000008)->bar() c++ex.C:18
#1  0x12000076c in main() c++ex.C:27
(ladebug)

To set a breakpoint that stops only in the member function for this specific object and not all instances of the same class type, you must specify this as an additional conditional clause to the stop command. Use one of the following stop in commands:

stop in  object.function if & object == this

stop in  objectpointer->function if & objectpointer ==
                this

These forms of the stop in command instruct the debugger to stop in the function only for the object specified by the this pointer. In Example 10-9, which is running the same program as Example 10-8, the breakpoint is set for the member function for object s only. After stopping in bar() for object s, further execution of the program results in the program running to completion.

Example 10-9 Setting Breakpoints in Member Functions for a Specific Object

(ladebug) stop in s.bar if &s==this
[#2: stop in s.bar if &s==this ]

(ladebug) status
#1 PC==0x120000744 in main(void) "c++ex.C":26 { break }
#2 (PC==0x120000658 in S::bar(void) "c++ex.C":18 and if &s==this) {break}

(ladebug) print &s
0x140000000

(ladebug) cont
[2] stopped at [S::bar(void):18 0x120000658]
     18      return j;

(ladebug) where
>0  0x120000658 in ((S*)0x10000010)->bar() c++ex.C:18
#1  0x120000750 in main() c++ex.C:26

(ladebug) cont
Thread has finished executing
(ladebug)

10.10.2 Setting Breakpoints in Overloaded Functions

To set a breakpoint in an overloaded function, you must provide the full type signature of the function. Use one of the following stop in commands:

stop in  function (type_signature)

stop in  class_name::function (type_signature)

If the desired version of the function has no parameters, you must enter void for the type signature. In Example 10-10, the breakpoint is set for specific versions of the overloaded function foo.

Example 10-10 Setting Breakpoints in Specific Overloaded Functions

(ladebug) stop in foo(double)
[#1: stop in void S::foo(double) ]

(ladebug) stop in foo(void)
[#2: stop in int S::foo(void) ]

(ladebug) status
#1 PC==0x120001508 in void S::foo(double) "c++over.C":156 { break }
#2 PC==0x120000ef4 in int S::foo(void) "c++over.C":59 { break }
(ladebug)

To set a breakpoint that stops in all versions of an overloaded function, use one of the following stop in all commands:

stop in all  function

stop in all  class_name::function

In Example 10-11, the breakpoint is set for all versions of the overloaded function foo.

Example 10-11 Setting Breakpoints in All Versions of an Overloaded Function

(ladebug) stop in all foo
[#1: stop in all foo ]
(ladebug)

You can also set a breakpoint in an overloaded function by setting a breakpoint at the line number where the function begins. Be sure the current file context points to the file containing the function's source code before you set the breakpoint. In Example 10-12, the breakpoint is set for the overloaded functions by line number.

Example 10-12 Setting Breakpoints in Overloaded Functions by Line Number

(ladebug) stop at 59
[#1: stop at "c++over.C":59 ]
(ladebug) stop at 156
[#2: stop at "c++over.C":156 ]
(ladebug) status
#1 PC==0x120000ef4 in S::foo(void) "c++over.C":59 { break }
#2 PC==0x120001508 in S::foo(double) "c++over.C":156 { break }
(ladebug)

10.10.3 Setting Breakpoints in Constructors and Destructors

To set a breakpoint in a constructor, use one of the following stop in commands:

stop in  class_name::class_name [(type_signature)]

stop in  class_name [(type_signature)]

The type signature is only necessary to resolve an ambiguous reference to a constructor that is overloaded. In Example 10-13, a breakpoint is set in a constructor.

Example 10-13 Setting Breakpoints in Constructors

(ladebug) class S
class S  {
  int i;
  int j;
  S (void);
  ~S (void);
  int foo (void);
  virtual int bar (void);
}

(ladebug) stop in S
[#1: stop in S::NS(void) ]

(ladebug) status
#1 PC==0x1200005b8 in S::S(void) "c++ex.C":5 { break }
(ladebug)

You can similarly set a breakpoint in a destructor using the following stop in command syntax:

stop in  ~class_name

In Example 10-14, the breakpoint is set for the destructor.

Example 10-14 Setting Breakpoints in Destructors

(ladebug) stop in ~S
[#1: stop in ~S::S(void) ]
(ladebug) status
#1 PC==0x1200005f8 in S::~S(void) "c++ex.C":6 { break }
(ladebug)

As with any function's type signature specification, constructors and destructors that have no parameters must be referenced with a type signature of void.

10.11 Class Templates and Function Templates

The debugger provides support for debugging class templates and function templates in much the same way as other classes and functions in C++, with the limitations described in Section 10.14.

You can use the whatis command on an instantiation of the function template as shown in Example 10-15.

Example 10-15 Example of a Function Template

(ladebug) list 1
      1 // remember to compile with -define_templates
      2 template<class T> int compare(T t1, T t2)
      3 {
      4         if (t1 < t2) return 0;
      5         else         return 1;
      6 }
      7
      8 main()
      9 {
>    10         int i = compare(1,2);
     11 }

(ladebug) whatis compare
int compare (int, int)
(ladebug)

You can set a breakpoint in a template function as shown in Example 10-16.

Example 10-16 Setting a Breakpoint in the Template Function

(ladebug) stop in compare
[#2: stop in compare(int, int) ]
(ladebug) run
[2] stopped at [compare(int, int):4 0x120000560]
      4         if (t1 < t2) return 0;
(ladebug) stop in S.pop
[#1: stop in stack<int,100>::pop(void) ]
(ladebug) run
stopped at [stack<int,100>::pop(void):17 0x120001e0c]
     17         return s[--top];
(ladebug) func
stack<int,100>::pop(void) in c++classtemp.C line No. 17:
     17         return s[--top];
(ladebug) print top
2
(ladebug)

Example 10-17 displays the class definition of a particular instantiation of a parameterized stack.

Example 10-17 Displaying an Instantiated Class Template

(ladebug) whatis stack<int,100>
class stack<int,100>  {
  array [subrange 0 ... 99 of int] of int s;
  int top;
  stack<int,100> (void);
  void push (int);
  int pop (void);
} stack<int,100>
(ladebug)

You can explicitly set your current class scope to a particular instantiation of a class template if you are not in the proper class scope. See Example 10-18.

Example 10-18 Setting Current Class Scope to an Instantiated Class

(ladebug) stop in push
Symbol push not visible in current scope.
push has no valid breakpoint address
Warning: Breakpoint not set

(ladebug) class
Current context is not a class

(ladebug) class S
class stack<int,100>  {
  array [subrange 0 ... 99 of int] of int s;
  int top;
  stack<int,100> (void);
  ~stack<int,100> (void);
  void push (int);
  int pop (void);
}

(ladebug) stop in push
[#4: stop in stack<int,100>::push(int) ]

(ladebug) run
[4] stopped at [stack<int,100>::push(int):10 0x120001cd0]
     10               s[top++] = item;
(ladebug)

As an alternative, you can use the following syntax:

(ladebug)  class stack<int,100>

10.12 Debugging C++ Exception Handlers

You can debug C++ exception handlers in programs by setting breakpoints in the exception handler or in the predefined C++ functions that are used when exceptions occur. You can also examine and modify variables that are used in exception handlers.

10.12.1 Setting Breakpoints in Exception Handlers

As shown in Example 10-19, you can set a breakpoint for an exception handler at the line number where the code for the exception handler begins. You can then step through the exception handler, examine or modify variables, or continue executing the program. You can also set breakpoints in C++ functions used to handle exceptions as follows:
terminate   Gains control when any unhandled exception occurs and terminates the progrm 
unexpected   Gains control when a function containing an exception specification tries to throw an exception that is not in the exception specification 

Example 10-19 Setting Breakpoints in Exception Handlers

(ladebug) list 24
     24     try
     25     {
     26          foo();
     27     }
     28     catch(char * str) { printf("Caught %s.\n",str); }
     29     catch(...) { printf("Caught something.\n"); }
     30
     31 return 0;
     32 }

(ladebug) stop at 24
[#1: stop at "except.C":26 ]

(ladebug) stop in unexpected
[#2: stop in unexpected ]

(ladebug) run
[1] stopped at [int main(void):26 0x400370]
     26          foo();

(ladebug) cont
[2] stopped at [unexpected:631 0x4010a8]
(Cannot find source file cxx_exc.c)

(ladebug) cont
In my_unexpected().
Caught HELP.
Thread has finished executing
(ladebug)

10.12.2 Examining and Modifying Variables in Exception Handlers

After you set a breakpoint to stop the execution in the exception handler, you can access the variables used in the exception handler the same way you would examine and modify other program variables. See Section 8.3.

10.13 Advanced Program Information: Verbose Mode

By default, the debugger gives no information on virtual base class pointers for the following:

By setting the $verbose debugger variable to 1, you can request that this information be printed in subsequent debugger responses.

When the $verbose debugger variable is set to 1 and you display the contents of a class using the whatis command, several of the class members listed are not in the source code of the original class definition. The following line shows sample output from the whatis command:

array [subrange 0 ... 0 of int] of vtable * _\|_vptr;

The vtable variable contains the addresses of all virtual functions associated with the class. Several other class members are generated by the compiler for internal use.

The compiler generates additional parameters for nonstatic member functions. When the $verbose debugger variable is set to 1, these extra parameters are displayed as part of each member function's type signature. If you specify a version of an overloaded function by entering its type signature and the variable is set to 1, you must include these parameters. Do not include these parameters if the variable is set to 0.

When the $verbose variable is set to 1, the output of the dump command includes not only standard program variables but also compiler-generated temporary variables.

Example 10-20 prints class information using the whatis command when the $verbose variable is set to 1.

Example 10-20 Printing a Class Description in Verbose Mode

(ladebug) print $verbose
0
(ladebug) whatis S
class S  {
  int i;
  int j;
  S (void);
  ~S (void);
  int foo (void);
  virtual int bar (void);
} S
(ladebug) set $verbose = 1
(ladebug) print $verbose
1
(ladebug) whatis S
class S  {
  int i;
  int j;
  array [subrange 0 ... 0 of int] of vtbl * _\|_vptr;
  S (S* const);
  S (S* const, const S&);
  ~S (S* const, int);
  int foo (S* const);
  S& operator = (S* const, const S&);
  virtual int bar (S* const);
} S
(ladebug)

When displaying information on virtual base classes, the debugger prints pointers to the table describing the base class for each virtual base class object member. This pointer is known as the bptr base pointer. This pointer is printed after the class member information.

10.14 Limitations on Ladebug Support for C++

Ladebug interprets C++ names and expressions using the language rules described in The Annotated C++ Reference Manual (Ellis and Stroustrup, 1990, Addison- Wesley). C++ is a distinct language, rather than a superset of C. Where the semantics of C and C++ differ, Ladebug provides the interpretation appropriate for the language of the program being debugged.

To make Ladebug more useful, it relaxes some standard C++ name visibility rules. For example, you can reference both public and private class members.

The following limitations apply when you debug a C++ program:

Limitations for debugging templates include: