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