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