2 LLDB Formatters for LLVM data types.
4 Load into LLDB with 'command script import /path/to/lldbDataFormatters.py'
6 from __future__
import annotations
13 def __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<.+>$"'
20 debugger
.HandleCommand(
21 "type summary add -w llvm "
22 '-e -s "size=${svar%#}" '
23 '-x "^llvm::SmallVectorImpl<.+>$"'
25 debugger
.HandleCommand(
26 "type synthetic add -w llvm "
27 f
"-l {__name__}.SmallVectorSynthProvider "
28 '-x "^llvm::SmallVector<.+,.+>$"'
30 debugger
.HandleCommand(
31 "type summary add -w llvm "
32 '-e -s "size=${svar%#}" '
33 '-x "^llvm::SmallVector<.+,.+>$"'
35 debugger
.HandleCommand(
36 "type synthetic add -w llvm "
37 f
"-l {__name__}.ArrayRefSynthProvider "
38 '-x "^llvm::ArrayRef<.+>$"'
40 debugger
.HandleCommand(
41 "type summary add -w llvm "
42 '-e -s "size=${svar%#}" '
43 '-x "^llvm::ArrayRef<.+>$"'
45 debugger
.HandleCommand(
46 "type synthetic add -w llvm "
47 f
"-l {__name__}.OptionalSynthProvider "
48 '-x "^llvm::Optional<.+>$"'
50 debugger
.HandleCommand(
51 "type summary add -w llvm "
52 f
"-e -F {__name__}.OptionalSummaryProvider "
53 '-x "^llvm::Optional<.+>$"'
55 debugger
.HandleCommand(
56 "type summary add -w llvm "
57 f
"-F {__name__}.SmallStringSummaryProvider "
58 '-x "^llvm::SmallString<.+>$"'
60 debugger
.HandleCommand(
61 "type summary add -w llvm "
62 f
"-F {__name__}.StringRefSummaryProvider "
65 debugger
.HandleCommand(
66 "type summary add -w llvm "
67 f
"-F {__name__}.ConstStringSummaryProvider "
68 "lldb_private::ConstString"
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<.+>$"'
79 # debugger.HandleCommand(
80 # "type synthetic add -w llvm "
81 # f"-l {__name__}.PointerUnionSynthProvider "
82 # '-x "^llvm::PointerUnion<.+>$"'
85 debugger
.HandleCommand(
86 "type summary add -w llvm "
87 f
"-e -F {__name__}.DenseMapSummary "
88 '-x "^llvm::DenseMap<.+>$"'
90 debugger
.HandleCommand(
91 "type synthetic add -w llvm "
92 f
"-l {__name__}.DenseMapSynthetic "
93 '-x "^llvm::DenseMap<.+>$"'
97 # Pretty printer for llvm::SmallVector/llvm::SmallVectorImpl
98 class SmallVectorSynthProvider
:
99 def __init__(self
, valobj
, internal_dict
):
101 self
.update() # initialize this provider
103 def num_children(self
):
104 return self
.size
.GetValueAsUnsigned(0)
106 def get_child_index(self
, name
):
108 return int(name
.lstrip("[").rstrip("]"))
112 def get_child_at_index(self
, index
):
113 # Do bounds checking.
116 if index
>= self
.num_children():
119 offset
= index
* self
.type_size
120 return self
.begin
.CreateChildAtOffset(
121 "[" + str(index
) + "]", offset
, self
.data_type
125 self
.begin
= self
.valobj
.GetChildMemberWithName("BeginX")
126 self
.size
= self
.valobj
.GetChildMemberWithName("Size")
127 the_type
= self
.valobj
.GetType()
128 # If this is a reference type we have to dereference it to get to the
129 # template parameter.
130 if the_type
.IsReferenceType():
131 the_type
= the_type
.GetDereferencedType()
133 if the_type
.IsPointerType():
134 the_type
= the_type
.GetPointeeType()
136 self
.data_type
= the_type
.GetTemplateArgumentType(0)
137 self
.type_size
= self
.data_type
.GetByteSize()
138 assert self
.type_size
!= 0
141 class ArrayRefSynthProvider
:
142 """Provider for llvm::ArrayRef"""
144 def __init__(self
, valobj
, internal_dict
):
146 self
.update() # initialize this provider
148 def num_children(self
):
151 def get_child_index(self
, name
):
153 return int(name
.lstrip("[").rstrip("]"))
157 def get_child_at_index(self
, index
):
158 if index
< 0 or index
>= self
.num_children():
160 offset
= index
* self
.type_size
161 return self
.data
.CreateChildAtOffset(
162 "[" + str(index
) + "]", offset
, self
.data_type
166 self
.data
= self
.valobj
.GetChildMemberWithName("Data")
167 length_obj
= self
.valobj
.GetChildMemberWithName("Length")
168 self
.length
= length_obj
.GetValueAsUnsigned(0)
169 self
.data_type
= self
.data
.GetType().GetPointeeType()
170 self
.type_size
= self
.data_type
.GetByteSize()
171 assert self
.type_size
!= 0
174 def GetOptionalValue(valobj
):
175 storage
= valobj
.GetChildMemberWithName("Storage")
180 hasVal
= storage
.GetChildMemberWithName("hasVal").GetValueAsUnsigned(failure
)
181 if hasVal
== failure
:
182 return "<could not read llvm::Optional>"
187 underlying_type
= storage
.GetType().GetTemplateArgumentType(0)
188 storage
= storage
.GetChildMemberWithName("value")
189 return storage
.Cast(underlying_type
)
192 def OptionalSummaryProvider(valobj
, internal_dict
):
193 val
= GetOptionalValue(valobj
)
201 class OptionalSynthProvider
:
202 """Provides deref support to llvm::Optional<T>"""
204 def __init__(self
, valobj
, internal_dict
):
207 def num_children(self
):
208 return self
.valobj
.num_children
210 def get_child_index(self
, name
):
211 if name
== "$$dereference$$":
212 return self
.valobj
.num_children
213 return self
.valobj
.GetIndexOfChildWithName(name
)
215 def get_child_at_index(self
, index
):
216 if index
< self
.valobj
.num_children
:
217 return self
.valobj
.GetChildAtIndex(index
)
218 return GetOptionalValue(self
.valobj
) or lldb
.SBValue()
221 def SmallStringSummaryProvider(valobj
, internal_dict
):
222 # The underlying SmallVector base class is the first child.
223 vector
= valobj
.GetChildAtIndex(0)
224 num_elements
= vector
.GetNumChildren()
226 for i
in range(num_elements
):
227 c
= vector
.GetChildAtIndex(i
)
229 res
+= chr(c
.GetValueAsUnsigned())
234 def StringRefSummaryProvider(valobj
, internal_dict
):
235 if valobj
.GetNumChildren() == 2:
236 # StringRef's are also used to point at binary blobs in memory,
237 # so filter out suspiciously long strings.
239 actual_length
= valobj
.GetChildAtIndex(1).GetValueAsUnsigned()
240 truncate
= actual_length
> max_length
241 length
= min(max_length
, actual_length
)
245 data
= valobj
.GetChildAtIndex(0).GetPointeeData(item_count
=length
)
246 error
= lldb
.SBError()
247 string
= data
.ReadRawData(error
, 0, data
.GetByteSize()).decode()
249 return "<error: %s>" % error
.description
251 # json.dumps conveniently escapes the string for us.
252 string
= json
.dumps(string
)
259 def ConstStringSummaryProvider(valobj
, internal_dict
):
260 if valobj
.GetNumChildren() == 1:
261 return valobj
.GetChildAtIndex(0).GetSummary()
265 def get_expression_path(val
):
266 stream
= lldb
.SBStream()
267 if not val
.GetExpressionPath(stream
):
269 return stream
.GetData()
272 class PointerIntPairSynthProvider
:
273 def __init__(self
, valobj
, internal_dict
):
277 def num_children(self
):
280 def get_child_index(self
, name
):
281 if name
== "Pointer":
287 def get_child_at_index(self
, index
):
288 expr_path
= get_expression_path(self
.valobj
)
290 return self
.valobj
.CreateValueFromExpression(
291 "Pointer", f
"({self.pointer_ty.name}){expr_path}.getPointer()"
294 return self
.valobj
.CreateValueFromExpression(
295 "Int", f
"({self.int_ty.name}){expr_path}.getInt()"
300 self
.pointer_ty
= self
.valobj
.GetType().GetTemplateArgumentType(0)
301 self
.int_ty
= self
.valobj
.GetType().GetTemplateArgumentType(2)
304 def parse_template_parameters(typename
):
306 LLDB doesn't support template parameter packs, so let's parse them manually.
309 start
= typename
.find("<")
310 end
= typename
.rfind(">")
311 if start
< 1 or end
< 2 or end
- start
< 2:
315 current_parameter_start
= start
+ 1
317 for i
in range(start
+ 1, end
+ 1):
323 elif c
== "," and nesting_level
== 0:
324 result
.append(typename
[current_parameter_start
:i
].strip())
325 current_parameter_start
= i
+ 1
327 result
.append(typename
[current_parameter_start
:i
].strip())
332 class PointerUnionSynthProvider
:
333 def __init__(self
, valobj
, internal_dict
):
337 def num_children(self
):
340 def get_child_index(self
, name
):
345 def get_child_at_index(self
, index
):
348 ptr_type_name
= self
.template_args
[self
.active_type_tag
]
349 return self
.valobj
.CreateValueFromExpression(
350 "Ptr", f
"({ptr_type_name}){self.val_expr_path}.getPointer()"
354 self
.pointer_int_pair
= self
.valobj
.GetChildMemberWithName("Val")
355 self
.val_expr_path
= get_expression_path(
356 self
.valobj
.GetChildMemberWithName("Val")
358 self
.active_type_tag
= self
.valobj
.CreateValueFromExpression(
359 "", f
"(int){self.val_expr_path}.getInt()"
361 self
.template_args
= parse_template_parameters(self
.valobj
.GetType().name
)
364 def DenseMapSummary(valobj
: lldb
.SBValue
, _
) -> str:
365 raw_value
= valobj
.GetNonSyntheticValue()
366 num_entries
= raw_value
.GetChildMemberWithName("NumEntries").unsigned
367 num_tombstones
= raw_value
.GetChildMemberWithName("NumTombstones").unsigned
369 summary
= f
"size={num_entries}"
370 if num_tombstones
== 1:
371 # The heuristic to identify valid entries does not handle the case of a
372 # single tombstone. The summary calls attention to this.
373 summary
= f
"tombstones=1, {summary}"
377 class DenseMapSynthetic
:
380 # The indexes into `Buckets` that contain valid map entries.
381 child_buckets
: list[int]
383 def __init__(self
, valobj
: lldb
.SBValue
, _
) -> None:
386 def num_children(self
) -> int:
387 return len(self
.child_buckets
)
389 def get_child_at_index(self
, child_index
: int) -> lldb
.SBValue
:
390 bucket_index
= self
.child_buckets
[child_index
]
391 entry
= self
.valobj
.GetValueForExpressionPath(f
".Buckets[{bucket_index}]")
393 # By default, DenseMap instances use DenseMapPair to hold key-value
394 # entries. When the entry is a DenseMapPair, unwrap it to expose the
395 # children as simple std::pair values.
397 # This entry type is customizable (a template parameter). For other
398 # types, expose the entry type as is.
399 if entry
.type.name
.startswith("llvm::detail::DenseMapPair<"):
400 entry
= entry
.GetChildAtIndex(0)
402 return entry
.Clone(f
"[{child_index}]")
405 self
.child_buckets
= []
407 num_entries
= self
.valobj
.GetChildMemberWithName("NumEntries").unsigned
411 buckets
= self
.valobj
.GetChildMemberWithName("Buckets")
412 num_buckets
= self
.valobj
.GetChildMemberWithName("NumBuckets").unsigned
414 # Bucket entries contain one of the following:
417 # 3. Tombstone key (a deleted entry)
419 # NumBuckets is always greater than NumEntries. The empty key, and
420 # potentially the tombstone key, will occur multiple times. A key that
421 # is repeated is either the empty key or the tombstone key.
423 # For each key, collect a list of buckets it appears in.
424 key_buckets
: dict[str, list[int]] = collections
.defaultdict(list)
425 for index
in range(num_buckets
):
426 key
= buckets
.GetValueForExpressionPath(f
"[{index}].first")
427 key_buckets
[str(key
.data
)].append(index
)
429 # Heuristic: This is not a multi-map, any repeated (non-unique) keys are
430 # either the the empty key or the tombstone key. Populate child_buckets
431 # with the indexes of entries containing unique keys.
432 for indexes
in key_buckets
.values():
433 if len(indexes
) == 1:
434 self
.child_buckets
.append(indexes
[0])