
*** to conform to clang-format’s LLVM style. This kind of mass change has *** two obvious implications: Firstly, merging this particular commit into a downstream fork may be a huge effort. Alternatively, it may be worth merging all changes up to this commit, performing the same reformatting operation locally, and then discarding the merge for this particular commit. The commands used to accomplish this reformatting were as follows (with current working directory as the root of the repository): find . \( -iname "*.c" -or -iname "*.cpp" -or -iname "*.h" -or -iname "*.mm" \) -exec clang-format -i {} + find . -iname "*.py" -exec autopep8 --in-place --aggressive --aggressive {} + ; The version of clang-format used was 3.9.0, and autopep8 was 1.2.4. Secondly, “blame” style tools will generally point to this commit instead of a meaningful prior commit. There are alternatives available that will attempt to look through this change and find the appropriate prior commit. YMMV. llvm-svn: 280751
870 lines
31 KiB
Python
870 lines
31 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 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.valobj.GetChildMemberWithName(
|
|
'__size_').GetChildMemberWithName('__first_').GetValueAsUnsigned(0)
|
|
# 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 = map_.GetChildMemberWithName(
|
|
'__end_cap_').GetChildMemberWithName('__first_').GetValueAsUnsigned(0)
|
|
# 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
|