Home | History | Annotate | Download | only in tools
      1 #!/usr/bin/env python
      2 #
      3 # Copyright 2012 the V8 project authors. All rights reserved.
      4 # Redistribution and use in source and binary forms, with or without
      5 # modification, are permitted provided that the following conditions are
      6 # met:
      7 #
      8 #     * Redistributions of source code must retain the above copyright
      9 #       notice, this list of conditions and the following disclaimer.
     10 #     * Redistributions in binary form must reproduce the above
     11 #       copyright notice, this list of conditions and the following
     12 #       disclaimer in the documentation and/or other materials provided
     13 #       with the distribution.
     14 #     * Neither the name of Google Inc. nor the names of its
     15 #       contributors may be used to endorse or promote products derived
     16 #       from this software without specific prior written permission.
     17 #
     18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29 
     30 import bisect
     31 import cmd
     32 import codecs
     33 import ctypes
     34 import datetime
     35 import disasm
     36 import mmap
     37 import optparse
     38 import os
     39 import re
     40 import struct
     41 import sys
     42 import types
     43 import v8heapconst
     44 
     45 USAGE="""usage: %prog [OPTIONS] [DUMP-FILE]
     46 
     47 Minidump analyzer.
     48 
     49 Shows the processor state at the point of exception including the
     50 stack of the active thread and the referenced objects in the V8
     51 heap. Code objects are disassembled and the addresses linked from the
     52 stack (e.g. pushed return addresses) are marked with "=>".
     53 
     54 Examples:
     55   $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp"""
     56 
     57 
     58 DEBUG=False
     59 
     60 
     61 def DebugPrint(s):
     62   if not DEBUG: return
     63   print s
     64 
     65 
     66 class Descriptor(object):
     67   """Descriptor of a structure in a memory."""
     68 
     69   def __init__(self, fields):
     70     self.fields = fields
     71     self.is_flexible = False
     72     for _, type_or_func in fields:
     73       if isinstance(type_or_func, types.FunctionType):
     74         self.is_flexible = True
     75         break
     76     if not self.is_flexible:
     77       self.ctype = Descriptor._GetCtype(fields)
     78       self.size = ctypes.sizeof(self.ctype)
     79 
     80   def Read(self, memory, offset):
     81     if self.is_flexible:
     82       fields_copy = self.fields[:]
     83       last = 0
     84       for name, type_or_func in fields_copy:
     85         if isinstance(type_or_func, types.FunctionType):
     86           partial_ctype = Descriptor._GetCtype(fields_copy[:last])
     87           partial_object = partial_ctype.from_buffer(memory, offset)
     88           type = type_or_func(partial_object)
     89           if type is not None:
     90             fields_copy[last] = (name, type)
     91             last += 1
     92         else:
     93           last += 1
     94       complete_ctype = Descriptor._GetCtype(fields_copy[:last])
     95     else:
     96       complete_ctype = self.ctype
     97     return complete_ctype.from_buffer(memory, offset)
     98 
     99   @staticmethod
    100   def _GetCtype(fields):
    101     class Raw(ctypes.Structure):
    102       _fields_ = fields
    103       _pack_ = 1
    104 
    105       def __str__(self):
    106         return "{" + ", ".join("%s: %s" % (field, self.__getattribute__(field))
    107                                for field, _ in Raw._fields_) + "}"
    108     return Raw
    109 
    110 
    111 def FullDump(reader, heap):
    112   """Dump all available memory regions."""
    113   def dump_region(reader, start, size, location):
    114     print
    115     while start & 3 != 0:
    116       start += 1
    117       size -= 1
    118       location += 1
    119     is_executable = reader.IsProbableExecutableRegion(location, size)
    120     is_ascii = reader.IsProbableASCIIRegion(location, size)
    121 
    122     if is_executable is not False:
    123       lines = reader.GetDisasmLines(start, size)
    124       for line in lines:
    125         print FormatDisasmLine(start, heap, line)
    126       print
    127 
    128     if is_ascii is not False:
    129       # Output in the same format as the Unix hd command
    130       addr = start
    131       for slot in xrange(location, location + size, 16):
    132         hex_line = ""
    133         asc_line = ""
    134         for i in xrange(0, 16):
    135           if slot + i < location + size:
    136             byte = ctypes.c_uint8.from_buffer(reader.minidump, slot + i).value
    137             if byte >= 0x20 and byte < 0x7f:
    138               asc_line += chr(byte)
    139             else:
    140               asc_line += "."
    141             hex_line += " %02x" % (byte)
    142           else:
    143             hex_line += "   "
    144           if i == 7:
    145             hex_line += " "
    146         print "%s  %s |%s|" % (reader.FormatIntPtr(addr),
    147                                hex_line,
    148                                asc_line)
    149         addr += 16
    150 
    151     if is_executable is not True and is_ascii is not True:
    152       print "%s - %s" % (reader.FormatIntPtr(start),
    153                          reader.FormatIntPtr(start + size))
    154       for slot in xrange(start,
    155                          start + size,
    156                          reader.PointerSize()):
    157         maybe_address = reader.ReadUIntPtr(slot)
    158         heap_object = heap.FindObject(maybe_address)
    159         print "%s: %s" % (reader.FormatIntPtr(slot),
    160                           reader.FormatIntPtr(maybe_address))
    161         if heap_object:
    162           heap_object.Print(Printer())
    163           print
    164 
    165   reader.ForEachMemoryRegion(dump_region)
    166 
    167 # Heap constants generated by 'make grokdump' in v8heapconst module.
    168 INSTANCE_TYPES = v8heapconst.INSTANCE_TYPES
    169 KNOWN_MAPS = v8heapconst.KNOWN_MAPS
    170 KNOWN_OBJECTS = v8heapconst.KNOWN_OBJECTS
    171 
    172 # Set of structures and constants that describe the layout of minidump
    173 # files. Based on MSDN and Google Breakpad.
    174 
    175 MINIDUMP_HEADER = Descriptor([
    176   ("signature", ctypes.c_uint32),
    177   ("version", ctypes.c_uint32),
    178   ("stream_count", ctypes.c_uint32),
    179   ("stream_directories_rva", ctypes.c_uint32),
    180   ("checksum", ctypes.c_uint32),
    181   ("time_date_stampt", ctypes.c_uint32),
    182   ("flags", ctypes.c_uint64)
    183 ])
    184 
    185 MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([
    186   ("data_size", ctypes.c_uint32),
    187   ("rva", ctypes.c_uint32)
    188 ])
    189 
    190 MINIDUMP_STRING = Descriptor([
    191   ("length", ctypes.c_uint32),
    192   ("buffer", lambda t: ctypes.c_uint8 * (t.length + 2))
    193 ])
    194 
    195 MINIDUMP_DIRECTORY = Descriptor([
    196   ("stream_type", ctypes.c_uint32),
    197   ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
    198 ])
    199 
    200 MD_EXCEPTION_MAXIMUM_PARAMETERS = 15
    201 
    202 MINIDUMP_EXCEPTION = Descriptor([
    203   ("code", ctypes.c_uint32),
    204   ("flags", ctypes.c_uint32),
    205   ("record", ctypes.c_uint64),
    206   ("address", ctypes.c_uint64),
    207   ("parameter_count", ctypes.c_uint32),
    208   ("unused_alignment", ctypes.c_uint32),
    209   ("information", ctypes.c_uint64 * MD_EXCEPTION_MAXIMUM_PARAMETERS)
    210 ])
    211 
    212 MINIDUMP_EXCEPTION_STREAM = Descriptor([
    213   ("thread_id", ctypes.c_uint32),
    214   ("unused_alignment", ctypes.c_uint32),
    215   ("exception", MINIDUMP_EXCEPTION.ctype),
    216   ("thread_context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
    217 ])
    218 
    219 # Stream types.
    220 MD_UNUSED_STREAM = 0
    221 MD_RESERVED_STREAM_0 = 1
    222 MD_RESERVED_STREAM_1 = 2
    223 MD_THREAD_LIST_STREAM = 3
    224 MD_MODULE_LIST_STREAM = 4
    225 MD_MEMORY_LIST_STREAM = 5
    226 MD_EXCEPTION_STREAM = 6
    227 MD_SYSTEM_INFO_STREAM = 7
    228 MD_THREAD_EX_LIST_STREAM = 8
    229 MD_MEMORY_64_LIST_STREAM = 9
    230 MD_COMMENT_STREAM_A = 10
    231 MD_COMMENT_STREAM_W = 11
    232 MD_HANDLE_DATA_STREAM = 12
    233 MD_FUNCTION_TABLE_STREAM = 13
    234 MD_UNLOADED_MODULE_LIST_STREAM = 14
    235 MD_MISC_INFO_STREAM = 15
    236 MD_MEMORY_INFO_LIST_STREAM = 16
    237 MD_THREAD_INFO_LIST_STREAM = 17
    238 MD_HANDLE_OPERATION_LIST_STREAM = 18
    239 
    240 MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE = 80
    241 
    242 MINIDUMP_FLOATING_SAVE_AREA_X86 = Descriptor([
    243   ("control_word", ctypes.c_uint32),
    244   ("status_word", ctypes.c_uint32),
    245   ("tag_word", ctypes.c_uint32),
    246   ("error_offset", ctypes.c_uint32),
    247   ("error_selector", ctypes.c_uint32),
    248   ("data_offset", ctypes.c_uint32),
    249   ("data_selector", ctypes.c_uint32),
    250   ("register_area", ctypes.c_uint8 * MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE),
    251   ("cr0_npx_state", ctypes.c_uint32)
    252 ])
    253 
    254 MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE = 512
    255 
    256 # Context flags.
    257 MD_CONTEXT_X86 = 0x00010000
    258 MD_CONTEXT_X86_CONTROL = (MD_CONTEXT_X86 | 0x00000001)
    259 MD_CONTEXT_X86_INTEGER = (MD_CONTEXT_X86 | 0x00000002)
    260 MD_CONTEXT_X86_SEGMENTS = (MD_CONTEXT_X86 | 0x00000004)
    261 MD_CONTEXT_X86_FLOATING_POINT = (MD_CONTEXT_X86 | 0x00000008)
    262 MD_CONTEXT_X86_DEBUG_REGISTERS = (MD_CONTEXT_X86 | 0x00000010)
    263 MD_CONTEXT_X86_EXTENDED_REGISTERS = (MD_CONTEXT_X86 | 0x00000020)
    264 
    265 def EnableOnFlag(type, flag):
    266   return lambda o: [None, type][int((o.context_flags & flag) != 0)]
    267 
    268 MINIDUMP_CONTEXT_X86 = Descriptor([
    269   ("context_flags", ctypes.c_uint32),
    270   # MD_CONTEXT_X86_DEBUG_REGISTERS.
    271   ("dr0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
    272   ("dr1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
    273   ("dr2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
    274   ("dr3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
    275   ("dr6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
    276   ("dr7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
    277   # MD_CONTEXT_X86_FLOATING_POINT.
    278   ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_X86.ctype,
    279                               MD_CONTEXT_X86_FLOATING_POINT)),
    280   # MD_CONTEXT_X86_SEGMENTS.
    281   ("gs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
    282   ("fs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
    283   ("es", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
    284   ("ds", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
    285   # MD_CONTEXT_X86_INTEGER.
    286   ("edi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
    287   ("esi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
    288   ("ebx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
    289   ("edx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
    290   ("ecx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
    291   ("eax", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
    292   # MD_CONTEXT_X86_CONTROL.
    293   ("ebp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
    294   ("eip", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
    295   ("cs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
    296   ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
    297   ("esp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
    298   ("ss", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
    299   # MD_CONTEXT_X86_EXTENDED_REGISTERS.
    300   ("extended_registers",
    301    EnableOnFlag(ctypes.c_uint8 * MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE,
    302                 MD_CONTEXT_X86_EXTENDED_REGISTERS))
    303 ])
    304 
    305 MD_CONTEXT_ARM = 0x40000000
    306 MD_CONTEXT_ARM_INTEGER = (MD_CONTEXT_ARM | 0x00000002)
    307 MD_CONTEXT_ARM_FLOATING_POINT = (MD_CONTEXT_ARM | 0x00000004)
    308 MD_FLOATINGSAVEAREA_ARM_FPR_COUNT = 32
    309 MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT = 8
    310 
    311 MINIDUMP_FLOATING_SAVE_AREA_ARM = Descriptor([
    312   ("fpscr", ctypes.c_uint64),
    313   ("regs", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPR_COUNT),
    314   ("extra", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT)
    315 ])
    316 
    317 MINIDUMP_CONTEXT_ARM = Descriptor([
    318   ("context_flags", ctypes.c_uint32),
    319   # MD_CONTEXT_ARM_INTEGER.
    320   ("r0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    321   ("r1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    322   ("r2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    323   ("r3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    324   ("r4", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    325   ("r5", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    326   ("r6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    327   ("r7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    328   ("r8", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    329   ("r9", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    330   ("r10", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    331   ("r11", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    332   ("r12", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    333   ("sp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    334   ("lr", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    335   ("pc", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    336   ("cpsr", ctypes.c_uint32),
    337   ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_ARM.ctype,
    338                               MD_CONTEXT_ARM_FLOATING_POINT))
    339 ])
    340 
    341 MD_CONTEXT_AMD64 = 0x00100000
    342 MD_CONTEXT_AMD64_CONTROL = (MD_CONTEXT_AMD64 | 0x00000001)
    343 MD_CONTEXT_AMD64_INTEGER = (MD_CONTEXT_AMD64 | 0x00000002)
    344 MD_CONTEXT_AMD64_SEGMENTS = (MD_CONTEXT_AMD64 | 0x00000004)
    345 MD_CONTEXT_AMD64_FLOATING_POINT = (MD_CONTEXT_AMD64 | 0x00000008)
    346 MD_CONTEXT_AMD64_DEBUG_REGISTERS = (MD_CONTEXT_AMD64 | 0x00000010)
    347 
    348 MINIDUMP_CONTEXT_AMD64 = Descriptor([
    349   ("p1_home", ctypes.c_uint64),
    350   ("p2_home", ctypes.c_uint64),
    351   ("p3_home", ctypes.c_uint64),
    352   ("p4_home", ctypes.c_uint64),
    353   ("p5_home", ctypes.c_uint64),
    354   ("p6_home", ctypes.c_uint64),
    355   ("context_flags", ctypes.c_uint32),
    356   ("mx_csr", ctypes.c_uint32),
    357   # MD_CONTEXT_AMD64_CONTROL.
    358   ("cs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
    359   # MD_CONTEXT_AMD64_SEGMENTS
    360   ("ds", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
    361   ("es", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
    362   ("fs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
    363   ("gs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
    364   # MD_CONTEXT_AMD64_CONTROL.
    365   ("ss", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
    366   ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_AMD64_CONTROL)),
    367   # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
    368   ("dr0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
    369   ("dr1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
    370   ("dr2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
    371   ("dr3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
    372   ("dr6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
    373   ("dr7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
    374   # MD_CONTEXT_AMD64_INTEGER.
    375   ("rax", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    376   ("rcx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    377   ("rdx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    378   ("rbx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    379   # MD_CONTEXT_AMD64_CONTROL.
    380   ("rsp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
    381   # MD_CONTEXT_AMD64_INTEGER.
    382   ("rbp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    383   ("rsi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    384   ("rdi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    385   ("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    386   ("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    387   ("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    388   ("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    389   ("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    390   ("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    391   ("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    392   ("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    393   # MD_CONTEXT_AMD64_CONTROL.
    394   ("rip", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
    395   # MD_CONTEXT_AMD64_FLOATING_POINT
    396   ("sse_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
    397                                  MD_CONTEXT_AMD64_FLOATING_POINT)),
    398   ("vector_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
    399                                     MD_CONTEXT_AMD64_FLOATING_POINT)),
    400   ("vector_control", EnableOnFlag(ctypes.c_uint64,
    401                                   MD_CONTEXT_AMD64_FLOATING_POINT)),
    402   # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
    403   ("debug_control", EnableOnFlag(ctypes.c_uint64,
    404                                  MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
    405   ("last_branch_to_rip", EnableOnFlag(ctypes.c_uint64,
    406                                       MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
    407   ("last_branch_from_rip", EnableOnFlag(ctypes.c_uint64,
    408                                         MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
    409   ("last_exception_to_rip", EnableOnFlag(ctypes.c_uint64,
    410                                          MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
    411   ("last_exception_from_rip", EnableOnFlag(ctypes.c_uint64,
    412                                            MD_CONTEXT_AMD64_DEBUG_REGISTERS))
    413 ])
    414 
    415 MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([
    416   ("start", ctypes.c_uint64),
    417   ("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
    418 ])
    419 
    420 MINIDUMP_MEMORY_DESCRIPTOR64 = Descriptor([
    421   ("start", ctypes.c_uint64),
    422   ("size", ctypes.c_uint64)
    423 ])
    424 
    425 MINIDUMP_MEMORY_LIST = Descriptor([
    426   ("range_count", ctypes.c_uint32),
    427   ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count)
    428 ])
    429 
    430 MINIDUMP_MEMORY_LIST64 = Descriptor([
    431   ("range_count", ctypes.c_uint64),
    432   ("base_rva", ctypes.c_uint64),
    433   ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR64.ctype * m.range_count)
    434 ])
    435 
    436 MINIDUMP_THREAD = Descriptor([
    437   ("id", ctypes.c_uint32),
    438   ("suspend_count", ctypes.c_uint32),
    439   ("priority_class", ctypes.c_uint32),
    440   ("priority", ctypes.c_uint32),
    441   ("ted", ctypes.c_uint64),
    442   ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype),
    443   ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
    444 ])
    445 
    446 MINIDUMP_THREAD_LIST = Descriptor([
    447   ("thread_count", ctypes.c_uint32),
    448   ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
    449 ])
    450 
    451 MINIDUMP_VS_FIXEDFILEINFO = Descriptor([
    452   ("dwSignature", ctypes.c_uint32),
    453   ("dwStrucVersion", ctypes.c_uint32),
    454   ("dwFileVersionMS", ctypes.c_uint32),
    455   ("dwFileVersionLS", ctypes.c_uint32),
    456   ("dwProductVersionMS", ctypes.c_uint32),
    457   ("dwProductVersionLS", ctypes.c_uint32),
    458   ("dwFileFlagsMask", ctypes.c_uint32),
    459   ("dwFileFlags", ctypes.c_uint32),
    460   ("dwFileOS", ctypes.c_uint32),
    461   ("dwFileType", ctypes.c_uint32),
    462   ("dwFileSubtype", ctypes.c_uint32),
    463   ("dwFileDateMS", ctypes.c_uint32),
    464   ("dwFileDateLS", ctypes.c_uint32)
    465 ])
    466 
    467 MINIDUMP_RAW_MODULE = Descriptor([
    468   ("base_of_image", ctypes.c_uint64),
    469   ("size_of_image", ctypes.c_uint32),
    470   ("checksum", ctypes.c_uint32),
    471   ("time_date_stamp", ctypes.c_uint32),
    472   ("module_name_rva", ctypes.c_uint32),
    473   ("version_info", MINIDUMP_VS_FIXEDFILEINFO.ctype),
    474   ("cv_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
    475   ("misc_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
    476   ("reserved0", ctypes.c_uint32 * 2),
    477   ("reserved1", ctypes.c_uint32 * 2)
    478 ])
    479 
    480 MINIDUMP_MODULE_LIST = Descriptor([
    481   ("number_of_modules", ctypes.c_uint32),
    482   ("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules)
    483 ])
    484 
    485 MINIDUMP_RAW_SYSTEM_INFO = Descriptor([
    486   ("processor_architecture", ctypes.c_uint16)
    487 ])
    488 
    489 MD_CPU_ARCHITECTURE_X86 = 0
    490 MD_CPU_ARCHITECTURE_ARM = 5
    491 MD_CPU_ARCHITECTURE_AMD64 = 9
    492 
    493 class FuncSymbol:
    494   def __init__(self, start, size, name):
    495     self.start = start
    496     self.end = self.start + size
    497     self.name = name
    498 
    499   def __cmp__(self, other):
    500     if isinstance(other, FuncSymbol):
    501       return self.start - other.start
    502     return self.start - other
    503 
    504   def Covers(self, addr):
    505     return (self.start <= addr) and (addr < self.end)
    506 
    507 class MinidumpReader(object):
    508   """Minidump (.dmp) reader."""
    509 
    510   _HEADER_MAGIC = 0x504d444d
    511 
    512   def __init__(self, options, minidump_name):
    513     self.minidump_name = minidump_name
    514     self.minidump_file = open(minidump_name, "r")
    515     self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE)
    516     self.header = MINIDUMP_HEADER.Read(self.minidump, 0)
    517     if self.header.signature != MinidumpReader._HEADER_MAGIC:
    518       print >>sys.stderr, "Warning: Unsupported minidump header magic!"
    519     DebugPrint(self.header)
    520     directories = []
    521     offset = self.header.stream_directories_rva
    522     for _ in xrange(self.header.stream_count):
    523       directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset))
    524       offset += MINIDUMP_DIRECTORY.size
    525     self.arch = None
    526     self.exception = None
    527     self.exception_context = None
    528     self.memory_list = None
    529     self.memory_list64 = None
    530     self.module_list = None
    531     self.thread_map = {}
    532 
    533     self.symdir = options.symdir
    534     self.modules_with_symbols = []
    535     self.symbols = []
    536 
    537     # Find MDRawSystemInfo stream and determine arch.
    538     for d in directories:
    539       if d.stream_type == MD_SYSTEM_INFO_STREAM:
    540         system_info = MINIDUMP_RAW_SYSTEM_INFO.Read(
    541             self.minidump, d.location.rva)
    542         self.arch = system_info.processor_architecture
    543         assert self.arch in [MD_CPU_ARCHITECTURE_AMD64,
    544                              MD_CPU_ARCHITECTURE_ARM,
    545                              MD_CPU_ARCHITECTURE_X86]
    546     assert not self.arch is None
    547 
    548     for d in directories:
    549       DebugPrint(d)
    550       if d.stream_type == MD_EXCEPTION_STREAM:
    551         self.exception = MINIDUMP_EXCEPTION_STREAM.Read(
    552           self.minidump, d.location.rva)
    553         DebugPrint(self.exception)
    554         if self.arch == MD_CPU_ARCHITECTURE_X86:
    555           self.exception_context = MINIDUMP_CONTEXT_X86.Read(
    556               self.minidump, self.exception.thread_context.rva)
    557         elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
    558           self.exception_context = MINIDUMP_CONTEXT_AMD64.Read(
    559               self.minidump, self.exception.thread_context.rva)
    560         elif self.arch == MD_CPU_ARCHITECTURE_ARM:
    561           self.exception_context = MINIDUMP_CONTEXT_ARM.Read(
    562               self.minidump, self.exception.thread_context.rva)
    563         DebugPrint(self.exception_context)
    564       elif d.stream_type == MD_THREAD_LIST_STREAM:
    565         thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva)
    566         assert ctypes.sizeof(thread_list) == d.location.data_size
    567         DebugPrint(thread_list)
    568         for thread in thread_list.threads:
    569           DebugPrint(thread)
    570           self.thread_map[thread.id] = thread
    571       elif d.stream_type == MD_MODULE_LIST_STREAM:
    572         assert self.module_list is None
    573         self.module_list = MINIDUMP_MODULE_LIST.Read(
    574           self.minidump, d.location.rva)
    575         assert ctypes.sizeof(self.module_list) == d.location.data_size
    576       elif d.stream_type == MD_MEMORY_LIST_STREAM:
    577         print >>sys.stderr, "Warning: This is not a full minidump!"
    578         assert self.memory_list is None
    579         self.memory_list = MINIDUMP_MEMORY_LIST.Read(
    580           self.minidump, d.location.rva)
    581         assert ctypes.sizeof(self.memory_list) == d.location.data_size
    582         DebugPrint(self.memory_list)
    583       elif d.stream_type == MD_MEMORY_64_LIST_STREAM:
    584         assert self.memory_list64 is None
    585         self.memory_list64 = MINIDUMP_MEMORY_LIST64.Read(
    586           self.minidump, d.location.rva)
    587         assert ctypes.sizeof(self.memory_list64) == d.location.data_size
    588         DebugPrint(self.memory_list64)
    589 
    590   def IsValidAddress(self, address):
    591     return self.FindLocation(address) is not None
    592 
    593   def ReadU8(self, address):
    594     location = self.FindLocation(address)
    595     return ctypes.c_uint8.from_buffer(self.minidump, location).value
    596 
    597   def ReadU32(self, address):
    598     location = self.FindLocation(address)
    599     return ctypes.c_uint32.from_buffer(self.minidump, location).value
    600 
    601   def ReadU64(self, address):
    602     location = self.FindLocation(address)
    603     return ctypes.c_uint64.from_buffer(self.minidump, location).value
    604 
    605   def ReadUIntPtr(self, address):
    606     if self.arch == MD_CPU_ARCHITECTURE_AMD64:
    607       return self.ReadU64(address)
    608     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
    609       return self.ReadU32(address)
    610     elif self.arch == MD_CPU_ARCHITECTURE_X86:
    611       return self.ReadU32(address)
    612 
    613   def ReadBytes(self, address, size):
    614     location = self.FindLocation(address)
    615     return self.minidump[location:location + size]
    616 
    617   def _ReadWord(self, location):
    618     if self.arch == MD_CPU_ARCHITECTURE_AMD64:
    619       return ctypes.c_uint64.from_buffer(self.minidump, location).value
    620     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
    621       return ctypes.c_uint32.from_buffer(self.minidump, location).value
    622     elif self.arch == MD_CPU_ARCHITECTURE_X86:
    623       return ctypes.c_uint32.from_buffer(self.minidump, location).value
    624 
    625   def IsProbableASCIIRegion(self, location, length):
    626     ascii_bytes = 0
    627     non_ascii_bytes = 0
    628     for loc in xrange(location, location + length):
    629       byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
    630       if byte >= 0x7f:
    631         non_ascii_bytes += 1
    632       if byte < 0x20 and byte != 0:
    633         non_ascii_bytes += 1
    634       if byte < 0x7f and byte >= 0x20:
    635         ascii_bytes += 1
    636       if byte == 0xa:  # newline
    637         ascii_bytes += 1
    638     if ascii_bytes * 10 <= length:
    639       return False
    640     if length > 0 and ascii_bytes > non_ascii_bytes * 7:
    641       return True
    642     if ascii_bytes > non_ascii_bytes * 3:
    643       return None  # Maybe
    644     return False
    645 
    646   def IsProbableExecutableRegion(self, location, length):
    647     opcode_bytes = 0
    648     sixty_four = self.arch == MD_CPU_ARCHITECTURE_AMD64
    649     for loc in xrange(location, location + length):
    650       byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
    651       if (byte == 0x8b or           # mov
    652           byte == 0x89 or           # mov reg-reg
    653           (byte & 0xf0) == 0x50 or  # push/pop
    654           (sixty_four and (byte & 0xf0) == 0x40) or  # rex prefix
    655           byte == 0xc3 or           # return
    656           byte == 0x74 or           # jeq
    657           byte == 0x84 or           # jeq far
    658           byte == 0x75 or           # jne
    659           byte == 0x85 or           # jne far
    660           byte == 0xe8 or           # call
    661           byte == 0xe9 or           # jmp far
    662           byte == 0xeb):            # jmp near
    663         opcode_bytes += 1
    664     opcode_percent = (opcode_bytes * 100) / length
    665     threshold = 20
    666     if opcode_percent > threshold + 2:
    667       return True
    668     if opcode_percent > threshold - 2:
    669       return None  # Maybe
    670     return False
    671 
    672   def FindRegion(self, addr):
    673     answer = [-1, -1]
    674     def is_in(reader, start, size, location):
    675       if addr >= start and addr < start + size:
    676         answer[0] = start
    677         answer[1] = size
    678     self.ForEachMemoryRegion(is_in)
    679     if answer[0] == -1:
    680       return None
    681     return answer
    682 
    683   def ForEachMemoryRegion(self, cb):
    684     if self.memory_list64 is not None:
    685       for r in self.memory_list64.ranges:
    686         location = self.memory_list64.base_rva + offset
    687         cb(self, r.start, r.size, location)
    688         offset += r.size
    689 
    690     if self.memory_list is not None:
    691       for r in self.memory_list.ranges:
    692         cb(self, r.start, r.memory.data_size, r.memory.rva)
    693 
    694   def FindWord(self, word, alignment=0):
    695     def search_inside_region(reader, start, size, location):
    696       location = (location + alignment) & ~alignment
    697       for loc in xrange(location, location + size - self.PointerSize()):
    698         if reader._ReadWord(loc) == word:
    699           slot = start + (loc - location)
    700           print "%s: %s" % (reader.FormatIntPtr(slot),
    701                             reader.FormatIntPtr(word))
    702     self.ForEachMemoryRegion(search_inside_region)
    703 
    704   def FindLocation(self, address):
    705     offset = 0
    706     if self.memory_list64 is not None:
    707       for r in self.memory_list64.ranges:
    708         if r.start <= address < r.start + r.size:
    709           return self.memory_list64.base_rva + offset + address - r.start
    710         offset += r.size
    711     if self.memory_list is not None:
    712       for r in self.memory_list.ranges:
    713         if r.start <= address < r.start + r.memory.data_size:
    714           return r.memory.rva + address - r.start
    715     return None
    716 
    717   def GetDisasmLines(self, address, size):
    718     def CountUndefinedInstructions(lines):
    719       pattern = "<UNDEFINED>"
    720       return sum([line.count(pattern) for (ignore, line) in lines])
    721 
    722     location = self.FindLocation(address)
    723     if location is None: return []
    724     arch = None
    725     possible_objdump_flags = [""]
    726     if self.arch == MD_CPU_ARCHITECTURE_X86:
    727       arch = "ia32"
    728     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
    729       arch = "arm"
    730       possible_objdump_flags = ["", "--disassembler-options=force-thumb"]
    731     elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
    732       arch = "x64"
    733     results = [ disasm.GetDisasmLines(self.minidump_name,
    734                                      location,
    735                                      size,
    736                                      arch,
    737                                      False,
    738                                      objdump_flags)
    739                 for objdump_flags in possible_objdump_flags ]
    740     return min(results, key=CountUndefinedInstructions)
    741 
    742 
    743   def Dispose(self):
    744     self.minidump.close()
    745     self.minidump_file.close()
    746 
    747   def ExceptionIP(self):
    748     if self.arch == MD_CPU_ARCHITECTURE_AMD64:
    749       return self.exception_context.rip
    750     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
    751       return self.exception_context.pc
    752     elif self.arch == MD_CPU_ARCHITECTURE_X86:
    753       return self.exception_context.eip
    754 
    755   def ExceptionSP(self):
    756     if self.arch == MD_CPU_ARCHITECTURE_AMD64:
    757       return self.exception_context.rsp
    758     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
    759       return self.exception_context.sp
    760     elif self.arch == MD_CPU_ARCHITECTURE_X86:
    761       return self.exception_context.esp
    762 
    763   def ExceptionFP(self):
    764     if self.arch == MD_CPU_ARCHITECTURE_AMD64:
    765       return self.exception_context.rbp
    766     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
    767       return None
    768     elif self.arch == MD_CPU_ARCHITECTURE_X86:
    769       return self.exception_context.ebp
    770 
    771   def FormatIntPtr(self, value):
    772     if self.arch == MD_CPU_ARCHITECTURE_AMD64:
    773       return "%016x" % value
    774     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
    775       return "%08x" % value
    776     elif self.arch == MD_CPU_ARCHITECTURE_X86:
    777       return "%08x" % value
    778 
    779   def PointerSize(self):
    780     if self.arch == MD_CPU_ARCHITECTURE_AMD64:
    781       return 8
    782     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
    783       return 4
    784     elif self.arch == MD_CPU_ARCHITECTURE_X86:
    785       return 4
    786 
    787   def Register(self, name):
    788     return self.exception_context.__getattribute__(name)
    789 
    790   def ReadMinidumpString(self, rva):
    791     string = bytearray(MINIDUMP_STRING.Read(self.minidump, rva).buffer)
    792     string = string.decode("utf16")
    793     return string[0:len(string) - 1]
    794 
    795   # Load FUNC records from a BreakPad symbol file
    796   #
    797   #    http://code.google.com/p/google-breakpad/wiki/SymbolFiles
    798   #
    799   def _LoadSymbolsFrom(self, symfile, baseaddr):
    800     print "Loading symbols from %s" % (symfile)
    801     funcs = []
    802     with open(symfile) as f:
    803       for line in f:
    804         result = re.match(
    805             r"^FUNC ([a-f0-9]+) ([a-f0-9]+) ([a-f0-9]+) (.*)$", line)
    806         if result is not None:
    807           start = int(result.group(1), 16)
    808           size = int(result.group(2), 16)
    809           name = result.group(4).rstrip()
    810           bisect.insort_left(self.symbols,
    811                              FuncSymbol(baseaddr + start, size, name))
    812     print " ... done"
    813 
    814   def TryLoadSymbolsFor(self, modulename, module):
    815     try:
    816       symfile = os.path.join(self.symdir,
    817                              modulename.replace('.', '_') + ".pdb.sym")
    818       if os.path.isfile(symfile):
    819         self._LoadSymbolsFrom(symfile, module.base_of_image)
    820         self.modules_with_symbols.append(module)
    821     except Exception as e:
    822       print "  ... failure (%s)" % (e)
    823 
    824   # Returns true if address is covered by some module that has loaded symbols.
    825   def _IsInModuleWithSymbols(self, addr):
    826     for module in self.modules_with_symbols:
    827       start = module.base_of_image
    828       end = start + module.size_of_image
    829       if (start <= addr) and (addr < end):
    830         return True
    831     return False
    832 
    833   # Find symbol covering the given address and return its name in format
    834   #     <symbol name>+<offset from the start>
    835   def FindSymbol(self, addr):
    836     if not self._IsInModuleWithSymbols(addr):
    837       return None
    838 
    839     i = bisect.bisect_left(self.symbols, addr)
    840     symbol = None
    841     if (0 < i) and self.symbols[i - 1].Covers(addr):
    842       symbol = self.symbols[i - 1]
    843     elif (i < len(self.symbols)) and self.symbols[i].Covers(addr):
    844       symbol = self.symbols[i]
    845     else:
    846       return None
    847     diff = addr - symbol.start
    848     return "%s+0x%x" % (symbol.name, diff)
    849 
    850 
    851 class Printer(object):
    852   """Printer with indentation support."""
    853 
    854   def __init__(self):
    855     self.indent = 0
    856 
    857   def Indent(self):
    858     self.indent += 2
    859 
    860   def Dedent(self):
    861     self.indent -= 2
    862 
    863   def Print(self, string):
    864     print "%s%s" % (self._IndentString(), string)
    865 
    866   def PrintLines(self, lines):
    867     indent = self._IndentString()
    868     print "\n".join("%s%s" % (indent, line) for line in lines)
    869 
    870   def _IndentString(self):
    871     return self.indent * " "
    872 
    873 
    874 ADDRESS_RE = re.compile(r"0x[0-9a-fA-F]+")
    875 
    876 
    877 def FormatDisasmLine(start, heap, line):
    878   line_address = start + line[0]
    879   stack_slot = heap.stack_map.get(line_address)
    880   marker = "  "
    881   if stack_slot:
    882     marker = "=>"
    883   code = AnnotateAddresses(heap, line[1])
    884   return "%s%08x %08x: %s" % (marker, line_address, line[0], code)
    885 
    886 
    887 def AnnotateAddresses(heap, line):
    888   extra = []
    889   for m in ADDRESS_RE.finditer(line):
    890     maybe_address = int(m.group(0), 16)
    891     object = heap.FindObject(maybe_address)
    892     if not object: continue
    893     extra.append(str(object))
    894   if len(extra) == 0: return line
    895   return "%s  ;; %s" % (line, ", ".join(extra))
    896 
    897 
    898 class HeapObject(object):
    899   def __init__(self, heap, map, address):
    900     self.heap = heap
    901     self.map = map
    902     self.address = address
    903 
    904   def Is(self, cls):
    905     return isinstance(self, cls)
    906 
    907   def Print(self, p):
    908     p.Print(str(self))
    909 
    910   def __str__(self):
    911     return "HeapObject(%s, %s)" % (self.heap.reader.FormatIntPtr(self.address),
    912                                    INSTANCE_TYPES[self.map.instance_type])
    913 
    914   def ObjectField(self, offset):
    915     field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
    916     return self.heap.FindObjectOrSmi(field_value)
    917 
    918   def SmiField(self, offset):
    919     field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
    920     assert (field_value & 1) == 0
    921     return field_value / 2
    922 
    923 
    924 class Map(HeapObject):
    925   def Decode(self, offset, size, value):
    926     return (value >> offset) & ((1 << size) - 1)
    927 
    928   # Instance Sizes
    929   def InstanceSizesOffset(self):
    930     return self.heap.PointerSize()
    931 
    932   def InstanceSizeOffset(self):
    933     return self.InstanceSizesOffset()
    934 
    935   def InObjectProperties(self):
    936     return self.InstanceSizeOffset() + 1
    937 
    938   def PreAllocatedPropertyFields(self):
    939     return self.InObjectProperties() + 1
    940 
    941   def VisitorId(self):
    942     return self.PreAllocatedPropertyFields() + 1
    943 
    944   # Instance Attributes
    945   def InstanceAttributesOffset(self):
    946     return self.InstanceSizesOffset() + self.heap.IntSize()
    947 
    948   def InstanceTypeOffset(self):
    949     return self.InstanceAttributesOffset()
    950 
    951   def UnusedPropertyFieldsOffset(self):
    952     return self.InstanceTypeOffset() + 1
    953 
    954   def BitFieldOffset(self):
    955     return self.UnusedPropertyFieldsOffset() + 1
    956 
    957   def BitField2Offset(self):
    958     return self.BitFieldOffset() + 1
    959 
    960   # Other fields
    961   def PrototypeOffset(self):
    962     return self.InstanceAttributesOffset() + self.heap.IntSize()
    963 
    964   def ConstructorOffset(self):
    965     return self.PrototypeOffset() + self.heap.PointerSize()
    966 
    967   def TransitionsOrBackPointerOffset(self):
    968     return self.ConstructorOffset() + self.heap.PointerSize()
    969 
    970   def DescriptorsOffset(self):
    971     return self.TransitionsOrBackPointerOffset() + self.heap.PointerSize()
    972 
    973   def CodeCacheOffset(self):
    974     return self.DescriptorsOffset() + self.heap.PointerSize()
    975 
    976   def DependentCodeOffset(self):
    977     return self.CodeCacheOffset() + self.heap.PointerSize()
    978 
    979   def BitField3Offset(self):
    980     return self.DependentCodeOffset() + self.heap.PointerSize()
    981 
    982   def ReadByte(self, offset):
    983     return self.heap.reader.ReadU8(self.address + offset)
    984 
    985   def Print(self, p):
    986     p.Print("Map(%08x)" % (self.address))
    987     p.Print("- size: %d, inobject: %d, preallocated: %d, visitor: %d" % (
    988         self.ReadByte(self.InstanceSizeOffset()),
    989         self.ReadByte(self.InObjectProperties()),
    990         self.ReadByte(self.PreAllocatedPropertyFields()),
    991         self.VisitorId()))
    992 
    993     bitfield = self.ReadByte(self.BitFieldOffset())
    994     bitfield2 = self.ReadByte(self.BitField2Offset())
    995     p.Print("- %s, unused: %d, bf: %d, bf2: %d" % (
    996         INSTANCE_TYPES[self.ReadByte(self.InstanceTypeOffset())],
    997         self.ReadByte(self.UnusedPropertyFieldsOffset()),
    998         bitfield, bitfield2))
    999 
   1000     p.Print("- kind: %s" % (self.Decode(3, 5, bitfield2)))
   1001 
   1002     bitfield3 = self.ObjectField(self.BitField3Offset())
   1003     p.Print(
   1004         "- EnumLength: %d NumberOfOwnDescriptors: %d OwnsDescriptors: %s" % (
   1005             self.Decode(0, 11, bitfield3),
   1006             self.Decode(11, 11, bitfield3),
   1007             self.Decode(25, 1, bitfield3)))
   1008     p.Print("- IsShared: %s" % (self.Decode(22, 1, bitfield3)))
   1009     p.Print("- FunctionWithPrototype: %s" % (self.Decode(23, 1, bitfield3)))
   1010     p.Print("- DictionaryMap: %s" % (self.Decode(24, 1, bitfield3)))
   1011 
   1012     descriptors = self.ObjectField(self.DescriptorsOffset())
   1013     if descriptors.__class__ == FixedArray:
   1014       DescriptorArray(descriptors).Print(p)
   1015     else:
   1016       p.Print("Descriptors: %s" % (descriptors))
   1017 
   1018     transitions = self.ObjectField(self.TransitionsOrBackPointerOffset())
   1019     if transitions.__class__ == FixedArray:
   1020       TransitionArray(transitions).Print(p)
   1021     else:
   1022       p.Print("TransitionsOrBackPointer: %s" % (transitions))
   1023 
   1024   def __init__(self, heap, map, address):
   1025     HeapObject.__init__(self, heap, map, address)
   1026     self.instance_type = \
   1027         heap.reader.ReadU8(self.address + self.InstanceTypeOffset())
   1028 
   1029 
   1030 class String(HeapObject):
   1031   def LengthOffset(self):
   1032     # First word after the map is the hash, the second is the length.
   1033     return self.heap.PointerSize() * 2
   1034 
   1035   def __init__(self, heap, map, address):
   1036     HeapObject.__init__(self, heap, map, address)
   1037     self.length = self.SmiField(self.LengthOffset())
   1038 
   1039   def GetChars(self):
   1040     return "?string?"
   1041 
   1042   def Print(self, p):
   1043     p.Print(str(self))
   1044 
   1045   def __str__(self):
   1046     return "\"%s\"" % self.GetChars()
   1047 
   1048 
   1049 class SeqString(String):
   1050   def CharsOffset(self):
   1051     return self.heap.PointerSize() * 3
   1052 
   1053   def __init__(self, heap, map, address):
   1054     String.__init__(self, heap, map, address)
   1055     self.chars = heap.reader.ReadBytes(self.address + self.CharsOffset(),
   1056                                        self.length)
   1057 
   1058   def GetChars(self):
   1059     return self.chars
   1060 
   1061 
   1062 class ExternalString(String):
   1063   # TODO(vegorov) fix ExternalString for X64 architecture
   1064   RESOURCE_OFFSET = 12
   1065 
   1066   WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
   1067   WEBKIT_STRING_IMPL_CHARS_OFFSET = 8
   1068 
   1069   def __init__(self, heap, map, address):
   1070     String.__init__(self, heap, map, address)
   1071     reader = heap.reader
   1072     self.resource = \
   1073         reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET)
   1074     self.chars = "?external string?"
   1075     if not reader.IsValidAddress(self.resource): return
   1076     string_impl_address = self.resource + \
   1077         ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET
   1078     if not reader.IsValidAddress(string_impl_address): return
   1079     string_impl = reader.ReadU32(string_impl_address)
   1080     chars_ptr_address = string_impl + \
   1081         ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET
   1082     if not reader.IsValidAddress(chars_ptr_address): return
   1083     chars_ptr = reader.ReadU32(chars_ptr_address)
   1084     if not reader.IsValidAddress(chars_ptr): return
   1085     raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length)
   1086     self.chars = codecs.getdecoder("utf16")(raw_chars)[0]
   1087 
   1088   def GetChars(self):
   1089     return self.chars
   1090 
   1091 
   1092 class ConsString(String):
   1093   def LeftOffset(self):
   1094     return self.heap.PointerSize() * 3
   1095 
   1096   def RightOffset(self):
   1097     return self.heap.PointerSize() * 4
   1098 
   1099   def __init__(self, heap, map, address):
   1100     String.__init__(self, heap, map, address)
   1101     self.left = self.ObjectField(self.LeftOffset())
   1102     self.right = self.ObjectField(self.RightOffset())
   1103 
   1104   def GetChars(self):
   1105     try:
   1106       return self.left.GetChars() + self.right.GetChars()
   1107     except:
   1108       return "***CAUGHT EXCEPTION IN GROKDUMP***"
   1109 
   1110 
   1111 class Oddball(HeapObject):
   1112   # Should match declarations in objects.h
   1113   KINDS = [
   1114     "False",
   1115     "True",
   1116     "TheHole",
   1117     "Null",
   1118     "ArgumentMarker",
   1119     "Undefined",
   1120     "Other"
   1121   ]
   1122 
   1123   def ToStringOffset(self):
   1124     return self.heap.PointerSize()
   1125 
   1126   def ToNumberOffset(self):
   1127     return self.ToStringOffset() + self.heap.PointerSize()
   1128 
   1129   def KindOffset(self):
   1130     return self.ToNumberOffset() + self.heap.PointerSize()
   1131 
   1132   def __init__(self, heap, map, address):
   1133     HeapObject.__init__(self, heap, map, address)
   1134     self.to_string = self.ObjectField(self.ToStringOffset())
   1135     self.kind = self.SmiField(self.KindOffset())
   1136 
   1137   def Print(self, p):
   1138     p.Print(str(self))
   1139 
   1140   def __str__(self):
   1141     if self.to_string:
   1142       return "Oddball(%08x, <%s>)" % (self.address, str(self.to_string))
   1143     else:
   1144       kind = "???"
   1145       if 0 <= self.kind < len(Oddball.KINDS):
   1146         kind = Oddball.KINDS[self.kind]
   1147       return "Oddball(%08x, kind=%s)" % (self.address, kind)
   1148 
   1149 
   1150 class FixedArray(HeapObject):
   1151   def LengthOffset(self):
   1152     return self.heap.PointerSize()
   1153 
   1154   def ElementsOffset(self):
   1155     return self.heap.PointerSize() * 2
   1156 
   1157   def MemberOffset(self, i):
   1158     return self.ElementsOffset() + self.heap.PointerSize() * i
   1159 
   1160   def Get(self, i):
   1161     return self.ObjectField(self.MemberOffset(i))
   1162 
   1163   def __init__(self, heap, map, address):
   1164     HeapObject.__init__(self, heap, map, address)
   1165     self.length = self.SmiField(self.LengthOffset())
   1166 
   1167   def Print(self, p):
   1168     p.Print("FixedArray(%s) {" % self.heap.reader.FormatIntPtr(self.address))
   1169     p.Indent()
   1170     p.Print("length: %d" % self.length)
   1171     base_offset = self.ElementsOffset()
   1172     for i in xrange(self.length):
   1173       offset = base_offset + 4 * i
   1174       try:
   1175         p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
   1176       except TypeError:
   1177         p.Dedent()
   1178         p.Print("...")
   1179         p.Print("}")
   1180         return
   1181     p.Dedent()
   1182     p.Print("}")
   1183 
   1184   def __str__(self):
   1185     return "FixedArray(%08x, length=%d)" % (self.address, self.length)
   1186 
   1187 
   1188 class DescriptorArray(object):
   1189   def __init__(self, array):
   1190     self.array = array
   1191 
   1192   def Length(self):
   1193     return self.array.Get(0)
   1194 
   1195   def Decode(self, offset, size, value):
   1196     return (value >> offset) & ((1 << size) - 1)
   1197 
   1198   TYPES = [
   1199       "normal",
   1200       "field",
   1201       "function",
   1202       "callbacks"
   1203   ]
   1204 
   1205   def Type(self, value):
   1206     return DescriptorArray.TYPES[self.Decode(0, 3, value)]
   1207 
   1208   def Attributes(self, value):
   1209     attributes = self.Decode(3, 3, value)
   1210     result = []
   1211     if (attributes & 0): result += ["ReadOnly"]
   1212     if (attributes & 1): result += ["DontEnum"]
   1213     if (attributes & 2): result += ["DontDelete"]
   1214     return "[" + (",".join(result)) + "]"
   1215 
   1216   def Deleted(self, value):
   1217     return self.Decode(6, 1, value) == 1
   1218 
   1219   def FieldIndex(self, value):
   1220     return self.Decode(20, 11, value)
   1221 
   1222   def Pointer(self, value):
   1223     return self.Decode(6, 11, value)
   1224 
   1225   def Details(self, di, value):
   1226     return (
   1227         di,
   1228         self.Type(value),
   1229         self.Attributes(value),
   1230         self.FieldIndex(value),
   1231         self.Pointer(value)
   1232     )
   1233 
   1234 
   1235   def Print(self, p):
   1236     length = self.Length()
   1237     array = self.array
   1238 
   1239     p.Print("Descriptors(%08x, length=%d)" % (array.address, length))
   1240     p.Print("[et] %s" % (array.Get(1)))
   1241 
   1242     for di in xrange(length):
   1243       i = 2 + di * 3
   1244       p.Print("0x%x" % (array.address + array.MemberOffset(i)))
   1245       p.Print("[%i] name:    %s" % (di, array.Get(i + 0)))
   1246       p.Print("[%i] details: %s %s field-index %i pointer %i" % \
   1247               self.Details(di, array.Get(i + 1)))
   1248       p.Print("[%i] value:   %s" % (di, array.Get(i + 2)))
   1249 
   1250     end = self.array.length // 3
   1251     if length != end:
   1252       p.Print("[%i-%i] slack descriptors" % (length, end))
   1253 
   1254 
   1255 class TransitionArray(object):
   1256   def __init__(self, array):
   1257     self.array = array
   1258 
   1259   def IsSimpleTransition(self):
   1260     return self.array.length <= 2
   1261 
   1262   def Length(self):
   1263     # SimpleTransition cases
   1264     if self.IsSimpleTransition():
   1265       return self.array.length - 1
   1266     return (self.array.length - 3) // 2
   1267 
   1268   def Print(self, p):
   1269     length = self.Length()
   1270     array = self.array
   1271 
   1272     p.Print("Transitions(%08x, length=%d)" % (array.address, length))
   1273     p.Print("[backpointer] %s" % (array.Get(0)))
   1274     if self.IsSimpleTransition():
   1275       if length == 1:
   1276         p.Print("[simple target] %s" % (array.Get(1)))
   1277       return
   1278 
   1279     elements = array.Get(1)
   1280     if elements is not None:
   1281       p.Print("[elements   ] %s" % (elements))
   1282 
   1283     prototype = array.Get(2)
   1284     if prototype is not None:
   1285       p.Print("[prototype  ] %s" % (prototype))
   1286 
   1287     for di in xrange(length):
   1288       i = 3 + di * 2
   1289       p.Print("[%i] symbol: %s" % (di, array.Get(i + 0)))
   1290       p.Print("[%i] target: %s" % (di, array.Get(i + 1)))
   1291 
   1292 
   1293 class JSFunction(HeapObject):
   1294   def CodeEntryOffset(self):
   1295     return 3 * self.heap.PointerSize()
   1296 
   1297   def SharedOffset(self):
   1298     return 5 * self.heap.PointerSize()
   1299 
   1300   def __init__(self, heap, map, address):
   1301     HeapObject.__init__(self, heap, map, address)
   1302     code_entry = \
   1303         heap.reader.ReadU32(self.address + self.CodeEntryOffset())
   1304     self.code = heap.FindObject(code_entry - Code.HeaderSize(heap) + 1)
   1305     self.shared = self.ObjectField(self.SharedOffset())
   1306 
   1307   def Print(self, p):
   1308     source = "\n".join("  %s" % line for line in self._GetSource().split("\n"))
   1309     p.Print("JSFunction(%s) {" % self.heap.reader.FormatIntPtr(self.address))
   1310     p.Indent()
   1311     p.Print("inferred name: %s" % self.shared.inferred_name)
   1312     if self.shared.script.Is(Script) and self.shared.script.name.Is(String):
   1313       p.Print("script name: %s" % self.shared.script.name)
   1314     p.Print("source:")
   1315     p.PrintLines(self._GetSource().split("\n"))
   1316     p.Print("code:")
   1317     self.code.Print(p)
   1318     if self.code != self.shared.code:
   1319       p.Print("unoptimized code:")
   1320       self.shared.code.Print(p)
   1321     p.Dedent()
   1322     p.Print("}")
   1323 
   1324   def __str__(self):
   1325     inferred_name = ""
   1326     if self.shared.Is(SharedFunctionInfo):
   1327       inferred_name = self.shared.inferred_name
   1328     return "JSFunction(%s, %s)" % \
   1329           (self.heap.reader.FormatIntPtr(self.address), inferred_name)
   1330 
   1331   def _GetSource(self):
   1332     source = "?source?"
   1333     start = self.shared.start_position
   1334     end = self.shared.end_position
   1335     if not self.shared.script.Is(Script): return source
   1336     script_source = self.shared.script.source
   1337     if not script_source.Is(String): return source
   1338     return script_source.GetChars()[start:end]
   1339 
   1340 
   1341 class SharedFunctionInfo(HeapObject):
   1342   def CodeOffset(self):
   1343     return 2 * self.heap.PointerSize()
   1344 
   1345   def ScriptOffset(self):
   1346     return 7 * self.heap.PointerSize()
   1347 
   1348   def InferredNameOffset(self):
   1349     return 9 * self.heap.PointerSize()
   1350 
   1351   def EndPositionOffset(self):
   1352     return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize()
   1353 
   1354   def StartPositionAndTypeOffset(self):
   1355     return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize()
   1356 
   1357   def __init__(self, heap, map, address):
   1358     HeapObject.__init__(self, heap, map, address)
   1359     self.code = self.ObjectField(self.CodeOffset())
   1360     self.script = self.ObjectField(self.ScriptOffset())
   1361     self.inferred_name = self.ObjectField(self.InferredNameOffset())
   1362     if heap.PointerSize() == 8:
   1363       start_position_and_type = \
   1364           heap.reader.ReadU32(self.StartPositionAndTypeOffset())
   1365       self.start_position = start_position_and_type >> 2
   1366       pseudo_smi_end_position = \
   1367           heap.reader.ReadU32(self.EndPositionOffset())
   1368       self.end_position = pseudo_smi_end_position >> 2
   1369     else:
   1370       start_position_and_type = \
   1371           self.SmiField(self.StartPositionAndTypeOffset())
   1372       self.start_position = start_position_and_type >> 2
   1373       self.end_position = \
   1374           self.SmiField(self.EndPositionOffset())
   1375 
   1376 
   1377 class Script(HeapObject):
   1378   def SourceOffset(self):
   1379     return self.heap.PointerSize()
   1380 
   1381   def NameOffset(self):
   1382     return self.SourceOffset() + self.heap.PointerSize()
   1383 
   1384   def __init__(self, heap, map, address):
   1385     HeapObject.__init__(self, heap, map, address)
   1386     self.source = self.ObjectField(self.SourceOffset())
   1387     self.name = self.ObjectField(self.NameOffset())
   1388 
   1389 
   1390 class CodeCache(HeapObject):
   1391   def DefaultCacheOffset(self):
   1392     return self.heap.PointerSize()
   1393 
   1394   def NormalTypeCacheOffset(self):
   1395     return self.DefaultCacheOffset() + self.heap.PointerSize()
   1396 
   1397   def __init__(self, heap, map, address):
   1398     HeapObject.__init__(self, heap, map, address)
   1399     self.default_cache = self.ObjectField(self.DefaultCacheOffset())
   1400     self.normal_type_cache = self.ObjectField(self.NormalTypeCacheOffset())
   1401 
   1402   def Print(self, p):
   1403     p.Print("CodeCache(%s) {" % self.heap.reader.FormatIntPtr(self.address))
   1404     p.Indent()
   1405     p.Print("default cache: %s" % self.default_cache)
   1406     p.Print("normal type cache: %s" % self.normal_type_cache)
   1407     p.Dedent()
   1408     p.Print("}")
   1409 
   1410 
   1411 class Code(HeapObject):
   1412   CODE_ALIGNMENT_MASK = (1 << 5) - 1
   1413 
   1414   def InstructionSizeOffset(self):
   1415     return self.heap.PointerSize()
   1416 
   1417   @staticmethod
   1418   def HeaderSize(heap):
   1419     return (heap.PointerSize() + heap.IntSize() + \
   1420         4 * heap.PointerSize() + 3 * heap.IntSize() + \
   1421         Code.CODE_ALIGNMENT_MASK) & ~Code.CODE_ALIGNMENT_MASK
   1422 
   1423   def __init__(self, heap, map, address):
   1424     HeapObject.__init__(self, heap, map, address)
   1425     self.entry = self.address + Code.HeaderSize(heap)
   1426     self.instruction_size = \
   1427         heap.reader.ReadU32(self.address + self.InstructionSizeOffset())
   1428 
   1429   def Print(self, p):
   1430     lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
   1431     p.Print("Code(%s) {" % self.heap.reader.FormatIntPtr(self.address))
   1432     p.Indent()
   1433     p.Print("instruction_size: %d" % self.instruction_size)
   1434     p.PrintLines(self._FormatLine(line) for line in lines)
   1435     p.Dedent()
   1436     p.Print("}")
   1437 
   1438   def _FormatLine(self, line):
   1439     return FormatDisasmLine(self.entry, self.heap, line)
   1440 
   1441 
   1442 class V8Heap(object):
   1443   CLASS_MAP = {
   1444     "SYMBOL_TYPE": SeqString,
   1445     "ASCII_SYMBOL_TYPE": SeqString,
   1446     "CONS_SYMBOL_TYPE": ConsString,
   1447     "CONS_ASCII_SYMBOL_TYPE": ConsString,
   1448     "EXTERNAL_SYMBOL_TYPE": ExternalString,
   1449     "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString,
   1450     "EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString,
   1451     "SHORT_EXTERNAL_SYMBOL_TYPE": ExternalString,
   1452     "SHORT_EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString,
   1453     "SHORT_EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString,
   1454     "STRING_TYPE": SeqString,
   1455     "ASCII_STRING_TYPE": SeqString,
   1456     "CONS_STRING_TYPE": ConsString,
   1457     "CONS_ASCII_STRING_TYPE": ConsString,
   1458     "EXTERNAL_STRING_TYPE": ExternalString,
   1459     "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE": ExternalString,
   1460     "EXTERNAL_ASCII_STRING_TYPE": ExternalString,
   1461     "MAP_TYPE": Map,
   1462     "ODDBALL_TYPE": Oddball,
   1463     "FIXED_ARRAY_TYPE": FixedArray,
   1464     "JS_FUNCTION_TYPE": JSFunction,
   1465     "SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo,
   1466     "SCRIPT_TYPE": Script,
   1467     "CODE_CACHE_TYPE": CodeCache,
   1468     "CODE_TYPE": Code,
   1469   }
   1470 
   1471   def __init__(self, reader, stack_map):
   1472     self.reader = reader
   1473     self.stack_map = stack_map
   1474     self.objects = {}
   1475 
   1476   def FindObjectOrSmi(self, tagged_address):
   1477     if (tagged_address & 1) == 0: return tagged_address / 2
   1478     return self.FindObject(tagged_address)
   1479 
   1480   def FindObject(self, tagged_address):
   1481     if tagged_address in self.objects:
   1482       return self.objects[tagged_address]
   1483     if (tagged_address & self.ObjectAlignmentMask()) != 1: return None
   1484     address = tagged_address - 1
   1485     if not self.reader.IsValidAddress(address): return None
   1486     map_tagged_address = self.reader.ReadUIntPtr(address)
   1487     if tagged_address == map_tagged_address:
   1488       # Meta map?
   1489       meta_map = Map(self, None, address)
   1490       instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type)
   1491       if instance_type_name != "MAP_TYPE": return None
   1492       meta_map.map = meta_map
   1493       object = meta_map
   1494     else:
   1495       map = self.FindMap(map_tagged_address)
   1496       if map is None: return None
   1497       instance_type_name = INSTANCE_TYPES.get(map.instance_type)
   1498       if instance_type_name is None: return None
   1499       cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
   1500       object = cls(self, map, address)
   1501     self.objects[tagged_address] = object
   1502     return object
   1503 
   1504   def FindMap(self, tagged_address):
   1505     if (tagged_address & self.MapAlignmentMask()) != 1: return None
   1506     address = tagged_address - 1
   1507     if not self.reader.IsValidAddress(address): return None
   1508     object = Map(self, None, address)
   1509     return object
   1510 
   1511   def IntSize(self):
   1512     return 4
   1513 
   1514   def PointerSize(self):
   1515     return self.reader.PointerSize()
   1516 
   1517   def ObjectAlignmentMask(self):
   1518     return self.PointerSize() - 1
   1519 
   1520   def MapAlignmentMask(self):
   1521     if self.reader.arch == MD_CPU_ARCHITECTURE_AMD64:
   1522       return (1 << 4) - 1
   1523     elif self.reader.arch == MD_CPU_ARCHITECTURE_ARM:
   1524       return (1 << 4) - 1
   1525     elif self.reader.arch == MD_CPU_ARCHITECTURE_X86:
   1526       return (1 << 5) - 1
   1527 
   1528   def PageAlignmentMask(self):
   1529     return (1 << 20) - 1
   1530 
   1531 
   1532 class KnownObject(HeapObject):
   1533   def __init__(self, heap, known_name):
   1534     HeapObject.__init__(self, heap, None, None)
   1535     self.known_name = known_name
   1536 
   1537   def __str__(self):
   1538     return "<%s>" % self.known_name
   1539 
   1540 
   1541 class KnownMap(HeapObject):
   1542   def __init__(self, heap, known_name, instance_type):
   1543     HeapObject.__init__(self, heap, None, None)
   1544     self.instance_type = instance_type
   1545     self.known_name = known_name
   1546 
   1547   def __str__(self):
   1548     return "<%s>" % self.known_name
   1549 
   1550 
   1551 class InspectionPadawan(object):
   1552   """The padawan can improve annotations by sensing well-known objects."""
   1553   def __init__(self, reader, heap):
   1554     self.reader = reader
   1555     self.heap = heap
   1556     self.known_first_map_page = 0
   1557     self.known_first_data_page = 0
   1558     self.known_first_pointer_page = 0
   1559 
   1560   def __getattr__(self, name):
   1561     """An InspectionPadawan can be used instead of V8Heap, even though
   1562        it does not inherit from V8Heap (aka. mixin)."""
   1563     return getattr(self.heap, name)
   1564 
   1565   def GetPageOffset(self, tagged_address):
   1566     return tagged_address & self.heap.PageAlignmentMask()
   1567 
   1568   def IsInKnownMapSpace(self, tagged_address):
   1569     page_address = tagged_address & ~self.heap.PageAlignmentMask()
   1570     return page_address == self.known_first_map_page
   1571 
   1572   def IsInKnownOldSpace(self, tagged_address):
   1573     page_address = tagged_address & ~self.heap.PageAlignmentMask()
   1574     return page_address in [self.known_first_data_page,
   1575                             self.known_first_pointer_page]
   1576 
   1577   def ContainingKnownOldSpaceName(self, tagged_address):
   1578     page_address = tagged_address & ~self.heap.PageAlignmentMask()
   1579     if page_address == self.known_first_data_page: return "OLD_DATA_SPACE"
   1580     if page_address == self.known_first_pointer_page: return "OLD_POINTER_SPACE"
   1581     return None
   1582 
   1583   def SenseObject(self, tagged_address):
   1584     if self.IsInKnownOldSpace(tagged_address):
   1585       offset = self.GetPageOffset(tagged_address)
   1586       lookup_key = (self.ContainingKnownOldSpaceName(tagged_address), offset)
   1587       known_obj_name = KNOWN_OBJECTS.get(lookup_key)
   1588       if known_obj_name:
   1589         return KnownObject(self, known_obj_name)
   1590     if self.IsInKnownMapSpace(tagged_address):
   1591       known_map = self.SenseMap(tagged_address)
   1592       if known_map:
   1593         return known_map
   1594     found_obj = self.heap.FindObject(tagged_address)
   1595     if found_obj: return found_obj
   1596     address = tagged_address - 1
   1597     if self.reader.IsValidAddress(address):
   1598       map_tagged_address = self.reader.ReadUIntPtr(address)
   1599       map = self.SenseMap(map_tagged_address)
   1600       if map is None: return None
   1601       instance_type_name = INSTANCE_TYPES.get(map.instance_type)
   1602       if instance_type_name is None: return None
   1603       cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
   1604       return cls(self, map, address)
   1605     return None
   1606 
   1607   def SenseMap(self, tagged_address):
   1608     if self.IsInKnownMapSpace(tagged_address):
   1609       offset = self.GetPageOffset(tagged_address)
   1610       known_map_info = KNOWN_MAPS.get(offset)
   1611       if known_map_info:
   1612         known_map_type, known_map_name = known_map_info
   1613         return KnownMap(self, known_map_name, known_map_type)
   1614     found_map = self.heap.FindMap(tagged_address)
   1615     if found_map: return found_map
   1616     return None
   1617 
   1618   def FindObjectOrSmi(self, tagged_address):
   1619     """When used as a mixin in place of V8Heap."""
   1620     found_obj = self.SenseObject(tagged_address)
   1621     if found_obj: return found_obj
   1622     if (tagged_address & 1) == 0:
   1623       return "Smi(%d)" % (tagged_address / 2)
   1624     else:
   1625       return "Unknown(%s)" % self.reader.FormatIntPtr(tagged_address)
   1626 
   1627   def FindObject(self, tagged_address):
   1628     """When used as a mixin in place of V8Heap."""
   1629     raise NotImplementedError
   1630 
   1631   def FindMap(self, tagged_address):
   1632     """When used as a mixin in place of V8Heap."""
   1633     raise NotImplementedError
   1634 
   1635   def PrintKnowledge(self):
   1636     print "  known_first_map_page = %s\n"\
   1637           "  known_first_data_page = %s\n"\
   1638           "  known_first_pointer_page = %s" % (
   1639           self.reader.FormatIntPtr(self.known_first_map_page),
   1640           self.reader.FormatIntPtr(self.known_first_data_page),
   1641           self.reader.FormatIntPtr(self.known_first_pointer_page))
   1642 
   1643 
   1644 class InspectionShell(cmd.Cmd):
   1645   def __init__(self, reader, heap):
   1646     cmd.Cmd.__init__(self)
   1647     self.reader = reader
   1648     self.heap = heap
   1649     self.padawan = InspectionPadawan(reader, heap)
   1650     self.prompt = "(grok) "
   1651 
   1652   def do_da(self, address):
   1653     """
   1654      Print ASCII string starting at specified address.
   1655     """
   1656     address = int(address, 16)
   1657     string = ""
   1658     while self.reader.IsValidAddress(address):
   1659       code = self.reader.ReadU8(address)
   1660       if code < 128:
   1661         string += chr(code)
   1662       else:
   1663         break
   1664       address += 1
   1665     if string == "":
   1666       print "Not an ASCII string at %s" % self.reader.FormatIntPtr(address)
   1667     else:
   1668       print "%s\n" % string
   1669 
   1670   def do_dd(self, address):
   1671     """
   1672      Interpret memory at the given address (if available) as a sequence
   1673      of words. Automatic alignment is not performed.
   1674     """
   1675     start = int(address, 16)
   1676     if (start & self.heap.ObjectAlignmentMask()) != 0:
   1677       print "Warning: Dumping un-aligned memory, is this what you had in mind?"
   1678     for slot in xrange(start,
   1679                        start + self.reader.PointerSize() * 10,
   1680                        self.reader.PointerSize()):
   1681       if not self.reader.IsValidAddress(slot):
   1682         print "Address is not contained within the minidump!"
   1683         return
   1684       maybe_address = self.reader.ReadUIntPtr(slot)
   1685       heap_object = self.padawan.SenseObject(maybe_address)
   1686       print "%s: %s %s" % (self.reader.FormatIntPtr(slot),
   1687                            self.reader.FormatIntPtr(maybe_address),
   1688                            heap_object or '')
   1689 
   1690   def do_do(self, address):
   1691     """
   1692      Interpret memory at the given address as a V8 object. Automatic
   1693      alignment makes sure that you can pass tagged as well as un-tagged
   1694      addresses.
   1695     """
   1696     address = int(address, 16)
   1697     if (address & self.heap.ObjectAlignmentMask()) == 0:
   1698       address = address + 1
   1699     elif (address & self.heap.ObjectAlignmentMask()) != 1:
   1700       print "Address doesn't look like a valid pointer!"
   1701       return
   1702     heap_object = self.padawan.SenseObject(address)
   1703     if heap_object:
   1704       heap_object.Print(Printer())
   1705     else:
   1706       print "Address cannot be interpreted as object!"
   1707 
   1708   def do_do_desc(self, address):
   1709     """
   1710       Print a descriptor array in a readable format.
   1711     """
   1712     start = int(address, 16)
   1713     if ((start & 1) == 1): start = start - 1
   1714     DescriptorArray(FixedArray(self.heap, None, start)).Print(Printer())
   1715 
   1716   def do_do_map(self, address):
   1717     """
   1718       Print a descriptor array in a readable format.
   1719     """
   1720     start = int(address, 16)
   1721     if ((start & 1) == 1): start = start - 1
   1722     Map(self.heap, None, start).Print(Printer())
   1723 
   1724   def do_do_trans(self, address):
   1725     """
   1726       Print a transition array in a readable format.
   1727     """
   1728     start = int(address, 16)
   1729     if ((start & 1) == 1): start = start - 1
   1730     TransitionArray(FixedArray(self.heap, None, start)).Print(Printer())
   1731 
   1732   def do_dp(self, address):
   1733     """
   1734      Interpret memory at the given address as being on a V8 heap page
   1735      and print information about the page header (if available).
   1736     """
   1737     address = int(address, 16)
   1738     page_address = address & ~self.heap.PageAlignmentMask()
   1739     if self.reader.IsValidAddress(page_address):
   1740       raise NotImplementedError
   1741     else:
   1742       print "Page header is not available!"
   1743 
   1744   def do_k(self, arguments):
   1745     """
   1746      Teach V8 heap layout information to the inspector. This increases
   1747      the amount of annotations the inspector can produce while dumping
   1748      data. The first page of each heap space is of particular interest
   1749      because it contains known objects that do not move.
   1750     """
   1751     self.padawan.PrintKnowledge()
   1752 
   1753   def do_kd(self, address):
   1754     """
   1755      Teach V8 heap layout information to the inspector. Set the first
   1756      data-space page by passing any pointer into that page.
   1757     """
   1758     address = int(address, 16)
   1759     page_address = address & ~self.heap.PageAlignmentMask()
   1760     self.padawan.known_first_data_page = page_address
   1761 
   1762   def do_km(self, address):
   1763     """
   1764      Teach V8 heap layout information to the inspector. Set the first
   1765      map-space page by passing any pointer into that page.
   1766     """
   1767     address = int(address, 16)
   1768     page_address = address & ~self.heap.PageAlignmentMask()
   1769     self.padawan.known_first_map_page = page_address
   1770 
   1771   def do_kp(self, address):
   1772     """
   1773      Teach V8 heap layout information to the inspector. Set the first
   1774      pointer-space page by passing any pointer into that page.
   1775     """
   1776     address = int(address, 16)
   1777     page_address = address & ~self.heap.PageAlignmentMask()
   1778     self.padawan.known_first_pointer_page = page_address
   1779 
   1780   def do_list(self, smth):
   1781     """
   1782      List all available memory regions.
   1783     """
   1784     def print_region(reader, start, size, location):
   1785       print "  %s - %s (%d bytes)" % (reader.FormatIntPtr(start),
   1786                                       reader.FormatIntPtr(start + size),
   1787                                       size)
   1788     print "Available memory regions:"
   1789     self.reader.ForEachMemoryRegion(print_region)
   1790 
   1791   def do_lm(self, arg):
   1792     """
   1793      List details for all loaded modules in the minidump. An argument can
   1794      be passed to limit the output to only those modules that contain the
   1795      argument as a substring (case insensitive match).
   1796     """
   1797     for module in self.reader.module_list.modules:
   1798       if arg:
   1799         name = GetModuleName(self.reader, module).lower()
   1800         if name.find(arg.lower()) >= 0:
   1801           PrintModuleDetails(self.reader, module)
   1802       else:
   1803         PrintModuleDetails(self.reader, module)
   1804     print
   1805 
   1806   def do_s(self, word):
   1807     """
   1808      Search for a given word in available memory regions. The given word
   1809      is expanded to full pointer size and searched at aligned as well as
   1810      un-aligned memory locations. Use 'sa' to search aligned locations
   1811      only.
   1812     """
   1813     try:
   1814       word = int(word, 0)
   1815     except ValueError:
   1816       print "Malformed word, prefix with '0x' to use hexadecimal format."
   1817       return
   1818     print "Searching for word %d/0x%s:" % (word, self.reader.FormatIntPtr(word))
   1819     self.reader.FindWord(word)
   1820 
   1821   def do_sh(self, none):
   1822     """
   1823      Search for the V8 Heap object in all available memory regions. You
   1824      might get lucky and find this rare treasure full of invaluable
   1825      information.
   1826     """
   1827     raise NotImplementedError
   1828 
   1829   def do_u(self, args):
   1830     """
   1831      Unassemble memory in the region [address, address + size). If the
   1832      size is not specified, a default value of 32 bytes is used.
   1833      Synopsis: u 0x<address> 0x<size>
   1834     """
   1835     args = args.split(' ')
   1836     start = int(args[0], 16)
   1837     size = int(args[1], 16) if len(args) > 1 else 0x20
   1838     if not self.reader.IsValidAddress(start):
   1839       print "Address is not contained within the minidump!"
   1840       return
   1841     lines = self.reader.GetDisasmLines(start, size)
   1842     for line in lines:
   1843       print FormatDisasmLine(start, self.heap, line)
   1844     print
   1845 
   1846   def do_EOF(self, none):
   1847     raise KeyboardInterrupt
   1848 
   1849 EIP_PROXIMITY = 64
   1850 
   1851 CONTEXT_FOR_ARCH = {
   1852     MD_CPU_ARCHITECTURE_AMD64:
   1853       ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip',
   1854        'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15'],
   1855     MD_CPU_ARCHITECTURE_ARM:
   1856       ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9',
   1857        'r10', 'r11', 'r12', 'sp', 'lr', 'pc'],
   1858     MD_CPU_ARCHITECTURE_X86:
   1859       ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip']
   1860 }
   1861 
   1862 KNOWN_MODULES = {'chrome.exe', 'chrome.dll'}
   1863 
   1864 def GetVersionString(ms, ls):
   1865   return "%d.%d.%d.%d" % (ms >> 16, ms & 0xffff, ls >> 16, ls & 0xffff)
   1866 
   1867 
   1868 def GetModuleName(reader, module):
   1869   name = reader.ReadMinidumpString(module.module_name_rva)
   1870   # simplify for path manipulation
   1871   name = name.encode('utf-8')
   1872   return str(os.path.basename(str(name).replace("\\", "/")))
   1873 
   1874 
   1875 def PrintModuleDetails(reader, module):
   1876   print "%s" % GetModuleName(reader, module)
   1877   file_version = GetVersionString(module.version_info.dwFileVersionMS,
   1878                                   module.version_info.dwFileVersionLS)
   1879   product_version = GetVersionString(module.version_info.dwProductVersionMS,
   1880                                      module.version_info.dwProductVersionLS)
   1881   print "  base: %s" % reader.FormatIntPtr(module.base_of_image)
   1882   print "  end: %s" % reader.FormatIntPtr(module.base_of_image +
   1883                                           module.size_of_image)
   1884   print "  file version: %s" % file_version
   1885   print "  product version: %s" % product_version
   1886   time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp)
   1887   print "  timestamp: %s" % time_date_stamp
   1888 
   1889 
   1890 def AnalyzeMinidump(options, minidump_name):
   1891   reader = MinidumpReader(options, minidump_name)
   1892   heap = None
   1893   DebugPrint("========================================")
   1894   if reader.exception is None:
   1895     print "Minidump has no exception info"
   1896   else:
   1897     print "Exception info:"
   1898     exception_thread = reader.thread_map[reader.exception.thread_id]
   1899     print "  thread id: %d" % exception_thread.id
   1900     print "  code: %08X" % reader.exception.exception.code
   1901     print "  context:"
   1902     for r in CONTEXT_FOR_ARCH[reader.arch]:
   1903       print "    %s: %s" % (r, reader.FormatIntPtr(reader.Register(r)))
   1904     # TODO(vitalyr): decode eflags.
   1905     if reader.arch == MD_CPU_ARCHITECTURE_ARM:
   1906       print "    cpsr: %s" % bin(reader.exception_context.cpsr)[2:]
   1907     else:
   1908       print "    eflags: %s" % bin(reader.exception_context.eflags)[2:]
   1909 
   1910     print
   1911     print "  modules:"
   1912     for module in reader.module_list.modules:
   1913       name = GetModuleName(reader, module)
   1914       if name in KNOWN_MODULES:
   1915         print "    %s at %08X" % (name, module.base_of_image)
   1916         reader.TryLoadSymbolsFor(name, module)
   1917     print
   1918 
   1919     stack_top = reader.ExceptionSP()
   1920     stack_bottom = exception_thread.stack.start + \
   1921         exception_thread.stack.memory.data_size
   1922     stack_map = {reader.ExceptionIP(): -1}
   1923     for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
   1924       maybe_address = reader.ReadUIntPtr(slot)
   1925       if not maybe_address in stack_map:
   1926         stack_map[maybe_address] = slot
   1927     heap = V8Heap(reader, stack_map)
   1928 
   1929     print "Disassembly around exception.eip:"
   1930     eip_symbol = reader.FindSymbol(reader.ExceptionIP())
   1931     if eip_symbol is not None:
   1932       print eip_symbol
   1933     disasm_start = reader.ExceptionIP() - EIP_PROXIMITY
   1934     disasm_bytes = 2 * EIP_PROXIMITY
   1935     if (options.full):
   1936       full_range = reader.FindRegion(reader.ExceptionIP())
   1937       if full_range is not None:
   1938         disasm_start = full_range[0]
   1939         disasm_bytes = full_range[1]
   1940 
   1941     lines = reader.GetDisasmLines(disasm_start, disasm_bytes)
   1942 
   1943     for line in lines:
   1944       print FormatDisasmLine(disasm_start, heap, line)
   1945     print
   1946 
   1947   if heap is None:
   1948     heap = V8Heap(reader, None)
   1949 
   1950   if options.full:
   1951     FullDump(reader, heap)
   1952 
   1953   if options.command:
   1954     InspectionShell(reader, heap).onecmd(options.command)
   1955 
   1956   if options.shell:
   1957     try:
   1958       InspectionShell(reader, heap).cmdloop("type help to get help")
   1959     except KeyboardInterrupt:
   1960       print "Kthxbye."
   1961   elif not options.command:
   1962     if reader.exception is not None:
   1963       frame_pointer = reader.ExceptionFP()
   1964       print "Annotated stack (from exception.esp to bottom):"
   1965       for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
   1966         maybe_address = reader.ReadUIntPtr(slot)
   1967         heap_object = heap.FindObject(maybe_address)
   1968         maybe_symbol = reader.FindSymbol(maybe_address)
   1969         if slot == frame_pointer:
   1970           maybe_symbol = "<---- frame pointer"
   1971           frame_pointer = maybe_address
   1972         print "%s: %s %s" % (reader.FormatIntPtr(slot),
   1973                              reader.FormatIntPtr(maybe_address),
   1974                              maybe_symbol or "")
   1975         if heap_object:
   1976           heap_object.Print(Printer())
   1977           print
   1978 
   1979   reader.Dispose()
   1980 
   1981 
   1982 if __name__ == "__main__":
   1983   parser = optparse.OptionParser(USAGE)
   1984   parser.add_option("-s", "--shell", dest="shell", action="store_true",
   1985                     help="start an interactive inspector shell")
   1986   parser.add_option("-c", "--command", dest="command", default="",
   1987                     help="run an interactive inspector shell command and exit")
   1988   parser.add_option("-f", "--full", dest="full", action="store_true",
   1989                     help="dump all information contained in the minidump")
   1990   parser.add_option("--symdir", dest="symdir", default=".",
   1991                     help="directory containing *.pdb.sym file with symbols")
   1992   parser.add_option("--objdump",
   1993                     default="/usr/bin/objdump",
   1994                     help="objdump tool to use [default: %default]")
   1995   options, args = parser.parse_args()
   1996   if os.path.exists(options.objdump):
   1997     disasm.OBJDUMP_BIN = options.objdump
   1998     OBJDUMP_BIN = options.objdump
   1999   else:
   2000     print "Cannot find %s, falling back to default objdump" % options.objdump
   2001   if len(args) != 1:
   2002     parser.print_help()
   2003     sys.exit(1)
   2004   AnalyzeMinidump(options, args[0])
   2005