Commit e24b0e1e authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

[tools] Directly show message from PushStackTraceAndDie in grokdump.py

This CL adds support to parse a PushStackTraceAndDie dump on a windows
minidump:
  Stack Message:
    magic1:        00000000bbbbbbbb
    magic2:        00000000bbbbbbbb
    ptr1:          00000015f9ca78d1 T
    ptr2:          0000000000000000
    message start: 00000000002c58f0 S
    stack_start:   00000000002cd8f0 S

All addresses within the message are annotated with the address marker to
make it easier to spot objects that are contained in the minidump.
Currently this doesn't work on OSX yet as we do not correctly push the two
magic markers on the stack.

Change-Id: I8385bb66a76bd253c4014bc7e25971d03830dd4d
Reviewed-on: https://chromium-review.googlesource.com/466007Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#44339}
parent 28ae2168
...@@ -108,12 +108,10 @@ class Descriptor(object): ...@@ -108,12 +108,10 @@ class Descriptor(object):
def _GetCtype(fields): def _GetCtype(fields):
class Raw(ctypes.Structure): class Raw(ctypes.Structure):
_fields_ = fields _fields_ = fields
_maxWidth = max(map(lambda s: len(s), fields))
_pack_ = 1 _pack_ = 1
def __str__(self): def __str__(self):
return "{" + ", ".join("%s: %s" % (str(field).ljust(_maxWidth), return "{" + ", ".join("%s: %s" % (field, self.__getattribute__(field))
self.__getattribute__(field))
for field, _ in Raw._fields_) + "}" for field, _ in Raw._fields_) + "}"
return Raw return Raw
...@@ -181,6 +179,12 @@ KNOWN_MAPS = v8heapconst.KNOWN_MAPS ...@@ -181,6 +179,12 @@ KNOWN_MAPS = v8heapconst.KNOWN_MAPS
KNOWN_OBJECTS = v8heapconst.KNOWN_OBJECTS KNOWN_OBJECTS = v8heapconst.KNOWN_OBJECTS
FRAME_MARKERS = v8heapconst.FRAME_MARKERS FRAME_MARKERS = v8heapconst.FRAME_MARKERS
# Markers pushed on the stack by PushStackTraceAndDie
MAGIC_MARKER_PAIRS = (
(0xbbbbbbbb, 0xbbbbbbbb),
(0xfefefefe, 0xfefefeff),
)
# Set of structures and constants that describe the layout of minidump # Set of structures and constants that describe the layout of minidump
# files. Based on MSDN and Google Breakpad. # files. Based on MSDN and Google Breakpad.
...@@ -744,6 +748,17 @@ class MinidumpReader(object): ...@@ -744,6 +748,17 @@ class MinidumpReader(object):
for c in self.ReadBytes(address, self.PointerSize())] for c in self.ReadBytes(address, self.PointerSize())]
return ''.join(ascii_content) return ''.join(ascii_content)
def ReadAsciiString(self, address):
string = ""
while self.IsValidAddress(address):
code = self.ReadU8(address)
if 0 < code < 128:
string += chr(code)
else:
break
address += 1
return string
def IsProbableASCIIRegion(self, location, length): def IsProbableASCIIRegion(self, location, length):
ascii_bytes = 0 ascii_bytes = 0
non_ascii_bytes = 0 non_ascii_bytes = 0
...@@ -1725,7 +1740,7 @@ class V8Heap(object): ...@@ -1725,7 +1740,7 @@ class V8Heap(object):
return (1 << 19) - 1 return (1 << 19) - 1
def IsTaggedAddress(self, address): def IsTaggedAddress(self, address):
return (address & 1) == 1 return (address & self.ObjectAlignmentMask()) == 1
def IsSmi(self, tagged_address): def IsSmi(self, tagged_address):
if self.reader.Is64(): if self.reader.Is64():
...@@ -1743,6 +1758,13 @@ class V8Heap(object): ...@@ -1743,6 +1758,13 @@ class V8Heap(object):
if self.IsTaggedAddress(address): return "T" if self.IsTaggedAddress(address): return "T"
return "*" return "*"
def FormatIntPtr(self, address):
marker = self.AddressTypeMarker(address)
address = self.reader.FormatIntPtr(address)
if marker == " ": return address
return "%s %s" % (address, marker)
def RelativeOffset(self, slot, address): def RelativeOffset(self, slot, address):
if not self.reader.IsValidAddress(slot): return None if not self.reader.IsValidAddress(slot): return None
if not self.reader.IsAlignedAddress(slot): return None if not self.reader.IsAlignedAddress(slot): return None
...@@ -1959,6 +1981,59 @@ class InspectionPadawan(object): ...@@ -1959,6 +1981,59 @@ class InspectionPadawan(object):
self.reader.FormatIntPtr(self.known_first_map_page), self.reader.FormatIntPtr(self.known_first_map_page),
self.reader.FormatIntPtr(self.known_first_old_page)) self.reader.FormatIntPtr(self.known_first_old_page))
def PrintStackTraceMessage(self, start=None, print_message=True):
"""
Try to print a possible message from PushStackTraceAndDie.
Returns the first address where the normal stack starts again.
"""
# Only look at the first 32 words on the stack
ptr_size = self.reader.PointerSize()
if start is None:
start = self.reader.ExceptionSP()
end = start + ptr_size * 32
message_start = 0
for slot in xrange(start, end, ptr_size):
magic1 = self.reader.ReadUIntPtr(slot)
magic2 = self.reader.ReadUIntPtr(slot + ptr_size)
pair = (magic1 & 0xFFFFFFFF, magic2 & 0xFFFFFFFF)
if pair in MAGIC_MARKER_PAIRS:
message_slot = slot + ptr_size * 4
message_start = self.reader.ReadUIntPtr(message_slot)
break
if message_start == 0: return start
ptr1 = self.reader.ReadUIntPtr(slot + ptr_size * 2)
ptr2 = self.reader.ReadUIntPtr(slot + ptr_size * 3)
message = self.reader.ReadAsciiString(message_start)
stack_start = message_start + len(message) + 1
# Make sure the address is word aligned
stack_start = stack_start - (stack_start % ptr_size)
print "Stack Message:"
print " magic1: %s" % self.heap.FormatIntPtr(magic1)
print " magic2: %s" % self.heap.FormatIntPtr(magic2)
print " ptr1: %s" % self.heap.FormatIntPtr(ptr1)
print " ptr2: %s" % self.heap.FormatIntPtr(ptr2)
print " message start: %s" % self.heap.FormatIntPtr(message_start)
print " stack_start: %s" % self.heap.FormatIntPtr(stack_start )
print ""
if not print_message:
print " Use `das` to print the message with annotated addresses."
print ""
return stack_start
# Annotate all addresses in the dumped message
prog = re.compile("[0-9a-fA-F]{%s}" % ptr_size*2)
addresses = list(set(prog.findall(message)))
for i in range(len(addresses)):
address_org = addresses[i]
address = self.heap.FormatIntPtr(int(address_org, 16))
if address_org != address:
message = message.replace(address_org, address)
print "Message:"
print "="*80
print message
print "="*80
print ""
return stack_start
def TryInferFramePointer(self, slot, address): def TryInferFramePointer(self, slot, address):
""" Assume we have a framepointer if we find 4 consecutive links """ """ Assume we have a framepointer if we find 4 consecutive links """
for i in range(0, 4): for i in range(0, 4):
...@@ -1982,7 +2057,7 @@ class InspectionPadawan(object): ...@@ -1982,7 +2057,7 @@ class InspectionPadawan(object):
address_info = [] address_info = []
heap_object = self.SenseObject(maybe_address, slot) heap_object = self.SenseObject(maybe_address, slot)
if heap_object: if heap_object:
address_info.append(heap_object) address_info.append(str(heap_object))
relative_offset = self.heap.RelativeOffset(slot, maybe_address) relative_offset = self.heap.RelativeOffset(slot, maybe_address)
if relative_offset: if relative_offset:
address_info.append(relative_offset) address_info.append(relative_offset)
...@@ -3077,24 +3152,6 @@ class InspectionShell(cmd.Cmd): ...@@ -3077,24 +3152,6 @@ class InspectionShell(cmd.Cmd):
self.padawan = InspectionPadawan(reader, heap) self.padawan = InspectionPadawan(reader, heap)
self.prompt = "(grok) " self.prompt = "(grok) "
def do_da(self, address):
"""
Print ASCII string starting at specified address.
"""
address = int(address, 16)
string = ""
while self.reader.IsValidAddress(address):
code = self.reader.ReadU8(address)
if code < 128:
string += chr(code)
else:
break
address += 1
if string == "":
print "Not an ASCII string at %s" % self.reader.FormatIntPtr(address)
else:
print "%s\n" % string
def ParseAddressExpr(self, expr): def ParseAddressExpr(self, expr):
""" """
Support parsing simple expressions of the form: Support parsing simple expressions of the form:
...@@ -3128,6 +3185,30 @@ class InspectionShell(cmd.Cmd): ...@@ -3128,6 +3185,30 @@ class InspectionShell(cmd.Cmd):
return 0 return 0
return address return address
def do_da(self, address):
"""
Print ASCII string starting at specified address.
"""
address = self.ParseAddressExpr(address)
string = self.reader.ReadAsciiString(address)
if string == "":
print "Not an ASCII string at %s" % self.reader.FormatIntPtr(address)
else:
print "%s\n" % string
def do_das(self, address):
"""
Print ASCII stack error message.
"""
if self.reader.exception is None:
print "Minidump has no exception info"
return
if len(address) == 0:
address = None
else:
address = self.ParseAddressExpr(address)
self.padawan.PrintStackTraceMessage(address)
def do_dd(self, args): def do_dd(self, args):
""" """
Interpret memory in the given region [address, address + num * word_size) Interpret memory in the given region [address, address + num * word_size)
...@@ -3354,7 +3435,9 @@ def AnalyzeMinidump(options, minidump_name): ...@@ -3354,7 +3435,9 @@ def AnalyzeMinidump(options, minidump_name):
maybe_address = reader.ReadUIntPtr(slot) maybe_address = reader.ReadUIntPtr(slot)
if not maybe_address in stack_map: if not maybe_address in stack_map:
stack_map[maybe_address] = slot stack_map[maybe_address] = slot
heap = V8Heap(reader, stack_map) heap = V8Heap(reader, stack_map)
padawan = InspectionPadawan(reader, heap)
DebugPrint("========================================") DebugPrint("========================================")
if reader.exception is None: if reader.exception is None:
...@@ -3365,6 +3448,7 @@ def AnalyzeMinidump(options, minidump_name): ...@@ -3365,6 +3448,7 @@ def AnalyzeMinidump(options, minidump_name):
print " S = address on the exception stack" print " S = address on the exception stack"
print " C = address in loaded C/C++ module" print " C = address in loaded C/C++ module"
print " * = address in the minidump" print " * = address in the minidump"
print ""
print "Exception info:" print "Exception info:"
exception_thread = reader.ExceptionThread() exception_thread = reader.ExceptionThread()
print " thread id: %d" % exception_thread.id print " thread id: %d" % exception_thread.id
...@@ -3374,9 +3458,8 @@ def AnalyzeMinidump(options, minidump_name): ...@@ -3374,9 +3458,8 @@ def AnalyzeMinidump(options, minidump_name):
maxWidth = max(map(lambda s: len(s), context)) maxWidth = max(map(lambda s: len(s), context))
for r in context: for r in context:
register_value = reader.Register(r) register_value = reader.Register(r)
print " %s: %s %s" % (r.rjust(maxWidth), print " %s: %s" % (r.rjust(maxWidth),
reader.FormatIntPtr(register_value ), heap.FormatIntPtr(register_value))
heap.AddressTypeMarker(register_value))
# TODO(vitalyr): decode eflags. # TODO(vitalyr): decode eflags.
if reader.arch in [MD_CPU_ARCHITECTURE_ARM, MD_CPU_ARCHITECTURE_ARM64]: if reader.arch in [MD_CPU_ARCHITECTURE_ARM, MD_CPU_ARCHITECTURE_ARM64]:
print " cpsr: %s" % bin(reader.exception_context.cpsr)[2:] print " cpsr: %s" % bin(reader.exception_context.cpsr)[2:]
...@@ -3392,6 +3475,13 @@ def AnalyzeMinidump(options, minidump_name): ...@@ -3392,6 +3475,13 @@ def AnalyzeMinidump(options, minidump_name):
reader.TryLoadSymbolsFor(name, module) reader.TryLoadSymbolsFor(name, module)
print print
print " stack-top: %s" % heap.FormatIntPtr(reader.StackTop())
print " stack-bottom: %s" % heap.FormatIntPtr(reader.StackBottom())
print ""
if options.shell:
padawan.PrintStackTraceMessage(print_message=False)
print "Disassembly around exception.eip:" print "Disassembly around exception.eip:"
eip_symbol = reader.FindSymbol(reader.ExceptionIP()) eip_symbol = reader.FindSymbol(reader.ExceptionIP())
if eip_symbol is not None: if eip_symbol is not None:
...@@ -3431,8 +3521,8 @@ def AnalyzeMinidump(options, minidump_name): ...@@ -3431,8 +3521,8 @@ def AnalyzeMinidump(options, minidump_name):
elif not options.command: elif not options.command:
if reader.exception is not None: if reader.exception is not None:
print "Annotated stack (from exception.esp to bottom):" print "Annotated stack (from exception.esp to bottom):"
padawan = InspectionPadawan(reader, heap) stack_start = padawan.PrintStackTraceMessage()
padawan.InterpretMemory(stack_top, stack_bottom) padawan.InterpretMemory(stack_start, stack_bottom)
reader.Dispose() reader.Dispose()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment