forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			877 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			877 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			Python
		
	
	
	
| import lldb
 | |
| import lldb.formatters.Logger
 | |
| 
 | |
| # libcxx STL formatters for LLDB
 | |
| # These formatters are based upon the implementation of libc++ that
 | |
| # ships with current releases of OS X - They will not work for other implementations
 | |
| # of the standard C++ library - and they are bound to use the
 | |
| # libc++-specific namespace
 | |
| 
 | |
| # the std::string summary is just an example for your convenience
 | |
| # the actual summary that LLDB uses is C++ code inside the debugger's own core
 | |
| 
 | |
| # this could probably be made more efficient but since it only reads a handful of bytes at a time
 | |
| # we probably don't need to worry too much about this for the time being
 | |
| 
 | |
| 
 | |
| def make_string(F, L):
 | |
|     strval = ''
 | |
|     G = F.GetData().uint8
 | |
|     for X in range(L):
 | |
|         V = G[X]
 | |
|         if V == 0:
 | |
|             break
 | |
|         strval = strval + chr(V % 256)
 | |
|     return '"' + strval + '"'
 | |
| 
 | |
| # if we ever care about big-endian, these two functions might need to change
 | |
| 
 | |
| 
 | |
| def is_short_string(value):
 | |
|     return True if (value & 1) == 0 else False
 | |
| 
 | |
| 
 | |
| def extract_short_size(value):
 | |
|     return ((value >> 1) % 256)
 | |
| 
 | |
| # some of the members of libc++ std::string are anonymous or have internal names that convey
 | |
| # no external significance - we access them by index since this saves a name lookup that would add
 | |
| # no information for readers of the code, but when possible try to use
 | |
| # meaningful variable names
 | |
| 
 | |
| 
 | |
| def stdstring_SummaryProvider(valobj, dict):
 | |
|     logger = lldb.formatters.Logger.Logger()
 | |
|     r = valobj.GetChildAtIndex(0)
 | |
|     B = r.GetChildAtIndex(0)
 | |
|     first = B.GetChildAtIndex(0)
 | |
|     D = first.GetChildAtIndex(0)
 | |
|     l = D.GetChildAtIndex(0)
 | |
|     s = D.GetChildAtIndex(1)
 | |
|     D20 = s.GetChildAtIndex(0)
 | |
|     size_mode = D20.GetChildAtIndex(0).GetValueAsUnsigned(0)
 | |
|     if is_short_string(size_mode):
 | |
|         size = extract_short_size(size_mode)
 | |
|         return make_string(s.GetChildAtIndex(1), size)
 | |
|     else:
 | |
|         data_ptr = l.GetChildAtIndex(2)
 | |
|         size_vo = l.GetChildAtIndex(1)
 | |
|         # the NULL terminator must be accounted for
 | |
|         size = size_vo.GetValueAsUnsigned(0) + 1
 | |
|         if size <= 1 or size is None:  # should never be the case
 | |
|             return '""'
 | |
|         try:
 | |
|             data = data_ptr.GetPointeeData(0, size)
 | |
|         except:
 | |
|             return '""'
 | |
|         error = lldb.SBError()
 | |
|         strval = data.GetString(error, 0)
 | |
|         if error.Fail():
 | |
|             return '<error:' + error.GetCString() + '>'
 | |
|         else:
 | |
|             return '"' + strval + '"'
 | |
| 
 | |
| 
 | |
| class stdvector_SynthProvider:
 | |
| 
 | |
|     def __init__(self, valobj, dict):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         self.valobj = valobj
 | |
| 
 | |
|     def num_children(self):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         try:
 | |
|             start_val = self.start.GetValueAsUnsigned(0)
 | |
|             finish_val = self.finish.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
 | |
|             # uninitialized 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:
 | |
|                 return 0
 | |
|             # Make sure start is less than finish
 | |
|             if start_val >= finish_val:
 | |
|                 return 0
 | |
