1*061da546Spatrick"""
2*061da546SpatrickLLDB AppKit formatters
3*061da546Spatrick
4*061da546SpatrickPart of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5*061da546SpatrickSee https://llvm.org/LICENSE.txt for license information.
6*061da546SpatrickSPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7*061da546Spatrick"""
8*061da546Spatrick
9*061da546Spatrick# summary provider for CF(Mutable)BitVector
10*061da546Spatrickimport lldb
11*061da546Spatrickimport ctypes
12*061da546Spatrickimport lldb.runtime.objc.objc_runtime
13*061da546Spatrickimport lldb.formatters.metrics
14*061da546Spatrickimport lldb.formatters.Logger
15*061da546Spatrick
16*061da546Spatrick# first define some utility functions
17*061da546Spatrick
18*061da546Spatrick
19*061da546Spatrickdef byte_index(abs_pos):
20*061da546Spatrick    logger = lldb.formatters.Logger.Logger()
21*061da546Spatrick    return abs_pos / 8
22*061da546Spatrick
23*061da546Spatrick
24*061da546Spatrickdef bit_index(abs_pos):
25*061da546Spatrick    logger = lldb.formatters.Logger.Logger()
26*061da546Spatrick    return abs_pos & 7
27*061da546Spatrick
28*061da546Spatrick
29*061da546Spatrickdef get_bit(byte, index):
30*061da546Spatrick    logger = lldb.formatters.Logger.Logger()
31*061da546Spatrick    if index < 0 or index > 7:
32*061da546Spatrick        return None
33*061da546Spatrick    return (byte >> (7 - index)) & 1
34*061da546Spatrick
35*061da546Spatrick
36*061da546Spatrickdef grab_array_item_data(pointer, index):
37*061da546Spatrick    logger = lldb.formatters.Logger.Logger()
38*061da546Spatrick    return pointer.GetPointeeData(index, 1)
39*061da546Spatrick
40*061da546Spatrickstatistics = lldb.formatters.metrics.Metrics()
41*061da546Spatrickstatistics.add_metric('invalid_isa')
42*061da546Spatrickstatistics.add_metric('invalid_pointer')
43*061da546Spatrickstatistics.add_metric('unknown_class')
44*061da546Spatrickstatistics.add_metric('code_notrun')
45*061da546Spatrick
46*061da546Spatrick# despite the similary to synthetic children providers, these classes are not
47*061da546Spatrick# trying to provide anything but a summary for a CF*BitVector, so they need not
48*061da546Spatrick# obey the interface specification for synthetic children providers
49*061da546Spatrick
50*061da546Spatrick
51*061da546Spatrickclass CFBitVectorKnown_SummaryProvider:
52*061da546Spatrick
53*061da546Spatrick    def adjust_for_architecture(self):
54*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
55*061da546Spatrick        self.uiint_size = self.sys_params.types_cache.NSUInteger.GetByteSize()
56*061da546Spatrick        pass
57*061da546Spatrick
58*061da546Spatrick    def __init__(self, valobj, params):
59*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
60*061da546Spatrick        self.valobj = valobj
61*061da546Spatrick        self.sys_params = params
62*061da546Spatrick        if not(self.sys_params.types_cache.NSUInteger):
63*061da546Spatrick            if self.sys_params.is_64_bit:
64*061da546Spatrick                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
65*061da546Spatrick                ).GetBasicType(lldb.eBasicTypeUnsignedLong)
66*061da546Spatrick            else:
67*061da546Spatrick                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
68*061da546Spatrick                ).GetBasicType(lldb.eBasicTypeUnsignedInt)
69*061da546Spatrick        if not(self.sys_params.types_cache.charptr):
70*061da546Spatrick            self.sys_params.types_cache.charptr = self.valobj.GetType(
71*061da546Spatrick            ).GetBasicType(lldb.eBasicTypeChar).GetPointerType()
72*061da546Spatrick        self.update()
73*061da546Spatrick
74*061da546Spatrick    def update(self):
75*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
76*061da546Spatrick        self.adjust_for_architecture()
77*061da546Spatrick
78*061da546Spatrick    # we skip the CFRuntimeBase
79*061da546Spatrick    # then the next CFIndex is the count
80*061da546Spatrick    # then we skip another CFIndex and then we get at a byte array
81*061da546Spatrick    # that wraps the individual bits
82*061da546Spatrick
83*061da546Spatrick    def contents(self):
84*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
85*061da546Spatrick        count_vo = self.valobj.CreateChildAtOffset(
86*061da546Spatrick            "count",
87*061da546Spatrick            self.sys_params.cfruntime_size,
88*061da546Spatrick            self.sys_params.types_cache.NSUInteger)
89*061da546Spatrick        count = count_vo.GetValueAsUnsigned(0)
90*061da546Spatrick        if count == 0:
91*061da546Spatrick            return '(empty)'
92*061da546Spatrick
93*061da546Spatrick        array_vo = self.valobj.CreateChildAtOffset(
94*061da546Spatrick            "data",
95*061da546Spatrick            self.sys_params.cfruntime_size +
96*061da546Spatrick            2 *
97*061da546Spatrick            self.uiint_size,
98*061da546Spatrick            self.sys_params.types_cache.charptr)
99*061da546Spatrick
100*061da546Spatrick        data_list = []
101*061da546Spatrick        cur_byte_pos = None
102*061da546Spatrick        for i in range(0, count):
103*061da546Spatrick            if cur_byte_pos is None:
104*061da546Spatrick                cur_byte_pos = byte_index(i)
105*061da546Spatrick                cur_byte = grab_array_item_data(array_vo, cur_byte_pos)
106*061da546Spatrick                cur_byte_val = cur_byte.uint8[0]
107*061da546Spatrick            else:
108*061da546Spatrick                byte_pos = byte_index(i)
109*061da546Spatrick                # do not fetch the pointee data every single time through
110*061da546Spatrick                if byte_pos != cur_byte_pos:
111*061da546Spatrick                    cur_byte_pos = byte_pos
112*061da546Spatrick                    cur_byte = grab_array_item_data(array_vo, cur_byte_pos)
113*061da546Spatrick                    cur_byte_val = cur_byte.uint8[0]
114*061da546Spatrick            bit = get_bit(cur_byte_val, bit_index(i))
115*061da546Spatrick            if (i % 4) == 0:
116*061da546Spatrick                data_list.append(' ')
117*061da546Spatrick            if bit == 1:
118*061da546Spatrick                data_list.append('1')
119*061da546Spatrick            else:
120*061da546Spatrick                data_list.append('0')
121*061da546Spatrick        return ''.join(data_list)
122*061da546Spatrick
123*061da546Spatrick
124*061da546Spatrickclass CFBitVectorUnknown_SummaryProvider:
125*061da546Spatrick
126*061da546Spatrick    def adjust_for_architecture(self):
127*061da546Spatrick        pass
128*061da546Spatrick
129*061da546Spatrick    def __init__(self, valobj, params):
130*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
131*061da546Spatrick        self.valobj = valobj
132*061da546Spatrick        self.sys_params = params
133*061da546Spatrick        self.update()
134*061da546Spatrick
135*061da546Spatrick    def update(self):
136*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
137*061da546Spatrick        self.adjust_for_architecture()
138*061da546Spatrick
139*061da546Spatrick    def contents(self):
140*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
141*061da546Spatrick        return '<unable to summarize this CFBitVector>'
142*061da546Spatrick
143*061da546Spatrick
144*061da546Spatrickdef GetSummary_Impl(valobj):
145*061da546Spatrick    logger = lldb.formatters.Logger.Logger()
146*061da546Spatrick    global statistics
147*061da546Spatrick    class_data, wrapper = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(
148*061da546Spatrick        valobj, statistics)
149*061da546Spatrick    if wrapper:
150*061da546Spatrick        return wrapper
151*061da546Spatrick
152*061da546Spatrick    name_string = class_data.class_name()
153*061da546Spatrick    actual_name = name_string
154*061da546Spatrick
155*061da546Spatrick    logger >> "name string got was " + \
156*061da546Spatrick        str(name_string) + " but actual name is " + str(actual_name)
157*061da546Spatrick
158*061da546Spatrick    if class_data.is_cftype():
159*061da546Spatrick        # CFBitVectorRef does not expose an actual NSWrapper type, so we have to check that this is
160*061da546Spatrick        # an NSCFType and then check we are a pointer-to CFBitVectorRef
161*061da546Spatrick        valobj_type = valobj.GetType()
162*061da546Spatrick        if valobj_type.IsValid() and valobj_type.IsPointerType():
163*061da546Spatrick            valobj_type = valobj_type.GetPointeeType()
164*061da546Spatrick            if valobj_type.IsValid():
165*061da546Spatrick                actual_name = valobj_type.GetName()
166*061da546Spatrick        if actual_name == '__CFBitVector' or actual_name == '__CFMutableBitVector':
167*061da546Spatrick            wrapper = CFBitVectorKnown_SummaryProvider(
168*061da546Spatrick                valobj, class_data.sys_params)
169*061da546Spatrick            statistics.metric_hit('code_notrun', valobj)
170*061da546Spatrick        else:
171*061da546Spatrick            wrapper = CFBitVectorUnknown_SummaryProvider(
172*061da546Spatrick                valobj, class_data.sys_params)
173*061da546Spatrick            print(actual_name)
174*061da546Spatrick    else:
175*061da546Spatrick        wrapper = CFBitVectorUnknown_SummaryProvider(
176*061da546Spatrick            valobj, class_data.sys_params)
177*061da546Spatrick        print(name_string)
178*061da546Spatrick        statistics.metric_hit(
179*061da546Spatrick            'unknown_class',
180*061da546Spatrick            valobj.GetName() +
181*061da546Spatrick            " seen as " +
182*061da546Spatrick            name_string)
183*061da546Spatrick    return wrapper
184*061da546Spatrick
185*061da546Spatrick
186*061da546Spatrickdef CFBitVector_SummaryProvider(valobj, dict):
187*061da546Spatrick    logger = lldb.formatters.Logger.Logger()
188*061da546Spatrick    provider = GetSummary_Impl(valobj)
189*061da546Spatrick    if provider is not None:
190*061da546Spatrick        if isinstance(
191*061da546Spatrick                provider,
192*061da546Spatrick                lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
193*061da546Spatrick            return provider.message()
194*061da546Spatrick        try:
195*061da546Spatrick            summary = provider.contents()
196*061da546Spatrick        except:
197*061da546Spatrick            summary = None
198*061da546Spatrick        logger >> "summary got from provider: " + str(summary)
199*061da546Spatrick        if summary is None or summary == '':
200*061da546Spatrick            summary = '<variable is not CFBitVector>'
201*061da546Spatrick        return summary
202*061da546Spatrick    return 'Summary Unavailable'
203*061da546Spatrick
204*061da546Spatrick
205*061da546Spatrickdef __lldb_init_module(debugger, dict):
206*061da546Spatrick    debugger.HandleCommand(
207*061da546Spatrick        "type summary add -F CFBitVector.CFBitVector_SummaryProvider CFBitVectorRef CFMutableBitVectorRef")
208