Hi Filipe,
glad to hear it works on a recent clang.
My bet is that the culprit is actually the compiler, not lldb.
The way the data formatter works is by accessing the type information for a specific child and extracting type information for the map out of it. This is what the DWARF emitted by old clang has to say about the type we use:
0x0000bff4: TAG_class_type [35]
AT_name( “__tree_node” )
AT_declaration( 0x01 )
0x0000bffa: TAG_class_type [35]
AT_name( “__tree_node_base” )
AT_declaration( 0x01 )
Not much. OTOH, the newer clang:
0x0000c6cc: TAG_class_type [13] *
AT_name( “__tree_node<std::__1::pair<int, int>, void *>” )
AT_byte_size( 0x28 )
AT_decl_file( “/usr/lib/c++/v1/__tree” )
AT_decl_line( 595 )
0x0000c6d5: TAG_inheritance [31]
AT_type( {0x00009b4a} ( __tree_node_base<void *> ) )
AT_data_member_location( +0 )
AT_accessibility( DW_ACCESS_public )
0x0000c6de: TAG_typedef [14]
AT_type( {0x0000d9bd} ( pair<int, int> ) )
AT_name( “value_type” )
AT_decl_file( “/usr/lib/c++/v1/__tree” )
AT_decl_line( 600 )
0x0000c6ea: TAG_member [15]
AT_name( “_value” ) <---- this is what we use, and the type information is good enough
AT_type( {0x0000c6de} ( value_type ) )
AT_decl_file( “/usr/lib/c++/v1/__tree” )
AT_decl_line( 602 )
AT_data_member_location( +28 )
AT_accessibility( DW_ACCESS_public )
0x0000c6fa: TAG_subprogram [17] *
AT_name( “__tree_node” )
AT_decl_file( “/usr/lib/c++/v1/__tree” )
AT_decl_line( 611 )
AT_declaration( 0x01 )
AT_external( 0x01 )
AT_accessibility( DW_ACCESS_public )
AT_explicit( 0x01 )
0x0000c706: TAG_formal_parameter [8]
AT_type( {0x00016c30} ( __tree_node<std::__1::pair<int, int>, void > ) )
AT_artificial( 0x01 )
0x0000c70c: TAG_formal_parameter [18]
AT_type( {0x00016c3a} ( const value_type& ) )
0x0000c711: NULL
0x0000c712: TAG_template_type_parameter [10]
AT_type( {0x0000d9bd} ( pair<int, int> ) )
AT_name( “_Tp” )
0x0000c71b: TAG_template_type_parameter [10]
AT_type( {0x0000d646} ( * ) )
AT_name( “_VoidPtr” )
0x0000c724: NULL
The type information for _value refers to:
0x0000d9bd: TAG_class_type [6] *
AT_name( “pair<int, int>” )
AT_byte_size( 0x08 )
AT_decl_file( “/usr/lib/c++/v1/utility” )
AT_decl_line( 212 )
which is the right type, and hence the test works 
Without type information there is not much that we can do. We could, of course, try and make up the type manually by string concatenation - that is done in the libstdcpp map formatter, but this latter approach is much safer when it works. If you look into the other formatter, you’ll see we need to play some tricks to keep things working:
we need this function as a temporary workaround for rdar://problem/10801549
which prevents us from extracting the std::pair<K,V> SBType out of the template
arguments for _Rep_Type _M_t in the map itself - because we have to make up the
typename and then find it, we may hit the situation were std::string has multiple
names but only one is actually referenced in the debug information. hence, we need
to replace the longer versions of std::string with the shorter one in order to be able
to find the type name
def fixup_class_name(self, class_name):
logger = Logger.Logger()
if class_name == ‘std::basic_string<char, std::char_traits, std::allocator >’:
return ‘std::basic_string’,True
if class_name == ‘basic_string<char, std::char_traits, std::allocator >’:
return ‘std::basic_string’,True
if class_name == ‘std::basic_string<char, std::char_traits, std::allocator >’:
return ‘std::basic_string’,True
if class_name == ‘basic_string<char, std::char_traits, std::allocator >’:
return ‘std::basic_string’,True
return class_name,False
def update(self):
logger = Logger.Logger()
try:
self.count = None
we will set this to True if we find out that discovering a node in the map takes more steps than the overall size of the RB tree
if this gets set to True, then we will merrily return None for any child from that moment on
self.garbage = False
self.Mt = self.valobj.GetChildMemberWithName(‘_M_t’)
self.Mimpl = self.Mt.GetChildMemberWithName(‘_M_impl’)
self.Mheader = self.Mimpl.GetChildMemberWithName(‘_M_header’)
map_type = self.valobj.GetType()
if map_type.IsReferenceType():
logger >> “Dereferencing type”
map_type = map_type.GetDereferencedType()
map_arg_0 = str(map_type.GetTemplateArgumentType(0).GetName())
map_arg_1 = str(map_type.GetTemplateArgumentType(1).GetName())
logger >> "map has args " + str(map_arg_0) + " and " + str(map_arg_1)
map_arg_0,fixed_0 = self.fixup_class_name(map_arg_0)
map_arg_1,fixed_1 = self.fixup_class_name(map_arg_1)
logger >> "arg_0 has become: " + str(map_arg_0) + " (fixed: " + str(fixed_0) + “)”
logger >> "arg_1 has become: " + str(map_arg_1) + " (fixed: " + str(fixed_1) + “)”
HACK: this is related to the above issue with the typename for std::string
being shortened by clang - the changes to typename display and searching to honor
namespaces make it so that we go looking for std::pair<const std::basic_string, …>
but when we find a type for this, we then compare it against the fully-qualified
std::pair<const std::basic_string<char, std::char_traits… and of course fail
the way to bypass this problem is to avoid using the std:: prefix in this specific case
if fixed_0 or fixed_1:
map_arg_type = "pair<const " + map_arg_0 + ", " + map_arg_1
else:
map_arg_type = "std::pair<const " + map_arg_0 + ", " + map_arg_1
if map_arg_1[-1] == ‘>’:
map_arg_type = map_arg_type + " >"
else:
map_arg_type = map_arg_type + “>”
logger >> "final contents datatype is: " + str(map_arg_type)
self.data_type = self.valobj.GetTarget().FindFirstType(map_arg_type)
logger >> "and the SBType is: " + str(self.data_type)
from libstdc++ implementation of _M_root for rbtree
self.Mroot = self.Mheader.GetChildMemberWithName(‘_M_parent’)
self.data_size = self.data_type.GetByteSize()
self.skip_size = self.Mheader.GetType().GetByteSize()
except:
pass
This is way more complicated than desirable. There should be ways to mark the test as an expected failure for older-than-X clang versions. Maybe that is the best way to tackle this issue.
Thanks.
Enrico Granata
egranata@.com
✆ (four oh eight) 862-7683