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