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