xref: /llvm-project/lldb/examples/summaries/cocoa/CFDictionary.py (revision 7bc0ec3aad663a2c81fddf9da38dba46bba6be19)
1# summary provider for NSDictionary
2import lldb
3import ctypes
4import objc_runtime
5import metrics
6
7statistics = metrics.Metrics()
8statistics.add_metric('invalid_isa')
9statistics.add_metric('invalid_pointer')
10statistics.add_metric('unknown_class')
11statistics.add_metric('code_notrun')
12
13# despite the similary to synthetic children providers, these classes are not
14# trying to provide anything but the count for an NSDictionary, so they need not
15# obey the interface specification for synthetic children providers
16class NSCFDictionary_SummaryProvider:
17	def adjust_for_architecture(self):
18		self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8)
19		self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle)
20		self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize()
21
22	def __init__(self, valobj):
23		self.valobj = valobj;
24		self.update();
25
26	def update(self):
27		self.adjust_for_architecture();
28		self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID)
29		if self.is_64_bit:
30			self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
31		else:
32			self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt)
33
34	# empirically determined on both 32 and 64bit desktop Mac OS X
35	# probably boils down to 2 pointers and 4 bytes of data, but
36	# the description of __CFDictionary is not readily available so most
37	# of this is guesswork, plain and simple
38	def offset(self):
39		if self.is_64_bit:
40			return 20
41		else:
42			return 12
43
44	def num_children(self):
45		num_children_vo = self.valobj.CreateChildAtOffset("count",
46							self.offset(),
47							self.NSUInteger)
48		return num_children_vo.GetValueAsUnsigned(0)
49
50
51class NSDictionaryI_SummaryProvider:
52	def adjust_for_architecture(self):
53		self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8)
54		self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle)
55		self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize()
56
57	def __init__(self, valobj):
58		self.valobj = valobj;
59		self.update();
60
61	def update(self):
62		self.adjust_for_architecture();
63		self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID)
64		if self.is_64_bit:
65			self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
66		else:
67			self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt)
68
69	# we just need to skip the ISA and the count immediately follows
70	def offset(self):
71		if self.is_64_bit:
72			return 8
73		else:
74			return 4
75
76	def num_children(self):
77		num_children_vo = self.valobj.CreateChildAtOffset("count",
78							self.offset(),
79							self.NSUInteger)
80		value = num_children_vo.GetValueAsUnsigned(0)
81		if value != None:
82			# the MS6bits on immutable dictionaries seem to be taken by the LSB of capacity
83			# not sure if it is a bug or some weird sort of feature, but masking that out
84			# gets the count right
85			if self.is_64_bit:
86				value = value & ~0xFC00000000000000
87			else:
88				value = value & ~0xFC000000
89		return value
90
91class NSDictionaryM_SummaryProvider:
92	def adjust_for_architecture(self):
93		self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8)
94		self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle)
95		self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize()
96
97	def __init__(self, valobj):
98		self.valobj = valobj;
99		self.update();
100
101	def update(self):
102		self.adjust_for_architecture();
103		self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID)
104		if self.is_64_bit:
105			self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
106		else:
107			self.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt)
108
109	# we just need to skip the ISA and the count immediately follows
110	def offset(self):
111		if self.is_64_bit:
112			return 8
113		else:
114			return 4
115
116	def num_children(self):
117		num_children_vo = self.valobj.CreateChildAtOffset("count",
118							self.offset(),
119							self.NSUInteger)
120		value = num_children_vo.GetValueAsUnsigned(0)
121		if value != None:
122			# the MS6bits on mutable dictionaries seem to be taken by flags for
123			# KVO and probably other features. however, masking it out does get
124			# the count right
125			if self.is_64_bit:
126				value = value & ~0xFC00000000000000
127			else:
128				value = value & ~0xFC000000
129		return value
130
131
132class NSDictionaryUnknown_SummaryProvider:
133	def adjust_for_architecture(self):
134		self.is_64_bit = (self.valobj.GetTarget().GetProcess().GetAddressByteSize() == 8)
135		self.is_little = (self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle)
136		self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize()
137
138	def __init__(self, valobj):
139		self.valobj = valobj;
140		self.update()
141
142	def update(self):
143		self.adjust_for_architecture();
144		self.id_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeObjCID)
145
146	def num_children(self):
147		stream = lldb.SBStream()
148		self.valobj.GetExpressionPath(stream)
149		num_children_vo = self.valobj.CreateValueFromExpression("count","(int)[" + stream.GetData() + " count]");
150		return num_children_vo.GetValueAsUnsigned(0)
151
152
153def GetSummary_Impl(valobj):
154	global statistics
155	class_data = objc_runtime.ObjCRuntime(valobj)
156	if class_data.is_valid() == False:
157		statistics.metric_hit('invalid_pointer',valobj)
158		wrapper = None
159		return
160	class_data = class_data.read_class_data()
161	if class_data.is_valid() == False:
162		statistics.metric_hit('invalid_isa',valobj)
163		wrapper = None
164		return
165	if class_data.is_kvo():
166		class_data = class_data.get_superclass()
167	if class_data.is_valid() == False:
168		statistics.metric_hit('invalid_isa',valobj)
169		wrapper = None
170		return
171
172	name_string = class_data.class_name()
173	if name_string == '__NSCFDictionary':
174		wrapper = NSCFDictionary_SummaryProvider(valobj)
175		statistics.metric_hit('code_notrun',valobj)
176	elif name_string == '__NSDictionaryI':
177		wrapper = NSDictionaryI_SummaryProvider(valobj)
178		statistics.metric_hit('code_notrun',valobj)
179	elif name_string == '__NSDictionaryM':
180		wrapper = NSDictionaryM_SummaryProvider(valobj)
181		statistics.metric_hit('code_notrun',valobj)
182	else:
183		wrapper = NSDictionaryUnknown_SummaryProvider(valobj)
184		statistics.metric_hit('unknown_class',str(valobj) + " seen as " + name_string)
185	return wrapper;
186
187def CFDictionary_SummaryProvider (valobj,dict):
188	provider = GetSummary_Impl(valobj);
189	if provider != None:
190	    try:
191	        summary = str(provider.num_children());
192	    except:
193	        summary = None
194	    if summary == None:
195	        summary = 'no valid dictionary here'
196	    return summary + " key/value pairs"
197	return ''
198
199def CFDictionary_SummaryProvider2 (valobj,dict):
200	provider = GetSummary_Impl(valobj);
201	if provider != None:
202		try:
203			summary = (provider.num_children());
204		except:
205			summary = None
206		if summary == None:
207			summary = 'no valid dictionary here'
208		# needed on OSX Mountain Lion
209		elif provider.is_64_bit:
210			summary = int(summary) & ~0x0f1f000000000000
211		return str(summary) + " key/value pairs"
212	return ''
213
214def __lldb_init_module(debugger,dict):
215	debugger.HandleCommand("type summary add -F CFDictionary.CFDictionary_SummaryProvider NSDictionary")
216	debugger.HandleCommand("type summary add -F CFDictionary.CFDictionary_SummaryProvider2 CFDictionaryRef CFMutableDictionaryRef")
217