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