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 BaseHTTPServer
     31 import bisect
     32 import cgi
     33 import cmd
     34 import codecs
     35 import ctypes
     36 import datetime
     37 import disasm
     38 import mmap
     39 import optparse
     40 import os
     41 import re
     42 import StringIO
     43 import sys
     44 import types
     45 import urllib
     46 import urlparse
     47 import v8heapconst
     48 import webbrowser
     49 
     50 PORT_NUMBER = 8081
     51 
     52 
     53 USAGE="""usage: %prog [OPTIONS] [DUMP-FILE]
     54 
     55 Minidump analyzer.
     56 
     57 Shows the processor state at the point of exception including the
     58 stack of the active thread and the referenced objects in the V8
     59 heap. Code objects are disassembled and the addresses linked from the
     60 stack (e.g. pushed return addresses) are marked with "=>".
     61 
     62 Examples:
     63   $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp"""
     64 
     65 
     66 DEBUG=False
     67 
     68 
     69 def DebugPrint(s):
     70   if not DEBUG: return
     71   print s
     72 
     73 
     74 class Descriptor(object):
     75   """Descriptor of a structure in a memory."""
     76 
     77   def __init__(self, fields):
     78     self.fields = fields
     79     self.is_flexible = False
     80     for _, type_or_func in fields:
     81       if isinstance(type_or_func, types.FunctionType):
     82         self.is_flexible = True
     83         break
     84     if not self.is_flexible:
     85       self.ctype = Descriptor._GetCtype(fields)
     86       self.size = ctypes.sizeof(self.ctype)
     87 
     88   def Read(self, memory, offset):
     89     if self.is_flexible:
     90       fields_copy = self.fields[:]
     91       last = 0
     92       for name, type_or_func in fields_copy:
     93         if isinstance(type_or_func, types.FunctionType):
     94           partial_ctype = Descriptor._GetCtype(fields_copy[:last])
     95           partial_object = partial_ctype.from_buffer(memory, offset)
     96           type = type_or_func(partial_object)
     97           if type is not None:
     98             fields_copy[last] = (name, type)
     99             last += 1
    100         else:
    101           last += 1
    102       complete_ctype = Descriptor._GetCtype(fields_copy[:last])
    103     else:
    104       complete_ctype = self.ctype
    105     return complete_ctype.from_buffer(memory, offset)
    106 
    107   @staticmethod
    108   def _GetCtype(fields):
    109     class Raw(ctypes.Structure):
    110       _fields_ = fields
    111       _pack_ = 1
    112 
    113       def __str__(self):
    114         return "{" + ", ".join("%s: %s" % (field, self.__getattribute__(field))
    115                                for field, _ in Raw._fields_) + "}"
    116     return Raw
    117 
    118 
    119 def FullDump(reader, heap):
    120   """Dump all available memory regions."""
    121   def dump_region(reader, start, size, location):
    122     print
    123     while start & 3 != 0:
    124       start += 1
    125       size -= 1
    126       location += 1
    127     is_executable = reader.IsProbableExecutableRegion(location, size)
    128     is_ascii = reader.IsProbableASCIIRegion(location, size)
    129 
    130     if is_executable is not False:
    131       lines = reader.GetDisasmLines(start, size)
    132       for line in lines:
    133         print FormatDisasmLine(start, heap, line)
    134       print
    135 
    136     if is_ascii is not False:
    137       # Output in the same format as the Unix hd command
    138       addr = start
    139       for i in xrange(0, size, 16):
    140         slot = i + location
    141         hex_line = ""
    142         asc_line = ""
    143         for i in xrange(16):
    144           if slot + i < location + size:
    145             byte = ctypes.c_uint8.from_buffer(reader.minidump, slot + i).value
    146             if byte >= 0x20 and byte < 0x7f:
    147               asc_line += chr(byte)
    148             else:
    149               asc_line += "."
    150             hex_line += " %02x" % (byte)
    151           else:
    152             hex_line += "   "
    153           if i == 7:
    154             hex_line += " "
    155         print "%s  %s |%s|" % (reader.FormatIntPtr(addr),
    156                                hex_line,
    157                                asc_line)
    158         addr += 16
    159 
    160     if is_executable is not True and is_ascii is not True:
    161       print "%s - %s" % (reader.FormatIntPtr(start),
    162                          reader.FormatIntPtr(start + size))
    163       print start + size + 1;
    164       for i in xrange(0, size, reader.PointerSize()):
    165         slot = start + i
    166         maybe_address = reader.ReadUIntPtr(slot)
    167         heap_object = heap.FindObject(maybe_address)
    168         print "%s: %s" % (reader.FormatIntPtr(slot),
    169                           reader.FormatIntPtr(maybe_address))
    170         if heap_object:
    171           heap_object.Print(Printer())
    172           print
    173 
    174   reader.ForEachMemoryRegion(dump_region)
    175 
    176 # Heap constants generated by 'make grokdump' in v8heapconst module.
    177 INSTANCE_TYPES = v8heapconst.INSTANCE_TYPES
    178 KNOWN_MAPS = v8heapconst.KNOWN_MAPS
    179 KNOWN_OBJECTS = v8heapconst.KNOWN_OBJECTS
    180 
    181 # Set of structures and constants that describe the layout of minidump
    182 # files. Based on MSDN and Google Breakpad.
    183 
    184 MINIDUMP_HEADER = Descriptor([
    185   ("signature", ctypes.c_uint32),
    186   ("version", ctypes.c_uint32),
    187   ("stream_count", ctypes.c_uint32),
    188   ("stream_directories_rva", ctypes.c_uint32),
    189   ("checksum", ctypes.c_uint32),
    190   ("time_date_stampt", ctypes.c_uint32),
    191   ("flags", ctypes.c_uint64)
    192 ])
    193 
    194 MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([
    195   ("data_size", ctypes.c_uint32),
    196   ("rva", ctypes.c_uint32)
    197 ])
    198 
    199 MINIDUMP_STRING = Descriptor([
    200   ("length", ctypes.c_uint32),
    201   ("buffer", lambda t: ctypes.c_uint8 * (t.length + 2))
    202 ])
    203 
    204 MINIDUMP_DIRECTORY = Descriptor([
    205   ("stream_type", ctypes.c_uint32),
    206   ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
    207 ])
    208 
    209 MD_EXCEPTION_MAXIMUM_PARAMETERS = 15
    210 
    211 MINIDUMP_EXCEPTION = Descriptor([
    212   ("code", ctypes.c_uint32),
    213   ("flags", ctypes.c_uint32),
    214   ("record", ctypes.c_uint64),
    215   ("address", ctypes.c_uint64),
    216   ("parameter_count", ctypes.c_uint32),
    217   ("unused_alignment", ctypes.c_uint32),
    218   ("information", ctypes.c_uint64 * MD_EXCEPTION_MAXIMUM_PARAMETERS)
    219 ])
    220 
    221 MINIDUMP_EXCEPTION_STREAM = Descriptor([
    222   ("thread_id", ctypes.c_uint32),
    223   ("unused_alignment", ctypes.c_uint32),
    224   ("exception", MINIDUMP_EXCEPTION.ctype),
    225   ("thread_context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
    226 ])
    227 
    228 # Stream types.
    229 MD_UNUSED_STREAM = 0
    230 MD_RESERVED_STREAM_0 = 1
    231 MD_RESERVED_STREAM_1 = 2
    232 MD_THREAD_LIST_STREAM = 3
    233 MD_MODULE_LIST_STREAM = 4
    234 MD_MEMORY_LIST_STREAM = 5
    235 MD_EXCEPTION_STREAM = 6
    236 MD_SYSTEM_INFO_STREAM = 7
    237 MD_THREAD_EX_LIST_STREAM = 8
    238 MD_MEMORY_64_LIST_STREAM = 9
    239 MD_COMMENT_STREAM_A = 10
    240 MD_COMMENT_STREAM_W = 11
    241 MD_HANDLE_DATA_STREAM = 12
    242 MD_FUNCTION_TABLE_STREAM = 13
    243 MD_UNLOADED_MODULE_LIST_STREAM = 14
    244 MD_MISC_INFO_STREAM = 15
    245 MD_MEMORY_INFO_LIST_STREAM = 16
    246 MD_THREAD_INFO_LIST_STREAM = 17
    247 MD_HANDLE_OPERATION_LIST_STREAM = 18
    248 
    249 MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE = 80
    250 
    251 MINIDUMP_FLOATING_SAVE_AREA_X86 = Descriptor([
    252   ("control_word", ctypes.c_uint32),
    253   ("status_word", ctypes.c_uint32),
    254   ("tag_word", ctypes.c_uint32),
    255   ("error_offset", ctypes.c_uint32),
    256   ("error_selector", ctypes.c_uint32),
    257   ("data_offset", ctypes.c_uint32),
    258   ("data_selector", ctypes.c_uint32),
    259   ("register_area", ctypes.c_uint8 * MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE),
    260   ("cr0_npx_state", ctypes.c_uint32)
    261 ])
    262 
    263 MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE = 512
    264 
    265 # Context flags.
    266 MD_CONTEXT_X86 = 0x00010000
    267 MD_CONTEXT_X86_CONTROL = (MD_CONTEXT_X86 | 0x00000001)
    268 MD_CONTEXT_X86_INTEGER = (MD_CONTEXT_X86 | 0x00000002)
    269 MD_CONTEXT_X86_SEGMENTS = (MD_CONTEXT_X86 | 0x00000004)
    270 MD_CONTEXT_X86_FLOATING_POINT = (MD_CONTEXT_X86 | 0x00000008)
    271 MD_CONTEXT_X86_DEBUG_REGISTERS = (MD_CONTEXT_X86 | 0x00000010)
    272 MD_CONTEXT_X86_EXTENDED_REGISTERS = (MD_CONTEXT_X86 | 0x00000020)
    273 
    274 def EnableOnFlag(type, flag):
    275   return lambda o: [None, type][int((o.context_flags & flag) != 0)]
    276 
    277 MINIDUMP_CONTEXT_X86 = Descriptor([
    278   ("context_flags", ctypes.c_uint32),
    279   # MD_CONTEXT_X86_DEBUG_REGISTERS.
    280   ("dr0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
    281   ("dr1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
    282   ("dr2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
    283   ("dr3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
    284   ("dr6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
    285   ("dr7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
    286   # MD_CONTEXT_X86_FLOATING_POINT.
    287   ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_X86.ctype,
    288                               MD_CONTEXT_X86_FLOATING_POINT)),
    289   # MD_CONTEXT_X86_SEGMENTS.
    290   ("gs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
    291   ("fs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
    292   ("es", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
    293   ("ds", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
    294   # MD_CONTEXT_X86_INTEGER.
    295   ("edi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
    296   ("esi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
    297   ("ebx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
    298   ("edx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
    299   ("ecx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
    300   ("eax", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
    301   # MD_CONTEXT_X86_CONTROL.
    302   ("ebp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
    303   ("eip", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
    304   ("cs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
    305   ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
    306   ("esp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
    307   ("ss", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
    308   # MD_CONTEXT_X86_EXTENDED_REGISTERS.
    309   ("extended_registers",
    310    EnableOnFlag(ctypes.c_uint8 * MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE,
    311                 MD_CONTEXT_X86_EXTENDED_REGISTERS))
    312 ])
    313 
    314 MD_CONTEXT_ARM = 0x40000000
    315 MD_CONTEXT_ARM_INTEGER = (MD_CONTEXT_ARM | 0x00000002)
    316 MD_CONTEXT_ARM_FLOATING_POINT = (MD_CONTEXT_ARM | 0x00000004)
    317 MD_FLOATINGSAVEAREA_ARM_FPR_COUNT = 32
    318 MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT = 8
    319 
    320 MINIDUMP_FLOATING_SAVE_AREA_ARM = Descriptor([
    321   ("fpscr", ctypes.c_uint64),
    322   ("regs", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPR_COUNT),
    323   ("extra", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT)
    324 ])
    325 
    326 MINIDUMP_CONTEXT_ARM = Descriptor([
    327   ("context_flags", ctypes.c_uint32),
    328   # MD_CONTEXT_ARM_INTEGER.
    329   ("r0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    330   ("r1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    331   ("r2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    332   ("r3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    333   ("r4", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    334   ("r5", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    335   ("r6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    336   ("r7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    337   ("r8", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    338   ("r9", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    339   ("r10", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    340   ("r11", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    341   ("r12", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    342   ("sp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    343   ("lr", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    344   ("pc", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
    345   ("cpsr", ctypes.c_uint32),
    346   ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_ARM.ctype,
    347                               MD_CONTEXT_ARM_FLOATING_POINT))
    348 ])
    349 
    350 
    351 MD_CONTEXT_ARM64 =  0x80000000
    352 MD_CONTEXT_ARM64_INTEGER = (MD_CONTEXT_ARM64 | 0x00000002)
    353 MD_CONTEXT_ARM64_FLOATING_POINT = (MD_CONTEXT_ARM64 | 0x00000004)
    354 MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT = 64
    355 
    356 MINIDUMP_FLOATING_SAVE_AREA_ARM = Descriptor([
    357   ("fpscr", ctypes.c_uint64),
    358   ("regs", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT),
    359 ])
    360 
    361 MINIDUMP_CONTEXT_ARM64 = Descriptor([
    362   ("context_flags", ctypes.c_uint64),
    363   # MD_CONTEXT_ARM64_INTEGER.
    364   ("r0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    365   ("r1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    366   ("r2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    367   ("r3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    368   ("r4", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    369   ("r5", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    370   ("r6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    371   ("r7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    372   ("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    373   ("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    374   ("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    375   ("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    376   ("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    377   ("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    378   ("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    379   ("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    380   ("r16", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    381   ("r17", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    382   ("r18", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    383   ("r19", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    384   ("r20", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    385   ("r21", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    386   ("r22", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    387   ("r23", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    388   ("r24", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    389   ("r25", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    390   ("r26", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    391   ("r27", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    392   ("r28", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    393   ("fp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    394   ("lr", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    395   ("sp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    396   ("pc", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_ARM64_INTEGER)),
    397   ("cpsr", ctypes.c_uint32),
    398   ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_ARM.ctype,
    399                               MD_CONTEXT_ARM64_FLOATING_POINT))
    400 ])
    401 
    402 
    403 MD_CONTEXT_AMD64 = 0x00100000
    404 MD_CONTEXT_AMD64_CONTROL = (MD_CONTEXT_AMD64 | 0x00000001)
    405 MD_CONTEXT_AMD64_INTEGER = (MD_CONTEXT_AMD64 | 0x00000002)
    406 MD_CONTEXT_AMD64_SEGMENTS = (MD_CONTEXT_AMD64 | 0x00000004)
    407 MD_CONTEXT_AMD64_FLOATING_POINT = (MD_CONTEXT_AMD64 | 0x00000008)
    408 MD_CONTEXT_AMD64_DEBUG_REGISTERS = (MD_CONTEXT_AMD64 | 0x00000010)
    409 
    410 MINIDUMP_CONTEXT_AMD64 = Descriptor([
    411   ("p1_home", ctypes.c_uint64),
    412   ("p2_home", ctypes.c_uint64),
    413   ("p3_home", ctypes.c_uint64),
    414   ("p4_home", ctypes.c_uint64),
    415   ("p5_home", ctypes.c_uint64),
    416   ("p6_home", ctypes.c_uint64),
    417   ("context_flags", ctypes.c_uint32),
    418   ("mx_csr", ctypes.c_uint32),
    419   # MD_CONTEXT_AMD64_CONTROL.
    420   ("cs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
    421   # MD_CONTEXT_AMD64_SEGMENTS
    422   ("ds", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
    423   ("es", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
    424   ("fs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
    425   ("gs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
    426   # MD_CONTEXT_AMD64_CONTROL.
    427   ("ss", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
    428   ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_AMD64_CONTROL)),
    429   # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
    430   ("dr0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
    431   ("dr1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
    432   ("dr2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
    433   ("dr3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
    434   ("dr6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
    435   ("dr7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
    436   # MD_CONTEXT_AMD64_INTEGER.
    437   ("rax", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    438   ("rcx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    439   ("rdx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    440   ("rbx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    441   # MD_CONTEXT_AMD64_CONTROL.
    442   ("rsp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
    443   # MD_CONTEXT_AMD64_INTEGER.
    444   ("rbp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    445   ("rsi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    446   ("rdi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    447   ("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    448   ("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    449   ("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    450   ("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    451   ("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    452   ("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    453   ("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    454   ("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
    455   # MD_CONTEXT_AMD64_CONTROL.
    456   ("rip", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
    457   # MD_CONTEXT_AMD64_FLOATING_POINT
    458   ("sse_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
    459                                  MD_CONTEXT_AMD64_FLOATING_POINT)),
    460   ("vector_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
    461                                     MD_CONTEXT_AMD64_FLOATING_POINT)),
    462   ("vector_control", EnableOnFlag(ctypes.c_uint64,
    463                                   MD_CONTEXT_AMD64_FLOATING_POINT)),
    464   # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
    465   ("debug_control", EnableOnFlag(ctypes.c_uint64,
    466                                  MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
    467   ("last_branch_to_rip", EnableOnFlag(ctypes.c_uint64,
    468                                       MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
    469   ("last_branch_from_rip", EnableOnFlag(ctypes.c_uint64,
    470                                         MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
    471   ("last_exception_to_rip", EnableOnFlag(ctypes.c_uint64,
    472                                          MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
    473   ("last_exception_from_rip", EnableOnFlag(ctypes.c_uint64,
    474                                            MD_CONTEXT_AMD64_DEBUG_REGISTERS))
    475 ])
    476 
    477 MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([
    478   ("start", ctypes.c_uint64),
    479   ("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
    480 ])
    481 
    482 MINIDUMP_MEMORY_DESCRIPTOR64 = Descriptor([
    483   ("start", ctypes.c_uint64),
    484   ("size", ctypes.c_uint64)
    485 ])
    486 
    487 MINIDUMP_MEMORY_LIST = Descriptor([
    488   ("range_count", ctypes.c_uint32),
    489   ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count)
    490 ])
    491 
    492 MINIDUMP_MEMORY_LIST_Mac = Descriptor([
    493   ("range_count", ctypes.c_uint32),
    494   ("junk", ctypes.c_uint32),
    495   ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count)
    496 ])
    497 
    498 MINIDUMP_MEMORY_LIST64 = Descriptor([
    499   ("range_count", ctypes.c_uint64),
    500   ("base_rva", ctypes.c_uint64),
    501   ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR64.ctype * m.range_count)
    502 ])
    503 
    504 MINIDUMP_THREAD = Descriptor([
    505   ("id", ctypes.c_uint32),
    506   ("suspend_count", ctypes.c_uint32),
    507   ("priority_class", ctypes.c_uint32),
    508   ("priority", ctypes.c_uint32),
    509   ("ted", ctypes.c_uint64),
    510   ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype),
    511   ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
    512 ])
    513 
    514 MINIDUMP_THREAD_LIST = Descriptor([
    515   ("thread_count", ctypes.c_uint32),
    516   ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
    517 ])
    518 
    519 MINIDUMP_THREAD_LIST_Mac = Descriptor([
    520   ("thread_count", ctypes.c_uint32),
    521   ("junk", ctypes.c_uint32),
    522   ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
    523 ])
    524 
    525 MINIDUMP_VS_FIXEDFILEINFO = Descriptor([
    526   ("dwSignature", ctypes.c_uint32),
    527   ("dwStrucVersion", ctypes.c_uint32),
    528   ("dwFileVersionMS", ctypes.c_uint32),
    529   ("dwFileVersionLS", ctypes.c_uint32),
    530   ("dwProductVersionMS", ctypes.c_uint32),
    531   ("dwProductVersionLS", ctypes.c_uint32),
    532   ("dwFileFlagsMask", ctypes.c_uint32),
    533   ("dwFileFlags", ctypes.c_uint32),
    534   ("dwFileOS", ctypes.c_uint32),
    535   ("dwFileType", ctypes.c_uint32),
    536   ("dwFileSubtype", ctypes.c_uint32),
    537   ("dwFileDateMS", ctypes.c_uint32),
    538   ("dwFileDateLS", ctypes.c_uint32)
    539 ])
    540 
    541 MINIDUMP_RAW_MODULE = Descriptor([
    542   ("base_of_image", ctypes.c_uint64),
    543   ("size_of_image", ctypes.c_uint32),
    544   ("checksum", ctypes.c_uint32),
    545   ("time_date_stamp", ctypes.c_uint32),
    546   ("module_name_rva", ctypes.c_uint32),
    547   ("version_info", MINIDUMP_VS_FIXEDFILEINFO.ctype),
    548   ("cv_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
    549   ("misc_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
    550   ("reserved0", ctypes.c_uint32 * 2),
    551   ("reserved1", ctypes.c_uint32 * 2)
    552 ])
    553 
    554 MINIDUMP_MODULE_LIST = Descriptor([
    555   ("number_of_modules", ctypes.c_uint32),
    556   ("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules)
    557 ])
    558 
    559 MINIDUMP_MODULE_LIST_Mac = Descriptor([
    560   ("number_of_modules", ctypes.c_uint32),
    561   ("junk", ctypes.c_uint32),
    562   ("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules)
    563 ])
    564 
    565 MINIDUMP_RAW_SYSTEM_INFO = Descriptor([
    566   ("processor_architecture", ctypes.c_uint16)
    567 ])
    568 
    569 MD_CPU_ARCHITECTURE_X86 = 0
    570 MD_CPU_ARCHITECTURE_ARM = 5
    571 MD_CPU_ARCHITECTURE_ARM64 = 0x8003
    572 MD_CPU_ARCHITECTURE_AMD64 = 9
    573 
    574 class FuncSymbol:
    575   def __init__(self, start, size, name):
    576     self.start = start
    577     self.end = self.start + size
    578     self.name = name
    579 
    580   def __cmp__(self, other):
    581     if isinstance(other, FuncSymbol):
    582       return self.start - other.start
    583     return self.start - other
    584 
    585   def Covers(self, addr):
    586     return (self.start <= addr) and (addr < self.end)
    587 
    588 class MinidumpReader(object):
    589   """Minidump (.dmp) reader."""
    590 
    591   _HEADER_MAGIC = 0x504d444d
    592 
    593   def __init__(self, options, minidump_name):
    594     self.minidump_name = minidump_name
    595     self.minidump_file = open(minidump_name, "r")
    596     self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE)
    597     self.header = MINIDUMP_HEADER.Read(self.minidump, 0)
    598     if self.header.signature != MinidumpReader._HEADER_MAGIC:
    599       print >>sys.stderr, "Warning: Unsupported minidump header magic!"
    600     DebugPrint(self.header)
    601     directories = []
    602     offset = self.header.stream_directories_rva
    603     for _ in xrange(self.header.stream_count):
    604       directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset))
    605       offset += MINIDUMP_DIRECTORY.size
    606     self.arch = None
    607     self.exception = None
    608     self.exception_context = None
    609     self.memory_list = None
    610     self.memory_list64 = None
    611     self.module_list = None
    612     self.thread_map = {}
    613 
    614     self.symdir = options.symdir
    615     self.modules_with_symbols = []
    616     self.symbols = []
    617 
    618     # Find MDRawSystemInfo stream and determine arch.
    619     for d in directories:
    620       if d.stream_type == MD_SYSTEM_INFO_STREAM:
    621         system_info = MINIDUMP_RAW_SYSTEM_INFO.Read(
    622             self.minidump, d.location.rva)
    623         self.arch = system_info.processor_architecture
    624         assert self.arch in [MD_CPU_ARCHITECTURE_AMD64,
    625                              MD_CPU_ARCHITECTURE_ARM,
    626                              MD_CPU_ARCHITECTURE_ARM64,
    627                              MD_CPU_ARCHITECTURE_X86]
    628     assert not self.arch is None
    629 
    630     for d in directories:
    631       DebugPrint(d)
    632       if d.stream_type == MD_EXCEPTION_STREAM:
    633         self.exception = MINIDUMP_EXCEPTION_STREAM.Read(
    634           self.minidump, d.location.rva)
    635         DebugPrint(self.exception)
    636         if self.arch == MD_CPU_ARCHITECTURE_X86:
    637           self.exception_context = MINIDUMP_CONTEXT_X86.Read(
    638               self.minidump, self.exception.thread_context.rva)
    639         elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
    640           self.exception_context = MINIDUMP_CONTEXT_AMD64.Read(
    641               self.minidump, self.exception.thread_context.rva)
    642         elif self.arch == MD_CPU_ARCHITECTURE_ARM:
    643           self.exception_context = MINIDUMP_CONTEXT_ARM.Read(
    644               self.minidump, self.exception.thread_context.rva)
    645         elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
    646           self.exception_context = MINIDUMP_CONTEXT_ARM64.Read(
    647               self.minidump, self.exception.thread_context.rva)
    648         DebugPrint(self.exception_context)
    649       elif d.stream_type == MD_THREAD_LIST_STREAM:
    650         thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva)
    651         if ctypes.sizeof(thread_list) + 4 == d.location.data_size:
    652           thread_list = MINIDUMP_THREAD_LIST_Mac.Read(
    653               self.minidump, d.location.rva)
    654         assert ctypes.sizeof(thread_list) == d.location.data_size
    655         DebugPrint(thread_list)
    656         for thread in thread_list.threads:
    657           DebugPrint(thread)
    658           self.thread_map[thread.id] = thread
    659       elif d.stream_type == MD_MODULE_LIST_STREAM:
    660         assert self.module_list is None
    661         self.module_list = MINIDUMP_MODULE_LIST.Read(
    662           self.minidump, d.location.rva)
    663         if ctypes.sizeof(self.module_list) + 4 == d.location.data_size:
    664           self.module_list = MINIDUMP_MODULE_LIST_Mac.Read(
    665               self.minidump, d.location.rva)
    666         assert ctypes.sizeof(self.module_list) == d.location.data_size
    667         DebugPrint(self.module_list)
    668       elif d.stream_type == MD_MEMORY_LIST_STREAM:
    669         print >>sys.stderr, "Warning: This is not a full minidump!"
    670         assert self.memory_list is None
    671         self.memory_list = MINIDUMP_MEMORY_LIST.Read(
    672           self.minidump, d.location.rva)
    673         if ctypes.sizeof(self.memory_list) + 4 == d.location.data_size:
    674           self.memory_list = MINIDUMP_MEMORY_LIST_Mac.Read(
    675               self.minidump, d.location.rva)
    676         assert ctypes.sizeof(self.memory_list) == d.location.data_size
    677         DebugPrint(self.memory_list)
    678       elif d.stream_type == MD_MEMORY_64_LIST_STREAM:
    679         assert self.memory_list64 is None
    680         self.memory_list64 = MINIDUMP_MEMORY_LIST64.Read(
    681           self.minidump, d.location.rva)
    682         assert ctypes.sizeof(self.memory_list64) == d.location.data_size
    683         DebugPrint(self.memory_list64)
    684 
    685   def IsValidAddress(self, address):
    686     return self.FindLocation(address) is not None
    687 
    688   def ReadU8(self, address):
    689     location = self.FindLocation(address)
    690     return ctypes.c_uint8.from_buffer(self.minidump, location).value
    691 
    692   def ReadU32(self, address):
    693     location = self.FindLocation(address)
    694     return ctypes.c_uint32.from_buffer(self.minidump, location).value
    695 
    696   def ReadU64(self, address):
    697     location = self.FindLocation(address)
    698     return ctypes.c_uint64.from_buffer(self.minidump, location).value
    699 
    700   def ReadUIntPtr(self, address):
    701     if self.arch == MD_CPU_ARCHITECTURE_AMD64:
    702       return self.ReadU64(address)
    703     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
    704       return self.ReadU32(address)
    705     elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
    706       return self.ReadU64(address)
    707     elif self.arch == MD_CPU_ARCHITECTURE_X86:
    708       return self.ReadU32(address)
    709 
    710   def ReadBytes(self, address, size):
    711     location = self.FindLocation(address)
    712     return self.minidump[location:location + size]
    713 
    714   def _ReadWord(self, location):
    715     if self.arch == MD_CPU_ARCHITECTURE_AMD64:
    716       return ctypes.c_uint64.from_buffer(self.minidump, location).value
    717     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
    718       return ctypes.c_uint32.from_buffer(self.minidump, location).value
    719     elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
    720       return ctypes.c_uint64.from_buffer(self.minidump, location).value
    721     elif self.arch == MD_CPU_ARCHITECTURE_X86:
    722       return ctypes.c_uint32.from_buffer(self.minidump, location).value
    723 
    724   def IsProbableASCIIRegion(self, location, length):
    725     ascii_bytes = 0
    726     non_ascii_bytes = 0
    727     for i in xrange(length):
    728       loc = location + i
    729       byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
    730       if byte >= 0x7f:
    731         non_ascii_bytes += 1
    732       if byte < 0x20 and byte != 0:
    733         non_ascii_bytes += 1
    734       if byte < 0x7f and byte >= 0x20:
    735         ascii_bytes += 1
    736       if byte == 0xa:  # newline
    737         ascii_bytes += 1
    738     if ascii_bytes * 10 <= length:
    739       return False
    740     if length > 0 and ascii_bytes > non_ascii_bytes * 7:
    741       return True
    742     if ascii_bytes > non_ascii_bytes * 3:
    743       return None  # Maybe
    744     return False
    745 
    746   def IsProbableExecutableRegion(self, location, length):
    747     opcode_bytes = 0
    748     sixty_four = self.arch == MD_CPU_ARCHITECTURE_AMD64
    749     for i in xrange(length):
    750       loc = location + i
    751       byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
    752       if (byte == 0x8b or           # mov
    753           byte == 0x89 or           # mov reg-reg
    754           (byte & 0xf0) == 0x50 or  # push/pop
    755           (sixty_four and (byte & 0xf0) == 0x40) or  # rex prefix
    756           byte == 0xc3 or           # return
    757           byte == 0x74 or           # jeq
    758           byte == 0x84 or           # jeq far
    759           byte == 0x75 or           # jne
    760           byte == 0x85 or           # jne far
    761           byte == 0xe8 or           # call
    762           byte == 0xe9 or           # jmp far
    763           byte == 0xeb):            # jmp near
    764         opcode_bytes += 1
    765     opcode_percent = (opcode_bytes * 100) / length
    766     threshold = 20
    767     if opcode_percent > threshold + 2:
    768       return True
    769     if opcode_percent > threshold - 2:
    770       return None  # Maybe
    771     return False
    772 
    773   def FindRegion(self, addr):
    774     answer = [-1, -1]
    775     def is_in(reader, start, size, location):
    776       if addr >= start and addr < start + size:
    777         answer[0] = start
    778         answer[1] = size
    779     self.ForEachMemoryRegion(is_in)
    780     if answer[0] == -1:
    781       return None
    782     return answer
    783 
    784   def ForEachMemoryRegion(self, cb):
    785     if self.memory_list64 is not None:
    786       for r in self.memory_list64.ranges:
    787         location = self.memory_list64.base_rva + offset
    788         cb(self, r.start, r.size, location)
    789         offset += r.size
    790 
    791     if self.memory_list is not None:
    792       for r in self.memory_list.ranges:
    793         cb(self, r.start, r.memory.data_size, r.memory.rva)
    794 
    795   def FindWord(self, word, alignment=0):
    796     def search_inside_region(reader, start, size, location):
    797       location = (location + alignment) & ~alignment
    798       for i in xrange(size - self.PointerSize()):
    799         loc = location + i
    800         if reader._ReadWord(loc) == word:
    801           slot = start + (loc - location)
    802           print "%s: %s" % (reader.FormatIntPtr(slot),
    803                             reader.FormatIntPtr(word))
    804     self.ForEachMemoryRegion(search_inside_region)
    805 
    806   def FindWordList(self, word):
    807     aligned_res = []
    808     unaligned_res = []
    809     def search_inside_region(reader, start, size, location):
    810       for i in xrange(size - self.PointerSize()):
    811         loc = location + i
    812         if reader._ReadWord(loc) == word:
    813           slot = start + (loc - location)
    814           if slot % self.PointerSize() == 0:
    815             aligned_res.append(slot)
    816           else:
    817             unaligned_res.append(slot)
    818     self.ForEachMemoryRegion(search_inside_region)
    819     return (aligned_res, unaligned_res)
    820 
    821   def FindLocation(self, address):
    822     offset = 0
    823     if self.memory_list64 is not None:
    824       for r in self.memory_list64.ranges:
    825         if r.start <= address < r.start + r.size:
    826           return self.memory_list64.base_rva + offset + address - r.start
    827         offset += r.size
    828     if self.memory_list is not None:
    829       for r in self.memory_list.ranges:
    830         if r.start <= address < r.start + r.memory.data_size:
    831           return r.memory.rva + address - r.start
    832     return None
    833 
    834   def GetDisasmLines(self, address, size):
    835     def CountUndefinedInstructions(lines):
    836       pattern = "<UNDEFINED>"
    837       return sum([line.count(pattern) for (ignore, line) in lines])
    838 
    839     location = self.FindLocation(address)
    840     if location is None: return []
    841     arch = None
    842     possible_objdump_flags = [""]
    843     if self.arch == MD_CPU_ARCHITECTURE_X86:
    844       arch = "ia32"
    845     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
    846       arch = "arm"
    847       possible_objdump_flags = ["", "--disassembler-options=force-thumb"]
    848     elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
    849       arch = "arm64"
    850       possible_objdump_flags = ["", "--disassembler-options=force-thumb"]
    851     elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
    852       arch = "x64"
    853     results = [ disasm.GetDisasmLines(self.minidump_name,
    854                                      location,
    855                                      size,
    856                                      arch,
    857                                      False,
    858                                      objdump_flags)
    859                 for objdump_flags in possible_objdump_flags ]
    860     return min(results, key=CountUndefinedInstructions)
    861 
    862 
    863   def Dispose(self):
    864     self.minidump.close()
    865     self.minidump_file.close()
    866 
    867   def ExceptionIP(self):
    868     if self.arch == MD_CPU_ARCHITECTURE_AMD64:
    869       return self.exception_context.rip
    870     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
    871       return self.exception_context.pc
    872     elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
    873       return self.exception_context.pc
    874     elif self.arch == MD_CPU_ARCHITECTURE_X86:
    875       return self.exception_context.eip
    876 
    877   def ExceptionSP(self):
    878     if self.arch == MD_CPU_ARCHITECTURE_AMD64:
    879       return self.exception_context.rsp
    880     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
    881       return self.exception_context.sp
    882     elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
    883       return self.exception_context.sp
    884     elif self.arch == MD_CPU_ARCHITECTURE_X86:
    885       return self.exception_context.esp
    886 
    887   def ExceptionFP(self):
    888     if self.arch == MD_CPU_ARCHITECTURE_AMD64:
    889       return self.exception_context.rbp
    890     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
    891       return None
    892     elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
    893       return self.exception_context.fp
    894     elif self.arch == MD_CPU_ARCHITECTURE_X86:
    895       return self.exception_context.ebp
    896 
    897   def FormatIntPtr(self, value):
    898     if self.arch == MD_CPU_ARCHITECTURE_AMD64:
    899       return "%016x" % value
    900     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
    901       return "%08x" % value
    902     elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
    903       return "%016x" % value
    904     elif self.arch == MD_CPU_ARCHITECTURE_X86:
    905       return "%08x" % value
    906 
    907   def PointerSize(self):
    908     if self.arch == MD_CPU_ARCHITECTURE_AMD64:
    909       return 8
    910     elif self.arch == MD_CPU_ARCHITECTURE_ARM:
    911       return 4
    912     elif self.arch == MD_CPU_ARCHITECTURE_ARM64:
    913       return 8
    914     elif self.arch == MD_CPU_ARCHITECTURE_X86:
    915       return 4
    916 
    917   def Register(self, name):
    918     return self.exception_context.__getattribute__(name)
    919 
    920   def ReadMinidumpString(self, rva):
    921     string = bytearray(MINIDUMP_STRING.Read(self.minidump, rva).buffer)
    922     string = string.decode("utf16")
    923     return string[0:len(string) - 1]
    924 
    925   # Load FUNC records from a BreakPad symbol file
    926   #
    927   #    http://code.google.com/p/google-breakpad/wiki/SymbolFiles
    928   #
    929   def _LoadSymbolsFrom(self, symfile, baseaddr):
    930     print "Loading symbols from %s" % (symfile)
    931     funcs = []
    932     with open(symfile) as f:
    933       for line in f:
    934         result = re.match(
    935             r"^FUNC ([a-f0-9]+) ([a-f0-9]+) ([a-f0-9]+) (.*)$", line)
    936         if result is not None:
    937           start = int(result.group(1), 16)
    938           size = int(result.group(2), 16)
    939           name = result.group(4).rstrip()
    940           bisect.insort_left(self.symbols,
    941                              FuncSymbol(baseaddr + start, size, name))
    942     print " ... done"
    943 
    944   def TryLoadSymbolsFor(self, modulename, module):
    945     try:
    946       symfile = os.path.join(self.symdir,
    947                              modulename.replace('.', '_') + ".pdb.sym")
    948       if os.path.isfile(symfile):
    949         self._LoadSymbolsFrom(symfile, module.base_of_image)
    950         self.modules_with_symbols.append(module)
    951     except Exception as e:
    952       print "  ... failure (%s)" % (e)
    953 
    954   # Returns true if address is covered by some module that has loaded symbols.
    955   def _IsInModuleWithSymbols(self, addr):
    956     for module in self.modules_with_symbols:
    957       start = module.base_of_image
    958       end = start + module.size_of_image
    959       if (start <= addr) and (addr < end):
    960         return True
    961     return False
    962 
    963   # Find symbol covering the given address and return its name in format
    964   #     <symbol name>+<offset from the start>
    965   def FindSymbol(self, addr):
    966     if not self._IsInModuleWithSymbols(addr):
    967       return None
    968 
    969     i = bisect.bisect_left(self.symbols, addr)
    970     symbol = None
    971     if (0 < i) and self.symbols[i - 1].Covers(addr):
    972       symbol = self.symbols[i - 1]
    973     elif (i < len(self.symbols)) and self.symbols[i].Covers(addr):
    974       symbol = self.symbols[i]
    975     else:
    976       return None
    977     diff = addr - symbol.start
    978     return "%s+0x%x" % (symbol.name, diff)
    979 
    980 
    981 class Printer(object):
    982   """Printer with indentation support."""
    983 
    984   def __init__(self):
    985     self.indent = 0
    986 
    987   def Indent(self):
    988     self.indent += 2
    989 
    990   def Dedent(self):
    991     self.indent -= 2
    992 
    993   def Print(self, string):
    994     print "%s%s" % (self._IndentString(), string)
    995 
    996   def PrintLines(self, lines):
    997     indent = self._IndentString()
    998     print "\n".join("%s%s" % (indent, line) for line in lines)
    999 
   1000   def _IndentString(self):
   1001     return self.indent * " "
   1002 
   1003 
   1004 ADDRESS_RE = re.compile(r"0x[0-9a-fA-F]+")
   1005 
   1006 
   1007 def FormatDisasmLine(start, heap, line):
   1008   line_address = start + line[0]
   1009   stack_slot = heap.stack_map.get(line_address)
   1010   marker = "  "
   1011   if stack_slot:
   1012     marker = "=>"
   1013   code = AnnotateAddresses(heap, line[1])
   1014 
   1015   # Compute the actual call target which the disassembler is too stupid
   1016   # to figure out (it adds the call offset to the disassembly offset rather
   1017   # than the absolute instruction address).
   1018   if heap.reader.arch == MD_CPU_ARCHITECTURE_X86:
   1019     if code.startswith("e8"):
   1020       words = code.split()
   1021       if len(words) > 6 and words[5] == "call":
   1022         offset = int(words[4] + words[3] + words[2] + words[1], 16)
   1023         target = (line_address + offset + 5) & 0xFFFFFFFF
   1024         code = code.replace(words[6], "0x%08x" % target)
   1025   # TODO(jkummerow): port this hack to ARM and x64.
   1026 
   1027   return "%s%08x %08x: %s" % (marker, line_address, line[0], code)
   1028 
   1029 
   1030 def AnnotateAddresses(heap, line):
   1031   extra = []
   1032   for m in ADDRESS_RE.finditer(line):
   1033     maybe_address = int(m.group(0), 16)
   1034     object = heap.FindObject(maybe_address)
   1035     if not object: continue
   1036     extra.append(str(object))
   1037   if len(extra) == 0: return line
   1038   return "%s  ;; %s" % (line, ", ".join(extra))
   1039 
   1040 
   1041 class HeapObject(object):
   1042   def __init__(self, heap, map, address):
   1043     self.heap = heap
   1044     self.map = map
   1045     self.address = address
   1046 
   1047   def Is(self, cls):
   1048     return isinstance(self, cls)
   1049 
   1050   def Print(self, p):
   1051     p.Print(str(self))
   1052 
   1053   def __str__(self):
   1054     instance_type = "???"
   1055     if self.map is not None:
   1056       instance_type = INSTANCE_TYPES[self.map.instance_type]
   1057     return "HeapObject(%s, %s)" % (self.heap.reader.FormatIntPtr(self.address),
   1058                                    instance_type)
   1059 
   1060   def ObjectField(self, offset):
   1061     field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
   1062     return self.heap.FindObjectOrSmi(field_value)
   1063 
   1064   def SmiField(self, offset):
   1065     field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
   1066     if (field_value & 1) == 0:
   1067       return field_value / 2
   1068     return None
   1069 
   1070 
   1071 class Map(HeapObject):
   1072   def Decode(self, offset, size, value):
   1073     return (value >> offset) & ((1 << size) - 1)
   1074 
   1075   # Instance Sizes
   1076   def InstanceSizesOffset(self):
   1077     return self.heap.PointerSize()
   1078 
   1079   def InstanceSizeOffset(self):
   1080     return self.InstanceSizesOffset()
   1081 
   1082   def InObjectProperties(self):
   1083     return self.InstanceSizeOffset() + 1
   1084 
   1085   def PreAllocatedPropertyFields(self):
   1086     return self.InObjectProperties() + 1
   1087 
   1088   def VisitorId(self):
   1089     return self.PreAllocatedPropertyFields() + 1
   1090 
   1091   # Instance Attributes
   1092   def InstanceAttributesOffset(self):
   1093     return self.InstanceSizesOffset() + self.heap.IntSize()
   1094 
   1095   def InstanceTypeOffset(self):
   1096     return self.InstanceAttributesOffset()
   1097 
   1098   def UnusedPropertyFieldsOffset(self):
   1099     return self.InstanceTypeOffset() + 1
   1100 
   1101   def BitFieldOffset(self):
   1102     return self.UnusedPropertyFieldsOffset() + 1
   1103 
   1104   def BitField2Offset(self):
   1105     return self.BitFieldOffset() + 1
   1106 
   1107   # Other fields
   1108   def PrototypeOffset(self):
   1109     return self.InstanceAttributesOffset() + self.heap.IntSize()
   1110 
   1111   def ConstructorOffset(self):
   1112     return self.PrototypeOffset() + self.heap.PointerSize()
   1113 
   1114   def TransitionsOrBackPointerOffset(self):
   1115     return self.ConstructorOffset() + self.heap.PointerSize()
   1116 
   1117   def DescriptorsOffset(self):
   1118     return self.TransitionsOrBackPointerOffset() + self.heap.PointerSize()
   1119 
   1120   def CodeCacheOffset(self):
   1121     return self.DescriptorsOffset() + self.heap.PointerSize()
   1122 
   1123   def DependentCodeOffset(self):
   1124     return self.CodeCacheOffset() + self.heap.PointerSize()
   1125 
   1126   def BitField3Offset(self):
   1127     return self.DependentCodeOffset() + self.heap.PointerSize()
   1128 
   1129   def ReadByte(self, offset):
   1130     return self.heap.reader.ReadU8(self.address + offset)
   1131 
   1132   def Print(self, p):
   1133     p.Print("Map(%08x)" % (self.address))
   1134     p.Print("- size: %d, inobject: %d, preallocated: %d, visitor: %d" % (
   1135         self.ReadByte(self.InstanceSizeOffset()),
   1136         self.ReadByte(self.InObjectProperties()),
   1137         self.ReadByte(self.PreAllocatedPropertyFields()),
   1138         self.VisitorId()))
   1139 
   1140     bitfield = self.ReadByte(self.BitFieldOffset())
   1141     bitfield2 = self.ReadByte(self.BitField2Offset())
   1142     p.Print("- %s, unused: %d, bf: %d, bf2: %d" % (
   1143         INSTANCE_TYPES[self.ReadByte(self.InstanceTypeOffset())],
   1144         self.ReadByte(self.UnusedPropertyFieldsOffset()),
   1145         bitfield, bitfield2))
   1146 
   1147     p.Print("- kind: %s" % (self.Decode(3, 5, bitfield2)))
   1148 
   1149     bitfield3 = self.ObjectField(self.BitField3Offset())
   1150     p.Print(
   1151         "- EnumLength: %d NumberOfOwnDescriptors: %d OwnsDescriptors: %s" % (
   1152             self.Decode(0, 11, bitfield3),
   1153             self.Decode(11, 11, bitfield3),
   1154             self.Decode(25, 1, bitfield3)))
   1155     p.Print("- IsShared: %s" % (self.Decode(22, 1, bitfield3)))
   1156     p.Print("- FunctionWithPrototype: %s" % (self.Decode(23, 1, bitfield3)))
   1157     p.Print("- DictionaryMap: %s" % (self.Decode(24, 1, bitfield3)))
   1158 
   1159     descriptors = self.ObjectField(self.DescriptorsOffset())
   1160     if descriptors.__class__ == FixedArray:
   1161       DescriptorArray(descriptors).Print(p)
   1162     else:
   1163       p.Print("Descriptors: %s" % (descriptors))
   1164 
   1165     transitions = self.ObjectField(self.TransitionsOrBackPointerOffset())
   1166     if transitions.__class__ == FixedArray:
   1167       TransitionArray(transitions).Print(p)
   1168     else:
   1169       p.Print("TransitionsOrBackPointer: %s" % (transitions))
   1170 
   1171   def __init__(self, heap, map, address):
   1172     HeapObject.__init__(self, heap, map, address)
   1173     self.instance_type = \
   1174         heap.reader.ReadU8(self.address + self.InstanceTypeOffset())
   1175 
   1176 
   1177 class String(HeapObject):
   1178   def LengthOffset(self):
   1179     # First word after the map is the hash, the second is the length.
   1180     return self.heap.PointerSize() * 2
   1181 
   1182   def __init__(self, heap, map, address):
   1183     HeapObject.__init__(self, heap, map, address)
   1184     self.length = self.SmiField(self.LengthOffset())
   1185 
   1186   def GetChars(self):
   1187     return "?string?"
   1188 
   1189   def Print(self, p):
   1190     p.Print(str(self))
   1191 
   1192   def __str__(self):
   1193     return "\"%s\"" % self.GetChars()
   1194 
   1195 
   1196 class SeqString(String):
   1197   def CharsOffset(self):
   1198     return self.heap.PointerSize() * 3
   1199 
   1200   def __init__(self, heap, map, address):
   1201     String.__init__(self, heap, map, address)
   1202     self.chars = heap.reader.ReadBytes(self.address + self.CharsOffset(),
   1203                                        self.length)
   1204 
   1205   def GetChars(self):
   1206     return self.chars
   1207 
   1208 
   1209 class ExternalString(String):
   1210   # TODO(vegorov) fix ExternalString for X64 architecture
   1211   RESOURCE_OFFSET = 12
   1212 
   1213   WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
   1214   WEBKIT_STRING_IMPL_CHARS_OFFSET = 8
   1215 
   1216   def __init__(self, heap, map, address):
   1217     String.__init__(self, heap, map, address)
   1218     reader = heap.reader
   1219     self.resource = \
   1220         reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET)
   1221     self.chars = "?external string?"
   1222     if not reader.IsValidAddress(self.resource): return
   1223     string_impl_address = self.resource + \
   1224         ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET
   1225     if not reader.IsValidAddress(string_impl_address): return
   1226     string_impl = reader.ReadU32(string_impl_address)
   1227     chars_ptr_address = string_impl + \
   1228         ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET
   1229     if not reader.IsValidAddress(chars_ptr_address): return
   1230     chars_ptr = reader.ReadU32(chars_ptr_address)
   1231     if not reader.IsValidAddress(chars_ptr): return
   1232     raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length)
   1233     self.chars = codecs.getdecoder("utf16")(raw_chars)[0]
   1234 
   1235   def GetChars(self):
   1236     return self.chars
   1237 
   1238 
   1239 class ConsString(String):
   1240   def LeftOffset(self):
   1241     return self.heap.PointerSize() * 3
   1242 
   1243   def RightOffset(self):
   1244     return self.heap.PointerSize() * 4
   1245 
   1246   def __init__(self, heap, map, address):
   1247     String.__init__(self, heap, map, address)
   1248     self.left = self.ObjectField(self.LeftOffset())
   1249     self.right = self.ObjectField(self.RightOffset())
   1250 
   1251   def GetChars(self):
   1252     try:
   1253       return self.left.GetChars() + self.right.GetChars()
   1254     except:
   1255       return "***CAUGHT EXCEPTION IN GROKDUMP***"
   1256 
   1257 
   1258 class Oddball(HeapObject):
   1259   # Should match declarations in objects.h
   1260   KINDS = [
   1261     "False",
   1262     "True",
   1263     "TheHole",
   1264     "Null",
   1265     "ArgumentMarker",
   1266     "Undefined",
   1267     "Other"
   1268   ]
   1269 
   1270   def ToStringOffset(self):
   1271     return self.heap.PointerSize()
   1272 
   1273   def ToNumberOffset(self):
   1274     return self.ToStringOffset() + self.heap.PointerSize()
   1275 
   1276   def KindOffset(self):
   1277     return self.ToNumberOffset() + self.heap.PointerSize()
   1278 
   1279   def __init__(self, heap, map, address):
   1280     HeapObject.__init__(self, heap, map, address)
   1281     self.to_string = self.ObjectField(self.ToStringOffset())
   1282     self.kind = self.SmiField(self.KindOffset())
   1283 
   1284   def Print(self, p):
   1285     p.Print(str(self))
   1286 
   1287   def __str__(self):
   1288     if self.to_string:
   1289       return "Oddball(%08x, <%s>)" % (self.address, str(self.to_string))
   1290     else:
   1291       kind = "???"
   1292       if 0 <= self.kind < len(Oddball.KINDS):
   1293         kind = Oddball.KINDS[self.kind]
   1294       return "Oddball(%08x, kind=%s)" % (self.address, kind)
   1295 
   1296 
   1297 class FixedArray(HeapObject):
   1298   def LengthOffset(self):
   1299     return self.heap.PointerSize()
   1300 
   1301   def ElementsOffset(self):
   1302     return self.heap.PointerSize() * 2
   1303 
   1304   def MemberOffset(self, i):
   1305     return self.ElementsOffset() + self.heap.PointerSize() * i
   1306 
   1307   def Get(self, i):
   1308     return self.ObjectField(self.MemberOffset(i))
   1309 
   1310   def __init__(self, heap, map, address):
   1311     HeapObject.__init__(self, heap, map, address)
   1312     self.length = self.SmiField(self.LengthOffset())
   1313 
   1314   def Print(self, p):
   1315     p.Print("FixedArray(%s) {" % self.heap.reader.FormatIntPtr(self.address))
   1316     p.Indent()
   1317     p.Print("length: %d" % self.length)
   1318     base_offset = self.ElementsOffset()
   1319     for i in xrange(self.length):
   1320       offset = base_offset + 4 * i
   1321       try:
   1322         p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
   1323       except TypeError:
   1324         p.Dedent()
   1325         p.Print("...")
   1326         p.Print("}")
   1327         return
   1328     p.Dedent()
   1329     p.Print("}")
   1330 
   1331   def __str__(self):
   1332     return "FixedArray(%08x, length=%d)" % (self.address, self.length)
   1333 
   1334 
   1335 class DescriptorArray(object):
   1336   def __init__(self, array):
   1337     self.array = array
   1338 
   1339   def Length(self):
   1340     return self.array.Get(0)
   1341 
   1342   def Decode(self, offset, size, value):
   1343     return (value >> offset) & ((1 << size) - 1)
   1344 
   1345   TYPES = [
   1346       "normal",
   1347       "field",
   1348       "function",
   1349       "callbacks"
   1350   ]
   1351 
   1352   def Type(self, value):
   1353     return DescriptorArray.TYPES[self.Decode(0, 3, value)]
   1354 
   1355   def Attributes(self, value):
   1356     attributes = self.Decode(3, 3, value)
   1357     result = []
   1358     if (attributes & 0): result += ["ReadOnly"]
   1359     if (attributes & 1): result += ["DontEnum"]
   1360     if (attributes & 2): result += ["DontDelete"]
   1361     return "[" + (",".join(result)) + "]"
   1362 
   1363   def Deleted(self, value):
   1364     return self.Decode(6, 1, value) == 1
   1365 
   1366   def FieldIndex(self, value):
   1367     return self.Decode(20, 11, value)
   1368 
   1369   def Pointer(self, value):
   1370     return self.Decode(6, 11, value)
   1371 
   1372   def Details(self, di, value):
   1373     return (
   1374         di,
   1375         self.Type(value),
   1376         self.Attributes(value),
   1377         self.FieldIndex(value),
   1378         self.Pointer(value)
   1379     )
   1380 
   1381 
   1382   def Print(self, p):
   1383     length = self.Length()
   1384     array = self.array
   1385 
   1386     p.Print("Descriptors(%08x, length=%d)" % (array.address, length))
   1387     p.Print("[et] %s" % (array.Get(1)))
   1388 
   1389     for di in xrange(length):
   1390       i = 2 + di * 3
   1391       p.Print("0x%x" % (array.address + array.MemberOffset(i)))
   1392       p.Print("[%i] name:    %s" % (di, array.Get(i + 0)))
   1393       p.Print("[%i] details: %s %s field-index %i pointer %i" % \
   1394               self.Details(di, array.Get(i + 1)))
   1395       p.Print("[%i] value:   %s" % (di, array.Get(i + 2)))
   1396 
   1397     end = self.array.length // 3
   1398     if length != end:
   1399       p.Print("[%i-%i] slack descriptors" % (length, end))
   1400 
   1401 
   1402 class TransitionArray(object):
   1403   def __init__(self, array):
   1404     self.array = array
   1405 
   1406   def IsSimpleTransition(self):
   1407     return self.array.length <= 2
   1408 
   1409   def Length(self):
   1410     # SimpleTransition cases
   1411     if self.IsSimpleTransition():
   1412       return self.array.length - 1
   1413     return (self.array.length - 3) // 2
   1414 
   1415   def Print(self, p):
   1416     length = self.Length()
   1417     array = self.array
   1418 
   1419     p.Print("Transitions(%08x, length=%d)" % (array.address, length))
   1420     p.Print("[backpointer] %s" % (array.Get(0)))
   1421     if self.IsSimpleTransition():
   1422       if length == 1:
   1423         p.Print("[simple target] %s" % (array.Get(1)))
   1424       return
   1425 
   1426     elements = array.Get(1)
   1427     if elements is not None:
   1428       p.Print("[elements   ] %s" % (elements))
   1429 
   1430     prototype = array.Get(2)
   1431     if prototype is not None:
   1432       p.Print("[prototype  ] %s" % (prototype))
   1433 
   1434     for di in xrange(length):
   1435       i = 3 + di * 2
   1436       p.Print("[%i] symbol: %s" % (di, array.Get(i + 0)))
   1437       p.Print("[%i] target: %s" % (di, array.Get(i + 1)))
   1438 
   1439 
   1440 class JSFunction(HeapObject):
   1441   def CodeEntryOffset(self):
   1442     return 3 * self.heap.PointerSize()
   1443 
   1444   def SharedOffset(self):
   1445     return 5 * self.heap.PointerSize()
   1446 
   1447   def __init__(self, heap, map, address):
   1448     HeapObject.__init__(self, heap, map, address)
   1449     code_entry = \
   1450         heap.reader.ReadU32(self.address + self.CodeEntryOffset())
   1451     self.code = heap.FindObject(code_entry - Code.HeaderSize(heap) + 1)
   1452     self.shared = self.ObjectField(self.SharedOffset())
   1453 
   1454   def Print(self, p):
   1455     source = "\n".join("  %s" % line for line in self._GetSource().split("\n"))
   1456     p.Print("JSFunction(%s) {" % self.heap.reader.FormatIntPtr(self.address))
   1457     p.Indent()
   1458     p.Print("inferred name: %s" % self.shared.inferred_name)
   1459     if self.shared.script.Is(Script) and self.shared.script.name.Is(String):
   1460       p.Print("script name: %s" % self.shared.script.name)
   1461     p.Print("source:")
   1462     p.PrintLines(self._GetSource().split("\n"))
   1463     p.Print("code:")
   1464     self.code.Print(p)
   1465     if self.code != self.shared.code:
   1466       p.Print("unoptimized code:")
   1467       self.shared.code.Print(p)
   1468     p.Dedent()
   1469     p.Print("}")
   1470 
   1471   def __str__(self):
   1472     inferred_name = ""
   1473     if self.shared is not None and self.shared.Is(SharedFunctionInfo):
   1474       inferred_name = self.shared.inferred_name
   1475     return "JSFunction(%s, %s) " % \
   1476           (self.heap.reader.FormatIntPtr(self.address), inferred_name)
   1477 
   1478   def _GetSource(self):
   1479     source = "?source?"
   1480     start = self.shared.start_position
   1481     end = self.shared.end_position
   1482     if not self.shared.script.Is(Script): return source
   1483     script_source = self.shared.script.source
   1484     if not script_source.Is(String): return source
   1485     if start and end:
   1486       source = script_source.GetChars()[start:end]
   1487     return source
   1488 
   1489 
   1490 class SharedFunctionInfo(HeapObject):
   1491   def CodeOffset(self):
   1492     return 2 * self.heap.PointerSize()
   1493 
   1494   def ScriptOffset(self):
   1495     return 7 * self.heap.PointerSize()
   1496 
   1497   def InferredNameOffset(self):
   1498     return 9 * self.heap.PointerSize()
   1499 
   1500   def EndPositionOffset(self):
   1501     return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize()
   1502 
   1503   def StartPositionAndTypeOffset(self):
   1504     return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize()
   1505 
   1506   def __init__(self, heap, map, address):
   1507     HeapObject.__init__(self, heap, map, address)
   1508     self.code = self.ObjectField(self.CodeOffset())
   1509     self.script = self.ObjectField(self.ScriptOffset())
   1510     self.inferred_name = self.ObjectField(self.InferredNameOffset())
   1511     if heap.PointerSize() == 8:
   1512       start_position_and_type = \
   1513           heap.reader.ReadU32(self.StartPositionAndTypeOffset())
   1514       self.start_position = start_position_and_type >> 2
   1515       pseudo_smi_end_position = \
   1516           heap.reader.ReadU32(self.EndPositionOffset())
   1517       self.end_position = pseudo_smi_end_position >> 2
   1518     else:
   1519       start_position_and_type = \
   1520           self.SmiField(self.StartPositionAndTypeOffset())
   1521       if start_position_and_type:
   1522         self.start_position = start_position_and_type >> 2
   1523       else:
   1524         self.start_position = None
   1525       self.end_position = \
   1526           self.SmiField(self.EndPositionOffset())
   1527 
   1528 
   1529 class Script(HeapObject):
   1530   def SourceOffset(self):
   1531     return self.heap.PointerSize()
   1532 
   1533   def NameOffset(self):
   1534     return self.SourceOffset() + self.heap.PointerSize()
   1535 
   1536   def __init__(self, heap, map, address):
   1537     HeapObject.__init__(self, heap, map, address)
   1538     self.source = self.ObjectField(self.SourceOffset())
   1539     self.name = self.ObjectField(self.NameOffset())
   1540 
   1541 
   1542 class CodeCache(HeapObject):
   1543   def DefaultCacheOffset(self):
   1544     return self.heap.PointerSize()
   1545 
   1546   def NormalTypeCacheOffset(self):
   1547     return self.DefaultCacheOffset() + self.heap.PointerSize()
   1548 
   1549   def __init__(self, heap, map, address):
   1550     HeapObject.__init__(self, heap, map, address)
   1551     self.default_cache = self.ObjectField(self.DefaultCacheOffset())
   1552     self.normal_type_cache = self.ObjectField(self.NormalTypeCacheOffset())
   1553 
   1554   def Print(self, p):
   1555     p.Print("CodeCache(%s) {" % self.heap.reader.FormatIntPtr(self.address))
   1556     p.Indent()
   1557     p.Print("default cache: %s" % self.default_cache)
   1558     p.Print("normal type cache: %s" % self.normal_type_cache)
   1559     p.Dedent()
   1560     p.Print("}")
   1561 
   1562 
   1563 class Code(HeapObject):
   1564   CODE_ALIGNMENT_MASK = (1 << 5) - 1
   1565 
   1566   def InstructionSizeOffset(self):
   1567     return self.heap.PointerSize()
   1568 
   1569   @staticmethod
   1570   def HeaderSize(heap):
   1571     return (heap.PointerSize() + heap.IntSize() + \
   1572         4 * heap.PointerSize() + 3 * heap.IntSize() + \
   1573         Code.CODE_ALIGNMENT_MASK) & ~Code.CODE_ALIGNMENT_MASK
   1574 
   1575   def __init__(self, heap, map, address):
   1576     HeapObject.__init__(self, heap, map, address)
   1577     self.entry = self.address + Code.HeaderSize(heap)
   1578     self.instruction_size = \
   1579         heap.reader.ReadU32(self.address + self.InstructionSizeOffset())
   1580 
   1581   def Print(self, p):
   1582     lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
   1583     p.Print("Code(%s) {" % self.heap.reader.FormatIntPtr(self.address))
   1584     p.Indent()
   1585     p.Print("instruction_size: %d" % self.instruction_size)
   1586     p.PrintLines(self._FormatLine(line) for line in lines)
   1587     p.Dedent()
   1588     p.Print("}")
   1589 
   1590   def _FormatLine(self, line):
   1591     return FormatDisasmLine(self.entry, self.heap, line)
   1592 
   1593 
   1594 class V8Heap(object):
   1595   CLASS_MAP = {
   1596     "SYMBOL_TYPE": SeqString,
   1597     "ONE_BYTE_SYMBOL_TYPE": SeqString,
   1598     "CONS_SYMBOL_TYPE": ConsString,
   1599     "CONS_ONE_BYTE_SYMBOL_TYPE": ConsString,
   1600     "EXTERNAL_SYMBOL_TYPE": ExternalString,
   1601     "EXTERNAL_SYMBOL_WITH_ONE_BYTE_DATA_TYPE": ExternalString,
   1602     "EXTERNAL_ONE_BYTE_SYMBOL_TYPE": ExternalString,
   1603     "SHORT_EXTERNAL_SYMBOL_TYPE": ExternalString,
   1604     "SHORT_EXTERNAL_SYMBOL_WITH_ONE_BYTE_DATA_TYPE": ExternalString,
   1605     "SHORT_EXTERNAL_ONE_BYTE_SYMBOL_TYPE": ExternalString,
   1606     "STRING_TYPE": SeqString,
   1607     "ONE_BYTE_STRING_TYPE": SeqString,
   1608     "CONS_STRING_TYPE": ConsString,
   1609     "CONS_ONE_BYTE_STRING_TYPE": ConsString,
   1610     "EXTERNAL_STRING_TYPE": ExternalString,
   1611     "EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE": ExternalString,
   1612     "EXTERNAL_ONE_BYTE_STRING_TYPE": ExternalString,
   1613     "MAP_TYPE": Map,
   1614     "ODDBALL_TYPE": Oddball,
   1615     "FIXED_ARRAY_TYPE": FixedArray,
   1616     "JS_FUNCTION_TYPE": JSFunction,
   1617     "SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo,
   1618     "SCRIPT_TYPE": Script,
   1619     "CODE_CACHE_TYPE": CodeCache,
   1620     "CODE_TYPE": Code,
   1621   }
   1622 
   1623   def __init__(self, reader, stack_map):
   1624     self.reader = reader
   1625     self.stack_map = stack_map
   1626     self.objects = {}
   1627 
   1628   def FindObjectOrSmi(self, tagged_address):
   1629     if (tagged_address & 1) == 0: return tagged_address / 2
   1630     return self.FindObject(tagged_address)
   1631 
   1632   def FindObject(self, tagged_address):
   1633     if tagged_address in self.objects:
   1634       return self.objects[tagged_address]
   1635     if (tagged_address & self.ObjectAlignmentMask()) != 1: return None
   1636     address = tagged_address - 1
   1637     if not self.reader.IsValidAddress(address): return None
   1638     map_tagged_address = self.reader.ReadUIntPtr(address)
   1639     if tagged_address == map_tagged_address:
   1640       # Meta map?
   1641       meta_map = Map(self, None, address)
   1642       instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type)
   1643       if instance_type_name != "MAP_TYPE": return None
   1644       meta_map.map = meta_map
   1645       object = meta_map
   1646     else:
   1647       map = self.FindMap(map_tagged_address)
   1648       if map is None: return None
   1649       instance_type_name = INSTANCE_TYPES.get(map.instance_type)
   1650       if instance_type_name is None: return None
   1651       cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
   1652       object = cls(self, map, address)
   1653     self.objects[tagged_address] = object
   1654     return object
   1655 
   1656   def FindMap(self, tagged_address):
   1657     if (tagged_address & self.MapAlignmentMask()) != 1: return None
   1658     address = tagged_address - 1
   1659     if not self.reader.IsValidAddress(address): return None
   1660     object = Map(self, None, address)
   1661     return object
   1662 
   1663   def IntSize(self):
   1664     return 4
   1665 
   1666   def PointerSize(self):
   1667     return self.reader.PointerSize()
   1668 
   1669   def ObjectAlignmentMask(self):
   1670     return self.PointerSize() - 1
   1671 
   1672   def MapAlignmentMask(self):
   1673     if self.reader.arch == MD_CPU_ARCHITECTURE_AMD64:
   1674       return (1 << 4) - 1
   1675     elif self.reader.arch == MD_CPU_ARCHITECTURE_ARM:
   1676       return (1 << 4) - 1
   1677     elif self.reader.arch == MD_CPU_ARCHITECTURE_ARM64:
   1678       return (1 << 4) - 1
   1679     elif self.reader.arch == MD_CPU_ARCHITECTURE_X86:
   1680       return (1 << 5) - 1
   1681 
   1682   def PageAlignmentMask(self):
   1683     return (1 << 20) - 1
   1684 
   1685 
   1686 class KnownObject(HeapObject):
   1687   def __init__(self, heap, known_name):
   1688     HeapObject.__init__(self, heap, None, None)
   1689     self.known_name = known_name
   1690 
   1691   def __str__(self):
   1692     return "<%s>" % self.known_name
   1693 
   1694 
   1695 class KnownMap(HeapObject):
   1696   def __init__(self, heap, known_name, instance_type):
   1697     HeapObject.__init__(self, heap, None, None)
   1698     self.instance_type = instance_type
   1699     self.known_name = known_name
   1700 
   1701   def __str__(self):
   1702     return "<%s>" % self.known_name
   1703 
   1704 
   1705 COMMENT_RE = re.compile(r"^C (0x[0-9a-fA-F]+) (.*)$")
   1706 PAGEADDRESS_RE = re.compile(
   1707     r"^P (mappage|oldpage) (0x[0-9a-fA-F]+)$")
   1708 
   1709 
   1710 class InspectionInfo(object):
   1711   def __init__(self, minidump_name, reader):
   1712     self.comment_file = minidump_name + ".comments"
   1713     self.address_comments = {}
   1714     self.page_address = {}
   1715     if os.path.exists(self.comment_file):
   1716       with open(self.comment_file, "r") as f:
   1717         lines = f.readlines()
   1718         f.close()
   1719 
   1720         for l in lines:
   1721           m = COMMENT_RE.match(l)
   1722           if m:
   1723             self.address_comments[int(m.group(1), 0)] = m.group(2)
   1724           m = PAGEADDRESS_RE.match(l)
   1725           if m:
   1726             self.page_address[m.group(1)] = int(m.group(2), 0)
   1727     self.reader = reader
   1728     self.styles = {}
   1729     self.color_addresses()
   1730     return
   1731 
   1732   def get_page_address(self, page_kind):
   1733     return self.page_address.get(page_kind, 0)
   1734 
   1735   def save_page_address(self, page_kind, address):
   1736     with open(self.comment_file, "a") as f:
   1737       f.write("P %s 0x%x\n" % (page_kind, address))
   1738       f.close()
   1739 
   1740   def color_addresses(self):
   1741     # Color all stack addresses.
   1742     exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
   1743     stack_top = self.reader.ExceptionSP()
   1744     stack_bottom = exception_thread.stack.start + \
   1745         exception_thread.stack.memory.data_size
   1746     frame_pointer = self.reader.ExceptionFP()
   1747     self.styles[frame_pointer] = "frame"
   1748     for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()):
   1749       # stack address
   1750       self.styles[slot] = "sa"
   1751     for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()):
   1752       maybe_address = self.reader.ReadUIntPtr(slot)
   1753       # stack value
   1754       self.styles[maybe_address] = "sv"
   1755       if slot == frame_pointer:
   1756         self.styles[slot] = "frame"
   1757         frame_pointer = maybe_address
   1758     self.styles[self.reader.ExceptionIP()] = "pc"
   1759 
   1760   def get_style_class(self, address):
   1761     return self.styles.get(address, None)
   1762 
   1763   def get_style_class_string(self, address):
   1764     style = self.get_style_class(address)
   1765     if style != None:
   1766       return " class=%s " % style
   1767     else:
   1768       return ""
   1769 
   1770   def set_comment(self, address, comment):
   1771     self.address_comments[address] = comment
   1772     with open(self.comment_file, "a") as f:
   1773       f.write("C 0x%x %s\n" % (address, comment))
   1774       f.close()
   1775 
   1776   def get_comment(self, address):
   1777     return self.address_comments.get(address, "")
   1778 
   1779 
   1780 class InspectionPadawan(object):
   1781   """The padawan can improve annotations by sensing well-known objects."""
   1782   def __init__(self, reader, heap):
   1783     self.reader = reader
   1784     self.heap = heap
   1785     self.known_first_map_page = 0
   1786     self.known_first_old_page = 0
   1787 
   1788   def __getattr__(self, name):
   1789     """An InspectionPadawan can be used instead of V8Heap, even though
   1790        it does not inherit from V8Heap (aka. mixin)."""
   1791     return getattr(self.heap, name)
   1792 
   1793   def GetPageOffset(self, tagged_address):
   1794     return tagged_address & self.heap.PageAlignmentMask()
   1795 
   1796   def IsInKnownMapSpace(self, tagged_address):
   1797     page_address = tagged_address & ~self.heap.PageAlignmentMask()
   1798     return page_address == self.known_first_map_page
   1799 
   1800   def IsInKnownOldSpace(self, tagged_address):
   1801     page_address = tagged_address & ~self.heap.PageAlignmentMask()
   1802     return page_address == self.known_first_old_page
   1803 
   1804   def ContainingKnownOldSpaceName(self, tagged_address):
   1805     page_address = tagged_address & ~self.heap.PageAlignmentMask()
   1806     if page_address == self.known_first_old_page: return "OLD_SPACE"
   1807     return None
   1808 
   1809   def SenseObject(self, tagged_address):
   1810     if self.IsInKnownOldSpace(tagged_address):
   1811       offset = self.GetPageOffset(tagged_address)
   1812       lookup_key = (self.ContainingKnownOldSpaceName(tagged_address), offset)
   1813       known_obj_name = KNOWN_OBJECTS.get(lookup_key)
   1814       if known_obj_name:
   1815         return KnownObject(self, known_obj_name)
   1816     if self.IsInKnownMapSpace(tagged_address):
   1817       known_map = self.SenseMap(tagged_address)
   1818       if known_map:
   1819         return known_map
   1820     found_obj = self.heap.FindObject(tagged_address)
   1821     if found_obj: return found_obj
   1822     address = tagged_address - 1
   1823     if self.reader.IsValidAddress(address):
   1824       map_tagged_address = self.reader.ReadUIntPtr(address)
   1825       map = self.SenseMap(map_tagged_address)
   1826       if map is None: return None
   1827       instance_type_name = INSTANCE_TYPES.get(map.instance_type)
   1828       if instance_type_name is None: return None
   1829       cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
   1830       return cls(self, map, address)
   1831     return None
   1832 
   1833   def SenseMap(self, tagged_address):
   1834     if self.IsInKnownMapSpace(tagged_address):
   1835       offset = self.GetPageOffset(tagged_address)
   1836       known_map_info = KNOWN_MAPS.get(offset)
   1837       if known_map_info:
   1838         known_map_type, known_map_name = known_map_info
   1839         return KnownMap(self, known_map_name, known_map_type)
   1840     found_map = self.heap.FindMap(tagged_address)
   1841     if found_map: return found_map
   1842     return None
   1843 
   1844   def FindObjectOrSmi(self, tagged_address):
   1845     """When used as a mixin in place of V8Heap."""
   1846     found_obj = self.SenseObject(tagged_address)
   1847     if found_obj: return found_obj
   1848     if (tagged_address & 1) == 0:
   1849       return "Smi(%d)" % (tagged_address / 2)
   1850     else:
   1851       return "Unknown(%s)" % self.reader.FormatIntPtr(tagged_address)
   1852 
   1853   def FindObject(self, tagged_address):
   1854     """When used as a mixin in place of V8Heap."""
   1855     raise NotImplementedError
   1856 
   1857   def FindMap(self, tagged_address):
   1858     """When used as a mixin in place of V8Heap."""
   1859     raise NotImplementedError
   1860 
   1861   def PrintKnowledge(self):
   1862     print "  known_first_map_page = %s\n"\
   1863           "  known_first_old_page = %s" % (
   1864           self.reader.FormatIntPtr(self.known_first_map_page),
   1865           self.reader.FormatIntPtr(self.known_first_old_page))
   1866 
   1867 WEB_HEADER = """
   1868 <!DOCTYPE html>
   1869 <html>
   1870 <head>
   1871 <meta content="text/html; charset=utf-8" http-equiv="content-type">
   1872 <style media="screen" type="text/css">
   1873 
   1874 .code {
   1875   font-family: monospace;
   1876 }
   1877 
   1878 .dmptable {
   1879   border-collapse : collapse;
   1880   border-spacing : 0px;
   1881   table-layout: fixed;
   1882 }
   1883 
   1884 .codedump {
   1885   border-collapse : collapse;
   1886   border-spacing : 0px;
   1887   table-layout: fixed;
   1888 }
   1889 
   1890 .addrcomments {
   1891   border : 0px;
   1892 }
   1893 
   1894 .register {
   1895   padding-right : 1em;
   1896 }
   1897 
   1898 .header {
   1899   clear : both;
   1900 }
   1901 
   1902 .header .navigation {
   1903   float : left;
   1904 }
   1905 
   1906 .header .dumpname {
   1907   float : right;
   1908 }
   1909 
   1910 tr.highlight-line {
   1911   background-color : yellow;
   1912 }
   1913 
   1914 .highlight {
   1915   background-color : magenta;
   1916 }
   1917 
   1918 tr.inexact-highlight-line {
   1919   background-color : pink;
   1920 }
   1921 
   1922 input {
   1923   background-color: inherit;
   1924   border: 1px solid LightGray;
   1925 }
   1926 
   1927 .dumpcomments {
   1928   border : 1px solid LightGray;
   1929   width : 32em;
   1930 }
   1931 
   1932 .regions td {
   1933   padding:0 15px 0 15px;
   1934 }
   1935 
   1936 .stackframe td {
   1937   background-color : cyan;
   1938 }
   1939 
   1940 .stackaddress, .sa {
   1941   background-color : LightGray;
   1942 }
   1943 
   1944 .stackval, .sv {
   1945   background-color : LightCyan;
   1946 }
   1947 
   1948 .frame {
   1949   background-color : cyan;
   1950 }
   1951 
   1952 .commentinput, .ci {
   1953   width : 20em;
   1954 }
   1955 
   1956 /* a.nodump */
   1957 a.nd:visited {
   1958   color : black;
   1959   text-decoration : none;
   1960 }
   1961 
   1962 a.nd:link {
   1963   color : black;
   1964   text-decoration : none;
   1965 }
   1966 
   1967 a:visited {
   1968   color : blueviolet;
   1969 }
   1970 
   1971 a:link {
   1972   color : blue;
   1973 }
   1974 
   1975 .disasmcomment {
   1976   color : DarkGreen;
   1977 }
   1978 
   1979 </style>
   1980 
   1981 <script type="application/javascript">
   1982 
   1983 var address_str = "address-";
   1984 var address_len = address_str.length;
   1985 
   1986 function comment() {
   1987   var s = event.srcElement.id;
   1988   var index = s.indexOf(address_str);
   1989   if (index >= 0) {
   1990     send_comment(s.substring(index + address_len), event.srcElement.value);
   1991   }
   1992 }
   1993 var c = comment;
   1994 
   1995 function send_comment(address, comment) {
   1996   xmlhttp = new XMLHttpRequest();
   1997   address = encodeURIComponent(address)
   1998   comment = encodeURIComponent(comment)
   1999   xmlhttp.open("GET",
   2000       "setcomment?%(query_dump)s&address=" + address +
   2001       "&comment=" + comment, true);
   2002   xmlhttp.send();
   2003 }
   2004 
   2005 var dump_str = "dump-";
   2006 var dump_len = dump_str.length;
   2007 
   2008 function dump_comment() {
   2009   var s = event.srcElement.id;
   2010   var index = s.indexOf(dump_str);
   2011   if (index >= 0) {
   2012     send_dump_desc(s.substring(index + dump_len), event.srcElement.value);
   2013   }
   2014 }
   2015 
   2016 function send_dump_desc(name, desc) {
   2017   xmlhttp = new XMLHttpRequest();
   2018   name = encodeURIComponent(name)
   2019   desc = encodeURIComponent(desc)
   2020   xmlhttp.open("GET",
   2021       "setdumpdesc?dump=" + name +
   2022       "&description=" + desc, true);
   2023   xmlhttp.send();
   2024 }
   2025 
   2026 function onpage(kind, address) {
   2027   xmlhttp = new XMLHttpRequest();
   2028   kind = encodeURIComponent(kind)
   2029   address = encodeURIComponent(address)
   2030   xmlhttp.onreadystatechange = function() {
   2031     if (xmlhttp.readyState==4 && xmlhttp.status==200) {
   2032       location.reload(true)
   2033     }
   2034   };
   2035   xmlhttp.open("GET",
   2036       "setpageaddress?%(query_dump)s&kind=" + kind +
   2037       "&address=" + address);
   2038   xmlhttp.send();
   2039 }
   2040 
   2041 </script>
   2042 
   2043 <title>Dump %(dump_name)s</title>
   2044 </head>
   2045 
   2046 <body>
   2047   <div class="header">
   2048     <form class="navigation" action=/search.html">
   2049       <a href="summary.html?%(query_dump)s">Context info</a>&nbsp;&nbsp;&nbsp;
   2050       <a href="info.html?%(query_dump)s">Dump info</a>&nbsp;&nbsp;&nbsp;
   2051       <a href="modules.html?%(query_dump)s">Modules</a>&nbsp;&nbsp;&nbsp;
   2052       &nbsp;
   2053       <input type="search" name="val">
   2054       <input type="submit" name="search" value="Search">
   2055       <input type="hidden" name="dump" value="%(dump_name)s">
   2056     </form>
   2057     <form class="navigation" action="disasm.html#highlight">
   2058       &nbsp;
   2059       &nbsp;
   2060       &nbsp;
   2061       <input type="search" name="val">
   2062       <input type="submit" name="disasm" value="Disasm">
   2063       &nbsp;
   2064       &nbsp;
   2065       &nbsp;
   2066       <a href="dumps.html">Dumps...</a>
   2067     </form>
   2068   </div>
   2069   <br>
   2070   <hr>
   2071 """
   2072 
   2073 
   2074 WEB_FOOTER = """
   2075 </body>
   2076 </html>
   2077 """
   2078 
   2079 
   2080 class WebParameterError(Exception):
   2081   def __init__(self, message):
   2082     Exception.__init__(self, message)
   2083 
   2084 
   2085 class InspectionWebHandler(BaseHTTPServer.BaseHTTPRequestHandler):
   2086   def formatter(self, query_components):
   2087     name = query_components.get("dump", [None])[0]
   2088     return self.server.get_dump_formatter(name)
   2089 
   2090   def send_success_html_headers(self):
   2091     self.send_response(200)
   2092     self.send_header("Cache-Control", "no-cache, no-store, must-revalidate")
   2093     self.send_header("Pragma", "no-cache")
   2094     self.send_header("Expires", "0")
   2095     self.send_header('Content-type','text/html')
   2096     self.end_headers()
   2097     return
   2098 
   2099   def do_GET(self):
   2100     try:
   2101       parsedurl = urlparse.urlparse(self.path)
   2102       query_components = urlparse.parse_qs(parsedurl.query)
   2103       if parsedurl.path == "/dumps.html":
   2104         self.send_success_html_headers()
   2105         out_buffer = StringIO.StringIO()
   2106         self.server.output_dumps(out_buffer)
   2107         self.wfile.write(out_buffer.getvalue())
   2108       elif parsedurl.path == "/summary.html":
   2109         self.send_success_html_headers()
   2110         out_buffer = StringIO.StringIO()
   2111         self.formatter(query_components).output_summary(out_buffer)
   2112         self.wfile.write(out_buffer.getvalue())
   2113       elif parsedurl.path == "/info.html":
   2114         self.send_success_html_headers()
   2115         out_buffer = StringIO.StringIO()
   2116         self.formatter(query_components).output_info(out_buffer)
   2117         self.wfile.write(out_buffer.getvalue())
   2118       elif parsedurl.path == "/modules.html":
   2119         self.send_success_html_headers()
   2120         out_buffer = StringIO.StringIO()
   2121         self.formatter(query_components).output_modules(out_buffer)
   2122         self.wfile.write(out_buffer.getvalue())
   2123       elif parsedurl.path == "/search.html" or parsedurl.path == "/s":
   2124         address = query_components.get("val", [])
   2125         if len(address) != 1:
   2126           self.send_error(404, "Invalid params")
   2127           return
   2128         self.send_success_html_headers()
   2129         out_buffer = StringIO.StringIO()
   2130         self.formatter(query_components).output_search_res(
   2131             out_buffer, address[0])
   2132         self.wfile.write(out_buffer.getvalue())
   2133       elif parsedurl.path == "/disasm.html":
   2134         address = query_components.get("val", [])
   2135         exact = query_components.get("exact", ["on"])
   2136         if len(address) != 1:
   2137           self.send_error(404, "Invalid params")
   2138           return
   2139         self.send_success_html_headers()
   2140         out_buffer = StringIO.StringIO()
   2141         self.formatter(query_components).output_disasm(
   2142             out_buffer, address[0], exact[0])
   2143         self.wfile.write(out_buffer.getvalue())
   2144       elif parsedurl.path == "/data.html":
   2145         address = query_components.get("val", [])
   2146         datakind = query_components.get("type", ["address"])
   2147         if len(address) == 1 and len(datakind) == 1:
   2148           self.send_success_html_headers()
   2149           out_buffer = StringIO.StringIO()
   2150           self.formatter(query_components).output_data(
   2151               out_buffer, address[0], datakind[0])
   2152           self.wfile.write(out_buffer.getvalue())
   2153         else:
   2154           self.send_error(404,'Invalid params')
   2155       elif parsedurl.path == "/setdumpdesc":
   2156         name = query_components.get("dump", [""])
   2157         description = query_components.get("description", [""])
   2158         if len(name) == 1 and len(description) == 1:
   2159           name = name[0]
   2160           description = description[0]
   2161           if self.server.set_dump_desc(name, description):
   2162             self.send_success_html_headers()
   2163             self.wfile.write("OK")
   2164             return
   2165         self.send_error(404,'Invalid params')
   2166       elif parsedurl.path == "/setcomment":
   2167         address = query_components.get("address", [])
   2168         comment = query_components.get("comment", [""])
   2169         if len(address) == 1 and len(comment) == 1:
   2170           address = address[0]
   2171           comment = comment[0]
   2172           self.formatter(query_components).set_comment(address, comment)
   2173           self.send_success_html_headers()
   2174           self.wfile.write("OK")
   2175         else:
   2176           self.send_error(404,'Invalid params')
   2177       elif parsedurl.path == "/setpageaddress":
   2178         kind = query_components.get("kind", [])
   2179         address = query_components.get("address", [""])
   2180         if len(kind) == 1 and len(address) == 1:
   2181           kind = kind[0]
   2182           address = address[0]
   2183           self.formatter(query_components).set_page_address(kind, address)
   2184           self.send_success_html_headers()
   2185           self.wfile.write("OK")
   2186         else:
   2187           self.send_error(404,'Invalid params')
   2188       else:
   2189         self.send_error(404,'File Not Found: %s' % self.path)
   2190 
   2191     except IOError:
   2192       self.send_error(404,'File Not Found: %s' % self.path)
   2193 
   2194     except WebParameterError as e:
   2195       self.send_error(404, 'Web parameter error: %s' % e.message)
   2196 
   2197 
   2198 HTML_REG_FORMAT = "<span class=\"register\"><b>%s</b>:&nbsp;%s</span><br/>\n"
   2199 
   2200 
   2201 class InspectionWebFormatter(object):
   2202   CONTEXT_FULL = 0
   2203   CONTEXT_SHORT = 1
   2204 
   2205   def __init__(self, switches, minidump_name, http_server):
   2206     self.dumpfilename = os.path.split(minidump_name)[1]
   2207     self.encfilename = urllib.urlencode({ 'dump' : self.dumpfilename })
   2208     self.reader = MinidumpReader(switches, minidump_name)
   2209     self.server = http_server
   2210 
   2211     # Set up the heap
   2212     exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
   2213     stack_top = self.reader.ExceptionSP()
   2214     stack_bottom = exception_thread.stack.start + \
   2215         exception_thread.stack.memory.data_size
   2216     stack_map = {self.reader.ExceptionIP(): -1}
   2217     for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()):
   2218       maybe_address = self.reader.ReadUIntPtr(slot)
   2219       if not maybe_address in stack_map:
   2220         stack_map[maybe_address] = slot
   2221     self.heap = V8Heap(self.reader, stack_map)
   2222 
   2223     self.padawan = InspectionPadawan(self.reader, self.heap)
   2224     self.comments = InspectionInfo(minidump_name, self.reader)
   2225     self.padawan.known_first_old_page = (
   2226         self.comments.get_page_address("oldpage"))
   2227     self.padawan.known_first_map_page = (
   2228         self.comments.get_page_address("mappage"))
   2229 
   2230   def set_comment(self, straddress, comment):
   2231     try:
   2232       address = int(straddress, 0)
   2233       self.comments.set_comment(address, comment)
   2234     except ValueError:
   2235       print "Invalid address"
   2236 
   2237   def set_page_address(self, kind, straddress):
   2238     try:
   2239       address = int(straddress, 0)
   2240       if kind == "oldpage":
   2241         self.padawan.known_first_old_page = address
   2242       elif kind == "mappage":
   2243         self.padawan.known_first_map_page = address
   2244       self.comments.save_page_address(kind, address)
   2245     except ValueError:
   2246       print "Invalid address"
   2247 
   2248   def td_from_address(self, f, address):
   2249     f.write("<td %s>" % self.comments.get_style_class_string(address))
   2250 
   2251   def format_address(self, maybeaddress, straddress = None):
   2252     if maybeaddress is None:
   2253       return "not in dump"
   2254     else:
   2255       if straddress is None:
   2256         straddress = "0x" + self.reader.FormatIntPtr(maybeaddress)
   2257       style_class = ""
   2258       if not self.reader.IsValidAddress(maybeaddress):
   2259         style_class = "class=nd"
   2260       return ("<a %s href=s?%s&amp;val=%s>%s</a>" %
   2261               (style_class, self.encfilename, straddress, straddress))
   2262 
   2263   def output_header(self, f):
   2264     f.write(WEB_HEADER %
   2265         { "query_dump" : self.encfilename,
   2266           "dump_name"  : cgi.escape(self.dumpfilename) })
   2267 
   2268   def output_footer(self, f):
   2269     f.write(WEB_FOOTER)
   2270 
   2271   MAX_CONTEXT_STACK = 2048
   2272 
   2273   def output_summary(self, f):
   2274     self.output_header(f)
   2275     f.write('<div class="code">')
   2276     self.output_context(f, InspectionWebFormatter.CONTEXT_SHORT)
   2277     self.output_disasm_pc(f)
   2278 
   2279     # Output stack
   2280     exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
   2281     stack_top = self.reader.ExceptionSP()
   2282     stack_bottom = min(exception_thread.stack.start + \
   2283         exception_thread.stack.memory.data_size,
   2284         stack_top + self.MAX_CONTEXT_STACK)
   2285     self.output_words(f, stack_top - 16, stack_bottom, stack_top, "Stack")
   2286 
   2287     f.write('</div>')
   2288     self.output_footer(f)
   2289     return
   2290 
   2291   def output_info(self, f):
   2292     self.output_header(f)
   2293     f.write("<h3>Dump info</h3>")
   2294     f.write("Description: ")
   2295     self.server.output_dump_desc_field(f, self.dumpfilename)
   2296     f.write("<br>")
   2297     f.write("Filename: ")
   2298     f.write("<span class=\"code\">%s</span><br>" % (self.dumpfilename))
   2299     dt = datetime.datetime.fromtimestamp(self.reader.header.time_date_stampt)
   2300     f.write("Timestamp: %s<br>" % dt.strftime('%Y-%m-%d %H:%M:%S'))
   2301     self.output_context(f, InspectionWebFormatter.CONTEXT_FULL)
   2302     self.output_address_ranges(f)
   2303     self.output_footer(f)
   2304     return
   2305 
   2306   def output_address_ranges(self, f):
   2307     regions = {}
   2308     def print_region(_reader, start, size, _location):
   2309       regions[start] = size
   2310     self.reader.ForEachMemoryRegion(print_region)
   2311     f.write("<h3>Available memory regions</h3>")
   2312     f.write('<div class="code">')
   2313     f.write("<table class=\"regions\">")
   2314     f.write("<thead><tr>")
   2315     f.write("<th>Start address</th>")
   2316     f.write("<th>End address</th>")
   2317     f.write("<th>Number of bytes</th>")
   2318     f.write("</tr></thead>")
   2319     for start in sorted(regions):
   2320       size = regions[start]
   2321       f.write("<tr>")
   2322       f.write("<td>%s</td>" % self.format_address(start))
   2323       f.write("<td>&nbsp;%s</td>" % self.format_address(start + size))
   2324       f.write("<td>&nbsp;%d</td>" % size)
   2325       f.write("</tr>")
   2326     f.write("</table>")
   2327     f.write('</div>')
   2328     return
   2329 
   2330   def output_module_details(self, f, module):
   2331     f.write("<b>%s</b>" % GetModuleName(self.reader, module))
   2332     file_version = GetVersionString(module.version_info.dwFileVersionMS,
   2333                                     module.version_info.dwFileVersionLS)
   2334     product_version = GetVersionString(module.version_info.dwProductVersionMS,
   2335                                        module.version_info.dwProductVersionLS)
   2336     f.write("<br>&nbsp;&nbsp;")
   2337     f.write("base: %s" % self.reader.FormatIntPtr(module.base_of_image))
   2338     f.write("<br>&nbsp;&nbsp;")
   2339     f.write("  end: %s" % self.reader.FormatIntPtr(module.base_of_image +
   2340                                             module.size_of_image))
   2341     f.write("<br>&nbsp;&nbsp;")
   2342     f.write("  file version: %s" % file_version)
   2343     f.write("<br>&nbsp;&nbsp;")
   2344     f.write("  product version: %s" % product_version)
   2345     f.write("<br>&nbsp;&nbsp;")
   2346     time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp)
   2347     f.write("  timestamp: %s" % time_date_stamp)
   2348     f.write("<br>");
   2349 
   2350   def output_modules(self, f):
   2351     self.output_header(f)
   2352     f.write('<div class="code">')
   2353     for module in self.reader.module_list.modules:
   2354       self.output_module_details(f, module)
   2355     f.write("</div>")
   2356     self.output_footer(f)
   2357     return
   2358 
   2359   def output_context(self, f, details):
   2360     exception_thread = self.reader.thread_map[self.reader.exception.thread_id]
   2361     f.write("<h3>Exception context</h3>")
   2362     f.write('<div class="code">')
   2363     f.write("Thread id: %d" % exception_thread.id)
   2364     f.write("&nbsp;&nbsp; Exception code: %08X<br/>" %
   2365             self.reader.exception.exception.code)
   2366     if details == InspectionWebFormatter.CONTEXT_FULL:
   2367       if self.reader.exception.exception.parameter_count > 0:
   2368         f.write("&nbsp;&nbsp; Exception parameters: ")
   2369         for i in xrange(0, self.reader.exception.exception.parameter_count):
   2370           f.write("%08x" % self.reader.exception.exception.information[i])
   2371         f.write("<br><br>")
   2372 
   2373     for r in CONTEXT_FOR_ARCH[self.reader.arch]:
   2374       f.write(HTML_REG_FORMAT %
   2375               (r, self.format_address(self.reader.Register(r))))
   2376     # TODO(vitalyr): decode eflags.
   2377     if self.reader.arch in [MD_CPU_ARCHITECTURE_ARM, MD_CPU_ARCHITECTURE_ARM64]:
   2378       f.write("<b>cpsr</b>: %s" % bin(self.reader.exception_context.cpsr)[2:])
   2379     else:
   2380       f.write("<b>eflags</b>: %s" %
   2381               bin(self.reader.exception_context.eflags)[2:])
   2382     f.write('</div>')
   2383     return
   2384 
   2385   def align_down(self, a, size):
   2386     alignment_correction = a % size
   2387     return a - alignment_correction
   2388 
   2389   def align_up(self, a, size):
   2390     alignment_correction = (size - 1) - ((a + size - 1) % size)
   2391     return a + alignment_correction
   2392 
   2393   def format_object(self, address):
   2394     heap_object = self.padawan.SenseObject(address)
   2395     return cgi.escape(str(heap_object or ""))
   2396 
   2397   def output_data(self, f, straddress, datakind):
   2398     try:
   2399       self.output_header(f)
   2400       address = int(straddress, 0)
   2401       if not self.reader.IsValidAddress(address):
   2402         f.write("<h3>Address 0x%x not found in the dump.</h3>" % address)
   2403         return
   2404       region = self.reader.FindRegion(address)
   2405       if datakind == "address":
   2406         self.output_words(f, region[0], region[0] + region[1], address, "Dump")
   2407       elif datakind == "ascii":
   2408         self.output_ascii(f, region[0], region[0] + region[1], address)
   2409       self.output_footer(f)
   2410 
   2411     except ValueError:
   2412       f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress)
   2413     return
   2414 
   2415   def output_words(self, f, start_address, end_address,
   2416                    highlight_address, desc):
   2417     region = self.reader.FindRegion(highlight_address)
   2418     if region is None:
   2419       f.write("<h3>Address 0x%x not found in the dump.</h3>" %
   2420               (highlight_address))
   2421       return
   2422     size = self.heap.PointerSize()
   2423     start_address = self.align_down(start_address, size)
   2424     low = self.align_down(region[0], size)
   2425     high = self.align_up(region[0] + region[1], size)
   2426     if start_address < low:
   2427       start_address = low
   2428     end_address = self.align_up(end_address, size)
   2429     if end_address > high:
   2430       end_address = high
   2431 
   2432     expand = ""
   2433     if start_address != low or end_address != high:
   2434       expand = ("(<a href=\"data.html?%s&amp;val=0x%x#highlight\">"
   2435                 " more..."
   2436                 " </a>)" %
   2437                 (self.encfilename, highlight_address))
   2438 
   2439     f.write("<h3>%s 0x%x - 0x%x, "
   2440             "highlighting <a href=\"#highlight\">0x%x</a> %s</h3>" %
   2441             (desc, start_address, end_address, highlight_address, expand))
   2442     f.write('<div class="code">')
   2443     f.write("<table class=codedump>")
   2444 
   2445     for j in xrange(0, end_address - start_address, size):
   2446       slot = start_address + j
   2447       heap_object = ""
   2448       maybe_address = None
   2449       end_region = region[0] + region[1]
   2450       if slot < region[0] or slot + size > end_region:
   2451         straddress = "0x"
   2452         for i in xrange(end_region, slot + size):
   2453           straddress += "??"
   2454         for i in reversed(
   2455             xrange(max(slot, region[0]), min(slot + size, end_region))):
   2456           straddress += "%02x" % self.reader.ReadU8(i)
   2457         for i in xrange(slot, region[0]):
   2458           straddress += "??"
   2459       else:
   2460         maybe_address = self.reader.ReadUIntPtr(slot)
   2461         straddress = self.format_address(maybe_address)
   2462         if maybe_address:
   2463           heap_object = self.format_object(maybe_address)
   2464 
   2465       address_fmt = "%s&nbsp;</td>"
   2466       if slot == highlight_address:
   2467         f.write("<tr class=highlight-line>")
   2468         address_fmt = "<a id=highlight></a>%s&nbsp;</td>"
   2469       elif slot < highlight_address and highlight_address < slot + size:
   2470         f.write("<tr class=inexact-highlight-line>")
   2471         address_fmt = "<a id=highlight></a>%s&nbsp;</td>"
   2472       else:
   2473         f.write("<tr>")
   2474 
   2475       f.write("<td>")
   2476       self.output_comment_box(f, "da-", slot)
   2477       f.write("</td>")
   2478       self.td_from_address(f, slot)
   2479       f.write(address_fmt % self.format_address(slot))
   2480       self.td_from_address(f, maybe_address)
   2481       f.write(":&nbsp;%s&nbsp;</td>" % straddress)
   2482       f.write("<td>")
   2483       if maybe_address != None:
   2484         self.output_comment_box(
   2485             f, "sv-" + self.reader.FormatIntPtr(slot), maybe_address)
   2486       f.write("</td>")
   2487       f.write("<td>%s</td>" % (heap_object or ''))
   2488       f.write("</tr>")
   2489     f.write("</table>")
   2490     f.write("</div>")
   2491     return
   2492 
   2493   def output_ascii(self, f, start_address, end_address, highlight_address):
   2494     region = self.reader.FindRegion(highlight_address)
   2495     if region is None:
   2496       f.write("<h3>Address %x not found in the dump.</h3>" %
   2497           highlight_address)
   2498       return
   2499     if start_address < region[0]:
   2500       start_address = region[0]
   2501     if end_address > region[0] + region[1]:
   2502       end_address = region[0] + region[1]
   2503 
   2504     expand = ""
   2505     if start_address != region[0] or end_address != region[0] + region[1]:
   2506       link = ("data.html?%s&amp;val=0x%x&amp;type=ascii#highlight" %
   2507               (self.encfilename, highlight_address))
   2508       expand = "(<a href=\"%s\">more...</a>)" % link
   2509 
   2510     f.write("<h3>ASCII dump 0x%x - 0x%x, highlighting 0x%x %s</h3>" %
   2511             (start_address, end_address, highlight_address, expand))
   2512 
   2513     line_width = 64
   2514 
   2515     f.write('<div class="code">')
   2516 
   2517     start = self.align_down(start_address, line_width)
   2518 
   2519     for i in xrange(end_address - start):
   2520       address = start + i
   2521       if address % 64 == 0:
   2522         if address != start:
   2523           f.write("<br>")
   2524         f.write("0x%08x:&nbsp;" % address)
   2525       if address < start_address:
   2526         f.write("&nbsp;")
   2527       else:
   2528         if address == highlight_address:
   2529           f.write("<span class=\"highlight\">")
   2530         code = self.reader.ReadU8(address)
   2531         if code < 127 and code >= 32:
   2532           f.write("&#")
   2533           f.write(str(code))
   2534           f.write(";")
   2535         else:
   2536           f.write("&middot;")
   2537         if address == highlight_address:
   2538           f.write("</span>")
   2539     f.write("</div>")
   2540     return
   2541 
   2542   def output_disasm(self, f, straddress, strexact):
   2543     try:
   2544       self.output_header(f)
   2545       address = int(straddress, 0)
   2546       if not self.reader.IsValidAddress(address):
   2547         f.write("<h3>Address 0x%x not found in the dump.</h3>" % address)
   2548         return
   2549       region = self.reader.FindRegion(address)
   2550       self.output_disasm_range(
   2551           f, region[0], region[0] + region[1], address, strexact == "on")
   2552       self.output_footer(f)
   2553     except ValueError:
   2554       f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress)
   2555     return
   2556 
   2557   def output_disasm_range(
   2558       self, f, start_address, end_address, highlight_address, exact):
   2559     region = self.reader.FindRegion(highlight_address)
   2560     if start_address < region[0]:
   2561       start_address = region[0]
   2562     if end_address > region[0] + region[1]:
   2563       end_address = region[0] + region[1]
   2564     count = end_address - start_address
   2565     lines = self.reader.GetDisasmLines(start_address, count)
   2566     found = False
   2567     if exact:
   2568       for line in lines:
   2569         if line[0] + start_address == highlight_address:
   2570           found = True
   2571           break
   2572       if not found:
   2573         start_address = highlight_address
   2574         count = end_address - start_address
   2575         lines = self.reader.GetDisasmLines(highlight_address, count)
   2576     expand = ""
   2577     if start_address != region[0] or end_address != region[0] + region[1]:
   2578       exactness = ""
   2579       if exact and not found and end_address == region[0] + region[1]:
   2580         exactness = "&amp;exact=off"
   2581       expand = ("(<a href=\"disasm.html?%s%s"
   2582                 "&amp;val=0x%x#highlight\">more...</a>)" %
   2583                 (self.encfilename, exactness, highlight_address))
   2584 
   2585     f.write("<h3>Disassembling 0x%x - 0x%x, highlighting 0x%x %s</h3>" %
   2586             (start_address, end_address, highlight_address, expand))
   2587     f.write('<div class="code">')
   2588     f.write("<table class=\"codedump\">");
   2589     for i in xrange(len(lines)):
   2590       line = lines[i]
   2591       next_address = count
   2592       if i + 1 < len(lines):
   2593         next_line = lines[i + 1]
   2594         next_address = next_line[0]
   2595       self.format_disasm_line(
   2596           f, start_address, line, next_address, highlight_address)
   2597     f.write("</table>")
   2598     f.write("</div>")
   2599     return
   2600 
   2601   def annotate_disasm_addresses(self, line):
   2602     extra = []
   2603     for m in ADDRESS_RE.finditer(line):
   2604       maybe_address = int(m.group(0), 16)
   2605       formatted_address = self.format_address(maybe_address, m.group(0))
   2606       line = line.replace(m.group(0), formatted_address)
   2607       object_info = self.padawan.SenseObject(maybe_address)
   2608       if not object_info:
   2609         continue
   2610       extra.append(cgi.escape(str(object_info)))
   2611     if len(extra) == 0:
   2612       return line
   2613     return ("%s <span class=disasmcomment>;; %s</span>" %
   2614             (line, ", ".join(extra)))
   2615 
   2616   def format_disasm_line(
   2617       self, f, start, line, next_address, highlight_address):
   2618     line_address = start + line[0]
   2619     address_fmt = "  <td>%s</td>"
   2620     if line_address == highlight_address:
   2621       f.write("<tr class=highlight-line>")
   2622       address_fmt = "  <td><a id=highlight>%s</a></td>"
   2623     elif (line_address < highlight_address and
   2624           highlight_address < next_address + start):
   2625       f.write("<tr class=inexact-highlight-line>")
   2626       address_fmt = "  <td><a id=highlight>%s</a></td>"
   2627     else:
   2628       f.write("<tr>")
   2629     num_bytes = next_address - line[0]
   2630     stack_slot = self.heap.stack_map.get(line_address)
   2631     marker = ""
   2632     if stack_slot:
   2633       marker = "=>"
   2634     op_offset = 3 * num_bytes - 1
   2635 
   2636     code = line[1]
   2637     # Compute the actual call target which the disassembler is too stupid
   2638     # to figure out (it adds the call offset to the disassembly offset rather
   2639     # than the absolute instruction address).
   2640     if self.heap.reader.arch == MD_CPU_ARCHITECTURE_X86:
   2641       if code.startswith("e8"):
   2642         words = code.split()
   2643         if len(words) > 6 and words[5] == "call":
   2644           offset = int(words[4] + words[3] + words[2] + words[1], 16)
   2645           target = (line_address + offset + 5) & 0xFFFFFFFF
   2646           code = code.replace(words[6], "0x%08x" % target)
   2647     # TODO(jkummerow): port this hack to ARM and x64.
   2648 
   2649     opcodes = code[:op_offset]
   2650     code = self.annotate_disasm_addresses(code[op_offset:])
   2651     f.write("  <td>")
   2652     self.output_comment_box(f, "codel-", line_address)
   2653     f.write("</td>")
   2654     f.write(address_fmt % marker)
   2655     f.write("  ")
   2656     self.td_from_address(f, line_address)
   2657     f.write(self.format_address(line_address))
   2658     f.write(" (+0x%x)</td>" % line[0])
   2659     f.write("<td>:&nbsp;%s&nbsp;</td>" % opcodes)
   2660     f.write("<td>%s</td>" % code)
   2661     f.write("</tr>")
   2662 
   2663   def output_comment_box(self, f, prefix, address):
   2664     comment = self.comments.get_comment(address)
   2665     value = ""
   2666     if comment:
   2667       value = " value=\"%s\"" % cgi.escape(comment)
   2668     f.write("<input type=text class=ci "
   2669             "id=%s-address-0x%s onchange=c()%s>" %
   2670             (prefix,
   2671              self.reader.FormatIntPtr(address),
   2672              value))
   2673 
   2674   MAX_FOUND_RESULTS = 100
   2675 
   2676   def output_find_results(self, f, results):
   2677     f.write("Addresses")
   2678     toomany = len(results) > self.MAX_FOUND_RESULTS
   2679     if toomany:
   2680       f.write("(found %i results, displaying only first %i)" %
   2681               (len(results), self.MAX_FOUND_RESULTS))
   2682     f.write(": ")
   2683     results = sorted(results)
   2684     results = results[:min(len(results), self.MAX_FOUND_RESULTS)]
   2685     for address in results:
   2686       f.write("<span %s>%s</span>" %
   2687               (self.comments.get_style_class_string(address),
   2688                self.format_address(address)))
   2689     if toomany:
   2690       f.write("...")
   2691 
   2692 
   2693   def output_page_info(self, f, page_kind, page_address, my_page_address):
   2694     if my_page_address == page_address and page_address != 0:
   2695       f.write("Marked first %s page." % page_kind)
   2696     else:
   2697       f.write("<span id=\"%spage\" style=\"display:none\">" % page_kind)
   2698       f.write("Marked first %s page." % page_kind)
   2699       f.write("</span>\n")
   2700       f.write("<button onclick=\"onpage('%spage', '0x%x')\">" %
   2701               (page_kind, my_page_address))
   2702       f.write("Mark as first %s page</button>" % page_kind)
   2703     return
   2704 
   2705   def output_search_res(self, f, straddress):
   2706     try:
   2707       self.output_header(f)
   2708       f.write("<h3>Search results for %s</h3>" % straddress)
   2709 
   2710       address = int(straddress, 0)
   2711 
   2712       f.write("Comment: ")
   2713       self.output_comment_box(f, "search-", address)
   2714       f.write("<br>")
   2715 
   2716       page_address = address & ~self.heap.PageAlignmentMask()
   2717 
   2718       f.write("Page info: ")
   2719       self.output_page_info(f, "old", self.padawan.known_first_old_page, \
   2720                             page_address)
   2721       self.output_page_info(f, "map", self.padawan.known_first_map_page, \
   2722                             page_address)
   2723 
   2724       if not self.reader.IsValidAddress(address):
   2725         f.write("<h3>The contents at address %s not found in the dump.</h3>" % \
   2726                 straddress)
   2727       else:
   2728         # Print as words
   2729         self.output_words(f, address - 8, address + 32, address, "Dump")
   2730 
   2731         # Print as ASCII
   2732         f.write("<hr>")
   2733         self.output_ascii(f, address, address + 256, address)
   2734 
   2735         # Print as code
   2736         f.write("<hr>")
   2737         self.output_disasm_range(f, address - 16, address + 16, address, True)
   2738 
   2739       aligned_res, unaligned_res = self.reader.FindWordList(address)
   2740 
   2741       if len(aligned_res) > 0:
   2742         f.write("<h3>Occurrences of 0x%x at aligned addresses</h3>" %
   2743                 address)
   2744         self.output_find_results(f, aligned_res)
   2745 
   2746       if len(unaligned_res) > 0:
   2747         f.write("<h3>Occurrences of 0x%x at unaligned addresses</h3>" % \
   2748                 address)
   2749         self.output_find_results(f, unaligned_res)
   2750 
   2751       if len(aligned_res) + len(unaligned_res) == 0:
   2752         f.write("<h3>No occurences of 0x%x found in the dump</h3>" % address)
   2753 
   2754       self.output_footer(f)
   2755 
   2756     except ValueError:
   2757       f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress)
   2758     return
   2759 
   2760   def output_disasm_pc(self, f):
   2761     address = self.reader.ExceptionIP()
   2762     if not self.reader.IsValidAddress(address):
   2763       return
   2764     self.output_disasm_range(f, address - 16, address + 16, address, True)
   2765 
   2766 
   2767 WEB_DUMPS_HEADER = """
   2768 <!DOCTYPE html>
   2769 <html>
   2770 <head>
   2771 <meta content="text/html; charset=utf-8" http-equiv="content-type">
   2772 <style media="screen" type="text/css">
   2773 
   2774 .dumplist {
   2775   border-collapse : collapse;
   2776   border-spacing : 0px;
   2777   font-family: monospace;
   2778 }
   2779 
   2780 .dumpcomments {
   2781   border : 1px solid LightGray;
   2782   width : 32em;
   2783 }
   2784 
   2785 </style>
   2786 
   2787 <script type="application/javascript">
   2788 
   2789 var dump_str = "dump-";
   2790 var dump_len = dump_str.length;
   2791 
   2792 function dump_comment() {
   2793   var s = event.srcElement.id;
   2794   var index = s.indexOf(dump_str);
   2795   if (index >= 0) {
   2796     send_dump_desc(s.substring(index + dump_len), event.srcElement.value);
   2797   }
   2798 }
   2799 
   2800 function send_dump_desc(name, desc) {
   2801   xmlhttp = new XMLHttpRequest();
   2802   name = encodeURIComponent(name)
   2803   desc = encodeURIComponent(desc)
   2804   xmlhttp.open("GET",
   2805       "setdumpdesc?dump=" + name +
   2806       "&description=" + desc, true);
   2807   xmlhttp.send();
   2808 }
   2809 
   2810 </script>
   2811 
   2812 <title>Dump list</title>
   2813 </head>
   2814 
   2815 <body>
   2816 """
   2817 
   2818 WEB_DUMPS_FOOTER = """
   2819 </body>
   2820 </html>
   2821 """
   2822 
   2823 DUMP_FILE_RE = re.compile(r"[-_0-9a-zA-Z][-\._0-9a-zA-Z]*\.dmp$")
   2824 
   2825 
   2826 class InspectionWebServer(BaseHTTPServer.HTTPServer):
   2827   def __init__(self, port_number, switches, minidump_name):
   2828     BaseHTTPServer.HTTPServer.__init__(
   2829         self, ('', port_number), InspectionWebHandler)
   2830     splitpath = os.path.split(minidump_name)
   2831     self.dumppath = splitpath[0]
   2832     self.dumpfilename = splitpath[1]
   2833     self.default_formatter = InspectionWebFormatter(
   2834         switches, minidump_name, self)
   2835     self.formatters = { self.dumpfilename : self.default_formatter }
   2836     self.switches = switches
   2837 
   2838   def output_dump_desc_field(self, f, name):
   2839     try:
   2840       descfile = open(os.path.join(self.dumppath, name + ".desc"), "r")
   2841       desc = descfile.readline()
   2842       descfile.close()
   2843     except IOError:
   2844       desc = ""
   2845     f.write("<input type=\"text\" class=\"dumpcomments\" "
   2846             "id=\"dump-%s\" onchange=\"dump_comment()\" value=\"%s\">\n" %
   2847             (cgi.escape(name), desc))
   2848 
   2849   def set_dump_desc(self, name, description):
   2850     if not DUMP_FILE_RE.match(name):
   2851       return False
   2852     fname = os.path.join(self.dumppath, name)
   2853     if not os.path.isfile(fname):
   2854       return False
   2855     fname = fname + ".desc"
   2856     descfile = open(fname, "w")
   2857     descfile.write(description)
   2858     descfile.close()
   2859     return True
   2860 
   2861   def get_dump_formatter(self, name):
   2862     if name is None:
   2863       return self.default_formatter
   2864     else:
   2865       if not DUMP_FILE_RE.match(name):
   2866         raise WebParameterError("Invalid name '%s'" % name)
   2867       formatter = self.formatters.get(name, None)
   2868       if formatter is None:
   2869         try:
   2870           formatter = InspectionWebFormatter(
   2871               self.switches, os.path.join(self.dumppath, name), self)
   2872           self.formatters[name] = formatter
   2873         except IOError:
   2874           raise WebParameterError("Could not open dump '%s'" % name)
   2875       return formatter
   2876 
   2877   def output_dumps(self, f):
   2878     f.write(WEB_DUMPS_HEADER)
   2879     f.write("<h3>List of available dumps</h3>")
   2880     f.write("<table class=\"dumplist\">\n")
   2881     f.write("<thead><tr>")
   2882     f.write("<th>Name</th>")
   2883     f.write("<th>File time</th>")
   2884     f.write("<th>Comment</th>")
   2885     f.write("</tr></thead>")
   2886     dumps_by_time = {}
   2887     for fname in os.listdir(self.dumppath):
   2888       if DUMP_FILE_RE.match(fname):
   2889         mtime = os.stat(os.path.join(self.dumppath, fname)).st_mtime
   2890         fnames = dumps_by_time.get(mtime, [])
   2891         fnames.append(fname)
   2892         dumps_by_time[mtime] = fnames
   2893 
   2894     for mtime in sorted(dumps_by_time, reverse=True):
   2895       fnames = dumps_by_time[mtime]
   2896       for fname in fnames:
   2897         f.write("<tr>\n")
   2898         f.write("<td><a href=\"summary.html?%s\">%s</a></td>\n" % (
   2899             (urllib.urlencode({ 'dump' : fname }), fname)))
   2900         f.write("<td>&nbsp;&nbsp;&nbsp;")
   2901         f.write(datetime.datetime.fromtimestamp(mtime))
   2902         f.write("</td>")
   2903         f.write("<td>&nbsp;&nbsp;&nbsp;")
   2904         self.output_dump_desc_field(f, fname)
   2905         f.write("</td>")
   2906         f.write("</tr>\n")
   2907     f.write("</table>\n")
   2908     f.write(WEB_DUMPS_FOOTER)
   2909     return
   2910 
   2911 class InspectionShell(cmd.Cmd):
   2912   def __init__(self, reader, heap):
   2913     cmd.Cmd.__init__(self)
   2914     self.reader = reader
   2915     self.heap = heap
   2916     self.padawan = InspectionPadawan(reader, heap)
   2917     self.prompt = "(grok) "
   2918 
   2919   def do_da(self, address):
   2920     """
   2921      Print ASCII string starting at specified address.
   2922     """
   2923     address = int(address, 16)
   2924     string = ""
   2925     while self.reader.IsValidAddress(address):
   2926       code = self.reader.ReadU8(address)
   2927       if code < 128:
   2928         string += chr(code)
   2929       else:
   2930         break
   2931       address += 1
   2932     if string == "":
   2933       print "Not an ASCII string at %s" % self.reader.FormatIntPtr(address)
   2934     else:
   2935       print "%s\n" % string
   2936 
   2937   def do_dd(self, args):
   2938     """
   2939      Interpret memory in the given region [address, address + num * word_size)
   2940      (if available) as a sequence of words. Automatic alignment is not performed.
   2941      If the num is not specified, a default value of 16 words is used.
   2942      Synopsis: dd 0x<address> 0x<num>
   2943     """
   2944     args = args.split(' ')
   2945     start = int(args[0], 16)
   2946     num = int(args[1], 16) if len(args) > 1 else 0x10
   2947     if (start & self.heap.ObjectAlignmentMask()) != 0:
   2948       print "Warning: Dumping un-aligned memory, is this what you had in mind?"
   2949     for i in xrange(0,
   2950                     self.reader.PointerSize() * num,
   2951                     self.reader.PointerSize()):
   2952       slot = start + i
   2953       if not self.reader.IsValidAddress(slot):
   2954         print "Address is not contained within the minidump!"
   2955         return
   2956       maybe_address = self.reader.ReadUIntPtr(slot)
   2957       heap_object = self.padawan.SenseObject(maybe_address)
   2958       print "%s: %s %s" % (self.reader.FormatIntPtr(slot),
   2959                            self.reader.FormatIntPtr(maybe_address),
   2960                            heap_object or '')
   2961 
   2962   def do_do(self, address):
   2963     """
   2964      Interpret memory at the given address as a V8 object. Automatic
   2965      alignment makes sure that you can pass tagged as well as un-tagged
   2966      addresses.
   2967     """
   2968     address = int(address, 16)
   2969     if (address & self.heap.ObjectAlignmentMask()) == 0:
   2970       address = address + 1
   2971     elif (address & self.heap.ObjectAlignmentMask()) != 1:
   2972       print "Address doesn't look like a valid pointer!"
   2973       return
   2974     heap_object = self.padawan.SenseObject(address)
   2975     if heap_object:
   2976       heap_object.Print(Printer())
   2977     else:
   2978       print "Address cannot be interpreted as object!"
   2979 
   2980   def do_do_desc(self, address):
   2981     """
   2982       Print a descriptor array in a readable format.
   2983     """
   2984     start = int(address, 16)
   2985     if ((start & 1) == 1): start = start - 1
   2986     DescriptorArray(FixedArray(self.heap, None, start)).Print(Printer())
   2987 
   2988   def do_do_map(self, address):
   2989     """
   2990       Print a descriptor array in a readable format.
   2991     """
   2992     start = int(address, 16)
   2993     if ((start & 1) == 1): start = start - 1
   2994     Map(self.heap, None, start).Print(Printer())
   2995 
   2996   def do_do_trans(self, address):
   2997     """
   2998       Print a transition array in a readable format.
   2999     """
   3000     start = int(address, 16)
   3001     if ((start & 1) == 1): start = start - 1
   3002     TransitionArray(FixedArray(self.heap, None, start)).Print(Printer())
   3003 
   3004   def do_dp(self, address):
   3005     """
   3006      Interpret memory at the given address as being on a V8 heap page
   3007      and print information about the page header (if available).
   3008     """
   3009     address = int(address, 16)
   3010     page_address = address & ~self.heap.PageAlignmentMask()
   3011     if self.reader.IsValidAddress(page_address):
   3012       raise NotImplementedError
   3013     else:
   3014       print "Page header is not available!"
   3015 
   3016   def do_k(self, arguments):
   3017     """
   3018      Teach V8 heap layout information to the inspector. This increases
   3019      the amount of annotations the inspector can produce while dumping
   3020      data. The first page of each heap space is of particular interest
   3021      because it contains known objects that do not move.
   3022     """
   3023     self.padawan.PrintKnowledge()
   3024 
   3025   def do_ko(self, address):
   3026     """
   3027      Teach V8 heap layout information to the inspector. Set the first
   3028      old space page by passing any pointer into that page.
   3029     """
   3030     address = int(address, 16)
   3031     page_address = address & ~self.heap.PageAlignmentMask()
   3032     self.padawan.known_first_old_page = page_address
   3033 
   3034   def do_km(self, address):
   3035     """
   3036      Teach V8 heap layout information to the inspector. Set the first
   3037      map-space page by passing any pointer into that page.
   3038     """
   3039     address = int(address, 16)
   3040     page_address = address & ~self.heap.PageAlignmentMask()
   3041     self.padawan.known_first_map_page = page_address
   3042 
   3043   def do_list(self, smth):
   3044     """
   3045      List all available memory regions.
   3046     """
   3047     def print_region(reader, start, size, location):
   3048       print "  %s - %s (%d bytes)" % (reader.FormatIntPtr(start),
   3049                                       reader.FormatIntPtr(start + size),
   3050                                       size)
   3051     print "Available memory regions:"
   3052     self.reader.ForEachMemoryRegion(print_region)
   3053 
   3054   def do_lm(self, arg):
   3055     """
   3056      List details for all loaded modules in the minidump. An argument can
   3057      be passed to limit the output to only those modules that contain the
   3058      argument as a substring (case insensitive match).
   3059     """
   3060     for module in self.reader.module_list.modules:
   3061       if arg:
   3062         name = GetModuleName(self.reader, module).lower()
   3063         if name.find(arg.lower()) >= 0:
   3064           PrintModuleDetails(self.reader, module)
   3065       else:
   3066         PrintModuleDetails(self.reader, module)
   3067     print
   3068 
   3069   def do_s(self, word):
   3070     """
   3071      Search for a given word in available memory regions. The given word
   3072      is expanded to full pointer size and searched at aligned as well as
   3073      un-aligned memory locations. Use 'sa' to search aligned locations
   3074      only.
   3075     """
   3076     try:
   3077       word = int(word, 0)
   3078     except ValueError:
   3079       print "Malformed word, prefix with '0x' to use hexadecimal format."
   3080       return
   3081     print "Searching for word %d/0x%s:" % (word, self.reader.FormatIntPtr(word))
   3082     self.reader.FindWord(word)
   3083 
   3084   def do_sh(self, none):
   3085     """
   3086      Search for the V8 Heap object in all available memory regions. You
   3087      might get lucky and find this rare treasure full of invaluable
   3088      information.
   3089     """
   3090     raise NotImplementedError
   3091 
   3092   def do_u(self, args):
   3093     """
   3094      Unassemble memory in the region [address, address + size). If the
   3095      size is not specified, a default value of 32 bytes is used.
   3096      Synopsis: u 0x<address> 0x<size>
   3097     """
   3098     args = args.split(' ')
   3099     start = int(args[0], 16)
   3100     size = int(args[1], 16) if len(args) > 1 else 0x20
   3101     if not self.reader.IsValidAddress(start):
   3102       print "Address is not contained within the minidump!"
   3103       return
   3104     lines = self.reader.GetDisasmLines(start, size)
   3105     for line in lines:
   3106       print FormatDisasmLine(start, self.heap, line)
   3107     print
   3108 
   3109   def do_EOF(self, none):
   3110     raise KeyboardInterrupt
   3111 
   3112 EIP_PROXIMITY = 64
   3113 
   3114 CONTEXT_FOR_ARCH = {
   3115     MD_CPU_ARCHITECTURE_AMD64:
   3116       ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip',
   3117        'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15'],
   3118     MD_CPU_ARCHITECTURE_ARM:
   3119       ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9',
   3120        'r10', 'r11', 'r12', 'sp', 'lr', 'pc'],
   3121     MD_CPU_ARCHITECTURE_ARM64:
   3122       ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9',
   3123        'r10', 'r11', 'r12', 'r13', 'r14', 'r15', 'r16', 'r17', 'r18', 'r19',
   3124        'r20', 'r21', 'r22', 'r23', 'r24', 'r25', 'r26', 'r27', 'r28',
   3125        'fp', 'lr', 'sp', 'pc'],
   3126     MD_CPU_ARCHITECTURE_X86:
   3127       ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip']
   3128 }
   3129 
   3130 KNOWN_MODULES = {'chrome.exe', 'chrome.dll'}
   3131 
   3132 def GetVersionString(ms, ls):
   3133   return "%d.%d.%d.%d" % (ms >> 16, ms & 0xffff, ls >> 16, ls & 0xffff)
   3134 
   3135 
   3136 def GetModuleName(reader, module):
   3137   name = reader.ReadMinidumpString(module.module_name_rva)
   3138   # simplify for path manipulation
   3139   name = name.encode('utf-8')
   3140   return str(os.path.basename(str(name).replace("\\", "/")))
   3141 
   3142 
   3143 def PrintModuleDetails(reader, module):
   3144   print "%s" % GetModuleName(reader, module)
   3145   file_version = GetVersionString(module.version_info.dwFileVersionMS,
   3146                                   module.version_info.dwFileVersionLS)
   3147   product_version = GetVersionString(module.version_info.dwProductVersionMS,
   3148                                      module.version_info.dwProductVersionLS)
   3149   print "  base: %s" % reader.FormatIntPtr(module.base_of_image)
   3150   print "  end: %s" % reader.FormatIntPtr(module.base_of_image +
   3151                                           module.size_of_image)
   3152   print "  file version: %s" % file_version
   3153   print "  product version: %s" % product_version
   3154   time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp)
   3155   print "  timestamp: %s" % time_date_stamp
   3156 
   3157 
   3158 def AnalyzeMinidump(options, minidump_name):
   3159   reader = MinidumpReader(options, minidump_name)
   3160   heap = None
   3161   DebugPrint("========================================")
   3162   if reader.exception is None:
   3163     print "Minidump has no exception info"
   3164   else:
   3165     print "Exception info:"
   3166     exception_thread = reader.thread_map[reader.exception.thread_id]
   3167     print "  thread id: %d" % exception_thread.id
   3168     print "  code: %08X" % reader.exception.exception.code
   3169     print "  context:"
   3170     for r in CONTEXT_FOR_ARCH[reader.arch]:
   3171       print "    %s: %s" % (r, reader.FormatIntPtr(reader.Register(r)))
   3172     # TODO(vitalyr): decode eflags.
   3173     if reader.arch in [MD_CPU_ARCHITECTURE_ARM, MD_CPU_ARCHITECTURE_ARM64]:
   3174       print "    cpsr: %s" % bin(reader.exception_context.cpsr)[2:]
   3175     else:
   3176       print "    eflags: %s" % bin(reader.exception_context.eflags)[2:]
   3177 
   3178     print
   3179     print "  modules:"
   3180     for module in reader.module_list.modules:
   3181       name = GetModuleName(reader, module)
   3182       if name in KNOWN_MODULES:
   3183         print "    %s at %08X" % (name, module.base_of_image)
   3184         reader.TryLoadSymbolsFor(name, module)
   3185     print
   3186 
   3187     stack_top = reader.ExceptionSP()
   3188     stack_bottom = exception_thread.stack.start + \
   3189         exception_thread.stack.memory.data_size
   3190     stack_map = {reader.ExceptionIP(): -1}
   3191     for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
   3192       maybe_address = reader.ReadUIntPtr(slot)
   3193       if not maybe_address in stack_map:
   3194         stack_map[maybe_address] = slot
   3195     heap = V8Heap(reader, stack_map)
   3196 
   3197     print "Disassembly around exception.eip:"
   3198     eip_symbol = reader.FindSymbol(reader.ExceptionIP())
   3199     if eip_symbol is not None:
   3200       print eip_symbol
   3201     disasm_start = reader.ExceptionIP() - EIP_PROXIMITY
   3202     disasm_bytes = 2 * EIP_PROXIMITY
   3203     if (options.full):
   3204       full_range = reader.FindRegion(reader.ExceptionIP())
   3205       if full_range is not None:
   3206         disasm_start = full_range[0]
   3207         disasm_bytes = full_range[1]
   3208 
   3209     lines = reader.GetDisasmLines(disasm_start, disasm_bytes)
   3210 
   3211     if not lines:
   3212       print "Could not disassemble using %s." % OBJDUMP_BIN
   3213       print "Pass path to architecture specific objdump via --objdump?"
   3214 
   3215     for line in lines:
   3216       print FormatDisasmLine(disasm_start, heap, line)
   3217     print
   3218 
   3219   if heap is None:
   3220     heap = V8Heap(reader, None)
   3221 
   3222   if options.full:
   3223     FullDump(reader, heap)
   3224 
   3225   if options.command:
   3226     InspectionShell(reader, heap).onecmd(options.command)
   3227 
   3228   if options.shell:
   3229     try:
   3230       InspectionShell(reader, heap).cmdloop("type help to get help")
   3231     except KeyboardInterrupt:
   3232       print "Kthxbye."
   3233   elif not options.command:
   3234     if reader.exception is not None:
   3235       frame_pointer = reader.ExceptionFP()
   3236       in_oom_dump_area = False
   3237       print "Annotated stack (from exception.esp to bottom):"
   3238       for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
   3239         ascii_content = [c if c >= '\x20' and c <  '\x7f' else '.'
   3240                          for c in reader.ReadBytes(slot, reader.PointerSize())]
   3241         maybe_address = reader.ReadUIntPtr(slot)
   3242         maybe_address_contents = None
   3243         if maybe_address >= stack_top and maybe_address <= stack_bottom:
   3244           maybe_address_contents = reader.ReadUIntPtr(maybe_address)
   3245           if maybe_address_contents == 0xdecade00:
   3246             in_oom_dump_area = True
   3247         heap_object = heap.FindObject(maybe_address)
   3248         maybe_symbol = reader.FindSymbol(maybe_address)
   3249         oom_comment = ""
   3250         if in_oom_dump_area:
   3251           if maybe_address_contents == 0xdecade00:
   3252             oom_comment = " <----- HeapStats start marker"
   3253           elif maybe_address_contents == 0xdecade01:
   3254             oom_comment = " <----- HeapStats end marker"
   3255           elif maybe_address_contents is not None:
   3256             oom_comment = " %d (%d Mbytes)" % (maybe_address_contents,
   3257                                             maybe_address_contents >> 20)
   3258         if slot == frame_pointer:
   3259           maybe_symbol = "<---- frame pointer"
   3260           frame_pointer = maybe_address
   3261         print "%s: %s %s %s%s" % (reader.FormatIntPtr(slot),
   3262                                   reader.FormatIntPtr(maybe_address),
   3263                                    "".join(ascii_content),
   3264                                    maybe_symbol or "",
   3265                                    oom_comment)
   3266         if maybe_address_contents == 0xdecade01:
   3267           in_oom_dump_area = False
   3268         if heap_object:
   3269           heap_object.Print(Printer())
   3270           print
   3271 
   3272   reader.Dispose()
   3273 
   3274 
   3275 if __name__ == "__main__":
   3276   parser = optparse.OptionParser(USAGE)
   3277   parser.add_option("-s", "--shell", dest="shell", action="store_true",
   3278                     help="start an interactive inspector shell")
   3279   parser.add_option("-w", "--web", dest="web", action="store_true",
   3280                     help="start a web server on localhost:%i" % PORT_NUMBER)
   3281   parser.add_option("-c", "--command", dest="command", default="",
   3282                     help="run an interactive inspector shell command and exit")
   3283   parser.add_option("-f", "--full", dest="full", action="store_true",
   3284                     help="dump all information contained in the minidump")
   3285   parser.add_option("--symdir", dest="symdir", default=".",
   3286                     help="directory containing *.pdb.sym file with symbols")
   3287   parser.add_option("--objdump",
   3288                     default="/usr/bin/objdump",
   3289                     help="objdump tool to use [default: %default]")
   3290   options, args = parser.parse_args()
   3291   if os.path.exists(options.objdump):
   3292     disasm.OBJDUMP_BIN = options.objdump
   3293     OBJDUMP_BIN = options.objdump
   3294   else:
   3295     print "Cannot find %s, falling back to default objdump" % options.objdump
   3296   if len(args) != 1:
   3297     parser.print_help()
   3298     sys.exit(1)
   3299   if options.web:
   3300     try:
   3301       server = InspectionWebServer(PORT_NUMBER, options, args[0])
   3302       print 'Started httpserver on port ' , PORT_NUMBER
   3303       webbrowser.open('http://localhost:%i/summary.html' % PORT_NUMBER)
   3304       server.serve_forever()
   3305     except KeyboardInterrupt:
   3306       print '^C received, shutting down the web server'
   3307       server.socket.close()
   3308   else:
   3309     AnalyzeMinidump(options, args[0])
   3310