751c80918388c23c1d0ecb5a36c540aa683fac11
[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 guess_string_length(ptr):
44     """Guess length of string pointed by ptr.
45
46     Returns a tuple of (length, an error message).
47     """
48     # Try to guess at the length.
49     for i in xrange(0, 2048):
50         try:
51             if int((ptr + i).dereference()) == 0:
52                 return i, ''
53         except RuntimeError:
54             # We indexed into inaccessible memory; give up.
55             return i, ' (gdb hit inaccessible memory)'
56     return 256, ' (gdb found no trailing NUL)'
57
58
59 def ustring_to_string(ptr, length=None):
60     """Convert a pointer to UTF-16 data into a Python string encoded with utf-8.
61
62     ptr and length are both gdb.Value objects.
63     If length is unspecified, will guess at the length."""
64     error_message = ''
65     if length is None:
66         length, error_message = guess_string_length(ptr)
67     else:
68         length = int(length)
69     char_vals = [int((ptr + i).dereference()) for i in xrange(length)]
70     string = struct.pack('H' * length, *char_vals).decode('utf-16', 'replace').encode('utf-8')
71     return string + error_message
72
73
74 def lstring_to_string(ptr, length=None):
75     """Convert a pointer to LChar* data into a Python (non-Unicode) string.
76
77     ptr and length are both gdb.Value objects.
78     If length is unspecified, will guess at the length."""
79     error_message = ''
80     if length is None:
81         length, error_message = guess_string_length(ptr)
82     else:
83         length = int(length)
84     string = ''.join([chr((ptr + i).dereference()) for i in xrange(length)])
85     return string + error_message
86
87
88 class StringPrinter(object):
89     "Shared code between different string-printing classes"
90     def __init__(self, val):
91         self.val = val
92
93     def display_hint(self):
94         return 'string'
95
96
97 class UCharStringPrinter(StringPrinter):
98     "Print a UChar*; we must guess at the length"
99     def to_string(self):
100         return ustring_to_string(self.val)
101
102
103 class LCharStringPrinter(StringPrinter):
104     "Print a LChar*; we must guess at the length"
105     def to_string(self):
106         return lstring_to_string(self.val)
107
108
109 class WTFAtomicStringPrinter(StringPrinter):
110     "Print a WTF::AtomicString"
111     def to_string(self):
112         return self.val['m_string']
113
114
115 class WTFCStringPrinter(StringPrinter):
116     "Print a WTF::CString"
117     def to_string(self):
118         # The CString holds a buffer, which is a refptr to a WTF::CStringBuffer.
119         data = self.val['m_buffer']['m_ptr']['m_data'].cast(gdb.lookup_type('char').pointer())
120         length = self.val['m_buffer']['m_ptr']['m_length']
121         return ''.join([chr((data + i).dereference()) for i in xrange(length)])
122
123
124 class WTFStringImplPrinter(StringPrinter):
125     "Print a WTF::StringImpl"
126     def get_length(self):
127         return self.val['m_length']
128
129     def to_string(self):
130         if self.is_8bit():
131             return lstring_to_string(self.val['m_data8'], self.get_length())
132         return ustring_to_string(self.val['m_data16'], self.get_length())
133
134     def is_8bit(self):
135         return self.val['m_hashAndFlags'] & self.val['s_hashFlag8BitBuffer']
136
137
138 class WTFStringPrinter(StringPrinter):
139     "Print a WTF::String"
140     def stringimpl_ptr(self):
141         return self.val['m_impl']['m_ptr']
142
143     def get_length(self):
144         if not self.stringimpl_ptr():
145             return 0
146         return WTFStringImplPrinter(self.stringimpl_ptr().dereference()).get_length()
147
148     def to_string(self):
149         if not self.stringimpl_ptr():
150             return '(null)'
151         return self.stringimpl_ptr().dereference()
152
153
154
155 class JSCIdentifierPrinter(StringPrinter):
156     "Print a JSC::Identifier"
157     def to_string(self):
158         return WTFStringPrinter(self.val['m_string']).to_string()
159
160
161 class JSCJSStringPrinter(StringPrinter):
162     "Print a JSC::JSString"
163     def to_string(self):
164         if self.val['m_length'] == 0:
165             return ''
166
167         return WTFStringImplPrinter(self.val['m_value']).to_string()
168
169
170 class WebCoreQualifiedNamePrinter(StringPrinter):
171     "Print a WebCore::QualifiedName"
172
173     def __init__(self, val):
174         super(WebCoreQualifiedNamePrinter, self).__init__(val)
175         self.prefix_length = 0
176         self.length = 0
177         if self.val['m_impl']:
178             self.prefix_printer = WTFStringPrinter(
179                 self.val['m_impl']['m_prefix']['m_string'])
180             self.local_name_printer = WTFStringPrinter(
181                 self.val['m_impl']['m_localName']['m_string'])
182             self.prefix_length = self.prefix_printer.get_length()
183             if self.prefix_length > 0:
184                 self.length = (self.prefix_length + 1 +
185                     self.local_name_printer.get_length())
186             else:
187                 self.length = self.local_name_printer.get_length()
188
189     def get_length(self):
190         return self.length
191
192     def to_string(self):
193         if self.get_length() == 0:
194             return "(null)"
195         else:
196             if self.prefix_length > 0:
197                 return (self.prefix_printer.to_string() + ":" +
198                     self.local_name_printer.to_string())
199             else:
200                 return self.local_name_printer.to_string()
201
202
203 class WTFVectorPrinter:
204     """Pretty Printer for a WTF::Vector.
205
206     The output of this pretty printer is similar to the output of std::vector's
207     pretty printer, which is bundled in gcc.
208
209     Example gdb session should look like:
210     (gdb) p v
211     $3 = WTF::Vector of length 7, capacity 16 = {7, 17, 27, 37, 47, 57, 67}
212     (gdb) set print elements 3
213     (gdb) p v
214     $6 = WTF::Vector of length 7, capacity 16 = {7, 17, 27...}
215     (gdb) set print array
216     (gdb) p v
217     $7 = WTF::Vector of length 7, capacity 16 = {
218       7,
219       17,
220       27
221       ...
222     }
223     (gdb) set print elements 200
224     (gdb) p v
225     $8 = WTF::Vector of length 7, capacity 16 = {
226       7,
227       17,
228       27,
229       37,
230       47,
231       57,
232       67
233     }
234     """
235
236     class Iterator:
237         def __init__(self, start, finish):
238             self.item = start
239             self.finish = finish
240             self.count = 0
241
242         def __iter__(self):
243             return self
244
245         def next(self):
246             if self.item == self.finish:
247                 raise StopIteration
248             count = self.count
249             self.count += 1
250             element = self.item.dereference()
251             self.item += 1
252             return ('[%d]' % count, element)
253
254     def __init__(self, val):
255         self.val = val
256
257     def children(self):
258         start = self.val['m_buffer']
259         return self.Iterator(start, start + self.val['m_size'])
260
261     def to_string(self):
262         return ('%s of length %d, capacity %d'
263                 % ('WTF::Vector', self.val['m_size'], self.val['m_capacity']))
264
265     def display_hint(self):
266         return 'array'
267
268 def add_pretty_printers():
269     pretty_printers = (
270         (re.compile("^WTF::Vector<.*>$"), WTFVectorPrinter),
271         (re.compile("^WTF::AtomicString$"), WTFAtomicStringPrinter),
272         (re.compile("^WTF::CString$"), WTFCStringPrinter),
273         (re.compile("^WTF::String$"), WTFStringPrinter),
274         (re.compile("^WTF::StringImpl$"), WTFStringImplPrinter),
275         (re.compile("^WebCore::QualifiedName$"), WebCoreQualifiedNamePrinter),
276         (re.compile("^JSC::Identifier$"), JSCIdentifierPrinter),
277         (re.compile("^JSC::JSString$"), JSCJSStringPrinter),
278     )
279
280     def lookup_function(val):
281         """Function used to load pretty printers; will be passed to GDB."""
282         type = val.type
283         if type.code == gdb.TYPE_CODE_REF:
284             type = type.target()
285         type = type.unqualified().strip_typedefs()
286         tag = type.tag
287         if tag:
288             for function, pretty_printer in pretty_printers:
289                 if function.search(tag):
290                     return pretty_printer(val)
291
292         if type.code == gdb.TYPE_CODE_PTR:
293             name = str(type.target().unqualified())
294             if name == 'UChar':
295                 return UCharStringPrinter(val)
296             if name == 'LChar':
297                 return LCharStringPrinter(val)
298         return None
299
300     gdb.pretty_printers.append(lookup_function)
301
302
303 add_pretty_printers()
304
305
306 class PrintPathToRootCommand(gdb.Command):
307     """Command for printing WebKit Node trees.
308
309     Usage: printpathtoroot variable_name"""
310
311     def __init__(self):
312         super(PrintPathToRootCommand, self).__init__("printpathtoroot",
313             gdb.COMMAND_SUPPORT,
314             gdb.COMPLETE_NONE)
315
316     def invoke(self, arg, from_tty):
317         element_type = gdb.lookup_type('WebCore::Element')
318         node_type = gdb.lookup_type('WebCore::Node')
319         frame = gdb.selected_frame()
320         try:
321             val = gdb.Frame.read_var(frame, arg)
322         except:
323             print "No such variable, or invalid type"
324             return
325
326         target_type = str(val.type.target().strip_typedefs())
327         if target_type == str(node_type):
328             stack = []
329             while val:
330                 stack.append([val,
331                     val.cast(element_type.pointer()).dereference()['m_tagName']])
332                 val = val.dereference()['m_parent']
333
334             padding = ''
335             while len(stack) > 0:
336                 pair = stack.pop()
337                 print padding, pair[1], pair[0]
338                 padding = padding + '  '
339         else:
340             print 'Sorry: I don\'t know how to deal with %s yet.' % target_type
341
342
343 PrintPathToRootCommand()