webkit.py revision f05b935882198ccf7d81675736e3aeb089c5113a
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
31Add 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
38import gdb
39import re
40import struct
41
42
43def 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            if int((ptr + i).dereference()) == 0:
53                length = i
54                break
55        if length is None:
56            length = 256
57            extra = u' (no trailing NUL found)'
58    else:
59        length = int(length)
60
61    char_vals = [int((ptr + i).dereference()) for i in xrange(length)]
62    string = struct.pack('H' * length, *char_vals).decode('utf-16', 'replace')
63
64    return string + extra
65
66
67class StringPrinter(object):
68    "Shared code between different string-printing classes"
69    def __init__(self, val):
70        self.val = val
71
72    def display_hint(self):
73        return 'string'
74
75
76class UCharStringPrinter(StringPrinter):
77    "Print a UChar*; we must guess at the length"
78    def to_string(self):
79        return ustring_to_string(self.val)
80
81
82class WTFAtomicStringPrinter(StringPrinter):
83    "Print a WTF::AtomicString"
84    def to_string(self):
85        return self.val['m_string']
86
87
88class WTFStringPrinter(StringPrinter):
89    "Print a WTF::String"
90    def get_length(self):
91        if not self.val['m_impl']['m_ptr']:
92            return 0
93        return self.val['m_impl']['m_ptr']['m_length']
94
95    def to_string(self):
96        if self.get_length() == 0:
97            return '(null)'
98
99        return ustring_to_string(self.val['m_impl']['m_ptr']['m_data'],
100                                 self.get_length())
101
102
103class JSCUStringPrinter(StringPrinter):
104    "Print a JSC::UString"
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 ''
113
114        return ustring_to_string(self.val['m_impl']['m_ptr']['m_data'],
115                                 self.get_length())
116
117
118class JSCIdentifierPrinter(StringPrinter):
119    "Print a JSC::Identifier"
120    def to_string(self):
121        return JSCUStringPrinter(self.val['m_string']).to_string()
122
123
124class JSCJSStringPrinter(StringPrinter):
125    "Print a JSC::JSString"
126    def to_string(self):
127        if self.val['m_length'] == 0:
128            return ''
129
130        return JSCUStringPrinter(self.val['m_value']).to_string()
131
132
133class WebCoreQualifiedNamePrinter(StringPrinter):
134    "Print a WebCore::QualifiedName"
135
136    def __init__(self, val):
137        super(WebCoreQualifiedNamePrinter, self).__init__(val)
138        self.prefix_length = 0
139        self.length = 0
140        if self.val['m_impl']:
141            self.prefix_printer = WTFStringPrinter(
142                self.val['m_impl']['m_prefix']['m_string'])
143            self.local_name_printer = WTFStringPrinter(
144                self.val['m_impl']['m_localName']['m_string'])
145            self.prefix_length = self.prefix_printer.get_length()
146            if self.prefix_length > 0:
147                self.length = (self.prefix_length + 1 +
148                    self.local_name_printer.get_length())
149            else:
150                self.length = self.local_name_printer.get_length()
151
152    def get_length(self):
153        return self.length
154
155    def to_string(self):
156        if self.get_length() == 0:
157            return "(null)"
158        else:
159            if self.prefix_length > 0:
160                return (self.prefix_printer.to_string() + ":" +
161                    self.local_name_printer.to_string())
162            else:
163                return self.local_name_printer.to_string()
164
165
166class WTFVectorPrinter:
167    """Pretty Printer for a WTF::Vector.
168
169    The output of this pretty printer is similar to the output of std::vector's
170    pretty printer, which is bundled in gcc.
171
172    Example gdb session should look like:
173    (gdb) p v
174    $3 = WTF::Vector of length 7, capacity 16 = {7, 17, 27, 37, 47, 57, 67}
175    (gdb) set print elements 3
176    (gdb) p v
177    $6 = WTF::Vector of length 7, capacity 16 = {7, 17, 27...}
178    (gdb) set print array
179    (gdb) p v
180    $7 = WTF::Vector of length 7, capacity 16 = {
181      7,
182      17,
183      27
184      ...
185    }
186    (gdb) set print elements 200
187    (gdb) p v
188    $8 = WTF::Vector of length 7, capacity 16 = {
189      7,
190      17,
191      27,
192      37,
193      47,
194      57,
195      67
196    }
197    """
198
199    class Iterator:
200        def __init__(self, start, finish):
201            self.item = start
202            self.finish = finish
203            self.count = 0
204
205        def __iter__(self):
206            return self
207
208        def next(self):
209            if self.item == self.finish:
210                raise StopIteration
211            count = self.count
212            self.count += 1
213            element = self.item.dereference()
214            self.item += 1
215            return ('[%d]' % count, element)
216
217    def __init__(self, val):
218        self.val = val
219
220    def children(self):
221        start = self.val['m_buffer']['m_buffer']
222        return self.Iterator(start, start + self.val['m_size'])
223
224    def to_string(self):
225        return ('%s of length %d, capacity %d'
226                % ('WTF::Vector', self.val['m_size'], self.val['m_buffer']['m_capacity']))
227
228    def display_hint(self):
229        return 'array'
230
231def add_pretty_printers():
232    pretty_printers_dict = {
233        re.compile("^WTF::Vector<.*>$"): WTFVectorPrinter,
234        re.compile("^WTF::AtomicString$"): WTFAtomicStringPrinter,
235        re.compile("^WTF::String$"): WTFStringPrinter,
236        re.compile("^WebCore::QualifiedName$"): WebCoreQualifiedNamePrinter,
237        re.compile("^JSC::UString$"): JSCUStringPrinter,
238        re.compile("^JSC::Identifier$"): JSCIdentifierPrinter,
239        re.compile("^JSC::JSString$"): JSCJSStringPrinter,
240    }
241
242    def lookup_function(val):
243        """Function used to load pretty printers; will be passed to GDB."""
244        type = val.type
245        if type.code == gdb.TYPE_CODE_REF:
246            type = type.target()
247        type = type.unqualified().strip_typedefs()
248        typename = type.tag
249        if not typename:
250            return None
251        for function, pretty_printer in pretty_printers_dict.items():
252            if function.search(typename):
253                return pretty_printer(val)
254
255        if type.code == gdb.TYPE_CODE_PTR:
256            name = str(type.target().unqualified())
257            if name == 'UChar':
258                return UCharStringPrinter(val)
259        return None
260
261    gdb.pretty_printers.append(lookup_function)
262
263
264add_pretty_printers()
265
266
267class PrintPathToRootCommand(gdb.Command):
268    """Command for printing WebKit Node trees.
269
270    Usage: printpathtoroot variable_name"""
271
272    def __init__(self):
273        super(PrintPathToRootCommand, self).__init__("printpathtoroot",
274            gdb.COMMAND_SUPPORT,
275            gdb.COMPLETE_NONE)
276
277    def invoke(self, arg, from_tty):
278        element_type = gdb.lookup_type('WebCore::Element')
279        node_type = gdb.lookup_type('WebCore::Node')
280        frame = gdb.selected_frame()
281        try:
282            val = gdb.Frame.read_var(frame, arg)
283        except:
284            print "No such variable, or invalid type"
285            return
286
287        target_type = str(val.type.target().strip_typedefs())
288        if target_type == str(node_type):
289            stack = []
290            while val:
291                stack.append([val,
292                    val.cast(element_type.pointer()).dereference()['m_tagName']])
293                val = val.dereference()['m_parent']
294
295            padding = ''
296            while len(stack) > 0:
297                pair = stack.pop()
298                print padding, pair[1], pair[0]
299                padding = padding + '  '
300        else:
301            print 'Sorry: I don\'t know how to deal with %s yet.' % target_type
302
303
304PrintPathToRootCommand()
305