 Chromium Code Reviews
 Chromium Code Reviews Issue 194573005:
  Web page front-end for grokdump.  (Closed) 
  Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
    
  
    Issue 194573005:
  Web page front-end for grokdump.  (Closed) 
  Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge| 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 10 matching lines...) Expand all Loading... | |
| 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 bisect | 
| 31 import cgi | |
| 31 import cmd | 32 import cmd | 
| 32 import codecs | 33 import codecs | 
| 33 import ctypes | 34 import ctypes | 
| 34 import datetime | 35 import datetime | 
| 35 import disasm | 36 import disasm | 
| 36 import mmap | 37 import mmap | 
| 37 import optparse | 38 import optparse | 
| 38 import os | 39 import os | 
| 39 import re | 40 import re | 
| 40 import struct | 41 import struct | 
| 41 import sys | 42 import sys | 
| 43 import time | |
| 42 import types | 44 import types | 
| 45 import urllib | |
| 43 import v8heapconst | 46 import v8heapconst | 
| 47 import webbrowser | |
| 48 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer | |
| 49 from urlparse import urlparse, parse_qs | |
| 50 | |
| 51 PORT_NUMBER = 8081 | |
| 52 | |
| 44 | 53 | 
| 45 USAGE="""usage: %prog [OPTIONS] [DUMP-FILE] | 54 USAGE="""usage: %prog [OPTIONS] [DUMP-FILE] | 
| 46 | 55 | 
| 47 Minidump analyzer. | 56 Minidump analyzer. | 
| 48 | 57 | 
| 49 Shows the processor state at the point of exception including the | 58 Shows the processor state at the point of exception including the | 
| 50 stack of the active thread and the referenced objects in the V8 | 59 stack of the active thread and the referenced objects in the V8 | 
| 51 heap. Code objects are disassembled and the addresses linked from the | 60 heap. Code objects are disassembled and the addresses linked from the | 
| 52 stack (e.g. pushed return addresses) are marked with "=>". | 61 stack (e.g. pushed return addresses) are marked with "=>". | 
| 53 | 62 | 
| (...skipping 640 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 694 def FindWord(self, word, alignment=0): | 703 def FindWord(self, word, alignment=0): | 
| 695 def search_inside_region(reader, start, size, location): | 704 def search_inside_region(reader, start, size, location): | 
| 696 location = (location + alignment) & ~alignment | 705 location = (location + alignment) & ~alignment | 
| 697 for loc in xrange(location, location + size - self.PointerSize()): | 706 for loc in xrange(location, location + size - self.PointerSize()): | 
| 698 if reader._ReadWord(loc) == word: | 707 if reader._ReadWord(loc) == word: | 
| 699 slot = start + (loc - location) | 708 slot = start + (loc - location) | 
| 700 print "%s: %s" % (reader.FormatIntPtr(slot), | 709 print "%s: %s" % (reader.FormatIntPtr(slot), | 
| 701 reader.FormatIntPtr(word)) | 710 reader.FormatIntPtr(word)) | 
| 702 self.ForEachMemoryRegion(search_inside_region) | 711 self.ForEachMemoryRegion(search_inside_region) | 
| 703 | 712 | 
| 713 def FindWordList(self, word): | |
| 714 aligned_res = [ ] | |
| 
Jakob Kummerow
2014/03/24 15:21:32
nit: the style guide doesn't like spaces inside em
 
Jarin
2014/03/26 10:54:28
Done.
 | |
| 715 unaligned_res = [ ] | |
| 716 def search_inside_region(reader, start, size, location): | |
| 717 for loc in xrange(location, location + size - self.PointerSize()): | |
| 718 if reader._ReadWord(loc) == word: | |
| 719 slot = start + (loc - location) | |
| 720 if slot % self.PointerSize() == 0: | |
| 721 aligned_res.append(slot) | |
| 722 else: | |
| 723 unaligned_res.append(slot) | |
| 724 self.ForEachMemoryRegion(search_inside_region) | |
| 725 return (aligned_res, unaligned_res) | |
| 726 | |
| 704 def FindLocation(self, address): | 727 def FindLocation(self, address): | 
| 705 offset = 0 | 728 offset = 0 | 
| 706 if self.memory_list64 is not None: | 729 if self.memory_list64 is not None: | 
| 707 for r in self.memory_list64.ranges: | 730 for r in self.memory_list64.ranges: | 
| 708 if r.start <= address < r.start + r.size: | 731 if r.start <= address < r.start + r.size: | 
| 709 return self.memory_list64.base_rva + offset + address - r.start | 732 return self.memory_list64.base_rva + offset + address - r.start | 
| 710 offset += r.size | 733 offset += r.size | 
| 711 if self.memory_list is not None: | 734 if self.memory_list is not None: | 
| 712 for r in self.memory_list.ranges: | 735 for r in self.memory_list.ranges: | 
| 713 if r.start <= address < r.start + r.memory.data_size: | 736 if r.start <= address < r.start + r.memory.data_size: | 
| (...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 923 def __str__(self): | 946 def __str__(self): | 
| 924 return "HeapObject(%s, %s)" % (self.heap.reader.FormatIntPtr(self.address), | 947 return "HeapObject(%s, %s)" % (self.heap.reader.FormatIntPtr(self.address), | 
| 925 INSTANCE_TYPES[self.map.instance_type]) | 948 INSTANCE_TYPES[self.map.instance_type]) | 
| 926 | 949 | 
| 927 def ObjectField(self, offset): | 950 def ObjectField(self, offset): | 
| 928 field_value = self.heap.reader.ReadUIntPtr(self.address + offset) | 951 field_value = self.heap.reader.ReadUIntPtr(self.address + offset) | 
| 929 return self.heap.FindObjectOrSmi(field_value) | 952 return self.heap.FindObjectOrSmi(field_value) | 
| 930 | 953 | 
| 931 def SmiField(self, offset): | 954 def SmiField(self, offset): | 
| 932 field_value = self.heap.reader.ReadUIntPtr(self.address + offset) | 955 field_value = self.heap.reader.ReadUIntPtr(self.address + offset) | 
| 933 assert (field_value & 1) == 0 | 956 if (field_value & 1) == 0: | 
| 934 return field_value / 2 | 957 return field_value / 2 | 
| 958 return None | |
| 935 | 959 | 
| 936 | 960 | 
| 937 class Map(HeapObject): | 961 class Map(HeapObject): | 
| 938 def Decode(self, offset, size, value): | 962 def Decode(self, offset, size, value): | 
| 939 return (value >> offset) & ((1 << size) - 1) | 963 return (value >> offset) & ((1 << size) - 1) | 
| 940 | 964 | 
| 941 # Instance Sizes | 965 # Instance Sizes | 
| 942 def InstanceSizesOffset(self): | 966 def InstanceSizesOffset(self): | 
| 943 return self.heap.PointerSize() | 967 return self.heap.PointerSize() | 
| 944 | 968 | 
| (...skipping 396 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1341 return "JSFunction(%s, %s)" % \ | 1365 return "JSFunction(%s, %s)" % \ | 
| 1342 (self.heap.reader.FormatIntPtr(self.address), inferred_name) | 1366 (self.heap.reader.FormatIntPtr(self.address), inferred_name) | 
| 1343 | 1367 | 
| 1344 def _GetSource(self): | 1368 def _GetSource(self): | 
| 1345 source = "?source?" | 1369 source = "?source?" | 
| 1346 start = self.shared.start_position | 1370 start = self.shared.start_position | 
| 1347 end = self.shared.end_position | 1371 end = self.shared.end_position | 
| 1348 if not self.shared.script.Is(Script): return source | 1372 if not self.shared.script.Is(Script): return source | 
| 1349 script_source = self.shared.script.source | 1373 script_source = self.shared.script.source | 
| 1350 if not script_source.Is(String): return source | 1374 if not script_source.Is(String): return source | 
| 1351 return script_source.GetChars()[start:end] | 1375 if start and end: | 
| 1376 source = script_source.GetChars()[start:end] | |
| 1377 return source | |
| 1352 | 1378 | 
| 1353 | 1379 | 
| 1354 class SharedFunctionInfo(HeapObject): | 1380 class SharedFunctionInfo(HeapObject): | 
| 1355 def CodeOffset(self): | 1381 def CodeOffset(self): | 
| 1356 return 2 * self.heap.PointerSize() | 1382 return 2 * self.heap.PointerSize() | 
| 1357 | 1383 | 
| 1358 def ScriptOffset(self): | 1384 def ScriptOffset(self): | 
| 1359 return 7 * self.heap.PointerSize() | 1385 return 7 * self.heap.PointerSize() | 
| 1360 | 1386 | 
| 1361 def InferredNameOffset(self): | 1387 def InferredNameOffset(self): | 
| (...skipping 13 matching lines...) Expand all Loading... | |
| 1375 if heap.PointerSize() == 8: | 1401 if heap.PointerSize() == 8: | 
| 1376 start_position_and_type = \ | 1402 start_position_and_type = \ | 
| 1377 heap.reader.ReadU32(self.StartPositionAndTypeOffset()) | 1403 heap.reader.ReadU32(self.StartPositionAndTypeOffset()) | 
| 1378 self.start_position = start_position_and_type >> 2 | 1404 self.start_position = start_position_and_type >> 2 | 
| 1379 pseudo_smi_end_position = \ | 1405 pseudo_smi_end_position = \ | 
| 1380 heap.reader.ReadU32(self.EndPositionOffset()) | 1406 heap.reader.ReadU32(self.EndPositionOffset()) | 
| 1381 self.end_position = pseudo_smi_end_position >> 2 | 1407 self.end_position = pseudo_smi_end_position >> 2 | 
| 1382 else: | 1408 else: | 
| 1383 start_position_and_type = \ | 1409 start_position_and_type = \ | 
| 1384 self.SmiField(self.StartPositionAndTypeOffset()) | 1410 self.SmiField(self.StartPositionAndTypeOffset()) | 
| 1385 self.start_position = start_position_and_type >> 2 | 1411 if start_position_and_type: | 
| 1412 self.start_position = start_position_and_type >> 2 | |
| 1413 else: | |
| 1414 self.start_position = None | |
| 1386 self.end_position = \ | 1415 self.end_position = \ | 
| 1387 self.SmiField(self.EndPositionOffset()) | 1416 self.SmiField(self.EndPositionOffset()) | 
| 1388 | 1417 | 
| 1389 | 1418 | 
| 1390 class Script(HeapObject): | 1419 class Script(HeapObject): | 
| 1391 def SourceOffset(self): | 1420 def SourceOffset(self): | 
| 1392 return self.heap.PointerSize() | 1421 return self.heap.PointerSize() | 
| 1393 | 1422 | 
| 1394 def NameOffset(self): | 1423 def NameOffset(self): | 
| 1395 return self.SourceOffset() + self.heap.PointerSize() | 1424 return self.SourceOffset() + self.heap.PointerSize() | 
| (...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1553 | 1582 | 
| 1554 class KnownMap(HeapObject): | 1583 class KnownMap(HeapObject): | 
| 1555 def __init__(self, heap, known_name, instance_type): | 1584 def __init__(self, heap, known_name, instance_type): | 
| 1556 HeapObject.__init__(self, heap, None, None) | 1585 HeapObject.__init__(self, heap, None, None) | 
| 1557 self.instance_type = instance_type | 1586 self.instance_type = instance_type | 
| 1558 self.known_name = known_name | 1587 self.known_name = known_name | 
| 1559 | 1588 | 
| 1560 def __str__(self): | 1589 def __str__(self): | 
| 1561 return "<%s>" % self.known_name | 1590 return "<%s>" % self.known_name | 
| 1562 | 1591 | 
| 1592 COMMENT_RE = re.compile(r"^C (0x[0-9a-fA-F]+) (.*)$") | |
| 
Jakob Kummerow
2014/03/24 15:21:32
style nit: two empty lines between top-level thing
 
Jarin
2014/03/26 10:54:28
Done.
 | |
| 1593 PAGEADDRESS_RE = re.compile( | |
| 1594 r"^P (mappage|pointerpage|datapage) (0x[0-9a-fA-F]+)$") | |
| 1595 | |
| 1596 class InspectionInfo(object): | |
| 1597 def __init__(self, minidump_name, reader): | |
| 1598 self.cmt_file = minidump_name + ".comments" | |
| 1599 self.address_comments = { } | |
| 1600 self.page_address = { } | |
| 1601 print "Opening comment file '%s'" % self.cmt_file | |
| 1602 try: | |
| 1603 f = open(self.cmt_file, "r") | |
| 
Jakob Kummerow
2014/03/24 15:21:32
The pythonic way to express open/close sequences i
 
Jarin
2014/03/26 10:54:28
Done.
 | |
| 1604 lines = f.readlines() | |
| 1605 f.close() | |
| 1606 | |
| 1607 for l in lines: | |
| 1608 m = COMMENT_RE.match(l) | |
| 1609 if m: | |
| 1610 self.address_comments[int(m.group(1), 0)] = m.group(2) | |
| 1611 m = PAGEADDRESS_RE.match(l) | |
| 1612 if m: | |
| 1613 self.page_address[m.group(1)] = int(m.group(2), 0) | |
| 1614 except IOError: | |
| 1615 print "No comments file, starting a new one" | |
| 1616 self.reader = reader | |
| 1617 self.color_addresses() | |
| 1618 return | |
| 1619 | |
| 1620 def get_page_address(self, page_kind): | |
| 1621 return self.page_address.get(page_kind, 0) | |
| 1622 | |
| 1623 def save_page_address(self, page_kind, address): | |
| 1624 f = open(self.cmt_file, "a") | |
| 
Jakob Kummerow
2014/03/24 15:21:32
again, prefer "with"
 
Jarin
2014/03/26 10:54:28
Done.
 | |
| 1625 f.write("P %s 0x%x\n" % (page_kind, address)) | |
| 1626 f.close() | |
| 1627 | |
| 1628 def color_addresses(self): | |
| 1629 self.styles = { } | |
| 
Jakob Kummerow
2014/03/24 15:21:32
nit: I have a weak preference to initialize all me
 
Jarin
2014/03/26 10:54:28
Done.
 | |
| 1630 | |
| 1631 # Color all stack addresses | |
| 
Jakob Kummerow
2014/03/24 15:21:32
nit: trailing full stop please
 
Jarin
2014/03/26 10:54:28
Done.
 | |
| 1632 exception_thread = self.reader.thread_map[self.reader.exception.thread_id] | |
| 1633 stack_top = self.reader.ExceptionSP() | |
| 1634 stack_bottom = exception_thread.stack.start + \ | |
| 1635 exception_thread.stack.memory.data_size | |
| 1636 frame_pointer = self.reader.ExceptionFP() | |
| 1637 self.styles[frame_pointer] = "frame" | |
| 1638 for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()): | |
| 1639 self.styles[slot] = "stackaddress" | |
| 1640 for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()): | |
| 1641 maybe_address = self.reader.ReadUIntPtr(slot) | |
| 1642 self.styles[maybe_address] = "stackval" | |
| 1643 if slot == frame_pointer: | |
| 1644 self.styles[slot] = "frame" | |
| 1645 frame_pointer = maybe_address | |
| 1646 self.styles[self.reader.ExceptionIP()] = "pc" | |
| 1647 | |
| 1648 def get_style_class(self, address): | |
| 1649 return self.styles.get(address, None) | |
| 1650 | |
| 1651 def get_style_class_string(self, address): | |
| 1652 style = self.get_style_class(address) | |
| 1653 if style != None: | |
| 1654 return " class=\"%s\" " % style | |
| 1655 else: | |
| 1656 return "" | |
| 1657 | |
| 1658 def set_comment(self, address, comment): | |
| 1659 self.address_comments[address] = comment | |
| 1660 f = open(self.cmt_file, "a") | |
| 
Jakob Kummerow
2014/03/24 15:21:32
with
 
Jarin
2014/03/26 10:54:28
Done.
 | |
| 1661 f.write("C 0x%x %s\n" % (address, comment)) | |
| 1662 f.close() | |
| 1663 | |
| 1664 def get_comment(self, address): | |
| 1665 return self.address_comments.get(address, "") | |
| 1666 | |
| 1563 | 1667 | 
| 1564 class InspectionPadawan(object): | 1668 class InspectionPadawan(object): | 
| 1565 """The padawan can improve annotations by sensing well-known objects.""" | 1669 """The padawan can improve annotations by sensing well-known objects.""" | 
| 1566 def __init__(self, reader, heap): | 1670 def __init__(self, reader, heap): | 
| 1567 self.reader = reader | 1671 self.reader = reader | 
| 1568 self.heap = heap | 1672 self.heap = heap | 
| 1569 self.known_first_map_page = 0 | 1673 self.known_first_map_page = 0 | 
| 1570 self.known_first_data_page = 0 | 1674 self.known_first_data_page = 0 | 
| 1571 self.known_first_pointer_page = 0 | 1675 self.known_first_pointer_page = 0 | 
| 1572 | 1676 | 
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1646 raise NotImplementedError | 1750 raise NotImplementedError | 
| 1647 | 1751 | 
| 1648 def PrintKnowledge(self): | 1752 def PrintKnowledge(self): | 
| 1649 print " known_first_map_page = %s\n"\ | 1753 print " known_first_map_page = %s\n"\ | 
| 1650 " known_first_data_page = %s\n"\ | 1754 " known_first_data_page = %s\n"\ | 
| 1651 " known_first_pointer_page = %s" % ( | 1755 " known_first_pointer_page = %s" % ( | 
| 1652 self.reader.FormatIntPtr(self.known_first_map_page), | 1756 self.reader.FormatIntPtr(self.known_first_map_page), | 
| 1653 self.reader.FormatIntPtr(self.known_first_data_page), | 1757 self.reader.FormatIntPtr(self.known_first_data_page), | 
| 1654 self.reader.FormatIntPtr(self.known_first_pointer_page)) | 1758 self.reader.FormatIntPtr(self.known_first_pointer_page)) | 
| 1655 | 1759 | 
| 1760 WEB_HEADER = """ | |
| 1761 <!DOCTYPE html> | |
| 1762 <html> | |
| 1763 <head> | |
| 1764 <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"> | |
| 1765 <style media="screen" type="text/css"> | |
| 1766 | |
| 1767 .code {{ | |
| 
Jakob Kummerow
2014/03/24 15:21:32
If you used %s-placeholders ("foo %s baz" % bar),
 
Jarin
2014/03/26 10:54:28
However, with %s you cannot reuse the same argumen
 | |
| 1768 font-family: monospace; | |
| 
Jakob Kummerow
2014/03/24 15:21:32
nit: funky indentation
 
Jarin
2014/03/26 10:54:28
Done.
 | |
| 1769 }} | |
| 1770 | |
| 1771 .dmptable {{ | |
| 1772 border-collapse : collapse; | |
| 1773 border-spacing : 0px; | |
| 1774 }} | |
| 1775 | |
| 1776 .codedump {{ | |
| 1777 border-collapse : collapse; | |
| 1778 border-spacing : 0px; | |
| 1779 }} | |
| 1780 | |
| 1781 .addrcomments {{ | |
| 1782 border : 0px; | |
| 1783 }} | |
| 1784 | |
| 1785 .register {{ | |
| 1786 padding-right : 1em; | |
| 1787 }} | |
| 1788 | |
| 1789 .header {{ | |
| 1790 clear : both; | |
| 1791 }} | |
| 1792 | |
| 1793 .header .navigation {{ | |
| 1794 float : left; | |
| 1795 }} | |
| 1796 | |
| 1797 .header .dumpname {{ | |
| 1798 float : right; | |
| 1799 }} | |
| 1800 | |
| 1801 tr.highlight-line {{ | |
| 1802 background-color : yellow; | |
| 1803 }} | |
| 1804 | |
| 1805 .highlight {{ | |
| 1806 background-color : magenta; | |
| 1807 }} | |
| 1808 | |
| 1809 tr.inexact-highlight-line {{ | |
| 1810 background-color : pink; | |
| 1811 }} | |
| 1812 | |
| 1813 input {{ | |
| 1814 background-color: inherit; | |
| 1815 border: 1px solid LightGray; | |
| 1816 }} | |
| 1817 | |
| 1818 .dumpcomments {{ | |
| 1819 border : 1px solid LightGray; | |
| 1820 width : 32em; | |
| 1821 }} | |
| 1822 | |
| 1823 .regions td {{ | |
| 1824 padding:0 15px 0 15px; | |
| 1825 }} | |
| 1826 | |
| 1827 .stackframe td {{ | |
| 1828 background-color : cyan; | |
| 1829 }} | |
| 1830 | |
| 1831 .pc {{ | |
| 1832 }} | |
| 
Jakob Kummerow
2014/03/24 15:21:32
Is this intentionally empty?
 
Jarin
2014/03/26 10:54:28
Removed.
 | |
| 1833 | |
| 1834 .stackaddress {{ | |
| 1835 background-color : LightGray; | |
| 1836 }} | |
| 1837 | |
| 1838 .stackval {{ | |
| 1839 background-color : LightCyan; | |
| 1840 }} | |
| 1841 | |
| 1842 .frame {{ | |
| 1843 background-color : cyan; | |
| 1844 }} | |
| 1845 | |
| 1846 .commentinput {{ | |
| 1847 width : 20em; | |
| 1848 }} | |
| 1849 | |
| 1850 a.nodump:visited {{ | |
| 1851 color : black; | |
| 1852 text-decoration : none; | |
| 1853 }} | |
| 1854 | |
| 1855 a.nodump:link {{ | |
| 1856 color : black; | |
| 1857 text-decoration : none; | |
| 1858 }} | |
| 1859 | |
| 1860 a:visited {{ | |
| 1861 color : blueviolet; | |
| 1862 }} | |
| 1863 | |
| 1864 a:link {{ | |
| 1865 color : blue; | |
| 1866 }} | |
| 1867 | |
| 1868 .disasmcmt {{ | |
| 1869 color : DarkGreen; | |
| 1870 }} | |
| 1871 | |
| 1872 </style> | |
| 1873 | |
| 1874 <script type="application/javascript"> | |
| 1875 | |
| 1876 var address_str = "address-"; | |
| 1877 var address_len = address_str.length; | |
| 1878 | |
| 1879 function cmt() {{ | |
| 1880 var s = event.srcElement.id; | |
| 1881 var index = s.indexOf(address_str); | |
| 1882 if (index >= 0) {{ | |
| 1883 send_comment(s.substring(index + address_len), event.srcElement.value); | |
| 1884 }} | |
| 1885 }} | |
| 1886 | |
| 1887 function send_comment(address, comment) {{ | |
| 1888 xmlhttp = new XMLHttpRequest(); | |
| 1889 address = encodeURIComponent(address) | |
| 1890 comment = encodeURIComponent(comment) | |
| 1891 xmlhttp.open("GET", | |
| 
Jakob Kummerow
2014/03/24 15:21:32
I think using GET to perform actions is considered
 
Jarin
2014/03/26 10:54:28
Yeah, I am leaving as is.
 | |
| 1892 "setcomment?{0}&address=" + address + | |
| 1893 "&comment=" + comment, true); | |
| 1894 xmlhttp.send(); | |
| 1895 }} | |
| 1896 | |
| 1897 var dump_str = "dump-"; | |
| 1898 var dump_len = dump_str.length; | |
| 1899 | |
| 1900 function dump_cmt() {{ | |
| 1901 var s = event.srcElement.id; | |
| 1902 var index = s.indexOf(dump_str); | |
| 1903 if (index >= 0) {{ | |
| 1904 send_dump_desc(s.substring(index + dump_len), event.srcElement.value); | |
| 1905 }} | |
| 1906 }} | |
| 1907 | |
| 1908 function send_dump_desc(name, desc) {{ | |
| 1909 xmlhttp = new XMLHttpRequest(); | |
| 1910 name = encodeURIComponent(name) | |
| 1911 desc = encodeURIComponent(desc) | |
| 1912 xmlhttp.open("GET", | |
| 1913 "setdumpdesc?dump=" + name + | |
| 1914 "&description=" + desc, true); | |
| 1915 xmlhttp.send(); | |
| 1916 }} | |
| 1917 | |
| 1918 function onpage(kind, address) {{ | |
| 1919 xmlhttp = new XMLHttpRequest(); | |
| 1920 kind = encodeURIComponent(kind) | |
| 1921 address = encodeURIComponent(address) | |
| 1922 xmlhttp.onreadystatechange = function() {{ | |
| 1923 if (xmlhttp.readyState==4 && xmlhttp.status==200) | |
| 1924 location.reload(true) | |
| 
Jakob Kummerow
2014/03/24 15:21:32
nit: {} around the block please
 
Jarin
2014/03/26 10:54:28
Done.
 | |
| 1925 }}; | |
| 1926 xmlhttp.open("GET", | |
| 1927 "setpageaddress?{0}&kind=" + kind + | |
| 1928 "&address=" + address); | |
| 1929 xmlhttp.send(); | |
| 1930 }} | |
| 1931 | |
| 1932 function main() {{ | |
| 
Jakob Kummerow
2014/03/24 15:21:32
looks like we don't need this urgently.
 
Jarin
2014/03/26 10:54:28
Removed.
 | |
| 1933 }} | |
| 1934 | |
| 1935 </script> | |
| 1936 | |
| 1937 <title>Dump {1}</title> | |
| 1938 </head> | |
| 1939 | |
| 1940 <body onload="main()"> | |
| 1941 <div class="header"> | |
| 1942 <form class="navigation" action="search.html"> | |
| 1943 <a href="summary.html?{0}">Context info</a>    | |
| 1944 <a href="info.html?{0}">Dump info</a>    | |
| 1945 <a href="modules.html?{0}">Modules</a>    | |
| 1946   | |
| 1947 <input type="search" name="val"> | |
| 1948 <input type="submit" name="search" value="Search"> | |
| 1949 <input type="hidden" name="dump" value="{1}"> | |
| 1950 </form> | |
| 1951 <form class="navigation" action="disasm.html#highlight"> | |
| 1952   | |
| 1953   | |
| 1954   | |
| 1955 <input type="search" name="val"> | |
| 1956 <input type="submit" name="disasm" value="Disasm"> | |
| 1957   | |
| 1958   | |
| 1959   | |
| 1960 <a href="dumps.html">Dumps...</a> | |
| 1961 </form> | |
| 1962 <!-- | |
| 
Jakob Kummerow
2014/03/24 15:21:32
nit: remove this if it's not needed
 
Jarin
2014/03/26 10:54:28
Done.
 | |
| 1963 <form class="dumpname" action="dump.html"> | |
| 1964 Dump id: | |
| 1965 <input type="search" name="dumpid"> | |
| 1966 <input type="submit" name="action" value="Go"> | |
| 1967 <input type="hidden" name="dump" value="{1}"> | |
| 1968 </form> | |
| 1969 --> | |
| 1970 </div> | |
| 1971 <br> | |
| 1972 <hr> | |
| 1973 """ | |
| 1974 | |
| 1975 WEB_FOOTER = """ | |
| 1976 </body> | |
| 1977 </html> | |
| 1978 """ | |
| 1979 | |
| 1980 class WebParameterError(Exception): | |
| 
Jakob Kummerow
2014/03/24 15:21:32
again, two empty lines before and after top-level
 
Jarin
2014/03/26 10:54:28
Done.
 | |
| 1981 def __init__(self, message): | |
| 1982 Exception.__init__(self, message) | |
| 1983 | |
| 1984 class InspectionWebHandler(BaseHTTPRequestHandler): | |
| 1985 def fmt(self, qc): | |
| 
Jakob Kummerow
2014/03/24 15:21:32
naming nit: abbreviations are generally frowned up
 
Jarin
2014/03/26 10:54:28
Done.
 | |
| 1986 name = qc.get("dump", [None])[0] | |
| 1987 return self.server.get_dump_formatter(name) | |
| 1988 | |
| 1989 def send_success_html_headers(self): | |
| 1990 self.send_response(200) | |
| 1991 self.send_header("Cache-Control", "no-cache, no-store, must-revalidate") | |
| 1992 self.send_header("Pragma", "no-cache") | |
| 1993 self.send_header("Expires", "0") | |
| 1994 self.send_header('Content-type','text/html') | |
| 1995 self.end_headers() | |
| 1996 return | |
| 1997 | |
| 1998 def do_GET(self): | |
| 1999 try: | |
| 2000 print "GET!" | |
| 
Jakob Kummerow
2014/03/24 15:21:32
Looks like a debugging aid. Is this still needed?
 
Jarin
2014/03/26 10:54:28
Removed.
 | |
| 2001 parsedurl = urlparse(self.path) | |
| 2002 qc = parse_qs(parsedurl.query) | |
| 2003 if parsedurl.path == "/dumps.html": | |
| 2004 self.send_success_html_headers() | |
| 2005 self.server.output_dumps(self.wfile) | |
| 2006 return | |
| 2007 if parsedurl.path == "/summary.html": | |
| 
Jakob Kummerow
2014/03/24 15:21:32
s/if/elif/ like below? In turn you could omit all
 | |
| 2008 self.send_success_html_headers() | |
| 2009 self.fmt(qc).output_summary(self.wfile) | |
| 2010 return | |
| 2011 if parsedurl.path == "/info.html": | |
| 2012 self.send_success_html_headers() | |
| 2013 self.fmt(qc).output_info(self.wfile) | |
| 2014 return | |
| 2015 elif parsedurl.path == "/modules.html": | |
| 2016 self.send_success_html_headers() | |
| 2017 self.server.formatter.output_modules(self.wfile) | |
| 2018 return | |
| 2019 elif parsedurl.path == "/search.html": | |
| 2020 address = qc.get("val", []) | |
| 2021 if len(address) != 1: | |
| 2022 self.send_error(404, "Invalid params") | |
| 2023 return | |
| 2024 if qc.get("search", None) != None: | |
| 2025 self.send_success_html_headers() | |
| 2026 self.fmt(qc).output_search_res(self.wfile, address[0]) | |
| 2027 return | |
| 2028 elif parsedurl.path == "/disasm.html": | |
| 2029 address = qc.get("val", []) | |
| 2030 exact = qc.get("exact", ["on"]) | |
| 2031 if len(address) != 1: | |
| 2032 self.send_error(404, "Invalid params") | |
| 2033 return | |
| 2034 self.send_success_html_headers() | |
| 2035 self.fmt(qc).output_disasm(self.wfile, address[0], exact[0]) | |
| 2036 return | |
| 2037 elif parsedurl.path == "/data.html": | |
| 2038 address = qc.get("val", []) | |
| 2039 datakind = qc.get("type", ["address"]) | |
| 2040 if len(address) == 1 and len(datakind) == 1: | |
| 2041 self.send_success_html_headers() | |
| 2042 self.fmt(qc).output_data(self.wfile, address[0], datakind[0]) | |
| 2043 else: | |
| 2044 self.send_error(404,'Invalid params') | |
| 2045 return | |
| 2046 elif parsedurl.path == "/setdumpdesc": | |
| 2047 name = qc.get("dump", [""]) | |
| 2048 description = qc.get("description", [""]) | |
| 2049 | |
| 2050 if len(name) == 1 and len(description) == 1: | |
| 2051 name = name[0] | |
| 2052 description = description[0] | |
| 2053 if self.server.set_dump_desc(name, description): | |
| 2054 self.send_success_html_headers() | |
| 2055 self.wfile.write("OK") | |
| 2056 return | |
| 2057 self.send_error(404,'Invalid params') | |
| 
Jakob Kummerow
2014/03/24 15:21:32
again, I'd prefer if+else over if+return
 
Jarin
2014/03/26 10:54:28
Done.
 | |
| 2058 return | |
| 2059 | |
| 2060 elif parsedurl.path == "/setcomment": | |
| 2061 address = qc.get("address", []) | |
| 2062 comment = qc.get("comment", [""]) | |
| 2063 | |
| 2064 if len(address) == 1 and len(comment) == 1: | |
| 2065 address = address[0] | |
| 2066 comment = comment[0] | |
| 2067 self.fmt(qc).set_comment(address, comment) | |
| 2068 self.send_success_html_headers() | |
| 2069 self.wfile.write("OK") | |
| 2070 else: | |
| 2071 self.send_error(404,'Invalid params') | |
| 2072 return | |
| 2073 | |
| 2074 elif parsedurl.path == "/setpageaddress": | |
| 2075 kind = qc.get("kind", []) | |
| 2076 address = qc.get("address", [""]) | |
| 2077 | |
| 2078 if len(kind) == 1 and len(address) == 1: | |
| 2079 kind = kind[0] | |
| 2080 address = address[0] | |
| 2081 self.fmt(qc).set_page_address(kind, address) | |
| 2082 self.send_success_html_headers() | |
| 2083 self.wfile.write("OK") | |
| 2084 else: | |
| 2085 self.send_error(404,'Invalid params') | |
| 2086 return | |
| 2087 | |
| 2088 else: | |
| 2089 self.send_error(404,'File Not Found: %s' % self.path) | |
| 2090 | |
| 2091 except IOError: | |
| 2092 self.send_error(404,'File Not Found: %s' % self.path) | |
| 2093 | |
| 2094 except WebParameterError as e: | |
| 2095 self.send_error(404, 'Web parameter error: %s' % e.message) | |
| 2096 | |
| 2097 HTML_REG_FORMAT = "<span class=\"register\"><b>%s</b>: %s</span>\n" | |
| 2098 | |
| 2099 class InspectionWebFormatter(object): | |
| 2100 CONTEXT_FULL = 0 | |
| 2101 CONTEXT_SHORT = 1 | |
| 2102 | |
| 2103 def __init__(self, options, minidump_name, server): | |
| 2104 self.dumpfilename = os.path.split(minidump_name)[1] | |
| 2105 self.encfilename = urllib.urlencode({ 'dump' : self.dumpfilename }) | |
| 2106 self.reader = MinidumpReader(options, minidump_name) | |
| 2107 self.server = server | |
| 2108 | |
| 2109 # Set up the heap | |
| 2110 exception_thread = self.reader.thread_map[self.reader.exception.thread_id] | |
| 2111 stack_top = self.reader.ExceptionSP() | |
| 2112 stack_bottom = exception_thread.stack.start + \ | |
| 
Jakob Kummerow
2014/03/24 15:21:32
nit: the style guide prefers () over trailing \, i
 | |
| 2113 exception_thread.stack.memory.data_size | |
| 2114 stack_map = {self.reader.ExceptionIP(): -1} | |
| 2115 for slot in xrange(stack_top, stack_bottom, self.reader.PointerSize()): | |
| 2116 maybe_address = self.reader.ReadUIntPtr(slot) | |
| 2117 if not maybe_address in stack_map: | |
| 2118 stack_map[maybe_address] = slot | |
| 2119 self.heap = V8Heap(self.reader, stack_map) | |
| 2120 | |
| 2121 self.padawan = InspectionPadawan(self.reader, self.heap) | |
| 2122 self.comments = InspectionInfo(minidump_name, self.reader) | |
| 2123 self.padawan.known_first_data_page = \ | |
| 2124 self.comments.get_page_address("datapage") | |
| 2125 self.padawan.known_first_map_page = \ | |
| 2126 self.comments.get_page_address("mappage") | |
| 2127 self.padawan.known_first_pointer_page = \ | |
| 2128 self.comments.get_page_address("pointerpage") | |
| 2129 | |
| 2130 def set_comment(self, straddress, comment): | |
| 2131 try: | |
| 2132 print "Adding comment to dump '%s': %s -> '%s'" % ( | |
| 
Jakob Kummerow
2014/03/24 15:21:32
Still needed? If you don't want to delete it, cons
 
Jarin
2014/03/26 10:54:28
Removed.
 | |
| 2133 self.dumpfilename, straddress, comment) | |
| 2134 address = int(straddress, 0) | |
| 2135 self.comments.set_comment(address, comment) | |
| 2136 except ValueError: | |
| 2137 print "Invalid address" | |
| 2138 | |
| 2139 def set_page_address(self, kind, straddress): | |
| 2140 try: | |
| 2141 print "Adding page annotation to dump '%s': %s -> %s" % ( | |
| 
Jakob Kummerow
2014/03/24 15:21:32
still needed?
 
Jarin
2014/03/26 10:54:28
Removed.
 | |
| 2142 self.dumpfilename, kind, straddress) | |
| 2143 address = int(straddress, 0) | |
| 2144 if kind == "datapage": | |
| 2145 self.padawan.known_first_data_page = address | |
| 2146 elif kind == "mappage": | |
| 2147 self.padawan.known_first_map_page = address | |
| 2148 elif kind == "pointerpage": | |
| 2149 self.padawan.known_first_pointer_page = address | |
| 2150 self.comments.save_page_address(kind, address) | |
| 2151 except ValueError: | |
| 2152 print "Invalid address" | |
| 2153 | |
| 2154 def td_from_address(self, f, address): | |
| 2155 f.write("<td %s>" % self.comments.get_style_class_string(address)) | |
| 2156 | |
| 2157 def format_address(self, maybeaddress, straddress = None): | |
| 2158 if maybeaddress == None: | |
| 
Jakob Kummerow
2014/03/24 15:21:32
nit: the style guide wants "is None" instead of "=
 | |
| 2159 return "not in dump" | |
| 2160 else: | |
| 2161 if straddress == None: | |
| 2162 straddress = "0x" + self.reader.FormatIntPtr(maybeaddress) | |
| 2163 style_class = "" | |
| 2164 if not self.reader.IsValidAddress(maybeaddress): | |
| 2165 style_class = " class=\"nodump\"" | |
| 2166 return "<a %s href=\"search.html?%s&val=%s&search=yes\">%s</a>" % ( | |
| 2167 style_class, self.encfilename, straddress, straddress) | |
| 2168 | |
| 2169 def output_header(self, f): | |
| 2170 f.write(WEB_HEADER.format(self.encfilename, cgi.escape(self.dumpfilename))) | |
| 2171 | |
| 2172 def output_footer(self, f): | |
| 2173 f.write(WEB_FOOTER) | |
| 2174 | |
| 2175 MAX_CONTEXT_STACK = 4096 | |
| 2176 | |
| 2177 def output_summary(self, f): | |
| 2178 self.output_header(f) | |
| 2179 f.write('<div class="code">') | |
| 2180 self.output_context(f, InspectionWebFormatter.CONTEXT_SHORT) | |
| 2181 self.output_disasm_pc(f) | |
| 2182 | |
| 2183 # Output stack | |
| 2184 exception_thread = self.reader.thread_map[self.reader.exception.thread_id] | |
| 2185 stack_bottom = exception_thread.stack.start + \ | |
| 2186 min(exception_thread.stack.memory.data_size, self.MAX_CONTEXT_STACK) | |
| 2187 stack_top = self.reader.ExceptionSP() | |
| 2188 self.output_words(f, stack_top - 16, stack_bottom, stack_top, "Stack") | |
| 2189 | |
| 2190 f.write('</div>') | |
| 2191 self.output_footer(f) | |
| 2192 return | |
| 2193 | |
| 2194 def output_info(self, f): | |
| 2195 self.output_header(f) | |
| 2196 f.write("<h3>Dump info</h3>\n") | |
| 2197 f.write("Description: ") | |
| 2198 self.server.output_dump_desc_field(f, self.dumpfilename) | |
| 2199 f.write("<br>\n") | |
| 2200 f.write("Filename: ") | |
| 2201 f.write("<span class=\"code\">%s</span><br>\n" % (self.dumpfilename)) | |
| 2202 dt = datetime.datetime.fromtimestamp(self.reader.header.time_date_stampt) | |
| 2203 f.write("Timestamp: %s<br>\n" % dt.strftime('%Y-%m-%d %H:%M:%S')) | |
| 2204 self.output_context(f, InspectionWebFormatter.CONTEXT_FULL) | |
| 2205 self.output_address_ranges(f) | |
| 2206 self.output_footer(f) | |
| 2207 return | |
| 2208 | |
| 2209 def output_address_ranges(self, f): | |
| 2210 regions = { } | |
| 2211 def print_region(reader, start, size, location): | |
| 2212 regions[start] = size | |
| 2213 self.reader.ForEachMemoryRegion(print_region) | |
| 2214 f.write("<h3>Available memory regions</h3>\n") | |
| 2215 f.write('<div class="code">') | |
| 2216 f.write("<table class=\"regions\">\n") | |
| 2217 f.write("<thead><tr>") | |
| 2218 f.write("<th>Start address</th>") | |
| 2219 f.write("<th>End address</th>") | |
| 2220 f.write("<th>Number of bytes</th>") | |
| 2221 f.write("</tr></thead>") | |
| 2222 f.write("</thead>\n") | |
| 2223 for start in sorted(regions): | |
| 2224 size = regions[start] | |
| 2225 f.write("<tr>") | |
| 2226 f.write("<td>%s</td>" % self.format_address(start)) | |
| 2227 f.write("<td> %s</td>" % self.format_address(start + size)) | |
| 2228 f.write("<td> %d</td>" % size) | |
| 2229 f.write("</tr>\n") | |
| 2230 f.write("</table>\n") | |
| 2231 f.write('</div>') | |
| 2232 return | |
| 2233 | |
| 2234 def output_module_details(self, f, module): | |
| 2235 f.write("<b>%s</b>" % GetModuleName(self.reader, module)) | |
| 2236 file_version = GetVersionString(module.version_info.dwFileVersionMS, | |
| 2237 module.version_info.dwFileVersionLS) | |
| 2238 product_version = GetVersionString(module.version_info.dwProductVersionMS, | |
| 2239 module.version_info.dwProductVersionLS) | |
| 2240 f.write("<br>  \n") | |
| 2241 f.write("base: %s" % self.reader.FormatIntPtr(module.base_of_image)) | |
| 2242 f.write("<br>  \n") | |
| 2243 f.write(" end: %s" % self.reader.FormatIntPtr(module.base_of_image + | |
| 2244 module.size_of_image)) | |
| 2245 f.write("<br>  \n") | |
| 2246 f.write(" file version: %s" % file_version) | |
| 2247 f.write("<br>  \n") | |
| 2248 f.write(" product version: %s" % product_version) | |
| 2249 f.write("<br>  \n") | |
| 2250 time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp) | |
| 2251 f.write(" timestamp: %s" % time_date_stamp) | |
| 2252 f.write("<br>\n"); | |
| 2253 | |
| 2254 def output_modules(self, f): | |
| 2255 self.output_header(f) | |
| 2256 f.write('<div class="code">') | |
| 2257 for module in self.reader.module_list.modules: | |
| 2258 self.output_module_details(f, module) | |
| 2259 f.write("</div>") | |
| 2260 self.output_footer(f) | |
| 2261 return | |
| 2262 | |
| 2263 def output_context(self, f, details): | |
| 2264 exception_thread = self.reader.thread_map[self.reader.exception.thread_id] | |
| 2265 f.write("<h3>Exception context</h3>") | |
| 2266 f.write('<div class="code">\n') | |
| 2267 f.write("Thread id: %d" % exception_thread.id) | |
| 2268 f.write("   Exception code: %08X\n" % ( | |
| 
Jakob Kummerow
2014/03/24 15:21:32
nit: I'd break as:
    f.write("   Excep
 
Jarin
2014/03/26 10:54:28
Done.
 | |
| 2269 self.reader.exception.exception.code)) | |
| 2270 if details == InspectionWebFormatter.CONTEXT_FULL: | |
| 2271 if self.reader.exception.exception.parameter_count > 0: | |
| 2272 f.write("   Exception parameters: \n") | |
| 2273 for i in xrange(0, self.reader.exception.exception.parameter_count): | |
| 2274 f.write("%08x" % self.reader.exception.exception.information[i]) | |
| 2275 f.write("<br><br>\n") | |
| 2276 | |
| 2277 for r in CONTEXT_FOR_ARCH[self.reader.arch]: | |
| 2278 f.write(HTML_REG_FORMAT % ( | |
| 2279 r, self.format_address(self.reader.Register(r)))) | |
| 2280 # TODO(vitalyr): decode eflags. | |
| 2281 if self.reader.arch == MD_CPU_ARCHITECTURE_ARM: | |
| 2282 f.write("<b>cpsr</b>: %s" % bin(self.reader.exception_context.cpsr)[2:]) | |
| 2283 else: | |
| 2284 f.write("<b>eflags</b>: %s" % ( | |
| 2285 bin(self.reader.exception_context.eflags)[2:])) | |
| 2286 f.write('</div>\n') | |
| 2287 return | |
| 2288 | |
| 2289 def align_down(self, a, size): | |
| 2290 alignment_correction = a % size | |
| 2291 return a - alignment_correction | |
| 2292 | |
| 2293 def align_up(self, a, size): | |
| 2294 alignment_correction = (size - 1) - ( | |
| 2295 (a + size - 1) % size) | |
| 
Jakob Kummerow
2014/03/24 15:21:32
nit: fits on previous line?
 
Jarin
2014/03/26 10:54:28
Done.
 | |
| 2296 return a + alignment_correction | |
| 2297 | |
| 2298 | |
| 2299 def format_object(self, address): | |
| 
Jakob Kummerow
2014/03/24 15:21:32
nit: one empty line is enough between non-toplevel
 
Jarin
2014/03/26 10:54:28
Done.
 | |
| 2300 heap_object = self.padawan.SenseObject(address) | |
| 2301 return cgi.escape(str(heap_object or "")) | |
| 2302 | |
| 2303 | |
| 2304 def output_data(self, f, straddress, datakind): | |
| 2305 try: | |
| 2306 self.output_header(f) | |
| 2307 address = int(straddress, 0) | |
| 2308 if not self.reader.IsValidAddress(address): | |
| 2309 f.write("<h3>Address 0x%x not found in the dump.</h3>" % address) | |
| 2310 return | |
| 2311 region = self.reader.FindRegion(address) | |
| 2312 if datakind == "address": | |
| 2313 self.output_words(f, region[0], region[0] + region[1], address, "Dump") | |
| 2314 elif datakind == "ascii": | |
| 2315 self.output_ascii(f, region[0], region[0] + region[1], address) | |
| 2316 self.output_footer(f) | |
| 2317 | |
| 2318 except ValueError: | |
| 2319 f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress) | |
| 2320 return | |
| 2321 | |
| 2322 | |
| 2323 def output_words(self, f, start_address, end_address, \ | |
| 
Jakob Kummerow
2014/03/24 15:21:32
nit: trailing \ is not necessary
 
Jarin
2014/03/26 10:54:28
Done.
 | |
| 2324 highlight_address, desc): | |
| 2325 region = self.reader.FindRegion(highlight_address) | |
| 2326 if region == None: | |
| 2327 f.write("<h3>Address 0x%x not found in the dump.</h3>\n" % ( | |
| 2328 highlight_address)) | |
| 2329 return | |
| 2330 size = self.heap.PointerSize() | |
| 2331 start_address = self.align_down(start_address, size) | |
| 2332 low = self.align_down(region[0], size) | |
| 2333 high = self.align_up(region[0] + region[1], size) | |
| 2334 if start_address < low: | |
| 2335 start_address = low | |
| 2336 end_address = self.align_up(end_address, size) | |
| 2337 if end_address > high: | |
| 2338 end_address = high | |
| 2339 | |
| 2340 expand = "" | |
| 2341 if start_address != low or end_address != high: | |
| 2342 expand = "(<a href=\"data.html?%s&val=0x%x#highlight\">more...</a>)" % ( | |
| 2343 self.encfilename, highlight_address) | |
| 2344 | |
| 2345 f.write(("<h3>%s 0x%x - 0x%x, " | |
| 2346 "highlighting <a href=\"#highlight\">0x%x</a> %s</h3>\n") % ( | |
| 2347 desc, start_address, end_address, highlight_address, expand)) | |
| 2348 f.write('<div class="code">') | |
| 2349 f.write("<table class=\"codedump\"" | |
| 2350 "cellspacing=\"0\" cellpadding=\"0\">\n") | |
| 2351 | |
| 2352 for slot in xrange(start_address, | |
| 2353 end_address, | |
| 
Jakob Kummerow
2014/03/24 15:21:32
nit: fits on one line?
 
Jarin
2014/03/26 10:54:28
Done.
 | |
| 2354 size): | |
| 2355 heap_object = "" | |
| 2356 maybe_address = None | |
| 2357 end_region = region[0] + region[1] | |
| 2358 if slot < region[0] or slot + size > end_region: | |
| 2359 straddress = "0x" | |
| 2360 for i in xrange(end_region, slot + size): | |
| 2361 straddress += "??" | |
| 2362 for i in reversed( | |
| 2363 xrange(max(slot, region[0]), min(slot + size, end_region))): | |
| 2364 straddress += "%02x" % self.reader.ReadU8(i) | |
| 2365 for i in xrange(slot, region[0]): | |
| 2366 straddress += "??" | |
| 2367 else: | |
| 2368 maybe_address = self.reader.ReadUIntPtr(slot) | |
| 2369 straddress = self.format_address(maybe_address) | |
| 2370 if maybe_address: | |
| 2371 heap_object = self.format_object(maybe_address) | |
| 2372 | |
| 2373 address_fmt = "%s </td>\n" | |
| 2374 if slot == highlight_address: | |
| 2375 f.write("<tr class=\"highlight-line\">\n") | |
| 2376 address_fmt = "<a name=\"highlight\"></a>%s </td>\n" | |
| 2377 if slot <= highlight_address and highlight_address < slot + size: | |
| 2378 f.write("<tr class=\"inexact-highlight-line\">\n") | |
| 2379 address_fmt = "<a name=\"highlight\"></a>%s </td>\n" | |
| 2380 else: | |
| 2381 f.write("<tr>\n") | |
| 2382 | |
| 2383 f.write(" <td>") | |
| 2384 self.output_comment_box(f, "da-", slot) | |
| 2385 f.write(" </td>\n") | |
| 2386 self.td_from_address(f, slot) | |
| 2387 f.write(address_fmt % self.format_address(slot)) | |
| 2388 self.td_from_address(f, maybe_address) | |
| 2389 f.write(":  %s  </td>\n" % straddress) | |
| 2390 f.write(" <td>") | |
| 2391 if maybe_address != None: | |
| 2392 self.output_comment_box( | |
| 2393 f, "sv-" + self.reader.FormatIntPtr(slot), maybe_address) | |
| 2394 f.write(" </td>\n") | |
| 2395 f.write(" <td>%s</td>\n" % (heap_object or '')) | |
| 2396 f.write("</tr>\n") | |
| 2397 f.write("</table>\n") | |
| 2398 f.write("</div>") | |
| 2399 return | |
| 2400 | |
| 2401 def output_ascii(self, f, start_address, end_address, highlight_address): | |
| 2402 region = self.reader.FindRegion(highlight_address) | |
| 2403 if region == None: | |
| 2404 f.write("<h3>Address %x not found in the dump.</h3>" % | |
| 2405 highlight_address) | |
| 2406 return | |
| 2407 if start_address < region[0]: | |
| 2408 start_address = region[0] | |
| 2409 if end_address > region[0] + region[1]: | |
| 2410 end_address = region[0] + region[1] | |
| 2411 | |
| 2412 expand = "" | |
| 2413 if start_address != region[0] or end_address != region[0] + region[1]: | |
| 2414 expand = ("(<a href=\"data.html?%s&val=0x%x&type=ascii#highlight\">" | |
| 2415 "more...</a>)") % ( | |
| 2416 self.encfilename, highlight_address) | |
| 2417 | |
| 2418 f.write("<h3>ASCII dump 0x%x - 0x%x, highlighting 0x%x %s</h3>" % ( | |
| 2419 start_address, end_address, highlight_address, expand)) | |
| 2420 | |
| 2421 line_width = 64 | |
| 2422 | |
| 2423 f.write('<div class="code">') | |
| 2424 | |
| 2425 start = self.align_down(start_address, line_width) | |
| 2426 | |
| 2427 for address in xrange(start, end_address): | |
| 2428 if address % 64 == 0: | |
| 2429 if address != start: | |
| 2430 f.write("<br>") | |
| 2431 f.write("0x%08x: " % address) | |
| 2432 if address < start_address: | |
| 2433 f.write(" ") | |
| 2434 else: | |
| 2435 if address == highlight_address: | |
| 2436 f.write("<span class=\"highlight\">") | |
| 2437 code = self.reader.ReadU8(address) | |
| 2438 if code < 127 and code >= 32: | |
| 2439 f.write("&#") | |
| 2440 f.write(str(code)) | |
| 2441 else: | |
| 2442 f.write("·") | |
| 
Jakob Kummerow
2014/03/24 15:21:32
Doesn't this need s/·/·/ ?
 
Jarin
2014/03/26 10:54:28
Done (the char code above needs ';', too)
 | |
| 2443 if address == highlight_address: | |
| 2444 f.write("</span>") | |
| 2445 f.write("</div>") | |
| 2446 return | |
| 2447 | |
| 2448 def output_disasm(self, f, straddress, strexact): | |
| 2449 try: | |
| 2450 self.output_header(f) | |
| 2451 address = int(straddress, 0) | |
| 2452 if not self.reader.IsValidAddress(address): | |
| 2453 f.write("<h3>Address 0x%x not found in the dump.</h3>" % address) | |
| 2454 return | |
| 2455 region = self.reader.FindRegion(address) | |
| 2456 self.output_disasm_range( | |
| 2457 f, region[0], region[0] + region[1], address, strexact == "on") | |
| 2458 self.output_footer(f) | |
| 2459 except ValueError: | |
| 2460 f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress) | |
| 2461 return | |
| 2462 | |
| 2463 def output_disasm_range( | |
| 2464 self, f, start_address, end_address, highlight_address, exact): | |
| 2465 region = self.reader.FindRegion(highlight_address) | |
| 2466 if start_address < region[0]: | |
| 2467 start_address = region[0] | |
| 2468 if end_address > region[0] + region[1]: | |
| 2469 end_address = region[0] + region[1] | |
| 2470 count = end_address - start_address | |
| 2471 lines = self.reader.GetDisasmLines(start_address, count) | |
| 2472 found = False | |
| 2473 if exact: | |
| 2474 for line in lines: | |
| 2475 if line[0] + start_address == highlight_address: | |
| 2476 found = True | |
| 2477 break | |
| 2478 if not found: | |
| 2479 start_address = highlight_address | |
| 2480 count = end_address - start_address | |
| 2481 lines = self.reader.GetDisasmLines(highlight_address, count) | |
| 2482 expand = "" | |
| 2483 if start_address != region[0] or end_address != region[0] + region[1]: | |
| 2484 exactness = "" | |
| 2485 if exact and not found and end_address == region[0] + region[1]: | |
| 2486 exactness = "&exact=off" | |
| 2487 expand = ("(<a href=\"disasm.html?%s%s" | |
| 2488 "&val=0x%x#highlight\">more...</a>)") % ( | |
| 2489 self.encfilename, exactness, highlight_address) | |
| 2490 | |
| 2491 f.write("<h3>Disassembling 0x%x - 0x%x, highlighting 0x%x %s</h3>" % ( | |
| 2492 start_address, end_address, highlight_address, expand)) | |
| 2493 f.write('<div class="code">') | |
| 2494 f.write("<table class=\"codedump\" cellspacing=\"0\" cellpadding=\"0\">\n"); | |
| 2495 for i in xrange(0, len(lines)): | |
| 2496 line = lines[i] | |
| 2497 next_address = count | |
| 2498 if i + 1 < len(lines): | |
| 2499 next_line = lines[i + 1] | |
| 2500 next_address = next_line[0] | |
| 2501 self.format_disasm_line( | |
| 2502 f, start_address, line, next_address, highlight_address) | |
| 2503 f.write("</table>\n") | |
| 2504 f.write("</div>") | |
| 2505 return | |
| 2506 | |
| 2507 def annotate_disasm_addresses(self, line): | |
| 2508 extra = [] | |
| 2509 for m in ADDRESS_RE.finditer(line): | |
| 2510 maybe_address = int(m.group(0), 16) | |
| 2511 formatted_address = self.format_address(maybe_address, m.group(0)) | |
| 2512 line = line.replace(m.group(0), formatted_address) | |
| 2513 object = self.padawan.SenseObject(maybe_address) | |
| 2514 if not object: continue | |
| 2515 extra.append(cgi.escape(str(object))) | |
| 2516 if len(extra) == 0: return line | |
| 2517 return "%s <span class=\"disasmcmt\">;; %s</span>" % ( | |
| 2518 line, ", ".join(extra)) | |
| 2519 | |
| 2520 | |
| 2521 def format_disasm_line( | |
| 2522 self, f, start, line, next_address, highlight_address): | |
| 2523 line_address = start + line[0] | |
| 2524 address_fmt = " <td>%s</td>\n" | |
| 2525 if line_address == highlight_address: | |
| 2526 f.write("<tr class=\"highlight-line\">\n") | |
| 2527 address_fmt = " <td><a name=\"highlight\">%s</a></td>\n" | |
| 2528 elif (line_address < highlight_address and | |
| 2529 highlight_address < next_address + start): | |
| 2530 f.write("<tr class=\"inexact-highlight-line\">\n") | |
| 2531 address_fmt = " <td><a name=\"highlight\">%s</a></td>\n" | |
| 2532 else: | |
| 2533 f.write("<tr>\n") | |
| 2534 num_bytes = next_address - line[0] | |
| 2535 stack_slot = self.heap.stack_map.get(line_address) | |
| 2536 marker = "" | |
| 2537 if stack_slot: | |
| 2538 marker = "=>" | |
| 2539 op_offset = 3 * num_bytes - 1 | |
| 2540 | |
| 2541 code = line[1] | |
| 2542 # Compute the actual call target which the disassembler is too stupid | |
| 2543 # to figure out (it adds the call offset to the disassembly offset rather | |
| 2544 # than the absolute instruction address). | |
| 2545 if self.heap.reader.arch == MD_CPU_ARCHITECTURE_X86: | |
| 2546 if code.startswith("e8"): | |
| 2547 words = code.split() | |
| 2548 if len(words) > 6 and words[5] == "call": | |
| 2549 offset = int(words[4] + words[3] + words[2] + words[1], 16) | |
| 2550 target = (line_address + offset + 5) & 0xFFFFFFFF | |
| 2551 code = code.replace(words[6], "0x%08x" % target) | |
| 2552 # TODO(jkummerow): port this hack to ARM and x64. | |
| 2553 | |
| 2554 opcodes = code[:op_offset] | |
| 2555 code = self.annotate_disasm_addresses(code[op_offset:]) | |
| 2556 f.write(" <td>") | |
| 2557 self.output_comment_box(f, "codel-", line_address) | |
| 2558 f.write("</td>\n") | |
| 2559 f.write(address_fmt % marker) | |
| 2560 self.td_from_address(f, line_address) | |
| 2561 f.write("%s (+0x%x)</td>\n" % ( | |
| 2562 self.format_address(line_address), line[0])) | |
| 2563 f.write(" <td>: %s </td>\n" % opcodes) | |
| 2564 f.write(" <td>%s</td>\n" % code) | |
| 2565 f.write("</tr>\n") | |
| 2566 | |
| 2567 def output_comment_box(self, f, prefix, address): | |
| 2568 f.write(("<input type=\"text\" class=\"commentinput\"" | |
| 2569 "id=\"%s-address-0x%s\" onchange=\"cmt()\" value=\"%s\">\n") % ( | |
| 2570 prefix, | |
| 2571 self.reader.FormatIntPtr(address), | |
| 2572 cgi.escape(self.comments.get_comment(address)) or "")) | |
| 2573 | |
| 2574 MAX_FOUND_RESULTS = 100 | |
| 2575 | |
| 2576 def output_find_results(self, f, results): | |
| 2577 f.write("Addresses") | |
| 2578 toomany = len(results) > self.MAX_FOUND_RESULTS | |
| 2579 if toomany: | |
| 2580 f.write("(found %i results, displaying only first %i)" % ( | |
| 2581 len(results), self.MAX_FOUND_RESULTS)) | |
| 2582 f.write(": \n") | |
| 2583 results = sorted(results) | |
| 2584 results = results[:min(len(results), self.MAX_FOUND_RESULTS)] | |
| 2585 for address in results: | |
| 2586 f.write("<span %s>%s</span>\n" % ( | |
| 2587 self.comments.get_style_class_string(address), \ | |
| 2588 self.format_address(address))) | |
| 2589 if toomany: | |
| 2590 f.write("...\n") | |
| 2591 | |
| 2592 | |
| 
Jakob Kummerow
2014/03/24 15:21:32
nit: just one empty line
 
