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