Re: How to setup a breakpoint on constructor

Michael Elizabeth Chastain
2004-07-15T11:31:11Z

Hi Roland,

Would you like to try a makeshift and experimental patch?

ftp://ftp.shout.net/pub/users/mec/ctor-dtor-base.patch

(Patch attached to this message too).

You have to apply this to the libiberty/ subdirectory of a
recent gdb, such as gdb 6.1.1, and then rebuild gdb.

There's a lot of doco in the patch but it rambles.
The basic idea is that I patched the demangler so that
it demangles the different ctor's with different names:

A::A()
A::A$base()
A::A$allocate()

I don't know if it's a good idea for gdb to work this way,
but I think it's worth getting some user feedback.

Testing: I haven't run the test suite with this.
I did test a small program (testsuite/gdb.cp/derivation.cc)
and it works the way I think it should.

Michael C

===

gdb has a problem with g++ constructors and destructors.
This patch resolves the problem in a simple but crude way.

The problem is described in the gdb PROBLEMS file:

gdb/1091: Constructor breakpoints ignored
gdb/1193: g++ 3.3 creates multiple constructors: gdb 5.3 can't set breakpoints

When gcc 3.x compiles a C++ constructor or C++ destructor, it generates
2 or 3 different versions of the object code. These versions have
unique mangled names (they have to, in order for linking to work), but
they have identical source code names, which leads to a great deal of
confusion. Specifically, if you set a breakpoint in a constructor or a
destructor, gdb will put a breakpoint in one of the versions, but your
program may execute the other version. This makes it impossible to set
breakpoints reliably in constructors or destructors.

gcc 3.x generates these multiple object code functions in order to
implement virtual base classes. gcc 2.x generated just one object code
function with a hidden parameter, but gcc 3.x conforms to a multi-vendor
ABI for C++ which requires multiple object code functions.

This patch changes the demangler so that it demangles different
linkage names back to different source code names, so that gdb and
gdb users are no longer confused by several object code functions
with identical source code names.

Before this patch:

08048544 _ZN1AC1Ev A::A()
080486e2 _ZN1AC2Ev A::A()

After this patch:

08048544 _ZN1AC1Ev A::A()
080486e2 _ZN1AC2Ev A::A$base()

You still have to understand that g++ emits two (sometimes three)
copies of each constructor.

The constructor with "C1" in the linkage name is the complete
object constructor. Your program calls this constructor when it
creates an object whose complete type is A, such as "new A".

The constructor with "C2" in the linkage name is the base object
constructor. Your program calls this constructor when it creates
an object derived from A, such as "new B". Your program does *not*
call the base object constructor for "new A".

The difference between type "C1" and type "C2" has to do with
virtual base classes. In C++, the constructor for the complete
object initializes all the virtual bases, and constructors for
base classes do not initialize any virtual bases.

Here is an example:

class A { ... };
class B1 : virtual public A { ... };
class B2 : virtual public A { ... };
class C : public B1, public B2 { ... };

When your program creates a C with "new C", your program calls the type
C1 constructor for C::C. C::C (type C1) calls A::A (type C2) to
initialize the virtual base. Then C::C (type C1) calls B1::B1 (type C2)
and B2::B2 (type C2) to initialize the normal bases.

If you understand virtual bases, all this should make sense.
If you don't understand virtual bases, just remember the simple rule:
"new Foo" calls Foo::Foo (type C1), and everything that Foo::Foo
for all its bases is of type C2.

There is also a constructor of type "C3". I have never actually
seen one of these, but it would be marked "Foo::Foo$allocate".

After you apply this patch and rebuild gdb, all the constructors
with names 'Foo::Foo' are the complete object constructors.
'Foo::Foo$base' are the base object constructors, and
'Foo::Foo$allocate' are the allocating object constructors.

All three constructors are compiled from the same source code.
To set a breakpoint on the constructor, you actually have to set
two or three breakpoints (usually just two). If you take the
short-cut and just break on 'Foo::Foo', you will get breakpoints
whenever 'new Foo' happens, but not when 'new Bar' happens if
Bar is derived from Foo. You need to break on 'Foo::Foo$base'
to get breakpoints for that.

Michael C

===

Here is a typescript of a gdb session with this patch.
Note how 'A::A()' is called for an object of type A,
and 'A::A$base()' is called for objects of types D, E, F, and G,
which have A as a base class.

