| 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 |
| 11 # copyright notice, this list of conditions and the following | 11 # copyright notice, this list of conditions and the following |
| 12 # disclaimer in the documentation and/or other materials provided | 12 # disclaimer in the documentation and/or other materials provided |
| 13 # with the distribution. | 13 # with the distribution. |
| 14 # * Neither the name of Google Inc. nor the names of its | 14 # * Neither the name of Google Inc. nor the names of its |
| 15 # contributors may be used to endorse or promote products derived | 15 # contributors may be used to endorse or promote products derived |
| 16 # from this software without specific prior written permission. | 16 # from this software without specific prior written permission. |
| 17 # | 17 # |
| 18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 | 29 |
| 30 import bisect | |
| 31 import cmd | 30 import cmd |
| 32 import codecs | |
| 33 import ctypes | 31 import ctypes |
| 34 import disasm | |
| 35 import mmap | 32 import mmap |
| 36 import optparse | 33 import optparse |
| 37 import os | 34 import os |
| 35 import disasm |
| 36 import sys |
| 37 import types |
| 38 import codecs |
| 38 import re | 39 import re |
| 39 import struct | 40 import struct |
| 40 import sys | |
| 41 import types | |
| 42 | 41 |
| 43 | 42 |
| 44 USAGE="""usage: %prog [OPTIONS] [DUMP-FILE] | 43 USAGE="""usage: %prog [OPTIONS] [DUMP-FILE] |
| 45 | 44 |
| 46 Minidump analyzer. | 45 Minidump analyzer. |
| 47 | 46 |
| 48 Shows the processor state at the point of exception including the | 47 Shows the processor state at the point of exception including the |
| 49 stack of the active thread and the referenced objects in the V8 | 48 stack of the active thread and the referenced objects in the V8 |
| 50 heap. Code objects are disassembled and the addresses linked from the | 49 heap. Code objects are disassembled and the addresses linked from the |
| 51 stack (e.g. pushed return addresses) are marked with "=>". | 50 stack (e.g. pushed return addresses) are marked with "=>". |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 174 ("checksum", ctypes.c_uint32), | 173 ("checksum", ctypes.c_uint32), |
| 175 ("time_date_stampt", ctypes.c_uint32), | 174 ("time_date_stampt", ctypes.c_uint32), |
| 176 ("flags", ctypes.c_uint64) | 175 ("flags", ctypes.c_uint64) |
| 177 ]) | 176 ]) |
| 178 | 177 |
| 179 MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([ | 178 MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([ |
| 180 ("data_size", ctypes.c_uint32), | 179 ("data_size", ctypes.c_uint32), |
| 181 ("rva", ctypes.c_uint32) | 180 ("rva", ctypes.c_uint32) |
| 182 ]) | 181 ]) |
| 183 | 182 |
| 184 MINIDUMP_STRING = Descriptor([ | |
| 185 ("length", ctypes.c_uint32), | |
| 186 ("buffer", lambda t: ctypes.c_uint8 * (t.length + 2)) | |
| 187 ]) | |
| 188 | |
| 189 MINIDUMP_DIRECTORY = Descriptor([ | 183 MINIDUMP_DIRECTORY = Descriptor([ |
| 190 ("stream_type", ctypes.c_uint32), | 184 ("stream_type", ctypes.c_uint32), |
| 191 ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype) | 185 ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype) |
| 192 ]) | 186 ]) |
| 193 | 187 |
| 194 MD_EXCEPTION_MAXIMUM_PARAMETERS = 15 | 188 MD_EXCEPTION_MAXIMUM_PARAMETERS = 15 |
| 195 | 189 |
| 196 MINIDUMP_EXCEPTION = Descriptor([ | 190 MINIDUMP_EXCEPTION = Descriptor([ |
| 197 ("code", ctypes.c_uint32), | 191 ("code", ctypes.c_uint32), |
| 198 ("flags", ctypes.c_uint32), | 192 ("flags", ctypes.c_uint32), |
| (...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 399 ("ted", ctypes.c_uint64), | 393 ("ted", ctypes.c_uint64), |
| 400 ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype), | 394 ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype), |
| 401 ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype) | 395 ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype) |
| 402 ]) | 396 ]) |
| 403 | 397 |
| 404 MINIDUMP_THREAD_LIST = Descriptor([ | 398 MINIDUMP_THREAD_LIST = Descriptor([ |
| 405 ("thread_count", ctypes.c_uint32), | 399 ("thread_count", ctypes.c_uint32), |
| 406 ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count) | 400 ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count) |
| 407 ]) | 401 ]) |
| 408 | 402 |
| 409 MINIDUMP_RAW_MODULE = Descriptor([ | |
| 410 ("base_of_image", ctypes.c_uint64), | |
| 411 ("size_of_image", ctypes.c_uint32), | |
| 412 ("checksum", ctypes.c_uint32), | |
| 413 ("time_date_stamp", ctypes.c_uint32), | |
| 414 ("module_name_rva", ctypes.c_uint32), | |
| 415 ("version_info", ctypes.c_uint32 * 13), | |
| 416 ("cv_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype), | |
| 417 ("misc_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype), | |
| 418 ("reserved0", ctypes.c_uint32 * 2), | |
| 419 ("reserved1", ctypes.c_uint32 * 2) | |
| 420 ]) | |
| 421 | |
| 422 MINIDUMP_MODULE_LIST = Descriptor([ | |
| 423 ("number_of_modules", ctypes.c_uint32), | |
| 424 ("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules) | |
| 425 ]) | |
| 426 | |
| 427 MINIDUMP_RAW_SYSTEM_INFO = Descriptor([ | 403 MINIDUMP_RAW_SYSTEM_INFO = Descriptor([ |
| 428 ("processor_architecture", ctypes.c_uint16) | 404 ("processor_architecture", ctypes.c_uint16) |
| 429 ]) | 405 ]) |
| 430 | 406 |
| 431 MD_CPU_ARCHITECTURE_X86 = 0 | 407 MD_CPU_ARCHITECTURE_X86 = 0 |
| 432 MD_CPU_ARCHITECTURE_AMD64 = 9 | 408 MD_CPU_ARCHITECTURE_AMD64 = 9 |
| 433 | 409 |
| 434 class FuncSymbol: | |
| 435 def __init__(self, start, size, name): | |
| 436 self.start = start | |
| 437 self.end = self.start + size | |
| 438 self.name = name | |
| 439 | |
| 440 def __cmp__(self, other): | |
| 441 if isinstance(other, FuncSymbol): | |
| 442 return self.start - other.start | |
| 443 return self.start - other | |
| 444 | |
| 445 def Covers(self, addr): | |
| 446 return (self.start <= addr) and (addr < self.end) | |
| 447 | |
| 448 class MinidumpReader(object): | 410 class MinidumpReader(object): |
| 449 """Minidump (.dmp) reader.""" | 411 """Minidump (.dmp) reader.""" |
| 450 | 412 |
| 451 _HEADER_MAGIC = 0x504d444d | 413 _HEADER_MAGIC = 0x504d444d |
| 452 | 414 |
| 453 def __init__(self, options, minidump_name): | 415 def __init__(self, options, minidump_name): |
| 454 self.minidump_name = minidump_name | 416 self.minidump_name = minidump_name |
| 455 self.minidump_file = open(minidump_name, "r") | 417 self.minidump_file = open(minidump_name, "r") |
| 456 self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE) | 418 self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE) |
| 457 self.header = MINIDUMP_HEADER.Read(self.minidump, 0) | 419 self.header = MINIDUMP_HEADER.Read(self.minidump, 0) |
| 458 if self.header.signature != MinidumpReader._HEADER_MAGIC: | 420 if self.header.signature != MinidumpReader._HEADER_MAGIC: |
| 459 print >>sys.stderr, "Warning: Unsupported minidump header magic!" | 421 print >>sys.stderr, "Warning: Unsupported minidump header magic!" |
| 460 DebugPrint(self.header) | 422 DebugPrint(self.header) |
| 461 directories = [] | 423 directories = [] |
| 462 offset = self.header.stream_directories_rva | 424 offset = self.header.stream_directories_rva |
| 463 for _ in xrange(self.header.stream_count): | 425 for _ in xrange(self.header.stream_count): |
| 464 directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset)) | 426 directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset)) |
| 465 offset += MINIDUMP_DIRECTORY.size | 427 offset += MINIDUMP_DIRECTORY.size |
| 466 self.arch = None | 428 self.arch = None |
| 467 self.exception = None | 429 self.exception = None |
| 468 self.exception_context = None | 430 self.exception_context = None |
| 469 self.memory_list = None | 431 self.memory_list = None |
| 470 self.memory_list64 = None | 432 self.memory_list64 = None |
| 471 self.module_list = None | |
| 472 self.thread_map = {} | 433 self.thread_map = {} |
| 473 | 434 |
| 474 self.symdir = options.symdir | |
| 475 self.modules_with_symbols = [] | |
| 476 self.symbols = [] | |
| 477 | |
| 478 # Find MDRawSystemInfo stream and determine arch. | 435 # Find MDRawSystemInfo stream and determine arch. |
| 479 for d in directories: | 436 for d in directories: |
| 480 if d.stream_type == MD_SYSTEM_INFO_STREAM: | 437 if d.stream_type == MD_SYSTEM_INFO_STREAM: |
| 481 system_info = MINIDUMP_RAW_SYSTEM_INFO.Read( | 438 system_info = MINIDUMP_RAW_SYSTEM_INFO.Read( |
| 482 self.minidump, d.location.rva) | 439 self.minidump, d.location.rva) |
| 483 self.arch = system_info.processor_architecture | 440 self.arch = system_info.processor_architecture |
| 484 assert self.arch in [MD_CPU_ARCHITECTURE_AMD64, MD_CPU_ARCHITECTURE_X86] | 441 assert self.arch in [MD_CPU_ARCHITECTURE_AMD64, MD_CPU_ARCHITECTURE_X86] |
| 485 assert not self.arch is None | 442 assert not self.arch is None |
| 486 | 443 |
| 487 for d in directories: | 444 for d in directories: |
| 488 DebugPrint(d) | 445 DebugPrint(d) |
| 489 if d.stream_type == MD_EXCEPTION_STREAM: | 446 if d.stream_type == MD_EXCEPTION_STREAM: |
| 490 self.exception = MINIDUMP_EXCEPTION_STREAM.Read( | 447 self.exception = MINIDUMP_EXCEPTION_STREAM.Read( |
| 491 self.minidump, d.location.rva) | 448 self.minidump, d.location.rva) |
| 492 DebugPrint(self.exception) | 449 DebugPrint(self.exception) |
| 493 if self.arch == MD_CPU_ARCHITECTURE_X86: | 450 if self.arch == MD_CPU_ARCHITECTURE_X86: |
| 494 self.exception_context = MINIDUMP_CONTEXT_X86.Read( | 451 self.exception_context = MINIDUMP_CONTEXT_X86.Read( |
| 495 self.minidump, self.exception.thread_context.rva) | 452 self.minidump, self.exception.thread_context.rva) |
| 496 elif self.arch == MD_CPU_ARCHITECTURE_AMD64: | 453 elif self.arch == MD_CPU_ARCHITECTURE_AMD64: |
| 497 self.exception_context = MINIDUMP_CONTEXT_AMD64.Read( | 454 self.exception_context = MINIDUMP_CONTEXT_AMD64.Read( |
| 498 self.minidump, self.exception.thread_context.rva) | 455 self.minidump, self.exception.thread_context.rva) |
| 499 DebugPrint(self.exception_context) | 456 DebugPrint(self.exception_context) |
| 500 elif d.stream_type == MD_THREAD_LIST_STREAM: | 457 elif d.stream_type == MD_THREAD_LIST_STREAM: |
| 501 thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva) | 458 thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva) |
| 502 assert ctypes.sizeof(thread_list) == d.location.data_size | 459 assert ctypes.sizeof(thread_list) == d.location.data_size |
| 503 DebugPrint(thread_list) | 460 DebugPrint(thread_list) |
| 504 for thread in thread_list.threads: | 461 for thread in thread_list.threads: |
| 505 DebugPrint(thread) | 462 DebugPrint(thread) |
| 506 self.thread_map[thread.id] = thread | 463 self.thread_map[thread.id] = thread |
| 507 elif d.stream_type == MD_MODULE_LIST_STREAM: | |
| 508 assert self.module_list is None | |
| 509 self.module_list = MINIDUMP_MODULE_LIST.Read( | |
| 510 self.minidump, d.location.rva) | |
| 511 assert ctypes.sizeof(self.module_list) == d.location.data_size | |
| 512 elif d.stream_type == MD_MEMORY_LIST_STREAM: | 464 elif d.stream_type == MD_MEMORY_LIST_STREAM: |
| 513 print >>sys.stderr, "Warning: This is not a full minidump!" | 465 print >>sys.stderr, "Warning: This is not a full minidump!" |
| 514 assert self.memory_list is None | 466 assert self.memory_list is None |
| 515 self.memory_list = MINIDUMP_MEMORY_LIST.Read( | 467 self.memory_list = MINIDUMP_MEMORY_LIST.Read( |
| 516 self.minidump, d.location.rva) | 468 self.minidump, d.location.rva) |
| 517 assert ctypes.sizeof(self.memory_list) == d.location.data_size | 469 assert ctypes.sizeof(self.memory_list) == d.location.data_size |
| 518 DebugPrint(self.memory_list) | 470 DebugPrint(self.memory_list) |
| 519 elif d.stream_type == MD_MEMORY_64_LIST_STREAM: | 471 elif d.stream_type == MD_MEMORY_64_LIST_STREAM: |
| 520 assert self.memory_list64 is None | 472 assert self.memory_list64 is None |
| 521 self.memory_list64 = MINIDUMP_MEMORY_LIST64.Read( | 473 self.memory_list64 = MINIDUMP_MEMORY_LIST64.Read( |
| (...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 685 | 637 |
| 686 def PointerSize(self): | 638 def PointerSize(self): |
| 687 if self.arch == MD_CPU_ARCHITECTURE_AMD64: | 639 if self.arch == MD_CPU_ARCHITECTURE_AMD64: |
| 688 return 8 | 640 return 8 |
| 689 elif self.arch == MD_CPU_ARCHITECTURE_X86: | 641 elif self.arch == MD_CPU_ARCHITECTURE_X86: |
| 690 return 4 | 642 return 4 |
| 691 | 643 |
| 692 def Register(self, name): | 644 def Register(self, name): |
| 693 return self.exception_context.__getattribute__(name) | 645 return self.exception_context.__getattribute__(name) |
| 694 | 646 |
| 695 def ReadMinidumpString(self, rva): | |
| 696 string = bytearray(MINIDUMP_STRING.Read(self.minidump, rva).buffer) | |
| 697 string = string.decode("utf16") | |
| 698 return string[0:len(string) - 1] | |
| 699 | |
| 700 # Load FUNC records from a BreakPad symbol file | |
| 701 # | |
| 702 # http://code.google.com/p/google-breakpad/wiki/SymbolFiles | |
| 703 # | |
| 704 def _LoadSymbolsFrom(self, symfile, baseaddr): | |
| 705 print "Loading symbols from %s" % (symfile) | |
| 706 funcs = [] | |
| 707 with open(symfile) as f: | |
| 708 for line in f: | |
| 709 result = re.match( | |
| 710 r"^FUNC ([a-f0-9]+) ([a-f0-9]+) ([a-f0-9]+) (.*)$", line) | |
| 711 if result is not None: | |
| 712 start = int(result.group(1), 16) | |
| 713 size = int(result.group(2), 16) | |
| 714 name = result.group(4).rstrip() | |
| 715 bisect.insort_left(self.symbols, | |
| 716 FuncSymbol(baseaddr + start, size, name)) | |
| 717 print " ... done" | |
| 718 | |
| 719 def TryLoadSymbolsFor(self, modulename, module): | |
| 720 try: | |
| 721 symfile = os.path.join(self.symdir, | |
| 722 modulename.replace('.', '_') + ".pdb.sym") | |
| 723 self._LoadSymbolsFrom(symfile, module.base_of_image) | |
| 724 self.modules_with_symbols.append(module) | |
| 725 except Exception as e: | |
| 726 print " ... failure (%s)" % (e) | |
| 727 | |
| 728 # Returns true if address is covered by some module that has loaded symbols. | |
| 729 def _IsInModuleWithSymbols(self, addr): | |
| 730 for module in self.modules_with_symbols: | |
| 731 start = module.base_of_image | |
| 732 end = start + module.size_of_image | |
| 733 if (start <= addr) and (addr < end): | |
| 734 return True | |
| 735 return False | |
| 736 | |
| 737 # Find symbol covering the given address and return its name in format | |
| 738 # <symbol name>+<offset from the start> | |
| 739 def FindSymbol(self, addr): | |
| 740 if not self._IsInModuleWithSymbols(addr): | |
| 741 return None | |
| 742 | |
| 743 i = bisect.bisect_left(self.symbols, addr) | |
| 744 symbol = None | |
| 745 if (0 < i) and self.symbols[i - 1].Covers(addr): | |
| 746 symbol = self.symbols[i - 1] | |
| 747 elif (i < len(self.symbols)) and self.symbols[i].Covers(addr): | |
| 748 symbol = self.symbols[i] | |
| 749 else: | |
| 750 return None | |
| 751 diff = addr - symbol.start | |
| 752 return "%s+0x%x" % (symbol.name, diff) | |
| 753 | |
| 754 | |
| 755 | 647 |
| 756 # List of V8 instance types. Obtained by adding the code below to any .cc file. | 648 # List of V8 instance types. Obtained by adding the code below to any .cc file. |
| 757 # | 649 # |
| 758 # #define DUMP_TYPE(T) printf(" %d: \"%s\",\n", T, #T); | 650 # #define DUMP_TYPE(T) printf(" %d: \"%s\",\n", T, #T); |
| 759 # struct P { | 651 # struct P { |
| 760 # P() { | 652 # P() { |
| 761 # printf("INSTANCE_TYPES = {\n"); | 653 # printf("INSTANCE_TYPES = {\n"); |
| 762 # INSTANCE_TYPE_LIST(DUMP_TYPE) | 654 # INSTANCE_TYPE_LIST(DUMP_TYPE) |
| 763 # printf("}\n"); | 655 # printf("}\n"); |
| 764 # } | 656 # } |
| (...skipping 975 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1740 EIP_PROXIMITY = 64 | 1632 EIP_PROXIMITY = 64 |
| 1741 | 1633 |
| 1742 CONTEXT_FOR_ARCH = { | 1634 CONTEXT_FOR_ARCH = { |
| 1743 MD_CPU_ARCHITECTURE_AMD64: | 1635 MD_CPU_ARCHITECTURE_AMD64: |
| 1744 ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip', | 1636 ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip', |
| 1745 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15'], | 1637 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15'], |
| 1746 MD_CPU_ARCHITECTURE_X86: | 1638 MD_CPU_ARCHITECTURE_X86: |
| 1747 ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip'] | 1639 ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip'] |
| 1748 } | 1640 } |
| 1749 | 1641 |
| 1750 KNOWN_MODULES = {'chrome.exe', 'chrome.dll'} | |
| 1751 | |
| 1752 def GetModuleName(reader, module): | |
| 1753 name = reader.ReadMinidumpString(module.module_name_rva) | |
| 1754 return str(os.path.basename(str(name).replace("\\", "/"))) | |
| 1755 | 1642 |
| 1756 def AnalyzeMinidump(options, minidump_name): | 1643 def AnalyzeMinidump(options, minidump_name): |
| 1757 reader = MinidumpReader(options, minidump_name) | 1644 reader = MinidumpReader(options, minidump_name) |
| 1758 heap = None | 1645 heap = None |
| 1759 DebugPrint("========================================") | 1646 DebugPrint("========================================") |
| 1760 if reader.exception is None: | 1647 if reader.exception is None: |
| 1761 print "Minidump has no exception info" | 1648 print "Minidump has no exception info" |
| 1762 else: | 1649 else: |
| 1763 print "Exception info:" | 1650 print "Exception info:" |
| 1764 exception_thread = reader.thread_map[reader.exception.thread_id] | 1651 exception_thread = reader.thread_map[reader.exception.thread_id] |
| 1765 print " thread id: %d" % exception_thread.id | 1652 print " thread id: %d" % exception_thread.id |
| 1766 print " code: %08X" % reader.exception.exception.code | 1653 print " code: %08X" % reader.exception.exception.code |
| 1767 print " context:" | 1654 print " context:" |
| 1768 for r in CONTEXT_FOR_ARCH[reader.arch]: | 1655 for r in CONTEXT_FOR_ARCH[reader.arch]: |
| 1769 print " %s: %s" % (r, reader.FormatIntPtr(reader.Register(r))) | 1656 print " %s: %s" % (r, reader.FormatIntPtr(reader.Register(r))) |
| 1770 # TODO(vitalyr): decode eflags. | 1657 # TODO(vitalyr): decode eflags. |
| 1771 print " eflags: %s" % bin(reader.exception_context.eflags)[2:] | 1658 print " eflags: %s" % bin(reader.exception_context.eflags)[2:] |
| 1772 print | 1659 print |
| 1773 print " modules:" | |
| 1774 for module in reader.module_list.modules: | |
| 1775 name = GetModuleName(reader, module) | |
| 1776 if name in KNOWN_MODULES: | |
| 1777 print " %s at %08X" % (name, module.base_of_image) | |
| 1778 reader.TryLoadSymbolsFor(name, module) | |
| 1779 print | |
| 1780 | 1660 |
| 1781 stack_top = reader.ExceptionSP() | 1661 stack_top = reader.ExceptionSP() |
| 1782 stack_bottom = exception_thread.stack.start + \ | 1662 stack_bottom = exception_thread.stack.start + \ |
| 1783 exception_thread.stack.memory.data_size | 1663 exception_thread.stack.memory.data_size |
| 1784 stack_map = {reader.ExceptionIP(): -1} | 1664 stack_map = {reader.ExceptionIP(): -1} |
| 1785 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()): | 1665 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()): |
| 1786 maybe_address = reader.ReadUIntPtr(slot) | 1666 maybe_address = reader.ReadUIntPtr(slot) |
| 1787 if not maybe_address in stack_map: | 1667 if not maybe_address in stack_map: |
| 1788 stack_map[maybe_address] = slot | 1668 stack_map[maybe_address] = slot |
| 1789 heap = V8Heap(reader, stack_map) | 1669 heap = V8Heap(reader, stack_map) |
| 1790 | 1670 |
| 1791 print "Disassembly around exception.eip:" | 1671 print "Disassembly around exception.eip:" |
| 1792 eip_symbol = reader.FindSymbol(reader.ExceptionIP()) | |
| 1793 if eip_symbol is not None: | |
| 1794 print eip_symbol | |
| 1795 disasm_start = reader.ExceptionIP() - EIP_PROXIMITY | 1672 disasm_start = reader.ExceptionIP() - EIP_PROXIMITY |
| 1796 disasm_bytes = 2 * EIP_PROXIMITY | 1673 disasm_bytes = 2 * EIP_PROXIMITY |
| 1797 if (options.full): | 1674 if (options.full): |
| 1798 full_range = reader.FindRegion(reader.ExceptionIP()) | 1675 full_range = reader.FindRegion(reader.ExceptionIP()) |
| 1799 if full_range is not None: | 1676 if full_range is not None: |
| 1800 disasm_start = full_range[0] | 1677 disasm_start = full_range[0] |
| 1801 disasm_bytes = full_range[1] | 1678 disasm_bytes = full_range[1] |
| 1802 | 1679 |
| 1803 lines = reader.GetDisasmLines(disasm_start, disasm_bytes) | 1680 lines = reader.GetDisasmLines(disasm_start, disasm_bytes) |
| 1804 | 1681 |
| 1805 for line in lines: | 1682 for line in lines: |
| 1806 print FormatDisasmLine(disasm_start, heap, line) | 1683 print FormatDisasmLine(disasm_start, heap, line) |
| 1807 print | 1684 print |
| 1808 | 1685 |
| 1809 if heap is None: | 1686 if heap is None: |
| 1810 heap = V8Heap(reader, None) | 1687 heap = V8Heap(reader, None) |
| 1811 | 1688 |
| 1812 if options.full: | 1689 if options.full: |
| 1813 FullDump(reader, heap) | 1690 FullDump(reader, heap) |
| 1814 | 1691 |
| 1815 if options.shell: | 1692 if options.shell: |
| 1816 InspectionShell(reader, heap).cmdloop("type help to get help") | 1693 InspectionShell(reader, heap).cmdloop("type help to get help") |
| 1817 else: | 1694 else: |
| 1818 if reader.exception is not None: | 1695 if reader.exception is not None: |
| 1819 print "Annotated stack (from exception.esp to bottom):" | 1696 print "Annotated stack (from exception.esp to bottom):" |
| 1820 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()): | 1697 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()): |
| 1821 maybe_address = reader.ReadUIntPtr(slot) | 1698 maybe_address = reader.ReadUIntPtr(slot) |
| 1822 heap_object = heap.FindObject(maybe_address) | 1699 heap_object = heap.FindObject(maybe_address) |
| 1823 maybe_symbol = reader.FindSymbol(maybe_address) | 1700 print "%s: %s" % (reader.FormatIntPtr(slot), |
| 1824 print "%s: %s %s" % (reader.FormatIntPtr(slot), | 1701 reader.FormatIntPtr(maybe_address)) |
| 1825 reader.FormatIntPtr(maybe_address), | |
| 1826 maybe_symbol or "") | |
| 1827 if heap_object: | 1702 if heap_object: |
| 1828 heap_object.Print(Printer()) | 1703 heap_object.Print(Printer()) |
| 1829 print | 1704 print |
| 1830 | 1705 |
| 1831 reader.Dispose() | 1706 reader.Dispose() |
| 1832 | 1707 |
| 1833 | 1708 |
| 1834 if __name__ == "__main__": | 1709 if __name__ == "__main__": |
| 1835 parser = optparse.OptionParser(USAGE) | 1710 parser = optparse.OptionParser(USAGE) |
| 1836 parser.add_option("-s", "--shell", dest="shell", action="store_true", | 1711 parser.add_option("-s", "--shell", dest="shell", action="store_true", |
| 1837 help="start an interactive inspector shell") | 1712 help="start an interactive inspector shell") |
| 1838 parser.add_option("-f", "--full", dest="full", action="store_true", | 1713 parser.add_option("-f", "--full", dest="full", action="store_true", |
| 1839 help="dump all information contained in the minidump") | 1714 help="dump all information contained in the minidump") |
| 1840 parser.add_option("--symdir", dest="symdir", default=".", | |
| 1841 help="directory containing *.pdb.sym file with symbols") | |
| 1842 options, args = parser.parse_args() | 1715 options, args = parser.parse_args() |
| 1843 if len(args) != 1: | 1716 if len(args) != 1: |
| 1844 parser.print_help() | 1717 parser.print_help() |
| 1845 sys.exit(1) | 1718 sys.exit(1) |
| 1846 AnalyzeMinidump(options, args[0]) | 1719 AnalyzeMinidump(options, args[0]) |
| OLD | NEW |