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 chars_start = self.val.address + 1 131 if self.is_8bit(): 132 return lstring_to_string(chars_start.cast(gdb.lookup_type('char').pointer()), 133 self.get_length()) 134 return ustring_to_string(chars_start.cast(gdb.lookup_type('UChar').pointer()), 135 self.get_length()) 136 137 def is_8bit(self): 138 return self.val['m_is8Bit'] 139 140 141 class WTFStringPrinter(StringPrinter): 142 "Print a WTF::String" 143 def stringimpl_ptr(self): 144 return self.val['m_impl']['m_ptr'] 145 146 def get_length(self): 147 if not self.stringimpl_ptr(): 148 return 0 149 return WTFStringImplPrinter(self.stringimpl_ptr().dereference()).get_length() 150 151 def to_string(self): 152 if not self.stringimpl_ptr(): 153 return '(null)' 154 return self.stringimpl_ptr().dereference() 155 156 157 158 class JSCIdentifierPrinter(StringPrinter): 159 "Print a JSC::Identifier" 160 def to_string(self): 161 return WTFStringImplPrinter(self.val['m_string']).to_string() 162 163 164 class JSCJSStringPrinter(StringPrinter): 165 "Print a JSC::JSString" 166 def to_string(self): 167 if self.val['m_length'] == 0: 168 return '' 169 170 return WTFStringImplPrinter(self.val['m_value']).to_string() 171 172 173 class WebCoreKURLGooglePrivatePrinter(StringPrinter): 174 "Print a WebCore::KURLGooglePrivate" 175 def to_string(self): 176 return WTFCStringPrinter(self.val['m_utf8']).to_string() 177 178 179 class WebCoreQualifiedNamePrinter(StringPrinter): 180 "Print a WebCore::QualifiedName" 181 182 def __init__(self, val): 183 super(WebCoreQualifiedNamePrinter, self).__init__(val) 184 self.prefix_length = 0 185 self.length = 0 186 if self.val['m_impl']: 187 self.prefix_printer = WTFStringPrinter( 188 self.val['m_impl']['m_prefix']['m_string']) 189 self.local_name_printer = WTFStringPrinter( 190 self.val['m_impl']['m_localName']['m_string']) 191 self.prefix_length = self.prefix_printer.get_length() 192 if self.prefix_length > 0: 193 self.length = (self.prefix_length + 1 + 194 self.local_name_printer.get_length()) 195 else: 196 self.length = self.local_name_printer.get_length() 197 198 def get_length(self): 199 return self.length 200 201 def to_string(self): 202 if self.get_length() == 0: 203 return "(null)" 204 else: 205 if self.prefix_length > 0: 206 return (self.prefix_printer.to_string() + ":" + 207 self.local_name_printer.to_string()) 208 else: 209 return self.local_name_printer.to_string() 210 211 212 class WTFVectorPrinter: 213 """Pretty Printer for a WTF::Vector. 214 215 The output of this pretty printer is similar to the output of std::vector's 216 pretty printer, which is bundled in gcc. 217 218 Example gdb session should look like: 219 (gdb) p v 220 $3 = WTF::Vector of length 7, capacity 16 = {7, 17, 27, 37, 47, 57, 67} 221 (gdb) set print elements 3 222 (gdb) p v 223 $6 = WTF::Vector of length 7, capacity 16 = {7, 17, 27...} 224 (gdb) set print array 225 (gdb) p v 226 $7 = WTF::Vector of length 7, capacity 16 = { 227 7, 228 17, 229 27 230 ... 231 } 232 (gdb) set print elements 200 233 (gdb) p v 234 $8 = WTF::Vector of length 7, capacity 16 = { 235 7, 236 17, 237 27, 238 37, 239 47, 240 57, 241 67 242 } 243 """ 244 245 class Iterator: 246 def __init__(self, start, finish): 247 self.item = start 248 self.finish = finish 249 self.count = 0 250 251 def __iter__(self): 252 return self 253 254 def next(self): 255 if self.item == self.finish: 256 raise StopIteration 257 count = self.count 258 self.count += 1 259 element = self.item.dereference() 260 self.item += 1 261 return ('[%d]' % count, element) 262 263 def __init__(self, val): 264 self.val = val 265 266 def children(self): 267 start = self.val['m_buffer'] 268 return self.Iterator(start, start + self.val['m_size']) 269 270 def to_string(self): 271 return ('%s of length %d, capacity %d' 272 % ('WTF::Vector', self.val['m_size'], self.val['m_capacity'])) 273 274 def display_hint(self): 275 return 'array' 276 277 def add_pretty_printers(): 278 pretty_printers = ( 279 (re.compile("^WTF::Vector<.*>$"), WTFVectorPrinter), 280 (re.compile("^WTF::AtomicString$"), WTFAtomicStringPrinter), 281 (re.compile("^WTF::CString$"), WTFCStringPrinter), 282 (re.compile("^WTF::String$"), WTFStringPrinter), 283 (re.compile("^WTF::StringImpl$"), WTFStringImplPrinter), 284 (re.compile("^WebCore::KURLGooglePrivate$"), WebCoreKURLGooglePrivatePrinter), 285 (re.compile("^WebCore::QualifiedName$"), WebCoreQualifiedNamePrinter), 286 (re.compile("^JSC::Identifier$"), JSCIdentifierPrinter), 287 (re.compile("^JSC::JSString$"), JSCJSStringPrinter), 288 ) 289 290 def lookup_function(val): 291 """Function used to load pretty printers; will be passed to GDB.""" 292 type = val.type 293 if type.code == gdb.TYPE_CODE_REF: 294 type = type.target() 295 type = type.unqualified().strip_typedefs() 296 tag = type.tag 297 if tag: 298 for function, pretty_printer in pretty_printers: 299 if function.search(tag): 300 return pretty_printer(val) 301 302 if type.code == gdb.TYPE_CODE_PTR: 303 name = str(type.target().unqualified()) 304 if name == 'UChar': 305 return UCharStringPrinter(val) 306 if name == 'LChar': 307 return LCharStringPrinter(val) 308 return None 309 310 gdb.pretty_printers.append(lookup_function) 311 312 313 add_pretty_printers() 314 315 316 class PrintPathToRootCommand(gdb.Command): 317 """Command for printing WebKit Node trees. 318 319 Usage: printpathtoroot variable_name""" 320 321 def __init__(self): 322 super(PrintPathToRootCommand, self).__init__("printpathtoroot", 323 gdb.COMMAND_SUPPORT, 324 gdb.COMPLETE_NONE) 325 326 def invoke(self, arg, from_tty): 327 element_type = gdb.lookup_type('WebCore::Element') 328 node_type = gdb.lookup_type('WebCore::Node') 329 frame = gdb.selected_frame() 330 try: 331 val = gdb.Frame.read_var(frame, arg) 332 except: 333 print "No such variable, or invalid type" 334 return 335 336 target_type = str(val.type.target().strip_typedefs()) 337 if target_type == str(node_type): 338 stack = [] 339 while val: 340 stack.append([val, 341 val.cast(element_type.pointer()).dereference()['m_tagName']]) 342 val = val.dereference()['m_parent'] 343 344 padding = '' 345 while len(stack) > 0: 346 pair = stack.pop() 347 print padding, pair[1], pair[0] 348 padding = padding + ' ' 349 else: 350 print 'Sorry: I don\'t know how to deal with %s yet.' % target_type 351 352 353 PrintPathToRootCommand() 354