4 Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 See https://llvm.org/LICENSE.txt for license information.
6 SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8 # example summary provider for NSDictionary
9 # the real summary is now C++ code built into LLDB
12 import lldb
.runtime
.objc
.objc_runtime
13 import lldb
.formatters
.metrics
14 import lldb
.formatters
.Logger
16 statistics
= lldb
.formatters
.metrics
.Metrics()
17 statistics
.add_metric("invalid_isa")
18 statistics
.add_metric("invalid_pointer")
19 statistics
.add_metric("unknown_class")
20 statistics
.add_metric("code_notrun")
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
27 class NSCFDictionary_SummaryProvider
:
28 def adjust_for_architecture(self
):
31 def __init__(self
, valobj
, params
):
32 logger
= lldb
.formatters
.Logger
.Logger()
34 self
.sys_params
= params
35 if not (self
.sys_params
.types_cache
.NSUInteger
):
36 if self
.sys_params
.is_64_bit
:
37 self
.sys_params
.types_cache
.NSUInteger
= (
38 self
.valobj
.GetType().GetBasicType(lldb
.eBasicTypeUnsignedLong
)
41 self
.sys_params
.types_cache
.NSUInteger
= (
42 self
.valobj
.GetType().GetBasicType(lldb
.eBasicTypeUnsignedInt
)
47 logger
= lldb
.formatters
.Logger
.Logger()
48 self
.adjust_for_architecture()
50 # empirically determined on both 32 and 64bit desktop Mac OS X
51 # probably boils down to 2 pointers and 4 bytes of data, but
52 # the description of __CFDictionary is not readily available so most
53 # of this is guesswork, plain and simple
55 logger
= lldb
.formatters
.Logger
.Logger()
56 if self
.sys_params
.is_64_bit
:
61 def num_children(self
):
62 logger
= lldb
.formatters
.Logger
.Logger()
63 num_children_vo
= self
.valobj
.CreateChildAtOffset(
64 "count", self
.offset(), self
.sys_params
.types_cache
.NSUInteger
66 return num_children_vo
.GetValueAsUnsigned(0)
69 class NSDictionaryI_SummaryProvider
:
70 def adjust_for_architecture(self
):
73 def __init__(self
, valobj
, params
):
74 logger
= lldb
.formatters
.Logger
.Logger()
76 self
.sys_params
= params
77 if not (self
.sys_params
.types_cache
.NSUInteger
):
78 if self
.sys_params
.is_64_bit
:
79 self
.sys_params
.types_cache
.NSUInteger
= (
80 self
.valobj
.GetType().GetBasicType(lldb
.eBasicTypeUnsignedLong
)
83 self
.sys_params
.types_cache
.NSUInteger
= (
84 self
.valobj
.GetType().GetBasicType(lldb
.eBasicTypeUnsignedInt
)
89 logger
= lldb
.formatters
.Logger
.Logger()
90 self
.adjust_for_architecture()
92 # we just need to skip the ISA and the count immediately follows
94 logger
= lldb
.formatters
.Logger
.Logger()
95 return self
.sys_params
.pointer_size
97 def num_children(self
):
98 logger
= lldb
.formatters
.Logger
.Logger()
99 num_children_vo
= self
.valobj
.CreateChildAtOffset(
100 "count", self
.offset(), self
.sys_params
.types_cache
.NSUInteger
102 value
= num_children_vo
.GetValueAsUnsigned(0)
103 if value
is not None:
104 # the MS6bits on immutable dictionaries seem to be taken by the LSB of capacity
105 # not sure if it is a bug or some weird sort of feature, but masking that out
106 # gets the count right
107 if self
.sys_params
.is_64_bit
:
108 value
= value
& ~
0xFC00000000000000
110 value
= value
& ~
0xFC000000
114 class NSDictionaryM_SummaryProvider
:
115 def adjust_for_architecture(self
):
118 def __init__(self
, valobj
, params
):
119 logger
= lldb
.formatters
.Logger
.Logger()
121 self
.sys_params
= params
122 if not (self
.sys_params
.types_cache
.NSUInteger
):
123 if self
.sys_params
.is_64_bit
:
124 self
.sys_params
.types_cache
.NSUInteger
= (
125 self
.valobj
.GetType().GetBasicType(lldb
.eBasicTypeUnsignedLong
)
128 self
.sys_params
.types_cache
.NSUInteger
= (
129 self
.valobj
.GetType().GetBasicType(lldb
.eBasicTypeUnsignedInt
)
134 logger
= lldb
.formatters
.Logger
.Logger()
135 self
.adjust_for_architecture()
137 # we just need to skip the ISA and the count immediately follows
139 return self
.sys_params
.pointer_size
141 def num_children(self
):
142 logger
= lldb
.formatters
.Logger
.Logger()
143 num_children_vo
= self
.valobj
.CreateChildAtOffset(
144 "count", self
.offset(), self
.sys_params
.types_cache
.NSUInteger
146 value
= num_children_vo
.GetValueAsUnsigned(0)
147 if value
is not None:
148 # the MS6bits on immutable dictionaries seem to be taken by the LSB of capacity
149 # not sure if it is a bug or some weird sort of feature, but masking that out
150 # gets the count right
151 if self
.sys_params
.is_64_bit
:
152 value
= value
& ~
0xFC00000000000000
154 value
= value
& ~
0xFC000000
158 class NSDictionaryUnknown_SummaryProvider
:
159 def adjust_for_architecture(self
):
162 def __init__(self
, valobj
, params
):
163 logger
= lldb
.formatters
.Logger
.Logger()
165 self
.sys_params
= params
169 logger
= lldb
.formatters
.Logger
.Logger()
170 self
.adjust_for_architecture()
172 def num_children(self
):
173 logger
= lldb
.formatters
.Logger
.Logger()
174 stream
= lldb
.SBStream()
175 self
.valobj
.GetExpressionPath(stream
)
176 num_children_vo
= self
.valobj
.CreateValueFromExpression(
177 "count", "(int)[" + stream
.GetData() + " count]"
179 if num_children_vo
.IsValid():
180 return num_children_vo
.GetValueAsUnsigned(0)
181 return "<variable is not NSDictionary>"
184 def GetSummary_Impl(valobj
):
185 logger
= lldb
.formatters
.Logger
.Logger()
190 ) = lldb
.runtime
.objc
.objc_runtime
.Utilities
.prepare_class_detection(
196 name_string
= class_data
.class_name()
198 logger
>> "class name is: " + str(name_string
)
200 if name_string
== "__NSCFDictionary":
201 wrapper
= NSCFDictionary_SummaryProvider(valobj
, class_data
.sys_params
)
202 statistics
.metric_hit("code_notrun", valobj
)
203 elif name_string
== "__NSDictionaryI":
204 wrapper
= NSDictionaryI_SummaryProvider(valobj
, class_data
.sys_params
)
205 statistics
.metric_hit("code_notrun", valobj
)
206 elif name_string
== "__NSDictionaryM":
207 wrapper
= NSDictionaryM_SummaryProvider(valobj
, class_data
.sys_params
)
208 statistics
.metric_hit("code_notrun", valobj
)
210 wrapper
= NSDictionaryUnknown_SummaryProvider(valobj
, class_data
.sys_params
)
211 statistics
.metric_hit(
212 "unknown_class", valobj
.GetName() + " seen as " + name_string
217 def CFDictionary_SummaryProvider(valobj
, dict):
218 logger
= lldb
.formatters
.Logger
.Logger()
219 provider
= GetSummary_Impl(valobj
)
220 if provider
is not None:
222 provider
, lldb
.runtime
.objc
.objc_runtime
.SpecialSituation_Description
224 return provider
.message()
226 summary
= provider
.num_children()
229 logger
>> "got summary " + str(summary
)
231 return "<variable is not NSDictionary>"
232 if isinstance(summary
, str):
234 return str(summary
) + (
235 " key/value pairs" if summary
!= 1 else " key/value pair"
237 return "Summary Unavailable"
240 def CFDictionary_SummaryProvider2(valobj
, dict):
241 logger
= lldb
.formatters
.Logger
.Logger()
242 provider
= GetSummary_Impl(valobj
)
243 if provider
is not None:
245 provider
, lldb
.runtime
.objc
.objc_runtime
.SpecialSituation_Description
247 return provider
.message()
249 summary
= provider
.num_children()
252 logger
>> "got summary " + str(summary
)
254 summary
= "<variable is not CFDictionary>"
255 if isinstance(summary
, str):
258 # needed on OSX Mountain Lion
259 if provider
.sys_params
.is_64_bit
:
260 summary
= summary
& ~
0x0F1F000000000000
261 summary
= '@"' + str(summary
) + (' entries"' if summary
!= 1 else ' entry"')
263 return "Summary Unavailable"
266 def __lldb_init_module(debugger
, dict):
267 debugger
.HandleCommand(
268 "type summary add -F CFDictionary.CFDictionary_SummaryProvider NSDictionary"
270 debugger
.HandleCommand(
271 "type summary add -F CFDictionary.CFDictionary_SummaryProvider2 CFDictionaryRef CFMutableDictionaryRef"