summaryrefslogtreecommitdiff
path: root/examples/synthetic
diff options
context:
space:
mode:
Diffstat (limited to 'examples/synthetic')
-rw-r--r--examples/synthetic/bitfield/example.py100
-rw-r--r--examples/synthetic/bitfield/program.cpp74
-rw-r--r--examples/synthetic/gnu_libstdcpp.py451
-rw-r--r--examples/synthetic/libcxx.py787
-rw-r--r--examples/synthetic/unordered_multi.py110
5 files changed, 1522 insertions, 0 deletions
diff --git a/examples/synthetic/bitfield/example.py b/examples/synthetic/bitfield/example.py
new file mode 100644
index 000000000000..7995919a4904
--- /dev/null
+++ b/examples/synthetic/bitfield/example.py
@@ -0,0 +1,100 @@
+# Synthetic children provider example for class MaskedData
+# to use me:
+# command script import ./example.py --allow-reload
+# type synthetic add MaskedData --python-class example.MaskedData_SyntheticChildrenProvider
+class MaskedData_SyntheticChildrenProvider:
+ def __init__(self, valobj, dict):
+ self.valobj = valobj # remember the SBValue since you will not have another chance to get it :-)
+
+ def num_children(self):
+ # you could perform calculations involving the SBValue and/or its children to determine this value
+ # here, we have an hardcoded value - but since you have stored the SBValue you could use it to
+ # help figure out the correct thing to return here. if you return a number N, you should be prepared to
+ # answer questions about N children
+ return 4
+
+ def has_children(self):
+ # we simply say True here because we know we have 4 children
+ # in general, you want to make this calculation as simple as possible
+ # and return True if in doubt (you can always return num_children == 0 later)
+ return True
+
+ def get_child_index(self,name):
+ # given a name, return its index
+ # you can return None if you don't know the answer for a given name
+ if name == "value":
+ return 0
+ # here, we are using a reserved C++ keyword as a child name - we could not do that in the source code
+ # but we are free to use the names we like best in the synthetic children provider class
+ # we are also not respecting the order of declaration in the C++ class itself - as long as
+ # we are consistent, we can do that freely
+ if name == "operator":
+ return 1
+ if name == "mask":
+ return 2
+ # this member does not exist in the original class - we will compute its value and show it to the user
+ # when returning synthetic children, there is no need to only stick to what already exists in memory
+ if name == "apply()":
+ return 3
+ return None # no clue, just say none
+
+ def get_child_at_index(self,index):
+ # precautionary measures
+ if index < 0:
+ return None
+ if index > self.num_children():
+ return None
+ if self.valobj.IsValid() == False:
+ return None
+ if index == 0:
+ return self.valobj.GetChildMemberWithName("value")
+ if index == 1:
+ # fetch the value of the operator
+ op_chosen = self.valobj.GetChildMemberWithName("oper").GetValueAsUnsigned()
+ # if it is a known value, return a descriptive string for it
+ # we are not doing this in the most efficient possible way, but the code is very readable
+ # and easy to maintain - if you change the values on the C++ side, the same changes must be made here
+ if op_chosen == 0:
+ return self.valobj.CreateValueFromExpression("operator",'(const char*)"none"')
+ elif op_chosen == 1:
+ return self.valobj.CreateValueFromExpression("operator",'(const char*)"AND"')
+ elif op_chosen == 2:
+ return self.valobj.CreateValueFromExpression("operator",'(const char*)"OR"')
+ elif op_chosen == 3:
+ return self.valobj.CreateValueFromExpression("operator",'(const char*)"XOR"')
+ elif op_chosen == 4:
+ return self.valobj.CreateValueFromExpression("operator",'(const char*)"NAND"')
+ elif op_chosen == 5:
+ return self.valobj.CreateValueFromExpression("operator",'(const char*)"NOR"')
+ else:
+ return self.valobj.CreateValueFromExpression("operator",'(const char*)"unknown"') # something else
+ if index == 2:
+ return self.valobj.GetChildMemberWithName("mask")
+ if index == 3:
+ # for this, we must fetch all the other elements
+ # in an efficient implementation, we would be caching this data for efficiency
+ value = self.valobj.GetChildMemberWithName("value").GetValueAsUnsigned()
+ operator = self.valobj.GetChildMemberWithName("oper").GetValueAsUnsigned()
+ mask = self.valobj.GetChildMemberWithName("mask").GetValueAsUnsigned()
+ # compute the masked value according to the operator
+ if operator == 1:
+ value = value & mask
+ elif operator == 2:
+ value = value | mask
+ elif operator == 3:
+ value = value ^ mask
+ elif operator == 4:
+ value = ~(value & mask)
+ elif operator == 5:
+ value = ~(value | mask)
+ else:
+ pass
+ value &= 0xFFFFFFFF # make sure Python does not extend our values to 64-bits
+ # return it - again, not the most efficient possible way. we should actually be pushing the computed value
+ # into an SBData, and using the SBData to create an SBValue - this has the advantage of readability
+ return self.valobj.CreateValueFromExpression("apply()",'(uint32_t)(' + str(value) + ')')
+
+ def update(self):
+ # we do not do anything special in update - but this would be the right place to lookup
+ # the data we use in get_child_at_index and cache it
+ pass
diff --git a/examples/synthetic/bitfield/program.cpp b/examples/synthetic/bitfield/program.cpp
new file mode 100644
index 000000000000..5276824a2fb4
--- /dev/null
+++ b/examples/synthetic/bitfield/program.cpp
@@ -0,0 +1,74 @@
+typedef unsigned int uint32_t;
+
+enum MaskingOperator
+{
+ eMaskingOperatorDefault = 0,
+ eMaskingOperatorAnd = 1,
+ eMaskingOperatorOr = 2,
+ eMaskingOperatorXor = 3,
+ eMaskingOperatorNand = 4,
+ eMaskingOperatorNor = 5
+};
+
+class MaskedData
+{
+private:
+ uint32_t value;
+ uint32_t mask;
+ MaskingOperator oper;
+public:
+ MaskedData( uint32_t V = 0,
+ uint32_t M = 0,
+ MaskingOperator P = eMaskingOperatorDefault) :
+ value(V),
+ mask(M),
+ oper(P)
+ {
+ }
+
+ uint32_t apply()
+ {
+ switch(oper)
+ {
+ case eMaskingOperatorAnd:
+ return value & mask;
+ case eMaskingOperatorOr:
+ return value | mask;
+ case eMaskingOperatorXor:
+ return value ^ mask;
+ case eMaskingOperatorNand:
+ return ~(value & mask);
+ case eMaskingOperatorNor:
+ return ~(value | mask);
+ case eMaskingOperatorDefault: // fall through
+ default:
+ return value;
+ }
+ }
+
+ void setValue(uint32_t V)
+ {
+ value = V;
+ }
+
+ void setMask (uint32_t M)
+ {
+ mask = M;
+ }
+
+ void setOperator(MaskingOperator P)
+ {
+ oper = P;
+ }
+};
+
+int main()
+{
+ MaskedData data_1(0xFF0F,0xA01F,eMaskingOperatorAnd);
+ MaskedData data_2(data_1.apply(),0x1AFC,eMaskingOperatorXor);
+ MaskedData data_3(data_2.apply(),0xFFCF,eMaskingOperatorOr);
+ MaskedData data_4(data_3.apply(),0xAABC,eMaskingOperatorAnd);
+ MaskedData data_5(data_4.apply(),0xFFAC,eMaskingOperatorNor);
+ MaskedData data_6(data_5.apply(),0x0000BEEF,eMaskingOperatorAnd);
+ return data_6.apply(); // <-- what comes out of here?
+} \ No newline at end of file
diff --git a/examples/synthetic/gnu_libstdcpp.py b/examples/synthetic/gnu_libstdcpp.py
new file mode 100644
index 000000000000..b6bf42235acd
--- /dev/null
+++ b/examples/synthetic/gnu_libstdcpp.py
@@ -0,0 +1,451 @@
+import re
+import lldb.formatters.Logger
+
+# C++ STL formatters for LLDB
+# These formatters are based upon the version of the GNU libstdc++
+# as it ships with Mac OS X 10.6.8 thru 10.8.0
+# You are encouraged to look at the STL implementation for your platform
+# before relying on these formatters to do the right thing for your setup
+
+class StdListSynthProvider:
+
+ def __init__(self, valobj, dict):
+ logger = lldb.formatters.Logger.Logger()
+ self.valobj = valobj
+ self.count = None
+ logger >> "Providing synthetic children for a list named " + str(valobj.GetName())
+
+ def next_node(self,node):
+ logger = lldb.formatters.Logger.Logger()
+ return node.GetChildMemberWithName('_M_next')
+
+ def is_valid(self,node):
+ logger = lldb.formatters.Logger.Logger()
+ valid = self.value(self.next_node(node)) != self.node_address
+ if valid:
+ logger >> "%s is valid" % str(self.valobj.GetName())
+ else:
+ logger >> "synthetic value is not valid"
+ return valid
+
+ def value(self,node):
+ logger = lldb.formatters.Logger.Logger()
+ value = node.GetValueAsUnsigned()
+ logger >> "synthetic value for {}: {}".format(str(self.valobj.GetName()), value)
+ return value
+
+ # 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 _list_uses_loop_detector == False:
+ logger >> "Asked not to use loop detection"
+ return False
+ slow = self.next
+ fast1 = self.next
+ fast2 = self.next
+ while self.is_valid(slow):
+ slow_value = self.value(slow)
+ fast1 = self.next_node(fast2)
+ fast2 = self.next_node(fast1)
+ if self.value(fast1) == slow_value or self.value(fast2) == slow_value:
+ return True
+ slow = self.next_node(slow)
+ return False
+
+ def num_children(self):
+ logger = lldb.formatters.Logger.Logger()
+ if self.count is None:
+ # libstdc++ 6.0.21 added dedicated count field.
+ count_child = self.node.GetChildMemberWithName('_M_data')
+ if count_child and count_child.IsValid():
+ self.count = count_child.GetValueAsUnsigned(0)
+ if self.count is None:
+ self.count = self.num_children_impl()
+ return self.count
+
+ def num_children_impl(self):
+ logger = lldb.formatters.Logger.Logger()
+ try:
+ next_val = self.next.GetValueAsUnsigned(0)
+ prev_val = self.prev.GetValueAsUnsigned(0)
+ # After a std::list has been initialized, both next and prev will be non-NULL
+ if next_val == 0 or prev_val == 0:
+ return 0
+ if next_val == self.node_address:
+ return 0
+ if next_val == prev_val:
+ return 1
+ if self.has_loop():
+ return 0
+ size = 2
+ current = self.next
+ while current.GetChildMemberWithName('_M_next').GetValueAsUnsigned(0) != self.node_address:
+ size = size + 1
+ current = current.GetChildMemberWithName('_M_next')
+ return (size - 1)
+ except:
+ return 0;
+
+ def get_child_index(self,name):
+ logger = lldb.formatters.Logger.Logger()
+ try:
+ return int(name.lstrip('[').rstrip(']'))
+ except:
+ return -1
+
+ def get_child_at_index(self,index):
+ logger = lldb.formatters.Logger.Logger()
+ logger >> "Fetching child " + str(index)
+ if index < 0:
+ return None;
+ if index >= self.num_children():
+ return None;
+ try:
+ offset = index
+ current = self.next
+ while offset > 0:
+ current = current.GetChildMemberWithName('_M_next')
+ offset = offset - 1
+ return current.CreateChildAtOffset('['+str(index)+']',2*current.GetType().GetByteSize(),self.data_type)
+ except:
+ return None
+
+ def extract_type(self):
+ logger = lldb.formatters.Logger.Logger()
+ list_type = self.valobj.GetType().GetUnqualifiedType()
+ if list_type.IsReferenceType():
+ list_type = list_type.GetDereferencedType()
+ if list_type.GetNumberOfTemplateArguments() > 0:
+ data_type = list_type.GetTemplateArgumentType(0)
+ else:
+ data_type = None
+ return data_type
+
+ def update(self):
+ logger = lldb.formatters.Logger.Logger()
+ # preemptively setting this to None - we might end up changing our mind later
+ self.count = None
+ try:
+ impl = self.valobj.GetChildMemberWithName('_M_impl')
+ self.node = impl.GetChildMemberWithName('_M_node')
+ self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0)
+ self.next = self.node.GetChildMemberWithName('_M_next')
+ self.prev = self.node.GetChildMemberWithName('_M_prev')
+ self.data_type = self.extract_type()
+ self.data_size = self.data_type.GetByteSize()
+ except:
+ pass
+
+ def has_children(self):
+ return True
+
+class StdVectorSynthProvider:
+
+ class StdVectorImplementation(object):
+ def __init__(self, valobj):
+ self.valobj = valobj
+ self.count = None
+
+ def num_children(self):
+ if self.count == None:
+ self.count = self.num_children_impl()
+ return self.count
+
+ def num_children_impl(self):
+ try:
+ start_val = self.start.GetValueAsUnsigned(0)
+ finish_val = self.finish.GetValueAsUnsigned(0)
+ end_val = self.end.GetValueAsUnsigned(0)
+ # Before a vector has been constructed, it will contain bad values
+ # so we really need to be careful about the length we return since
+ # 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 or end_val == 0:
+ return 0
+ # Make sure start is less than finish
+ if start_val >= finish_val:
+ return 0
+ # Make sure finish is less than or equal to end of storage
+ if finish_val > end_val:
+ return 0
+
+ # if we have a struct (or other data type that the compiler pads to native word size)
+ # this check might fail, unless the sizeof() we get is itself incremented to take the
+ # padding bytes into account - on current clang it looks like this is the case
+ num_children = (finish_val-start_val)
+ if (num_children % self.data_size) != 0:
+ return 0
+ else:
+ num_children = num_children/self.data_size
+ return num_children
+ except:
+ return 0;
+
+ def get_child_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):
+ # preemptively setting this to None - we might end up changing our mind later
+ self.count = None
+ try:
+ impl = self.valobj.GetChildMemberWithName('_M_impl')
+ self.start = impl.GetChildMemberWithName('_M_start')
+ self.finish = impl.GetChildMemberWithName('_M_finish')
+ self.end = impl.GetChildMemberWithName('_M_end_of_storage')
+ self.data_type = self.start.GetType().GetPointeeType()
+ self.data_size = self.data_type.GetByteSize()
+ # if any of these objects is invalid, it means there is no point in trying to fetch anything
+ if self.start.IsValid() and self.finish.IsValid() and self.end.IsValid() and self.data_type.IsValid():
+ self.count = None
+ else:
+ self.count = 0
+ except:
+ pass
+ return True
+
+ class StdVBoolImplementation(object):
+ def __init__(self, valobj, bool_type):
+ self.valobj = valobj
+ self.bool_type = bool_type
+ self.valid = False
+
+ def num_children(self):
+ if self.valid:
+ start = self.start_p.GetValueAsUnsigned(0)
+ finish = self.finish_p.GetValueAsUnsigned(0)
+ offset = self.offset.GetValueAsUnsigned(0)
+ if finish >= start:
+ return (finish - start) * 8 + offset
+ return 0
+
+ def get_child_at_index(self, index):
+ if index >= self.num_children():
+ return None
+ byte_offset = index / 8
+ bit_offset = index % 8
+ element_size = self.start_p.GetType().GetPointeeType().GetByteSize()
+ data = self.start_p.GetPointeeData(byte_offset / element_size)
+ bit = data.GetUnsignedInt8(lldb.SBError(), byte_offset % element_size) & (1 << bit_offset)
+ if bit != 0:
+ value_expr = "(bool)true"
+ else:
+ value_expr = "(bool)false"
+ return self.valobj.CreateValueFromExpression("[%d]" % index, value_expr)
+
+ def update(self):
+ try:
+ m_impl = self.valobj.GetChildMemberWithName('_M_impl')
+ self.m_start = m_impl.GetChildMemberWithName('_M_start')
+ self.m_finish = m_impl.GetChildMemberWithName('_M_finish')
+ self.start_p = self.m_start.GetChildMemberWithName('_M_p')
+ self.finish_p = self.m_finish.GetChildMemberWithName('_M_p')
+ self.offset = self.m_finish.GetChildMemberWithName('_M_offset')
+ self.valid = True
+ except:
+ self.valid = False
+ return True
+
+ def __init__(self, valobj, dict):
+ logger = lldb.formatters.Logger.Logger()
+ first_template_arg_type = valobj.GetType().GetTemplateArgumentType(0)
+ if str(first_template_arg_type.GetName()) == "bool":
+ self.impl = self.StdVBoolImplementation(valobj, first_template_arg_type)
+ else:
+ self.impl = self.StdVectorImplementation(valobj)
+ logger >> "Providing synthetic children for a vector named " + str(valobj.GetName())
+
+ def num_children(self):
+ return self.impl.num_children()
+
+ def get_child_index(self,name):
+ try:
+ return int(name.lstrip('[').rstrip(']'))
+ except:
+ return -1
+
+ def get_child_at_index(self, index):
+ return self.impl.get_child_at_index(index)
+
+ def update(self):
+ return self.impl.update()
+
+ def has_children(self):
+ return True
+
+
+class StdMapSynthProvider:
+
+ def __init__(self, valobj, dict):
+ logger = lldb.formatters.Logger.Logger()
+ self.valobj = valobj;
+ self.count = None
+ logger >> "Providing synthetic children for a map named " + str(valobj.GetName())
+
+ # we need this function as a temporary workaround for rdar://problem/10801549
+ # which prevents us from extracting the std::pair<K,V> SBType out of the template
+ # arguments for _Rep_Type _M_t in the map itself - because we have to make up the
+ # typename and then find it, we may hit the situation were std::string has multiple
+ # names but only one is actually referenced in the debug information. hence, we need
+ # to replace the longer versions of std::string with the shorter one in order to be able
+ # to find the type name
+ def fixup_class_name(self, class_name):
+ logger = lldb.formatters.Logger.Logger()
+ if class_name == 'std::basic_string<char, std::char_traits<char>, std::allocator<char> >':
+ return 'std::basic_string<char>',True
+ if class_name == 'basic_string<char, std::char_traits<char>, std::allocator<char> >':
+ return 'std::basic_string<char>',True
+ if class_name == 'std::basic_string<char, std::char_traits<char>, std::allocator<char> >':
+ return 'std::basic_string<char>',True
+ if class_name == 'basic_string<char, std::char_traits<char>, std::allocator<char> >':
+ return 'std::basic_string<char>',True
+ return class_name,False
+
+ def update(self):
+ logger = lldb.formatters.Logger.Logger()
+ # preemptively setting this to None - we might end up changing our mind later
+ self.count = None
+ try:
+ # we will set this to True if we find out that discovering a node in the map takes more steps than the overall size of the RB tree
+ # if this gets set to True, then we will merrily return None for any child from that moment on
+ self.garbage = False
+ self.Mt = self.valobj.GetChildMemberWithName('_M_t')
+ self.Mimpl = self.Mt.GetChildMemberWithName('_M_impl')
+ self.Mheader = self.Mimpl.GetChildMemberWithName('_M_header')
+
+ map_type = self.valobj.GetType()
+ if map_type.IsReferenceType():
+ logger >> "Dereferencing type"
+ map_type = map_type.GetDereferencedType()
+
+ # Get the type of std::pair<key, value>. It is the first template
+ # argument type of the 4th template argument to std::map.
+ allocator_type = map_type.GetTemplateArgumentType(3)
+ self.data_type = allocator_type.GetTemplateArgumentType(0)
+ if not self.data_type:
+ # GCC does not emit DW_TAG_template_type_parameter for
+ # std::allocator<...>. For such a case, get the type of
+ # std::pair from a member of std::map.
+ rep_type = self.valobj.GetChildMemberWithName('_M_t').GetType()
+ self.data_type = rep_type.GetTypedefedType().GetTemplateArgumentType(1)
+
+ # from libstdc++ implementation of _M_root for rbtree
+ self.Mroot = self.Mheader.GetChildMemberWithName('_M_parent')
+ self.data_size = self.data_type.GetByteSize()
+ self.skip_size = self.Mheader.GetType().GetByteSize()
+ except:
+ pass
+
+ def num_children(self):
+ logger = lldb.formatters.Logger.Logger()
+ if self.count == None:
+ self.count = self.num_children_impl()
+ return self.count
+
+ def num_children_impl(self):
+ logger = lldb.formatters.Logger.Logger()
+ try:
+ root_ptr_val = self.node_ptr_value(self.Mroot)
+ if root_ptr_val == 0:
+ return 0;
+ count = self.Mimpl.GetChildMemberWithName('_M_node_count').GetValueAsUnsigned(0)
+ logger >> "I have " + str(count) + " children available"
+ return count
+ except:
+ return 0;
+
+ def get_child_index(self,name):
+ logger = lldb.formatters.Logger.Logger()
+ try:
+ return int(name.lstrip('[').rstrip(']'))
+ except:
+ return -1
+
+ def get_child_at_index(self,index):
+ logger = lldb.formatters.Logger.Logger()
+ logger >> "Being asked to fetch child[" + str(index) + "]"
+ if index < 0:
+ return None
+ if index >= self.num_children():
+ return None;
+ if self.garbage:
+ logger >> "Returning None since we are a garbage tree"
+ return None
+ try:
+ offset = index
+ current = self.left(self.Mheader);
+ while offset > 0:
+ current = self.increment_node(current)
+ offset = offset - 1;
+ # skip all the base stuff and get at the data
+ return current.CreateChildAtOffset('['+str(index)+']',self.skip_size,self.data_type)
+ except:
+ return None
+
+ # utility functions
+ def node_ptr_value(self,node):
+ logger = lldb.formatters.Logger.Logger()
+ return node.GetValueAsUnsigned(0)
+
+ def right(self,node):
+ logger = lldb.formatters.Logger.Logger()
+ return node.GetChildMemberWithName("_M_right");
+
+ def left(self,node):
+ logger = lldb.formatters.Logger.Logger()
+ return node.GetChildMemberWithName("_M_left");
+
+ def parent(self,node):
+ logger = lldb.formatters.Logger.Logger()
+ return node.GetChildMemberWithName("_M_parent");
+
+ # from libstdc++ implementation of iterator for rbtree
+ def increment_node(self,node):
+ logger = lldb.formatters.Logger.Logger()
+ max_steps = self.num_children()
+ if self.node_ptr_value(self.right(node)) != 0:
+ x = self.right(node);
+ max_steps -= 1
+ while self.node_ptr_value(self.left(x)) != 0:
+ x = self.left(x);
+ max_steps -= 1
+ logger >> str(max_steps) + " more to go before giving up"
+ if max_steps <= 0:
+ self.garbage = True
+ return None
+ return x;
+ else:
+ x = node;
+ y = self.parent(x)
+ max_steps -= 1
+ while(self.node_ptr_value(x) == self.node_ptr_value(self.right(y))):
+ x = y;
+ y = self.parent(y);
+ max_steps -= 1
+ logger >> str(max_steps) + " more to go before giving up"
+ if max_steps <= 0:
+ self.garbage = True
+ return None
+ if self.node_ptr_value(self.right(x)) != self.node_ptr_value(y):
+ x = y;
+ return x;
+
+ def has_children(self):
+ return True
+
+_list_uses_loop_detector = True
diff --git a/examples/synthetic/libcxx.py b/examples/synthetic/libcxx.py
new file mode 100644
index 000000000000..6623fea097c4
--- /dev/null
+++ b/examples/synthetic/libcxx.py
@@ -0,0 +1,787 @@
+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)
+ size = size_vo.GetValueAsUnsigned(0)+1 # the NULL terminator must be accounted for
+ if size <= 1 or size == 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()
+ self.node = stdlist_entry(node) # we convert the SBValue to an internal node object on entry
+
+ 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 != 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 _list_uses_loop_detector == False:
+ 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 == 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()
+ self.node = stdmap_iterator_node(node) # we convert the SBValue to an internal node object on entry
+ 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 != 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() == 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 == 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 == 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):
+ logger = lldb.formatters.Logger.Logger()
+ 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):
+ 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 == 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()
+ 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:
+ 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 == 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 == 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
diff --git a/examples/synthetic/unordered_multi.py b/examples/synthetic/unordered_multi.py
new file mode 100644
index 000000000000..3389a01aea30
--- /dev/null
+++ b/examples/synthetic/unordered_multi.py
@@ -0,0 +1,110 @@
+import lldb
+
+_map_capping_size = 255
+
+class libcxx_hash_table_SynthProvider:
+ def __init__(self, valobj, dict):
+ self.valobj = valobj
+ self.num_elements = None
+ self.next_element = None
+ self.bucket_count = None
+
+ def update(self):
+ logger = lldb.formatters.Logger.Logger()
+ self.num_elements = None
+ self.next_element = None
+ self.bucket_count = None
+ try:
+ # unordered_map is made up of a hash_map, which has 4 pieces in it:
+ # bucket list :
+ # array of buckets
+ # p1 (pair):
+ # first - pointer to first loaded element
+ # p2 (pair):
+ # first - number of elements
+ # second - hash function
+ # p3 (pair):
+ # first - max_load_factor
+ # second - equality operator function
+ #
+ # For display, we actually don't need to go inside the buckets, since 'p1' has a way to iterate over all
+ # the elements directly.
+ #
+ # We will calculate other values about the map because they will be useful for the summary.
+ #
+ table = self.valobj.GetChildMemberWithName('__table_')
+
+ bl_ptr = table.GetChildMemberWithName('__bucket_list_').GetChildMemberWithName('__ptr_')
+ self.bucket_array_ptr = bl_ptr.GetChildMemberWithName('__first_').GetValueAsUnsigned(0)
+ self.bucket_count = bl_ptr.GetChildMemberWithName('__second_').GetChildMemberWithName('__data_').GetChildMemberWithName('__first_').GetValueAsUnsigned(0)
+ logger >> "Bucket count = %r" % self.bucket_count
+
+ self.begin_ptr = table.GetChildMemberWithName('__p1_').GetChildMemberWithName('__first_').GetChildMemberWithName('__next_')
+
+ self.num_elements = table.GetChildMemberWithName('__p2_').GetChildMemberWithName('__first_').GetValueAsUnsigned(0)
+ self.max_load_factor = table.GetChildMemberWithName('__p3_').GetChildMemberWithName('__first_').GetValueAsUnsigned(0)
+ logger >> "Num elements = %r" % self.num_elements
+
+ # save the pointers as we get them
+ # -- don't access this first element if num_element==0!
+ self.elements_cache = []
+ if self.num_elements:
+ self.next_element = self.begin_ptr
+ else:
+ self.next_element = None
+ except Exception as e:
+ logger >> "Caught exception: %r" % e
+ pass
+
+ def num_children(self):
+ global _map_capping_size
+ num_elements = self.num_elements
+ if num_elements is not None:
+ if num_elements > _map_capping_size:
+ num_elements = _map_capping_size
+ return num_elements
+
+ 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 >> "Retrieving child " + str(index)
+ if index < 0:
+ return None
+ if index >= self.num_children():
+ return None
+
+ # extend
+ logger >> " : cache size starts with %d elements" % len(self.elements_cache)
+ while index >= len(self.elements_cache):
+ # if we hit the end before we get the index, give up:
+ if not self.next_element:
+ logger >> " : hit end of list"
+ return None
+
+ node = self.next_element.Dereference()
+
+ value = node.GetChildMemberWithName('__value_')
+ hash_value = node.GetChildMemberWithName('__hash_').GetValueAsUnsigned()
+ self.elements_cache.append((value, hash_value))
+
+ self.next_element = node.GetChildMemberWithName('__next_')
+ if not self.next_element.GetValueAsUnsigned(0):
+ self.next_element = None
+
+ # hit the index! so we have the value
+ logger >> " : cache size ends with %d elements" % len(self.elements_cache)
+ value, hash_value = self.elements_cache[index]
+ return self.valobj.CreateValueFromData('[%d] <hash %d>'%(index,hash_value), value.GetData(), value.GetType())
+
+
+def __lldb_init_module(debugger,dict):
+ debugger.HandleCommand('type synthetic add -l unordered_multi.libcxx_hash_table_SynthProvider -x "^(std::__1::)unordered_(multi)?(map|set)<.+> >$" -w libcxx')