1# GDB pretty printers for STLport. 2# 3# Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc. 4# Copyright (C) 2010 Joachim Reichel 5# 6# This program is free software; you can redistribute it and/or modify 7# it under the terms of the GNU General Public License as published by 8# the Free Software Foundation; either version 3 of the License, or 9# (at your option) any later version. 10# 11# This program is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14# GNU General Public License for more details. 15# 16# You should have received a copy of the GNU General Public License 17# along with this program. If not, see <http://www.gnu.org/licenses/>. 18 19 20# pylint: disable=C0103,C0111,R0201,R0903 21 22 23import gdb 24import re 25 26 27# Set the STLport version which is needed for a few features. 28# 29# - for std::list: 30# STLport older than 5.0? 31# - for std::deque, std::stack, and std::queue on 64bit systems: 32# STLport older than 5.2? 33stlport_version = 5.2 34 35# Indicates whether std::vector is printed with indices. 36print_vector_with_indices = False 37 38 39def lookup_stlport_type (typename): 40 "Look up a type in the public STLport namespace." 41 42 namespaces = ['std::', 'stlpd_std::', 'stlp_std::', '_STL::'] 43 for namespace in namespaces: 44 try: 45 return gdb.lookup_type (namespace + typename) 46 except RuntimeError: 47 pass 48 49def lookup_stlport_priv_type (typename): 50 "Look up a type in the private STLport namespace." 51 52 namespaces = ['std::priv::', 'stlpd_std::priv::', 'stlp_priv::', 'stlp_std::priv::', 53 'stlpd_std::', 'stlp_std::', '_STL::'] 54 for namespace in namespaces: 55 try: 56 return gdb.lookup_type (namespace + typename) 57 except RuntimeError: 58 pass 59 60 61def get_non_debug_impl (value, member = None): 62 "Return the non-debug implementation of value or value[member]." 63 if member: 64 value = value[member] 65 try: 66 return value['_M_non_dbg_impl'] 67 except RuntimeError: 68 return value 69 70 71class RbtreeIterator: 72 73 def __init__ (self, rbtree): 74 tree = get_non_debug_impl (rbtree , '_M_t') 75 self.size = tree['_M_node_count'] 76 self.node = tree['_M_header']['_M_data']['_M_left'] 77 self.count = 0 78 79 def __iter__ (self): 80 return self 81 82 def __len__ (self): 83 return int (self.size) 84 85 def next (self): 86 if self.count == self.size: 87 raise StopIteration 88 result = self.node 89 self.count += 1 90 if self.count < self.size: 91 node = self.node 92 # Is there a right child? 93 if node.dereference()['_M_right']: 94 # Walk down to left-most child in right subtree. 95 node = node.dereference()['_M_right'] 96 while node.dereference()['_M_left']: 97 node = node.dereference()['_M_left'] 98 else: 99 # Walk up to first parent reached via left subtree. 100 parent = node.dereference()['_M_parent'] 101 while node == parent.dereference()['_M_right']: 102 node = parent 103 parent = parent.dereference()['_M_parent'] 104 node = parent 105 self.node = node 106 return result 107 108 109class BitsetPrinter: 110 "Pretty printer for std::bitset." 111 112 def __init__(self, typename, val): 113 self.typename = typename 114 self.val = val 115 116 def to_string (self): 117 # If template_argument handled values, we could print the 118 # size. Or we could use a regexp on the type. 119 return '%s' % (self.typename) 120 121 def children (self): 122 words = self.val['_M_w'] 123 124 # The _M_w member can be either an unsigned long, or an 125 # array. This depends on the template specialization used. 126 # If it is a single long, convert to a single element list. 127 if words.type.code == gdb.TYPE_CODE_ARRAY: 128 word_size = words.type.target ().sizeof 129 n_words = words.type.sizeof / word_size 130 else: 131 word_size = words.type.sizeof 132 n_words = 1 133 words = [words] 134 135 result = [] 136 word = 0 137 while word < n_words: 138 w = words[word] 139 bit = 0 140 while w != 0: 141 if w & 1: 142 result.append (('[%d]' % (word * word_size * 8 + bit), 1)) 143 bit += 1 144 w = w >> 1 145 word += 1 146 return result 147 148 149class DequePrinter: 150 "Pretty printer for std::deque." 151 152 class Iterator: 153 def __init__ (self, start_node, start_cur, start_last, 154 finish_cur, buffer_size): 155 self.node = start_node 156 self.item = start_cur 157 self.node_last = start_last 158 self.last = finish_cur 159 self.buffer_size = buffer_size 160 self.count = 0 161 162 def __iter__ (self): 163 return self 164 165 def next (self): 166 if self.item == self.last: 167 raise StopIteration 168 result = ('[%d]' % self.count, self.item.dereference()) 169 self.count += 1 170 self.item += 1 171 if self.item == self.node_last: 172 self.node += 1 173 self.item = self.node[0] 174 self.node_last = self.item + self.buffer_size 175 return result 176 177 def __init__ (self, typename, val): 178 self.typename = typename 179 self.val = get_non_debug_impl (val) 180 size = val.type.template_argument(0).sizeof 181 # see MAX_BYTES in stlport/stl/_alloc.h 182 if stlport_version < 5.2: 183 blocksize = 128 184 else: 185 blocksize = 32 * gdb.lookup_type ("void").pointer().sizeof 186 if size < blocksize: 187 self.buffer_size = int (blocksize / size) 188 else: 189 self.buffer_size = 1 190 191 def to_string (self): 192 start = self.val['_M_start'] 193 finish = self.val['_M_finish'] 194 delta_n = finish['_M_node'] - start['_M_node'] - 1 195 delta_s = start['_M_last'] - start['_M_cur'] 196 delta_f = finish['_M_cur'] - finish['_M_first'] 197 if delta_n == -1: 198 size = delta_f 199 else: 200 size = self.buffer_size * delta_n + delta_s + delta_f 201 ta0 = self.val.type.template_argument (0) 202 return '%s<%s> with %d elements' % (self.typename, ta0, int (size)) 203 204 def children (self): 205 start = self.val['_M_start'] 206 finish = self.val['_M_finish'] 207 return self.Iterator (start['_M_node'], start['_M_cur'], 208 start['_M_last'], finish['_M_cur'], self.buffer_size) 209 210 def display_hint (self): 211 return 'array' 212 213 214class ListPrinter: 215 "Pretty printer for std::list." 216 217 class Iterator: 218 def __init__ (self, node_type, head): 219 self.node_type = node_type 220 # see empty() in stlport/stl/_list.h 221 if stlport_version < 5.0: 222 self.sentinel = head 223 else: 224 self.sentinel = head.address 225 self.item = head['_M_next'] 226 self.count = 0 227 228 def __iter__ (self): 229 return self 230 231 def next (self): 232 if self.item == self.sentinel: 233 raise StopIteration 234 node = self.item.cast (self.node_type).dereference() 235 self.item = node['_M_next'] 236 count = self.count 237 self.count += 1 238 return ('[%d]' % count, node['_M_data']) 239 240 def __init__(self, typename, val): 241 self.typename = typename 242 self.val = get_non_debug_impl (val) 243 244 def children (self): 245 ta0 = self.val.type.template_argument(0) 246 node_type = lookup_stlport_priv_type ('_List_node<%s>' % ta0).pointer() 247 return self.Iterator (node_type, self.val['_M_node']['_M_data']) 248 249 def to_string (self): 250 ta0 = self.val.type.template_argument (0) 251 # see empty() in stlport/stl/_list.h 252 if stlport_version < 5.0: 253 sentinel = self.val['_M_node']['_M_data'] 254 else: 255 sentinel = self.val['_M_node']['_M_data'].address 256 if self.val['_M_node']['_M_data']['_M_next'] == sentinel: 257 return 'empty %s<%s>' % (self.typename, ta0) 258 return '%s<%s>' % (self.typename, ta0) 259 260 def display_hint (self): 261 return 'array' 262 263 264class MapPrinter: 265 "Pretty printer for std::map and std::multimap." 266 267 class Iterator: 268 269 def __init__ (self, rbiter, node_type): 270 self.rbiter = rbiter 271 self.node_type = node_type 272 self.count = 0 273 274 def __iter__ (self): 275 return self 276 277 def next (self): 278 if self.count % 2 == 0: 279 item = self.rbiter.next().dereference() 280 self.pair = (item.cast (self.node_type))['_M_value_field'] 281 element = self.pair['first'] 282 else: 283 element = self.pair['second'] 284 count = self.count 285 self.count += 1 286 return ('[%d]' % count, element) 287 288 def __init__ (self, typename, val): 289 self.typename = typename 290 self.val = val 291 292 def children (self): 293 key_type = self.val.type.template_argument (0) 294 value_type = self.val.type.template_argument (1) 295 pair_type \ 296 = lookup_stlport_type ('pair<%s const,%s>' % (key_type,value_type)) 297 node_type \ 298 = lookup_stlport_priv_type ('_Rb_tree_node<%s >' % str (pair_type)) 299 return self.Iterator (RbtreeIterator (self.val), node_type) 300 301 def to_string (self): 302 ta0 = self.val.type.template_argument (0) 303 count = get_non_debug_impl (self.val, '_M_t')['_M_node_count'] 304 return ('%s<%s> with %d elements' % (self.typename, ta0, count)) 305 306 def display_hint (self): 307 return 'map' 308 309 310class SetPrinter: 311 "Pretty printer for std::set and std::multiset." 312 313 class Iterator: 314 def __init__ (self, rbiter, node_type): 315 self.rbiter = rbiter 316 self.node_type = node_type 317 self.count = 0 318 319 def __iter__ (self): 320 return self 321 322 def next (self): 323 item = self.rbiter.next().dereference() 324 element = (item.cast (self.node_type))['_M_value_field'] 325 count = self.count 326 self.count += 1 327 return ('[%d]' % count, element) 328 329 def __init__ (self, typename, val): 330 self.typename = typename 331 self.val = val 332 333 def children (self): 334 value_type = self.val.type.template_argument (0) 335 node_type \ 336 = lookup_stlport_priv_type ('_Rb_tree_node<%s>' % (value_type)) 337 return self.Iterator (RbtreeIterator (self.val), node_type) 338 339 def to_string (self): 340 ta0 = self.val.type.template_argument (0) 341 count = get_non_debug_impl (self.val, '_M_t')['_M_node_count'] 342 return ('%s<%s> with %d elements' % (self.typename, ta0, count)) 343 344 def display_hint (self): 345 return 'array' 346 347 348class SlistPrinter: 349 "Pretty printer for std::slist." 350 351 class Iterator: 352 def __init__ (self, node_type, head): 353 self.node_type = node_type 354 self.item = head['_M_next'] 355 self.count = 0 356 357 def __iter__ (self): 358 return self 359 360 def next (self): 361 if self.item == 0: 362 raise StopIteration 363 node = self.item.cast (self.node_type).dereference() 364 self.item = node['_M_next'] 365 count = self.count 366 self.count += 1 367 return ('[%d]' % count, node['_M_data']) 368 369 def __init__(self, typename, val): 370 self.typename = typename 371 self.val = get_non_debug_impl (val) 372 373 def children (self): 374 ta0 = self.val.type.template_argument(0) 375 node_type = lookup_stlport_priv_type ('_Slist_node<%s>' % ta0).pointer() 376 return self.Iterator (node_type, self.val['_M_head']['_M_data']) 377 378 def to_string (self): 379 ta0 = self.val.type.template_argument (0) 380 if self.val['_M_head']['_M_data']['_M_next'] == 0: 381 return 'empty %s<%s>' % (self.typename, ta0) 382 return '%s<%s>' % (self.typename, ta0) 383 384 def display_hint (self): 385 return 'array' 386 387 388class StringPrinter: 389 "Pretty printer for std::string or std::wstring." 390 391 def __init__ (self, _typename, val): 392 self.val = get_non_debug_impl (val) 393 394 def to_string (self): 395 try: 396 # STLport 5.2 and later 397 return self.val['_M_start_of_storage']['_M_data'] 398 except RuntimeError: 399 try: 400 # STLport 5.0 and 5.1 with short string optimization 401 static_buf = self.val['_M_buffers']['_M_static_buf'] 402 data = self.val['_M_end_of_storage']['_M_data'] 403 if static_buf.address + 1 == data: 404 ta0 = self.val.type.template_argument (0) 405 start = static_buf.cast (ta0.pointer()) 406 finish = self.val['_M_finish'] 407 if start == finish: 408 # STLport 5.0 without _STLP_FORCE_STRING_TERMINATION 409 return "" 410 return start 411 return self.val['_M_buffers']['_M_dynamic_buf'] 412 except RuntimeError: 413 # STLport 5.0 and 5.1 without short string optimization, 414 # and STLport 4.6 415 start = self.val['_M_start'] 416 finish = self.val['_M_finish'] 417 if start == finish: 418 # STLport 5.0 without _STLP_FORCE_STRING_TERMINATION 419 return "" 420 return start 421 422 def display_hint (self): 423 return 'string' 424 425 426class VectorPrinter: 427 "Pretty printer for std::vector." 428 429 class Iterator: 430 431 def __init__ (self, start, finish, bit_vector): 432 self.bit_vector = bit_vector 433 self.count = 0 434 if bit_vector: 435 self.item = start['_M_p'] 436 self.io = start['_M_offset'] 437 self.finish = finish['_M_p'] 438 self.fo = finish['_M_offset'] 439 self.isize = 8 * self.item.dereference().type.sizeof 440 else: 441 self.item = start 442 self.finish = finish 443 444 def __iter__ (self): 445 return self 446 447 def next (self): 448 count = self.count 449 self.count += 1 450 if self.bit_vector: 451 if self.item == self.finish and self.io == self.fo: 452 raise StopIteration 453 element = self.item.dereference() 454 value = 0 455 if element & (1 << self.io): 456 value = 1 457 self.io += 1 458 if self.io >= self.isize: 459 self.item += 1 460 self.io = 0 461 return ('[%d]' % count, value) 462 else: 463 if self.item == self.finish: 464 raise StopIteration 465 element = self.item.dereference() 466 self.item += 1 467 return ('[%d]' % count, element) 468 469 def __init__ (self, typename, val): 470 self.typename = typename 471 self.val = get_non_debug_impl (val) 472 self.bit_vector \ 473 = val.type.template_argument (0).code == gdb.TYPE_CODE_BOOL 474 475 def children (self): 476 start = self.val['_M_start'] 477 finish = self.val['_M_finish'] 478 return self.Iterator (start, finish, self.bit_vector) 479 480 def to_string (self): 481 if self.bit_vector: 482 start = self.val['_M_start']['_M_p'] 483 so = self.val['_M_start']['_M_offset'] 484 finish = self.val['_M_finish']['_M_p'] 485 fo = self.val['_M_finish']['_M_offset'] 486 end = self.val['_M_end_of_storage']['_M_data'] 487 isize = 8 * start.dereference().type.sizeof 488 length = (isize - so) + isize * (finish - start - 1) + fo 489 capacity = isize * (end - start) 490 return ('%s<bool> of length %d, capacity %d' 491 % (self.typename, length, capacity)) 492 else: 493 start = self.val['_M_start'] 494 finish = self.val['_M_finish'] 495 end = self.val['_M_end_of_storage']['_M_data'] 496 length = finish - start 497 capacity = end - start 498 ta0 = self.val.type.template_argument (0) 499 return ('%s<%s> of length %d, capacity %d' 500 % (self.typename, ta0, length, capacity)) 501 502 def display_hint (self): 503 if print_vector_with_indices: 504 return None 505 else: 506 return 'array' 507 508 509class WrapperPrinter: 510 "Pretty printer for std::stack, std::queue, and std::priority_queue." 511 512 def __init__ (self, typename, val): 513 self.typename = typename 514 self.val = val 515 self.visualizer = gdb.default_visualizer (val['c']) 516 517 def children (self): 518 return self.visualizer.children() 519 520 def to_string (self): 521 ta0 = self.val.type.template_argument (0) 522 return ('%s<%s>, wrapping %s' 523 % (self.typename, ta0, self.visualizer.to_string())) 524 525 def display_hint (self): 526 if hasattr (self.visualizer, 'display_hint'): 527 return self.visualizer.display_hint() 528 return None 529 530 531class UnorderedMapPrinter: 532 """Pretty printer for std::tr1::unordered_map 533 and std::tr1::unordered_multimap.""" 534 535 class Iterator: 536 def __init__ (self, node_type, head): 537 self.node_type = node_type 538 self.item = head['_M_next'] 539 self.count = 0 540 541 def __iter__ (self): 542 return self 543 544 def next (self): 545 if self.item == 0 and self.count % 2 == 0: 546 raise StopIteration 547 if self.count % 2 == 0: 548 self.pair = self.item.cast (self.node_type).dereference() 549 self.item = self.pair['_M_next'] 550 element = self.pair['_M_data']['first'] 551 else: 552 element = self.pair['_M_data']['second'] 553 count = self.count 554 self.count += 1 555 return ('[%d]' % count, element) 556 557 def __init__(self, typename, val): 558 self.typename = typename 559 self.val = get_non_debug_impl (val) 560 561 def children (self): 562 key_type = self.val.type.template_argument (0) 563 value_type = self.val.type.template_argument (1) 564 pair_type \ 565 = lookup_stlport_type ('pair<%s const,%s>' % (key_type,value_type)) 566 node_type \ 567 = lookup_stlport_priv_type ('_Slist_node<%s >' 568 % str (pair_type)).pointer() 569 elements = get_non_debug_impl (self.val, '_M_ht')['_M_elems'] 570 return self.Iterator (node_type, elements['_M_head']['_M_data']) 571 572 def to_string (self): 573 ta0 = self.val.type.template_argument (0) 574 length = get_non_debug_impl (self.val, '_M_ht')['_M_num_elements'] 575 if length == 0: 576 return 'empty %s<%s>' % (self.typename, ta0) 577 return '%s<%s> with %d elements' % (self.typename, ta0, length) 578 579 def display_hint (self): 580 return 'map' 581 582 583class UnorderedSetPrinter: 584 """Pretty printer for std::tr1::unordered_set 585 and std::tr1::unordered_multiset.""" 586 587 class Iterator: 588 def __init__ (self, node_type, head): 589 self.node_type = node_type 590 self.item = head['_M_next'] 591 self.count = 0 592 593 def __iter__ (self): 594 return self 595 596 def next (self): 597 if self.item == 0: 598 raise StopIteration 599 node = self.item.cast (self.node_type).dereference() 600 self.item = node['_M_next'] 601 count = self.count 602 self.count += 1 603 return ('[%d]' % count, node['_M_data']) 604 605 def __init__(self, typename, val): 606 self.typename = typename 607 self.val = get_non_debug_impl (val) 608 609 def children (self): 610 ta0 = self.val.type.template_argument(0) 611 node_type = lookup_stlport_priv_type ('_Slist_node<%s>' % ta0).pointer() 612 elements = get_non_debug_impl (self.val, '_M_ht')['_M_elems'] 613 return self.Iterator (node_type, elements['_M_head']['_M_data']) 614 615 def to_string (self): 616 ta0 = self.val.type.template_argument (0) 617 length = get_non_debug_impl (self.val, '_M_ht')['_M_num_elements'] 618 if length == 0: 619 return 'empty %s<%s>' % (self.typename, ta0) 620 return '%s<%s> with %d elements' % (self.typename, ta0, length) 621 622 def display_hint (self): 623 return 'array' 624 625 626class AutoptrPrinter: 627 "Pretty printer for std::auto_ptr." 628 629 def __init__ (self, typename, val): 630 self.typename = typename 631 self.val = val 632 633 def to_string (self): 634 ta0 = self.val.type.template_argument (0) 635 pointer = self.val['_M_p'].cast (ta0.pointer()) 636 if pointer == 0: 637 return ('%s<%s> (empty)' % (self.typename, ta0)) 638 else: 639 return ('%s<%s>, pointing to %s' 640 % (self.typename, ta0, pointer.dereference())) 641 642 def display_hint (self): 643 return None 644 645 646class SharedptrPrinter: 647 "Pretty printer for std::shared_ptr and std::weak_ptr." 648 649 def __init__ (self, typename, val): 650 self.typename = typename 651 self.val = val 652 653 def to_string (self): 654 ta0 = self.val.type.template_argument (0) 655 pointer = self.val['px'].cast (ta0.pointer()) 656 if pointer == 0: 657 return ('%s<%s> (empty)' % (self.typename, ta0)) 658 else: 659 count = self.val['pn']['pi_']['use_count_'] 660 return ('%s<%s> (count %d), pointing to %s' 661 % (self.typename, ta0, count, pointer.dereference())) 662 663 def display_hint (self): 664 return None 665 666 667def lookup_function (val): 668 "Look-up and return a pretty-printer that can print val." 669 670 type = val.type 671 if type.code == gdb.TYPE_CODE_REF: 672 type = type.target() 673 type = type.unqualified().strip_typedefs() 674 675 typename = type.tag 676 if typename == None: 677 return None 678 679 for function in pretty_printers_dict: 680 if function.search (typename): 681 return pretty_printers_dict[function] (val) 682 return None 683 684 685def register_stlport_printers (obj): 686 "Register STLport pretty-printers with object file obj." 687 688 if obj == None: 689 obj = gdb 690 obj.pretty_printers.append (lookup_function) 691 692 693 694pretty_printers_dict = {} 695 696def add_entry (regex, printer, typename): 697 prefix = "^(stlpd?_std|_STL|std)::" 698 suffix = "<.*>$" 699 if typename != None: 700 typename = "std::" + typename 701 if regex[0:5] == "boost": 702 prefix = "" 703 pretty_printers_dict[re.compile (prefix+regex+suffix)] \ 704 = lambda val: printer (typename, val) 705 706add_entry ("basic_string", StringPrinter, None) 707add_entry ("bitset", BitsetPrinter, "bitset") 708add_entry ("deque", DequePrinter, "deque") 709add_entry ("map", MapPrinter, "map") 710add_entry ("list", ListPrinter, "list") 711add_entry ("multimap", MapPrinter, "multimap") 712add_entry ("multiset", SetPrinter, "multiset") 713add_entry ("queue", WrapperPrinter, "queue") 714add_entry ("priority_queue", WrapperPrinter, "priority_queue") 715add_entry ("set", SetPrinter, "set") 716add_entry ("slist", SlistPrinter, "slist") 717add_entry ("stack", WrapperPrinter, "stack") 718add_entry ("vector", VectorPrinter, "vector") 719 720add_entry ("tr1::unordered_map", UnorderedMapPrinter, "tr1::unordered_map") 721add_entry ("tr1::unordered_multimap", UnorderedMapPrinter, "tr1::unordered_multimap") 722add_entry ("tr1::unordered_set", UnorderedSetPrinter, "tr1::unordered_set") 723add_entry ("tr1::unordered_multiset", UnorderedSetPrinter, "tr1::unordered_multiset") 724 725add_entry ("auto_ptr", AutoptrPrinter, "auto_ptr") 726add_entry ("boost::shared_ptr", SharedptrPrinter, "tr1::shared_ptr") 727add_entry ("boost::weak_ptr", SharedptrPrinter, "tr1::weak_ptr") 728