blob: 14ef1b42bf130a0f9e71ebc6e19d73c0b2797295 [file] [log] [blame]
Enrico Granatacfdafa32012-03-05 19:56:33 +00001"""
2Objective-C runtime wrapper for use by LLDB Python formatters
3
4part of The LLVM Compiler Infrastructure
5This file is distributed under the University of Illinois Open Source
6License. See LICENSE.TXT for details.
7"""
Enrico Granataeb4a4792012-02-23 23:10:27 +00008import lldb
Enrico Granata28399ad2012-04-25 01:39:27 +00009import lldb.formatters.cache
10import lldb.formatters.attrib_fromdict
Enrico Granatacfdafa32012-03-05 19:56:33 +000011import functools
Enrico Granata28399ad2012-04-25 01:39:27 +000012import lldb.formatters.Logger
Enrico Granataeb4a4792012-02-23 23:10:27 +000013
Kate Stoneb9c1b512016-09-06 20:57:50 +000014
Enrico Granataeb4a4792012-02-23 23:10:27 +000015class Utilities:
Enrico Granataeb4a4792012-02-23 23:10:27 +000016
Kate Stoneb9c1b512016-09-06 20:57:50 +000017 @staticmethod
18 def read_ascii(process, pointer, max_len=128):
19 logger = lldb.formatters.Logger.Logger()
20 error = lldb.SBError()
21 content = None
22 try:
23 content = process.ReadCStringFromMemory(pointer, max_len, error)
24 except:
25 pass
26 if content is None or len(content) == 0 or error.fail:
27 return None
28 return content
Enrico Granataeb4a4792012-02-23 23:10:27 +000029
Kate Stoneb9c1b512016-09-06 20:57:50 +000030 @staticmethod
31 def is_valid_pointer(pointer, pointer_size, allow_tagged=0, allow_NULL=0):
32 logger = lldb.formatters.Logger.Logger()
33 if pointer is None:
34 return 0
35 if pointer == 0:
36 return allow_NULL
37 if allow_tagged and (pointer % 2) == 1:
38 return 1
39 return ((pointer % pointer_size) == 0)
Enrico Granataeb4a4792012-02-23 23:10:27 +000040
Kate Stoneb9c1b512016-09-06 20:57:50 +000041 # Objective-C runtime has a rule that pointers in a class_t will only have bits 0 thru 46 set
42 # so if any pointer has bits 47 thru 63 high we know that this is not a
43 # valid isa
44 @staticmethod
45 def is_allowed_pointer(pointer):
46 logger = lldb.formatters.Logger.Logger()
47 if pointer is None:
48 return 0
49 return ((pointer & 0xFFFF800000000000) == 0)
Enrico Granataeb4a4792012-02-23 23:10:27 +000050
Kate Stoneb9c1b512016-09-06 20:57:50 +000051 @staticmethod
52 def read_child_of(valobj, offset, type):
53 logger = lldb.formatters.Logger.Logger()
54 if offset == 0 and type.GetByteSize() == valobj.GetByteSize():
55 return valobj.GetValueAsUnsigned()
56 child = valobj.CreateChildAtOffset("childUNK", offset, type)
57 if child is None or child.IsValid() == 0:
58 return None
59 return child.GetValueAsUnsigned()
Enrico Granata7bc0ec32012-02-29 03:28:49 +000060
Kate Stoneb9c1b512016-09-06 20:57:50 +000061 @staticmethod
62 def is_valid_identifier(name):
63 logger = lldb.formatters.Logger.Logger()
64 if name is None:
65 return None
66 if len(name) == 0:
67 return None
68 # technically, the ObjC runtime does not enforce any rules about what name a class can have
69 # in practice, the commonly used byte values for a class name are the letters, digits and some
70 # symbols: $, %, -, _, .
71 # WARNING: this means that you cannot use this runtime implementation if you need to deal
72 # with class names that use anything but what is allowed here
73 ok_values = dict.fromkeys(
74 "$%_.-ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890")
75 return all(c in ok_values for c in name)
Enrico Granataeb4a4792012-02-23 23:10:27 +000076
Kate Stoneb9c1b512016-09-06 20:57:50 +000077 @staticmethod
78 def check_is_osx_lion(target):
79 logger = lldb.formatters.Logger.Logger()
80 # assume the only thing that has a Foundation.framework is a Mac
81 # assume anything < Lion does not even exist
82 try:
83 mod = target.module['Foundation']
84 except:
85 mod = None
86 if mod is None or mod.IsValid() == 0:
87 return None
88 ver = mod.GetVersion()
89 if ver is None or ver == []:
90 return None
91 return (ver[0] < 900)
92
93 # a utility method that factors out code common to almost all the formatters
94 # takes in an SBValue and a metrics object
95 # returns a class_data and a wrapper (or None, if the runtime alone can't
96 # decide on a wrapper)
97 @staticmethod
98 def prepare_class_detection(valobj, statistics):
99 logger = lldb.formatters.Logger.Logger()
100 class_data = ObjCRuntime(valobj)
101 if class_data.is_valid() == 0:
102 statistics.metric_hit('invalid_pointer', valobj)
103 wrapper = InvalidPointer_Description(
104 valobj.GetValueAsUnsigned(0) == 0)
105 return class_data, wrapper
106 class_data = class_data.read_class_data()
107 if class_data.is_valid() == 0:
108 statistics.metric_hit('invalid_isa', valobj)
109 wrapper = InvalidISA_Description()
110 return class_data, wrapper
111 if class_data.is_kvo():
112 class_data = class_data.get_superclass()
113 if class_data.class_name() == '_NSZombie_OriginalClass':
114 wrapper = ThisIsZombie_Description()
115 return class_data, wrapper
116 return class_data, None
Enrico Granata3f1052b2012-03-13 21:52:00 +0000117
118
Enrico Granataeb4a4792012-02-23 23:10:27 +0000119class RoT_Data:
Enrico Granataeb4a4792012-02-23 23:10:27 +0000120
Kate Stoneb9c1b512016-09-06 20:57:50 +0000121 def __init__(self, rot_pointer, params):
122 logger = lldb.formatters.Logger.Logger()
123 if (Utilities.is_valid_pointer(rot_pointer.GetValueAsUnsigned(),
124 params.pointer_size, allow_tagged=0)):
125 self.sys_params = params
126 self.valobj = rot_pointer
127 #self.flags = Utilities.read_child_of(self.valobj,0,self.sys_params.uint32_t)
128 #self.instanceStart = Utilities.read_child_of(self.valobj,4,self.sys_params.uint32_t)
129 self.instanceSize = None # lazy fetching
130 offset = 24 if self.sys_params.is_64_bit else 16
131 #self.ivarLayoutPtr = Utilities.read_child_of(self.valobj,offset,self.sys_params.addr_ptr_type)
132 self.namePointer = Utilities.read_child_of(
133 self.valobj, offset, self.sys_params.types_cache.addr_ptr_type)
134 self.valid = 1 # self.check_valid()
135 else:
136 logger >> "Marking as invalid - rot is invalid"
137 self.valid = 0
138 if self.valid:
139 self.name = Utilities.read_ascii(
140 self.valobj.GetTarget().GetProcess(), self.namePointer)
141 if not(Utilities.is_valid_identifier(self.name)):
142 logger >> "Marking as invalid - name is invalid"
143 self.valid = 0
Enrico Granataeb4a4792012-02-23 23:10:27 +0000144
Kate Stoneb9c1b512016-09-06 20:57:50 +0000145 # perform sanity checks on the contents of this class_ro_t
146 def check_valid(self):
147 self.valid = 1
148 # misaligned pointers seem to be possible for this field
149 # if not(Utilities.is_valid_pointer(self.namePointer,self.sys_params.pointer_size,allow_tagged=0)):
150 # self.valid = 0
151 # pass
Enrico Granataeb4a4792012-02-23 23:10:27 +0000152
Kate Stoneb9c1b512016-09-06 20:57:50 +0000153 def __str__(self):
154 logger = lldb.formatters.Logger.Logger()
155 return \
156 "instanceSize = " + hex(self.instance_size()) + "\n" + \
157 "namePointer = " + hex(self.namePointer) + " --> " + self.name
Enrico Granataeb4a4792012-02-23 23:10:27 +0000158
Kate Stoneb9c1b512016-09-06 20:57:50 +0000159 def is_valid(self):
160 return self.valid
161
162 def instance_size(self, align=0):
163 logger = lldb.formatters.Logger.Logger()
164 if self.is_valid() == 0:
165 return None
166 if self.instanceSize is None:
167 self.instanceSize = Utilities.read_child_of(
168 self.valobj, 8, self.sys_params.types_cache.uint32_t)
169 if align:
170 unalign = self.instance_size(0)
171 if self.sys_params.is_64_bit:
172 return ((unalign + 7) & ~7) % 0x100000000
173 else:
174 return ((unalign + 3) & ~3) % 0x100000000
175 else:
176 return self.instanceSize
177
Enrico Granataeb4a4792012-02-23 23:10:27 +0000178
179class RwT_Data:
Kate Stoneb9c1b512016-09-06 20:57:50 +0000180
181 def __init__(self, rwt_pointer, params):
182 logger = lldb.formatters.Logger.Logger()
183 if (Utilities.is_valid_pointer(rwt_pointer.GetValueAsUnsigned(),
184 params.pointer_size, allow_tagged=0)):
185 self.sys_params = params
186 self.valobj = rwt_pointer
187 #self.flags = Utilities.read_child_of(self.valobj,0,self.sys_params.uint32_t)
188 #self.version = Utilities.read_child_of(self.valobj,4,self.sys_params.uint32_t)
189 self.roPointer = Utilities.read_child_of(
190 self.valobj, 8, self.sys_params.types_cache.addr_ptr_type)
191 self.check_valid()
192 else:
193 logger >> "Marking as invalid - rwt is invald"
194 self.valid = 0
195 if self.valid:
196 self.rot = self.valobj.CreateValueFromData(
197 "rot", lldb.SBData.CreateDataFromUInt64Array(
198 self.sys_params.endianness, self.sys_params.pointer_size, [
199 self.roPointer]), self.sys_params.types_cache.addr_ptr_type)
Enrico Granata263ebe52012-05-18 19:55:37 +0000200# self.rot = self.valobj.CreateValueFromAddress("rot",self.roPointer,self.sys_params.types_cache.addr_ptr_type).AddressOf()
Kate Stoneb9c1b512016-09-06 20:57:50 +0000201 self.data = RoT_Data(self.rot, self.sys_params)
Enrico Granataeb4a4792012-02-23 23:10:27 +0000202
Kate Stoneb9c1b512016-09-06 20:57:50 +0000203 # perform sanity checks on the contents of this class_rw_t
204 def check_valid(self):
205 logger = lldb.formatters.Logger.Logger()
206 self.valid = 1
207 if not(
208 Utilities.is_valid_pointer(
209 self.roPointer,
210 self.sys_params.pointer_size,
211 allow_tagged=0)):
212 logger >> "Marking as invalid - ropointer is invalid"
213 self.valid = 0
Enrico Granataeb4a4792012-02-23 23:10:27 +0000214
Kate Stoneb9c1b512016-09-06 20:57:50 +0000215 def __str__(self):
216 logger = lldb.formatters.Logger.Logger()
217 return \
218 "roPointer = " + hex(self.roPointer)
Enrico Granataeb4a4792012-02-23 23:10:27 +0000219
Kate Stoneb9c1b512016-09-06 20:57:50 +0000220 def is_valid(self):
221 logger = lldb.formatters.Logger.Logger()
222 if self.valid:
223 return self.data.is_valid()
224 return 0
225
Enrico Granataeb4a4792012-02-23 23:10:27 +0000226
227class Class_Data_V2:
Kate Stoneb9c1b512016-09-06 20:57:50 +0000228
229 def __init__(self, isa_pointer, params):
230 logger = lldb.formatters.Logger.Logger()
231 if (isa_pointer is not None) and (Utilities.is_valid_pointer(
232 isa_pointer.GetValueAsUnsigned(), params.pointer_size, allow_tagged=0)):
233 self.sys_params = params
234 self.valobj = isa_pointer
235 self.check_valid()
236 else:
237 logger >> "Marking as invalid - isa is invalid or None"
238 self.valid = 0
239 if self.valid:
240 self.rwt = self.valobj.CreateValueFromData(
241 "rwt", lldb.SBData.CreateDataFromUInt64Array(
242 self.sys_params.endianness, self.sys_params.pointer_size, [
243 self.dataPointer]), self.sys_params.types_cache.addr_ptr_type)
Enrico Granata263ebe52012-05-18 19:55:37 +0000244# self.rwt = self.valobj.CreateValueFromAddress("rwt",self.dataPointer,self.sys_params.types_cache.addr_ptr_type).AddressOf()
Kate Stoneb9c1b512016-09-06 20:57:50 +0000245 self.data = RwT_Data(self.rwt, self.sys_params)
Enrico Granataeb4a4792012-02-23 23:10:27 +0000246
Kate Stoneb9c1b512016-09-06 20:57:50 +0000247 # perform sanity checks on the contents of this class_t
248 # this call tries to minimize the amount of data fetched- as soon as we have "proven"
249 # that we have an invalid object, we stop reading
250 def check_valid(self):
251 logger = lldb.formatters.Logger.Logger()
252 self.valid = 1
Filipe Cabecinhasbbb648e2012-08-01 14:38:37 +0000253
Kate Stoneb9c1b512016-09-06 20:57:50 +0000254 self.isaPointer = Utilities.read_child_of(
255 self.valobj, 0, self.sys_params.types_cache.addr_ptr_type)
256 if not(
257 Utilities.is_valid_pointer(
258 self.isaPointer,
259 self.sys_params.pointer_size,
260 allow_tagged=0)):
261 logger >> "Marking as invalid - isaPointer is invalid"
262 self.valid = 0
263 return
264 if not(Utilities.is_allowed_pointer(self.isaPointer)):
265 logger >> "Marking as invalid - isaPointer is not allowed"
266 self.valid = 0
267 return
Enrico Granatacfdafa32012-03-05 19:56:33 +0000268
Kate Stoneb9c1b512016-09-06 20:57:50 +0000269 self.cachePointer = Utilities.read_child_of(
270 self.valobj,
271 2 * self.sys_params.pointer_size,
272 self.sys_params.types_cache.addr_ptr_type)
273 if not(
274 Utilities.is_valid_pointer(
275 self.cachePointer,
276 self.sys_params.pointer_size,
277 allow_tagged=0)):
278 logger >> "Marking as invalid - cachePointer is invalid"
279 self.valid = 0
280 return
281 if not(Utilities.is_allowed_pointer(self.cachePointer)):
282 logger >> "Marking as invalid - cachePointer is not allowed"
283 self.valid = 0
284 return
285 self.dataPointer = Utilities.read_child_of(
286 self.valobj,
287 4 * self.sys_params.pointer_size,
288 self.sys_params.types_cache.addr_ptr_type)
289 if not(
290 Utilities.is_valid_pointer(
291 self.dataPointer,
292 self.sys_params.pointer_size,
293 allow_tagged=0)):
294 logger >> "Marking as invalid - dataPointer is invalid"
295 self.valid = 0
296 return
297 if not(Utilities.is_allowed_pointer(self.dataPointer)):
298 logger >> "Marking as invalid - dataPointer is not allowed"
299 self.valid = 0
300 return
Enrico Granataeb4a4792012-02-23 23:10:27 +0000301
Kate Stoneb9c1b512016-09-06 20:57:50 +0000302 self.superclassIsaPointer = Utilities.read_child_of(
303 self.valobj,
304 1 * self.sys_params.pointer_size,
305 self.sys_params.types_cache.addr_ptr_type)
306 if not(
307 Utilities.is_valid_pointer(
308 self.superclassIsaPointer,
309 self.sys_params.pointer_size,
310 allow_tagged=0,
311 allow_NULL=1)):
312 logger >> "Marking as invalid - superclassIsa is invalid"
313 self.valid = 0
314 return
315 if not(Utilities.is_allowed_pointer(self.superclassIsaPointer)):
316 logger >> "Marking as invalid - superclassIsa is not allowed"
317 self.valid = 0
318 return
Enrico Granatacfdafa32012-03-05 19:56:33 +0000319
Kate Stoneb9c1b512016-09-06 20:57:50 +0000320 # in general, KVO is implemented by transparently subclassing
321 # however, there could be exceptions where a class does something else
322 # internally to implement the feature - this method will have no clue that a class
323 # has been KVO'ed unless the standard implementation technique is used
324 def is_kvo(self):
325 logger = lldb.formatters.Logger.Logger()
326 if self.is_valid():
327 if self.class_name().startswith("NSKVONotifying_"):
328 return 1
329 return 0
Enrico Granataeb4a4792012-02-23 23:10:27 +0000330
Kate Stoneb9c1b512016-09-06 20:57:50 +0000331 # some CF classes have a valid ObjC isa in their CFRuntimeBase
332 # but instead of being class-specific this isa points to a match-'em-all class
333 # which is __NSCFType (the versions without __ also exists and we are matching to it
334 # just to be on the safe side)
335 def is_cftype(self):
336 logger = lldb.formatters.Logger.Logger()
337 if self.is_valid():
338 return self.class_name() == '__NSCFType' or self.class_name() == 'NSCFType'
Enrico Granatacfdafa32012-03-05 19:56:33 +0000339
Kate Stoneb9c1b512016-09-06 20:57:50 +0000340 def get_superclass(self):
341 logger = lldb.formatters.Logger.Logger()
342 if self.is_valid():
343 parent_isa_pointer = self.valobj.CreateChildAtOffset(
344 "parent_isa", self.sys_params.pointer_size, self.sys_params.addr_ptr_type)
345 return Class_Data_V2(parent_isa_pointer, self.sys_params)
346 else:
347 return None
Enrico Granataeb4a4792012-02-23 23:10:27 +0000348
Kate Stoneb9c1b512016-09-06 20:57:50 +0000349 def class_name(self):
350 logger = lldb.formatters.Logger.Logger()
351 if self.is_valid():
352 return self.data.data.name
353 else:
354 return None
Enrico Granataeb4a4792012-02-23 23:10:27 +0000355
Kate Stoneb9c1b512016-09-06 20:57:50 +0000356 def is_valid(self):
357 logger = lldb.formatters.Logger.Logger()
358 if self.valid:
359 return self.data.is_valid()
360 return 0
Enrico Granataeb4a4792012-02-23 23:10:27 +0000361
Kate Stoneb9c1b512016-09-06 20:57:50 +0000362 def __str__(self):
363 logger = lldb.formatters.Logger.Logger()
364 return 'isaPointer = ' + hex(self.isaPointer) + "\n" + \
365 "superclassIsaPointer = " + hex(self.superclassIsaPointer) + "\n" + \
366 "cachePointer = " + hex(self.cachePointer) + "\n" + \
367 "data = " + hex(self.dataPointer)
Enrico Granataeb4a4792012-02-23 23:10:27 +0000368
Kate Stoneb9c1b512016-09-06 20:57:50 +0000369 def is_tagged(self):
370 return 0
Enrico Granataeb4a4792012-02-23 23:10:27 +0000371
Kate Stoneb9c1b512016-09-06 20:57:50 +0000372 def instance_size(self, align=0):
373 logger = lldb.formatters.Logger.Logger()
374 if self.is_valid() == 0:
375 return None
376 return self.rwt.rot.instance_size(align)
Enrico Granatab8cbe9c2012-02-23 23:26:48 +0000377
Kate Stoneb9c1b512016-09-06 20:57:50 +0000378# runtime v1 is much less intricate than v2 and stores relevant
379# information directly in the class_t object
380
381
Enrico Granataeb4a4792012-02-23 23:10:27 +0000382class Class_Data_V1:
Enrico Granataeb4a4792012-02-23 23:10:27 +0000383
Kate Stoneb9c1b512016-09-06 20:57:50 +0000384 def __init__(self, isa_pointer, params):
385 logger = lldb.formatters.Logger.Logger()
386 if (isa_pointer is not None) and (Utilities.is_valid_pointer(
387 isa_pointer.GetValueAsUnsigned(), params.pointer_size, allow_tagged=0)):
388 self.valid = 1
389 self.sys_params = params
390 self.valobj = isa_pointer
391 self.check_valid()
392 else:
393 logger >> "Marking as invalid - isaPointer is invalid or None"
394 self.valid = 0
395 if self.valid:
396 self.name = Utilities.read_ascii(
397 self.valobj.GetTarget().GetProcess(), self.namePointer)
398 if not(Utilities.is_valid_identifier(self.name)):
399 logger >> "Marking as invalid - name is not valid"
400 self.valid = 0
Enrico Granatacfdafa32012-03-05 19:56:33 +0000401
Kate Stoneb9c1b512016-09-06 20:57:50 +0000402 # perform sanity checks on the contents of this class_t
403 def check_valid(self):
404 logger = lldb.formatters.Logger.Logger()
405 self.valid = 1
Enrico Granatacfdafa32012-03-05 19:56:33 +0000406
Kate Stoneb9c1b512016-09-06 20:57:50 +0000407 self.isaPointer = Utilities.read_child_of(
408 self.valobj, 0, self.sys_params.types_cache.addr_ptr_type)
409 if not(
410 Utilities.is_valid_pointer(
411 self.isaPointer,
412 self.sys_params.pointer_size,
413 allow_tagged=0)):
414 logger >> "Marking as invalid - isaPointer is invalid"
415 self.valid = 0
416 return
Enrico Granataeb4a4792012-02-23 23:10:27 +0000417
Kate Stoneb9c1b512016-09-06 20:57:50 +0000418 self.superclassIsaPointer = Utilities.read_child_of(
419 self.valobj,
420 1 * self.sys_params.pointer_size,
421 self.sys_params.types_cache.addr_ptr_type)
422 if not(
423 Utilities.is_valid_pointer(
424 self.superclassIsaPointer,
425 self.sys_params.pointer_size,
426 allow_tagged=0,
427 allow_NULL=1)):
428 logger >> "Marking as invalid - superclassIsa is invalid"
429 self.valid = 0
430 return
Enrico Granatacfdafa32012-03-05 19:56:33 +0000431
Kate Stoneb9c1b512016-09-06 20:57:50 +0000432 self.namePointer = Utilities.read_child_of(
433 self.valobj,
434 2 * self.sys_params.pointer_size,
435 self.sys_params.types_cache.addr_ptr_type)
436 # if not(Utilities.is_valid_pointer(self.namePointer,self.sys_params.pointer_size,allow_tagged=0,allow_NULL=0)):
437 # self.valid = 0
438 # return
Enrico Granataeb4a4792012-02-23 23:10:27 +0000439
Kate Stoneb9c1b512016-09-06 20:57:50 +0000440 # in general, KVO is implemented by transparently subclassing
441 # however, there could be exceptions where a class does something else
442 # internally to implement the feature - this method will have no clue that a class
443 # has been KVO'ed unless the standard implementation technique is used
444 def is_kvo(self):
445 logger = lldb.formatters.Logger.Logger()
446 if self.is_valid():
447 if self.class_name().startswith("NSKVONotifying_"):
448 return 1
449 return 0
Enrico Granatacfdafa32012-03-05 19:56:33 +0000450
Kate Stoneb9c1b512016-09-06 20:57:50 +0000451 # some CF classes have a valid ObjC isa in their CFRuntimeBase
452 # but instead of being class-specific this isa points to a match-'em-all class
453 # which is __NSCFType (the versions without __ also exists and we are matching to it
454 # just to be on the safe side)
455 def is_cftype(self):
456 logger = lldb.formatters.Logger.Logger()
457 if self.is_valid():
458 return self.class_name() == '__NSCFType' or self.class_name() == 'NSCFType'
Enrico Granataeb4a4792012-02-23 23:10:27 +0000459
Kate Stoneb9c1b512016-09-06 20:57:50 +0000460 def get_superclass(self):
461 logger = lldb.formatters.Logger.Logger()
462 if self.is_valid():
463 parent_isa_pointer = self.valobj.CreateChildAtOffset(
464 "parent_isa", self.sys_params.pointer_size, self.sys_params.addr_ptr_type)
465 return Class_Data_V1(parent_isa_pointer, self.sys_params)
466 else:
467 return None
Enrico Granataeb4a4792012-02-23 23:10:27 +0000468
Kate Stoneb9c1b512016-09-06 20:57:50 +0000469 def class_name(self):
470 logger = lldb.formatters.Logger.Logger()
471 if self.is_valid():
472 return self.name
473 else:
474 return None
Enrico Granataeb4a4792012-02-23 23:10:27 +0000475
Kate Stoneb9c1b512016-09-06 20:57:50 +0000476 def is_valid(self):
477 return self.valid
Enrico Granataeb4a4792012-02-23 23:10:27 +0000478
Kate Stoneb9c1b512016-09-06 20:57:50 +0000479 def __str__(self):
480 logger = lldb.formatters.Logger.Logger()
481 return 'isaPointer = ' + hex(self.isaPointer) + "\n" + \
482 "superclassIsaPointer = " + hex(self.superclassIsaPointer) + "\n" + \
483 "namePointer = " + hex(self.namePointer) + " --> " + self.name + \
484 "instanceSize = " + hex(self.instanceSize()) + "\n"
Enrico Granataeb4a4792012-02-23 23:10:27 +0000485
Kate Stoneb9c1b512016-09-06 20:57:50 +0000486 def is_tagged(self):
487 return 0
488
489 def instance_size(self, align=0):
490 logger = lldb.formatters.Logger.Logger()
491 if self.is_valid() == 0:
492 return None
493 if self.instanceSize is None:
494 self.instanceSize = Utilities.read_child_of(
495 self.valobj,
496 5 * self.sys_params.pointer_size,
497 self.sys_params.types_cache.addr_ptr_type)
498 if align:
499 unalign = self.instance_size(0)
500 if self.sys_params.is_64_bit:
501 return ((unalign + 7) & ~7) % 0x100000000
502 else:
503 return ((unalign + 3) & ~3) % 0x100000000
504 else:
505 return self.instanceSize
Enrico Granatab8cbe9c2012-02-23 23:26:48 +0000506
Enrico Granata0448d9f2012-02-29 20:46:00 +0000507# these are the only tagged pointers values for current versions
Enrico Granata0c489f52012-03-01 04:24:26 +0000508# of OSX - they might change in future OS releases, and no-one is
Enrico Granata0448d9f2012-02-29 20:46:00 +0000509# advised to rely on these values, or any of the bitmasking formulas
510# in TaggedClass_Data. doing otherwise is at your own risk
Kate Stoneb9c1b512016-09-06 20:57:50 +0000511TaggedClass_Values_Lion = {1: 'NSNumber',
512 5: 'NSManagedObject',
513 6: 'NSDate',
514 7: 'NSDateTS'}
515TaggedClass_Values_NMOS = {0: 'NSAtom',
516 3: 'NSNumber',
517 4: 'NSDateTS',
518 5: 'NSManagedObject',
519 6: 'NSDate'}
520
Enrico Granata7bc0ec32012-02-29 03:28:49 +0000521
Enrico Granataeb4a4792012-02-23 23:10:27 +0000522class TaggedClass_Data:
Filipe Cabecinhasbbb648e2012-08-01 14:38:37 +0000523
Kate Stoneb9c1b512016-09-06 20:57:50 +0000524 def __init__(self, pointer, params):
525 logger = lldb.formatters.Logger.Logger()
526 global TaggedClass_Values_Lion, TaggedClass_Values_NMOS
527 self.valid = 1
528 self.name = None
529 self.sys_params = params
530 self.valobj = pointer
531 self.val = (pointer & ~0x0000000000000000FF) >> 8
532 self.class_bits = (pointer & 0xE) >> 1
533 self.i_bits = (pointer & 0xF0) >> 4
Enrico Granata7bc0ec32012-02-29 03:28:49 +0000534
Kate Stoneb9c1b512016-09-06 20:57:50 +0000535 if self.sys_params.is_lion:
536 if self.class_bits in TaggedClass_Values_Lion:
537 self.name = TaggedClass_Values_Lion[self.class_bits]
538 else:
539 logger >> "Marking as invalid - not a good tagged pointer for Lion"
540 self.valid = 0
541 else:
542 if self.class_bits in TaggedClass_Values_NMOS:
543 self.name = TaggedClass_Values_NMOS[self.class_bits]
544 else:
545 logger >> "Marking as invalid - not a good tagged pointer for NMOS"
546 self.valid = 0
Enrico Granataeb4a4792012-02-23 23:10:27 +0000547
Kate Stoneb9c1b512016-09-06 20:57:50 +0000548 def is_valid(self):
549 return self.valid
Enrico Granataeb4a4792012-02-23 23:10:27 +0000550
Kate Stoneb9c1b512016-09-06 20:57:50 +0000551 def class_name(self):
552 logger = lldb.formatters.Logger.Logger()
553 if self.is_valid():
554 return self.name
555 else:
556 return 0
Enrico Granataeb4a4792012-02-23 23:10:27 +0000557
Kate Stoneb9c1b512016-09-06 20:57:50 +0000558 def value(self):
559 return self.val if self.is_valid() else None
Enrico Granataeb4a4792012-02-23 23:10:27 +0000560
Kate Stoneb9c1b512016-09-06 20:57:50 +0000561 def info_bits(self):
562 return self.i_bits if self.is_valid() else None
Enrico Granataeb4a4792012-02-23 23:10:27 +0000563
Kate Stoneb9c1b512016-09-06 20:57:50 +0000564 def is_kvo(self):
565 return 0
Enrico Granataeb4a4792012-02-23 23:10:27 +0000566
Kate Stoneb9c1b512016-09-06 20:57:50 +0000567 def is_cftype(self):
568 return 0
Enrico Granatacfdafa32012-03-05 19:56:33 +0000569
Kate Stoneb9c1b512016-09-06 20:57:50 +0000570 # we would need to go around looking for the superclass or ask the runtime
571 # for now, we seem not to require support for this operation so we will merrily
572 # pretend to be at a root point in the hierarchy
573 def get_superclass(self):
574 return None
Enrico Granataeb4a4792012-02-23 23:10:27 +0000575
Kate Stoneb9c1b512016-09-06 20:57:50 +0000576 # anything that is handled here is tagged
577 def is_tagged(self):
578 return 1
Enrico Granataeb4a4792012-02-23 23:10:27 +0000579
Kate Stoneb9c1b512016-09-06 20:57:50 +0000580 # it seems reasonable to say that a tagged pointer is the size of a pointer
581 def instance_size(self, align=0):
582 logger = lldb.formatters.Logger.Logger()
583 if self.is_valid() == 0:
584 return None
585 return self.sys_params.pointer_size
Enrico Granatab8cbe9c2012-02-23 23:26:48 +0000586
587
Enrico Granataeb4a4792012-02-23 23:10:27 +0000588class InvalidClass_Data:
Kate Stoneb9c1b512016-09-06 20:57:50 +0000589
590 def __init__(self):
591 pass
592
593 def is_valid(self):
594 return 0
Enrico Granataeb4a4792012-02-23 23:10:27 +0000595
Filipe Cabecinhasbbb648e2012-08-01 14:38:37 +0000596
Enrico Granatacfdafa32012-03-05 19:56:33 +0000597class Version:
Enrico Granatacfdafa32012-03-05 19:56:33 +0000598
Kate Stoneb9c1b512016-09-06 20:57:50 +0000599 def __init__(self, major, minor, release, build_string):
600 self._major = major
601 self._minor = minor
602 self._release = release
603 self._build_string = build_string
Enrico Granatacfdafa32012-03-05 19:56:33 +0000604
Kate Stoneb9c1b512016-09-06 20:57:50 +0000605 def get_major(self):
606 return self._major
Enrico Granatacfdafa32012-03-05 19:56:33 +0000607
Kate Stoneb9c1b512016-09-06 20:57:50 +0000608 def get_minor(self):
609 return self._minor
Enrico Granatacfdafa32012-03-05 19:56:33 +0000610
Kate Stoneb9c1b512016-09-06 20:57:50 +0000611 def get_release(self):
612 return self._release
Enrico Granatacfdafa32012-03-05 19:56:33 +0000613
Kate Stoneb9c1b512016-09-06 20:57:50 +0000614 def get_build_string(self):
615 return self._build_string
Filipe Cabecinhasbbb648e2012-08-01 14:38:37 +0000616
Kate Stoneb9c1b512016-09-06 20:57:50 +0000617 major = property(get_major, None)
618 minor = property(get_minor, None)
619 release = property(get_release, None)
620 build_string = property(get_build_string, None)
Filipe Cabecinhasbbb648e2012-08-01 14:38:37 +0000621
Kate Stoneb9c1b512016-09-06 20:57:50 +0000622 def __lt__(self, other):
623 if (self.major < other.major):
624 return 1
625 if (self.minor < other.minor):
626 return 1
627 if (self.release < other.release):
628 return 1
629 # build strings are not compared since they are heavily platform-dependent and might not always
630 # be available
631 return 0
632
633 def __eq__(self, other):
634 return (self.major == other.major) and \
635 (self.minor == other.minor) and \
636 (self.release == other.release) and \
637 (self.build_string == other.build_string)
638
639 # Python 2.6 doesn't have functools.total_ordering, so we have to implement
640 # other comparators
641 def __gt__(self, other):
642 return other < self
643
644 def __le__(self, other):
645 return not other < self
646
647 def __ge__(self, other):
648 return not self < other
Filipe Cabecinhasbbb648e2012-08-01 14:38:37 +0000649
650
Enrico Granata28399ad2012-04-25 01:39:27 +0000651runtime_version = lldb.formatters.cache.Cache()
652os_version = lldb.formatters.cache.Cache()
653types_caches = lldb.formatters.cache.Cache()
654isa_caches = lldb.formatters.cache.Cache()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000655
Kate Stoneb9c1b512016-09-06 20:57:50 +0000656
Enrico Granataeb4a4792012-02-23 23:10:27 +0000657class SystemParameters:
Enrico Granataeb4a4792012-02-23 23:10:27 +0000658
Kate Stoneb9c1b512016-09-06 20:57:50 +0000659 def __init__(self, valobj):
660 logger = lldb.formatters.Logger.Logger()
661 self.adjust_for_architecture(valobj)
662 self.adjust_for_process(valobj)
Filipe Cabecinhasbbb648e2012-08-01 14:38:37 +0000663
Kate Stoneb9c1b512016-09-06 20:57:50 +0000664 def adjust_for_process(self, valobj):
665 logger = lldb.formatters.Logger.Logger()
666 global runtime_version
667 global os_version
668 global types_caches
669 global isa_caches
Filipe Cabecinhasbbb648e2012-08-01 14:38:37 +0000670
Kate Stoneb9c1b512016-09-06 20:57:50 +0000671 process = valobj.GetTarget().GetProcess()
672 # using the unique ID for added guarantees (see svn revision 172628 for
673 # further details)
674 self.pid = process.GetUniqueID()
Filipe Cabecinhasbbb648e2012-08-01 14:38:37 +0000675
Kate Stoneb9c1b512016-09-06 20:57:50 +0000676 if runtime_version.look_for_key(self.pid):
677 self.runtime_version = runtime_version.get_value(self.pid)
678 else:
679 self.runtime_version = ObjCRuntime.runtime_version(process)
680 runtime_version.add_item(self.pid, self.runtime_version)
Filipe Cabecinhasbbb648e2012-08-01 14:38:37 +0000681
Kate Stoneb9c1b512016-09-06 20:57:50 +0000682 if os_version.look_for_key(self.pid):
683 self.is_lion = os_version.get_value(self.pid)
684 else:
685 self.is_lion = Utilities.check_is_osx_lion(valobj.GetTarget())
686 os_version.add_item(self.pid, self.is_lion)
Filipe Cabecinhasbbb648e2012-08-01 14:38:37 +0000687
Kate Stoneb9c1b512016-09-06 20:57:50 +0000688 if types_caches.look_for_key(self.pid):
689 self.types_cache = types_caches.get_value(self.pid)
690 else:
691 self.types_cache = lldb.formatters.attrib_fromdict.AttributesDictionary(
692 allow_reset=0)
693 self.types_cache.addr_type = valobj.GetType(
694 ).GetBasicType(lldb.eBasicTypeUnsignedLong)
695 self.types_cache.addr_ptr_type = self.types_cache.addr_type.GetPointerType()
696 self.types_cache.uint32_t = valobj.GetType(
697 ).GetBasicType(lldb.eBasicTypeUnsignedInt)
698 types_caches.add_item(self.pid, self.types_cache)
Enrico Granatacfdafa32012-03-05 19:56:33 +0000699
Kate Stoneb9c1b512016-09-06 20:57:50 +0000700 if isa_caches.look_for_key(self.pid):
701 self.isa_cache = isa_caches.get_value(self.pid)
702 else:
703 self.isa_cache = lldb.formatters.cache.Cache()
704 isa_caches.add_item(self.pid, self.isa_cache)
Enrico Granataeb4a4792012-02-23 23:10:27 +0000705
Kate Stoneb9c1b512016-09-06 20:57:50 +0000706 def adjust_for_architecture(self, valobj):
707 process = valobj.GetTarget().GetProcess()
708 self.pointer_size = process.GetAddressByteSize()
709 self.is_64_bit = (self.pointer_size == 8)
710 self.endianness = process.GetByteOrder()
711 self.is_little = (self.endianness == lldb.eByteOrderLittle)
712 self.cfruntime_size = 16 if self.is_64_bit else 8
713
714 # a simple helper function that makes it more explicit that one is calculating
715 # an offset that is made up of X pointers and Y bytes of additional data
716 # taking into account pointer size - if you know there is going to be some padding
717 # you can pass that in and it will be taken into account (since padding may be different between
718 # 32 and 64 bit versions, you can pass padding value for both, the right
719 # one will be used)
720 def calculate_offset(
721 self,
722 num_pointers=0,
723 bytes_count=0,
724 padding32=0,
725 padding64=0):
726 value = bytes_count + num_pointers * self.pointer_size
727 return value + padding64 if self.is_64_bit else value + padding32
728
Enrico Granataeb4a4792012-02-23 23:10:27 +0000729
730class ObjCRuntime:
731
Kate Stoneb9c1b512016-09-06 20:57:50 +0000732 # the ObjC runtime has no explicit "version" field that we can use
733 # instead, we discriminate v1 from v2 by looking for the presence
734 # of a well-known section only present in v1
735 @staticmethod
736 def runtime_version(process):
737 logger = lldb.formatters.Logger.Logger()
738 if process.IsValid() == 0:
739 logger >> "No process - bailing out"
740 return None
741 target = process.GetTarget()
742 num_modules = target.GetNumModules()
743 module_objc = None
744 for idx in range(num_modules):
745 module = target.GetModuleAtIndex(idx)
746 if module.GetFileSpec().GetFilename() == 'libobjc.A.dylib':
747 module_objc = module
748 break
749 if module_objc is None or module_objc.IsValid() == 0:
750 logger >> "no libobjc - bailing out"
751 return None
752 num_sections = module.GetNumSections()
753 section_objc = None
754 for idx in range(num_sections):
755 section = module.GetSectionAtIndex(idx)
756 if section.GetName() == '__OBJC':
757 section_objc = section
758 break
759 if section_objc is not None and section_objc.IsValid():
760 logger >> "found __OBJC: v1"
761 return 1
762 logger >> "no __OBJC: v2"
763 return 2
Enrico Granataeb4a4792012-02-23 23:10:27 +0000764
Kate Stoneb9c1b512016-09-06 20:57:50 +0000765 @staticmethod
766 def runtime_from_isa(isa):
767 logger = lldb.formatters.Logger.Logger()
768 runtime = ObjCRuntime(isa)
769 runtime.isa = isa
770 return runtime
Enrico Granatabf70ee92012-03-27 21:49:20 +0000771
Kate Stoneb9c1b512016-09-06 20:57:50 +0000772 def __init__(self, valobj):
773 logger = lldb.formatters.Logger.Logger()
774 self.valobj = valobj
775 self.adjust_for_architecture()
776 self.sys_params = SystemParameters(self.valobj)
777 self.unsigned_value = self.valobj.GetValueAsUnsigned()
778 self.isa_value = None
Enrico Granataeb4a4792012-02-23 23:10:27 +0000779
Kate Stoneb9c1b512016-09-06 20:57:50 +0000780 def adjust_for_architecture(self):
781 pass
Enrico Granataeb4a4792012-02-23 23:10:27 +0000782
783# an ObjC pointer can either be tagged or must be aligned
Kate Stoneb9c1b512016-09-06 20:57:50 +0000784 def is_tagged(self):
785 logger = lldb.formatters.Logger.Logger()
786 if self.valobj is None:
787 return 0
788 return (
789 Utilities.is_valid_pointer(
790 self.unsigned_value,
791 self.sys_params.pointer_size,
792 allow_tagged=1) and not(
793 Utilities.is_valid_pointer(
794 self.unsigned_value,
795 self.sys_params.pointer_size,
796 allow_tagged=0)))
Enrico Granataeb4a4792012-02-23 23:10:27 +0000797
Kate Stoneb9c1b512016-09-06 20:57:50 +0000798 def is_valid(self):
799 logger = lldb.formatters.Logger.Logger()
800 if self.valobj is None:
801 return 0
802 if self.valobj.IsInScope() == 0:
803 return 0
804 return Utilities.is_valid_pointer(
805 self.unsigned_value,
806 self.sys_params.pointer_size,
807 allow_tagged=1)
Enrico Granataeb4a4792012-02-23 23:10:27 +0000808
Kate Stoneb9c1b512016-09-06 20:57:50 +0000809 def is_nil(self):
810 return self.unsigned_value == 0
Enrico Granata3f1052b2012-03-13 21:52:00 +0000811
Kate Stoneb9c1b512016-09-06 20:57:50 +0000812 def read_isa(self):
813 logger = lldb.formatters.Logger.Logger()
814 if self.isa_value is not None:
815 logger >> "using cached isa"
816 return self.isa_value
817 self.isa_pointer = self.valobj.CreateChildAtOffset(
818 "cfisa", 0, self.sys_params.types_cache.addr_ptr_type)
819 if self.isa_pointer is None or self.isa_pointer.IsValid() == 0:
820 logger >> "invalid isa - bailing out"
821 return None
822 self.isa_value = self.isa_pointer.GetValueAsUnsigned(1)
823 if self.isa_value == 1:
824 logger >> "invalid isa value - bailing out"
825 return None
826 return Ellipsis
Enrico Granataeb4a4792012-02-23 23:10:27 +0000827
Kate Stoneb9c1b512016-09-06 20:57:50 +0000828 def read_class_data(self):
829 logger = lldb.formatters.Logger.Logger()
830 global isa_cache
831 if self.is_tagged():
832 # tagged pointers only exist in ObjC v2
833 if self.sys_params.runtime_version == 2:
834 logger >> "on v2 and tagged - maybe"
835 # not every odd-valued pointer is actually tagged. most are just plain wrong
836 # we could try and predetect this before even creating a TaggedClass_Data object
837 # but unless performance requires it, this seems a cleaner way
838 # to tackle the task
839 tentative_tagged = TaggedClass_Data(
840 self.unsigned_value, self.sys_params)
841 if tentative_tagged.is_valid():
842 logger >> "truly tagged"
843 return tentative_tagged
844 else:
845 logger >> "not tagged - error"
846 return InvalidClass_Data()
847 else:
848 logger >> "on v1 and tagged - error"
849 return InvalidClass_Data()
850 if self.is_valid() == 0 or self.read_isa() is None:
851 return InvalidClass_Data()
852 data = self.sys_params.isa_cache.get_value(
853 self.isa_value, default=None)
854 if data is not None:
855 return data
856 if self.sys_params.runtime_version == 2:
857 data = Class_Data_V2(self.isa_pointer, self.sys_params)
858 else:
859 data = Class_Data_V1(self.isa_pointer, self.sys_params)
860 if data is None:
861 return InvalidClass_Data()
862 if data.is_valid():
863 self.sys_params.isa_cache.add_item(
864 self.isa_value, data, ok_to_replace=1)
865 return data
Enrico Granatab8cbe9c2012-02-23 23:26:48 +0000866
Kate Stoneb9c1b512016-09-06 20:57:50 +0000867# these classes below can be used by the data formatters to provide a
868# consistent message that describes a given runtime-generated situation
869
870
Enrico Granata3f1052b2012-03-13 21:52:00 +0000871class SpecialSituation_Description:
Kate Stoneb9c1b512016-09-06 20:57:50 +0000872
873 def message(self):
874 return ''
875
Enrico Granata3f1052b2012-03-13 21:52:00 +0000876
877class InvalidPointer_Description(SpecialSituation_Description):
878
Kate Stoneb9c1b512016-09-06 20:57:50 +0000879 def __init__(self, nil):
880 self.is_nil = nil
Enrico Granata3f1052b2012-03-13 21:52:00 +0000881
Kate Stoneb9c1b512016-09-06 20:57:50 +0000882 def message(self):
883 if self.is_nil:
884 return '@"<nil>"'
885 else:
886 return '<invalid pointer>'
887
Enrico Granata3f1052b2012-03-13 21:52:00 +0000888
889class InvalidISA_Description(SpecialSituation_Description):
890
Kate Stoneb9c1b512016-09-06 20:57:50 +0000891 def __init__(self):
892 pass
Enrico Granata3f1052b2012-03-13 21:52:00 +0000893
Kate Stoneb9c1b512016-09-06 20:57:50 +0000894 def message(self):
895 return '<not an Objective-C object>'
896
Enrico Granata3f1052b2012-03-13 21:52:00 +0000897
Enrico Granata5d311032012-09-18 18:34:14 +0000898class ThisIsZombie_Description(SpecialSituation_Description):
Kate Stoneb9c1b512016-09-06 20:57:50 +0000899
900 def message(self):
901 return '<freed object>'