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:
vegorov@chromium.org 2012-09-26 12:51:46 +00:00
parent aba09dcf2e
commit d6853c3697

View File

@ -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()