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 if val is None: 142 return 'None' 143 if val.summary: 144 return val.summary 145 return '' 146 147class OptionalSynthProvider: 148 """Provides deref support to llvm::Optional<T>""" 149 def __init__(self, valobj, internal_dict): 150 self.valobj = valobj 151 152 def num_children(self): 153 return self.valobj.num_children 154 155 def get_child_index(self, name): 156 if name == '$$dereference$$': 157 return self.valobj.num_children 158 return self.valobj.GetIndexOfChildWithName(name) 159 160 def get_child_at_index(self, index): 161 if index < self.valobj.num_children: 162 return self.valobj.GetChildAtIndex(index) 163 return GetOptionalValue(self.valobj) or lldb.SBValue() 164 165def SmallStringSummaryProvider(valobj, internal_dict): 166 num_elements = valobj.GetNumChildren() 167 res = "\"" 168 for i in range(0, num_elements): 169 c = valobj.GetChildAtIndex(i).GetValue() 170 if c: 171 res += c.strip("'") 172 res += "\"" 173 return res 174 175 176def StringRefSummaryProvider(valobj, internal_dict): 177 if valobj.GetNumChildren() == 2: 178 # StringRef's are also used to point at binary blobs in memory, 179 # so filter out suspiciously long strings. 180 max_length = 1024 181 actual_length = valobj.GetChildAtIndex(1).GetValueAsUnsigned() 182 truncate = actual_length > max_length 183 length = min(max_length, actual_length) 184 if length == 0: 185 return '""' 186 187 data = valobj.GetChildAtIndex(0).GetPointeeData(item_count=length) 188 error = lldb.SBError() 189 string = data.ReadRawData(error, 0, data.GetByteSize()).decode() 190 if error.Fail(): 191 return "<error: %s>" % error.description 192 193 # json.dumps conveniently escapes the string for us. 194 string = json.dumps(string) 195 if truncate: 196 string += "..." 197 return string 198 return None 199 200 201def ConstStringSummaryProvider(valobj, internal_dict): 202 if valobj.GetNumChildren() == 1: 203 return valobj.GetChildAtIndex(0).GetSummary() 204 return "" 205 206 207def get_expression_path(val): 208 stream = lldb.SBStream() 209 if not val.GetExpressionPath(stream): 210 return None 211 return stream.GetData() 212 213 214class PointerIntPairSynthProvider: 215 def __init__(self, valobj, internal_dict): 216 self.valobj = valobj 217 self.update() 218 219 def num_children(self): 220 return 2 221 222 def get_child_index(self, name): 223 if name == 'Pointer': 224 return 0 225 if name == 'Int': 226 return 1 227 return None 228 229 def get_child_at_index(self, index): 230 expr_path = get_expression_path(self.valobj) 231 if index == 0: 232 return self.valobj.CreateValueFromExpression('Pointer', f'({self.pointer_ty.name}){expr_path}.getPointer()') 233 if index == 1: 234 return self.valobj.CreateValueFromExpression('Int', f'({self.int_ty.name}){expr_path}.getInt()') 235 return None 236 237 def update(self): 238 self.pointer_ty = self.valobj.GetType().GetTemplateArgumentType(0) 239 self.int_ty = self.valobj.GetType().GetTemplateArgumentType(2) 240 241 242def parse_template_parameters(typename): 243 """ 244 LLDB doesn't support template parameter packs, so let's parse them manually. 245 """ 246 result = [] 247 start = typename.find('<') 248 end = typename.rfind('>') 249 if start < 1 or end < 2 or end - start < 2: 250 return result 251 252 nesting_level = 0 253 current_parameter_start = start + 1 254 255 for i in range(start + 1, end + 1): 256 c = typename[i] 257 if c == '<': 258 nesting_level += 1 259 elif c == '>': 260 nesting_level -= 1 261 elif c == ',' and nesting_level == 0: 262 result.append(typename[current_parameter_start:i].strip()) 263 current_parameter_start = i + 1 264 265 result.append(typename[current_parameter_start:i].strip()) 266 267 return result 268 269 270class PointerUnionSynthProvider: 271 def __init__(self, valobj, internal_dict): 272 self.valobj = valobj 273 self.update() 274 275 def num_children(self): 276 return 1 277 278 def get_child_index(self, name): 279 if name == 'Ptr': 280 return 0 281 return None 282 283 def get_child_at_index(self, index): 284 if index != 0: 285 return None 286 ptr_type_name = self.template_args[self.active_type_tag] 287 return self.valobj.CreateValueFromExpression('Ptr', f'({ptr_type_name}){self.val_expr_path}.getPointer()') 288 289 def update(self): 290 self.pointer_int_pair = self.valobj.GetChildMemberWithName('Val') 291 self.val_expr_path = get_expression_path(self.valobj.GetChildMemberWithName('Val')) 292 self.active_type_tag = self.valobj.CreateValueFromExpression('', f'(int){self.val_expr_path}.getInt()').GetValueAsSigned() 293 self.template_args = parse_template_parameters(self.valobj.GetType().name) 294