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