| 
 | |
|             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()
 | |
|         try:
 | |
|             self.start = self.valobj.GetChildMemberWithName('__begin_')
 | |
|             self.finish = self.valobj.GetChildMemberWithName('__end_')
 | |
|             # the purpose of this field is unclear, but it is the only field whose type is clearly T* for a vector<T>
 | |
|             # if this ends up not being correct, we can use the APIs to get at
 | |
|             # template arguments
 | |
|             data_type_finder = self.valobj.GetChildMemberWithName(
 | |
|                 '__end_cap_').GetChildMemberWithName('__first_')
 | |
|             self.data_type = data_type_finder.GetType().GetPointeeType()
 | |
|             self.data_size = self.data_type.GetByteSize()
 | |
|         except:
 | |
|             pass
 | |
| 
 | |
|     def has_children(self):
 | |
|         return True
 | |
| 
 | |
| # Just an example: the actual summary is produced by a summary string:
 | |
| # size=${svar%#}
 | |
| 
 | |
| 
 | |
| def stdvector_SummaryProvider(valobj, dict):
 | |
|     prov = stdvector_SynthProvider(valobj, None)
 | |
|     return 'size=' + str(prov.num_children())
 | |
| 
 | |
| 
 | |
| class stdlist_entry:
 | |
| 
 | |
|     def __init__(self, entry):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         self.entry = entry
 | |
| 
 | |
|     def _next_impl(self):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         return stdlist_entry(self.entry.GetChildMemberWithName('__next_'))
 | |
| 
 | |
|     def _prev_impl(self):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         return stdlist_entry(self.entry.GetChildMemberWithName('__prev_'))
 | |
| 
 | |
|     def _value_impl(self):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         return self.entry.GetValueAsUnsigned(0)
 | |
| 
 | |
|     def _isnull_impl(self):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         return self._value_impl() == 0
 | |
| 
 | |
|     def _sbvalue_impl(self):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         return self.entry
 | |
| 
 | |
|     next = property(_next_impl, None)
 | |
|     value = property(_value_impl, None)
 | |
|     is_null = property(_isnull_impl, None)
 | |
|     sbvalue = property(_sbvalue_impl, None)
 | |
| 
 | |
| 
 | |
| class stdlist_iterator:
 | |
| 
 | |
|     def increment_node(self, node):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         if node.is_null:
 | |
|             return None
 | |
|         return node.next
 | |
| 
 | |
|     def __init__(self, node):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         # we convert the SBValue to an internal node object on entry
 | |
|         self.node = stdlist_entry(node)
 | |
| 
 | |
|     def value(self):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         return self.node.sbvalue  # and return the SBValue back on exit
 | |
| 
 | |
|     def next(self):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         node = self.increment_node(self.node)
 | |
|         if node is not None and node.sbvalue.IsValid() and not(node.is_null):
 | |
|             self.node = node
 | |
|             return self.value()
 | |
|         else:
 | |
|             return None
 | |
| 
 | |
|     def advance(self, N):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         if N < 0:
 | |
|             return None
 | |
|         if N == 0:
 | |
|             return self.value()
 | |
|         if N == 1:
 | |
|             return self.next()
 | |
|         while N > 0:
 | |
|             self.next()
 | |
|             N = N - 1
 | |
|         return self.value()
 | |
| 
 | |
| 
 | |
| class stdlist_SynthProvider:
 | |
| 
 | |
|     def __init__(self, valobj, dict):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         self.valobj = valobj
 | |
|         self.count = None
 | |
| 
 | |
|     def next_node(self, node):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         return node.GetChildMemberWithName('__next_')
 | |
| 
 | |
|     def value(self, node):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         return node.GetValueAsUnsigned()
 | |
| 
 | |
|     # Floyd's cycle-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 not _list_uses_loop_detector:
 | |
|             logger >> "Asked not to use loop detection"
 | |
|             return False
 | |