Jarin
2014/03/26 10:54:28
Done.
 | |
| 2593 def output_page_info(self, f, page_kind, page_address, my_page_address): | |
| 2594 if my_page_address == page_address and page_address != 0: | |
| 2595 f.write("Marked first %s page.\n" % page_kind) | |
| 2596 else: | |
| 2597 f.write("<span id=\"%spage\" style=\"display:none\">" % page_kind) | |
| 2598 f.write("Marked first %s page." % page_kind) | |
| 2599 f.write("</span>\n") | |
| 2600 f.write("<button onclick=\"onpage('%spage', '0x%x')\">" % ( | |
| 2601 page_kind, my_page_address)) | |
| 2602 f.write("Mark as first %s page</button>\n" % page_kind) | |
| 2603 return | |
| 2604 | |
| 2605 def output_search_res(self, f, straddress): | |
| 2606 try: | |
| 2607 self.output_header(f) | |
| 2608 f.write("<h3>Search results for %s</h3>" % straddress) | |
| 2609 | |
| 2610 address = int(straddress, 0) | |
| 2611 | |
| 2612 f.write("Comment: ") | |
| 2613 self.output_comment_box(f, "search-", address) | |
| 2614 f.write("<br>\n") | |
| 2615 | |
| 2616 page_address = address & ~self.heap.PageAlignmentMask() | |
| 2617 | |
| 2618 f.write("Page info: \n") | |
| 2619 self.output_page_info(f, "data", self.padawan.known_first_data_page, \ | |
| 2620 page_address) | |
| 2621 self.output_page_info(f, "map", self.padawan.known_first_map_page, \ | |
| 2622 page_address) | |
| 2623 self.output_page_info(f, "pointer", \ | |
| 2624 self.padawan.known_first_pointer_page, \ | |
| 2625 page_address) | |
| 2626 | |
| 2627 if not self.reader.IsValidAddress(address): | |
| 2628 f.write("<h3>The contents at address %s not found in the dump.</h3>" % \ | |
| 2629 straddress) | |
| 2630 else: | |
| 2631 # Print as words | |
| 2632 self.output_words(f, address - 8, address + 32, address, "Dump") | |
| 2633 | |
| 2634 # Print as ASCII | |
| 2635 f.write("<hr>\n") | |
| 2636 self.output_ascii(f, address, address + 256, address) | |
| 2637 | |
| 2638 # Print as code | |
| 2639 f.write("<hr>\n") | |
| 2640 self.output_disasm_range(f, address - 16, address + 16, address, True) | |
| 2641 | |
| 2642 aligned_res, unaligned_res = self.reader.FindWordList(address) | |
| 2643 | |
| 2644 if len(aligned_res) > 0: | |
| 2645 f.write("<h3>Occurrences of 0x%x at aligned addresses</h3>\n" % | |
| 2646 address) | |
| 2647 self.output_find_results(f, aligned_res) | |
| 2648 | |
| 2649 if len(unaligned_res) > 0: | |
| 2650 f.write("<h3>Occurrences of 0x%x at unaligned addresses</h3>\n" % \ | |
| 2651 address) | |
| 2652 self.output_find_results(f, unaligned_res) | |
| 2653 | |
| 2654 if len(aligned_res) + len(unaligned_res) == 0: | |
| 2655 f.write("<h3>No occurences of 0x%x found in the dump</h3>\n" % address) | |
| 2656 | |
| 2657 self.output_footer(f) | |
| 2658 | |
| 2659 except ValueError: | |
| 2660 f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress) | |
| 2661 return | |
| 2662 | |
| 2663 def output_disasm_pc(self, f): | |
| 2664 address = self.reader.ExceptionIP() | |
| 2665 if not self.reader.IsValidAddress(address): | |
| 2666 return | |
| 2667 self.output_disasm_range(f, address - 16, address + 16, address, True) | |
| 2668 | |
| 2669 | |
| 2670 WEB_DUMPS_HEADER = """ | |
| 2671 <!DOCTYPE html> | |
| 2672 <html> | |
| 2673 <head> | |
| 2674 <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"> | |
| 2675 <style media="screen" type="text/css"> | |
| 2676 | |
| 2677 .dumplist { | |
| 2678 border-collapse : collapse; | |
| 2679 border-spacing : 0px; | |
| 2680 font-family: monospace; | |
| 2681 } | |
| 2682 | |
| 2683 .dumpcomments { | |
| 2684 border : 1px solid LightGray; | |
| 2685 width : 32em; | |
| 2686 } | |
| 2687 | |
| 2688 </style> | |
| 2689 | |
| 2690 <script type="application/javascript"> | |
| 2691 | |
| 2692 var dump_str = "dump-"; | |
| 2693 var dump_len = dump_str.length; | |
| 2694 | |
| 2695 function dump_cmt() { | |
| 2696 var s = event.srcElement.id; | |
| 2697 var index = s.indexOf(dump_str); | |
| 2698 if (index >= 0) { | |
| 2699 send_dump_desc(s.substring(index + dump_len), event.srcElement.value); | |
| 2700 } | |
| 2701 } | |
| 2702 | |
| 2703 function send_dump_desc(name, desc) { | |
| 2704 xmlhttp = new XMLHttpRequest(); | |
| 2705 name = encodeURIComponent(name) | |
| 2706 desc = encodeURIComponent(desc) | |
| 2707 xmlhttp.open("GET", | |
| 2708 "setdumpdesc?dump=" + name + | |
| 2709 "&description=" + desc, true); | |
| 2710 xmlhttp.send(); | |
| 2711 } | |
| 2712 | |
| 2713 </script> | |
| 2714 | |
| 2715 <title>Dump list</title> | |
| 2716 </head> | |
| 2717 | |
| 2718 <body> | |
| 2719 """ | |
| 2720 | |
| 2721 WEB_DUMPS_FOOTER = """ | |
| 2722 </body> | |
| 2723 </html> | |
| 2724 """ | |
| 2725 | |
| 2726 DUMP_FILE_RE = re.compile(r"[-_0-9a-zA-Z][-\._0-9a-zA-Z]*\.dmp$") | |
| 2727 | |
| 2728 | |
| 2729 class InspectionWebServer(HTTPServer): | |
| 2730 def __init__(self, port_number, options, minidump_name): | |
| 2731 HTTPServer.__init__(self, ('', port_number), InspectionWebHandler) | |
| 2732 splitpath = os.path.split(minidump_name) | |
| 2733 self.dumppath = splitpath[0] | |
| 2734 self.dumpfilename = splitpath[1] | |
| 2735 self.formatter = InspectionWebFormatter(options, minidump_name, self) | |
| 2736 self.formatters = { self.dumpfilename : self.formatter } | |
| 2737 print "Initialized at path '%s'" % self.dumppath | |
| 2738 | |
| 2739 def output_dump_desc_field(self, f, name): | |
| 2740 try: | |
| 2741 descfile = open(os.path.join(self.dumppath, name + ".desc"), "r") | |
| 2742 desc = descfile.readline() | |
| 2743 descfile.close() | |
| 2744 except IOError: | |
| 2745 desc = "" | |
| 2746 f.write(("<input type=\"text\" class=\"dumpcomments\" " | |
| 
Jakob Kummerow
2014/03/24 15:21:32
nit: the indentation here is particularly inconsis
 
Jarin
2014/03/26 10:54:28
Done.
 | |
| 2747 "id=\"dump-%s\" onchange=\"dump_cmt()\" value=\"%s\">\n") % ( | |
| 2748 cgi.escape(name), | |
| 2749 desc)) | |
| 2750 | |
| 2751 def set_dump_desc(self, name, description): | |
| 2752 if not DUMP_FILE_RE.match(name): | |
| 2753 return False | |
| 2754 fname = os.path.join(self.dumppath, name) | |
| 2755 if not os.path.isfile(fname): | |
| 2756 return False | |
| 2757 fname = fname + ".desc" | |
| 2758 descfile = open(fname, "w") | |
| 2759 descfile.write(description) | |
| 2760 descfile.close() | |
| 2761 return True | |
| 2762 | |
| 2763 def get_dump_formatter(self, name): | |
| 2764 if name == None: | |
| 2765 # Get the default formatter | |
| 2766 return self.formatter | |
| 2767 else: | |
| 2768 if not DUMP_FILE_RE.match(name): | |
| 2769 raise WebParameterError("Invalid name '%s'" % name) | |
| 2770 fmt = self.formatters.get(name, None) | |
| 2771 if fmt == None: | |
| 2772 try: | |
| 2773 fmt = InspectionWebFormatter( | |
| 2774 options, os.path.join(self.dumppath, name), self) | |
| 2775 self.formatters[name] = fmt | |
| 2776 except IOError: | |
| 2777 raise WebParameterError("Could not open dump '%s'" % name) | |
| 2778 return fmt | |
| 2779 | |
| 2780 def output_dumps(self, f): | |
| 2781 f.write(WEB_DUMPS_HEADER) | |
| 2782 f.write("<h3>List of available dumps</h3>") | |
| 2783 f.write("<table class=\"dumplist\">\n") | |
| 2784 f.write("<thead><tr>") | |
| 2785 f.write("<th>Name</th>") | |
| 2786 f.write("<th>File time</th>") | |
| 2787 f.write("<th>Comment</th>") | |
| 2788 f.write("</tr></thead>") | |
| 2789 dumps_by_time = { } | |
| 2790 for fname in os.listdir(self.dumppath): | |
| 2791 if DUMP_FILE_RE.match(fname): | |
| 2792 mtime = os.stat(os.path.join(self.dumppath, fname)).st_mtime | |
| 2793 fnames = dumps_by_time.get(mtime, []) | |
| 2794 fnames.append(fname) | |
| 2795 dumps_by_time[mtime] = fnames | |
| 2796 | |
| 2797 for mtime in sorted(dumps_by_time, reverse=True): | |
| 2798 fnames = dumps_by_time[mtime] | |
| 2799 for fname in fnames: | |
| 2800 f.write("<tr>\n") | |
| 2801 f.write("<td><a href=\"summary.html?%s\">%s</td>\n" % ( | |
| 2802 (urllib.urlencode({ 'dump' : fname }), fname))) | |
| 2803 f.write("<td>   ") | |
| 2804 f.write(datetime.datetime.fromtimestamp(mtime)) | |
| 2805 f.write("</td>") | |
| 2806 f.write("<td>   ") | |
| 2807 self.output_dump_desc_field(f, fname) | |
| 2808 f.write("</td>") | |
| 2809 f.write("</tr>\n") | |
| 2810 f.write("</table>\n") | |
| 2811 f.write(WEB_DUMPS_FOOTER) | |
| 2812 return | |
| 1656 | 2813 | 
| 1657 class InspectionShell(cmd.Cmd): | 2814 class InspectionShell(cmd.Cmd): | 
| 1658 def __init__(self, reader, heap): | 2815 def __init__(self, reader, heap): | 
| 1659 cmd.Cmd.__init__(self) | 2816 cmd.Cmd.__init__(self) | 
| 1660 self.reader = reader | 2817 self.reader = reader | 
| 1661 self.heap = heap | 2818 self.heap = heap | 
| 1662 self.padawan = InspectionPadawan(reader, heap) | 2819 self.padawan = InspectionPadawan(reader, heap) | 
| 1663 self.prompt = "(grok) " | 2820 self.prompt = "(grok) " | 
| 1664 | 2821 | 
| 1665 def do_da(self, address): | 2822 def do_da(self, address): | 
| (...skipping 323 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1989 heap_object.Print(Printer()) | 3146 heap_object.Print(Printer()) | 
| 1990 print | 3147 print | 
| 1991 | 3148 | 
| 1992 reader.Dispose() | 3149 reader.Dispose() | 
| 1993 | 3150 | 
| 1994 | 3151 | 
| 1995 if __name__ == "__main__": | 3152 if __name__ == "__main__": | 
| 1996 parser = optparse.OptionParser(USAGE) | 3153 parser = optparse.OptionParser(USAGE) | 
| 1997 parser.add_option("-s", "--shell", dest="shell", action="store_true", | 3154 parser.add_option("-s", "--shell", dest="shell", action="store_true", | 
| 1998 help="start an interactive inspector shell") | 3155 help="start an interactive inspector shell") | 
| 3156 parser.add_option("-w", "--web", dest="web", action="store_true", | |
| 3157 help="start a web page") | |
| 
Jakob Kummerow
2014/03/24 15:21:32
nit: s/page/server on localhost:8081/ (is it worth
 
Jarin
2014/03/26 10:54:28
Done (the port not configurable yet)
 | |
| 1999 parser.add_option("-c", "--command", dest="command", default="", | 3158 parser.add_option("-c", "--command", dest="command", default="", | 
| 2000 help="run an interactive inspector shell command and exit") | 3159 help="run an interactive inspector shell command and exit") | 
| 2001 parser.add_option("-f", "--full", dest="full", action="store_true", | 3160 parser.add_option("-f", "--full", dest="full", action="store_true", | 
| 2002 help="dump all information contained in the minidump") | 3161 help="dump all information contained in the minidump") | 
| 2003 parser.add_option("--symdir", dest="symdir", default=".", | 3162 parser.add_option("--symdir", dest="symdir", default=".", | 
| 2004 help="directory containing *.pdb.sym file with symbols") | 3163 help="directory containing *.pdb.sym file with symbols") | 
| 2005 parser.add_option("--objdump", | 3164 parser.add_option("--objdump", | 
| 2006 default="/usr/bin/objdump", | 3165 default="/usr/bin/objdump", | 
| 2007 help="objdump tool to use [default: %default]") | 3166 help="objdump tool to use [default: %default]") | 
| 2008 options, args = parser.parse_args() | 3167 options, args = parser.parse_args() | 
| 2009 if os.path.exists(options.objdump): | 3168 if os.path.exists(options.objdump): | 
| 2010 disasm.OBJDUMP_BIN = options.objdump | 3169 disasm.OBJDUMP_BIN = options.objdump | 
| 2011 OBJDUMP_BIN = options.objdump | 3170 OBJDUMP_BIN = options.objdump | 
| 2012 else: | 3171 else: | 
| 2013 print "Cannot find %s, falling back to default objdump" % options.objdump | 3172 print "Cannot find %s, falling back to default objdump" % options.objdump | 
| 2014 if len(args) != 1: | 3173 if len(args) != 1: | 
| 2015 parser.print_help() | 3174 parser.print_help() | 
| 2016 sys.exit(1) | 3175 sys.exit(1) | 
| 2017 AnalyzeMinidump(options, args[0]) | 3176 if options.web: | 
| 3177 try: | |
| 3178 server = InspectionWebServer(PORT_NUMBER, options, args[0]) | |
| 3179 print 'Started httpserver on port ' , PORT_NUMBER | |
| 3180 webbrowser.open('http://localhost:8081/summary.html') | |
| 3181 server.serve_forever() | |
| 3182 except KeyboardInterrupt: | |
| 3183 print '^C received, shutting down the web server' | |
| 3184 server.socket.close() | |
| 3185 else: | |
| 3186 AnalyzeMinidump(options, args[0]) | |
| OLD | NEW |