REGRESSION(r216217): lldb_webkit.py no longer prints out a WTFString, StringImpl...
[WebKit-https.git] / Tools / lldb / lldb_webkit.py
1 # Copyright (C) 2012-2017 Apple Inc. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions
5 # are met:
6 # 1.  Redistributions of source code must retain the above copyright
7 #     notice, this list of conditions and the following disclaimer.
8 # 2.  Redistributions in binary form must reproduce the above copyright
9 #     notice, this list of conditions and the following disclaimer in the
10 #     documentation and/or other materials provided with the distribution.
11 #
12 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
13 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15 # DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
16 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
19 # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23 """
24     LLDB Support for WebKit Types
25
26     Add the following to your .lldbinit file to add WebKit Type summaries in LLDB and Xcode:
27
28     command script import {Path to WebKit Root}/Tools/lldb/lldb_webkit.py
29
30 """
31
32 import lldb
33 import string
34 import struct
35
36 def __lldb_init_module(debugger, dict):
37     debugger.HandleCommand('command script add -f lldb_webkit.btjs btjs')
38     debugger.HandleCommand('type summary add --expand -F lldb_webkit.WTFString_SummaryProvider WTF::String')
39     debugger.HandleCommand('type summary add --expand -F lldb_webkit.WTFStringImpl_SummaryProvider WTF::StringImpl')
40     debugger.HandleCommand('type summary add --expand -F lldb_webkit.WTFAtomicString_SummaryProvider WTF::AtomicString')
41     debugger.HandleCommand('type summary add --expand -F lldb_webkit.WTFVector_SummaryProvider -x "WTF::Vector<.+>$"')
42     debugger.HandleCommand('type summary add --expand -F lldb_webkit.WTFHashTable_SummaryProvider -x "WTF::HashTable<.+>$"')
43     debugger.HandleCommand('type summary add --expand -F lldb_webkit.WTFMediaTime_SummaryProvider WTF::MediaTime')
44     debugger.HandleCommand('type synthetic add -x "WTF::Vector<.+>$" --python-class lldb_webkit.WTFVectorProvider')
45     debugger.HandleCommand('type synthetic add -x "WTF::HashTable<.+>$" --python-class lldb_webkit.WTFHashTableProvider')
46     debugger.HandleCommand('type summary add -F lldb_webkit.WebCoreLayoutUnit_SummaryProvider WebCore::LayoutUnit')
47     debugger.HandleCommand('type summary add -F lldb_webkit.WebCoreLayoutSize_SummaryProvider WebCore::LayoutSize')
48     debugger.HandleCommand('type summary add -F lldb_webkit.WebCoreLayoutPoint_SummaryProvider WebCore::LayoutPoint')
49     debugger.HandleCommand('type summary add -F lldb_webkit.WebCoreURL_SummaryProvider WebCore::URL')
50
51 def WTFString_SummaryProvider(valobj, dict):
52     provider = WTFStringProvider(valobj, dict)
53     return "{ length = %d, contents = '%s' }" % (provider.get_length(), provider.to_string())
54
55
56 def WTFStringImpl_SummaryProvider(valobj, dict):
57     provider = WTFStringImplProvider(valobj, dict)
58     if not provider.is_initialized():
59         return ""
60     return "{ length = %d, is8bit = %d, contents = '%s' }" % (provider.get_length(), provider.is_8bit(), provider.to_string())
61
62
63 def WTFAtomicString_SummaryProvider(valobj, dict):
64     return WTFString_SummaryProvider(valobj.GetChildMemberWithName('m_string'), dict)
65
66
67 def WTFVector_SummaryProvider(valobj, dict):
68     provider = WTFVectorProvider(valobj, dict)
69     return "{ size = %d, capacity = %d }" % (provider.size, provider.capacity)
70
71
72 def WTFHashTable_SummaryProvider(valobj, dict):
73     provider = WTFHashTableProvider(valobj, dict)
74     return "{ tableSize = %d, keyCount = %d }" % (provider.tableSize(), provider.keyCount())
75
76
77 def WTFMediaTime_SummaryProvider(valobj, dict):
78     provider = WTFMediaTimeProvider(valobj, dict)
79     if provider.isInvalid():
80         return "{ Invalid }"
81     if provider.isPositiveInfinity():
82         return "{ +Infinity }"
83     if provider.isNegativeInfinity():
84         return "{ -Infinity }"
85     if provider.isIndefinite():
86         return "{ Indefinite }"
87     if provider.hasDoubleValue():
88         return "{ %f }" % (provider.timeValueAsDouble())
89     return "{ %d/%d, %f }" % (provider.timeValue(), provider.timeScale(), float(provider.timeValue()) / provider.timeScale())
90
91
92 def WebCoreURL_SummaryProvider(valobj, dict):
93     provider = WebCoreURLProvider(valobj, dict)
94     return "{ %s }" % provider.to_string()
95
96
97 def WebCoreLayoutUnit_SummaryProvider(valobj, dict):
98     provider = WebCoreLayoutUnitProvider(valobj, dict)
99     return "{ %s }" % provider.to_string()
100
101
102 def WebCoreLayoutSize_SummaryProvider(valobj, dict):
103     provider = WebCoreLayoutSizeProvider(valobj, dict)
104     return "{ width = %s, height = %s }" % (provider.get_width(), provider.get_height())
105
106
107 def WebCoreLayoutPoint_SummaryProvider(valobj, dict):
108     provider = WebCoreLayoutPointProvider(valobj, dict)
109     return "{ x = %s, y = %s }" % (provider.get_x(), provider.get_y())
110
111
112 def btjs(debugger, command, result, internal_dict):
113     '''Prints a stack trace of current thread with JavaScript frames decoded.  Takes optional frame count argument'''
114
115     target = debugger.GetSelectedTarget()
116     addressFormat = '#0{width}x'.format(width=target.GetAddressByteSize() * 2 + 2)
117     process = target.GetProcess()
118     thread = process.GetSelectedThread()
119
120     if target.FindFunctions("JSC::ExecState::describeFrame").GetSize() or target.FindFunctions("_ZN3JSC9ExecState13describeFrameEv").GetSize():
121         annotateJSFrames = True
122     else:
123         annotateJSFrames = False
124
125     if not annotateJSFrames:
126         print "Warning: Can't find JSC::ExecState::describeFrame() in executable to annotate JavaScript frames"
127
128     backtraceDepth = thread.GetNumFrames()
129
130     if len(command) > 0:
131         try:
132             backtraceDepth = int(command)
133         except ValueError:
134             return
135
136     threadFormat = '* thread #{num}: tid = {tid:#x}, {pcAddr:' + addressFormat + '}, queue = \'{queueName}, stop reason = {stopReason}'
137     print threadFormat.format(num=thread.GetIndexID(), tid=thread.GetThreadID(), pcAddr=thread.GetFrameAtIndex(0).GetPC(), queueName=thread.GetQueueName(), stopReason=thread.GetStopDescription(30))
138
139     for frame in thread:
140         if backtraceDepth < 1:
141             break
142
143         backtraceDepth = backtraceDepth - 1
144
145         function = frame.GetFunction()
146
147         if annotateJSFrames and not frame or not frame.GetSymbol() or frame.GetSymbol().GetName() == "llint_entry":
148             callFrame = frame.GetSP()
149             JSFrameDescription = frame.EvaluateExpression("((JSC::ExecState*)0x%x)->describeFrame()" % frame.GetFP()).GetSummary()
150             if not JSFrameDescription:
151                 JSFrameDescription = frame.EvaluateExpression("((JSC::CallFrame*)0x%x)->describeFrame()" % frame.GetFP()).GetSummary()
152             if not JSFrameDescription:
153                 JSFrameDescription = frame.EvaluateExpression("(char*)_ZN3JSC9ExecState13describeFrameEv(0x%x)" % frame.GetFP()).GetSummary()
154             if JSFrameDescription:
155                 JSFrameDescription = string.strip(JSFrameDescription, '"')
156                 frameFormat = '    frame #{num}: {addr:' + addressFormat + '} {desc}'
157                 print frameFormat.format(num=frame.GetFrameID(), addr=frame.GetPC(), desc=JSFrameDescription)
158                 continue
159         print '    %s' % frame
160
161 # FIXME: Provide support for the following types:
162 # def WTFVector_SummaryProvider(valobj, dict):
163 # def WTFCString_SummaryProvider(valobj, dict):
164 # def WebCoreQualifiedName_SummaryProvider(valobj, dict):
165 # def JSCIdentifier_SummaryProvider(valobj, dict):
166 # def JSCJSString_SummaryProvider(valobj, dict):
167
168
169 def guess_string_length(valobj, charSize, error):
170     if not valobj.GetValue():
171         return 0
172
173     maxLength = 256
174
175     pointer = valobj.GetValueAsUnsigned()
176     contents = valobj.GetProcess().ReadMemory(pointer, maxLength * charSize, lldb.SBError())
177     format = 'B' if charSize == 1 else 'H'
178
179     for i in xrange(0, maxLength):
180         if not struct.unpack_from(format, contents, i * charSize)[0]:
181             return i
182
183     return maxLength
184
185 def ustring_to_string(valobj, error, length=None):
186     if length is None:
187         length = guess_string_length(valobj, 2, error)
188     else:
189         length = int(length)
190
191     if length == 0:
192         return ""
193
194     pointer = valobj.GetValueAsUnsigned()
195     contents = valobj.GetProcess().ReadMemory(pointer, length * 2, lldb.SBError())
196
197     # lldb does not (currently) support returning unicode from python summary providers,
198     # so potentially convert this to ascii by escaping
199     string = contents.decode('utf16')
200     try:
201         return str(string)
202     except:
203         return string.encode('unicode_escape')
204
205 def lstring_to_string(valobj, error, length=None):
206     if length is None:
207         length = guess_string_length(valobj, 1, error)
208     else:
209         length = int(length)
210
211     if length == 0:
212         return ""
213
214     pointer = valobj.GetValueAsUnsigned()
215     contents = valobj.GetProcess().ReadMemory(pointer, length, lldb.SBError())
216
217     # lldb does not (currently) support returning unicode from python summary providers,
218     # so potentially convert this to ascii by escaping
219     string = contents.decode('utf8')
220     try:
221         return str(string)
222     except:
223         return string.encode('unicode_escape')
224
225 class WTFStringImplProvider:
226     def __init__(self, valobj, dict):
227         self.valobj = valobj
228
229     def get_length(self):
230         return self.valobj.GetChildMemberWithName('m_length').GetValueAsUnsigned(0)
231
232     def get_data8(self):
233         return self.valobj.GetChildAtIndex(0).GetChildAtIndex(2).GetChildMemberWithName('m_data8')
234
235     def get_data16(self):
236         return self.valobj.GetChildAtIndex(0).GetChildAtIndex(2).GetChildMemberWithName('m_data16')
237
238     def to_string(self):
239         error = lldb.SBError()
240
241         if not self.is_initialized():
242             return u""
243
244         if self.is_8bit():
245             return lstring_to_string(self.get_data8(), error, self.get_length())
246         return ustring_to_string(self.get_data16(), error, self.get_length())
247
248     def is_8bit(self):
249         # FIXME: find a way to access WTF::StringImpl::s_hashFlag8BitBuffer
250         return bool(self.valobj.GetChildMemberWithName('m_hashAndFlags').GetValueAsUnsigned(0) \
251             & 1 << 3)
252
253     def is_initialized(self):
254         return self.valobj.GetValueAsUnsigned() != 0
255
256
257 class WTFStringProvider:
258     def __init__(self, valobj, dict):
259         self.valobj = valobj
260
261     def stringimpl(self):
262         impl_ptr = self.valobj.GetChildMemberWithName('m_impl').GetChildMemberWithName('m_ptr')
263         return WTFStringImplProvider(impl_ptr, dict)
264
265     def get_length(self):
266         impl = self.stringimpl()
267         if not impl:
268             return 0
269         return impl.get_length()
270
271     def to_string(self):
272         impl = self.stringimpl()
273         if not impl:
274             return u""
275         return impl.to_string()
276
277
278 class WebCoreLayoutUnitProvider:
279     "Print a WebCore::LayoutUnit"
280     def __init__(self, valobj, dict):
281         self.valobj = valobj
282
283     def to_string(self):
284         layoutUnitValue = self.valobj.GetChildMemberWithName('m_value').GetValueAsSigned(0)
285         return "%gpx (%d)" % (float(layoutUnitValue) / 64, layoutUnitValue)
286
287
288 class WebCoreLayoutSizeProvider:
289     "Print a WebCore::LayoutSize"
290     def __init__(self, valobj, dict):
291         self.valobj = valobj
292
293     def get_width(self):
294         return WebCoreLayoutUnitProvider(self.valobj.GetChildMemberWithName('m_width'), dict).to_string()
295
296     def get_height(self):
297         return WebCoreLayoutUnitProvider(self.valobj.GetChildMemberWithName('m_height'), dict).to_string()
298
299
300 class WebCoreLayoutPointProvider:
301     "Print a WebCore::LayoutPoint"
302     def __init__(self, valobj, dict):
303         self.valobj = valobj
304
305     def get_x(self):
306         return WebCoreLayoutUnitProvider(self.valobj.GetChildMemberWithName('m_x'), dict).to_string()
307
308     def get_y(self):
309         return WebCoreLayoutUnitProvider(self.valobj.GetChildMemberWithName('m_y'), dict).to_string()
310
311
312 class WebCoreURLProvider:
313     "Print a WebCore::URL"
314     def __init__(self, valobj, dict):
315         self.valobj = valobj
316
317     def to_string(self):
318         return WTFStringProvider(self.valobj.GetChildMemberWithName('m_string'), dict).to_string()
319
320 class WTFVectorProvider:
321     def __init__(self, valobj, internal_dict):
322         self.valobj = valobj
323         self.update()
324
325     def num_children(self):
326         return self.size + 3
327
328     def get_child_index(self, name):
329         if name == "m_size":
330             return self.size
331         elif name == "m_capacity":
332             return self.size + 1
333         elif name == "m_buffer":
334             return self.size + 2
335         else:
336             return int(name.lstrip('[').rstrip(']'))
337
338     def get_child_at_index(self, index):
339         if index == self.size:
340             return self.valobj.GetChildMemberWithName("m_size")
341         elif index == self.size + 1:
342             return self.valobj.GetChildMemberWithName("m_capacity")
343         elif index == self.size + 2:
344             return self.buffer
345         elif index < self.size:
346             offset = index * self.data_size
347             child = self.buffer.CreateChildAtOffset('[' + str(index) + ']', offset, self.data_type)
348             return child
349         else:
350             return None
351
352     def update(self):
353         self.buffer = self.valobj.GetChildMemberWithName('m_buffer')
354         self.size = self.valobj.GetChildMemberWithName('m_size').GetValueAsUnsigned(0)
355         self.capacity = self.buffer.GetChildMemberWithName('m_capacity').GetValueAsUnsigned(0)
356         self.data_type = self.buffer.GetType().GetPointeeType()
357         self.data_size = self.data_type.GetByteSize()
358
359     def has_children(self):
360         return True
361
362
363 class WTFHashTableProvider:
364     def __init__(self, valobj, internal_dict):
365         self.valobj = valobj
366         self.update()
367
368     def num_children(self):
369         return self.tableSize() + 5
370
371     def get_child_index(self, name):
372         if name == "m_table":
373             return self.tableSize()
374         elif name == "m_tableSize":
375             return self.tableSize() + 1
376         elif name == "m_tableSizeMask":
377             return self.tableSize() + 2
378         elif name == "m_keyCount":
379             return self.tableSize() + 3
380         elif name == "m_deletedCount":
381             return self.tableSize() + 4
382         else:
383             return int(name.lstrip('[').rstrip(']'))
384
385     def get_child_at_index(self, index):
386         if index == self.tableSize():
387             return self.valobj.GetChildMemberWithName('m_table')
388         elif index == self.tableSize() + 1:
389             return self.valobj.GetChildMemberWithName('m_tableSize')
390         elif index == self.tableSize() + 2:
391             return self.valobj.GetChildMemberWithName('m_tableSizeMask')
392         elif index == self.tableSize() + 3:
393             return self.valobj.GetChildMemberWithName('m_keyCount')
394         elif index == self.tableSize() + 4:
395             return self.valobj.GetChildMemberWithName('m_deletedCount')
396         elif index < self.tableSize():
397             table = self.valobj.GetChildMemberWithName('m_table')
398             return table.CreateChildAtOffset('[' + str(index) + ']', index * self.data_size, self.data_type)
399         else:
400             return None
401
402     def tableSize(self):
403         return self.valobj.GetChildMemberWithName('m_tableSize').GetValueAsUnsigned(0)
404
405     def keyCount(self):
406         return self.valobj.GetChildMemberWithName('m_keyCount').GetValueAsUnsigned(0)
407
408     def update(self):
409         self.data_type = self.valobj.GetType().GetTemplateArgumentType(0)
410         self.data_size = self.data_type.GetByteSize()
411
412     def has_children(self):
413         return True
414
415
416 class WTFMediaTimeProvider:
417     def __init__(self, valobj, internal_dict):
418         self.valobj = valobj
419
420     def timeValue(self):
421         return self.valobj.GetChildMemberWithName('m_timeValue').GetValueAsSigned(0)
422
423     def timeValueAsDouble(self):
424         error = lldb.SBError()
425         return self.valobj.GetChildMemberWithName('m_timeValueAsDouble').GetData().GetDouble(error, 0)
426
427     def timeScale(self):
428         return self.valobj.GetChildMemberWithName('m_timeScale').GetValueAsSigned(0)
429
430     def isInvalid(self):
431         return not self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 0)
432
433     def isPositiveInfinity(self):
434         return self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 2)
435
436     def isNegativeInfinity(self):
437         return self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 3)
438
439     def isIndefinite(self):
440         return self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 4)
441
442     def hasDoubleValue(self):
443         return self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 5)