HP.com home

HP C++ User Documentation

 

HP C++

HP C++
Using HP C++ for Tru64 UNIX and Linux Alpha


Previous Contents Index


Chapter 9
Using the Ladebug Debugger

A debugger helps you find run-time errors by letting you observe and interactively manipulate execution step by step, until you discover where the program functions incorrectly.

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

This chapter discusses debugging programs using the command line interface to the Ladebug Debugger. A graphical user interface is also available through DEC FUSE, which supports all of the Ladebug commands mentioned in this chapter.

DEC FUSE is the integrated software development environment for HP Tru64 UNIX systems. Besides the Ladebug commands described in this chapter, DEC FUSE provides the programmer with a class browser, call graph browser, and cross-referencing capability to assist in debugging HP C++ programs. For more information, see the DEC FUSE Debugger Manual.

9.1 Debugging C++ Programs

The Ladebug debugger is a source-level debugger that debugs C++ programs compiled with the HP C++ compiler. For general information on how to use the Ladebug debugger, see the HP Tru64 UNIX Ladebug Debugger Manual and the Ladebug(1) reference page.

When debugging C++ code, the debugger supports the following features:

  • C++ names and expressions, including:
    • Using an explicit and implicit this pointer to refer to class members
    • The scope resolution operator (::)
    • The member access operators, period (.) and right arrow (->)
    • Reference types
    • Template instantiations
    • C++ exception handling
  • Setting breakpoints in:
    • Member functions, including static and virtual functions
    • A member function in a particular object
    • Overloaded functions
    • Constructors and destructors
    • Template instantiations
    • Exception handlers
  • Changing the current class scope to set breakpoints and examine static members of a class that are not currently in scope
  • Calling overloaded functions
  • Debugging programs containing a mixture of C and C++ code

The debugger interprets C++ names and expressions using the language rules described in the International C++ Standard. C++ is a distinct language, rather than a superset of C. Where the semantics of C and C++ differ, the debugger provides the interpretation appropriate for the language of the program being debugged.

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

9.2 Using Absolute and Relative Path Names

If your program supplies an absolute filename like two/two.cxx , the object file contains that name. For example:


$ gemc_cxx -ptr ~/cxx_repository -I templates two/two.cxx 
$ a.out 
in is_zero(long) templates/template.hxx 
in is_zero(double) templates/template.hxx 

If you supply the full path name, the object file contains the full name:


$ gemc_cxx -ptr ~/cxx_repository -I ~/templates two/two.cxx 
$ a.out 
in is_zero(long) /usr/users/smith/templates/template.hxx 
in is_zero(double) /usr/users/smith/templates/template.hxx 

There are advantages and disadvantages to using relative path names versus complete path names. With relative pathnames, you can move the development tree and continue to debug. With absolute path names, you can move your executable and continue to debug, and using multiple repositories is easier.

Although moving development trees might not be common, mounting them on some other system is, and there is no reason why the mount points on the two systems need have the same name---for example, in a cluster.

On sys1 , /usr/users/smith points to
/usr/var/ase/mnt/ab-nfs/ab-users/users4/smith .
On sys2 , /usr/users/smith points to /tmp_mnt/var/users/smith .

Consider what would happen if the compiler tried to save the complete path name of a file instead of the name as supplied. It would use something like getcwd() to complete the name. On sys1 , the compiler would save the name


/usr/var/ase/mnt/abcd-nfs/abcd_users/users45/smith/templates/template.hxx 

You would be unable to debug the program on sys2 , which does not have access to


/usr/var/ase/mnt/abcd-nfs/abcd_users/users45/smith/templates/template.hxx 

By saving the name the user supplies, the compiler gives the user control over how they access the files. If you want full path names, supply them on the command line. If you want relative path names, supply those instead.

9.3 Debugging Programs Containing C and C++ Code

The debugger lets you debug mixed-language programs. Program flow of control across functions written in different languages is transparent.

The debugger automatically identifies the language of the current function or code segment based on information embedded in the executable file. If program execution is suspended in a C function, the current language is C. If the program executes a C++ function, the current language becomes C++.

