xref: /openbsd-src/gnu/llvm/lldb/examples/synthetic/libcxx.py (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1061da546Spatrickimport lldb
2061da546Spatrickimport lldb.formatters.Logger
3061da546Spatrick
4061da546Spatrick# libcxx STL formatters for LLDB
5061da546Spatrick# These formatters are based upon the implementation of libc++ that
6061da546Spatrick# ships with current releases of OS X - They will not work for other implementations
7061da546Spatrick# of the standard C++ library - and they are bound to use the
8061da546Spatrick# libc++-specific namespace
9061da546Spatrick
10061da546Spatrick# the std::string summary is just an example for your convenience
11061da546Spatrick# the actual summary that LLDB uses is C++ code inside the debugger's own core
12061da546Spatrick
13061da546Spatrick# this could probably be made more efficient but since it only reads a handful of bytes at a time
14061da546Spatrick# we probably don't need to worry too much about this for the time being
15061da546Spatrick
16061da546Spatrick
17061da546Spatrickdef make_string(F, L):
18061da546Spatrick    strval = ''
19061da546Spatrick    G = F.GetData().uint8
20061da546Spatrick    for X in range(L):
21061da546Spatrick        V = G[X]
22061da546Spatrick        if V == 0:
23061da546Spatrick            break
24061da546Spatrick        strval = strval + chr(V % 256)
25061da546Spatrick    return '"' + strval + '"'
26061da546Spatrick
27061da546Spatrick# if we ever care about big-endian, these two functions might need to change
28061da546Spatrick
29061da546Spatrick
30061da546Spatrickdef is_short_string(value):
31061da546Spatrick    return True if (value & 1) == 0 else False
32061da546Spatrick
33061da546Spatrick
34061da546Spatrickdef extract_short_size(value):
35061da546Spatrick    return ((value >> 1) % 256)
36061da546Spatrick
37061da546Spatrick# some of the members of libc++ std::string are anonymous or have internal names that convey
38061da546Spatrick# no external significance - we access them by index since this saves a name lookup that would add
39061da546Spatrick# no information for readers of the code, but when possible try to use
40061da546Spatrick# meaningful variable names
41061da546Spatrick
42061da546Spatrick
43061da546Spatrickdef stdstring_SummaryProvider(valobj, dict):
44061da546Spatrick    logger = lldb.formatters.Logger.Logger()
45061da546Spatrick    r = valobj.GetChildAtIndex(0)
46061da546Spatrick    B = r.GetChildAtIndex(0)
47061da546Spatrick    first = B.GetChildAtIndex(0)
48061da546Spatrick    D = first.GetChildAtIndex(0)
49061da546Spatrick    l = D.GetChildAtIndex(0)
50061da546Spatrick    s = D.GetChildAtIndex(1)
51061da546Spatrick    D20 = s.GetChildAtIndex(0)
52061da546Spatrick    size_mode = D20.GetChildAtIndex(0).GetValueAsUnsigned(0)
53061da546Spatrick    if is_short_string(size_mode):
54061da546Spatrick        size = extract_short_size(size_mode)
55061da546Spatrick        return make_string(s.GetChildAtIndex(1), size)
56061da546Spatrick    else:
57061da546Spatrick        data_ptr = l.GetChildAtIndex(2)
58061da546Spatrick        size_vo = l.GetChildAtIndex(1)
59061da546Spatrick        # the NULL terminator must be accounted for
60061da546Spatrick        size = size_vo.GetValueAsUnsigned(0) + 1
61061da546Spatrick        if size <= 1 or size is None:  # should never be the case
62061da546Spatrick            return '""'
63061da546Spatrick        try:
64061da546Spatrick            data = data_ptr.GetPointeeData(0, size)
65061da546Spatrick        except:
66061da546Spatrick            return '""'
67061da546Spatrick        error = lldb.SBError()
68061da546Spatrick        strval = data.GetString(error, 0)
69061da546Spatrick        if error.Fail():
70061da546Spatrick            return '<error:' + error.GetCString() + '>'
71061da546Spatrick        else:
72061da546Spatrick            return '"' + strval + '"'
73061da546Spatrick
74061da546Spatrick
75061da546Spatrickclass stdvector_SynthProvider:
76061da546Spatrick
77061da546Spatrick    def __init__(self, valobj, dict):
78061da546Spatrick        logger = lldb.formatters.Logger.Logger()
79061da546Spatrick        self.valobj = valobj
80061da546Spatrick
81061da546Spatrick    def num_children(self):
82061da546Spatrick        logger = lldb.formatters.Logger.Logger()
83061da546Spatrick        try:
84061da546Spatrick            start_val = self.start.GetValueAsUnsigned(0)
85061da546Spatrick            finish_val = self.finish.GetValueAsUnsigned(0)
86061da546Spatrick            # Before a vector has been constructed, it will contain bad values
87061da546Spatrick            # so we really need to be careful about the length we return since
88061da546Spatrick            # uninitialized data can cause us to return a huge number. We need
89061da546Spatrick            # to also check for any of the start, finish or end of storage values
90061da546Spatrick            # being zero (NULL). If any are, then this vector has not been
91061da546Spatrick            # initialized yet and we should return zero
92061da546Spatrick
93061da546Spatrick            # Make sure nothing is NULL
94061da546Spatrick            if start_val == 0 or finish_val == 0:
95061da546Spatrick                return 0
96061da546Spatrick            # Make sure start is less than finish
97061da546Spatrick            if start_val >= finish_val:
98061da546Spatrick                return 0
99061da546Spatrick
100061da546Spatrick            num_children = (finish_val - start_val)
101061da546Spatrick            if (num_children % self.data_size) != 0:
102061da546Spatrick                return 0
103061da546Spatrick            else:
104061da546Spatrick                num_children = num_children / self.data_size
105061da546Spatrick            return num_children
106061da546Spatrick        except:
107061da546Spatrick            return 0
108061da546Spatrick
109061da546Spatrick    def get_child_index(self, name):
110061da546Spatrick        logger = lldb.formatters.Logger.Logger()
111061da546Spatrick        try:
112061da546Spatrick            return int(name.lstrip('[').rstrip(']'))
113061da546Spatrick        except:
114061da546Spatrick            return -1
115061da546Spatrick
116061da546Spatrick    def get_child_at_index(self, index):
117061da546Spatrick        logger = lldb.formatters.Logger.Logger()
118061da546Spatrick        logger >> "Retrieving child " + str(index)
119061da546Spatrick        if index < 0:
120061da546Spatrick            return None
121061da546Spatrick        if index >= self.num_children():
122061da546Spatrick            return None
123061da546Spatrick        try:
124061da546Spatrick            offset = index * self.data_size
125061da546Spatrick            return self.start.CreateChildAtOffset(
126061da546Spatrick                '[' + str(index) + ']', offset, self.data_type)
127061da546Spatrick        except:
128061da546Spatrick            return None
129061da546Spatrick
130061da546Spatrick    def update(self):
131061da546Spatrick        logger = lldb.formatters.Logger.Logger()
132061da546Spatrick        try:
133061da546Spatrick            self.start = self.valobj.GetChildMemberWithName('__begin_')
134061da546Spatrick            self.finish = self.valobj.GetChildMemberWithName('__end_')
135061da546Spatrick            # the purpose of this field is unclear, but it is the only field whose type is clearly T* for a vector<T>
136061da546Spatrick            # if this ends up not being correct, we can use the APIs to get at
137061da546Spatrick            # template arguments
138061da546Spatrick            data_type_finder = self.valobj.GetChildMemberWithName(
139061da546Spatrick                '__end_cap_').GetChildMemberWithName('__first_')
140061da546Spatrick            self.data_type = data_type_finder.GetType().GetPointeeType()
141061da546Spatrick            self.data_size = self.data_type.GetByteSize()
142061da546Spatrick        except:
143061da546Spatrick            pass
144061da546Spatrick
145061da546Spatrick    def has_children(self):
146061da546Spatrick        return True
147061da546Spatrick
148061da546Spatrick# Just an example: the actual summary is produced by a summary string:
149061da546Spatrick# size=${svar%#}
150061da546Spatrick
151061da546Spatrick
152061da546Spatrickdef stdvector_SummaryProvider(valobj, dict):
153061da546Spatrick    prov = stdvector_SynthProvider(valobj, None)
154061da546Spatrick    return 'size=' + str(prov.num_children())
155061da546Spatrick
156061da546Spatrick
157061da546Spatrickclass stdlist_entry:
158061da546Spatrick
159061da546Spatrick    def __init__(self, entry):
160061da546Spatrick        logger = lldb.formatters.Logger.Logger()
161061da546Spatrick        self.entry = entry
162061da546Spatrick
163061da546Spatrick    def _next_impl(self):
164061da546Spatrick        logger = lldb.formatters.Logger.Logger()
165061da546Spatrick        return stdlist_entry(self.entry.GetChildMemberWithName('__next_'))
166061da546Spatrick
167061da546Spatrick    def _prev_impl(self):
168061da546Spatrick        logger = lldb.formatters.Logger.Logger()
169061da546Spatrick        return stdlist_entry(self.entry.GetChildMemberWithName('__prev_'))
170061da546Spatrick
171061da546Spatrick    def _value_impl(self):
172061da546Spatrick        logger = lldb.formatters.Logger.Logger()
173061da546Spatrick        return self.entry.GetValueAsUnsigned(0)
174061da546Spatrick
175061da546Spatrick    def _isnull_impl(self):
176061da546Spatrick        logger = lldb.formatters.Logger.Logger()
177061da546Spatrick        return self._value_impl() == 0
178061da546Spatrick
179061da546Spatrick    def _sbvalue_impl(self):
180061da546Spatrick        logger = lldb.formatters.Logger.Logger()
181061da546Spatrick        return self.entry
182061da546Spatrick
183061da546Spatrick    next = property(_next_impl, None)
184061da546Spatrick    value = property(_value_impl, None)
185061da546Spatrick    is_null = property(_isnull_impl, None)
186061da546Spatrick    sbvalue = property(_sbvalue_impl, None)
187061da546Spatrick
188061da546Spatrick
189061da546Spatrickclass stdlist_iterator:
190061da546Spatrick
191061da546Spatrick    def increment_node(self, node):
192061da546Spatrick        logger = lldb.formatters.Logger.Logger()
193061da546Spatrick        if node.is_null:
194061da546Spatrick            return None
195061da546Spatrick        return node.next
196061da546Spatrick
197061da546Spatrick    def __init__(self, node):
198061da546Spatrick        logger = lldb.formatters.Logger.Logger()
199061da546Spatrick        # we convert the SBValue to an internal node object on entry
200061da546Spatrick        self.node = stdlist_entry(node)
201061da546Spatrick
202061da546Spatrick    def value(self):
203061da546Spatrick        logger = lldb.formatters.Logger.Logger()
204061da546Spatrick        return self.node.sbvalue  # and return the SBValue back on exit
205061da546Spatrick
206061da546Spatrick    def next(self):
207061da546Spatrick        logger = lldb.formatters.Logger.Logger()
208061da546Spatrick        node = self.increment_node(self.node)
209061da546Spatrick        if node is not None and node.sbvalue.IsValid() and not(node.is_null):
210061da546Spatrick            self.node = node
211061da546Spatrick            return self.value()
212061da546Spatrick        else:
213061da546Spatrick            return None
214061da546Spatrick
215061da546Spatrick    def advance(self, N):
216061da546Spatrick        logger = lldb.formatters.Logger.Logger()
217061da546Spatrick        if N < 0:
218061da546Spatrick            return None
219061da546Spatrick        if N == 0:
220061da546Spatrick            return self.value()
221061da546Spatrick        if N == 1:
222061da546Spatrick            return self.next()
223061da546Spatrick        while N > 0:
224061da546Spatrick            self.next()
225061da546Spatrick            N = N - 1
226061da546Spatrick        return self.value()
227061da546Spatrick
228061da546Spatrick
229061da546Spatrickclass stdlist_SynthProvider:
230061da546Spatrick
231061da546Spatrick    def __init__(self, valobj, dict):
232061da546Spatrick        logger = lldb.formatters.Logger.Logger()
233061da546Spatrick        self.valobj = valobj
234061da546Spatrick        self.count = None
235061da546Spatrick
236061da546Spatrick    def next_node(self, node):
237061da546Spatrick        logger = lldb.formatters.Logger.Logger()
238061da546Spatrick        return node.GetChildMemberWithName('__next_')
239061da546Spatrick
240061da546Spatrick    def value(self, node):
241061da546Spatrick        logger = lldb.formatters.Logger.Logger()
242061da546Spatrick        return node.GetValueAsUnsigned()
243061da546Spatrick
244061da546Spatrick    # Floyd's cycle-finding algorithm
245061da546Spatrick    # try to detect if this list has a loop
246061da546Spatrick    def has_loop(self):
247061da546Spatrick        global _list_uses_loop_detector
248061da546Spatrick        logger = lldb.formatters.Logger.Logger()
249061da546Spatrick        if not _list_uses_loop_detector:
250061da546Spatrick            logger >> "Asked not to use loop detection"
251061da546Spatrick            return False
252061da546Spatrick        slow = stdlist_entry(self.head)
253061da546Spatrick        fast1 = stdlist_entry(self.head)
254061da546Spatrick        fast2 = stdlist_entry(self.head)
255061da546Spatrick        while slow.next.value != self.node_address:
256061da546Spatrick            slow_value = slow.value
257061da546Spatrick            fast1 = fast2.next
258061da546Spatrick            fast2 = fast1.next
259061da546Spatrick            if fast1.value == slow_value or fast2.value == slow_value:
260061da546Spatrick                return True
261061da546Spatrick            slow = slow.next
262061da546Spatrick        return False
263061da546Spatrick
264061da546Spatrick    def num_children(self):
265061da546Spatrick        global _list_capping_size
266061da546Spatrick        logger = lldb.formatters.Logger.Logger()
267061da546Spatrick        if self.count is None:
268061da546Spatrick            self.count = self.num_children_impl()
269061da546Spatrick            if self.count > _list_capping_size:
270061da546Spatrick                self.count = _list_capping_size
271061da546Spatrick        return self.count
272061da546Spatrick
273061da546Spatrick    def num_children_impl(self):
274061da546Spatrick        global _list_capping_size
275061da546Spatrick        logger = lldb.formatters.Logger.Logger()
276061da546Spatrick        try:
277061da546Spatrick            next_val = self.head.GetValueAsUnsigned(0)
278061da546Spatrick            prev_val = self.tail.GetValueAsUnsigned(0)
279061da546Spatrick            # After a std::list has been initialized, both next and prev will
280061da546Spatrick            # be non-NULL
281061da546Spatrick            if next_val == 0 or prev_val == 0:
282061da546Spatrick                return 0
283061da546Spatrick            if next_val == self.node_address:
284061da546Spatrick                return 0
285061da546Spatrick            if next_val == prev_val:
286061da546Spatrick                return 1
287061da546Spatrick            if self.has_loop():
288061da546Spatrick                return 0
289061da546Spatrick            size = 2
290061da546Spatrick            current = stdlist_entry(self.head)
291061da546Spatrick            while current.next.value != self.node_address:
292061da546Spatrick                size = size + 1
293061da546Spatrick                current = current.next
294061da546Spatrick                if size > _list_capping_size:
295061da546Spatrick                    return _list_capping_size
296061da546Spatrick            return (size - 1)
297061da546Spatrick        except:
298061da546Spatrick            return 0
299061da546Spatrick
300061da546Spatrick    def get_child_index(self, name):
301061da546Spatrick        logger = lldb.formatters.Logger.Logger()
302061da546Spatrick        try:
303061da546Spatrick            return int(name.lstrip('[').rstrip(']'))
304061da546Spatrick        except:
305061da546Spatrick            return -1
306061da546Spatrick
307061da546Spatrick    def get_child_at_index(self, index):
308061da546Spatrick        logger = lldb.formatters.Logger.Logger()
309061da546Spatrick        logger >> "Fetching child " + str(index)
310061da546Spatrick        if index < 0:
311061da546Spatrick            return None
312061da546Spatrick        if index >= self.num_children():
313061da546Spatrick            return None
314061da546Spatrick        try:
315061da546Spatrick            current = stdlist_iterator(self.head)
316061da546Spatrick            current = current.advance(index)
317061da546Spatrick            # we do not return __value_ because then all our children would be named __value_
318061da546Spatrick            # we need to make a copy of __value__ with the right name -
319061da546Spatrick            # unfortunate
320061da546Spatrick            obj = current.GetChildMemberWithName('__value_')
321061da546Spatrick            obj_data = obj.GetData()
322061da546Spatrick            return self.valobj.CreateValueFromData(
323061da546Spatrick                '[' + str(index) + ']', obj_data, self.data_type)
324061da546Spatrick        except:
325061da546Spatrick            return None
326061da546Spatrick
327061da546Spatrick    def extract_type(self):
328061da546Spatrick        logger = lldb.formatters.Logger.Logger()
329061da546Spatrick        list_type = self.valobj.GetType().GetUnqualifiedType()
330061da546Spatrick        if list_type.IsReferenceType():
331061da546Spatrick            list_type = list_type.GetDereferencedType()
332061da546Spatrick        if list_type.GetNumberOfTemplateArguments() > 0:
333061da546Spatrick            data_type = list_type.GetTemplateArgumentType(0)
334061da546Spatrick        else:
335061da546Spatrick            data_type = None
336061da546Spatrick        return data_type
337061da546Spatrick
338061da546Spatrick    def update(self):
339061da546Spatrick        logger = lldb.formatters.Logger.Logger()
340061da546Spatrick        self.count = None
341061da546Spatrick        try:
342061da546Spatrick            impl = self.valobj.GetChildMemberWithName('__end_')
343061da546Spatrick            self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0)
344061da546Spatrick            self.head = impl.GetChildMemberWithName('__next_')
345061da546Spatrick            self.tail = impl.GetChildMemberWithName('__prev_')
346061da546Spatrick            self.data_type = self.extract_type()
347061da546Spatrick            self.data_size = self.data_type.GetByteSize()
348061da546Spatrick        except:
349061da546Spatrick            pass
350061da546Spatrick
351061da546Spatrick    def has_children(self):
352061da546Spatrick        return True
353061da546Spatrick
354061da546Spatrick
355061da546Spatrick# Just an example: the actual summary is produced by a summary string:
356061da546Spatrick# size=${svar%#}
357061da546Spatrickdef stdlist_SummaryProvider(valobj, dict):
358061da546Spatrick    prov = stdlist_SynthProvider(valobj, None)
359061da546Spatrick    return 'size=' + str(prov.num_children())
360061da546Spatrick
361061da546Spatrick# a tree node - this class makes the syntax in the actual iterator nicer
362061da546Spatrick# to read and maintain
363061da546Spatrick
364061da546Spatrick
365061da546Spatrickclass stdmap_iterator_node:
366061da546Spatrick
367061da546Spatrick    def _left_impl(self):
368061da546Spatrick        logger = lldb.formatters.Logger.Logger()
369061da546Spatrick        return stdmap_iterator_node(
370061da546Spatrick            self.node.GetChildMemberWithName("__left_"))
371061da546Spatrick
372061da546Spatrick    def _right_impl(self):
373061da546Spatrick        logger = lldb.formatters.Logger.Logger()
374061da546Spatrick        return stdmap_iterator_node(
375061da546Spatrick            self.node.GetChildMemberWithName("__right_"))
376061da546Spatrick
377061da546Spatrick    def _parent_impl(self):
378061da546Spatrick        logger = lldb.formatters.Logger.Logger()
379061da546Spatrick        return stdmap_iterator_node(
380061da546Spatrick            self.node.GetChildMemberWithName("__parent_"))
381061da546Spatrick
382061da546Spatrick    def _value_impl(self):
383061da546Spatrick        logger = lldb.formatters.Logger.Logger()
384061da546Spatrick        return self.node.GetValueAsUnsigned(0)
385061da546Spatrick
386061da546Spatrick    def _sbvalue_impl(self):
387061da546Spatrick        logger = lldb.formatters.Logger.Logger()
388061da546Spatrick        return self.node
389061da546Spatrick
390061da546Spatrick    def _null_impl(self):
391061da546Spatrick        logger = lldb.formatters.Logger.Logger()
392061da546Spatrick        return self.value == 0
393061da546Spatrick
394061da546Spatrick    def __init__(self, node):
395061da546Spatrick        logger = lldb.formatters.Logger.Logger()
396061da546Spatrick        self.node = node
397061da546Spatrick
398061da546Spatrick    left = property(_left_impl, None)
399061da546Spatrick    right = property(_right_impl, None)
400061da546Spatrick    parent = property(_parent_impl, None)
401061da546Spatrick    value = property(_value_impl, None)
402061da546Spatrick    is_null = property(_null_impl, None)
403061da546Spatrick    sbvalue = property(_sbvalue_impl, None)
404061da546Spatrick
405061da546Spatrick# a Python implementation of the tree iterator used by libc++
406061da546Spatrick
407061da546Spatrick
408061da546Spatrickclass stdmap_iterator:
409061da546Spatrick
410061da546Spatrick    def tree_min(self, x):
411061da546Spatrick        logger = lldb.formatters.Logger.Logger()
412061da546Spatrick        steps = 0
413061da546Spatrick        if x.is_null:
414061da546Spatrick            return None
415061da546Spatrick        while (not x.left.is_null):
416061da546Spatrick            x = x.left
417061da546Spatrick            steps += 1
418061da546Spatrick            if steps > self.max_count:
419061da546Spatrick                logger >> "Returning None - we overflowed"
420061da546Spatrick                return None
421061da546Spatrick        return x
422061da546Spatrick
423061da546Spatrick    def tree_max(self, x):
424061da546Spatrick        logger = lldb.formatters.Logger.Logger()
425061da546Spatrick        if x.is_null:
426061da546Spatrick            return None
427061da546Spatrick        while (not x.right.is_null):
428061da546Spatrick            x = x.right
429061da546Spatrick        return x
430061da546Spatrick
431061da546Spatrick    def tree_is_left_child(self, x):
432061da546Spatrick        logger = lldb.formatters.Logger.Logger()
433061da546Spatrick        if x.is_null:
434061da546Spatrick            return None
435061da546Spatrick        return True if x.value == x.parent.left.value else False
436061da546Spatrick
437061da546Spatrick    def increment_node(self, node):
438061da546Spatrick        logger = lldb.formatters.Logger.Logger()
439061da546Spatrick        if node.is_null:
440061da546Spatrick            return None
441061da546Spatrick        if not node.right.is_null:
442061da546Spatrick            return self.tree_min(node.right)
443061da546Spatrick        steps = 0
444061da546Spatrick        while (not self.tree_is_left_child(node)):
445061da546Spatrick            steps += 1
446061da546Spatrick            if steps > self.max_count:
447061da546Spatrick                logger >> "Returning None - we overflowed"
448061da546Spatrick                return None
449061da546Spatrick            node = node.parent
450061da546Spatrick        return node.parent
451061da546Spatrick
452061da546Spatrick    def __init__(self, node, max_count=0):
453061da546Spatrick        logger = lldb.formatters.Logger.Logger()
454061da546Spatrick        # we convert the SBValue to an internal node object on entry
455061da546Spatrick        self.node = stdmap_iterator_node(node)
456061da546Spatrick        self.max_count = max_count
457061da546Spatrick
458061da546Spatrick    def value(self):
459061da546Spatrick        logger = lldb.formatters.Logger.Logger()
460061da546Spatrick        return self.node.sbvalue  # and return the SBValue back on exit
461061da546Spatrick
462061da546Spatrick    def next(self):
463061da546Spatrick        logger = lldb.formatters.Logger.Logger()
464061da546Spatrick        node = self.increment_node(self.node)
465061da546Spatrick        if node is not None and node.sbvalue.IsValid() and not(node.is_null):
466061da546Spatrick            self.node = node
467061da546Spatrick            return self.value()
468061da546Spatrick        else:
469061da546Spatrick            return None
470061da546Spatrick
471061da546Spatrick    def advance(self, N):
472061da546Spatrick        logger = lldb.formatters.Logger.Logger()
473061da546Spatrick        if N < 0:
474061da546Spatrick            return None
475061da546Spatrick        if N == 0:
476061da546Spatrick            return self.value()
477061da546Spatrick        if N == 1:
478061da546Spatrick            return self.next()
479061da546Spatrick        while N > 0:
480061da546Spatrick            if self.next() is None:
481061da546Spatrick                return None
482061da546Spatrick            N = N - 1
483061da546Spatrick        return self.value()
484061da546Spatrick
485061da546Spatrick
486061da546Spatrickclass stdmap_SynthProvider:
487061da546Spatrick
488061da546Spatrick    def __init__(self, valobj, dict):
489061da546Spatrick        logger = lldb.formatters.Logger.Logger()
490061da546Spatrick        self.valobj = valobj
491061da546Spatrick        self.pointer_size = self.valobj.GetProcess().GetAddressByteSize()
492061da546Spatrick        self.count = None
493061da546Spatrick
494061da546Spatrick    def update(self):
495061da546Spatrick        logger = lldb.formatters.Logger.Logger()
496061da546Spatrick        self.count = None
497061da546Spatrick        try:
498061da546Spatrick            # we will set this to True if we find out that discovering a node in the map takes more steps than the overall size of the RB tree
499061da546Spatrick            # if this gets set to True, then we will merrily return None for
500061da546Spatrick            # any child from that moment on
501061da546Spatrick            self.garbage = False
502061da546Spatrick            self.tree = self.valobj.GetChildMemberWithName('__tree_')
503061da546Spatrick            self.root_node = self.tree.GetChildMemberWithName('__begin_node_')
504061da546Spatrick            # this data is either lazily-calculated, or cannot be inferred at this moment
505061da546Spatrick            # we still need to mark it as None, meaning "please set me ASAP"
506061da546Spatrick            self.data_type = None
507061da546Spatrick            self.data_size = None
508061da546Spatrick            self.skip_size = None
509061da546Spatrick        except:
510061da546Spatrick            pass
511061da546Spatrick
512061da546Spatrick    def num_children(self):
513061da546Spatrick        global _map_capping_size
514061da546Spatrick        logger = lldb.formatters.Logger.Logger()
515061da546Spatrick        if self.count is None:
516061da546Spatrick            self.count = self.num_children_impl()
517061da546Spatrick            if self.count > _map_capping_size:
518061da546Spatrick                self.count = _map_capping_size
519061da546Spatrick        return self.count
520061da546Spatrick
521061da546Spatrick    def num_children_impl(self):
522061da546Spatrick        logger = lldb.formatters.Logger.Logger()
523061da546Spatrick        try:
524061da546Spatrick            return self.valobj.GetChildMemberWithName('__tree_').GetChildMemberWithName(
525061da546Spatrick                '__pair3_').GetChildMemberWithName('__first_').GetValueAsUnsigned()
526061da546Spatrick        except:
527061da546Spatrick            return 0
528061da546Spatrick
529061da546Spatrick    def has_children(self):
530061da546Spatrick        return True
531061da546Spatrick
532061da546Spatrick    def get_data_type(self):
533061da546Spatrick        logger = lldb.formatters.Logger.Logger()
534061da546Spatrick        if self.data_type is None or self.data_size is None:
535061da546Spatrick            if self.num_children() == 0:
536061da546Spatrick                return False
537061da546Spatrick            deref = self.root_node.Dereference()
538061da546Spatrick            if not(deref.IsValid()):
539061da546Spatrick                return False
540061da546Spatrick            value = deref.GetChildMemberWithName('__value_')
541061da546Spatrick            if not(value.IsValid()):
542061da546Spatrick                return False
543061da546Spatrick            self.data_type = value.GetType()
544061da546Spatrick            self.data_size = self.data_type.GetByteSize()
545061da546Spatrick            self.skip_size = None
546061da546Spatrick            return True
547061da546Spatrick        else:
548061da546Spatrick            return True
549061da546Spatrick
550061da546Spatrick    def get_value_offset(self, node):
551061da546Spatrick        logger = lldb.formatters.Logger.Logger()
552061da546Spatrick        if self.skip_size is None:
553061da546Spatrick            node_type = node.GetType()
554061da546Spatrick            fields_count = node_type.GetNumberOfFields()
555061da546Spatrick            for i in range(fields_count):
556061da546Spatrick                field = node_type.GetFieldAtIndex(i)
557061da546Spatrick                if field.GetName() == '__value_':
558061da546Spatrick                    self.skip_size = field.GetOffsetInBytes()
559061da546Spatrick                    break
560061da546Spatrick        return (self.skip_size is not None)
561061da546Spatrick
562061da546Spatrick    def get_child_index(self, name):
563061da546Spatrick        logger = lldb.formatters.Logger.Logger()
564061da546Spatrick        try:
565061da546Spatrick            return int(name.lstrip('[').rstrip(']'))
566061da546Spatrick        except:
567061da546Spatrick            return -1
568061da546Spatrick
569061da546Spatrick    def get_child_at_index(self, index):
570061da546Spatrick        logger = lldb.formatters.Logger.Logger()
571061da546Spatrick        logger >> "Retrieving child " + str(index)
572061da546Spatrick        if index < 0:
573061da546Spatrick            return None
574061da546Spatrick        if index >= self.num_children():
575061da546Spatrick            return None
576061da546Spatrick        if self.garbage:
577061da546Spatrick            logger >> "Returning None since this tree is garbage"
578061da546Spatrick            return None
579061da546Spatrick        try:
580061da546Spatrick            iterator = stdmap_iterator(
581061da546Spatrick                self.root_node, max_count=self.num_children())
582061da546Spatrick            # the debug info for libc++ std::map is such that __begin_node_ has a very nice and useful type
583061da546Spatrick            # out of which we can grab the information we need - every other node has a less informative
584061da546Spatrick            # type which omits all value information and only contains housekeeping information for the RB tree
585061da546Spatrick            # hence, we need to know if we are at a node != 0, so that we can
586061da546Spatrick            # still get at the data
587061da546Spatrick            need_to_skip = (index > 0)
588061da546Spatrick            current = iterator.advance(index)
589061da546Spatrick            if current is None:
590061da546Spatrick                logger >> "Tree is garbage - returning None"
591061da546Spatrick                self.garbage = True
592061da546Spatrick                return None
593061da546Spatrick            if self.get_data_type():
594061da546Spatrick                if not(need_to_skip):
595061da546Spatrick                    current = current.Dereference()
596061da546Spatrick                    obj = current.GetChildMemberWithName('__value_')
597061da546Spatrick                    obj_data = obj.GetData()
598061da546Spatrick                    # make sure we have a valid offset for the next items
599061da546Spatrick                    self.get_value_offset(current)
600061da546Spatrick                    # we do not return __value_ because then we would end up with a child named
601061da546Spatrick                    # __value_ instead of [0]
602061da546Spatrick                    return self.valobj.CreateValueFromData(
603061da546Spatrick                        '[' + str(index) + ']', obj_data, self.data_type)
604061da546Spatrick                else:
605061da546Spatrick                    # FIXME we need to have accessed item 0 before accessing
606061da546Spatrick                    # any other item!
607061da546Spatrick                    if self.skip_size is None:
608061da546Spatrick                        logger >> "You asked for item > 0 before asking for item == 0, I will fetch 0 now then retry"
609061da546Spatrick                        if self.get_child_at_index(0):
610061da546Spatrick                            return self.get_child_at_index(index)
611061da546Spatrick                        else:
612061da546Spatrick                            logger >> "item == 0 could not be found. sorry, nothing can be done here."
613061da546Spatrick                            return None
614061da546Spatrick                    return current.CreateChildAtOffset(
615061da546Spatrick                        '[' + str(index) + ']', self.skip_size, self.data_type)
616061da546Spatrick            else:
617061da546Spatrick                logger >> "Unable to infer data-type - returning None (should mark tree as garbage here?)"
618061da546Spatrick                return None
619061da546Spatrick        except Exception as err:
620061da546Spatrick            logger >> "Hit an exception: " + str(err)
621061da546Spatrick            return None
622061da546Spatrick
623061da546Spatrick# Just an example: the actual summary is produced by a summary string:
624061da546Spatrick# size=${svar%#}
625061da546Spatrick
626061da546Spatrick
627061da546Spatrickdef stdmap_SummaryProvider(valobj, dict):
628061da546Spatrick    prov = stdmap_SynthProvider(valobj, None)
629061da546Spatrick    return 'size=' + str(prov.num_children())
630061da546Spatrick
631061da546Spatrick
632061da546Spatrickclass stddeque_SynthProvider:
633061da546Spatrick
634061da546Spatrick    def __init__(self, valobj, d):
635061da546Spatrick        logger = lldb.formatters.Logger.Logger()
636061da546Spatrick        logger.write("init")
637061da546Spatrick        self.valobj = valobj
638061da546Spatrick        self.pointer_size = self.valobj.GetProcess().GetAddressByteSize()
639061da546Spatrick        self.count = None
640061da546Spatrick        try:
641061da546Spatrick            self.find_block_size()
642061da546Spatrick        except:
643061da546Spatrick            self.block_size = -1
644061da546Spatrick            self.element_size = -1
645061da546Spatrick        logger.write(
646061da546Spatrick            "block_size=%d, element_size=%d" %
647061da546Spatrick            (self.block_size, self.element_size))
648061da546Spatrick
649061da546Spatrick    def find_block_size(self):
650061da546Spatrick        # in order to use the deque we must have the block size, or else
651061da546Spatrick        # it's impossible to know what memory addresses are valid
652061da546Spatrick        self.element_type = self.valobj.GetType().GetTemplateArgumentType(0)
653061da546Spatrick        self.element_size = self.element_type.GetByteSize()
654061da546Spatrick        # The code says this, but there must be a better way:
655061da546Spatrick        # template <class _Tp, class _Allocator>
656061da546Spatrick        # class __deque_base {
657061da546Spatrick        #    static const difference_type __block_size = sizeof(value_type) < 256 ? 4096 / sizeof(value_type) : 16;
658061da546Spatrick        # }
659061da546Spatrick        if self.element_size < 256:
660*f6aab3d8Srobert            self.block_size = 4096 // self.element_size
661061da546Spatrick        else:
662061da546Spatrick            self.block_size = 16
663061da546Spatrick
664061da546Spatrick    def num_children(self):
665061da546Spatrick        logger = lldb.formatters.Logger.Logger()
666061da546Spatrick        if self.count is None:
667061da546Spatrick            return 0
668*f6aab3d8Srobert        return self.count
669061da546Spatrick
670061da546Spatrick    def has_children(self):
671061da546Spatrick        return True
672061da546Spatrick
673061da546Spatrick    def get_child_index(self, name):
674061da546Spatrick        logger = lldb.formatters.Logger.Logger()
675061da546Spatrick        try:
676061da546Spatrick            return int(name.lstrip('[').rstrip(']'))
677061da546Spatrick        except:
678061da546Spatrick            return -1
679061da546Spatrick
680061da546Spatrick    def get_child_at_index(self, index):
681061da546Spatrick        logger = lldb.formatters.Logger.Logger()
682061da546Spatrick        logger.write("Fetching child " + str(index))
683061da546Spatrick        if index < 0 or self.count is None:
684061da546Spatrick            return None
685061da546Spatrick        if index >= self.num_children():
686061da546Spatrick            return None
687061da546Spatrick        try:
688061da546Spatrick            i, j = divmod(self.start + index, self.block_size)
689*f6aab3d8Srobert
690061da546Spatrick            return self.first.CreateValueFromExpression(
691061da546Spatrick                '[' + str(index) + ']', '*(*(%s + %d) + %d)' %
692*f6aab3d8Srobert                (self.map_begin.get_expr_path(), i, j))
693061da546Spatrick        except:
694061da546Spatrick            return None
695061da546Spatrick
696061da546Spatrick    def _get_value_of_compressed_pair(self, pair):
697061da546Spatrick        value = pair.GetChildMemberWithName("__value_")
698061da546Spatrick        if not value.IsValid():
699061da546Spatrick            # pre-r300140 member name
700061da546Spatrick            value = pair.GetChildMemberWithName("__first_")
701061da546Spatrick        return value.GetValueAsUnsigned(0)
702061da546Spatrick
703061da546Spatrick    def update(self):
704061da546Spatrick        logger = lldb.formatters.Logger.Logger()
705061da546Spatrick        try:
706061da546Spatrick            # A deque is effectively a two-dim array, with fixed width.
707061da546Spatrick            # 'map' contains pointers to the rows of this array. The
708061da546Spatrick            # full memory area allocated by the deque is delimited
709061da546Spatrick            # by 'first' and 'end_cap'. However, only a subset of this
710061da546Spatrick            # memory contains valid data since a deque may have some slack
711061da546Spatrick            # at the front and back in order to have O(1) insertion at
712061da546Spatrick            # both ends. The rows in active use are delimited by
713061da546Spatrick            # 'begin' and 'end'.
714061da546Spatrick            #
715061da546Spatrick            # To find the elements that are actually constructed, the 'start'
716061da546Spatrick            # variable tells which element in this NxM array is the 0th
717061da546Spatrick            # one, and the 'size' element gives the number of elements
718061da546Spatrick            # in the deque.
719061da546Spatrick            count = self._get_value_of_compressed_pair(
720061da546Spatrick                    self.valobj.GetChildMemberWithName('__size_'))
721061da546Spatrick            # give up now if we cant access memory reliably
722061da546Spatrick            if self.block_size < 0:
723061da546Spatrick                logger.write("block_size < 0")
724061da546Spatrick                return
725061da546Spatrick            map_ = self.valobj.GetChildMemberWithName('__map_')
726061da546Spatrick            start = self.valobj.GetChildMemberWithName(
727061da546Spatrick                '__start_').GetValueAsUnsigned(0)
728061da546Spatrick            first = map_.GetChildMemberWithName('__first_')
729061da546Spatrick            map_first = first.GetValueAsUnsigned(0)
730*f6aab3d8Srobert            self.map_begin = map_.GetChildMemberWithName(
731*f6aab3d8Srobert                '__begin_')
732*f6aab3d8Srobert            map_begin = self.map_begin.GetValueAsUnsigned(0)
733061da546Spatrick            map_end = map_.GetChildMemberWithName(
734061da546Spatrick                '__end_').GetValueAsUnsigned(0)
735061da546Spatrick            map_endcap = self._get_value_of_compressed_pair(
736061da546Spatrick                    map_.GetChildMemberWithName( '__end_cap_'))
737*f6aab3d8Srobert
738061da546Spatrick            # check consistency
739061da546Spatrick            if not map_first <= map_begin <= map_end <= map_endcap:
740061da546Spatrick                logger.write("map pointers are not monotonic")
741061da546Spatrick                return
742061da546Spatrick            total_rows, junk = divmod(
743061da546Spatrick                map_endcap - map_first, self.pointer_size)
744061da546Spatrick            if junk:
745061da546Spatrick                logger.write("endcap-first doesnt align correctly")
746061da546Spatrick                return
747061da546Spatrick            active_rows, junk = divmod(map_end - map_begin, self.pointer_size)
748061da546Spatrick            if junk:
749061da546Spatrick                logger.write("end-begin doesnt align correctly")
750061da546Spatrick                return
751061da546Spatrick            start_row, junk = divmod(map_begin - map_first, self.pointer_size)
752061da546Spatrick            if junk:
753061da546Spatrick                logger.write("begin-first doesnt align correctly")
754061da546Spatrick                return
755*f6aab3d8Srobert
756061da546Spatrick            logger.write(
757061da546Spatrick                "update success: count=%r, start=%r, first=%r" %
758061da546Spatrick                (count, start, first))
759061da546Spatrick            # if consistent, save all we really need:
760061da546Spatrick            self.count = count
761061da546Spatrick            self.start = start
762061da546Spatrick            self.first = first
763061da546Spatrick        except:
764061da546Spatrick            self.count = None
765061da546Spatrick            self.start = None
766061da546Spatrick            self.map_first = None
767061da546Spatrick            self.map_begin = None
768*f6aab3d8Srobert        return False
769061da546Spatrick
770061da546Spatrick
771061da546Spatrickclass stdsharedptr_SynthProvider:
772061da546Spatrick
773061da546Spatrick    def __init__(self, valobj, d):
774061da546Spatrick        logger = lldb.formatters.Logger.Logger()
775061da546Spatrick        logger.write("init")
776061da546Spatrick        self.valobj = valobj
777061da546Spatrick        #self.element_ptr_type = self.valobj.GetType().GetTemplateArgumentType(0).GetPointerType()
778061da546Spatrick        self.ptr = None
779061da546Spatrick        self.cntrl = None
780061da546Spatrick        process = valobj.GetProcess()
781061da546Spatrick        self.endianness = process.GetByteOrder()
782061da546Spatrick        self.pointer_size = process.GetAddressByteSize()
783061da546Spatrick        self.count_type = valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
784061da546Spatrick
785061da546Spatrick    def num_children(self):
786061da546Spatrick        return 1
787061da546Spatrick
788061da546Spatrick    def has_children(self):
789061da546Spatrick        return True
790061da546Spatrick
791061da546Spatrick    def get_child_index(self, name):
792061da546Spatrick        if name == "__ptr_":
793061da546Spatrick            return 0
794061da546Spatrick        if name == "count":
795061da546Spatrick            return 1
796061da546Spatrick        if name == "weak_count":
797061da546Spatrick            return 2
798061da546Spatrick        return -1
799061da546Spatrick
800061da546Spatrick    def get_child_at_index(self, index):
801061da546Spatrick        if index == 0:
802061da546Spatrick            return self.ptr
803061da546Spatrick        if index == 1:
804061da546Spatrick            if self.cntrl is None:
805061da546Spatrick                count = 0
806061da546Spatrick            else:
807061da546Spatrick                count = 1 + \
808061da546Spatrick                    self.cntrl.GetChildMemberWithName('__shared_owners_').GetValueAsSigned()
809061da546Spatrick            return self.valobj.CreateValueFromData(
810061da546Spatrick                "count", lldb.SBData.CreateDataFromUInt64Array(
811061da546Spatrick                    self.endianness, self.pointer_size, [count]), self.count_type)
812061da546Spatrick        if index == 2:
813061da546Spatrick            if self.cntrl is None:
814061da546Spatrick                count = 0
815061da546Spatrick            else:
816061da546Spatrick                count = 1 + \
817061da546Spatrick                    self.cntrl.GetChildMemberWithName('__shared_weak_owners_').GetValueAsSigned()
818061da546Spatrick            return self.valobj.CreateValueFromData(
819061da546Spatrick                "weak_count", lldb.SBData.CreateDataFromUInt64Array(
820061da546Spatrick                    self.endianness, self.pointer_size, [count]), self.count_type)
821061da546Spatrick        return None
822061da546Spatrick
823061da546Spatrick    def update(self):
824061da546Spatrick        logger = lldb.formatters.Logger.Logger()
825061da546Spatrick        self.ptr = self.valobj.GetChildMemberWithName(
826061da546Spatrick            '__ptr_')  # .Cast(self.element_ptr_type)
827061da546Spatrick        cntrl = self.valobj.GetChildMemberWithName('__cntrl_')
828061da546Spatrick        if cntrl.GetValueAsUnsigned(0):
829061da546Spatrick            self.cntrl = cntrl.Dereference()
830061da546Spatrick        else:
831061da546Spatrick            self.cntrl = None
832061da546Spatrick
833061da546Spatrick# we can use two different categories for old and new formatters - type names are different enough that we should make no confusion
834061da546Spatrick# talking with libc++ developer: "std::__1::class_name is set in stone
835061da546Spatrick# until we decide to change the ABI. That shouldn't happen within a 5 year
836061da546Spatrick# time frame"
837061da546Spatrick
838061da546Spatrick
839061da546Spatrickdef __lldb_init_module(debugger, dict):
840061da546Spatrick    debugger.HandleCommand(
841061da546Spatrick        'type summary add -F libcxx.stdstring_SummaryProvider "std::__1::string" -w libcxx')
842061da546Spatrick    debugger.HandleCommand(
843061da546Spatrick        'type summary add -F libcxx.stdstring_SummaryProvider "std::__1::basic_string<char, class std::__1::char_traits<char>, class std::__1::allocator<char> >" -w libcxx')
844061da546Spatrick    debugger.HandleCommand(
845061da546Spatrick        'type synthetic add -l libcxx.stdvector_SynthProvider -x "^(std::__1::)vector<.+>$" -w libcxx')
846061da546Spatrick    debugger.HandleCommand(
847061da546Spatrick        'type summary add -F libcxx.stdvector_SummaryProvider -e -x "^(std::__1::)vector<.+>$" -w libcxx')
848061da546Spatrick    debugger.HandleCommand(
849061da546Spatrick        'type synthetic add -l libcxx.stdlist_SynthProvider -x "^(std::__1::)list<.+>$" -w libcxx')
850061da546Spatrick    debugger.HandleCommand(
851061da546Spatrick        'type summary add -F libcxx.stdlist_SummaryProvider -e -x "^(std::__1::)list<.+>$" -w libcxx')
852061da546Spatrick    debugger.HandleCommand(
853061da546Spatrick        'type synthetic add -l libcxx.stdmap_SynthProvider -x "^(std::__1::)map<.+> >$" -w libcxx')
854061da546Spatrick    debugger.HandleCommand(
855061da546Spatrick        'type summary add -F libcxx.stdmap_SummaryProvider -e -x "^(std::__1::)map<.+> >$" -w libcxx')
856061da546Spatrick    debugger.HandleCommand("type category enable libcxx")
857061da546Spatrick    debugger.HandleCommand(
858061da546Spatrick        'type synthetic add -l libcxx.stddeque_SynthProvider -x "^(std::__1::)deque<.+>$" -w libcxx')
859061da546Spatrick    debugger.HandleCommand(
860061da546Spatrick        'type synthetic add -l libcxx.stdsharedptr_SynthProvider -x "^(std::__1::)shared_ptr<.+>$" -w libcxx')
861061da546Spatrick    # turns out the structs look the same, so weak_ptr can be handled the same!
862061da546Spatrick    debugger.HandleCommand(
863061da546Spatrick        'type synthetic add -l libcxx.stdsharedptr_SynthProvider -x "^(std::__1::)weak_ptr<.+>$" -w libcxx')
864061da546Spatrick
865061da546Spatrick_map_capping_size = 255
866061da546Spatrick_list_capping_size = 255
867061da546Spatrick_list_uses_loop_detector = True
868