Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Tools / gdb / webkit.py
blob3aa50535a6bee27c5306513eab310bde19d99d33
1 # Copyright (C) 2010, Google Inc. All rights reserved.
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are
5 # met:
7 # * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer.
9 # * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following disclaimer
11 # in the documentation and/or other materials provided with the
12 # distribution.
13 # * Neither the name of Google Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 """GDB support for WebKit types.
31 Add this to your gdb by amending your ~/.gdbinit as follows:
32 python
33 import sys
34 sys.path.insert(0, "/path/to/tools/gdb/")
35 import webkit
36 """
38 from __future__ import print_function
40 import gdb
41 import re
42 import struct
44 def guess_string_length(ptr):
45 """Guess length of string pointed by ptr.
47 Returns a tuple of (length, an error message).
48 """
49 # Try to guess at the length.
50 for i in range(0, 2048):
51 try:
52 if int((ptr + i).dereference()) == 0:
53 return i, ''
54 except RuntimeError:
55 # We indexed into inaccessible memory; give up.
56 return i, ' (gdb hit inaccessible memory)'
57 return 256, ' (gdb found no trailing NUL)'
60 def ustring_to_string(ptr, length=None):
61 """Convert a pointer to UTF-16 data into a Python string encoded with utf-8.
63 ptr and length are both gdb.Value objects.
64 If length is unspecified, will guess at the length."""
65 error_message = ''
66 if length is None:
67 length, error_message = guess_string_length(ptr)
68 else:
69 length = int(length)
70 char_vals = [int((ptr + i).dereference()) for i in range(length)]
71 string = struct.pack('H' * length, *char_vals).decode('utf-16', 'replace').encode('utf-8')
72 return string + error_message
75 def lstring_to_string(ptr, length=None):
76 """Convert a pointer to LChar* data into a Python (non-Unicode) string.
78 ptr and length are both gdb.Value objects.
79 If length is unspecified, will guess at the length."""
80 error_message = ''
81 if length is None:
82 length, error_message = guess_string_length(ptr)
83 else:
84 length = int(length)
85 string = ''.join([chr((ptr + i).dereference()) for i in range(length)])
86 return string + error_message
89 class StringPrinter(object):
90 "Shared code between different string-printing classes"
91 def __init__(self, val):
92 self.val = val
94 def display_hint(self):
95 return 'string'
98 class UCharStringPrinter(StringPrinter):
99 "Print a UChar*; we must guess at the length"
100 def to_string(self):
101 return ustring_to_string(self.val)
104 class LCharStringPrinter(StringPrinter):
105 "Print a LChar*; we must guess at the length"
106 def to_string(self):
107 return lstring_to_string(self.val)
110 class WTFAtomicStringPrinter(StringPrinter):
111 "Print a WTF::AtomicString"
112 def to_string(self):
113 return self.val['m_string']
116 class WTFCStringPrinter(StringPrinter):
117 "Print a WTF::CString"
118 def to_string(self):
119 # The CString holds a buffer, which is a refptr to a WTF::CStringBuffer.
120 buf_ptr = self.val['m_buffer']['m_ptr']
121 if not buf_ptr:
122 return 0
123 data = (buf_ptr + 1).cast(gdb.lookup_type('char').pointer())
124 length = self.val['m_buffer']['m_ptr']['m_length']
125 return ''.join([chr((data + i).dereference()) for i in range(length)])
128 class WTFStringImplPrinter(StringPrinter):
129 "Print a WTF::StringImpl"
130 def get_length(self):
131 return self.val['m_length']
133 def to_string(self):
134 chars_start = self.val.address + 1
135 if self.is_8bit():
136 return lstring_to_string(chars_start.cast(gdb.lookup_type('char').pointer()),
137 self.get_length())
138 return ustring_to_string(chars_start.cast(gdb.lookup_type('UChar').pointer()),
139 self.get_length())
141 def is_8bit(self):
142 return self.val['m_is8Bit']
145 class WTFStringPrinter(StringPrinter):
146 "Print a WTF::String"
147 def stringimpl_ptr(self):
148 return self.val['m_impl']['m_ptr']
150 def get_length(self):
151 if not self.stringimpl_ptr():
152 return 0
153 return WTFStringImplPrinter(self.stringimpl_ptr().dereference()).get_length()
155 def to_string(self):
156 if not self.stringimpl_ptr():
157 return '(null)'
158 return self.stringimpl_ptr().dereference()
162 class blinkKURLPrinter(StringPrinter):
163 "Print a blink::KURL"
164 def to_string(self):
165 return WTFStringPrinter(self.val['m_string']).to_string()
168 class blinkLayoutUnitPrinter:
169 "Print a blink::LayoutUnit"
170 def __init__(self, val):
171 self.val = val
173 def to_string(self):
174 return "%.14gpx" % (self.val['m_value'] / 64.0)
177 class blinkLayoutSizePrinter:
178 "Print a blink::LayoutSize"
179 def __init__(self, val):
180 self.val = val
182 def to_string(self):
183 return 'LayoutSize(%s, %s)' % (blinkLayoutUnitPrinter(self.val['m_width']).to_string(), blinkLayoutUnitPrinter(self.val['m_height']).to_string())
186 class blinkLayoutPointPrinter:
187 "Print a blink::LayoutPoint"
188 def __init__(self, val):
189 self.val = val
191 def to_string(self):
192 return 'LayoutPoint(%s, %s)' % (blinkLayoutUnitPrinter(self.val['m_x']).to_string(), blinkLayoutUnitPrinter(self.val['m_y']).to_string())
195 class blinkQualifiedNamePrinter(StringPrinter):
196 "Print a blink::QualifiedName"
198 def __init__(self, val):
199 super(blinkQualifiedNamePrinter, self).__init__(val)
200 self.prefix_length = 0
201 self.length = 0
202 if self.val['m_impl']:
203 self.prefix_printer = WTFStringPrinter(
204 self.val['m_impl']['m_ptr']['m_prefix']['m_string'])
205 self.local_name_printer = WTFStringPrinter(
206 self.val['m_impl']['m_ptr']['m_localName']['m_string'])
207 self.prefix_length = self.prefix_printer.get_length()
208 if self.prefix_length > 0:
209 self.length = (self.prefix_length + 1 +
210 self.local_name_printer.get_length())
211 else:
212 self.length = self.local_name_printer.get_length()
214 def get_length(self):
215 return self.length
217 def to_string(self):
218 if self.get_length() == 0:
219 return "(null)"
220 else:
221 if self.prefix_length > 0:
222 return (self.prefix_printer.to_string() + ":" +
223 self.local_name_printer.to_string())
224 else:
225 return self.local_name_printer.to_string()
228 class BlinkPixelsAndPercentPrinter:
229 "Print a blink::PixelsAndPercent value"
230 def __init__(self, val):
231 self.val = val
233 def to_string(self):
234 return "(%gpx, %g%%)" % (self.val['pixels'], self.val['percent'])
237 class BlinkLengthPrinter:
238 """Print a blink::Length."""
239 def __init__(self, val):
240 self.val = val
242 def to_string(self):
243 ltype = self.val['m_type']
244 if self.val['m_isFloat']:
245 val = self.val['m_floatValue']
246 else:
247 val = int(self.val['m_intValue'])
249 quirk = ''
250 if self.val['m_quirk']:
251 quirk = ', quirk=true'
253 if ltype == 0:
254 return 'Length(Auto)'
255 if ltype == 1:
256 return 'Length(%g%%, Percent%s)' % (val, quirk)
257 if ltype == 2:
258 return 'Length(%g, Fixed%s)' % (val, quirk)
259 if ltype == 3:
260 return 'Length(Intrinsic)'
261 if ltype == 4:
262 return 'Length(MinIntrinsic)'
263 if ltype == 5:
264 return 'Length(MinContent)'
265 if ltype == 6:
266 return 'Length(MaxContent)'
267 if ltype == 7:
268 return 'Length(FillAvailable)'
269 if ltype == 8:
270 return 'Length(FitContent)'
271 if ltype == 9:
272 # Would like to print pixelsAndPercent() but can't call member
273 # functions - https://sourceware.org/bugzilla/show_bug.cgi?id=13326
274 return 'Length(Calculated)'
275 if ltype == 10:
276 return 'Length(ExtendToZoom)'
277 if ltype == 11:
278 return 'Length(DeviceWidth)'
279 if ltype == 12:
280 return 'Length(DeviceHeight)'
281 if ltype == 13:
282 return 'Length(MaxSizeNone)'
283 return 'Length(unknown type %i)' % ltype
286 class WTFVectorPrinter:
287 """Pretty Printer for a WTF::Vector.
289 The output of this pretty printer is similar to the output of std::vector's
290 pretty printer, which is bundled in gcc.
292 Example gdb session should look like:
293 (gdb) p v
294 $3 = WTF::Vector of length 7, capacity 16 = {7, 17, 27, 37, 47, 57, 67}
295 (gdb) set print elements 3
296 (gdb) p v
297 $6 = WTF::Vector of length 7, capacity 16 = {7, 17, 27...}
298 (gdb) set print array
299 (gdb) p v
300 $7 = WTF::Vector of length 7, capacity 16 = {
306 (gdb) set print elements 200
307 (gdb) p v
308 $8 = WTF::Vector of length 7, capacity 16 = {
319 class Iterator:
320 def __init__(self, start, finish):
321 self.item = start
322 self.finish = finish
323 self.count = 0
325 def __iter__(self):
326 return self
328 def next(self):
329 if self.item == self.finish:
330 raise StopIteration
331 count = self.count
332 self.count += 1
333 element = self.item.dereference()
334 self.item += 1
335 return ('[%d]' % count, element)
337 def __init__(self, val):
338 self.val = val
340 def children(self):
341 start = self.val['m_buffer']
342 return self.Iterator(start, start + self.val['m_size'])
344 def to_string(self):
345 return ('%s of length %d, capacity %d'
346 % ('WTF::Vector', self.val['m_size'], self.val['m_capacity']))
348 def display_hint(self):
349 return 'array'
352 # Copied from //tools/gdb/gdb_chrome.py
353 def typed_ptr(ptr):
354 """Prints a pointer along with its exact type.
356 By default, gdb would print just the address, which takes more
357 steps to interpret.
359 # Returning this as a cast expression surrounded by parentheses
360 # makes it easier to cut+paste inside of gdb.
361 return '((%s)%s)' % (ptr.dynamic_type, ptr)
364 class WTFRefOrOwnPtrPrinter:
365 def __init__(self, val):
366 self.val = val
368 def to_string(self):
369 type_without_param = re.sub(r'<.*>', '', self.val.type.name)
370 return '%s%s' % (type_without_param, typed_ptr(self.val['m_ptr']))
373 class BlinkDataRefPrinter:
374 def __init__(self, val):
375 self.val = val
377 def to_string(self):
378 return 'DataRef(%s)' % (
379 WTFRefOrOwnPtrPrinter(self.val['m_data']).to_string())
382 def add_pretty_printers():
383 pretty_printers = (
384 (re.compile("^WTF::Vector<.*>$"), WTFVectorPrinter),
385 (re.compile("^WTF::AtomicString$"), WTFAtomicStringPrinter),
386 (re.compile("^WTF::CString$"), WTFCStringPrinter),
387 (re.compile("^WTF::String$"), WTFStringPrinter),
388 (re.compile("^WTF::StringImpl$"), WTFStringImplPrinter),
389 (re.compile("^blink::KURL$"), blinkKURLPrinter),
390 (re.compile("^blink::LayoutUnit$"), blinkLayoutUnitPrinter),
391 (re.compile("^blink::LayoutPoint$"), blinkLayoutPointPrinter),
392 (re.compile("^blink::LayoutSize$"), blinkLayoutSizePrinter),
393 (re.compile("^blink::QualifiedName$"), blinkQualifiedNamePrinter),
394 (re.compile("^blink::PixelsAndPercent$"), BlinkPixelsAndPercentPrinter),
395 (re.compile("^blink::Length$"), BlinkLengthPrinter),
396 (re.compile("^WTF::(Ref|Own)Ptr<.*>$"), WTFRefOrOwnPtrPrinter),
397 (re.compile("^blink::DataRef<.*>$"), BlinkDataRefPrinter),
400 def lookup_function(val):
401 """Function used to load pretty printers; will be passed to GDB."""
402 type = val.type
403 if type.code == gdb.TYPE_CODE_REF:
404 type = type.target()
405 type = type.unqualified().strip_typedefs()
406 tag = type.tag
407 if tag:
408 for function, pretty_printer in pretty_printers:
409 if function.search(tag):
410 return pretty_printer(val)
412 if type.code == gdb.TYPE_CODE_PTR:
413 name = str(type.target().unqualified())
414 if name == 'UChar':
415 return UCharStringPrinter(val)
416 if name == 'LChar':
417 return LCharStringPrinter(val)
418 return None
420 gdb.pretty_printers.append(lookup_function)
423 add_pretty_printers()
426 class PrintPathToRootCommand(gdb.Command):
427 """Command for printing WebKit Node trees.
429 Usage: printpathtoroot variable_name"""
431 def __init__(self):
432 super(PrintPathToRootCommand, self).__init__("printpathtoroot",
433 gdb.COMMAND_SUPPORT,
434 gdb.COMPLETE_NONE)
436 def invoke(self, arg, from_tty):
437 element_type = gdb.lookup_type('blink::Element')
438 node_type = gdb.lookup_type('blink::Node')
439 frame = gdb.selected_frame()
440 try:
441 val = gdb.Frame.read_var(frame, arg)
442 except:
443 print("No such variable, or invalid type")
444 return
446 target_type = str(val.type.target().strip_typedefs())
447 if target_type == str(node_type):
448 stack = []
449 while val:
450 stack.append([val,
451 val.cast(element_type.pointer()).dereference()['m_tagName']])
452 val = val.dereference()['m_parent']
454 padding = ''
455 while len(stack) > 0:
456 pair = stack.pop()
457 print(padding, pair[1], pair[0])
458 padding = padding + ' '
459 else:
460 print('Sorry: I don\'t know how to deal with %s yet.' % target_type)
463 PrintPathToRootCommand()