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