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