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