Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(228)

Unified Diff: tools/grokdump.py

Issue 10923003: Extend grokdump.py with simple BreakPad symbol files support. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/grokdump.py
diff --git a/tools/grokdump.py b/tools/grokdump.py
index 5d9a053afde48af009121e67587b5d730856885f..33ec742b87d3fd50a5e11d553f0d9caed342d2bb 100755
--- a/tools/grokdump.py
+++ b/tools/grokdump.py
@@ -27,6 +27,7 @@
# (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 ctypes
import mmap
@@ -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,64 @@ 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)
Michael Starzinger 2012/09/19 09:54:32 More than 80 characters.
Vyacheslav Egorov (Google) 2012/09/26 12:48:57 Done.
+ 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))
Michael Starzinger 2012/09/19 09:54:32 More than 80 characters.
Vyacheslav Egorov (Google) 2012/09/26 12:48:57 Done.
+ 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 +1745,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 +1768,12 @@ def AnalyzeMinidump(options, minidump_name):
# TODO(vitalyr): decode eflags.
print " eflags: %s" % bin(reader.exception_context.eflags)[2:]
print
Michael Starzinger 2012/09/19 09:54:32 Can we move this empty print to after the modules
Vyacheslav Egorov (Google) 2012/09/26 12:48:57 I will add another one after modules. I like some
+ print " Modules:"
Michael Starzinger 2012/09/19 09:54:32 Lowercase "modules:" would be consistent with the
Vyacheslav Egorov (Google) 2012/09/26 12:48:58 Done.
+ 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)
stack_top = reader.ExceptionSP()
stack_bottom = exception_thread.stack.start + \
@@ -1669,6 +1786,7 @@ def AnalyzeMinidump(options, minidump_name):
heap = V8Heap(reader, stack_map)
print "Disassembly around exception.eip:"
+ print reader.FindSymbol(reader.ExceptionIP())
Michael Starzinger 2012/09/19 09:54:32 This will print "None" when no symbol is found, ca
Vyacheslav Egorov (Google) 2012/09/26 12:48:58 Done.
disasm_start = reader.ExceptionIP() - EIP_PROXIMITY
disasm_bytes = 2 * EIP_PROXIMITY
if (options.full):
@@ -1697,8 +1815,13 @@ 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)
+ if maybe_symbol is None:
+ maybe_symbol = ""
+
+ print "%s: %s %s" % (reader.FormatIntPtr(slot),
+ reader.FormatIntPtr(maybe_address),
+ maybe_symbol)
Michael Starzinger 2012/09/19 09:54:32 Just use "maybe_symbol or ''" here and drop the if
Vyacheslav Egorov (Google) 2012/09/26 12:48:58 Done.
if heap_object:
heap_object.Print(Printer())
print
@@ -1712,6 +1835,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()
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698