xref: /openbsd-src/gnu/llvm/lldb/examples/summaries/cocoa/NSSet.py (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1061da546Spatrick"""
2061da546SpatrickLLDB AppKit formatters
3061da546Spatrick
4061da546SpatrickPart of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5061da546SpatrickSee https://llvm.org/LICENSE.txt for license information.
6061da546SpatrickSPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7061da546Spatrick"""
8061da546Spatrick# summary provider for NSSet
9061da546Spatrickimport lldb
10061da546Spatrickimport ctypes
11061da546Spatrickimport lldb.runtime.objc.objc_runtime
12061da546Spatrickimport lldb.formatters.metrics
13061da546Spatrickimport CFBag
14061da546Spatrickimport lldb.formatters.Logger
15061da546Spatrick
16061da546Spatrickstatistics = lldb.formatters.metrics.Metrics()
17061da546Spatrickstatistics.add_metric('invalid_isa')
18061da546Spatrickstatistics.add_metric('invalid_pointer')
19061da546Spatrickstatistics.add_metric('unknown_class')
20061da546Spatrickstatistics.add_metric('code_notrun')
21061da546Spatrick
22061da546Spatrick# despite the similary to synthetic children providers, these classes are not
23061da546Spatrick# trying to provide anything but the port number of an NSMachPort, so they need not
24061da546Spatrick# obey the interface specification for synthetic children providers
25061da546Spatrick
26061da546Spatrick
27061da546Spatrickclass NSCFSet_SummaryProvider:
28061da546Spatrick
29061da546Spatrick    def adjust_for_architecture(self):
30061da546Spatrick        pass
31061da546Spatrick
32061da546Spatrick    def __init__(self, valobj, params):
33061da546Spatrick        logger = lldb.formatters.Logger.Logger()
34061da546Spatrick        self.valobj = valobj
35061da546Spatrick        self.sys_params = params
36061da546Spatrick        if not(self.sys_params.types_cache.NSUInteger):
37061da546Spatrick            if self.sys_params.is_64_bit:
38061da546Spatrick                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
39061da546Spatrick                ).GetBasicType(lldb.eBasicTypeUnsignedLong)
40061da546Spatrick            else:
41061da546Spatrick                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
42061da546Spatrick                ).GetBasicType(lldb.eBasicTypeUnsignedInt)
43061da546Spatrick        self.update()
44061da546Spatrick
45061da546Spatrick    def update(self):
46061da546Spatrick        logger = lldb.formatters.Logger.Logger()
47061da546Spatrick        self.adjust_for_architecture()
48061da546Spatrick
49061da546Spatrick    # one pointer is the ISA
50061da546Spatrick    # then we have one other internal pointer, plus
51061da546Spatrick    # 4 bytes worth of flags. hence, these values
52061da546Spatrick    def offset(self):
53061da546Spatrick        logger = lldb.formatters.Logger.Logger()
54061da546Spatrick        if self.sys_params.is_64_bit:
55061da546Spatrick            return 20
56061da546Spatrick        else:
57061da546Spatrick            return 12
58061da546Spatrick
59061da546Spatrick    def count(self):
60061da546Spatrick        logger = lldb.formatters.Logger.Logger()
61061da546Spatrick        vcount = self.valobj.CreateChildAtOffset(
62061da546Spatrick            "count", self.offset(), self.sys_params.types_cache.NSUInteger)
63061da546Spatrick        return vcount.GetValueAsUnsigned(0)
64061da546Spatrick
65061da546Spatrick
66061da546Spatrickclass NSSetUnknown_SummaryProvider:
67061da546Spatrick
68061da546Spatrick    def adjust_for_architecture(self):
69061da546Spatrick        pass
70061da546Spatrick
71061da546Spatrick    def __init__(self, valobj, params):
72061da546Spatrick        logger = lldb.formatters.Logger.Logger()
73061da546Spatrick        self.valobj = valobj
74061da546Spatrick        self.sys_params = params
75061da546Spatrick        self.update()
76061da546Spatrick
77061da546Spatrick    def update(self):
78061da546Spatrick        logger = lldb.formatters.Logger.Logger()
79061da546Spatrick        self.adjust_for_architecture()
80061da546Spatrick
81061da546Spatrick    def count(self):
82061da546Spatrick        logger = lldb.formatters.Logger.Logger()
83061da546Spatrick        stream = lldb.SBStream()
84061da546Spatrick        self.valobj.GetExpressionPath(stream)
85061da546Spatrick        expr = "(int)[" + stream.GetData() + " count]"
86061da546Spatrick        num_children_vo = self.valobj.CreateValueFromExpression("count", expr)
87061da546Spatrick        if num_children_vo.IsValid():
88061da546Spatrick            return num_children_vo.GetValueAsUnsigned(0)
89061da546Spatrick        return '<variable is not NSSet>'
90061da546Spatrick
91061da546Spatrick
92061da546Spatrickclass NSSetI_SummaryProvider:
93061da546Spatrick
94061da546Spatrick    def adjust_for_architecture(self):
95061da546Spatrick        pass
96061da546Spatrick
97061da546Spatrick    def __init__(self, valobj, params):
98061da546Spatrick        logger = lldb.formatters.Logger.Logger()
99061da546Spatrick        self.valobj = valobj
100061da546Spatrick        self.sys_params = params
101061da546Spatrick        if not(self.sys_params.types_cache.NSUInteger):
102061da546Spatrick            if self.sys_params.is_64_bit:
103061da546Spatrick                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
104061da546Spatrick                ).GetBasicType(lldb.eBasicTypeUnsignedLong)
105061da546Spatrick            else:
106061da546Spatrick                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
107061da546Spatrick                ).GetBasicType(lldb.eBasicTypeUnsignedInt)
108061da546Spatrick        self.update()
109061da546Spatrick
110061da546Spatrick    def update(self):
111061da546Spatrick        logger = lldb.formatters.Logger.Logger()
112061da546Spatrick        self.adjust_for_architecture()
113061da546Spatrick
114061da546Spatrick    # we just need to skip the ISA and the count immediately follows
115061da546Spatrick    def offset(self):
116061da546Spatrick        logger = lldb.formatters.Logger.Logger()
117061da546Spatrick        return self.sys_params.pointer_size
118061da546Spatrick
119061da546Spatrick    def count(self):
120061da546Spatrick        logger = lldb.formatters.Logger.Logger()
121061da546Spatrick        num_children_vo = self.valobj.CreateChildAtOffset(
122061da546Spatrick            "count", self.offset(), self.sys_params.types_cache.NSUInteger)
123061da546Spatrick        value = num_children_vo.GetValueAsUnsigned(0)
124061da546Spatrick        if value is not None:
125061da546Spatrick            # the MSB on immutable sets seems to be taken by some other data
126061da546Spatrick            # not sure if it is a bug or some weird sort of feature, but masking it out
127061da546Spatrick            # gets the count right (unless, of course, someone's dictionaries grow
128061da546Spatrick            #                       too large - but I have not tested this)
129061da546Spatrick            if self.sys_params.is_64_bit:
130061da546Spatrick                value = value & ~0xFF00000000000000
131061da546Spatrick            else:
132061da546Spatrick                value = value & ~0xFF000000
133061da546Spatrick        return value
134061da546Spatrick
135061da546Spatrick
136061da546Spatrickclass NSSetM_SummaryProvider:
137061da546Spatrick
138061da546Spatrick    def adjust_for_architecture(self):
139061da546Spatrick        pass
140061da546Spatrick
141061da546Spatrick    def __init__(self, valobj, params):
142061da546Spatrick        logger = lldb.formatters.Logger.Logger()
143061da546Spatrick        self.valobj = valobj
144061da546Spatrick        self.sys_params = params
145061da546Spatrick        if not(self.sys_params.types_cache.NSUInteger):
146061da546Spatrick            if self.sys_params.is_64_bit:
147061da546Spatrick                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
148061da546Spatrick                ).GetBasicType(lldb.eBasicTypeUnsignedLong)
149061da546Spatrick            else:
150061da546Spatrick                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
151061da546Spatrick                ).GetBasicType(lldb.eBasicTypeUnsignedInt)
152061da546Spatrick        self.update()
153061da546Spatrick
154061da546Spatrick    def update(self):
155061da546Spatrick        logger = lldb.formatters.Logger.Logger()
156061da546Spatrick        self.adjust_for_architecture()
157061da546Spatrick
158061da546Spatrick    # we just need to skip the ISA and the count immediately follows
159061da546Spatrick    def offset(self):
160061da546Spatrick        logger = lldb.formatters.Logger.Logger()
161061da546Spatrick        return self.sys_params.pointer_size
162061da546Spatrick
163061da546Spatrick    def count(self):
164061da546Spatrick        logger = lldb.formatters.Logger.Logger()
165061da546Spatrick        num_children_vo = self.valobj.CreateChildAtOffset(
166061da546Spatrick            "count", self.offset(), self.sys_params.types_cache.NSUInteger)
167061da546Spatrick        return num_children_vo.GetValueAsUnsigned(0)
168061da546Spatrick
169061da546Spatrick
170061da546Spatrickclass NSCountedSet_SummaryProvider:
171061da546Spatrick
172061da546Spatrick    def adjust_for_architecture(self):
173061da546Spatrick        pass
174061da546Spatrick
175061da546Spatrick    def __init__(self, valobj, params):
176061da546Spatrick        logger = lldb.formatters.Logger.Logger()
177061da546Spatrick        self.valobj = valobj
178061da546Spatrick        self.sys_params = params
179061da546Spatrick        if not (self.sys_params.types_cache.voidptr):
180061da546Spatrick            self.sys_params.types_cache.voidptr = self.valobj.GetType(
181061da546Spatrick            ).GetBasicType(lldb.eBasicTypeVoid).GetPointerType()
182061da546Spatrick        self.update()
183061da546Spatrick
184061da546Spatrick    def update(self):
185061da546Spatrick        logger = lldb.formatters.Logger.Logger()
186061da546Spatrick        self.adjust_for_architecture()
187061da546Spatrick
188061da546Spatrick    # an NSCountedSet is implemented using a CFBag whose pointer just follows
189061da546Spatrick    # the ISA
190061da546Spatrick    def offset(self):
191061da546Spatrick        logger = lldb.formatters.Logger.Logger()
192061da546Spatrick        return self.sys_params.pointer_size
193061da546Spatrick
194061da546Spatrick    def count(self):
195061da546Spatrick        logger = lldb.formatters.Logger.Logger()
196061da546Spatrick        cfbag_vo = self.valobj.CreateChildAtOffset(
197061da546Spatrick            "bag_impl", self.offset(), self.sys_params.types_cache.voidptr)
198061da546Spatrick        return CFBag.CFBagRef_SummaryProvider(
199061da546Spatrick            cfbag_vo, self.sys_params).length()
200061da546Spatrick
201061da546Spatrick
202061da546Spatrickdef GetSummary_Impl(valobj):
203061da546Spatrick    logger = lldb.formatters.Logger.Logger()
204061da546Spatrick    global statistics
205061da546Spatrick    class_data, wrapper = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(
206061da546Spatrick        valobj, statistics)
207061da546Spatrick    if wrapper:
208061da546Spatrick        return wrapper
209061da546Spatrick
210061da546Spatrick    name_string = class_data.class_name()
211061da546Spatrick    logger >> "class name is: " + str(name_string)
212061da546Spatrick
213061da546Spatrick    if name_string == '__NSCFSet':
214061da546Spatrick        wrapper = NSCFSet_SummaryProvider(valobj, class_data.sys_params)
215061da546Spatrick        statistics.metric_hit('code_notrun', valobj)
216061da546Spatrick    elif name_string == '__NSSetI':
217061da546Spatrick        wrapper = NSSetI_SummaryProvider(valobj, class_data.sys_params)
218061da546Spatrick        statistics.metric_hit('code_notrun', valobj)
219061da546Spatrick    elif name_string == '__NSSetM':
220061da546Spatrick        wrapper = NSSetM_SummaryProvider(valobj, class_data.sys_params)
221061da546Spatrick        statistics.metric_hit('code_notrun', valobj)
222061da546Spatrick    elif name_string == 'NSCountedSet':
223061da546Spatrick        wrapper = NSCountedSet_SummaryProvider(valobj, class_data.sys_params)
224061da546Spatrick        statistics.metric_hit('code_notrun', valobj)
225061da546Spatrick    else:
226061da546Spatrick        wrapper = NSSetUnknown_SummaryProvider(valobj, class_data.sys_params)
227061da546Spatrick        statistics.metric_hit(
228061da546Spatrick            'unknown_class',
229061da546Spatrick            valobj.GetName() +
230061da546Spatrick            " seen as " +
231061da546Spatrick            name_string)
232061da546Spatrick    return wrapper
233061da546Spatrick
234061da546Spatrick
235061da546Spatrickdef NSSet_SummaryProvider(valobj, dict):
236061da546Spatrick    logger = lldb.formatters.Logger.Logger()
237061da546Spatrick    provider = GetSummary_Impl(valobj)
238061da546Spatrick    if provider is not None:
239061da546Spatrick        try:
240061da546Spatrick            summary = provider.count()
241061da546Spatrick        except:
242061da546Spatrick            summary = None
243061da546Spatrick        if summary is None:
244061da546Spatrick            summary = '<variable is not NSSet>'
245*f6aab3d8Srobert        if isinstance(summary, str):
246061da546Spatrick            return summary
247061da546Spatrick        else:
248061da546Spatrick            summary = str(summary) + \
249061da546Spatrick                (' objects' if summary != 1 else ' object')
250061da546Spatrick        return summary
251061da546Spatrick    return 'Summary Unavailable'
252061da546Spatrick
253061da546Spatrick
254061da546Spatrickdef NSSet_SummaryProvider2(valobj, dict):
255061da546Spatrick    logger = lldb.formatters.Logger.Logger()
256061da546Spatrick    provider = GetSummary_Impl(valobj)
257061da546Spatrick    if provider is not None:
258061da546Spatrick        if isinstance(
259061da546Spatrick                provider,
260061da546Spatrick                lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
261061da546Spatrick            return provider.message()
262061da546Spatrick        try:
263061da546Spatrick            summary = provider.count()
264061da546Spatrick        except:
265061da546Spatrick            summary = None
266061da546Spatrick        logger >> "got summary " + str(summary)
267061da546Spatrick        # for some reason, one needs to clear some bits for the count returned
268061da546Spatrick        # to be correct when using directly CF*SetRef as compared to NS*Set
269061da546Spatrick        # this only happens on 64bit, and the bit mask was derived through
270061da546Spatrick        # experimentation (if counts start looking weird, then most probably
271061da546Spatrick        #                  the mask needs to be changed)
272061da546Spatrick        if summary is None:
273061da546Spatrick            summary = '<variable is not CFSet>'
274*f6aab3d8Srobert        if isinstance(summary, str):
275061da546Spatrick            return summary
276061da546Spatrick        else:
277061da546Spatrick            if provider.sys_params.is_64_bit:
278061da546Spatrick                summary = summary & ~0x1fff000000000000
279061da546Spatrick            summary = '@"' + str(summary) + \
280061da546Spatrick                (' values"' if summary != 1 else ' value"')
281061da546Spatrick        return summary
282061da546Spatrick    return 'Summary Unavailable'
283061da546Spatrick
284061da546Spatrick
285061da546Spatrickdef __lldb_init_module(debugger, dict):
286061da546Spatrick    debugger.HandleCommand(
287061da546Spatrick        "type summary add -F NSSet.NSSet_SummaryProvider NSSet")
288061da546Spatrick    debugger.HandleCommand(
289061da546Spatrick        "type summary add -F NSSet.NSSet_SummaryProvider2 CFSetRef CFMutableSetRef")
290