RTTI does not work stable in LLDB.

Hello,
I’m observing very strange LLDB behavior: it does not always shows a correct dynamic type when I ask for.

Originally I was working with LLDB 3.9, but it looks like trunk version behaves the same strange way.

I was able to capture this behavior in a small code snippet:

#include
#include

using namespace std;

struct base_type { virtual ~base_type(){} };

template <class T1, class T2, unsigned SIZE>
struct derived0 : base_type {};

template <class T1, class T2>
struct derived1 : base_type {};

int main(int argc, char ** argv) {

base_type * bptr0 = new derived0<int, int, 1024>();
base_type * bptr1 = new derived1<int, int >();

cout << typeid(*bptr0).name() << endl;
cout << typeid(*bptr1).name() << endl;

return 0;
}

lldb --version
lldb version 5.0.0 (http://llvm.org/svn/llvm-project/lldb/trunk revision 293398)
clang revision 293398
llvm revision 293398

Testing in LLDB:
(lldb) break set --file main.cpp --line 22

(lldb) expression -d no-run – bptr1
(derived1<int, int> *) $2 = 0x0000000000614c40

(lldb) expression -d no-run – bptr0
(base_type *) $3 = 0x0000000000614c20

Can someone explain me why for bptr0 I dont get a derived0<int, int, 1024> * as I expected?

Thanks,
Roman

You have found a bug. It should be reporting this correctly but it isn’t. I verified it fails on MacOSX.

Greg Clayton

Yes, that was my thought.

FYI, checked in GDB: it’s working correctly on this testcase showing correct dynamic type in both cases.

I am looking at this now. I will let you know what I find.

Greg

So I found the problem. This is a compiler bug. The DWARF for this type looks like:

0x000065da: TAG_structure_type [112] *
AT_containing_type( {0x0000000000006628} )
AT_name( “derived0<int, int, 1024>” )
AT_byte_size( 0x08 )
AT_decl_file( “/private/tmp/main.cpp” )
AT_decl_line( 9 )

But all of the type info in the symbol table has has the type named as "derived0<int, int, 1024u>”. Note the extra “u” that follows 1024. This stops LLDB from being able to lookup the type correctly so we can show the dynamic type. In LLDB we check the first pointer inside of a class to see if it is a symbol whose name is “vtable for TYPENAME”. If it is, we lookup the type “TYPENAME” to find it. In this case we try to lookup "derived0<int, int, 1024u>” and we fail since the DWARF has it as "derived0<int, int, 1024>”.

I have filed a radar on the compiler here at Apple for the fix.

Greg

So is LLDB expecting the name in the DWARF info to match the demangled name of the vtable pointer? The DWARF spec does not really specify what the name of a template instantiation should be, and in particular does not want to specify whether it matches any given demangler’s opinion of the name.

–paulr

I confirm GCC4.8 puts derived0<int, int, 1024u> into DWARF and LLDB correctly identifies dynamic type.

Yeah, when doing dynamic type resolution, we look at the first pointer inside the pointer and see if it resolves to a virtual table symbol. If it does, we extract the class name from the demangled symbol name and try to look up. GDB does the same thing. All debuggers do AFAIK.

If the DWARF specified the vtable address in the DWARF on the class definition this would help, but without that the only thing we can really do is to try and figure out the class and look it up by name. Also, even if this is added to future DWARF, it doesn’t fix the problem that we have many compilers that don’t have the info so we would still need to do what we do.

If anyone has any better ideas I am all ears?

Greg

Doesn’t the DWARF have a record for the VTable itself? I know PDB does, you can look up the class name through the VTable debug info record rather than trying to demangle the name.

It just says where it is inside the class itself, not what the value of the vtable pointer is:

0x00006628: TAG_structure_type [112] *
AT_containing_type( {0x0000000000006628} )
AT_name( “base_type” )
AT_byte_size( 0x08 )
AT_decl_file( “/private/tmp/main.cpp” )
AT_decl_line( 6 )

0x00006634: TAG_member [4]
AT_name( “_vptr$base_type” )
AT_type( {0x0000000000004a41} ( __vtbl_ptr_type* ) )
AT_data_member_location( 0x00 )
AT_artificial( true )

Just says “the vtable is at offset zero inside the class”. Not helpful for reading any vtable pointer and trying to figure out which class it belongs to.

Greg

It’s not practical for the DWARF to try to identify the actual address of the vtable; that address might not be available.

it seems like we could hang onto the linkage_name of the vtable though, somewhere, so you wouldn’t be relying on the demangler you have available at runtime to produce the same string that the compiler did at compile time. The symbolic name of the vtable should be unambiguous (one hopes!) but not depend on the existence of the vtable in any particular place.

Doesn’t solve the problem for today’s compilers, granted.

–paulr

P.S. It would be helpful to have these things come up before the next rev of the spec is frozen. Just sayin’. J

We had just identified this today and realized it was a problem so we had no idea there was a problem until today. Debuggers have been doing this pretty reliably for the past 15 years, so it was never anything we actually needed extra support for since it was so easy to do.

Greg

Yes, I do get that it was just unfortunate timing. Sorry for failing at being light-hearted.

I suspect the compiler can be persuaded to emit a name consistent with the demangling of the vtable name. Despite being the way-things-have-worked for a long time, it still seems moderately fragile, especially in the face of various compiler-list debates about what the name actually should contain.

–paulr

Hello,
I just found out that sometimes I don’t get correct dynamic type in LLDB even if I compile with g++. How can I get typeinfo/vtable dump from LLDB to check if it is still the same name matching issue?

Stop where dynamic typing fails, and take the pointer that is failing to be properly typed and do:

(lldb) memory read --format address my_ptr

Then look at the first entry that is in the output and it should be "vtable for " and take all the characters that follow this and are before the " + XXX" and check to see if LLDB knows about this type.

If we use your previous source:

#include <iostream>
#include <typeinfo>

using namespace std;

struct base_type { virtual ~base_type(){} };

template <class T1, class T2, unsigned SIZE>
struct derived0 : base_type {};

template <class T1, class T2>
struct derived1 : base_type {};

int main(int argc, char ** argv) {

    base_type * bptr0 = new derived0<int, int, 1024>();
    base_type * bptr1 = new derived1<int, int >();

    cout << typeid(*bptr0).name() << endl;
    cout << typeid(*bptr1).name() << endl;

    return 0;
}

If we compile this into "a.out":

% lldb a.out
(lldb) b /return 0/
(lldb) r
(lldb) memory read --format address bptr0
0x1001002f0: 0x0000000100002120 vtable for derived0<int, int, 1024u> + 16
....

We now take all text past the "vtable for " and before the " + 16" and lookup the type by name:

(lldb) image lookup -t "derived0<int, int, 1024u>"

Note this doesn't work, but if we remove the 'u' from 1024 it does work:

(lldb) image lookup -t "derived0<int, int, 1024>"
Best match found in /tmp/a.out:
id = {0x000065da}, name = "derived0<int, int, 1024>", byte-size = 8, decl = main.cpp:9, compiler_type = "class derived0 : public base_type {
}"

