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