2 LLDB Formatters for LLVM data types.
4 Load into LLDB with 'command script import /path/to/lldbDataFormatters.py'
12 def __lldb_init_module(debugger
, internal_dict
):
13 debugger
.HandleCommand("type category define -e llvm -l c++")
14 debugger
.HandleCommand(
15 "type synthetic add -w llvm "
16 f
"-l {__name__}.SmallVectorSynthProvider "
17 '-x "^llvm::SmallVectorImpl<.+>$"'
19 debugger
.HandleCommand(
20 "type summary add -w llvm "
21 '-e -s "size=${svar%#}" '
22 '-x "^llvm::SmallVectorImpl<.+>$"'
24 debugger
.HandleCommand(
25 "type synthetic add -w llvm "
26 f
"-l {__name__}.SmallVectorSynthProvider "
27 '-x "^llvm::SmallVector<.+,.+>$"'
29 debugger
.HandleCommand(
30 "type summary add -w llvm "
31 '-e -s "size=${svar%#}" '
32 '-x "^llvm::SmallVector<.+,.+>$"'
34 debugger
.HandleCommand(
35 "type synthetic add -w llvm "
36 f
"-l {__name__}.ArrayRefSynthProvider "
37 '-x "^llvm::ArrayRef<.+>$"'
39 debugger
.HandleCommand(
40 "type summary add -w llvm "
41 '-e -s "size=${svar%#}" '
42 '-x "^llvm::ArrayRef<.+>$"'
44 debugger
.HandleCommand(
45 "type synthetic add -w llvm "
46 f
"-l {__name__}.OptionalSynthProvider "
47 '-x "^llvm::Optional<.+>$"'
49 debugger
.HandleCommand(
50 "type summary add -w llvm "
51 f
"-e -F {__name__}.OptionalSummaryProvider "
52 '-x "^llvm::Optional<.+>$"'
54 debugger
.HandleCommand(
55 "type summary add -w llvm "
56 f
"-F {__name__}.SmallStringSummaryProvider "
57 '-x "^llvm::SmallString<.+>$"'
59 debugger
.HandleCommand(
60 "type summary add -w llvm "
61 f
"-F {__name__}.StringRefSummaryProvider "
64 debugger
.HandleCommand(
65 "type summary add -w llvm "
66 f
"-F {__name__}.ConstStringSummaryProvider "
67 "lldb_private::ConstString"
70 # The synthetic providers for PointerIntPair and PointerUnion are disabled
71 # because of a few issues. One example is template arguments that are
72 # non-pointer types that instead specialize PointerLikeTypeTraits.
73 # debugger.HandleCommand(
74 # "type synthetic add -w llvm "
75 # f"-l {__name__}.PointerIntPairSynthProvider "
76 # '-x "^llvm::PointerIntPair<.+>$"'
78 # debugger.HandleCommand(
79 # "type synthetic add -w llvm "
80 # f"-l {__name__}.PointerUnionSynthProvider "
81 # '-x "^llvm::PointerUnion<.+>$"'
84 debugger
.HandleCommand(
85 "type summary add -w llvm "
86 f
"-e -F {__name__}.DenseMapSummary "
87 '-x "^llvm::DenseMap<.+>$"'
89 debugger
.HandleCommand(
90 "type synthetic add -w llvm "
91 f
"-l {__name__}.DenseMapSynthetic "
92 '-x "^llvm::DenseMap<.+>$"'
96 # Pretty printer for llvm::SmallVector/llvm::SmallVectorImpl
97 class SmallVectorSynthProvider
:
98 def __init__(self
, valobj
, internal_dict
):
100 self
.update() # initialize this provider
102 def num_children(self
):
103 return self
.size
.GetValueAsUnsigned(0)
105 def get_child_index(self
, name
):
107 return int(name
.lstrip("[").rstrip("]"))
111 def get_child_at_index(self
, index
):
112 # Do bounds checking.
115 if index
>= self
.num_children():
118 offset
= index
* self
.type_size
119 return self
.begin
.CreateChildAtOffset(
120 "[" + str(index
) + "]", offset
, self
.data_type
124 self
.begin
= self
.valobj
.GetChildMemberWithName("BeginX")
125 self
.size
= self
.valobj
.GetChildMemberWithName("Size")
126 the_type
= self
.valobj
.GetType()
127 # If this is a reference type we have to dereference it to get to the
128 # template parameter.
129 if the_type
.IsReferenceType():
130 the_type
= the_type
.GetDereferencedType()
132 if the_type
.IsPointerType():
133 the_type
= the_type
.GetPointeeType()
135 self
.data_type
= the_type
.GetTemplateArgumentType(0)
136 self
.type_size
= self
.data_type
.GetByteSize()
137 assert self
.type_size
!= 0
140 class ArrayRefSynthProvider
:
141 """Provider for llvm::ArrayRef"""
143 def __init__(self
, valobj
, internal_dict
):
145 self
.update() # initialize this provider
147 def num_children(self
):
150 def get_child_index(self
, name
):
152 return int(name
.lstrip("[").rstrip("]"))
156 def get_child_at_index(self
, index
):
157 if index
< 0 or index
>= self
.num_children():
159 offset
= index
* self
.type_size
160 return self
.data
.CreateChildAtOffset(
161 "[" + str(index
) + "]", offset
, self
.data_type
165 self
.data
= self
.valobj
.GetChildMemberWithName("Data")
166 length_obj
= self
.valobj
.GetChildMemberWithName("Length")
167 self
.length
= length_obj
.GetValueAsUnsigned(0)
168 self
.data_type
= self
.data
.GetType().GetPointeeType()
169 self
.type_size
= self
.data_type
.GetByteSize()
170 assert self
.type_size
!= 0
173 def GetOptionalValue(valobj
):
174 storage
= valobj
.GetChildMemberWithName("Storage")
179 hasVal
= storage
.GetChildMemberWithName("hasVal").GetValueAsUnsigned(failure
)
180 if hasVal
== failure
:
181 return "<could not read llvm::Optional>"
186 underlying_type
= storage
.GetType().GetTemplateArgumentType(0)
187 storage
= storage
.GetChildMemberWithName("value")
188 return storage
.Cast(underlying_type
)
191 def OptionalSummaryProvider(valobj
, internal_dict
):
192 val
= GetOptionalValue(valobj
)
200 class OptionalSynthProvider
:
201 """Provides deref support to llvm::Optional<T>"""
203 def __init__(self
, valobj
, internal_dict
):
206 def num_children(self
):
207 return self
.valobj
.num_children
209 def get_child_index(self
, name
):
210 if name
== "$$dereference$$":
211 return self
.valobj
.num_children
212 return self
.valobj
.GetIndexOfChildWithName(name
)
214 def get_child_at_index(self
, index
):
215 if index
< self
.valobj
.num_children
:
216 return self
.valobj
.GetChildAtIndex(index
)
217 return GetOptionalValue(self
.valobj
) or lldb
.SBValue()
220 def SmallStringSummaryProvider(valobj
, internal_dict
):
221 # The underlying SmallVector base class is the first child.
222 vector
= valobj
.GetChildAtIndex(0)
223 num_elements
= vector
.GetNumChildren()
225 for i
in range(num_elements
):
226 c
= vector
.GetChildAtIndex(i
)
228 res
+= chr(c
.GetValueAsUnsigned())
233 def StringRefSummaryProvider(valobj
, internal_dict
):
234 if valobj
.GetNumChildren() == 2:
235 # StringRef's are also used to point at binary blobs in memory,
236 # so filter out suspiciously long strings.
238 actual_length
= valobj
.GetChildAtIndex(1).GetValueAsUnsigned()
239 truncate
= actual_length
> max_length
240 length
= min(max_length
, actual_length
)
244 data
= valobj
.GetChildAtIndex(0).GetPointeeData(item_count
=length
)
245 error
= lldb
.SBError()
246 string
= data
.ReadRawData(error
, 0, data
.GetByteSize()).decode()
248 return "<error: %s>" % error
.description
250 # json.dumps conveniently escapes the string for us.
251 string
= json
.dumps(string
)
258 def ConstStringSummaryProvider(valobj
, internal_dict
):
259 if valobj
.GetNumChildren() == 1:
260 return valobj
.GetChildAtIndex(0).GetSummary()
264 def get_expression_path(val
):
265 stream
= lldb
.SBStream()
266 if not val
.GetExpressionPath(stream
):
268 return stream
.GetData()
271 class PointerIntPairSynthProvider
:
272 def __init__(self
, valobj
, internal_dict
):
276 def num_children(self
):
279 def get_child_index(self
, name
):
280 if name
== "Pointer":
286 def get_child_at_index(self
, index
):
287 expr_path
= get_expression_path(self
.valobj
)
289 return self
.valobj
.CreateValueFromExpression(
290 "Pointer", f
"({self.pointer_ty.name}){expr_path}.getPointer()"
293 return self
.valobj
.CreateValueFromExpression(
294 "Int", f
"({self.int_ty.name}){expr_path}.getInt()"
299 self
.pointer_ty
= self
.valobj
.GetType().GetTemplateArgumentType(0)
300 self
.int_ty
= self
.valobj
.GetType().GetTemplateArgumentType(2)
303 def parse_template_parameters(typename
):
305 LLDB doesn't support template parameter packs, so let's parse them manually.
308 start
= typename
.find("<")
309 end
= typename
.rfind(">")
310 if start
< 1 or end
< 2 or end
- start
< 2:
314 current_parameter_start
= start
+ 1
316 for i
in range(start
+ 1, end
+ 1):
322 elif c
== "," and nesting_level
== 0:
323 result
.append(typename
[current_parameter_start
:i
].strip())
324 current_parameter_start
= i
+ 1
326 result
.append(typename
[current_parameter_start
:i
].strip())
331 class PointerUnionSynthProvider
:
332 def __init__(self
, valobj
, internal_dict
):
336 def num_children(self
):
339 def get_child_index(self
, name
):
344 def get_child_at_index(self
, index
):
347 ptr_type_name
= self
.template_args
[self
.active_type_tag
]
348 return self
.valobj
.CreateValueFromExpression(
349 "Ptr", f
"({ptr_type_name}){self.val_expr_path}.getPointer()"
353 self
.pointer_int_pair
= self
.valobj
.GetChildMemberWithName("Val")
354 self
.val_expr_path
= get_expression_path(
355 self
.valobj
.GetChildMemberWithName("Val")
357 self
.active_type_tag
= self
.valobj
.CreateValueFromExpression(
358 "", f
"(int){self.val_expr_path}.getInt()"
360 self
.template_args
= parse_template_parameters(self
.valobj
.GetType().name
)
363 def DenseMapSummary(valobj
: lldb
.SBValue
, _
) -> str:
364 raw_value
= valobj
.GetNonSyntheticValue()
365 num_entries
= raw_value
.GetChildMemberWithName("NumEntries").unsigned
366 num_tombstones
= raw_value
.GetChildMemberWithName("NumTombstones").unsigned
368 summary
= f
"size={num_entries}"
369 if num_tombstones
== 1:
370 # The heuristic to identify valid entries does not handle the case of a
371 # single tombstone. The summary calls attention to this.
372 summary
= f
"tombstones=1, {summary}"
376 class DenseMapSynthetic
:
379 # The indexes into `Buckets` that contain valid map entries.
380 child_buckets
: list[int]
382 def __init__(self
, valobj
: lldb
.SBValue
, _
) -> None:
385 def num_children(self
) -> int:
386 return len(self
.child_buckets
)
388 def get_child_at_index(self
, child_index
: int) -> lldb
.SBValue
:
389 bucket_index
= self
.child_buckets
[child_index
]
390 entry
= self
.valobj
.GetValueForExpressionPath(f
".Buckets[{bucket_index}]")
392 # By default, DenseMap instances use DenseMapPair to hold key-value
393 # entries. When the entry is a DenseMapPair, unwrap it to expose the
394 # children as simple std::pair values.
396 # This entry type is customizable (a template parameter). For other
397 # types, expose the entry type as is.
398 if entry
.type.name
.startswith("llvm::detail::DenseMapPair<"):
399 entry
= entry
.GetChildAtIndex(0)
401 return entry
.Clone(f
"[{child_index}]")
404 self
.child_buckets
= []
406 num_entries
= self
.valobj
.GetChildMemberWithName("NumEntries").unsigned
410 buckets
= self
.valobj
.GetChildMemberWithName("Buckets")
411 num_buckets
= self
.valobj
.GetChildMemberWithName("NumBuckets").unsigned
413 # Bucket entries contain one of the following:
416 # 3. Tombstone key (a deleted entry)
418 # NumBuckets is always greater than NumEntries. The empty key, and
419 # potentially the tombstone key, will occur multiple times. A key that
420 # is repeated is either the empty key or the tombstone key.
422 # For each key, collect a list of buckets it appears in.
423 key_buckets
: dict[str, list[int]] = collections
.defaultdict(list)
424 for index
in range(num_buckets
):
425 key
= buckets
.GetValueForExpressionPath(f
"[{index}].first")
426 key_buckets
[str(key
.data
)].append(index
)
428 # Heuristic: This is not a multi-map, any repeated (non-unique) keys are
429 # either the the empty key or the tombstone key. Populate child_buckets
430 # with the indexes of entries containing unique keys.
431 for indexes
in key_buckets
.values():
432 if len(indexes
) == 1:
433 self
.child_buckets
.append(indexes
[0])