brintos

brintos / llvm-project-archived public Read only

0
0
Text · 35.0 KiB · 549255e Raw
988 lines · python
1from enum import Enum2from sys import stderr3import sys4import lldb5import lldb.formatters.Logger6 7# libcxx STL formatters for LLDB8# These formatters are based upon the implementation of libc++ that9# ships with current releases of OS X - They will not work for other implementations10# of the standard C++ library - and they are bound to use the11# libc++-specific namespace12 13# the std::string summary is just an example for your convenience14# the actual summary that LLDB uses is C++ code inside the debugger's own core15 16# this could probably be made more efficient but since it only reads a handful of bytes at a time17# we probably don't need to worry too much about this for the time being18 19 20def make_string(F, L):21    strval = ""22    G = F.GetData().uint823    for X in range(L):24        V = G[X]25        if V == 0:26            break27        strval = strval + chr(V % 256)28    return '"' + strval + '"'29 30 31# if we ever care about big-endian, these two functions might need to change32 33 34def is_short_string(value):35    return True if (value & 1) == 0 else False36 37 38def extract_short_size(value):39    return (value >> 1) % 25640 41 42# some of the members of libc++ std::string are anonymous or have internal names that convey43# no external significance - we access them by index since this saves a name lookup that would add44# no information for readers of the code, but when possible try to use45# meaningful variable names46 47 48def stdstring_SummaryProvider(valobj, dict):49    logger = lldb.formatters.Logger.Logger()50    r = valobj.GetChildAtIndex(0)51    B = r.GetChildAtIndex(0)52    first = B.GetChildAtIndex(0)53    D = first.GetChildAtIndex(0)54    l = D.GetChildAtIndex(0)55    s = D.GetChildAtIndex(1)56    D20 = s.GetChildAtIndex(0)57    size_mode = D20.GetChildAtIndex(0).GetValueAsUnsigned(0)58    if is_short_string(size_mode):59        size = extract_short_size(size_mode)60        return make_string(s.GetChildAtIndex(1), size)61    else:62        data_ptr = l.GetChildAtIndex(2)63        size_vo = l.GetChildAtIndex(1)64        # the NULL terminator must be accounted for65        size = size_vo.GetValueAsUnsigned(0) + 166        if size <= 1 or size is None:  # should never be the case67            return '""'68        try:69            data = data_ptr.GetPointeeData(0, size)70        except:71            return '""'72        error = lldb.SBError()73        strval = data.GetString(error, 0)74        if error.Fail():75            return "<error:" + error.GetCString() + ">"76        else:77            return '"' + strval + '"'78 79 80def get_buffer_end(buffer, begin):81    """82    Returns a pointer to where the next element would be pushed.83 84    For libc++'s stable ABI and unstable < LLVM 22, returns `__end_`.85    For libc++'s unstable ABI, returns `__begin_ + __size_`.86    """87    map_end = buffer.GetChildMemberWithName("__end_")88    if map_end.IsValid():89        return map_end.GetValueAsUnsigned(0)90    map_size = buffer.GetChildMemberWithName("__size_").GetValueAsUnsigned(0)91    return begin + map_size92 93 94def get_buffer_endcap(parent, buffer, begin, has_compressed_pair_layout, is_size_based):95    """96    Returns a pointer to the end of the buffer.97 98    For libc++'s stable ABI and unstable < LLVM 22, returns:99        * `__end_cap_`, if `__compressed_pair` is being used100        * `__cap_`, otherwise101    For libc++'s unstable ABI, returns `__begin_ + __cap_`.102    """103    if has_compressed_pair_layout:104        map_endcap = parent._get_value_of_compressed_pair(105            buffer.GetChildMemberWithName("__end_cap_")106        )107    elif buffer.GetType().GetNumberOfDirectBaseClasses() == 1:108        # LLVM 22's __split_buffer is derived from a base class that describes its layout. When the109        # compressed pair ABI is required, we also use an anonymous struct. Per [#158131], LLDB110        # is unable to access members of an anonymous struct to a base class, through the derived111        # class. This means that in order to access the compressed pair's pointer, we need to first112        # get to its base class.113        #114        # [#158131]: https://github.com/llvm/llvm-project/issues/158131115        buffer = buffer.GetChildAtIndex(0)116        if is_size_based:117            map_endcap = buffer.GetChildMemberWithName("__cap_")118        else:119            map_endcap = buffer.GetChildMemberWithName("__back_cap_")120        map_endcap = map_endcap.GetValueAsUnsigned(0)121    else:122        map_endcap = buffer.GetChildMemberWithName("__cap_")123        if not map_endcap.IsValid():124            map_endcap = buffer.GetChildMemberWithName("__end_cap_")125        map_endcap = map_endcap.GetValueAsUnsigned(0)126 127    if is_size_based:128        return begin + map_endcap129 130    return map_endcap131 132 133class stdvector_SynthProvider:134    def __init__(self, valobj, dict):135        logger = lldb.formatters.Logger.Logger()136        self.valobj = valobj137 138    def num_children(self):139        logger = lldb.formatters.Logger.Logger()140        try:141            start_val = self.start.GetValueAsUnsigned(0)142            finish_val = self.finish.GetValueAsUnsigned(0)143            # Before a vector has been constructed, it will contain bad values144            # so we really need to be careful about the length we return since145            # uninitialized data can cause us to return a huge number. We need146            # to also check for any of the start, finish or end of storage values147            # being zero (NULL). If any are, then this vector has not been148            # initialized yet and we should return zero149 150            # Make sure nothing is NULL151            if start_val == 0 or finish_val == 0:152                return 0153            # Make sure start is less than finish154            if start_val >= finish_val:155                return 0156 157            num_children = finish_val - start_val158            if (num_children % self.data_size) != 0:159                return 0160            else:161                num_children = num_children / self.data_size162            return num_children163        except:164            return 0165 166    def get_child_index(self, name):167        logger = lldb.formatters.Logger.Logger()168        try:169            return int(name.lstrip("[").rstrip("]"))170        except:171            return -1172 173    def get_child_at_index(self, index):174        logger = lldb.formatters.Logger.Logger()175        logger >> "Retrieving child " + str(index)176        if index < 0:177            return None178        if index >= self.num_children():179            return None180        try:181            offset = index * self.data_size182            return self.start.CreateChildAtOffset(183                "[" + str(index) + "]", offset, self.data_type184            )185        except:186            return None187 188    def update(self):189        logger = lldb.formatters.Logger.Logger()190        try:191            self.start = self.valobj.GetChildMemberWithName("__begin_")192            self.finish = self.valobj.GetChildMemberWithName("__end_")193            # the purpose of this field is unclear, but it is the only field whose type is clearly T* for a vector<T>194            # if this ends up not being correct, we can use the APIs to get at195            # template arguments196            data_type_finder = self.valobj.GetChildMemberWithName(197                "__end_cap_"198            ).GetChildMemberWithName("__first_")199            self.data_type = data_type_finder.GetType().GetPointeeType()200            self.data_size = self.data_type.GetByteSize()201        except:202            pass203 204    def has_children(self):205        return True206 207 208# Just an example: the actual summary is produced by a summary string:209# size=${svar%#}210 211 212def stdvector_SummaryProvider(valobj, dict):213    prov = stdvector_SynthProvider(valobj, None)214    return "size=" + str(prov.num_children())215 216 217class stdlist_entry:218    def __init__(self, entry):219        logger = lldb.formatters.Logger.Logger()220        self.entry = entry221 222    def _next_impl(self):223        logger = lldb.formatters.Logger.Logger()224        return stdlist_entry(self.entry.GetChildMemberWithName("__next_"))225 226    def _prev_impl(self):227        logger = lldb.formatters.Logger.Logger()228        return stdlist_entry(self.entry.GetChildMemberWithName("__prev_"))229 230    def _value_impl(self):231        logger = lldb.formatters.Logger.Logger()232        return self.entry.GetValueAsUnsigned(0)233 234    def _isnull_impl(self):235        logger = lldb.formatters.Logger.Logger()236        return self._value_impl() == 0237 238    def _sbvalue_impl(self):239        logger = lldb.formatters.Logger.Logger()240        return self.entry241 242    next = property(_next_impl, None)243    value = property(_value_impl, None)244    is_null = property(_isnull_impl, None)245    sbvalue = property(_sbvalue_impl, None)246 247 248class stdlist_iterator:249    def increment_node(self, node):250        logger = lldb.formatters.Logger.Logger()251        if node.is_null:252            return None253        return node.next254 255    def __init__(self, node):256        logger = lldb.formatters.Logger.Logger()257        # we convert the SBValue to an internal node object on entry258        self.node = stdlist_entry(node)259 260    def value(self):261        logger = lldb.formatters.Logger.Logger()262        return self.node.sbvalue  # and return the SBValue back on exit263 264    def next(self):265        logger = lldb.formatters.Logger.Logger()266        node = self.increment_node(self.node)267        if node is not None and node.sbvalue.IsValid() and not (node.is_null):268            self.node = node269            return self.value()270        else:271            return None272 273    def advance(self, N):274        logger = lldb.formatters.Logger.Logger()275        if N < 0:276            return None277        if N == 0:278            return self.value()279        if N == 1:280            return self.next()281        while N > 0:282            self.next()283            N = N - 1284        return self.value()285 286 287class stdlist_SynthProvider:288    def __init__(self, valobj, dict):289        logger = lldb.formatters.Logger.Logger()290        self.valobj = valobj291        self.count = None292 293    def next_node(self, node):294        logger = lldb.formatters.Logger.Logger()295        return node.GetChildMemberWithName("__next_")296 297    def value(self, node):298        logger = lldb.formatters.Logger.Logger()299        return node.GetValueAsUnsigned()300 301    # Floyd's cycle-finding algorithm302    # try to detect if this list has a loop303    def has_loop(self):304        global _list_uses_loop_detector305        logger = lldb.formatters.Logger.Logger()306        if not _list_uses_loop_detector:307            logger >> "Asked not to use loop detection"308            return False309        slow = stdlist_entry(self.head)310        fast1 = stdlist_entry(self.head)311        fast2 = stdlist_entry(self.head)312        while slow.next.value != self.node_address:313            slow_value = slow.value314            fast1 = fast2.next315            fast2 = fast1.next316            if fast1.value == slow_value or fast2.value == slow_value:317                return True318            slow = slow.next319        return False320 321    def num_children(self):322        global _list_capping_size323        logger = lldb.formatters.Logger.Logger()324        if self.count is None:325            self.count = self.num_children_impl()326            if self.count > _list_capping_size:327                self.count = _list_capping_size328        return self.count329 330    def num_children_impl(self):331        global _list_capping_size332        logger = lldb.formatters.Logger.Logger()333        try:334            next_val = self.head.GetValueAsUnsigned(0)335            prev_val = self.tail.GetValueAsUnsigned(0)336            # After a std::list has been initialized, both next and prev will337            # be non-NULL338            if next_val == 0 or prev_val == 0:339                return 0340            if next_val == self.node_address:341                return 0342            if next_val == prev_val:343                return 1344            if self.has_loop():345                return 0346            size = 2347            current = stdlist_entry(self.head)348            while current.next.value != self.node_address:349                size = size + 1350                current = current.next351                if size > _list_capping_size:352                    return _list_capping_size353            return size - 1354        except:355            return 0356 357    def get_child_index(self, name):358        logger = lldb.formatters.Logger.Logger()359        try:360            return int(name.lstrip("[").rstrip("]"))361        except:362            return -1363 364    def get_child_at_index(self, index):365        logger = lldb.formatters.Logger.Logger()366        logger >> "Fetching child " + str(index)367        if index < 0:368            return None369        if index >= self.num_children():370            return None371        try:372            current = stdlist_iterator(self.head)373            current = current.advance(index)374            # we do not return __value_ because then all our children would be named __value_375            # we need to make a copy of __value__ with the right name -376            # unfortunate377            obj = current.GetChildMemberWithName("__value_")378            obj_data = obj.GetData()379            return self.valobj.CreateValueFromData(380                "[" + str(index) + "]", obj_data, self.data_type381            )382        except:383            return None384 385    def extract_type(self):386        logger = lldb.formatters.Logger.Logger()387        list_type = self.valobj.GetType().GetUnqualifiedType()388        if list_type.IsReferenceType():389            list_type = list_type.GetDereferencedType()390        if list_type.GetNumberOfTemplateArguments() > 0:391            data_type = list_type.GetTemplateArgumentType(0)392        else:393            data_type = None394        return data_type395 396    def update(self):397        logger = lldb.formatters.Logger.Logger()398        self.count = None399        try:400            impl = self.valobj.GetChildMemberWithName("__end_")401            self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0)402            self.head = impl.GetChildMemberWithName("__next_")403            self.tail = impl.GetChildMemberWithName("__prev_")404            self.data_type = self.extract_type()405            self.data_size = self.data_type.GetByteSize()406        except:407            pass408 409    def has_children(self):410        return True411 412 413# Just an example: the actual summary is produced by a summary string:414# size=${svar%#}415def stdlist_SummaryProvider(valobj, dict):416    prov = stdlist_SynthProvider(valobj, None)417    return "size=" + str(prov.num_children())418 419 420# a tree node - this class makes the syntax in the actual iterator nicer421# to read and maintain422 423 424class stdmap_iterator_node:425    def _left_impl(self):426        logger = lldb.formatters.Logger.Logger()427        return stdmap_iterator_node(self.node.GetChildMemberWithName("__left_"))428 429    def _right_impl(self):430        logger = lldb.formatters.Logger.Logger()431        return stdmap_iterator_node(self.node.GetChildMemberWithName("__right_"))432 433    def _parent_impl(self):434        logger = lldb.formatters.Logger.Logger()435        return stdmap_iterator_node(self.node.GetChildMemberWithName("__parent_"))436 437    def _value_impl(self):438        logger = lldb.formatters.Logger.Logger()439        return self.node.GetValueAsUnsigned(0)440 441    def _sbvalue_impl(self):442        logger = lldb.formatters.Logger.Logger()443        return self.node444 445    def _null_impl(self):446        logger = lldb.formatters.Logger.Logger()447        return self.value == 0448 449    def __init__(self, node):450        logger = lldb.formatters.Logger.Logger()451        self.node = node452 453    left = property(_left_impl, None)454    right = property(_right_impl, None)455    parent = property(_parent_impl, None)456    value = property(_value_impl, None)457    is_null = property(_null_impl, None)458    sbvalue = property(_sbvalue_impl, None)459 460 461# a Python implementation of the tree iterator used by libc++462 463 464class stdmap_iterator:465    def tree_min(self, x):466        logger = lldb.formatters.Logger.Logger()467        steps = 0468        if x.is_null:469            return None470        while not x.left.is_null:471            x = x.left472            steps += 1473            if steps > self.max_count:474                logger >> "Returning None - we overflowed"475                return None476        return x477 478    def tree_max(self, x):479        logger = lldb.formatters.Logger.Logger()480        if x.is_null:481            return None482        while not x.right.is_null:483            x = x.right484        return x485 486    def tree_is_left_child(self, x):487        logger = lldb.formatters.Logger.Logger()488        if x.is_null:489            return None490        return True if x.value == x.parent.left.value else False491 492    def increment_node(self, node):493        logger = lldb.formatters.Logger.Logger()494        if node.is_null:495            return None496        if not node.right.is_null:497            return self.tree_min(node.right)498        steps = 0499        while not self.tree_is_left_child(node):500            steps += 1501            if steps > self.max_count:502                logger >> "Returning None - we overflowed"503                return None504            node = node.parent505        return node.parent506 507    def __init__(self, node, max_count=0):508        logger = lldb.formatters.Logger.Logger()509        # we convert the SBValue to an internal node object on entry510        self.node = stdmap_iterator_node(node)511        self.max_count = max_count512 513    def value(self):514        logger = lldb.formatters.Logger.Logger()515        return self.node.sbvalue  # and return the SBValue back on exit516 517    def next(self):518        logger = lldb.formatters.Logger.Logger()519        node = self.increment_node(self.node)520        if node is not None and node.sbvalue.IsValid() and not (node.is_null):521            self.node = node522            return self.value()523        else:524            return None525 526    def advance(self, N):527        logger = lldb.formatters.Logger.Logger()528        if N < 0:529            return None530        if N == 0:531            return self.value()532        if N == 1:533            return self.next()534        while N > 0:535            if self.next() is None:536                return None537            N = N - 1538        return self.value()539 540 541class stdmap_SynthProvider:542    def __init__(self, valobj, dict):543        logger = lldb.formatters.Logger.Logger()544        self.valobj = valobj545        self.pointer_size = self.valobj.GetProcess().GetAddressByteSize()546        self.count = None547 548    def update(self):549        logger = lldb.formatters.Logger.Logger()550        self.count = None551        try:552            # 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 tree553            # if this gets set to True, then we will merrily return None for554            # any child from that moment on555            self.garbage = False556            self.tree = self.valobj.GetChildMemberWithName("__tree_")557            self.root_node = self.tree.GetChildMemberWithName("__begin_node_")558            # this data is either lazily-calculated, or cannot be inferred at this moment559            # we still need to mark it as None, meaning "please set me ASAP"560            self.data_type = None561            self.data_size = None562            self.skip_size = None563        except:564            pass565 566    def num_children(self):567        global _map_capping_size568        logger = lldb.formatters.Logger.Logger()569        if self.count is None:570            self.count = self.num_children_impl()571            if self.count > _map_capping_size:572                self.count = _map_capping_size573        return self.count574 575    def num_children_impl(self):576        logger = lldb.formatters.Logger.Logger()577        try:578            return (579                self.valobj.GetChildMemberWithName("__tree_")580                .GetChildMemberWithName("__pair3_")581                .GetChildMemberWithName("__first_")582                .GetValueAsUnsigned()583            )584        except:585            return 0586 587    def has_children(self):588        return True589 590    def get_data_type(self):591        logger = lldb.formatters.Logger.Logger()592        if self.data_type is None or self.data_size is None:593            if self.num_children() == 0:594                return False595            deref = self.root_node.Dereference()596            if not (deref.IsValid()):597                return False598            value = deref.GetChildMemberWithName("__value_")599            if not (value.IsValid()):600                return False601            self.data_type = value.GetType()602            self.data_size = self.data_type.GetByteSize()603            self.skip_size = None604            return True605        else:606            return True607 608    def get_value_offset(self, node):609        logger = lldb.formatters.Logger.Logger()610        if self.skip_size is None:611            node_type = node.GetType()612            fields_count = node_type.GetNumberOfFields()613            for i in range(fields_count):614                field = node_type.GetFieldAtIndex(i)615                if field.GetName() == "__value_":616                    self.skip_size = field.GetOffsetInBytes()617                    break618        return self.skip_size is not None619 620    def get_child_index(self, name):621        logger = lldb.formatters.Logger.Logger()622        try:623            return int(name.lstrip("[").rstrip("]"))624        except:625            return -1626 627    def get_child_at_index(self, index):628        logger = lldb.formatters.Logger.Logger()629        logger >> "Retrieving child " + str(index)630        if index < 0:631            return None632        if index >= self.num_children():633            return None634        if self.garbage:635            logger >> "Returning None since this tree is garbage"636            return None637        try:638            iterator = stdmap_iterator(self.root_node, max_count=self.num_children())639            # the debug info for libc++ std::map is such that __begin_node_ has a very nice and useful type640            # out of which we can grab the information we need - every other node has a less informative641            # type which omits all value information and only contains housekeeping information for the RB tree642            # hence, we need to know if we are at a node != 0, so that we can643            # still get at the data644            need_to_skip = index > 0645            current = iterator.advance(index)646            if current is None:647                logger >> "Tree is garbage - returning None"648                self.garbage = True649                return None650            if self.get_data_type():651                if not (need_to_skip):652                    current = current.Dereference()653                    obj = current.GetChildMemberWithName("__value_")654                    obj_data = obj.GetData()655                    # make sure we have a valid offset for the next items656                    self.get_value_offset(current)657                    # we do not return __value_ because then we would end up with a child named658                    # __value_ instead of [0]659                    return self.valobj.CreateValueFromData(660                        "[" + str(index) + "]", obj_data, self.data_type661                    )662                else:663                    # FIXME we need to have accessed item 0 before accessing664                    # any other item!665                    if self.skip_size is None:666                        (667                            logger668                            >> "You asked for item > 0 before asking for item == 0, I will fetch 0 now then retry"669                        )670                        if self.get_child_at_index(0):671                            return self.get_child_at_index(index)672                        else:673                            (674                                logger675                                >> "item == 0 could not be found. sorry, nothing can be done here."676                            )677                            return None678                    return current.CreateChildAtOffset(679                        "[" + str(index) + "]", self.skip_size, self.data_type680                    )681            else:682                (683                    logger684                    >> "Unable to infer data-type - returning None (should mark tree as garbage here?)"685                )686                return None687        except Exception as err:688            logger >> "Hit an exception: " + str(err)689            return None690 691 692# Just an example: the actual summary is produced by a summary string:693# size=${svar%#}694 695 696def stdmap_SummaryProvider(valobj, dict):697    prov = stdmap_SynthProvider(valobj, None)698    return "size=" + str(prov.num_children())699 700 701class stddeque_SynthProvider:702    def __init__(self, valobj, d):703        logger = lldb.formatters.Logger.Logger()704        logger.write("init")705        self.valobj = valobj706        self.pointer_size = self.valobj.GetProcess().GetAddressByteSize()707        self.count = None708        try:709            self.find_block_size()710        except:711            self.block_size = -1712            self.element_size = -1713        logger.write(714            "block_size=%d, element_size=%d" % (self.block_size, self.element_size)715        )716 717    def find_block_size(self):718        # in order to use the deque we must have the block size, or else719        # it's impossible to know what memory addresses are valid720        obj_type = self.valobj.GetType()721        if obj_type.IsReferenceType():722            obj_type = obj_type.GetDereferencedType()723        elif obj_type.IsPointerType():724            obj_type = obj_type.GetPointeeType()725        self.element_type = obj_type.GetTemplateArgumentType(0)726        self.element_size = self.element_type.GetByteSize()727        # The code says this, but there must be a better way:728        # template <class _Tp, class _Allocator>729        # class __deque_base {730        #    static const difference_type __block_size = sizeof(value_type) < 256 ? 4096 / sizeof(value_type) : 16;731        # }732        if self.element_size < 256:733            self.block_size = 4096 // self.element_size734        else:735            self.block_size = 16736 737    def num_children(self):738        logger = lldb.formatters.Logger.Logger()739        if self.count is None:740            return 0741        return self.count742 743    def has_children(self):744        return True745 746    def get_child_index(self, name):747        logger = lldb.formatters.Logger.Logger()748        try:749            return int(name.lstrip("[").rstrip("]"))750        except:751            return -1752 753    @staticmethod754    def _subscript(ptr: lldb.SBValue, idx: int, name: str) -> lldb.SBValue:755        """Access a pointer value as if it was an array. Returns ptr[idx]."""756        deref_t = ptr.GetType().GetPointeeType()757        offset = idx * deref_t.GetByteSize()758        return ptr.CreateChildAtOffset(name, offset, deref_t)759 760    def get_child_at_index(self, index):761        logger = lldb.formatters.Logger.Logger()762        logger.write("Fetching child " + str(index))763        if index < 0 or self.count is None:764            return None765        if index >= self.num_children():766            return None767        try:768            i, j = divmod(self.start + index, self.block_size)769            val = stddeque_SynthProvider._subscript(self.map_begin, i, "")770            return stddeque_SynthProvider._subscript(val, j, f"[{index}]")771        except:772            return None773 774    def _get_value_of_compressed_pair(self, pair):775        value = pair.GetChildMemberWithName("__value_")776        if not value.IsValid():777            # pre-r300140 member name778            value = pair.GetChildMemberWithName("__first_")779        return value.GetValueAsUnsigned(0)780 781    def update(self):782        logger = lldb.formatters.Logger.Logger()783        try:784            has_compressed_pair_layout = True785            alloc_valobj = self.valobj.GetChildMemberWithName("__alloc_")786            size_valobj = self.valobj.GetChildMemberWithName("__size_")787            if alloc_valobj.IsValid() and size_valobj.IsValid():788                has_compressed_pair_layout = False789 790            # A deque is effectively a two-dim array, with fixed width.791            # 'map' contains pointers to the rows of this array. The792            # full memory area allocated by the deque is delimited793            # by 'first' and 'end_cap'. However, only a subset of this794            # memory contains valid data since a deque may have some slack795            # at the front and back in order to have O(1) insertion at796            # both ends. The rows in active use are delimited by797            # 'begin' and 'end'.798            #799            # To find the elements that are actually constructed, the 'start'800            # variable tells which element in this NxM array is the 0th801            # one, and the 'size' element gives the number of elements802            # in the deque.803            if has_compressed_pair_layout:804                count = self._get_value_of_compressed_pair(805                    self.valobj.GetChildMemberWithName("__size_")806                )807            else:808                count = size_valobj.GetValueAsUnsigned(0)809 810            # give up now if we cant access memory reliably811            if self.block_size < 0:812                logger.write("block_size < 0")813                return814            start = self.valobj.GetChildMemberWithName("__start_").GetValueAsUnsigned(0)815 816            map_ = self.valobj.GetChildMemberWithName("__map_")817            is_size_based = map_.GetChildMemberWithName("__size_").IsValid()818            first = map_.GetChildMemberWithName("__first_")819            # LLVM 22 renames __map_.__begin_ to __map_.__front_cap_820            if not first:821                first = map_.GetChildMemberWithName("__front_cap_")822            map_first = first.GetValueAsUnsigned(0)823            self.map_begin = map_.GetChildMemberWithName("__begin_")824            map_begin = self.map_begin.GetValueAsUnsigned(0)825            map_end = get_buffer_end(map_, map_begin)826            map_endcap = get_buffer_endcap(827                self, map_, map_begin, has_compressed_pair_layout, is_size_based828            )829 830            # check consistency831            if not map_first <= map_begin <= map_end <= map_endcap:832                logger.write("map pointers are not monotonic")833                return834            total_rows, junk = divmod(map_endcap - map_first, self.pointer_size)835            if junk:836                logger.write("endcap-first doesnt align correctly")837                return838            active_rows, junk = divmod(map_end - map_begin, self.pointer_size)839            if junk:840                logger.write("end-begin doesnt align correctly")841                return842            start_row, junk = divmod(map_begin - map_first, self.pointer_size)843            if junk:844                logger.write("begin-first doesnt align correctly")845                return846 847            logger.write(848                "update success: count=%r, start=%r, first=%r" % (count, start, first)849            )850            # if consistent, save all we really need:851            self.count = count852            self.start = start853            self.first = first854        except:855            self.count = None856            self.start = None857            self.map_first = None858            self.map_begin = None859        return False860 861 862class stdsharedptr_SynthProvider:863    def __init__(self, valobj, d):864        logger = lldb.formatters.Logger.Logger()865        logger.write("init")866        self.valobj = valobj867        # self.element_ptr_type = self.valobj.GetType().GetTemplateArgumentType(0).GetPointerType()868        self.ptr = None869        self.cntrl = None870        process = valobj.GetProcess()871        self.endianness = process.GetByteOrder()872        self.pointer_size = process.GetAddressByteSize()873        self.count_type = valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)874 875    def num_children(self):876        return 1877 878    def has_children(self):879        return True880 881    def get_child_index(self, name):882        if name == "__ptr_":883            return 0884        if name == "count":885            return 1886        if name == "weak_count":887            return 2888        return -1889 890    def get_child_at_index(self, index):891        if index == 0:892            return self.ptr893        if index == 1:894            if self.cntrl is None:895                count = 0896            else:897                count = (898                    1899                    + self.cntrl.GetChildMemberWithName(900                        "__shared_owners_"901                    ).GetValueAsSigned()902                )903            return self.valobj.CreateValueFromData(904                "count",905                lldb.SBData.CreateDataFromUInt64Array(906                    self.endianness, self.pointer_size, [count]907                ),908                self.count_type,909            )910        if index == 2:911            if self.cntrl is None:912                count = 0913            else:914                count = (915                    1916                    + self.cntrl.GetChildMemberWithName(917                        "__shared_weak_owners_"918                    ).GetValueAsSigned()919                )920            return self.valobj.CreateValueFromData(921                "weak_count",922                lldb.SBData.CreateDataFromUInt64Array(923                    self.endianness, self.pointer_size, [count]924                ),925                self.count_type,926            )927        return None928 929    def update(self):930        logger = lldb.formatters.Logger.Logger()931        self.ptr = self.valobj.GetChildMemberWithName(932            "__ptr_"933        )  # .Cast(self.element_ptr_type)934        cntrl = self.valobj.GetChildMemberWithName("__cntrl_")935        if cntrl.GetValueAsUnsigned(0):936            self.cntrl = cntrl.Dereference()937        else:938            self.cntrl = None939 940 941# we can use two different categories for old and new formatters - type names are different enough that we should make no confusion942# talking with libc++ developer: "std::__1::class_name is set in stone943# until we decide to change the ABI. That shouldn't happen within a 5 year944# time frame"945 946 947def __lldb_init_module(debugger, dict):948    debugger.HandleCommand(949        'type summary add -F libcxx.stdstring_SummaryProvider "std::__1::string" -w libcxx'950    )951    debugger.HandleCommand(952        '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'953    )954    debugger.HandleCommand(955        'type synthetic add -l libcxx.stdvector_SynthProvider -x "^(std::__1::)vector<.+>$" -w libcxx'956    )957    debugger.HandleCommand(958        'type summary add -F libcxx.stdvector_SummaryProvider -e -x "^(std::__1::)vector<.+>$" -w libcxx'959    )960    debugger.HandleCommand(961        'type synthetic add -l libcxx.stdlist_SynthProvider -x "^(std::__1::)list<.+>$" -w libcxx'962    )963    debugger.HandleCommand(964        'type summary add -F libcxx.stdlist_SummaryProvider -e -x "^(std::__1::)list<.+>$" -w libcxx'965    )966    debugger.HandleCommand(967        'type synthetic add -l libcxx.stdmap_SynthProvider -x "^(std::__1::)map<.+> >$" -w libcxx'968    )969    debugger.HandleCommand(970        'type summary add -F libcxx.stdmap_SummaryProvider -e -x "^(std::__1::)map<.+> >$" -w libcxx'971    )972    debugger.HandleCommand("type category enable libcxx")973    debugger.HandleCommand(974        'type synthetic add -l libcxx.stddeque_SynthProvider -x "^(std::__1::)deque<.+>$" -w libcxx'975    )976    debugger.HandleCommand(977        'type synthetic add -l libcxx.stdsharedptr_SynthProvider -x "^(std::__1::)shared_ptr<.+>$" -w libcxx'978    )979    # turns out the structs look the same, so weak_ptr can be handled the same!980    debugger.HandleCommand(981        'type synthetic add -l libcxx.stdsharedptr_SynthProvider -x "^(std::__1::)weak_ptr<.+>$" -w libcxx'982    )983 984 985_map_capping_size = 255986_list_capping_size = 255987_list_uses_loop_detector = True988