1from __future__ import division 2import re 3import lldb.formatters.Logger 4 5# C++ STL formatters for LLDB 6# These formatters are based upon the version of the GNU libstdc++ 7# as it ships with Mac OS X 10.6.8 thru 10.8.0 8# You are encouraged to look at the STL implementation for your platform 9# before relying on these formatters to do the right thing for your setup 10 11def StdOptionalSummaryProvider(valobj, dict): 12 has_value = valobj.GetNumChildren() > 0 13 # We add wrapping spaces for consistency with the libcxx formatter 14 return " Has Value=" + ("true" if has_value else "false") + " " 15 16 17class StdOptionalSynthProvider: 18 def __init__(self, valobj, dict): 19 self.valobj = valobj 20 21 def update(self): 22 try: 23 self.payload = self.valobj.GetChildMemberWithName('_M_payload') 24 self.value = self.payload.GetChildMemberWithName('_M_payload') 25 self.count = self.payload.GetChildMemberWithName('_M_engaged').GetValueAsUnsigned(0) 26 except: 27 self.count = 0 28 return False 29 30 31 def num_children(self): 32 return self.count 33 34 def get_child_index(self, name): 35 return 0 36 37 def get_child_at_index(self, index): 38 # some versions of libstdcpp have an additional _M_value child with the actual value 39 possible_value = self.value.GetChildMemberWithName('_M_value') 40 if possible_value.IsValid(): 41 return possible_value.Clone('Value') 42 return self.value.Clone('Value') 43 44""" 45 This formatter can be applied to all 46 unordered map-like structures (unordered_map, unordered_multimap, unordered_set, unordered_multiset) 47""" 48class StdUnorderedMapSynthProvider: 49 def __init__(self, valobj, dict): 50 self.valobj = valobj 51 self.count = None 52 self.kind = self.get_object_kind(valobj) 53 54 def get_object_kind(self, valobj): 55 type_name = valobj.GetTypeName() 56 return "set" if "set" in type_name else "map" 57 58 def extract_type(self): 59 type = self.valobj.GetType() 60 # type of std::pair<key, value> is the first template 61 # argument type of the 4th template argument to std::map and 62 # 3rd template argument for std::set. That's why 63 # we need to know kind of the object 64 template_arg_num = 4 if self.kind == "map" else 3 65 allocator_type = type.GetTemplateArgumentType(template_arg_num) 66 data_type = allocator_type.GetTemplateArgumentType(0) 67 return data_type 68 69 def update(self): 70 # preemptively setting this to None - we might end up changing our mind 71 # later 72 self.count = None 73 try: 74 self.head = self.valobj.GetChildMemberWithName('_M_h') 75 self.before_begin = self.head.GetChildMemberWithName('_M_before_begin') 76 self.next = self.before_begin.GetChildMemberWithName('_M_nxt') 77 self.data_type = self.extract_type() 78 self.skip_size = self.next.GetType().GetByteSize() 79 self.data_size = self.data_type.GetByteSize() 80 except: 81 pass 82 return False 83 84 def get_child_index(self, name): 85 try: 86 return int(name.lstrip('[').rstrip(']')) 87 except: 88 return -1 89 90 def get_child_at_index(self, index): 91 logger = lldb.formatters.Logger.Logger() 92 logger >> "Being asked to fetch child[" + str(index) + "]" 93 if index < 0: 94 return None 95 if index >= self.num_children(): 96 return None 97 try: 98 offset = index 99 current = self.next 100 while offset > 0: 101 current = current.GetChildMemberWithName('_M_nxt') 102 offset = offset - 1 103 return current.CreateChildAtOffset( '[' + str(index) + ']', self.skip_size, self.data_type) 104 105 except: 106 logger >> "Cannot get child" 107 return None 108 109 def num_children(self): 110 if self.count is None: 111 self.count = self.num_children_impl() 112 return self.count 113 114 def num_children_impl(self): 115 logger = lldb.formatters.Logger.Logger() 116 try: 117 count = self.head.GetChildMemberWithName('_M_element_count').GetValueAsUnsigned(0) 118 return count 119 except: 120 logger >> "Could not determine the size" 121 return 0 122 123 124class AbstractListSynthProvider: 125 def __init__(self, valobj, dict, has_prev): 126 ''' 127 :param valobj: The value object of the list 128 :param dict: A dict with metadata provided by LLDB 129 :param has_prev: Whether the list supports a 'prev' pointer besides a 'next' one 130 ''' 131 logger = lldb.formatters.Logger.Logger() 132 self.valobj = valobj 133 self.count = None 134 self.has_prev = has_prev 135 logger >> "Providing synthetic children for a list named " + \ 136 str(valobj.GetName()) 137 138 def next_node(self, node): 139 logger = lldb.formatters.Logger.Logger() 140 return node.GetChildMemberWithName('_M_next') 141 142 def is_valid(self, node): 143 logger = lldb.formatters.Logger.Logger() 144 valid = self.value(self.next_node(node)) != self.get_end_of_list_address() 145 if valid: 146 logger >> "%s is valid" % str(self.valobj.GetName()) 147 else: 148 logger >> "synthetic value is not valid" 149 return valid 150 151 def value(self, node): 152 logger = lldb.formatters.Logger.Logger() 153 value = node.GetValueAsUnsigned() 154 logger >> "synthetic value for {}: {}".format( 155 str(self.valobj.GetName()), value) 156 return value 157 158 # Floyd's cycle-finding algorithm 159 # try to detect if this list has a loop 160 def has_loop(self): 161 global _list_uses_loop_detector 162 logger = lldb.formatters.Logger.Logger() 163 if not _list_uses_loop_detector: 164 logger >> "Asked not to use loop detection" 165 return False 166 slow = self.next 167 fast1 = self.next 168 fast2 = self.next 169 while self.is_valid(slow): 170 slow_value = self.value(slow) 171 fast1 = self.next_node(fast2) 172 fast2 = self.next_node(fast1) 173 if self.value(fast1) == slow_value or self.value( 174 fast2) == slow_value: 175 return True 176 slow = self.next_node(slow) 177 return False 178 179 def num_children(self): 180 logger = lldb.formatters.Logger.Logger() 181 if self.count is None: 182 # libstdc++ 6.0.21 added dedicated count field. 183 count_child = self.node.GetChildMemberWithName('_M_data') 184 if count_child and count_child.IsValid(): 185 self.count = count_child.GetValueAsUnsigned(0) 186 if self.count is None: 187 self.count = self.num_children_impl() 188 return self.count 189 190 def num_children_impl(self): 191 logger = lldb.formatters.Logger.Logger() 192 try: 193 # After a std::list has been initialized, both next and prev will 194 # be non-NULL 195 next_val = self.next.GetValueAsUnsigned(0) 196 if next_val == 0: 197 return 0 198 if self.has_loop(): 199 return 0 200 if self.has_prev: 201 prev_val = self.prev.GetValueAsUnsigned(0) 202 if prev_val == 0: 203 return 0 204 if next_val == self.node_address: 205 return 0 206 if next_val == prev_val: 207 return 1 208 size = 1 209 current = self.next 210 while current.GetChildMemberWithName( 211 '_M_next').GetValueAsUnsigned(0) != self.get_end_of_list_address(): 212 size = size + 1 213 current = current.GetChildMemberWithName('_M_next') 214 return size 215 except: 216 logger >> "Error determining the size" 217 return 0 218 219 def get_child_index(self, name): 220 logger = lldb.formatters.Logger.Logger() 221 try: 222 return int(name.lstrip('[').rstrip(']')) 223 except: 224 return -1 225 226 def get_child_at_index(self, index): 227 logger = lldb.formatters.Logger.Logger() 228 logger >> "Fetching child " + str(index) 229 if index < 0: 230 return None 231 if index >= self.num_children(): 232 return None 233 try: 234 offset = index 235 current = self.next 236 while offset > 0: 237 current = current.GetChildMemberWithName('_M_next') 238 offset = offset - 1 239 # 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 240 # in the case of a double-linked list, there's an additional pointer (prev). 241 return current.CreateChildAtOffset( 242 '[' + str(index) + ']', 243 (2 if self.has_prev else 1) * current.GetType().GetByteSize(), 244 self.data_type) 245 except: 246 return None 247 248 def extract_type(self): 249 logger = lldb.formatters.Logger.Logger() 250 list_type = self.valobj.GetType().GetUnqualifiedType() 251 if list_type.IsReferenceType(): 252 list_type = list_type.GetDereferencedType() 253 if list_type.GetNumberOfTemplateArguments() > 0: 254 data_type = list_type.GetTemplateArgumentType(0) 255 else: 256 data_type = None 257 return data_type 258 259 def update(self): 260 logger = lldb.formatters.Logger.Logger() 261 # preemptively setting this to None - we might end up changing our mind 262 # later 263 self.count = None 264 try: 265 self.impl = self.valobj.GetChildMemberWithName('_M_impl') 266 self.data_type = self.extract_type() 267 self.data_size = self.data_type.GetByteSize() 268 self.updateNodes() 269 except: 270 pass 271 return False 272 273 ''' 274 Method is used to extract the list pointers into the variables (e.g self.node, self.next, and optionally to self.prev) 275 and is mandatory to be overriden in each AbstractListSynthProvider subclass 276 ''' 277 def updateNodes(self): 278 raise NotImplementedError 279 280 def has_children(self): 281 return True 282 283 ''' 284 Method is used to identify if a node traversal has reached its end 285 and is mandatory to be overriden in each AbstractListSynthProvider subclass 286 ''' 287 def get_end_of_list_address(self): 288 raise NotImplementedError 289 290 291class StdForwardListSynthProvider(AbstractListSynthProvider): 292 293 def __init__(self, valobj, dict): 294 has_prev = False 295 super().__init__(valobj, dict, has_prev) 296 297 def updateNodes(self): 298 self.node = self.impl.GetChildMemberWithName('_M_head') 299 self.next = self.node.GetChildMemberWithName('_M_next') 300 301 def get_end_of_list_address(self): 302 return 0 303 304 305class StdListSynthProvider(AbstractListSynthProvider): 306 307 def __init__(self, valobj, dict): 308 has_prev = True 309 super().__init__(valobj, dict, has_prev) 310 311 def updateNodes(self): 312 self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0) 313 self.node = self.impl.GetChildMemberWithName('_M_node') 314 self.prev = self.node.GetChildMemberWithName('_M_prev') 315 self.next = self.node.GetChildMemberWithName('_M_next') 316 317 def get_end_of_list_address(self): 318 return self.node_address 319 320 321class StdVectorSynthProvider: 322 323 class StdVectorImplementation(object): 324 325 def __init__(self, valobj): 326 self.valobj = valobj 327 self.count = None 328 329 def num_children(self): 330 if self.count is None: 331 self.count = self.num_children_impl() 332 return self.count 333 334 def num_children_impl(self): 335 try: 336 start_val = self.start.GetValueAsUnsigned(0) 337 finish_val = self.finish.GetValueAsUnsigned(0) 338 end_val = self.end.GetValueAsUnsigned(0) 339 # Before a vector has been constructed, it will contain bad values 340 # so we really need to be careful about the length we return since 341 # uninitialized data can cause us to return a huge number. We need 342 # to also check for any of the start, finish or end of storage values 343 # being zero (NULL). If any are, then this vector has not been 344 # initialized yet and we should return zero 345 346 # Make sure nothing is NULL 347 if start_val == 0 or finish_val == 0 or end_val == 0: 348 return 0 349 # Make sure start is less than finish 350 if start_val >= finish_val: 351 return 0 352 # Make sure finish is less than or equal to end of storage 353 if finish_val > end_val: 354 return 0 355 356 # if we have a struct (or other data type that the compiler pads to native word size) 357 # this check might fail, unless the sizeof() we get is itself incremented to take the 358 # padding bytes into account - on current clang it looks like 359 # this is the case 360 num_children = (finish_val - start_val) 361 if (num_children % self.data_size) != 0: 362 return 0 363 else: 364 num_children = num_children // self.data_size 365 return num_children 366 except: 367 return 0 368 369 def get_child_at_index(self, index): 370 logger = lldb.formatters.Logger.Logger() 371 logger >> "Retrieving child " + str(index) 372 if index < 0: 373 return None 374 if index >= self.num_children(): 375 return None 376 try: 377 offset = index * self.data_size 378 return self.start.CreateChildAtOffset( 379 '[' + str(index) + ']', offset, self.data_type) 380 except: 381 return None 382 383 def update(self): 384 # preemptively setting this to None - we might end up changing our 385 # mind later 386 self.count = None 387 try: 388 impl = self.valobj.GetChildMemberWithName('_M_impl') 389 self.start = impl.GetChildMemberWithName('_M_start') 390 self.finish = impl.GetChildMemberWithName('_M_finish') 391 self.end = impl.GetChildMemberWithName('_M_end_of_storage') 392 self.data_type = self.start.GetType().GetPointeeType() 393 self.data_size = self.data_type.GetByteSize() 394 # if any of these objects is invalid, it means there is no 395 # point in trying to fetch anything 396 if self.start.IsValid() and self.finish.IsValid( 397 ) and self.end.IsValid() and self.data_type.IsValid(): 398 self.count = None 399 else: 400 self.count = 0 401 except: 402 pass 403 return False 404 405 class StdVBoolImplementation(object): 406 407 def __init__(self, valobj, bool_type): 408 self.valobj = valobj 409 self.bool_type = bool_type 410 self.valid = False 411 412 def num_children(self): 413 if self.valid: 414 start = self.start_p.GetValueAsUnsigned(0) 415 finish = self.finish_p.GetValueAsUnsigned(0) 416 offset = self.offset.GetValueAsUnsigned(0) 417 if finish >= start: 418 return (finish - start) * 8 + offset 419 return 0 420 421 def get_child_at_index(self, index): 422 if index >= self.num_children(): 423 return None 424 element_type = self.start_p.GetType().GetPointeeType() 425 element_bits = 8 * element_type.GetByteSize() 426 element_offset = (index // element_bits) * \ 427 element_type.GetByteSize() 428 bit_offset = index % element_bits 429 element = self.start_p.CreateChildAtOffset( 430 '[' + str(index) + ']', element_offset, element_type) 431 bit = element.GetValueAsUnsigned(0) & (1 << bit_offset) 432 if bit != 0: 433 value_expr = "(bool)true" 434 else: 435 value_expr = "(bool)false" 436 return self.valobj.CreateValueFromExpression( 437 "[%d]" % index, value_expr) 438 439 def update(self): 440 try: 441 m_impl = self.valobj.GetChildMemberWithName('_M_impl') 442 self.m_start = m_impl.GetChildMemberWithName('_M_start') 443 self.m_finish = m_impl.GetChildMemberWithName('_M_finish') 444 self.start_p = self.m_start.GetChildMemberWithName('_M_p') 445 self.finish_p = self.m_finish.GetChildMemberWithName('_M_p') 446 self.offset = self.m_finish.GetChildMemberWithName('_M_offset') 447 self.valid = True 448 except: 449 self.valid = False 450 return False 451 452 def __init__(self, valobj, dict): 453 logger = lldb.formatters.Logger.Logger() 454 first_template_arg_type = valobj.GetType().GetTemplateArgumentType(0) 455 if str(first_template_arg_type.GetName()) == "bool": 456 self.impl = self.StdVBoolImplementation( 457 valobj, first_template_arg_type) 458 else: 459 self.impl = self.StdVectorImplementation(valobj) 460 logger >> "Providing synthetic children for a vector named " + \ 461 str(valobj.GetName()) 462 463 def num_children(self): 464 return self.impl.num_children() 465 466 def get_child_index(self, name): 467 try: 468 return int(name.lstrip('[').rstrip(']')) 469 except: 470 return -1 471 472 def get_child_at_index(self, index): 473 return self.impl.get_child_at_index(index) 474 475 def update(self): 476 return self.impl.update() 477 478 def has_children(self): 479 return True 480 481 """ 482 This formatter can be applied to all 483 map-like structures (map, multimap, set, multiset) 484 """ 485class StdMapLikeSynthProvider: 486 487 def __init__(self, valobj, dict): 488 logger = lldb.formatters.Logger.Logger() 489 self.valobj = valobj 490 self.count = None 491 self.kind = self.get_object_kind(valobj) 492 logger >> "Providing synthetic children for a " + self.kind + " named " + \ 493 str(valobj.GetName()) 494 495 def get_object_kind(self, valobj): 496 type_name = valobj.GetTypeName() 497 for kind in ["multiset", "multimap", "set", "map"]: 498 if kind in type_name: 499 return kind 500 return type_name 501 502 # we need this function as a temporary workaround for rdar://problem/10801549 503 # which prevents us from extracting the std::pair<K,V> SBType out of the template 504 # arguments for _Rep_Type _M_t in the object itself - because we have to make up the 505 # typename and then find it, we may hit the situation were std::string has multiple 506 # names but only one is actually referenced in the debug information. hence, we need 507 # to replace the longer versions of std::string with the shorter one in order to be able 508 # to find the type name 509 def fixup_class_name(self, class_name): 510 logger = lldb.formatters.Logger.Logger() 511 if class_name == 'std::basic_string<char, std::char_traits<char>, std::allocator<char> >': 512 return 'std::basic_string<char>', True 513 if class_name == 'basic_string<char, std::char_traits<char>, std::allocator<char> >': 514 return 'std::basic_string<char>', True 515 if class_name == 'std::basic_string<char, std::char_traits<char>, std::allocator<char> >': 516 return 'std::basic_string<char>', True 517 if class_name == 'basic_string<char, std::char_traits<char>, std::allocator<char> >': 518 return 'std::basic_string<char>', True 519 return class_name, False 520 521 def update(self): 522 logger = lldb.formatters.Logger.Logger() 523 # preemptively setting this to None - we might end up changing our mind 524 # later 525 self.count = None 526 try: 527 # 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 528 # if this gets set to True, then we will merrily return None for 529 # any child from that moment on 530 self.garbage = False 531 self.Mt = self.valobj.GetChildMemberWithName('_M_t') 532 self.Mimpl = self.Mt.GetChildMemberWithName('_M_impl') 533 self.Mheader = self.Mimpl.GetChildMemberWithName('_M_header') 534 535 map_type = self.valobj.GetType() 536 if map_type.IsReferenceType(): 537 logger >> "Dereferencing type" 538 map_type = map_type.GetDereferencedType() 539 540 # Get the type of std::pair<key, value>. It is the first template 541 # argument type of the 4th template argument to std::map. 542 allocator_type = map_type.GetTemplateArgumentType(3) 543 self.data_type = allocator_type.GetTemplateArgumentType(0) 544 if not self.data_type: 545 # GCC does not emit DW_TAG_template_type_parameter for 546 # std::allocator<...>. For such a case, get the type of 547 # std::pair from a member of std::map. 548 rep_type = self.valobj.GetChildMemberWithName('_M_t').GetType() 549 self.data_type = rep_type.GetTypedefedType().GetTemplateArgumentType(1) 550 551 # from libstdc++ implementation of _M_root for rbtree 552 self.Mroot = self.Mheader.GetChildMemberWithName('_M_parent') 553 self.data_size = self.data_type.GetByteSize() 554 self.skip_size = self.Mheader.GetType().GetByteSize() 555 except: 556 pass 557 return False 558 559 def num_children(self): 560 logger = lldb.formatters.Logger.Logger() 561 if self.count is None: 562 self.count = self.num_children_impl() 563 return self.count 564 565 def num_children_impl(self): 566 logger = lldb.formatters.Logger.Logger() 567 try: 568 root_ptr_val = self.node_ptr_value(self.Mroot) 569 if root_ptr_val == 0: 570 return 0 571 count = self.Mimpl.GetChildMemberWithName( 572 '_M_node_count').GetValueAsUnsigned(0) 573 logger >> "I have " + str(count) + " children available" 574 return count 575 except: 576 return 0 577 578 def get_child_index(self, name): 579 logger = lldb.formatters.Logger.Logger() 580 try: 581 return int(name.lstrip('[').rstrip(']')) 582 except: 583 return -1 584 585 def get_child_at_index(self, index): 586 logger = lldb.formatters.Logger.Logger() 587 logger >> "Being asked to fetch child[" + str(index) + "]" 588 if index < 0: 589 return None 590 if index >= self.num_children(): 591 return None 592 if self.garbage: 593 logger >> "Returning None since we are a garbage tree" 594 return None 595 try: 596 offset = index 597 current = self.left(self.Mheader) 598 while offset > 0: 599 current = self.increment_node(current) 600 offset = offset - 1 601 # skip all the base stuff and get at the data 602 return current.CreateChildAtOffset( 603 '[' + str(index) + ']', self.skip_size, self.data_type) 604 except: 605 return None 606 607 # utility functions 608 def node_ptr_value(self, node): 609 logger = lldb.formatters.Logger.Logger() 610 return node.GetValueAsUnsigned(0) 611 612 def right(self, node): 613 logger = lldb.formatters.Logger.Logger() 614 return node.GetChildMemberWithName("_M_right") 615 616 def left(self, node): 617 logger = lldb.formatters.Logger.Logger() 618 return node.GetChildMemberWithName("_M_left") 619 620 def parent(self, node): 621 logger = lldb.formatters.Logger.Logger() 622 return node.GetChildMemberWithName("_M_parent") 623 624 # from libstdc++ implementation of iterator for rbtree 625 def increment_node(self, node): 626 logger = lldb.formatters.Logger.Logger() 627 max_steps = self.num_children() 628 if self.node_ptr_value(self.right(node)) != 0: 629 x = self.right(node) 630 max_steps -= 1 631 while self.node_ptr_value(self.left(x)) != 0: 632 x = self.left(x) 633 max_steps -= 1 634 logger >> str(max_steps) + " more to go before giving up" 635 if max_steps <= 0: 636 self.garbage = True 637 return None 638 return x 639 else: 640 x = node 641 y = self.parent(x) 642 max_steps -= 1 643 while(self.node_ptr_value(x) == self.node_ptr_value(self.right(y))): 644 x = y 645 y = self.parent(y) 646 max_steps -= 1 647 logger >> str(max_steps) + " more to go before giving up" 648 if max_steps <= 0: 649 self.garbage = True 650 return None 651 if self.node_ptr_value(self.right(x)) != self.node_ptr_value(y): 652 x = y 653 return x 654 655 def has_children(self): 656 return True 657 658_list_uses_loop_detector = True 659