[AArch64] Fix SDNode type mismatches between *.td files and ISel (#116523)
[llvm-project.git] / llvm / utils / lldbDataFormatters.py
blob4a34ad2a87c331f533b485d8a75cafeec695475e
1 """
2 LLDB Formatters for LLVM data types.
4 Load into LLDB with 'command script import /path/to/lldbDataFormatters.py'
5 """
6 from __future__ import annotations
8 import collections
9 import lldb
10 import json
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 "
63 "llvm::StringRef"
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<.+>$"'
78 # )
79 # debugger.HandleCommand(
80 # "type synthetic add -w llvm "
81 # f"-l {__name__}.PointerUnionSynthProvider "
82 # '-x "^llvm::PointerUnion<.+>$"'
83 # )
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):
100 self.valobj = valobj
101 self.update() # initialize this provider
103 def num_children(self):
104 return self.size.GetValueAsUnsigned(0)
106 def get_child_index(self, name):
107 try:
108 return int(name.lstrip("[").rstrip("]"))
109 except:
110 return -1
112 def get_child_at_index(self, index):
113 # Do bounds checking.
114 if index < 0:
115 return None
116 if index >= self.num_children():
117 return None
119 offset = index * self.type_size
120 return self.begin.CreateChildAtOffset(
121 "[" + str(index) + "]", offset, self.data_type
124 def update(self):
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):
145 self.valobj = valobj
146 self.update() # initialize this provider
148 def num_children(self):
149 return self.length
151 def get_child_index(self, name):
152 try:
153 return int(name.lstrip("[").rstrip("]"))
154 except:
155 return -1
157 def get_child_at_index(self, index):
158 if index < 0 or index >= self.num_children():
159 return None
160 offset = index * self.type_size
161 return self.data.CreateChildAtOffset(
162 "[" + str(index) + "]", offset, self.data_type
165 def update(self):
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")
176 if not storage:
177 storage = valobj
179 failure = 2
180 hasVal = storage.GetChildMemberWithName("hasVal").GetValueAsUnsigned(failure)
181 if hasVal == failure:
182 return "<could not read llvm::Optional>"
184 if hasVal == 0:
185 return None
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)
194 if val is None:
195 return "None"
196 if val.summary:
197 return val.summary
198 return ""
201 class OptionalSynthProvider:
202 """Provides deref support to llvm::Optional<T>"""
204 def __init__(self, valobj, internal_dict):
205 self.valobj = valobj
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()
225 res = '"'
226 for i in range(num_elements):
227 c = vector.GetChildAtIndex(i)
228 if c:
229 res += chr(c.GetValueAsUnsigned())
230 res += '"'
231 return res
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.
238 max_length = 1024
239 actual_length = valobj.GetChildAtIndex(1).GetValueAsUnsigned()
240 truncate = actual_length > max_length
241 length = min(max_length, actual_length)
242 if length == 0:
243 return '""'
245 data = valobj.GetChildAtIndex(0).GetPointeeData(item_count=length)
246 error = lldb.SBError()
247 string = data.ReadRawData(error, 0, data.GetByteSize()).decode()
248 if error.Fail():
249 return "<error: %s>" % error.description
251 # json.dumps conveniently escapes the string for us.
252 string = json.dumps(string)
253 if truncate:
254 string += "..."
255 return string
256 return None
259 def ConstStringSummaryProvider(valobj, internal_dict):
260 if valobj.GetNumChildren() == 1:
261 return valobj.GetChildAtIndex(0).GetSummary()
262 return ""
265 def get_expression_path(val):
266 stream = lldb.SBStream()
267 if not val.GetExpressionPath(stream):
268 return None
269 return stream.GetData()
272 class PointerIntPairSynthProvider:
273 def __init__(self, valobj, internal_dict):
274 self.valobj = valobj
275 self.update()
277 def num_children(self):
278 return 2
280 def get_child_index(self, name):
281 if name == "Pointer":
282 return 0
283 if name == "Int":
284 return 1
285 return None
287 def get_child_at_index(self, index):
288 expr_path = get_expression_path(self.valobj)
289 if index == 0:
290 return self.valobj.CreateValueFromExpression(
291 "Pointer", f"({self.pointer_ty.name}){expr_path}.getPointer()"
293 if index == 1:
294 return self.valobj.CreateValueFromExpression(
295 "Int", f"({self.int_ty.name}){expr_path}.getInt()"
297 return None
299 def update(self):
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.
308 result = []
309 start = typename.find("<")
310 end = typename.rfind(">")
311 if start < 1 or end < 2 or end - start < 2:
312 return result
314 nesting_level = 0
315 current_parameter_start = start + 1
317 for i in range(start + 1, end + 1):
318 c = typename[i]
319 if c == "<":
320 nesting_level += 1
321 elif c == ">":
322 nesting_level -= 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())
329 return result
332 class PointerUnionSynthProvider:
333 def __init__(self, valobj, internal_dict):
334 self.valobj = valobj
335 self.update()
337 def num_children(self):
338 return 1
340 def get_child_index(self, name):
341 if name == "Ptr":
342 return 0
343 return None
345 def get_child_at_index(self, index):
346 if index != 0:
347 return None
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()"
353 def update(self):
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()"
360 ).GetValueAsSigned()
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}"
374 return summary
377 class DenseMapSynthetic:
378 valobj: lldb.SBValue
380 # The indexes into `Buckets` that contain valid map entries.
381 child_buckets: list[int]
383 def __init__(self, valobj: lldb.SBValue, _) -> None:
384 self.valobj = valobj
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}]")
404 def update(self):
405 self.child_buckets = []
407 num_entries = self.valobj.GetChildMemberWithName("NumEntries").unsigned
408 if num_entries == 0:
409 return
411 buckets = self.valobj.GetChildMemberWithName("Buckets")
412 num_buckets = self.valobj.GetChildMemberWithName("NumBuckets").unsigned
414 # Bucket entries contain one of the following:
415 # 1. Valid key-value
416 # 2. Empty key
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])