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 NSData
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 NSData, so they need not
24# obey the interface specification for synthetic children providers
25
26
27class NSConcreteData_SummaryProvider:
28
29    def adjust_for_architecture(self):
30        pass
31
32    def __init__(self, valobj, params):
33        logger = lldb.formatters.Logger.Logger()
34        logger >> "NSConcreteData_SummaryProvider __init__"
35        self.valobj = valobj
36        self.sys_params = params
37        if not(self.sys_params.types_cache.NSUInteger):
38            if self.sys_params.is_64_bit:
39                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
40                ).GetBasicType(lldb.eBasicTypeUnsignedLong)
41            else:
42                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
43                ).GetBasicType(lldb.eBasicTypeUnsignedInt)
44        self.update()
45
46    def update(self):
47        self.adjust_for_architecture()
48
49    # one pointer is the ISA
50    # then there are 32 bit worth of flags and other data
51    # however, on 64bit systems these are padded to be a full
52    # machine word long, which means we actually have two pointers
53    # worth of data to skip
54    def offset(self):
55        return 2 * self.sys_params.pointer_size
56
57    def length(self):
58        logger = lldb.formatters.Logger.Logger()
59        logger >> "NSConcreteData_SummaryProvider length"
60        size = self.valobj.CreateChildAtOffset(
61            "count", self.offset(), self.sys_params.types_cache.NSUInteger)
62        logger >> str(size)
63        logger >> str(size.GetValueAsUnsigned(0))
64        return size.GetValueAsUnsigned(0)
65
66
67class NSDataUnknown_SummaryProvider:
68
69    def adjust_for_architecture(self):
70        pass
71
72    def __init__(self, valobj, params):
73        logger = lldb.formatters.Logger.Logger()
74        logger >> "NSDataUnknown_SummaryProvider __init__"
75        self.valobj = valobj
76        self.sys_params = params
77        self.update()
78
79    def update(self):
80        self.adjust_for_architecture()
81
82    def length(self):
83        logger = lldb.formatters.Logger.Logger()
84        logger >> "NSDataUnknown_SummaryProvider length"
85        stream = lldb.SBStream()
86        self.valobj.GetExpressionPath(stream)
87        logger >> stream.GetData()
88        num_children_vo = self.valobj.CreateValueFromExpression(
89            "count", "(int)[" + stream.GetData() + " length]")
90        logger >> "still in after expression: " + str(num_children_vo)
91        if num_children_vo.IsValid():
92            logger >> "wow - expr output is valid: " + \
93                str(num_children_vo.GetValueAsUnsigned())
94            return num_children_vo.GetValueAsUnsigned(0)
95        logger >> "invalid expr output - too bad"
96        return '<variable is not NSData>'
97
98
99def GetSummary_Impl(valobj):
100    global statistics
101    logger = lldb.formatters.Logger.Logger()
102    logger >> "NSData GetSummary_Impl"
103    class_data, wrapper = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(
104        valobj, statistics)
105    if wrapper:
106        logger >> "got a wrapper summary - using it"
107        return wrapper
108
109    name_string = class_data.class_name()
110    logger >> "class name: " + name_string
111    if name_string == 'NSConcreteData' or \
112       name_string == 'NSConcreteMutableData' or \
113       name_string == '__NSCFData':
114        wrapper = NSConcreteData_SummaryProvider(valobj, class_data.sys_params)
115        statistics.metric_hit('code_notrun', valobj)
116    else:
117        wrapper = NSDataUnknown_SummaryProvider(valobj, class_data.sys_params)
118        statistics.metric_hit(
119            'unknown_class',
120            valobj.GetName() +
121            " seen as " +
122            name_string)
123    return wrapper
124
125
126def NSData_SummaryProvider(valobj, dict):
127    logger = lldb.formatters.Logger.Logger()
128    logger >> "NSData_SummaryProvider"
129    provider = GetSummary_Impl(valobj)
130    logger >> "found a summary provider, it is: " + str(provider)
131    if provider is not None:
132        try:
133            summary = provider.length()
134        except:
135            summary = None
136        logger >> "got a summary: it is " + str(summary)
137        if summary is None:
138            summary = '<variable is not NSData>'
139        elif isinstance(summary, str):
140            pass
141        else:
142            if summary == 1:
143                summary = '1 byte'
144            else:
145                summary = str(summary) + ' bytes'
146        return summary
147    return 'Summary Unavailable'
148
149
150def NSData_SummaryProvider2(valobj, dict):
151    logger = lldb.formatters.Logger.Logger()
152    logger >> "NSData_SummaryProvider2"
153    provider = GetSummary_Impl(valobj)
154    logger >> "found a summary provider, it is: " + str(provider)
155    if provider is not None:
156        if isinstance(
157                provider,
158                lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
159            return provider.message()
160        try:
161            summary = provider.length()
162        except:
163            summary = None
164        logger >> "got a summary: it is " + str(summary)
165        if summary is None:
166            summary = '<variable is not CFData>'
167        elif isinstance(summary, str):
168            pass
169        else:
170            if summary == 1:
171                summary = '@"1 byte"'
172            else:
173                summary = '@"' + str(summary) + ' bytes"'
174        return summary
175    return 'Summary Unavailable'
176
177
178def __lldb_init_module(debugger, dict):
179    debugger.HandleCommand(
180        "type summary add -F NSData.NSData_SummaryProvider NSData")
181    debugger.HandleCommand(
182        "type summary add -F NSData.NSData_SummaryProvider2 CFDataRef CFMutableDataRef")
183