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 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 73class 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 82class 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 88class WTFAtomicStringPrinter(StringPrinter): 89 "Print a WTF::AtomicString" 90 def to_string(self): 91 return self.val['m_string'] 92 93 94class 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 103class 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 118class 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 133class JSCIdentifierPrinter(StringPrinter): 134 "Print a JSC::Identifier" 135 def to_string(self): 136 return JSCUStringPrinter(self.val['m_string']).to_string() 137 138 139class 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 148class WebCoreKURLGooglePrivatePrinter(StringPrinter): 149 "Print a WebCore::KURLGooglePrivate" 150 def to_string(self): 151 return WTFCStringPrinter(self.val['m_utf8']).to_string() 152 153 154class 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 187class 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 252def 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 286add_pretty_printers() 287 288 289class 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 326PrintPathToRootCommand() 327