940 lines · python
1import lldb.formatters.Logger2 3# C++ STL formatters for LLDB4# As there are many versions of the libstdc++, you are encouraged to look at the STL5# implementation for your platform before relying on these formatters to do the right6# thing for your setup7 8 9def ForwardListSummaryProvider(valobj, dict):10 list_capping_size = valobj.GetTarget().GetMaximumNumberOfChildrenToDisplay()11 text = "size=" + str(valobj.GetNumChildren())12 if valobj.GetNumChildren() > list_capping_size:13 return "(capped) " + text14 else:15 return text16 17 18def StdOptionalSummaryProvider(valobj, dict):19 has_value = valobj.GetNumChildren() > 020 # We add wrapping spaces for consistency with the libcxx formatter21 return " Has Value=" + ("true" if has_value else "false") + " "22 23 24class StdOptionalSynthProvider:25 def __init__(self, valobj, dict):26 self.valobj = valobj27 28 def update(self):29 try:30 self.payload = self.valobj.GetChildMemberWithName("_M_payload")31 self.value = self.payload.GetChildMemberWithName("_M_payload")32 self.has_value = (33 self.payload.GetChildMemberWithName("_M_engaged").GetValueAsUnsigned(0)34 != 035 )36 except:37 self.has_value = False38 return False39 40 def num_children(self):41 return 1 if self.has_value else 042 43 def get_child_index(self, name):44 return 045 46 def get_child_at_index(self, index):47 # some versions of libstdcpp have an additional _M_value child with the actual value48 possible_value = self.value.GetChildMemberWithName("_M_value")49 if possible_value.IsValid():50 return possible_value.Clone("Value")51 return self.value.Clone("Value")52 53 54"""55 This formatter can be applied to all56 unordered map-like structures (unordered_map, unordered_multimap, unordered_set, unordered_multiset)57"""58 59 60class StdUnorderedMapSynthProvider:61 def __init__(self, valobj, dict):62 self.valobj = valobj63 self.count = None64 65 def extract_type(self):66 head_type = self.head.GetType().GetCanonicalType()67 data_type = head_type.GetTemplateArgumentType(1)68 return data_type69 70 def update(self):71 # preemptively setting this to None - we might end up changing our mind72 # later73 self.count = None74 try:75 self.head = self.valobj.GetChildMemberWithName("_M_h")76 self.before_begin = self.head.GetChildMemberWithName("_M_before_begin")77 self.next = self.before_begin.GetChildMemberWithName("_M_nxt")78 self.data_type = self.extract_type()79 self.skip_size = self.next.GetType().GetByteSize()80 self.data_size = self.data_type.GetByteSize()81 if (not self.data_type.IsValid()) or (not self.next.IsValid()):82 self.count = 083 except:84 self.count = 085 return False86 87 def get_child_index(self, name):88 try:89 return int(name.lstrip("[").rstrip("]"))90 except:91 return -192 93 def get_child_at_index(self, index):94 logger = lldb.formatters.Logger.Logger()95 logger >> "Being asked to fetch child[" + str(index) + "]"96 if index < 0:97 return None98 if index >= self.num_children():99 return None100 try:101 offset = index102 current = self.next103 while offset > 0:104 current = current.GetChildMemberWithName("_M_nxt")105 offset = offset - 1106 return current.CreateChildAtOffset(107 "[" + str(index) + "]", self.skip_size, self.data_type108 )109 110 except:111 logger >> "Cannot get child"112 return None113 114 def num_children(self):115 if self.count is None:116 self.count = self.num_children_impl()117 return self.count118 119 def num_children_impl(self):120 logger = lldb.formatters.Logger.Logger()121 try:122 count = self.head.GetChildMemberWithName(123 "_M_element_count"124 ).GetValueAsUnsigned(0)125 return count126 except:127 logger >> "Could not determine the size"128 return 0129 130 131class AbstractListSynthProvider:132 def __init__(self, valobj, dict, has_prev):133 """134 :param valobj: The value object of the list135 :param dict: A dict with metadata provided by LLDB136 :param has_prev: Whether the list supports a 'prev' pointer besides a 'next' one137 """138 logger = lldb.formatters.Logger.Logger()139 self.valobj = valobj140 self.count = None141 self.has_prev = has_prev142 self.list_capping_size = (143 self.valobj.GetTarget().GetMaximumNumberOfChildrenToDisplay()144 )145 logger >> "Providing synthetic children for a list named " + str(146 valobj.GetName()147 )148 149 def next_node(self, node):150 logger = lldb.formatters.Logger.Logger()151 return node.GetChildMemberWithName("_M_next")152 153 def is_valid(self, node):154 logger = lldb.formatters.Logger.Logger()155 valid = self.value(self.next_node(node)) != self.get_end_of_list_address()156 if valid:157 logger >> "%s is valid" % str(self.valobj.GetName())158 else:159 logger >> "synthetic value is not valid"160 return valid161 162 def value(self, node):163 logger = lldb.formatters.Logger.Logger()164 value = node.GetValueAsUnsigned()165 logger >> "synthetic value for {}: {}".format(str(self.valobj.GetName()), value)166 return value167 168 # Floyd's cycle-finding algorithm169 # try to detect if this list has a loop170 def has_loop(self):171 global _list_uses_loop_detector172 logger = lldb.formatters.Logger.Logger()173 if not _list_uses_loop_detector:174 logger >> "Asked not to use loop detection"175 return False176 slow = self.next177 fast1 = self.next178 fast2 = self.next179 while self.is_valid(slow):180 slow_value = self.value(slow)181 fast1 = self.next_node(fast2)182 fast2 = self.next_node(fast1)183 if self.value(fast1) == slow_value or self.value(fast2) == slow_value:184 return True185 slow = self.next_node(slow)186 return False187 188 def num_children(self):189 logger = lldb.formatters.Logger.Logger()190 if self.count is None:191 # libstdc++ 6.0.21 added dedicated count field.192 count_child = self.node.GetChildMemberWithName("_M_data")193 if count_child and count_child.IsValid():194 self.count = count_child.GetValueAsUnsigned(0)195 if self.count is None:196 self.count = self.num_children_impl()197 return self.count198 199 def num_children_impl(self):200 logger = lldb.formatters.Logger.Logger()201 try:202 # After a std::list has been initialized, both next and prev will203 # be non-NULL204 next_val = self.next.GetValueAsUnsigned(0)205 if next_val == 0:206 return 0207 if self.has_loop():208 return 0209 if self.has_prev:210 prev_val = self.prev.GetValueAsUnsigned(0)211 if prev_val == 0:212 return 0213 if next_val == self.node_address:214 return 0215 if next_val == prev_val:216 return 1217 size = 1218 current = self.next219 while (220 current.GetChildMemberWithName("_M_next").GetValueAsUnsigned(0)221 != self.get_end_of_list_address()222 ):223 current = current.GetChildMemberWithName("_M_next")224 if not current.IsValid():225 break226 size = size + 1227 if size >= self.list_capping_size:228 break229 230 return size231 except:232 logger >> "Error determining the size"233 return 0234 235 def get_child_index(self, name):236 logger = lldb.formatters.Logger.Logger()237 try:238 return int(name.lstrip("[").rstrip("]"))239 except:240 return -1241 242 def get_child_at_index(self, index):243 logger = lldb.formatters.Logger.Logger()244 logger >> "Fetching child " + str(index)245 if index < 0:246 return None247 if index >= self.num_children():248 return None249 try:250 offset = index251 current = self.next252 while offset > 0:253 current = current.GetChildMemberWithName("_M_next")254 offset = offset - 1255 # C++ lists store the data of a node after its pointers. In the case of a forward list, there's just one pointer (next), and256 # in the case of a double-linked list, there's an additional pointer (prev).257 return current.CreateChildAtOffset(258 "[" + str(index) + "]",259 (2 if self.has_prev else 1) * current.GetType().GetByteSize(),260 self.data_type,261 )262 except:263 return None264 265 def extract_type(self):266 logger = lldb.formatters.Logger.Logger()267 list_type = self.valobj.GetType().GetUnqualifiedType()268 if list_type.IsReferenceType():269 list_type = list_type.GetDereferencedType()270 if list_type.GetNumberOfTemplateArguments() > 0:271 return list_type.GetTemplateArgumentType(0)272 return lldb.SBType()273 274 def update(self):275 logger = lldb.formatters.Logger.Logger()276 # preemptively setting this to None - we might end up changing our mind277 # later278 self.count = None279 try:280 self.impl = self.valobj.GetChildMemberWithName("_M_impl")281 self.data_type = self.extract_type()282 if (not self.data_type.IsValid()) or (not self.impl.IsValid()):283 self.count = 0284 elif not self.updateNodes():285 self.count = 0286 else:287 self.data_size = self.data_type.GetByteSize()288 except:289 self.count = 0290 return False291 292 """293 Method is used to extract the list pointers into the variables (e.g self.node, self.next, and optionally to self.prev)294 and is mandatory to be overriden in each AbstractListSynthProvider subclass.295 This should return True or False depending on wheter it found valid data.296 """297 298 def updateNodes(self):299 raise NotImplementedError300 301 def has_children(self):302 return True303 304 """305 Method is used to identify if a node traversal has reached its end306 and is mandatory to be overriden in each AbstractListSynthProvider subclass307 """308 309 def get_end_of_list_address(self):310 raise NotImplementedError311 312 313class StdForwardListSynthProvider(AbstractListSynthProvider):314 def __init__(self, valobj, dict):315 has_prev = False316 super().__init__(valobj, dict, has_prev)317 318 def updateNodes(self):319 self.node = self.impl.GetChildMemberWithName("_M_head")320 self.next = self.node.GetChildMemberWithName("_M_next")321 if (not self.node.IsValid()) or (not self.next.IsValid()):322 return False323 return True324 325 def get_end_of_list_address(self):326 return 0327 328 329class StdListSynthProvider(AbstractListSynthProvider):330 def __init__(self, valobj, dict):331 has_prev = True332 super().__init__(valobj, dict, has_prev)333 334 def updateNodes(self):335 self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0)336 self.node = self.impl.GetChildMemberWithName("_M_node")337 self.prev = self.node.GetChildMemberWithName("_M_prev")338 self.next = self.node.GetChildMemberWithName("_M_next")339 if (340 self.node_address == 0341 or (not self.node.IsValid())342 or (not self.next.IsValid())343 or (not self.prev.IsValid())344 ):345 return False346 return True347 348 def get_end_of_list_address(self):349 return self.node_address350 351 352class StdVectorSynthProvider:353 class StdVectorImplementation(object):354 def __init__(self, valobj):355 self.valobj = valobj356 self.count = None357 358 def num_children(self):359 if self.count is None:360 self.count = self.num_children_impl()361 return self.count362 363 def num_children_impl(self):364 try:365 start_val = self.start.GetValueAsUnsigned(0)366 finish_val = self.finish.GetValueAsUnsigned(0)367 end_val = self.end.GetValueAsUnsigned(0)368 # Before a vector has been constructed, it will contain bad values369 # so we really need to be careful about the length we return since370 # uninitialized data can cause us to return a huge number. We need371 # to also check for any of the start, finish or end of storage values372 # being zero (NULL). If any are, then this vector has not been373 # initialized yet and we should return zero374 375 # Make sure nothing is NULL376 if start_val == 0 or finish_val == 0 or end_val == 0:377 return 0378 # Make sure start is less than finish379 if start_val >= finish_val:380 return 0381 # Make sure finish is less than or equal to end of storage382 if finish_val > end_val:383 return 0384 385 # if we have a struct (or other data type that the compiler pads to native word size)386 # this check might fail, unless the sizeof() we get is itself incremented to take the387 # padding bytes into account - on current clang it looks like388 # this is the case389 num_children = finish_val - start_val390 if (num_children % self.data_size) != 0:391 return 0392 else:393 num_children = num_children // self.data_size394 return num_children395 except:396 return 0397 398 def get_child_at_index(self, index):399 logger = lldb.formatters.Logger.Logger()400 logger >> "Retrieving child " + str(index)401 if index < 0:402 return None403 if index >= self.num_children():404 return None405 try:406 offset = index * self.data_size407 return self.start.CreateChildAtOffset(408 "[" + str(index) + "]", offset, self.data_type409 )410 except:411 return None412 413 def update(self):414 # preemptively setting this to None - we might end up changing our415 # mind later416 self.count = None417 try:418 impl = self.valobj.GetChildMemberWithName("_M_impl")419 self.start = impl.GetChildMemberWithName("_M_start")420 self.finish = impl.GetChildMemberWithName("_M_finish")421 self.end = impl.GetChildMemberWithName("_M_end_of_storage")422 self.data_type = self.start.GetType().GetPointeeType()423 self.data_size = self.data_type.GetByteSize()424 # if any of these objects is invalid, it means there is no425 # point in trying to fetch anything426 if (427 self.start.IsValid()428 and self.finish.IsValid()429 and self.end.IsValid()430 and self.data_type.IsValid()431 ):432 self.count = None433 else:434 self.count = 0435 except:436 self.count = 0437 return False438 439 class StdVBoolImplementation(object):440 def __init__(self, valobj, bool_type):441 self.valobj = valobj442 self.bool_type = bool_type443 self.valid = False444 445 def num_children(self):446 if self.valid:447 start = self.start_p.GetValueAsUnsigned(0)448 finish = self.finish_p.GetValueAsUnsigned(0)449 offset = self.offset.GetValueAsUnsigned(0)450 if finish >= start:451 return (finish - start) * 8 + offset452 return 0453 454 def get_child_at_index(self, index):455 if index >= self.num_children():456 return None457 element_type = self.start_p.GetType().GetPointeeType()458 element_bits = 8 * element_type.GetByteSize()459 element_offset = (index // element_bits) * element_type.GetByteSize()460 bit_offset = index % element_bits461 element = self.start_p.CreateChildAtOffset(462 "[" + str(index) + "]", element_offset, element_type463 )464 bit = element.GetValueAsUnsigned(0) & (1 << bit_offset)465 return self.valobj.CreateBoolValue("[%d]" % index, bool(bit))466 467 def update(self):468 try:469 m_impl = self.valobj.GetChildMemberWithName("_M_impl")470 self.m_start = m_impl.GetChildMemberWithName("_M_start")471 self.m_finish = m_impl.GetChildMemberWithName("_M_finish")472 self.start_p = self.m_start.GetChildMemberWithName("_M_p")473 self.finish_p = self.m_finish.GetChildMemberWithName("_M_p")474 self.offset = self.m_finish.GetChildMemberWithName("_M_offset")475 if (476 self.offset.IsValid()477 and self.start_p.IsValid()478 and self.finish_p.IsValid()479 ):480 self.valid = True481 else:482 self.valid = False483 except:484 self.valid = False485 return False486 487 def __init__(self, valobj, dict):488 logger = lldb.formatters.Logger.Logger()489 first_template_arg_type = valobj.GetType().GetTemplateArgumentType(0)490 if str(first_template_arg_type.GetName()) == "bool":491 self.impl = self.StdVBoolImplementation(valobj, first_template_arg_type)492 else:493 self.impl = self.StdVectorImplementation(valobj)494 logger >> "Providing synthetic children for a vector named " + str(495 valobj.GetName()496 )497 498 def num_children(self):499 return self.impl.num_children()500 501 def get_child_index(self, name):502 try:503 return int(name.lstrip("[").rstrip("]"))504 except:505 return -1506 507 def get_child_at_index(self, index):508 return self.impl.get_child_at_index(index)509 510 def update(self):511 return self.impl.update()512 513 def has_children(self):514 return True515 516 """517 This formatter can be applied to all518 map-like structures (map, multimap, set, multiset)519 """520 521 522class StdMapLikeSynthProvider:523 def __init__(self, valobj, dict):524 logger = lldb.formatters.Logger.Logger()525 self.valobj = valobj526 self.count = None527 self.kind = self.get_object_kind(valobj)528 (529 logger530 >> "Providing synthetic children for a "531 + self.kind532 + " named "533 + str(valobj.GetName())534 )535 536 def get_object_kind(self, valobj):537 type_name = valobj.GetTypeName()538 for kind in ["multiset", "multimap", "set", "map"]:539 if kind in type_name:540 return kind541 return type_name542 543 # we need this function as a temporary workaround for rdar://problem/10801549544 # which prevents us from extracting the std::pair<K,V> SBType out of the template545 # arguments for _Rep_Type _M_t in the object itself - because we have to make up the546 # typename and then find it, we may hit the situation were std::string has multiple547 # names but only one is actually referenced in the debug information. hence, we need548 # to replace the longer versions of std::string with the shorter one in order to be able549 # to find the type name550 def fixup_class_name(self, class_name):551 logger = lldb.formatters.Logger.Logger()552 if (553 class_name554 == "std::basic_string<char, std::char_traits<char>, std::allocator<char> >"555 ):556 return "std::basic_string<char>", True557 if (558 class_name559 == "basic_string<char, std::char_traits<char>, std::allocator<char> >"560 ):561 return "std::basic_string<char>", True562 if (563 class_name564 == "std::basic_string<char, std::char_traits<char>, std::allocator<char> >"565 ):566 return "std::basic_string<char>", True567 if (568 class_name569 == "basic_string<char, std::char_traits<char>, std::allocator<char> >"570 ):571 return "std::basic_string<char>", True572 return class_name, False573 574 def update(self):575 logger = lldb.formatters.Logger.Logger()576 # preemptively setting this to None - we might end up changing our mind577 # later578 self.count = None579 try:580 # we will set this to True if we find out that discovering a node in the object takes more steps than the overall size of the RB tree581 # if this gets set to True, then we will merrily return None for582 # any child from that moment on583 self.garbage = False584 self.Mt = self.valobj.GetChildMemberWithName("_M_t")585 self.Mimpl = self.Mt.GetChildMemberWithName("_M_impl")586 self.Mheader = self.Mimpl.GetChildMemberWithName("_M_header")587 if not self.Mheader.IsValid():588 self.count = 0589 else:590 map_type = self.valobj.GetType()591 if map_type.IsReferenceType():592 logger >> "Dereferencing type"593 map_type = map_type.GetDereferencedType()594 595 # Get the type of std::pair<key, value>. It is the first template596 # argument type of the 4th template argument to std::map.597 allocator_type = map_type.GetTemplateArgumentType(3)598 self.data_type = allocator_type.GetTemplateArgumentType(0)599 if not self.data_type:600 # GCC does not emit DW_TAG_template_type_parameter for601 # std::allocator<...>. For such a case, get the type of602 # std::pair from a member of std::map.603 rep_type = self.valobj.GetChildMemberWithName("_M_t").GetType()604 self.data_type = (605 rep_type.GetTypedefedType().GetTemplateArgumentType(1)606 )607 608 # from libstdc++ implementation of _M_root for rbtree609 self.Mroot = self.Mheader.GetChildMemberWithName("_M_parent")610 self.data_size = self.data_type.GetByteSize()611 self.skip_size = self.Mheader.GetType().GetByteSize()612 except:613 self.count = 0614 return False615 616 def num_children(self):617 logger = lldb.formatters.Logger.Logger()618 if self.count is None:619 self.count = self.num_children_impl()620 return self.count621 622 def num_children_impl(self):623 logger = lldb.formatters.Logger.Logger()624 try:625 root_ptr_val = self.node_ptr_value(self.Mroot)626 if root_ptr_val == 0:627 return 0628 count = self.Mimpl.GetChildMemberWithName(629 "_M_node_count"630 ).GetValueAsUnsigned(0)631 logger >> "I have " + str(count) + " children available"632 return count633 except:634 return 0635 636 def get_child_index(self, name):637 logger = lldb.formatters.Logger.Logger()638 try:639 return int(name.lstrip("[").rstrip("]"))640 except:641 return -1642 643 def get_child_at_index(self, index):644 logger = lldb.formatters.Logger.Logger()645 logger >> "Being asked to fetch child[" + str(index) + "]"646 if index < 0:647 return None648 if index >= self.num_children():649 return None650 if self.garbage:651 logger >> "Returning None since we are a garbage tree"652 return None653 try:654 offset = index655 current = self.left(self.Mheader)656 while offset > 0:657 current = self.increment_node(current)658 offset = offset - 1659 # skip all the base stuff and get at the data660 return current.CreateChildAtOffset(661 "[" + str(index) + "]", self.skip_size, self.data_type662 )663 except:664 return None665 666 # utility functions667 def node_ptr_value(self, node):668 logger = lldb.formatters.Logger.Logger()669 return node.GetValueAsUnsigned(0)670 671 def right(self, node):672 logger = lldb.formatters.Logger.Logger()673 return node.GetChildMemberWithName("_M_right")674 675 def left(self, node):676 logger = lldb.formatters.Logger.Logger()677 return node.GetChildMemberWithName("_M_left")678 679 def parent(self, node):680 logger = lldb.formatters.Logger.Logger()681 return node.GetChildMemberWithName("_M_parent")682 683 # from libstdc++ implementation of iterator for rbtree684 def increment_node(self, node):685 logger = lldb.formatters.Logger.Logger()686 max_steps = self.num_children()687 if self.node_ptr_value(self.right(node)) != 0:688 x = self.right(node)689 max_steps -= 1690 while self.node_ptr_value(self.left(x)) != 0:691 x = self.left(x)692 max_steps -= 1693 logger >> str(max_steps) + " more to go before giving up"694 if max_steps <= 0:695 self.garbage = True696 return None697 return x698 else:699 x = node700 y = self.parent(x)701 max_steps -= 1702 while self.node_ptr_value(x) == self.node_ptr_value(self.right(y)):703 x = y704 y = self.parent(y)705 max_steps -= 1706 logger >> str(max_steps) + " more to go before giving up"707 if max_steps <= 0:708 self.garbage = True709 return None710 if self.node_ptr_value(self.right(x)) != self.node_ptr_value(y):711 x = y712 return x713 714 def has_children(self):715 return True716 717 718_list_uses_loop_detector = True719 720 721class StdDequeSynthProvider:722 def __init__(self, valobj, d):723 self.valobj = valobj724 self.pointer_size = self.valobj.GetProcess().GetAddressByteSize()725 self.count = None726 self.block_size = -1727 self.element_size = -1728 self.find_block_size()729 730 def find_block_size(self):731 # in order to use the deque we must have the block size, or else732 # it's impossible to know what memory addresses are valid733 self.element_type = self.valobj.GetType().GetTemplateArgumentType(0)734 if not self.element_type.IsValid():735 return736 self.element_size = self.element_type.GetByteSize()737 # The block size (i.e. number of elements per subarray) is defined in738 # this piece of code, so we need to replicate it.739 #740 # #define _GLIBCXX_DEQUE_BUF_SIZE 512741 #742 # return (__size < _GLIBCXX_DEQUE_BUF_SIZE743 # ? size_t(_GLIBCXX_DEQUE_BUF_SIZE / __size) : size_t(1));744 if self.element_size < 512:745 self.block_size = 512 // self.element_size746 else:747 self.block_size = 1748 749 def num_children(self):750 if self.count is None:751 return 0752 return self.count753 754 def has_children(self):755 return True756 757 def get_child_index(self, name):758 try:759 return int(name.lstrip("[").rstrip("]"))760 except:761 return -1762 763 def get_child_at_index(self, index):764 if index < 0 or self.count is None:765 return None766 if index >= self.num_children():767 return None768 try:769 name = "[" + str(index) + "]"770 # We first look for the element in the first subarray,771 # which might be incomplete.772 if index < self.first_node_size:773 # The following statement is valid because self.first_elem is the pointer774 # to the first element775 return self.first_elem.CreateChildAtOffset(776 name, index * self.element_size, self.element_type777 )778 779 # Now the rest of the subarrays except for maybe the last one780 # are going to be complete, so the final expression is simpler781 i, j = divmod(index - self.first_node_size, self.block_size)782 783 # We first move to the beginning of the node/subarray were our element is784 node = self.start_node.CreateChildAtOffset(785 "",786 (1 + i) * self.valobj.GetProcess().GetAddressByteSize(),787 self.element_type.GetPointerType(),788 )789 return node.CreateChildAtOffset(790 name, j * self.element_size, self.element_type791 )792 793 except:794 return None795 796 def update(self):797 logger = lldb.formatters.Logger.Logger()798 self.count = 0799 try:800 # A deque is effectively a two-dim array, with fixed width.801 # However, only a subset of this memory contains valid data802 # since a deque may have some slack at the front and back in803 # order to have O(1) insertion at both ends.804 # The rows in active use are delimited by '_M_start' and805 # '_M_finish'.806 #807 # To find the elements that are actually constructed, the 'start'808 # variable tells which element in this NxM array is the 0th809 # one.810 if self.block_size < 0 or self.element_size < 0:811 return False812 813 count = 0814 815 impl = self.valobj.GetChildMemberWithName("_M_impl")816 817 # we calculate the size of the first node (i.e. first internal array)818 self.start = impl.GetChildMemberWithName("_M_start")819 self.start_node = self.start.GetChildMemberWithName("_M_node")820 first_node_address = self.start_node.GetValueAsUnsigned(0)821 first_node_last_elem = self.start.GetChildMemberWithName(822 "_M_last"823 ).GetValueAsUnsigned(0)824 self.first_elem = self.start.GetChildMemberWithName("_M_cur")825 first_node_first_elem = self.first_elem.GetValueAsUnsigned(0)826 827 finish = impl.GetChildMemberWithName("_M_finish")828 last_node_address = finish.GetChildMemberWithName(829 "_M_node"830 ).GetValueAsUnsigned(0)831 last_node_first_elem = finish.GetChildMemberWithName(832 "_M_first"833 ).GetValueAsUnsigned(0)834 last_node_last_elem = finish.GetChildMemberWithName(835 "_M_cur"836 ).GetValueAsUnsigned(0)837 838 if (839 first_node_first_elem == 0840 or first_node_last_elem == 0841 or first_node_first_elem > first_node_last_elem842 ):843 return False844 if (845 last_node_first_elem == 0846 or last_node_last_elem == 0847 or last_node_first_elem > last_node_last_elem848 ):849 return False850 851 if last_node_address == first_node_address:852 self.first_node_size = (853 last_node_last_elem - first_node_first_elem854 ) // self.element_size855 count += self.first_node_size856 else:857 self.first_node_size = (858 first_node_last_elem - first_node_first_elem859 ) // self.element_size860 count += self.first_node_size861 862 # we calculate the size of the last node863 finish = impl.GetChildMemberWithName("_M_finish")864 last_node_address = finish.GetChildMemberWithName(865 "_M_node"866 ).GetValueAsUnsigned(0)867 count += (868 last_node_last_elem - last_node_first_elem869 ) // self.element_size870 871 # we calculate the size of the intermediate nodes872 num_intermediate_nodes = (873 last_node_address - first_node_address - 1874 ) // self.valobj.GetProcess().GetAddressByteSize()875 count += self.block_size * num_intermediate_nodes876 self.count = count877 except:878 pass879 return False880 881 882class VariantSynthProvider:883 def __init__(self, valobj, dict):884 self.raw_obj = valobj.GetNonSyntheticValue()885 self.is_valid = False886 self.index = None887 self.data_obj = None888 889 def update(self):890 try:891 self.index = self.raw_obj.GetChildMemberWithName(892 "_M_index"893 ).GetValueAsSigned(-1)894 self.is_valid = self.index != -1895 self.data_obj = self.raw_obj.GetChildMemberWithName("_M_u")896 except:897 self.is_valid = False898 return False899 900 def has_children(self):901 return True902 903 def num_children(self):904 return 1 if self.is_valid else 0905 906 def get_child_index(self, name):907 return 0908 909 def get_child_at_index(self, index):910 if not self.is_valid:911 return None912 cur = 0913 node = self.data_obj914 while cur < self.index:915 node = node.GetChildMemberWithName("_M_rest")916 cur += 1917 918 # _M_storage's type depends on variant field's type "_Type".919 # 1. if '_Type' is literal type: _Type _M_storage.920 # 2. otherwise, __gnu_cxx::__aligned_membuf<_Type> _M_storage.921 #922 # For 2. we have to cast it to underlying template _Type.923 924 value = node.GetChildMemberWithName("_M_first").GetChildMemberWithName(925 "_M_storage"926 )927 template_type = value.GetType().GetTemplateArgumentType(0)928 929 # Literal type will return None for GetTemplateArgumentType(0)930 if (931 template_type932 and "__gnu_cxx::__aligned_membuf" in value.GetType().GetDisplayTypeName()933 and template_type.IsValid()934 ):935 value = value.Cast(template_type)936 937 if value.IsValid():938 return value.Clone("Value")939 return None940