summaryrefslogtreecommitdiffstats
path: root/lldb/examples/synthetic
diff options
context:
space:
mode:
authorEnrico Granata <egranata@apple.com>2012-03-12 19:47:17 +0000
committerEnrico Granata <egranata@apple.com>2012-03-12 19:47:17 +0000
commitc7f873064bdd2059d0a8fe4d0066b0a920dcc094 (patch)
treee7fef529a4f5855d0f5f54a81f54957619085786 /lldb/examples/synthetic
parent701a6b473dd0b91e30a212ddd86f14307fa17bbc (diff)
downloadbcm5719-llvm-c7f873064bdd2059d0a8fe4d0066b0a920dcc094.tar.gz
bcm5719-llvm-c7f873064bdd2059d0a8fe4d0066b0a920dcc094.zip
Added formatters for libc++ (http://libcxx.llvm.org):
std::string has a summary provider std::vector std::list and std::map have both a summary and a synthetic children provider Given the usage of a custom namespace (std::__1::classname) for the implementation of libc++, we keep both libstdcpp and libc++ formatters enabled at the same time since that raises no conflicts and enabled for seamless transition between the two The formatters for libc++ reside in a libcxx category, and are loaded from libcxx.py (to be found in examples/synthetic) The formatters-stl test cases have been divided to be separate for libcxx and libstdcpp. This separation is necessary because (a) we need different compiler flags for libc++ than for libstdcpp (b) libc++ inlines a lot more than libstdcpp and some code changes were required to accommodate this difference llvm-svn: 152570
Diffstat (limited to 'lldb/examples/synthetic')
-rw-r--r--lldb/examples/synthetic/libcxx.py481
1 files changed, 481 insertions, 0 deletions
diff --git a/lldb/examples/synthetic/libcxx.py b/lldb/examples/synthetic/libcxx.py
new file mode 100644
index 00000000000..5e405df451a
--- /dev/null
+++ b/lldb/examples/synthetic/libcxx.py
@@ -0,0 +1,481 @@
+import lldb
+
+# 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
+
+# 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):
+ 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)
+ size = size_vo.GetValueAsUnsigned(0)+1 # the NULL terminator must be accounted for
+ if size <= 1: # should never be the case
+ return '""'
+ data = data_ptr.GetPointeeData(0,size)
+ 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):
+ self.valobj = valobj;
+ self.update()
+
+ def num_children(self):
+ 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
+ # unitialized data can cause us to return a huge number. We need
+ # to also check for any of the start, finish or end of storage values
+ # being zero (NULL). If any are, then this vector has not been
+ # initialized yet and we should return zero
+
+ # Make sure nothing is NULL
+ if start_val == 0 or finish_val == 0:
+ 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):
+ try:
+ return int(name.lstrip('[').rstrip(']'))
+ except:
+ return -1
+
+ def get_child_at_index(self,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):
+ 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 stdvector_SummaryProvider(valobj,dict):
+ prov = stdvector_SynthProvider(valobj,None)
+ return 'size=' + str(prov.num_children())
+
+class stdlist_entry:
+
+ def __init__(self,entry):
+ self.entry = entry
+
+ def _next_impl(self):
+ return stdlist_entry(self.entry.GetChildMemberWithName('__next_'))
+
+ def _prev_impl(self):
+ return stdlist_entry(self.entry.GetChildMemberWithName('__prev_'))
+
+ def _value_impl(self):
+ return self.entry.GetValueAsUnsigned(0)
+
+ def _isnull_impl(self):
+ return self._value_impl() == 0
+
+ def _sbvalue_impl(self):
+ 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):
+ if node.is_null:
+ return None
+ return node.next
+
+ def __init__(self,node):
+ self.node = stdlist_entry(node) # we convert the SBValue to an internal node object on entry
+
+ def value(self):
+ return self.node.sbvalue # and return the SBValue back on exit
+
+ def next(self):
+ node = self.increment_node(self.node)
+ if node != None and node.sbvalue.IsValid() and not(node.is_null):
+ self.node = node
+ return self.value()
+ else:
+ return None
+
+ def advance(self,N):
+ 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):
+ self.valobj = valobj
+ self.update()
+
+ def next_node(self,node):
+ return node.GetChildMemberWithName('__next_')
+
+ def value(self,node):
+ return node.GetValueAsUnsigned()
+
+ # Floyd's cyle-finding algorithm
+ # try to detect if this list has a loop
+ def has_loop(self):
+ 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):
+ if self.count == None:
+ self.count = self.num_children_impl()
+ return self.count
+
+ def num_children_impl(self):
+ 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
+ return (size - 1)
+ except:
+ return 0;
+
+ def get_child_index(self,name):
+ try:
+ return int(name.lstrip('[').rstrip(']'))
+ except:
+ return -1
+
+ def get_child_at_index(self,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):
+ list_type = self.valobj.GetType().GetUnqualifiedType()
+ if list_type.GetNumberOfTemplateArguments() > 0:
+ data_type = list_type.GetTemplateArgumentType(0)
+ else:
+ data_type = None
+ return data_type
+
+ def update(self):
+ 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()
+ self.count = None
+ except:
+ pass
+
+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):
+ return stdmap_iterator_node(self.node.GetChildMemberWithName("__left_"))
+
+ def _right_impl(self):
+ return stdmap_iterator_node(self.node.GetChildMemberWithName("__right_"))
+
+ def _parent_impl(self):
+ return stdmap_iterator_node(self.node.GetChildMemberWithName("__parent_"))
+
+ def _value_impl(self):
+ return self.node.GetValueAsUnsigned(0)
+
+ def _sbvalue_impl(self):
+ return self.node
+
+ def _null_impl(self):
+ return self.value == 0
+
+ def __init__(self,node):
+ 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):
+ if x.is_null:
+ return None
+ while (not x.left.is_null):
+ x = x.left
+ return x
+
+ def tree_max(self,x):
+ if x.is_null:
+ return None
+ while (not x.right.is_null):
+ x = x.right
+ return x
+
+ def tree_is_left_child(self,x):
+ if x.is_null:
+ return None
+ return True if x.value == x.parent.left.value else False
+
+ def increment_node(self,node):
+ if node.is_null:
+ return None
+ if not node.right.is_null:
+ return self.tree_min(node.right)
+ while (not self.tree_is_left_child(node)):
+ node = node.parent
+ return node.parent
+
+ def __init__(self,node):
+ self.node = stdmap_iterator_node(node) # we convert the SBValue to an internal node object on entry
+
+ def value(self):
+ return self.node.sbvalue # and return the SBValue back on exit
+
+ def next(self):
+ node = self.increment_node(self.node)
+ if node != None and node.sbvalue.IsValid() and not(node.is_null):
+ self.node = node
+ return self.value()
+ else:
+ return None
+
+ def advance(self,N):
+ 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 stdmap_SynthProvider:
+
+ def __init__(self, valobj, dict):
+ self.valobj = valobj;
+ self.pointer_size = self.valobj.GetProcess().GetAddressByteSize()
+ self.update()
+
+ def update(self):
+ try:
+ 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
+ self.count = None
+ except:
+ pass
+
+ def num_children(self):
+ if self.count == None:
+ self.count = self.num_children_impl()
+ return self.count
+
+ def num_children_impl(self):
+ try:
+ return self.valobj.GetChildMemberWithName('__tree_').GetChildMemberWithName('__pair3_').GetChildMemberWithName('__first_').GetValueAsUnsigned()
+ except:
+ return 0;
+
+ def get_data_type(self):
+ if self.data_type == None or self.data_size == 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):
+ if self.skip_size == 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 != None)
+
+ def get_child_index(self,name):
+ try:
+ return int(name.lstrip('[').rstrip(']'))
+ except:
+ return -1
+
+ def get_child_at_index(self,index):
+ if index < 0:
+ return None
+ if index >= self.num_children():
+ return None;
+ try:
+ iterator = stdmap_iterator(self.root_node)
+ # 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 self.get_data_type():
+ if not(need_to_skip):
+ current = current.Dereference()
+ obj = current.GetChildMemberWithName('__value_')
+ obj_data = obj.GetData()
+ self.get_value_offset(current) # make sure we have a valid offset for the next items
+ # 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 == None:
+ return None
+ return current.CreateChildAtOffset('[' + str(index) + ']',self.skip_size,self.data_type)
+ else:
+ print "foo"
+ return None
+ except Exception as err:
+ print err
+ return None
+
+def stdmap_SummaryProvider(valobj,dict):
+ prov = stdmap_SynthProvider(valobj,None)
+ return 'size=' + str(prov.num_children())
+
+
+# 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")
OpenPOWER on IntegriCloud