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