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
28# for py2/py3 compatibility
29from __future__ import print_function
30
31import re
32import tempfile
33import os
34import subprocess
35import time
36import gdb
37
38kSmiTag = 0
39kSmiTagSize = 1
40kSmiTagMask = (1 << kSmiTagSize) - 1
41
42kHeapObjectTag = 1
43kHeapObjectTagSize = 2
44kHeapObjectTagMask = (1 << kHeapObjectTagSize) - 1
45
46kFailureTag = 3
47kFailureTagSize = 2
48kFailureTagMask = (1 << kFailureTagSize) - 1
49
50kSmiShiftSize32 = 0
51kSmiValueSize32 = 31
52kSmiShiftBits32 = kSmiTagSize + kSmiShiftSize32
53
54kSmiShiftSize64 = 31
55kSmiValueSize64 = 32
56kSmiShiftBits64 = kSmiTagSize + kSmiShiftSize64
57
58kAllBits = 0xFFFFFFFF
59kTopBit32 = 0x80000000
60kTopBit64 = 0x8000000000000000
61
62t_u32 = gdb.lookup_type('unsigned int')
63t_u64 = gdb.lookup_type('unsigned long long')
64
65
66def has_smi_tag(v):
67  return v & kSmiTagMask == kSmiTag
68
69
70def has_failure_tag(v):
71  return v & kFailureTagMask == kFailureTag
72
73
74def has_heap_object_tag(v):
75  return v & kHeapObjectTagMask == kHeapObjectTag
76
77
78def raw_heap_object(v):
79  return v - kHeapObjectTag
80
81
82def smi_to_int_32(v):
83  v = v & kAllBits
84  if (v & kTopBit32) == kTopBit32:
85    return ((v & kAllBits) >> kSmiShiftBits32) - 2147483648
86  else:
87    return (v & kAllBits) >> kSmiShiftBits32
88
89
90def smi_to_int_64(v):
91  return (v >> kSmiShiftBits64)
92
93
94def decode_v8_value(v, bitness):
95  base_str = 'v8[%x]' % v
96  if has_smi_tag(v):
97    if bitness == 32:
98      return base_str + (" SMI(%d)" % smi_to_int_32(v))
99    else:
100      return base_str + (" SMI(%d)" % smi_to_int_64(v))
101  elif has_failure_tag(v):
102    return base_str + " (failure)"
103  elif has_heap_object_tag(v):
104    return base_str + (" H(0x%x)" % raw_heap_object(v))
105  else:
106    return base_str
107
108
109class V8ValuePrinter(object):
110  "Print a v8value."
111
112  def __init__(self, val):
113    self.val = val
114
115  def to_string(self):
116    if self.val.type.sizeof == 4:
117      v_u32 = self.val.cast(t_u32)
118      return decode_v8_value(int(v_u32), 32)
119    elif self.val.type.sizeof == 8:
120      v_u64 = self.val.cast(t_u64)
121      return decode_v8_value(int(v_u64), 64)
122    else:
123      return 'v8value?'
124
125  def display_hint(self):
126    return 'v8value'
127
128
129def v8_pretty_printers(val):
130  lookup_tag = val.type.tag
131  if lookup_tag == None:
132    return None
133  elif lookup_tag == 'v8value':
134    return V8ValuePrinter(val)
135  return None
136
137
138gdb.pretty_printers.append(v8_pretty_printers)
139
140
141def v8_to_int(v):
142  if v.type.sizeof == 4:
143    return int(v.cast(t_u32))
144  elif v.type.sizeof == 8:
145    return int(v.cast(t_u64))
146  else:
147    return '?'
148
149
150def v8_get_value(vstring):
151  v = gdb.parse_and_eval(vstring)
152  return v8_to_int(v)
153
154
155class V8PrintObject(gdb.Command):
156  """Prints a v8 object."""
157  def __init__(self):
158    super(V8PrintObject, self).__init__("v8print", gdb.COMMAND_DATA)
159
160  def invoke(self, arg, from_tty):
161    v = v8_get_value(arg)
162    gdb.execute('call __gdb_print_v8_object(%d)' % v)
163
164
165V8PrintObject()
166
167
168class FindAnywhere(gdb.Command):
169  """Search memory for the given pattern."""
170  MAPPING_RE = re.compile(r"^\s*\[\d+\]\s+0x([0-9A-Fa-f]+)->0x([0-9A-Fa-f]+)")
171  LIVE_MAPPING_RE = re.compile(r"^\s+0x([0-9A-Fa-f]+)\s+0x([0-9A-Fa-f]+)")
172
173  def __init__(self):
174    super(FindAnywhere, self).__init__("find-anywhere", gdb.COMMAND_DATA)
175
176  def find(self, startAddr, endAddr, value):
177    try:
178      result = gdb.execute("find 0x%s, 0x%s, %s" % (startAddr, endAddr, value),
179                           to_string=True)
180      if result.find("not found") == -1:
181        print(result)
182    except:
183      pass
184
185  def invoke(self, value, from_tty):
186    for l in gdb.execute("maint info sections", to_string=True).split('\n'):
187      m = FindAnywhere.MAPPING_RE.match(l)
188      if m is None:
189        continue
190      self.find(m.group(1), m.group(2), value)
191    for l in gdb.execute("info proc mappings", to_string=True).split('\n'):
192      m = FindAnywhere.LIVE_MAPPING_RE.match(l)
193      if m is None:
194        continue
195      self.find(m.group(1), m.group(2), value)
196
197
198FindAnywhere()
199
200
201class Redirect(gdb.Command):
202  """Redirect the subcommand's stdout  to a temporary file.
203
204Usage:   redirect subcommand...
205Example:
206  redirect job 0x123456789
207  redirect x/1024xg 0x12345678
208
209If provided, the generated temporary file is directly openend with the
210GDB_EXTERNAL_EDITOR environment variable.
211  """
212  def __init__(self):
213    super(Redirect, self).__init__("redirect", gdb.COMMAND_USER)
214
215  def invoke(self, subcommand, from_tty):
216    old_stdout = gdb.execute("p (int)dup(1)",
217                             to_string=True).split("=")[-1].strip()
218    try:
219      time_suffix = time.strftime("%Y%m%d-%H%M%S")
220      fd, file = tempfile.mkstemp(suffix="-%s.gdbout" % time_suffix)
221      try:
222        # Temporarily redirect stdout to the created tmp file for the
223        # duration of the subcommand.
224        gdb.execute('p (int)dup2((int)open("%s", 1), 1)' % file,
225                    to_string=True)
226        # Execute subcommand non interactively.
227        result = gdb.execute(subcommand, from_tty=False, to_string=True)
228        # Write returned string results to the temporary file as well.
229        with open(file, 'a') as f:
230          f.write(result)
231        # Open generated result.
232        if 'GDB_EXTERNAL_EDITOR' in os.environ:
233          open_cmd = os.environ['GDB_EXTERNAL_EDITOR']
234          print("Opening '%s' with %s" % (file, open_cmd))
235          subprocess.call([open_cmd, file])
236        else:
237          print("Output written to:\n '%s'" % file)
238      finally:
239        # Restore original stdout.
240        gdb.execute("p (int)dup2(%s, 1)" % old_stdout, to_string=True)
241        # Close the temporary file.
242        os.close(fd)
243    finally:
244      # Close the originally duplicated stdout descriptor.
245      gdb.execute("p (int)close(%s)" % old_stdout, to_string=True)
246
247
248Redirect()
249