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