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