xref: /llvm-project/lldb/examples/summaries/cocoa/CFBag.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# example summary provider for CFBag
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
16statistics = lldb.formatters.metrics.Metrics()
17statistics.add_metric("invalid_isa")
18statistics.add_metric("invalid_pointer")
19statistics.add_metric("unknown_class")
20statistics.add_metric("code_notrun")
21
22# despite the similary to synthetic children providers, these classes are not
23# trying to provide anything but the length for an CFBag, so they need not
24# obey the interface specification for synthetic children providers
25
26
27class CFBagRef_SummaryProvider:
28    def adjust_for_architecture(self):
29        pass
30
31    def __init__(self, valobj, params):
32        logger = lldb.formatters.Logger.Logger()
33        self.valobj = valobj
34        self.sys_params = params
35        if not (self.sys_params.types_cache.NSUInteger):
36            if self.sys_params.is_64_bit:
37                self.sys_params.types_cache.NSUInteger = (
38                    self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
39                )
40            else:
41                self.sys_params.types_cache.NSUInteger = (
42                    self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt)
43                )
44        self.update()
45
46    def update(self):
47        logger = lldb.formatters.Logger.Logger()
48        self.adjust_for_architecture()
49
50    # 12 bytes on i386
51    # 20 bytes on x64
52    # most probably 2 pointers and 4 bytes of data
53    def offset(self):
54        logger = lldb.formatters.Logger.Logger()
55        if self.sys_params.is_64_bit:
56            return 20
57        else:
58            return 12
59
60    def length(self):
61        logger = lldb.formatters.Logger.Logger()
62        size = self.valobj.CreateChildAtOffset(
63            "count", self.offset(), self.sys_params.types_cache.NSUInteger
64        )
65        return size.GetValueAsUnsigned(0)
66
67
68class CFBagUnknown_SummaryProvider:
69    def adjust_for_architecture(self):
70        pass
71
72    def __init__(self, valobj, params):
73        logger = lldb.formatters.Logger.Logger()
74        self.valobj = valobj
75        self.sys_params = params
76        self.update()
77
78    def update(self):
79        logger = lldb.formatters.Logger.Logger()
80        self.adjust_for_architecture()
81
82    def length(self):
83        logger = lldb.formatters.Logger.Logger()
84        stream = lldb.SBStream()
85        self.valobj.GetExpressionPath(stream)
86        num_children_vo = self.valobj.CreateValueFromExpression(
87            "count", "(int)CFBagGetCount(" + stream.GetData() + " )"
88        )
89        if num_children_vo.IsValid():
90            return num_children_vo.GetValueAsUnsigned(0)
91        return "<variable is not CFBag>"
92
93
94def GetSummary_Impl(valobj):
95    logger = lldb.formatters.Logger.Logger()
96    global statistics
97    (
98        class_data,
99        wrapper,
100    ) = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(
101        valobj, statistics
102    )
103    if wrapper:
104        return wrapper
105
106    name_string = class_data.class_name()
107    actual_name = name_string
108
109    logger >> "name string got was " + str(name_string) + " but actual name is " + str(
110        actual_name
111    )
112
113    if class_data.is_cftype():
114        # CFBag does not expose an actual NSWrapper type, so we have to check that this is
115        # an NSCFType and then check we are a pointer-to __CFBag
116        valobj_type = valobj.GetType()
117        if valobj_type.IsValid() and valobj_type.IsPointerType():
118            valobj_type = valobj_type.GetPointeeType()
119            if valobj_type.IsValid():
120                actual_name = valobj_type.GetName()
121        if actual_name == "__CFBag" or actual_name == "const struct __CFBag":
122            wrapper = CFBagRef_SummaryProvider(valobj, class_data.sys_params)
123            statistics.metric_hit("code_notrun", valobj)
124            return wrapper
125    wrapper = CFBagUnknown_SummaryProvider(valobj, class_data.sys_params)
126    statistics.metric_hit("unknown_class", valobj.GetName() + " seen as " + actual_name)
127    return wrapper
128
129
130def CFBag_SummaryProvider(valobj, dict):
131    logger = lldb.formatters.Logger.Logger()
132    provider = GetSummary_Impl(valobj)
133    if provider is not None:
134        if isinstance(
135            provider, lldb.runtime.objc.objc_runtime.SpecialSituation_Description
136        ):
137            return provider.message()
138        try:
139            summary = provider.length()
140        except:
141            summary = None
142        logger >> "summary got from provider: " + str(summary)
143        # for some reason, one needs to clear some bits for the count
144        # to be correct when using CF(Mutable)BagRef on x64
145        # the bit mask was derived through experimentation
146        # (if counts start looking weird, then most probably
147        #  the mask needs to be changed)
148        if summary is None:
149            summary = "<variable is not CFBag>"
150        elif isinstance(summary, str):
151            pass
152        else:
153            if provider.sys_params.is_64_bit:
154                summary = summary & ~0x1FFF000000000000
155            if summary == 1:
156                summary = '@"1 value"'
157            else:
158                summary = '@"' + str(summary) + ' values"'
159        return summary
160    return "Summary Unavailable"
161
162
163def __lldb_init_module(debugger, dict):
164    debugger.HandleCommand(
165        "type summary add -F CFBag.CFBag_SummaryProvider CFBagRef CFMutableBagRef"
166    )
167