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     return self.heap.PointerSize()
   1033 
   1034   def __init__(self, heap, map, address):
   1035     HeapObject.__init__(self, heap, map, address)
   1036     self.length = self.SmiField(self.LengthOffset())
   1037 
   1038   def GetChars(self):
   1039     return "?string?"
   1040 
   1041   def Print(self, p):
   1042     p.Print(str(self))
   1043 
   1044   def __str__(self):
   1045     return "\"%s\"" % self.GetChars()
   1046 
   1047 
   1048 class SeqString(String):
   1049   def CharsOffset(self):
   1050     return self.heap.PointerSize() * 3
   1051 
   1052   def __init__(self, heap, map, address):
   1053     String.__init__(self, heap, map, address)
   1054     self.chars = heap.reader.ReadBytes(self.address + self.CharsOffset(),
   1055                                        self.length)
   1056 
   1057   def GetChars(self):
   1058     return self.chars
   1059 
   1060 
   1061 class ExternalString(String):
   1062   # TODO(vegorov) fix ExternalString for X64 architecture
   1063   RESOURCE_OFFSET = 12
   1064 
   1065   WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
   1066   WEBKIT_STRING_IMPL_CHARS_OFFSET = 8
   1067 
   1068   def __init__(self, heap, map, address):
   1069     String.__init__(self, heap, map, address)
   1070     reader = heap.reader
   1071     self.resource = \
   1072         reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET)
   1073     self.chars = "?external string?"
   1074     if not reader.IsValidAddress(self.resource): return
   1075     string_impl_address = self.resource + \
   1076         ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET
   1077     if not reader.IsValidAddress(string_impl_address): return
   1078     string_impl = reader.ReadU32(string_impl_address)
   1079     chars_ptr_address = string_impl + \
   1080         ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET
   1081     if not reader.IsValidAddress(chars_ptr_address): return
   1082     chars_ptr = reader.ReadU32(chars_ptr_address)
   1083     if not reader.IsValidAddress(chars_ptr): return
   1084     raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length)
   1085     self.chars = codecs.getdecoder("utf16")(raw_chars)[0]
   1086 
   1087   def GetChars(self):
   1088     return self.chars
   1089 
   1090 
   1091 class ConsString(String):
   1092   def LeftOffset(self):
   1093     return self.heap.PointerSize() * 3
   1094 
   1095   def RightOffset(self):
   1096     return self.heap.PointerSize() * 4
   1097 
   1098   def __init__(self, heap, map, address):
   1099     String.__init__(self, heap, map, address)
   1100     self.left = self.ObjectField(self.LeftOffset())
   1101     self.right = self.ObjectField(self.RightOffset())
   1102 
   1103   def GetChars(self):
   1104     try:
   1105       return self.left.GetChars() + self.right.GetChars()
   1106     except:
   1107       return "***CAUGHT EXCEPTION IN GROKDUMP***"
   1108 
   1109 
   1110 class Oddball(HeapObject):
   1111   # Should match declarations in objects.h
   1112   KINDS = [
   1113     "False",
   1114     "True",
   1115     "TheHole",
   1116     "Null",
   1117     "ArgumentMarker",
   1118     "Undefined",
   1119     "Other"
   1120   ]
   1121 
   1122   def ToStringOffset(self):
   1123     return self.heap.PointerSize()
   1124 
   1125   def ToNumberOffset(self):
   1126     return self.ToStringOffset() + self.heap.PointerSize()
   1127 
   1128   def KindOffset(self):
   1129     return self.ToNumberOffset() + self.heap.PointerSize()
   1130 
   1131   def __init__(self, heap, map, address):
   1132     HeapObject.__init__(self, heap, map, address)
   1133     self.to_string = self.ObjectField(self.ToStringOffset())
   1134     self.kind = self.SmiField(self.KindOffset())
   1135 
   1136   def Print(self, p):
   1137     p.Print(str(self))
   1138 
   1139   def __str__(self):
   1140     if self.to_string:
   1141       return "Oddball(%08x, <%s>)" % (self.address, self.to_string.GetChars())
   1142     else:
   1143       kind = "???"
   1144       if 0 <= self.kind < len(Oddball.KINDS):
   1145         kind = Oddball.KINDS[self.kind]
   1146       return "Oddball(%08x, kind=%s)" % (self.address, kind)
   1147 
   1148 
   1149 class FixedArray(HeapObject):
   1150   def LengthOffset(self):
   1151     return self.heap.PointerSize()
   1152 
   1153   def ElementsOffset(self):
   1154     return self.heap.PointerSize() * 2
   1155 
   1156   def MemberOffset(self, i):
   1157     return self.ElementsOffset() + self.heap.PointerSize() * i
   1158 
   1159   def Get(self, i):
   1160     return self.ObjectField(self.MemberOffset(i))
   1161 
   1162   def __init__(self, heap, map, address):
   1163     HeapObject.__init__(self, heap, map, address)
   1164     self.length = self.SmiField(self.LengthOffset())
   1165 
   1166   def Print(self, p):
   1167     p.Print("FixedArray(%s) {" % self.heap.reader.FormatIntPtr(self.address))
   1168     p.Indent()
   1169     p.Print("length: %d" % self.length)
   1170     base_offset = self.ElementsOffset()
   1171     for i in xrange(self.length):
   1172       offset = base_offset + 4 * i
   1173       try:
   1174         p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
   1175       except TypeError:
   1176         p.Dedent()
   1177         p.Print("...")
   1178         p.Print("}")
   1179         return
   1180     p.Dedent()
   1181     p.Print("}")
   1182 
   1183   def __str__(self):
   1184     return "FixedArray(%08x, length=%d)" % (self.address, self.length)
   1185 
   1186 
   1187 class DescriptorArray(object):
   1188   def __init__(self, array):
   1189     self.array = array
   1190 
   1191   def Length(self):
   1192     return self.array.Get(0)
   1193 
   1194   def Decode(self, offset, size, value):
   1195     return (value >> offset) & ((1 << size) - 1)
   1196 
   1197   TYPES = [
   1198       "normal",
   1199       "field",
   1200       "function",
   1201       "callbacks"
   1202   ]
   1203 
   1204   def Type(self, value):
   1205     return DescriptorArray.TYPES[self.Decode(0, 3, value)]
   1206 
   1207   def Attributes(self, value):
   1208     attributes = self.Decode(3, 3, value)
   1209     result = []
   1210     if (attributes & 0): result += ["ReadOnly"]
   1211     if (attributes & 1): result += ["DontEnum"]
   1212     if (attributes & 2): result += ["DontDelete"]
   1213     return "[" + (",".join(result)) + "]"
   1214 
   1215   def Deleted(self, value):
   1216     return self.Decode(6, 1, value) == 1
   1217 
   1218   def Storage(self, value):
   1219     return self.Decode(7, 11, value)
   1220 
   1221   def Pointer(self, value):
   1222     return self.Decode(18, 11, value)
   1223 
   1224   def Details(self, di, value):
   1225     return (
   1226         di,
   1227         self.Type(value),
   1228         self.Attributes(value),
   1229         self.Storage(value),
   1230         self.Pointer(value)
   1231     )
   1232 
   1233 
   1234   def Print(self, p):
   1235     length = self.Length()
   1236     array = self.array
   1237 
   1238     p.Print("Descriptors(%08x, length=%d)" % (array.address, length))
   1239     p.Print("[et] %s" % (array.Get(1)))
   1240 
   1241     for di in xrange(length):
   1242       i = 2 + di * 3
   1243       p.Print("0x%x" % (array.address + array.MemberOffset(i)))
   1244       p.Print("[%i] name:    %s" % (di, array.Get(i + 0)))
   1245       p.Print("[%i] details: %s %s enum %i pointer %i" % \
   1246               self.Details(di, array.Get(i + 1)))
   1247       p.Print("[%i] value:   %s" % (di, array.Get(i + 2)))
   1248 
   1249     end = self.array.length // 3
   1250     if length != end:
   1251       p.Print("[%i-%i] slack descriptors" % (length, end))
   1252 
   1253 
   1254 class TransitionArray(object):
   1255   def __init__(self, array):
   1256     self.array = array
   1257 
   1258   def IsSimpleTransition(self):
   1259     return self.array.length <= 2
   1260 
   1261   def Length(self):
   1262     # SimpleTransition cases
   1263     if self.IsSimpleTransition():
   1264       return self.array.length - 1
   1265     return (self.array.length - 3) // 2
   1266 
   1267   def Print(self, p):
   1268     length = self.Length()
   1269     array = self.array
   1270 
   1271     p.Print("Transitions(%08x, length=%d)" % (array.address, length))
   1272     p.Print("[backpointer] %s" % (array.Get(0)))
   1273     if self.IsSimpleTransition():
   1274       if length == 1:
   1275         p.Print("[simple target] %s" % (array.Get(1)))
   1276       return
   1277 
   1278     elements = array.Get(1)
   1279     if elements is not None:
   1280       p.Print("[elements   ] %s" % (elements))
   1281 
   1282     prototype = array.Get(2)
   1283     if prototype is not None:
   1284       p.Print("[prototype  ] %s" % (prototype))
   1285 
   1286     for di in xrange(length):
   1287       i = 3 + di * 2
   1288       p.Print("[%i] symbol: %s" % (di, array.Get(i + 0)))
   1289       p.Print("[%i] target: %s" % (di, array.Get(i + 1)))
   1290 
   1291 
   1292 class JSFunction(HeapObject):
   1293   def CodeEntryOffset(self):
   1294     return 3 * self.heap.PointerSize()
   1295 
   1296   def SharedOffset(self):
   1297     return 5 * self.heap.PointerSize()
   1298 
   1299   def __init__(self, heap, map, address):
   1300     HeapObject.__init__(self, heap, map, address)
   1301     code_entry = \
   1302         heap.reader.ReadU32(self.address + self.CodeEntryOffset())
   1303     self.code = heap.FindObject(code_entry - Code.HeaderSize(heap) + 1)
   1304     self.shared = self.ObjectField(self.SharedOffset())
   1305 
   1306   def Print(self, p):
   1307     source = "\n".join("  %s" % line for line in self._GetSource().split("\n"))
   1308     p.Print("JSFunction(%s) {" % self.heap.reader.FormatIntPtr(self.address))
   1309     p.Indent()
   1310     p.Print("inferred name: %s" % self.shared.inferred_name)
   1311     if self.shared.script.Is(Script) and self.shared.script.name.Is(String):
   1312       p.Print("script name: %s" % self.shared.script.name)
   1313     p.Print("source:")
   1314     p.PrintLines(self._GetSource().split("\n"))
   1315     p.Print("code:")
   1316     self.code.Print(p)
   1317     if self.code != self.shared.code:
   1318       p.Print("unoptimized code:")
   1319       self.shared.code.Print(p)
   1320     p.Dedent()
   1321     p.Print("}")
   1322 
   1323   def __str__(self):
   1324     inferred_name = ""
   1325     if self.shared.Is(SharedFunctionInfo):
   1326       inferred_name = self.shared.inferred_name
   1327     return "JSFunction(%s, %s)" % \
   1328           (self.heap.reader.FormatIntPtr(self.address), inferred_name)
   1329 
   1330   def _GetSource(self):
   1331     source = "?source?"
   1332     start = self.shared.start_position
   1333     end = self.shared.end_position
   1334     if not self.shared.script.Is(Script): return source
   1335     script_source = self.shared.script.source
   1336     if not script_source.Is(String): return source
   1337     return script_source.GetChars()[start:end]
   1338 
   1339 
   1340 class SharedFunctionInfo(HeapObject):
   1341   def CodeOffset(self):
   1342     return 2 * self.heap.PointerSize()
   1343 
   1344   def ScriptOffset(self):
   1345     return 7 * self.heap.PointerSize()
   1346 
   1347   def InferredNameOffset(self):
   1348     return 9 * self.heap.PointerSize()
   1349 
   1350   def EndPositionOffset(self):
   1351     return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize()
   1352 
   1353   def StartPositionAndTypeOffset(self):
   1354     return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize()
   1355 
   1356   def __init__(self, heap, map, address):
   1357     HeapObject.__init__(self, heap, map, address)
   1358     self.code = self.ObjectField(self.CodeOffset())
   1359     self.script = self.ObjectField(self.ScriptOffset())
   1360     self.inferred_name = self.ObjectField(self.InferredNameOffset())
   1361     if heap.PointerSize() == 8:
   1362       start_position_and_type = \
   1363           heap.reader.ReadU32(self.StartPositionAndTypeOffset())
   1364       self.start_position = start_position_and_type >> 2
   1365       pseudo_smi_end_position = \
   1366           heap.reader.ReadU32(self.EndPositionOffset())
   1367       self.end_position = pseudo_smi_end_position >> 2
   1368     else:
   1369       start_position_and_type = \
   1370           self.SmiField(self.StartPositionAndTypeOffset())
   1371       self.start_position = start_position_and_type >> 2
   1372       self.end_position = \
   1373           self.SmiField(self.EndPositionOffset())
   1374 
   1375 
   1376 class Script(HeapObject):
   1377   def SourceOffset(self):
   1378     return self.heap.PointerSize()
   1379 
   1380   def NameOffset(self):
   1381     return self.SourceOffset() + self.heap.PointerSize()
   1382 
   1383   def __init__(self, heap, map, address):
   1384     HeapObject.__init__(self, heap, map, address)
   1385     self.source = self.ObjectField(self.SourceOffset())
   1386     self.name = self.ObjectField(self.NameOffset())
   1387 
   1388 
   1389 class CodeCache(HeapObject):
   1390   def DefaultCacheOffset(self):
   1391     return self.heap.PointerSize()
   1392 
   1393   def NormalTypeCacheOffset(self):
   1394     return self.DefaultCacheOffset() + self.heap.PointerSize()
   1395 
   1396   def __init__(self, heap, map, address):
   1397     HeapObject.__init__(self, heap, map, address)
   1398     self.default_cache = self.ObjectField(self.DefaultCacheOffset())
   1399     self.normal_type_cache = self.ObjectField(self.NormalTypeCacheOffset())
   1400 
   1401   def Print(self, p):
   1402     p.Print("CodeCache(%s) {" % self.heap.reader.FormatIntPtr(self.address))
   1403     p.Indent()
   1404     p.Print("default cache: %s" % self.default_cache)
   1405     p.Print("normal type cache: %s" % self.normal_type_cache)
   1406     p.Dedent()
   1407     p.Print("}")
   1408 
   1409 
   1410 class Code(HeapObject):
   1411   CODE_ALIGNMENT_MASK = (1 << 5) - 1
   1412 
   1413   def InstructionSizeOffset(self):
   1414     return self.heap.PointerSize()
   1415 
   1416   @staticmethod
   1417   def HeaderSize(heap):
   1418     return (heap.PointerSize() + heap.IntSize() + \
   1419         4 * heap.PointerSize() + 3 * heap.IntSize() + \
   1420         Code.CODE_ALIGNMENT_MASK) & ~Code.CODE_ALIGNMENT_MASK
   1421 
   1422   def __init__(self, heap, map, address):
   1423     HeapObject.__init__(self, heap, map, address)
   1424     self.entry = self.address + Code.HeaderSize(heap)
   1425     self.instruction_size = \
   1426         heap.reader.ReadU32(self.address + self.InstructionSizeOffset())
   1427 
   1428   def Print(self, p):
   1429     lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
   1430     p.Print("Code(%s) {" % self.heap.reader.FormatIntPtr(self.address))
   1431     p.Indent()
   1432     p.Print("instruction_size: %d" % self.instruction_size)
   1433     p.PrintLines(self._FormatLine(line) for line in lines)
   1434     p.Dedent()
   1435     p.Print("}")
   1436 
   1437   def _FormatLine(self, line):
   1438     return FormatDisasmLine(self.entry, self.heap, line)
   1439 
   1440 
   1441 class V8Heap(object):
   1442   CLASS_MAP = {
   1443     "SYMBOL_TYPE": SeqString,
   1444     "ASCII_SYMBOL_TYPE": SeqString,
   1445     "CONS_SYMBOL_TYPE": ConsString,
   1446     "CONS_ASCII_SYMBOL_TYPE": ConsString,
   1447     "EXTERNAL_SYMBOL_TYPE": ExternalString,
   1448     "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString,
   1449     "EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString,
   1450     "SHORT_EXTERNAL_SYMBOL_TYPE": ExternalString,
   1451     "SHORT_EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString,
   1452     "SHORT_EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString,
   1453     "STRING_TYPE": SeqString,
   1454     "ASCII_STRING_TYPE": SeqString,
   1455     "CONS_STRING_TYPE": ConsString,
   1456     "CONS_ASCII_STRING_TYPE": ConsString,
   1457     "EXTERNAL_STRING_TYPE": ExternalString,
   1458     "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE": ExternalString,
   1459     "EXTERNAL_ASCII_STRING_TYPE": ExternalString,
   1460     "MAP_TYPE": Map,
   1461     "ODDBALL_TYPE": Oddball,
   1462     "FIXED_ARRAY_TYPE": FixedArray,
   1463     "JS_FUNCTION_TYPE": JSFunction,
   1464     "SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo,
   1465     "SCRIPT_TYPE": Script,
   1466     "CODE_CACHE_TYPE": CodeCache,
   1467     "CODE_TYPE": Code,
   1468   }
   1469 
   1470   def __init__(self, reader, stack_map):
   1471     self.reader = reader
   1472     self.stack_map = stack_map
   1473     self.objects = {}
   1474 
   1475   def FindObjectOrSmi(self, tagged_address):
   1476     if (tagged_address & 1) == 0: return tagged_address / 2
   1477     return self.FindObject(tagged_address)
   1478 
   1479   def FindObject(self, tagged_address):
   1480     if tagged_address in self.objects:
   1481       return self.objects[tagged_address]
   1482     if (tagged_address & self.ObjectAlignmentMask()) != 1: return None
   1483     address = tagged_address - 1
   1484     if not self.reader.IsValidAddress(address): return None
   1485     map_tagged_address = self.reader.ReadUIntPtr(address)
   1486     if tagged_address == map_tagged_address:
   1487       # Meta map?
   1488       meta_map = Map(self, None, address)
   1489       instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type)
   1490       if instance_type_name != "MAP_TYPE": return None
   1491       meta_map.map = meta_map
   1492       object = meta_map
   1493     else:
   1494       map = self.FindMap(map_tagged_address)
   1495       if map is None: return None
   1496       instance_type_name = INSTANCE_TYPES.get(map.instance_type)
   1497       if instance_type_name is None: return None
   1498       cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
   1499       object = cls(self, map, address)
   1500     self.objects[tagged_address] = object
   1501     return object
   1502 
   1503   def FindMap(self, tagged_address):
   1504     if (tagged_address & self.MapAlignmentMask()) != 1: return None
   1505     address = tagged_address - 1
   1506     if not self.reader.IsValidAddress(address): return None
   1507     object = Map(self, None, address)
   1508     return object
   1509 
   1510   def IntSize(self):
   1511     return 4
   1512 
   1513   def PointerSize(self):
   1514     return self.reader.PointerSize()
   1515 
   1516   def ObjectAlignmentMask(self):
   1517     return self.PointerSize() - 1
   1518 
   1519   def MapAlignmentMask(self):
   1520     if self.reader.arch == MD_CPU_ARCHITECTURE_AMD64:
   1521       return (1 << 4) - 1
   1522     elif self.reader.arch == MD_CPU_ARCHITECTURE_ARM:
   1523       return (1 << 4) - 1
   1524     elif self.reader.arch == MD_CPU_ARCHITECTURE_X86:
   1525       return (1 << 5) - 1
   1526 
   1527   def PageAlignmentMask(self):
   1528     return (1 << 20) - 1
   1529 
   1530 
   1531 class KnownObject(HeapObject):
   1532   def __init__(self, heap, known_name):
   1533     HeapObject.__init__(self, heap, None, None)
   1534     self.known_name = known_name
   1535 
   1536   def __str__(self):
   1537     return "<%s>" % self.known_name
   1538 
   1539 
   1540 class KnownMap(HeapObject):
   1541   def __init__(self, heap, known_name, instance_type):
   1542     HeapObject.__init__(self, heap, None, None)
   1543     self.instance_type = instance_type
   1544     self.known_name = known_name
   1545 
   1546   def __str__(self):
   1547     return "<%s>" % self.known_name
   1548 
   1549 
   1550 class InspectionPadawan(object):
   1551   """The padawan can improve annotations by sensing well-known objects."""
   1552   def __init__(self, reader, heap):
   1553     self.reader = reader
   1554     self.heap = heap
   1555     self.known_first_map_page = 0
   1556     self.known_first_data_page = 0
   1557     self.known_first_pointer_page = 0
   1558 
   1559   def __getattr__(self, name):
   1560     """An InspectionPadawan can be used instead of V8Heap, even though
   1561        it does not inherit from V8Heap (aka. mixin)."""
   1562     return getattr(self.heap, name)
   1563 
   1564   def GetPageOffset(self, tagged_address):
   1565     return tagged_address & self.heap.PageAlignmentMask()
   1566 
   1567   def IsInKnownMapSpace(self, tagged_address):
   1568     page_address = tagged_address & ~self.heap.PageAlignmentMask()
   1569     return page_address == self.known_first_map_page
   1570 
   1571   def IsInKnownOldSpace(self, tagged_address):
   1572     page_address = tagged_address & ~self.heap.PageAlignmentMask()
   1573     return page_address in [self.known_first_data_page,
   1574                             self.known_first_pointer_page]
   1575 
   1576   def ContainingKnownOldSpaceName(self, tagged_address):
   1577     page_address = tagged_address & ~self.heap.PageAlignmentMask()
   1578     if page_address == self.known_first_data_page: return "OLD_DATA_SPACE"
   1579     if page_address == self.known_first_pointer_page: return "OLD_POINTER_SPACE"
   1580     return None
   1581 
   1582   def SenseObject(self, tagged_address):
   1583     if self.IsInKnownOldSpace(tagged_address):
   1584       offset = self.GetPageOffset(tagged_address)
   1585       lookup_key = (self.ContainingKnownOldSpaceName(tagged_address), offset)
   1586       known_obj_name = KNOWN_OBJECTS.get(lookup_key)
   1587       if known_obj_name:
   1588         return KnownObject(self, known_obj_name)
   1589     if self.IsInKnownMapSpace(tagged_address):
   1590       known_map = self.SenseMap(tagged_address)
   1591       if known_map:
   1592         return known_map
   1593     found_obj = self.heap.FindObject(tagged_address)
   1594     if found_obj: return found_obj
   1595     address = tagged_address - 1
   1596     if self.reader.IsValidAddress(address):
   1597       map_tagged_address = self.reader.ReadUIntPtr(address)
   1598       map = self.SenseMap(map_tagged_address)
   1599       if map is None: return None
   1600       instance_type_name = INSTANCE_TYPES.get(map.instance_type)
   1601       if instance_type_name is None: return None
   1602       cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
   1603       return cls(self, map, address)
   1604     return None
   1605 
   1606   def SenseMap(self, tagged_address):
   1607     if self.IsInKnownMapSpace(tagged_address):
   1608       offset = self.GetPageOffset(tagged_address)
   1609       known_map_info = KNOWN_MAPS.get(offset)
   1610       if known_map_info:
   1611         known_map_type, known_map_name = known_map_info
   1612         return KnownMap(self, known_map_name, known_map_type)
   1613     found_map = self.heap.FindMap(tagged_address)
   1614     if found_map: return found_map
   1615     return None
   1616 
   1617   def FindObjectOrSmi(self, tagged_address):
   1618     """When used as a mixin in place of V8Heap."""
   1619     found_obj = self.SenseObject(tagged_address)
   1620     if found_obj: return found_obj
   1621     if (tagged_address & 1) == 0:
   1622       return "Smi(%d)" % (tagged_address / 2)
   1623     else:
   1624       return "Unknown(%s)" % self.reader.FormatIntPtr(tagged_address)
   1625 
   1626   def FindObject(self, tagged_address):
   1627     """When used as a mixin in place of V8Heap."""
   1628     raise NotImplementedError
   1629 
   1630   def FindMap(self, tagged_address):
   1631     """When used as a mixin in place of V8Heap."""
   1632     raise NotImplementedError
   1633 
   1634   def PrintKnowledge(self):
   1635     print "  known_first_map_page = %s\n"\
   1636           "  known_first_data_page = %s\n"\
   1637           "  known_first_pointer_page = %s" % (
   1638           self.reader.FormatIntPtr(self.known_first_map_page),
   1639           self.reader.FormatIntPtr(self.known_first_data_page),
   1640           self.reader.FormatIntPtr(self.known_first_pointer_page))
   1641 
   1642 
   1643 class InspectionShell(cmd.Cmd):
   1644   def __init__(self, reader, heap):
   1645     cmd.Cmd.__init__(self)
   1646     self.reader = reader
   1647     self.heap = heap
   1648     self.padawan = InspectionPadawan(reader, heap)
   1649     self.prompt = "(grok) "
   1650 
   1651   def do_da(self, address):
   1652     """
   1653      Print ASCII string starting at specified address.
   1654     """
   1655     address = int(address, 16)
   1656     string = ""
   1657     while self.reader.IsValidAddress(address):
   1658       code = self.reader.ReadU8(address)
   1659       if code < 128:
   1660         string += chr(code)
   1661       else:
   1662         break
   1663       address += 1
   1664     if string == "":
   1665       print "Not an ASCII string at %s" % self.reader.FormatIntPtr(address)
   1666     else:
   1667       print "%s\n" % string
   1668 
   1669   def do_dd(self, address):
   1670     """
   1671      Interpret memory at the given address (if available) as a sequence
   1672      of words. Automatic alignment is not performed.
   1673     """
   1674     start = int(address, 16)
   1675     if (start & self.heap.ObjectAlignmentMask()) != 0:
   1676       print "Warning: Dumping un-aligned memory, is this what you had in mind?"
   1677     for slot in xrange(start,
   1678                        start + self.reader.PointerSize() * 10,
   1679                        self.reader.PointerSize()):
   1680       if not self.reader.IsValidAddress(slot):
   1681         print "Address is not contained within the minidump!"
   1682         return
   1683       maybe_address = self.reader.ReadUIntPtr(slot)
   1684       heap_object = self.padawan.SenseObject(maybe_address)
   1685       print "%s: %s %s" % (self.reader.FormatIntPtr(slot),
   1686                            self.reader.FormatIntPtr(maybe_address),
   1687                            heap_object or '')
   1688 
   1689   def do_do(self, address):
   1690     """
   1691      Interpret memory at the given address as a V8 object. Automatic
   1692      alignment makes sure that you can pass tagged as well as un-tagged
   1693      addresses.
   1694     """
   1695     address = int(address, 16)
   1696     if (address & self.heap.ObjectAlignmentMask()) == 0:
   1697       address = address + 1
   1698     elif (address & self.heap.ObjectAlignmentMask()) != 1:
   1699       print "Address doesn't look like a valid pointer!"
   1700       return
   1701     heap_object = self.padawan.SenseObject(address)
   1702     if heap_object:
   1703       heap_object.Print(Printer())
   1704     else:
   1705       print "Address cannot be interpreted as object!"
   1706 
   1707   def do_do_desc(self, address):
   1708     """
   1709       Print a descriptor array in a readable format.
   1710     """
   1711     start = int(address, 16)
   1712     if ((start & 1) == 1): start = start - 1
   1713     DescriptorArray(FixedArray(self.heap, None, start)).Print(Printer())
   1714 
   1715   def do_do_map(self, address):
   1716     """
   1717       Print a descriptor array in a readable format.
   1718     """
   1719     start = int(address, 16)
   1720     if ((start & 1) == 1): start = start - 1
   1721     Map(self.heap, None, start).Print(Printer())
   1722 
   1723   def do_do_trans(self, address):
   1724     """
   1725       Print a transition array in a readable format.
   1726     """
   1727     start = int(address, 16)
   1728     if ((start & 1) == 1): start = start - 1
   1729     TransitionArray(FixedArray(self.heap, None, start)).Print(Printer())
   1730 
   1731   def do_dp(self, address):
   1732     """
   1733      Interpret memory at the given address as being on a V8 heap page
   1734      and print information about the page header (if available).
   1735     """
   1736     address = int(address, 16)
   1737     page_address = address & ~self.heap.PageAlignmentMask()
   1738     if self.reader.IsValidAddress(page_address):
   1739       raise NotImplementedError
   1740     else:
   1741       print "Page header is not available!"
   1742 
   1743   def do_k(self, arguments):
   1744     """
   1745      Teach V8 heap layout information to the inspector. This increases
   1746      the amount of annotations the inspector can produce while dumping
   1747      data. The first page of each heap space is of particular interest
   1748      because it contains known objects that do not move.
   1749     """
   1750     self.padawan.PrintKnowledge()
   1751 
   1752   def do_kd(self, address):
   1753     """
   1754      Teach V8 heap layout information to the inspector. Set the first
   1755      data-space page by passing any pointer into that page.
   1756     """
   1757     address = int(address, 16)
   1758     page_address = address & ~self.heap.PageAlignmentMask()
   1759     self.padawan.known_first_data_page = page_address
   1760 
   1761   def do_km(self, address):
   1762     """
   1763      Teach V8 heap layout information to the inspector. Set the first
   1764      map-space page by passing any pointer into that page.
   1765     """
   1766     address = int(address, 16)
   1767     page_address = address & ~self.heap.PageAlignmentMask()
   1768     self.padawan.known_first_map_page = page_address
   1769 
   1770   def do_kp(self, address):
   1771     """
   1772      Teach V8 heap layout information to the inspector. Set the first
   1773      pointer-space page by passing any pointer into that page.
   1774     """
   1775     address = int(address, 16)
   1776     page_address = address & ~self.heap.PageAlignmentMask()
   1777     self.padawan.known_first_pointer_page = page_address
   1778 
   1779   def do_list(self, smth):
   1780     """
   1781      List all available memory regions.
   1782     """
   1783     def print_region(reader, start, size, location):
   1784       print "  %s - %s (%d bytes)" % (reader.FormatIntPtr(start),
   1785                                       reader.FormatIntPtr(start + size),
   1786                                       size)
   1787     print "Available memory regions:"
   1788     self.reader.ForEachMemoryRegion(print_region)
   1789 
   1790   def do_lm(self, arg):
   1791     """
   1792      List details for all loaded modules in the minidump. An argument can
   1793      be passed to limit the output to only those modules that contain the
   1794      argument as a substring (case insensitive match).
   1795     """
   1796     for module in self.reader.module_list.modules:
   1797       if arg:
   1798         name = GetModuleName(self.reader, module).lower()
   1799         if name.find(arg.lower()) >= 0:
   1800           PrintModuleDetails(self.reader, module)
   1801       else:
   1802         PrintModuleDetails(self.reader, module)
   1803     print
   1804 
   1805   def do_s(self, word):
   1806     """
   1807      Search for a given word in available memory regions. The given word
   1808      is expanded to full pointer size and searched at aligned as well as
   1809      un-aligned memory locations. Use 'sa' to search aligned locations
   1810      only.
   1811     """
   1812     try:
   1813       word = int(word, 0)
   1814     except ValueError:
   1815       print "Malformed word, prefix with '0x' to use hexadecimal format."
   1816       return
   1817     print "Searching for word %d/0x%s:" % (word, self.reader.FormatIntPtr(word))
   1818     self.reader.FindWord(word)
   1819 
   1820   def do_sh(self, none):
   1821     """
   1822      Search for the V8 Heap object in all available memory regions. You
   1823      might get lucky and find this rare treasure full of invaluable
   1824      information.
   1825     """
   1826     raise NotImplementedError
   1827 
   1828   def do_u(self, args):
   1829     """
   1830      Unassemble memory in the region [address, address + size). If the
   1831      size is not specified, a default value of 32 bytes is used.
   1832      Synopsis: u 0x<address> 0x<size>
   1833     """
   1834     args = args.split(' ')
   1835     start = int(args[0], 16)
   1836     size = int(args[1], 16) if len(args) > 1 else 0x20
   1837     if not self.reader.IsValidAddress(start):
   1838       print "Address is not contained within the minidump!"
   1839       return
   1840     lines = self.reader.GetDisasmLines(start, size)
   1841     for line in lines:
   1842       print FormatDisasmLine(start, self.heap, line)
   1843     print
   1844 
   1845   def do_EOF(self, none):
   1846     raise KeyboardInterrupt
   1847 
   1848 EIP_PROXIMITY = 64
   1849 
   1850 CONTEXT_FOR_ARCH = {
   1851     MD_CPU_ARCHITECTURE_AMD64:
   1852       ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip',
   1853        'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15'],
   1854     MD_CPU_ARCHITECTURE_ARM:
   1855       ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9',
   1856        'r10', 'r11', 'r12', 'sp', 'lr', 'pc'],
   1857     MD_CPU_ARCHITECTURE_X86:
   1858       ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip']
   1859 }
   1860 
   1861 KNOWN_MODULES = {'chrome.exe', 'chrome.dll'}
   1862 
   1863 def GetVersionString(ms, ls):
   1864   return "%d.%d.%d.%d" % (ms >> 16, ms & 0xffff, ls >> 16, ls & 0xffff)
   1865 
   1866 
   1867 def GetModuleName(reader, module):
   1868   name = reader.ReadMinidumpString(module.module_name_rva)
   1869   # simplify for path manipulation
   1870   name = name.encode('utf-8')
   1871   return str(os.path.basename(str(name).replace("\\", "/")))
   1872 
   1873 
   1874 def PrintModuleDetails(reader, module):
   1875   print "%s" % GetModuleName(reader, module)
   1876   file_version = GetVersionString(module.version_info.dwFileVersionMS,
   1877                                   module.version_info.dwFileVersionLS)
   1878   product_version = GetVersionString(module.version_info.dwProductVersionMS,
   1879                                      module.version_info.dwProductVersionLS)
   1880   print "  base: %s" % reader.FormatIntPtr(module.base_of_image)
   1881   print "  end: %s" % reader.FormatIntPtr(module.base_of_image +
   1882                                           module.size_of_image)
   1883   print "  file version: %s" % file_version
   1884   print "  product version: %s" % product_version
   1885   time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp)
   1886   print "  timestamp: %s" % time_date_stamp
   1887 
   1888 
   1889 def AnalyzeMinidump(options, minidump_name):
   1890   reader = MinidumpReader(options, minidump_name)
   1891   heap = None
   1892   DebugPrint("========================================")
   1893   if reader.exception is None:
   1894     print "Minidump has no exception info"
   1895   else:
   1896     print "Exception info:"
   1897     exception_thread = reader.thread_map[reader.exception.thread_id]
   1898     print "  thread id: %d" % exception_thread.id
   1899     print "  code: %08X" % reader.exception.exception.code
   1900     print "  context:"
   1901     for r in CONTEXT_FOR_ARCH[reader.arch]:
   1902       print "    %s: %s" % (r, reader.FormatIntPtr(reader.Register(r)))
   1903     # TODO(vitalyr): decode eflags.
   1904     if reader.arch == MD_CPU_ARCHITECTURE_ARM:
   1905       print "    cpsr: %s" % bin(reader.exception_context.cpsr)[2:]
   1906     else:
   1907       print "    eflags: %s" % bin(reader.exception_context.eflags)[2:]
   1908 
   1909     print
   1910     print "  modules:"
   1911     for module in reader.module_list.modules:
   1912       name = GetModuleName(reader, module)
   1913       if name in KNOWN_MODULES:
   1914         print "    %s at %08X" % (name, module.base_of_image)
   1915         reader.TryLoadSymbolsFor(name, module)
   1916     print
   1917 
   1918     stack_top = reader.ExceptionSP()
   1919     stack_bottom = exception_thread.stack.start + \
   1920         exception_thread.stack.memory.data_size
   1921     stack_map = {reader.ExceptionIP(): -1}
   1922     for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
   1923       maybe_address = reader.ReadUIntPtr(slot)
   1924       if not maybe_address in stack_map:
   1925         stack_map[maybe_address] = slot
   1926     heap = V8Heap(reader, stack_map)
   1927 
   1928     print "Disassembly around exception.eip:"
   1929     eip_symbol = reader.FindSymbol(reader.ExceptionIP())
   1930     if eip_symbol is not None:
   1931       print eip_symbol
   1932     disasm_start = reader.ExceptionIP() - EIP_PROXIMITY
   1933     disasm_bytes = 2 * EIP_PROXIMITY
   1934     if (options.full):
   1935       full_range = reader.FindRegion(reader.ExceptionIP())
   1936       if full_range is not None:
   1937         disasm_start = full_range[0]
   1938         disasm_bytes = full_range[1]
   1939 
   1940     lines = reader.GetDisasmLines(disasm_start, disasm_bytes)
   1941 
   1942     for line in lines:
   1943       print FormatDisasmLine(disasm_start, heap, line)
   1944     print
   1945 
   1946   if heap is None:
   1947     heap = V8Heap(reader, None)
   1948 
   1949   if options.full:
   1950     FullDump(reader, heap)
   1951 
   1952   if options.command:
   1953     InspectionShell(reader, heap).onecmd(options.command)
   1954 
   1955   if options.shell:
   1956     try:
   1957       InspectionShell(reader, heap).cmdloop("type help to get help")
   1958     except KeyboardInterrupt:
   1959       print "Kthxbye."
   1960   elif not options.command:
   1961     if reader.exception is not None:
   1962       frame_pointer = reader.ExceptionFP()
   1963       print "Annotated stack (from exception.esp to bottom):"
   1964       for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
   1965         maybe_address = reader.ReadUIntPtr(slot)
   1966         heap_object = heap.FindObject(maybe_address)
   1967         maybe_symbol = reader.FindSymbol(maybe_address)
   1968         if slot == frame_pointer:
   1969           maybe_symbol = "<---- frame pointer"
   1970           frame_pointer = maybe_address
   1971         print "%s: %s %s" % (reader.FormatIntPtr(slot),
   1972                              reader.FormatIntPtr(maybe_address),
   1973                              maybe_symbol or "")
   1974         if heap_object:
   1975           heap_object.Print(Printer())
   1976           print
   1977 
   1978   reader.Dispose()
   1979 
   1980 
   1981 if __name__ == "__main__":
   1982   parser = optparse.OptionParser(USAGE)
   1983   parser.add_option("-s", "--shell", dest="shell", action="store_true",
   1984                     help="start an interactive inspector shell")
   1985   parser.add_option("-c", "--command", dest="command", default="",
   1986                     help="run an interactive inspector shell command and exit")
   1987   parser.add_option("-f", "--full", dest="full", action="store_true",
   1988                     help="dump all information contained in the minidump")
   1989   parser.add_option("--symdir", dest="symdir", default=".",
   1990                     help="directory containing *.pdb.sym file with symbols")
   1991   parser.add_option("--objdump",
   1992                     default="/usr/bin/objdump",
   1993                     help="objdump tool to use [default: %default]")
   1994   options, args = parser.parse_args()
   1995   if os.path.exists(options.objdump):
   1996     disasm.OBJDUMP_BIN = options.objdump
   1997     OBJDUMP_BIN = options.objdump
   1998   else:
   1999     print "Cannot find %s, falling back to default objdump" % options.objdump
   2000   if len(args) != 1:
   2001     parser.print_help()
   2002     sys.exit(1)
   2003   AnalyzeMinidump(options, args[0])
   2004