• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
30import ctypes
31import mmap
32import optparse
33import os
34import disasm
35import sys
36import types
37import codecs
38import re
39
40
41USAGE="""usage: %prog [OPTION]...
42
43Minidump analyzer.
44
45Shows the processor state at the point of exception including the
46stack of the active thread and the referenced objects in the V8
47heap. Code objects are disassembled and the addresses linked from the
48stack (pushed return addresses) are marked with "=>".
49
50
51Examples:
52  $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp
53"""
54
55DEBUG=False
56
57
58def DebugPrint(s):
59  if not DEBUG: return
60  print s
61
62
63class 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
111MINIDUMP_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
121MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([
122  ("data_size", ctypes.c_uint32),
123  ("rva", ctypes.c_uint32)
124])
125
126MINIDUMP_DIRECTORY = Descriptor([
127  ("stream_type", ctypes.c_uint32),
128  ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
129])
130
131MD_EXCEPTION_MAXIMUM_PARAMETERS = 15
132
133MINIDUMP_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
143MINIDUMP_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.
151MD_UNUSED_STREAM = 0
152MD_RESERVED_STREAM_0 = 1
153MD_RESERVED_STREAM_1 = 2
154MD_THREAD_LIST_STREAM = 3
155MD_MODULE_LIST_STREAM = 4
156MD_MEMORY_LIST_STREAM = 5
157MD_EXCEPTION_STREAM = 6
158MD_SYSTEM_INFO_STREAM = 7
159MD_THREAD_EX_LIST_STREAM = 8
160MD_MEMORY_64_LIST_STREAM = 9
161MD_COMMENT_STREAM_A = 10
162MD_COMMENT_STREAM_W = 11
163MD_HANDLE_DATA_STREAM = 12
164MD_FUNCTION_TABLE_STREAM = 13
165MD_UNLOADED_MODULE_LIST_STREAM = 14
166MD_MISC_INFO_STREAM = 15
167MD_MEMORY_INFO_LIST_STREAM = 16
168MD_THREAD_INFO_LIST_STREAM = 17
169MD_HANDLE_OPERATION_LIST_STREAM = 18
170
171MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE = 80
172
173MINIDUMP_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
185MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE = 512
186
187# Context flags.
188MD_CONTEXT_X86 = 0x00010000
189MD_CONTEXT_X86_CONTROL = (MD_CONTEXT_X86 | 0x00000001)
190MD_CONTEXT_X86_INTEGER = (MD_CONTEXT_X86 | 0x00000002)
191MD_CONTEXT_X86_SEGMENTS = (MD_CONTEXT_X86 | 0x00000004)
192MD_CONTEXT_X86_FLOATING_POINT = (MD_CONTEXT_X86 | 0x00000008)
193MD_CONTEXT_X86_DEBUG_REGISTERS = (MD_CONTEXT_X86 | 0x00000010)
194MD_CONTEXT_X86_EXTENDED_REGISTERS = (MD_CONTEXT_X86 | 0x00000020)
195
196def EnableOnFlag(type, flag):
197  return lambda o: [None, type][int((o.context_flags & flag) != 0)]
198
199MINIDUMP_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
236MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([
237  ("start", ctypes.c_uint64),
238  ("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
239])
240
241MINIDUMP_MEMORY_DESCRIPTOR64 = Descriptor([
242  ("start", ctypes.c_uint64),
243  ("size", ctypes.c_uint64)
244])
245
246MINIDUMP_MEMORY_LIST = Descriptor([
247  ("range_count", ctypes.c_uint32),
248  ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count)
249])
250
251MINIDUMP_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
257MINIDUMP_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
267MINIDUMP_THREAD_LIST = Descriptor([
268  ("thread_count", ctypes.c_uint32),
269  ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
270])
271
272
273class 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;
375INSTANCE_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
434class 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
457ADDRESS_RE = re.compile(r"0x[0-9a-fA-F]+")
458
459
460def 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
470def 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
481class 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
507class 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
516class 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
533class 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
545class 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
574class 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
587class 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
601class 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
623class 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
667class 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
686class 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
696class 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
719class 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
779EIP_PROXIMITY = 64
780
781
782def 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
834if __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