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