The current language determines the valid expression syntax for the debugger. When the current language is C, printing an expression such as S::foo causes an error, because scope resolution operators and class types are not valid in C expressions. The current language of the debugger also affects the semantics used to evaluate an expression. For example, in C, all character constants are of type int . In C++, single character constants are of type char . In C, sizeof('a') = sizeof(int) . In C++, sizeof('a') = 1 .

The debugger sets the variable $lang to the language of the current function or code segment. By manually setting the debugger variable $lang , you can force the debugger to interpret expressions used in commands by the rules and semantics of a particular language. Example 9-1 shows how to switch between C++ and C modes while debugging.

Example 9-1 Switching Between C++ and C Debugging Modes

(Ladebug) print $lang
"C++" 
(Ladebug) print sizeof('a')
1 
(Ladebug) set $lang = "C" 
(Ladebug) print sizeof('a')
4 
(Ladebug) print sizeof(int)
4 
(Ladebug) 

When the debugger reaches the end of your program, the $lang variable is set to the language of the final function of your program, rather than the language of the _exit routine. The _exit routine is written in machine code and is the last function executed by every C or C++ program.

9.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 visibility into a class to set a breakpoint in a member function, to print static data members, or to examine any data member's type. 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.

There can be only one current context. If you set a class scope, you invalidate the current function scope. The opposite is also true: if you set a function scope, you invalidate the current class scope.

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

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

Example 9-2 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) 

9.5 Displaying Class Information

The whatis and print commands display information about 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 the data members, member functions, constructors, destructors, static data members, and 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. However, 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 using one of the following syntaxes:
class class_name::(type signature)
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 because the debugger relaxes these rules to be more helpful to you.

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 9-3 shows the whatis and print commands in conjunction with a class.

Example 9-3 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) 

9.6 Displaying Object Information

The print and whatis commands also 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 once using the following syntax:
print object

You can also display individual object members using the member access operators, period (.) and right arrow (->) in a print command. Static data members are treated as members of objects, so you can reference them by object name. If the debugger stops in a member function of an object, you can access other members of that same object using the this pointer implicitly or explicitly.

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 to otherwise 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 9-4 shows how to use the print and whatis commands to display object information.

Example 9-4 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) 

9.7 Displaying Virtual and Inherited Class Information

When you use the print command to display information on an instance of a derived class, the debugger 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.

Pointers to members of a class are not supported. Example 9-5 shows the format the debugger uses to print information on derived classes.

Example 9-5 Printing Information on a Derived Class

(Ladebug) list 1, 23
      1 class S { 
      2 public: 
      3      int i; 
      4      S() {i=1;}; 
      5      ~S() {i=0;}; 
      6      int foo(); 
      7 }; 
      8 
      9 S::foo() 
     10 { 
     11    i = i + 1; 
     12    return i; 
     13 } 
     14 
     15 class T : public S { 
     16 public: 
     17      int j; 
     18      T() {j=2;}; 
     19      ~T() {i=0;}; 
     20 }; 
     21 
     22 S a; 
     23 T b; 
