1import lldb.formatters.Logger 2 3# C++ STL formatters for LLDB 4# As there are many versions of the libstdc++, you are encouraged to look at the STL 5# implementation for your platform before relying on these formatters to do the right 6# thing for your setup 7 8 9def ForwardListSummaryProvider(valobj, dict): 10 list_capping_size = valobj.GetTarget().GetMaximumNumberOfChildrenToDisplay() 11 text = "size=" + str(valobj.GetNumChildren()) 12 if valobj.GetNumChildren() > list_capping_size: 13 return "(capped) " + text 14 else: 15 return text 16 17 18def StdOptionalSummaryProvider(valobj, dict): 19 has_value = valobj.GetNumChildren() > 0 20 # We add wrapping spaces for consistency with the libcxx formatter 21 return " Has Value=" + ("true" if has_value else "false") + " " 22 23 24class StdOptionalSynthProvider: 25 def __init__(self, valobj, dict): 26 self.valobj = valobj 27 28 def update(self): 29 try: 30 self.payload = self.valobj.GetChildMemberWithName("_M_payload") 31 self.value = self.payload.GetChildMemberWithName("_M_payload") 32 self.has_value = ( 33 self.payload.GetChildMemberWithName("_M_engaged").GetValueAsUnsigned(0) 34 != 0 35 ) 36 except: 37 self.has_value = False 38 return False 39 40 def num_children(self): 41 return 1 if self.has_value else 0 42 43 def get_child_index(self, name): 44 return 0 45 46 def get_child_at_index(self, index): 47 # some versions of libstdcpp have an additional _M_value child with the actual value 48 possible_value = self.value.GetChildMemberWithName("_M_value") 49 if possible_value.IsValid(): 50 return possible_value.Clone("Value") 51 return self.value.Clone("Value") 52 53 54""" 55 This formatter can be applied to all 56 unordered map-like structures (unordered_map, unordered_multimap, unordered_set, unordered_multiset) 57""" 58 59 60class StdUnorderedMapSynthProvider: 61 def __init__(self, valobj, dict): 62 self.valobj = valobj 63 self.count = None 64 65 def extract_type(self): 66 type = self.valobj.GetType() 67 # The last template argument is the allocator type. 68 template_arg_num = type.GetNumberOfTemplateArguments() - 1 69 allocator_type = type.GetTemplateArgumentType(template_arg_num) 70 data_type = allocator_type.GetTemplateArgumentType(0) 71 return data_type 72 73 def update(self): 74 # preemptively setting this to None - we might end up changing our mind 75 # later 76 self.count = None 77 try: 78 self.head = self.valobj.GetChildMemberWithName("_M_h") 79 self.before_begin = self.head.GetChildMemberWithName("_M_before_begin") 80 self.next = self.before_begin.GetChildMemberWithName("_M_nxt") 81 self.data_type = self.extract_type() 82 self.skip_size = self.next.GetType().GetByteSize() 83 self.data_size = self.data_type.GetByteSize() 84 if (not self.data_type.IsValid()) or (not self.next.IsValid()): 85 self.count = 0 86 except: 87 self.count = 0 88 return False 89 90 def get_child_index(self, name): 91 try: 92 return int(name.lstrip("[").rstrip("]")) 93 except: 94 return -1 95 96 def get_child_at_index(self, index): 97 logger = lldb.formatters.Logger.Logger() 98 logger >> "Being asked to fetch child[" + str(index) + "]" 99 if index < 0: 100 return None 101 if index >= self.num_children(): 102 return None 103 try: 104 offset = index 105 current = self.next 106 while offset > 0: 107 current = current.GetChildMemberWithName("_M_nxt") 108 offset = offset - 1 109 return current.CreateChildAtOffset( 110 "[" + str(index) + "]", self.skip_size, self.data_type 111 ) 112 113 except: 114 logger >> "Cannot get child" 115 return None 116 117 def num_children(self): 118 if self.count is None: 119 self.count = self.num_children_impl() 120 return self.count 121 122 def num_children_impl(self): 123 logger = lldb.formatters.Logger.Logger() 124 try: 125 count = self.head.GetChildMemberWithName( 126 "_M_element_count" 127 ).GetValueAsUnsigned(0) 128 return count 129 except: 130 logger >> "Could not determine the size" 131 return 0 132 133 134class AbstractListSynthProvider: 135 def __init__(self, valobj, dict, has_prev): 136 """ 137 :param valobj: The value object of the list 138 :param dict: A dict with metadata provided by LLDB 139 :param has_prev: Whether the list supports a 'prev' pointer besides a 'next' one 140 """ 141 logger = lldb.formatters.Logger.Logger() 142 self.valobj = valobj 143 self.count = None 144 self.has_prev = has_prev 145 self.list_capping_size = ( 146 self.valobj.GetTarget().GetMaximumNumberOfChildrenToDisplay() 147 ) 148 logger >> "Providing synthetic children for a list named " + str( 149 valobj.GetName() 150 ) 151 152 def next_node(self, node): 153 logger = lldb.formatters.Logger.Logger() 154 return node.GetChildMemberWithName("_M_next") 155 156 def is_valid(self, node): 157 logger = lldb.formatters.Logger.Logger() 158 valid = self.value(self.next_node(node)) != self.get_end_of_list_address() 159 if valid: 160 logger >> "%s is valid" % str(self.valobj.GetName()) 161 else: 162 logger >> "synthetic value is not valid" 163 return valid 164 165 def value(self, node): 166 logger = lldb.formatters.Logger.Logger() 167 value = node.GetValueAsUnsigned() 168 logger >> "synthetic value for {}: {}".format(str(self.valobj.GetName()), value) 169 return value 170 171 # Floyd's cycle-finding algorithm 172 # try to detect if this list has a loop 173 def has_loop(self): 174 global _list_uses_loop_detector 175 logger = lldb.formatters.Logger.Logger() 176 if not _list_uses_loop_detector: 177 logger >> "Asked not to use loop detection" 178 return False 179 slow = self.next 180 fast1 = self.next 181 fast2 = self.next 182 while self.is_valid(slow): 183 slow_value = self.value(slow) 184 fast1 = self.next_node(fast2) 185 fast2 = self.next_node(fast1) 186 if self.value(fast1) == slow_value or self.value(fast2) == slow_value: 187 return True 188 slow = self.next_node(slow) 189 return False 190 191 def num_children(self): 192 logger = lldb.formatters.Logger.Logger() 193 if self.count is None: 194 # libstdc++ 6.0.21 added dedicated count field. 195 count_child = self.node.GetChildMemberWithName("_M_data") 196 if count_child and count_child.IsValid(): 197 self.count = count_child.GetValueAsUnsigned(0) 198 if self.count is None: 199 self.count = self.num_children_impl() 200 return self.count 201 202 def num_children_impl(self): 203 logger = lldb.formatters.Logger.Logger() 204 try: 205 # After a std::list has been initialized, both next and prev will 206 # be non-NULL 207 next_val = self.next.GetValueAsUnsigned(0) 208 if next_val == 0: 209 return 0 210 if self.has_loop(): 211 return 0 212 if self.has_prev: 213 prev_val = self.prev.GetValueAsUnsigned(0) 214 if prev_val == 0: 215 return 0 216 if next_val == self.node_address: 217 return 0 218 if next_val == prev_val: 219 return 1 220 size = 1 221 current = self.next 222 while ( 223 current.GetChildMemberWithName("_M_next").GetValueAsUnsigned(0) 224 != self.get_end_of_list_address() 225 ): 226 current = current.GetChildMemberWithName("_M_next") 227 if not current.IsValid(): 228 break 229 size = size + 1 230 if size >= self.list_capping_size: 231 break 232 233 return size 234 except: 235 logger >> "Error determining the size" 236 return 0 237 238 def get_child_index(self, name): 239 logger = lldb.formatters.Logger.Logger() 240 try: 241 return int(name.lstrip("[").rstrip("]")) 242 except: 243 return -1 244 245 def get_child_at_index(self, index): 246 logger = lldb.formatters.Logger.Logger() 247 logger >> "Fetching child " + str(index) 248 if index < 0: 249 return None 250 if index >= self.num_children(): 251 return None 252 try: 253 offset = index 254 current = self.next 255 while offset > 0: 256 current = current.GetChildMemberWithName("_M_next") 257 offset = offset - 1 258 # C++ lists store the data of a node after its pointers. In the case of a forward list, there's just one pointer (next), and 259 # in the case of a double-linked list, there's an additional pointer (prev). 260 return current.CreateChildAtOffset( 261 "[" + str(index) + "]", 262 (2 if self.has_prev else 1) * current.GetType().GetByteSize(), 263 self.data_type, 264 ) 265 except: 266 return None 267 268 def extract_type(self): 269 logger = lldb.formatters.Logger.Logger() 270 list_type = self.valobj.GetType().GetUnqualifiedType() 271 if list_type.IsReferenceType(): 272 list_type = list_type.GetDereferencedType() 273 if list_type.GetNumberOfTemplateArguments() > 0: 274 return list_type.GetTemplateArgumentType(0) 275 return lldb.SBType() 276 277 def update(self): 278 logger = lldb.formatters.Logger.Logger() 279 # preemptively setting this to None - we might end up changing our mind 280 # later 281 self.count = None 282 try: 283 self.impl = self.valobj.GetChildMemberWithName("_M_impl") 284 self.data_type = self.extract_type() 285 if (not self.data_type.IsValid()) or (not self.impl.IsValid()): 286 self.count = 0 287 elif not self.updateNodes(): 288 self.count = 0 289 else: 290 self.data_size = self.data_type.GetByteSize() 291 except: 292 self.count = 0 293 return False 294 295 """ 296 Method is used to extract the list pointers into the variables (e.g self.node, self.next, and optionally to self.prev) 297 and is mandatory to be overriden in each AbstractListSynthProvider subclass. 298 This should return True or False depending on wheter it found valid data. 299 """ 300 301 def updateNodes(self): 302 raise NotImplementedError 303 304 def has_children(self): 305 return True 306 307 """ 308 Method is used to identify if a node traversal has reached its end 309 and is mandatory to be overriden in each AbstractListSynthProvider subclass 310 """ 311 312 def get_end_of_list_address(self): 313 raise NotImplementedError 314 315 316class StdForwardListSynthProvider(AbstractListSynthProvider): 317 def __init__(self, valobj, dict): 318 has_prev = False 319 super().__init__(valobj, dict, has_prev) 320 321 def updateNodes(self): 322 self.node = self.impl.GetChildMemberWithName("_M_head") 323 self.next = self.node.GetChildMemberWithName("_M_next") 324 if (not self.node.IsValid()) or (not self.next.IsValid()): 325 return False 326 return True 327 328 def get_end_of_list_address(self): 329 return 0 330 331 332class StdListSynthProvider(AbstractListSynthProvider): 333 def __init__(self, valobj, dict): 334 has_prev = True 335 super().__init__(valobj, dict, has_prev) 336 337 def updateNodes(self): 338 self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0) 339 self.node = self.impl.GetChildMemberWithName("_M_node") 340 self.prev = self.node.GetChildMemberWithName("_M_prev") 341 self.next = self.node.GetChildMemberWithName("_M_next") 342 if ( 343 self.node_address == 0 344 or (not self.node.IsValid()) 345 or (not self.next.IsValid()) 346 or (not self.prev.IsValid()) 347 ): 348 return False 349 return True 350 351 def get_end_of_list_address(self): 352 return self.node_address 353 354 355class StdVectorSynthProvider: 356 class StdVectorImplementation(object): 357 def __init__(self, valobj): 358 self.valobj = valobj 359 self.count = None 360 361 def num_children(self): 362 if self.count is None: 363 self.count = self.num_children_impl() 364 return self.count 365 366 def num_children_impl(self): 367 try: 368 start_val = self.start.GetValueAsUnsigned(0) 369 finish_val = self.finish.GetValueAsUnsigned(0) 370 end_val = self.end.GetValueAsUnsigned(0) 371 # Before a vector has been constructed, it will contain bad values 372 # so we really need to be careful about the length we return since 373 # uninitialized data can cause us to return a huge number. We need 374 # to also check for any of the start, finish or end of storage values 375 # being zero (NULL). If any are, then this vector has not been 376 # initialized yet and we should return zero 377 378 # Make sure nothing is NULL 379 if start_val == 0 or finish_val == 0 or end_val == 0: 380 return 0 381 # Make sure start is less than finish 382 if start_val >= finish_val: 383 return 0 384 # Make sure finish is less than or equal to end of storage 385 if finish_val > end_val: 386 return 0 387 388 # if we have a struct (or other data type that the compiler pads to native word size) 389 # this check might fail, unless the sizeof() we get is itself incremented to take the 390 # padding bytes into account - on current clang it looks like 391 # this is the case 392 num_children = finish_val - start_val 393 if (num_children % self.data_size) != 0: 394 return 0 395 else: 396 num_children = num_children // self.data_size 397 return num_children 398 except: 399 return 0 400 401 def get_child_at_index(self, index): 402 logger = lldb.formatters.Logger.Logger() 403 logger >> "Retrieving child " + str(index) 404 if index < 0: 405 return None 406 if index >= self.num_children(): 407 return None 408 try: 409 offset = index * self.data_size 410 return self.start.CreateChildAtOffset( 411 "[" + str(index) + "]", offset, self.data_type 412 ) 413 except: 414 return None 415 416 def update(self): 417 # preemptively setting this to None - we might end up changing our 418 # mind later 419 self.count = None 420 try: 421 impl = self.valobj.GetChildMemberWithName("_M_impl") 422 self.start = impl.GetChildMemberWithName("_M_start") 423 self.finish = impl.GetChildMemberWithName("_M_finish") 424 self.end = impl.GetChildMemberWithName("_M_end_of_storage") 425 self.data_type = self.start.GetType().GetPointeeType() 426 self.data_size = self.data_type.GetByteSize() 427 # if any of these objects is invalid, it means there is no 428 # point in trying to fetch anything 429 if ( 430 self.start.IsValid() 431 and self.finish.IsValid() 432 and self.end.IsValid() 433 and self.data_type.IsValid() 434 ): 435 self.count = None 436 else: 437 self.count = 0 438 except: 439 self.count = 0 440 return False 441 442 class StdVBoolImplementation(object): 443 def __init__(self, valobj, bool_type): 444 self.valobj = valobj 445 self.bool_type = bool_type 446 self.valid = False 447 448 def num_children(self): 449 if self.valid: 450 start = self.start_p.GetValueAsUnsigned(0) 451 finish = self.finish_p.GetValueAsUnsigned(0) 452 offset = self.offset.GetValueAsUnsigned(0) 453 if finish >= start: 454 return (finish - start) * 8 + offset 455 return 0 456 457 def get_child_at_index(self, index): 458 if index >= self.num_children(): 459 return None 460 element_type = self.start_p.GetType().GetPointeeType() 461 element_bits = 8 * element_type.GetByteSize() 462 element_offset = (index // element_bits) * element_type.GetByteSize() 463 bit_offset = index % element_bits 464 element = self.start_p.CreateChildAtOffset( 465 "[" + str(index) + "]", element_offset, element_type 466 ) 467 bit = element.GetValueAsUnsigned(0) & (1 << bit_offset) 468 return self.valobj.CreateBoolValue("[%d]" % index, bool(bit)) 469 470 def update(self): 471 try: 472 m_impl = self.valobj.GetChildMemberWithName("_M_impl") 473 self.m_start = m_impl.GetChildMemberWithName("_M_start") 474 self.m_finish = m_impl.GetChildMemberWithName("_M_finish") 475 self.start_p = self.m_start.GetChildMemberWithName("_M_p") 476 self.finish_p = self.m_finish.GetChildMemberWithName("_M_p") 477 self.offset = self.m_finish.GetChildMemberWithName("_M_offset") 478 if ( 479 self.offset.IsValid() 480 and self.start_p.IsValid() 481 and self.finish_p.IsValid() 482 ): 483 self.valid = True 484 else: 485 self.valid = False 486 except: 487 self.valid = False 488 return False 489 490 def __init__(self, valobj, dict): 491 logger = lldb.formatters.Logger.Logger() 492 first_template_arg_type = valobj.GetType().GetTemplateArgumentType(0) 493 if str(first_template_arg_type.GetName()) == "bool": 494 self.impl = self.StdVBoolImplementation(valobj, first_template_arg_type) 495 else: 496 self.impl = self.StdVectorImplementation(valobj) 497 logger >> "Providing synthetic children for a vector named " + str( 498 valobj.GetName() 499 ) 500 501 def num_children(self): 502 return self.impl.num_children() 503 504 def get_child_index(self, name): 505 try: 506 return int(name.lstrip("[").rstrip("]")) 507 except: 508 return -1 509 510 def get_child_at_index(self, index): 511 return self.impl.get_child_at_index(index) 512 513 def update(self): 514 return self.impl.update() 515 516 def has_children(self): 517 return True 518 519 """ 520 This formatter can be applied to all 521 map-like structures (map, multimap, set, multiset) 522 """ 523 524 525class StdMapLikeSynthProvider: 526 def __init__(self, valobj, dict): 527 logger = lldb.formatters.Logger.Logger() 528 self.valobj = valobj 529 self.count = None 530 self.kind = self.get_object_kind(valobj) 531 ( 532 logger 533 >> "Providing synthetic children for a " 534 + self.kind 535 + " named " 536 + str(valobj.GetName()) 537 ) 538 539 def get_object_kind(self, valobj): 540 type_name = valobj.GetTypeName() 541 for kind in ["multiset", "multimap", "set", "map"]: 542 if kind in type_name: 543 return kind 544 return type_name 545 546 # we need this function as a temporary workaround for rdar://problem/10801549 547 # which prevents us from extracting the std::pair<K,V> SBType out of the template 548 # arguments for _Rep_Type _M_t in the object itself - because we have to make up the 549 # typename and then find it, we may hit the situation were std::string has multiple 550 # names but only one is actually referenced in the debug information. hence, we need 551 # to replace the longer versions of std::string with the shorter one in order to be able 552 # to find the type name 553 def fixup_class_name(self, class_name): 554 logger = lldb.formatters.Logger.Logger() 555 if ( 556 class_name 557 == "std::basic_string<char, std::char_traits<char>, std::allocator<char> >" 558 ): 559 return "std::basic_string<char>", True 560 if ( 561 class_name 562 == "basic_string<char, std::char_traits<char>, std::allocator<char> >" 563 ): 564 return "std::basic_string<char>", True 565 if ( 566 class_name 567 == "std::basic_string<char, std::char_traits<char>, std::allocator<char> >" 568 ): 569 return "std::basic_string<char>", True 570 if ( 571 class_name 572 == "basic_string<char, std::char_traits<char>, std::allocator<char> >" 573 ): 574 return "std::basic_string<char>", True 575 return class_name, False 576 577 def update(self): 578 logger = lldb.formatters.Logger.Logger() 579 # preemptively setting this to None - we might end up changing our mind 580 # later 581 self.count = None 582 try: 583 # we will set this to True if we find out that discovering a node in the object takes more steps than the overall size of the RB tree 584 # if this gets set to True, then we will merrily return None for 585 # any child from that moment on 586 self.garbage = False 587 self.Mt = self.valobj.GetChildMemberWithName("_M_t") 588 self.Mimpl = self.Mt.GetChildMemberWithName("_M_impl") 589 self.Mheader = self.Mimpl.GetChildMemberWithName("_M_header") 590 if not self.Mheader.IsValid(): 591 self.count = 0 592 else: 593 map_type = self.valobj.GetType() 594 if map_type.IsReferenceType(): 595 logger >> "Dereferencing type" 596 map_type = map_type.GetDereferencedType() 597 598 # Get the type of std::pair<key, value>. It is the first template 599 # argument type of the 4th template argument to std::map. 600 allocator_type = map_type.GetTemplateArgumentType(3) 601 self.data_type = allocator_type.GetTemplateArgumentType(0) 602 if not self.data_type: 603 # GCC does not emit DW_TAG_template_type_parameter for 604 # std::allocator<...>. For such a case, get the type of 605 # std::pair from a member of std::map. 606 rep_type = self.valobj.GetChildMemberWithName("_M_t").GetType() 607 self.data_type = ( 608 rep_type.GetTypedefedType().GetTemplateArgumentType(1) 609 ) 610 611 # from libstdc++ implementation of _M_root for rbtree 612 self.Mroot = self.Mheader.GetChildMemberWithName("_M_parent") 613 self.data_size = self.data_type.GetByteSize() 614 self.skip_size = self.Mheader.GetType().GetByteSize() 615 except: 616 self.count = 0 617 return False 618 619 def num_children(self): 620 logger = lldb.formatters.Logger.Logger() 621 if self.count is None: 622 self.count = self.num_children_impl() 623 return self.count 624 625 def num_children_impl(self): 626 logger = lldb.formatters.Logger.Logger() 627 try: 628 root_ptr_val = self.node_ptr_value(self.Mroot) 629 if root_ptr_val == 0: 630 return 0 631 count = self.Mimpl.GetChildMemberWithName( 632 "_M_node_count" 633 ).GetValueAsUnsigned(0) 634 logger >> "I have " + str(count) + " children available" 635 return count 636 except: 637 return 0 638 639 def get_child_index(self, name): 640 logger = lldb.formatters.Logger.Logger() 641 try: 642 return int(name.lstrip("[").rstrip("]")) 643 except: 644 return -1 645 646 def get_child_at_index(self, index): 647 logger = lldb.formatters.Logger.Logger() 648 logger >> "Being asked to fetch child[" + str(index) + "]" 649 if index < 0: 650 return None 651 if index >= self.num_children(): 652 return None 653 if self.garbage: 654 logger >> "Returning None since we are a garbage tree" 655 return None 656 try: 657 offset = index 658 current = self.left(self.Mheader) 659 while offset > 0: 660 current = self.increment_node(current) 661 offset = offset - 1 662 # skip all the base stuff and get at the data 663 return current.CreateChildAtOffset( 664 "[" + str(index) + "]", self.skip_size, self.data_type 665 ) 666 except: 667 return None 668 669 # utility functions 670 def node_ptr_value(self, node): 671 logger = lldb.formatters.Logger.Logger() 672 return node.GetValueAsUnsigned(0) 673 674 def right(self, node): 675 logger = lldb.formatters.Logger.Logger() 676 return node.GetChildMemberWithName("_M_right") 677 678 def left(self, node): 679 logger = lldb.formatters.Logger.Logger() 680 return node.GetChildMemberWithName("_M_left") 681 682 def parent(self, node): 683 logger = lldb.formatters.Logger.Logger() 684 return node.GetChildMemberWithName("_M_parent") 685 686 # from libstdc++ implementation of iterator for rbtree 687 def increment_node(self, node): 688 logger = lldb.formatters.Logger.Logger() 689 max_steps = self.num_children() 690 if self.node_ptr_value(self.right(node)) != 0: 691 x = self.right(node) 692 max_steps -= 1 693 while self.node_ptr_value(self.left(x)) != 0: 694 x = self.left(x) 695 max_steps -= 1 696 logger >> str(max_steps) + " more to go before giving up" 697 if max_steps <= 0: 698 self.garbage = True 699 return None 700 return x 701 else: 702 x = node 703 y = self.parent(x) 704 max_steps -= 1 705 while self.node_ptr_value(x) == self.node_ptr_value(self.right(y)): 706 x = y 707 y = self.parent(y) 708 max_steps -= 1 709 logger >> str(max_steps) + " more to go before giving up" 710 if max_steps <= 0: 711 self.garbage = True 712 return None 713 if self.node_ptr_value(self.right(x)) != self.node_ptr_value(y): 714 x = y 715 return x 716 717 def has_children(self): 718 return True 719 720 721_list_uses_loop_detector = True 722 723 724class StdDequeSynthProvider: 725 def __init__(self, valobj, d): 726 self.valobj = valobj 727 self.pointer_size = self.valobj.GetProcess().GetAddressByteSize() 728 self.count = None 729 self.block_size = -1 730 self.element_size = -1 731 self.find_block_size() 732 733 def find_block_size(self): 734 # in order to use the deque we must have the block size, or else 735 # it's impossible to know what memory addresses are valid 736 self.element_type = self.valobj.GetType().GetTemplateArgumentType(0) 737 if not self.element_type.IsValid(): 738 return 739 self.element_size = self.element_type.GetByteSize() 740 # The block size (i.e. number of elements per subarray) is defined in 741 # this piece of code, so we need to replicate it. 742 # 743 # #define _GLIBCXX_DEQUE_BUF_SIZE 512 744 # 745 # return (__size < _GLIBCXX_DEQUE_BUF_SIZE 746 # ? size_t(_GLIBCXX_DEQUE_BUF_SIZE / __size) : size_t(1)); 747 if self.element_size < 512: 748 self.block_size = 512 // self.element_size 749 else: 750 self.block_size = 1 751 752 def num_children(self): 753 if self.count is None: 754 return 0 755 return self.count 756 757 def has_children(self): 758 return True 759 760 def get_child_index(self, name): 761 try: 762 return int(name.lstrip("[").rstrip("]")) 763 except: 764 return -1 765 766 def get_child_at_index(self, index): 767 if index < 0 or self.count is None: 768 return None 769 if index >= self.num_children(): 770 return None 771 try: 772 name = "[" + str(index) + "]" 773 # We first look for the element in the first subarray, 774 # which might be incomplete. 775 if index < self.first_node_size: 776 # The following statement is valid because self.first_elem is the pointer 777 # to the first element 778 return self.first_elem.CreateChildAtOffset( 779 name, index * self.element_size, self.element_type 780 ) 781 782 # Now the rest of the subarrays except for maybe the last one 783 # are going to be complete, so the final expression is simpler 784 i, j = divmod(index - self.first_node_size, self.block_size) 785 786 # We first move to the beginning of the node/subarray were our element is 787 node = self.start_node.CreateChildAtOffset( 788 "", 789 (1 + i) * self.valobj.GetProcess().GetAddressByteSize(), 790 self.element_type.GetPointerType(), 791 ) 792 return node.CreateChildAtOffset( 793 name, j * self.element_size, self.element_type 794 ) 795 796 except: 797 return None 798 799 def update(self): 800 logger = lldb.formatters.Logger.Logger() 801 self.count = 0 802 try: 803 # A deque is effectively a two-dim array, with fixed width. 804 # However, only a subset of this memory contains valid data 805 # since a deque may have some slack at the front and back in 806 # order to have O(1) insertion at both ends. 807 # The rows in active use are delimited by '_M_start' and 808 # '_M_finish'. 809 # 810 # To find the elements that are actually constructed, the 'start' 811 # variable tells which element in this NxM array is the 0th 812 # one. 813 if self.block_size < 0 or self.element_size < 0: 814 return False 815 816 count = 0 817 818 impl = self.valobj.GetChildMemberWithName("_M_impl") 819 820 # we calculate the size of the first node (i.e. first internal array) 821 self.start = impl.GetChildMemberWithName("_M_start") 822 self.start_node = self.start.GetChildMemberWithName("_M_node") 823 first_node_address = self.start_node.GetValueAsUnsigned(0) 824 first_node_last_elem = self.start.GetChildMemberWithName( 825 "_M_last" 826 ).GetValueAsUnsigned(0) 827 self.first_elem = self.start.GetChildMemberWithName("_M_cur") 828 first_node_first_elem = self.first_elem.GetValueAsUnsigned(0) 829 830 finish = impl.GetChildMemberWithName("_M_finish") 831 last_node_address = finish.GetChildMemberWithName( 832 "_M_node" 833 ).GetValueAsUnsigned(0) 834 last_node_first_elem = finish.GetChildMemberWithName( 835 "_M_first" 836 ).GetValueAsUnsigned(0) 837 last_node_last_elem = finish.GetChildMemberWithName( 838 "_M_cur" 839 ).GetValueAsUnsigned(0) 840 841 if ( 842 first_node_first_elem == 0 843 or first_node_last_elem == 0 844 or first_node_first_elem > first_node_last_elem 845 ): 846 return False 847 if ( 848 last_node_first_elem == 0 849 or last_node_last_elem == 0 850 or last_node_first_elem > last_node_last_elem 851 ): 852 return False 853 854 if last_node_address == first_node_address: 855 self.first_node_size = ( 856 last_node_last_elem - first_node_first_elem 857 ) // self.element_size 858 count += self.first_node_size 859 else: 860 self.first_node_size = ( 861 first_node_last_elem - first_node_first_elem 862 ) // self.element_size 863 count += self.first_node_size 864 865 # we calculate the size of the last node 866 finish = impl.GetChildMemberWithName("_M_finish") 867 last_node_address = finish.GetChildMemberWithName( 868 "_M_node" 869 ).GetValueAsUnsigned(0) 870 count += ( 871 last_node_last_elem - last_node_first_elem 872 ) // self.element_size 873 874 # we calculate the size of the intermediate nodes 875 num_intermediate_nodes = ( 876 last_node_address - first_node_address - 1 877 ) // self.valobj.GetProcess().GetAddressByteSize() 878 count += self.block_size * num_intermediate_nodes 879 self.count = count 880 except: 881 pass 882 return False 883 884 885def VariantSummaryProvider(valobj, dict): 886 raw_obj = valobj.GetNonSyntheticValue() 887 index_obj = raw_obj.GetChildMemberWithName("_M_index") 888 data_obj = raw_obj.GetChildMemberWithName("_M_u") 889 if not (index_obj and index_obj.IsValid() and data_obj and data_obj.IsValid()): 890 return "<Can't find _M_index or _M_u>" 891 892 def get_variant_npos_value(index_byte_size): 893 if index_byte_size == 1: 894 return 0xFF 895 elif index_byte_size == 2: 896 return 0xFFFF 897 else: 898 return 0xFFFFFFFF 899 900 npos_value = get_variant_npos_value(index_obj.GetByteSize()) 901 index = index_obj.GetValueAsUnsigned(0) 902 if index == npos_value: 903 return " No Value" 904 905 # Strip references and typedefs. 906 variant_type = raw_obj.GetType().GetCanonicalType().GetDereferencedType() 907 template_arg_count = variant_type.GetNumberOfTemplateArguments() 908 909 # Invalid index can happen when the variant is not initialized yet. 910 if index >= template_arg_count: 911 return " <Invalid>" 912 913 active_type = variant_type.GetTemplateArgumentType(index) 914 return f" Active Type = {active_type.GetDisplayTypeName()} " 915 916 917class VariantSynthProvider: 918 def __init__(self, valobj, dict): 919 self.raw_obj = valobj.GetNonSyntheticValue() 920 self.is_valid = False 921 self.index = None 922 self.data_obj = None 923 924 def update(self): 925 try: 926 self.index = self.raw_obj.GetChildMemberWithName( 927 "_M_index" 928 ).GetValueAsSigned(-1) 929 self.is_valid = self.index != -1 930 self.data_obj = self.raw_obj.GetChildMemberWithName("_M_u") 931 except: 932 self.is_valid = False 933 return False 934 935 def has_children(self): 936 return True 937 938 def num_children(self): 939 return 1 if self.is_valid else 0 940 941 def get_child_index(self, name): 942 return 0 943 944 def get_child_at_index(self, index): 945 if not self.is_valid: 946 return None 947 cur = 0 948 node = self.data_obj 949 while cur < self.index: 950 node = node.GetChildMemberWithName("_M_rest") 951 cur += 1 952 953 # _M_storage's type depends on variant field's type "_Type". 954 # 1. if '_Type' is literal type: _Type _M_storage. 955 # 2. otherwise, __gnu_cxx::__aligned_membuf<_Type> _M_storage. 956 # 957 # For 2. we have to cast it to underlying template _Type. 958 959 value = node.GetChildMemberWithName("_M_first").GetChildMemberWithName( 960 "_M_storage" 961 ) 962 template_type = value.GetType().GetTemplateArgumentType(0) 963 964 # Literal type will return None for GetTemplateArgumentType(0) 965 if ( 966 template_type 967 and "__gnu_cxx::__aligned_membuf" in value.GetType().GetDisplayTypeName() 968 and template_type.IsValid() 969 ): 970 value = value.Cast(template_type) 971 972 if value.IsValid(): 973 return value.Clone("Value") 974 return None 975