1 """ 2 LLDB AppKit formatters 3 4 part of The LLVM Compiler Infrastructure 5 This file is distributed under the University of Illinois Open Source 6 License. See LICENSE.TXT for details. 7 """ 8 # example synthetic children and summary provider for CFString (and related NSString class) 9 # the real code is part of the LLDB core 10 import lldb 11 import lldb.runtime.objc.objc_runtime 12 import lldb.formatters.Logger 13 14 def CFString_SummaryProvider (valobj,dict): 15 logger = lldb.formatters.Logger.Logger() 16 provider = CFStringSynthProvider(valobj,dict); 17 if provider.invalid == False: 18 try: 19 summary = provider.get_child_at_index(provider.get_child_index("content")) 20 if type(summary) == lldb.SBValue: 21 summary = summary.GetSummary() 22 else: 23 summary = '"' + summary + '"' 24 except: 25 summary = None 26 if summary == None: 27 summary = '<variable is not NSString>' 28 return '@'+summary 29 return '' 30 31 def CFAttributedString_SummaryProvider (valobj,dict): 32 logger = lldb.formatters.Logger.Logger() 33 offset = valobj.GetTarget().GetProcess().GetAddressByteSize() 34 pointee = valobj.GetValueAsUnsigned(0) 35 summary = '<variable is not NSAttributedString>' 36 if pointee != None and pointee != 0: 37 pointee = pointee + offset 38 child_ptr = valobj.CreateValueFromAddress("string_ptr",pointee,valobj.GetType()) 39 child = child_ptr.CreateValueFromAddress("string_data",child_ptr.GetValueAsUnsigned(),valobj.GetType()).AddressOf() 40 provider = CFStringSynthProvider(child,dict); 41 if provider.invalid == False: 42 try: 43 summary = provider.get_child_at_index(provider.get_child_index("content")).GetSummary(); 44 except: 45 summary = '<variable is not NSAttributedString>' 46 if summary == None: 47 summary = '<variable is not NSAttributedString>' 48 return '@'+summary 49 50 51 def __lldb_init_module(debugger,dict): 52 debugger.HandleCommand("type summary add -F CFString.CFString_SummaryProvider NSString CFStringRef CFMutableStringRef") 53 debugger.HandleCommand("type summary add -F CFString.CFAttributedString_SummaryProvider NSAttributedString") 54 55 class CFStringSynthProvider: 56 def __init__(self,valobj,dict): 57 logger = lldb.formatters.Logger.Logger() 58 self.valobj = valobj; 59 self.update() 60 61 # children other than "content" are for debugging only and must not be used in production code 62 def num_children(self): 63 logger = lldb.formatters.Logger.Logger() 64 if self.invalid: 65 return 0; 66 return 6; 67 68 def read_unicode(self, pointer,max_len=2048): 69 logger = lldb.formatters.Logger.Logger() 70 process = self.valobj.GetTarget().GetProcess() 71 error = lldb.SBError() 72 pystr = u'' 73 # cannot do the read at once because the length value has 74 # a weird encoding. better play it safe here 75 while max_len > 0: 76 content = process.ReadMemory(pointer, 2, error) 77 new_bytes = bytearray(content) 78 b0 = new_bytes[0] 79 b1 = new_bytes[1] 80 pointer = pointer + 2 81 if b0 == 0 and b1 == 0: 82 break 83 # rearrange bytes depending on endianness 84 # (do we really need this or is Cocoa going to 85 # use Windows-compatible little-endian even 86 # if the target is big endian?) 87 if self.is_little: 88 value = b1 * 256 + b0 89 else: 90 value = b0 * 256 + b1 91 pystr = pystr + unichr(value) 92 # read max_len unicode values, not max_len bytes 93 max_len = max_len - 1 94 return pystr 95 96 # handle the special case strings 97 # only use the custom code for the tested LP64 case 98 def handle_special(self): 99 logger = lldb.formatters.Logger.Logger() 100 if self.is_64_bit == False: 101 # for 32bit targets, use safe ObjC code 102 return self.handle_unicode_string_safe() 103 offset = 12 104 pointer = self.valobj.GetValueAsUnsigned(0) + offset 105 pystr = self.read_unicode(pointer) 106 return self.valobj.CreateValueFromExpression("content", 107 "(char*)\"" + pystr.encode('utf-8') + "\"") 108 109 # last resort call, use ObjC code to read; the final aim is to 110 # be able to strip this call away entirely and only do the read 111 # ourselves 112 def handle_unicode_string_safe(self): 113 return self.valobj.CreateValueFromExpression("content", 114 "(char*)\"" + self.valobj.GetObjectDescription() + "\""); 115 116 def handle_unicode_string(self): 117 logger = lldb.formatters.Logger.Logger() 118 # step 1: find offset 119 if self.inline: 120 pointer = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base(); 121 if self.explicit == False: 122 # untested, use the safe code path 123 return self.handle_unicode_string_safe(); 124 else: 125 # a full pointer is skipped here before getting to the live data 126 pointer = pointer + self.pointer_size 127 else: 128 pointer = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base() 129 # read 8 bytes here and make an address out of them 130 try: 131 char_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType() 132 vopointer = self.valobj.CreateValueFromAddress("dummy",pointer,char_type); 133 pointer = vopointer.GetValueAsUnsigned(0) 134 except: 135 return self.valobj.CreateValueFromExpression("content", 136 '(char*)"@\"invalid NSString\""') 137 # step 2: read Unicode data at pointer 138 pystr = self.read_unicode(pointer) 139 # step 3: return it 140 return pystr.encode('utf-8') 141 142 def handle_inline_explicit(self): 143 logger = lldb.formatters.Logger.Logger() 144 offset = 3*self.pointer_size 145 offset = offset + self.valobj.GetValueAsUnsigned(0) 146 return self.valobj.CreateValueFromExpression("content", 147 "(char*)(" + str(offset) + ")") 148 149 def handle_mutable_string(self): 150 logger = lldb.formatters.Logger.Logger() 151 offset = 2 * self.pointer_size 152 data = self.valobj.CreateChildAtOffset("content", 153 offset, self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType()); 154 data_value = data.GetValueAsUnsigned(0) 155 if self.explicit and self.unicode: 156 return self.read_unicode(data_value).encode('utf-8') 157 else: 158 data_value = data_value + 1 159 return self.valobj.CreateValueFromExpression("content", "(char*)(" + str(data_value) + ")") 160 161 def handle_UTF8_inline(self): 162 logger = lldb.formatters.Logger.Logger() 163 offset = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base(); 164 if self.explicit == False: 165 offset = offset + 1; 166 return self.valobj.CreateValueFromAddress("content", 167 offset, self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar)).AddressOf(); 168 169 def handle_UTF8_not_inline(self): 170 logger = lldb.formatters.Logger.Logger() 171 offset = self.size_of_cfruntime_base(); 172 return self.valobj.CreateChildAtOffset("content", 173 offset,self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType()); 174 175 def get_child_at_index(self,index): 176 logger = lldb.formatters.Logger.Logger() 177 logger >> "Querying for child [" + str(index) + "]" 178 if index == 0: 179 return self.valobj.CreateValueFromExpression("mutable", 180 str(int(self.mutable))); 181 if index == 1: 182 return self.valobj.CreateValueFromExpression("inline", 183 str(int(self.inline))); 184 if index == 2: 185 return self.valobj.CreateValueFromExpression("explicit", 186 str(int(self.explicit))); 187 if index == 3: 188 return self.valobj.CreateValueFromExpression("unicode", 189 str(int(self.unicode))); 190 if index == 4: 191 return self.valobj.CreateValueFromExpression("special", 192 str(int(self.special))); 193 if index == 5: 194 # we are handling the several possible combinations of flags. 195 # for each known combination we have a function that knows how to 196 # go fetch the data from memory instead of running code. if a string is not 197 # correctly displayed, one should start by finding a combination of flags that 198 # makes it different from these known cases, and provide a new reader function 199 # if this is not possible, a new flag might have to be made up (like the "special" flag 200 # below, which is not a real flag in CFString), or alternatively one might need to use 201 # the ObjC runtime helper to detect the new class and deal with it accordingly 202 #print 'mutable = ' + str(self.mutable) 203 #print 'inline = ' + str(self.inline) 204 #print 'explicit = ' + str(self.explicit) 205 #print 'unicode = ' + str(self.unicode) 206 #print 'special = ' + str(self.special) 207 if self.mutable == True: 208 return self.handle_mutable_string() 209 elif self.inline == True and self.explicit == True and \ 210 self.unicode == False and self.special == False and \ 211 self.mutable == False: 212 return self.handle_inline_explicit() 213 elif self.unicode == True: 214 return self.handle_unicode_string(); 215 elif self.special == True: 216 return self.handle_special(); 217 elif self.inline == True: 218 return self.handle_UTF8_inline(); 219 else: 220 return self.handle_UTF8_not_inline(); 221 222 def get_child_index(self,name): 223 logger = lldb.formatters.Logger.Logger() 224 logger >> "Querying for child ['" + str(name) + "']" 225 if name == "content": 226 return self.num_children() - 1; 227 if name == "mutable": 228 return 0; 229 if name == "inline": 230 return 1; 231 if name == "explicit": 232 return 2; 233 if name == "unicode": 234 return 3; 235 if name == "special": 236 return 4; 237 238 # CFRuntimeBase is defined as having an additional 239 # 4 bytes (padding?) on LP64 architectures 240 # to get its size we add up sizeof(pointer)+4 241 # and then add 4 more bytes if we are on a 64bit system 242 def size_of_cfruntime_base(self): 243 logger = lldb.formatters.Logger.Logger() 244 return self.pointer_size+4+(4 if self.is_64_bit else 0) 245 246 # the info bits are part of the CFRuntimeBase structure 247 # to get at them we have to skip a uintptr_t and then get 248 # at the least-significant byte of a 4 byte array. If we are 249 # on big-endian this means going to byte 3, if we are on 250 # little endian (OSX & iOS), this means reading byte 0 251 def offset_of_info_bits(self): 252 logger = lldb.formatters.Logger.Logger() 253 offset = self.pointer_size 254 if self.is_little == False: 255 offset = offset + 3; 256 return offset; 257 258 def read_info_bits(self): 259 logger = lldb.formatters.Logger.Logger() 260 cfinfo = self.valobj.CreateChildAtOffset("cfinfo", 261 self.offset_of_info_bits(), 262 self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar)); 263 cfinfo.SetFormat(11) 264 info = cfinfo.GetValue(); 265 if info != None: 266 self.invalid = False; 267 return int(info,0); 268 else: 269 self.invalid = True; 270 return None; 271 272 # calculating internal flag bits of the CFString object 273 # this stuff is defined and discussed in CFString.c 274 def is_mutable(self): 275 logger = lldb.formatters.Logger.Logger() 276 return (self.info_bits & 1) == 1; 277 278 def is_inline(self): 279 logger = lldb.formatters.Logger.Logger() 280 return (self.info_bits & 0x60) == 0; 281 282 # this flag's name is ambiguous, it turns out 283 # we must skip a length byte to get at the data 284 # when this flag is False 285 def has_explicit_length(self): 286 logger = lldb.formatters.Logger.Logger() 287 return (self.info_bits & (1 | 4)) != 4; 288 289 # probably a subclass of NSString. obtained this from [str pathExtension] 290 # here info_bits = 0 and Unicode data at the start of the padding word 291 # in the long run using the isa value might be safer as a way to identify this 292 # instead of reading the info_bits 293 def is_special_case(self): 294 logger = lldb.formatters.Logger.Logger() 295 return self.info_bits == 0; 296 297 def is_unicode(self): 298 logger = lldb.formatters.Logger.Logger() 299 return (self.info_bits & 0x10) == 0x10; 300 301 # preparing ourselves to read into memory 302 # by adjusting architecture-specific info 303 def adjust_for_architecture(self): 304 logger = lldb.formatters.Logger.Logger() 305 self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() 306 self.is_64_bit = self.pointer_size == 8 307 self.is_little = self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle 308 309 # reading info bits out of the CFString and computing 310 # useful values to get at the real data 311 def compute_flags(self): 312 logger = lldb.formatters.Logger.Logger() 313 self.info_bits = self.read_info_bits(); 314 if self.info_bits == None: 315 return; 316 self.mutable = self.is_mutable(); 317 self.inline = self.is_inline(); 318 self.explicit = self.has_explicit_length(); 319 self.unicode = self.is_unicode(); 320 self.special = self.is_special_case(); 321 322 def update(self): 323 logger = lldb.formatters.Logger.Logger() 324 self.adjust_for_architecture(); 325 self.compute_flags(); 326