(Ladebug) stop in main ; run
[#1: stop in main(void) ] 
[1] stopped at [main(void):27 0x1200007f4] 
     27    a.i = 1; 
(Ladebug) print b
class { 
        S = class { 
            i = 1; 
        }; 
        j = 2; 
    } 
(Ladebug) whatis b
class T : S { 
  int j; 
  T (void); 
  ~T (void); 
} b 
(Ladebug) whatis S
class S  { 
  int i; 
  S (void); 
  ~S (void); 
  int foo (void); 
} S 
(Ladebug) 

The whatis b command in this example displays the class type of the object b . Descriptions of derived classes obtained with the whatis command do not include members inherited from base classes. Class T inherits the public members of class S ; in this case, integer variable i and member function foo .

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 Ladebug debugger uses the C++ language rules as defined in The Annotated C++ Reference Manual to determine which member you are specifying.

Example 9-6 shows a case where the expanded syntax is necessary. In this example, two base classes, B and C inherit the public members of base class V. Derived class D inherits the public members of both class B and class C.

Example 9-6 Resolving References to Objects of Multiple Inherited Classes

(Ladebug) whatis D
class D : B, C { 
  D (void); 
  ~D (void); 
  void g (void); 
} D 
(Ladebug) whatis C
class C : virtual V { 
  int ambig; 
  C (void); 
  ~C (void); 
} C 
(Ladebug) whatis B
class B : virtual V { 
  int x; 
  int ambig; 
  B (void); 
  ~B (void); 
  int f (void); 
} B 
(Ladebug) whatis V
class V  { 
  int v; 
  int x; 
  V (void); 
  ~V (void); 
  int f (void); 
} V 
(Ladebug) stop in main; run
[#1: stop in main(void) ] 
[1] stopped at [main(void):59 0x120001024] 
     59   D dinst; 
(Ladebug) next
stopped at [main(void):60 0x120001030] 
     60   V vinst; 
(Ladebug) [Return]
stopped at [main(void):62 0x120001038] 
     62   printf("%d\en", dinst; 
(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 settings of the -noinline_auto compilation switches. 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 option, a breakpoint set on an inline member function may confuse the debugger.

9.8 Modifying Class and Object Data Members

When debugging C++ code, the debugger lets you modify a class's static data members and object data members as you would modify any other program variable by using the debugger's assign command. See the HP Tru64 UNIX Ladebug Debugger Manual for details on how to use the assign command to modify variable values. The following assignments are allowed by the debugger even though they are prohibited in the C++ language:

  • Variables of type const may be assigned a new value.
  • Variables declared as reference types (using a type& type definition) may be assigned a new value. The address referred to by a reference type may not be changed, but the value at that address may be changed.

9.9 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 gives the following error message:


Name is overloaded. 

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

Figure 9-1 A Stack Trace Displaying a Member Function


9.10 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 ways are under the control of the debugger variable $overloadmenu . The default setting of this debugger variable is 0.

One way to specify the desired function name is to choose the correct reference from a selection menu. To enable menu selection of overloaded names, set the $overloadmenu variable to 1. If you use this method, whenever you specify a function name that is overloaded, a menu will appear with all the possible functions; you must select from this menu. In Example 9-7, a breakpoint is set in foo , which is overloaded.

Example 9-7 Resolving Overloaded Functions by Selection Menu

(Ladebug) set $overloadmenu = 1
(Ladebug) class S
class S  { 
  int i; 
  float f; 
  double d; 
  char c; 
  S (void); 
  ~S (void); 
  int foo (void); 
  int foo ( S); 
  int foo ( S *); 
  int foo (int&); 
  int foo (const int&); 
  int foo (float*&); 
  int foo (int, float, char *, unsigned short, long&, const char*&); 
  int foo (const double *); 
  int foo (char); 
  void foo (short); 
  void foo (unsigned); 
  void foo (long); 
  void foo (int, float, char *, unsigned short, long&, char*&); 
  void foo (double); 
  void foo (char *); 
  void foo (const char *); 
} 
(Ladebug) stop in foo
Enter the number of the overloaded function you want 
---------------------------------------------------- 
     1 int foo (void) 
     2 void foo (const char *) 
     3 void foo (char *) 
     4 void foo (double) 
     5 void foo (int, float, char *, unsigned short, long&, char*&) 
     6 void foo (long) 
     7 void foo (unsigned) 
     8 void foo (short) 
     9 int foo (char) 
    10 int foo (const double *) 
    11 int foo (int, float, char *, unsigned short, long&, const char*&) 
    12 int foo (float*&) 
    13 int foo (const int&) 
    14 int foo (int&) 
    15 int foo ( S *) 
    16 int foo ( S) 
    17 None of the above 
---------------------------------------------------- 
10
[#1: stop in int S::foo(const double*) ] 
(Ladebug) 

The other way to resolve ambiguous overloaded function names is to enter the function name with its full type signature. If you prefer this method, set the $overloadmenu variable to 0. To see the possible type signatures for the overloaded function, first display all the declarations of an overloaded function by using one of the following syntax lines:
whatis function
whatis class_name::function

You cannot select a version of an overloaded function that has a type signature containing ellipsis points. Pointers to functions with type signatures that contain parameter list or ellipsis arguments are not supported.

Use one of the displayed function type signatures to refer to the desired version of the overloaded function. If a function has no parameter, include the parameter void as the function's type signature. In Example 9-8 the function context is set to foo() , which is overloaded.

Example 9-8 Resolving Overloaded Functions by Type Signature

(Ladebug) print $overloadmenu
0 
(Ladebug) class S
class S  { 
  int i; 
  float f; 
  double d; 
  char c; 
  S (void); 
  ~S (void); 
  int foo (void); 
  int foo ( S); 
  int foo ( S *); 
  int foo (int&); 
  int foo (const int&); 
  int foo (float*&); 
  int foo (int, float, char *, unsigned short, long&, const char*&); 
  int foo (const double *); 
  int foo (char); 
  void foo (short); 
  void foo (unsigned); 
  void foo (long); 
  void foo (int, float, char *, unsigned short, long&, char*&); 
  void foo (double); 
  void foo (char *); 
  void foo (const char *); 
} 
(Ladebug) whatis foo
Overloaded Function. Functions are: 
int S::foo(void) 
int S::foo(S) 
int S::foo(S*) 
int S::foo(int&) 
int S::foo(const int&) 
int S::foo(float*&) 
int S::foo(int, float, char*, unsigned short, long&, const char*&) 
int S::foo(const double*) 
int S::foo(char) 
void S::foo(short) 
void S::foo(unsigned) 
void S::foo(long) 
void S::foo(int, float, char*, unsigned short, long&, char*&) 
void S::foo(double) 
void S::foo(char*) 
void S::foo(const char*) 
(Ladebug) func foo
Error: foo is overloaded 
(Ladebug) func foo(double)
S::foo(double) in c++over.C line No. 156: 
    156     printf ("void S::foo (double d = %f)\n", d); 
(Ladebug) 

9.11 Setting Breakpoints in Member Functions

When you set a breakpoint in a C function, the debugger confirms the breakpoint 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.

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

This form of specifying a breakpoint in a function uses the static class type information to determine the address of the function at which to set the breakpoint, and presumes that no run-time information from an object is needed.

In Example 9-9 a breakpoint is set for member function bar of class S .

Example 9-9 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 syntax lines:
stop in object.function
stop in objectpointer->function

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

Example 9-10 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 syntax lines:
stop in object.function if & object == this
stop in objectpointer->function if & objectpointer == this

This form of the breakpoint command instructs the debugger to stop in the function only for the object specified by the this pointer. In Example 9-11, which is running the same program as Example 9-10, 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 9-11 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) 

9.11.1 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 command syntax lines:
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 9-12 the breakpoint is set for specific versions of the overloaded function foo .

Example 9-12 Setting Breakpoints in Specific Overloaded Functions

(Ladebug) class S
class S  { 
  int i; 
  float f; 
  double d; 
  char c; 
  S (void); 
  ~S (void); 
  int foo (void); 
  int foo ( S); 
  int foo ( S *); 
  int foo (int&); 
  int foo (const int&); 
  int foo (float*&); 
  int foo (int, float, char *, unsigned short, long&, const char*&); 
  int foo (const double *); 
  int foo (char); 
  void foo (short); 
  void foo (unsigned); 
  void foo (long); 
  void foo (int, float, char *, unsigned short, long&, char*&); 
  void foo (double); 
  void foo (char *); 
  void foo (const char *); 
} 
(Ladebug) whatis foo
Overloaded Function. Functions are: 
int S::foo(void) 
int S::foo(S) 
int S::foo(S*) 
int S::foo(int&) 
int S::foo(const int&) 
int S::foo(float*&) 
int S::foo(int, float, char*, unsigned short, long&, const char*&) 
int S::foo(const double*) 
int S::foo(char) 
void S::foo(short) 
void S::foo(unsigned) 
void S::foo(long) 
void S::foo(int, float, char*, unsigned short, long&, char*&) 
void S::foo(double) 
void S::foo(char*) 
void S::foo(const char*) 
(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 syntax lines:
stop in all function
stop in all class_name::function

In Example 9-13 the breakpoint is set for all versions of the overloaded function foo .

Example 9-13 Setting Breakpoints in All Versions of an Overloaded Function

(Ladebug) class S
class S  { 
  int i; 
  float f; 
  double d; 
  char c; 
  S (void); 
  ~S (void); 
  int foo (void); 
  int foo ( S); 
  int foo ( S *); 
  int foo (int&); 
  int foo (const int&); 
  int foo (float*&); 
  int foo (int, float, char *, unsigned short, long&, const char*&); 
  int foo (const double *); 
  int foo (char); 
  void foo (short); 
  void foo (unsigned); 
  void foo (long); 
  void foo (int, float, char *, unsigned short, long&, char*&); 
  void foo (double); 
  void foo (char *); 
  void foo (const char *); 
} 
(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 setting the breakpoint. In Example 9-14 the breakpoint is set for the overloaded functions by line number.

Example 9-14 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) 

9.11.2 Setting Breakpoints in Constructors and Destructors

To set a breakpoint in a constructor, use one of the following syntax lines:
stop in class_name::class_name [(type_signature)]
stop in class_name [(type_signature)]

The type signature is necessary only to resolve the ambiguity for a constructor that is overloaded. In Example 9-15, a breakpoint is set in a constructor.

Example 9-15 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::S(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 syntax:
stop in ~class_name

In Example 9-16, the breakpoint is set for the destructor.

Example 9-16 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 .

9.12 Calling Overloaded Functions

To call overloaded functions from the debugger, you must set $overloadmenu to 1. Then, use the following call command syntax:
call function ([parameter[,...]])

The debugger will call the function that you select from the menu of overloaded names. In Example 9-17 the overloaded function foo is called.

Example 9-17 Calling an Overloaded Function

(Ladebug) set $overloadmenu = 1
(Ladebug) call foo(15)
Enter the number of the overloaded function you want 
---------------------------------------------------- 
     1 void foo (const char *) 
     2 void foo (char *) 
     3 void foo (double) 
     4 void foo (float) 
     5 void foo (unsigned) 
     6 void foo (long) 
     7 void foo (short) 
     8 int foo (const double *) 
     9 int foo (int, float) 
    10 int foo (float*&) 
    11 int foo (const int&) 
    12 int foo (int&) 
    13 int foo (char) 
    14 int foo ( S *) 
    15 int foo ( S) 
    16 None of the above 
---------------------------------------------------- 
6
global foo (long):              15 
(Ladebug) 

9.13 Using Typecasts to Display Program Expressions

When debugging C++ programs, the debugger interprets casts as defined in the International C++ Standard. A cast has the following syntax:


(type) variable

The type is the type the debugger will use to interpret the program variable variable. The debugger does not allow cast conversion from an unsigned short type to a long int type.

In Example 9-18, a variable of type float is interpreted as an integer.

Example 9-18 Using a Cast to Perform Data Coercion

(Ladebug) whatis f
float f 
(Ladebug) print f
3.2 
(Ladebug) print (int)f
3 
(Ladebug) 

You can also use casts to interpret a base class object as a derived class object, or to interpret a derived class object as a base class object. In Example 9-19, the class derived inherits the public members of class base . A pointer to an object of class derived is declared to be a pointer to an object of class base . A cast is used to display the object's contents using the correct class type.

Example 9-19 Using Casts on a Derived Class Object

(Ladebug) whatis bvar
 base * bvar 
(Ladebug) whatis derived
class derived : base { 
  int i; 
  derived (void); 
  ~derived (void); 
  virtual int foo (void); 
} derived 
(Ladebug) whatis base
class base  { 
  int j; 
  base (void); 
  ~base (void); 
  virtual int foo (void); 
} base 
(Ladebug) print *bvar
class { 
        j = 0; 
    } 
(Ladebug) print *(derived *)bvar
class { 
        base = class { 
            j = 0; 
        }; 
        i = 4195376; 
    } 
(Ladebug) 

Example 9-20 shows how to use a type transfer to interpret a variable of type float as an integer.

Example 9-20 Using a Cast with Pointer Notation to Perform a Type Transfer

(Ladebug) print &f
0x11ffffe84 
(Ladebug) print *(int *)&f
1078774989 
(Ladebug) 

You can also perform the type transfer shown in this example by using C++ reference types. Example 9-21 shows how to use reference type notation to perform a type transfer.

Example 9-21 Using a Cast with Reference Type Notation to Perform a Type Transfer

(Ladebug) print &f
0x11ffffe84 
(Ladebug) print (int&)f
1078774989 
(Ladebug) 

9.14 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 this section.

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

Example 9-22 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 9-23.

Example 9-23 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) 

As shown in Example 9-24, while inside the function template, you can set or ask for the current function context, and the instantiated function will be displayed.

Example 9-24 Displaying the Current Function Context for a Function Template

(Ladebug) func
compare(int, int) in c++functemp.C line No. 4: 
      4         if (t1 < t2) return 0; 
(Ladebug) 

For class templates, you cannot use the whatis command with the template name, but you can use the whatis command on a specific instantiation of the class template. This is the instantiated name that the debugger prints when it encounters variables of the template class type. Example 9-25 displays the class definition of a particular instantiation of a parameterized stack.

Example 9-25 Displaying an Instantiated Class Template

(Ladebug) list 1, 24
      1 #include <iostream.h> 
      2 
      3 template <class T, int size> class stack { 
      4         T s[size]; 
      5         int top; 
      6 public: 
      7         stack() { top = 0; } 
      8         void push(T item) 
      9              { 
     10               s[top++] = item; 
     11              } 
     12         T pop(); 
     13 }; 
     14 
     15 template<class T, int size> T stack<T,size>::pop() 
     16 { 
     17         return s[--top]; 
     18 } 
     19 
     20 stack<int,10*10> S; 
     21 stack<double,10> F; 
     22 
     23 #pragma define_template stack<int,100> 
     24 #pragma define_template stack<double,10> 
(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) 

Similarly, as shown in Example 9-26, you can use the whatis S command. The instance of S is displayed as stack<int,100> rather than just S .

Example 9-26 Displaying an Instantiated Class Template

(Ladebug) whatis S
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); 
} S 
(Ladebug) 

As shown in Example 9-27, you can set breakpoints in template functions and ask for the current function context while inside a template function.

Example 9-27 Setting Breakpoints in an Instantiated Class Function

(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) 

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 9-28 and Example 9-29.

Example 9-28 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) 

Example 9-29 Alternate Method of Setting Current Class Scope

(Ladebug) class stack<int,100>
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
[#5: stop in stack<int,100>::push(int) ] 
(Ladebug) 

Additional limitations for debugging templates include:

  • You cannot specify a template by name in a debugger command. You must use the name of the instantiation of the template.
  • In C++, expressions in the instantiated template name can be full constant expressions such as stack<double,f*10> . This form is not yet supported in the Ladebug debugger; you must enter the value of the expression (for example, if f is 10 in the stack example, you must enter 100).
  • Setting a breakpoint at a line number that is inside a template function will not necessarily stop at all instantiations of the function within the given file, but only a randomly chosen few. This limitation is due to the limited symbol information generated by the compiler for templates.

9.15 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.

9.15.1 Setting Breakpoints in Exception Handlers

As shown in Example 9-30, you can set a breakpoint in an exception handler by setting a breakpoint 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.

Example 9-30 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) 

As this example shows, you can also set breakpoints in C++ functions used to handle exceptions as follows:

terminate Gains control when any unhandled exception occurs
unexpected Gains control when a function containing an exception specification tries to throw an exception that is not in the exception specification

9.15.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.

9.16 Advanced Program Information: Verbose Mode

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

  • Derived classes
  • Virtual pointer tables for virtual functions
  • Compiler-generated function members
  • Compiler-generated temporary variables
  • Implicit parameters in member functions

By setting the $verbose debugger variable to 1, you can request that this information be printed in subsequent debugger responses. This section explains the normally suppressed information that the debugger provides if the $verbose debugger variable is set to 1.

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. When the $verbose debugger variable is set to 1, you can see these members and reference them as you would reference any other member function.

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 are specifying a version of an overloaded function by entering its type signature and the $verbose variable is set to 1, you must include these parameters.

If the $verbose debugger variable is set to 0, you should not include the compiler-generated parameters in the type signature. 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 9-31 prints class information using the whatis command when the $verbose variable is set to 1.

Example 9-31 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 base pointer bptr . The bptr pointer is printed after the class member information. Example 9-32 shows a print command that displays the bptr pointer information when the $verbose variable is set to 1.

Example 9-32 Printing Base Pointer Information

(Ladebug) stop at 66
[#1: stop at "c++multinher.C":66 ] 
(Ladebug) run
0 
1 
3 
[1] stopped at [main(void):66 0x1200010b8] 
     66   printf("%d\n", dinst.f()); 
(Ladebug) whatis dinst
class D : B, C { 
  D (void); 
  ~D (void); 
  void g (void); 
} dinst 
(Ladebug) print dinst
class { 
        B = class { 
            V = class { 
                v = 1; 
                x = 3; 
            }; 
            x = 2; 
            ambig = 2; 
        }; 
        C = class { 
            V = class { 
                v = 1; 
                x = 3; 
            }; 
            ambig = 3; 
        }; 
    } 
(Ladebug) set $verbose = 1
(Ladebug) print dinst
class { 
        B = class { 
            V = class { 
                v = 1; 
                x = 3; 
            }; 
            x = 2; 
            ambig = 2; 
            _\|_bptr = 0x10001168; 
        }; 
        C = class { 
            V = class { 
                v = 1; 
                x = 3; 
            }; 
            ambig = 3; 
            _\|_bptr = 0x1000116c; 
        }; 
        _\|_bptr = 0x10001168; 
    } 
(Ladebug) 

When a class appears on the stack and the $verbose debugger variable is set to 0, the class's members are represented with an ellipsis {...}. When the $verbose debugger variable is set to 1, the debugger prints all members of classes on the stack trace.

Example 9-33 shows that member functions on the stack trace are printed with their this pointer value explicitly when the $verbose variable is set to 1.

Example 9-33 Printing a Stack Trace in Verbose Mode

(Ladebug) where
>0  0x120000789in ((S*)0x10000010)->bar(t={ ... }) c++exv.C:42 
#1  0x1200008bc in main() c++exv.C:50 
(Ladebug) set $verbose = 1
(Ladebug) where
>0  0x120000789c in ((S*)0x10000010)->bar(this=0x10000010, t=class { 
        i = 1; 
        j = 2; 
        _\|_vptr = 0x10000960; 
        virtual T::tbar = (function [0x400290]); 
    }) c++exv.C:42 
#1  0x1200008bc in main() c++exv.C:50 
(Ladebug) 

9.17 Reducing Object File Size During Debugging

With Version 5.6 and higher, the compiler reduces the amount of debugging information in object files when -g is specified. If the debugger describes a class as <opaque> or lacks debugging information, you may need to compile using the -gall option. See Section 9.17.1 for information on how to use the -gall option.

Before Version 5.6, debug information for classes and structs was generated whenever they were processed by the compiler. Thus, debug information for particular classes and structs defined in header files often appeared in multiple object files, which increased the overall size of objects and executables. To reduce debuggable executable size, the compiler now attempts to generate debug information for a particular class or struct as few times as possible. Describing classes and structs this way has tremendous savings; reductions in executable sizes of up to 70% have been observed for some applications.

In most cases, the object file that contains the debug information for a particular class is linked into the executable and is available to the debugger. However, there are some situations where this might not be the case:

  • A class description may be in a library object file that is not linked into the final debuggable executable.
  • A class description may be in a different shared object than the shared object you may be trying to debug.
  • The object file that would have contained the class description does not because it was compiled without the -g option.

9.17.1 Using the -gall and -gall_pattern Options

If Ladebug describes a class or struct as <opaque>, you should compile the file in which the class or struct is described as <opaque> with the -gall option. Compiling a file with the -gall option generates debug information for all classes and structs that appear in that source file.

You can generate complete debugging information when compiling a particular file by specifying the -gall option on the command line. You can also define a pattern using the -gall_pattern option so that a file is compiled with complete debugging only if the file name matches the pattern. Specifying -gall_pattern lets you compile all files with the same command and still have particular files compiled with the -gall option to generate complete debugging information. You can specify a pattern in two ways:

  • Specify -gall_pattern on the command line
  • Define environment variable CXX_GALL_PATTERN as the pattern

The -gall_pattern option accepts a list of file names (with each file name separated by a comma or colon), filename patterns, or a combination of a file name list and filename patterns. See fnmatch(3) for valid filename patterns.

If you specify -gall , -g is assumed. The -gall_pattern option is ignored unless -g , -g2 , or -g3 is also specified.

Example:

The following example shows how opaque_class is initially described as <opaque> by Ladebug, and how this is remedied by compiling the file opaque_file.cxx with the -gall option:


Welcome to the Ladebug Debugger Version 4.0-48 
...
 
     object file name: opaque_file 
     Reading symbolic information ...done 
     (Ladebug) # First, note the class is <opaque> 
     (Ladebug) whatis opaque_class 
     class opaque_class <opaque> 
 
     Information:  An <opaque> type was presented during execution of the previous 
     command.  For complete type information on this symbol, recompilation of the 
     program will be necessary.  Consult the compiler man pages for details on 
     producing full symbol table information using the -g (and -gall) for cxx) 
     flags. 
 
     (Ladebug) # Next, find the offending file with Ladebug's "file" command 
     (Ladebug) file 
     opaque_file.cxx 
     (Ladebug) # Quit to recompile opaque_file.cxx with the -gall option 
     (Ladebug) quit 

Recompile the file opaque_file.cxx with the -gall option in one of the following ways:

  • Supply -gall on the command line (useful for hand builds):


         cxx -g -gall -O0 opaque_file.cxx 
    
  • Supply -gall_pattern (useful for a generic rule in a makefile):


         cxx -g -gall_pattern opaque_file.cxx -O0 opaque_file.cxx 
    

    In a makefile, this may look something like the following:


         CXX      = cxx 
         CXXFLAGS = -g -gall_pattern opaque_file.cxx -O0 
         $(CXX) $(CXXFLAGS) opaque_file.cxx 
    
  • Supply the pattern to the environment variable CXX_GALL_PATTERN (useful for not modifying the makefile):


         setenv CXX_GALL_PATTERN "opaque_file.cxx" 
         cxx -g -O0 opaque_file.cxx 
    

Here are some alternate methods, showing the pattern capabilities of the -gall_pattern option:

  • Compile both opaque_file.cxx and another_file.cxx with -gall_pattern :


    cxx -g -gall_pattern opaque_file.cxx,another_file.cxx -O0 opaque_file.cxx 
    
  • Compile any file starting with opaque or another with -gall_pattern :


         setenv CXX_GALL_PATTERN "opaque*.cxx:another*.cxx" 
         cxx -g -O0 opaque_file.cxx 
    

9.17.2 Hints for Using the -gall Option Effectively

For best debugging and smallest objects and executables, we recommend that you use the -gall option selectively as follows:

  • Use the -gall option only when you see a class or struct described as <opaque>.
  • Try to compile as few files with -gall as possible.
  • Choose these few files wisely. If you are seeing <opaque> types in only one file, try compiling only that file with the -gall option. If you are seeing <opaque> types in many files, try compiling a file that includes many project header files with the -gall option. Doing so will increase the chance that <opaque> messages will disappear for the entire application.


Previous Next Contents Index
About PDF files: The PDF files on this site can be read online or printed using Adobe® Acrobat® Reader. If you do not have this software on your system, you may download it from Adobe's website.
Privacy statement Using this site means you accept its terms C++ support
© 2008 Hewlett-Packard Development Company, L.P.