|         slow = stdlist_entry(self.head)
 | |
|         fast1 = stdlist_entry(self.head)
 | |
|         fast2 = stdlist_entry(self.head)
 | |
|         while slow.next.value != self.node_address:
 | |
|             slow_value = slow.value
 | |
|             fast1 = fast2.next
 | |
|             fast2 = fast1.next
 | |
|             if fast1.value == slow_value or fast2.value == slow_value:
 | |
|                 return True
 | |
|             slow = slow.next
 | |
|         return False
 | |
| 
 | |
|     def num_children(self):
 | |
|         global _list_capping_size
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         if self.count is 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):
 | |
|         global _list_capping_size
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         try:
 | |
|             next_val = self.head.GetValueAsUnsigned(0)
 | |
|             prev_val = self.tail.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 = stdlist_entry(self.head)
 | |
|             while current.next.value != self.node_address:
 | |
|                 size = size + 1
 | |
|                 current = current.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:
 | |
|             current = stdlist_iterator(self.head)
 | |
|             current = current.advance(index)
 | |
|             # we do not return __value_ because then all our children would be named __value_
 | |
|             # we need to make a copy of __value__ with the right name -
 | |
|             # unfortunate
 | |
|             obj = current.GetChildMemberWithName('__value_')
 | |
|             obj_data = obj.GetData()
 | |
|             return self.valobj.CreateValueFromData(
 | |
|                 '[' + str(index) + ']', obj_data, 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()
 | |
|         self.count = None
 | |
|         try:
 | |
|             impl = self.valobj.GetChildMemberWithName('__end_')
 | |
|             self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0)
 | |
|             self.head = impl.GetChildMemberWithName('__next_')
 | |
|             self.tail = impl.GetChildMemberWithName('__prev_')
 | |
|             self.data_type = self.extract_type()
 | |
|             self.data_size = self.data_type.GetByteSize()
 | |
|         except:
 | |
|             pass
 | |
| 
 | |
|     def has_children(self):
 | |
|         return True
 | |
| 
 | |
| 
 | |
| # Just an example: the actual summary is produced by a summary string:
 | |
| # size=${svar%#}
 | |
| def stdlist_SummaryProvider(valobj, dict):
 | |
|     prov = stdlist_SynthProvider(valobj, None)
 | |
|     return 'size=' + str(prov.num_children())
 | |
| 
 | |
| # a tree node - this class makes the syntax in the actual iterator nicer
 | |
| # to read and maintain
 | |
| 
 | |
| 
 | |
| class stdmap_iterator_node:
 | |
| 
 | |
|     def _left_impl(self):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         return stdmap_iterator_node(
 | |
|             self.node.GetChildMemberWithName("__left_"))
 | |
| 
 | |
|     def _right_impl(self):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         return stdmap_iterator_node(
 | |
|             self.node.GetChildMemberWithName("__right_"))
 | |
| 
 | |
|     def _parent_impl(self):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         return stdmap_iterator_node(
 | |
|             self.node.GetChildMemberWithName("__parent_"))
 | |
| 
 | |
|     def _value_impl(self):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         return self.node.GetValueAsUnsigned(0)
 | |
| 
 | |
|     def _sbvalue_impl(self):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         return self.node
 | |
| 
 | |
|     def _null_impl(self):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         return self.value == 0
 | |
| 
 | |
|     def __init__(self, node):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         self.node = node
 | |
| 
 | |
|     left = property(_left_impl, None)
 | |
|     right = property(_right_impl, None)
 | |
|     parent = property(_parent_impl, None)
 | |
|     value = property(_value_impl, None)
 | |
|     is_null = property(_null_impl, None)
 | |
|     sbvalue = property(_sbvalue_impl, None)
 | |
| 
 | |
| # a Python implementation of the tree iterator used by libc++
 | |
| 
 | |
| 
 | |
| class stdmap_iterator:
 | |
| 
 | |
