gdb-v8-support.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. # Copyright 2011 the V8 project authors. All rights reserved.
  2. # Redistribution and use in source and binary forms, with or without
  3. # modification, are permitted provided that the following conditions are
  4. # met:
  5. #
  6. # * Redistributions of source code must retain the above copyright
  7. # notice, this list of conditions and the following disclaimer.
  8. # * Redistributions in binary form must reproduce the above
  9. # copyright notice, this list of conditions and the following
  10. # disclaimer in the documentation and/or other materials provided
  11. # with the distribution.
  12. # * Neither the name of Google Inc. nor the names of its
  13. # contributors may be used to endorse or promote products derived
  14. # from this software without specific prior written permission.
  15. #
  16. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  17. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  18. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  19. # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  20. # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  21. # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  22. # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23. # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24. # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  26. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. # for py2/py3 compatibility
  28. from __future__ import print_function
  29. import re
  30. import tempfile
  31. import os
  32. import subprocess
  33. import time
  34. import gdb
  35. kSmiTag = 0
  36. kSmiTagSize = 1
  37. kSmiTagMask = (1 << kSmiTagSize) - 1
  38. kHeapObjectTag = 1
  39. kHeapObjectTagSize = 2
  40. kHeapObjectTagMask = (1 << kHeapObjectTagSize) - 1
  41. kFailureTag = 3
  42. kFailureTagSize = 2
  43. kFailureTagMask = (1 << kFailureTagSize) - 1
  44. kSmiShiftSize32 = 0
  45. kSmiValueSize32 = 31
  46. kSmiShiftBits32 = kSmiTagSize + kSmiShiftSize32
  47. kSmiShiftSize64 = 31
  48. kSmiValueSize64 = 32
  49. kSmiShiftBits64 = kSmiTagSize + kSmiShiftSize64
  50. kAllBits = 0xFFFFFFFF
  51. kTopBit32 = 0x80000000
  52. kTopBit64 = 0x8000000000000000
  53. t_u32 = gdb.lookup_type('unsigned int')
  54. t_u64 = gdb.lookup_type('unsigned long long')
  55. def has_smi_tag(v):
  56. return v & kSmiTagMask == kSmiTag
  57. def has_failure_tag(v):
  58. return v & kFailureTagMask == kFailureTag
  59. def has_heap_object_tag(v):
  60. return v & kHeapObjectTagMask == kHeapObjectTag
  61. def raw_heap_object(v):
  62. return v - kHeapObjectTag
  63. def smi_to_int_32(v):
  64. v = v & kAllBits
  65. if (v & kTopBit32) == kTopBit32:
  66. return ((v & kAllBits) >> kSmiShiftBits32) - 2147483648
  67. else:
  68. return (v & kAllBits) >> kSmiShiftBits32
  69. def smi_to_int_64(v):
  70. return (v >> kSmiShiftBits64)
  71. def decode_v8_value(v, bitness):
  72. base_str = 'v8[%x]' % v
  73. if has_smi_tag(v):
  74. if bitness == 32:
  75. return base_str + (" SMI(%d)" % smi_to_int_32(v))
  76. else:
  77. return base_str + (" SMI(%d)" % smi_to_int_64(v))
  78. elif has_failure_tag(v):
  79. return base_str + " (failure)"
  80. elif has_heap_object_tag(v):
  81. return base_str + (" H(0x%x)" % raw_heap_object(v))
  82. else:
  83. return base_str
  84. class V8ValuePrinter(object):
  85. "Print a v8value."
  86. def __init__(self, val):
  87. self.val = val
  88. def to_string(self):
  89. if self.val.type.sizeof == 4:
  90. v_u32 = self.val.cast(t_u32)
  91. return decode_v8_value(int(v_u32), 32)
  92. elif self.val.type.sizeof == 8:
  93. v_u64 = self.val.cast(t_u64)
  94. return decode_v8_value(int(v_u64), 64)
  95. else:
  96. return 'v8value?'
  97. def display_hint(self):
  98. return 'v8value'
  99. def v8_pretty_printers(val):
  100. lookup_tag = val.type.tag
  101. if lookup_tag == None:
  102. return None
  103. elif lookup_tag == 'v8value':
  104. return V8ValuePrinter(val)
  105. return None
  106. gdb.pretty_printers.append(v8_pretty_printers)
  107. def v8_to_int(v):
  108. if v.type.sizeof == 4:
  109. return int(v.cast(t_u32))
  110. elif v.type.sizeof == 8:
  111. return int(v.cast(t_u64))
  112. else:
  113. return '?'
  114. def v8_get_value(vstring):
  115. v = gdb.parse_and_eval(vstring)
  116. return v8_to_int(v)
  117. class V8PrintObject(gdb.Command):
  118. """Prints a v8 object."""
  119. def __init__(self):
  120. super(V8PrintObject, self).__init__("v8print", gdb.COMMAND_DATA)
  121. def invoke(self, arg, from_tty):
  122. v = v8_get_value(arg)
  123. gdb.execute('call __gdb_print_v8_object(%d)' % v)
  124. V8PrintObject()
  125. class FindAnywhere(gdb.Command):
  126. """Search memory for the given pattern."""
  127. MAPPING_RE = re.compile(r"^\s*\[\d+\]\s+0x([0-9A-Fa-f]+)->0x([0-9A-Fa-f]+)")
  128. LIVE_MAPPING_RE = re.compile(r"^\s+0x([0-9A-Fa-f]+)\s+0x([0-9A-Fa-f]+)")
  129. def __init__(self):
  130. super(FindAnywhere, self).__init__("find-anywhere", gdb.COMMAND_DATA)
  131. def find(self, startAddr, endAddr, value):
  132. try:
  133. result = gdb.execute("find 0x%s, 0x%s, %s" % (startAddr, endAddr, value),
  134. to_string=True)
  135. if result.find("not found") == -1:
  136. print(result)
  137. except:
  138. pass
  139. def invoke(self, value, from_tty):
  140. for l in gdb.execute("maint info sections", to_string=True).split('\n'):
  141. m = FindAnywhere.MAPPING_RE.match(l)
  142. if m is None:
  143. continue
  144. self.find(m.group(1), m.group(2), value)
  145. for l in gdb.execute("info proc mappings", to_string=True).split('\n'):
  146. m = FindAnywhere.LIVE_MAPPING_RE.match(l)
  147. if m is None:
  148. continue
  149. self.find(m.group(1), m.group(2), value)
  150. FindAnywhere()
  151. class Redirect(gdb.Command):
  152. """Redirect the subcommand's stdout to a temporary file.
  153. Usage: redirect subcommand...
  154. Example:
  155. redirect job 0x123456789
  156. redirect x/1024xg 0x12345678
  157. If provided, the generated temporary file is directly openend with the
  158. GDB_EXTERNAL_EDITOR environment variable.
  159. """
  160. def __init__(self):
  161. super(Redirect, self).__init__("redirect", gdb.COMMAND_USER)
  162. def invoke(self, subcommand, from_tty):
  163. old_stdout = gdb.execute("p (int)dup(1)",
  164. to_string=True).split("=")[-1].strip()
  165. try:
  166. time_suffix = time.strftime("%Y%m%d-%H%M%S")
  167. fd, file = tempfile.mkstemp(suffix="-%s.gdbout" % time_suffix)
  168. try:
  169. # Temporarily redirect stdout to the created tmp file for the
  170. # duration of the subcommand.
  171. gdb.execute('p (int)dup2((int)open("%s", 1), 1)' % file,
  172. to_string=True)
  173. # Execute subcommand non interactively.
  174. result = gdb.execute(subcommand, from_tty=False, to_string=True)
  175. # Write returned string results to the temporary file as well.
  176. with open(file, 'a') as f:
  177. f.write(result)
  178. # Open generated result.
  179. if 'GDB_EXTERNAL_EDITOR' in os.environ:
  180. open_cmd = os.environ['GDB_EXTERNAL_EDITOR']
  181. print("Opening '%s' with %s" % (file, open_cmd))
  182. subprocess.call([open_cmd, file])
  183. else:
  184. print("Output written to:\n '%s'" % file)
  185. finally:
  186. # Restore original stdout.
  187. gdb.execute("p (int)dup2(%s, 1)" % old_stdout, to_string=True)
  188. # Close the temporary file.
  189. os.close(fd)
  190. finally:
  191. # Close the originally duplicated stdout descriptor.
  192. gdb.execute("p (int)close(%s)" % old_stdout, to_string=True)
  193. Redirect()