Chromium Code Reviews| 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:] |
|
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()) |
| @@ -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() |