• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""
2LLDB AppKit 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"""
8# example summary provider for NSDictionary
9# the real summary is now C++ code built into LLDB
10import lldb
11import ctypes
12import lldb.runtime.objc.objc_runtime
13import lldb.formatters.metrics
14import lldb.formatters.Logger
15
16statistics = lldb.formatters.metrics.Metrics()
17statistics.add_metric('invalid_isa')
18statistics.add_metric('invalid_pointer')
19statistics.add_metric('unknown_class')
20statistics.add_metric('code_notrun')
21
22# despite the similary to synthetic children providers, these classes are not
23# trying to provide anything but the count for an NSDictionary, so they need not
24# obey the interface specification for synthetic children providers
25class NSCFDictionary_SummaryProvider:
26	def adjust_for_architecture(self):
27		pass
28
29	def __init__(self, valobj, params):
30		logger = lldb.formatters.Logger.Logger()
31		self.valobj = valobj;
32		self.sys_params = params
33		if not(self.sys_params.types_cache.NSUInteger):
34			if self.sys_params.is_64_bit:
35				self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
36			else:
37				self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt)
38		self.update();
39
40	def update(self):
41		logger = lldb.formatters.Logger.Logger()
42		self.adjust_for_architecture();
43
44	# empirically determined on both 32 and 64bit desktop Mac OS X
45	# probably boils down to 2 pointers and 4 bytes of data, but
46	# the description of __CFDictionary is not readily available so most
47	# of this is guesswork, plain and simple
48	def offset(self):
49		logger = lldb.formatters.Logger.Logger()
50		if self.sys_params.is_64_bit:
51			return 20
52		else:
53			return 12
54
55	def num_children(self):
56		logger = lldb.formatters.Logger.Logger()
57		num_children_vo = self.valobj.CreateChildAtOffset("count",
58							self.offset(),
59							self.sys_params.types_cache.NSUInteger)
60		return num_children_vo.GetValueAsUnsigned(0)
61
62
63class NSDictionaryI_SummaryProvider:
64	def adjust_for_architecture(self):
65		pass
66
67	def __init__(self, valobj, params):
68		logger = lldb.formatters.Logger.Logger()
69		self.valobj = valobj;
70		self.sys_params = params
71		if not(self.sys_params.types_cache.NSUInteger):
72			if self.sys_params.is_64_bit:
73				self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
74			else:
75				self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt)
76		self.update();
77
78	def update(self):
79		logger = lldb.formatters.Logger.Logger()
80		self.adjust_for_architecture();
81
82	# we just need to skip the ISA and the count immediately follows
83	def offset(self):
84		logger = lldb.formatters.Logger.Logger()
85		return self.sys_params.pointer_size
86
87	def num_children(self):
88		logger = lldb.formatters.Logger.Logger()
89		num_children_vo = self.valobj.CreateChildAtOffset("count",
90							self.offset(),
91							self.sys_params.types_cache.NSUInteger)
92		value = num_children_vo.GetValueAsUnsigned(0)
93		if value != None:
94			# the MS6bits on immutable dictionaries seem to be taken by the LSB of capacity
95			# not sure if it is a bug or some weird sort of feature, but masking that out
96			# gets the count right
97			if self.sys_params.is_64_bit:
98				value = value & ~0xFC00000000000000
99			else:
100				value = value & ~0xFC000000
101		return value
102
103class NSDictionaryM_SummaryProvider:
104	def adjust_for_architecture(self):
105		pass
106
107	def __init__(self, valobj, params):
108		logger = lldb.formatters.Logger.Logger()
109		self.valobj = valobj;
110		self.sys_params = params
111		if not(self.sys_params.types_cache.NSUInteger):
112			if self.sys_params.is_64_bit:
113				self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
114			else:
115				self.sys_params.types_cache.NSUInteger = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt)
116		self.update();
117
118	def update(self):
119		logger = lldb.formatters.Logger.Logger()
120		self.adjust_for_architecture();
121
122	# we just need to skip the ISA and the count immediately follows
123	def offset(self):
124		return self.sys_params.pointer_size
125
126	def num_children(self):
127		logger = lldb.formatters.Logger.Logger()
128		num_children_vo = self.valobj.CreateChildAtOffset("count",
129							self.offset(),
130							self.sys_params.types_cache.NSUInteger)
131		value = num_children_vo.GetValueAsUnsigned(0)
132		if value != None:
133			# the MS6bits on immutable dictionaries seem to be taken by the LSB of capacity
134			# not sure if it is a bug or some weird sort of feature, but masking that out
135			# gets the count right
136			if self.sys_params.is_64_bit:
137				value = value & ~0xFC00000000000000
138			else:
139				value = value & ~0xFC000000
140		return value
141
142class NSDictionaryUnknown_SummaryProvider:
143	def adjust_for_architecture(self):
144		pass
145
146	def __init__(self, valobj, params):
147		logger = lldb.formatters.Logger.Logger()
148		self.valobj = valobj;
149		self.sys_params = params
150		self.update();
151
152	def update(self):
153		logger = lldb.formatters.Logger.Logger()
154		self.adjust_for_architecture();
155
156	def num_children(self):
157		logger = lldb.formatters.Logger.Logger()
158		stream = lldb.SBStream()
159		self.valobj.GetExpressionPath(stream)
160		num_children_vo = self.valobj.CreateValueFromExpression("count","(int)[" + stream.GetData() + " count]");
161		if num_children_vo.IsValid():
162			return num_children_vo.GetValueAsUnsigned(0)
163		return '<variable is not NSDictionary>'
164
165
166def GetSummary_Impl(valobj):
167	logger = lldb.formatters.Logger.Logger()
168	global statistics
169	class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(valobj,statistics)
170	if wrapper:
171		return wrapper
172
173	name_string = class_data.class_name()
174
175	logger >> "class name is: " + str(name_string)
176
177	if name_string == '__NSCFDictionary':
178		wrapper = NSCFDictionary_SummaryProvider(valobj, class_data.sys_params)
179		statistics.metric_hit('code_notrun',valobj)
180	elif name_string == '__NSDictionaryI':
181		wrapper = NSDictionaryI_SummaryProvider(valobj, class_data.sys_params)
182		statistics.metric_hit('code_notrun',valobj)
183	elif name_string == '__NSDictionaryM':
184		wrapper = NSDictionaryM_SummaryProvider(valobj, class_data.sys_params)
185		statistics.metric_hit('code_notrun',valobj)
186	else:
187		wrapper = NSDictionaryUnknown_SummaryProvider(valobj, class_data.sys_params)
188		statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + name_string)
189	return wrapper;
190
191def CFDictionary_SummaryProvider (valobj,dict):
192	logger = lldb.formatters.Logger.Logger()
193	provider = GetSummary_Impl(valobj);
194	if provider != None:
195		if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
196			return provider.message()
197		try:
198			summary = provider.num_children();
199		except:
200			summary = None
201		logger >> "got summary " + str(summary)
202		if summary == None:
203			return '<variable is not NSDictionary>'
204		if isinstance(summary,basestring):
205			return summary
206		return str(summary) + (" key/value pairs" if summary != 1 else " key/value pair")
207	return 'Summary Unavailable'
208
209def CFDictionary_SummaryProvider2 (valobj,dict):
210	logger = lldb.formatters.Logger.Logger()
211	provider = GetSummary_Impl(valobj);
212	if provider != None:
213		if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
214			return provider.message()
215		try:
216			summary = provider.num_children();
217		except:
218			summary = None
219		logger >> "got summary " + str(summary)
220		if summary == None:
221			summary = '<variable is not CFDictionary>'
222		if isinstance(summary,basestring):
223			return summary
224		else:
225		# needed on OSX Mountain Lion
226			if provider.sys_params.is_64_bit:
227				summary = summary & ~0x0f1f000000000000
228			summary = '@"' + str(summary) + (' entries"' if summary != 1 else ' entry"')
229		return summary
230	return 'Summary Unavailable'
231
232def __lldb_init_module(debugger,dict):
233	debugger.HandleCommand("type summary add -F CFDictionary.CFDictionary_SummaryProvider NSDictionary")
234	debugger.HandleCommand("type summary add -F CFDictionary.CFDictionary_SummaryProvider2 CFDictionaryRef CFMutableDictionaryRef")
235