|     def tree_min(self, x):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         steps = 0
 | |
|         if x.is_null:
 | |
|             return None
 | |
|         while (not x.left.is_null):
 | |
|             x = x.left
 | |
|             steps += 1
 | |
|             if steps > self.max_count:
 | |
|                 logger >> "Returning None - we overflowed"
 | |
|                 return None
 | |
|         return x
 | |
| 
 | |
|     def tree_max(self, x):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         if x.is_null:
 | |
|             return None
 | |
|         while (not x.right.is_null):
 | |
|             x = x.right
 | |
|         return x
 | |
| 
 | |
|     def tree_is_left_child(self, x):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         if x.is_null:
 | |
|             return None
 | |
|         return True if x.value == x.parent.left.value else False
 | |
| 
 | |
|     def increment_node(self, node):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         if node.is_null:
 | |
|             return None
 | |
|         if not node.right.is_null:
 | |
|             return self.tree_min(node.right)
 | |
|         steps = 0
 | |
|         while (not self.tree_is_left_child(node)):
 | |
|             steps += 1
 | |
|             if steps > self.max_count:
 | |
|                 logger >> "Returning None - we overflowed"
 | |
|                 return None
 | |
|             node = node.parent
 | |
|         return node.parent
 | |
| 
 | |
|     def __init__(self, node, max_count=0):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         # we convert the SBValue to an internal node object on entry
 | |
|         self.node = stdmap_iterator_node(node)
 | |
|         self.max_count = max_count
 | |
| 
 | |
|     def value(self):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         return self.node.sbvalue  # and return the SBValue back on exit
 | |
| 
 | |
|     def next(self):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         node = self.increment_node(self.node)
 | |
|         if node is not None and node.sbvalue.IsValid() and not(node.is_null):
 | |
|             self.node = node
 | |
|             return self.value()
 | |
|         else:
 | |
|             return None
 | |
| 
 | |
|     def advance(self, N):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         if N < 0:
 | |
|             return None
 | |
|         if N == 0:
 | |
|             return self.value()
 | |
|         if N == 1:
 | |
|             return self.next()
 | |
|         while N > 0:
 | |
|             if self.next() is None:
 | |
|                 return None
 | |
|             N = N - 1
 | |
|         return self.value()
 | |
| 
 | |
| 
 | |
| class stdmap_SynthProvider:
 | |
| 
 | |
|     def __init__(self, valobj, dict):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         self.valobj = valobj
 | |
|         self.pointer_size = self.valobj.GetProcess().GetAddressByteSize()
 | |
|         self.count = None
 | |
| 
 | |
|     def update(self):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         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.tree = self.valobj.GetChildMemberWithName('__tree_')
 | |
|             self.root_node = self.tree.GetChildMemberWithName('__begin_node_')
 | |
|             # this data is either lazily-calculated, or cannot be inferred at this moment
 | |
|             # we still need to mark it as None, meaning "please set me ASAP"
 | |
|             self.data_type = None
 | |
|             self.data_size = None
 | |
|             self.skip_size = None
 | |
|         except:
 | |
|             pass
 | |
| 
 | |
|     def num_children(self):
 | |
|         global _map_capping_size
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         if self.count is 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:
 | |
|             return self.valobj.GetChildMemberWithName('__tree_').GetChildMemberWithName(
 | |
|                 '__pair3_').GetChildMemberWithName('__first_').GetValueAsUnsigned()
 | |
|         except:
 | |
|             return 0
 | |
| 
 | |
|     def has_children(self):
 | |
|         return True
 | |
| 
 | |
|     def get_data_type(self):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         if self.data_type is None or self.data_size is None:
 | |
|             if self.num_children() == 0:
 | |
|                 return False
 | |
|             deref = self.root_node.Dereference()
 | |
|             if not(deref.IsValid()):
 | |
|                 return False
 | |
|             value = deref.GetChildMemberWithName('__value_')
 | |
