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.
5 """GDB support for Chrome types.
7 Add this to your gdb by amending your ~/.gdbinit as follows:
10 sys.path.insert(0, "/path/to/tools/gdb/")
14 This module relies on the WebKit gdb module already existing in
18 (gdb) p /r any_variable
19 to print |any_variable| without using any printers.
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
33 pp_set = gdb.printing.RegexpCollectionPrettyPrinter("chromium")
37 """Prints a pointer along with its exact type.
39 By default, gdb would print just the address, which takes more
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)
47 class Printer(object):
48 def __init__(self, val):
52 class StringPrinter(Printer):
53 def display_hint(self):
57 class String16Printer(StringPrinter):
59 return webkit.ustring_to_string(self.val['_M_dataplus']['_M_p'])
62 '^string16|std::basic_string<(unsigned short|char16|base::char16).*>$',
66 class GURLPrinter(StringPrinter):
68 return self.val['spec_']
69 pp_set.add_printer('GURL', '^GURL$', GURLPrinter)
72 class FilePathPrinter(StringPrinter):
74 return self.val['path_']['_M_dataplus']['_M_p']
75 pp_set.add_printer('FilePath', '^FilePath$', FilePathPrinter)
78 class SizePrinter(Printer):
80 return '%sx%s' % (self.val['width_'], self.val['height_'])
81 pp_set.add_printer('gfx::Size', '^gfx::(Size|SizeF|SizeBase<.*>)$', SizePrinter)
84 class PointPrinter(Printer):
86 return '%s,%s' % (self.val['x_'], self.val['y_'])
87 pp_set.add_printer('gfx::Point', '^gfx::(Point|PointF|PointBase<.*>)$',
91 class RectPrinter(Printer):
93 return '%s %s' % (self.val['origin_'], self.val['size_'])
94 pp_set.add_printer('gfx::Rect', '^gfx::(Rect|RectF|RectBase<.*>)$',
98 class SmartPtrPrinter(Printer):
100 return '%s%s' % (self.typename, typed_ptr(self.ptr()))
103 class ScopedRefPtrPrinter(SmartPtrPrinter):
104 typename = 'scoped_refptr'
106 return self.val['ptr_']
107 pp_set.add_printer('scoped_refptr', '^scoped_refptr<.*>$', ScopedRefPtrPrinter)
110 class LinkedPtrPrinter(SmartPtrPrinter):
111 typename = 'linked_ptr'
113 return self.val['value_']
114 pp_set.add_printer('linked_ptr', '^linked_ptr<.*>$', LinkedPtrPrinter)
117 class WeakPtrPrinter(SmartPtrPrinter):
118 typename = 'base::WeakPtr'
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)
127 class CallbackPrinter(Printer):
128 """Callbacks provide no usable information so reduce the space they take."""
131 pp_set.add_printer('base::Callback', '^base::Callback<.*>$', CallbackPrinter)
134 class LocationPrinter(Printer):
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$',
143 class LockPrinter(Printer):
146 if self.val['owned_by_thread_']:
147 return 'Locked by thread %s' % self.val['owning_thread_id_']
151 return 'Unknown state'
152 pp_set.add_printer('base::Lock', '^base::Lock$', LockPrinter)
155 class TimeDeltaPrinter(object):
156 def __init__(self, val):
157 self._timedelta = datetime.timedelta(microseconds=int(val['delta_']))
160 return self._timedelta
163 return str(self._timedelta)
164 pp_set.add_printer('base::TimeDelta', '^base::TimeDelta$', TimeDeltaPrinter)
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)
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)))
182 return self._datetime
185 return str(self._datetime)
186 pp_set.add_printer('base::Time', '^base::Time$', TimePrinter)
189 class IpcMessagePrinter(Printer):
191 return self.val['header_'].cast(
192 gdb.lookup_type('IPC::Message::Header').pointer())
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)
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:
208 yield (field.name, self.val[field.name])
209 pp_set.add_printer('IPC::Message', '^IPC::Message$', IpcMessagePrinter)
212 class NotificationRegistrarPrinter(Printer):
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))
228 return 'NotificationRegistrar'
229 pp_set.add_printer('content::NotificationRegistrar',
230 '^content::NotificationRegistrar$',
231 NotificationRegistrarPrinter)
234 class SiteInstanceImplPrinter(object):
235 def __init__(self, val):
236 self.val = val.cast(val.dynamic_type)
239 return 'SiteInstanceImpl@%s for %s' % (
240 self.val.address, self.val['site_'])
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)
256 class RenderProcessHostImplPrinter(object):
257 def __init__(self, val):
258 self.val = val.cast(val.dynamic_type)
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_'])
268 pid = ' PID %s' % str(context['process_']['process_'])
270 # The definition of the Context type may not be available.
273 return 'RenderProcessHostImpl@%s%s' % (self.val.address, pid)
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)
296 gdb.printing.register_pretty_printer(gdb, pp_set, replace=_DEBUGGING)