Unreviewed, rolling out r233934.
[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.WTFStringView_SummaryProvider WTF::StringView')
41     debugger.HandleCommand('type summary add --expand -F lldb_webkit.WTFAtomicString_SummaryProvider WTF::AtomicString')
42     debugger.HandleCommand('type summary add --expand -F lldb_webkit.WTFVector_SummaryProvider -x "WTF::Vector<.+>$"')
43     debugger.HandleCommand('type summary add --expand -F lldb_webkit.WTFHashTable_SummaryProvider -x "WTF::HashTable<.+>$"')
44     debugger.HandleCommand('type summary add --expand -F lldb_webkit.WTFMediaTime_SummaryProvider WTF::MediaTime')
45     debugger.HandleCommand('type synthetic add -x "WTF::Vector<.+>$" --python-class lldb_webkit.WTFVectorProvider')
46     debugger.HandleCommand('type synthetic add -x "WTF::HashTable<.+>$" --python-class lldb_webkit.WTFHashTableProvider')
47     debugger.HandleCommand('type summary add -F lldb_webkit.WebCoreLayoutUnit_SummaryProvider WebCore::LayoutUnit')
48     debugger.HandleCommand('type summary add -F lldb_webkit.WebCoreLayoutSize_SummaryProvider WebCore::LayoutSize')
49     debugger.HandleCommand('type summary add -F lldb_webkit.WebCoreLayoutPoint_SummaryProvider WebCore::LayoutPoint')
50     debugger.HandleCommand('type summary add -F lldb_webkit.WebCoreURL_SummaryProvider WebCore::URL')
51
52
53 def WTFString_SummaryProvider(valobj, dict):
54     provider = WTFStringProvider(valobj, dict)
55     return "{ length = %d, contents = '%s' }" % (provider.get_length(), provider.to_string())
56
57
58 def WTFStringImpl_SummaryProvider(valobj, dict):
59     provider = WTFStringImplProvider(valobj, dict)
60     if not provider.is_initialized():
61         return ""
62     return "{ length = %d, is8bit = %d, contents = '%s' }" % (provider.get_length(), provider.is_8bit(), provider.to_string())
63
64
65 def WTFStringView_SummaryProvider(valobj, dict):
66     provider = WTFStringViewProvider(valobj, dict)
67     return "{ length = %d, contents = '%s' }" % (provider.get_length(), provider.to_string())
68
69
70 def WTFAtomicString_SummaryProvider(valobj, dict):
71     return WTFString_SummaryProvider(valobj.GetChildMemberWithName('m_string'), dict)
72
73
74 def WTFVector_SummaryProvider(valobj, dict):
75     provider = WTFVectorProvider(valobj, dict)
76     return "{ size = %d, capacity = %d }" % (provider.size, provider.capacity)
77
78
79 def WTFHashTable_SummaryProvider(valobj, dict):
80     provider = WTFHashTableProvider(valobj, dict)
81     return "{ tableSize = %d, keyCount = %d }" % (provider.tableSize(), provider.keyCount())
82
83
84 def WTFMediaTime_SummaryProvider(valobj, dict):
85     provider = WTFMediaTimeProvider(valobj, dict)
86     if provider.isInvalid():
87         return "{ Invalid }"
88     if provider.isPositiveInfinity():
89         return "{ +Infinity }"
90     if provider.isNegativeInfinity():
91         return "{ -Infinity }"
92     if provider.isIndefinite():
93         return "{ Indefinite }"
94     if provider.hasDoubleValue():
95         return "{ %f }" % (provider.timeValueAsDouble())
96     return "{ %d/%d, %f }" % (provider.timeValue(), provider.timeScale(), float(provider.timeValue()) / provider.timeScale())
97
98
99 def WebCoreURL_SummaryProvider(valobj, dict):
100     provider = WebCoreURLProvider(valobj, dict)
101     return "{ %s }" % provider.to_string()
102
103
104 def WebCoreLayoutUnit_SummaryProvider(valobj, dict):
105     provider = WebCoreLayoutUnitProvider(valobj, dict)
106     return "{ %s }" % provider.to_string()
107
108
109 def WebCoreLayoutSize_SummaryProvider(valobj, dict):
110     provider = WebCoreLayoutSizeProvider(valobj, dict)
111     return "{ width = %s, height = %s }" % (provider.get_width(), provider.get_height())
112
113
114 def WebCoreLayoutPoint_SummaryProvider(valobj, dict):
115     provider = WebCoreLayoutPointProvider(valobj, dict)
116     return "{ x = %s, y = %s }" % (provider.get_x(), provider.get_y())
117
118
119 def btjs(debugger, command, result, internal_dict):
120     '''Prints a stack trace of current thread with JavaScript frames decoded.  Takes optional frame count argument'''
121
122     target = debugger.GetSelectedTarget()
123     addressFormat = '#0{width}x'.format(width=target.GetAddressByteSize() * 2 + 2)
124     process = target.GetProcess()
125     thread = process.GetSelectedThread()
126
127     if target.FindFunctions("JSC::ExecState::describeFrame").GetSize() or target.FindFunctions("_ZN3JSC9ExecState13describeFrameEv").GetSize():
128         annotateJSFrames = True
129     else:
130         annotateJSFrames = False
131
132     if not annotateJSFrames:
133         print("Warning: Can't find JSC::ExecState::describeFrame() in executable to annotate JavaScript frames")
134
135     backtraceDepth = thread.GetNumFrames()
136
137     if len(command) > 0:
138         try:
139             backtraceDepth = int(command)
140         except ValueError:
141             return
142
143     threadFormat = '* thread #{num}: tid = {tid:#x}, {pcAddr:' + addressFormat + '}, queue = \'{queueName}, stop reason = {stopReason}'
144     print(threadFormat.format(num=thread.GetIndexID(), tid=thread.GetThreadID(), pcAddr=thread.GetFrameAtIndex(0).GetPC(), queueName=thread.GetQueueName(), stopReason=thread.GetStopDescription(30)))
145
146     for frame in thread:
147         if backtraceDepth < 1:
148             break
149
150         backtraceDepth = backtraceDepth - 1
151
152         function = frame.GetFunction()
153
154         if annotateJSFrames and not frame or not frame.GetSymbol() or frame.GetSymbol().GetName() == "llint_entry":
155             callFrame = frame.GetSP()
156             JSFrameDescription = frame.EvaluateExpression("((JSC::ExecState*)0x%x)->describeFrame()" % frame.GetFP()).GetSummary()
157             if not JSFrameDescription:
158                 JSFrameDescription = frame.EvaluateExpression("((JSC::CallFrame*)0x%x)->describeFrame()" % frame.GetFP()).GetSummary()
159             if not JSFrameDescription:
160                 JSFrameDescription = frame.EvaluateExpression("(char*)_ZN3JSC9ExecState13describeFrameEv(0x%x)" % frame.GetFP()).GetSummary()
161             if JSFrameDescription:
162                 JSFrameDescription = string.strip(JSFrameDescription, '"')
163                 frameFormat = '    frame #{num}: {addr:' + addressFormat + '} {desc}'
164                 print(frameFormat.format(num=frame.GetFrameID(), addr=frame.GetPC(), desc=JSFrameDescription))
165                 continue
166         print('    %s' % frame)
167
168 # FIXME: Provide support for the following types:
169 # def WTFVector_SummaryProvider(valobj, dict):
170 # def WTFCString_SummaryProvider(valobj, dict):
171 # def WebCoreQualifiedName_SummaryProvider(valobj, dict):
172 # def JSCIdentifier_SummaryProvider(valobj, dict):
173 # def JSCJSString_SummaryProvider(valobj, dict):
174
175
176 def guess_string_length(valobj, charSize, error):
177     if not valobj.GetValue():
178         return 0
179
180     maxLength = 256
181
182     pointer = valobj.GetValueAsUnsigned()
183     contents = valobj.GetProcess().ReadMemory(pointer, maxLength * charSize, lldb.SBError())
184     format = 'B' if charSize == 1 else 'H'
185
186     for i in xrange(0, maxLength):
187         if not struct.unpack_from(format, contents, i * charSize)[0]:
188             return i
189
190     return maxLength
191
192 def ustring_to_string(valobj, error, length=None):
193     if length is None:
194         length = guess_string_length(valobj, 2, error)
195     else:
196         length = int(length)
197
198     if length == 0:
199         return ""
200
201     pointer = valobj.GetValueAsUnsigned()
202     contents = valobj.GetProcess().ReadMemory(pointer, length * 2, lldb.SBError())
203
204     # lldb does not (currently) support returning unicode from python summary providers,
205     # so potentially convert this to ascii by escaping
206     string = contents.decode('utf16')
207     try:
208         return str(string)
209     except:
210         return string.encode('unicode_escape')
211
212 def lstring_to_string(valobj, error, length=None):
213     if length is None:
214         length = guess_string_length(valobj, 1, error)
215     else:
216         length = int(length)
217
218     if length == 0:
219         return ""
220
221     pointer = valobj.GetValueAsUnsigned()
222     contents = valobj.GetProcess().ReadMemory(pointer, length, lldb.SBError())
223
224     # lldb does not (currently) support returning unicode from python summary providers,
225     # so potentially convert this to ascii by escaping
226     string = contents.decode('utf8')
227     try:
228         return str(string)
229     except:
230         return string.encode('unicode_escape')
231
232 class WTFStringImplProvider:
233     def __init__(self, valobj, dict):
234         # FIXME: For some reason lldb(1) sometimes has an issue accessing members of WTF::StringImplShape
235         # via a WTF::StringImpl pointer (why?). As a workaround we explicitly cast to WTF::StringImplShape*.
236         string_impl_shape_ptr_type = valobj.GetTarget().FindFirstType('WTF::StringImplShape').GetPointerType()
237         self.valobj = valobj.Cast(string_impl_shape_ptr_type)
238
239     def get_length(self):
240         return self.valobj.GetChildMemberWithName('m_length').GetValueAsUnsigned(0)
241
242     def get_data8(self):
243         return self.valobj.GetChildAtIndex(2).GetChildMemberWithName('m_data8')
244
245     def get_data16(self):
246         return self.valobj.GetChildAtIndex(2).GetChildMemberWithName('m_data16')
247
248     def to_string(self):
249         error = lldb.SBError()
250
251         if not self.is_initialized():
252             return u""
253
254         if self.is_8bit():
255             return lstring_to_string(self.get_data8(), error, self.get_length())
256         return ustring_to_string(self.get_data16(), error, self.get_length())
257
258     def is_8bit(self):
259         # FIXME: find a way to access WTF::StringImpl::s_hashFlag8BitBuffer
260         return bool(self.valobj.GetChildMemberWithName('m_hashAndFlags').GetValueAsUnsigned(0) \
261             & 1 << 3)
262
263     def is_initialized(self):
264         return self.valobj.GetValueAsUnsigned() != 0
265
266
267 class WTFStringViewProvider:
268     def __init__(self, valobj, dict):
269         self.valobj = valobj
270
271     def is_8bit(self):
272         return bool(self.valobj.GetChildMemberWithName('m_is8Bit').GetValueAsUnsigned(0))
273
274     def get_length(self):
275         return self.valobj.GetChildMemberWithName('m_length').GetValueAsUnsigned(0)
276
277     def get_characters(self):
278         return self.valobj.GetChildMemberWithName('m_characters')
279
280     def to_string(self):
281         error = lldb.SBError()
282
283         if not self.get_characters() or not self.get_length():
284             return u""
285
286         if self.is_8bit():
287             return lstring_to_string(self.get_characters(), error, self.get_length())
288         return ustring_to_string(self.get_characters(), error, self.get_length())
289
290
291 class WTFStringProvider:
292     def __init__(self, valobj, dict):
293         self.valobj = valobj
294
295     def stringimpl(self):
296         impl_ptr = self.valobj.GetChildMemberWithName('m_impl').GetChildMemberWithName('m_ptr')
297         return WTFStringImplProvider(impl_ptr, dict)
298
299     def get_length(self):
300         impl = self.stringimpl()
301         if not impl:
302             return 0
303         return impl.get_length()
304
305     def to_string(self):
306         impl = self.stringimpl()
307         if not impl:
308             return u""
309         return impl.to_string()
310
311
312 class WebCoreLayoutUnitProvider:
313     "Print a WebCore::LayoutUnit"
314     def __init__(self, valobj, dict):
315         self.valobj = valobj
316
317     def to_string(self):
318         layoutUnitValue = self.valobj.GetChildMemberWithName('m_value').GetValueAsSigned(0)
319         return "%gpx (%d)" % (float(layoutUnitValue) / 64, layoutUnitValue)
320
321
322 class WebCoreLayoutSizeProvider:
323     "Print a WebCore::LayoutSize"
324     def __init__(self, valobj, dict):
325         self.valobj = valobj
326
327     def get_width(self):
328         return WebCoreLayoutUnitProvider(self.valobj.GetChildMemberWithName('m_width'), dict).to_string()
329
330     def get_height(self):
331         return WebCoreLayoutUnitProvider(self.valobj.GetChildMemberWithName('m_height'), dict).to_string()
332
333
334 class WebCoreLayoutPointProvider:
335     "Print a WebCore::LayoutPoint"
336     def __init__(self, valobj, dict):
337         self.valobj = valobj
338
339     def get_x(self):
340         return WebCoreLayoutUnitProvider(self.valobj.GetChildMemberWithName('m_x'), dict).to_string()
341
342     def get_y(self):
343         return WebCoreLayoutUnitProvider(self.valobj.GetChildMemberWithName('m_y'), dict).to_string()
344
345
346 class WebCoreURLProvider:
347     "Print a WebCore::URL"
348     def __init__(self, valobj, dict):
349         self.valobj = valobj
350
351     def to_string(self):
352         return WTFStringProvider(self.valobj.GetChildMemberWithName('m_string'), dict).to_string()
353
354 class WTFVectorProvider:
355     def __init__(self, valobj, internal_dict):
356         self.valobj = valobj
357         self.update()
358
359     def num_children(self):
360         return self.size + 3
361
362     def get_child_index(self, name):
363         if name == "m_size":
364             return self.size
365         elif name == "m_capacity":
366             return self.size + 1
367         elif name == "m_buffer":
368             return self.size + 2
369         else:
370             return int(name.lstrip('[').rstrip(']'))
371
372     def get_child_at_index(self, index):
373         if index == self.size:
374             return self.valobj.GetChildMemberWithName("m_size")
375         elif index == self.size + 1:
376             return self.valobj.GetChildMemberWithName("m_capacity")
377         elif index == self.size + 2:
378             return self.buffer
379         elif index < self.size:
380             offset = index * self.data_size
381             child = self.buffer.CreateChildAtOffset('[' + str(index) + ']', offset, self.data_type)
382             return child
383         else:
384             return None
385
386     def update(self):
387         self.buffer = self.valobj.GetChildMemberWithName('m_buffer')
388         self.size = self.valobj.GetChildMemberWithName('m_size').GetValueAsUnsigned(0)
389         self.capacity = self.valobj.GetChildMemberWithName('m_capacity').GetValueAsUnsigned(0)
390         self.data_type = self.buffer.GetType().GetPointeeType()
391         self.data_size = self.data_type.GetByteSize()
392
393     def has_children(self):
394         return True
395
396
397 class WTFHashTableProvider:
398     def __init__(self, valobj, internal_dict):
399         self.valobj = valobj
400         self.update()
401
402     def num_children(self):
403         return self.tableSize() + 5
404
405     def get_child_index(self, name):
406         if name == "m_table":
407             return self.tableSize()
408         elif name == "m_tableSize":
409             return self.tableSize() + 1
410         elif name == "m_tableSizeMask":
411             return self.tableSize() + 2
412         elif name == "m_keyCount":
413             return self.tableSize() + 3
414         elif name == "m_deletedCount":
415             return self.tableSize() + 4
416         else:
417             return int(name.lstrip('[').rstrip(']'))
418
419     def get_child_at_index(self, index):
420         if index == self.tableSize():
421             return self.valobj.GetChildMemberWithName('m_table')
422         elif index == self.tableSize() + 1:
423             return self.valobj.GetChildMemberWithName('m_tableSize')
424         elif index == self.tableSize() + 2:
425             return self.valobj.GetChildMemberWithName('m_tableSizeMask')
426         elif index == self.tableSize() + 3:
427             return self.valobj.GetChildMemberWithName('m_keyCount')
428         elif index == self.tableSize() + 4:
429             return self.valobj.GetChildMemberWithName('m_deletedCount')
430         elif index < self.tableSize():
431             table = self.valobj.GetChildMemberWithName('m_table')
432             return table.CreateChildAtOffset('[' + str(index) + ']', index * self.data_size, self.data_type)
433         else:
434             return None
435
436     def tableSize(self):
437         return self.valobj.GetChildMemberWithName('m_tableSize').GetValueAsUnsigned(0)
438
439     def keyCount(self):
440         return self.valobj.GetChildMemberWithName('m_keyCount').GetValueAsUnsigned(0)
441
442     def update(self):
443         self.data_type = self.valobj.GetType().GetTemplateArgumentType(0)
444         self.data_size = self.data_type.GetByteSize()
445
446     def has_children(self):
447         return True
448
449
450 class WTFMediaTimeProvider:
451     def __init__(self, valobj, internal_dict):
452         self.valobj = valobj
453
454     def timeValue(self):
455         return self.valobj.GetChildMemberWithName('m_timeValue').GetValueAsSigned(0)
456
457     def timeValueAsDouble(self):
458         error = lldb.SBError()
459         return self.valobj.GetChildMemberWithName('m_timeValueAsDouble').GetData().GetDouble(error, 0)
460
461     def timeScale(self):
462         return self.valobj.GetChildMemberWithName('m_timeScale').GetValueAsSigned(0)
463
464     def isInvalid(self):
465         return not self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 0)
466
467     def isPositiveInfinity(self):
468         return self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 2)
469
470     def isNegativeInfinity(self):
471         return self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 3)
472
473     def isIndefinite(self):
474         return self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 4)
475
476     def hasDoubleValue(self):
477         return self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 5)