1""" 2LLDB Formatters for LLVM data types. 3 4Load into LLDB with 'command script import /path/to/lldbDataFormatters.py' 5""" 6 7import lldb 8import json 9 10def __lldb_init_module(debugger, internal_dict): 11 debugger.HandleCommand('type category define -e llvm -l c++') 12 debugger.HandleCommand('type synthetic add -w llvm ' 13 '-l lldbDataFormatters.SmallVectorSynthProvider ' 14 '-x "^llvm::SmallVectorImpl<.+>$"') 15 debugger.HandleCommand('type summary add -w llvm ' 16 '-s "size=${svar%#}" ' 17 '-x "^llvm::SmallVectorImpl<.+>$"') 18 debugger.HandleCommand('type synthetic add -w llvm ' 19 '-l lldbDataFormatters.SmallVectorSynthProvider ' 20 '-x "^llvm::SmallVector<.+,.+>$"') 21 debugger.HandleCommand('type summary add -w llvm ' 22 '-s "size=${svar%#}" ' 23 '-x "^llvm::SmallVector<.+,.+>$"') 24 debugger.HandleCommand('type synthetic add -w llvm ' 25 '-l lldbDataFormatters.ArrayRefSynthProvider ' 26 '-x "^llvm::ArrayRef<.+>$"') 27 debugger.HandleCommand('type summary add -w llvm ' 28 '-s "size=${svar%#}" ' 29 '-x "^llvm::ArrayRef<.+>$"') 30 debugger.HandleCommand('type synthetic add -w llvm ' 31 '-l lldbDataFormatters.OptionalSynthProvider ' 32 '-x "^llvm::Optional<.+>$"') 33 debugger.HandleCommand('type summary add -w llvm ' 34 '-F lldbDataFormatters.OptionalSummaryProvider ' 35 '-x "^llvm::Optional<.+>$"') 36 debugger.HandleCommand('type summary add -w llvm ' 37 '-F lldbDataFormatters.SmallStringSummaryProvider ' 38 '-x "^llvm::SmallString<.+>$"') 39 debugger.HandleCommand('type summary add -w llvm ' 40 '-F lldbDataFormatters.StringRefSummaryProvider ' 41 '-x "^llvm::StringRef$"') 42 debugger.HandleCommand('type summary add -w llvm ' 43 '-F lldbDataFormatters.ConstStringSummaryProvider ' 44 '-x "^lldb_private::ConstString$"') 45 debugger.HandleCommand('type synthetic add -w llvm ' 46 '-l lldbDataFormatters.PointerIntPairSynthProvider ' 47 '-x "^llvm::PointerIntPair<.+>$"') 48 debugger.HandleCommand('type synthetic add -w llvm ' 49 '-l lldbDataFormatters.PointerUnionSynthProvider ' 50 '-x "^llvm::PointerUnion<.+>$"') 51 52 53# Pretty printer for llvm::SmallVector/llvm::SmallVectorImpl 54class SmallVectorSynthProvider: 55 def __init__(self, valobj, internal_dict): 56 self.valobj = valobj; 57 self.update() # initialize this provider 58 59 def num_children(self): 60 return self.size.GetValueAsUnsigned(0) 61 62 def get_child_index(self, name): 63 try: 64 return int(name.lstrip('[').rstrip(']')) 65 except: 66 return -1; 67 68 def get_child_at_index(self, index): 69 # Do bounds checking. 70 if index < 0: 71 return None 72 if index >= self.num_children(): 73 return None; 74 75 offset = index * self.type_size 76 return self.begin.CreateChildAtOffset('['+str(index)+']', 77 offset, self.data_type) 78 79 def update(self): 80 self.begin = self.valobj.GetChildMemberWithName('BeginX') 81 self.size = self.valobj.GetChildMemberWithName('Size') 82 the_type = self.valobj.GetType() 83 # If this is a reference type we have to dereference it to get to the 84 # template parameter. 85 if the_type.IsReferenceType(): 86 the_type = the_type.GetDereferencedType() 87 88 self.data_type = the_type.GetTemplateArgumentType(0) 89 self.type_size = self.data_type.GetByteSize() 90 assert self.type_size != 0 91 92class ArrayRefSynthProvider: 93 """ Provider for llvm::ArrayRef """ 94 def __init__(self, valobj, internal_dict): 95 self.valobj = valobj; 96 self.update() # initialize this provider 97 98 def num_children(self): 99 return self.length 100 101 def get_child_index(self, name): 102 try: 103 return int(name.lstrip('[').rstrip(']')) 104 except: 105 return -1; 106 107 def get_child_at_index(self, index): 108 if index < 0 or index >= self.num_children(): 109 return None; 110 offset = index * self.type_size 111 return self.data.CreateChildAtOffset('[' + str(index) + ']', 112 offset, self.data_type) 113 114 def update(self): 115 self.data = self.valobj.GetChildMemberWithName('Data') 116 length_obj = self.valobj.GetChildMemberWithName('Length') 117 self.length = length_obj.GetValueAsUnsigned(0) 118 self.data_type = self.data.GetType().GetPointeeType() 119 self.type_size = self.data_type.GetByteSize() 120 assert self.type_size != 0 121 122def GetOptionalValue(valobj): 123 storage = valobj.GetChildMemberWithName('Storage') 124 if not storage: 125 storage = valobj 126 127 failure = 2 128 hasVal = storage.GetChildMemberWithName('hasVal').GetValueAsUnsigned(failure) 129 if hasVal == failure: 130 return '<could not read llvm::Optional>' 131 132 if hasVal == 0: 133 return None 134 135 underlying_type = storage.GetType().GetTemplateArgumentType(0) 136 storage = storage.GetChildMemberWithName('value') 137 return storage.Cast(underlying_type) 138 139def OptionalSummaryProvider(valobj, internal_dict): 140 val = GetOptionalValue(valobj) 141 return val.summary if val else 'None' 142 143class OptionalSynthProvider: 144 """Provides deref support to llvm::Optional<T>""" 145 def __init__(self, valobj, internal_dict): 146 self.valobj = valobj 147 148 def num_children(self): 149 return self.valobj.num_children 150 151 def get_child_index(self, name): 152 if name == '$$dereference$$': 153 return self.valobj.num_children 154 return self.valobj.GetIndexOfChildWithName(name) 155 156 def get_child_at_index(self, index): 157 if index < self.valobj.num_children: 158 return self.valobj.GetChildAtIndex(index) 159 return GetOptionalValue(self.valobj) or lldb.SBValue() 160 161def SmallStringSummaryProvider(valobj, internal_dict): 162 num_elements = valobj.GetNumChildren() 163 res = "\"" 164 for i in range(0, num_elements): 165 c = valobj.GetChildAtIndex(i).GetValue() 166 if c: 167 res += c.strip("'") 168 res += "\"" 169 return res 170 171 172def StringRefSummaryProvider(valobj, internal_dict): 173 if valobj.GetNumChildren() == 2: 174 # StringRef's are also used to point at binary blobs in memory, 175 # so filter out suspiciously long strings. 176 max_length = 1024 177 actual_length = valobj.GetChildAtIndex(1).GetValueAsUnsigned() 178 truncate = actual_length > max_length 179 length = min(max_length, actual_length) 180 if length == 0: 181 return '""' 182 183 data = valobj.GetChildAtIndex(0).GetPointeeData(item_count=length) 184 error = lldb.SBError() 185 string = data.ReadRawData(error, 0, data.GetByteSize()).decode() 186 if error.Fail(): 187 return "<error: %s>" % error.description 188 189 # json.dumps conveniently escapes the string for us. 190 string = json.dumps(string) 191 if truncate: 192 string += "..." 193 return string 194 return None 195 196 197def ConstStringSummaryProvider(valobj, internal_dict): 198 if valobj.GetNumChildren() == 1: 199 return valobj.GetChildAtIndex(0).GetSummary() 200 return "" 201 202 203def get_expression_path(val): 204 stream = lldb.SBStream() 205 if not val.GetExpressionPath(stream): 206 return None 207 return stream.GetData() 208 209 210class PointerIntPairSynthProvider: 211 def __init__(self, valobj, internal_dict): 212 self.valobj = valobj 213 self.update() 214 215 def num_children(self): 216 return 2 217 218 def get_child_index(self, name): 219 if name == 'Pointer': 220 return 0 221 if name == 'Int': 222 return 1 223 return None 224 225 def get_child_at_index(self, index): 226 expr_path = get_expression_path(self.valobj) 227 if index == 0: 228 return self.valobj.CreateValueFromExpression('Pointer', f'({self.pointer_ty.name}){expr_path}.getPointer()') 229 if index == 1: 230 return self.valobj.CreateValueFromExpression('Int', f'({self.int_ty.name}){expr_path}.getInt()') 231 return None 232 233 def update(self): 234 self.pointer_ty = self.valobj.GetType().GetTemplateArgumentType(0) 235 self.int_ty = self.valobj.GetType().GetTemplateArgumentType(2) 236 237 238def parse_template_parameters(typename): 239 """ 240 LLDB doesn't support template parameter packs, so let's parse them manually. 241 """ 242 result = [] 243 start = typename.find('<') 244 end = typename.rfind('>') 245 if start < 1 or end < 2 or end - start < 2: 246 return result 247 248 nesting_level = 0 249 current_parameter_start = start + 1 250 251 for i in range(start + 1, end + 1): 252 c = typename[i] 253 if c == '<': 254 nesting_level += 1 255 elif c == '>': 256 nesting_level -= 1 257 elif c == ',' and nesting_level == 0: 258 result.append(typename[current_parameter_start:i].strip()) 259 current_parameter_start = i + 1 260 261 result.append(typename[current_parameter_start:i].strip()) 262 263 return result 264 265 266class PointerUnionSynthProvider: 267 def __init__(self, valobj, internal_dict): 268 self.valobj = valobj 269 self.update() 270 271 def num_children(self): 272 return 1 273 274 def get_child_index(self, name): 275 if name == 'Ptr': 276 return 0 277 return None 278 279 def get_child_at_index(self, index): 280 if index != 0: 281 return None 282 ptr_type_name = self.template_args[self.active_type_tag] 283 return self.valobj.CreateValueFromExpression('Ptr', f'({ptr_type_name}){self.val_expr_path}.getPointer()') 284 285 def update(self): 286 self.pointer_int_pair = self.valobj.GetChildMemberWithName('Val') 287 self.val_expr_path = get_expression_path(self.valobj.GetChildMemberWithName('Val')) 288 self.active_type_tag = self.valobj.CreateValueFromExpression('', f'(int){self.val_expr_path}.getInt()').GetValueAsSigned() 289 self.template_args = parse_template_parameters(self.valobj.GetType().name) 290