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