xref: /llvm-project/lldb/examples/synthetic/libcxx.py (revision c7df10643bda4acdc9a02406a2eee8aa4ced747f)
1import lldb
2import lldb.formatters.Logger
3
4# libcxx STL formatters for LLDB
5# These formatters are based upon the implementation of libc++ that
6# ships with current releases of OS X - They will not work for other implementations
7# of the standard C++ library - and they are bound to use the
8# libc++-specific namespace
9
10# the std::string summary is just an example for your convenience
11# the actual summary that LLDB uses is C++ code inside the debugger's own core
12
13# this could probably be made more efficient but since it only reads a handful of bytes at a time
14# we probably don't need to worry too much about this for the time being
15
16
17def make_string(F, L):
18    strval = ""
19    G = F.GetData().uint8
20    for X in range(L):
21        V = G[X]
22        if V == 0:
23            break
24        strval = strval + chr(V % 256)
25    return '"' + strval + '"'
26
27
28# if we ever care about big-endian, these two functions might need to change
29
30
31def is_short_string(value):
32    return True if (value & 1) == 0 else False
33
34
35def extract_short_size(value):
36    return (value >> 1) % 256
37
38
39# some of the members of libc++ std::string are anonymous or have internal names that convey
40# no external significance - we access them by index since this saves a name lookup that would add
41# no information for readers of the code, but when possible try to use
42# meaningful variable names
43
44
45def stdstring_SummaryProvider(valobj, dict):
46    logger = lldb.formatters.Logger.Logger()
47    r = valobj.GetChildAtIndex(0)
48    B = r.GetChildAtIndex(0)
49    first = B.GetChildAtIndex(0)
50    D = first.GetChildAtIndex(0)
51    l = D.GetChildAtIndex(0)
52    s = D.GetChildAtIndex(1)
53    D20 = s.GetChildAtIndex(0)
54    size_mode = D20.GetChildAtIndex(0).GetValueAsUnsigned(0)
55    if is_short_string(size_mode):
56        size = extract_short_size(size_mode)
57        return make_string(s.GetChildAtIndex(1), size)
58    else:
59        data_ptr = l.GetChildAtIndex(2)
60        size_vo = l.GetChildAtIndex(1)
61        # the NULL terminator must be accounted for
62        size = size_vo.GetValueAsUnsigned(0) + 1
63        if size <= 1 or size is None:  # should never be the case
64            return '""'
65        try:
66            data = data_ptr.GetPointeeData(0, size)
67        except:
68            return '""'
69        error = lldb.SBError()
70        strval = data.GetString(error, 0)
71        if error.Fail():
72            return "<error:" + error.GetCString() + ">"
73        else:
74            return '"' + strval + '"'
75
76
77class stdvector_SynthProvider:
78    def __init__(self, valobj, dict):
79        logger = lldb.formatters.Logger.Logger()
80        self.valobj = valobj
81
82    def num_children(self):
83        logger = lldb.formatters.Logger.Logger()
84        try:
85            start_val = self.start.GetValueAsUnsigned(0)
86            finish_val = self.finish.GetValueAsUnsigned(0)
87            # Before a vector has been constructed, it will contain bad values
88            # so we really need to be careful about the length we return since
89            # uninitialized data can cause us to return a huge number. We need
90            # to also check for any of the start, finish or end of storage values
91            # being zero (NULL). If any are, then this vector has not been
92            # initialized yet and we should return zero
93
94            # Make sure nothing is NULL
95            if start_val == 0 or finish_val == 0:
96                return 0
97            # Make sure start is less than finish
98            if start_val >= finish_val:
99                return 0
100
101            num_children = finish_val - start_val
102            if (num_children % self.data_size) != 0:
103                return 0
104            else:
105                num_children = num_children / self.data_size
106            return num_children
107        except:
108            return 0
109
110    def get_child_index(self, name):
111        logger = lldb.formatters.Logger.Logger()
112        try:
113            return int(name.lstrip("[").rstrip("]"))
114        except:
115            return -1
116
117    def get_child_at_index(self, index):
118        logger = lldb.formatters.Logger.Logger()
119        logger >> "Retrieving child " + str(index)
120        if index < 0:
121            return None
122        if index >= self.num_children():
123            return None
124        try:
125            offset = index * self.data_size
126            return self.start.CreateChildAtOffset(
127                "[" + str(index) + "]", offset, self.data_type
128            )
129        except:
130            return None
131
132    def update(self):
133        logger = lldb.formatters.Logger.Logger()
134        try:
135            self.start = self.valobj.GetChildMemberWithName("__begin_")
136            self.finish = self.valobj.GetChildMemberWithName("__end_")
137            # the purpose of this field is unclear, but it is the only field whose type is clearly T* for a vector<T>
138            # if this ends up not being correct, we can use the APIs to get at
139            # template arguments
140            data_type_finder = self.valobj.GetChildMemberWithName(
141                "__end_cap_"
142            ).GetChildMemberWithName("__first_")
143            self.data_type = data_type_finder.GetType().GetPointeeType()
144            self.data_size = self.data_type.GetByteSize()
145        except:
146            pass
147
148    def has_children(self):
149        return True
150
151
152# Just an example: the actual summary is produced by a summary string:
153# size=${svar%#}
154
155
156def stdvector_SummaryProvider(valobj, dict):
157    prov = stdvector_SynthProvider(valobj, None)
158    return "size=" + str(prov.num_children())
159
160
161class stdlist_entry:
162    def __init__(self, entry):
163        logger = lldb.formatters.Logger.Logger()
164        self.entry = entry
165
166    def _next_impl(self):
167        logger = lldb.formatters.Logger.Logger()
168        return stdlist_entry(self.entry.GetChildMemberWithName("__next_"))
169
170    def _prev_impl(self):
171        logger = lldb.formatters.Logger.Logger()
172        return stdlist_entry(self.entry.GetChildMemberWithName("__prev_"))
173
174    def _value_impl(self):
175        logger = lldb.formatters.Logger.Logger()
176        return self.entry.GetValueAsUnsigned(0)
177
178    def _isnull_impl(self):
179        logger = lldb.formatters.Logger.Logger()
180        return self._value_impl() == 0
181
182    def _sbvalue_impl(self):
183        logger = lldb.formatters.Logger.Logger()
184        return self.entry
185
186    next = property(_next_impl, None)
187    value = property(_value_impl, None)
188    is_null = property(_isnull_impl, None)
189    sbvalue = property(_sbvalue_impl, None)
190
191
192class stdlist_iterator:
193    def increment_node(self, node):
194        logger = lldb.formatters.Logger.Logger()
195        if node.is_null:
196            return None
197        return node.next
198
199    def __init__(self, node):
200        logger = lldb.formatters.Logger.Logger()
201        # we convert the SBValue to an internal node object on entry
202        self.node = stdlist_entry(node)
203
204    def value(self):
205        logger = lldb.formatters.Logger.Logger()
206        return self.node.sbvalue  # and return the SBValue back on exit
207
208    def next(self):
209        logger = lldb.formatters.Logger.Logger()
210        node = self.increment_node(self.node)
211        if node is not None and node.sbvalue.IsValid() and not (node.is_null):
212            self.node = node
213            return self.value()
214        else:
215            return None
216
217    def advance(self, N):
218        logger = lldb.formatters.Logger.Logger()
219        if N < 0:
220            return None
221        if N == 0:
222            return self.value()
223        if N == 1:
224            return self.next()
225        while N > 0:
226            self.next()
227            N = N - 1
228        return self.value()
229
230
231class stdlist_SynthProvider:
232    def __init__(self, valobj, dict):
233        logger = lldb.formatters.Logger.Logger()
234        self.valobj = valobj
235        self.count = None
236
237    def next_node(self, node):
238        logger = lldb.formatters.Logger.Logger()
239        return node.GetChildMemberWithName("__next_")
240
241    def value(self, node):
242        logger = lldb.formatters.Logger.Logger()
243        return node.GetValueAsUnsigned()
244
245    # Floyd's cycle-finding algorithm
246    # try to detect if this list has a loop
247    def has_loop(self):
248        global _list_uses_loop_detector
249        logger = lldb.formatters.Logger.Logger()
250        if not _list_uses_loop_detector:
251            logger >> "Asked not to use loop detection"
252            return False
253        slow = stdlist_entry(self.head)
254        fast1 = stdlist_entry(self.head)
255        fast2 = stdlist_entry(self.head)
256        while slow.next.value != self.node_address:
257            slow_value = slow.value
258            fast1 = fast2.next
259            fast2 = fast1.next
260            if fast1.value == slow_value or fast2.value == slow_value:
261                return True
262            slow = slow.next
263        return False
264
265    def num_children(self):
266        global _list_capping_size
267        logger = lldb.formatters.Logger.Logger()
268        if self.count is None:
269            self.count = self.num_children_impl()
270            if self.count > _list_capping_size:
271                self.count = _list_capping_size
272        return self.count
273
274    def num_children_impl(self):
275        global _list_capping_size
276        logger = lldb.formatters.Logger.Logger()
277        try:
278            next_val = self.head.GetValueAsUnsigned(0)
279            prev_val = self.tail.GetValueAsUnsigned(0)
280            # After a std::list has been initialized, both next and prev will
281            # be non-NULL
282            if next_val == 0 or prev_val == 0:
283                return 0
284            if next_val == self.node_address:
285                return 0
286            if next_val == prev_val:
287                return 1
288            if self.has_loop():
289                return 0
290            size = 2
291            current = stdlist_entry(self.head)
292            while current.next.value != self.node_address:
293                size = size + 1
294                current = current.next
295                if size > _list_capping_size:
296                    return _list_capping_size
297            return size - 1
298        except:
299            return 0
300
301    def get_child_index(self, name):
302        logger = lldb.formatters.Logger.Logger()
303        try:
304            return int(name.lstrip("[").rstrip("]"))
305        except:
306            return -1
307
308    def get_child_at_index(self, index):
309        logger = lldb.formatters.Logger.Logger()
310        logger >> "Fetching child " + str(index)
311        if index < 0:
312            return None
313        if index >= self.num_children():
314            return None
315        try:
316            current = stdlist_iterator(self.head)
317            current = current.advance(index)
318            # we do not return __value_ because then all our children would be named __value_
319            # we need to make a copy of __value__ with the right name -
320            # unfortunate
321            obj = current.GetChildMemberWithName("__value_")
322            obj_data = obj.GetData()
323            return self.valobj.CreateValueFromData(
324                "[" + str(index) + "]", obj_data, self.data_type
325            )
326        except:
327            return None
328
329    def extract_type(self):
330        logger = lldb.formatters.Logger.Logger()
331        list_type = self.valobj.GetType().GetUnqualifiedType()
332        if list_type.IsReferenceType():
333            list_type = list_type.GetDereferencedType()
334        if list_type.GetNumberOfTemplateArguments() > 0:
335            data_type = list_type.GetTemplateArgumentType(0)
336        else:
337            data_type = None
338        return data_type
339
340    def update(self):
341        logger = lldb.formatters.Logger.Logger()
342        self.count = None
343        try:
344            impl = self.valobj.GetChildMemberWithName("__end_")
345            self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0)
346            self.head = impl.GetChildMemberWithName("__next_")
347            self.tail = impl.GetChildMemberWithName("__prev_")
348            self.data_type = self.extract_type()
349            self.data_size = self.data_type.GetByteSize()
350        except:
351            pass
352
353    def has_children(self):
354        return True
355
356
357# Just an example: the actual summary is produced by a summary string:
358# size=${svar%#}
359def stdlist_SummaryProvider(valobj, dict):
360    prov = stdlist_SynthProvider(valobj, None)
361    return "size=" + str(prov.num_children())
362
363
364# a tree node - this class makes the syntax in the actual iterator nicer
365# to read and maintain
366
367
368class stdmap_iterator_node:
369    def _left_impl(self):
370        logger = lldb.formatters.Logger.Logger()
371        return stdmap_iterator_node(self.node.GetChildMemberWithName("__left_"))
372
373    def _right_impl(self):
374        logger = lldb.formatters.Logger.Logger()
375        return stdmap_iterator_node(self.node.GetChildMemberWithName("__right_"))
376
377    def _parent_impl(self):
378        logger = lldb.formatters.Logger.Logger()
379        return stdmap_iterator_node(self.node.GetChildMemberWithName("__parent_"))
380
381    def _value_impl(self):
382        logger = lldb.formatters.Logger.Logger()
383        return self.node.GetValueAsUnsigned(0)
384
385    def _sbvalue_impl(self):
386        logger = lldb.formatters.Logger.Logger()
387        return self.node
388
389    def _null_impl(self):
390        logger = lldb.formatters.Logger.Logger()
391        return self.value == 0
392
393    def __init__(self, node):
394        logger = lldb.formatters.Logger.Logger()
395        self.node = node
396
397    left = property(_left_impl, None)
398    right = property(_right_impl, None)
399    parent = property(_parent_impl, None)
400    value = property(_value_impl, None)
401    is_null = property(_null_impl, None)
402    sbvalue = property(_sbvalue_impl, None)
403
404
405# a Python implementation of the tree iterator used by libc++
406
407
408class stdmap_iterator:
409    def tree_min(self, x):
410        logger = lldb.formatters.Logger.Logger()
411        steps = 0
412        if x.is_null:
413            return None
414        while not x.left.is_null:
415            x = x.left
416            steps += 1
417            if steps > self.max_count:
418                logger >> "Returning None - we overflowed"
419                return None
420        return x
421
422    def tree_max(self, x):
423        logger = lldb.formatters.Logger.Logger()
424        if x.is_null:
425            return None
426        while not x.right.is_null:
427            x = x.right
428        return x
429
430    def tree_is_left_child(self, x):
431        logger = lldb.formatters.Logger.Logger()
432        if x.is_null:
433            return None
434        return True if x.value == x.parent.left.value else False
435
436    def increment_node(self, node):
437        logger = lldb.formatters.Logger.Logger()
438        if node.is_null:
439            return None
440        if not node.right.is_null:
441            return self.tree_min(node.right)
442        steps = 0
443        while not self.tree_is_left_child(node):
444            steps += 1
445            if steps > self.max_count:
446                logger >> "Returning None - we overflowed"
447                return None
448            node = node.parent
449        return node.parent
450
451    def __init__(self, node, max_count=0):
452        logger = lldb.formatters.Logger.Logger()
453        # we convert the SBValue to an internal node object on entry
454        self.node = stdmap_iterator_node(node)
455        self.max_count = max_count
456
457    def value(self):
458        logger = lldb.formatters.Logger.Logger()
459        return self.node.sbvalue  # and return the SBValue back on exit
460
461    def next(self):
462        logger = lldb.formatters.Logger.Logger()
463        node = self.increment_node(self.node)
464        if node is not None and node.sbvalue.IsValid() and not (node.is_null):
465            self.node = node
466            return self.value()
467        else:
468            return None
469
470    def advance(self, N):
471        logger = lldb.formatters.Logger.Logger()
472        if N < 0:
473            return None
474        if N == 0:
475            return self.value()
476        if N == 1:
477            return self.next()
478        while N > 0:
479            if self.next() is None:
480                return None
481            N = N - 1
482        return self.value()
483
484
485class stdmap_SynthProvider:
486    def __init__(self, valobj, dict):
487        logger = lldb.formatters.Logger.Logger()
488        self.valobj = valobj
489        self.pointer_size = self.valobj.GetProcess().GetAddressByteSize()
490        self.count = None
491
492    def update(self):
493        logger = lldb.formatters.Logger.Logger()
494        self.count = None
495        try:
496            # we will set this to True if we find out that discovering a node in the map takes more steps than the overall size of the RB tree
497            # if this gets set to True, then we will merrily return None for
498            # any child from that moment on
499            self.garbage = False
500            self.tree = self.valobj.GetChildMemberWithName("__tree_")
501            self.root_node = self.tree.GetChildMemberWithName("__begin_node_")
502            # this data is either lazily-calculated, or cannot be inferred at this moment
503            # we still need to mark it as None, meaning "please set me ASAP"
504            self.data_type = None
505            self.data_size = None
506            self.skip_size = None
507        except:
508            pass
509
510    def num_children(self):
511        global _map_capping_size
512        logger = lldb.formatters.Logger.Logger()
513        if self.count is None:
514            self.count = self.num_children_impl()
515            if self.count > _map_capping_size:
516                self.count = _map_capping_size
517        return self.count
518
519    def num_children_impl(self):
520        logger = lldb.formatters.Logger.Logger()
521        try:
522            return (
523                self.valobj.GetChildMemberWithName("__tree_")
524                .GetChildMemberWithName("__pair3_")
525                .GetChildMemberWithName("__first_")
526                .GetValueAsUnsigned()
527            )
528        except:
529            return 0
530
531    def has_children(self):
532        return True
533
534    def get_data_type(self):
535        logger = lldb.formatters.Logger.Logger()
536        if self.data_type is None or self.data_size is None:
537            if self.num_children() == 0:
538                return False
539            deref = self.root_node.Dereference()
540            if not (deref.IsValid()):
541                return False
542            value = deref.GetChildMemberWithName("__value_")
543            if not (value.IsValid()):
544                return False
545            self.data_type = value.GetType()
546            self.data_size = self.data_type.GetByteSize()
547            self.skip_size = None
548            return True
549        else:
550            return True
551
552    def get_value_offset(self, node):
553        logger = lldb.formatters.Logger.Logger()
554        if self.skip_size is None:
555            node_type = node.GetType()
556            fields_count = node_type.GetNumberOfFields()
557            for i in range(fields_count):
558                field = node_type.GetFieldAtIndex(i)
559                if field.GetName() == "__value_":
560                    self.skip_size = field.GetOffsetInBytes()
561                    break
562        return self.skip_size is not None
563
564    def get_child_index(self, name):
565        logger = lldb.formatters.Logger.Logger()
566        try:
567            return int(name.lstrip("[").rstrip("]"))
568        except:
569            return -1
570
571    def get_child_at_index(self, index):
572        logger = lldb.formatters.Logger.Logger()
573        logger >> "Retrieving child " + str(index)
574        if index < 0:
575            return None
576        if index >= self.num_children():
577            return None
578        if self.garbage:
579            logger >> "Returning None since this tree is garbage"
580            return None
581        try:
582            iterator = stdmap_iterator(self.root_node, max_count=self.num_children())
583            # the debug info for libc++ std::map is such that __begin_node_ has a very nice and useful type
584            # out of which we can grab the information we need - every other node has a less informative
585            # type which omits all value information and only contains housekeeping information for the RB tree
586            # hence, we need to know if we are at a node != 0, so that we can
587            # still get at the data
588            need_to_skip = index > 0
589            current = iterator.advance(index)
590            if current is None:
591                logger >> "Tree is garbage - returning None"
592                self.garbage = True
593                return None
594            if self.get_data_type():
595                if not (need_to_skip):
596                    current = current.Dereference()
597                    obj = current.GetChildMemberWithName("__value_")
598                    obj_data = obj.GetData()
599                    # make sure we have a valid offset for the next items
600                    self.get_value_offset(current)
601                    # we do not return __value_ because then we would end up with a child named
602                    # __value_ instead of [0]
603                    return self.valobj.CreateValueFromData(
604                        "[" + str(index) + "]", obj_data, self.data_type
605                    )
606                else:
607                    # FIXME we need to have accessed item 0 before accessing
608                    # any other item!
609                    if self.skip_size is None:
610                        (
611                            logger
612                            >> "You asked for item > 0 before asking for item == 0, I will fetch 0 now then retry"
613                        )
614                        if self.get_child_at_index(0):
615                            return self.get_child_at_index(index)
616                        else:
617                            (
618                                logger
619                                >> "item == 0 could not be found. sorry, nothing can be done here."
620                            )
621                            return None
622                    return current.CreateChildAtOffset(
623                        "[" + str(index) + "]", self.skip_size, self.data_type
624                    )
625            else:
626                (
627                    logger
628                    >> "Unable to infer data-type - returning None (should mark tree as garbage here?)"
629                )
630                return None
631        except Exception as err:
632            logger >> "Hit an exception: " + str(err)
633            return None
634
635
636# Just an example: the actual summary is produced by a summary string:
637# size=${svar%#}
638
639
640def stdmap_SummaryProvider(valobj, dict):
641    prov = stdmap_SynthProvider(valobj, None)
642    return "size=" + str(prov.num_children())
643
644
645class stddeque_SynthProvider:
646    def __init__(self, valobj, d):
647        logger = lldb.formatters.Logger.Logger()
648        logger.write("init")
649        self.valobj = valobj
650        self.pointer_size = self.valobj.GetProcess().GetAddressByteSize()
651        self.count = None
652        try:
653            self.find_block_size()
654        except:
655            self.block_size = -1
656            self.element_size = -1
657        logger.write(
658            "block_size=%d, element_size=%d" % (self.block_size, self.element_size)
659        )
660
661    def find_block_size(self):
662        # in order to use the deque we must have the block size, or else
663        # it's impossible to know what memory addresses are valid
664        obj_type = self.valobj.GetType()
665        if obj_type.IsReferenceType():
666            obj_type = obj_type.GetDereferencedType()
667        elif obj_type.IsPointerType():
668            obj_type = obj_type.GetPointeeType()
669        self.element_type = obj_type.GetTemplateArgumentType(0)
670        self.element_size = self.element_type.GetByteSize()
671        # The code says this, but there must be a better way:
672        # template <class _Tp, class _Allocator>
673        # class __deque_base {
674        #    static const difference_type __block_size = sizeof(value_type) < 256 ? 4096 / sizeof(value_type) : 16;
675        # }
676        if self.element_size < 256:
677            self.block_size = 4096 // self.element_size
678        else:
679            self.block_size = 16
680
681    def num_children(self):
682        logger = lldb.formatters.Logger.Logger()
683        if self.count is None:
684            return 0
685        return self.count
686
687    def has_children(self):
688        return True
689
690    def get_child_index(self, name):
691        logger = lldb.formatters.Logger.Logger()
692        try:
693            return int(name.lstrip("[").rstrip("]"))
694        except:
695            return -1
696
697    def get_child_at_index(self, index):
698        logger = lldb.formatters.Logger.Logger()
699        logger.write("Fetching child " + str(index))
700        if index < 0 or self.count is None:
701            return None
702        if index >= self.num_children():
703            return None
704        try:
705            i, j = divmod(self.start + index, self.block_size)
706
707            return self.first.CreateValueFromExpression(
708                "[" + str(index) + "]",
709                "*(*(%s + %d) + %d)" % (self.map_begin.get_expr_path(), i, j),
710            )
711        except:
712            return None
713
714    def _get_value_of_compressed_pair(self, pair):
715        value = pair.GetChildMemberWithName("__value_")
716        if not value.IsValid():
717            # pre-r300140 member name
718            value = pair.GetChildMemberWithName("__first_")
719        return value.GetValueAsUnsigned(0)
720
721    def update(self):
722        logger = lldb.formatters.Logger.Logger()
723        try:
724            has_compressed_pair_layout = True
725            alloc_valobj = self.valobj.GetChildMemberWithName("__alloc_")
726            size_valobj = self.valobj.GetChildMemberWithName("__size_")
727            if alloc_valobj.IsValid() and size_valobj.IsValid():
728                has_compressed_pair_layout = False
729
730            # A deque is effectively a two-dim array, with fixed width.
731            # 'map' contains pointers to the rows of this array. The
732            # full memory area allocated by the deque is delimited
733            # by 'first' and 'end_cap'. However, only a subset of this
734            # memory contains valid data since a deque may have some slack
735            # at the front and back in order to have O(1) insertion at
736            # both ends. The rows in active use are delimited by
737            # 'begin' and 'end'.
738            #
739            # To find the elements that are actually constructed, the 'start'
740            # variable tells which element in this NxM array is the 0th
741            # one, and the 'size' element gives the number of elements
742            # in the deque.
743            if has_compressed_pair_layout:
744                count = self._get_value_of_compressed_pair(
745                    self.valobj.GetChildMemberWithName("__size_")
746                )
747            else:
748                count = size_valobj.GetValueAsUnsigned(0)
749
750            # give up now if we cant access memory reliably
751            if self.block_size < 0:
752                logger.write("block_size < 0")
753                return
754            map_ = self.valobj.GetChildMemberWithName("__map_")
755            start = self.valobj.GetChildMemberWithName("__start_").GetValueAsUnsigned(0)
756            first = map_.GetChildMemberWithName("__first_")
757            map_first = first.GetValueAsUnsigned(0)
758            self.map_begin = map_.GetChildMemberWithName("__begin_")
759            map_begin = self.map_begin.GetValueAsUnsigned(0)
760            map_end = map_.GetChildMemberWithName("__end_").GetValueAsUnsigned(0)
761
762            if has_compressed_pair_layout:
763                map_endcap = self._get_value_of_compressed_pair(
764                    map_.GetChildMemberWithName("__end_cap_")
765                )
766            else:
767                map_endcap = map_.GetChildMemberWithName("__cap_")
768                if not map_endcap.IsValid():
769                    map_endcap = map_.GetChildMemberWithName("__end_cap_")
770                map_endcap = map_endcap.GetValueAsUnsigned(0)
771
772            # check consistency
773            if not map_first <= map_begin <= map_end <= map_endcap:
774                logger.write("map pointers are not monotonic")
775                return
776            total_rows, junk = divmod(map_endcap - map_first, self.pointer_size)
777            if junk:
778                logger.write("endcap-first doesnt align correctly")
779                return
780            active_rows, junk = divmod(map_end - map_begin, self.pointer_size)
781            if junk:
782                logger.write("end-begin doesnt align correctly")
783                return
784            start_row, junk = divmod(map_begin - map_first, self.pointer_size)
785            if junk:
786                logger.write("begin-first doesnt align correctly")
787                return
788
789            logger.write(
790                "update success: count=%r, start=%r, first=%r" % (count, start, first)
791            )
792            # if consistent, save all we really need:
793            self.count = count
794            self.start = start
795            self.first = first
796        except:
797            self.count = None
798            self.start = None
799            self.map_first = None
800            self.map_begin = None
801        return False
802
803
804class stdsharedptr_SynthProvider:
805    def __init__(self, valobj, d):
806        logger = lldb.formatters.Logger.Logger()
807        logger.write("init")
808        self.valobj = valobj
809        # self.element_ptr_type = self.valobj.GetType().GetTemplateArgumentType(0).GetPointerType()
810        self.ptr = None
811        self.cntrl = None
812        process = valobj.GetProcess()
813        self.endianness = process.GetByteOrder()
814        self.pointer_size = process.GetAddressByteSize()
815        self.count_type = valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
816
817    def num_children(self):
818        return 1
819
820    def has_children(self):
821        return True
822
823    def get_child_index(self, name):
824        if name == "__ptr_":
825            return 0
826        if name == "count":
827            return 1
828        if name == "weak_count":
829            return 2
830        return -1
831
832    def get_child_at_index(self, index):
833        if index == 0:
834            return self.ptr
835        if index == 1:
836            if self.cntrl is None:
837                count = 0
838            else:
839                count = (
840                    1
841                    + self.cntrl.GetChildMemberWithName(
842                        "__shared_owners_"
843                    ).GetValueAsSigned()
844                )
845            return self.valobj.CreateValueFromData(
846                "count",
847                lldb.SBData.CreateDataFromUInt64Array(
848                    self.endianness, self.pointer_size, [count]
849                ),
850                self.count_type,
851            )
852        if index == 2:
853            if self.cntrl is None:
854                count = 0
855            else:
856                count = (
857                    1
858                    + self.cntrl.GetChildMemberWithName(
859                        "__shared_weak_owners_"
860                    ).GetValueAsSigned()
861                )
862            return self.valobj.CreateValueFromData(
863                "weak_count",
864                lldb.SBData.CreateDataFromUInt64Array(
865                    self.endianness, self.pointer_size, [count]
866                ),
867                self.count_type,
868            )
869        return None
870
871    def update(self):
872        logger = lldb.formatters.Logger.Logger()
873        self.ptr = self.valobj.GetChildMemberWithName(
874            "__ptr_"
875        )  # .Cast(self.element_ptr_type)
876        cntrl = self.valobj.GetChildMemberWithName("__cntrl_")
877        if cntrl.GetValueAsUnsigned(0):
878            self.cntrl = cntrl.Dereference()
879        else:
880            self.cntrl = None
881
882
883# we can use two different categories for old and new formatters - type names are different enough that we should make no confusion
884# talking with libc++ developer: "std::__1::class_name is set in stone
885# until we decide to change the ABI. That shouldn't happen within a 5 year
886# time frame"
887
888
889def __lldb_init_module(debugger, dict):
890    debugger.HandleCommand(
891        'type summary add -F libcxx.stdstring_SummaryProvider "std::__1::string" -w libcxx'
892    )
893    debugger.HandleCommand(
894        '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'
895    )
896    debugger.HandleCommand(
897        'type synthetic add -l libcxx.stdvector_SynthProvider -x "^(std::__1::)vector<.+>$" -w libcxx'
898    )
899    debugger.HandleCommand(
900        'type summary add -F libcxx.stdvector_SummaryProvider -e -x "^(std::__1::)vector<.+>$" -w libcxx'
901    )
902    debugger.HandleCommand(
903        'type synthetic add -l libcxx.stdlist_SynthProvider -x "^(std::__1::)list<.+>$" -w libcxx'
904    )
905    debugger.HandleCommand(
906        'type summary add -F libcxx.stdlist_SummaryProvider -e -x "^(std::__1::)list<.+>$" -w libcxx'
907    )
908    debugger.HandleCommand(
909        'type synthetic add -l libcxx.stdmap_SynthProvider -x "^(std::__1::)map<.+> >$" -w libcxx'
910    )
911    debugger.HandleCommand(
912        'type summary add -F libcxx.stdmap_SummaryProvider -e -x "^(std::__1::)map<.+> >$" -w libcxx'
913    )
914    debugger.HandleCommand("type category enable libcxx")
915    debugger.HandleCommand(
916        'type synthetic add -l libcxx.stddeque_SynthProvider -x "^(std::__1::)deque<.+>$" -w libcxx'
917    )
918    debugger.HandleCommand(
919        'type synthetic add -l libcxx.stdsharedptr_SynthProvider -x "^(std::__1::)shared_ptr<.+>$" -w libcxx'
920    )
921    # turns out the structs look the same, so weak_ptr can be handled the same!
922    debugger.HandleCommand(
923        'type synthetic add -l libcxx.stdsharedptr_SynthProvider -x "^(std::__1::)weak_ptr<.+>$" -w libcxx'
924    )
925
926
927_map_capping_size = 255
928_list_capping_size = 255
929_list_uses_loop_detector = True
930