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