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