|  | # Copyright 2011 the V8 project authors. All rights reserved. | 
|  | # Redistribution and use in source and binary forms, with or without | 
|  | # modification, are permitted provided that the following conditions are | 
|  | # met: | 
|  | # | 
|  | #     * Redistributions of source code must retain the above copyright | 
|  | #       notice, this list of conditions and the following disclaimer. | 
|  | #     * Redistributions in binary form must reproduce the above | 
|  | #       copyright notice, this list of conditions and the following | 
|  | #       disclaimer in the documentation and/or other materials provided | 
|  | #       with the distribution. | 
|  | #     * Neither the name of Google Inc. nor the names of its | 
|  | #       contributors may be used to endorse or promote products derived | 
|  | #       from this software without specific prior written permission. | 
|  | # | 
|  | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
|  | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
|  | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
|  | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
|  | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
|  | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
|  | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
|  | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
|  | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  |  | 
|  | import re | 
|  | import tempfile | 
|  | import os | 
|  | import subprocess | 
|  | import time | 
|  |  | 
|  |  | 
|  | kSmiTag = 0 | 
|  | kSmiTagSize = 1 | 
|  | kSmiTagMask = (1 << kSmiTagSize) - 1 | 
|  |  | 
|  |  | 
|  | kHeapObjectTag = 1 | 
|  | kHeapObjectTagSize = 2 | 
|  | kHeapObjectTagMask = (1 << kHeapObjectTagSize) - 1 | 
|  |  | 
|  |  | 
|  | kFailureTag = 3 | 
|  | kFailureTagSize = 2 | 
|  | kFailureTagMask = (1 << kFailureTagSize) - 1 | 
|  |  | 
|  |  | 
|  | kSmiShiftSize32 = 0 | 
|  | kSmiValueSize32 = 31 | 
|  | kSmiShiftBits32 = kSmiTagSize + kSmiShiftSize32 | 
|  |  | 
|  |  | 
|  | kSmiShiftSize64 = 31 | 
|  | kSmiValueSize64 = 32 | 
|  | kSmiShiftBits64 = kSmiTagSize + kSmiShiftSize64 | 
|  |  | 
|  |  | 
|  | kAllBits = 0xFFFFFFFF | 
|  | kTopBit32 = 0x80000000 | 
|  | kTopBit64 = 0x8000000000000000 | 
|  |  | 
|  |  | 
|  | t_u32 = gdb.lookup_type('unsigned int') | 
|  | t_u64 = gdb.lookup_type('unsigned long long') | 
|  |  | 
|  |  | 
|  | def has_smi_tag(v): | 
|  | return v & kSmiTagMask == kSmiTag | 
|  |  | 
|  |  | 
|  | def has_failure_tag(v): | 
|  | return v & kFailureTagMask == kFailureTag | 
|  |  | 
|  |  | 
|  | def has_heap_object_tag(v): | 
|  | return v & kHeapObjectTagMask == kHeapObjectTag | 
|  |  | 
|  |  | 
|  | def raw_heap_object(v): | 
|  | return v - kHeapObjectTag | 
|  |  | 
|  |  | 
|  | def smi_to_int_32(v): | 
|  | v = v & kAllBits | 
|  | if (v & kTopBit32) == kTopBit32: | 
|  | return ((v & kAllBits) >> kSmiShiftBits32) - 2147483648 | 
|  | else: | 
|  | return (v & kAllBits) >> kSmiShiftBits32 | 
|  |  | 
|  |  | 
|  | def smi_to_int_64(v): | 
|  | return (v >> kSmiShiftBits64) | 
|  |  | 
|  |  | 
|  | def decode_v8_value(v, bitness): | 
|  | base_str = 'v8[%x]' % v | 
|  | if has_smi_tag(v): | 
|  | if bitness == 32: | 
|  | return base_str + (" SMI(%d)" % smi_to_int_32(v)) | 
|  | else: | 
|  | return base_str + (" SMI(%d)" % smi_to_int_64(v)) | 
|  | elif has_failure_tag(v): | 
|  | return base_str + " (failure)" | 
|  | elif has_heap_object_tag(v): | 
|  | return base_str + (" H(0x%x)" % raw_heap_object(v)) | 
|  | else: | 
|  | return base_str | 
|  |  | 
|  |  | 
|  | class V8ValuePrinter(object): | 
|  | "Print a v8value." | 
|  | def __init__(self, val): | 
|  | self.val = val | 
|  | def to_string(self): | 
|  | if self.val.type.sizeof == 4: | 
|  | v_u32 = self.val.cast(t_u32) | 
|  | return decode_v8_value(int(v_u32), 32) | 
|  | elif self.val.type.sizeof == 8: | 
|  | v_u64 = self.val.cast(t_u64) | 
|  | return decode_v8_value(int(v_u64), 64) | 
|  | else: | 
|  | return 'v8value?' | 
|  | def display_hint(self): | 
|  | return 'v8value' | 
|  |  | 
|  |  | 
|  | def v8_pretty_printers(val): | 
|  | lookup_tag = val.type.tag | 
|  | if lookup_tag == None: | 
|  | return None | 
|  | elif lookup_tag == 'v8value': | 
|  | return V8ValuePrinter(val) | 
|  | return None | 
|  | gdb.pretty_printers.append(v8_pretty_printers) | 
|  |  | 
|  |  | 
|  | def v8_to_int(v): | 
|  | if v.type.sizeof == 4: | 
|  | return int(v.cast(t_u32)) | 
|  | elif v.type.sizeof == 8: | 
|  | return int(v.cast(t_u64)) | 
|  | else: | 
|  | return '?' | 
|  |  | 
|  |  | 
|  | def v8_get_value(vstring): | 
|  | v = gdb.parse_and_eval(vstring) | 
|  | return v8_to_int(v) | 
|  |  | 
|  |  | 
|  | class V8PrintObject (gdb.Command): | 
|  | """Prints a v8 object.""" | 
|  | def __init__ (self): | 
|  | super (V8PrintObject, self).__init__ ("v8print", gdb.COMMAND_DATA) | 
|  | def invoke (self, arg, from_tty): | 
|  | v = v8_get_value(arg) | 
|  | gdb.execute('call __gdb_print_v8_object(%d)' % v) | 
|  | V8PrintObject() | 
|  |  | 
|  |  | 
|  | class FindAnywhere (gdb.Command): | 
|  | """Search memory for the given pattern.""" | 
|  | MAPPING_RE = re.compile(r"^\s*\[\d+\]\s+0x([0-9A-Fa-f]+)->0x([0-9A-Fa-f]+)") | 
|  | LIVE_MAPPING_RE = re.compile(r"^\s+0x([0-9A-Fa-f]+)\s+0x([0-9A-Fa-f]+)") | 
|  | def __init__ (self): | 
|  | super (FindAnywhere, self).__init__ ("find-anywhere", gdb.COMMAND_DATA) | 
|  | def find (self, startAddr, endAddr, value): | 
|  | try: | 
|  | result = gdb.execute( | 
|  | "find 0x%s, 0x%s, %s" % (startAddr, endAddr, value), | 
|  | to_string = True) | 
|  | if result.find("not found") == -1: | 
|  | print(result) | 
|  | except: | 
|  | pass | 
|  |  | 
|  | def invoke (self, value, from_tty): | 
|  | for l in gdb.execute("maint info sections", to_string = True).split('\n'): | 
|  | m = FindAnywhere.MAPPING_RE.match(l) | 
|  | if m is None: | 
|  | continue | 
|  | self.find(m.group(1), m.group(2), value) | 
|  | for l in gdb.execute("info proc mappings", to_string = True).split('\n'): | 
|  | m = FindAnywhere.LIVE_MAPPING_RE.match(l) | 
|  | if m is None: | 
|  | continue | 
|  | self.find(m.group(1), m.group(2), value) | 
|  |  | 
|  | FindAnywhere() | 
|  |  | 
|  |  | 
|  | class Redirect(gdb.Command): | 
|  | """Redirect the subcommand's stdout  to a temporary file. | 
|  |  | 
|  | Usage:   redirect subcommand... | 
|  | Example: | 
|  | redirect job 0x123456789 | 
|  | redirect x/1024xg 0x12345678 | 
|  |  | 
|  | If provided, the generated temporary file is directly openend with the | 
|  | GDB_EXTERNAL_EDITOR environment variable. | 
|  | """ | 
|  | def __init__(self): | 
|  | super(Redirect, self).__init__("redirect", gdb.COMMAND_USER) | 
|  |  | 
|  | def invoke(self, subcommand, from_tty): | 
|  | old_stdout = gdb.execute("p dup(1)", to_string=True).split("=")[-1].strip() | 
|  | try: | 
|  | time_suffix = time.strftime("%Y%m%d-%H%M%S") | 
|  | fd, file = tempfile.mkstemp(suffix="-%s.gdbout" % time_suffix) | 
|  | try: | 
|  | # Temporaily redirect stdout to the created tmp file for the | 
|  | # duration of the subcommand. | 
|  | gdb.execute('p dup2(open("%s", 1), 1)' % file, to_string=True) | 
|  | # Execute subcommand non interactively. | 
|  | result = gdb.execute(subcommand, from_tty=False, to_string=True) | 
|  | # Write returned string results to the temporary file as well. | 
|  | with open(file, 'a') as f: | 
|  | f.write(result) | 
|  | # Open generated result. | 
|  | if 'GDB_EXTERNAL_EDITOR' in os.environ: | 
|  | open_cmd = os.environ['GDB_EXTERNAL_EDITOR'] | 
|  | print("Opening '%s' with %s" % (file, open_cmd)) | 
|  | subprocess.call([open_cmd, file]) | 
|  | else: | 
|  | print("Open output:\n  %s '%s'" % (os.environ['EDITOR'], file)) | 
|  | finally: | 
|  | # Restore original stdout. | 
|  | gdb.execute("p dup2(%s, 1)" % old_stdout, to_string=True) | 
|  | # Close the temporary file. | 
|  | os.close(fd) | 
|  | finally: | 
|  | # Close the originally duplicated stdout descriptor. | 
|  | gdb.execute("p close(%s)" % old_stdout, to_string=True) | 
|  |  | 
|  | Redirect() |