Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(202)

Side by Side Diff: tools/grokdump.py

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

Powered by Google App Engine
This is Rietveld 408576698