|             if not(value.IsValid()):
 | |
|                 return False
 | |
|             self.data_type = value.GetType()
 | |
|             self.data_size = self.data_type.GetByteSize()
 | |
|             self.skip_size = None
 | |
|             return True
 | |
|         else:
 | |
|             return True
 | |
| 
 | |
|     def get_value_offset(self, node):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         if self.skip_size is None:
 | |
|             node_type = node.GetType()
 | |
|             fields_count = node_type.GetNumberOfFields()
 | |
|             for i in range(fields_count):
 | |
|                 field = node_type.GetFieldAtIndex(i)
 | |
|                 if field.GetName() == '__value_':
 | |
|                     self.skip_size = field.GetOffsetInBytes()
 | |
|                     break
 | |
|         return (self.skip_size is not None)
 | |
| 
 | |
|     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
 | |
|         if self.garbage:
 | |
|             logger >> "Returning None since this tree is garbage"
 | |
|             return None
 | |
|         try:
 | |
|             iterator = stdmap_iterator(
 | |
|                 self.root_node, max_count=self.num_children())
 | |
|             # the debug info for libc++ std::map is such that __begin_node_ has a very nice and useful type
 | |
|             # out of which we can grab the information we need - every other node has a less informative
 | |
|             # type which omits all value information and only contains housekeeping information for the RB tree
 | |
|             # hence, we need to know if we are at a node != 0, so that we can
 | |
|             # still get at the data
 | |
|             need_to_skip = (index > 0)
 | |
|             current = iterator.advance(index)
 | |
|             if current is None:
 | |
|                 logger >> "Tree is garbage - returning None"
 | |
|                 self.garbage = True
 | |
|                 return None
 | |
|             if self.get_data_type():
 | |
|                 if not(need_to_skip):
 | |
|                     current = current.Dereference()
 | |
|                     obj = current.GetChildMemberWithName('__value_')
 | |
|                     obj_data = obj.GetData()
 | |
|                     # make sure we have a valid offset for the next items
 | |
|                     self.get_value_offset(current)
 | |
|                     # we do not return __value_ because then we would end up with a child named
 | |
|                     # __value_ instead of [0]
 | |
|                     return self.valobj.CreateValueFromData(
 | |
|                         '[' + str(index) + ']', obj_data, self.data_type)
 | |
|                 else:
 | |
|                     # FIXME we need to have accessed item 0 before accessing
 | |
|                     # any other item!
 | |
|                     if self.skip_size is None:
 | |
|                         logger >> "You asked for item > 0 before asking for item == 0, I will fetch 0 now then retry"
 | |
|                         if self.get_child_at_index(0):
 | |
|                             return self.get_child_at_index(index)
 | |
|                         else:
 | |
|                             logger >> "item == 0 could not be found. sorry, nothing can be done here."
 | |
|                             return None
 | |
|                     return current.CreateChildAtOffset(
 | |
|                         '[' + str(index) + ']', self.skip_size, self.data_type)
 | |
|             else:
 | |
|                 logger >> "Unable to infer data-type - returning None (should mark tree as garbage here?)"
 | |
|                 return None
 | |
|         except Exception as err:
 | |
|             logger >> "Hit an exception: " + str(err)
 | |
|             return None
 | |
| 
 | |
| # Just an example: the actual summary is produced by a summary string:
 | |
| # size=${svar%#}
 | |
| 
 | |
| 
 | |
| def stdmap_SummaryProvider(valobj, dict):
 | |
|     prov = stdmap_SynthProvider(valobj, None)
 | |
|     return 'size=' + str(prov.num_children())
 | |
| 
 | |
| 
 | |
| class stddeque_SynthProvider:
 | |
| 
 | |
|     def __init__(self, valobj, d):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         logger.write("init")
 | |
|         self.valobj = valobj
 | |
|         self.pointer_size = self.valobj.GetProcess().GetAddressByteSize()
 | |
|         self.count = None
 | |
