diff options
Diffstat (limited to 'lldb/examples/synthetic')
-rw-r--r-- | lldb/examples/synthetic/bitfield/example.py | 205 | ||||
-rw-r--r-- | lldb/examples/synthetic/bitfield/program.cpp | 112 | ||||
-rw-r--r-- | lldb/examples/synthetic/gnu_libstdcpp.py | 892 | ||||
-rw-r--r-- | lldb/examples/synthetic/libcxx.py | 1240 | ||||
-rw-r--r-- | lldb/examples/synthetic/unordered_multi.py | 224 |
5 files changed, 1401 insertions, 1272 deletions
diff --git a/lldb/examples/synthetic/bitfield/example.py b/lldb/examples/synthetic/bitfield/example.py index 7995919..57bbf0f 100644 --- a/lldb/examples/synthetic/bitfield/example.py +++ b/lldb/examples/synthetic/bitfield/example.py @@ -1,100 +1,123 @@ # 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 +# 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 __init__(self, valobj, dict): + # remember the SBValue since you will not have another chance to get it + # :-) + self.valobj = valobj + + 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 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_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 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 + 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/lldb/examples/synthetic/bitfield/program.cpp b/lldb/examples/synthetic/bitfield/program.cpp index 5276824..1db4aa4 100644 --- a/lldb/examples/synthetic/bitfield/program.cpp +++ b/lldb/examples/synthetic/bitfield/program.cpp @@ -1,74 +1,56 @@ typedef unsigned int uint32_t; -enum MaskingOperator -{ - eMaskingOperatorDefault = 0, - eMaskingOperatorAnd = 1, - eMaskingOperatorOr = 2, - eMaskingOperatorXor = 3, - eMaskingOperatorNand = 4, - eMaskingOperatorNor = 5 +enum MaskingOperator { + eMaskingOperatorDefault = 0, + eMaskingOperatorAnd = 1, + eMaskingOperatorOr = 2, + eMaskingOperatorXor = 3, + eMaskingOperatorNand = 4, + eMaskingOperatorNor = 5 }; -class MaskedData -{ +class MaskedData { private: - uint32_t value; - uint32_t mask; - MaskingOperator oper; + 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; - } + 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? +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/lldb/examples/synthetic/gnu_libstdcpp.py b/lldb/examples/synthetic/gnu_libstdcpp.py index 9f26282..becf168 100644 --- a/lldb/examples/synthetic/gnu_libstdcpp.py +++ b/lldb/examples/synthetic/gnu_libstdcpp.py @@ -7,446 +7,474 @@ import lldb.formatters.Logger # 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 + 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 not _list_uses_loop_detector: + 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 - element_type = self.start_p.GetType().GetPointeeType() - element_bits = 8 * element_type.GetByteSize() - element_offset = (index / element_bits) * element_type.GetByteSize() - bit_offset = index % element_bits - element = self.start_p.CreateChildAtOffset('['+str(index)+']',element_offset,element_type) - bit = element.GetValueAsUnsigned(0) & (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 StdVectorImplementation(object): + + def __init__(self, valobj): + self.valobj = valobj + self.count = None + + def num_children(self): + if self.count is 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 + element_type = self.start_p.GetType().GetPointeeType() + element_bits = 8 * element_type.GetByteSize() + element_offset = (index / element_bits) * \ + element_type.GetByteSize() + bit_offset = index % element_bits + element = self.start_p.CreateChildAtOffset( + '[' + str(index) + ']', element_offset, element_type) + bit = element.GetValueAsUnsigned(0) & (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 + 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 is 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/lldb/examples/synthetic/libcxx.py b/lldb/examples/synthetic/libcxx.py index 6623fea..e6f8223 100644 --- a/lldb/examples/synthetic/libcxx.py +++ b/lldb/examples/synthetic/libcxx.py @@ -4,583 +4,633 @@ 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 +# 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 + '"' + + +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 + return True if (value & 1) == 0 else False + + def extract_short_size(value): - return ((value >> 1) % 256) + 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 + '"' +# 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()) + 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 __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 _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 _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 _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 _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 - 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) - 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() + 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 _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 + + 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 _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 _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 _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 _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 _null_impl(self): + logger = lldb.formatters.Logger.Logger() + return self.value == 0 - def __init__(self,node): - logger = lldb.formatters.Logger.Logger() - self.node = node + 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) + 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() + 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 == 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()) + 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") @@ -592,7 +642,9 @@ class stddeque_SynthProvider: except: self.block_size = -1 self.element_size = -1 - logger.write("block_size=%d, element_size=%d" % (self.block_size, self.element_size)) + 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 @@ -605,7 +657,7 @@ class stddeque_SynthProvider: # 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 + self.block_size = 4096 / self.element_size else: self.block_size = 16 @@ -619,24 +671,25 @@ class stddeque_SynthProvider: def has_children(self): return True - def get_child_index(self,name): + 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): + 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; + return None if index >= self.num_children(): - return None; + 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)) + 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 @@ -656,23 +709,29 @@ class stddeque_SynthProvider: # 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) + 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) + 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) + 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) + total_rows, junk = divmod( + map_endcap - map_first, self.pointer_size) if junk: logger.write("endcap-first doesnt align correctly") return @@ -684,18 +743,21 @@ class stddeque_SynthProvider: if junk: logger.write("begin-first doesnt align correctly") return - if not start_row*self.block_size <= start < (start_row+1)*self.block_size: + 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: + 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)) + 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 @@ -706,7 +768,9 @@ class stddeque_SynthProvider: self.map_first = None self.map_begin = None + class stdsharedptr_SynthProvider: + def __init__(self, valobj, d): logger = lldb.formatters.Logger.Logger() logger.write("init") @@ -725,39 +789,42 @@ class stdsharedptr_SynthProvider: def has_children(self): return True - def get_child_index(self,name): - if name=="__ptr_": + def get_child_index(self, name): + if name == "__ptr_": return 0 - if name=="count": + if name == "count": return 1 - if name=="weak_count": + if name == "weak_count": return 2 return -1 - def get_child_at_index(self,index): + def get_child_at_index(self, index): if index == 0: return self.ptr if index == 1: - if self.cntrl == None: + 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) + 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: + 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) + 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) + self.ptr = self.valobj.GetChildMemberWithName( + '__ptr_') # .Cast(self.element_ptr_type) cntrl = self.valobj.GetChildMemberWithName('__cntrl_') if cntrl.GetValueAsUnsigned(0): self.cntrl = cntrl.Dereference() @@ -765,21 +832,36 @@ class stdsharedptr_SynthProvider: 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') +# 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 diff --git a/lldb/examples/synthetic/unordered_multi.py b/lldb/examples/synthetic/unordered_multi.py index 3389a01..da7ba45 100644 --- a/lldb/examples/synthetic/unordered_multi.py +++ b/lldb/examples/synthetic/unordered_multi.py @@ -2,109 +2,123 @@ 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') + + 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') |