Unreviewed; updated chromium test expectations.
[WebKit-https.git] / Tools / gdb / webkit.py
1 # Copyright (C) 2010, Google 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 are
5 # met:
6 #
7 #     * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer.
9 #     * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following disclaimer
11 # in the documentation and/or other materials provided with the
12 # distribution.
13 #     * Neither the name of Google Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 """GDB support for WebKit types.
30
31 Add this to your gdb by amending your ~/.gdbinit as follows:
32   python
33   import sys
34   sys.path.insert(0, "/path/to/tools/gdb/")
35   import webkit
36 """
37
38 import gdb
39 import re
40 import struct
41
42
43 def ustring_to_string(ptr, length=None):
44     """Convert a pointer to UTF-16 data into a Python Unicode string.
45
46     ptr and length are both gdb.Value objects.
47     If length is unspecified, will guess at the length."""
48     extra = ''
49     if length is None:
50         # Try to guess at the length.
51         for i in xrange(0, 2048):
52             try:
53                 if int((ptr + i).dereference()) == 0:
54                     length = i
55                     break
56             except RuntimeError:
57                 # We indexed into inaccessible memory; give up.
58                 length = i
59                 extra = u' (gdb hit inaccessible memory)'
60                 break
61         if length is None:
62             length = 256
63             extra = u' (gdb found no trailing NUL)'
64     else:
65         length = int(length)
66
67     char_vals = [int((ptr + i).dereference()) for i in xrange(length)]
68     string = struct.pack('H' * length, *char_vals).decode('utf-16', 'replace')
69
70     return string + extra
71
72
73 class StringPrinter(object):
74     "Shared code between different string-printing classes"
75     def __init__(self, val):
76         self.val = val
77
78     def display_hint(self):
79         return 'string'
80
81
82 class UCharStringPrinter(StringPrinter):
83     "Print a UChar*; we must guess at the length"
84     def to_string(self):
85         return ustring_to_string(self.val)
86
87
88 class WTFAtomicStringPrinter(StringPrinter):
89     "Print a WTF::AtomicString"
90     def to_string(self):
91         return self.val['m_string']
92
93
94 class WTFCStringPrinter(StringPrinter):
95     "Print a WTF::CString"
96     def to_string(self):
97         # The CString holds a buffer, which is a refptr to a WTF::Vector of chars.
98         vector = self.val['m_buffer']['m_ptr']['m_vector']
99         # The vector has two more layers of buffer members.
100         return vector['m_buffer']['m_buffer']
101
102
103 class WTFStringPrinter(StringPrinter):
104     "Print a WTF::String"
105     def get_length(self):
106         if not self.val['m_impl']['m_ptr']:
107             return 0
108         return self.val['m_impl']['m_ptr']['m_length']
109
110     def to_string(self):
111         if self.get_length() == 0:
112             return '(null)'
113
114         return ustring_to_string(self.val['m_impl']['m_ptr']['m_data'],
115                                  self.get_length())
116
117
118 class JSCUStringPrinter(StringPrinter):
119     "Print a JSC::UString"
120     def get_length(self):
121         if not self.val['m_impl']['m_ptr']:
122             return 0
123         return self.val['m_impl']['m_ptr']['m_length']
124
125     def to_string(self):
126         if self.get_length() == 0:
127             return ''
128
129         return ustring_to_string(self.val['m_impl']['m_ptr']['m_data'],
130                                  self.get_length())
131
132
133 class JSCIdentifierPrinter(StringPrinter):
134     "Print a JSC::Identifier"
135     def to_string(self):
136         return JSCUStringPrinter(self.val['m_string']).to_string()
137
138
139 class JSCJSStringPrinter(StringPrinter):
140     "Print a JSC::JSString"
141     def to_string(self):
142         if self.val['m_length'] == 0:
143             return ''
144
145         return JSCUStringPrinter(self.val['m_value']).to_string()
146
147
148 class WebCoreKURLGooglePrivatePrinter(StringPrinter):
149     "Print a WebCore::KURLGooglePrivate"
150     def to_string(self):
151         return WTFCStringPrinter(self.val['m_utf8']).to_string()
152
153
154 class WebCoreQualifiedNamePrinter(StringPrinter):
155     "Print a WebCore::QualifiedName"
156
157     def __init__(self, val):
158         super(WebCoreQualifiedNamePrinter, self).__init__(val)
159         self.prefix_length = 0
160         self.length = 0
161         if self.val['m_impl']:
162             self.prefix_printer = WTFStringPrinter(
163                 self.val['m_impl']['m_prefix']['m_string'])
164             self.local_name_printer = WTFStringPrinter(
165                 self.val['m_impl']['m_localName']['m_string'])
166             self.prefix_length = self.prefix_printer.get_length()
167             if self.prefix_length > 0:
168                 self.length = (self.prefix_length + 1 +
169                     self.local_name_printer.get_length())
170             else:
171                 self.length = self.local_name_printer.get_length()
172
173     def get_length(self):
174         return self.length
175
176     def to_string(self):
177         if self.get_length() == 0:
178             return "(null)"
179         else:
180             if self.prefix_length > 0:
181                 return (self.prefix_printer.to_string() + ":" +
182                     self.local_name_printer.to_string())
183             else:
184                 return self.local_name_printer.to_string()
185
186
187 class WTFVectorPrinter:
188     """Pretty Printer for a WTF::Vector.
189
190     The output of this pretty printer is similar to the output of std::vector's
191     pretty printer, which is bundled in gcc.
192
193     Example gdb session should look like:
194     (gdb) p v
195     $3 = WTF::Vector of length 7, capacity 16 = {7, 17, 27, 37, 47, 57, 67}
196     (gdb) set print elements 3
197     (gdb) p v
198     $6 = WTF::Vector of length 7, capacity 16 = {7, 17, 27...}
199     (gdb) set print array
200     (gdb) p v
201     $7 = WTF::Vector of length 7, capacity 16 = {
202       7,
203       17,
204       27
205       ...
206     }
207     (gdb) set print elements 200
208     (gdb) p v
209     $8 = WTF::Vector of length 7, capacity 16 = {
210       7,
211       17,
212       27,
213       37,
214       47,
215       57,
216       67
217     }
218     """
219
220     class Iterator:
221         def __init__(self, start, finish):
222             self.item = start
223             self.finish = finish
224             self.count = 0
225
226         def __iter__(self):
227             return self
228
229         def next(self):
230             if self.item == self.finish:
231                 raise StopIteration
232             count = self.count
233             self.count += 1
234             element = self.item.dereference()
235             self.item += 1
236             return ('[%d]' % count, element)
237
238     def __init__(self, val):
239         self.val = val
240
241     def children(self):
242         start = self.val['m_buffer']['m_buffer']
243         return self.Iterator(start, start + self.val['m_size'])
244
245     def to_string(self):
246         return ('%s of length %d, capacity %d'
247                 % ('WTF::Vector', self.val['m_size'], self.val['m_buffer']['m_capacity']))
248
249     def display_hint(self):
250         return 'array'
251
252 def add_pretty_printers():
253     pretty_printers = (
254         (re.compile("^WTF::Vector<.*>$"), WTFVectorPrinter),
255         (re.compile("^WTF::AtomicString$"), WTFAtomicStringPrinter),
256         (re.compile("^WTF::CString$"), WTFCStringPrinter),
257         (re.compile("^WTF::String$"), WTFStringPrinter),
258         (re.compile("^WebCore::KURLGooglePrivate$"), WebCoreKURLGooglePrivatePrinter),
259         (re.compile("^WebCore::QualifiedName$"), WebCoreQualifiedNamePrinter),
260         (re.compile("^JSC::UString$"), JSCUStringPrinter),
261         (re.compile("^JSC::Identifier$"), JSCIdentifierPrinter),
262         (re.compile("^JSC::JSString$"), JSCJSStringPrinter),
263     )
264
265     def lookup_function(val):
266         """Function used to load pretty printers; will be passed to GDB."""
267         type = val.type
268         if type.code == gdb.TYPE_CODE_REF:
269             type = type.target()
270         type = type.unqualified().strip_typedefs()
271         tag = type.tag
272         if tag:
273             for function, pretty_printer in pretty_printers:
274                 if function.search(tag):
275                     return pretty_printer(val)
276
277         if type.code == gdb.TYPE_CODE_PTR:
278             name = str(type.target().unqualified())
279             if name == 'UChar':
280                 return UCharStringPrinter(val)
281         return None
282
283     gdb.pretty_printers.append(lookup_function)
284
285
286 add_pretty_printers()
287
288
289 class PrintPathToRootCommand(gdb.Command):
290     """Command for printing WebKit Node trees.
291
292     Usage: printpathtoroot variable_name"""
293
294     def __init__(self):
295         super(PrintPathToRootCommand, self).__init__("printpathtoroot",
296             gdb.COMMAND_SUPPORT,
297             gdb.COMPLETE_NONE)
298
299     def invoke(self, arg, from_tty):
300         element_type = gdb.lookup_type('WebCore::Element')
301         node_type = gdb.lookup_type('WebCore::Node')
302         frame = gdb.selected_frame()
303         try:
304             val = gdb.Frame.read_var(frame, arg)
305         except:
306             print "No such variable, or invalid type"
307             return
308
309         target_type = str(val.type.target().strip_typedefs())
310         if target_type == str(node_type):
311             stack = []
312             while val:
313                 stack.append([val,
314                     val.cast(element_type.pointer()).dereference()['m_tagName']])
315                 val = val.dereference()['m_parent']
316
317             padding = ''
318             while len(stack) > 0:
319                 pair = stack.pop()
320                 print padding, pair[1], pair[0]
321                 padding = padding + '  '
322         else:
323             print 'Sorry: I don\'t know how to deal with %s yet.' % target_type
324
325
326 PrintPathToRootCommand()