|         try:
 | |
|             self.find_block_size()
 | |
|         except:
 | |
|             self.block_size = -1
 | |
|             self.element_size = -1
 | |
|         logger.write(
 | |
|             "block_size=%d, element_size=%d" %
 | |
|             (self.block_size, self.element_size))
 | |
| 
 | |
|     def find_block_size(self):
 | |
|         # in order to use the deque we must have the block size, or else
 | |
|         # it's impossible to know what memory addresses are valid
 | |
|         self.element_type = self.valobj.GetType().GetTemplateArgumentType(0)
 | |
|         self.element_size = self.element_type.GetByteSize()
 | |
|         # The code says this, but there must be a better way:
 | |
|         # template <class _Tp, class _Allocator>
 | |
|         # class __deque_base {
 | |
|         #    static const difference_type __block_size = sizeof(value_type) < 256 ? 4096 / sizeof(value_type) : 16;
 | |
|         # }
 | |
|         if self.element_size < 256:
 | |
|             self.block_size = 4096 / self.element_size
 | |
|         else:
 | |
|             self.block_size = 16
 | |
| 
 | |
|     def num_children(self):
 | |
|         global _deque_capping_size
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         if self.count is None:
 | |
|             return 0
 | |
|         return min(self.count, _deque_capping_size)
 | |
| 
 | |
|     def has_children(self):
 | |
|         return True
 | |
| 
 | |
|     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.write("Fetching child " + str(index))
 | |
|         if index < 0 or self.count is None:
 | |
|             return None
 | |
|         if index >= self.num_children():
 | |
|             return None
 | |
|         try:
 | |
|             i, j = divmod(self.start + index, self.block_size)
 | |
|             return self.first.CreateValueFromExpression(
 | |
|                 '[' + str(index) + ']', '*(*(%s + %d) + %d)' %
 | |
|                 (self.first.get_expr_path(), i, j))
 | |
|         except:
 | |
|             return None
 | |
| 
 | |
|     def _get_value_of_compressed_pair(self, pair):
 | |
|         value = pair.GetChildMemberWithName("__value_")
 | |
|         if not value.IsValid():
 | |
|             # pre-r300140 member name
 | |
|             value = pair.GetChildMemberWithName("__first_")
 | |
|         return value.GetValueAsUnsigned(0)
 | |
| 
 | |
|     def update(self):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         try:
 | |
|             # A deque is effectively a two-dim array, with fixed width.
 | |
|             # 'map' contains pointers to the rows of this array. The
 | |
|             # full memory area allocated by the deque is delimited
 | |
|             # by 'first' and 'end_cap'. However, only a subset of this
 | |
|             # memory contains valid data since a deque may have some slack
 | |
|             # at the front and back in order to have O(1) insertion at
 | |
|             # both ends. The rows in active use are delimited by
 | |
|             # 'begin' and 'end'.
 | |
|             #
 | |
|             # To find the elements that are actually constructed, the 'start'
 | |
|             # variable tells which element in this NxM array is the 0th
 | |
|             # one, and the 'size' element gives the number of elements
 | |
|             # in the deque.
 | |
|             count = self._get_value_of_compressed_pair(
 | |
|                     self.valobj.GetChildMemberWithName('__size_'))
 | |
|             # give up now if we cant access memory reliably
 | |
|             if self.block_size < 0:
 | |
|                 logger.write("block_size < 0")
 | |
|                 return
 | |
|             map_ = self.valobj.GetChildMemberWithName('__map_')
 | |
|             start = self.valobj.GetChildMemberWithName(
 | |
|                 '__start_').GetValueAsUnsigned(0)
 | |
|             first = map_.GetChildMemberWithName('__first_')
 | |
|             map_first = first.GetValueAsUnsigned(0)
 | |
|             map_begin = map_.GetChildMemberWithName(
 | |
|                 '__begin_').GetValueAsUnsigned(0)
 | |
