OLD | NEW |
(Empty) | |
| 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 pp_set.add_printer('content::SiteInstanceImpl', '^content::SiteInstanceImpl$', |
| 287 SiteInstanceImplPrinter) |
| 288 |
| 289 |
| 290 class RenderProcessHostImplPrinter(object): |
| 291 def __init__(self, val): |
| 292 self.val = val.cast(val.dynamic_type) |
| 293 |
| 294 def to_string(self): |
| 295 pid = '' |
| 296 try: |
| 297 child_process_launcher_ptr = ( |
| 298 self.val['child_process_launcher_']['impl_']['data_']['ptr']) |
| 299 if child_process_launcher_ptr: |
| 300 context = (child_process_launcher_ptr['context_']['ptr_']) |
| 301 if context: |
| 302 pid = ' PID %s' % str(context['process_']['process_']) |
| 303 except gdb.error: |
| 304 # The definition of the Context type may not be available. |
| 305 # b/8242773 |
| 306 pass |
| 307 return 'RenderProcessHostImpl@%s%s' % (self.val.address, pid) |
| 308 |
| 309 def children(self): |
| 310 yield ('id_', self.val['id_']) |
| 311 yield ('listeners_', |
| 312 self.val['listeners_']['data_']) |
| 313 yield ('worker_ref_count_', self.val['worker_ref_count_']) |
| 314 yield ('fast_shutdown_started_', self.val['fast_shutdown_started_']) |
| 315 yield ('deleting_soon_', self.val['deleting_soon_']) |
| 316 yield ('pending_views_', self.val['pending_views_']) |
| 317 yield ('visible_widgets_', self.val['visible_widgets_']) |
| 318 yield ('backgrounded_', self.val['backgrounded_']) |
| 319 yield ('widget_helper_', self.val['widget_helper_']) |
| 320 yield ('is_initialized_', self.val['is_initialized_']) |
| 321 yield ('browser_context_', typed_ptr(self.val['browser_context_'])) |
| 322 yield ('sudden_termination_allowed_', |
| 323 self.val['sudden_termination_allowed_']) |
| 324 yield ('ignore_input_events_', self.val['ignore_input_events_']) |
| 325 yield ('is_guest_', self.val['is_guest_']) |
| 326 pp_set.add_printer('content::RenderProcessHostImpl', |
| 327 '^content::RenderProcessHostImpl$', |
| 328 RenderProcessHostImplPrinter) |
| 329 |
| 330 |
| 331 gdb.printing.register_pretty_printer(gdb, pp_set, replace=_DEBUGGING) |
OLD | NEW |