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