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 # summary provider for NSSet
11 import lldb
.runtime
.objc
.objc_runtime
12 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 port number of an NSMachPort, so they need not
24 # obey the interface specification for synthetic children providers
27 class NSCFSet_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 # one pointer is the ISA
51 # then we have one other internal pointer, plus
52 # 4 bytes worth of flags. hence, these values
54 logger
= lldb
.formatters
.Logger
.Logger()
55 if self
.sys_params
.is_64_bit
:
61 logger
= lldb
.formatters
.Logger
.Logger()
62 vcount
= self
.valobj
.CreateChildAtOffset(
63 "count", self
.offset(), self
.sys_params
.types_cache
.NSUInteger
65 return vcount
.GetValueAsUnsigned(0)
68 class NSSetUnknown_SummaryProvider
:
69 def adjust_for_architecture(self
):
72 def __init__(self
, valobj
, params
):
73 logger
= lldb
.formatters
.Logger
.Logger()
75 self
.sys_params
= params
79 logger
= lldb
.formatters
.Logger
.Logger()
80 self
.adjust_for_architecture()
83 logger
= lldb
.formatters
.Logger
.Logger()
84 stream
= lldb
.SBStream()
85 self
.valobj
.GetExpressionPath(stream
)
86 expr
= "(int)[" + stream
.GetData() + " count]"
87 num_children_vo
= self
.valobj
.CreateValueFromExpression("count", expr
)
88 if num_children_vo
.IsValid():
89 return num_children_vo
.GetValueAsUnsigned(0)
90 return "<variable is not NSSet>"
93 class NSSetI_SummaryProvider
:
94 def adjust_for_architecture(self
):
97 def __init__(self
, valobj
, params
):
98 logger
= lldb
.formatters
.Logger
.Logger()
100 self
.sys_params
= params
101 if not (self
.sys_params
.types_cache
.NSUInteger
):
102 if self
.sys_params
.is_64_bit
:
103 self
.sys_params
.types_cache
.NSUInteger
= (
104 self
.valobj
.GetType().GetBasicType(lldb
.eBasicTypeUnsignedLong
)
107 self
.sys_params
.types_cache
.NSUInteger
= (
108 self
.valobj
.GetType().GetBasicType(lldb
.eBasicTypeUnsignedInt
)
113 logger
= lldb
.formatters
.Logger
.Logger()
114 self
.adjust_for_architecture()
116 # we just need to skip the ISA and the count immediately follows
118 logger
= lldb
.formatters
.Logger
.Logger()
119 return self
.sys_params
.pointer_size
122 logger
= lldb
.formatters
.Logger
.Logger()
123 num_children_vo
= self
.valobj
.CreateChildAtOffset(
124 "count", self
.offset(), self
.sys_params
.types_cache
.NSUInteger
126 value
= num_children_vo
.GetValueAsUnsigned(0)
127 if value
is not None:
128 # the MSB on immutable sets seems to be taken by some other data
129 # not sure if it is a bug or some weird sort of feature, but masking it out
130 # gets the count right (unless, of course, someone's dictionaries grow
131 # too large - but I have not tested this)
132 if self
.sys_params
.is_64_bit
:
133 value
= value
& ~
0xFF00000000000000
135 value
= value
& ~
0xFF000000
139 class NSSetM_SummaryProvider
:
140 def adjust_for_architecture(self
):
143 def __init__(self
, valobj
, params
):
144 logger
= lldb
.formatters
.Logger
.Logger()
146 self
.sys_params
= params
147 if not (self
.sys_params
.types_cache
.NSUInteger
):
148 if self
.sys_params
.is_64_bit
:
149 self
.sys_params
.types_cache
.NSUInteger
= (
150 self
.valobj
.GetType().GetBasicType(lldb
.eBasicTypeUnsignedLong
)
153 self
.sys_params
.types_cache
.NSUInteger
= (
154 self
.valobj
.GetType().GetBasicType(lldb
.eBasicTypeUnsignedInt
)
159 logger
= lldb
.formatters
.Logger
.Logger()
160 self
.adjust_for_architecture()
162 # we just need to skip the ISA and the count immediately follows
164 logger
= lldb
.formatters
.Logger
.Logger()
165 return self
.sys_params
.pointer_size
168 logger
= lldb
.formatters
.Logger
.Logger()
169 num_children_vo
= self
.valobj
.CreateChildAtOffset(
170 "count", self
.offset(), self
.sys_params
.types_cache
.NSUInteger
172 return num_children_vo
.GetValueAsUnsigned(0)
175 class NSCountedSet_SummaryProvider
:
176 def adjust_for_architecture(self
):
179 def __init__(self
, valobj
, params
):
180 logger
= lldb
.formatters
.Logger
.Logger()
182 self
.sys_params
= params
183 if not (self
.sys_params
.types_cache
.voidptr
):
184 self
.sys_params
.types_cache
.voidptr
= (
185 self
.valobj
.GetType().GetBasicType(lldb
.eBasicTypeVoid
).GetPointerType()
190 logger
= lldb
.formatters
.Logger
.Logger()
191 self
.adjust_for_architecture()
193 # an NSCountedSet is implemented using a CFBag whose pointer just follows
196 logger
= lldb
.formatters
.Logger
.Logger()
197 return self
.sys_params
.pointer_size
200 logger
= lldb
.formatters
.Logger
.Logger()
201 cfbag_vo
= self
.valobj
.CreateChildAtOffset(
202 "bag_impl", self
.offset(), self
.sys_params
.types_cache
.voidptr
204 return CFBag
.CFBagRef_SummaryProvider(cfbag_vo
, self
.sys_params
).length()
207 def GetSummary_Impl(valobj
):
208 logger
= lldb
.formatters
.Logger
.Logger()
213 ) = lldb
.runtime
.objc
.objc_runtime
.Utilities
.prepare_class_detection(
219 name_string
= class_data
.class_name()
220 logger
>> "class name is: " + str(name_string
)
222 if name_string
== "__NSCFSet":
223 wrapper
= NSCFSet_SummaryProvider(valobj
, class_data
.sys_params
)
224 statistics
.metric_hit("code_notrun", valobj
)
225 elif name_string
== "__NSSetI":
226 wrapper
= NSSetI_SummaryProvider(valobj
, class_data
.sys_params
)
227 statistics
.metric_hit("code_notrun", valobj
)
228 elif name_string
== "__NSSetM":
229 wrapper
= NSSetM_SummaryProvider(valobj
, class_data
.sys_params
)
230 statistics
.metric_hit("code_notrun", valobj
)
231 elif name_string
== "NSCountedSet":
232 wrapper
= NSCountedSet_SummaryProvider(valobj
, class_data
.sys_params
)
233 statistics
.metric_hit("code_notrun", valobj
)
235 wrapper
= NSSetUnknown_SummaryProvider(valobj
, class_data
.sys_params
)
236 statistics
.metric_hit(
237 "unknown_class", valobj
.GetName() + " seen as " + name_string
242 def NSSet_SummaryProvider(valobj
, dict):
243 logger
= lldb
.formatters
.Logger
.Logger()
244 provider
= GetSummary_Impl(valobj
)
245 if provider
is not None:
247 summary
= provider
.count()
251 summary
= "<variable is not NSSet>"
252 if isinstance(summary
, str):
255 summary
= str(summary
) + (" objects" if summary
!= 1 else " object")
257 return "Summary Unavailable"
260 def NSSet_SummaryProvider2(valobj
, dict):
261 logger
= lldb
.formatters
.Logger
.Logger()
262 provider
= GetSummary_Impl(valobj
)
263 if provider
is not None:
265 provider
, lldb
.runtime
.objc
.objc_runtime
.SpecialSituation_Description
267 return provider
.message()
269 summary
= provider
.count()
272 logger
>> "got summary " + str(summary
)
273 # for some reason, one needs to clear some bits for the count returned
274 # to be correct when using directly CF*SetRef as compared to NS*Set
275 # this only happens on 64bit, and the bit mask was derived through
276 # experimentation (if counts start looking weird, then most probably
277 # the mask needs to be changed)
279 summary
= "<variable is not CFSet>"
280 if isinstance(summary
, str):
283 if provider
.sys_params
.is_64_bit
:
284 summary
= summary
& ~
0x1FFF000000000000
285 summary
= '@"' + str(summary
) + (' values"' if summary
!= 1 else ' value"')
287 return "Summary Unavailable"
290 def __lldb_init_module(debugger
, dict):
291 debugger
.HandleCommand("type summary add -F NSSet.NSSet_SummaryProvider NSSet")
292 debugger
.HandleCommand(
293 "type summary add -F NSSet.NSSet_SummaryProvider2 CFSetRef CFMutableSetRef"