2010-04-29 Yaar Schnitman <yaar@chromium.org>
[WebKit-https.git] / WebKitTools / gdb / webcore.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 webcore
36 """
37
38 import gdb
39 import struct
40
41 def ustring_to_string(ptr, length=None):
42     """Convert a pointer to UTF-16 data into a Python Unicode string.
43
44     ptr and length are both gdb.Value objects.
45     If length is unspecified, will guess at the length."""
46     extra = ''
47     if length is None:
48         # Try to guess at the length.
49         for i in xrange(0, 2048):
50             if int((ptr + i).dereference()) == 0:
51                 length = i
52                 break
53         if length is None:
54             length = 256
55             extra = u' (no trailing NUL found)'
56     else:
57         length = int(length)
58
59     char_vals = [int((ptr + i).dereference()) for i in xrange(length)]
60     string = struct.pack('H' * length, *char_vals).decode('utf-16', 'replace')
61
62     return string + extra
63
64
65 class StringPrinter(object):
66     "Shared code between different string-printing classes"
67     def __init__(self, val):
68         self.val = val
69
70     def display_hint(self):
71         return 'string'
72
73
74 class UCharStringPrinter(StringPrinter):
75     "Print a UChar*; we must guess at the length"
76     def to_string(self):
77         return ustring_to_string(self.val)
78
79
80 class WebCoreAtomicStringPrinter(StringPrinter):
81     "Print a WebCore::AtomicString"
82     def to_string(self):
83         return self.val['m_string']
84
85
86 class WebCoreStringPrinter(StringPrinter):
87     "Print a WebCore::String"
88     def get_length(self):
89         if not self.val['m_impl']['m_ptr']:
90             return 0
91         return self.val['m_impl']['m_ptr']['m_length']
92
93     def to_string(self):
94         if self.get_length() == 0:
95             return '(null)'
96
97         return ustring_to_string(self.val['m_impl']['m_ptr']['m_data'],
98                                  self.get_length())
99
100
101 class WebCoreQualifiedNamePrinter(StringPrinter):
102     "Print a WebCore::QualifiedName"
103
104     def __init__(self, val):
105         super(WebCoreQualifiedNamePrinter, self).__init__(val)
106         self.prefix_length = 0
107         self.length = 0
108         if self.val['m_impl']:
109             self.prefix_printer = WebCoreStringPrinter(
110                 self.val['m_impl']['m_prefix']['m_string'])
111             self.local_name_printer = WebCoreStringPrinter(
112                 self.val['m_impl']['m_localName']['m_string'])
113             self.prefix_length = self.prefix_printer.get_length()
114             if self.prefix_length > 0:
115                 self.length = (self.prefix_length + 1 +
116                     self.local_name_printer.get_length())
117             else:
118                 self.length = self.local_name_printer.get_length()
119
120     def get_length(self):
121         return self.length
122
123     def to_string(self):
124         if self.get_length() == 0:
125             return "(null)"
126         else:
127             if self.prefix_length > 0:
128                 return (self.prefix_printer.to_string() + ":" +
129                     self.local_name_printer.to_string())
130             else:
131                 return self.local_name_printer.to_string()
132
133
134
135 def lookup_function(val):
136     """Function used to load pretty printers; will be passed to GDB."""
137     lookup_tag = val.type.tag
138     printers = {
139         "WebCore::AtomicString": WebCoreAtomicStringPrinter,
140         "WebCore::String": WebCoreStringPrinter,
141         "WebCore::QualifiedName": WebCoreQualifiedNamePrinter,
142     }
143     name = val.type.tag
144     if name in printers:
145         return printers[name](val)
146
147     if val.type.code == gdb.TYPE_CODE_PTR:
148         name = str(val.type.target().unqualified())
149         if name == 'UChar':
150             return UCharStringPrinter(val)
151
152     return None
153
154
155 gdb.pretty_printers.append(lookup_function)
156
157
158
159 class PrintPathToRootCommand(gdb.Command):
160   """Command for printing WebKit Node trees.
161 Usage: printpathtoroot variable_name
162 """
163
164   def __init__(self):
165       super(PrintPathToRootCommand, self).__init__("printpathtoroot",
166           gdb.COMMAND_SUPPORT,
167           gdb.COMPLETE_NONE)
168
169   def invoke(self, arg, from_tty):
170       element_type = gdb.lookup_type('WebCore::Element')
171       node_type = gdb.lookup_type('WebCore::Node')
172       frame = gdb.selected_frame()
173       try:
174           val = gdb.Frame.read_var(frame, arg)
175       except:
176           print "No such variable, or invalid type"
177           return
178
179       target_type = str(val.type.target().strip_typedefs())
180       if target_type == str(node_type):
181           stack = []
182           while val:
183               stack.append([val,
184                   val.cast(element_type.pointer()).dereference()['m_tagName']])
185               val = val.dereference()['m_parent']
186
187           padding = ''
188           while len(stack) > 0:
189               pair = stack.pop()
190               print padding, pair[1], pair[0]
191               padding = padding + '  '
192       else:
193           print 'Sorry: I don\'t know how to deal with %s yet.' % target_type
194
195 PrintPathToRootCommand()