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