import re import lldb.formatters.Logger # C++ STL formatters for LLDB # These formatters are based upon the version of the GNU libstdc++ # as it ships with Mac OS X 10.6.8 thru 10.8.0 # You are encouraged to look at the STL implementation for your platform # before relying on these formatters to do the right thing for your setup class StdListSynthProvider: def __init__(self, valobj, dict): logger = lldb.formatters.Logger.Logger() self.valobj = valobj self.count = None logger >> "Providing synthetic children for a map named " + str(valobj.GetName()) def next_node(self,node): logger = lldb.formatters.Logger.Logger() return node.GetChildMemberWithName('_M_next') def is_valid(self,node): logger = lldb.formatters.Logger.Logger() return self.value(self.next_node(node)) != self.node_address def value(self,node): logger = lldb.formatters.Logger.Logger() return node.GetValueAsUnsigned() # Floyd's cyle-finding algorithm # try to detect if this list has a loop def has_loop(self): global _list_uses_loop_detector logger = lldb.formatters.Logger.Logger() if _list_uses_loop_detector == False: logger >> "Asked not to use loop detection" return False slow = self.next fast1 = self.next fast2 = self.next while self.is_valid(slow): slow_value = self.value(slow) fast1 = self.next_node(fast2) fast2 = self.next_node(fast1) if self.value(fast1) == slow_value or self.value(fast2) == slow_value: return True slow = self.next_node(slow) return False def num_children(self): global _list_capping_size logger = lldb.formatters.Logger.Logger() if self.count == None: self.count = self.num_children_impl() if self.count > _list_capping_size: self.count = _list_capping_size return self.count def num_children_impl(self): logger = lldb.formatters.Logger.Logger() global _list_capping_size try: next_val = self.next.GetValueAsUnsigned(0) prev_val = self.prev.GetValueAsUnsigned(0) # After a std::list has been initialized, both next and prev will be non-NULL if next_val == 0 or prev_val == 0: return 0 if next_val == self.node_address: return 0 if next_val == prev_val: return 1 if self.has_loop(): return 0 size = 2 current = self.next while current.GetChildMemberWithName('_M_next').GetValueAsUnsigned(0) != self.node_address: size = size + 1 current = current.GetChildMemberWithName('_M_next') if size > _list_capping_size: return _list_capping_size return (size - 1) except: return 0; def get_child_index(self,name): logger = lldb.formatters.Logger.Logger() try: return int(name.lstrip('[').rstrip(']')) except: return -1 def get_child_at_index(self,index): logger = lldb.formatters.Logger.Logger() logger >> "Fetching child " + str(index) if index < 0: return None; if index >= self.num_children(): return None; try: offset = index current = self.next while offset > 0: current = current.GetChildMemberWithName('_M_next') offset = offset - 1 return current.CreateChildAtOffset('['+str(index)+']',2*current.GetType().GetByteSize(),self.data_type) except: return None def extract_type(self): logger = lldb.formatters.Logger.Logger() list_type = self.valobj.GetType().GetUnqualifiedType() if list_type.IsReferenceType(): list_type = list_type.GetDereferencedType() if list_type.GetNumberOfTemplateArguments() > 0: data_type = list_type.GetTemplateArgumentType(0) else: data_type = None return data_type def update(self): logger = lldb.formatters.Logger.Logger() # preemptively setting this to None - we might end up changing our mind later self.count = None try: impl = self.valobj.GetChildMemberWithName('_M_impl') node = impl.GetChildMemberWithName('_M_node') self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0) self.next = node.GetChildMemberWithName('_M_next') self.prev = node.GetChildMemberWithName('_M_prev') self.data_type = self.extract_type() self.data_size = self.data_type.GetByteSize() except: pass def has_children(self): return True class StdVectorSynthProvider: def __init__(self, valobj, dict): logger = lldb.formatters.Logger.Logger() self.count = None self.valobj = valobj logger >> "Providing synthetic children for a map named " + str(valobj.GetName()) def num_children(self): logger = lldb.formatters.Logger.Logger() if self.count == None: self.count = self.num_children_impl() return self.count def is_valid_pointer(ptr,process): logger = lldb.formatters.Logger.Logger() error = lldb.SBError() process.ReadMemory(ptr,1,error) return False if error.Fail() else True def num_children_impl(self): logger = lldb.formatters.Logger.Logger() try: start_val = self.start.GetValueAsUnsigned(0) finish_val = self.finish.GetValueAsUnsigned(0) end_val = self.end.GetValueAsUnsigned(0) # Before a vector has been constructed, it will contain bad values # so we really need to be careful about the length we return since # unitialized data can cause us to return a huge number. We need # to also check for any of the start, finish or end of storage values # being zero (NULL). If any are, then this vector has not been # initialized yet and we should return zero # Make sure nothing is NULL if start_val == 0 or finish_val == 0 or end_val == 0: return 0 # Make sure start is less than finish if start_val >= finish_val: return 0 # Make sure finish is less than or equal to end of storage if finish_val > end_val: return 0 # if we have a struct (or other data type that the compiler pads to native word size) # this check might fail, unless the sizeof() we get is itself incremented to take the # padding bytes into account - on current clang it looks like this is the case num_children = (finish_val-start_val) if (num_children % self.data_size) != 0: return 0 else: num_children = num_children/self.data_size return num_children except: return 0; def get_child_index(self,name): logger = lldb.formatters.Logger.Logger() try: return int(name.lstrip('[').rstrip(']')) except: return -1 def get_child_at_index(self,index): logger = lldb.formatters.Logger.Logger() logger >> "Retrieving child " + str(index) if index < 0: return None; if index >= self.num_children(): return None; try: offset = index * self.data_size return self.start.CreateChildAtOffset('['+str(index)+']',offset,self.data_type) except: return None def update(self): logger = lldb.formatters.Logger.Logger() # preemptively setting this to None - we might end up changing our mind later self.count = None try: impl = self.valobj.GetChildMemberWithName('_M_impl') self.start = impl.GetChildMemberWithName('_M_start') self.finish = impl.GetChildMemberWithName('_M_finish') self.end = impl.GetChildMemberWithName('_M_end_of_storage') self.data_type = self.start.GetType().GetPointeeType() self.data_size = self.data_type.GetByteSize() # if any of these objects is invalid, it means there is no point in trying to fetch anything if self.start.IsValid() and self.finish.IsValid() and self.end.IsValid() and self.data_type.IsValid(): self.count = None else: self.count = 0 except: pass def has_children(self): return True class StdMapSynthProvider: def __init__(self, valobj, dict): logger = lldb.formatters.Logger.Logger() self.valobj = valobj; self.count = None logger >> "Providing synthetic children for a map named " + str(valobj.GetName()) # we need this function as a temporary workaround for rdar://problem/10801549 # which prevents us from extracting the std::pair 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 = lldb.formatters.Logger.Logger() if class_name == 'std::basic_string, std::allocator >': return 'std::basic_string',True if class_name == 'basic_string, std::allocator >': return 'std::basic_string',True if class_name == 'std::basic_string, std::allocator >': return 'std::basic_string',True if class_name == 'basic_string, std::allocator >': return 'std::basic_string',True return class_name,False def update(self): logger = lldb.formatters.Logger.Logger() # preemptively setting this to None - we might end up changing our mind later self.count = None try: # 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, ...> # but when we find a type for this, we then compare it against the fully-qualified # std::pair': 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 def num_children(self): global _map_capping_size logger = lldb.formatters.Logger.Logger() if self.count == None: self.count = self.num_children_impl() if self.count > _map_capping_size: self.count = _map_capping_size return self.count def num_children_impl(self): logger = lldb.formatters.Logger.Logger() try: root_ptr_val = self.node_ptr_value(self.Mroot) if root_ptr_val == 0: return 0; count = self.Mimpl.GetChildMemberWithName('_M_node_count').GetValueAsUnsigned(0) logger >> "I have " + str(count) + " children available" return count except: return 0; def get_child_index(self,name): logger = lldb.formatters.Logger.Logger() try: return int(name.lstrip('[').rstrip(']')) except: return -1 def get_child_at_index(self,index): logger = lldb.formatters.Logger.Logger() logger >> "Being asked to fetch child[" + str(index) + "]" if index < 0: return None if index >= self.num_children(): return None; if self.garbage: logger >> "Returning None since we are a garbage tree" return None try: offset = index current = self.left(self.Mheader); while offset > 0: current = self.increment_node(current) offset = offset - 1; # skip all the base stuff and get at the data return current.CreateChildAtOffset('['+str(index)+']',self.skip_size,self.data_type) except: return None # utility functions def node_ptr_value(self,node): logger = lldb.formatters.Logger.Logger() return node.GetValueAsUnsigned(0) def right(self,node): logger = lldb.formatters.Logger.Logger() return node.GetChildMemberWithName("_M_right"); def left(self,node): logger = lldb.formatters.Logger.Logger() return node.GetChildMemberWithName("_M_left"); def parent(self,node): logger = lldb.formatters.Logger.Logger() return node.GetChildMemberWithName("_M_parent"); # from libstdc++ implementation of iterator for rbtree def increment_node(self,node): logger = lldb.formatters.Logger.Logger() max_steps = self.num_children() if self.node_ptr_value(self.right(node)) != 0: x = self.right(node); max_steps -= 1 while self.node_ptr_value(self.left(x)) != 0: x = self.left(x); max_steps -= 1 logger >> str(max_steps) + " more to go before giving up" if max_steps <= 0: self.garbage = True return None return x; else: x = node; y = self.parent(x) max_steps -= 1 while(self.node_ptr_value(x) == self.node_ptr_value(self.right(y))): x = y; y = self.parent(y); max_steps -= 1 logger >> str(max_steps) + " more to go before giving up" if max_steps <= 0: self.garbage = True return None if self.node_ptr_value(self.right(x)) != self.node_ptr_value(y): x = y; return x; def has_children(self): return True _map_capping_size = 255 _list_capping_size = 255 _list_uses_loop_detector = True