Script started on Thu Jul 15 07:14:52 2004

[mec.gnu@berman HEAD]$ ./gdb/gdb ~/tmp/a.out
GNU gdb 2004-07-14-cvs
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".

(gdb) break 'A::A^G
A::A$base() A::A()

(gdb) break 'A::A()'
Breakpoint 1 at 0x8048547: file derivation.cc, line 7.

(gdb) break 'A::A$base()'
Breakpoint 2 at 0x80486e5: file derivation.cc, line 7.

(gdb) run
Starting program: /berman/home/mec.gnu/tmp/a.out

Breakpoint 1, A (this=0xbffff888) at derivation.cc:8
8 a=1;

(gdb) backtrace
#0 A (this=0xbffff888) at derivation.cc:8
#1 0x08048481 in main () at derivation.cc:203

(gdb) list 203
198
199
200 int main(void)
201 {
202
203 A a_instance;
204 B b_instance;
205 C c_instance;
206 D d_instance;
207 E e_instance;

(gdb) cont
Continuing.

Breakpoint 2, A (this=0xbffff850) at derivation.cc:8
8 a=1;

(gdb) backtrace
#0 A (this=0xbffff850) at derivation.cc:8
#1 0x0804859d in D (this=0xbffff850) at derivation.cc:58
#2 0x080484ae in main () at derivation.cc:206

(gdb) cont
Continuing.

Breakpoint 2, A (this=0xbffff830) at derivation.cc:8
8 a=1;

(gdb) backtrace
#0 A (this=0xbffff830) at derivation.cc:8
#1 0x080485eb in E (this=0xbffff830) at derivation.cc:74
#2 0x080484bd in main () at derivation.cc:207

(gdb) cont
Continuing.

Breakpoint 2, A (this=0xbffff810) at derivation.cc:8
8 a=1;

(gdb) backtrace
#0 A (this=0xbffff810) at derivation.cc:8
#1 0x08048639 in F (this=0xbffff810) at derivation.cc:90
#2 0x080484cf in main () at derivation.cc:208

(gdb) cont
Continuing.

Breakpoint 2, A (this=0xbffff7e0) at derivation.cc:8
8 a=1;

(gdb) backtrace
#0 A (this=0xbffff7e0) at derivation.cc:8
#1 0x08048687 in G (this=0xbffff7e0) at derivation.cc:108
#2 0x080484e1 in main () at derivation.cc:209

(gdb) cont
Continuing.

Program exited normally.

(gdb) quit
[mec.gnu@berman HEAD]$ exit

Script done on Thu Jul 15 07:15:50 2004
===

2004-07-15 Michael Chastain <mec.gnu@mindspring.com>

* cp-demangle.c (d_print_comp) Return unique names for
ctor and dtor names by decorating them with "$base",
"$allocate", and "$delete".

Index: cp-demangle.c
===================================================================
RCS file: /cvs/src/src/libiberty/cp-demangle.c,v
retrieving revision 1.51
diff -c -3 -p -r1.51 cp-demangle.c
*** cp-demangle.c 28 Jun 2004 18:01:41 -0000 1.51
--- cp-demangle.c 15 Jul 2004 10:53:01 -0000
*************** d_print_comp (dpi, dc)
*** 2978,2988 ****
--- 2978,3012 ----

case DEMANGLE_COMPONENT_CTOR:
d_print_comp (dpi, dc->u.s_ctor.name);
+ switch (dc->u.s_ctor.kind)
+ {
+ case gnu_v3_complete_object_ctor:
+ /* no decoration */
+ break;
+ case gnu_v3_base_object_ctor:
+ d_append_string_constant (dpi, "$base");
+ break;
+ case gnu_v3_complete_object_allocating_ctor:
+ d_append_string_constant (dpi, "$allocate");
+ break;
+ }
return;

case DEMANGLE_COMPONENT_DTOR:
d_append_char (dpi, '~');
d_print_comp (dpi, dc->u.s_dtor.name);
+ switch (dc->u.s_dtor.kind)
+ {
+ case gnu_v3_deleting_dtor:
+ d_append_string_constant (dpi, "$delete");
+ break;
+ case gnu_v3_complete_object_dtor:
+ /* no decoration */
+ break;
+ case gnu_v3_base_object_dtor:
+ d_append_string_constant (dpi, "$base");
+ break;
+ }
return;