• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 NSArray
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# much less functional than the other two cases below
28# just runs code to get to the count and then returns
29# no children
30
31
32class NSArrayKVC_SynthProvider:
33
34    def adjust_for_architecture(self):
35        pass
36
37    def __init__(self, valobj, dict, params):
38        logger = lldb.formatters.Logger.Logger()
39        self.valobj = valobj
40        self.update()
41
42    def update(self):
43        logger = lldb.formatters.Logger.Logger()
44        self.adjust_for_architecture()
45
46    def num_children(self):
47        logger = lldb.formatters.Logger.Logger()
48        stream = lldb.SBStream()
49        self.valobj.GetExpressionPath(stream)
50        num_children_vo = self.valobj.CreateValueFromExpression(
51            "count", "(int)[" + stream.GetData() + " count]")
52        if num_children_vo.IsValid():
53            return num_children_vo.GetValueAsUnsigned(0)
54        return "<variable is not NSArray>"
55
56# much less functional than the other two cases below
57# just runs code to get to the count and then returns
58# no children
59
60
61class NSArrayCF_SynthProvider:
62
63    def adjust_for_architecture(self):
64        pass
65
66    def __init__(self, valobj, dict, params):
67        logger = lldb.formatters.Logger.Logger()
68        self.valobj = valobj
69        self.sys_params = params
70        if not (self.sys_params.types_cache.ulong):
71            self.sys_params.types_cache.ulong = self.valobj.GetType(
72            ).GetBasicType(lldb.eBasicTypeUnsignedLong)
73        self.update()
74
75    def update(self):
76        logger = lldb.formatters.Logger.Logger()
77        self.adjust_for_architecture()
78
79    def num_children(self):
80        logger = lldb.formatters.Logger.Logger()
81        num_children_vo = self.valobj.CreateChildAtOffset(
82            "count", self.sys_params.cfruntime_size, self.sys_params.types_cache.ulong)
83        return num_children_vo.GetValueAsUnsigned(0)
84
85
86class NSArrayI_SynthProvider:
87
88    def adjust_for_architecture(self):
89        pass
90
91    def __init__(self, valobj, dict, params):
92        logger = lldb.formatters.Logger.Logger()
93        self.valobj = valobj
94        self.sys_params = params
95        if not(self.sys_params.types_cache.long):
96            self.sys_params.types_cache.long = self.valobj.GetType(
97            ).GetBasicType(lldb.eBasicTypeLong)
98        self.update()
99
100    def update(self):
101        logger = lldb.formatters.Logger.Logger()
102        self.adjust_for_architecture()
103
104    # skip the isa pointer and get at the size
105    def num_children(self):
106        logger = lldb.formatters.Logger.Logger()
107        count = self.valobj.CreateChildAtOffset(
108            "count",
109            self.sys_params.pointer_size,
110            self.sys_params.types_cache.long)
111        return count.GetValueAsUnsigned(0)
112
113
114class NSArrayM_SynthProvider:
115
116    def adjust_for_architecture(self):
117        pass
118
119    def __init__(self, valobj, dict, params):
120        logger = lldb.formatters.Logger.Logger()
121        self.valobj = valobj
122        self.sys_params = params
123        if not(self.sys_params.types_cache.long):
124            self.sys_params.types_cache.long = self.valobj.GetType(
125            ).GetBasicType(lldb.eBasicTypeLong)
126        self.update()
127
128    def update(self):
129        logger = lldb.formatters.Logger.Logger()
130        self.adjust_for_architecture()
131
132    # skip the isa pointer and get at the size
133    def num_children(self):
134        logger = lldb.formatters.Logger.Logger()
135        count = self.valobj.CreateChildAtOffset(
136            "count",
137            self.sys_params.pointer_size,
138            self.sys_params.types_cache.long)
139        return count.GetValueAsUnsigned(0)
140
141# this is the actual synth provider, but is just a wrapper that checks
142# whether valobj is an instance of __NSArrayI or __NSArrayM and sets up an
143# appropriate backend layer to do the computations
144
145
146class NSArray_SynthProvider:
147
148    def adjust_for_architecture(self):
149        pass
150
151    def __init__(self, valobj, dict):
152        logger = lldb.formatters.Logger.Logger()
153        self.valobj = valobj
154        self.adjust_for_architecture()
155        self.error = False
156        self.wrapper = self.make_wrapper()
157        self.invalid = (self.wrapper is None)
158
159    def num_children(self):
160        logger = lldb.formatters.Logger.Logger()
161        if self.wrapper is None:
162            return 0
163        return self.wrapper.num_children()
164
165    def update(self):
166        logger = lldb.formatters.Logger.Logger()
167        if self.wrapper is None:
168            return
169        self.wrapper.update()
170
171    # this code acts as our defense against NULL and uninitialized
172    # NSArray pointers, which makes it much longer than it would be otherwise
173    def make_wrapper(self):
174        logger = lldb.formatters.Logger.Logger()
175        if self.valobj.GetValueAsUnsigned() == 0:
176            self.error = True
177            return lldb.runtime.objc.objc_runtime.InvalidPointer_Description(
178                True)
179        else:
180            global statistics
181            class_data, wrapper = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(
182                self.valobj, statistics)
183            if wrapper:
184                self.error = True
185                return wrapper
186
187        name_string = class_data.class_name()
188
189        logger >> "Class name is " + str(name_string)
190
191        if name_string == '__NSArrayI':
192            wrapper = NSArrayI_SynthProvider(
193                self.valobj, dict, class_data.sys_params)
194            statistics.metric_hit('code_notrun', self.valobj.GetName())
195        elif name_string == '__NSArrayM':
196            wrapper = NSArrayM_SynthProvider(
197                self.valobj, dict, class_data.sys_params)
198            statistics.metric_hit('code_notrun', self.valobj.GetName())
199        elif name_string == '__NSCFArray':
200            wrapper = NSArrayCF_SynthProvider(
201                self.valobj, dict, class_data.sys_params)
202            statistics.metric_hit('code_notrun', self.valobj.GetName())
203        else:
204            wrapper = NSArrayKVC_SynthProvider(
205                self.valobj, dict, class_data.sys_params)
206            statistics.metric_hit(
207                'unknown_class', str(
208                    self.valobj.GetName()) + " seen as " + name_string)
209        return wrapper
210
211
212def CFArray_SummaryProvider(valobj, dict):
213    logger = lldb.formatters.Logger.Logger()
214    provider = NSArray_SynthProvider(valobj, dict)
215    if not provider.invalid:
216        if provider.error:
217            return provider.wrapper.message()
218        try:
219            summary = int(provider.num_children())
220        except:
221            summary = None
222        logger >> "provider gave me " + str(summary)
223        if summary is None:
224            summary = '<variable is not NSArray>'
225        elif isinstance(summary, basestring):
226            pass
227        else:
228            # we format it like it were a CFString to make it look the same as
229            # the summary from Xcode
230            summary = '@"' + str(summary) + \
231                (" objects" if summary != 1 else " object") + '"'
232        return summary
233    return 'Summary Unavailable'
234
235
236def __lldb_init_module(debugger, dict):
237    debugger.HandleCommand(
238        "type summary add -F CFArray.CFArray_SummaryProvider NSArray CFArrayRef CFMutableArrayRef")
239