4 Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 See https://llvm.org/LICENSE.txt for license information.
6 SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8 # example summary provider for NSNumber
9 # the real summary is now C++ code built into LLDB
13 import lldb
.runtime
.objc
.objc_runtime
14 import lldb
.formatters
.metrics
16 import lldb
.formatters
.Logger
18 statistics
= lldb
.formatters
.metrics
.Metrics()
19 statistics
.add_metric("invalid_isa")
20 statistics
.add_metric("invalid_pointer")
21 statistics
.add_metric("unknown_class")
22 statistics
.add_metric("code_notrun")
24 # despite the similary to synthetic children providers, these classes are not
25 # trying to provide anything but the port number of an NSNumber, so they need not
26 # obey the interface specification for synthetic children providers
29 class NSTaggedNumber_SummaryProvider
:
30 def adjust_for_architecture(self
):
33 def __init__(self
, valobj
, info_bits
, data
, params
):
34 logger
= lldb
.formatters
.Logger
.Logger()
36 self
.sys_params
= params
37 self
.info_bits
= info_bits
42 logger
= lldb
.formatters
.Logger
.Logger()
43 self
.adjust_for_architecture()
46 logger
= lldb
.formatters
.Logger
.Logger()
47 # in spite of the plenty of types made available by the public NSNumber API
48 # only a bunch of these are actually used in the internal implementation
49 # unfortunately, the original type information appears to be lost
50 # so we try to at least recover the proper magnitude of the data
51 if self
.info_bits
== 0:
52 return "(char)" + str(ord(ctypes
.c_char(chr(self
.data
% 256)).value
))
53 if self
.info_bits
== 4:
54 return "(short)" + str(ctypes
.c_short(self
.data
% (256 * 256)).value
)
55 if self
.info_bits
== 8:
57 ctypes
.c_int(self
.data
% (256 * 256 * 256 * 256)).value
59 if self
.info_bits
== 12:
60 return "(long)" + str(ctypes
.c_long(self
.data
).value
)
63 "unexpected value:(info="
71 class NSUntaggedNumber_SummaryProvider
:
72 def adjust_for_architecture(self
):
75 def __init__(self
, valobj
, params
):
76 logger
= lldb
.formatters
.Logger
.Logger()
78 self
.sys_params
= params
79 if not (self
.sys_params
.types_cache
.char
):
80 self
.sys_params
.types_cache
.char
= self
.valobj
.GetType().GetBasicType(
83 if not (self
.sys_params
.types_cache
.short
):
84 self
.sys_params
.types_cache
.short
= self
.valobj
.GetType().GetBasicType(
87 if not (self
.sys_params
.types_cache
.ushort
):
88 self
.sys_params
.types_cache
.ushort
= self
.valobj
.GetType().GetBasicType(
89 lldb
.eBasicTypeUnsignedShort
91 if not (self
.sys_params
.types_cache
.int):
92 self
.sys_params
.types_cache
.int = self
.valobj
.GetType().GetBasicType(
95 if not (self
.sys_params
.types_cache
.long):
96 self
.sys_params
.types_cache
.long = self
.valobj
.GetType().GetBasicType(
99 if not (self
.sys_params
.types_cache
.ulong
):
100 self
.sys_params
.types_cache
.ulong
= self
.valobj
.GetType().GetBasicType(
101 lldb
.eBasicTypeUnsignedLong
103 if not (self
.sys_params
.types_cache
.longlong
):
104 self
.sys_params
.types_cache
.longlong
= self
.valobj
.GetType().GetBasicType(
105 lldb
.eBasicTypeLongLong
107 if not (self
.sys_params
.types_cache
.ulonglong
):
108 self
.sys_params
.types_cache
.ulonglong
= self
.valobj
.GetType().GetBasicType(
109 lldb
.eBasicTypeUnsignedLongLong
111 if not (self
.sys_params
.types_cache
.float):
112 self
.sys_params
.types_cache
.float = self
.valobj
.GetType().GetBasicType(
115 if not (self
.sys_params
.types_cache
.double
):
116 self
.sys_params
.types_cache
.double
= self
.valobj
.GetType().GetBasicType(
117 lldb
.eBasicTypeDouble
122 logger
= lldb
.formatters
.Logger
.Logger()
123 self
.adjust_for_architecture()
126 logger
= lldb
.formatters
.Logger
.Logger()
128 # we need to skip the ISA, then the next byte tells us what to read
129 # we then skip one other full pointer worth of data and then fetch the contents
130 # if we are fetching an int64 value, one more pointer must be skipped
132 data_type_vo
= self
.valobj
.CreateChildAtOffset(
133 "dt", self
.sys_params
.pointer_size
, self
.sys_params
.types_cache
.char
135 data_type
= (data_type_vo
.GetValueAsUnsigned(0) % 256) & 0x1F
136 data_offset
= 2 * self
.sys_params
.pointer_size
137 if data_type
== 0b00001:
138 data_vo
= self
.valobj
.CreateChildAtOffset(
139 "data", data_offset
, self
.sys_params
.types_cache
.char
141 statistics
.metric_hit("code_notrun", self
.valobj
)
142 return "(char)" + str(
143 ord(ctypes
.c_char(chr(data_vo
.GetValueAsUnsigned(0))).value
)
145 elif data_type
== 0b0010:
146 data_vo
= self
.valobj
.CreateChildAtOffset(
147 "data", data_offset
, self
.sys_params
.types_cache
.short
149 statistics
.metric_hit("code_notrun", self
.valobj
)
150 return "(short)" + str(
151 ctypes
.c_short(data_vo
.GetValueAsUnsigned(0) % (256 * 256)).value
153 # IF tagged pointers are possible on 32bit+v2 runtime
154 # (of which the only existing instance should be iOS)
155 # then values of this type might be tagged
156 elif data_type
== 0b0011:
157 data_vo
= self
.valobj
.CreateChildAtOffset(
158 "data", data_offset
, self
.sys_params
.types_cache
.int
160 statistics
.metric_hit("code_notrun", self
.valobj
)
161 return "(int)" + str(
163 data_vo
.GetValueAsUnsigned(0) % (256 * 256 * 256 * 256)
166 # apparently, on is_64_bit architectures, these are the only values that will ever
167 # be represented by a non tagged pointers
168 elif data_type
== 0b10001:
169 data_offset
= data_offset
+ 8 # 8 is needed even if we are on 32bit
170 data_vo
= self
.valobj
.CreateChildAtOffset(
171 "data", data_offset
, self
.sys_params
.types_cache
.longlong
173 statistics
.metric_hit("code_notrun", self
.valobj
)
174 return "(long)" + str(ctypes
.c_long(data_vo
.GetValueAsUnsigned(0)).value
)
175 elif data_type
== 0b0100:
176 if self
.sys_params
.is_64_bit
:
177 data_offset
= data_offset
+ self
.sys_params
.pointer_size
178 data_vo
= self
.valobj
.CreateChildAtOffset(
179 "data", data_offset
, self
.sys_params
.types_cache
.longlong
181 statistics
.metric_hit("code_notrun", self
.valobj
)
182 return "(long)" + str(ctypes
.c_long(data_vo
.GetValueAsUnsigned(0)).value
)
183 elif data_type
== 0b0101:
184 data_vo
= self
.valobj
.CreateChildAtOffset(
185 "data", data_offset
, self
.sys_params
.types_cache
.longlong
187 data_plain
= int(str(data_vo
.GetValueAsUnsigned(0) & 0x00000000FFFFFFFF))
188 packed
= struct
.pack("I", data_plain
)
189 data_float
= struct
.unpack("f", packed
)[0]
190 statistics
.metric_hit("code_notrun", self
.valobj
)
191 return "(float)" + str(data_float
)
192 elif data_type
== 0b0110:
193 data_vo
= self
.valobj
.CreateChildAtOffset(
194 "data", data_offset
, self
.sys_params
.types_cache
.longlong
196 data_plain
= data_vo
.GetValueAsUnsigned(0)
197 data_double
= struct
.unpack("d", struct
.pack("Q", data_plain
))[0]
198 statistics
.metric_hit("code_notrun", self
.valobj
)
199 return "(double)" + str(data_double
)
200 statistics
.metric_hit(
202 str(valobj
.GetName()) + " had unknown data_type " + str(data_type
),
204 return "unexpected: dt = " + str(data_type
)
207 class NSUnknownNumber_SummaryProvider
:
208 def adjust_for_architecture(self
):
211 def __init__(self
, valobj
, params
):
212 logger
= lldb
.formatters
.Logger
.Logger()
214 self
.sys_params
= params
218 logger
= lldb
.formatters
.Logger
.Logger()
219 self
.adjust_for_architecture()
222 logger
= lldb
.formatters
.Logger
.Logger()
223 stream
= lldb
.SBStream()
224 self
.valobj
.GetExpressionPath(stream
)
225 expr
= "(NSString*)[" + stream
.GetData() + " stringValue]"
226 num_children_vo
= self
.valobj
.CreateValueFromExpression("str", expr
)
227 if num_children_vo
.IsValid():
228 return num_children_vo
.GetSummary()
229 return "<variable is not NSNumber>"
232 def GetSummary_Impl(valobj
):
233 logger
= lldb
.formatters
.Logger
.Logger()
238 ) = lldb
.runtime
.objc
.objc_runtime
.Utilities
.prepare_class_detection(
244 name_string
= class_data
.class_name()
245 logger
>> "class name is: " + str(name_string
)
247 if name_string
== "NSNumber" or name_string
== "__NSCFNumber":
248 if class_data
.is_tagged():
249 wrapper
= NSTaggedNumber_SummaryProvider(
251 class_data
.info_bits(),
253 class_data
.sys_params
,
255 statistics
.metric_hit("code_notrun", valobj
)
257 # the wrapper might be unable to decipher what is into the NSNumber
258 # and then have to run code on it
259 wrapper
= NSUntaggedNumber_SummaryProvider(valobj
, class_data
.sys_params
)
261 wrapper
= NSUnknownNumber_SummaryProvider(valobj
, class_data
.sys_params
)
262 statistics
.metric_hit(
263 "unknown_class", valobj
.GetName() + " seen as " + name_string
268 def NSNumber_SummaryProvider(valobj
, dict):
269 logger
= lldb
.formatters
.Logger
.Logger()
270 provider
= GetSummary_Impl(valobj
)
271 if provider
is not None:
273 provider
, lldb
.runtime
.objc
.objc_runtime
.SpecialSituation_Description
275 return provider
.message()
277 summary
= provider
.value()
278 except Exception as foo
:
282 logger
>> "got summary " + str(summary
)
284 summary
= "<variable is not NSNumber>"
286 return "Summary Unavailable"
289 def __lldb_init_module(debugger
, dict):
290 debugger
.HandleCommand(
291 "type summary add -F NSNumber.NSNumber_SummaryProvider NSNumber"
293 debugger
.HandleCommand(
294 "type summary add -F NSNumber.NSNumber_SummaryProvider __NSCFBoolean"
296 debugger
.HandleCommand(
297 "type summary add -F NSNumber.NSNumber_SummaryProvider __NSCFNumber"