| 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 |