| # Copyright (C) 2010, Google Inc. All rights reserved. | 
 | # | 
 | # Redistribution and use in source and binary forms, with or without | 
 | # modification, are permitted provided that the following conditions are | 
 | # met: | 
 | # | 
 | #     * Redistributions of source code must retain the above copyright | 
 | # notice, this list of conditions and the following disclaimer. | 
 | #     * Redistributions in binary form must reproduce the above | 
 | # copyright notice, this list of conditions and the following disclaimer | 
 | # in the documentation and/or other materials provided with the | 
 | # distribution. | 
 | #     * Neither the name of Google Inc. nor the names of its | 
 | # contributors may be used to endorse or promote products derived from | 
 | # this software without specific prior written permission. | 
 | # | 
 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
 |  | 
 | """GDB support for WebKit types. | 
 |  | 
 | Add this to your gdb by amending your ~/.gdbinit as follows: | 
 |   python | 
 |   import sys | 
 |   sys.path.insert(0, "/path/to/tools/gdb/") | 
 |   import webkit | 
 | """ | 
 |  | 
 | from __future__ import print_function | 
 |  | 
 | import gdb | 
 | import re | 
 | import struct | 
 |  | 
 | def guess_string_length(ptr): | 
 |     """Guess length of string pointed by ptr. | 
 |  | 
 |     Returns a tuple of (length, an error message). | 
 |     """ | 
 |     # Try to guess at the length. | 
 |     for i in range(0, 2048): | 
 |         try: | 
 |             if int((ptr + i).dereference()) == 0: | 
 |                 return i, '' | 
 |         except RuntimeError: | 
 |             # We indexed into inaccessible memory; give up. | 
 |             return i, ' (gdb hit inaccessible memory)' | 
 |     return 256, ' (gdb found no trailing NUL)' | 
 |  | 
 |  | 
 | def ustring_to_string(ptr, length=None): | 
 |     """Convert a pointer to UTF-16 data into a Python string encoded with utf-8. | 
 |  | 
 |     ptr and length are both gdb.Value objects. | 
 |     If length is unspecified, will guess at the length.""" | 
 |     error_message = '' | 
 |     if length is None: | 
 |         length, error_message = guess_string_length(ptr) | 
 |     else: | 
 |         length = int(length) | 
 |     char_vals = [int((ptr + i).dereference()) for i in range(length)] | 
 |     string = struct.pack('H' * length, *char_vals).decode('utf-16', 'replace').encode('utf-8') | 
 |     return string + error_message | 
 |  | 
 |  | 
 | def lstring_to_string(ptr, length=None): | 
 |     """Convert a pointer to LChar* data into a Python (non-Unicode) string. | 
 |  | 
 |     ptr and length are both gdb.Value objects. | 
 |     If length is unspecified, will guess at the length.""" | 
 |     error_message = '' | 
 |     if length is None: | 
 |         length, error_message = guess_string_length(ptr) | 
 |     else: | 
 |         length = int(length) | 
 |     string = ''.join([chr((ptr + i).dereference()) for i in range(length)]) | 
 |     return string + error_message | 
 |  | 
 |  | 
 | class StringPrinter(object): | 
 |     "Shared code between different string-printing classes" | 
 |     def __init__(self, val): | 
 |         self.val = val | 
 |  | 
 |     def display_hint(self): | 
 |         return 'string' | 
 |  | 
 |  | 
 | class UCharStringPrinter(StringPrinter): | 
 |     "Print a UChar*; we must guess at the length" | 
 |     def to_string(self): | 
 |         return ustring_to_string(self.val) | 
 |  | 
 |  | 
 | class LCharStringPrinter(StringPrinter): | 
 |     "Print a LChar*; we must guess at the length" | 
 |     def to_string(self): | 
 |         return lstring_to_string(self.val) | 
 |  | 
 |  | 
 | class WTFAtomicStringPrinter(StringPrinter): | 
 |     "Print a WTF::AtomicString" | 
 |     def to_string(self): | 
 |         return self.val['string_'] | 
 |  | 
 |  | 
 | class WTFCStringPrinter(StringPrinter): | 
 |     "Print a WTF::CString" | 
 |     def to_string(self): | 
 |         # The CString holds a buffer, which is a refptr to a WTF::CStringBuffer. | 
 |         buf_ptr = self.val['buffer_']['ptr_'] | 
 |         if not buf_ptr: | 
 |             return 0 | 
 |         data = (buf_ptr + 1).cast(gdb.lookup_type('char').pointer()) | 
 |         length = self.val['buffer_']['ptr_']['length_'] | 
 |         return ''.join([chr((data + i).dereference()) for i in range(length)]) | 
 |  | 
 |  | 
 | class WTFStringImplPrinter(StringPrinter): | 
 |     "Print a WTF::StringImpl" | 
 |     def get_length(self): | 
 |         return self.val['length_'] | 
 |  | 
 |     def to_string(self): | 
 |         chars_start = self.val.address + 1 | 
 |         if self.is_8bit(): | 
 |             return lstring_to_string(chars_start.cast(gdb.lookup_type('char').pointer()), | 
 |                                      self.get_length()) | 
 |         return ustring_to_string(chars_start.cast(gdb.lookup_type('UChar').pointer()), | 
 |                                  self.get_length()) | 
 |  | 
 |     def is_8bit(self): | 
 |         return self.val['is8_bit_'] | 
 |  | 
 |  | 
 | class WTFStringPrinter(StringPrinter): | 
 |     "Print a WTF::String" | 
 |     def stringimpl_ptr(self): | 
 |         return self.val['impl_']['ptr_'] | 
 |  | 
 |     def get_length(self): | 
 |         if not self.stringimpl_ptr(): | 
 |             return 0 | 
 |         return WTFStringImplPrinter(self.stringimpl_ptr().dereference()).get_length() | 
 |  | 
 |     def to_string(self): | 
 |         if not self.stringimpl_ptr(): | 
 |             return '(null)' | 
 |         return self.stringimpl_ptr().dereference() | 
 |  | 
 |  | 
 |  | 
 | class blinkKURLPrinter(StringPrinter): | 
 |     "Print a blink::KURL" | 
 |     def to_string(self): | 
 |         return WTFStringPrinter(self.val['string_']).to_string() | 
 |  | 
 |  | 
 | class blinkLayoutUnitPrinter: | 
 |     "Print a blink::LayoutUnit" | 
 |     def __init__(self, val): | 
 |         self.val = val | 
 |  | 
 |     def to_string(self): | 
 |         return "%.14gpx" % (self.val['value_'] / 64.0) | 
 |  | 
 |  | 
 | class blinkLayoutSizePrinter: | 
 |     "Print a blink::LayoutSize" | 
 |     def __init__(self, val): | 
 |         self.val = val | 
 |  | 
 |     def to_string(self): | 
 |         return 'LayoutSize(%s, %s)' % ( | 
 |             blinkLayoutUnitPrinter(self.val['width_']).to_string(), | 
 |             blinkLayoutUnitPrinter(self.val['height_']).to_string()) | 
 |  | 
 |  | 
 | class blinkLayoutPointPrinter: | 
 |     "Print a blink::LayoutPoint" | 
 |     def __init__(self, val): | 
 |         self.val = val | 
 |  | 
 |     def to_string(self): | 
 |         return 'LayoutPoint(%s, %s)' % ( | 
 |             blinkLayoutUnitPrinter(self.val['x_']).to_string(), | 
 |             blinkLayoutUnitPrinter(self.val['y_']).to_string()) | 
 |  | 
 |  | 
 | class blinkQualifiedNamePrinter(StringPrinter): | 
 |     "Print a blink::QualifiedName" | 
 |  | 
 |     def __init__(self, val): | 
 |         super(blinkQualifiedNamePrinter, self).__init__(val) | 
 |         self.prefix_length = 0 | 
 |         self.length = 0 | 
 |         if self.val['impl_']: | 
 |             self.prefix_printer = WTFStringPrinter( | 
 |                 self.val['impl_']['ptr_']['prefix_']['string_']) | 
 |             self.local_name_printer = WTFStringPrinter( | 
 |                 self.val['impl_']['ptr_']['local_name_']['string_']) | 
 |             self.prefix_length = self.prefix_printer.get_length() | 
 |             if self.prefix_length > 0: | 
 |                 self.length = (self.prefix_length + 1 + | 
 |                     self.local_name_printer.get_length()) | 
 |             else: | 
 |                 self.length = self.local_name_printer.get_length() | 
 |  | 
 |     def get_length(self): | 
 |         return self.length | 
 |  | 
 |     def to_string(self): | 
 |         if self.get_length() == 0: | 
 |             return "(null)" | 
 |         else: | 
 |             if self.prefix_length > 0: | 
 |                 return (self.prefix_printer.to_string() + ":" + | 
 |                     self.local_name_printer.to_string()) | 
 |             else: | 
 |                 return self.local_name_printer.to_string() | 
 |  | 
 |  | 
 | class BlinkPixelsAndPercentPrinter: | 
 |     "Print a blink::PixelsAndPercent value" | 
 |     def __init__(self, val): | 
 |         self.val = val | 
 |  | 
 |     def to_string(self): | 
 |         return "(%gpx, %g%%)" % (self.val['pixels'], self.val['percent']) | 
 |  | 
 |  | 
 | class BlinkLengthPrinter: | 
 |     """Print a blink::Length.""" | 
 |     def __init__(self, val): | 
 |         self.val = val | 
 |  | 
 |     def to_string(self): | 
 |         ltype = self.val['type_'] | 
 |         if self.val['is_float_']: | 
 |             val = self.val['float_value_'] | 
 |         else: | 
 |             val = int(self.val['int_value_']) | 
 |  | 
 |         quirk = '' | 
 |         if self.val['quirk_']: | 
 |             quirk = ', quirk=true' | 
 |  | 
 |         if ltype == 0: | 
 |             return 'Length(Auto)' | 
 |         if ltype == 1: | 
 |             return 'Length(%g%%, Percent%s)' % (val, quirk) | 
 |         if ltype == 2: | 
 |             return 'Length(%g, Fixed%s)' % (val, quirk) | 
 |         if ltype == 3: | 
 |             return 'Length(Intrinsic)' | 
 |         if ltype == 4: | 
 |             return 'Length(MinIntrinsic)' | 
 |         if ltype == 5: | 
 |             return 'Length(MinContent)' | 
 |         if ltype == 6: | 
 |             return 'Length(MaxContent)' | 
 |         if ltype == 7: | 
 |             return 'Length(FillAvailable)' | 
 |         if ltype == 8: | 
 |             return 'Length(FitContent)' | 
 |         if ltype == 9: | 
 |             # Would like to print pixelsAndPercent() but can't call member | 
 |             # functions - https://sourceware.org/bugzilla/show_bug.cgi?id=13326 | 
 |             return 'Length(Calculated)' | 
 |         if ltype == 10: | 
 |             return 'Length(ExtendToZoom)' | 
 |         if ltype == 11: | 
 |             return 'Length(DeviceWidth)' | 
 |         if ltype == 12: | 
 |             return 'Length(DeviceHeight)' | 
 |         if ltype == 13: | 
 |             return 'Length(MaxSizeNone)' | 
 |         return 'Length(unknown type %i)' % ltype | 
 |  | 
 |  | 
 | class WTFVectorPrinter: | 
 |     """Pretty Printer for a WTF::Vector. | 
 |  | 
 |     The output of this pretty printer is similar to the output of std::vector's | 
 |     pretty printer, which is bundled in gcc. | 
 |  | 
 |     Example gdb session should look like: | 
 |     (gdb) p v | 
 |     $3 = WTF::Vector of length 7, capacity 16 = {7, 17, 27, 37, 47, 57, 67} | 
 |     (gdb) set print elements 3 | 
 |     (gdb) p v | 
 |     $6 = WTF::Vector of length 7, capacity 16 = {7, 17, 27...} | 
 |     (gdb) set print array | 
 |     (gdb) p v | 
 |     $7 = WTF::Vector of length 7, capacity 16 = { | 
 |       7, | 
 |       17, | 
 |       27 | 
 |       ... | 
 |     } | 
 |     (gdb) set print elements 200 | 
 |     (gdb) p v | 
 |     $8 = WTF::Vector of length 7, capacity 16 = { | 
 |       7, | 
 |       17, | 
 |       27, | 
 |       37, | 
 |       47, | 
 |       57, | 
 |       67 | 
 |     } | 
 |     """ | 
 |  | 
 |     class Iterator: | 
 |         def __init__(self, start, finish): | 
 |             self.item = start | 
 |             self.finish = finish | 
 |             self.count = 0 | 
 |  | 
 |         def __iter__(self): | 
 |             return self | 
 |  | 
 |         def __next__(self): | 
 |             if self.item == self.finish: | 
 |                 raise StopIteration | 
 |             count = self.count | 
 |             self.count += 1 | 
 |             element = self.item.dereference() | 
 |             self.item += 1 | 
 |             return ('[%d]' % count, element) | 
 |  | 
 |         # Python version < 3 compatibility: | 
 |         def next(self): | 
 |             return self.__next__() | 
 |  | 
 |     def __init__(self, val): | 
 |         self.val = val | 
 |  | 
 |     def children(self): | 
 |         start = self.val['buffer_'] | 
 |         return self.Iterator(start, start + self.val['size_']) | 
 |  | 
 |     def to_string(self): | 
 |         return ('%s of length %d, capacity %d' | 
 |                 % ('WTF::Vector', self.val['size_'], self.val['capacity_'])) | 
 |  | 
 |     def display_hint(self): | 
 |         return 'array' | 
 |  | 
 |  | 
 | # Copied from //tools/gdb/gdb_chrome.py | 
 | def typed_ptr(ptr): | 
 |     """Prints a pointer along with its exact type. | 
 |  | 
 |     By default, gdb would print just the address, which takes more | 
 |     steps to interpret. | 
 |     """ | 
 |     # Returning this as a cast expression surrounded by parentheses | 
 |     # makes it easier to cut+paste inside of gdb. | 
 |     return '((%s)%s)' % (ptr.dynamic_type, ptr) | 
 |  | 
 |  | 
 | class BlinkDataRefPrinter: | 
 |     def __init__(self, val): | 
 |         self.val = val | 
 |  | 
 |     def to_string(self): | 
 |         return 'DataRef(%s)' % (str(self.val['data_'])) | 
 |  | 
 |  | 
 | def add_pretty_printers(): | 
 |     pretty_printers = ( | 
 |         (re.compile("^WTF::Vector<.*>$"), WTFVectorPrinter), | 
 |         (re.compile("^WTF::AtomicString$"), WTFAtomicStringPrinter), | 
 |         (re.compile("^WTF::CString$"), WTFCStringPrinter), | 
 |         (re.compile("^WTF::String$"), WTFStringPrinter), | 
 |         (re.compile("^WTF::StringImpl$"), WTFStringImplPrinter), | 
 |         (re.compile("^blink::KURL$"), blinkKURLPrinter), | 
 |         (re.compile("^blink::LayoutUnit$"), blinkLayoutUnitPrinter), | 
 |         (re.compile("^blink::LayoutPoint$"), blinkLayoutPointPrinter), | 
 |         (re.compile("^blink::LayoutSize$"), blinkLayoutSizePrinter), | 
 |         (re.compile("^blink::QualifiedName$"), blinkQualifiedNamePrinter), | 
 |         (re.compile("^blink::PixelsAndPercent$"), BlinkPixelsAndPercentPrinter), | 
 |         (re.compile("^blink::Length$"), BlinkLengthPrinter), | 
 |         (re.compile("^blink::DataRef<.*>$"), BlinkDataRefPrinter), | 
 |     ) | 
 |  | 
 |     def lookup_function(val): | 
 |         """Function used to load pretty printers; will be passed to GDB.""" | 
 |         type = val.type | 
 |         if type.code == gdb.TYPE_CODE_REF: | 
 |             type = type.target() | 
 |         type = type.unqualified().strip_typedefs() | 
 |         tag = type.tag | 
 |         if tag: | 
 |             for function, pretty_printer in pretty_printers: | 
 |                 if function.search(tag): | 
 |                     return pretty_printer(val) | 
 |  | 
 |         if type.code == gdb.TYPE_CODE_PTR: | 
 |             name = str(type.target().unqualified()) | 
 |             if name == 'UChar': | 
 |                 return UCharStringPrinter(val) | 
 |             if name == 'LChar': | 
 |                 return LCharStringPrinter(val) | 
 |         return None | 
 |  | 
 |     gdb.pretty_printers.append(lookup_function) | 
 |  | 
 |  | 
 | add_pretty_printers() | 
 |  | 
 |  | 
 | class PrintPathToRootCommand(gdb.Command): | 
 |     """Command for printing WebKit Node trees. | 
 |  | 
 |     Usage: printpathtoroot variable_name""" | 
 |  | 
 |     def __init__(self): | 
 |         super(PrintPathToRootCommand, self).__init__("printpathtoroot", | 
 |             gdb.COMMAND_SUPPORT, | 
 |             gdb.COMPLETE_NONE) | 
 |  | 
 |     def invoke(self, arg, from_tty): | 
 |         element_type = gdb.lookup_type('blink::Element') | 
 |         node_type = gdb.lookup_type('blink::Node') | 
 |         frame = gdb.selected_frame() | 
 |         try: | 
 |             val = gdb.Frame.read_var(frame, arg) | 
 |         except: | 
 |             print("No such variable, or invalid type") | 
 |             return | 
 |  | 
 |         target_type = str(val.type.target().strip_typedefs()) | 
 |         if target_type == str(node_type): | 
 |             stack = [] | 
 |             while val: | 
 |                 stack.append([val, | 
 |                               val.cast(element_type.pointer()).dereference()[ | 
 |                                   'tag_name_']]) | 
 |                 val = val.dereference()['parent_'] | 
 |  | 
 |             padding = '' | 
 |             while len(stack) > 0: | 
 |                 pair = stack.pop() | 
 |                 print(padding, pair[1], pair[0]) | 
 |                 padding = padding + '  ' | 
 |         else: | 
 |             print('Sorry: I don\'t know how to deal with %s yet.' % target_type) | 
 |  | 
 |  | 
 | PrintPathToRootCommand() |