[tools] Improve grokdump.py

- Add new address markers:
   T: tagged pointer in the minidump
   C: address into a module in the minidump
   S: pointer into the exception stack in the minidump
   *: other address in the minidump
- Show ASCII decoding of address in dd
- Display potential frame markers on the exception stack:
   00000032212fdae8: 0000000300000000   ........ Smi(3) EXIT frame marker
- Display relative addresses, useful to detect stack frames:
   00000032212fdb68: 00000032212fdb98 S ........  [+6]=00000032212fdcb0 S
   00000032212fdb70: 0000010ff5ca0a84   ........
   00000032212fdb78: 000001064c1fa881   ........
   00000032212fdb80: 0000016a8e52fcb1   ........
   00000032212fdb88: 0000010ff5ca0981   ........
   00000032212fdb90: 0000000d00000000   ........ Smi(13) INTERNAL frame marker
   00000032212fdb98: 00000032212fdcb0 S ........  [+35]=00000032212fdd61 S

Change-Id: I56bd7e6723a34bcb668719246dd5ff2898224928
Reviewed-on: https://chromium-review.googlesource.com/461862
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#44269}
This commit is contained in:
Camillo Bruni 2017-03-30 13:44:00 +02:00 committed by Commit Bot
parent 4024e6a1bb
commit 5ca9632e51
3 changed files with 244 additions and 94 deletions

View File

