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
14Use
15  (gdb) p /r any_variable
16to print |any_variable| without using any printers.
17"""
18
19import datetime
20import gdb
21import gdb.printing
22import os
23import sys
24
25sys.path.insert(0, os.path.join(
26    os.path.dirname(os.path.abspath(__file__)),
27    '..', '..', 'third_party', 'WebKit', 'Tools', 'gdb'))
28try:
29  import webkit
30finally:
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
40pp_set = gdb.printing.RegexpCollectionPrettyPrinter("chromium")
41
42
43def 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
54def 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
73class Printer(object):
74    def __init__(self, val):
75        self.val = val
76
77
78class StringPrinter(Printer):
79    def display_hint(self):
80        return 'string'
81
82
83class String16Printer(StringPrinter):
84    def to_string(self):
85        return webkit.ustring_to_string(self.val['_M_dataplus']['_M_p'])
86pp_set.add_printer(
87    'string16',
88    '^string16|std::basic_string<(unsigned short|base::char16).*>$',
89    String16Printer);
90
91
92class GURLPrinter(StringPrinter):
93    def to_string(self):
94        return self.val['spec_']
95pp_set.add_printer('GURL', '^GURL$', GURLPrinter)
96
97
98class FilePathPrinter(StringPrinter):
99    def to_string(self):
100        return self.val['path_']['_M_dataplus']['_M_p']
101pp_set.add_printer('FilePath', '^FilePath$', FilePathPrinter)
102
103
104class SizePrinter(Printer):
105    def to_string(self):
106        return '%sx%s' % (self.val['width_'], self.val['height_'])
107pp_set.add_printer('gfx::Size', '^gfx::(Size|SizeF|SizeBase<.*>)$', SizePrinter)
108
109
110class PointPrinter(Printer):
111    def to_string(self):
112        return '%s,%s' % (self.val['x_'], self.val['y_'])
113pp_set.add_printer('gfx::Point', '^gfx::(Point|PointF|PointBase<.*>)$',
114                   PointPrinter)
115
116
117class RectPrinter(Printer):
118    def to_string(self):
119        return '%s %s' % (self.val['origin_'], self.val['size_'])
120pp_set.add_printer('gfx::Rect', '^gfx::(Rect|RectF|RectBase<.*>)$',
121                   RectPrinter)
122
123
124class SmartPtrPrinter(Printer):
125    def to_string(self):
126        return '%s%s' % (self.typename, typed_ptr(self.ptr()))
127
128
129class ScopedRefPtrPrinter(SmartPtrPrinter):
130    typename = 'scoped_refptr'
131    def ptr(self):
132        return self.val['ptr_']
133pp_set.add_printer('scoped_refptr', '^scoped_refptr<.*>$', ScopedRefPtrPrinter)
134
135
136class LinkedPtrPrinter(SmartPtrPrinter):
137    typename = 'linked_ptr'
138    def ptr(self):
139        return self.val['value_']
140pp_set.add_printer('linked_ptr', '^linked_ptr<.*>$', LinkedPtrPrinter)
141
142
143class 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)
150pp_set.add_printer('base::WeakPtr', '^base::WeakPtr<.*>$', WeakPtrPrinter)
151
152
153class CallbackPrinter(Printer):
154    """Callbacks provide no usable information so reduce the space they take."""
155    def to_string(self):
156        return '...'
157pp_set.add_printer('base::Callback', '^base::Callback<.*>$', CallbackPrinter)
158
159
160class 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_'])
165pp_set.add_printer('tracked_objects::Location', '^tracked_objects::Location$',
166                   LocationPrinter)
167
168
169class 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
177pp_set.add_printer('base::PendingTask', '^base::PendingTask$',
178                   PendingTaskPrinter)
179
180
181class 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'
190pp_set.add_printer('base::Lock', '^base::Lock$', LockPrinter)
191
192
193class 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)
202pp_set.add_printer('base::TimeDelta', '^base::TimeDelta$', TimeDeltaPrinter)
203
204
205class TimeTicksPrinter(TimeDeltaPrinter):
206    def __init__(self, val):
207        self._timedelta = datetime.timedelta(microseconds=int(val['ticks_']))
208pp_set.add_printer('base::TimeTicks', '^base::TimeTicks$', TimeTicksPrinter)
209
210
211class 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)
224pp_set.add_printer('base::Time', '^base::Time$', TimePrinter)
225
226
227class 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])
246pp_set.add_printer('IPC::Message', '^IPC::Message$', IpcMessagePrinter)
247
248
249class 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'
266pp_set.add_printer('content::NotificationRegistrar',
267                   '^content::NotificationRegistrar$',
268                   NotificationRegistrarPrinter)
269
270
271class 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        if self.val['render_process_host_factory_']:
287            yield ('render_process_host_factory_',
288                   self.val['render_process_host_factory_'])
289pp_set.add_printer('content::SiteInstanceImpl', '^content::SiteInstanceImpl$',
290                   SiteInstanceImplPrinter)
291
292
293class RenderProcessHostImplPrinter(object):
294    def __init__(self, val):
295        self.val = val.cast(val.dynamic_type)
296
297    def to_string(self):
298        pid = ''
299        try:
300            child_process_launcher_ptr = (
301                self.val['child_process_launcher_']['impl_']['data_']['ptr'])
302            if child_process_launcher_ptr:
303                context = (child_process_launcher_ptr['context_']['ptr_'])
304                if context:
305                    pid = ' PID %s' % str(context['process_']['process_'])
306        except gdb.error:
307            # The definition of the Context type may not be available.
308            # b/8242773
309            pass
310        return 'RenderProcessHostImpl@%s%s' % (self.val.address, pid)
311
312    def children(self):
313        yield ('id_', self.val['id_'])
314        yield ('listeners_',
315               self.val['listeners_']['data_'])
316        yield ('worker_ref_count_', self.val['worker_ref_count_'])
317        yield ('fast_shutdown_started_', self.val['fast_shutdown_started_'])
318        yield ('deleting_soon_', self.val['deleting_soon_'])
319        yield ('pending_views_', self.val['pending_views_'])
320        yield ('visible_widgets_', self.val['visible_widgets_'])
321        yield ('backgrounded_', self.val['backgrounded_'])
322        yield ('widget_helper_', self.val['widget_helper_'])
323        yield ('is_initialized_', self.val['is_initialized_'])
324        yield ('browser_context_', typed_ptr(self.val['browser_context_']))
325        yield ('sudden_termination_allowed_',
326               self.val['sudden_termination_allowed_'])
327        yield ('ignore_input_events_', self.val['ignore_input_events_'])
328        yield ('is_guest_', self.val['is_guest_'])
329pp_set.add_printer('content::RenderProcessHostImpl',
330                   '^content::RenderProcessHostImpl$',
331                   RenderProcessHostImplPrinter)
332
333
334gdb.printing.register_pretty_printer(gdb, pp_set, replace=_DEBUGGING)
335