btjs no longer accepts optional frame count argument
[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) > 0:
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         return "%gpx (%d)" % (float(layoutUnitValue) / 64, layoutUnitValue)
281
282
283 class WebCoreLayoutSizeProvider:
284     "Print a WebCore::LayoutSize"
285     def __init__(self, valobj, dict):
286         self.valobj = valobj
287
288     def get_width(self):
289         return WebCoreLayoutUnitProvider(self.valobj.GetChildMemberWithName('m_width'), dict).to_string()
290
291     def get_height(self):
292         return WebCoreLayoutUnitProvider(self.valobj.GetChildMemberWithName('m_height'), dict).to_string()
293
294
295 class WebCoreLayoutPointProvider:
296     "Print a WebCore::LayoutPoint"
297     def __init__(self, valobj, dict):
298         self.valobj = valobj
299
300     def get_x(self):
301         return WebCoreLayoutUnitProvider(self.valobj.GetChildMemberWithName('m_x'), dict).to_string()
302
303     def get_y(self):
304         return WebCoreLayoutUnitProvider(self.valobj.GetChildMemberWithName('m_y'), dict).to_string()
305
306
307 class WTFVectorProvider:
308     def __init__(self, valobj, internal_dict):
309         self.valobj = valobj
310         self.update()
311
312     def num_children(self):
313         return self.size + 3
314
315     def get_child_index(self, name):
316         if name == "m_size":
317             return self.size
318         elif name == "m_capacity":
319             return self.size + 1
320         elif name == "m_buffer":
321             return self.size + 2
322         else:
323             return int(name.lstrip('[').rstrip(']'))
324
325     def get_child_at_index(self, index):
326         if index == self.size:
327             return self.valobj.GetChildMemberWithName("m_size")
328         elif index == self.size + 1:
329             return self.valobj.GetChildMemberWithName("m_capacity")
330         elif index == self.size + 2:
331             return self.buffer
332         elif index < self.size:
333             offset = index * self.data_size
334             child = self.buffer.CreateChildAtOffset('[' + str(index) + ']', offset, self.data_type)
335             return child
336         else:
337             return None
338
339     def update(self):
340         self.buffer = self.valobj.GetChildMemberWithName('m_buffer')
341         self.size = self.valobj.GetChildMemberWithName('m_size').GetValueAsUnsigned(0)
342         self.capacity = self.buffer.GetChildMemberWithName('m_capacity').GetValueAsUnsigned(0)
343         self.data_type = self.buffer.GetType().GetPointeeType()
344         self.data_size = self.data_type.GetByteSize()
345
346     def has_children(self):
347         return True
348
349
350 class WTFHashTableProvider:
351     def __init__(self, valobj, internal_dict):
352         self.valobj = valobj
353         self.update()
354
355     def num_children(self):
356         return self.tableSize() + 5
357
358     def get_child_index(self, name):
359         if name == "m_table":
360             return self.tableSize()
361         elif name == "m_tableSize":
362             return self.tableSize() + 1
363         elif name == "m_tableSizeMask":
364             return self.tableSize() + 2
365         elif name == "m_keyCount":
366             return self.tableSize() + 3
367         elif name == "m_deletedCount":
368             return self.tableSize() + 4
369         else:
370             return int(name.lstrip('[').rstrip(']'))
371
372     def get_child_at_index(self, index):
373         if index == self.tableSize():
374             return self.valobj.GetChildMemberWithName('m_table')
375         elif index == self.tableSize() + 1:
376             return self.valobj.GetChildMemberWithName('m_tableSize')
377         elif index == self.tableSize() + 2:
378             return self.valobj.GetChildMemberWithName('m_tableSizeMask')
379         elif index == self.tableSize() + 3:
380             return self.valobj.GetChildMemberWithName('m_keyCount')
381         elif index == self.tableSize() + 4:
382             return self.valobj.GetChildMemberWithName('m_deletedCount')
383         elif index < self.tableSize():
384             table = self.valobj.GetChildMemberWithName('m_table')
385             return table.CreateChildAtOffset('[' + str(index) + ']', index * self.data_size, self.data_type)
386         else:
387             return None
388
389     def tableSize(self):
390         return self.valobj.GetChildMemberWithName('m_tableSize').GetValueAsUnsigned(0)
391
392     def keyCount(self):
393         return self.valobj.GetChildMemberWithName('m_keyCount').GetValueAsUnsigned(0)
394
395     def update(self):
396         self.data_type = self.valobj.GetType().GetTemplateArgumentType(0)
397         self.data_size = self.data_type.GetByteSize()
398
399     def has_children(self):
400         return True
401
402
403 class WTFMediaTimeProvider:
404     def __init__(self, valobj, internal_dict):
405         self.valobj = valobj
406
407     def timeValue(self):
408         return self.valobj.GetChildMemberWithName('m_timeValue').GetValueAsSigned(0)
409
410     def timeValueAsDouble(self):
411         error = lldb.SBError()
412         return self.valobj.GetChildMemberWithName('m_timeValueAsDouble').GetData().GetDouble(error, 0)
413
414     def timeScale(self):
415         return self.valobj.GetChildMemberWithName('m_timeScale').GetValueAsSigned(0)
416
417     def isInvalid(self):
418         return not self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 0)
419
420     def isPositiveInfinity(self):
421         return self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 2)
422
423     def isNegativeInfinity(self):
424         return self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 3)
425
426     def isIndefinite(self):
427         return self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 4)
428
429     def hasDoubleValue(self):
430         return self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 5)