xref: /openbsd-src/gnu/llvm/lldb/examples/summaries/cocoa/NSSet.py (revision 1a8dbaac879b9f3335ad7fb25429ce63ac1d6bac)
1"""
2LLDB AppKit formatters
3
4Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5See https://llvm.org/LICENSE.txt for license information.
6SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7"""
8# summary provider for NSSet
9import lldb
10import ctypes
11import lldb.runtime.objc.objc_runtime
12import lldb.formatters.metrics
13import CFBag
14import lldb.formatters.Logger
15
16try:
17    basestring
18except NameError:
19    basestring = str
20
21statistics = lldb.formatters.metrics.Metrics()
22statistics.add_metric('invalid_isa')
23statistics.add_metric('invalid_pointer')
24statistics.add_metric('unknown_class')
25statistics.add_metric('code_notrun')
26
27# despite the similary to synthetic children providers, these classes are not
28# trying to provide anything but the port number of an NSMachPort, so they need not
29# obey the interface specification for synthetic children providers
30
31
32class NSCFSet_SummaryProvider:
33
34    def adjust_for_architecture(self):
35        pass
36
37    def __init__(self, valobj, params):
38        logger = lldb.formatters.Logger.Logger()
39        self.valobj = valobj
40        self.sys_params = params
41        if not(self.sys_params.types_cache.NSUInteger):
42            if self.sys_params.is_64_bit:
43                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
44                ).GetBasicType(lldb.eBasicTypeUnsignedLong)
45            else:
46                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
47                ).GetBasicType(lldb.eBasicTypeUnsignedInt)
48        self.update()
49
50    def update(self):
51        logger = lldb.formatters.Logger.Logger()
52        self.adjust_for_architecture()
53
54    # one pointer is the ISA
55    # then we have one other internal pointer, plus
56    # 4 bytes worth of flags. hence, these values
57    def offset(self):
58        logger = lldb.formatters.Logger.Logger()
59        if self.sys_params.is_64_bit:
60            return 20
61        else:
62            return 12
63
64    def count(self):
65        logger = lldb.formatters.Logger.Logger()
66        vcount = self.valobj.CreateChildAtOffset(
67            "count", self.offset(), self.sys_params.types_cache.NSUInteger)
68        return vcount.GetValueAsUnsigned(0)
69
70
71class NSSetUnknown_SummaryProvider:
72
73    def adjust_for_architecture(self):
74        pass
75
76    def __init__(self, valobj, params):
77        logger = lldb.formatters.Logger.Logger()
78        self.valobj = valobj
79        self.sys_params = params
80        self.update()
81
82    def update(self):
83        logger = lldb.formatters.Logger.Logger()
84        self.adjust_for_architecture()
85
86    def count(self):
87        logger = lldb.formatters.Logger.Logger()
88        stream = lldb.SBStream()
89        self.valobj.GetExpressionPath(stream)
90        expr = "(int)[" + stream.GetData() + " count]"
91        num_children_vo = self.valobj.CreateValueFromExpression("count", expr)
92        if num_children_vo.IsValid():
93            return num_children_vo.GetValueAsUnsigned(0)
94        return '<variable is not NSSet>'
95
96
97class NSSetI_SummaryProvider:
98
99    def adjust_for_architecture(self):
100        pass
101
102    def __init__(self, valobj, params):
103        logger = lldb.formatters.Logger.Logger()
104        self.valobj = valobj
105        self.sys_params = params
106        if not(self.sys_params.types_cache.NSUInteger):
107            if self.sys_params.is_64_bit:
108                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
109                ).GetBasicType(lldb.eBasicTypeUnsignedLong)
110            else:
111                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
112                ).GetBasicType(lldb.eBasicTypeUnsignedInt)
113        self.update()
114
115    def update(self):
116        logger = lldb.formatters.Logger.Logger()
117        self.adjust_for_architecture()
118
119    # we just need to skip the ISA and the count immediately follows
120    def offset(self):
121        logger = lldb.formatters.Logger.Logger()
122        return self.sys_params.pointer_size
123
124    def count(self):
125        logger = lldb.formatters.Logger.Logger()
126        num_children_vo = self.valobj.CreateChildAtOffset(
127            "count", self.offset(), self.sys_params.types_cache.NSUInteger)
128        value = num_children_vo.GetValueAsUnsigned(0)
129        if value is not None:
130            # the MSB on immutable sets seems to be taken by some other data
131            # not sure if it is a bug or some weird sort of feature, but masking it out
132            # gets the count right (unless, of course, someone's dictionaries grow
133            #                       too large - but I have not tested this)
134            if self.sys_params.is_64_bit:
135                value = value & ~0xFF00000000000000
136            else:
137                value = value & ~0xFF000000
138        return value
139
140
141class NSSetM_SummaryProvider:
142
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        if not(self.sys_params.types_cache.NSUInteger):
151            if self.sys_params.is_64_bit:
152                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
153                ).GetBasicType(lldb.eBasicTypeUnsignedLong)
154            else:
155                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
156                ).GetBasicType(lldb.eBasicTypeUnsignedInt)
157        self.update()
158
159    def update(self):
160        logger = lldb.formatters.Logger.Logger()
161        self.adjust_for_architecture()
162
163    # we just need to skip the ISA and the count immediately follows
164    def offset(self):
165        logger = lldb.formatters.Logger.Logger()
166        return self.sys_params.pointer_size
167
168    def count(self):
169        logger = lldb.formatters.Logger.Logger()
170        num_children_vo = self.valobj.CreateChildAtOffset(
171            "count", self.offset(), self.sys_params.types_cache.NSUInteger)
172        return num_children_vo.GetValueAsUnsigned(0)
173
174
175class NSCountedSet_SummaryProvider:
176
177    def adjust_for_architecture(self):
178        pass
179
180    def __init__(self, valobj, params):
181        logger = lldb.formatters.Logger.Logger()
182        self.valobj = valobj
183        self.sys_params = params
184        if not (self.sys_params.types_cache.voidptr):
185            self.sys_params.types_cache.voidptr = self.valobj.GetType(
186            ).GetBasicType(lldb.eBasicTypeVoid).GetPointerType()
187        self.update()
188
189    def update(self):
190        logger = lldb.formatters.Logger.Logger()
191        self.adjust_for_architecture()
192
193    # an NSCountedSet is implemented using a CFBag whose pointer just follows
194    # the ISA
195    def offset(self):
196        logger = lldb.formatters.Logger.Logger()
197        return self.sys_params.pointer_size
198
199    def count(self):
200        logger = lldb.formatters.Logger.Logger()
201        cfbag_vo = self.valobj.CreateChildAtOffset(
202            "bag_impl", self.offset(), self.sys_params.types_cache.voidptr)
203        return CFBag.CFBagRef_SummaryProvider(
204            cfbag_vo, self.sys_params).length()
205
206
207def GetSummary_Impl(valobj):
208    logger = lldb.formatters.Logger.Logger()
209    global statistics
210    class_data, wrapper = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(
211        valobj, statistics)
212    if wrapper:
213        return wrapper
214
215    name_string = class_data.class_name()
216    logger >> "class name is: " + str(name_string)
217
218    if name_string == '__NSCFSet':
219        wrapper = NSCFSet_SummaryProvider(valobj, class_data.sys_params)
220        statistics.metric_hit('code_notrun', valobj)
221    elif name_string == '__NSSetI':
222        wrapper = NSSetI_SummaryProvider(valobj, class_data.sys_params)
223        statistics.metric_hit('code_notrun', valobj)
224    elif name_string == '__NSSetM':
225        wrapper = NSSetM_SummaryProvider(valobj, class_data.sys_params)
226        statistics.metric_hit('code_notrun', valobj)
227    elif name_string == 'NSCountedSet':
228        wrapper = NSCountedSet_SummaryProvider(valobj, class_data.sys_params)
229        statistics.metric_hit('code_notrun', valobj)
230    else:
231        wrapper = NSSetUnknown_SummaryProvider(valobj, class_data.sys_params)
232        statistics.metric_hit(
233            'unknown_class',
234            valobj.GetName() +
235            " seen as " +
236            name_string)
237    return wrapper
238
239
240def NSSet_SummaryProvider(valobj, dict):
241    logger = lldb.formatters.Logger.Logger()
242    provider = GetSummary_Impl(valobj)
243    if provider is not None:
244        try:
245            summary = provider.count()
246        except:
247            summary = None
248        if summary is None:
249            summary = '<variable is not NSSet>'
250        if isinstance(summary, basestring):
251            return summary
252        else:
253            summary = str(summary) + \
254                (' objects' if summary != 1 else ' object')
255        return summary
256    return 'Summary Unavailable'
257
258
259def NSSet_SummaryProvider2(valobj, dict):
260    logger = lldb.formatters.Logger.Logger()
261    provider = GetSummary_Impl(valobj)
262    if provider is not None:
263        if isinstance(
264                provider,
265                lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
266            return provider.message()
267        try:
268            summary = provider.count()
269        except:
270            summary = None
271        logger >> "got summary " + str(summary)
272        # for some reason, one needs to clear some bits for the count returned
273        # to be correct when using directly CF*SetRef as compared to NS*Set
274        # this only happens on 64bit, and the bit mask was derived through
275        # experimentation (if counts start looking weird, then most probably
276        #                  the mask needs to be changed)
277        if summary is None:
278            summary = '<variable is not CFSet>'
279        if isinstance(summary, basestring):
280            return summary
281        else:
282            if provider.sys_params.is_64_bit:
283                summary = summary & ~0x1fff000000000000
284            summary = '@"' + str(summary) + \
285                (' values"' if summary != 1 else ' value"')
286        return summary
287    return 'Summary Unavailable'
288
289
290def __lldb_init_module(debugger, dict):
291    debugger.HandleCommand(
292        "type summary add -F NSSet.NSSet_SummaryProvider NSSet")
293    debugger.HandleCommand(
294        "type summary add -F NSSet.NSSet_SummaryProvider2 CFSetRef CFMutableSetRef")
295