Thanks Greg,

So here is another case when LLDB fails to resolve dynamic type. Compiled with G++5.4 on Ubuntu 16.04.

Here I want to get dynamic type for some variable apb_memories

(lldb) expr -d no-run – apb_memories

(sc_core::sc_object *) $3 = 0x0000000000cb6aa8

(lldb) memory read --format address apb_memories
0x00cb6aa8: 0x00000000004e33f8 test_design`vtable for demo::apb_memory<sc_dt::sc_uint<32>, sc_dt::sc_uint<32>, 1024u> + 24

(lldb) image lookup -t “demo::apb_memory<sc_dt::sc_uint<32>, sc_dt::sc_uint<32>, 1024u>”

(lldb) image lookup -t “apb_memory<sc_dt::sc_uint<32>, sc_dt::sc_uint<32>, 1024u>”
Best match found in …
id = {0x0002cc4b}, name = “apb_memory<sc_dt::sc_uint<32>, sc_dt::sc_uint<32>, 1024u>”, qualified = “demo::apb_memory<sc_dt::sc_uint<32>, sc_dt::sc_uint<32>, 1024>”, byte-size = 27384, decl = apb_memory.h:15, compiler_type = “class apb_memory : public sc_core::sc_module, public demo::clk_rstn_sif, public demo::apb_if<sc_dt::sc_uint<32>, sc_dt::sc_uint<32> > {
…}”

Looks like in that case the problem is with namespace specifier. G++ did not put it into debug info.

I hope it will be fixed soon, at least for Clang+LLDB combo. Probably you need to write a unit-test that will check typeinfo against debug info for various scenarios.

Thanks Greg,

So here is another case when LLDB fails to resolve dynamic type. Compiled with G++5.4 on Ubuntu 16.04.

Here I want to get dynamic type for some variable apb_memories

(lldb) expr -d no-run – apb_memories

(sc_core::sc_object *) $3 = 0x0000000000cb6aa8

(lldb) memory read --format address apb_memories
0x00cb6aa8: 0x00000000004e33f8 test_design`vtable for demo::apb_memory<sc_dt::sc_uint<32>, sc_dt::sc_uint<32>, 1024u> + 24

(lldb) image lookup -t “demo::apb_memory<sc_dt::sc_uint<32>, sc_dt::sc_uint<32>, 1024u>”

(lldb) image lookup -t “apb_memory<sc_dt::sc_uint<32>, sc_dt::sc_uint<32>, 1024u>”
Best match found in …
id = {0x0002cc4b}, name = “apb_memory<sc_dt::sc_uint<32>, sc_dt::sc_uint<32>, 1024u>”, qualified = “demo::apb_memory<sc_dt::sc_uint<32>, sc_dt::sc_uint<32>, 1024>”, byte-size = 27384, decl = apb_memory.h:15, compiler_type = “class apb_memory : public sc_core::sc_module, public demo::clk_rstn_sif, public demo::apb_if<sc_dt::sc_uint<32>, sc_dt::sc_uint<32> > {
…}”

If you look at the “qualified” name in the type info you dumped, we see a ‘u’ mismatch on the last 1024:

qualified = “demo::apb_memory<sc_dt::sc_uint<32>, sc_dt::sc_uint<32>, 1024>”

Note the missing ‘u’. In the case of GCC and everything linux, we manually create the accelerator tables by indexing the DWARF manually. I am guessing that since the “qualified” name is wrong, this is what is keeping us from finding it. So this is the same problem, though this one is an LLDB bug if the qualified name is dropping the ‘u’. We are presumably using the same code path that clang uses (which is causing this bug) to generate the qualified name.

Greg

Yes, you’re right. When I replaced unsigned with signed int it works properly.

Roman,

Thanks for verifying. I am trying to see how far this goes.

It would be interesting to look at the GCC DWARF for this. If you can attach a copy of the binary, I would like to take a look to see if the DWARF itself has any offending type info where it sometimes has the ‘u’ and other times doesn’t. We know and understand that clang does this, but we don’t know if GCC does this as well and it would be good to know.

Greg