Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # | 2 # |
| 3 # Copyright 2012 the V8 project authors. All rights reserved. | 3 # Copyright 2012 the V8 project authors. All rights reserved. |
| 4 # Redistribution and use in source and binary forms, with or without | 4 # Redistribution and use in source and binary forms, with or without |
| 5 # modification, are permitted provided that the following conditions are | 5 # modification, are permitted provided that the following conditions are |
| 6 # met: | 6 # met: |
| 7 # | 7 # |
| 8 # * Redistributions of source code must retain the above copyright | 8 # * Redistributions of source code must retain the above copyright |
| 9 # notice, this list of conditions and the following disclaimer. | 9 # notice, this list of conditions and the following disclaimer. |
| 10 # * Redistributions in binary form must reproduce the above | 10 # * Redistributions in binary form must reproduce the above |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 32 import codecs | 32 import codecs |
| 33 import ctypes | 33 import ctypes |
| 34 import disasm | 34 import disasm |
| 35 import mmap | 35 import mmap |
| 36 import optparse | 36 import optparse |
| 37 import os | 37 import os |
| 38 import re | 38 import re |
| 39 import struct | 39 import struct |
| 40 import sys | 40 import sys |
| 41 import types | 41 import types |
| 42 | 42 import datetime |
|
Michael Starzinger
2013/07/01 11:17:37
nit: Can we alpha-sort the imports?
| |
| 43 | 43 |
| 44 USAGE="""usage: %prog [OPTIONS] [DUMP-FILE] | 44 USAGE="""usage: %prog [OPTIONS] [DUMP-FILE] |
| 45 | 45 |
| 46 Minidump analyzer. | 46 Minidump analyzer. |
| 47 | 47 |
| 48 Shows the processor state at the point of exception including the | 48 Shows the processor state at the point of exception including the |
| 49 stack of the active thread and the referenced objects in the V8 | 49 stack of the active thread and the referenced objects in the V8 |
| 50 heap. Code objects are disassembled and the addresses linked from the | 50 heap. Code objects are disassembled and the addresses linked from the |
| 51 stack (e.g. pushed return addresses) are marked with "=>". | 51 stack (e.g. pushed return addresses) are marked with "=>". |
| 52 | 52 |
| (...skipping 382 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 435 ("ted", ctypes.c_uint64), | 435 ("ted", ctypes.c_uint64), |
| 436 ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype), | 436 ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype), |
| 437 ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype) | 437 ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype) |
| 438 ]) | 438 ]) |
| 439 | 439 |
| 440 MINIDUMP_THREAD_LIST = Descriptor([ | 440 MINIDUMP_THREAD_LIST = Descriptor([ |
| 441 ("thread_count", ctypes.c_uint32), | 441 ("thread_count", ctypes.c_uint32), |
| 442 ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count) | 442 ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count) |
| 443 ]) | 443 ]) |
| 444 | 444 |
| 445 MINIDUMP_VS_FIXEDFILEINFO = Descriptor([ | |
| 446 ("dwSignature", ctypes.c_uint32), | |
| 447 ("dwStrucVersion", ctypes.c_uint32), | |
| 448 ("dwFileVersionMS", ctypes.c_uint32), | |
| 449 ("dwFileVersionLS", ctypes.c_uint32), | |
| 450 ("dwProductVersionMS", ctypes.c_uint32), | |
| 451 ("dwProductVersionLS", ctypes.c_uint32), | |
| 452 ("dwFileFlagsMask", ctypes.c_uint32), | |
| 453 ("dwFileFlags", ctypes.c_uint32), | |
| 454 ("dwFileOS", ctypes.c_uint32), | |
| 455 ("dwFileType", ctypes.c_uint32), | |
| 456 ("dwFileSubtype", ctypes.c_uint32), | |
| 457 ("dwFileDateMS", ctypes.c_uint32), | |
| 458 ("dwFileDateLS", ctypes.c_uint32) | |
| 459 ]) | |
| 460 | |
| 445 MINIDUMP_RAW_MODULE = Descriptor([ | 461 MINIDUMP_RAW_MODULE = Descriptor([ |
| 446 ("base_of_image", ctypes.c_uint64), | 462 ("base_of_image", ctypes.c_uint64), |
| 447 ("size_of_image", ctypes.c_uint32), | 463 ("size_of_image", ctypes.c_uint32), |
| 448 ("checksum", ctypes.c_uint32), | 464 ("checksum", ctypes.c_uint32), |
| 449 ("time_date_stamp", ctypes.c_uint32), | 465 ("time_date_stamp", ctypes.c_uint32), |
| 450 ("module_name_rva", ctypes.c_uint32), | 466 ("module_name_rva", ctypes.c_uint32), |
| 451 ("version_info", ctypes.c_uint32 * 13), | 467 ("version_info", MINIDUMP_VS_FIXEDFILEINFO.ctype), |
| 452 ("cv_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype), | 468 ("cv_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype), |
| 453 ("misc_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype), | 469 ("misc_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype), |
| 454 ("reserved0", ctypes.c_uint32 * 2), | 470 ("reserved0", ctypes.c_uint32 * 2), |
| 455 ("reserved1", ctypes.c_uint32 * 2) | 471 ("reserved1", ctypes.c_uint32 * 2) |
| 456 ]) | 472 ]) |
| 457 | 473 |
| 458 MINIDUMP_MODULE_LIST = Descriptor([ | 474 MINIDUMP_MODULE_LIST = Descriptor([ |
| 459 ("number_of_modules", ctypes.c_uint32), | 475 ("number_of_modules", ctypes.c_uint32), |
| 460 ("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules) | 476 ("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules) |
| 461 ]) | 477 ]) |
| (...skipping 1542 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2004 """ | 2020 """ |
| 2005 List all available memory regions. | 2021 List all available memory regions. |
| 2006 """ | 2022 """ |
| 2007 def print_region(reader, start, size, location): | 2023 def print_region(reader, start, size, location): |
| 2008 print " %s - %s (%d bytes)" % (reader.FormatIntPtr(start), | 2024 print " %s - %s (%d bytes)" % (reader.FormatIntPtr(start), |
| 2009 reader.FormatIntPtr(start + size), | 2025 reader.FormatIntPtr(start + size), |
| 2010 size) | 2026 size) |
| 2011 print "Available memory regions:" | 2027 print "Available memory regions:" |
| 2012 self.reader.ForEachMemoryRegion(print_region) | 2028 self.reader.ForEachMemoryRegion(print_region) |
| 2013 | 2029 |
| 2030 def do_lm(self, arg): | |
| 2031 """ | |
| 2032 List details for all loaded modules in the minidump. An argument can | |
| 2033 be passed to limit the output to only those modules that contain the | |
| 2034 argument as a substring (case insensitive match). | |
| 2035 """ | |
| 2036 for module in self.reader.module_list.modules: | |
| 2037 if arg: | |
| 2038 name = GetModuleName(self.reader, module).lower() | |
| 2039 if name.find(arg.lower()) >= 0: | |
| 2040 GetModuleDetails(self.reader, module) | |
| 2041 else: | |
| 2042 GetModuleDetails(self.reader, module) | |
| 2043 print | |
| 2044 | |
| 2014 def do_s(self, word): | 2045 def do_s(self, word): |
| 2015 """ | 2046 """ |
| 2016 Search for a given word in available memory regions. The given word | 2047 Search for a given word in available memory regions. The given word |
| 2017 is expanded to full pointer size and searched at aligned as well as | 2048 is expanded to full pointer size and searched at aligned as well as |
| 2018 un-aligned memory locations. Use 'sa' to search aligned locations | 2049 un-aligned memory locations. Use 'sa' to search aligned locations |
| 2019 only. | 2050 only. |
| 2020 """ | 2051 """ |
| 2021 try: | 2052 try: |
| 2022 word = int(word, 0) | 2053 word = int(word, 0) |
| 2023 except ValueError: | 2054 except ValueError: |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2062 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15'], | 2093 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15'], |
| 2063 MD_CPU_ARCHITECTURE_ARM: | 2094 MD_CPU_ARCHITECTURE_ARM: |
| 2064 ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', | 2095 ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', |
| 2065 'r10', 'r11', 'r12', 'sp', 'lr', 'pc'], | 2096 'r10', 'r11', 'r12', 'sp', 'lr', 'pc'], |
| 2066 MD_CPU_ARCHITECTURE_X86: | 2097 MD_CPU_ARCHITECTURE_X86: |
| 2067 ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip'] | 2098 ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip'] |
| 2068 } | 2099 } |
| 2069 | 2100 |
| 2070 KNOWN_MODULES = {'chrome.exe', 'chrome.dll'} | 2101 KNOWN_MODULES = {'chrome.exe', 'chrome.dll'} |
| 2071 | 2102 |
| 2103 def GetVersionString(ms, ls): | |
| 2104 return "%d.%d.%d.%d" % (ms >> 16, ms & 0xffff, ls >> 16, ls & 0xffff) | |
| 2105 | |
| 2106 | |
| 2072 def GetModuleName(reader, module): | 2107 def GetModuleName(reader, module): |
| 2073 name = reader.ReadMinidumpString(module.module_name_rva) | 2108 name = reader.ReadMinidumpString(module.module_name_rva) |
| 2074 return str(os.path.basename(str(name).replace("\\", "/"))) | 2109 return str(os.path.basename(str(name).replace("\\", "/"))) |
| 2075 | 2110 |
| 2111 | |
| 2112 def GetModuleDetails(reader, module): | |
|
Michael Starzinger
2013/07/01 11:17:37
nit: Can we rename this to PrintModuleDetails, I t
| |
| 2113 print "%s" % GetModuleName(reader, module) | |
| 2114 file_version = GetVersionString(module.version_info.dwFileVersionMS, | |
| 2115 module.version_info.dwFileVersionLS) | |
| 2116 product_version = GetVersionString(module.version_info.dwProductVersionMS, | |
| 2117 module.version_info.dwProductVersionLS) | |
| 2118 print " base: %s" % reader.FormatIntPtr(module.base_of_image) | |
| 2119 print " end: %s" % reader.FormatIntPtr(module.base_of_image + | |
| 2120 module.size_of_image) | |
| 2121 print " file version: %s" % file_version | |
| 2122 print " product version: %s" % product_version | |
| 2123 time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp) | |
| 2124 print " timestamp: %s" % time_date_stamp | |
| 2125 | |
| 2126 | |
| 2076 def AnalyzeMinidump(options, minidump_name): | 2127 def AnalyzeMinidump(options, minidump_name): |
| 2077 reader = MinidumpReader(options, minidump_name) | 2128 reader = MinidumpReader(options, minidump_name) |
| 2078 heap = None | 2129 heap = None |
| 2079 DebugPrint("========================================") | 2130 DebugPrint("========================================") |
| 2080 if reader.exception is None: | 2131 if reader.exception is None: |
| 2081 print "Minidump has no exception info" | 2132 print "Minidump has no exception info" |
| 2082 else: | 2133 else: |
| 2083 print "Exception info:" | 2134 print "Exception info:" |
| 2084 exception_thread = reader.thread_map[reader.exception.thread_id] | 2135 exception_thread = reader.thread_map[reader.exception.thread_id] |
| 2085 print " thread id: %d" % exception_thread.id | 2136 print " thread id: %d" % exception_thread.id |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2130 for line in lines: | 2181 for line in lines: |
| 2131 print FormatDisasmLine(disasm_start, heap, line) | 2182 print FormatDisasmLine(disasm_start, heap, line) |
| 2132 print | 2183 print |
| 2133 | 2184 |
| 2134 if heap is None: | 2185 if heap is None: |
| 2135 heap = V8Heap(reader, None) | 2186 heap = V8Heap(reader, None) |
| 2136 | 2187 |
| 2137 if options.full: | 2188 if options.full: |
| 2138 FullDump(reader, heap) | 2189 FullDump(reader, heap) |
| 2139 | 2190 |
| 2191 if options.command: | |
| 2192 InspectionShell(reader, heap).onecmd(options.command) | |
| 2193 | |
| 2140 if options.shell: | 2194 if options.shell: |
| 2141 try: | 2195 try: |
| 2142 InspectionShell(reader, heap).cmdloop("type help to get help") | 2196 InspectionShell(reader, heap).cmdloop("type help to get help") |
| 2143 except KeyboardInterrupt: | 2197 except KeyboardInterrupt: |
| 2144 print "Kthxbye." | 2198 print "Kthxbye." |
| 2145 else: | 2199 elif not options.command: |
| 2146 if reader.exception is not None: | 2200 if reader.exception is not None: |
| 2147 print "Annotated stack (from exception.esp to bottom):" | 2201 print "Annotated stack (from exception.esp to bottom):" |
| 2148 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()): | 2202 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()): |
| 2149 maybe_address = reader.ReadUIntPtr(slot) | 2203 maybe_address = reader.ReadUIntPtr(slot) |
| 2150 heap_object = heap.FindObject(maybe_address) | 2204 heap_object = heap.FindObject(maybe_address) |
| 2151 maybe_symbol = reader.FindSymbol(maybe_address) | 2205 maybe_symbol = reader.FindSymbol(maybe_address) |
| 2152 print "%s: %s %s" % (reader.FormatIntPtr(slot), | 2206 print "%s: %s %s" % (reader.FormatIntPtr(slot), |
| 2153 reader.FormatIntPtr(maybe_address), | 2207 reader.FormatIntPtr(maybe_address), |
| 2154 maybe_symbol or "") | 2208 maybe_symbol or "") |
| 2155 if heap_object: | 2209 if heap_object: |
| 2156 heap_object.Print(Printer()) | 2210 heap_object.Print(Printer()) |
| 2157 print | 2211 print |
| 2158 | 2212 |
| 2159 reader.Dispose() | 2213 reader.Dispose() |
| 2160 | 2214 |
| 2161 | 2215 |
| 2162 if __name__ == "__main__": | 2216 if __name__ == "__main__": |
| 2163 parser = optparse.OptionParser(USAGE) | 2217 parser = optparse.OptionParser(USAGE) |
| 2164 parser.add_option("-s", "--shell", dest="shell", action="store_true", | 2218 parser.add_option("-s", "--shell", dest="shell", action="store_true", |
| 2165 help="start an interactive inspector shell") | 2219 help="start an interactive inspector shell") |
| 2220 parser.add_option("-c", "--command", dest="command", default="", | |
| 2221 help="run an interactive inspector shell command and exit") | |
| 2166 parser.add_option("-f", "--full", dest="full", action="store_true", | 2222 parser.add_option("-f", "--full", dest="full", action="store_true", |
| 2167 help="dump all information contained in the minidump") | 2223 help="dump all information contained in the minidump") |
| 2168 parser.add_option("--symdir", dest="symdir", default=".", | 2224 parser.add_option("--symdir", dest="symdir", default=".", |
| 2169 help="directory containing *.pdb.sym file with symbols") | 2225 help="directory containing *.pdb.sym file with symbols") |
| 2170 parser.add_option("--objdump", | 2226 parser.add_option("--objdump", |
| 2171 default="/usr/bin/objdump", | 2227 default="/usr/bin/objdump", |
| 2172 help="objdump tool to use [default: %default]") | 2228 help="objdump tool to use [default: %default]") |
| 2173 options, args = parser.parse_args() | 2229 options, args = parser.parse_args() |
| 2174 if os.path.exists(options.objdump): | 2230 if os.path.exists(options.objdump): |
| 2175 disasm.OBJDUMP_BIN = options.objdump | 2231 disasm.OBJDUMP_BIN = options.objdump |
| 2176 OBJDUMP_BIN = options.objdump | 2232 OBJDUMP_BIN = options.objdump |
| 2177 else: | 2233 else: |
| 2178 print "Cannot find %s, falling back to default objdump" % options.objdump | 2234 print "Cannot find %s, falling back to default objdump" % options.objdump |
| 2179 if len(args) != 1: | 2235 if len(args) != 1: |
| 2180 parser.print_help() | 2236 parser.print_help() |
| 2181 sys.exit(1) | 2237 sys.exit(1) |
| 2182 AnalyzeMinidump(options, args[0]) | 2238 AnalyzeMinidump(options, args[0]) |
| OLD | NEW |