Home | History | Annotate | Download | only in gdb
      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