|             map_end = map_.GetChildMemberWithName(
 | |
|                 '__end_').GetValueAsUnsigned(0)
 | |
|             map_endcap = self._get_value_of_compressed_pair(
 | |
|                     map_.GetChildMemberWithName( '__end_cap_'))
 | |
|             # check consistency
 | |
|             if not map_first <= map_begin <= map_end <= map_endcap:
 | |
|                 logger.write("map pointers are not monotonic")
 | |
|                 return
 | |
|             total_rows, junk = divmod(
 | |
|                 map_endcap - map_first, self.pointer_size)
 | |
|             if junk:
 | |
|                 logger.write("endcap-first doesnt align correctly")
 | |
|                 return
 | |
|             active_rows, junk = divmod(map_end - map_begin, self.pointer_size)
 | |
|             if junk:
 | |
|                 logger.write("end-begin doesnt align correctly")
 | |
|                 return
 | |
|             start_row, junk = divmod(map_begin - map_first, self.pointer_size)
 | |
|             if junk:
 | |
|                 logger.write("begin-first doesnt align correctly")
 | |
|                 return
 | |
|             if not start_row * \
 | |
|                     self.block_size <= start < (start_row + 1) * self.block_size:
 | |
|                 logger.write("0th element must be in the 'begin' row")
 | |
|                 return
 | |
|             end_row = start_row + active_rows
 | |
|             if not count:
 | |
|                 if active_rows:
 | |
|                     logger.write("empty deque but begin!=end")
 | |
|                     return
 | |
|             elif not (end_row - 1) * self.block_size <= start + count < end_row * self.block_size:
 | |
|                 logger.write("nth element must be before the 'end' row")
 | |
|                 return
 | |
|             logger.write(
 | |
|                 "update success: count=%r, start=%r, first=%r" %
 | |
|                 (count, start, first))
 | |
|             # if consistent, save all we really need:
 | |
|             self.count = count
 | |
|             self.start = start
 | |
|             self.first = first
 | |
|         except:
 | |
|             self.count = None
 | |
|             self.start = None
 | |
|             self.map_first = None
 | |
|             self.map_begin = None
 | |
| 
 | |
| 
 | |
| class stdsharedptr_SynthProvider:
 | |
| 
 | |
|     def __init__(self, valobj, d):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         logger.write("init")
 | |
|         self.valobj = valobj
 | |
|         #self.element_ptr_type = self.valobj.GetType().GetTemplateArgumentType(0).GetPointerType()
 | |
|         self.ptr = None
 | |
|         self.cntrl = None
 | |
|         process = valobj.GetProcess()
 | |
|         self.endianness = process.GetByteOrder()
 | |
|         self.pointer_size = process.GetAddressByteSize()
 | |
|         self.count_type = valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
 | |
| 
 | |
|     def num_children(self):
 | |
|         return 1
 | |
| 
 | |
|     def has_children(self):
 | |
|         return True
 | |
| 
 | |
|     def get_child_index(self, name):
 | |
|         if name == "__ptr_":
 | |
|             return 0
 | |
|         if name == "count":
 | |
|             return 1
 | |
|         if name == "weak_count":
 | |
|             return 2
 | |
|         return -1
 | |
| 
 | |
|     def get_child_at_index(self, index):
 | |
|         if index == 0:
 | |
|             return self.ptr
 | |
|         if index == 1:
 | |
|             if self.cntrl is None:
 | |
|                 count = 0
 | |
|             else:
 | |
|                 count = 1 + \
 | |
|                     self.cntrl.GetChildMemberWithName('__shared_owners_').GetValueAsSigned()
 | |
|             return self.valobj.CreateValueFromData(
 | |
|                 "count", lldb.SBData.CreateDataFromUInt64Array(
 | |
|                     self.endianness, self.pointer_size, [count]), self.count_type)
 | |
|         if index == 2:
 | |
|             if self.cntrl is None:
 | |
