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
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
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:
34 sys.path.insert(0, "/path/to/tools/gdb/")
38 from __future__
import print_function
44 def guess_string_length(ptr
):
45 """Guess length of string pointed by ptr.
47 Returns a tuple of (length, an error message).
49 # Try to guess at the length.
50 for i
in range(0, 2048):
52 if int((ptr
+ i
).dereference()) == 0:
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."""
67 length
, error_message
= guess_string_length(ptr
)
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."""
82 length
, error_message
= guess_string_length(ptr
)
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
):
94 def display_hint(self
):
98 class UCharStringPrinter(StringPrinter
):
99 "Print a UChar*; we must guess at the length"
101 return ustring_to_string(self
.val
)
104 class LCharStringPrinter(StringPrinter
):
105 "Print a LChar*; we must guess at the length"
107 return lstring_to_string(self
.val
)
110 class WTFAtomicStringPrinter(StringPrinter
):
111 "Print a WTF::AtomicString"
113 return self
.val
['m_string']
116 class WTFCStringPrinter(StringPrinter
):
117 "Print a WTF::CString"
119 # The CString holds a buffer, which is a refptr to a WTF::CStringBuffer.
120 buf_ptr
= self
.val
['m_buffer']['m_ptr']
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']
134 chars_start
= self
.val
.address
+ 1
136 return lstring_to_string(chars_start
.cast(gdb
.lookup_type('char').pointer()),
138 return ustring_to_string(chars_start
.cast(gdb
.lookup_type('UChar').pointer()),
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():
153 return WTFStringImplPrinter(self
.stringimpl_ptr().dereference()).get_length()
156 if not self
.stringimpl_ptr():
158 return self
.stringimpl_ptr().dereference()
162 class blinkKURLPrinter(StringPrinter
):
163 "Print a blink::KURL"
165 return WTFStringPrinter(self
.val
['m_string']).to_string()
168 class blinkLayoutUnitPrinter
:
169 "Print a blink::LayoutUnit"
170 def __init__(self
, val
):
174 return "%.14gpx" % (self
.val
['m_value'] / 64.0)
177 class blinkLayoutSizePrinter
:
178 "Print a blink::LayoutSize"
179 def __init__(self
, val
):
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
):
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
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())
212 self
.length
= self
.local_name_printer
.get_length()
214 def get_length(self
):
218 if self
.get_length() == 0:
221 if self
.prefix_length
> 0:
222 return (self
.prefix_printer
.to_string() + ":" +
223 self
.local_name_printer
.to_string())
225 return self
.local_name_printer
.to_string()
228 class BlinkPixelsAndPercentPrinter
:
229 "Print a blink::PixelsAndPercent value"
230 def __init__(self
, val
):
234 return "(%gpx, %g%%)" % (self
.val
['pixels'], self
.val
['percent'])
237 class BlinkLengthPrinter
:
238 """Print a blink::Length."""
239 def __init__(self
, val
):
243 ltype
= self
.val
['m_type']
244 if self
.val
['m_isFloat']:
245 val
= self
.val
['m_floatValue']
247 val
= int(self
.val
['m_intValue'])
250 if self
.val
['m_quirk']:
251 quirk
= ', quirk=true'
254 return 'Length(Auto)'
256 return 'Length(%g%%, Percent%s)' % (val
, quirk
)
258 return 'Length(%g, Fixed%s)' % (val
, quirk
)
260 return 'Length(Intrinsic)'
262 return 'Length(MinIntrinsic)'
264 return 'Length(MinContent)'
266 return 'Length(MaxContent)'
268 return 'Length(FillAvailable)'
270 return 'Length(FitContent)'
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)'
276 return 'Length(ExtendToZoom)'
278 return 'Length(DeviceWidth)'
280 return 'Length(DeviceHeight)'
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:
294 $3 = WTF::Vector of length 7, capacity 16 = {7, 17, 27, 37, 47, 57, 67}
295 (gdb) set print elements 3
297 $6 = WTF::Vector of length 7, capacity 16 = {7, 17, 27...}
298 (gdb) set print array
300 $7 = WTF::Vector of length 7, capacity 16 = {
306 (gdb) set print elements 200
308 $8 = WTF::Vector of length 7, capacity 16 = {
320 def __init__(self
, start
, finish
):
329 if self
.item
== self
.finish
:
333 element
= self
.item
.dereference()
335 return ('[%d]' % count
, element
)
337 def __init__(self
, val
):
341 start
= self
.val
['m_buffer']
342 return self
.Iterator(start
, start
+ self
.val
['m_size'])
345 return ('%s of length %d, capacity %d'
346 % ('WTF::Vector', self
.val
['m_size'], self
.val
['m_capacity']))
348 def display_hint(self
):
352 # Copied from //tools/gdb/gdb_chrome.py
354 """Prints a pointer along with its exact type.
356 By default, gdb would print just the address, which takes more
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
):
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
):
378 return 'DataRef(%s)' % (
379 WTFRefOrOwnPtrPrinter(self
.val
['m_data']).to_string())
382 def add_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."""
403 if type.code
== gdb
.TYPE_CODE_REF
:
405 type = type.unqualified().strip_typedefs()
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())
415 return UCharStringPrinter(val
)
417 return LCharStringPrinter(val
)
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"""
432 super(PrintPathToRootCommand
, self
).__init
__("printpathtoroot",
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()
441 val
= gdb
.Frame
.read_var(frame
, arg
)
443 print("No such variable, or invalid type")
446 target_type
= str(val
.type.target().strip_typedefs())
447 if target_type
== str(node_type
):
451 val
.cast(element_type
.pointer()).dereference()['m_tagName']])
452 val
= val
.dereference()['m_parent']
455 while len(stack
) > 0:
457 print(padding
, pair
[1], pair
[0])
458 padding
= padding
+ ' '
460 print('Sorry: I don\'t know how to deal with %s yet.' % target_type
)
463 PrintPathToRootCommand()