lldb: Add type summary for StringView
[WebKit.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         self.valobj = valobj
235
236     def get_length(self):
237         return self.valobj.GetChildMemberWithName('m_length').GetValueAsUnsigned(0)
238
239     def get_data8(self):
240         return self.valobj.GetChildAtIndex(0).GetChildAtIndex(2).GetChildMemberWithName('m_data8')
241
242     def get_data16(self):
243         return self.valobj.GetChildAtIndex(0).GetChildAtIndex(2).GetChildMemberWithName('m_data16')
244
245     def to_string(self):
246         error = lldb.SBError()
247
248         if not self.is_initialized():
249             return u""
250
251         if self.is_8bit():
252             return lstring_to_string(self.get_data8(), error, self.get_length())
253         return ustring_to_string(self.get_data16(), error, self.get_length())
254
255     def is_8bit(self):
256         # FIXME: find a way to access WTF::StringImpl::s_hashFlag8BitBuffer
257         return bool(self.valobj.GetChildMemberWithName('m_hashAndFlags').GetValueAsUnsigned(0) \
258             & 1 << 3)
259
260     def is_initialized(self):
261         return self.valobj.GetValueAsUnsigned() != 0
262
263
264 class WTFStringViewProvider:
265     def __init__(self, valobj, dict):
266         self.valobj = valobj
267
268     def is_8bit(self):
269         return bool(self.valobj.GetChildMemberWithName('m_is8Bit').GetValueAsUnsigned(0))
270
271     def get_length(self):
272         return self.valobj.GetChildMemberWithName('m_length').GetValueAsUnsigned(0)
273
274     def get_characters(self):
275         return self.valobj.GetChildMemberWithName('m_characters')
276
277     def to_string(self):
278         error = lldb.SBError()
279
280         if not self.get_characters() or not self.get_length():
281             return u""
282
283         if self.is_8bit():
284             return lstring_to_string(self.get_characters(), error, self.get_length())
285         return ustring_to_string(self.get_characters(), error, self.get_length())
286
287
288 class WTFStringProvider:
289     def __init__(self, valobj, dict):
290         self.valobj = valobj
291
292     def stringimpl(self):
293         impl_ptr = self.valobj.GetChildMemberWithName('m_impl').GetChildMemberWithName('m_ptr')
294         return WTFStringImplProvider(impl_ptr, dict)
295
296     def get_length(self):
297         impl = self.stringimpl()
298         if not impl:
299             return 0
300         return impl.get_length()
301
302     def to_string(self):
303         impl = self.stringimpl()
304         if not impl:
305             return u""
306         return impl.to_string()
307
308
309 class WebCoreLayoutUnitProvider:
310     "Print a WebCore::LayoutUnit"
311     def __init__(self, valobj, dict):
312         self.valobj = valobj
313
314     def to_string(self):
315         layoutUnitValue = self.valobj.GetChildMemberWithName('m_value').GetValueAsSigned(0)
316         return "%gpx (%d)" % (float(layoutUnitValue) / 64, layoutUnitValue)
317
318
319 class WebCoreLayoutSizeProvider:
320     "Print a WebCore::LayoutSize"
321     def __init__(self, valobj, dict):
322         self.valobj = valobj
323
324     def get_width(self):
325         return WebCoreLayoutUnitProvider(self.valobj.GetChildMemberWithName('m_width'), dict).to_string()
326
327     def get_height(self):
328         return WebCoreLayoutUnitProvider(self.valobj.GetChildMemberWithName('m_height'), dict).to_string()
329
330
331 class WebCoreLayoutPointProvider:
332     "Print a WebCore::LayoutPoint"
333     def __init__(self, valobj, dict):
334         self.valobj = valobj
335
336     def get_x(self):
337         return WebCoreLayoutUnitProvider(self.valobj.GetChildMemberWithName('m_x'), dict).to_string()
338
339     def get_y(self):
340         return WebCoreLayoutUnitProvider(self.valobj.GetChildMemberWithName('m_y'), dict).to_string()
341
342
343 class WebCoreURLProvider:
344     "Print a WebCore::URL"
345     def __init__(self, valobj, dict):
346         self.valobj = valobj
347
348     def to_string(self):
349         return WTFStringProvider(self.valobj.GetChildMemberWithName('m_string'), dict).to_string()
350
351 class WTFVectorProvider:
352     def __init__(self, valobj, internal_dict):
353         self.valobj = valobj
354         self.update()
355
356     def num_children(self):
357         return self.size + 3
358
359     def get_child_index(self, name):
360         if name == "m_size":
361             return self.size
362         elif name == "m_capacity":
363             return self.size + 1
364         elif name == "m_buffer":
365             return self.size + 2
366         else:
367             return int(name.lstrip('[').rstrip(']'))
368
369     def get_child_at_index(self, index):
370         if index == self.size:
371             return self.valobj.GetChildMemberWithName("m_size")
372         elif index == self.size + 1:
373             return self.valobj.GetChildMemberWithName("m_capacity")
374         elif index == self.size + 2:
375             return self.buffer
376         elif index < self.size:
377             offset = index * self.data_size
378             child = self.buffer.CreateChildAtOffset('[' + str(index) + ']', offset, self.data_type)
379             return child
380         else:
381             return None
382
383     def update(self):
384         self.buffer = self.valobj.GetChildMemberWithName('m_buffer')
385         self.size = self.valobj.GetChildMemberWithName('m_size').GetValueAsUnsigned(0)
386         self.capacity = self.buffer.GetChildMemberWithName('m_capacity').GetValueAsUnsigned(0)
387         self.data_type = self.buffer.GetType().GetPointeeType()
388         self.data_size = self.data_type.GetByteSize()
389
390     def has_children(self):
391         return True
392
393
394 class WTFHashTableProvider:
395     def __init__(self, valobj, internal_dict):
396         self.valobj = valobj
397         self.update()
398
399     def num_children(self):
400         return self.tableSize() + 5
401
402     def get_child_index(self, name):
403         if name == "m_table":
404             return self.tableSize()
405         elif name == "m_tableSize":
406             return self.tableSize() + 1
407         elif name == "m_tableSizeMask":
408             return self.tableSize() + 2
409         elif name == "m_keyCount":
410             return self.tableSize() + 3
411         elif name == "m_deletedCount":
412             return self.tableSize() + 4
413         else:
414             return int(name.lstrip('[').rstrip(']'))
415
416     def get_child_at_index(self, index):
417         if index == self.tableSize():
418             return self.valobj.GetChildMemberWithName('m_table')
419         elif index == self.tableSize() + 1:
420             return self.valobj.GetChildMemberWithName('m_tableSize')
421         elif index == self.tableSize() + 2:
422             return self.valobj.GetChildMemberWithName('m_tableSizeMask')
423         elif index == self.tableSize() + 3:
424             return self.valobj.GetChildMemberWithName('m_keyCount')
425         elif index == self.tableSize() + 4:
426             return self.valobj.GetChildMemberWithName('m_deletedCount')
427         elif index < self.tableSize():
428             table = self.valobj.GetChildMemberWithName('m_table')
429             return table.CreateChildAtOffset('[' + str(index) + ']', index * self.data_size, self.data_type)
430         else:
431             return None
432
433     def tableSize(self):
434         return self.valobj.GetChildMemberWithName('m_tableSize').GetValueAsUnsigned(0)
435
436     def keyCount(self):
437         return self.valobj.GetChildMemberWithName('m_keyCount').GetValueAsUnsigned(0)
438
439     def update(self):
440         self.data_type = self.valobj.GetType().GetTemplateArgumentType(0)
441         self.data_size = self.data_type.GetByteSize()
442
443     def has_children(self):
444         return True
445
446
447 class WTFMediaTimeProvider:
448     def __init__(self, valobj, internal_dict):
449         self.valobj = valobj
450
451     def timeValue(self):
452         return self.valobj.GetChildMemberWithName('m_timeValue').GetValueAsSigned(0)
453
454     def timeValueAsDouble(self):
455         error = lldb.SBError()
456         return self.valobj.GetChildMemberWithName('m_timeValueAsDouble').GetData().GetDouble(error, 0)
457
458     def timeScale(self):
459         return self.valobj.GetChildMemberWithName('m_timeScale').GetValueAsSigned(0)
460
461     def isInvalid(self):
462         return not self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 0)
463
464     def isPositiveInfinity(self):
465         return self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 2)
466
467     def isNegativeInfinity(self):
468         return self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 3)
469
470     def isIndefinite(self):
471         return self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 4)
472
473     def hasDoubleValue(self):
474         return self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 5)