brintos

brintos / llvm-project-archived public Read only

0
0
Text · 33.9 KiB · 8a41ddf Raw
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