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