Add X64 minidumps support to tools/grokdump.py

R=erik.corry@gmail.com

Review URL: http://codereview.chromium.org/8957005

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10263 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
vegorov@chromium.org 2011-12-15 12:13:34 +00:00
parent 626b61f967
commit 7a93464ca0

View File

@ -52,6 +52,7 @@ Examples:
$ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp
"""
DEBUG=False
@ -233,6 +234,80 @@ MINIDUMP_CONTEXT_X86 = Descriptor([
MD_CONTEXT_X86_EXTENDED_REGISTERS))
])
MD_CONTEXT_AMD64 = 0x00100000
MD_CONTEXT_AMD64_CONTROL = (MD_CONTEXT_AMD64 | 0x00000001)
MD_CONTEXT_AMD64_INTEGER = (MD_CONTEXT_AMD64 | 0x00000002)
MD_CONTEXT_AMD64_SEGMENTS = (MD_CONTEXT_AMD64 | 0x00000004)
MD_CONTEXT_AMD64_FLOATING_POINT = (MD_CONTEXT_AMD64 | 0x00000008)
MD_CONTEXT_AMD64_DEBUG_REGISTERS = (MD_CONTEXT_AMD64 | 0x00000010)
MINIDUMP_CONTEXT_AMD64 = Descriptor([
("p1_home", ctypes.c_uint64),
("p2_home", ctypes.c_uint64),
("p3_home", ctypes.c_uint64),
("p4_home", ctypes.c_uint64),
("p5_home", ctypes.c_uint64),
("p6_home", ctypes.c_uint64),
("context_flags", ctypes.c_uint32),
("mx_csr", ctypes.c_uint32),
# MD_CONTEXT_AMD64_CONTROL.
("cs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
# MD_CONTEXT_AMD64_SEGMENTS
("ds", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
("es", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
("fs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
("gs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
# MD_CONTEXT_AMD64_CONTROL.
("ss", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_AMD64_CONTROL)),
# MD_CONTEXT_AMD64_DEBUG_REGISTERS.
("dr0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
("dr1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
("dr2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
("dr3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
("dr6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
("dr7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
# MD_CONTEXT_AMD64_INTEGER.
("rax", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
("rcx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
("rdx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
("rbx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
# MD_CONTEXT_AMD64_CONTROL.
("rsp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
# MD_CONTEXT_AMD64_INTEGER.
("rbp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
("rsi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
("rdi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
# MD_CONTEXT_AMD64_CONTROL.
("rip", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
# MD_CONTEXT_AMD64_FLOATING_POINT
("sse_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
MD_CONTEXT_AMD64_FLOATING_POINT)),
("vector_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
MD_CONTEXT_AMD64_FLOATING_POINT)),
("vector_control", EnableOnFlag(ctypes.c_uint64,
MD_CONTEXT_AMD64_FLOATING_POINT)),
# MD_CONTEXT_AMD64_DEBUG_REGISTERS.
("debug_control", EnableOnFlag(ctypes.c_uint64,
MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
("last_branch_to_rip", EnableOnFlag(ctypes.c_uint64,
MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
("last_branch_from_rip", EnableOnFlag(ctypes.c_uint64,
MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
("last_exception_to_rip", EnableOnFlag(ctypes.c_uint64,
MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
("last_exception_from_rip", EnableOnFlag(ctypes.c_uint64,
MD_CONTEXT_AMD64_DEBUG_REGISTERS))
])
MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([
("start", ctypes.c_uint64),
("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
@ -269,6 +344,12 @@ MINIDUMP_THREAD_LIST = Descriptor([
("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
])
MINIDUMP_RAW_SYSTEM_INFO = Descriptor([
("processor_architecture", ctypes.c_uint16)
])
MD_CPU_ARCHITECTURE_X86 = 0
MD_CPU_ARCHITECTURE_AMD64 = 9
class MinidumpReader(object):
"""Minidump (.dmp) reader."""
@ -288,20 +369,34 @@ class MinidumpReader(object):
for _ in xrange(self.header.stream_count):
directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset))
offset += MINIDUMP_DIRECTORY.size
self.arch = None
self.exception = None
self.exception_context = None
self.memory_list = None
self.memory_list64 = None
self.thread_map = {}
# Find MDRawSystemInfo stream and determine arch.
for d in directories:
if d.stream_type == MD_SYSTEM_INFO_STREAM:
system_info = MINIDUMP_RAW_SYSTEM_INFO.Read(
self.minidump, d.location.rva)
self.arch = system_info.processor_architecture
assert self.arch in [MD_CPU_ARCHITECTURE_AMD64, MD_CPU_ARCHITECTURE_X86]
assert not self.arch is None
for d in directories:
DebugPrint(d)
# TODO(vitalyr): extract system info including CPU features.
if d.stream_type == MD_EXCEPTION_STREAM:
self.exception = MINIDUMP_EXCEPTION_STREAM.Read(
self.minidump, d.location.rva)
DebugPrint(self.exception)
self.exception_context = MINIDUMP_CONTEXT_X86.Read(
self.minidump, self.exception.thread_context.rva)
if self.arch == MD_CPU_ARCHITECTURE_X86:
self.exception_context = MINIDUMP_CONTEXT_X86.Read(
self.minidump, self.exception.thread_context.rva)
elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
self.exception_context = MINIDUMP_CONTEXT_AMD64.Read(
self.minidump, self.exception.thread_context.rva)
DebugPrint(self.exception_context)
elif d.stream_type == MD_THREAD_LIST_STREAM:
thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva)
@ -335,6 +430,16 @@ class MinidumpReader(object):
location = self.FindLocation(address)
return ctypes.c_uint32.from_buffer(self.minidump, location).value
def ReadU64(self, address):
location = self.FindLocation(address)
return ctypes.c_uint64.from_buffer(self.minidump, location).value
def ReadUIntPtr(self, address):
if self.arch == MD_CPU_ARCHITECTURE_AMD64:
return self.ReadU64(address)
elif self.arch == MD_CPU_ARCHITECTURE_X86:
return self.ReadU32(address)
def ReadBytes(self, address, size):
location = self.FindLocation(address)
return self.minidump[location:location + size]
@ -355,10 +460,15 @@ class MinidumpReader(object):
def GetDisasmLines(self, address, size):
location = self.FindLocation(address)
if location is None: return []
arch = None
if self.arch == MD_CPU_ARCHITECTURE_X86:
arch = "ia32"
elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
arch = "x64"
return disasm.GetDisasmLines(self.minidump_name,
location,
size,
"ia32",
arch,
False)
@ -366,6 +476,33 @@ class MinidumpReader(object):
self.minidump.close()
self.minidump_file.close()
def ExceptionIP(self):
if self.arch == MD_CPU_ARCHITECTURE_AMD64:
return self.exception_context.rip
elif self.arch == MD_CPU_ARCHITECTURE_X86:
return self.exception_context.eip
def ExceptionSP(self):
if self.arch == MD_CPU_ARCHITECTURE_AMD64:
return self.exception_context.rsp
elif self.arch == MD_CPU_ARCHITECTURE_X86:
return self.exception_context.rbp
def FormatIntPtr(self, value):
if self.arch == MD_CPU_ARCHITECTURE_AMD64:
return "%016x" % value
elif self.arch == MD_CPU_ARCHITECTURE_X86:
return "%08x" % value
def PointerSize(self):
if self.arch == MD_CPU_ARCHITECTURE_AMD64:
return 8
elif self.arch == MD_CPU_ARCHITECTURE_X86:
return 4
def Register(self, name):
return self.exception_context.__getattribute__(name)
# List of V8 instance types. Obtained by adding the code below to any .cc file.
#
@ -501,34 +638,36 @@ class HeapObject(object):
p.Print(str(self))
def __str__(self):
return "HeapObject(%08x, %s)" % (self.address,
INSTANCE_TYPES[self.map.instance_type])
return "HeapObject(%s, %s)" % (self.heap.reader.FormatIntPtr(self.address),
INSTANCE_TYPES[self.map.instance_type])
def ObjectField(self, offset):
field_value = self.heap.reader.ReadU32(self.address + offset)
field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
return self.heap.FindObjectOrSmi(field_value)
def SmiField(self, offset):
field_value = self.heap.reader.ReadU32(self.address + offset)
field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
assert (field_value & 1) == 0
return field_value / 2
class Map(HeapObject):
INSTANCE_TYPE_OFFSET = 8
def InstanceTypeOffset():
return self.heap.PointerSize() + self.heap.IntSize()
def __init__(self, heap, map, address):
HeapObject.__init__(self, heap, map, address)
self.instance_type = \
heap.reader.ReadU8(self.address + Map.INSTANCE_TYPE_OFFSET)
heap.reader.ReadU8(self.address + self.InstanceTypeOffset())
class String(HeapObject):
LENGTH_OFFSET = 4
def LengthOffset(self):
return self.heap.PointerSize()
def __init__(self, heap, map, address):
HeapObject.__init__(self, heap, map, address)
self.length = self.SmiField(String.LENGTH_OFFSET)
self.length = self.SmiField(self.LengthOffset())
def GetChars(self):
return "?string?"
@ -541,11 +680,12 @@ class String(HeapObject):
class SeqString(String):
CHARS_OFFSET = 12
def CharsOffset(self):
return self.heap.PointerSize() * 3
def __init__(self, heap, map, address):
String.__init__(self, heap, map, address)
self.chars = heap.reader.ReadBytes(self.address + SeqString.CHARS_OFFSET,
self.chars = heap.reader.ReadBytes(self.address + self.CharsOffset(),
self.length)
def GetChars(self):
@ -553,6 +693,7 @@ class SeqString(String):
class ExternalString(String):
# TODO(vegorov) fix ExternalString for X64 architecture
RESOURCE_OFFSET = 12
WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
@ -582,24 +723,28 @@ class ExternalString(String):
class ConsString(String):
LEFT_OFFSET = 12
RIGHT_OFFSET = 16
def LeftOffset(self):
return self.heap.PointerSize() * 3
def RightOffset(self):
return self.heap.PointerSize() * 4
def __init__(self, heap, map, address):
String.__init__(self, heap, map, address)
self.left = self.ObjectField(ConsString.LEFT_OFFSET)
self.right = self.ObjectField(ConsString.RIGHT_OFFSET)
self.left = self.ObjectField(self.LeftOffset())
self.right = self.ObjectField(self.RightOffset())
def GetChars(self):
return self.left.GetChars() + self.right.GetChars()
class Oddball(HeapObject):
TO_STRING_OFFSET = 4
def ToStringOffset(self):
return self.heap.PointerSize()
def __init__(self, heap, map, address):
HeapObject.__init__(self, heap, map, address)
self.to_string = self.ObjectField(Oddball.TO_STRING_OFFSET)
self.to_string = self.ObjectField(self.ToStringOffset())
def Print(self, p):
p.Print(str(self))
@ -609,19 +754,23 @@ class Oddball(HeapObject):
class FixedArray(HeapObject):
LENGTH_OFFSET = 4
ELEMENTS_OFFSET = 8
def LengthOffset(self):
return self.heap.PointerSize()
def ElementsOffset(self):
return self.heap.PointerSize() * 2
def __init__(self, heap, map, address):
HeapObject.__init__(self, heap, map, address)
self.length = self.SmiField(FixedArray.LENGTH_OFFSET)
self.length = self.SmiField(self.LengthOffset())
def Print(self, p):
p.Print("FixedArray(%08x) {" % self.address)
p.Print("FixedArray(%s) {" % self.heap.reader.FormatIntPtr(self.address))
p.Indent()
p.Print("length: %d" % self.length)
base_offset = self.ElementsOffset()
for i in xrange(self.length):
offset = FixedArray.ELEMENTS_OFFSET + 4 * i
offset = base_offset + 4 * i
p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
p.Dedent()
p.Print("}")
@ -631,19 +780,22 @@ class FixedArray(HeapObject):
class JSFunction(HeapObject):
CODE_ENTRY_OFFSET = 12
SHARED_OFFSET = 20
def CodeEntryOffset(self):
return 3 * self.heap.PointerSize()
def SharedOffset(self):
return 5 * self.heap.PointerSize()
def __init__(self, heap, map, address):
HeapObject.__init__(self, heap, map, address)
code_entry = \
heap.reader.ReadU32(self.address + JSFunction.CODE_ENTRY_OFFSET)
self.code = heap.FindObject(code_entry - Code.ENTRY_OFFSET + 1)
self.shared = self.ObjectField(JSFunction.SHARED_OFFSET)
heap.reader.ReadU32(self.address + self.CodeEntryOffset())
self.code = heap.FindObject(code_entry - Code.HeaderSize(heap) + 1)
self.shared = self.ObjectField(self.SharedOffset())
def Print(self, p):
source = "\n".join(" %s" % line for line in self._GetSource().split("\n"))
p.Print("JSFunction(%08x) {" % self.address)
p.Print("JSFunction(%s) {" % self.heap.reader.FormatIntPtr(self.address))
p.Indent()
p.Print("inferred name: %s" % self.shared.inferred_name)
if self.shared.script.Is(Script) and self.shared.script.name.Is(String):
@ -662,7 +814,8 @@ class JSFunction(HeapObject):
inferred_name = ""
if self.shared.Is(SharedFunctionInfo):
inferred_name = self.shared.inferred_name
return "JSFunction(%08x, %s)" % (self.address, inferred_name)
return "JSFunction(%s, %s)" % \
(self.heap.reader.FormatIntPtr(self.address), inferred_name)
def _GetSource(self):
source = "?source?"
@ -675,47 +828,75 @@ class JSFunction(HeapObject):
class SharedFunctionInfo(HeapObject):
CODE_OFFSET = 2 * 4
SCRIPT_OFFSET = 7 * 4
INFERRED_NAME_OFFSET = 9 * 4
START_POSITION_AND_TYPE_OFFSET = 17 * 4
END_POSITION_OFFSET = 18 * 4
def CodeOffset(self):
return 2 * self.heap.PointerSize()
def ScriptOffset(self):
return 7 * self.heap.PointerSize()
def InferredNameOffset(self):
return 9 * self.heap.PointerSize()
def EndPositionOffset(self):
return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize()
def StartPositionAndTypeOffset(self):
return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize()
def __init__(self, heap, map, address):
HeapObject.__init__(self, heap, map, address)
self.code = self.ObjectField(SharedFunctionInfo.CODE_OFFSET)
self.script = self.ObjectField(SharedFunctionInfo.SCRIPT_OFFSET)
self.inferred_name = \
self.ObjectField(SharedFunctionInfo.INFERRED_NAME_OFFSET)
start_position_and_type = \
self.SmiField(SharedFunctionInfo.START_POSITION_AND_TYPE_OFFSET)
self.start_position = start_position_and_type >> 2
self.end_position = self.SmiField(SharedFunctionInfo.END_POSITION_OFFSET)
self.code = self.ObjectField(self.CodeOffset())
self.script = self.ObjectField(self.ScriptOffset())
self.inferred_name = self.ObjectField(self.InferredNameOffset())
if heap.PointerSize() == 8:
start_position_and_type = \
heap.reader.ReadU32(self.StartPositionAndTypeOffset())
self.start_position = start_position_and_type >> 2
pseudo_smi_end_position = \
heap.reader.ReadU32(self.EndPositionOffset())
self.end_position = pseudo_smi_end_position >> 2
else:
start_position_and_type = \
self.SmiField(self.StartPositionAndTypeOffset())
self.start_position = start_position_and_type >> 2
self.end_position = \
self.SmiField(self.EndPositionOffset())
class Script(HeapObject):
SOURCE_OFFSET = 4
NAME_OFFSET = 8
def SourceOffset(self):
return self.heap.PointerSize()
def NameOffset(self):
return self.SourceOffset() + self.heap.PointerSize()
def __init__(self, heap, map, address):
HeapObject.__init__(self, heap, map, address)
self.source = self.ObjectField(Script.SOURCE_OFFSET)
self.name = self.ObjectField(Script.NAME_OFFSET)
self.source = self.ObjectField(self.SourceOffset())
self.name = self.ObjectField(self.NameOffset())
class Code(HeapObject):
INSTRUCTION_SIZE_OFFSET = 4
ENTRY_OFFSET = 32
CODE_ALIGNMENT_MASK = (1 << 5) - 1
def InstructionSizeOffset(self):
return self.heap.PointerSize()
@staticmethod
def HeaderSize(heap):
return (heap.PointerSize() + heap.IntSize() + \
4 * heap.PointerSize() + 3 * heap.IntSize() + \
CODE_ALIGNMENT_MASK) & ~CODE_ALIGNMENT_MASK
def __init__(self, heap, map, address):
HeapObject.__init__(self, heap, map, address)
self.entry = self.address + Code.ENTRY_OFFSET
self.entry = self.address + Code.HeaderSize(heap)
self.instruction_size = \
heap.reader.ReadU32(self.address + Code.INSTRUCTION_SIZE_OFFSET)
heap.reader.ReadU32(self.address + self.InstructionSizeOffset())
def Print(self, p):
lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
p.Print("Code(%08x) {" % self.address)
p.Print("Code(%s) {" % self.heap.reader.FormatIntPtr(self.address))
p.Indent()
p.Print("instruction_size: %d" % self.instruction_size)
p.PrintLines(self._FormatLine(line) for line in lines)
@ -767,7 +948,7 @@ class V8Heap(object):
if (tagged_address & 1) != 1: return None
address = tagged_address - 1
if not self.reader.IsValidAddress(address): return None
map_tagged_address = self.reader.ReadU32(address)
map_tagged_address = self.reader.ReadUIntPtr(address)
if tagged_address == map_tagged_address:
# Meta map?
meta_map = Map(self, None, address)
@ -785,9 +966,19 @@ class V8Heap(object):
self.objects[tagged_address] = object
return object
def PointerSize(self):
return self.reader.PointerSize()
EIP_PROXIMITY = 64
CONTEXT_FOR_ARCH = {
MD_CPU_ARCHITECTURE_AMD64:
['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip'],
MD_CPU_ARCHITECTURE_X86:
['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip']
}
def AnalyzeMinidump(options, minidump_name):
reader = MinidumpReader(options, minidump_name)
@ -800,40 +991,35 @@ def AnalyzeMinidump(options, minidump_name):
print " thread id: %d" % exception_thread.id
print " code: %08X" % reader.exception.exception.code
print " context:"
print " eax: %08x" % reader.exception_context.eax
print " ebx: %08x" % reader.exception_context.ebx
print " ecx: %08x" % reader.exception_context.ecx
print " edx: %08x" % reader.exception_context.edx
print " edi: %08x" % reader.exception_context.edi
print " esi: %08x" % reader.exception_context.esi
print " ebp: %08x" % reader.exception_context.ebp
print " esp: %08x" % reader.exception_context.esp
print " eip: %08x" % reader.exception_context.eip
for r in CONTEXT_FOR_ARCH[reader.arch]:
print " %s: %s" % (r, reader.FormatIntPtr(reader.Register(r)))
# TODO(vitalyr): decode eflags.
print " eflags: %s" % bin(reader.exception_context.eflags)[2:]
print
stack_top = reader.ExceptionSP()
stack_bottom = exception_thread.stack.start + \
exception_thread.stack.memory.data_size
stack_map = {reader.exception_context.eip: -1}
for slot in xrange(reader.exception_context.esp, stack_bottom, 4):
maybe_address = reader.ReadU32(slot)
stack_map = {reader.ExceptionIP(): -1}
for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
maybe_address = reader.ReadUIntPtr(slot)
if not maybe_address in stack_map:
stack_map[maybe_address] = slot
heap = V8Heap(reader, stack_map)
print "Disassembly around exception.eip:"
start = reader.exception_context.eip - EIP_PROXIMITY
start = reader.ExceptionIP() - EIP_PROXIMITY
lines = reader.GetDisasmLines(start, 2 * EIP_PROXIMITY)
for line in lines:
print FormatDisasmLine(start, heap, line)
print
print "Annotated stack (from exception.esp to bottom):"
for slot in xrange(reader.exception_context.esp, stack_bottom, 4):
maybe_address = reader.ReadU32(slot)
for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
maybe_address = reader.ReadUIntPtr(slot)
heap_object = heap.FindObject(maybe_address)
print "%08x: %08x" % (slot, maybe_address)
print "%s: %s" % (reader.FormatIntPtr(slot),
reader.FormatIntPtr(maybe_address))
if heap_object:
heap_object.Print(Printer())
print