xref: /llvm-project/llvm/utils/lldbDataFormatters.py (revision 398f3b368af9bced530e65c02bb38136d7f69caf)
1"""
2LLDB Formatters for LLVM data types.
3
4Load into LLDB with 'command script import /path/to/lldbDataFormatters.py'
5"""
6from __future__ import annotations
7
8import collections
9import lldb
10import json
11
12
13def __lldb_init_module(debugger, internal_dict):
14    debugger.HandleCommand("type category define -e llvm -l c++")
15    debugger.HandleCommand(
16        "type synthetic add -w llvm "
17        f"-l {__name__}.SmallVectorSynthProvider "
18        '-x "^llvm::SmallVectorImpl<.+>$"'
19    )
20    debugger.HandleCommand(
21        "type summary add -w llvm "
22        '-e -s "size=${svar%#}" '
23        '-x "^llvm::SmallVectorImpl<.+>$"'
24    )
25    debugger.HandleCommand(
26        "type synthetic add -w llvm "
27        f"-l {__name__}.SmallVectorSynthProvider "
28        '-x "^llvm::SmallVector<.+,.+>$"'
29    )
30    debugger.HandleCommand(
31        "type summary add -w llvm "
32        '-e -s "size=${svar%#}" '
33        '-x "^llvm::SmallVector<.+,.+>$"'
34    )
35    debugger.HandleCommand(
36        "type synthetic add -w llvm "
37        f"-l {__name__}.ArrayRefSynthProvider "
38        '-x "^llvm::ArrayRef<.+>$"'
39    )
40    debugger.HandleCommand(
41        "type summary add -w llvm "
42        '-e -s "size=${svar%#}" '
43        '-x "^llvm::ArrayRef<.+>$"'
44    )
45    debugger.HandleCommand(
46        "type synthetic add -w llvm "
47        f"-l {__name__}.OptionalSynthProvider "
48        '-x "^llvm::Optional<.+>$"'
49    )
50    debugger.HandleCommand(
51        "type summary add -w llvm "
52        f"-e -F {__name__}.OptionalSummaryProvider "
53        '-x "^llvm::Optional<.+>$"'
54    )
55    debugger.HandleCommand(
56        "type summary add -w llvm "
57        f"-F {__name__}.SmallStringSummaryProvider "
58        '-x "^llvm::SmallString<.+>$"'
59    )
60    debugger.HandleCommand(
61        "type summary add -w llvm "
62        f"-F {__name__}.StringRefSummaryProvider "
63        "llvm::StringRef"
64    )
65    debugger.HandleCommand(
66        "type summary add -w llvm "
67        f"-F {__name__}.ConstStringSummaryProvider "
68        "lldb_private::ConstString"
69    )
70
71    # The synthetic providers for PointerIntPair and PointerUnion are disabled
72    # because of a few issues. One example is template arguments that are
73    # non-pointer types that instead specialize PointerLikeTypeTraits.
74    # debugger.HandleCommand(
75    #     "type synthetic add -w llvm "
76    #     f"-l {__name__}.PointerIntPairSynthProvider "
77    #     '-x "^llvm::PointerIntPair<.+>$"'
78    # )
79    # debugger.HandleCommand(
80    #     "type synthetic add -w llvm "
81    #     f"-l {__name__}.PointerUnionSynthProvider "
82    #     '-x "^llvm::PointerUnion<.+>$"'
83    # )
84
85    debugger.HandleCommand(
86        "type summary add -w llvm "
87        f"-e -F {__name__}.DenseMapSummary "
88        '-x "^llvm::DenseMap<.+>$"'
89    )
90    debugger.HandleCommand(
91        "type synthetic add -w llvm "
92        f"-l {__name__}.DenseMapSynthetic "
93        '-x "^llvm::DenseMap<.+>$"'
94    )
95
96    debugger.HandleCommand(
97        "type synthetic add -w llvm "
98        f"-l {__name__}.ExpectedSynthetic "
99        '-x "^llvm::Expected<.+>$"'
100    )
101
102
103# Pretty printer for llvm::SmallVector/llvm::SmallVectorImpl
104class SmallVectorSynthProvider:
105    def __init__(self, valobj, internal_dict):
106        self.valobj = valobj
107        self.update()  # initialize this provider
108
109    def num_children(self):
110        return self.size.GetValueAsUnsigned(0)
111
112    def get_child_index(self, name):
113        try:
114            return int(name.lstrip("[").rstrip("]"))
115        except:
116            return -1
117
118    def get_child_at_index(self, index):
119        # Do bounds checking.
120        if index < 0:
121            return None
122        if index >= self.num_children():
123            return None
124
125        offset = index * self.type_size
126        return self.begin.CreateChildAtOffset(
127            "[" + str(index) + "]", offset, self.data_type
128        )
129
130    def update(self):
131        self.begin = self.valobj.GetChildMemberWithName("BeginX")
132        self.size = self.valobj.GetChildMemberWithName("Size")
133        the_type = self.valobj.GetType()
134        # If this is a reference type we have to dereference it to get to the
135        # template parameter.
136        if the_type.IsReferenceType():
137            the_type = the_type.GetDereferencedType()
138
139        if the_type.IsPointerType():
140            the_type = the_type.GetPointeeType()
141
142        self.data_type = the_type.GetTemplateArgumentType(0)
143        self.type_size = self.data_type.GetByteSize()
144        assert self.type_size != 0
145
146
147class ArrayRefSynthProvider:
148    """Provider for llvm::ArrayRef"""
149
150    def __init__(self, valobj, internal_dict):
151        self.valobj = valobj
152        self.update()  # initialize this provider
153
154    def num_children(self):
155        return self.length
156
157    def get_child_index(self, name):
158        try:
159            return int(name.lstrip("[").rstrip("]"))
160        except:
161            return -1
162
163    def get_child_at_index(self, index):
164        if index < 0 or index >= self.num_children():
165            return None
166        offset = index * self.type_size
167        return self.data.CreateChildAtOffset(
168            "[" + str(index) + "]", offset, self.data_type
169        )
170
171    def update(self):
172        self.data = self.valobj.GetChildMemberWithName("Data")
173        length_obj = self.valobj.GetChildMemberWithName("Length")
174        self.length = length_obj.GetValueAsUnsigned(0)
175        self.data_type = self.data.GetType().GetPointeeType()
176        self.type_size = self.data_type.GetByteSize()
177        assert self.type_size != 0
178
179
180def GetOptionalValue(valobj):
181    storage = valobj.GetChildMemberWithName("Storage")
182    if not storage:
183        storage = valobj
184
185    failure = 2
186    hasVal = storage.GetChildMemberWithName("hasVal").GetValueAsUnsigned(failure)
187    if hasVal == failure:
188        return "<could not read llvm::Optional>"
189
190    if hasVal == 0:
191        return None
192
193    underlying_type = storage.GetType().GetTemplateArgumentType(0)
194    storage = storage.GetChildMemberWithName("value")
195    return storage.Cast(underlying_type)
196
197
198def OptionalSummaryProvider(valobj, internal_dict):
199    val = GetOptionalValue(valobj)
200    if val is None:
201        return "None"
202    if val.summary:
203        return val.summary
204    return ""
205
206
207class OptionalSynthProvider:
208    """Provides deref support to llvm::Optional<T>"""
209
210    def __init__(self, valobj, internal_dict):
211        self.valobj = valobj
212
213    def num_children(self):
214        return self.valobj.num_children
215
216    def get_child_index(self, name):
217        if name == "$$dereference$$":
218            return self.valobj.num_children
219        return self.valobj.GetIndexOfChildWithName(name)
220
221    def get_child_at_index(self, index):
222        if index < self.valobj.num_children:
223            return self.valobj.GetChildAtIndex(index)
224        return GetOptionalValue(self.valobj) or lldb.SBValue()
225
226
227def SmallStringSummaryProvider(valobj, internal_dict):
228    # The underlying SmallVector base class is the first child.
229    vector = valobj.GetChildAtIndex(0)
230    num_elements = vector.GetNumChildren()
231    res = '"'
232    for i in range(num_elements):
233        c = vector.GetChildAtIndex(i)
234        if c:
235            res += chr(c.GetValueAsUnsigned())
236    res += '"'
237    return res
238
239
240def StringRefSummaryProvider(valobj, internal_dict):
241    if valobj.GetNumChildren() == 2:
242        # StringRef's are also used to point at binary blobs in memory,
243        # so filter out suspiciously long strings.
244        max_length = 1024
245        actual_length = valobj.GetChildAtIndex(1).GetValueAsUnsigned()
246        truncate = actual_length > max_length
247        length = min(max_length, actual_length)
248        if length == 0:
249            return '""'
250
251        data = valobj.GetChildAtIndex(0).GetPointeeData(item_count=length)
252        error = lldb.SBError()
253        string = data.ReadRawData(error, 0, data.GetByteSize()).decode()
254        if error.Fail():
255            return "<error: %s>" % error.description
256
257        # json.dumps conveniently escapes the string for us.
258        string = json.dumps(string)
259        if truncate:
260            string += "..."
261        return string
262    return None
263
264
265def ConstStringSummaryProvider(valobj, internal_dict):
266    if valobj.GetNumChildren() == 1:
267        return valobj.GetChildAtIndex(0).GetSummary()
268    return ""
269
270
271def get_expression_path(val):
272    stream = lldb.SBStream()
273    if not val.GetExpressionPath(stream):
274        return None
275    return stream.GetData()
276
277
278class PointerIntPairSynthProvider:
279    def __init__(self, valobj, internal_dict):
280        self.valobj = valobj
281        self.update()
282
283    def num_children(self):
284        return 2
285
286    def get_child_index(self, name):
287        if name == "Pointer":
288            return 0
289        if name == "Int":
290            return 1
291        return None
292
293    def get_child_at_index(self, index):
294        expr_path = get_expression_path(self.valobj)
295        if index == 0:
296            return self.valobj.CreateValueFromExpression(
297                "Pointer", f"({self.pointer_ty.name}){expr_path}.getPointer()"
298            )
299        if index == 1:
300            return self.valobj.CreateValueFromExpression(
301                "Int", f"({self.int_ty.name}){expr_path}.getInt()"
302            )
303        return None
304
305    def update(self):
306        self.pointer_ty = self.valobj.GetType().GetTemplateArgumentType(0)
307        self.int_ty = self.valobj.GetType().GetTemplateArgumentType(2)
308
309
310def parse_template_parameters(typename):
311    """
312    LLDB doesn't support template parameter packs, so let's parse them manually.
313    """
314    result = []
315    start = typename.find("<")
316    end = typename.rfind(">")
317    if start < 1 or end < 2 or end - start < 2:
318        return result
319
320    nesting_level = 0
321    current_parameter_start = start + 1
322
323    for i in range(start + 1, end + 1):
324        c = typename[i]
325        if c == "<":
326            nesting_level += 1
327        elif c == ">":
328            nesting_level -= 1
329        elif c == "," and nesting_level == 0:
330            result.append(typename[current_parameter_start:i].strip())
331            current_parameter_start = i + 1
332
333    result.append(typename[current_parameter_start:i].strip())
334
335    return result
336
337
338class PointerUnionSynthProvider:
339    def __init__(self, valobj, internal_dict):
340        self.valobj = valobj
341        self.update()
342
343    def num_children(self):
344        return 1
345
346    def get_child_index(self, name):
347        if name == "Ptr":
348            return 0
349        return None
350
351    def get_child_at_index(self, index):
352        if index != 0:
353            return None
354        ptr_type_name = self.template_args[self.active_type_tag]
355        return self.valobj.CreateValueFromExpression(
356            "Ptr", f"({ptr_type_name}){self.val_expr_path}.getPointer()"
357        )
358
359    def update(self):
360        self.pointer_int_pair = self.valobj.GetChildMemberWithName("Val")
361        self.val_expr_path = get_expression_path(
362            self.valobj.GetChildMemberWithName("Val")
363        )
364        self.active_type_tag = self.valobj.CreateValueFromExpression(
365            "", f"(int){self.val_expr_path}.getInt()"
366        ).GetValueAsSigned()
367        self.template_args = parse_template_parameters(self.valobj.GetType().name)
368
369
370def DenseMapSummary(valobj: lldb.SBValue, _) -> str:
371    raw_value = valobj.GetNonSyntheticValue()
372    num_entries = raw_value.GetChildMemberWithName("NumEntries").unsigned
373    num_tombstones = raw_value.GetChildMemberWithName("NumTombstones").unsigned
374
375    summary = f"size={num_entries}"
376    if num_tombstones == 1:
377        # The heuristic to identify valid entries does not handle the case of a
378        # single tombstone. The summary calls attention to this.
379        summary = f"tombstones=1, {summary}"
380    return summary
381
382
383class DenseMapSynthetic:
384    valobj: lldb.SBValue
385
386    # The indexes into `Buckets` that contain valid map entries.
387    child_buckets: list[int]
388
389    def __init__(self, valobj: lldb.SBValue, _) -> None:
390        self.valobj = valobj
391
392    def num_children(self) -> int:
393        return len(self.child_buckets)
394
395    def get_child_at_index(self, child_index: int) -> lldb.SBValue:
396        bucket_index = self.child_buckets[child_index]
397        entry = self.valobj.GetValueForExpressionPath(f".Buckets[{bucket_index}]")
398
399        # By default, DenseMap instances use DenseMapPair to hold key-value
400        # entries. When the entry is a DenseMapPair, unwrap it to expose the
401        # children as simple std::pair values.
402        #
403        # This entry type is customizable (a template parameter). For other
404        # types, expose the entry type as is.
405        if entry.type.name.startswith("llvm::detail::DenseMapPair<"):
406            entry = entry.GetChildAtIndex(0)
407
408        return entry.Clone(f"[{child_index}]")
409
410    def update(self):
411        self.child_buckets = []
412
413        num_entries = self.valobj.GetChildMemberWithName("NumEntries").unsigned
414        if num_entries == 0:
415            return
416
417        buckets = self.valobj.GetChildMemberWithName("Buckets")
418        num_buckets = self.valobj.GetChildMemberWithName("NumBuckets").unsigned
419
420        # Bucket entries contain one of the following:
421        #   1. Valid key-value
422        #   2. Empty key
423        #   3. Tombstone key (a deleted entry)
424        #
425        # NumBuckets is always greater than NumEntries. The empty key, and
426        # potentially the tombstone key, will occur multiple times. A key that
427        # is repeated is either the empty key or the tombstone key.
428
429        # For each key, collect a list of buckets it appears in.
430        key_buckets: dict[str, list[int]] = collections.defaultdict(list)
431        for index in range(num_buckets):
432            key = buckets.GetValueForExpressionPath(f"[{index}].first")
433            key_buckets[str(key.data)].append(index)
434
435        # Heuristic: This is not a multi-map, any repeated (non-unique) keys are
436        # either the the empty key or the tombstone key. Populate child_buckets
437        # with the indexes of entries containing unique keys.
438        for indexes in key_buckets.values():
439            if len(indexes) == 1:
440                self.child_buckets.append(indexes[0])
441
442
443class ExpectedSynthetic:
444    # The llvm::Expected<T> value.
445    expected: lldb.SBValue
446    # The stored success value or error value.
447    stored_value: lldb.SBValue
448
449    def __init__(self, valobj: lldb.SBValue, _) -> None:
450        self.expected = valobj
451
452    def update(self) -> None:
453        has_error = self.expected.GetChildMemberWithName("HasError").unsigned
454        if not has_error:
455            name = "value"
456            member = "TStorage"
457        else:
458            name = "error"
459            member = "ErrorStorage"
460        # Anonymous union.
461        union = self.expected.child[0]
462        storage = union.GetChildMemberWithName(member)
463        stored_type = storage.type.template_args[0]
464        self.stored_value = storage.Cast(stored_type).Clone(name)
465
466    def num_children(self) -> int:
467        return 1
468
469    def get_child_index(self, name: str) -> int:
470        if name == self.stored_value.name:
471            return 0
472        # Allow dereferencing for values, not errors.
473        if name == "$$dereference$$" and self.stored_value.name == "value":
474            return 0
475        return -1
476
477    def get_child_at_index(self, idx: int) -> lldb.SBValue:
478        if idx == 0:
479            return self.stored_value
480        return lldb.SBValue()
481