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
55
56DEBUG=False
57
58
59def DebugPrint(s):
60  if not DEBUG: return
61  print s
62
63
64class Descriptor(object):
65  """Descriptor of a structure in a memory."""
66
67  def __init__(self, fields):
68    self.fields = fields
69    self.is_flexible = False
70    for _, type_or_func in fields:
71      if isinstance(type_or_func, types.FunctionType):
72        self.is_flexible = True
73        break
74    if not self.is_flexible:
75      self.ctype = Descriptor._GetCtype(fields)
76      self.size = ctypes.sizeof(self.ctype)
77
78  def Read(self, memory, offset):
79    if self.is_flexible:
80      fields_copy = self.fields[:]
81      last = 0
82      for name, type_or_func in fields_copy:
83        if isinstance(type_or_func, types.FunctionType):
84          partial_ctype = Descriptor._GetCtype(fields_copy[:last])
85          partial_object = partial_ctype.from_buffer(memory, offset)
86          type = type_or_func(partial_object)
87          if type is not None:
88            fields_copy[last] = (name, type)
89            last += 1
90        else:
91          last += 1
92      complete_ctype = Descriptor._GetCtype(fields_copy[:last])
93    else:
94      complete_ctype = self.ctype
95    return complete_ctype.from_buffer(memory, offset)
96
97  @staticmethod
98  def _GetCtype(fields):
99    class Raw(ctypes.Structure):
100      _fields_ = fields
101      _pack_ = 1
102
103      def __str__(self):
104        return "{" + ", ".join("%s: %s" % (field, self.__getattribute__(field))
105                               for field, _ in Raw._fields_) + "}"
106    return Raw
107
108
109# Set of structures and constants that describe the layout of minidump
110# files. Based on MSDN and Google Breakpad.
111
112MINIDUMP_HEADER = Descriptor([
113  ("signature", ctypes.c_uint32),
114  ("version", ctypes.c_uint32),
115  ("stream_count", ctypes.c_uint32),
116  ("stream_directories_rva", ctypes.c_uint32),
117  ("checksum", ctypes.c_uint32),
118  ("time_date_stampt", ctypes.c_uint32),
119  ("flags", ctypes.c_uint64)
120])
121
122MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([
123  ("data_size", ctypes.c_uint32),
124  ("rva", ctypes.c_uint32)
125])
126
127MINIDUMP_DIRECTORY = Descriptor([
128  ("stream_type", ctypes.c_uint32),
129  ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
130])
131
132MD_EXCEPTION_MAXIMUM_PARAMETERS = 15
133
134MINIDUMP_EXCEPTION = Descriptor([
135  ("code", ctypes.c_uint32),
136  ("flags", ctypes.c_uint32),
137  ("record", ctypes.c_uint64),
138  ("address", ctypes.c_uint64),
139  ("parameter_count", ctypes.c_uint32),
140  ("unused_alignment", ctypes.c_uint32),
141  ("information", ctypes.c_uint64 * MD_EXCEPTION_MAXIMUM_PARAMETERS)
142])
143
144MINIDUMP_EXCEPTION_STREAM = Descriptor([
145  ("thread_id", ctypes.c_uint32),
146  ("unused_alignment", ctypes.c_uint32),
147  ("exception", MINIDUMP_EXCEPTION.ctype),
148  ("thread_context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
149])
150
151# Stream types.
152MD_UNUSED_STREAM = 0
153MD_RESERVED_STREAM_0 = 1
154MD_RESERVED_STREAM_1 = 2
155MD_THREAD_LIST_STREAM = 3
156MD_MODULE_LIST_STREAM = 4
157MD_MEMORY_LIST_STREAM = 5
158MD_EXCEPTION_STREAM = 6
159MD_SYSTEM_INFO_STREAM = 7
160MD_THREAD_EX_LIST_STREAM = 8
161MD_MEMORY_64_LIST_STREAM = 9
162MD_COMMENT_STREAM_A = 10
163MD_COMMENT_STREAM_W = 11
164MD_HANDLE_DATA_STREAM = 12
165MD_FUNCTION_TABLE_STREAM = 13
166MD_UNLOADED_MODULE_LIST_STREAM = 14
167MD_MISC_INFO_STREAM = 15
168MD_MEMORY_INFO_LIST_STREAM = 16
169MD_THREAD_INFO_LIST_STREAM = 17
170MD_HANDLE_OPERATION_LIST_STREAM = 18
171
172MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE = 80
173
174MINIDUMP_FLOATING_SAVE_AREA_X86 = Descriptor([
175  ("control_word", ctypes.c_uint32),
176  ("status_word", ctypes.c_uint32),
177  ("tag_word", ctypes.c_uint32),
178  ("error_offset", ctypes.c_uint32),
179  ("error_selector", ctypes.c_uint32),
180  ("data_offset", ctypes.c_uint32),
181  ("data_selector", ctypes.c_uint32),
182  ("register_area", ctypes.c_uint8 * MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE),
183  ("cr0_npx_state", ctypes.c_uint32)
184])
185
186MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE = 512
187
188# Context flags.
189MD_CONTEXT_X86 = 0x00010000
190MD_CONTEXT_X86_CONTROL = (MD_CONTEXT_X86 | 0x00000001)
191MD_CONTEXT_X86_INTEGER = (MD_CONTEXT_X86 | 0x00000002)
192MD_CONTEXT_X86_SEGMENTS = (MD_CONTEXT_X86 | 0x00000004)
193MD_CONTEXT_X86_FLOATING_POINT = (MD_CONTEXT_X86 | 0x00000008)
194MD_CONTEXT_X86_DEBUG_REGISTERS = (MD_CONTEXT_X86 | 0x00000010)
195MD_CONTEXT_X86_EXTENDED_REGISTERS = (MD_CONTEXT_X86 | 0x00000020)
196
197def EnableOnFlag(type, flag):
198  return lambda o: [None, type][int((o.context_flags & flag) != 0)]
199
200MINIDUMP_CONTEXT_X86 = Descriptor([
201  ("context_flags", ctypes.c_uint32),
202  # MD_CONTEXT_X86_DEBUG_REGISTERS.
203  ("dr0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
204  ("dr1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
205  ("dr2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
206  ("dr3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
207  ("dr6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
208  ("dr7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
209  # MD_CONTEXT_X86_FLOATING_POINT.
210  ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_X86.ctype,
211                              MD_CONTEXT_X86_FLOATING_POINT)),
212  # MD_CONTEXT_X86_SEGMENTS.
213  ("gs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
214  ("fs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
215  ("es", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
216  ("ds", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
217  # MD_CONTEXT_X86_INTEGER.
218  ("edi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
219  ("esi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
220  ("ebx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
221  ("edx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
222  ("ecx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
223  ("eax", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
224  # MD_CONTEXT_X86_CONTROL.
225  ("ebp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
226  ("eip", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
227  ("cs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
228  ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
229  ("esp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
230  ("ss", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
231  # MD_CONTEXT_X86_EXTENDED_REGISTERS.
232  ("extended_registers",
233   EnableOnFlag(ctypes.c_uint8 * MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE,
234                MD_CONTEXT_X86_EXTENDED_REGISTERS))
235])
236
237MD_CONTEXT_AMD64 = 0x00100000
238MD_CONTEXT_AMD64_CONTROL = (MD_CONTEXT_AMD64 | 0x00000001)
239MD_CONTEXT_AMD64_INTEGER = (MD_CONTEXT_AMD64 | 0x00000002)
240MD_CONTEXT_AMD64_SEGMENTS = (MD_CONTEXT_AMD64 | 0x00000004)
241MD_CONTEXT_AMD64_FLOATING_POINT = (MD_CONTEXT_AMD64 | 0x00000008)
242MD_CONTEXT_AMD64_DEBUG_REGISTERS = (MD_CONTEXT_AMD64 | 0x00000010)
243
244MINIDUMP_CONTEXT_AMD64 = Descriptor([
245  ("p1_home", ctypes.c_uint64),
246  ("p2_home", ctypes.c_uint64),
247  ("p3_home", ctypes.c_uint64),
248  ("p4_home", ctypes.c_uint64),
249  ("p5_home", ctypes.c_uint64),
250  ("p6_home", ctypes.c_uint64),
251  ("context_flags", ctypes.c_uint32),
252  ("mx_csr", ctypes.c_uint32),
253  # MD_CONTEXT_AMD64_CONTROL.
254  ("cs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
255  # MD_CONTEXT_AMD64_SEGMENTS
256  ("ds", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
257  ("es", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
258  ("fs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
259  ("gs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
260  # MD_CONTEXT_AMD64_CONTROL.
261  ("ss", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
262  ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_AMD64_CONTROL)),
263  # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
264  ("dr0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
265  ("dr1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
266  ("dr2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
267  ("dr3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
268  ("dr6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
269  ("dr7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
270  # MD_CONTEXT_AMD64_INTEGER.
271  ("rax", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
272  ("rcx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
273  ("rdx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
274  ("rbx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
275  # MD_CONTEXT_AMD64_CONTROL.
276  ("rsp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
277  # MD_CONTEXT_AMD64_INTEGER.
278  ("rbp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
279  ("rsi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
280  ("rdi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
281  ("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
282  ("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
283  ("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
284  ("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
285  ("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
286  ("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
287  ("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
288  ("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
289  # MD_CONTEXT_AMD64_CONTROL.
290  ("rip", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
291  # MD_CONTEXT_AMD64_FLOATING_POINT
292  ("sse_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
293                                 MD_CONTEXT_AMD64_FLOATING_POINT)),
294  ("vector_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
295                                    MD_CONTEXT_AMD64_FLOATING_POINT)),
296  ("vector_control", EnableOnFlag(ctypes.c_uint64,
297                                  MD_CONTEXT_AMD64_FLOATING_POINT)),
298  # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
299  ("debug_control", EnableOnFlag(ctypes.c_uint64,
300                                 MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
301  ("last_branch_to_rip", EnableOnFlag(ctypes.c_uint64,
302                                      MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
303  ("last_branch_from_rip", EnableOnFlag(ctypes.c_uint64,
304                                        MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
305  ("last_exception_to_rip", EnableOnFlag(ctypes.c_uint64,
306                                         MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
307  ("last_exception_from_rip", EnableOnFlag(ctypes.c_uint64,
308                                           MD_CONTEXT_AMD64_DEBUG_REGISTERS))
309])
310
311MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([
312  ("start", ctypes.c_uint64),
313  ("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
314])
315
316MINIDUMP_MEMORY_DESCRIPTOR64 = Descriptor([
317  ("start", ctypes.c_uint64),
318  ("size", ctypes.c_uint64)
319])
320
321MINIDUMP_MEMORY_LIST = Descriptor([
322  ("range_count", ctypes.c_uint32),
323  ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count)
324])
325
326MINIDUMP_MEMORY_LIST64 = Descriptor([
327  ("range_count", ctypes.c_uint64),
328  ("base_rva", ctypes.c_uint64),
329  ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR64.ctype * m.range_count)
330])
331
332MINIDUMP_THREAD = Descriptor([
333  ("id", ctypes.c_uint32),
334  ("suspend_count", ctypes.c_uint32),
335  ("priority_class", ctypes.c_uint32),
336  ("priority", ctypes.c_uint32),
337  ("ted", ctypes.c_uint64),
338  ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype),
339  ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
340])
341
342MINIDUMP_THREAD_LIST = Descriptor([
343  ("thread_count", ctypes.c_uint32),
344  ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
345])
346
347MINIDUMP_RAW_SYSTEM_INFO = Descriptor([
348  ("processor_architecture", ctypes.c_uint16)
349])
350
351MD_CPU_ARCHITECTURE_X86 = 0
352MD_CPU_ARCHITECTURE_AMD64 = 9
353
354class MinidumpReader(object):
355  """Minidump (.dmp) reader."""
356
357  _HEADER_MAGIC = 0x504d444d
358
359  def __init__(self, options, minidump_name):
360    self.minidump_name = minidump_name
361    self.minidump_file = open(minidump_name, "r")
362    self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE)
363    self.header = MINIDUMP_HEADER.Read(self.minidump, 0)
364    if self.header.signature != MinidumpReader._HEADER_MAGIC:
365      print >>sys.stderr, "Warning: unsupported minidump header magic"
366    DebugPrint(self.header)
367    directories = []
368    offset = self.header.stream_directories_rva
369    for _ in xrange(self.header.stream_count):
370      directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset))
371      offset += MINIDUMP_DIRECTORY.size
372    self.arch = None
373    self.exception = None
374    self.exception_context = None
375    self.memory_list = None
376    self.memory_list64 = None
377    self.thread_map = {}
378
379    # Find MDRawSystemInfo stream and determine arch.
380    for d in directories:
381      if d.stream_type == MD_SYSTEM_INFO_STREAM:
382        system_info = MINIDUMP_RAW_SYSTEM_INFO.Read(
383            self.minidump, d.location.rva)
384        self.arch = system_info.processor_architecture
385        assert self.arch in [MD_CPU_ARCHITECTURE_AMD64, MD_CPU_ARCHITECTURE_X86]
386    assert not self.arch is None
387
388    for d in directories:
389      DebugPrint(d)
390      if d.stream_type == MD_EXCEPTION_STREAM:
391        self.exception = MINIDUMP_EXCEPTION_STREAM.Read(
392          self.minidump, d.location.rva)
393        DebugPrint(self.exception)
394        if self.arch == MD_CPU_ARCHITECTURE_X86:
395          self.exception_context = MINIDUMP_CONTEXT_X86.Read(
396              self.minidump, self.exception.thread_context.rva)
397        elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
398          self.exception_context = MINIDUMP_CONTEXT_AMD64.Read(
399              self.minidump, self.exception.thread_context.rva)
400        DebugPrint(self.exception_context)
401      elif d.stream_type == MD_THREAD_LIST_STREAM:
402        thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva)
403        assert ctypes.sizeof(thread_list) == d.location.data_size
404        DebugPrint(thread_list)
405        for thread in thread_list.threads:
406          DebugPrint(thread)
407          self.thread_map[thread.id] = thread
408      elif d.stream_type == MD_MEMORY_LIST_STREAM:
409        print >>sys.stderr, "Warning: not a full minidump"
410        assert self.memory_list is None
411        self.memory_list = MINIDUMP_MEMORY_LIST.Read(
412          self.minidump, d.location.rva)
413        assert ctypes.sizeof(self.memory_list) == d.location.data_size
414        DebugPrint(self.memory_list)
415      elif d.stream_type == MD_MEMORY_64_LIST_STREAM:
416        assert self.memory_list64 is None
417        self.memory_list64 = MINIDUMP_MEMORY_LIST64.Read(
418          self.minidump, d.location.rva)
419        assert ctypes.sizeof(self.memory_list64) == d.location.data_size
420        DebugPrint(self.memory_list64)
421
422  def IsValidAddress(self, address):
423    return self.FindLocation(address) is not None
424
425  def ReadU8(self, address):
426    location = self.FindLocation(address)
427    return ctypes.c_uint8.from_buffer(self.minidump, location).value
428
429  def ReadU32(self, address):
430    location = self.FindLocation(address)
431    return ctypes.c_uint32.from_buffer(self.minidump, location).value
432
433  def ReadU64(self, address):
434    location = self.FindLocation(address)
435    return ctypes.c_uint64.from_buffer(self.minidump, location).value
436
437  def ReadUIntPtr(self, address):
438    if self.arch == MD_CPU_ARCHITECTURE_AMD64:
439      return self.ReadU64(address)
440    elif self.arch == MD_CPU_ARCHITECTURE_X86:
441      return self.ReadU32(address)
442
443  def ReadBytes(self, address, size):
444    location = self.FindLocation(address)
445    return self.minidump[location:location + size]
446
447  def FindLocation(self, address):
448    offset = 0
449    if self.memory_list64 is not None:
450      for r in self.memory_list64.ranges:
451        if r.start <= address < r.start + r.size:
452          return self.memory_list64.base_rva + offset + address - r.start
453        offset += r.size
454    if self.memory_list is not None:
455      for r in self.memory_list.ranges:
456        if r.start <= address < r.start + r.memory.data_size:
457          return r.memory.rva + address - r.start
458    return None
459
460  def GetDisasmLines(self, address, size):
461    location = self.FindLocation(address)
462    if location is None: return []
463    arch = None
464    if self.arch == MD_CPU_ARCHITECTURE_X86:
465      arch = "ia32"
466    elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
467      arch = "x64"
468    return disasm.GetDisasmLines(self.minidump_name,
469                                 location,
470                                 size,
471                                 arch,
472                                 False)
473
474
475  def Dispose(self):
476    self.minidump.close()
477    self.minidump_file.close()
478
479  def ExceptionIP(self):
480    if self.arch == MD_CPU_ARCHITECTURE_AMD64:
481      return self.exception_context.rip
482    elif self.arch == MD_CPU_ARCHITECTURE_X86:
483      return self.exception_context.eip
484
485  def ExceptionSP(self):
486    if self.arch == MD_CPU_ARCHITECTURE_AMD64:
487      return self.exception_context.rsp
488    elif self.arch == MD_CPU_ARCHITECTURE_X86:
489      return self.exception_context.esp
490
491  def FormatIntPtr(self, value):
492    if self.arch == MD_CPU_ARCHITECTURE_AMD64:
493      return "%016x" % value
494    elif self.arch == MD_CPU_ARCHITECTURE_X86:
495      return "%08x" % value
496
497  def PointerSize(self):
498    if self.arch == MD_CPU_ARCHITECTURE_AMD64:
499      return 8
500    elif self.arch == MD_CPU_ARCHITECTURE_X86:
501      return 4
502
503  def Register(self, name):
504    return self.exception_context.__getattribute__(name)
505
506
507# List of V8 instance types. Obtained by adding the code below to any .cc file.
508#
509# #define DUMP_TYPE(T) printf("  %d: \"%s\",\n", T, #T);
510# struct P {
511#   P() {
512#     printf("INSTANCE_TYPES = {\n");
513#     INSTANCE_TYPE_LIST(DUMP_TYPE)
514#     printf("}\n");
515#   }
516# };
517# static P p;
518INSTANCE_TYPES = {
519  64: "SYMBOL_TYPE",
520  68: "ASCII_SYMBOL_TYPE",
521  65: "CONS_SYMBOL_TYPE",
522  69: "CONS_ASCII_SYMBOL_TYPE",
523  66: "EXTERNAL_SYMBOL_TYPE",
524  74: "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE",
525  70: "EXTERNAL_ASCII_SYMBOL_TYPE",
526  82: "SHORT_EXTERNAL_SYMBOL_TYPE",
527  90: "SHORT_EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE",
528  86: "SHORT_EXTERNAL_ASCII_SYMBOL_TYPE",
529  0: "STRING_TYPE",
530  4: "ASCII_STRING_TYPE",
531  1: "CONS_STRING_TYPE",
532  5: "CONS_ASCII_STRING_TYPE",
533  3: "SLICED_STRING_TYPE",
534  2: "EXTERNAL_STRING_TYPE",
535  10: "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE",
536  6: "EXTERNAL_ASCII_STRING_TYPE",
537  18: "SHORT_EXTERNAL_STRING_TYPE",
538  26: "SHORT_EXTERNAL_STRING_WITH_ASCII_DATA_TYPE",
539  22: "SHORT_EXTERNAL_ASCII_STRING_TYPE",
540  6: "PRIVATE_EXTERNAL_ASCII_STRING_TYPE",
541  128: "MAP_TYPE",
542  129: "CODE_TYPE",
543  130: "ODDBALL_TYPE",
544  131: "JS_GLOBAL_PROPERTY_CELL_TYPE",
545  132: "HEAP_NUMBER_TYPE",
546  133: "FOREIGN_TYPE",
547  134: "BYTE_ARRAY_TYPE",
548  135: "FREE_SPACE_TYPE",
549  136: "EXTERNAL_BYTE_ARRAY_TYPE",
550  137: "EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE",
551  138: "EXTERNAL_SHORT_ARRAY_TYPE",
552  139: "EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE",
553  140: "EXTERNAL_INT_ARRAY_TYPE",
554  141: "EXTERNAL_UNSIGNED_INT_ARRAY_TYPE",
555  142: "EXTERNAL_FLOAT_ARRAY_TYPE",
556  144: "EXTERNAL_PIXEL_ARRAY_TYPE",
557  146: "FILLER_TYPE",
558  147: "ACCESSOR_INFO_TYPE",
559  148: "ACCESSOR_PAIR_TYPE",
560  149: "ACCESS_CHECK_INFO_TYPE",
561  150: "INTERCEPTOR_INFO_TYPE",
562  151: "CALL_HANDLER_INFO_TYPE",
563  152: "FUNCTION_TEMPLATE_INFO_TYPE",
564  153: "OBJECT_TEMPLATE_INFO_TYPE",
565  154: "SIGNATURE_INFO_TYPE",
566  155: "TYPE_SWITCH_INFO_TYPE",
567  156: "SCRIPT_TYPE",
568  157: "CODE_CACHE_TYPE",
569  158: "POLYMORPHIC_CODE_CACHE_TYPE",
570  161: "FIXED_ARRAY_TYPE",
571  145: "FIXED_DOUBLE_ARRAY_TYPE",
572  162: "SHARED_FUNCTION_INFO_TYPE",
573  163: "JS_MESSAGE_OBJECT_TYPE",
574  166: "JS_VALUE_TYPE",
575  167: "JS_OBJECT_TYPE",
576  168: "JS_CONTEXT_EXTENSION_OBJECT_TYPE",
577  169: "JS_GLOBAL_OBJECT_TYPE",
578  170: "JS_BUILTINS_OBJECT_TYPE",
579  171: "JS_GLOBAL_PROXY_TYPE",
580  172: "JS_ARRAY_TYPE",
581  165: "JS_PROXY_TYPE",
582  175: "JS_WEAK_MAP_TYPE",
583  176: "JS_REGEXP_TYPE",
584  177: "JS_FUNCTION_TYPE",
585  164: "JS_FUNCTION_PROXY_TYPE",
586  159: "DEBUG_INFO_TYPE",
587  160: "BREAK_POINT_INFO_TYPE",
588}
589
590
591class Printer(object):
592  """Printer with indentation support."""
593
594  def __init__(self):
595    self.indent = 0
596
597  def Indent(self):
598    self.indent += 2
599
600  def Dedent(self):
601    self.indent -= 2
602
603  def Print(self, string):
604    print "%s%s" % (self._IndentString(), string)
605
606  def PrintLines(self, lines):
607    indent = self._IndentString()
608    print "\n".join("%s%s" % (indent, line) for line in lines)
609
610  def _IndentString(self):
611    return self.indent * " "
612
613
614ADDRESS_RE = re.compile(r"0x[0-9a-fA-F]+")
615
616
617def FormatDisasmLine(start, heap, line):
618  line_address = start + line[0]
619  stack_slot = heap.stack_map.get(line_address)
620  marker = "  "
621  if stack_slot:
622    marker = "=>"
623  code = AnnotateAddresses(heap, line[1])
624  return "%s%08x %08x: %s" % (marker, line_address, line[0], code)
625
626
627def AnnotateAddresses(heap, line):
628  extra = []
629  for m in ADDRESS_RE.finditer(line):
630    maybe_address = int(m.group(0), 16)
631    object = heap.FindObject(maybe_address)
632    if not object: continue
633    extra.append(str(object))
634  if len(extra) == 0: return line
635  return "%s  ;; %s" % (line, ", ".join(extra))
636
637
638class HeapObject(object):
639  def __init__(self, heap, map, address):
640    self.heap = heap
641    self.map = map
642    self.address = address
643
644  def Is(self, cls):
645    return isinstance(self, cls)
646
647  def Print(self, p):
648    p.Print(str(self))
649
650  def __str__(self):
651    return "HeapObject(%s, %s)" % (self.heap.reader.FormatIntPtr(self.address),
652                                   INSTANCE_TYPES[self.map.instance_type])
653
654  def ObjectField(self, offset):
655    field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
656    return self.heap.FindObjectOrSmi(field_value)
657
658  def SmiField(self, offset):
659    field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
660    assert (field_value & 1) == 0
661    return field_value / 2
662
663
664class Map(HeapObject):
665  def InstanceTypeOffset(self):
666    return self.heap.PointerSize() + self.heap.IntSize()
667
668  def __init__(self, heap, map, address):
669    HeapObject.__init__(self, heap, map, address)
670    self.instance_type = \
671        heap.reader.ReadU8(self.address + self.InstanceTypeOffset())
672
673
674class String(HeapObject):
675  def LengthOffset(self):
676    return self.heap.PointerSize()
677
678  def __init__(self, heap, map, address):
679    HeapObject.__init__(self, heap, map, address)
680    self.length = self.SmiField(self.LengthOffset())
681
682  def GetChars(self):
683    return "?string?"
684
685  def Print(self, p):
686    p.Print(str(self))
687
688  def __str__(self):
689    return "\"%s\"" % self.GetChars()
690
691
692class SeqString(String):
693  def CharsOffset(self):
694    return self.heap.PointerSize() * 3
695
696  def __init__(self, heap, map, address):
697    String.__init__(self, heap, map, address)
698    self.chars = heap.reader.ReadBytes(self.address + self.CharsOffset(),
699                                       self.length)
700
701  def GetChars(self):
702    return self.chars
703
704
705class ExternalString(String):
706  # TODO(vegorov) fix ExternalString for X64 architecture
707  RESOURCE_OFFSET = 12
708
709  WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
710  WEBKIT_STRING_IMPL_CHARS_OFFSET = 8
711
712  def __init__(self, heap, map, address):
713    String.__init__(self, heap, map, address)
714    reader = heap.reader
715    self.resource = \
716        reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET)
717    self.chars = "?external string?"
718    if not reader.IsValidAddress(self.resource): return
719    string_impl_address = self.resource + \
720        ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET
721    if not reader.IsValidAddress(string_impl_address): return
722    string_impl = reader.ReadU32(string_impl_address)
723    chars_ptr_address = string_impl + \
724        ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET
725    if not reader.IsValidAddress(chars_ptr_address): return
726    chars_ptr = reader.ReadU32(chars_ptr_address)
727    if not reader.IsValidAddress(chars_ptr): return
728    raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length)
729    self.chars = codecs.getdecoder("utf16")(raw_chars)[0]
730
731  def GetChars(self):
732    return self.chars
733
734
735class ConsString(String):
736  def LeftOffset(self):
737    return self.heap.PointerSize() * 3
738
739  def RightOffset(self):
740    return self.heap.PointerSize() * 4
741
742  def __init__(self, heap, map, address):
743    String.__init__(self, heap, map, address)
744    self.left = self.ObjectField(self.LeftOffset())
745    self.right = self.ObjectField(self.RightOffset())
746
747  def GetChars(self):
748    return self.left.GetChars() + self.right.GetChars()
749
750
751class Oddball(HeapObject):
752  def ToStringOffset(self):
753    return self.heap.PointerSize()
754
755  def __init__(self, heap, map, address):
756    HeapObject.__init__(self, heap, map, address)
757    self.to_string = self.ObjectField(self.ToStringOffset())
758
759  def Print(self, p):
760    p.Print(str(self))
761
762  def __str__(self):
763    return "<%s>" % self.to_string.GetChars()
764
765
766class FixedArray(HeapObject):
767  def LengthOffset(self):
768    return self.heap.PointerSize()
769
770  def ElementsOffset(self):
771    return self.heap.PointerSize() * 2
772
773  def __init__(self, heap, map, address):
774    HeapObject.__init__(self, heap, map, address)
775    self.length = self.SmiField(self.LengthOffset())
776
777  def Print(self, p):
778    p.Print("FixedArray(%s) {" % self.heap.reader.FormatIntPtr(self.address))
779    p.Indent()
780    p.Print("length: %d" % self.length)
781    base_offset = self.ElementsOffset()
782    for i in xrange(self.length):
783      offset = base_offset + 4 * i
784      p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
785    p.Dedent()
786    p.Print("}")
787
788  def __str__(self):
789    return "FixedArray(%08x, length=%d)" % (self.address, self.length)
790
791
792class JSFunction(HeapObject):
793  def CodeEntryOffset(self):
794    return 3 * self.heap.PointerSize()
795
796  def SharedOffset(self):
797    return 5 * self.heap.PointerSize()
798
799  def __init__(self, heap, map, address):
800    HeapObject.__init__(self, heap, map, address)
801    code_entry = \
802        heap.reader.ReadU32(self.address + self.CodeEntryOffset())
803    self.code = heap.FindObject(code_entry - Code.HeaderSize(heap) + 1)
804    self.shared = self.ObjectField(self.SharedOffset())
805
806  def Print(self, p):
807    source = "\n".join("  %s" % line for line in self._GetSource().split("\n"))
808    p.Print("JSFunction(%s) {" % self.heap.reader.FormatIntPtr(self.address))
809    p.Indent()
810    p.Print("inferred name: %s" % self.shared.inferred_name)
811    if self.shared.script.Is(Script) and self.shared.script.name.Is(String):
812      p.Print("script name: %s" % self.shared.script.name)
813    p.Print("source:")
814    p.PrintLines(self._GetSource().split("\n"))
815    p.Print("code:")
816    self.code.Print(p)
817    if self.code != self.shared.code:
818      p.Print("unoptimized code:")
819      self.shared.code.Print(p)
820    p.Dedent()
821    p.Print("}")
822
823  def __str__(self):
824    inferred_name = ""
825    if self.shared.Is(SharedFunctionInfo):
826      inferred_name = self.shared.inferred_name
827    return "JSFunction(%s, %s)" % \
828          (self.heap.reader.FormatIntPtr(self.address), inferred_name)
829
830  def _GetSource(self):
831    source = "?source?"
832    start = self.shared.start_position
833    end = self.shared.end_position
834    if not self.shared.script.Is(Script): return source
835    script_source = self.shared.script.source
836    if not script_source.Is(String): return source
837    return script_source.GetChars()[start:end]
838
839
840class SharedFunctionInfo(HeapObject):
841  def CodeOffset(self):
842    return 2 * self.heap.PointerSize()
843
844  def ScriptOffset(self):
845    return 7 * self.heap.PointerSize()
846
847  def InferredNameOffset(self):
848    return 9 * self.heap.PointerSize()
849
850  def EndPositionOffset(self):
851    return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize()
852
853  def StartPositionAndTypeOffset(self):
854    return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize()
855
856  def __init__(self, heap, map, address):
857    HeapObject.__init__(self, heap, map, address)
858    self.code = self.ObjectField(self.CodeOffset())
859    self.script = self.ObjectField(self.ScriptOffset())
860    self.inferred_name = self.ObjectField(self.InferredNameOffset())
861    if heap.PointerSize() == 8:
862      start_position_and_type = \
863          heap.reader.ReadU32(self.StartPositionAndTypeOffset())
864      self.start_position = start_position_and_type >> 2
865      pseudo_smi_end_position = \
866          heap.reader.ReadU32(self.EndPositionOffset())
867      self.end_position = pseudo_smi_end_position >> 2
868    else:
869      start_position_and_type = \
870          self.SmiField(self.StartPositionAndTypeOffset())
871      self.start_position = start_position_and_type >> 2
872      self.end_position = \
873          self.SmiField(self.EndPositionOffset())
874
875
876class Script(HeapObject):
877  def SourceOffset(self):
878    return self.heap.PointerSize()
879
880  def NameOffset(self):
881    return self.SourceOffset() + self.heap.PointerSize()
882
883  def __init__(self, heap, map, address):
884    HeapObject.__init__(self, heap, map, address)
885    self.source = self.ObjectField(self.SourceOffset())
886    self.name = self.ObjectField(self.NameOffset())
887
888
889class Code(HeapObject):
890  CODE_ALIGNMENT_MASK = (1 << 5) - 1
891
892  def InstructionSizeOffset(self):
893    return self.heap.PointerSize()
894
895  @staticmethod
896  def HeaderSize(heap):
897    return (heap.PointerSize() + heap.IntSize() + \
898        4 * heap.PointerSize() + 3 * heap.IntSize() + \
899        Code.CODE_ALIGNMENT_MASK) & ~Code.CODE_ALIGNMENT_MASK
900
901  def __init__(self, heap, map, address):
902    HeapObject.__init__(self, heap, map, address)
903    self.entry = self.address + Code.HeaderSize(heap)
904    self.instruction_size = \
905        heap.reader.ReadU32(self.address + self.InstructionSizeOffset())
906
907  def Print(self, p):
908    lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
909    p.Print("Code(%s) {" % self.heap.reader.FormatIntPtr(self.address))
910    p.Indent()
911    p.Print("instruction_size: %d" % self.instruction_size)
912    p.PrintLines(self._FormatLine(line) for line in lines)
913    p.Dedent()
914    p.Print("}")
915
916  def _FormatLine(self, line):
917    return FormatDisasmLine(self.entry, self.heap, line)
918
919
920class V8Heap(object):
921  CLASS_MAP = {
922    "SYMBOL_TYPE": SeqString,
923    "ASCII_SYMBOL_TYPE": SeqString,
924    "CONS_SYMBOL_TYPE": ConsString,
925    "CONS_ASCII_SYMBOL_TYPE": ConsString,
926    "EXTERNAL_SYMBOL_TYPE": ExternalString,
927    "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString,
928    "EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString,
929    "SHORT_EXTERNAL_SYMBOL_TYPE": ExternalString,
930    "SHORT_EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString,
931    "SHORT_EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString,
932    "STRING_TYPE": SeqString,
933    "ASCII_STRING_TYPE": SeqString,
934    "CONS_STRING_TYPE": ConsString,
935    "CONS_ASCII_STRING_TYPE": ConsString,
936    "EXTERNAL_STRING_TYPE": ExternalString,
937    "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE": ExternalString,
938    "EXTERNAL_ASCII_STRING_TYPE": ExternalString,
939
940    "MAP_TYPE": Map,
941    "ODDBALL_TYPE": Oddball,
942    "FIXED_ARRAY_TYPE": FixedArray,
943    "JS_FUNCTION_TYPE": JSFunction,
944    "SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo,
945    "SCRIPT_TYPE": Script,
946    "CODE_TYPE": Code
947  }
948
949  def __init__(self, reader, stack_map):
950    self.reader = reader
951    self.stack_map = stack_map
952    self.objects = {}
953
954  def FindObjectOrSmi(self, tagged_address):
955    if (tagged_address & 1) == 0: return tagged_address / 2
956    return self.FindObject(tagged_address)
957
958  def FindObject(self, tagged_address):
959    if tagged_address in self.objects:
960      return self.objects[tagged_address]
961    if (tagged_address & self.ObjectAlignmentMask()) != 1: return None
962    address = tagged_address - 1
963    if not self.reader.IsValidAddress(address): return None
964    map_tagged_address = self.reader.ReadUIntPtr(address)
965    if tagged_address == map_tagged_address:
966      # Meta map?
967      meta_map = Map(self, None, address)
968      instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type)
969      if instance_type_name != "MAP_TYPE": return None
970      meta_map.map = meta_map
971      object = meta_map
972    else:
973      map = self.FindMap(map_tagged_address)
974      if map is None: return None
975      instance_type_name = INSTANCE_TYPES.get(map.instance_type)
976      if instance_type_name is None: return None
977      cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
978      object = cls(self, map, address)
979    self.objects[tagged_address] = object
980    return object
981
982  def FindMap(self, tagged_address):
983    if (tagged_address & self.MapAlignmentMask()) != 1: return None
984    address = tagged_address - 1
985    if not self.reader.IsValidAddress(address): return None
986    object = Map(self, None, address)
987    return object
988
989  def IntSize(self):
990    return 4
991
992  def PointerSize(self):
993    return self.reader.PointerSize()
994
995  def ObjectAlignmentMask(self):
996    return self.PointerSize() - 1
997
998  def MapAlignmentMask(self):
999    if self.reader.arch == MD_CPU_ARCHITECTURE_AMD64:
1000      return (1 << 4) - 1
1001    elif self.reader.arch == MD_CPU_ARCHITECTURE_X86:
1002      return (1 << 5) - 1
1003
1004
1005EIP_PROXIMITY = 64
1006
1007CONTEXT_FOR_ARCH = {
1008    MD_CPU_ARCHITECTURE_AMD64:
1009      ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip'],
1010    MD_CPU_ARCHITECTURE_X86:
1011      ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip']
1012}
1013
1014def AnalyzeMinidump(options, minidump_name):
1015  reader = MinidumpReader(options, minidump_name)
1016  DebugPrint("========================================")
1017  if reader.exception is None:
1018    print "Minidump has no exception info"
1019    return
1020  print "Exception info:"
1021  exception_thread = reader.thread_map[reader.exception.thread_id]
1022  print "  thread id: %d" % exception_thread.id
1023  print "  code: %08X" % reader.exception.exception.code
1024  print "  context:"
1025  for r in CONTEXT_FOR_ARCH[reader.arch]:
1026    print "    %s: %s" % (r, reader.FormatIntPtr(reader.Register(r)))
1027  # TODO(vitalyr): decode eflags.
1028  print "    eflags: %s" % bin(reader.exception_context.eflags)[2:]
1029  print
1030
1031  stack_top = reader.ExceptionSP()
1032  stack_bottom = exception_thread.stack.start + \
1033      exception_thread.stack.memory.data_size
1034  stack_map = {reader.ExceptionIP(): -1}
1035  for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
1036    maybe_address = reader.ReadUIntPtr(slot)
1037    if not maybe_address in stack_map:
1038      stack_map[maybe_address] = slot
1039  heap = V8Heap(reader, stack_map)
1040
1041  print "Disassembly around exception.eip:"
1042  start = reader.ExceptionIP() - EIP_PROXIMITY
1043  lines = reader.GetDisasmLines(start, 2 * EIP_PROXIMITY)
1044  for line in lines:
1045    print FormatDisasmLine(start, heap, line)
1046  print
1047
1048  print "Annotated stack (from exception.esp to bottom):"
1049  for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
1050    maybe_address = reader.ReadUIntPtr(slot)
1051    heap_object = heap.FindObject(maybe_address)
1052    print "%s: %s" % (reader.FormatIntPtr(slot),
1053                      reader.FormatIntPtr(maybe_address))
1054    if heap_object:
1055      heap_object.Print(Printer())
1056      print
1057
1058  reader.Dispose()
1059
1060
1061if __name__ == "__main__":
1062  parser = optparse.OptionParser(USAGE)
1063  options, args = parser.parse_args()
1064  if len(args) != 1:
1065    parser.print_help()
1066    sys.exit(1)
1067  AnalyzeMinidump(options, args[0])
1068