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