Home | History | Annotate | Download | only in tools
      1 #!/usr/bin/env python
      2 #
      3 # Copyright 2011 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 ctypes
     31 import mmap
     32 import optparse
     33 import os
     34 import disasm
     35 import sys
     36 import types
     37 import codecs
     38 import re
     39 
     40 
     41 USAGE="""usage: %prog [OPTION]...
     42 
     43 Minidump analyzer.
     44 
     45 Shows the processor state at the point of exception including the
     46 stack of the active thread and the referenced objects in the V8
     47 heap. Code objects are disassembled and the addresses linked from the
     48 stack (pushed return addresses) are marked with "=>".
     49 
     50 
     51 Examples:
     52   $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp
     53 """
     54 
     55 DEBUG=False
     56 
     57 
     58 def DebugPrint(s):
     59   if not DEBUG: return
     60   print s
     61 
     62 
     63 class Descriptor(object):
     64   """Descriptor of a structure in a memory."""
     65 
     66   def __init__(self, fields):
     67     self.fields = fields
     68     self.is_flexible = False
     69     for _, type_or_func in fields:
     70       if isinstance(type_or_func, types.FunctionType):
     71         self.is_flexible = True
     72         break
     73     if not self.is_flexible:
     74       self.ctype = Descriptor._GetCtype(fields)
     75       self.size = ctypes.sizeof(self.ctype)
     76 
     77   def Read(self, memory, offset):
     78     if self.is_flexible:
     79       fields_copy = self.fields[:]
     80       last = 0
     81       for name, type_or_func in fields_copy:
     82         if isinstance(type_or_func, types.FunctionType):
     83           partial_ctype = Descriptor._GetCtype(fields_copy[:last])
     84           partial_object = partial_ctype.from_buffer(memory, offset)
     85           type = type_or_func(partial_object)
     86           if type is not None:
     87             fields_copy[last] = (name, type)
     88             last += 1
     89         else:
     90           last += 1
     91       complete_ctype = Descriptor._GetCtype(fields_copy[:last])
     92     else:
     93       complete_ctype = self.ctype
     94     return complete_ctype.from_buffer(memory, offset)
     95 
     96   @staticmethod
     97   def _GetCtype(fields):
     98     class Raw(ctypes.Structure):
     99       _fields_ = fields
    100       _pack_ = 1
    101 
    102       def __str__(self):
    103         return "{" + ", ".join("%s: %s" % (field, self.__getattribute__(field))
    104                                for field, _ in Raw._fields_) + "}"
    105     return Raw
    106 
    107 
    108 # Set of structures and constants that describe the layout of minidump
    109 # files. Based on MSDN and Google Breakpad.
    110 
    111 MINIDUMP_HEADER = Descriptor([
    112   ("signature", ctypes.c_uint32),
    113   ("version", ctypes.c_uint32),
    114   ("stream_count", ctypes.c_uint32),
    115   ("stream_directories_rva", ctypes.c_uint32),
    116   ("checksum", ctypes.c_uint32),
    117   ("time_date_stampt", ctypes.c_uint32),
    118   ("flags", ctypes.c_uint64)
    119 ])
    120 
    121 MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([
    122   ("data_size", ctypes.c_uint32),
    123   ("rva", ctypes.c_uint32)
    124 ])
    125 
    126 MINIDUMP_DIRECTORY = Descriptor([
    127   ("stream_type", ctypes.c_uint32),
    128   ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
    129 ])
    130 
    131 MD_EXCEPTION_MAXIMUM_PARAMETERS = 15
    132 
    133 MINIDUMP_EXCEPTION = Descriptor([
    134   ("code", ctypes.c_uint32),
    135   ("flags", ctypes.c_uint32),
    136   ("record", ctypes.c_uint64),
    137   ("address", ctypes.c_uint64),
    138   ("parameter_count", ctypes.c_uint32),
    139   ("unused_alignment", ctypes.c_uint32),
    140   ("information", ctypes.c_uint64 * MD_EXCEPTION_MAXIMUM_PARAMETERS)
    141 ])
    142 
    143 MINIDUMP_EXCEPTION_STREAM = Descriptor([
    144   ("thread_id", ctypes.c_uint32),
    145   ("unused_alignment", ctypes.c_uint32),
    146   ("exception", MINIDUMP_EXCEPTION.ctype),
    147   ("thread_context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
    148 ])
    149 
    150 # Stream types.
    151 MD_UNUSED_STREAM = 0
    152 MD_RESERVED_STREAM_0 = 1
    153 MD_RESERVED_STREAM_1 = 2
    154 MD_THREAD_LIST_STREAM = 3
    155 MD_MODULE_LIST_STREAM = 4
    156 MD_MEMORY_LIST_STREAM = 5
    157 MD_EXCEPTION_STREAM = 6
    158 MD_SYSTEM_INFO_STREAM = 7
    159 MD_THREAD_EX_LIST_STREAM = 8
    160 MD_MEMORY_64_LIST_STREAM = 9
    161 MD_COMMENT_STREAM_A = 10
    162 MD_COMMENT_STREAM_W = 11
    163 MD_HANDLE_DATA_STREAM = 12
    164 MD_FUNCTION_TABLE_STREAM = 13
    165 MD_UNLOADED_MODULE_LIST_STREAM = 14
    166 MD_MISC_INFO_STREAM = 15
    167 MD_MEMORY_INFO_LIST_STREAM = 16
    168 MD_THREAD_INFO_LIST_STREAM = 17
    169 MD_HANDLE_OPERATION_LIST_STREAM = 18
    170 
    171 MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE = 80
    172 
    173 MINIDUMP_FLOATING_SAVE_AREA_X86 = Descriptor([
    174   ("control_word", ctypes.c_uint32),
    175   ("status_word", ctypes.c_uint32),
    176   ("tag_word", ctypes.c_uint32),
    177   ("error_offset", ctypes.c_uint32),
    178   ("error_selector", ctypes.c_uint32),
    179   ("data_offset", ctypes.c_uint32),
    180   ("data_selector", ctypes.c_uint32),
    181   ("register_area", ctypes.c_uint8 * MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE),
    182   ("cr0_npx_state", ctypes.c_uint32)
    183 ])
    184 
    185 MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE = 512
    186 
    187 # Context flags.
    188 MD_CONTEXT_X86 = 0x00010000
    189 MD_CONTEXT_X86_CONTROL = (MD_CONTEXT_X86 | 0x00000001)
    190 MD_CONTEXT_X86_INTEGER = (MD_CONTEXT_X86 | 0x00000002)
    191 MD_CONTEXT_X86_SEGMENTS = (MD_CONTEXT_X86 | 0x00000004)
    192 MD_CONTEXT_X86_FLOATING_POINT = (MD_CONTEXT_X86 | 0x00000008)
    193 MD_CONTEXT_X86_DEBUG_REGISTERS = (MD_CONTEXT_X86 | 0x00000010)
    194 MD_CONTEXT_X86_EXTENDED_REGISTERS = (MD_CONTEXT_X86 | 0x00000020)
    195 
    196 def EnableOnFlag(type, flag):
    197   return lambda o: [None, type][int((o.context_flags & flag) != 0)]
    198 
    199 MINIDUMP_CONTEXT_X86 = Descriptor([
    200   ("context_flags", ctypes.c_uint32),
    201   # MD_CONTEXT_X86_DEBUG_REGISTERS.
    202   ("dr0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
    203   ("dr1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
    204   ("dr2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
    205   ("dr3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
    206   ("dr6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
    207   ("dr7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
    208   # MD_CONTEXT_X86_FLOATING_POINT.
    209   ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_X86.ctype,
    210                               MD_CONTEXT_X86_FLOATING_POINT)),
    211   # MD_CONTEXT_X86_SEGMENTS.
    212   ("gs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
    213   ("fs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
    214   ("es", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
    215   ("ds", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
    216   # MD_CONTEXT_X86_INTEGER.
    217   ("edi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
    218   ("esi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
    219   ("ebx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
    220   ("edx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
    221   ("ecx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
    222   ("eax", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
    223   # MD_CONTEXT_X86_CONTROL.
    224   ("ebp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
    225   ("eip", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
    226   ("cs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
    227   ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
    228   ("esp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
    229   ("ss", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
    230   # MD_CONTEXT_X86_EXTENDED_REGISTERS.
    231   ("extended_registers",
    232    EnableOnFlag(ctypes.c_uint8 * MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE,
    233                 MD_CONTEXT_X86_EXTENDED_REGISTERS))
    234 ])
    235 
    236 MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([
    237   ("start", ctypes.c_uint64),
    238   ("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
    239 ])
    240 
    241 MINIDUMP_MEMORY_DESCRIPTOR64 = Descriptor([
    242   ("start", ctypes.c_uint64),
    243   ("size", ctypes.c_uint64)
    244 ])
    245 
    246 MINIDUMP_MEMORY_LIST = Descriptor([
    247   ("range_count", ctypes.c_uint32),
    248   ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count)
    249 ])
    250 
    251 MINIDUMP_MEMORY_LIST64 = Descriptor([
    252   ("range_count", ctypes.c_uint64),
    253   ("base_rva", ctypes.c_uint64),
    254   ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR64.ctype * m.range_count)
    255 ])
    256 
    257 MINIDUMP_THREAD = Descriptor([
    258   ("id", ctypes.c_uint32),
    259   ("suspend_count", ctypes.c_uint32),
    260   ("priority_class", ctypes.c_uint32),
    261   ("priority", ctypes.c_uint32),
    262   ("ted", ctypes.c_uint64),
    263   ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype),
    264   ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
    265 ])
    266 
    267 MINIDUMP_THREAD_LIST = Descriptor([
    268   ("thread_count", ctypes.c_uint32),
    269   ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
    270 ])
    271 
    272 
    273 class MinidumpReader(object):
    274   """Minidump (.dmp) reader."""
    275 
    276   _HEADER_MAGIC = 0x504d444d
    277 
    278   def __init__(self, options, minidump_name):
    279     self.minidump_name = minidump_name
    280     self.minidump_file = open(minidump_name, "r")
    281     self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE)
    282     self.header = MINIDUMP_HEADER.Read(self.minidump, 0)
    283     if self.header.signature != MinidumpReader._HEADER_MAGIC:
    284       print >>sys.stderr, "Warning: unsupported minidump header magic"
    285     DebugPrint(self.header)
    286     directories = []
    287     offset = self.header.stream_directories_rva
    288     for _ in xrange(self.header.stream_count):
    289       directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset))
    290       offset += MINIDUMP_DIRECTORY.size
    291     self.exception = None
    292     self.exception_context = None
    293     self.memory_list = None
    294     self.thread_map = {}
    295     for d in directories:
    296       DebugPrint(d)
    297       # TODO(vitalyr): extract system info including CPU features.
    298       if d.stream_type == MD_EXCEPTION_STREAM:
    299         self.exception = MINIDUMP_EXCEPTION_STREAM.Read(
    300           self.minidump, d.location.rva)
    301         DebugPrint(self.exception)
    302         self.exception_context = MINIDUMP_CONTEXT_X86.Read(
    303           self.minidump, self.exception.thread_context.rva)
    304         DebugPrint(self.exception_context)
    305       elif d.stream_type == MD_THREAD_LIST_STREAM:
    306         thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva)
    307         assert ctypes.sizeof(thread_list) == d.location.data_size
    308         DebugPrint(thread_list)
    309         for thread in thread_list.threads:
    310           DebugPrint(thread)
    311           self.thread_map[thread.id] = thread
    312       elif d.stream_type == MD_MEMORY_LIST_STREAM:
    313         print >>sys.stderr, "Warning: not a full minidump"
    314         ml = MINIDUMP_MEMORY_LIST.Read(self.minidump, d.location.rva)
    315         DebugPrint(ml)
    316         for m in ml.ranges:
    317           DebugPrint(m)
    318       elif d.stream_type == MD_MEMORY_64_LIST_STREAM:
    319         assert self.memory_list is None
    320         self.memory_list = MINIDUMP_MEMORY_LIST64.Read(
    321           self.minidump, d.location.rva)
    322         assert ctypes.sizeof(self.memory_list) == d.location.data_size
    323         DebugPrint(self.memory_list)
    324 
    325   def IsValidAddress(self, address):
    326     return self.FindLocation(address) is not None
    327 
    328   def ReadU8(self, address):
    329     location = self.FindLocation(address)
    330     return ctypes.c_uint8.from_buffer(self.minidump, location).value
    331 
    332   def ReadU32(self, address):
    333     location = self.FindLocation(address)
    334     return ctypes.c_uint32.from_buffer(self.minidump, location).value
    335 
    336   def ReadBytes(self, address, size):
    337     location = self.FindLocation(address)
    338     return self.minidump[location:location + size]
    339 
    340   def FindLocation(self, address):
    341     # TODO(vitalyr): only works for full minidumps (...64 structure variants).
    342     offset = 0
    343     for r in self.memory_list.ranges:
    344       if r.start <= address < r.start + r.size:
    345         return self.memory_list.base_rva + offset + address - r.start
    346       offset += r.size
    347     return None
    348 
    349   def GetDisasmLines(self, address, size):
    350     location = self.FindLocation(address)
    351     if location is None: return []
    352     return disasm.GetDisasmLines(self.minidump_name,
    353                                  location,
    354                                  size,
    355                                  "ia32",
    356                                  False)
    357 
    358 
    359   def Dispose(self):
    360     self.minidump.close()
    361     self.minidump_file.close()
    362 
    363 
    364 # List of V8 instance types. Obtained by adding the code below to any .cc file.
    365 #
    366 # #define DUMP_TYPE(T) printf("%d: \"%s\",\n", T, #T);
    367 # struct P {
    368 #   P() {
    369 #     printf("{\n");
    370 #     INSTANCE_TYPE_LIST(DUMP_TYPE)
    371 #     printf("}\n");
    372 #   }
    373 # };
    374 # static P p;
    375 INSTANCE_TYPES = {
    376   64: "SYMBOL_TYPE",
    377   68: "ASCII_SYMBOL_TYPE",
    378   65: "CONS_SYMBOL_TYPE",
    379   69: "CONS_ASCII_SYMBOL_TYPE",
    380   66: "EXTERNAL_SYMBOL_TYPE",
    381   74: "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE",
    382   70: "EXTERNAL_ASCII_SYMBOL_TYPE",
    383   0: "STRING_TYPE",
    384   4: "ASCII_STRING_TYPE",
    385   1: "CONS_STRING_TYPE",
    386   5: "CONS_ASCII_STRING_TYPE",
    387   2: "EXTERNAL_STRING_TYPE",
    388   10: "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE",
    389   6: "EXTERNAL_ASCII_STRING_TYPE",
    390   6: "PRIVATE_EXTERNAL_ASCII_STRING_TYPE",
    391   128: "MAP_TYPE",
    392   129: "CODE_TYPE",
    393   130: "ODDBALL_TYPE",
    394   131: "JS_GLOBAL_PROPERTY_CELL_TYPE",
    395   132: "HEAP_NUMBER_TYPE",
    396   133: "PROXY_TYPE",
    397   134: "BYTE_ARRAY_TYPE",
    398   135: "PIXEL_ARRAY_TYPE",
    399   136: "EXTERNAL_BYTE_ARRAY_TYPE",
    400   137: "EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE",
    401   138: "EXTERNAL_SHORT_ARRAY_TYPE",
    402   139: "EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE",
    403   140: "EXTERNAL_INT_ARRAY_TYPE",
    404   141: "EXTERNAL_UNSIGNED_INT_ARRAY_TYPE",
    405   142: "EXTERNAL_FLOAT_ARRAY_TYPE",
    406   143: "FILLER_TYPE",
    407   144: "ACCESSOR_INFO_TYPE",
    408   145: "ACCESS_CHECK_INFO_TYPE",
    409   146: "INTERCEPTOR_INFO_TYPE",
    410   147: "CALL_HANDLER_INFO_TYPE",
    411   148: "FUNCTION_TEMPLATE_INFO_TYPE",
    412   149: "OBJECT_TEMPLATE_INFO_TYPE",
    413   150: "SIGNATURE_INFO_TYPE",
    414   151: "TYPE_SWITCH_INFO_TYPE",
    415   152: "SCRIPT_TYPE",
    416   153: "CODE_CACHE_TYPE",
    417   156: "FIXED_ARRAY_TYPE",
    418   157: "SHARED_FUNCTION_INFO_TYPE",
    419   158: "JS_MESSAGE_OBJECT_TYPE",
    420   159: "JS_VALUE_TYPE",
    421   160: "JS_OBJECT_TYPE",
    422   161: "JS_CONTEXT_EXTENSION_OBJECT_TYPE",
    423   162: "JS_GLOBAL_OBJECT_TYPE",
    424   163: "JS_BUILTINS_OBJECT_TYPE",
    425   164: "JS_GLOBAL_PROXY_TYPE",
    426   165: "JS_ARRAY_TYPE",
    427   166: "JS_REGEXP_TYPE",
    428   167: "JS_FUNCTION_TYPE",
    429   154: "DEBUG_INFO_TYPE",
    430   155: "BREAK_POINT_INFO_TYPE",
    431 }
    432 
    433 
    434 class Printer(object):
    435   """Printer with indentation support."""
    436 
    437   def __init__(self):
    438     self.indent = 0
    439 
    440   def Indent(self):
    441     self.indent += 2
    442 
    443   def Dedent(self):
    444     self.indent -= 2
    445 
    446   def Print(self, string):
    447     print "%s%s" % (self._IndentString(), string)
    448 
    449   def PrintLines(self, lines):
    450     indent = self._IndentString()
    451     print "\n".join("%s%s" % (indent, line) for line in lines)
    452 
    453   def _IndentString(self):
    454     return self.indent * " "
    455 
    456 
    457 ADDRESS_RE = re.compile(r"0x[0-9a-fA-F]+")
    458 
    459 
    460 def FormatDisasmLine(start, heap, line):
    461   line_address = start + line[0]
    462   stack_slot = heap.stack_map.get(line_address)
    463   marker = "  "
    464   if stack_slot:
    465     marker = "=>"
    466   code = AnnotateAddresses(heap, line[1])
    467   return "%s%08x %08x: %s" % (marker, line_address, line[0], code)
    468 
    469 
    470 def AnnotateAddresses(heap, line):
    471   extra = []
    472   for m in ADDRESS_RE.finditer(line):
    473     maybe_address = int(m.group(0), 16)
    474     object = heap.FindObject(maybe_address)
    475     if not object: continue
    476     extra.append(str(object))
    477   if len(extra) == 0: return line
    478   return "%s  ;; %s" % (line, ", ".join(extra))
    479 
    480 
    481 class HeapObject(object):
    482   def __init__(self, heap, map, address):
    483     self.heap = heap
    484     self.map = map
    485     self.address = address
    486 
    487   def Is(self, cls):
    488     return isinstance(self, cls)
    489 
    490   def Print(self, p):
    491     p.Print(str(self))
    492 
    493   def __str__(self):
    494     return "HeapObject(%08x, %s)" % (self.address,
    495                                      INSTANCE_TYPES[self.map.instance_type])
    496 
    497   def ObjectField(self, offset):
    498     field_value = self.heap.reader.ReadU32(self.address + offset)
    499     return self.heap.FindObjectOrSmi(field_value)
    500 
    501   def SmiField(self, offset):
    502     field_value = self.heap.reader.ReadU32(self.address + offset)
    503     assert (field_value & 1) == 0
    504     return field_value / 2
    505 
    506 
    507 class Map(HeapObject):
    508   INSTANCE_TYPE_OFFSET = 8
    509 
    510   def __init__(self, heap, map, address):
    511     HeapObject.__init__(self, heap, map, address)
    512     self.instance_type = \
    513         heap.reader.ReadU8(self.address + Map.INSTANCE_TYPE_OFFSET)
    514 
    515 
    516 class String(HeapObject):
    517   LENGTH_OFFSET = 4
    518 
    519   def __init__(self, heap, map, address):
    520     HeapObject.__init__(self, heap, map, address)
    521     self.length = self.SmiField(String.LENGTH_OFFSET)
    522 
    523   def GetChars(self):
    524     return "?string?"
    525 
    526   def Print(self, p):
    527     p.Print(str(self))
    528 
    529   def __str__(self):
    530     return "\"%s\"" % self.GetChars()
    531 
    532 
    533 class SeqString(String):
    534   CHARS_OFFSET = 12
    535 
    536   def __init__(self, heap, map, address):
    537     String.__init__(self, heap, map, address)
    538     self.chars = heap.reader.ReadBytes(self.address + SeqString.CHARS_OFFSET,
    539                                        self.length)
    540 
    541   def GetChars(self):
    542     return self.chars
    543 
    544 
    545 class ExternalString(String):
    546   RESOURCE_OFFSET = 12
    547 
    548   WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
    549   WEBKIT_STRING_IMPL_CHARS_OFFSET = 8
    550 
    551   def __init__(self, heap, map, address):
    552     String.__init__(self, heap, map, address)
    553     reader = heap.reader
    554     self.resource = \
    555         reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET)
    556     self.chars = "?external string?"
    557     if not reader.IsValidAddress(self.resource): return
    558     string_impl_address = self.resource + \
    559         ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET
    560     if not reader.IsValidAddress(string_impl_address): return
    561     string_impl = reader.ReadU32(string_impl_address)
    562     chars_ptr_address = string_impl + \
    563         ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET
    564     if not reader.IsValidAddress(chars_ptr_address): return
    565     chars_ptr = reader.ReadU32(chars_ptr_address)
    566     if not reader.IsValidAddress(chars_ptr): return
    567     raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length)
    568     self.chars = codecs.getdecoder("utf16")(raw_chars)[0]
    569 
    570   def GetChars(self):
    571     return self.chars
    572 
    573 
    574 class ConsString(String):
    575   LEFT_OFFSET = 12
    576   RIGHT_OFFSET = 16
    577 
    578   def __init__(self, heap, map, address):
    579     String.__init__(self, heap, map, address)
    580     self.left = self.ObjectField(ConsString.LEFT_OFFSET)
    581     self.right = self.ObjectField(ConsString.RIGHT_OFFSET)
    582 
    583   def GetChars(self):
    584     return self.left.GetChars() + self.right.GetChars()
    585 
    586 
    587 class Oddball(HeapObject):
    588   TO_STRING_OFFSET = 4
    589 
    590   def __init__(self, heap, map, address):
    591     HeapObject.__init__(self, heap, map, address)
    592     self.to_string = self.ObjectField(Oddball.TO_STRING_OFFSET)
    593 
    594   def Print(self, p):
    595     p.Print(str(self))
    596 
    597   def __str__(self):
    598     return "<%s>" % self.to_string.GetChars()
    599 
    600 
    601 class FixedArray(HeapObject):
    602   LENGTH_OFFSET = 4
    603   ELEMENTS_OFFSET = 8
    604 
    605   def __init__(self, heap, map, address):
    606     HeapObject.__init__(self, heap, map, address)
    607     self.length = self.SmiField(FixedArray.LENGTH_OFFSET)
    608 
    609   def Print(self, p):
    610     p.Print("FixedArray(%08x) {" % self.address)
    611     p.Indent()
    612     p.Print("length: %d" % self.length)
    613     for i in xrange(self.length):
    614       offset = FixedArray.ELEMENTS_OFFSET + 4 * i
    615       p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
    616     p.Dedent()
    617     p.Print("}")
    618 
    619   def __str__(self):
    620     return "FixedArray(%08x, length=%d)" % (self.address, self.length)
    621 
    622 
    623 class JSFunction(HeapObject):
    624   CODE_ENTRY_OFFSET = 12
    625   SHARED_OFFSET = 20
    626 
    627   def __init__(self, heap, map, address):
    628     HeapObject.__init__(self, heap, map, address)
    629     code_entry = \
    630         heap.reader.ReadU32(self.address + JSFunction.CODE_ENTRY_OFFSET)
    631     self.code = heap.FindObject(code_entry - Code.ENTRY_OFFSET + 1)
    632     self.shared = self.ObjectField(JSFunction.SHARED_OFFSET)
    633 
    634   def Print(self, p):
    635     source = "\n".join("  %s" % line for line in self._GetSource().split("\n"))
    636     p.Print("JSFunction(%08x) {" % self.address)
    637     p.Indent()
    638     p.Print("inferred name: %s" % self.shared.inferred_name)
    639     if self.shared.script.Is(Script) and self.shared.script.name.Is(String):
    640       p.Print("script name: %s" % self.shared.script.name)
    641     p.Print("source:")
    642     p.PrintLines(self._GetSource().split("\n"))
    643     p.Print("code:")
    644     self.code.Print(p)
    645     if self.code != self.shared.code:
    646       p.Print("unoptimized code:")
    647       self.shared.code.Print(p)
    648     p.Dedent()
    649     p.Print("}")
    650 
    651   def __str__(self):
    652     inferred_name = ""
    653     if self.shared.Is(SharedFunctionInfo):
    654       inferred_name = self.shared.inferred_name
    655     return "JSFunction(%08x, %s)" % (self.address, inferred_name)
    656 
    657   def _GetSource(self):
    658     source = "?source?"
    659     start = self.shared.start_position
    660     end = self.shared.end_position
    661     if not self.shared.script.Is(Script): return source
    662     script_source = self.shared.script.source
    663     if not script_source.Is(String): return source
    664     return script_source.GetChars()[start:end]
    665 
    666 
    667 class SharedFunctionInfo(HeapObject):
    668   CODE_OFFSET = 2 * 4
    669   SCRIPT_OFFSET = 7 * 4
    670   INFERRED_NAME_OFFSET = 9 * 4
    671   START_POSITION_AND_TYPE_OFFSET = 17 * 4
    672   END_POSITION_OFFSET = 18 * 4
    673 
    674   def __init__(self, heap, map, address):
    675     HeapObject.__init__(self, heap, map, address)
    676     self.code = self.ObjectField(SharedFunctionInfo.CODE_OFFSET)
    677     self.script = self.ObjectField(SharedFunctionInfo.SCRIPT_OFFSET)
    678     self.inferred_name = \
    679         self.ObjectField(SharedFunctionInfo.INFERRED_NAME_OFFSET)
    680     start_position_and_type = \
    681         self.SmiField(SharedFunctionInfo.START_POSITION_AND_TYPE_OFFSET)
    682     self.start_position = start_position_and_type >> 2
    683     self.end_position = self.SmiField(SharedFunctionInfo.END_POSITION_OFFSET)
    684 
    685 
    686 class Script(HeapObject):
    687   SOURCE_OFFSET = 4
    688   NAME_OFFSET = 8
    689 
    690   def __init__(self, heap, map, address):
    691     HeapObject.__init__(self, heap, map, address)
    692     self.source = self.ObjectField(Script.SOURCE_OFFSET)
    693     self.name = self.ObjectField(Script.NAME_OFFSET)
    694 
    695 
    696 class Code(HeapObject):
    697   INSTRUCTION_SIZE_OFFSET = 4
    698   ENTRY_OFFSET = 32
    699 
    700   def __init__(self, heap, map, address):
    701     HeapObject.__init__(self, heap, map, address)
    702     self.entry = self.address + Code.ENTRY_OFFSET
    703     self.instruction_size = \
    704         heap.reader.ReadU32(self.address + Code.INSTRUCTION_SIZE_OFFSET)
    705 
    706   def Print(self, p):
    707     lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
    708     p.Print("Code(%08x) {" % self.address)
    709     p.Indent()
    710     p.Print("instruction_size: %d" % self.instruction_size)
    711     p.PrintLines(self._FormatLine(line) for line in lines)
    712     p.Dedent()
    713     p.Print("}")
    714 
    715   def _FormatLine(self, line):
    716     return FormatDisasmLine(self.entry, self.heap, line)
    717 
    718 
    719 class V8Heap(object):
    720   CLASS_MAP = {
    721     "SYMBOL_TYPE": SeqString,
    722     "ASCII_SYMBOL_TYPE": SeqString,
    723     "CONS_SYMBOL_TYPE": ConsString,
    724     "CONS_ASCII_SYMBOL_TYPE": ConsString,
    725     "EXTERNAL_SYMBOL_TYPE": ExternalString,
    726     "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString,
    727     "EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString,
    728     "STRING_TYPE": SeqString,
    729     "ASCII_STRING_TYPE": SeqString,
    730     "CONS_STRING_TYPE": ConsString,
    731     "CONS_ASCII_STRING_TYPE": ConsString,
    732     "EXTERNAL_STRING_TYPE": ExternalString,
    733     "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE": ExternalString,
    734     "EXTERNAL_ASCII_STRING_TYPE": ExternalString,
    735 
    736     "MAP_TYPE": Map,
    737     "ODDBALL_TYPE": Oddball,
    738     "FIXED_ARRAY_TYPE": FixedArray,
    739     "JS_FUNCTION_TYPE": JSFunction,
    740     "SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo,
    741     "SCRIPT_TYPE": Script,
    742     "CODE_TYPE": Code
    743   }
    744 
    745   def __init__(self, reader, stack_map):
    746     self.reader = reader
    747     self.stack_map = stack_map
    748     self.objects = {}
    749 
    750   def FindObjectOrSmi(self, tagged_address):
    751     if (tagged_address & 1) == 0: return tagged_address / 2
    752     return self.FindObject(tagged_address)
    753 
    754   def FindObject(self, tagged_address):
    755     if tagged_address in self.objects:
    756       return self.objects[tagged_address]
    757     if (tagged_address & 1) != 1: return None
    758     address = tagged_address - 1
    759     if not self.reader.IsValidAddress(address): return None
    760     map_tagged_address = self.reader.ReadU32(address)
    761     if tagged_address == map_tagged_address:
    762       # Meta map?
    763       meta_map = Map(self, None, address)
    764       instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type)
    765       if instance_type_name != "MAP_TYPE": return None
    766       meta_map.map = meta_map
    767       object = meta_map
    768     else:
    769       map = self.FindObject(map_tagged_address)
    770       if map is None: return None
    771       instance_type_name = INSTANCE_TYPES.get(map.instance_type)
    772       if instance_type_name is None: return None
    773       cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
    774       object = cls(self, map, address)
    775     self.objects[tagged_address] = object
    776     return object
    777 
    778 
    779 EIP_PROXIMITY = 64
    780 
    781 
    782 def AnalyzeMinidump(options, minidump_name):
    783   reader = MinidumpReader(options, minidump_name)
    784   DebugPrint("========================================")
    785   if reader.exception is None:
    786     print "Minidump has no exception info"
    787     return
    788   print "Exception info:"
    789   exception_thread = reader.thread_map[reader.exception.thread_id]
    790   print "  thread id: %d" % exception_thread.id
    791   print "  code: %08X" % reader.exception.exception.code
    792   print "  context:"
    793   print "    eax: %08x" % reader.exception_context.eax
    794   print "    ebx: %08x" % reader.exception_context.ebx
    795   print "    ecx: %08x" % reader.exception_context.ecx
    796   print "    edx: %08x" % reader.exception_context.edx
    797   print "    edi: %08x" % reader.exception_context.edi
    798   print "    esi: %08x" % reader.exception_context.esi
    799   print "    ebp: %08x" % reader.exception_context.ebp
    800   print "    esp: %08x" % reader.exception_context.esp
    801   print "    eip: %08x" % reader.exception_context.eip
    802   # TODO(vitalyr): decode eflags.
    803   print "    eflags: %s" % bin(reader.exception_context.eflags)[2:]
    804   print
    805 
    806   stack_bottom = exception_thread.stack.start + \
    807       exception_thread.stack.memory.data_size
    808   stack_map = {reader.exception_context.eip: -1}
    809   for slot in xrange(reader.exception_context.esp, stack_bottom, 4):
    810     maybe_address = reader.ReadU32(slot)
    811     if not maybe_address in stack_map:
    812       stack_map[maybe_address] = slot
    813   heap = V8Heap(reader, stack_map)
    814 
    815   print "Disassembly around exception.eip:"
    816   start = reader.exception_context.eip - EIP_PROXIMITY
    817   lines = reader.GetDisasmLines(start, 2 * EIP_PROXIMITY)
    818   for line in lines:
    819     print FormatDisasmLine(start, heap, line)
    820   print
    821 
    822   print "Annotated stack (from exception.esp to bottom):"
    823   for slot in xrange(reader.exception_context.esp, stack_bottom, 4):
    824     maybe_address = reader.ReadU32(slot)
    825     heap_object = heap.FindObject(maybe_address)
    826     print "%08x: %08x" % (slot, maybe_address)
    827     if heap_object:
    828       heap_object.Print(Printer())
    829       print
    830 
    831   reader.Dispose()
    832 
    833 
    834 if __name__ == "__main__":
    835   parser = optparse.OptionParser(USAGE)
    836   options, args = parser.parse_args()
    837   if len(args) != 1:
    838     parser.print_help()
    839     sys.exit(1)
    840   AnalyzeMinidump(options, args[0])
    841