webkit.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. # Copyright (C) 2010, Google Inc. All rights reserved.
  2. #
  3. # Redistribution and use in source and binary forms, with or without
  4. # modification, are permitted provided that the following conditions are
  5. # met:
  6. #
  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.
  16. #
  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.
  28. """GDB support for WebKit types.
  29. Add this to your gdb by amending your ~/.gdbinit as follows:
  30. python
  31. import sys
  32. sys.path.insert(0, "/path/to/tools/gdb/")
  33. import webkit
  34. """
  35. import gdb
  36. import re
  37. import struct
  38. def guess_string_length(ptr):
  39. """Guess length of string pointed by ptr.
  40. Returns a tuple of (length, an error message).
  41. """
  42. # Try to guess at the length.
  43. for i in xrange(0, 2048):
  44. try:
  45. if int((ptr + i).dereference()) == 0:
  46. return i, ''
  47. except RuntimeError:
  48. # We indexed into inaccessible memory; give up.
  49. return i, ' (gdb hit inaccessible memory)'
  50. return 256, ' (gdb found no trailing NUL)'
  51. def ustring_to_string(ptr, length=None):
  52. """Convert a pointer to UTF-16 data into a Python string encoded with utf-8.
  53. ptr and length are both gdb.Value objects.
  54. If length is unspecified, will guess at the length."""
  55. error_message = ''
  56. if length is None:
  57. length, error_message = guess_string_length(ptr)
  58. else:
  59. length = int(length)
  60. char_vals = [int((ptr + i).dereference()) for i in xrange(length)]
  61. string = struct.pack('H' * length, *char_vals).decode('utf-16', 'replace').encode('utf-8')
  62. return string + error_message
  63. def lstring_to_string(ptr, length=None):
  64. """Convert a pointer to LChar* data into a Python (non-Unicode) string.
  65. ptr and length are both gdb.Value objects.
  66. If length is unspecified, will guess at the length."""
  67. error_message = ''
  68. if length is None:
  69. length, error_message = guess_string_length(ptr)
  70. else:
  71. length = int(length)
  72. string = ''.join([chr((ptr + i).dereference()) for i in xrange(length)])
  73. return string + error_message
  74. class StringPrinter(object):
  75. "Shared code between different string-printing classes"
  76. def __init__(self, val):
  77. self.val = val
  78. def display_hint(self):
  79. return 'string'
  80. class UCharStringPrinter(StringPrinter):
  81. "Print a UChar*; we must guess at the length"
  82. def to_string(self):
  83. return ustring_to_string(self.val)
  84. class LCharStringPrinter(StringPrinter):
  85. "Print a LChar*; we must guess at the length"
  86. def to_string(self):
  87. return lstring_to_string(self.val)
  88. class WTFAtomicStringPrinter(StringPrinter):
  89. "Print a WTF::AtomicString"
  90. def to_string(self):
  91. return self.val['m_string']
  92. class WTFCStringPrinter(StringPrinter):
  93. "Print a WTF::CString"
  94. def to_string(self):
  95. # The CString holds a buffer, which is a refptr to a WTF::CStringBuffer.
  96. data = self.val['m_buffer']['m_ptr']['m_data'].cast(gdb.lookup_type('char').pointer())
  97. length = self.val['m_buffer']['m_ptr']['m_length']
  98. return ''.join([chr((data + i).dereference()) for i in xrange(length)])
  99. class WTFStringImplPrinter(StringPrinter):
  100. "Print a WTF::StringImpl"
  101. def get_length(self):
  102. return self.val['m_length']
  103. def to_string(self):
  104. if self.is_8bit():
  105. return lstring_to_string(self.val['m_data8'], self.get_length())
  106. return ustring_to_string(self.val['m_data16'], self.get_length())
  107. def is_8bit(self):
  108. return self.val['m_hashAndFlags'] & self.val['s_hashFlag8BitBuffer']
  109. class WTFStringPrinter(StringPrinter):
  110. "Print a WTF::String"
  111. def stringimpl_ptr(self):
  112. return self.val['m_impl']['m_ptr']
  113. def get_length(self):
  114. if not self.stringimpl_ptr():
  115. return 0
  116. return WTFStringImplPrinter(self.stringimpl_ptr().dereference()).get_length()
  117. def to_string(self):
  118. if not self.stringimpl_ptr():
  119. return '(null)'
  120. return self.stringimpl_ptr().dereference()
  121. class JSCIdentifierPrinter(StringPrinter):
  122. "Print a JSC::Identifier"
  123. def to_string(self):
  124. return WTFStringPrinter(self.val['m_string']).to_string()
  125. class JSCJSStringPrinter(StringPrinter):
  126. "Print a JSC::JSString"
  127. def to_string(self):
  128. if self.val['m_length'] == 0:
  129. return ''
  130. return WTFStringImplPrinter(self.val['m_value']).to_string()
  131. class WebCoreKURLGooglePrivatePrinter(StringPrinter):
  132. "Print a WebCore::KURLGooglePrivate"
  133. def to_string(self):
  134. return WTFCStringPrinter(self.val['m_utf8']).to_string()
  135. class WebCoreQualifiedNamePrinter(StringPrinter):
  136. "Print a WebCore::QualifiedName"
  137. def __init__(self, val):
  138. super(WebCoreQualifiedNamePrinter, self).__init__(val)
  139. self.prefix_length = 0
  140. self.length = 0
  141. if self.val['m_impl']:
  142. self.prefix_printer = WTFStringPrinter(
  143. self.val['m_impl']['m_prefix']['m_string'])
  144. self.local_name_printer = WTFStringPrinter(
  145. self.val['m_impl']['m_localName']['m_string'])
  146. self.prefix_length = self.prefix_printer.get_length()
  147. if self.prefix_length > 0:
  148. self.length = (self.prefix_length + 1 +
  149. self.local_name_printer.get_length())
  150. else:
  151. self.length = self.local_name_printer.get_length()
  152. def get_length(self):
  153. return self.length
  154. def to_string(self):
  155. if self.get_length() == 0:
  156. return "(null)"
  157. else:
  158. if self.prefix_length > 0:
  159. return (self.prefix_printer.to_string() + ":" +
  160. self.local_name_printer.to_string())
  161. else:
  162. return self.local_name_printer.to_string()
  163. class WTFVectorPrinter:
  164. """Pretty Printer for a WTF::Vector.
  165. The output of this pretty printer is similar to the output of std::vector's
  166. pretty printer, which is bundled in gcc.
  167. Example gdb session should look like:
  168. (gdb) p v
  169. $3 = WTF::Vector of length 7, capacity 16 = {7, 17, 27, 37, 47, 57, 67}
  170. (gdb) set print elements 3
  171. (gdb) p v
  172. $6 = WTF::Vector of length 7, capacity 16 = {7, 17, 27...}
  173. (gdb) set print array
  174. (gdb) p v
  175. $7 = WTF::Vector of length 7, capacity 16 = {
  176. 7,
  177. 17,
  178. 27
  179. ...
  180. }
  181. (gdb) set print elements 200
  182. (gdb) p v
  183. $8 = WTF::Vector of length 7, capacity 16 = {
  184. 7,
  185. 17,
  186. 27,
  187. 37,
  188. 47,
  189. 57,
  190. 67
  191. }
  192. """
  193. class Iterator:
  194. def __init__(self, start, finish):
  195. self.item = start
  196. self.finish = finish
  197. self.count = 0
  198. def __iter__(self):
  199. return self
  200. def next(self):
  201. if self.item == self.finish:
  202. raise StopIteration
  203. count = self.count
  204. self.count += 1
  205. element = self.item.dereference()
  206. self.item += 1
  207. return ('[%d]' % count, element)
  208. def __init__(self, val):
  209. self.val = val
  210. def children(self):
  211. start = self.val['m_buffer']
  212. return self.Iterator(start, start + self.val['m_size'])
  213. def to_string(self):
  214. return ('%s of length %d, capacity %d'
  215. % ('WTF::Vector', self.val['m_size'], self.val['m_capacity']))
  216. def display_hint(self):
  217. return 'array'
  218. def add_pretty_printers():
  219. pretty_printers = (
  220. (re.compile("^WTF::Vector<.*>$"), WTFVectorPrinter),
  221. (re.compile("^WTF::AtomicString$"), WTFAtomicStringPrinter),
  222. (re.compile("^WTF::CString$"), WTFCStringPrinter),
  223. (re.compile("^WTF::String$"), WTFStringPrinter),
  224. (re.compile("^WTF::StringImpl$"), WTFStringImplPrinter),
  225. (re.compile("^WebCore::KURLGooglePrivate$"), WebCoreKURLGooglePrivatePrinter),
  226. (re.compile("^WebCore::QualifiedName$"), WebCoreQualifiedNamePrinter),
  227. (re.compile("^JSC::Identifier$"), JSCIdentifierPrinter),
  228. (re.compile("^JSC::JSString$"), JSCJSStringPrinter),
  229. )
  230. def lookup_function(val):
  231. """Function used to load pretty printers; will be passed to GDB."""
  232. type = val.type
  233. if type.code == gdb.TYPE_CODE_REF:
  234. type = type.target()
  235. type = type.unqualified().strip_typedefs()
  236. tag = type.tag
  237. if tag:
  238. for function, pretty_printer in pretty_printers:
  239. if function.search(tag):
  240. return pretty_printer(val)
  241. if type.code == gdb.TYPE_CODE_PTR:
  242. name = str(type.target().unqualified())
  243. if name == 'UChar':
  244. return UCharStringPrinter(val)
  245. if name == 'LChar':
  246. return LCharStringPrinter(val)
  247. return None
  248. gdb.pretty_printers.append(lookup_function)
  249. add_pretty_printers()
  250. class PrintPathToRootCommand(gdb.Command):
  251. """Command for printing WebKit Node trees.
  252. Usage: printpathtoroot variable_name"""
  253. def __init__(self):
  254. super(PrintPathToRootCommand, self).__init__("printpathtoroot",
  255. gdb.COMMAND_SUPPORT,
  256. gdb.COMPLETE_NONE)
  257. def invoke(self, arg, from_tty):
  258. element_type = gdb.lookup_type('WebCore::Element')
  259. node_type = gdb.lookup_type('WebCore::Node')
  260. frame = gdb.selected_frame()
  261. try:
  262. val = gdb.Frame.read_var(frame, arg)
  263. except:
  264. print "No such variable, or invalid type"
  265. return
  266. target_type = str(val.type.target().strip_typedefs())
  267. if target_type == str(node_type):
  268. stack = []
  269. while val:
  270. stack.append([val,
  271. val.cast(element_type.pointer()).dereference()['m_tagName']])
  272. val = val.dereference()['m_parent']
  273. padding = ''
  274. while len(stack) > 0:
  275. pair = stack.pop()
  276. print padding, pair[1], pair[0]
  277. padding = padding + ' '
  278. else:
  279. print 'Sorry: I don\'t know how to deal with %s yet.' % target_type
  280. PrintPathToRootCommand()