Extend grokdump.py with simple BreakPad symbol files support.
R=mstarzinger@chromium.org BUG= Review URL: https://codereview.chromium.org/10923003 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12620 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
aba09dcf2e
commit
d6853c3697
@ -27,17 +27,18 @@
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import bisect
|
||||
import cmd
|
||||
import codecs
|
||||
import ctypes
|
||||
import disasm
|
||||
import mmap
|
||||
import optparse
|
||||
import os
|
||||
import disasm
|
||||
import sys
|
||||
import types
|
||||
import codecs
|
||||
import re
|
||||
import struct
|
||||
import sys
|
||||
import types
|
||||
|
||||
|
||||
USAGE="""usage: %prog [OPTIONS] [DUMP-FILE]
|
||||
@ -180,6 +181,11 @@ MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([
|
||||
("rva", ctypes.c_uint32)
|
||||
])
|
||||
|
||||
MINIDUMP_STRING = Descriptor([
|
||||
("length", ctypes.c_uint32),
|
||||
("buffer", lambda t: ctypes.c_uint8 * (t.length + 2))
|
||||
])
|
||||
|
||||
MINIDUMP_DIRECTORY = Descriptor([
|
||||
("stream_type", ctypes.c_uint32),
|
||||
("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
|
||||
@ -400,6 +406,24 @@ MINIDUMP_THREAD_LIST = Descriptor([
|
||||
("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
|
||||
])
|
||||
|
||||
MINIDUMP_RAW_MODULE = Descriptor([
|
||||
("base_of_image", ctypes.c_uint64),
|
||||
("size_of_image", ctypes.c_uint32),
|
||||
("checksum", ctypes.c_uint32),
|
||||
("time_date_stamp", ctypes.c_uint32),
|
||||
("module_name_rva", ctypes.c_uint32),
|
||||
("version_info", ctypes.c_uint32 * 13),
|
||||
("cv_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
|
||||
("misc_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
|
||||
("reserved0", ctypes.c_uint32 * 2),
|
||||
("reserved1", ctypes.c_uint32 * 2)
|
||||
])
|
||||
|
||||
MINIDUMP_MODULE_LIST = Descriptor([
|
||||
("number_of_modules", ctypes.c_uint32),
|
||||
("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules)
|
||||
])
|
||||
|
||||
MINIDUMP_RAW_SYSTEM_INFO = Descriptor([
|
||||
("processor_architecture", ctypes.c_uint16)
|
||||
])
|
||||
@ -407,6 +431,20 @@ MINIDUMP_RAW_SYSTEM_INFO = Descriptor([
|
||||
MD_CPU_ARCHITECTURE_X86 = 0
|
||||
MD_CPU_ARCHITECTURE_AMD64 = 9
|
||||
|
||||
class FuncSymbol:
|
||||
def __init__(self, start, size, name):
|
||||
self.start = start
|
||||
self.end = self.start + size
|
||||
self.name = name
|
||||
|
||||
def __cmp__(self, other):
|
||||
if isinstance(other, FuncSymbol):
|
||||
return self.start - other.start
|
||||
return self.start - other
|
||||
|
||||
def Covers(self, addr):
|
||||
return (self.start <= addr) and (addr < self.end)
|
||||
|
||||
class MinidumpReader(object):
|
||||
"""Minidump (.dmp) reader."""
|
||||
|
||||
@ -430,8 +468,13 @@ class MinidumpReader(object):
|
||||
self.exception_context = None
|
||||
self.memory_list = None
|
||||
self.memory_list64 = None
|
||||
self.module_list = None
|
||||
self.thread_map = {}
|
||||
|
||||
self.symdir = options.symdir
|
||||
self.modules_with_symbols = []
|
||||
self.symbols = []
|
||||
|
||||
# Find MDRawSystemInfo stream and determine arch.
|
||||
for d in directories:
|
||||
if d.stream_type == MD_SYSTEM_INFO_STREAM:
|
||||
@ -461,6 +504,11 @@ class MinidumpReader(object):
|
||||
for thread in thread_list.threads:
|
||||
DebugPrint(thread)
|
||||
self.thread_map[thread.id] = thread
|
||||
elif d.stream_type == MD_MODULE_LIST_STREAM:
|
||||
assert self.module_list is None
|
||||
self.module_list = MINIDUMP_MODULE_LIST.Read(
|
||||
self.minidump, d.location.rva)
|
||||
assert ctypes.sizeof(self.module_list) == d.location.data_size
|
||||
elif d.stream_type == MD_MEMORY_LIST_STREAM:
|
||||
print >>sys.stderr, "Warning: This is not a full minidump!"
|
||||
assert self.memory_list is None
|
||||
@ -644,6 +692,66 @@ class MinidumpReader(object):
|
||||
def Register(self, name):
|
||||
return self.exception_context.__getattribute__(name)
|
||||
|
||||
def ReadMinidumpString(self, rva):
|
||||
string = bytearray(MINIDUMP_STRING.Read(self.minidump, rva).buffer)
|
||||
string = string.decode("utf16")
|
||||
return string[0:len(string) - 1]
|
||||
|
||||
# Load FUNC records from a BreakPad symbol file
|
||||
#
|
||||
# http://code.google.com/p/google-breakpad/wiki/SymbolFiles
|
||||
#
|
||||
def _LoadSymbolsFrom(self, symfile, baseaddr):
|
||||
print "Loading symbols from %s" % (symfile)
|
||||
funcs = []
|
||||
with open(symfile) as f:
|
||||
for line in f:
|
||||
result = re.match(
|
||||
r"^FUNC ([a-f0-9]+) ([a-f0-9]+) ([a-f0-9]+) (.*)$", line)
|
||||
if result is not None:
|
||||
start = int(result.group(1), 16)
|
||||
size = int(result.group(2), 16)
|
||||
name = result.group(4).rstrip()
|
||||
bisect.insort_left(self.symbols,
|
||||
FuncSymbol(baseaddr + start, size, name))
|
||||
print " ... done"
|
||||
|
||||
def TryLoadSymbolsFor(self, modulename, module):
|
||||
try:
|
||||
symfile = os.path.join(self.symdir,
|
||||
modulename.replace('.', '_') + ".pdb.sym")
|
||||
self._LoadSymbolsFrom(symfile, module.base_of_image)
|
||||
self.modules_with_symbols.append(module)
|
||||
except Exception as e:
|
||||
print " ... failure (%s)" % (e)
|
||||
|
||||
# Returns true if address is covered by some module that has loaded symbols.
|
||||
def _IsInModuleWithSymbols(self, addr):
|
||||
for module in self.modules_with_symbols:
|
||||
start = module.base_of_image
|
||||
end = start + module.size_of_image
|
||||
if (start <= addr) and (addr < end):
|
||||
return True
|
||||
return False
|
||||
|
||||
# Find symbol covering the given address and return its name in format
|
||||
# <symbol name>+<offset from the start>
|
||||
def FindSymbol(self, addr):
|
||||
if not self._IsInModuleWithSymbols(addr):
|
||||
return None
|
||||
|
||||
i = bisect.bisect_left(self.symbols, addr)
|
||||
symbol = None
|
||||
if (0 < i) and self.symbols[i - 1].Covers(addr):
|
||||
symbol = self.symbols[i - 1]
|
||||
elif (i < len(self.symbols)) and self.symbols[i].Covers(addr):
|
||||
symbol = self.symbols[i]
|
||||
else:
|
||||
return None
|
||||
diff = addr - symbol.start
|
||||
return "%s+0x%x" % (symbol.name, diff)
|
||||
|
||||
|
||||
|
||||
# List of V8 instance types. Obtained by adding the code below to any .cc file.
|
||||
#
|
||||
@ -1639,6 +1747,11 @@ CONTEXT_FOR_ARCH = {
|
||||
['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip']
|
||||
}
|
||||
|
||||
KNOWN_MODULES = {'chrome.exe', 'chrome.dll'}
|
||||
|
||||
def GetModuleName(reader, module):
|
||||
name = reader.ReadMinidumpString(module.module_name_rva)
|
||||
return str(os.path.basename(str(name).replace("\\", "/")))
|
||||
|
||||
def AnalyzeMinidump(options, minidump_name):
|
||||
reader = MinidumpReader(options, minidump_name)
|
||||
@ -1657,6 +1770,13 @@ def AnalyzeMinidump(options, minidump_name):
|
||||
# TODO(vitalyr): decode eflags.
|
||||
print " eflags: %s" % bin(reader.exception_context.eflags)[2:]
|
||||
print
|
||||
print " modules:"
|
||||
for module in reader.module_list.modules:
|
||||
name = GetModuleName(reader, module)
|
||||
if name in KNOWN_MODULES:
|
||||
print " %s at %08X" % (name, module.base_of_image)
|
||||
reader.TryLoadSymbolsFor(name, module)
|
||||
print
|
||||
|
||||
stack_top = reader.ExceptionSP()
|
||||
stack_bottom = exception_thread.stack.start + \
|
||||
@ -1669,6 +1789,9 @@ def AnalyzeMinidump(options, minidump_name):
|
||||
heap = V8Heap(reader, stack_map)
|
||||
|
||||
print "Disassembly around exception.eip:"
|
||||
eip_symbol = reader.FindSymbol(reader.ExceptionIP())
|
||||
if eip_symbol is not None:
|
||||
print eip_symbol
|
||||
disasm_start = reader.ExceptionIP() - EIP_PROXIMITY
|
||||
disasm_bytes = 2 * EIP_PROXIMITY
|
||||
if (options.full):
|
||||
@ -1697,8 +1820,10 @@ def AnalyzeMinidump(options, minidump_name):
|
||||
for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
|
||||
maybe_address = reader.ReadUIntPtr(slot)
|
||||
heap_object = heap.FindObject(maybe_address)
|
||||
print "%s: %s" % (reader.FormatIntPtr(slot),
|
||||
reader.FormatIntPtr(maybe_address))
|
||||
maybe_symbol = reader.FindSymbol(maybe_address)
|
||||
print "%s: %s %s" % (reader.FormatIntPtr(slot),
|
||||
reader.FormatIntPtr(maybe_address),
|
||||
maybe_symbol or "")
|
||||
if heap_object:
|
||||
heap_object.Print(Printer())
|
||||
print
|
||||
@ -1712,6 +1837,8 @@ if __name__ == "__main__":
|
||||
help="start an interactive inspector shell")
|
||||
parser.add_option("-f", "--full", dest="full", action="store_true",
|
||||
help="dump all information contained in the minidump")
|
||||
parser.add_option("--symdir", dest="symdir", default=".",
|
||||
help="directory containing *.pdb.sym file with symbols")
|
||||
options, args = parser.parse_args()
|
||||
if len(args) != 1:
|
||||
parser.print_help()
|
||||
|
Loading…
Reference in New Issue
Block a user