Chromium Code Reviews| 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() |