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