| # 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)' % ( |
| WTFRefOrOwnPtrPrinter(self.val['data_']).to_string()) |
| |
| |
| 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() |