|                 count = 0
 | |
|             else:
 | |
|                 count = 1 + \
 | |
|                     self.cntrl.GetChildMemberWithName('__shared_weak_owners_').GetValueAsSigned()
 | |
|             return self.valobj.CreateValueFromData(
 | |
|                 "weak_count", lldb.SBData.CreateDataFromUInt64Array(
 | |
|                     self.endianness, self.pointer_size, [count]), self.count_type)
 | |
|         return None
 | |
| 
 | |
|     def update(self):
 | |
|         logger = lldb.formatters.Logger.Logger()
 | |
|         self.ptr = self.valobj.GetChildMemberWithName(
 | |
|             '__ptr_')  # .Cast(self.element_ptr_type)
 | |
|         cntrl = self.valobj.GetChildMemberWithName('__cntrl_')
 | |
|         if cntrl.GetValueAsUnsigned(0):
 | |
|             self.cntrl = cntrl.Dereference()
 | |
|         else:
 | |
|             self.cntrl = None
 | |
| 
 | |
| # we can use two different categories for old and new formatters - type names are different enough that we should make no confusion
 | |
| # talking with libc++ developer: "std::__1::class_name is set in stone
 | |
| # until we decide to change the ABI. That shouldn't happen within a 5 year
 | |
| # time frame"
 | |
| 
 | |
| 
 | |
| def __lldb_init_module(debugger, dict):
 | |
|     debugger.HandleCommand(
 | |
|         'type summary add -F libcxx.stdstring_SummaryProvider "std::__1::string" -w libcxx')
 | |
|     debugger.HandleCommand(
 | |
|         'type summary add -F libcxx.stdstring_SummaryProvider "std::__1::basic_string<char, class std::__1::char_traits<char>, class std::__1::allocator<char> >" -w libcxx')
 | |
|     debugger.HandleCommand(
 | |
|         'type synthetic add -l libcxx.stdvector_SynthProvider -x "^(std::__1::)vector<.+>$" -w libcxx')
 | |
|     debugger.HandleCommand(
 | |
|         'type summary add -F libcxx.stdvector_SummaryProvider -e -x "^(std::__1::)vector<.+>$" -w libcxx')
 | |
|     debugger.HandleCommand(
 | |
|         'type synthetic add -l libcxx.stdlist_SynthProvider -x "^(std::__1::)list<.+>$" -w libcxx')
 | |
|     debugger.HandleCommand(
 | |
|         'type summary add -F libcxx.stdlist_SummaryProvider -e -x "^(std::__1::)list<.+>$" -w libcxx')
 | |
|     debugger.HandleCommand(
 | |
|         'type synthetic add -l libcxx.stdmap_SynthProvider -x "^(std::__1::)map<.+> >$" -w libcxx')
 | |
|     debugger.HandleCommand(
 | |
|         'type summary add -F libcxx.stdmap_SummaryProvider -e -x "^(std::__1::)map<.+> >$" -w libcxx')
 | |
|     debugger.HandleCommand("type category enable libcxx")
 | |
|     debugger.HandleCommand(
 | |
|         'type synthetic add -l libcxx.stddeque_SynthProvider -x "^(std::__1::)deque<.+>$" -w libcxx')
 | |
|     debugger.HandleCommand(
 | |
|         'type synthetic add -l libcxx.stdsharedptr_SynthProvider -x "^(std::__1::)shared_ptr<.+>$" -w libcxx')
 | |
|     # turns out the structs look the same, so weak_ptr can be handled the same!
 | |
|     debugger.HandleCommand(
 | |
|         'type synthetic add -l libcxx.stdsharedptr_SynthProvider -x "^(std::__1::)weak_ptr<.+>$" -w libcxx')
 | |
| 
 | |
| _map_capping_size = 255
 | |
| _list_capping_size = 255
 | |
| _list_uses_loop_detector = True
 | |
| _deque_capping_size = 255
 |