@ -2836,7 +2836,10 @@ void Shell::CleanupWorkers() {
static void DumpHeapConstants(i::Isolate* isolate) {
i::Heap* heap = isolate->heap();
printf(
"# Copyright 2017 the V8 project authors. All rights reserved.\n"
"# Use of this source code is governed by a BSD-style license that can\n"
"# be found in the LICENSE file.\n\n");
// Dump the INSTANCE_TYPES table to the console.
printf("# List of known V8 instance types.\n");
#define DUMP_TYPE(T) printf(" %d: \"%s\",\n", i::T, #T);
@ -2886,6 +2889,14 @@ static void DumpHeapConstants(i::Isolate* isolate) {
}
printf("}\n");
#undef ROOT_LIST_CASE
// Dump frame markers
printf("\n# List of known V8 Frame Markers.\n");
#define DUMP_MARKER(T, class) printf(" \"%s\",\n", #T);
printf("FRAME_MARKERS = (\n");
STACK_FRAME_TYPE_LIST(DUMP_MARKER)
printf(")\n");
#undef DUMP_TYPE
}

View File

@ -179,6 +179,7 @@ def FullDump(reader, heap):
INSTANCE_TYPES = v8heapconst.INSTANCE_TYPES
KNOWN_MAPS = v8heapconst.KNOWN_MAPS
KNOWN_OBJECTS = v8heapconst.KNOWN_OBJECTS
FRAME_MARKERS = v8heapconst.FRAME_MARKERS
# Set of structures and constants that describe the layout of minidump
# files. Based on MSDN and Google Breakpad.
@ -687,6 +688,27 @@ class MinidumpReader(object):
def IsValidAddress(self, address):
return self.FindLocation(address) is not None
def IsAlignedAddress(self, address):
return (address % self.PointerSize()) == 0
def IsExceptionStackAddress(self, address):
if not self.IsAlignedAddress(address): return False
return self.StackTop() <= address <= self.StackBottom()
def IsValidExceptionStackAddress(self, address):
if not self.IsValidAddress(address): return False
return self.isExceptionStackAddress(address)
def IsModuleAddress(self, address):
return self.GetModuleForAddress(address) != None
def GetModuleForAddress(self, address):
for module in self.module_list.modules:
start = module.base_of_image
end = start + module.size_of_image
if start <= address < end: return module
return None
def ReadU8(self, address):
location = self.FindLocation(address)
return ctypes.c_uint8.from_buffer(self.minidump, location).value
@ -717,6 +739,11 @@ class MinidumpReader(object):
return ctypes.c_uint64.from_buffer(self.minidump, location).value
return ctypes.c_uint32.from_buffer(self.minidump, location).value
def ReadAsciiPtr(self, address):
ascii_content = [c if c >= '\x20' and c < '\x7f' else '.'
for c in self.ReadBytes(address, self.PointerSize())]
return ''.join(ascii_content)
def IsProbableASCIIRegion(self, location, length):
ascii_bytes = 0
non_ascii_bytes = 0
@ -807,7 +834,7 @@ class MinidumpReader(object):
loc = location + i
if reader._ReadWord(loc) == word:
slot = start + (loc - location)
if slot % self.PointerSize() == 0:
if self.IsAlignedAddress(slot):
aligned_res.append(slot)
else:
unaligned_res.append(slot)
@ -890,6 +917,17 @@ class MinidumpReader(object):
elif self.arch == MD_CPU_ARCHITECTURE_X86:
return self.exception_context.ebp
def ExceptionThread(self):
return self.thread_map[self.exception.thread_id]
def StackTop(self):
return self.ExceptionSP()
def StackBottom(self):
exception_thread = self.ExceptionThread()
return exception_thread.stack.start + \
exception_thread.stack.memory.data_size
def FormatIntPtr(self, value):
if self.Is64():
return "%016x" % value
@ -1257,7 +1295,7 @@ class ConsString(String):
class Oddball(HeapObject):
# Should match declarations in objects.h
#Should match declarations in objects.h
KINDS = [
"False",
"True",
@ -1633,7 +1671,7 @@ class V8Heap(object):
def FindObject(self, tagged_address):
if tagged_address in self.objects:
return self.objects[tagged_address]
if (tagged_address & self.ObjectAlignmentMask()) != 1: return None
if not self.IsTaggedObjectAddress(tagged_address): return None
address = tagged_address - 1
if not self.reader.IsValidAddress(address): return None
map_tagged_address = self.reader.ReadUIntPtr(address)
@ -1670,6 +1708,9 @@ class V8Heap(object):
def ObjectAlignmentMask(self):
return self.PointerSize() - 1
def IsTaggedObjectAddress(self, address):
return (address & self.ObjectAlignmentMask()) == 1
def MapAlignmentMask(self):
if self.reader.arch == MD_CPU_ARCHITECTURE_AMD64:
return (1 << 4) - 1
@ -1683,15 +1724,41 @@ class V8Heap(object):
def PageAlignmentMask(self):
return (1 << 19) - 1
def IsTaggedAddress(self, address):
return (address & 1) == 1
def IsSmi(self, tagged_address):
if self.reader.Is64():
return (tagged_address & 0xFFFFFFFF) == 0
return (tagged_address & 1) == 0
return not self.IsTaggedAddress(tagged_address)
def SmiUntag(self, tagged_address):
if self.reader.Is64(): return tagged_address >> 32
return tagged_address >> 1
def AddressTypeMarker(self, address):
if not self.reader.IsValidAddress(address): return " "
if self.reader.IsExceptionStackAddress(address): return "S"
if self.reader.IsModuleAddress(address): return "C"
if self.IsTaggedAddress(address): return "T"
return "*"
def RelativeOffset(self, slot, address):
if not self.reader.IsValidAddress(slot): return None
if not self.reader.IsAlignedAddress(slot): return None
if not self.reader.IsValidAddress(address): return None
if not self.reader.IsAlignedAddress(address): return None
offset = (address - slot) / self.PointerSize()
lower_limit = -32
upper_limit = 128
if self.reader.IsExceptionStackAddress(address):
upper_limit = 0xFFFFFF
if offset < lower_limit or upper_limit < offset: return None
target_address = self.reader.ReadUIntPtr(address)
return "[%+02d]=%s %s" % (offset, self.reader.FormatIntPtr(target_address),
self.AddressTypeMarker(target_address))
class KnownObject(HeapObject):
def __init__(self, heap, known_name):
@ -1816,12 +1883,25 @@ class InspectionPadawan(object):
if page_address == self.known_first_old_page: return "OLD_SPACE"
return None
def FormatSmi(self, tagged_address):
return "Smi(%d)" % self.SmiUntag(tagged_address)
def FrameMarkerName(self, value):
if 0 < value <= len(FRAME_MARKERS):
return "Possible %s frame marker" % FRAME_MARKERS[value-1]
return ""
def SenseObject(self, tagged_address):
if self.heap.IsSmi(tagged_address):
return self.FormatSmi(tagged_address)
def FormatSmi(self, address, slot=None):
value = self.heap.SmiUntag(address)
marker = ""
if slot and self.reader.IsExceptionStackAddress(slot):
marker = self.FrameMarkerName(value)
# On 32-bit systems almost everything looks like a Smi.
if not self.reader.Is64() or value == 0: return marker
return "Smi(%d) %s" % (value, marker)
def SenseObject(self, address, slot=None):
if self.heap.IsSmi(address):
return self.FormatSmi(address, slot)
if not self.heap.IsTaggedAddress(address): return None
tagged_address = address
if self.IsInKnownOldSpace(tagged_address):
offset = self.GetPageOffset(tagged_address)
lookup_key = (self.ContainingKnownOldSpaceName(tagged_address), offset)
@ -1879,6 +1959,72 @@ class InspectionPadawan(object):
self.reader.FormatIntPtr(self.known_first_map_page),
self.reader.FormatIntPtr(self.known_first_old_page))
def TryInferFramePointer(self, slot, address):
""" Assume we have a framepointer if we find 4 consecutive links """
for i in range(0, 4):
if not self.reader.IsExceptionStackAddress(address): return 0
address = self.reader.ReadUIntPtr(address)
return slot
def InterpretMemory(self, start, end):
# On 64 bit we omit frame pointers, so we have to do some more guesswork.A
frame_pointer = 0
if not self.reader.Is64():
frame_pointer = self.reader.ExceptionFP()
in_oom_dump_area = False
for slot in xrange(start, end, self.reader.PointerSize()):
if not self.reader.IsValidAddress(slot):
print "%s: Address is not contained within the minidump!" % slot
return
maybe_address = self.reader.ReadUIntPtr(slot)
address_info = []
heap_object = self.SenseObject(maybe_address, slot)
if heap_object:
address_info.append(heap_object)
relative_offset = self.heap.RelativeOffset(slot, maybe_address)
if relative_offset:
address_info.append(relative_offset)
maybe_address_contents = None
if self.reader.IsExceptionStackAddress(maybe_address):
maybe_address_contents = \
self.reader.ReadUIntPtr(maybe_address) & 0xFFFFFFFF
if maybe_address_contents == 0xdecade00:
in_oom_dump_area = True
if frame_pointer == 0:
frame_pointer = self.TryInferFramePointer(slot, maybe_address)
maybe_symbol = self.reader.FindSymbol(maybe_address)
if in_oom_dump_area:
if maybe_address_contents == 0xdecade00:
address_info = ["<==== HeapStats start marker"]
elif maybe_address_contents == 0xdecade01:
address_info = ["<==== HeapStats end marker"]
elif maybe_address_contents is not None:
address_info = [" %d (%d Mbytes)" % (maybe_address_contents,
maybe_address_contents >> 20)]
if slot == frame_pointer:
if not self.reader.IsExceptionStackAddress(maybe_address):
address_info.append("<==== BAD frame pointer")
frame_pointer = 0
else:
address_info.append("<==== Frame pointer")
frame_pointer = maybe_address
address_type_marker = self.heap.AddressTypeMarker(maybe_address)
string_value = self.reader.ReadAsciiPtr(slot)
print "%s: %s %s %s %s" % (self.reader.FormatIntPtr(slot),
self.reader.FormatIntPtr(maybe_address),
address_type_marker,
string_value,
' | '.join(address_info))
if maybe_address_contents == 0xdecade01:
in_oom_dump_area = False
heap_object = self.heap.FindObject(maybe_address)
if heap_object:
heap_object.Print(Printer())
print ""
WEB_HEADER = """
<!DOCTYPE html>
<html>
@ -2962,20 +3108,31 @@ class InspectionShell(cmd.Cmd):
sign = -1
maybe_addr = expr[0]
if maybe_addr[0] == "$":
try:
# Referring to a register
address = self.reader.Register(maybe_addr[1:])
except:
print("**** Register %s does not exist." % maybe_addr)
return 0
else:
try:
address = int(maybe_addr, 16)
except:
print("**** Invalid address: %s" % maybe_addr)
return 0
if len(expr) > 1:
try:
address += sign * int(expr[1], 16)
except:
print("*** Invalid offset: %s" % expr[1])
return 0
return address
def do_dd(self, args):
"""
Interpret memory in the given region [address, address + num * word_size)
(if available) as a sequence of words. Automatic alignment is not performed.
If the num is not specified, a default value of 16 words is used.
If the num is not specified, a default value of 16 words is usif not self.Is
If no address is given, dd continues printing at the next word.
Synopsis: dd 0x<address>|$register [0x<num>]
"""
@ -2985,22 +3142,10 @@ class InspectionShell(cmd.Cmd):
self.dd_num = int(args[1], 16) if len(args) > 1 else 0x10
else:
self.dd_start += self.dd_num * self.reader.PointerSize()
if (self.dd_start & self.heap.ObjectAlignmentMask()) != 0:
if not self.reader.IsAlignedAddress(self.dd_start):
print "Warning: Dumping un-aligned memory, is this what you had in mind?"
for i in xrange(0,
self.reader.PointerSize() * self.dd_num,
self.reader.PointerSize()):
slot = self.dd_start + i
if not self.reader.IsValidAddress(slot):
print "Address is not contained within the minidump!"
return
maybe_address = self.reader.ReadUIntPtr(slot)
heap_object = self.padawan.SenseObject(maybe_address)
valid_address = "*" if self.reader.IsValidAddress(maybe_address) else " "
print "%s: %s %s %s" % (self.reader.FormatIntPtr(slot),
self.reader.FormatIntPtr(maybe_address),
valid_address,
heap_object or '')
end = self.dd_start + self.reader.PointerSize() * self.dd_num
self.padawan.InterpretMemory(self.dd_start, end)
def do_do(self, address):
"""
@ -3009,9 +3154,9 @@ class InspectionShell(cmd.Cmd):
addresses.
"""
address = self.ParseAddressExpr(address)
if (address & self.heap.ObjectAlignmentMask()) == 0:
if self.reader.IsAlignedAddress(address):
address = address + 1
elif (address & self.heap.ObjectAlignmentMask()) != 1:
elif not self.IsTaggedObjectAddress(address):
print "Address doesn't look like a valid pointer!"
return
heap_object = self.padawan.SenseObject(address)
@ -3201,20 +3346,37 @@ def PrintModuleDetails(reader, module):
def AnalyzeMinidump(options, minidump_name):
reader = MinidumpReader(options, minidump_name)
heap = None
stack_top = reader.ExceptionSP()
stack_bottom = reader.StackBottom()
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)
DebugPrint("========================================")
if reader.exception is None:
print "Minidump has no exception info"
else:
print "Address markers:"
print " T = valid tagged pointer in the minidump"
print " S = address on the exception stack"
print " C = address in loaded C/C++ module"
print " * = address in the minidump"
print "Exception info:"
exception_thread = reader.thread_map[reader.exception.thread_id]
exception_thread = reader.ExceptionThread()
print " thread id: %d" % exception_thread.id
print " code: %08X" % reader.exception.exception.code
print " context:"
context = CONTEXT_FOR_ARCH[reader.arch]
maxWidth = max(map(lambda s: len(s), context))
for r in context:
print " %s: %s" % (r.rjust(maxWidth),
reader.FormatIntPtr(reader.Register(r)))
register_value = reader.Register(r)
print " %s: %s %s" % (r.rjust(maxWidth),
reader.FormatIntPtr(register_value ),
heap.AddressTypeMarker(register_value))
# TODO(vitalyr): decode eflags.
if reader.arch in [MD_CPU_ARCHITECTURE_ARM, MD_CPU_ARCHITECTURE_ARM64]:
print " cpsr: %s" % bin(reader.exception_context.cpsr)[2:]
@ -3230,16 +3392,6 @@ def AnalyzeMinidump(options, minidump_name):
reader.TryLoadSymbolsFor(name, module)
print
stack_top = reader.ExceptionSP()
stack_bottom = exception_thread.stack.start + \
exception_thread.stack.memory.data_size
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:"
eip_symbol = reader.FindSymbol(reader.ExceptionIP())
if eip_symbol is not None:
@ -3278,43 +3430,9 @@ def AnalyzeMinidump(options, minidump_name):
print "Kthxbye."
elif not options.command:
if reader.exception is not None:
frame_pointer = reader.ExceptionFP()
in_oom_dump_area = False
print "Annotated stack (from exception.esp to bottom):"
for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
ascii_content = [c if c >= '\x20' and c < '\x7f' else '.'
for c in reader.ReadBytes(slot, reader.PointerSize())]
maybe_address = reader.ReadUIntPtr(slot)
maybe_address_contents = None
if maybe_address >= stack_top and maybe_address <= stack_bottom:
maybe_address_contents = reader.ReadUIntPtr(maybe_address)
if maybe_address_contents == 0xdecade00:
in_oom_dump_area = True
heap_object = heap.FindObject(maybe_address)
maybe_symbol = reader.FindSymbol(maybe_address)
oom_comment = ""
if in_oom_dump_area:
if maybe_address_contents == 0xdecade00:
oom_comment = " <----- HeapStats start marker"
elif maybe_address_contents == 0xdecade01:
oom_comment = " <----- HeapStats end marker"
elif maybe_address_contents is not None:
oom_comment = " %d (%d Mbytes)" % (maybe_address_contents,
maybe_address_contents >> 20)
if slot == frame_pointer:
maybe_symbol = "<---- frame pointer"
frame_pointer = maybe_address
print "%s: %s %s %s%s" % (reader.FormatIntPtr(slot),
reader.FormatIntPtr(maybe_address),
"".join(ascii_content),
maybe_symbol or "",
oom_comment)
if maybe_address_contents == 0xdecade01:
in_oom_dump_area = False
if heap_object:
heap_object.Print(Printer())
print
padawan = InspectionPadawan(reader, heap)
padawan.InterpretMemory(stack_top, stack_bottom)
reader.Dispose()

View File

@ -1,6 +1,6 @@
# Copyright 2017 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Use of this source code is governed by a BSD-style license that can
# be found in the LICENSE file.
# List of known V8 instance types.
INSTANCE_TYPES = {
@ -309,13 +309,34 @@ KNOWN_OBJECTS = {
("OLD_SPACE", 0x07969): "ExperimentalExtraNativesSourceCache",
("OLD_SPACE", 0x07981): "EmptyPropertiesDictionary",
("OLD_SPACE", 0x079d1): "ScriptList",
("OLD_SPACE", 0x23709): "CodeStubs",
("OLD_SPACE", 0x30781): "WeakObjectToCodeTable",
("OLD_SPACE", 0x309a9): "WeakNewSpaceObjectToCodeList",
("OLD_SPACE", 0x30a39): "NoScriptSharedFunctionInfos",
("OLD_SPACE", 0x4cb11): "MessageListeners",
("OLD_SPACE", 0x4cb31): "NoOpInterceptorInfo",
("OLD_SPACE", 0x550e1): "StringTable",
("CODE_SPACE", 0x2c4e1): "JsEntryCode",
("CODE_SPACE", 0x30941): "JsConstructEntryCode",
("OLD_SPACE", 0x22019): "CodeStubs",
("OLD_SPACE", 0x2f199): "WeakObjectToCodeTable",
("OLD_SPACE", 0x2f3c1): "WeakNewSpaceObjectToCodeList",
("OLD_SPACE", 0x2f451): "NoScriptSharedFunctionInfos",
("OLD_SPACE", 0x4abd9): "MessageListeners",
("OLD_SPACE", 0x4abf9): "NoOpInterceptorInfo",
("OLD_SPACE", 0x531d1): "StringTable",
("CODE_SPACE", 0x2cde1): "JsEntryCode",
("CODE_SPACE", 0x31241): "JsConstructEntryCode",
}
# List of known V8 Frame Markers.
FRAME_MARKERS = (
"ENTRY",
"ENTRY_CONSTRUCT",
"EXIT",
"JAVA_SCRIPT",
"OPTIMIZED",
"WASM_COMPILED",
"WASM_TO_JS",
"JS_TO_WASM",
"WASM_INTERPRETER_ENTRY",
"INTERPRETED",
"STUB",
"STUB_FAILURE_TRAMPOLINE",
"INTERNAL",
"CONSTRUCT",
"ARGUMENTS_ADAPTOR",
"BUILTIN",
"BUILTIN_EXIT",
)