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