| Index: tools/gdb/gdb_chrome.py
|
| diff --git a/tools/gdb/gdb_chrome.py b/tools/gdb/gdb_chrome.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2ba7ce8b1daff0ee6496f24c7abb9bcdb2270915
|
| --- /dev/null
|
| +++ b/tools/gdb/gdb_chrome.py
|
| @@ -0,0 +1,331 @@
|
| +# Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +"""GDB support for Chrome types.
|
| +
|
| +Add this to your gdb by amending your ~/.gdbinit as follows:
|
| + python
|
| + import sys
|
| + sys.path.insert(0, "/path/to/tools/gdb/")
|
| + import gdb_chrome
|
| + end
|
| +
|
| +Use
|
| + (gdb) p /r any_variable
|
| +to print |any_variable| without using any printers.
|
| +"""
|
| +
|
| +import datetime
|
| +import gdb
|
| +import gdb.printing
|
| +import os
|
| +import sys
|
| +
|
| +sys.path.insert(0, os.path.join(
|
| + os.path.dirname(os.path.abspath(__file__)),
|
| + '..', '..', 'third_party', 'WebKit', 'Tools', 'gdb'))
|
| +try:
|
| + import webkit
|
| +finally:
|
| + sys.path.pop(0)
|
| +
|
| +# When debugging this module, set the below variable to True, and then use
|
| +# (gdb) python del sys.modules['gdb_chrome']
|
| +# (gdb) python import gdb_chrome
|
| +# to reload.
|
| +_DEBUGGING = False
|
| +
|
| +
|
| +pp_set = gdb.printing.RegexpCollectionPrettyPrinter("chromium")
|
| +
|
| +
|
| +def typed_ptr(ptr):
|
| + """Prints a pointer along with its exact type.
|
| +
|
| + By default, gdb would print just the address, which takes more
|
| + steps to interpret.
|
| + """
|
| + # Returning this as a cast expression surrounded by parentheses
|
| + # makes it easier to cut+paste inside of gdb.
|
| + return '((%s)%s)' % (ptr.dynamic_type, ptr)
|
| +
|
| +
|
| +def yield_fields(val):
|
| + """Use this in a printer's children() method to print an object's fields.
|
| +
|
| + e.g.
|
| + def children():
|
| + for result in yield_fields(self.val):
|
| + yield result
|
| + """
|
| + try:
|
| + fields = val.type.target().fields()
|
| + except:
|
| + fields = val.type.fields()
|
| + for field in fields:
|
| + if field.is_base_class:
|
| + yield (field.name, val.cast(gdb.lookup_type(field.name)))
|
| + else:
|
| + yield (field.name, val[field.name])
|
| +
|
| +
|
| +class Printer(object):
|
| + def __init__(self, val):
|
| + self.val = val
|
| +
|
| +
|
| +class StringPrinter(Printer):
|
| + def display_hint(self):
|
| + return 'string'
|
| +
|
| +
|
| +class String16Printer(StringPrinter):
|
| + def to_string(self):
|
| + return webkit.ustring_to_string(self.val['_M_dataplus']['_M_p'])
|
| +pp_set.add_printer(
|
| + 'string16',
|
| + '^string16|std::basic_string<(unsigned short|base::char16).*>$',
|
| + String16Printer);
|
| +
|
| +
|
| +class GURLPrinter(StringPrinter):
|
| + def to_string(self):
|
| + return self.val['spec_']
|
| +pp_set.add_printer('GURL', '^GURL$', GURLPrinter)
|
| +
|
| +
|
| +class FilePathPrinter(StringPrinter):
|
| + def to_string(self):
|
| + return self.val['path_']['_M_dataplus']['_M_p']
|
| +pp_set.add_printer('FilePath', '^FilePath$', FilePathPrinter)
|
| +
|
| +
|
| +class SizePrinter(Printer):
|
| + def to_string(self):
|
| + return '%sx%s' % (self.val['width_'], self.val['height_'])
|
| +pp_set.add_printer('gfx::Size', '^gfx::(Size|SizeF|SizeBase<.*>)$', SizePrinter)
|
| +
|
| +
|
| +class PointPrinter(Printer):
|
| + def to_string(self):
|
| + return '%s,%s' % (self.val['x_'], self.val['y_'])
|
| +pp_set.add_printer('gfx::Point', '^gfx::(Point|PointF|PointBase<.*>)$',
|
| + PointPrinter)
|
| +
|
| +
|
| +class RectPrinter(Printer):
|
| + def to_string(self):
|
| + return '%s %s' % (self.val['origin_'], self.val['size_'])
|
| +pp_set.add_printer('gfx::Rect', '^gfx::(Rect|RectF|RectBase<.*>)$',
|
| + RectPrinter)
|
| +
|
| +
|
| +class SmartPtrPrinter(Printer):
|
| + def to_string(self):
|
| + return '%s%s' % (self.typename, typed_ptr(self.ptr()))
|
| +
|
| +
|
| +class ScopedRefPtrPrinter(SmartPtrPrinter):
|
| + typename = 'scoped_refptr'
|
| + def ptr(self):
|
| + return self.val['ptr_']
|
| +pp_set.add_printer('scoped_refptr', '^scoped_refptr<.*>$', ScopedRefPtrPrinter)
|
| +
|
| +
|
| +class LinkedPtrPrinter(SmartPtrPrinter):
|
| + typename = 'linked_ptr'
|
| + def ptr(self):
|
| + return self.val['value_']
|
| +pp_set.add_printer('linked_ptr', '^linked_ptr<.*>$', LinkedPtrPrinter)
|
| +
|
| +
|
| +class WeakPtrPrinter(SmartPtrPrinter):
|
| + typename = 'base::WeakPtr'
|
| + def ptr(self):
|
| + flag = ScopedRefPtrPrinter(self.val['ref_']['flag_']).ptr()
|
| + if flag and flag['is_valid_']:
|
| + return self.val['ptr_']
|
| + return gdb.Value(0).cast(self.val['ptr_'].type)
|
| +pp_set.add_printer('base::WeakPtr', '^base::WeakPtr<.*>$', WeakPtrPrinter)
|
| +
|
| +
|
| +class CallbackPrinter(Printer):
|
| + """Callbacks provide no usable information so reduce the space they take."""
|
| + def to_string(self):
|
| + return '...'
|
| +pp_set.add_printer('base::Callback', '^base::Callback<.*>$', CallbackPrinter)
|
| +
|
| +
|
| +class LocationPrinter(Printer):
|
| + def to_string(self):
|
| + return '%s()@%s:%s' % (self.val['function_name_'].string(),
|
| + self.val['file_name_'].string(),
|
| + self.val['line_number_'])
|
| +pp_set.add_printer('tracked_objects::Location', '^tracked_objects::Location$',
|
| + LocationPrinter)
|
| +
|
| +
|
| +class PendingTaskPrinter(Printer):
|
| + def to_string(self):
|
| + return 'From %s' % (self.val['posted_from'],)
|
| +
|
| + def children(self):
|
| + for result in yield_fields(self.val):
|
| + if result[0] not in ('task', 'posted_from'):
|
| + yield result
|
| +pp_set.add_printer('base::PendingTask', '^base::PendingTask$',
|
| + PendingTaskPrinter)
|
| +
|
| +
|
| +class LockPrinter(Printer):
|
| + def to_string(self):
|
| + try:
|
| + if self.val['owned_by_thread_']:
|
| + return 'Locked by thread %s' % self.val['owning_thread_id_']
|
| + else:
|
| + return 'Unlocked'
|
| + except gdb.error:
|
| + return 'Unknown state'
|
| +pp_set.add_printer('base::Lock', '^base::Lock$', LockPrinter)
|
| +
|
| +
|
| +class TimeDeltaPrinter(object):
|
| + def __init__(self, val):
|
| + self._timedelta = datetime.timedelta(microseconds=int(val['delta_']))
|
| +
|
| + def timedelta(self):
|
| + return self._timedelta
|
| +
|
| + def to_string(self):
|
| + return str(self._timedelta)
|
| +pp_set.add_printer('base::TimeDelta', '^base::TimeDelta$', TimeDeltaPrinter)
|
| +
|
| +
|
| +class TimeTicksPrinter(TimeDeltaPrinter):
|
| + def __init__(self, val):
|
| + self._timedelta = datetime.timedelta(microseconds=int(val['ticks_']))
|
| +pp_set.add_printer('base::TimeTicks', '^base::TimeTicks$', TimeTicksPrinter)
|
| +
|
| +
|
| +class TimePrinter(object):
|
| + def __init__(self, val):
|
| + timet_offset = gdb.parse_and_eval(
|
| + 'base::Time::kTimeTToMicrosecondsOffset')
|
| + self._datetime = (datetime.datetime.fromtimestamp(0) +
|
| + datetime.timedelta(microseconds=
|
| + int(val['us_'] - timet_offset)))
|
| +
|
| + def datetime(self):
|
| + return self._datetime
|
| +
|
| + def to_string(self):
|
| + return str(self._datetime)
|
| +pp_set.add_printer('base::Time', '^base::Time$', TimePrinter)
|
| +
|
| +
|
| +class IpcMessagePrinter(Printer):
|
| + def header(self):
|
| + return self.val['header_'].cast(
|
| + gdb.lookup_type('IPC::Message::Header').pointer())
|
| +
|
| + def to_string(self):
|
| + message_type = self.header()['type']
|
| + return '%s of kind %s line %s' % (
|
| + self.val.dynamic_type,
|
| + (message_type >> 16).cast(gdb.lookup_type('IPCMessageStart')),
|
| + message_type & 0xffff)
|
| +
|
| + def children(self):
|
| + yield ('header_', self.header().dereference())
|
| + yield ('capacity_after_header_', self.val['capacity_after_header_'])
|
| + for field in self.val.type.fields():
|
| + if field.is_base_class:
|
| + continue
|
| + yield (field.name, self.val[field.name])
|
| +pp_set.add_printer('IPC::Message', '^IPC::Message$', IpcMessagePrinter)
|
| +
|
| +
|
| +class NotificationRegistrarPrinter(Printer):
|
| + def to_string(self):
|
| + try:
|
| + registrations = self.val['registered_']
|
| + vector_finish = registrations['_M_impl']['_M_finish']
|
| + vector_start = registrations['_M_impl']['_M_start']
|
| + if vector_start == vector_finish:
|
| + return 'Not watching notifications'
|
| + if vector_start.dereference().type.sizeof == 0:
|
| + # Incomplete type: b/8242773
|
| + return 'Watching some notifications'
|
| + return ('Watching %s notifications; '
|
| + 'print %s->registered_ for details') % (
|
| + int(vector_finish - vector_start),
|
| + typed_ptr(self.val.address))
|
| + except gdb.error:
|
| + return 'NotificationRegistrar'
|
| +pp_set.add_printer('content::NotificationRegistrar',
|
| + '^content::NotificationRegistrar$',
|
| + NotificationRegistrarPrinter)
|
| +
|
| +
|
| +class SiteInstanceImplPrinter(object):
|
| + def __init__(self, val):
|
| + self.val = val.cast(val.dynamic_type)
|
| +
|
| + def to_string(self):
|
| + return 'SiteInstanceImpl@%s for %s' % (
|
| + self.val.address, self.val['site_'])
|
| +
|
| + def children(self):
|
| + yield ('id_', self.val['id_'])
|
| + yield ('has_site_', self.val['has_site_'])
|
| + if self.val['browsing_instance_']['ptr_']:
|
| + yield ('browsing_instance_', self.val['browsing_instance_']['ptr_'])
|
| + if self.val['process_']:
|
| + yield ('process_', typed_ptr(self.val['process_']))
|
| +pp_set.add_printer('content::SiteInstanceImpl', '^content::SiteInstanceImpl$',
|
| + SiteInstanceImplPrinter)
|
| +
|
| +
|
| +class RenderProcessHostImplPrinter(object):
|
| + def __init__(self, val):
|
| + self.val = val.cast(val.dynamic_type)
|
| +
|
| + def to_string(self):
|
| + pid = ''
|
| + try:
|
| + child_process_launcher_ptr = (
|
| + self.val['child_process_launcher_']['impl_']['data_']['ptr'])
|
| + if child_process_launcher_ptr:
|
| + context = (child_process_launcher_ptr['context_']['ptr_'])
|
| + if context:
|
| + pid = ' PID %s' % str(context['process_']['process_'])
|
| + except gdb.error:
|
| + # The definition of the Context type may not be available.
|
| + # b/8242773
|
| + pass
|
| + return 'RenderProcessHostImpl@%s%s' % (self.val.address, pid)
|
| +
|
| + def children(self):
|
| + yield ('id_', self.val['id_'])
|
| + yield ('listeners_',
|
| + self.val['listeners_']['data_'])
|
| + yield ('worker_ref_count_', self.val['worker_ref_count_'])
|
| + yield ('fast_shutdown_started_', self.val['fast_shutdown_started_'])
|
| + yield ('deleting_soon_', self.val['deleting_soon_'])
|
| + yield ('pending_views_', self.val['pending_views_'])
|
| + yield ('visible_widgets_', self.val['visible_widgets_'])
|
| + yield ('backgrounded_', self.val['backgrounded_'])
|
| + yield ('widget_helper_', self.val['widget_helper_'])
|
| + yield ('is_initialized_', self.val['is_initialized_'])
|
| + yield ('browser_context_', typed_ptr(self.val['browser_context_']))
|
| + yield ('sudden_termination_allowed_',
|
| + self.val['sudden_termination_allowed_'])
|
| + yield ('ignore_input_events_', self.val['ignore_input_events_'])
|
| + yield ('is_guest_', self.val['is_guest_'])
|
| +pp_set.add_printer('content::RenderProcessHostImpl',
|
| + '^content::RenderProcessHostImpl$',
|
| + RenderProcessHostImplPrinter)
|
| +
|
| +
|
| +gdb.printing.register_pretty_printer(gdb, pp_set, replace=_DEBUGGING)
|
|
|