xref: /openbsd-src/gnu/llvm/lldb/examples/summaries/cocoa/CFDictionary.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# 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
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 count for an NSDictionary, so they need not
29# obey the interface specification for synthetic children providers
30
31
32class NSCFDictionary_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    # empirically determined on both 32 and 64bit desktop Mac OS X
55    # probably boils down to 2 pointers and 4 bytes of data, but
56    # the description of __CFDictionary is not readily available so most
57    # of this is guesswork, plain and simple
58    def offset(self):
59        logger = lldb.formatters.Logger.Logger()
60        if self.sys_params.is_64_bit:
61            return 20
62        else:
63            return 12
64
65    def num_children(self):
66        logger = lldb.formatters.Logger.Logger()
67        num_children_vo = self.valobj.CreateChildAtOffset(
68            "count", self.offset(), self.sys_params.types_cache.NSUInteger)
69        return num_children_vo.GetValueAsUnsigned(0)
70
71
72class NSDictionaryI_SummaryProvider:
73
74    def adjust_for_architecture(self):
75        pass
76
77    def __init__(self, valobj, params):
78        logger = lldb.formatters.Logger.Logger()
79        self.valobj = valobj
80        self.sys_params = params
81        if not(self.sys_params.types_cache.NSUInteger):
82            if self.sys_params.is_64_bit:
83                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
84                ).GetBasicType(lldb.eBasicTypeUnsignedLong)
85            else:
86                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
87                ).GetBasicType(lldb.eBasicTypeUnsignedInt)
88        self.update()
89
90    def update(self):
91        logger = lldb.formatters.Logger.Logger()
92        self.adjust_for_architecture()
93
94    # we just need to skip the ISA and the count immediately follows
95    def offset(self):
96        logger = lldb.formatters.Logger.Logger()
97        return self.sys_params.pointer_size
98
99    def num_children(self):
100        logger = lldb.formatters.Logger.Logger()
101        num_children_vo = self.valobj.CreateChildAtOffset(
102            "count", self.offset(), self.sys_params.types_cache.NSUInteger)
103        value = num_children_vo.GetValueAsUnsigned(0)
104        if value is not None:
105            # the MS6bits on immutable dictionaries seem to be taken by the LSB of capacity
106            # not sure if it is a bug or some weird sort of feature, but masking that out
107            # gets the count right
108            if self.sys_params.is_64_bit:
109                value = value & ~0xFC00000000000000
110            else:
111                value = value & ~0xFC000000
112        return value
113
114
115class NSDictionaryM_SummaryProvider:
116
117    def adjust_for_architecture(self):
118        pass
119
120    def __init__(self, valobj, params):
121        logger = lldb.formatters.Logger.Logger()
122        self.valobj = valobj
123        self.sys_params = params
124        if not(self.sys_params.types_cache.NSUInteger):
125            if self.sys_params.is_64_bit:
126                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
127                ).GetBasicType(lldb.eBasicTypeUnsignedLong)
128            else:
129                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
130                ).GetBasicType(lldb.eBasicTypeUnsignedInt)
131        self.update()
132
133    def update(self):
134        logger = lldb.formatters.Logger.Logger()
135        self.adjust_for_architecture()
136
137    # we just need to skip the ISA and the count immediately follows
138    def offset(self):
139        return self.sys_params.pointer_size
140
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)
145        value = num_children_vo.GetValueAsUnsigned(0)
146        if value is not None:
147            # the MS6bits on immutable dictionaries seem to be taken by the LSB of capacity
148            # not sure if it is a bug or some weird sort of feature, but masking that out
149            # gets the count right
150            if self.sys_params.is_64_bit:
151                value = value & ~0xFC00000000000000
152            else:
153                value = value & ~0xFC000000
154        return value
155
156
157class NSDictionaryUnknown_SummaryProvider:
158
159    def adjust_for_architecture(self):
160        pass
161
162    def __init__(self, valobj, params):
163        logger = lldb.formatters.Logger.Logger()
164        self.valobj = valobj
165        self.sys_params = params
166        self.update()
167
168    def update(self):
169        logger = lldb.formatters.Logger.Logger()
170        self.adjust_for_architecture()
171
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]")
178        if num_children_vo.IsValid():
179            return num_children_vo.GetValueAsUnsigned(0)
180        return '<variable is not NSDictionary>'
181
182
183def GetSummary_Impl(valobj):
184    logger = lldb.formatters.Logger.Logger()
185    global statistics
186    class_data, wrapper = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(
187        valobj, statistics)
188    if wrapper:
189        return wrapper
190
191    name_string = class_data.class_name()
192
193    logger >> "class name is: " + str(name_string)
194
195    if name_string == '__NSCFDictionary':
196        wrapper = NSCFDictionary_SummaryProvider(valobj, class_data.sys_params)
197        statistics.metric_hit('code_notrun', valobj)
198    elif name_string == '__NSDictionaryI':
199        wrapper = NSDictionaryI_SummaryProvider(valobj, class_data.sys_params)
200        statistics.metric_hit('code_notrun', valobj)
201    elif name_string == '__NSDictionaryM':
202        wrapper = NSDictionaryM_SummaryProvider(valobj, class_data.sys_params)
203        statistics.metric_hit('code_notrun', valobj)
204    else:
205        wrapper = NSDictionaryUnknown_SummaryProvider(
206            valobj, class_data.sys_params)
207        statistics.metric_hit(
208            'unknown_class',
209            valobj.GetName() +
210            " seen as " +
211            name_string)
212    return wrapper
213
214
215def CFDictionary_SummaryProvider(valobj, dict):
216    logger = lldb.formatters.Logger.Logger()
217    provider = GetSummary_Impl(valobj)
218    if provider is not None:
219        if isinstance(
220                provider,
221                lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
222            return provider.message()
223        try:
224            summary = provider.num_children()
225        except:
226            summary = None
227        logger >> "got summary " + str(summary)
228        if summary is None:
229            return '<variable is not NSDictionary>'
230        if isinstance(summary, basestring):
231            return summary
232        return str(summary) + (" key/value pairs" if summary !=
233                               1 else " key/value pair")
234    return 'Summary Unavailable'
235
236
237def CFDictionary_SummaryProvider2(valobj, dict):
238    logger = lldb.formatters.Logger.Logger()
239    provider = GetSummary_Impl(valobj)
240    if provider is not None:
241        if isinstance(
242                provider,
243                lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
244            return provider.message()
245        try:
246            summary = provider.num_children()
247        except:
248            summary = None
249        logger >> "got summary " + str(summary)
250        if summary is None:
251            summary = '<variable is not CFDictionary>'
252        if isinstance(summary, basestring):
253            return summary
254        else:
255            # needed on OSX Mountain Lion
256            if provider.sys_params.is_64_bit:
257                summary = summary & ~0x0f1f000000000000
258            summary = '@"' + str(summary) + \
259                (' entries"' if summary != 1 else ' entry"')
260        return summary
261    return 'Summary Unavailable'
262
263
264def __lldb_init_module(debugger, dict):
265    debugger.HandleCommand(
266        "type summary add -F CFDictionary.CFDictionary_SummaryProvider NSDictionary")
267    debugger.HandleCommand(
268        "type summary add -F CFDictionary.CFDictionary_SummaryProvider2 CFDictionaryRef CFMutableDictionaryRef")
269