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<.+>$"'
96 debugger
.HandleCommand(
97 "type synthetic add -w llvm "
98 f
"-l {__name__}.ExpectedSynthetic "
99 '-x "^llvm::Expected<.+>$"'
103 # Pretty printer for llvm::SmallVector/llvm::SmallVectorImpl
104 class SmallVectorSynthProvider
:
105 def __init__(self
, valobj
, internal_dict
):
107 self
.update() # initialize this provider
109 def num_children(self
):
110 return self
.size
.GetValueAsUnsigned(0)
112 def get_child_index(self
, name
):
114 return int(name
.lstrip("[").rstrip("]"))
118 def get_child_at_index(self
, index
):
119 # Do bounds checking.
122 if index
>= self
.num_children():
125 offset
= index
* self
.type_size
126 return self
.begin
.CreateChildAtOffset(
127 "[" + str(index
) + "]", offset
, self
.data_type
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()
139 if the_type
.IsPointerType():
140 the_type
= the_type
.GetPointeeType()
142 self
.data_type
= the_type
.GetTemplateArgumentType(0)
143 self
.type_size
= self
.data_type
.GetByteSize()
144 assert self
.type_size
!= 0
147 class ArrayRefSynthProvider
:
148 """Provider for llvm::ArrayRef"""
150 def __init__(self
, valobj
, internal_dict
):
152 self
.update() # initialize this provider
154 def num_children(self
):
157 def get_child_index(self
, name
):
159 return int(name
.lstrip("[").rstrip("]"))
163 def get_child_at_index(self
, index
):
164 if index
< 0 or index
>= self
.num_children():
166 offset
= index
* self
.type_size
167 return self
.data
.CreateChildAtOffset(
168 "[" + str(index
) + "]", offset
, self
.data_type
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
180 def GetOptionalValue(valobj
):
181 storage
= valobj
.GetChildMemberWithName("Storage")
186 hasVal
= storage
.GetChildMemberWithName("hasVal").GetValueAsUnsigned(failure
)
187 if hasVal
== failure
:
188 return "<could not read llvm::Optional>"
193 underlying_type
= storage
.GetType().GetTemplateArgumentType(0)
194 storage
= storage
.GetChildMemberWithName("value")
195 return storage
.Cast(underlying_type
)
198 def OptionalSummaryProvider(valobj
, internal_dict
):
199 val
= GetOptionalValue(valobj
)
207 class OptionalSynthProvider
:
208 """Provides deref support to llvm::Optional<T>"""
210 def __init__(self
, valobj
, internal_dict
):
213 def num_children(self
):
214 return self
.valobj
.num_children
216 def get_child_index(self
, name
):
217 if name
== "$$dereference$$":
218 return self
.valobj
.num_children
219 return self
.valobj
.GetIndexOfChildWithName(name
)
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()
227 def SmallStringSummaryProvider(valobj
, internal_dict
):
228 # The underlying SmallVector base class is the first child.
229 vector
= valobj
.GetChildAtIndex(0)
230 num_elements
= vector
.GetNumChildren()
232 for i
in range(num_elements
):
233 c
= vector
.GetChildAtIndex(i
)
235 res
+= chr(c
.GetValueAsUnsigned())
240 def 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.
245 actual_length
= valobj
.GetChildAtIndex(1).GetValueAsUnsigned()
246 truncate
= actual_length
> max_length
247 length
= min(max_length
, actual_length
)
251 data
= valobj
.GetChildAtIndex(0).GetPointeeData(item_count
=length
)
252 error
= lldb
.SBError()
253 string
= data
.ReadRawData(error
, 0, data
.GetByteSize()).decode()
255 return "<error: %s>" % error
.description
257 # json.dumps conveniently escapes the string for us.
258 string
= json
.dumps(string
)
265 def ConstStringSummaryProvider(valobj
, internal_dict
):
266 if valobj
.GetNumChildren() == 1:
267 return valobj
.GetChildAtIndex(0).GetSummary()
271 def get_expression_path(val
):
272 stream
= lldb
.SBStream()
273 if not val
.GetExpressionPath(stream
):
275 return stream
.GetData()
278 class PointerIntPairSynthProvider
:
279 def __init__(self
, valobj
, internal_dict
):
283 def num_children(self
):
286 def get_child_index(self
, name
):
287 if name
== "Pointer":
293 def get_child_at_index(self
, index
):
294 expr_path
= get_expression_path(self
.valobj
)
296 return self
.valobj
.CreateValueFromExpression(
297 "Pointer", f
"({self.pointer_ty.name}){expr_path}.getPointer()"
300 return self
.valobj
.CreateValueFromExpression(
301 "Int", f
"({self.int_ty.name}){expr_path}.getInt()"
306 self
.pointer_ty
= self
.valobj
.GetType().GetTemplateArgumentType(0)
307 self
.int_ty
= self
.valobj
.GetType().GetTemplateArgumentType(2)
310 def parse_template_parameters(typename
):
312 LLDB doesn't support template parameter packs, so let's parse them manually.
315 start
= typename
.find("<")
316 end
= typename
.rfind(">")
317 if start
< 1 or end
< 2 or end
- start
< 2:
321 current_parameter_start
= start
+ 1
323 for i
in range(start
+ 1, end
+ 1):
329 elif c
== "," and nesting_level
== 0:
330 result
.append(typename
[current_parameter_start
:i
].strip())
331 current_parameter_start
= i
+ 1
333 result
.append(typename
[current_parameter_start
:i
].strip())
338 class PointerUnionSynthProvider
:
339 def __init__(self
, valobj
, internal_dict
):
343 def num_children(self
):
346 def get_child_index(self
, name
):
351 def get_child_at_index(self
, index
):
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()"
360 self
.pointer_int_pair
= self
.valobj
.GetChildMemberWithName("Val")
361 self
.val_expr_path
= get_expression_path(
362 self
.valobj
.GetChildMemberWithName("Val")
364 self
.active_type_tag
= self
.valobj
.CreateValueFromExpression(
365 "", f
"(int){self.val_expr_path}.getInt()"
367 self
.template_args
= parse_template_parameters(self
.valobj
.GetType().name
)
370 def 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
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}"
383 class DenseMapSynthetic
:
386 # The indexes into `Buckets` that contain valid map entries.
387 child_buckets
: list[int]
389 def __init__(self
, valobj
: lldb
.SBValue
, _
) -> None:
392 def num_children(self
) -> int:
393 return len(self
.child_buckets
)
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}]")
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.
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)
408 return entry
.Clone(f
"[{child_index}]")
411 self
.child_buckets
= []
413 num_entries
= self
.valobj
.GetChildMemberWithName("NumEntries").unsigned
417 buckets
= self
.valobj
.GetChildMemberWithName("Buckets")
418 num_buckets
= self
.valobj
.GetChildMemberWithName("NumBuckets").unsigned
420 # Bucket entries contain one of the following:
423 # 3. Tombstone key (a deleted entry)
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.
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
)
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])
443 class ExpectedSynthetic
:
444 # The llvm::Expected<T> value.
445 expected
: lldb
.SBValue
446 # The stored success value or error value.
447 stored_value
: lldb
.SBValue
449 def __init__(self
, valobj
: lldb
.SBValue
, _
) -> None:
450 self
.expected
= valobj
452 def update(self
) -> None:
453 has_error
= self
.expected
.GetChildMemberWithName("HasError").unsigned
459 member
= "ErrorStorage"
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
)
466 def num_children(self
) -> int:
469 def get_child_index(self
, name
: str) -> int:
470 if name
== self
.stored_value
.name
:
472 # Allow dereferencing for values, not errors.
473 if name
== "$$dereference$$" and self
.stored_value
.name
== "value":
477 def get_child_at_index(self
, idx
: int) -> lldb
.SBValue
:
479 return self
.stored_value
480 return lldb
.SBValue()