Home | History | Annotate | Download | only in gdb
      1 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 """GDB support for Chrome types.
      6 
      7 Add this to your gdb by amending your ~/.gdbinit as follows:
      8   python
      9   import sys
     10   sys.path.insert(0, "/path/to/tools/gdb/")
     11   import gdb_chrome
     12   end
     13 
     14 Use
     15   (gdb) p /r any_variable
     16 to print |any_variable| without using any printers.
     17 """
     18 
     19 import datetime
     20 import gdb
     21 import gdb.printing
     22 import os
     23 import sys
     24 
     25 sys.path.insert(0, os.path.join(
     26     os.path.dirname(os.path.abspath(__file__)),
     27     '..', '..', 'third_party', 'WebKit', 'Tools', 'gdb'))
     28 try:
     29   import webkit
     30 finally:
     31   sys.path.pop(0)
     32 
     33 # When debugging this module, set the below variable to True, and then use
     34 #   (gdb) python del sys.modules['gdb_chrome']
     35 #   (gdb) python import gdb_chrome
     36 # to reload.
     37 _DEBUGGING = False
     38 
     39 
     40 pp_set = gdb.printing.RegexpCollectionPrettyPrinter("chromium")
     41 
     42 
     43 def typed_ptr(ptr):
     44     """Prints a pointer along with its exact type.
     45 
     46     By default, gdb would print just the address, which takes more
     47     steps to interpret.
     48     """
     49     # Returning this as a cast expression surrounded by parentheses
     50     # makes it easier to cut+paste inside of gdb.
     51     return '((%s)%s)' % (ptr.dynamic_type, ptr)
     52 
     53 
     54 def yield_fields(val):
     55     """Use this in a printer's children() method to print an object's fields.
     56 
     57     e.g.
     58       def children():
     59         for result in yield_fields(self.val):
     60           yield result
     61     """
     62     try:
     63         fields = val.type.target().fields()
     64     except:
     65         fields = val.type.fields()
     66     for field in fields:
     67         if field.is_base_class:
     68             yield (field.name, val.cast(gdb.lookup_type(field.name)))
     69         else:
     70             yield (field.name, val[field.name])
     71 
     72 
     73 class Printer(object):
     74     def __init__(self, val):
     75         self.val = val
     76 
     77 
     78 class StringPrinter(Printer):
     79     def display_hint(self):
     80         return 'string'
     81 
     82 
     83 class String16Printer(StringPrinter):
     84     def to_string(self):
     85         return webkit.ustring_to_string(self.val['_M_dataplus']['_M_p'])
     86 pp_set.add_printer(
     87     'string16',
     88     '^string16|std::basic_string<(unsigned short|base::char16).*>$',
     89     String16Printer);
     90 
     91 
     92 class GURLPrinter(StringPrinter):
     93     def to_string(self):
     94         return self.val['spec_']
     95 pp_set.add_printer('GURL', '^GURL$', GURLPrinter)
     96 
     97 
     98 class FilePathPrinter(StringPrinter):
     99     def to_string(self):
    100         return self.val['path_']['_M_dataplus']['_M_p']
    101 pp_set.add_printer('FilePath', '^FilePath$', FilePathPrinter)
    102 
    103 
    104 class SizePrinter(Printer):
    105     def to_string(self):
    106         return '%sx%s' % (self.val['width_'], self.val['height_'])
    107 pp_set.add_printer('gfx::Size', '^gfx::(Size|SizeF|SizeBase<.*>)$', SizePrinter)
    108 
    109 
    110 class PointPrinter(Printer):
    111     def to_string(self):
    112         return '%s,%s' % (self.val['x_'], self.val['y_'])
    113 pp_set.add_printer('gfx::Point', '^gfx::(Point|PointF|PointBase<.*>)$',
    114                    PointPrinter)
    115 
    116 
    117 class RectPrinter(Printer):
    118     def to_string(self):
    119         return '%s %s' % (self.val['origin_'], self.val['size_'])
    120 pp_set.add_printer('gfx::Rect', '^gfx::(Rect|RectF|RectBase<.*>)$',
    121                    RectPrinter)
    122 
    123 
    124 class SmartPtrPrinter(Printer):
    125     def to_string(self):
    126         return '%s%s' % (self.typename, typed_ptr(self.ptr()))
    127 
    128 
    129 class ScopedRefPtrPrinter(SmartPtrPrinter):
    130     typename = 'scoped_refptr'
    131     def ptr(self):
    132         return self.val['ptr_']
    133 pp_set.add_printer('scoped_refptr', '^scoped_refptr<.*>$', ScopedRefPtrPrinter)
    134 
    135 
    136 class LinkedPtrPrinter(SmartPtrPrinter):
    137     typename = 'linked_ptr'
    138     def ptr(self):
    139         return self.val['value_']
    140 pp_set.add_printer('linked_ptr', '^linked_ptr<.*>$', LinkedPtrPrinter)
    141 
    142 
    143 class WeakPtrPrinter(SmartPtrPrinter):
    144     typename = 'base::WeakPtr'
    145     def ptr(self):
    146         flag = ScopedRefPtrPrinter(self.val['ref_']['flag_']).ptr()
    147         if flag and flag['is_valid_']:
    148             return self.val['ptr_']
    149         return gdb.Value(0).cast(self.val['ptr_'].type)
    150 pp_set.add_printer('base::WeakPtr', '^base::WeakPtr<.*>$', WeakPtrPrinter)
    151 
    152 
    153 class CallbackPrinter(Printer):
    154     """Callbacks provide no usable information so reduce the space they take."""
    155     def to_string(self):
    156         return '...'
    157 pp_set.add_printer('base::Callback', '^base::Callback<.*>$', CallbackPrinter)
    158 
    159 
    160 class LocationPrinter(Printer):
    161     def to_string(self):
    162         return '%s()@%s:%s' % (self.val['function_name_'].string(),
    163                                self.val['file_name_'].string(),
    164                                self.val['line_number_'])
    165 pp_set.add_printer('tracked_objects::Location', '^tracked_objects::Location$',
    166                    LocationPrinter)
    167 
    168 
    169 class PendingTaskPrinter(Printer):
    170     def to_string(self):
    171         return 'From %s' % (self.val['posted_from'],)
    172 
    173     def children(self):
    174         for result in yield_fields(self.val):
    175             if result[0] not in ('task', 'posted_from'):
    176                 yield result
    177 pp_set.add_printer('base::PendingTask', '^base::PendingTask$',
    178                    PendingTaskPrinter)
    179 
    180 
    181 class LockPrinter(Printer):
    182     def to_string(self):
    183         try:
    184             if self.val['owned_by_thread_']:
    185                 return 'Locked by thread %s' % self.val['owning_thread_id_']
    186             else:
    187                 return 'Unlocked'
    188         except gdb.error:
    189             return 'Unknown state'
    190 pp_set.add_printer('base::Lock', '^base::Lock$', LockPrinter)
    191 
    192 
    193 class TimeDeltaPrinter(object):
    194     def __init__(self, val):
    195         self._timedelta = datetime.timedelta(microseconds=int(val['delta_']))
    196 
    197     def timedelta(self):
    198         return self._timedelta
    199 
    200     def to_string(self):
    201         return str(self._timedelta)
    202 pp_set.add_printer('base::TimeDelta', '^base::TimeDelta$', TimeDeltaPrinter)
    203 
    204 
    205 class TimeTicksPrinter(TimeDeltaPrinter):
    206     def __init__(self, val):
    207         self._timedelta = datetime.timedelta(microseconds=int(val['ticks_']))
    208 pp_set.add_printer('base::TimeTicks', '^base::TimeTicks$', TimeTicksPrinter)
    209 
    210 
    211 class TimePrinter(object):
    212     def __init__(self, val):
    213         timet_offset = gdb.parse_and_eval(
    214             'base::Time::kTimeTToMicrosecondsOffset')
    215         self._datetime = (datetime.datetime.fromtimestamp(0) +
    216                           datetime.timedelta(microseconds=
    217                                              int(val['us_'] - timet_offset)))
    218 
    219     def datetime(self):
    220         return self._datetime
    221 
    222     def to_string(self):
    223         return str(self._datetime)
    224 pp_set.add_printer('base::Time', '^base::Time$', TimePrinter)
    225 
    226 
    227 class IpcMessagePrinter(Printer):
    228     def header(self):
    229         return self.val['header_'].cast(
    230             gdb.lookup_type('IPC::Message::Header').pointer())
    231 
    232     def to_string(self):
    233         message_type = self.header()['type']
    234         return '%s of kind %s line %s' % (
    235             self.val.dynamic_type,
    236             (message_type >> 16).cast(gdb.lookup_type('IPCMessageStart')),
    237             message_type & 0xffff)
    238 
    239     def children(self):
    240         yield ('header_', self.header().dereference())
    241         yield ('capacity_after_header_', self.val['capacity_after_header_'])
    242         for field in self.val.type.fields():
    243             if field.is_base_class:
    244                 continue
    245             yield (field.name, self.val[field.name])
    246 pp_set.add_printer('IPC::Message', '^IPC::Message$', IpcMessagePrinter)
    247 
    248 
    249 class NotificationRegistrarPrinter(Printer):
    250     def to_string(self):
    251         try:
    252             registrations = self.val['registered_']
    253             vector_finish = registrations['_M_impl']['_M_finish']
    254             vector_start = registrations['_M_impl']['_M_start']
    255             if vector_start == vector_finish:
    256                 return 'Not watching notifications'
    257             if vector_start.dereference().type.sizeof == 0:
    258                 # Incomplete type: b/8242773
    259                 return 'Watching some notifications'
    260             return ('Watching %s notifications; '
    261                     'print %s->registered_ for details') % (
    262                         int(vector_finish - vector_start),
    263                         typed_ptr(self.val.address))
    264         except gdb.error:
    265             return 'NotificationRegistrar'
    266 pp_set.add_printer('content::NotificationRegistrar',
    267                    '^content::NotificationRegistrar$',
    268                    NotificationRegistrarPrinter)
    269 
    270 
    271 class SiteInstanceImplPrinter(object):
    272     def __init__(self, val):
    273         self.val = val.cast(val.dynamic_type)
    274 
    275     def to_string(self):
    276         return 'SiteInstanceImpl@%s for %s' % (
    277             self.val.address, self.val['site_'])
    278 
    279     def children(self):
    280         yield ('id_', self.val['id_'])
    281         yield ('has_site_', self.val['has_site_'])
    282         if self.val['browsing_instance_']['ptr_']:
    283             yield ('browsing_instance_', self.val['browsing_instance_']['ptr_'])
    284         if self.val['process_']:
    285             yield ('process_', typed_ptr(self.val['process_']))
    286         if self.val['render_process_host_factory_']:
    287             yield ('render_process_host_factory_',
    288                    self.val['render_process_host_factory_'])
    289 pp_set.add_printer('content::SiteInstanceImpl', '^content::SiteInstanceImpl$',
    290                    SiteInstanceImplPrinter)
    291 
    292 
    293 class RenderProcessHostImplPrinter(object):
    294     def __init__(self, val):
    295         self.val = val.cast(val.dynamic_type)
    296 
    297     def to_string(self):
    298         pid = ''
    299         try:
    300             child_process_launcher_ptr = (
    301                 self.val['child_process_launcher_']['impl_']['data_']['ptr'])
    302             if child_process_launcher_ptr:
    303                 context = (child_process_launcher_ptr['context_']['ptr_'])
    304                 if context:
    305                     pid = ' PID %s' % str(context['process_']['process_'])
    306         except gdb.error:
    307             # The definition of the Context type may not be available.
    308             # b/8242773
    309             pass
    310         return 'RenderProcessHostImpl@%s%s' % (self.val.address, pid)
    311 
    312     def children(self):
    313         yield ('id_', self.val['id_'])
    314         yield ('listeners_',
    315                self.val['listeners_']['data_'])
    316         yield ('worker_ref_count_', self.val['worker_ref_count_'])
    317         yield ('fast_shutdown_started_', self.val['fast_shutdown_started_'])
    318         yield ('deleting_soon_', self.val['deleting_soon_'])
    319         yield ('pending_views_', self.val['pending_views_'])
    320         yield ('visible_widgets_', self.val['visible_widgets_'])
    321         yield ('backgrounded_', self.val['backgrounded_'])
    322         yield ('widget_helper_', self.val['widget_helper_'])
    323         yield ('is_initialized_', self.val['is_initialized_'])
    324         yield ('browser_context_', typed_ptr(self.val['browser_context_']))
    325         yield ('sudden_termination_allowed_',
    326                self.val['sudden_termination_allowed_'])
    327         yield ('ignore_input_events_', self.val['ignore_input_events_'])
    328         yield ('is_guest_', self.val['is_guest_'])
    329 pp_set.add_printer('content::RenderProcessHostImpl',
    330                    '^content::RenderProcessHostImpl$',
    331                    RenderProcessHostImplPrinter)
    332 
    333 
    334 gdb.printing.register_pretty_printer(gdb, pp_set, replace=_DEBUGGING)
    335