Index: tools/grokdump.py |
diff --git a/tools/grokdump.py b/tools/grokdump.py |
index 407130695a823c58758d914c247155e0c649c52b..2227635d3fa6c9f6a4db75e04304eb212c20410a 100755 |
--- a/tools/grokdump.py |
+++ b/tools/grokdump.py |
@@ -1,6 +1,6 @@ |
#!/usr/bin/env python |
# |
-# Copyright 2011 the V8 project authors. All rights reserved. |
+# Copyright 2012 the V8 project authors. All rights reserved. |
# Redistribution and use in source and binary forms, with or without |
# modification, are permitted provided that the following conditions are |
# met: |
@@ -40,19 +40,17 @@ import re |
import struct |
-USAGE="""usage: %prog [OPTION]... |
+USAGE="""usage: %prog [OPTIONS] [DUMP-FILE] |
Minidump analyzer. |
Shows the processor state at the point of exception including the |
stack of the active thread and the referenced objects in the V8 |
heap. Code objects are disassembled and the addresses linked from the |
-stack (pushed return addresses) are marked with "=>". |
- |
+stack (e.g. pushed return addresses) are marked with "=>". |
Examples: |
- $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp |
-""" |
+ $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp""" |
DEBUG=False |
@@ -108,7 +106,7 @@ class Descriptor(object): |
return Raw |
-def do_dump(reader, heap): |
+def FullDump(reader, heap): |
"""Dump all available memory regions.""" |
def dump_region(reader, start, size, location): |
@@ -420,7 +418,7 @@ class MinidumpReader(object): |
self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE) |
self.header = MINIDUMP_HEADER.Read(self.minidump, 0) |
if self.header.signature != MinidumpReader._HEADER_MAGIC: |
- print >>sys.stderr, "Warning: unsupported minidump header magic" |
+ print >>sys.stderr, "Warning: Unsupported minidump header magic!" |
DebugPrint(self.header) |
directories = [] |
offset = self.header.stream_directories_rva |
@@ -464,7 +462,7 @@ class MinidumpReader(object): |
DebugPrint(thread) |
self.thread_map[thread.id] = thread |
elif d.stream_type == MD_MEMORY_LIST_STREAM: |
- print >>sys.stderr, "Warning: not a full minidump" |
+ print >>sys.stderr, "Warning: This is not a full minidump!" |
assert self.memory_list is None |
self.memory_list = MINIDUMP_MEMORY_LIST.Read( |
self.minidump, d.location.rva) |
@@ -577,14 +575,14 @@ class MinidumpReader(object): |
for r in self.memory_list.ranges: |
cb(self, r.start, r.memory.data_size, r.memory.rva) |
- def FindWord(self, word): |
+ def FindWord(self, word, alignment=0): |
def search_inside_region(reader, start, size, location): |
- for loc in xrange(location, location + size): |
+ location = (location + alignment) & ~alignment |
+ for loc in xrange(location, location + size - self.PointerSize()): |
if reader._ReadWord(loc) == word: |
slot = start + (loc - location) |
print "%s: %s" % (reader.FormatIntPtr(slot), |
reader.FormatIntPtr(word)) |
- |
self.ForEachMemoryRegion(search_inside_region) |
def FindLocation(self, address): |
@@ -710,24 +708,182 @@ INSTANCE_TYPES = { |
156: "SCRIPT_TYPE", |
157: "CODE_CACHE_TYPE", |
158: "POLYMORPHIC_CODE_CACHE_TYPE", |
- 161: "FIXED_ARRAY_TYPE", |
+ 159: "TYPE_FEEDBACK_INFO_TYPE", |
+ 160: "ALIASED_ARGUMENTS_ENTRY_TYPE", |
+ 163: "FIXED_ARRAY_TYPE", |
145: "FIXED_DOUBLE_ARRAY_TYPE", |
- 162: "SHARED_FUNCTION_INFO_TYPE", |
- 163: "JS_MESSAGE_OBJECT_TYPE", |
- 166: "JS_VALUE_TYPE", |
- 167: "JS_OBJECT_TYPE", |
- 168: "JS_CONTEXT_EXTENSION_OBJECT_TYPE", |
- 169: "JS_GLOBAL_OBJECT_TYPE", |
- 170: "JS_BUILTINS_OBJECT_TYPE", |
- 171: "JS_GLOBAL_PROXY_TYPE", |
- 172: "JS_ARRAY_TYPE", |
- 165: "JS_PROXY_TYPE", |
- 175: "JS_WEAK_MAP_TYPE", |
- 176: "JS_REGEXP_TYPE", |
- 177: "JS_FUNCTION_TYPE", |
- 164: "JS_FUNCTION_PROXY_TYPE", |
- 159: "DEBUG_INFO_TYPE", |
- 160: "BREAK_POINT_INFO_TYPE", |
+ 164: "SHARED_FUNCTION_INFO_TYPE", |
+ 165: "JS_MESSAGE_OBJECT_TYPE", |
+ 168: "JS_VALUE_TYPE", |
+ 169: "JS_DATE_TYPE", |
+ 170: "JS_OBJECT_TYPE", |
+ 171: "JS_CONTEXT_EXTENSION_OBJECT_TYPE", |
+ 172: "JS_MODULE_TYPE", |
+ 173: "JS_GLOBAL_OBJECT_TYPE", |
+ 174: "JS_BUILTINS_OBJECT_TYPE", |
+ 175: "JS_GLOBAL_PROXY_TYPE", |
+ 176: "JS_ARRAY_TYPE", |
+ 167: "JS_PROXY_TYPE", |
+ 179: "JS_WEAK_MAP_TYPE", |
+ 180: "JS_REGEXP_TYPE", |
+ 181: "JS_FUNCTION_TYPE", |
+ 166: "JS_FUNCTION_PROXY_TYPE", |
+ 161: "DEBUG_INFO_TYPE", |
+ 162: "BREAK_POINT_INFO_TYPE", |
+} |
+ |
+ |
+# List of known V8 maps. Used to determine the instance type and name |
+# for maps that are part of the root-set and hence on the first page of |
+# the map-space. Obtained by adding the code below to an IA32 release |
+# build with enabled snapshots to the end of the Isolate::Init method. |
+# |
+# #define ROOT_LIST_CASE(type, name, camel_name) \ |
Vitaly Repeshko
2012/05/31 17:34:20
Updating this stuff is going to be even more painf
Michael Starzinger
2012/06/01 09:13:06
Yes, I totally agree. At some point the build syst
|
+# if (o == heap_.name()) n = #camel_name; |
+# #define STRUCT_LIST_CASE(upper_name, camel_name, name) \ |
+# if (o == heap_.name##_map()) n = #camel_name "Map"; |
+# HeapObjectIterator it(heap_.map_space()); |
+# printf("KNOWN_MAPS = {\n"); |
+# for (Object* o = it.Next(); o != NULL; o = it.Next()) { |
+# Map* m = Map::cast(o); |
+# const char* n = ""; |
+# intptr_t p = reinterpret_cast<intptr_t>(m) & 0xfffff; |
+# int t = m->instance_type(); |
+# ROOT_LIST(ROOT_LIST_CASE) |
+# STRUCT_LIST(STRUCT_LIST_CASE) |
+# printf(" 0x%05x: (%d, \"%s\"),\n", p, t, n); |
+# } |
+# printf("}\n"); |
+KNOWN_MAPS = { |
+ 0x08081: (134, "ByteArrayMap"), |
+ 0x080a1: (128, "MetaMap"), |
+ 0x080c1: (130, "OddballMap"), |
+ 0x080e1: (163, "FixedArrayMap"), |
+ 0x08101: (68, "AsciiSymbolMap"), |
+ 0x08121: (132, "HeapNumberMap"), |
+ 0x08141: (135, "FreeSpaceMap"), |
+ 0x08161: (146, "OnePointerFillerMap"), |
+ 0x08181: (146, "TwoPointerFillerMap"), |
+ 0x081a1: (131, "GlobalPropertyCellMap"), |
+ 0x081c1: (164, "SharedFunctionInfoMap"), |
+ 0x081e1: (4, "AsciiStringMap"), |
+ 0x08201: (163, "GlobalContextMap"), |
+ 0x08221: (129, "CodeMap"), |
+ 0x08241: (163, "ScopeInfoMap"), |
+ 0x08261: (163, "FixedCOWArrayMap"), |
+ 0x08281: (145, "FixedDoubleArrayMap"), |
+ 0x082a1: (163, "HashTableMap"), |
+ 0x082c1: (0, "StringMap"), |
+ 0x082e1: (64, "SymbolMap"), |
+ 0x08301: (1, "ConsStringMap"), |
+ 0x08321: (5, "ConsAsciiStringMap"), |
+ 0x08341: (3, "SlicedStringMap"), |
+ 0x08361: (7, "SlicedAsciiStringMap"), |
+ 0x08381: (65, "ConsSymbolMap"), |
+ 0x083a1: (69, "ConsAsciiSymbolMap"), |
+ 0x083c1: (66, "ExternalSymbolMap"), |
+ 0x083e1: (74, "ExternalSymbolWithAsciiDataMap"), |
+ 0x08401: (70, "ExternalAsciiSymbolMap"), |
+ 0x08421: (2, "ExternalStringMap"), |
+ 0x08441: (10, "ExternalStringWithAsciiDataMap"), |
+ 0x08461: (6, "ExternalAsciiStringMap"), |
+ 0x08481: (82, "ShortExternalSymbolMap"), |
+ 0x084a1: (90, "ShortExternalSymbolWithAsciiDataMap"), |
+ 0x084c1: (86, "ShortExternalAsciiSymbolMap"), |
+ 0x084e1: (18, "ShortExternalStringMap"), |
+ 0x08501: (26, "ShortExternalStringWithAsciiDataMap"), |
+ 0x08521: (22, "ShortExternalAsciiStringMap"), |
+ 0x08541: (0, "UndetectableStringMap"), |
+ 0x08561: (4, "UndetectableAsciiStringMap"), |
+ 0x08581: (144, "ExternalPixelArrayMap"), |
+ 0x085a1: (136, "ExternalByteArrayMap"), |
+ 0x085c1: (137, "ExternalUnsignedByteArrayMap"), |
+ 0x085e1: (138, "ExternalShortArrayMap"), |
+ 0x08601: (139, "ExternalUnsignedShortArrayMap"), |
+ 0x08621: (140, "ExternalIntArrayMap"), |
+ 0x08641: (141, "ExternalUnsignedIntArrayMap"), |
+ 0x08661: (142, "ExternalFloatArrayMap"), |
+ 0x08681: (143, "ExternalDoubleArrayMap"), |
+ 0x086a1: (163, "NonStrictArgumentsElementsMap"), |
+ 0x086c1: (163, "FunctionContextMap"), |
+ 0x086e1: (163, "CatchContextMap"), |
+ 0x08701: (163, "WithContextMap"), |
+ 0x08721: (163, "BlockContextMap"), |
+ 0x08741: (163, "ModuleContextMap"), |
+ 0x08761: (165, "JSMessageObjectMap"), |
+ 0x08781: (133, "ForeignMap"), |
+ 0x087a1: (170, "NeanderMap"), |
+ 0x087c1: (158, "PolymorphicCodeCacheMap"), |
+ 0x087e1: (156, "ScriptMap"), |
+ 0x08801: (147, "AccessorInfoMap"), |
+ 0x08821: (148, "AccessorPairMap"), |
+ 0x08841: (149, "AccessCheckInfoMap"), |
+ 0x08861: (150, "InterceptorInfoMap"), |
+ 0x08881: (151, "CallHandlerInfoMap"), |
+ 0x088a1: (152, "FunctionTemplateInfoMap"), |
+ 0x088c1: (153, "ObjectTemplateInfoMap"), |
+ 0x088e1: (154, "SignatureInfoMap"), |
+ 0x08901: (155, "TypeSwitchInfoMap"), |
+ 0x08921: (157, "CodeCacheMap"), |
+ 0x08941: (159, "TypeFeedbackInfoMap"), |
+ 0x08961: (160, "AliasedArgumentsEntryMap"), |
+ 0x08981: (161, "DebugInfoMap"), |
+ 0x089a1: (162, "BreakPointInfoMap"), |
+} |
+ |
+ |
+# List of known V8 objects. Used to determine name for objects that are |
+# part of the root-set and hence on the first page of various old-space |
+# paged. Obtained by adding the code below to an IA32 release build with |
+# enabled snapshots to the end of the Isolate::Init method. |
+# |
+# #define ROOT_LIST_CASE(type, name, camel_name) \ |
+# if (o == heap_.name()) n = #camel_name; |
+# OldSpaces spit; |
+# printf("KNOWN_OBJECTS = {\n"); |
+# for (PagedSpace* s = spit.next(); s != NULL; s = spit.next()) { |
+# HeapObjectIterator it(s); |
+# const char* sname = AllocationSpaceName(s->identity()); |
+# for (Object* o = it.Next(); o != NULL; o = it.Next()) { |
+# const char* n = NULL; |
+# intptr_t p = reinterpret_cast<intptr_t>(o) & 0xfffff; |
+# ROOT_LIST(ROOT_LIST_CASE) |
+# if (n != NULL) { |
+# printf(" (\"%s\", 0x%05x): \"%s\",\n", sname, p, n); |
+# } |
+# } |
+# } |
+# printf("}\n"); |
+KNOWN_OBJECTS = { |
+ ("OLD_POINTER_SPACE", 0x08081): "NullValue", |
+ ("OLD_POINTER_SPACE", 0x08091): "UndefinedValue", |
+ ("OLD_POINTER_SPACE", 0x080a1): "InstanceofCacheMap", |
+ ("OLD_POINTER_SPACE", 0x080b1): "TrueValue", |
+ ("OLD_POINTER_SPACE", 0x080c1): "FalseValue", |
+ ("OLD_POINTER_SPACE", 0x080d1): "NoInterceptorResultSentinel", |
+ ("OLD_POINTER_SPACE", 0x080e1): "ArgumentsMarker", |
+ ("OLD_POINTER_SPACE", 0x080f1): "NumberStringCache", |
+ ("OLD_POINTER_SPACE", 0x088f9): "SingleCharacterStringCache", |
+ ("OLD_POINTER_SPACE", 0x08b01): "StringSplitCache", |
+ ("OLD_POINTER_SPACE", 0x08f09): "TerminationException", |
+ ("OLD_POINTER_SPACE", 0x08f19): "MessageListeners", |
+ ("OLD_POINTER_SPACE", 0x08f35): "CodeStubs", |
+ ("OLD_POINTER_SPACE", 0x09b61): "NonMonomorphicCache", |
+ ("OLD_POINTER_SPACE", 0x0a175): "PolymorphicCodeCache", |
+ ("OLD_POINTER_SPACE", 0x0a17d): "NativesSourceCache", |
+ ("OLD_POINTER_SPACE", 0x0a1bd): "EmptyScript", |
+ ("OLD_POINTER_SPACE", 0x0a1f9): "IntrinsicFunctionNames", |
+ ("OLD_POINTER_SPACE", 0x24a49): "SymbolTable", |
+ ("OLD_DATA_SPACE", 0x08081): "EmptyFixedArray", |
+ ("OLD_DATA_SPACE", 0x080a1): "NanValue", |
+ ("OLD_DATA_SPACE", 0x0811d): "EmptyByteArray", |
+ ("OLD_DATA_SPACE", 0x08125): "EmptyString", |
+ ("OLD_DATA_SPACE", 0x08131): "EmptyDescriptorArray", |
+ ("OLD_DATA_SPACE", 0x08259): "InfinityValue", |
+ ("OLD_DATA_SPACE", 0x08265): "MinusZeroValue", |
+ ("OLD_DATA_SPACE", 0x08271): "PrototypeAccessors", |
+ ("CODE_SPACE", 0x12b81): "JsEntryCode", |
+ ("CODE_SPACE", 0x12c61): "JsConstructEntryCode", |
} |
@@ -906,7 +1062,10 @@ class Oddball(HeapObject): |
p.Print(str(self)) |
def __str__(self): |
- return "<%s>" % self.to_string.GetChars() |
+ if self.to_string: |
+ return "Oddball(%08x, <%s>)" % (self.address, self.to_string.GetChars()) |
+ else: |
+ return "Oddball(%08x, kind=%s)" % (self.address, "???") |
class FixedArray(HeapObject): |
@@ -1032,6 +1191,27 @@ class Script(HeapObject): |
self.name = self.ObjectField(self.NameOffset()) |
+class CodeCache(HeapObject): |
+ def DefaultCacheOffset(self): |
+ return self.heap.PointerSize() |
+ |
+ def NormalTypeCacheOffset(self): |
+ return self.DefaultCacheOffset() + self.heap.PointerSize() |
+ |
+ def __init__(self, heap, map, address): |
+ HeapObject.__init__(self, heap, map, address) |
+ self.default_cache = self.ObjectField(self.DefaultCacheOffset()) |
+ self.normal_type_cache = self.ObjectField(self.NormalTypeCacheOffset()) |
+ |
+ def Print(self, p): |
+ p.Print("CodeCache(%s) {" % self.heap.reader.FormatIntPtr(self.address)) |
+ p.Indent() |
+ p.Print("default cache: %s" % self.default_cache) |
+ p.Print("normal type cache: %s" % self.normal_type_cache) |
+ p.Dedent() |
+ p.Print("}") |
+ |
+ |
class Code(HeapObject): |
CODE_ALIGNMENT_MASK = (1 << 5) - 1 |
@@ -1082,14 +1262,14 @@ class V8Heap(object): |
"EXTERNAL_STRING_TYPE": ExternalString, |
"EXTERNAL_STRING_WITH_ASCII_DATA_TYPE": ExternalString, |
"EXTERNAL_ASCII_STRING_TYPE": ExternalString, |
- |
"MAP_TYPE": Map, |
"ODDBALL_TYPE": Oddball, |
"FIXED_ARRAY_TYPE": FixedArray, |
"JS_FUNCTION_TYPE": JSFunction, |
"SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo, |
"SCRIPT_TYPE": Script, |
- "CODE_TYPE": Code |
+ "CODE_CACHE_TYPE": CodeCache, |
+ "CODE_TYPE": Code, |
} |
def __init__(self, reader, stack_map): |
@@ -1147,52 +1327,258 @@ class V8Heap(object): |
elif self.reader.arch == MD_CPU_ARCHITECTURE_X86: |
return (1 << 5) - 1 |
+ def PageAlignmentMask(self): |
+ return (1 << 20) - 1 |
-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'] |
-} |
+class KnownObject(HeapObject): |
+ def __init__(self, heap, known_name): |
+ HeapObject.__init__(self, heap, None, None) |
+ self.known_name = known_name |
+ |
+ def __str__(self): |
+ return "<%s>" % self.known_name |
+ |
+ |
+class KnownMap(HeapObject): |
+ def __init__(self, heap, known_name, instance_type): |
+ HeapObject.__init__(self, heap, None, None) |
+ self.instance_type = instance_type |
+ self.known_name = known_name |
+ |
+ def __str__(self): |
+ return "<%s>" % self.known_name |
+ |
+ |
+class InspectionPadawan(object): |
+ """The padawan can improve annotations by sensing well-known objects.""" |
+ def __init__(self, reader, heap): |
+ self.reader = reader |
+ self.heap = heap |
+ self.known_first_map_page = 0 |
+ self.known_first_data_page = 0 |
+ self.known_first_pointer_page = 0 |
+ |
+ def __getattr__(self, name): |
+ """An InspectionPadawan can be used instead of V8Heap, even though |
+ it does not inherit from V8Heap (aka. mixin).""" |
+ return getattr(self.heap, name) |
+ |
+ def GetPageOffset(self, tagged_address): |
+ return tagged_address & self.heap.PageAlignmentMask() |
+ |
+ def IsInKnownMapSpace(self, tagged_address): |
+ page_address = tagged_address & ~self.heap.PageAlignmentMask() |
+ return page_address == self.known_first_map_page |
+ |
+ def IsInKnownOldSpace(self, tagged_address): |
+ page_address = tagged_address & ~self.heap.PageAlignmentMask() |
+ return page_address in [self.known_first_data_page, |
+ self.known_first_pointer_page] |
+ |
+ def ContainingKnownOldSpaceName(self, tagged_address): |
+ page_address = tagged_address & ~self.heap.PageAlignmentMask() |
+ if page_address == self.known_first_data_page: return "OLD_DATA_SPACE" |
+ if page_address == self.known_first_pointer_page: return "OLD_POINTER_SPACE" |
+ return None |
+ |
+ def SenseObject(self, tagged_address): |
+ if self.IsInKnownOldSpace(tagged_address): |
+ offset = self.GetPageOffset(tagged_address) |
+ lookup_key = (self.ContainingKnownOldSpaceName(tagged_address), offset) |
+ known_obj_name = KNOWN_OBJECTS.get(lookup_key) |
+ if known_obj_name: |
+ return KnownObject(self, known_obj_name) |
+ if self.IsInKnownMapSpace(tagged_address): |
+ known_map = self.SenseMap(tagged_address) |
+ if known_map: |
+ return known_map |
+ found_obj = self.heap.FindObject(tagged_address) |
+ if found_obj: return found_ob |
+ address = tagged_address - 1 |
+ if self.reader.IsValidAddress(address): |
+ map_tagged_address = self.reader.ReadUIntPtr(address) |
+ map = self.SenseMap(map_tagged_address) |
+ if map is None: return None |
+ instance_type_name = INSTANCE_TYPES.get(map.instance_type) |
+ if instance_type_name is None: return None |
+ cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject) |
+ return cls(self, map, address) |
+ return None |
+ |
+ def SenseMap(self, tagged_address): |
+ if self.IsInKnownMapSpace(tagged_address): |
+ offset = self.GetPageOffset(tagged_address) |
+ known_map_info = KNOWN_MAPS.get(offset) |
+ if known_map_info: |
+ known_map_type, known_map_name = known_map_info |
+ return KnownMap(self, known_map_name, known_map_type) |
+ found_map = self.heap.FindMap(tagged_address) |
+ if found_map: return found_map |
+ return None |
+ |
+ def FindObjectOrSmi(self, tagged_address): |
+ """When used as a mixin in place of V8Heap.""" |
+ found_obj = self.SenseObject(tagged_address) |
+ if found_obj: return found_obj |
+ if (tagged_address & 1) == 0: |
+ return "Smi(%d)" % (tagged_address / 2) |
+ else: |
+ return "Unknown(%s)" % self.reader.FormatIntPtr(tagged_address) |
+ |
+ def FindObject(self, tagged_address): |
+ """When used as a mixin in place of V8Heap.""" |
+ raise NotImplementedError |
+ |
+ def FindMap(self, tagged_address): |
+ """When used as a mixin in place of V8Heap.""" |
+ raise NotImplementedError |
+ |
+ def PrintKnowledge(self): |
+ print " known_first_map_page = %s\n"\ |
+ " known_first_data_page = %s\n"\ |
+ " known_first_pointer_page = %s" % ( |
+ self.reader.FormatIntPtr(self.known_first_map_page), |
+ self.reader.FormatIntPtr(self.known_first_data_page), |
+ self.reader.FormatIntPtr(self.known_first_pointer_page)) |
+ |
class InspectionShell(cmd.Cmd): |
def __init__(self, reader, heap): |
cmd.Cmd.__init__(self) |
self.reader = reader |
self.heap = heap |
+ self.padawan = InspectionPadawan(reader, heap) |
self.prompt = "(grok) " |
def do_dd(self, address): |
- "Interpret memory at the given address (if available)"\ |
- " as a sequence of words." |
+ """ |
+ Interpret memory at the given address (if available) as a sequence |
+ of words. Automatic alignment is not performed. |
+ """ |
start = int(address, 16) |
+ if (start & self.heap.ObjectAlignmentMask()) != 0: |
+ print "Warning: Dumping un-aligned memory, is this what you had in mind?" |
for slot in xrange(start, |
start + self.reader.PointerSize() * 10, |
self.reader.PointerSize()): |
maybe_address = self.reader.ReadUIntPtr(slot) |
- heap_object = self.heap.FindObject(maybe_address) |
- print "%s: %s" % (self.reader.FormatIntPtr(slot), |
- self.reader.FormatIntPtr(maybe_address)) |
- if heap_object: |
- heap_object.Print(Printer()) |
+ heap_object = self.padawan.SenseObject(maybe_address) |
+ print "%s: %s %s" % (self.reader.FormatIntPtr(slot), |
+ self.reader.FormatIntPtr(maybe_address), |
+ heap_object or '') |
+ |
+ def do_do(self, address): |
+ """ |
+ Interpret memory at the given address as a V8 object. Automatic |
+ alignment makes sure that you can pass tagged as well as un-tagged |
+ addresses. |
+ """ |
+ address = int(address, 16) |
+ if (address & self.heap.ObjectAlignmentMask()) == 0: |
+ address = address + 1 |
+ elif (address & self.heap.ObjectAlignmentMask()) != 1: |
+ print "Address doesn't look like a valid pointer!" |
+ return |
+ heap_object = self.padawan.SenseObject(address) |
+ if heap_object: |
+ heap_object.Print(Printer()) |
+ else: |
+ print "Address cannot be interpreted as object!" |
+ |
+ def do_dp(self, address): |
+ """ |
+ Interpret memory at the given address as being on a V8 heap page |
+ and print information about the page header (if available). |
+ """ |
+ address = int(address, 16) |
+ page_address = address & ~self.heap.PageAlignmentMask() |
+ if self.reader.IsValidAddress(page_address): |
+ raise NotImplementedError |
+ else: |
+ print "Page header is not available!" |
+ |
+ def do_k(self, arguments): |
+ """ |
+ Teach V8 heap layout information to the inspector. This increases |
+ the amount of annotations the inspector can produce while dumping |
+ data. The first page of each heap space is of particular interest |
+ because it contains known objects that do not move. |
+ """ |
+ self.padawan.PrintKnowledge() |
+ |
+ def do_km(self, address): |
+ """ |
+ Teach V8 heap layout information to the inspector. Set the first |
+ map-space page by passing any pointer into that page. |
+ """ |
+ address = int(address, 16) |
+ page_address = address & ~self.heap.PageAlignmentMask() |
+ self.padawan.known_first_map_page = page_address |
+ |
+ def do_kd(self, address): |
+ """ |
+ Teach V8 heap layout information to the inspector. Set the first |
+ data-space page by passing any pointer into that page. |
+ """ |
+ address = int(address, 16) |
+ page_address = address & ~self.heap.PageAlignmentMask() |
+ self.padawan.known_first_data_page = page_address |
+ |
+ def do_kp(self, address): |
+ """ |
+ Teach V8 heap layout information to the inspector. Set the first |
+ pointer-space page by passing any pointer into that page. |
+ """ |
+ address = int(address, 16) |
+ page_address = address & ~self.heap.PageAlignmentMask() |
+ self.padawan.known_first_pointer_page = page_address |
def do_s(self, word): |
- "Search for a given word in available memory regions" |
- word = int(word, 0) |
- print "searching for word", word |
+ """ |
+ Search for a given word in available memory regions. The given word |
+ is expanded to full pointer size and searched at aligned as well as |
+ un-aligned memory locations. Use 'sa' to search aligned locations |
+ only. |
+ """ |
+ try: |
+ word = int(word, 0) |
+ except ValueError: |
+ print "Malformed word, prefix with '0x' to use hexadecimal format." |
+ return |
+ print "Searching for word %d/0x%s:" % (word, self.reader.FormatIntPtr(word)) |
self.reader.FindWord(word) |
+ def do_sh(self, none): |
+ """ |
+ Search for the V8 Heap object in all available memory regions. You |
+ might get lucky and find this rare treasure full of invaluable |
+ information. |
+ """ |
+ raise NotImplementedError |
+ |
def do_list(self, smth): |
- """List all available memory regions.""" |
+ """ |
+ List all available memory regions. |
+ """ |
def print_region(reader, start, size, location): |
- print "%s - %s" % (reader.FormatIntPtr(start), |
- reader.FormatIntPtr(start + size)) |
- |
+ print " %s - %s (%d bytes)" % (reader.FormatIntPtr(start), |
+ reader.FormatIntPtr(start + size), |
+ size) |
+ print "Available memory regions:" |
self.reader.ForEachMemoryRegion(print_region) |
+ |
+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) |
heap = None |
@@ -1240,7 +1626,7 @@ def AnalyzeMinidump(options, minidump_name): |
heap = V8Heap(reader, None) |
if options.full: |
- do_dump(reader, heap) |
+ FullDump(reader, heap) |
if options.shell: |
InspectionShell(reader, heap).cmdloop("type help to get help") |
@@ -1261,8 +1647,10 @@ def AnalyzeMinidump(options, minidump_name): |
if __name__ == "__main__": |
parser = optparse.OptionParser(USAGE) |
- parser.add_option("-s", "--shell", dest="shell", action="store_true") |
- parser.add_option("-f", "--full", dest="full", action="store_true") |
+ parser.add_option("-s", "--shell", dest="shell", action="store_true", |
+ help="start an interactive inspector shell") |
+ parser.add_option("-f", "--full", dest="full", action="store_true", |
+ help="dump all information contained in the minidump") |
options, args = parser.parse_args() |
if len(args) != 1: |
parser.print_help() |