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