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
48def yield_fields(val):
49    """Use this in a printer's children() method to print an object's fields.
50
51    e.g.
52      def children():
53        for result in yield_fields(self.val):
54          yield result
55    """
56    try:
57        fields = val.type.target().fields()
58    except:
59        fields = val.type.fields()
60    for field in fields:
61        if field.is_base_class:
62            yield (field.name, val.cast(gdb.lookup_type(field.name)))
63        else:
64            yield (field.name, val[field.name])
65
66
67class Printer(object):
68    def __init__(self, val):
69        self.val = val
70
71
72class StringPrinter(Printer):
73    def display_hint(self):
74        return 'string'
75
76
77class String16Printer(StringPrinter):
78    def to_string(self):
79        return webkit.ustring_to_string(self.val['_M_dataplus']['_M_p'])
80pp_set.add_printer(
81    'string16',
82    '^string16|std::basic_string<(unsigned short|base::char16).*>$',
83    String16Printer);
84
85
86class GURLPrinter(StringPrinter):
87    def to_string(self):
88        return self.val['spec_']
89pp_set.add_printer('GURL', '^GURL$', GURLPrinter)
90
91
92class FilePathPrinter(StringPrinter):
93    def to_string(self):
94        return self.val['path_']['_M_dataplus']['_M_p']
95pp_set.add_printer('FilePath', '^FilePath$', FilePathPrinter)
96
97
98class SizePrinter(Printer):
99    def to_string(self):
100        return '%sx%s' % (self.val['width_'], self.val['height_'])
101pp_set.add_printer('gfx::Size', '^gfx::(Size|SizeF|SizeBase<.*>)$', SizePrinter)
102
103
104class PointPrinter(Printer):
105    def to_string(self):
106        return '%s,%s' % (self.val['x_'], self.val['y_'])
107pp_set.add_printer('gfx::Point', '^gfx::(Point|PointF|PointBase<.*>)$',
108                   PointPrinter)
109
110
111class RectPrinter(Printer):
112    def to_string(self):
113        return '%s %s' % (self.val['origin_'], self.val['size_'])
114pp_set.add_printer('gfx::Rect', '^gfx::(Rect|RectF|RectBase<.*>)$',
115                   RectPrinter)
116
117
118class SmartPtrPrinter(Printer):
119    def to_string(self):
120        return '%s%s' % (self.typename, typed_ptr(self.ptr()))
121
122
123class ScopedRefPtrPrinter(SmartPtrPrinter):
124    typename = 'scoped_refptr'
125    def ptr(self):
126        return self.val['ptr_']
127pp_set.add_printer('scoped_refptr', '^scoped_refptr<.*>$', ScopedRefPtrPrinter)
128
129
130class LinkedPtrPrinter(SmartPtrPrinter):
131    typename = 'linked_ptr'
132    def ptr(self):
133        return self.val['value_']
134pp_set.add_printer('linked_ptr', '^linked_ptr<.*>$', LinkedPtrPrinter)
135
136
137class WeakPtrPrinter(SmartPtrPrinter):
138    typename = 'base::WeakPtr'
139    def ptr(self):
140        flag = ScopedRefPtrPrinter(self.val['ref_']['flag_']).ptr()
141        if flag and flag['is_valid_']:
142            return self.val['ptr_']
143        return gdb.Value(0).cast(self.val['ptr_'].type)
144pp_set.add_printer('base::WeakPtr', '^base::WeakPtr<.*>$', WeakPtrPrinter)
145
146
147class CallbackPrinter(Printer):
148    """Callbacks provide no usable information so reduce the space they take."""
149    def to_string(self):
150        return '...'
151pp_set.add_printer('base::Callback', '^base::Callback<.*>$', CallbackPrinter)
152
153
154class LocationPrinter(Printer):
155    def to_string(self):
156        return '%s()@%s:%s' % (self.val['function_name_'].string(),
157                               self.val['file_name_'].string(),
158                               self.val['line_number_'])
159pp_set.add_printer('tracked_objects::Location', '^tracked_objects::Location$',
160                   LocationPrinter)
161
162
163class PendingTaskPrinter(Printer):
164    def to_string(self):
165        return 'From %s' % (self.val['posted_from'],)
166
167    def children(self):
168        for result in yield_fields(self.val):
169            if result[0] not in ('task', 'posted_from'):
170                yield result
171pp_set.add_printer('base::PendingTask', '^base::PendingTask$',
172                   PendingTaskPrinter)
173
174
175class LockPrinter(Printer):
176    def to_string(self):
177        try:
178            if self.val['owned_by_thread_']:
179                return 'Locked by thread %s' % self.val['owning_thread_id_']
180            else:
181                return 'Unlocked'
182        except gdb.error:
183            return 'Unknown state'
184pp_set.add_printer('base::Lock', '^base::Lock$', LockPrinter)
185
186
187class TimeDeltaPrinter(object):
188    def __init__(self, val):
189        self._timedelta = datetime.timedelta(microseconds=int(val['delta_']))
190
191    def timedelta(self):
192        return self._timedelta
193
194    def to_string(self):
195        return str(self._timedelta)
196pp_set.add_printer('base::TimeDelta', '^base::TimeDelta$', TimeDeltaPrinter)
197
198
199class TimeTicksPrinter(TimeDeltaPrinter):
200    def __init__(self, val):
201        self._timedelta = datetime.timedelta(microseconds=int(val['ticks_']))
202pp_set.add_printer('base::TimeTicks', '^base::TimeTicks$', TimeTicksPrinter)
203
204
205class TimePrinter(object):
206    def __init__(self, val):
207        timet_offset = gdb.parse_and_eval(
208            'base::Time::kTimeTToMicrosecondsOffset')
209        self._datetime = (datetime.datetime.fromtimestamp(0) +
210                          datetime.timedelta(microseconds=
211                                             int(val['us_'] - timet_offset)))
212
213    def datetime(self):
214        return self._datetime
215
216    def to_string(self):
217        return str(self._datetime)
218pp_set.add_printer('base::Time', '^base::Time$', TimePrinter)
219
220
221class IpcMessagePrinter(Printer):
222    def header(self):
223        return self.val['header_'].cast(
224            gdb.lookup_type('IPC::Message::Header').pointer())
225
226    def to_string(self):
227        message_type = self.header()['type']
228        return '%s of kind %s line %s' % (
229            self.val.dynamic_type,
230            (message_type >> 16).cast(gdb.lookup_type('IPCMessageStart')),
231            message_type & 0xffff)
232
233    def children(self):
234        yield ('header_', self.header().dereference())
235        yield ('capacity_after_header_', self.val['capacity_after_header_'])
236        for field in self.val.type.fields():
237            if field.is_base_class:
238                continue
239            yield (field.name, self.val[field.name])
240pp_set.add_printer('IPC::Message', '^IPC::Message$', IpcMessagePrinter)
241
242
243class NotificationRegistrarPrinter(Printer):
244    def to_string(self):
245        try:
246            registrations = self.val['registered_']
247            vector_finish = registrations['_M_impl']['_M_finish']
248            vector_start = registrations['_M_impl']['_M_start']
249            if vector_start == vector_finish:
250                return 'Not watching notifications'
251            if vector_start.dereference().type.sizeof == 0:
252                # Incomplete type: b/8242773
253                return 'Watching some notifications'
254            return ('Watching %s notifications; '
255                    'print %s->registered_ for details') % (
256                        int(vector_finish - vector_start),
257                        typed_ptr(self.val.address))
258        except gdb.error:
259            return 'NotificationRegistrar'
260pp_set.add_printer('content::NotificationRegistrar',
261                   '^content::NotificationRegistrar$',
262                   NotificationRegistrarPrinter)
263
264
265class SiteInstanceImplPrinter(object):
266    def __init__(self, val):
267        self.val = val.cast(val.dynamic_type)
268
269    def to_string(self):
270        return 'SiteInstanceImpl@%s for %s' % (
271            self.val.address, self.val['site_'])
272
273    def children(self):
274        yield ('id_', self.val['id_'])
275        yield ('has_site_', self.val['has_site_'])
276        if self.val['browsing_instance_']['ptr_']:
277            yield ('browsing_instance_', self.val['browsing_instance_']['ptr_'])
278        if self.val['process_']:
279            yield ('process_', typed_ptr(self.val['process_']))
280        if self.val['render_process_host_factory_']:
281            yield ('render_process_host_factory_',
282                   self.val['render_process_host_factory_'])
283pp_set.add_printer('content::SiteInstanceImpl', '^content::SiteInstanceImpl$',
284                   SiteInstanceImplPrinter)
285
286
287class RenderProcessHostImplPrinter(object):
288    def __init__(self, val):
289        self.val = val.cast(val.dynamic_type)
290
291    def to_string(self):
292        pid = ''
293        try:
294            child_process_launcher_ptr = (
295                self.val['child_process_launcher_']['impl_']['data_']['ptr'])
296            if child_process_launcher_ptr:
297                context = (child_process_launcher_ptr['context_']['ptr_'])
298                if context:
299                    pid = ' PID %s' % str(context['process_']['process_'])
300        except gdb.error:
301            # The definition of the Context type may not be available.
302            # b/8242773
303            pass
304        return 'RenderProcessHostImpl@%s%s' % (self.val.address, pid)
305
306    def children(self):
307        yield ('id_', self.val['id_'])
308        yield ('listeners_',
309               self.val['listeners_']['data_'])
310        yield ('worker_ref_count_', self.val['worker_ref_count_'])
311        yield ('fast_shutdown_started_', self.val['fast_shutdown_started_'])
312        yield ('deleting_soon_', self.val['deleting_soon_'])
313        yield ('pending_views_', self.val['pending_views_'])
314        yield ('visible_widgets_', self.val['visible_widgets_'])
315        yield ('backgrounded_', self.val['backgrounded_'])
316        yield ('widget_helper_', self.val['widget_helper_'])
317        yield ('is_initialized_', self.val['is_initialized_'])
318        yield ('browser_context_', typed_ptr(self.val['browser_context_']))
319        yield ('sudden_termination_allowed_',
320               self.val['sudden_termination_allowed_'])
321        yield ('ignore_input_events_', self.val['ignore_input_events_'])
322        yield ('is_guest_', self.val['is_guest_'])
323pp_set.add_printer('content::RenderProcessHostImpl',
324                   '^content::RenderProcessHostImpl$',
325                   RenderProcessHostImplPrinter)
326
327
328gdb.printing.register_pretty_printer(gdb, pp_set, replace=_DEBUGGING)
329