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 |
| 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 | |
| 30 import cmd | 31 import cmd |
| 31 import ctypes | 32 import ctypes |
| 32 import mmap | 33 import mmap |
| 33 import optparse | 34 import optparse |
| 34 import os | 35 import os |
| 35 import disasm | 36 import disasm |
|
Michael Starzinger
2012/09/19 09:54:32
I know you didn't touch, but can we alpha-sort the
Vyacheslav Egorov (Google)
2012/09/26 12:48:57
Done.
| |
| 36 import sys | 37 import sys |
| 37 import types | 38 import types |
| 38 import codecs | 39 import codecs |
| 39 import re | 40 import re |
| 40 import struct | 41 import struct |
| 41 | 42 |
| 42 | 43 |
| 43 USAGE="""usage: %prog [OPTIONS] [DUMP-FILE] | 44 USAGE="""usage: %prog [OPTIONS] [DUMP-FILE] |
| 44 | 45 |
| 45 Minidump analyzer. | 46 Minidump analyzer. |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 173 ("checksum", ctypes.c_uint32), | 174 ("checksum", ctypes.c_uint32), |
| 174 ("time_date_stampt", ctypes.c_uint32), | 175 ("time_date_stampt", ctypes.c_uint32), |
| 175 ("flags", ctypes.c_uint64) | 176 ("flags", ctypes.c_uint64) |
| 176 ]) | 177 ]) |
| 177 | 178 |
| 178 MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([ | 179 MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([ |
| 179 ("data_size", ctypes.c_uint32), | 180 ("data_size", ctypes.c_uint32), |
| 180 ("rva", ctypes.c_uint32) | 181 ("rva", ctypes.c_uint32) |
| 181 ]) | 182 ]) |
| 182 | 183 |
| 184 MINIDUMP_STRING = Descriptor([ | |
| 185 ("length", ctypes.c_uint32), | |
| 186 ("buffer", lambda t: ctypes.c_uint8 * (t.length + 2)) | |
| 187 ]) | |
| 188 | |
| 183 MINIDUMP_DIRECTORY = Descriptor([ | 189 MINIDUMP_DIRECTORY = Descriptor([ |
| 184 ("stream_type", ctypes.c_uint32), | 190 ("stream_type", ctypes.c_uint32), |
| 185 ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype) | 191 ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype) |
| 186 ]) | 192 ]) |
| 187 | 193 |
| 188 MD_EXCEPTION_MAXIMUM_PARAMETERS = 15 | 194 MD_EXCEPTION_MAXIMUM_PARAMETERS = 15 |
| 189 | 195 |
| 190 MINIDUMP_EXCEPTION = Descriptor([ | 196 MINIDUMP_EXCEPTION = Descriptor([ |
| 191 ("code", ctypes.c_uint32), | 197 ("code", ctypes.c_uint32), |
| 192 ("flags", ctypes.c_uint32), | 198 ("flags", ctypes.c_uint32), |
| (...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 393 ("ted", ctypes.c_uint64), | 399 ("ted", ctypes.c_uint64), |
| 394 ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype), | 400 ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype), |
| 395 ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype) | 401 ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype) |
| 396 ]) | 402 ]) |
| 397 | 403 |
| 398 MINIDUMP_THREAD_LIST = Descriptor([ | 404 MINIDUMP_THREAD_LIST = Descriptor([ |
| 399 ("thread_count", ctypes.c_uint32), | 405 ("thread_count", ctypes.c_uint32), |
| 400 ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count) | 406 ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count) |
| 401 ]) | 407 ]) |
| 402 | 408 |
| 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 | |
| 403 MINIDUMP_RAW_SYSTEM_INFO = Descriptor([ | 427 MINIDUMP_RAW_SYSTEM_INFO = Descriptor([ |
| 404 ("processor_architecture", ctypes.c_uint16) | 428 ("processor_architecture", ctypes.c_uint16) |
| 405 ]) | 429 ]) |
| 406 | 430 |
| 407 MD_CPU_ARCHITECTURE_X86 = 0 | 431 MD_CPU_ARCHITECTURE_X86 = 0 |
| 408 MD_CPU_ARCHITECTURE_AMD64 = 9 | 432 MD_CPU_ARCHITECTURE_AMD64 = 9 |
| 409 | 433 |
| 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 | |
| 410 class MinidumpReader(object): | 448 class MinidumpReader(object): |
| 411 """Minidump (.dmp) reader.""" | 449 """Minidump (.dmp) reader.""" |
| 412 | 450 |
| 413 _HEADER_MAGIC = 0x504d444d | 451 _HEADER_MAGIC = 0x504d444d |
| 414 | 452 |
| 415 def __init__(self, options, minidump_name): | 453 def __init__(self, options, minidump_name): |
| 416 self.minidump_name = minidump_name | 454 self.minidump_name = minidump_name |
| 417 self.minidump_file = open(minidump_name, "r") | 455 self.minidump_file = open(minidump_name, "r") |
| 418 self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE) | 456 self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE) |
| 419 self.header = MINIDUMP_HEADER.Read(self.minidump, 0) | 457 self.header = MINIDUMP_HEADER.Read(self.minidump, 0) |
| 420 if self.header.signature != MinidumpReader._HEADER_MAGIC: | 458 if self.header.signature != MinidumpReader._HEADER_MAGIC: |
| 421 print >>sys.stderr, "Warning: Unsupported minidump header magic!" | 459 print >>sys.stderr, "Warning: Unsupported minidump header magic!" |
| 422 DebugPrint(self.header) | 460 DebugPrint(self.header) |
| 423 directories = [] | 461 directories = [] |
| 424 offset = self.header.stream_directories_rva | 462 offset = self.header.stream_directories_rva |
| 425 for _ in xrange(self.header.stream_count): | 463 for _ in xrange(self.header.stream_count): |
| 426 directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset)) | 464 directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset)) |
| 427 offset += MINIDUMP_DIRECTORY.size | 465 offset += MINIDUMP_DIRECTORY.size |
| 428 self.arch = None | 466 self.arch = None |
| 429 self.exception = None | 467 self.exception = None |
| 430 self.exception_context = None | 468 self.exception_context = None |
| 431 self.memory_list = None | 469 self.memory_list = None |
| 432 self.memory_list64 = None | 470 self.memory_list64 = None |
| 471 self.module_list = None | |
| 433 self.thread_map = {} | 472 self.thread_map = {} |
| 434 | 473 |
| 474 self.symdir = options.symdir | |
| 475 self.modules_with_symbols = [] | |
| 476 self.symbols = [] | |
| 477 | |
| 435 # Find MDRawSystemInfo stream and determine arch. | 478 # Find MDRawSystemInfo stream and determine arch. |
| 436 for d in directories: | 479 for d in directories: |
| 437 if d.stream_type == MD_SYSTEM_INFO_STREAM: | 480 if d.stream_type == MD_SYSTEM_INFO_STREAM: |
| 438 system_info = MINIDUMP_RAW_SYSTEM_INFO.Read( | 481 system_info = MINIDUMP_RAW_SYSTEM_INFO.Read( |
| 439 self.minidump, d.location.rva) | 482 self.minidump, d.location.rva) |
| 440 self.arch = system_info.processor_architecture | 483 self.arch = system_info.processor_architecture |
| 441 assert self.arch in [MD_CPU_ARCHITECTURE_AMD64, MD_CPU_ARCHITECTURE_X86] | 484 assert self.arch in [MD_CPU_ARCHITECTURE_AMD64, MD_CPU_ARCHITECTURE_X86] |
| 442 assert not self.arch is None | 485 assert not self.arch is None |
| 443 | 486 |
| 444 for d in directories: | 487 for d in directories: |
| 445 DebugPrint(d) | 488 DebugPrint(d) |
| 446 if d.stream_type == MD_EXCEPTION_STREAM: | 489 if d.stream_type == MD_EXCEPTION_STREAM: |
| 447 self.exception = MINIDUMP_EXCEPTION_STREAM.Read( | 490 self.exception = MINIDUMP_EXCEPTION_STREAM.Read( |
| 448 self.minidump, d.location.rva) | 491 self.minidump, d.location.rva) |
| 449 DebugPrint(self.exception) | 492 DebugPrint(self.exception) |
| 450 if self.arch == MD_CPU_ARCHITECTURE_X86: | 493 if self.arch == MD_CPU_ARCHITECTURE_X86: |
| 451 self.exception_context = MINIDUMP_CONTEXT_X86.Read( | 494 self.exception_context = MINIDUMP_CONTEXT_X86.Read( |
| 452 self.minidump, self.exception.thread_context.rva) | 495 self.minidump, self.exception.thread_context.rva) |
| 453 elif self.arch == MD_CPU_ARCHITECTURE_AMD64: | 496 elif self.arch == MD_CPU_ARCHITECTURE_AMD64: |
| 454 self.exception_context = MINIDUMP_CONTEXT_AMD64.Read( | 497 self.exception_context = MINIDUMP_CONTEXT_AMD64.Read( |
| 455 self.minidump, self.exception.thread_context.rva) | 498 self.minidump, self.exception.thread_context.rva) |
| 456 DebugPrint(self.exception_context) | 499 DebugPrint(self.exception_context) |
| 457 elif d.stream_type == MD_THREAD_LIST_STREAM: | 500 elif d.stream_type == MD_THREAD_LIST_STREAM: |
| 458 thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva) | 501 thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva) |
| 459 assert ctypes.sizeof(thread_list) == d.location.data_size | 502 assert ctypes.sizeof(thread_list) == d.location.data_size |
| 460 DebugPrint(thread_list) | 503 DebugPrint(thread_list) |
| 461 for thread in thread_list.threads: | 504 for thread in thread_list.threads: |
| 462 DebugPrint(thread) | 505 DebugPrint(thread) |
| 463 self.thread_map[thread.id] = thread | 506 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 | |
| 464 elif d.stream_type == MD_MEMORY_LIST_STREAM: | 512 elif d.stream_type == MD_MEMORY_LIST_STREAM: |
| 465 print >>sys.stderr, "Warning: This is not a full minidump!" | 513 print >>sys.stderr, "Warning: This is not a full minidump!" |
| 466 assert self.memory_list is None | 514 assert self.memory_list is None |
| 467 self.memory_list = MINIDUMP_MEMORY_LIST.Read( | 515 self.memory_list = MINIDUMP_MEMORY_LIST.Read( |
| 468 self.minidump, d.location.rva) | 516 self.minidump, d.location.rva) |
| 469 assert ctypes.sizeof(self.memory_list) == d.location.data_size | 517 assert ctypes.sizeof(self.memory_list) == d.location.data_size |
| 470 DebugPrint(self.memory_list) | 518 DebugPrint(self.memory_list) |
| 471 elif d.stream_type == MD_MEMORY_64_LIST_STREAM: | 519 elif d.stream_type == MD_MEMORY_64_LIST_STREAM: |
| 472 assert self.memory_list64 is None | 520 assert self.memory_list64 is None |
| 473 self.memory_list64 = MINIDUMP_MEMORY_LIST64.Read( | 521 self.memory_list64 = MINIDUMP_MEMORY_LIST64.Read( |
| (...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 637 | 685 |
| 638 def PointerSize(self): | 686 def PointerSize(self): |
| 639 if self.arch == MD_CPU_ARCHITECTURE_AMD64: | 687 if self.arch == MD_CPU_ARCHITECTURE_AMD64: |
| 640 return 8 | 688 return 8 |
| 641 elif self.arch == MD_CPU_ARCHITECTURE_X86: | 689 elif self.arch == MD_CPU_ARCHITECTURE_X86: |
| 642 return 4 | 690 return 4 |
| 643 | 691 |
| 644 def Register(self, name): | 692 def Register(self, name): |
| 645 return self.exception_context.__getattribute__(name) | 693 return self.exception_context.__getattribute__(name) |
| 646 | 694 |
| 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(r"^FUNC ([a-f0-9]+) ([a-f0-9]+) ([a-f0-9]+) (.*)$", li ne) | |
|
Michael Starzinger
2012/09/19 09:54:32
More than 80 characters.
Vyacheslav Egorov (Google)
2012/09/26 12:48:57
Done.
| |
| 710 if result is not None: | |
| 711 start = int(result.group(1), 16) | |
| 712 size = int(result.group(2), 16) | |
| 713 name = result.group(4).rstrip() | |
| 714 bisect.insort_left(self.symbols, FuncSymbol(baseaddr + start, size, na me)) | |
|
Michael Starzinger
2012/09/19 09:54:32
More than 80 characters.
Vyacheslav Egorov (Google)
2012/09/26 12:48:57
Done.
| |
| 715 print " ... done" | |
| 716 | |
| 717 def TryLoadSymbolsFor(self, modulename, module): | |
| 718 try: | |
| 719 symfile = os.path.join(self.symdir, | |
| 720 modulename.replace('.', '_') + ".pdb.sym") | |
| 721 self._LoadSymbolsFrom(symfile, module.base_of_image) | |
| 722 self.modules_with_symbols.append(module) | |
| 723 except Exception as e: | |
| 724 print " ... failure (%s)" % (e) | |
| 725 | |
| 726 # Returns true if address is covered by some module that has loaded symbols. | |
| 727 def _IsInModuleWithSymbols(self, addr): | |
| 728 for module in self.modules_with_symbols: | |
| 729 start = module.base_of_image | |
| 730 end = start + module.size_of_image | |
| 731 if (start <= addr) and (addr < end): | |
| 732 return True | |
| 733 return False | |
| 734 | |
| 735 # Find symbol covering the given address and return its name in format | |
| 736 # <symbol name>+<offset from the start> | |
| 737 def FindSymbol(self, addr): | |
| 738 if not self._IsInModuleWithSymbols(addr): | |
| 739 return None | |
| 740 | |
| 741 i = bisect.bisect_left(self.symbols, addr) | |
| 742 symbol = None | |
| 743 if (0 < i) and self.symbols[i - 1].Covers(addr): | |
| 744 symbol = self.symbols[i - 1] | |
| 745 elif (i < len(self.symbols)) and self.symbols[i].Covers(addr): | |
| 746 symbol = self.symbols[i] | |
| 747 else: | |
| 748 return None | |
| 749 diff = addr - symbol.start | |
| 750 return "%s+0x%x" % (symbol.name, diff) | |
| 751 | |
| 752 | |
| 647 | 753 |
| 648 # List of V8 instance types. Obtained by adding the code below to any .cc file. | 754 # List of V8 instance types. Obtained by adding the code below to any .cc file. |
| 649 # | 755 # |
| 650 # #define DUMP_TYPE(T) printf(" %d: \"%s\",\n", T, #T); | 756 # #define DUMP_TYPE(T) printf(" %d: \"%s\",\n", T, #T); |
| 651 # struct P { | 757 # struct P { |
| 652 # P() { | 758 # P() { |
| 653 # printf("INSTANCE_TYPES = {\n"); | 759 # printf("INSTANCE_TYPES = {\n"); |
| 654 # INSTANCE_TYPE_LIST(DUMP_TYPE) | 760 # INSTANCE_TYPE_LIST(DUMP_TYPE) |
| 655 # printf("}\n"); | 761 # printf("}\n"); |
| 656 # } | 762 # } |
| (...skipping 975 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1632 EIP_PROXIMITY = 64 | 1738 EIP_PROXIMITY = 64 |
| 1633 | 1739 |
| 1634 CONTEXT_FOR_ARCH = { | 1740 CONTEXT_FOR_ARCH = { |
| 1635 MD_CPU_ARCHITECTURE_AMD64: | 1741 MD_CPU_ARCHITECTURE_AMD64: |
| 1636 ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip', | 1742 ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip', |
| 1637 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15'], | 1743 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15'], |
| 1638 MD_CPU_ARCHITECTURE_X86: | 1744 MD_CPU_ARCHITECTURE_X86: |
| 1639 ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip'] | 1745 ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip'] |
| 1640 } | 1746 } |
| 1641 | 1747 |
| 1748 KNOWN_MODULES = {'chrome.exe', 'chrome.dll'} | |
| 1749 | |
| 1750 def GetModuleName(reader, module): | |
| 1751 name = reader.ReadMinidumpString(module.module_name_rva) | |
| 1752 return str(os.path.basename(str(name).replace("\\", "/"))) | |
| 1642 | 1753 |
| 1643 def AnalyzeMinidump(options, minidump_name): | 1754 def AnalyzeMinidump(options, minidump_name): |
| 1644 reader = MinidumpReader(options, minidump_name) | 1755 reader = MinidumpReader(options, minidump_name) |
| 1645 heap = None | 1756 heap = None |
| 1646 DebugPrint("========================================") | 1757 DebugPrint("========================================") |
| 1647 if reader.exception is None: | 1758 if reader.exception is None: |
| 1648 print "Minidump has no exception info" | 1759 print "Minidump has no exception info" |
| 1649 else: | 1760 else: |
| 1650 print "Exception info:" | 1761 print "Exception info:" |
| 1651 exception_thread = reader.thread_map[reader.exception.thread_id] | 1762 exception_thread = reader.thread_map[reader.exception.thread_id] |
| 1652 print " thread id: %d" % exception_thread.id | 1763 print " thread id: %d" % exception_thread.id |
| 1653 print " code: %08X" % reader.exception.exception.code | 1764 print " code: %08X" % reader.exception.exception.code |
| 1654 print " context:" | 1765 print " context:" |
| 1655 for r in CONTEXT_FOR_ARCH[reader.arch]: | 1766 for r in CONTEXT_FOR_ARCH[reader.arch]: |
| 1656 print " %s: %s" % (r, reader.FormatIntPtr(reader.Register(r))) | 1767 print " %s: %s" % (r, reader.FormatIntPtr(reader.Register(r))) |
| 1657 # TODO(vitalyr): decode eflags. | 1768 # TODO(vitalyr): decode eflags. |
| 1658 print " eflags: %s" % bin(reader.exception_context.eflags)[2:] | 1769 print " eflags: %s" % bin(reader.exception_context.eflags)[2:] |
| 1659 print | 1770 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
| |
| 1771 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.
| |
| 1772 for module in reader.module_list.modules: | |
| 1773 name = GetModuleName(reader, module) | |
| 1774 if name in KNOWN_MODULES: | |
| 1775 print " %s at %08X" % (name, module.base_of_image) | |
| 1776 reader.TryLoadSymbolsFor(name, module) | |
| 1660 | 1777 |
| 1661 stack_top = reader.ExceptionSP() | 1778 stack_top = reader.ExceptionSP() |
| 1662 stack_bottom = exception_thread.stack.start + \ | 1779 stack_bottom = exception_thread.stack.start + \ |
| 1663 exception_thread.stack.memory.data_size | 1780 exception_thread.stack.memory.data_size |
| 1664 stack_map = {reader.ExceptionIP(): -1} | 1781 stack_map = {reader.ExceptionIP(): -1} |
| 1665 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()): | 1782 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()): |
| 1666 maybe_address = reader.ReadUIntPtr(slot) | 1783 maybe_address = reader.ReadUIntPtr(slot) |
| 1667 if not maybe_address in stack_map: | 1784 if not maybe_address in stack_map: |
| 1668 stack_map[maybe_address] = slot | 1785 stack_map[maybe_address] = slot |
| 1669 heap = V8Heap(reader, stack_map) | 1786 heap = V8Heap(reader, stack_map) |
| 1670 | 1787 |
| 1671 print "Disassembly around exception.eip:" | 1788 print "Disassembly around exception.eip:" |
| 1789 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.
| |
| 1672 disasm_start = reader.ExceptionIP() - EIP_PROXIMITY | 1790 disasm_start = reader.ExceptionIP() - EIP_PROXIMITY |
| 1673 disasm_bytes = 2 * EIP_PROXIMITY | 1791 disasm_bytes = 2 * EIP_PROXIMITY |
| 1674 if (options.full): | 1792 if (options.full): |
| 1675 full_range = reader.FindRegion(reader.ExceptionIP()) | 1793 full_range = reader.FindRegion(reader.ExceptionIP()) |
| 1676 if full_range is not None: | 1794 if full_range is not None: |
| 1677 disasm_start = full_range[0] | 1795 disasm_start = full_range[0] |
| 1678 disasm_bytes = full_range[1] | 1796 disasm_bytes = full_range[1] |
| 1679 | 1797 |
| 1680 lines = reader.GetDisasmLines(disasm_start, disasm_bytes) | 1798 lines = reader.GetDisasmLines(disasm_start, disasm_bytes) |
| 1681 | 1799 |
| 1682 for line in lines: | 1800 for line in lines: |
| 1683 print FormatDisasmLine(disasm_start, heap, line) | 1801 print FormatDisasmLine(disasm_start, heap, line) |
| 1684 print | 1802 print |
| 1685 | 1803 |
| 1686 if heap is None: | 1804 if heap is None: |
| 1687 heap = V8Heap(reader, None) | 1805 heap = V8Heap(reader, None) |
| 1688 | 1806 |
| 1689 if options.full: | 1807 if options.full: |
| 1690 FullDump(reader, heap) | 1808 FullDump(reader, heap) |
| 1691 | 1809 |
| 1692 if options.shell: | 1810 if options.shell: |
| 1693 InspectionShell(reader, heap).cmdloop("type help to get help") | 1811 InspectionShell(reader, heap).cmdloop("type help to get help") |
| 1694 else: | 1812 else: |
| 1695 if reader.exception is not None: | 1813 if reader.exception is not None: |
| 1696 print "Annotated stack (from exception.esp to bottom):" | 1814 print "Annotated stack (from exception.esp to bottom):" |
| 1697 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()): | 1815 for slot in xrange(stack_top, stack_bottom, reader.PointerSize()): |
| 1698 maybe_address = reader.ReadUIntPtr(slot) | 1816 maybe_address = reader.ReadUIntPtr(slot) |
| 1699 heap_object = heap.FindObject(maybe_address) | 1817 heap_object = heap.FindObject(maybe_address) |
| 1700 print "%s: %s" % (reader.FormatIntPtr(slot), | 1818 maybe_symbol = reader.FindSymbol(maybe_address) |
| 1701 reader.FormatIntPtr(maybe_address)) | 1819 if maybe_symbol is None: |
| 1820 maybe_symbol = "" | |
| 1821 | |
| 1822 print "%s: %s %s" % (reader.FormatIntPtr(slot), | |
| 1823 reader.FormatIntPtr(maybe_address), | |
| 1824 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.
| |
| 1702 if heap_object: | 1825 if heap_object: |
| 1703 heap_object.Print(Printer()) | 1826 heap_object.Print(Printer()) |
| 1704 print | 1827 print |
| 1705 | 1828 |
| 1706 reader.Dispose() | 1829 reader.Dispose() |
| 1707 | 1830 |
| 1708 | 1831 |
| 1709 if __name__ == "__main__": | 1832 if __name__ == "__main__": |
| 1710 parser = optparse.OptionParser(USAGE) | 1833 parser = optparse.OptionParser(USAGE) |
| 1711 parser.add_option("-s", "--shell", dest="shell", action="store_true", | 1834 parser.add_option("-s", "--shell", dest="shell", action="store_true", |
| 1712 help="start an interactive inspector shell") | 1835 help="start an interactive inspector shell") |
| 1713 parser.add_option("-f", "--full", dest="full", action="store_true", | 1836 parser.add_option("-f", "--full", dest="full", action="store_true", |
| 1714 help="dump all information contained in the minidump") | 1837 help="dump all information contained in the minidump") |
| 1838 parser.add_option("--symdir", dest="symdir", default=".", | |
| 1839 help="directory containing *.pdb.sym file with symbols") | |
| 1715 options, args = parser.parse_args() | 1840 options, args = parser.parse_args() |
| 1716 if len(args) != 1: | 1841 if len(args) != 1: |
| 1717 parser.print_help() | 1842 parser.print_help() |
| 1718 sys.exit(1) | 1843 sys.exit(1) |
| 1719 AnalyzeMinidump(options, args[0]) | 1844 AnalyzeMinidump(options, args[0]) |
| OLD | NEW |