• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
7Add 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
14This module relies on the WebKit gdb module already existing in
15your Python path.
16
17Use
18  (gdb) p /r any_variable
19to print |any_variable| without using any printers.
20"""
21
22import datetime
23import gdb
24import gdb.printing
25import webkit
26
27# When debugging this module, set the below variable to True, and then use
28#   (gdb) python del sys.modules['gdb_chrome']
29#   (gdb) python import gdb_chrome
30# to reload.
31_DEBUGGING = False
32
33
34pp_set = gdb.printing.RegexpCollectionPrettyPrinter("chromium")
35
36
37def typed_ptr(ptr):
38    """Prints a pointer along with its exact type.
39
40    By default, gdb would print just the address, which takes more
41    steps to interpret.
42    """
43    # Returning this as a cast expression surrounded by parentheses
44    # makes it easier to cut+paste inside of gdb.
45    return '((%s)%s)' % (ptr.dynamic_type, ptr)
46
47
48class Printer(object):
49    def __init__(self, val):
50        self.val = val
51
52
53class StringPrinter(Printer):
54    def display_hint(self):
55        return 'string'
56
57
58class String16Printer(StringPrinter):
59    def to_string(self):
60        return webkit.ustring_to_string(self.val['_M_dataplus']['_M_p'])
61pp_set.add_printer(
62    'string16',
63    '^string16|std::basic_string<(unsigned short|char16|base::char16).*>$',
64    String16Printer);
65
66
67class GURLPrinter(StringPrinter):
68    def to_string(self):
69        return self.val['spec_']
70pp_set.add_printer('GURL', '^GURL$', GURLPrinter)
71
72
73class FilePathPrinter(StringPrinter):
74    def to_string(self):
75        return self.val['path_']['_M_dataplus']['_M_p']
76pp_set.add_printer('FilePath', '^FilePath$', FilePathPrinter)
77
78
79class SizePrinter(Printer):
80    def to_string(self):
81        return '%sx%s' % (self.val['width_'], self.val['height_'])
82pp_set.add_printer('gfx::Size', '^gfx::(Size|SizeF|SizeBase<.*>)$', SizePrinter)
83
84
85class PointPrinter(Printer):
86    def to_string(self):
87        return '%s,%s' % (self.val['x_'], self.val['y_'])
88pp_set.add_printer('gfx::Point', '^gfx::(Point|PointF|PointBase<.*>)$',
89                   PointPrinter)
90
91
92class RectPrinter(Printer):
93    def to_string(self):
94        return '%s %s' % (self.val['origin_'], self.val['size_'])
95pp_set.add_printer('gfx::Rect', '^gfx::(Rect|RectF|RectBase<.*>)$',
96                   RectPrinter)
97
98
99class SmartPtrPrinter(Printer):
100    def to_string(self):
101        return '%s%s' % (self.typename, typed_ptr(self.ptr()))
102
103
104class ScopedRefPtrPrinter(SmartPtrPrinter):
105    typename = 'scoped_refptr'
106    def ptr(self):
107        return self.val['ptr_']
108pp_set.add_printer('scoped_refptr', '^scoped_refptr<.*>$', ScopedRefPtrPrinter)
109
110
111class LinkedPtrPrinter(SmartPtrPrinter):
112    typename = 'linked_ptr'
113    def ptr(self):
114        return self.val['value_']
115pp_set.add_printer('linked_ptr', '^linked_ptr<.*>$', LinkedPtrPrinter)
116
117
118class WeakPtrPrinter(SmartPtrPrinter):
119    typename = 'base::WeakPtr'
120    def ptr(self):
121        flag = ScopedRefPtrPrinter(self.val['ref_']['flag_']).ptr()
122        if flag and flag['is_valid_']:
123            return self.val['ptr_']
124        return gdb.Value(0).cast(self.val['ptr_'].type)
125pp_set.add_printer('base::WeakPtr', '^base::WeakPtr<.*>$', WeakPtrPrinter)
126
127
128class CallbackPrinter(Printer):
129    """Callbacks provide no usable information so reduce the space they take."""
130    def to_string(self):
131        return '...'
132pp_set.add_printer('base::Callback', '^base::Callback<.*>$', CallbackPrinter)
133
134
135class LocationPrinter(Printer):
136    def to_string(self):
137        return '%s()@%s:%s' % (self.val['function_name_'].string(),
138                               self.val['file_name_'].string(),
139                               self.val['line_number_'])
140pp_set.add_printer('tracked_objects::Location', '^tracked_objects::Location$',
141                   LocationPrinter)
142
143
144class LockPrinter(Printer):
145    def to_string(self):
146        try:
147            if self.val['owned_by_thread_']:
148                return 'Locked by thread %s' % self.val['owning_thread_id_']
149            else:
150                return 'Unlocked'
151        except gdb.error:
152            return 'Unknown state'
153pp_set.add_printer('base::Lock', '^base::Lock$', LockPrinter)
154
155
156class TimeDeltaPrinter(object):
157    def __init__(self, val):
158        self._timedelta = datetime.timedelta(microseconds=int(val['delta_']))
159
160    def timedelta(self):
161        return self._timedelta
162
163    def to_string(self):
164        return str(self._timedelta)
165pp_set.add_printer('base::TimeDelta', '^base::TimeDelta$', TimeDeltaPrinter)
166
167
168class TimeTicksPrinter(TimeDeltaPrinter):
169    def __init__(self, val):
170        self._timedelta = datetime.timedelta(microseconds=int(val['ticks_']))
171pp_set.add_printer('base::TimeTicks', '^base::TimeTicks$', TimeTicksPrinter)
172
173
174class TimePrinter(object):
175    def __init__(self, val):
176        timet_offset = gdb.parse_and_eval(
177            'base::Time::kTimeTToMicrosecondsOffset')
178        self._datetime = (datetime.datetime.fromtimestamp(0) +
179                          datetime.timedelta(microseconds=
180                                             int(val['us_'] - timet_offset)))
181
182    def datetime(self):
183        return self._datetime
184
185    def to_string(self):
186        return str(self._datetime)
187pp_set.add_printer('base::Time', '^base::Time$', TimePrinter)
188
189
190class IpcMessagePrinter(Printer):
191    def header(self):
192        return self.val['header_'].cast(
193            gdb.lookup_type('IPC::Message::Header').pointer())
194
195    def to_string(self):
196        message_type = self.header()['type']
197        return '%s of kind %s line %s' % (
198            self.val.dynamic_type,
199            (message_type >> 16).cast(gdb.lookup_type('IPCMessageStart')),
200            message_type & 0xffff)
201
202    def children(self):
203        yield ('header_', self.header().dereference())
204        yield ('capacity_', self.val['capacity_'])
205        yield ('variable_buffer_offset_', self.val['variable_buffer_offset_'])
206        for field in self.val.type.fields():
207            if field.is_base_class:
208                continue
209            yield (field.name, self.val[field.name])
210pp_set.add_printer('IPC::Message', '^IPC::Message$', IpcMessagePrinter)
211
212
213class NotificationRegistrarPrinter(Printer):
214    def to_string(self):
215        try:
216            registrations = self.val['registered_']
217            vector_finish = registrations['_M_impl']['_M_finish']
218            vector_start = registrations['_M_impl']['_M_start']
219            if vector_start == vector_finish:
220                return 'Not watching notifications'
221            if vector_start.dereference().type.sizeof == 0:
222                # Incomplete type: b/8242773
223                return 'Watching some notifications'
224            return ('Watching %s notifications; '
225                    'print %s->registered_ for details') % (
226                        int(vector_finish - vector_start),
227                        typed_ptr(self.val.address))
228        except gdb.error:
229            return 'NotificationRegistrar'
230pp_set.add_printer('content::NotificationRegistrar',
231                   '^content::NotificationRegistrar$',
232                   NotificationRegistrarPrinter)
233
234
235class SiteInstanceImplPrinter(object):
236    def __init__(self, val):
237        self.val = val.cast(val.dynamic_type)
238
239    def to_string(self):
240        return 'SiteInstanceImpl@%s for %s' % (
241            self.val.address, self.val['site_'])
242
243    def children(self):
244        yield ('id_', self.val['id_'])
245        yield ('has_site_', self.val['has_site_'])
246        if self.val['browsing_instance_']['ptr_']:
247            yield ('browsing_instance_', self.val['browsing_instance_']['ptr_'])
248        if self.val['process_']:
249            yield ('process_', typed_ptr(self.val['process_']))
250        if self.val['render_process_host_factory_']:
251            yield ('render_process_host_factory_',
252                   self.val['render_process_host_factory_'])
253pp_set.add_printer('content::SiteInstanceImpl', '^content::SiteInstanceImpl$',
254                   SiteInstanceImplPrinter)
255
256
257class RenderProcessHostImplPrinter(object):
258    def __init__(self, val):
259        self.val = val.cast(val.dynamic_type)
260
261    def to_string(self):
262        pid = ''
263        try:
264            child_process_launcher_ptr = (
265                self.val['child_process_launcher_']['impl_']['data_']['ptr'])
266            if child_process_launcher_ptr:
267                context = (child_process_launcher_ptr['context_']['ptr_'])
268                if context:
269                    pid = ' PID %s' % str(context['process_']['process_'])
270        except gdb.error:
271            # The definition of the Context type may not be available.
272            # b/8242773
273            pass
274        return 'RenderProcessHostImpl@%s%s' % (self.val.address, pid)
275
276    def children(self):
277        yield ('id_', self.val['id_'])
278        yield ('render_widget_hosts_',
279               self.val['render_widget_hosts_']['data_'])
280        yield ('fast_shutdown_started_', self.val['fast_shutdown_started_'])
281        yield ('deleting_soon_', self.val['deleting_soon_'])
282        yield ('pending_views_', self.val['pending_views_'])
283        yield ('visible_widgets_', self.val['visible_widgets_'])
284        yield ('backgrounded_', self.val['backgrounded_'])
285        yield ('widget_helper_', self.val['widget_helper_'])
286        yield ('is_initialized_', self.val['is_initialized_'])
287        yield ('browser_context_', typed_ptr(self.val['browser_context_']))
288        yield ('sudden_termination_allowed_',
289               self.val['sudden_termination_allowed_'])
290        yield ('ignore_input_events_', self.val['ignore_input_events_'])
291        yield ('is_guest_', self.val['is_guest_'])
292pp_set.add_printer('content::RenderProcessHostImpl',
293                   '^content::RenderProcessHostImpl$',
294                   RenderProcessHostImplPrinter)
295
296
297gdb.printing.register_pretty_printer(gdb, pp_set, replace=_DEBUGGING)
298