Moved from scripts into tools, and updated from 4.5 to 4.6 - Changed the tool title to SleepGraph - Reformatted the code so analyze_suspend can be used as a library - Reorganized all html/js/css handling code to be used by other tools - upgraded the -summary feature to work faster with better readability
Unified · Split
| @@ -12,10 +12,6 @@ · 11 unmodified lines | |||
| 12 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | 12 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 13 | # more details. | 13 | # more details. |
| 14 | # | 14 | # |
| 15 | # You should have received a copy of the GNU General Public License along with | ||
| 16 | # this program; if not, write to the Free Software Foundation, Inc., | ||
| 17 | # 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 18 | # | ||
| 19 | # Authors: | 15 | # Authors: |
| 20 | # Todd Brandt <todd.e.brandt@linux.intel.com> | 16 | # Todd Brandt <todd.e.brandt@linux.intel.com> |
| 21 | # | 17 | # |
| @@ -23,7 +19,7 @@ · 1 unmodified line | |||
| 23 | # Home Page | 19 | # Home Page |
| 24 | # https://01.org/suspendresume | 20 | # https://01.org/suspendresume |
| 25 | # Source repo | 21 | # Source repo |
| 26 | # https://github.com/01org/suspendresume | 22 | # https://github.com/01org/pm-graph |
| 27 | # | 23 | # |
| 28 | # Description: | 24 | # Description: |
| 29 | # This tool is designed to assist kernel and OS developers in optimizing | 25 | # This tool is designed to assist kernel and OS developers in optimizing |
| @@ -71,14 +67,16 @@ from subprocess import call, Popen, PIPE · 41 unmodified lines | |||
| 71 | # A global, single-instance container used to | 67 | # A global, single-instance container used to |
| 72 | # store system values and test parameters | 68 | # store system values and test parameters |
| 73 | class SystemValues: | 69 | class SystemValues: |
| 70 | title = 'SleepGraph' | ||
| 71 | version = '4.6' | ||
| 74 | ansi = False | 72 | ansi = False |
| 75 | version = '4.5' | ||
| 76 | verbose = False | 73 | verbose = False |
| 77 | addlogs = False | 74 | addlogs = False |
| 78 | mindevlen = 0.0 | 75 | mindevlen = 0.0 |
| 79 | mincglen = 0.0 | 76 | mincglen = 0.0 |
| 80 | cgphase = '' | 77 | cgphase = '' |
| 81 | cgtest = -1 | 78 | cgtest = -1 |
| 79 | max_graph_depth = 0 | ||
| 82 | callloopmaxgap = 0.0001 | 80 | callloopmaxgap = 0.0001 |
| 83 | callloopmaxlen = 0.005 | 81 | callloopmaxlen = 0.005 |
| 84 | srgap = 0 | 82 | srgap = 0 |
| @@ -106,8 +104,8 @@ class SystemValues: · 21 unmodified lines | |||
| 106 | ftracefile = '' | 104 | ftracefile = '' |
| 107 | htmlfile = '' | 105 | htmlfile = '' |
| 108 | embedded = False | 106 | embedded = False |
| 109 | rtcwake = False | 107 | rtcwake = True |
| 110 | rtcwaketime = 10 | 108 | rtcwaketime = 15 |
| 111 | rtcpath = '' | 109 | rtcpath = '' |
| 112 | devicefilter = [] | 110 | devicefilter = [] |
| 113 | stamp = 0 | 111 | stamp = 0 |
| @@ -235,6 +233,12 @@ class SystemValues: · 121 unmodified lines | |||
| 235 | self.rtcpath = rtc | 233 | self.rtcpath = rtc |
| 236 | if (hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()): | 234 | if (hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()): |
| 237 | self.ansi = True | 235 | self.ansi = True |
| 236 | def rootUser(self, fatal=False): | ||
| 237 | if 'USER' in os.environ and os.environ['USER'] == 'root': | ||
| 238 | return True | ||
| 239 | if fatal: | ||
| 240 | doError('This command must be run as root') | ||
| 241 | return False | ||
| 238 | def setPrecision(self, num): | 242 | def setPrecision(self, num): |
| 239 | if num < 0 or num > 6: | 243 | if num < 0 or num > 6: |
| 240 | return | 244 | return |
| @@ -564,7 +568,7 @@ class SystemValues: · 323 unmodified lines | |||
| 564 | self.fsetVal('global', 'trace_clock') | 568 | self.fsetVal('global', 'trace_clock') |
| 565 | # set trace buffer to a huge value | 569 | # set trace buffer to a huge value |
| 566 | self.fsetVal('nop', 'current_tracer') | 570 | self.fsetVal('nop', 'current_tracer') |
| 567 | self.fsetVal('100000', 'buffer_size_kb') | 571 | self.fsetVal('131073', 'buffer_size_kb') |
| 568 | # go no further if this is just a status check | 572 | # go no further if this is just a status check |
| 569 | if testing: | 573 | if testing: |
| 570 | return | 574 | return |
| @@ -583,7 +587,7 @@ class SystemValues: · 12 unmodified lines | |||
| 583 | self.fsetVal('nofuncgraph-overhead', 'trace_options') | 587 | self.fsetVal('nofuncgraph-overhead', 'trace_options') |
| 584 | self.fsetVal('context-info', 'trace_options') | 588 | self.fsetVal('context-info', 'trace_options') |
| 585 | self.fsetVal('graph-time', 'trace_options') | 589 | self.fsetVal('graph-time', 'trace_options') |
| 586 | self.fsetVal('0', 'max_graph_depth') | 590 | self.fsetVal('%d' % self.max_graph_depth, 'max_graph_depth') |
| 587 | cf = ['dpm_run_callback'] | 591 | cf = ['dpm_run_callback'] |
| 588 | if(self.usetraceeventsonly): | 592 | if(self.usetraceeventsonly): |
| 589 | cf += ['dpm_prepare', 'dpm_complete'] | 593 | cf += ['dpm_prepare', 'dpm_complete'] |
| @@ -639,6 +643,12 @@ class SystemValues: · 49 unmodified lines | |||
| 639 | return '\x1B[%d;40m%s\x1B[m' % (color, str) | 643 | return '\x1B[%d;40m%s\x1B[m' % (color, str) |
| 640 | 644 | ||
| 641 | sysvals = SystemValues() | 645 | sysvals = SystemValues() |
| 646 | suspendmodename = { | ||
| 647 | 'freeze': 'Freeze (S0)', | ||
| 648 | 'standby': 'Standby (S1)', | ||
| 649 | 'mem': 'Suspend (S3)', | ||
| 650 | 'disk': 'Hibernate (S4)' | ||
| 651 | } | ||
| 642 | 652 | ||
| 643 | # Class: DevProps | 653 | # Class: DevProps |
| 644 | # Description: | 654 | # Description: |
| @@ -1013,6 +1023,8 @@ class Data: · 368 unmodified lines | |||
| 1013 | tmp = dict() | 1023 | tmp = dict() |
| 1014 | for devname in list: | 1024 | for devname in list: |
| 1015 | dev = list[devname] | 1025 | dev = list[devname] |
| 1026 | if dev['length'] == 0: | ||
| 1027 | continue | ||
| 1016 | tmp[dev['start']] = devname | 1028 | tmp[dev['start']] = devname |
| 1017 | for t in sorted(tmp): | 1029 | for t in sorted(tmp): |
| 1018 | slist.append(tmp[t]) | 1030 | slist.append(tmp[t]) |
| @@ -1477,12 +1489,14 @@ class FTraceLine: · 458 unmodified lines | |||
| 1477 | # Each instance is tied to a single device in a single phase, and is | 1489 | # Each instance is tied to a single device in a single phase, and is |
| 1478 | # comprised of an ordered list of FTraceLine objects | 1490 | # comprised of an ordered list of FTraceLine objects |
| 1479 | class FTraceCallGraph: | 1491 | class FTraceCallGraph: |
| 1492 | id = '' | ||
| 1480 | start = -1.0 | 1493 | start = -1.0 |
| 1481 | end = -1.0 | 1494 | end = -1.0 |
| 1482 | list = [] | 1495 | list = [] |
| 1483 | invalid = False | 1496 | invalid = False |
| 1484 | depth = 0 | 1497 | depth = 0 |
| 1485 | pid = 0 | 1498 | pid = 0 |
| 1499 | name = '' | ||
| 1486 | def __init__(self, pid): | 1500 | def __init__(self, pid): |
| 1487 | self.start = -1.0 | 1501 | self.start = -1.0 |
| 1488 | self.end = -1.0 | 1502 | self.end = -1.0 |
| @@ -1631,9 +1645,17 @@ class FTraceCallGraph: · 142 unmodified lines | |||
| 1631 | return True | 1645 | return True |
| 1632 | return False | 1646 | return False |
| 1633 | def postProcess(self, debug=False): | 1647 | def postProcess(self, debug=False): |
| 1648 | if len(self.list) > 0: | ||
| 1649 | self.name = self.list[0].name | ||
| 1634 | stack = dict() | 1650 | stack = dict() |
| 1635 | cnt = 0 | 1651 | cnt = 0 |
| 1652 | last = 0 | ||
| 1636 | for l in self.list: | 1653 | for l in self.list: |
| 1654 | # ftrace bug: reported duration is not reliable | ||
| 1655 | # check each leaf and clip it at max possible length | ||
| 1656 | if(last and last.freturn and last.fcall): | ||
| 1657 | if last.length > l.time - last.time: | ||
| 1658 | last.length = l.time - last.time | ||
| 1637 | if(l.fcall and not l.freturn): | 1659 | if(l.fcall and not l.freturn): |
| 1638 | stack[l.depth] = l | 1660 | stack[l.depth] = l |
| 1639 | cnt += 1 | 1661 | cnt += 1 |
| @@ -1643,11 +1665,12 @@ class FTraceCallGraph: · 3 unmodified lines | |||
| 1643 | print 'Post Process Error: Depth missing' | 1665 | print 'Post Process Error: Depth missing' |
| 1644 | l.debugPrint() | 1666 | l.debugPrint() |
| 1645 | return False | 1667 | return False |
| 1646 | # transfer total time from return line to call line | 1668 | # calculate call length from call/return lines |
| 1647 | stack[l.depth].length = l.length | 1669 | stack[l.depth].length = l.time - stack[l.depth].time |
| 1648 | stack.pop(l.depth) | 1670 | stack.pop(l.depth) |
| 1649 | l.length = 0 | 1671 | l.length = 0 |
| 1650 | cnt -= 1 | 1672 | cnt -= 1 |
| 1673 | last = l | ||
| 1651 | if(cnt == 0): | 1674 | if(cnt == 0): |
| 1652 | # trace caught the whole call tree | 1675 | # trace caught the whole call tree |
| 1653 | return True | 1676 | return True |
| @@ -1664,8 +1687,8 @@ class FTraceCallGraph: · 10 unmodified lines | |||
| 1664 | 'dpm_prepare': 'suspend_prepare', | 1687 | 'dpm_prepare': 'suspend_prepare', |
| 1665 | 'dpm_complete': 'resume_complete' | 1688 | 'dpm_complete': 'resume_complete' |
| 1666 | } | 1689 | } |
| 1667 | if(self.list[0].name in borderphase): | 1690 | if(self.name in borderphase): |
| 1668 | p = borderphase[self.list[0].name] | 1691 | p = borderphase[self.name] |
| 1669 | list = data.dmesg[p]['list'] | 1692 | list = data.dmesg[p]['list'] |
| 1670 | for devname in list: | 1693 | for devname in list: |
| 1671 | dev = list[devname] | 1694 | dev = list[devname] |
| @@ -1690,7 +1713,7 @@ class FTraceCallGraph: · 18 unmodified lines | |||
| 1690 | break | 1713 | break |
| 1691 | return found | 1714 | return found |
| 1692 | def newActionFromFunction(self, data): | 1715 | def newActionFromFunction(self, data): |
| 1693 | name = self.list[0].name | 1716 | name = self.name |
| 1694 | if name in ['dpm_run_callback', 'dpm_prepare', 'dpm_complete']: | 1717 | if name in ['dpm_run_callback', 'dpm_prepare', 'dpm_complete']: |
| 1695 | return | 1718 | return |
| 1696 | fs = self.start | 1719 | fs = self.start |
| @@ -1710,7 +1733,7 @@ class FTraceCallGraph: · 13 unmodified lines | |||
| 1710 | phase, myname = out | 1733 | phase, myname = out |
| 1711 | data.dmesg[phase]['list'][myname]['ftrace'] = self | 1734 | data.dmesg[phase]['list'][myname]['ftrace'] = self |
| 1712 | def debugPrint(self): | 1735 | def debugPrint(self): |
| 1713 | print('[%f - %f] %s (%d)') % (self.start, self.end, self.list[0].name, self.pid) | 1736 | print('[%f - %f] %s (%d)') % (self.start, self.end, self.name, self.pid) |
| 1714 | for l in self.list: | 1737 | for l in self.list: |
| 1715 | if(l.freturn and l.fcall): | 1738 | if(l.freturn and l.fcall): |
| 1716 | print('%f (%02d): %s(); (%.3f us)' % (l.time, \ | 1739 | print('%f (%02d): %s(); (%.3f us)' % (l.time, \ |
| @@ -1738,7 +1761,7 @@ class DevItem: · 21 unmodified lines | |||
| 1738 | # A container for a device timeline which calculates | 1761 | # A container for a device timeline which calculates |
| 1739 | # all the html properties to display it correctly | 1762 | # all the html properties to display it correctly |
| 1740 | class Timeline: | 1763 | class Timeline: |
| 1741 | html = {} | 1764 | html = '' |
| 1742 | height = 0 # total timeline height | 1765 | height = 0 # total timeline height |
| 1743 | scaleH = 20 # timescale (top) row height | 1766 | scaleH = 20 # timescale (top) row height |
| 1744 | rowH = 30 # device row height | 1767 | rowH = 30 # device row height |
| @@ -1746,14 +1769,28 @@ class Timeline: · 1 unmodified line | |||
| 1746 | rows = 0 # total timeline rows | 1769 | rows = 0 # total timeline rows |
| 1747 | rowlines = dict() | 1770 | rowlines = dict() |
| 1748 | rowheight = dict() | 1771 | rowheight = dict() |
| 1772 | html_tblock = '<div id="block{0}" class="tblock" style="left:{1}%;width:{2}%;"><div class="tback" style="height:{3}px"></div>\n' | ||
| 1773 | html_device = '<div id="{0}" title="{1}" class="thread{7}" style="left:{2}%;top:{3}px;height:{4}px;width:{5}%;{8}">{6}</div>\n' | ||
| 1774 | html_phase = '<div class="phase" style="left:{0}%;width:{1}%;top:{2}px;height:{3}px;background:{4}">{5}</div>\n' | ||
| 1775 | html_phaselet = '<div id="{0}" class="phaselet" style="left:{1}%;width:{2}%;background:{3}"></div>\n' | ||
| 1749 | def __init__(self, rowheight, scaleheight): | 1776 | def __init__(self, rowheight, scaleheight): |
| 1750 | self.rowH = rowheight | 1777 | self.rowH = rowheight |
| 1751 | self.scaleH = scaleheight | 1778 | self.scaleH = scaleheight |
| 1752 | self.html = { | 1779 | self.html = '' |
| 1753 | 'header': '', | 1780 | def createHeader(self, sv, suppress=''): |
| 1754 | 'timeline': '', | 1781 | if(not sv.stamp['time']): |
| 1755 | 'legend': '', | 1782 | return |
| 1756 | } | 1783 | self.html += '<div class="version"><a href="https://01.org/suspendresume">%s v%s</a></div>' \ |
| 1784 | % (sv.title, sv.version) | ||
| 1785 | if sv.logmsg and 'log' not in suppress: | ||
| 1786 | self.html += '<button id="showtest" class="logbtn">log</button>' | ||
| 1787 | if sv.addlogs and 'dmesg' not in suppress: | ||
| 1788 | self.html += '<button id="showdmesg" class="logbtn">dmesg</button>' | ||
| 1789 | if sv.addlogs and sv.ftracefile and 'ftrace' not in suppress: | ||
| 1790 | self.html += '<button id="showftrace" class="logbtn">ftrace</button>' | ||
| 1791 | headline_stamp = '<div class="stamp">{0} {1} {2} {3}</div>\n' | ||
| 1792 | self.html += headline_stamp.format(sv.stamp['host'], sv.stamp['kernel'], | ||
| 1793 | sv.stamp['mode'], sv.stamp['time']) | ||
| 1757 | # Function: getDeviceRows | 1794 | # Function: getDeviceRows |
| 1758 | # Description: | 1795 | # Description: |
| 1759 | # determine how may rows the device funcs will take | 1796 | # determine how may rows the device funcs will take |
| @@ -1880,10 +1917,8 @@ class Timeline: · 120 unmodified lines | |||
| 1880 | break | 1917 | break |
| 1881 | top += self.rowheight[test][phase][i] | 1918 | top += self.rowheight[test][phase][i] |
| 1882 | return top | 1919 | return top |
| 1883 | # Function: calcTotalRows | ||
| 1884 | # Description: | ||
| 1885 | # Calculate the heights and offsets for the header and rows | ||
| 1886 | def calcTotalRows(self): | 1920 | def calcTotalRows(self): |
| 1921 | # Calculate the heights and offsets for the header and rows | ||
| 1887 | maxrows = 0 | 1922 | maxrows = 0 |
| 1888 | standardphases = [] | 1923 | standardphases = [] |
| 1889 | for t in self.rowlines: | 1924 | for t in self.rowlines: |
| @@ -1901,6 +1936,20 @@ class Timeline: · 11 unmodified lines | |||
| 1901 | for t, p in standardphases: | 1936 | for t, p in standardphases: |
| 1902 | for i in sorted(self.rowheight[t][p]): | 1937 | for i in sorted(self.rowheight[t][p]): |
| 1903 | self.rowheight[t][p][i] = self.bodyH/len(self.rowlines[t][p]) | 1938 | self.rowheight[t][p][i] = self.bodyH/len(self.rowlines[t][p]) |
| 1939 | def createZoomBox(self, mode='command', testcount=1): | ||
| 1940 | # Create bounding box, add buttons | ||
| 1941 | html_zoombox = '<center><button id="zoomin">ZOOM IN +</button><button id="zoomout">ZOOM OUT -</button><button id="zoomdef">ZOOM 1:1</button></center>\n' | ||
| 1942 | html_timeline = '<div id="dmesgzoombox" class="zoombox">\n<div id="{0}" class="timeline" style="height:{1}px">\n' | ||
| 1943 | html_devlist1 = '<button id="devlist1" class="devlist" style="float:left;">Device Detail{0}</button>' | ||
| 1944 | html_devlist2 = '<button id="devlist2" class="devlist" style="float:right;">Device Detail2</button>\n' | ||
| 1945 | if mode != 'command': | ||
| 1946 | if testcount > 1: | ||
| 1947 | self.html += html_devlist2 | ||
| 1948 | self.html += html_devlist1.format('1') | ||
| 1949 | else: | ||
| 1950 | self.html += html_devlist1.format('') | ||
| 1951 | self.html += html_zoombox | ||
| 1952 | self.html += html_timeline.format('dmesg', self.height) | ||
| 1904 | # Function: createTimeScale | 1953 | # Function: createTimeScale |
| 1905 | # Description: | 1954 | # Description: |
| 1906 | # Create the timescale for a timeline block | 1955 | # Create the timescale for a timeline block |
| @@ -1913,7 +1962,7 @@ class Timeline: · 6 unmodified lines | |||
| 1913 | # The html code needed to display the time scale | 1962 | # The html code needed to display the time scale |
| 1914 | def createTimeScale(self, m0, mMax, tTotal, mode): | 1963 | def createTimeScale(self, m0, mMax, tTotal, mode): |
| 1915 | timescale = '<div class="t" style="right:{0}%">{1}</div>\n' | 1964 | timescale = '<div class="t" style="right:{0}%">{1}</div>\n' |
| 1916 | rline = '<div class="t" style="left:0;border-left:1px solid black;border-right:0;">Resume</div>\n' | 1965 | rline = '<div class="t" style="left:0;border-left:1px solid black;border-right:0;">{0}</div>\n' |
| 1917 | output = '<div class="timescale">\n' | 1966 | output = '<div class="timescale">\n' |
| 1918 | # set scale for timeline | 1967 | # set scale for timeline |
| 1919 | mTotal = mMax - m0 | 1968 | mTotal = mMax - m0 |
| @@ -1926,21 +1975,20 @@ class Timeline: · 6 unmodified lines | |||
| 1926 | divEdge = (mTotal - tS*(divTotal-1))*100/mTotal | 1975 | divEdge = (mTotal - tS*(divTotal-1))*100/mTotal |
| 1927 | for i in range(divTotal): | 1976 | for i in range(divTotal): |
| 1928 | htmlline = '' | 1977 | htmlline = '' |
| 1929 | if(mode == 'resume'): | 1978 | if(mode == 'suspend'): |
| 1930 | pos = '%0.3f' % (100 - ((float(i)*tS*100)/mTotal)) | ||
| 1931 | val = '%0.fms' % (float(i)*tS*1000) | ||
| 1932 | htmlline = timescale.format(pos, val) | ||
| 1933 | if(i == 0): | ||
| 1934 | htmlline = rline | ||
| 1935 | else: | ||
| 1936 | pos = '%0.3f' % (100 - ((float(i)*tS*100)/mTotal) - divEdge) | 1979 | pos = '%0.3f' % (100 - ((float(i)*tS*100)/mTotal) - divEdge) |
| 1937 | val = '%0.fms' % (float(i-divTotal+1)*tS*1000) | 1980 | val = '%0.fms' % (float(i-divTotal+1)*tS*1000) |
| 1938 | if(i == divTotal - 1): | 1981 | if(i == divTotal - 1): |
| 1939 | val = 'Suspend' | 1982 | val = mode |
| 1983 | htmlline = timescale.format(pos, val) | ||
| 1984 | else: | ||
| 1985 | pos = '%0.3f' % (100 - ((float(i)*tS*100)/mTotal)) | ||
| 1986 | val = '%0.fms' % (float(i)*tS*1000) | ||
| 1940 | htmlline = timescale.format(pos, val) | 1987 | htmlline = timescale.format(pos, val) |
| 1988 | if(i == 0): | ||
| 1989 | htmlline = rline.format(mode) | ||
| 1941 | output += htmlline | 1990 | output += htmlline |
| 1942 | output += '</div>\n' | 1991 | self.html += output+'</div>\n' |
| 1943 | return output | ||
| 1944 | 1992 | ||
| 1945 | # Class: TestProps | 1993 | # Class: TestProps |
| 1946 | # Description: | 1994 | # Description: |
| @@ -2009,7 +2057,7 @@ class ProcessMonitor: · 62 unmodified lines | |||
| 2009 | val['kern'] = kern | 2057 | val['kern'] = kern |
| 2010 | if ujiff > 0 or kjiff > 0: | 2058 | if ujiff > 0 or kjiff > 0: |
| 2011 | running[pid] = ujiff + kjiff | 2059 | running[pid] = ujiff + kjiff |
| 2012 | result = process.wait() | 2060 | process.wait() |
| 2013 | out = '' | 2061 | out = '' |
| 2014 | for pid in running: | 2062 | for pid in running: |
| 2015 | jiffies = running[pid] | 2063 | jiffies = running[pid] |
| @@ -2071,26 +2119,6 @@ def parseStamp(line, data): · 55 unmodified lines | |||
| 2071 | if not sysvals.stamp: | 2119 | if not sysvals.stamp: |
| 2072 | sysvals.stamp = data.stamp | 2120 | sysvals.stamp = data.stamp |
| 2073 | 2121 | ||
| 2074 | # Function: diffStamp | ||
| 2075 | # Description: | ||
| 2076 | # compare the host, kernel, and mode fields in 3 stamps | ||
| 2077 | # Arguments: | ||
| 2078 | # stamp1: string array with mode, kernel, and host | ||
| 2079 | # stamp2: string array with mode, kernel, and host | ||
| 2080 | # Return: | ||
| 2081 | # True if stamps differ, False if they're the same | ||
| 2082 | def diffStamp(stamp1, stamp2): | ||
| 2083 | if 'host' in stamp1 and 'host' in stamp2: | ||
| 2084 | if stamp1['host'] != stamp2['host']: | ||
| 2085 | return True | ||
| 2086 | if 'kernel' in stamp1 and 'kernel' in stamp2: | ||
| 2087 | if stamp1['kernel'] != stamp2['kernel']: | ||
| 2088 | return True | ||
| 2089 | if 'mode' in stamp1 and 'mode' in stamp2: | ||
| 2090 | if stamp1['mode'] != stamp2['mode']: | ||
| 2091 | return True | ||
| 2092 | return False | ||
| 2093 | |||
| 2094 | # Function: doesTraceLogHaveTraceEvents | 2122 | # Function: doesTraceLogHaveTraceEvents |
| 2095 | # Description: | 2123 | # Description: |
| 2096 | # Quickly determine if the ftrace log has some or all of the trace events | 2124 | # Quickly determine if the ftrace log has some or all of the trace events |
| @@ -2722,7 +2750,7 @@ def parseTraceLog(): · 625 unmodified lines | |||
| 2722 | # create blocks for orphan cg data | 2750 | # create blocks for orphan cg data |
| 2723 | for sortkey in sorted(sortlist): | 2751 | for sortkey in sorted(sortlist): |
| 2724 | cg = sortlist[sortkey] | 2752 | cg = sortlist[sortkey] |
| 2725 | name = cg.list[0].name | 2753 | name = cg.name |
| 2726 | if sysvals.isCallgraphFunc(name): | 2754 | if sysvals.isCallgraphFunc(name): |
| 2727 | vprint('Callgraph found for task %d: %.3fms, %s' % (cg.pid, (cg.end - cg.start)*1000, name)) | 2755 | vprint('Callgraph found for task %d: %.3fms, %s' % (cg.pid, (cg.end - cg.start)*1000, name)) |
| 2728 | cg.newActionFromFunction(data) | 2756 | cg.newActionFromFunction(data) |
| @@ -3100,149 +3128,154 @@ def parseKernelLog(data): · 371 unmodified lines | |||
| 3100 | data.fixupInitcallsThatDidntReturn() | 3128 | data.fixupInitcallsThatDidntReturn() |
| 3101 | return True | 3129 | return True |
| 3102 | 3130 | ||
| 3131 | def callgraphHTML(sv, hf, num, cg, title, color, devid): | ||
| 3132 | html_func_top = '<article id="{0}" class="atop" style="background:{1}">\n<input type="checkbox" class="pf" id="f{2}" checked/><label for="f{2}">{3} {4}</label>\n' | ||
| 3133 | html_func_start = '<article>\n<input type="checkbox" class="pf" id="f{0}" checked/><label for="f{0}">{1} {2}</label>\n' | ||
| 3134 | html_func_end = '</article>\n' | ||
| 3135 | html_func_leaf = '<article>{0} {1}</article>\n' | ||
| 3136 | |||
| 3137 | cgid = devid | ||
| 3138 | if cg.id: | ||
| 3139 | cgid += cg.id | ||
| 3140 | cglen = (cg.end - cg.start) * 1000 | ||
| 3141 | if cglen < sv.mincglen: | ||
| 3142 | return num | ||
| 3143 | |||
| 3144 | fmt = '<r>(%.3f ms @ '+sv.timeformat+' to '+sv.timeformat+')</r>' | ||
| 3145 | flen = fmt % (cglen, cg.start, cg.end) | ||
| 3146 | hf.write(html_func_top.format(cgid, color, num, title, flen)) | ||
| 3147 | num += 1 | ||
| 3148 | for line in cg.list: | ||
| 3149 | if(line.length < 0.000000001): | ||
| 3150 | flen = '' | ||
| 3151 | else: | ||
| 3152 | fmt = '<n>(%.3f ms @ '+sv.timeformat+')</n>' | ||
| 3153 | flen = fmt % (line.length*1000, line.time) | ||
| 3154 | if(line.freturn and line.fcall): | ||
| 3155 | hf.write(html_func_leaf.format(line.name, flen)) | ||
| 3156 | elif(line.freturn): | ||
| 3157 | hf.write(html_func_end) | ||
| 3158 | else: | ||
| 3159 | hf.write(html_func_start.format(num, line.name, flen)) | ||
| 3160 | num += 1 | ||
| 3161 | hf.write(html_func_end) | ||
| 3162 | return num | ||
| 3163 | |||
| 3164 | def addCallgraphs(sv, hf, data): | ||
| 3165 | hf.write('<section id="callgraphs" class="callgraph">\n') | ||
| 3166 | # write out the ftrace data converted to html | ||
| 3167 | num = 0 | ||
| 3168 | for p in data.phases: | ||
| 3169 | if sv.cgphase and p != sv.cgphase: | ||
| 3170 | continue | ||
| 3171 | list = data.dmesg[p]['list'] | ||
| 3172 | for devname in data.sortedDevices(p): | ||
| 3173 | dev = list[devname] | ||
| 3174 | color = 'white' | ||
| 3175 | if 'color' in data.dmesg[p]: | ||
| 3176 | color = data.dmesg[p]['color'] | ||
| 3177 | if 'color' in dev: | ||
| 3178 | color = dev['color'] | ||
| 3179 | name = devname | ||
| 3180 | if(devname in sv.devprops): | ||
| 3181 | name = sv.devprops[devname].altName(devname) | ||
| 3182 | if sv.suspendmode in suspendmodename: | ||
| 3183 | name += ' '+p | ||
| 3184 | if('ftrace' in dev): | ||
| 3185 | cg = dev['ftrace'] | ||
| 3186 | num = callgraphHTML(sv, hf, num, cg, | ||
| 3187 | name, color, dev['id']) | ||
| 3188 | if('ftraces' in dev): | ||
| 3189 | for cg in dev['ftraces']: | ||
| 3190 | num = callgraphHTML(sv, hf, num, cg, | ||
| 3191 | name+' → '+cg.name, color, dev['id']) | ||
| 3192 | |||
| 3193 | hf.write('\n\n </section>\n') | ||
| 3194 | |||
| 3103 | # Function: createHTMLSummarySimple | 3195 | # Function: createHTMLSummarySimple |
| 3104 | # Description: | 3196 | # Description: |
| 3105 | # Create summary html file for a series of tests | 3197 | # Create summary html file for a series of tests |
| 3106 | # Arguments: | 3198 | # Arguments: |
| 3107 | # testruns: array of Data objects from parseTraceLog | 3199 | # testruns: array of Data objects from parseTraceLog |
| 3108 | def createHTMLSummarySimple(testruns, htmlfile): | 3200 | def createHTMLSummarySimple(testruns, htmlfile, folder): |
| 3109 | # print out the basic summary of all the tests | ||
| 3110 | hf = open(htmlfile, 'w') | ||
| 3111 | |||
| 3112 | # write the html header first (html head, css code, up to body start) | 3201 | # write the html header first (html head, css code, up to body start) |
| 3113 | html = '<!DOCTYPE html>\n<html>\n<head>\n\ | 3202 | html = '<!DOCTYPE html>\n<html>\n<head>\n\ |
| 3114 | <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\ | 3203 | <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\ |
| 3115 | <title>AnalyzeSuspend Summary</title>\n\ | 3204 | <title>SleepGraph Summary</title>\n\ |
| 3116 | <style type=\'text/css\'>\n\ | 3205 | <style type=\'text/css\'>\n\ |
| 3117 | body {overflow-y: scroll;}\n\ | 3206 | .stamp {width: 100%;text-align:center;background:#888;line-height:30px;color:white;font: 25px Arial;}\n\ |
| 3118 | .stamp {width: 100%;text-align:center;background-color:#495E09;line-height:30px;color:white;font: 25px Arial;}\n\ | ||
| 3119 | table {width:100%;border-collapse: collapse;}\n\ | 3207 | table {width:100%;border-collapse: collapse;}\n\ |
| 3120 | .summary {font: 22px Arial;border:1px solid;}\n\ | 3208 | .summary {border:1px solid;}\n\ |
| 3121 | th {border: 1px solid black;background-color:#A7C942;color:white;}\n\ | 3209 | th {border: 1px solid black;background:#222;color:white;}\n\ |
| 3122 | td {text-align: center;}\n\ | 3210 | td {font: 16px "Times New Roman";text-align: center;}\n\ |
| 3123 | tr.alt td {background-color:#EAF2D3;}\n\ | 3211 | tr.alt td {background:#ddd;}\n\ |
| 3124 | tr.avg td {background-color:#BDE34C;}\n\ | 3212 | tr.avg td {background:#aaa;}\n\ |
| 3125 | a:link {color: #90B521;}\n\ | ||
| 3126 | a:visited {color: #495E09;}\n\ | ||
| 3127 | a:hover {color: #B1DF28;}\n\ | ||
| 3128 | a:active {color: #FFFFFF;}\n\ | ||
| 3129 | </style>\n</head>\n<body>\n' | 3213 | </style>\n</head>\n<body>\n' |
| 3130 | 3214 | ||
| 3131 | # group test header | 3215 | # group test header |
| 3132 | count = len(testruns) | 3216 | html += '<div class="stamp">%s (%d tests)</div>\n' % (folder, len(testruns)) |
| 3133 | headline_stamp = '<div class="stamp">{0} {1} {2} {3} ({4} tests)</div>\n' | ||
| 3134 | html += headline_stamp.format(sysvals.stamp['host'], | ||
| 3135 | sysvals.stamp['kernel'], sysvals.stamp['mode'], | ||
| 3136 | sysvals.stamp['time'], count) | ||
| 3137 | |||
| 3138 | # check to see if all the tests have the same value | ||
| 3139 | stampcolumns = False | ||
| 3140 | for data in testruns: | ||
| 3141 | if diffStamp(sysvals.stamp, data.stamp): | ||
| 3142 | stampcolumns = True | ||
| 3143 | break | ||
| 3144 | |||
| 3145 | th = '\t<th>{0}</th>\n' | 3217 | th = '\t<th>{0}</th>\n' |
| 3146 | td = '\t<td>{0}</td>\n' | 3218 | td = '\t<td>{0}</td>\n' |
| 3147 | tdlink = '\t<td><a href="{0}">Click Here</a></td>\n' | 3219 | tdlink = '\t<td><a href="{0}">html</a></td>\n' |
| 3148 | 3220 | ||
| 3149 | # table header | 3221 | # table header |
| 3150 | html += '<table class="summary">\n<tr>\n' | 3222 | html += '<table class="summary">\n<tr>\n' + th.format('#') +\ |
| 3151 | html += th.format("Test #") | 3223 | th.format('Mode') + th.format('Host') + th.format('Kernel') +\ |
| 3152 | if stampcolumns: | 3224 | th.format('Test Time') + th.format('Suspend') + th.format('Resume') +\ |
| 3153 | html += th.format("Hostname") | 3225 | th.format('Detail') + '</tr>\n' |
| 3154 | html += th.format("Kernel Version") | ||
| 3155 | html += th.format("Suspend Mode") | ||
| 3156 | html += th.format("Test Time") | ||
| 3157 | html += th.format("Suspend Time") | ||
| 3158 | html += th.format("Resume Time") | ||
| 3159 | html += th.format("Detail") | ||
| 3160 | html += '</tr>\n' | ||
| 3161 | 3226 | ||
| 3162 | # test data, 1 row per test | 3227 | # test data, 1 row per test |
| 3163 | sTimeAvg = 0.0 | 3228 | avg = '<tr class="avg"><td></td><td></td><td></td><td></td>'+\ |
| 3164 | rTimeAvg = 0.0 | 3229 | '<td>Average of {0} {1} tests</td><td>{2}</td><td>{3}</td><td></td></tr>\n' |
| 3165 | num = 1 | 3230 | sTimeAvg = rTimeAvg = 0.0 |
| 3166 | for data in testruns: | 3231 | mode = '' |
| 3167 | # data.end is the end of post_resume | 3232 | num = 0 |
| 3168 | resumeEnd = data.dmesg['resume_complete']['end'] | 3233 | for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'])): |
| 3234 | if mode != data['mode']: | ||
| 3235 | # test average line | ||
| 3236 | if(num > 0): | ||
| 3237 | sTimeAvg /= (num - 1) | ||
| 3238 | rTimeAvg /= (num - 1) | ||
| 3239 | html += avg.format('%d' % (num - 1), mode, | ||
| 3240 | '%3.3f ms' % sTimeAvg, '%3.3f ms' % rTimeAvg) | ||
| 3241 | sTimeAvg = rTimeAvg = 0.0 | ||
| 3242 | mode = data['mode'] | ||
| 3243 | num = 1 | ||
| 3244 | # alternate row color | ||
| 3169 | if num % 2 == 1: | 3245 | if num % 2 == 1: |
| 3170 | html += '<tr class="alt">\n' | 3246 | html += '<tr class="alt">\n' |
| 3171 | else: | 3247 | else: |
| 3172 | html += '<tr>\n' | 3248 | html += '<tr>\n' |
| 3173 | 3249 | html += td.format("%d" % num) | |
| 3174 | # test num | ||
| 3175 | html += td.format("test %d" % num) | ||
| 3176 | num += 1 | 3250 | num += 1 |
| 3177 | if stampcolumns: | 3251 | # basic info |
| 3178 | # host name | 3252 | for item in ['mode', 'host', 'kernel', 'time']: |
| 3179 | val = "unknown" | ||
| 3180 | if('host' in data.stamp): | ||
| 3181 | val = data.stamp['host'] | ||
| 3182 | html += td.format(val) | ||
| 3183 | # host kernel | ||
| 3184 | val = "unknown" | 3253 | val = "unknown" |
| 3185 | if('kernel' in data.stamp): | 3254 | if(item in data): |
| 3186 | val = data.stamp['kernel'] | 3255 | val = data[item] |
| 3187 | html += td.format(val) | 3256 | html += td.format(val) |
| 3188 | # suspend mode | ||
| 3189 | val = "unknown" | ||
| 3190 | if('mode' in data.stamp): | ||
| 3191 | val = data.stamp['mode'] | ||
| 3192 | html += td.format(val) | ||
| 3193 | # test time | ||
| 3194 | val = "unknown" | ||
| 3195 | if('time' in data.stamp): | ||
| 3196 | val = data.stamp['time'] | ||
| 3197 | html += td.format(val) | ||
| 3198 | # suspend time | 3257 | # suspend time |
| 3199 | sTime = (data.tSuspended - data.start)*1000 | 3258 | sTime = float(data['suspend']) |
| 3200 | sTimeAvg += sTime | 3259 | sTimeAvg += sTime |
| 3201 | html += td.format("%3.3f ms" % sTime) | 3260 | html += td.format('%.3f ms' % sTime) |
| 3202 | # resume time | 3261 | # resume time |
| 3203 | rTime = (resumeEnd - data.tResumed)*1000 | 3262 | rTime = float(data['resume']) |
| 3204 | rTimeAvg += rTime | 3263 | rTimeAvg += rTime |
| 3205 | html += td.format("%3.3f ms" % rTime) | 3264 | html += td.format('%.3f ms' % rTime) |
| 3206 | # link to the output html | 3265 | # link to the output html |
| 3207 | html += tdlink.format(data.outfile) | 3266 | html += tdlink.format(data['url']) + '</tr>\n' |
| 3208 | 3267 | # last test average line | |
| 3209 | html += '</tr>\n' | 3268 | if(num > 0): |
| 3210 | 3269 | sTimeAvg /= (num - 1) | |
| 3211 | # last line: test average | 3270 | rTimeAvg /= (num - 1) |
| 3212 | if(count > 0): | 3271 | html += avg.format('%d' % (num - 1), mode, |
| 3213 | sTimeAvg /= count | 3272 | '%3.3f ms' % sTimeAvg, '%3.3f ms' % rTimeAvg) |
| 3214 | rTimeAvg /= count | ||
| 3215 | html += '<tr class="avg">\n' | ||
| 3216 | html += td.format('Average') # name | ||
| 3217 | if stampcolumns: | ||
| 3218 | html += td.format('') # host | ||
| 3219 | html += td.format('') # kernel | ||
| 3220 | html += td.format('') # mode | ||
| 3221 | html += td.format('') # time | ||
| 3222 | html += td.format("%3.3f ms" % sTimeAvg) # suspend time | ||
| 3223 | html += td.format("%3.3f ms" % rTimeAvg) # resume time | ||
| 3224 | html += td.format('') # output link | ||
| 3225 | html += '</tr>\n' | ||
| 3226 | 3273 | ||
| 3227 | # flush the data to file | 3274 | # flush the data to file |
| 3228 | hf.write(html+'</table>\n') | 3275 | hf = open(htmlfile, 'w') |
| 3229 | hf.write('</body>\n</html>\n') | 3276 | hf.write(html+'</table>\n</body>\n</html>\n') |
| 3230 | hf.close() | 3277 | hf.close() |
| 3231 | 3278 | ||
| 3232 | def htmlTitle(): | ||
| 3233 | modename = { | ||
| 3234 | 'freeze': 'Freeze (S0)', | ||
| 3235 | 'standby': 'Standby (S1)', | ||
| 3236 | 'mem': 'Suspend (S3)', | ||
| 3237 | 'disk': 'Hibernate (S4)' | ||
| 3238 | } | ||
| 3239 | kernel = sysvals.stamp['kernel'] | ||
| 3240 | host = sysvals.hostname[0].upper()+sysvals.hostname[1:] | ||
| 3241 | mode = sysvals.suspendmode | ||
| 3242 | if sysvals.suspendmode in modename: | ||
| 3243 | mode = modename[sysvals.suspendmode] | ||
| 3244 | return host+' '+mode+' '+kernel | ||
| 3245 | |||
| 3246 | def ordinal(value): | 3279 | def ordinal(value): |
| 3247 | suffix = 'th' | 3280 | suffix = 'th' |
| 3248 | if value < 10 or value > 19: | 3281 | if value < 10 or value > 19: |
| @@ -3272,24 +3305,11 @@ def createHTML(testruns): · 23 unmodified lines | |||
| 3272 | kerror = True | 3305 | kerror = True |
| 3273 | data.normalizeTime(testruns[-1].tSuspended) | 3306 | data.normalizeTime(testruns[-1].tSuspended) |
| 3274 | 3307 | ||
| 3275 | x2changes = ['', 'absolute'] | ||
| 3276 | if len(testruns) > 1: | ||
| 3277 | x2changes = ['1', 'relative'] | ||
| 3278 | # html function templates | 3308 | # html function templates |
| 3279 | headline_version = '<div class="version"><a href="https://01.org/suspendresume">AnalyzeSuspend v%s</a></div>' % sysvals.version | ||
| 3280 | headline_stamp = '<div class="stamp">{0} {1} {2} {3}</div>\n' | ||
| 3281 | html_devlist1 = '<button id="devlist1" class="devlist" style="float:left;">Device Detail%s</button>' % x2changes[0] | ||
| 3282 | html_zoombox = '<center><button id="zoomin">ZOOM IN +</button><button id="zoomout">ZOOM OUT -</button><button id="zoomdef">ZOOM 1:1</button></center>\n' | ||
| 3283 | html_devlist2 = '<button id="devlist2" class="devlist" style="float:right;">Device Detail2</button>\n' | ||
| 3284 | html_timeline = '<div id="dmesgzoombox" class="zoombox">\n<div id="{0}" class="timeline" style="height:{1}px">\n' | ||
| 3285 | html_tblock = '<div id="block{0}" class="tblock" style="left:{1}%;width:{2}%;"><div class="tback" style="height:{3}px"></div>\n' | ||
| 3286 | html_device = '<div id="{0}" title="{1}" class="thread{7}" style="left:{2}%;top:{3}px;height:{4}px;width:{5}%;{8}">{6}</div>\n' | ||
| 3287 | html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">ERROR→</div>\n' | 3309 | html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">ERROR→</div>\n' |
| 3288 | html_traceevent = '<div title="{0}" class="traceevent{6}" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;{7}">{5}</div>\n' | 3310 | html_traceevent = '<div title="{0}" class="traceevent{6}" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;{7}">{5}</div>\n' |
| 3289 | html_cpuexec = '<div class="jiffie" style="left:{0}%;top:{1}px;height:{2}px;width:{3}%;background:{4};"></div>\n' | 3311 | html_cpuexec = '<div class="jiffie" style="left:{0}%;top:{1}px;height:{2}px;width:{3}%;background:{4};"></div>\n' |
| 3290 | html_phase = '<div class="phase" style="left:{0}%;width:{1}%;top:{2}px;height:{3}px;background-color:{4}">{5}</div>\n' | 3312 | html_legend = '<div id="p{3}" class="square" style="left:{0}%;background:{1}"> {2}</div>\n' |
| 3291 | html_phaselet = '<div id="{0}" class="phaselet" style="left:{1}%;width:{2}%;background:{3}"></div>\n' | ||
| 3292 | html_legend = '<div id="p{3}" class="square" style="left:{0}%;background-color:{1}"> {2}</div>\n' | ||
| 3293 | html_timetotal = '<table class="time1">\n<tr>'\ | 3313 | html_timetotal = '<table class="time1">\n<tr>'\ |
| 3294 | '<td class="green" title="{3}">{2} Suspend Time: <b>{0} ms</b></td>'\ | 3314 | '<td class="green" title="{3}">{2} Suspend Time: <b>{0} ms</b></td>'\ |
| 3295 | '<td class="yellow" title="{4}">{2} Resume Time: <b>{1} ms</b></td>'\ | 3315 | '<td class="yellow" title="{4}">{2} Resume Time: <b>{1} ms</b></td>'\ |
| @@ -3311,20 +3331,18 @@ def createHTML(testruns): · 15 unmodified lines | |||
| 3311 | '</tr>\n</table>\n' | 3331 | '</tr>\n</table>\n' |
| 3312 | 3332 | ||
| 3313 | # html format variables | 3333 | # html format variables |
| 3314 | hoverZ = 'z-index:8;' | ||
| 3315 | if sysvals.usedevsrc: | ||
| 3316 | hoverZ = '' | ||
| 3317 | scaleH = 20 | 3334 | scaleH = 20 |
| 3318 | scaleTH = 20 | ||
| 3319 | if kerror: | 3335 | if kerror: |
| 3320 | scaleH = 40 | 3336 | scaleH = 40 |
| 3321 | scaleTH = 60 | ||
| 3322 | 3337 | ||
| 3323 | # device timeline | 3338 | # device timeline |
| 3324 | vprint('Creating Device Timeline...') | 3339 | vprint('Creating Device Timeline...') |
| 3325 | 3340 | ||
| 3326 | devtl = Timeline(30, scaleH) | 3341 | devtl = Timeline(30, scaleH) |
| 3327 | 3342 | ||
| 3343 | # write the test title and general info header | ||
| 3344 | devtl.createHeader(sysvals) | ||
| 3345 | |||
| 3328 | # Generate the header for this timeline | 3346 | # Generate the header for this timeline |
| 3329 | for data in testruns: | 3347 | for data in testruns: |
| 3330 | tTotal = data.end - data.start | 3348 | tTotal = data.end - data.start |
| @@ -3346,7 +3364,7 @@ def createHTML(testruns): · 15 unmodified lines | |||
| 3346 | if(len(testruns) > 1): | 3364 | if(len(testruns) > 1): |
| 3347 | testdesc = ordinal(data.testnumber+1)+' '+testdesc | 3365 | testdesc = ordinal(data.testnumber+1)+' '+testdesc |
| 3348 | thtml = html_timetotal3.format(run_time, testdesc) | 3366 | thtml = html_timetotal3.format(run_time, testdesc) |
| 3349 | devtl.html['header'] += thtml | 3367 | devtl.html += thtml |
| 3350 | elif data.fwValid: | 3368 | elif data.fwValid: |
| 3351 | suspend_time = '%.0f'%(sktime + (data.fwSuspend/1000000.0)) | 3369 | suspend_time = '%.0f'%(sktime + (data.fwSuspend/1000000.0)) |
| 3352 | resume_time = '%.0f'%(rktime + (data.fwResume/1000000.0)) | 3370 | resume_time = '%.0f'%(rktime + (data.fwResume/1000000.0)) |
| @@ -3363,10 +3381,10 @@ def createHTML(testruns): · 10 unmodified lines | |||
| 3363 | else: | 3381 | else: |
| 3364 | thtml = html_timetotal2.format(suspend_time, low_time, \ | 3382 | thtml = html_timetotal2.format(suspend_time, low_time, \ |
| 3365 | resume_time, testdesc1, stitle, rtitle) | 3383 | resume_time, testdesc1, stitle, rtitle) |
| 3366 | devtl.html['header'] += thtml | 3384 | devtl.html += thtml |
| 3367 | sftime = '%.3f'%(data.fwSuspend / 1000000.0) | 3385 | sftime = '%.3f'%(data.fwSuspend / 1000000.0) |
| 3368 | rftime = '%.3f'%(data.fwResume / 1000000.0) | 3386 | rftime = '%.3f'%(data.fwResume / 1000000.0) |
| 3369 | devtl.html['header'] += html_timegroups.format('%.3f'%sktime, \ | 3387 | devtl.html += html_timegroups.format('%.3f'%sktime, \ |
| 3370 | sftime, rftime, '%.3f'%rktime, testdesc2, sysvals.suspendmode) | 3388 | sftime, rftime, '%.3f'%rktime, testdesc2, sysvals.suspendmode) |
| 3371 | else: | 3389 | else: |
| 3372 | suspend_time = '%.3f' % sktime | 3390 | suspend_time = '%.3f' % sktime |
| @@ -3382,7 +3400,7 @@ def createHTML(testruns): · 9 unmodified lines | |||
| 3382 | else: | 3400 | else: |
| 3383 | thtml = html_timetotal2.format(suspend_time, low_time, \ | 3401 | thtml = html_timetotal2.format(suspend_time, low_time, \ |
| 3384 | resume_time, testdesc, stitle, rtitle) | 3402 | resume_time, testdesc, stitle, rtitle) |
| 3385 | devtl.html['header'] += thtml | 3403 | devtl.html += thtml |
| 3386 | 3404 | ||
| 3387 | # time scale for potentially multiple datasets | 3405 | # time scale for potentially multiple datasets |
| 3388 | t0 = testruns[0].start | 3406 | t0 = testruns[0].start |
| @@ -3429,15 +3447,8 @@ def createHTML(testruns): · 40 unmodified lines | |||
| 3429 | devtl.getPhaseRows(threadlist, devtl.rows) | 3447 | devtl.getPhaseRows(threadlist, devtl.rows) |
| 3430 | devtl.calcTotalRows() | 3448 | devtl.calcTotalRows() |
| 3431 | 3449 | ||
| 3432 | # create bounding box, add buttons | ||
| 3433 | if sysvals.suspendmode != 'command': | ||
| 3434 | devtl.html['timeline'] += html_devlist1 | ||
| 3435 | if len(testruns) > 1: | ||
| 3436 | devtl.html['timeline'] += html_devlist2 | ||
| 3437 | devtl.html['timeline'] += html_zoombox | ||
| 3438 | devtl.html['timeline'] += html_timeline.format('dmesg', devtl.height) | ||
| 3439 | |||
| 3440 | # draw the full timeline | 3450 | # draw the full timeline |
| 3451 | devtl.createZoomBox(sysvals.suspendmode, len(testruns)) | ||
| 3441 | phases = {'suspend':[],'resume':[]} | 3452 | phases = {'suspend':[],'resume':[]} |
| 3442 | for phase in data.dmesg: | 3453 | for phase in data.dmesg: |
| 3443 | if 'resume' in phase: | 3454 | if 'resume' in phase: |
| @@ -3452,37 +3463,36 @@ def createHTML(testruns): · 8 unmodified lines | |||
| 3452 | # draw suspend and resume blocks separately | 3463 | # draw suspend and resume blocks separately |
| 3453 | bname = '%s%d' % (dir[0], data.testnumber) | 3464 | bname = '%s%d' % (dir[0], data.testnumber) |
| 3454 | if dir == 'suspend': | 3465 | if dir == 'suspend': |
| 3455 | m0 = testruns[data.testnumber].start | 3466 | m0 = data.start |
| 3456 | mMax = testruns[data.testnumber].tSuspended | 3467 | mMax = data.tSuspended |
| 3457 | mTotal = mMax - m0 | ||
| 3458 | left = '%f' % (((m0-t0)*100.0)/tTotal) | 3468 | left = '%f' % (((m0-t0)*100.0)/tTotal) |
| 3459 | else: | 3469 | else: |
| 3460 | m0 = testruns[data.testnumber].tSuspended | 3470 | m0 = data.tSuspended |
| 3461 | mMax = testruns[data.testnumber].end | 3471 | mMax = data.end |
| 3462 | # in an x2 run, remove any gap between blocks | 3472 | # in an x2 run, remove any gap between blocks |
| 3463 | if len(testruns) > 1 and data.testnumber == 0: | 3473 | if len(testruns) > 1 and data.testnumber == 0: |
| 3464 | mMax = testruns[1].start | 3474 | mMax = testruns[1].start |
| 3465 | mTotal = mMax - m0 | ||
| 3466 | left = '%f' % ((((m0-t0)*100.0)+sysvals.srgap/2)/tTotal) | 3475 | left = '%f' % ((((m0-t0)*100.0)+sysvals.srgap/2)/tTotal) |
| 3476 | mTotal = mMax - m0 | ||
| 3467 | # if a timeline block is 0 length, skip altogether | 3477 | # if a timeline block is 0 length, skip altogether |
| 3468 | if mTotal == 0: | 3478 | if mTotal == 0: |
| 3469 | continue | 3479 | continue |
| 3470 | width = '%f' % (((mTotal*100.0)-sysvals.srgap/2)/tTotal) | 3480 | width = '%f' % (((mTotal*100.0)-sysvals.srgap/2)/tTotal) |
| 3471 | devtl.html['timeline'] += html_tblock.format(bname, left, width, devtl.scaleH) | 3481 | devtl.html += devtl.html_tblock.format(bname, left, width, devtl.scaleH) |
| 3472 | for b in sorted(phases[dir]): | 3482 | for b in sorted(phases[dir]): |
| 3473 | # draw the phase color background | 3483 | # draw the phase color background |
| 3474 | phase = data.dmesg[b] | 3484 | phase = data.dmesg[b] |
| 3475 | length = phase['end']-phase['start'] | 3485 | length = phase['end']-phase['start'] |
| 3476 | left = '%f' % (((phase['start']-m0)*100.0)/mTotal) | 3486 | left = '%f' % (((phase['start']-m0)*100.0)/mTotal) |
| 3477 | width = '%f' % ((length*100.0)/mTotal) | 3487 | width = '%f' % ((length*100.0)/mTotal) |
| 3478 | devtl.html['timeline'] += html_phase.format(left, width, \ | 3488 | devtl.html += devtl.html_phase.format(left, width, \ |
| 3479 | '%.3f'%devtl.scaleH, '%.3f'%devtl.bodyH, \ | 3489 | '%.3f'%devtl.scaleH, '%.3f'%devtl.bodyH, \ |
| 3480 | data.dmesg[b]['color'], '') | 3490 | data.dmesg[b]['color'], '') |
| 3481 | for e in data.errorinfo[dir]: | 3491 | for e in data.errorinfo[dir]: |
| 3482 | # draw red lines for any kernel errors found | 3492 | # draw red lines for any kernel errors found |
| 3483 | t, err = e | 3493 | t, err = e |
| 3484 | right = '%f' % (((mMax-t)*100.0)/mTotal) | 3494 | right = '%f' % (((mMax-t)*100.0)/mTotal) |
| 3485 | devtl.html['timeline'] += html_error.format(right, err) | 3495 | devtl.html += html_error.format(right, err) |
| 3486 | for b in sorted(phases[dir]): | 3496 | for b in sorted(phases[dir]): |
| 3487 | # draw the devices for this phase | 3497 | # draw the devices for this phase |
| 3488 | phaselist = data.dmesg[b]['list'] | 3498 | phaselist = data.dmesg[b]['list'] |
| @@ -3496,7 +3506,7 @@ def createHTML(testruns): · 7 unmodified lines | |||
| 3496 | if 'htmlclass' in dev: | 3506 | if 'htmlclass' in dev: |
| 3497 | xtraclass = dev['htmlclass'] | 3507 | xtraclass = dev['htmlclass'] |
| 3498 | if 'color' in dev: | 3508 | if 'color' in dev: |
| 3499 | xtrastyle = 'background-color:%s;' % dev['color'] | 3509 | xtrastyle = 'background:%s;' % dev['color'] |
| 3500 | if(d in sysvals.devprops): | 3510 | if(d in sysvals.devprops): |
| 3501 | name = sysvals.devprops[d].altName(d) | 3511 | name = sysvals.devprops[d].altName(d) |
| 3502 | xtraclass = sysvals.devprops[d].xtraClass() | 3512 | xtraclass = sysvals.devprops[d].xtraClass() |
| @@ -3521,7 +3531,7 @@ def createHTML(testruns): · 18 unmodified lines | |||
| 3521 | title += 'post_resume_process' | 3531 | title += 'post_resume_process' |
| 3522 | else: | 3532 | else: |
| 3523 | title += b | 3533 | title += b |
| 3524 | devtl.html['timeline'] += html_device.format(dev['id'], \ | 3534 | devtl.html += devtl.html_device.format(dev['id'], \ |
| 3525 | title, left, top, '%.3f'%rowheight, width, \ | 3535 | title, left, top, '%.3f'%rowheight, width, \ |
| 3526 | d+drv, xtraclass, xtrastyle) | 3536 | d+drv, xtraclass, xtrastyle) |
| 3527 | if('cpuexec' in dev): | 3537 | if('cpuexec' in dev): |
| @@ -3535,7 +3545,7 @@ def createHTML(testruns): · 7 unmodified lines | |||
| 3535 | left = '%f' % (((start-m0)*100)/mTotal) | 3545 | left = '%f' % (((start-m0)*100)/mTotal) |
| 3536 | width = '%f' % ((end-start)*100/mTotal) | 3546 | width = '%f' % ((end-start)*100/mTotal) |
| 3537 | color = 'rgba(255, 0, 0, %f)' % j | 3547 | color = 'rgba(255, 0, 0, %f)' % j |
| 3538 | devtl.html['timeline'] += \ | 3548 | devtl.html += \ |
| 3539 | html_cpuexec.format(left, top, height, width, color) | 3549 | html_cpuexec.format(left, top, height, width, color) |
| 3540 | if('src' not in dev): | 3550 | if('src' not in dev): |
| 3541 | continue | 3551 | continue |
| @@ -3548,20 +3558,20 @@ def createHTML(testruns): · 6 unmodified lines | |||
| 3548 | xtrastyle = '' | 3558 | xtrastyle = '' |
| 3549 | if e.color: | 3559 | if e.color: |
| 3550 | xtrastyle = 'background:%s;' % e.color | 3560 | xtrastyle = 'background:%s;' % e.color |
| 3551 | devtl.html['timeline'] += \ | 3561 | devtl.html += \ |
| 3552 | html_traceevent.format(e.title(), \ | 3562 | html_traceevent.format(e.title(), \ |
| 3553 | left, top, height, width, e.text(), '', xtrastyle) | 3563 | left, top, height, width, e.text(), '', xtrastyle) |
| 3554 | # draw the time scale, try to make the number of labels readable | 3564 | # draw the time scale, try to make the number of labels readable |
| 3555 | devtl.html['timeline'] += devtl.createTimeScale(m0, mMax, tTotal, dir) | 3565 | devtl.createTimeScale(m0, mMax, tTotal, dir) |
| 3556 | devtl.html['timeline'] += '</div>\n' | 3566 | devtl.html += '</div>\n' |
| 3557 | 3567 | ||
| 3558 | # timeline is finished | 3568 | # timeline is finished |
| 3559 | devtl.html['timeline'] += '</div>\n</div>\n' | 3569 | devtl.html += '</div>\n</div>\n' |
| 3560 | 3570 | ||
| 3561 | # draw a legend which describes the phases by color | 3571 | # draw a legend which describes the phases by color |
| 3562 | if sysvals.suspendmode != 'command': | 3572 | if sysvals.suspendmode != 'command': |
| 3563 | data = testruns[-1] | 3573 | data = testruns[-1] |
| 3564 | devtl.html['legend'] = '<div class="legend">\n' | 3574 | devtl.html += '<div class="legend">\n' |
| 3565 | pdelta = 100.0/len(data.phases) | 3575 | pdelta = 100.0/len(data.phases) |
| 3566 | pmargin = pdelta / 4.0 | 3576 | pmargin = pdelta / 4.0 |
| 3567 | for phase in data.phases: | 3577 | for phase in data.phases: |
| @@ -3571,127 +3581,41 @@ def createHTML(testruns): · 3 unmodified lines | |||
| 3571 | id += tmp[1][0] | 3581 | id += tmp[1][0] |
| 3572 | order = '%.2f' % ((data.dmesg[phase]['order'] * pdelta) + pmargin) | 3582 | order = '%.2f' % ((data.dmesg[phase]['order'] * pdelta) + pmargin) |
| 3573 | name = string.replace(phase, '_', ' ') | 3583 | name = string.replace(phase, '_', ' ') |
| 3574 | devtl.html['legend'] += html_legend.format(order, \ | 3584 | devtl.html += html_legend.format(order, \ |
| 3575 | data.dmesg[phase]['color'], name, id) | 3585 | data.dmesg[phase]['color'], name, id) |
| 3576 | devtl.html['legend'] += '</div>\n' | 3586 | devtl.html += '</div>\n' |
| 3577 | 3587 | ||
| 3578 | hf = open(sysvals.htmlfile, 'w') | 3588 | hf = open(sysvals.htmlfile, 'w') |
| 3579 | 3589 | ||
| 3580 | if not sysvals.cgexp: | ||
| 3581 | cgchk = 'checked' | ||
| 3582 | cgnchk = 'not(:checked)' | ||
| 3583 | else: | ||
| 3584 | cgchk = 'not(:checked)' | ||
| 3585 | cgnchk = 'checked' | ||
| 3586 | |||
| 3587 | # write the html header first (html head, css code, up to body start) | ||
| 3588 | html_header = '<!DOCTYPE html>\n<html>\n<head>\n\ | ||
| 3589 | <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\ | ||
| 3590 | <title>'+htmlTitle()+'</title>\n\ | ||
| 3591 | <style type=\'text/css\'>\n\ | ||
| 3592 | body {overflow-y:scroll;}\n\ | ||
| 3593 | .stamp {width:100%;text-align:center;background-color:gray;line-height:30px;color:white;font:25px Arial;}\n\ | ||
| 3594 | .callgraph {margin-top:30px;box-shadow:5px 5px 20px black;}\n\ | ||
| 3595 | .callgraph article * {padding-left:28px;}\n\ | ||
| 3596 | h1 {color:black;font:bold 30px Times;}\n\ | ||
| 3597 | t0 {color:black;font:bold 30px Times;}\n\ | ||
| 3598 | t1 {color:black;font:30px Times;}\n\ | ||
| 3599 | t2 {color:black;font:25px Times;}\n\ | ||
| 3600 | t3 {color:black;font:20px Times;white-space:nowrap;}\n\ | ||
| 3601 | t4 {color:black;font:bold 30px Times;line-height:60px;white-space:nowrap;}\n\ | ||
| 3602 | cS {font:bold 13px Times;}\n\ | ||
| 3603 | table {width:100%;}\n\ | ||
| 3604 | .gray {background-color:rgba(80,80,80,0.1);}\n\ | ||
| 3605 | .green {background-color:rgba(204,255,204,0.4);}\n\ | ||
| 3606 | .purple {background-color:rgba(128,0,128,0.2);}\n\ | ||
| 3607 | .yellow {background-color:rgba(255,255,204,0.4);}\n\ | ||
| 3608 | .time1 {font:22px Arial;border:1px solid;}\n\ | ||
| 3609 | .time2 {font:15px Arial;border-bottom:1px solid;border-left:1px solid;border-right:1px solid;}\n\ | ||
| 3610 | td {text-align:center;}\n\ | ||
| 3611 | r {color:#500000;font:15px Tahoma;}\n\ | ||
| 3612 | n {color:#505050;font:15px Tahoma;}\n\ | ||
| 3613 | .tdhl {color:red;}\n\ | ||
| 3614 | .hide {display:none;}\n\ | ||
| 3615 | .pf {display:none;}\n\ | ||
| 3616 | .pf:'+cgchk+' + label {background:url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/><rect x="8" y="4" width="2" height="10" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\ | ||
| 3617 | .pf:'+cgnchk+' ~ label {background:url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\ | ||
| 3618 | .pf:'+cgchk+' ~ *:not(:nth-child(2)) {display:none;}\n\ | ||
| 3619 | .zoombox {position:relative;width:100%;overflow-x:scroll;-webkit-user-select:none;-moz-user-select:none;user-select:none;}\n\ | ||
| 3620 | .timeline {position:relative;font-size:14px;cursor:pointer;width:100%; overflow:hidden;background:linear-gradient(#cccccc, white);}\n\ | ||
| 3621 | .thread {position:absolute;height:0%;overflow:hidden;z-index:7;line-height:30px;font-size:14px;border:1px solid;text-align:center;white-space:nowrap;}\n\ | ||
| 3622 | .thread.ps {border-radius:3px;background:linear-gradient(to top, #ccc, #eee);}\n\ | ||
| 3623 | .thread:hover {background-color:white;border:1px solid red;'+hoverZ+'}\n\ | ||
| 3624 | .thread.sec,.thread.sec:hover {background-color:black;border:0;color:white;line-height:15px;font-size:10px;}\n\ | ||
| 3625 | .hover {background-color:white;border:1px solid red;'+hoverZ+'}\n\ | ||
| 3626 | .hover.sync {background-color:white;}\n\ | ||
| 3627 | .hover.bg,.hover.kth,.hover.sync,.hover.ps {background-color:white;}\n\ | ||
| 3628 | .jiffie {position:absolute;pointer-events: none;z-index:8;}\n\ | ||
| 3629 | .traceevent {position:absolute;font-size:10px;z-index:7;overflow:hidden;color:black;text-align:center;white-space:nowrap;border-radius:5px;border:1px solid black;background:linear-gradient(to bottom right,#CCC,#969696);}\n\ | ||
| 3630 | .traceevent:hover {color:white;font-weight:bold;border:1px solid white;}\n\ | ||
| 3631 | .phase {position:absolute;overflow:hidden;border:0px;text-align:center;}\n\ | ||
| 3632 | .phaselet {position:absolute;overflow:hidden;border:0px;text-align:center;height:100px;font-size:24px;}\n\ | ||
| 3633 | .t {position:absolute;line-height:'+('%d'%scaleTH)+'px;pointer-events:none;top:0;height:100%;border-right:1px solid black;z-index:6;}\n\ | ||
| 3634 | .err {position:absolute;top:0%;height:100%;border-right:3px solid red;color:red;font:bold 14px Times;line-height:18px;}\n\ | ||
| 3635 | .legend {position:relative; width:100%; height:40px; text-align:center;margin-bottom:20px}\n\ | ||
| 3636 | .legend .square {position:absolute;cursor:pointer;top:10px; width:0px;height:20px;border:1px solid;padding-left:20px;}\n\ | ||
| 3637 | button {height:40px;width:200px;margin-bottom:20px;margin-top:20px;font-size:24px;}\n\ | ||
| 3638 | .logbtn {position:relative;float:right;height:25px;width:50px;margin-top:3px;margin-bottom:0;font-size:10px;text-align:center;}\n\ | ||
| 3639 | .devlist {position:'+x2changes[1]+';width:190px;}\n\ | ||
| 3640 | a:link {color:white;text-decoration:none;}\n\ | ||
| 3641 | a:visited {color:white;}\n\ | ||
| 3642 | a:hover {color:white;}\n\ | ||
| 3643 | a:active {color:white;}\n\ | ||
| 3644 | .version {position:relative;float:left;color:white;font-size:10px;line-height:30px;margin-left:10px;}\n\ | ||
| 3645 | #devicedetail {height:100px;box-shadow:5px 5px 20px black;}\n\ | ||
| 3646 | .tblock {position:absolute;height:100%;background-color:#ddd;}\n\ | ||
| 3647 | .tback {position:absolute;width:100%;background:linear-gradient(#ccc, #ddd);}\n\ | ||
| 3648 | .bg {z-index:1;}\n\ | ||
| 3649 | </style>\n</head>\n<body>\n' | ||
| 3650 | |||
| 3651 | # no header or css if its embedded | 3590 | # no header or css if its embedded |
| 3652 | if(sysvals.embedded): | 3591 | if(sysvals.embedded): |
| 3653 | hf.write('pass True tSus %.3f tRes %.3f tLow %.3f fwvalid %s tSus %.3f tRes %.3f\n' % | 3592 | hf.write('pass True tSus %.3f tRes %.3f tLow %.3f fwvalid %s tSus %.3f tRes %.3f\n' % |
| 3654 | (data.tSuspended-data.start, data.end-data.tSuspended, data.tLow, data.fwValid, \ | 3593 | (data.tSuspended-data.start, data.end-data.tSuspended, data.tLow, data.fwValid, \ |
| 3655 | data.fwSuspend/1000000, data.fwResume/1000000)) | 3594 | data.fwSuspend/1000000, data.fwResume/1000000)) |
| 3656 | else: | 3595 | else: |
| 3657 | hf.write(html_header) | 3596 | addCSS(hf, sysvals, len(testruns), kerror) |
| 3658 | |||
| 3659 | # write the test title and general info header | ||
| 3660 | if(sysvals.stamp['time'] != ""): | ||
| 3661 | hf.write(headline_version) | ||
| 3662 | if sysvals.logmsg: | ||
| 3663 | hf.write('<button id="showtest" class="logbtn">log</button>') | ||
| 3664 | if sysvals.addlogs and sysvals.dmesgfile: | ||
| 3665 | hf.write('<button id="showdmesg" class="logbtn">dmesg</button>') | ||
| 3666 | if sysvals.addlogs and sysvals.ftracefile: | ||
| 3667 | hf.write('<button id="showftrace" class="logbtn">ftrace</button>') | ||
| 3668 | hf.write(headline_stamp.format(sysvals.stamp['host'], | ||
| 3669 | sysvals.stamp['kernel'], sysvals.stamp['mode'], \ | ||
| 3670 | sysvals.stamp['time'])) | ||
| 3671 | 3597 | ||
| 3672 | # write the device timeline | 3598 | # write the device timeline |
| 3673 | hf.write(devtl.html['header']) | 3599 | hf.write(devtl.html) |
| 3674 | hf.write(devtl.html['timeline']) | ||
| 3675 | hf.write(devtl.html['legend']) | ||
| 3676 | hf.write('<div id="devicedetailtitle"></div>\n') | 3600 | hf.write('<div id="devicedetailtitle"></div>\n') |
| 3677 | hf.write('<div id="devicedetail" style="display:none;">\n') | 3601 | hf.write('<div id="devicedetail" style="display:none;">\n') |
| 3678 | # draw the colored boxes for the device detail section | 3602 | # draw the colored boxes for the device detail section |
| 3679 | for data in testruns: | 3603 | for data in testruns: |
| 3680 | hf.write('<div id="devicedetail%d">\n' % data.testnumber) | 3604 | hf.write('<div id="devicedetail%d">\n' % data.testnumber) |
| 3681 | pscolor = 'linear-gradient(to top left, #ccc, #eee)' | 3605 | pscolor = 'linear-gradient(to top left, #ccc, #eee)' |
| 3682 | hf.write(html_phaselet.format('pre_suspend_process', \ | 3606 | hf.write(devtl.html_phaselet.format('pre_suspend_process', \ |
| 3683 | '0', '0', pscolor)) | 3607 | '0', '0', pscolor)) |
| 3684 | for b in data.phases: | 3608 | for b in data.phases: |
| 3685 | phase = data.dmesg[b] | 3609 | phase = data.dmesg[b] |
| 3686 | length = phase['end']-phase['start'] | 3610 | length = phase['end']-phase['start'] |
| 3687 | left = '%.3f' % (((phase['start']-t0)*100.0)/tTotal) | 3611 | left = '%.3f' % (((phase['start']-t0)*100.0)/tTotal) |
| 3688 | width = '%.3f' % ((length*100.0)/tTotal) | 3612 | width = '%.3f' % ((length*100.0)/tTotal) |
| 3689 | hf.write(html_phaselet.format(b, left, width, \ | 3613 | hf.write(devtl.html_phaselet.format(b, left, width, \ |
| 3690 | data.dmesg[b]['color'])) | 3614 | data.dmesg[b]['color'])) |
| 3691 | hf.write(html_phaselet.format('post_resume_process', \ | 3615 | hf.write(devtl.html_phaselet.format('post_resume_process', \ |
| 3692 | '0', '0', pscolor)) | 3616 | '0', '0', pscolor)) |
| 3693 | if sysvals.suspendmode == 'command': | 3617 | if sysvals.suspendmode == 'command': |
| 3694 | hf.write(html_phaselet.format('cmdexec', '0', '0', pscolor)) | 3618 | hf.write(devtl.html_phaselet.format('cmdexec', '0', '0', pscolor)) |
| 3695 | hf.write('</div>\n') | 3619 | hf.write('</div>\n') |
| 3696 | hf.write('</div>\n') | 3620 | hf.write('</div>\n') |
| 3697 | 3621 | ||
| @@ -3701,52 +3625,7 @@ def createHTML(testruns): · 3 unmodified lines | |||
| 3701 | else: | 3625 | else: |
| 3702 | data = testruns[-1] | 3626 | data = testruns[-1] |
| 3703 | if(sysvals.usecallgraph and not sysvals.embedded): | 3627 | if(sysvals.usecallgraph and not sysvals.embedded): |
| 3704 | hf.write('<section id="callgraphs" class="callgraph">\n') | 3628 | addCallgraphs(sysvals, hf, data) |
| 3705 | # write out the ftrace data converted to html | ||
| 3706 | html_func_top = '<article id="{0}" class="atop" style="background-color:{1}">\n<input type="checkbox" class="pf" id="f{2}" checked/><label for="f{2}">{3} {4}</label>\n' | ||
| 3707 | html_func_start = '<article>\n<input type="checkbox" class="pf" id="f{0}" checked/><label for="f{0}">{1} {2}</label>\n' | ||
| 3708 | html_func_end = '</article>\n' | ||
| 3709 | html_func_leaf = '<article>{0} {1}</article>\n' | ||
| 3710 | num = 0 | ||
| 3711 | for p in data.phases: | ||
| 3712 | if sysvals.cgphase and p != sysvals.cgphase: | ||
| 3713 | continue | ||
| 3714 | list = data.dmesg[p]['list'] | ||
| 3715 | for devname in data.sortedDevices(p): | ||
| 3716 | if('ftrace' not in list[devname]): | ||
| 3717 | continue | ||
| 3718 | devid = list[devname]['id'] | ||
| 3719 | cg = list[devname]['ftrace'] | ||
| 3720 | clen = (cg.end - cg.start) * 1000 | ||
| 3721 | if clen < sysvals.mincglen: | ||
| 3722 | continue | ||
| 3723 | fmt = '<r>(%.3f ms @ '+sysvals.timeformat+' to '+sysvals.timeformat+')</r>' | ||
| 3724 | flen = fmt % (clen, cg.start, cg.end) | ||
| 3725 | name = devname | ||
| 3726 | if(devname in sysvals.devprops): | ||
| 3727 | name = sysvals.devprops[devname].altName(devname) | ||
| 3728 | if sysvals.suspendmode == 'command': | ||
| 3729 | ftitle = name | ||
| 3730 | else: | ||
| 3731 | ftitle = name+' '+p | ||
| 3732 | hf.write(html_func_top.format(devid, data.dmesg[p]['color'], \ | ||
| 3733 | num, ftitle, flen)) | ||
| 3734 | num += 1 | ||
| 3735 | for line in cg.list: | ||
| 3736 | if(line.length < 0.000000001): | ||
| 3737 | flen = '' | ||
| 3738 | else: | ||
| 3739 | fmt = '<n>(%.3f ms @ '+sysvals.timeformat+')</n>' | ||
| 3740 | flen = fmt % (line.length*1000, line.time) | ||
| 3741 | if(line.freturn and line.fcall): | ||
| 3742 | hf.write(html_func_leaf.format(line.name, flen)) | ||
| 3743 | elif(line.freturn): | ||
| 3744 | hf.write(html_func_end) | ||
| 3745 | else: | ||
| 3746 | hf.write(html_func_start.format(num, line.name, flen)) | ||
| 3747 | num += 1 | ||
| 3748 | hf.write(html_func_end) | ||
| 3749 | hf.write('\n\n </section>\n') | ||
| 3750 | 3629 | ||
| 3751 | # add the test log as a hidden div | 3630 | # add the test log as a hidden div |
| 3752 | if sysvals.logmsg: | 3631 | if sysvals.logmsg: |
| @@ -3788,6 +3667,100 @@ def createHTML(testruns): · 35 unmodified lines | |||
| 3788 | hf.close() | 3667 | hf.close() |
| 3789 | return True | 3668 | return True |
| 3790 | 3669 | ||
| 3670 | def addCSS(hf, sv, testcount=1, kerror=False, extra=''): | ||
| 3671 | kernel = sv.stamp['kernel'] | ||
| 3672 | host = sv.hostname[0].upper()+sv.hostname[1:] | ||
| 3673 | mode = sv.suspendmode | ||
| 3674 | if sv.suspendmode in suspendmodename: | ||
| 3675 | mode = suspendmodename[sv.suspendmode] | ||
| 3676 | title = host+' '+mode+' '+kernel | ||
| 3677 | |||
| 3678 | # various format changes by flags | ||
| 3679 | cgchk = 'checked' | ||
| 3680 | cgnchk = 'not(:checked)' | ||
| 3681 | if sv.cgexp: | ||
| 3682 | cgchk = 'not(:checked)' | ||
| 3683 | cgnchk = 'checked' | ||
| 3684 | |||
| 3685 | hoverZ = 'z-index:8;' | ||
| 3686 | if sv.usedevsrc: | ||
| 3687 | hoverZ = '' | ||
| 3688 | |||
| 3689 | devlistpos = 'absolute' | ||
| 3690 | if testcount > 1: | ||
| 3691 | devlistpos = 'relative' | ||
| 3692 | |||
| 3693 | scaleTH = 20 | ||
| 3694 | if kerror: | ||
| 3695 | scaleTH = 60 | ||
| 3696 | |||
| 3697 | # write the html header first (html head, css code, up to body start) | ||
| 3698 | html_header = '<!DOCTYPE html>\n<html>\n<head>\n\ | ||
| 3699 | <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\ | ||
| 3700 | <title>'+title+'</title>\n\ | ||
| 3701 | <style type=\'text/css\'>\n\ | ||
| 3702 | body {overflow-y:scroll;}\n\ | ||
| 3703 | .stamp {width:100%;text-align:center;background:gray;line-height:30px;color:white;font:25px Arial;}\n\ | ||
| 3704 | .callgraph {margin-top:30px;box-shadow:5px 5px 20px black;}\n\ | ||
| 3705 | .callgraph article * {padding-left:28px;}\n\ | ||
| 3706 | h1 {color:black;font:bold 30px Times;}\n\ | ||
| 3707 | t0 {color:black;font:bold 30px Times;}\n\ | ||
| 3708 | t1 {color:black;font:30px Times;}\n\ | ||
| 3709 | t2 {color:black;font:25px Times;}\n\ | ||
| 3710 | t3 {color:black;font:20px Times;white-space:nowrap;}\n\ | ||
| 3711 | t4 {color:black;font:bold 30px Times;line-height:60px;white-space:nowrap;}\n\ | ||
| 3712 | cS {font:bold 13px Times;}\n\ | ||
| 3713 | table {width:100%;}\n\ | ||
| 3714 | .gray {background:rgba(80,80,80,0.1);}\n\ | ||
| 3715 | .green {background:rgba(204,255,204,0.4);}\n\ | ||
| 3716 | .purple {background:rgba(128,0,128,0.2);}\n\ | ||
| 3717 | .yellow {background:rgba(255,255,204,0.4);}\n\ | ||
| 3718 | .blue {background:rgba(169,208,245,0.4);}\n\ | ||
| 3719 | .time1 {font:22px Arial;border:1px solid;}\n\ | ||
| 3720 | .time2 {font:15px Arial;border-bottom:1px solid;border-left:1px solid;border-right:1px solid;}\n\ | ||
| 3721 | td {text-align:center;}\n\ | ||
| 3722 | r {color:#500000;font:15px Tahoma;}\n\ | ||
| 3723 | n {color:#505050;font:15px Tahoma;}\n\ | ||
| 3724 | .tdhl {color:red;}\n\ | ||
| 3725 | .hide {display:none;}\n\ | ||
| 3726 | .pf {display:none;}\n\ | ||
| 3727 | .pf:'+cgchk+' + label {background:url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/><rect x="8" y="4" width="2" height="10" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\ | ||
| 3728 | .pf:'+cgnchk+' ~ label {background:url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\ | ||
| 3729 | .pf:'+cgchk+' ~ *:not(:nth-child(2)) {display:none;}\n\ | ||
| 3730 | .zoombox {position:relative;width:100%;overflow-x:scroll;-webkit-user-select:none;-moz-user-select:none;user-select:none;}\n\ | ||
| 3731 | .timeline {position:relative;font-size:14px;cursor:pointer;width:100%; overflow:hidden;background:linear-gradient(#cccccc, white);}\n\ | ||
| 3732 | .thread {position:absolute;height:0%;overflow:hidden;z-index:7;line-height:30px;font-size:14px;border:1px solid;text-align:center;white-space:nowrap;}\n\ | ||
| 3733 | .thread.ps {border-radius:3px;background:linear-gradient(to top, #ccc, #eee);}\n\ | ||
| 3734 | .thread:hover {background:white;border:1px solid red;'+hoverZ+'}\n\ | ||
| 3735 | .thread.sec,.thread.sec:hover {background:black;border:0;color:white;line-height:15px;font-size:10px;}\n\ | ||
| 3736 | .hover {background:white;border:1px solid red;'+hoverZ+'}\n\ | ||
| 3737 | .hover.sync {background:white;}\n\ | ||
| 3738 | .hover.bg,.hover.kth,.hover.sync,.hover.ps {background:white;}\n\ | ||
| 3739 | .jiffie {position:absolute;pointer-events: none;z-index:8;}\n\ | ||
| 3740 | .traceevent {position:absolute;font-size:10px;z-index:7;overflow:hidden;color:black;text-align:center;white-space:nowrap;border-radius:5px;border:1px solid black;background:linear-gradient(to bottom right,#CCC,#969696);}\n\ | ||
| 3741 | .traceevent:hover {color:white;font-weight:bold;border:1px solid white;}\n\ | ||
| 3742 | .phase {position:absolute;overflow:hidden;border:0px;text-align:center;}\n\ | ||
| 3743 | .phaselet {float:left;overflow:hidden;border:0px;text-align:center;min-height:100px;font-size:24px;}\n\ | ||
| 3744 | .t {position:absolute;line-height:'+('%d'%scaleTH)+'px;pointer-events:none;top:0;height:100%;border-right:1px solid black;z-index:6;}\n\ | ||
| 3745 | .err {position:absolute;top:0%;height:100%;border-right:3px solid red;color:red;font:bold 14px Times;line-height:18px;}\n\ | ||
| 3746 | .legend {position:relative; width:100%; height:40px; text-align:center;margin-bottom:20px}\n\ | ||
| 3747 | .legend .square {position:absolute;cursor:pointer;top:10px; width:0px;height:20px;border:1px solid;padding-left:20px;}\n\ | ||
| 3748 | button {height:40px;width:200px;margin-bottom:20px;margin-top:20px;font-size:24px;}\n\ | ||
| 3749 | .logbtn {position:relative;float:right;height:25px;width:50px;margin-top:3px;margin-bottom:0;font-size:10px;text-align:center;}\n\ | ||
| 3750 | .devlist {position:'+devlistpos+';width:190px;}\n\ | ||
| 3751 | a:link {color:white;text-decoration:none;}\n\ | ||
| 3752 | a:visited {color:white;}\n\ | ||
| 3753 | a:hover {color:white;}\n\ | ||
| 3754 | a:active {color:white;}\n\ | ||
| 3755 | .version {position:relative;float:left;color:white;font-size:10px;line-height:30px;margin-left:10px;}\n\ | ||
| 3756 | #devicedetail {min-height:100px;box-shadow:5px 5px 20px black;}\n\ | ||
| 3757 | .tblock {position:absolute;height:100%;background:#ddd;}\n\ | ||
| 3758 | .tback {position:absolute;width:100%;background:linear-gradient(#ccc, #ddd);}\n\ | ||
| 3759 | .bg {z-index:1;}\n\ | ||
| 3760 | '+extra+'\ | ||
| 3761 | </style>\n</head>\n<body>\n' | ||
| 3762 | hf.write(html_header) | ||
| 3763 | |||
| 3791 | # Function: addScriptCode | 3764 | # Function: addScriptCode |
| 3792 | # Description: | 3765 | # Description: |
| 3793 | # Adds the javascript code to the output html | 3766 | # Adds the javascript code to the output html |
| @@ -3809,7 +3782,7 @@ def addScriptCode(hf, testruns): · 15 unmodified lines | |||
| 3809 | ' var resolution = -1;\n'\ | 3782 | ' var resolution = -1;\n'\ |
| 3810 | ' var dragval = [0, 0];\n'\ | 3783 | ' var dragval = [0, 0];\n'\ |
| 3811 | ' function redrawTimescale(t0, tMax, tS) {\n'\ | 3784 | ' function redrawTimescale(t0, tMax, tS) {\n'\ |
| 3812 | ' var rline = \'<div class="t" style="left:0;border-left:1px solid black;border-right:0;"><cS>←R</cS></div>\';\n'\ | 3785 | ' var rline = \'<div class="t" style="left:0;border-left:1px solid black;border-right:0;">\';\n'\ |
| 3813 | ' var tTotal = tMax - t0;\n'\ | 3786 | ' var tTotal = tMax - t0;\n'\ |
| 3814 | ' var list = document.getElementsByClassName("tblock");\n'\ | 3787 | ' var list = document.getElementsByClassName("tblock");\n'\ |
| 3815 | ' for (var i = 0; i < list.length; i++) {\n'\ | 3788 | ' for (var i = 0; i < list.length; i++) {\n'\ |
| @@ -3824,19 +3797,23 @@ def addScriptCode(hf, testruns): · 8 unmodified lines | |||
| 3824 | ' var pos = 0.0, val = 0.0;\n'\ | 3797 | ' var pos = 0.0, val = 0.0;\n'\ |
| 3825 | ' for (var j = 0; j < divTotal; j++) {\n'\ | 3798 | ' for (var j = 0; j < divTotal; j++) {\n'\ |
| 3826 | ' var htmlline = "";\n'\ | 3799 | ' var htmlline = "";\n'\ |
| 3827 | ' if(list[i].id[5] == "r") {\n'\ | 3800 | ' var mode = list[i].id[5];\n'\ |
| 3828 | ' pos = 100 - (((j)*tS*100)/mTotal);\n'\ | 3801 | ' if(mode == "s") {\n'\ |
| 3829 | ' val = (j)*tS;\n'\ | ||
| 3830 | ' htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\ | ||
| 3831 | ' if(j == 0)\n'\ | ||
| 3832 | ' htmlline = rline;\n'\ | ||
| 3833 | ' } else {\n'\ | ||
| 3834 | ' pos = 100 - (((j)*tS*100)/mTotal) - divEdge;\n'\ | 3802 | ' pos = 100 - (((j)*tS*100)/mTotal) - divEdge;\n'\ |
| 3835 | ' val = (j-divTotal+1)*tS;\n'\ | 3803 | ' val = (j-divTotal+1)*tS;\n'\ |
| 3836 | ' if(j == divTotal - 1)\n'\ | 3804 | ' if(j == divTotal - 1)\n'\ |
| 3837 | ' htmlline = \'<div class="t" style="right:\'+pos+\'%"><cS>S→</cS></div>\';\n'\ | 3805 | ' htmlline = \'<div class="t" style="right:\'+pos+\'%"><cS>S→</cS></div>\';\n'\ |
| 3838 | ' else\n'\ | 3806 | ' else\n'\ |
| 3839 | ' htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\ | 3807 | ' htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\ |
| 3808 | ' } else {\n'\ | ||
| 3809 | ' pos = 100 - (((j)*tS*100)/mTotal);\n'\ | ||
| 3810 | ' val = (j)*tS;\n'\ | ||
| 3811 | ' htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\ | ||
| 3812 | ' if(j == 0)\n'\ | ||
| 3813 | ' if(mode == "r")\n'\ | ||
| 3814 | ' htmlline = rline+"<cS>←R</cS></div>";\n'\ | ||
| 3815 | ' else\n'\ | ||
| 3816 | ' htmlline = rline+"<cS>0ms</div>";\n'\ | ||
| 3840 | ' }\n'\ | 3817 | ' }\n'\ |
| 3841 | ' html += htmlline;\n'\ | 3818 | ' html += htmlline;\n'\ |
| 3842 | ' }\n'\ | 3819 | ' }\n'\ |
| @@ -4002,12 +3979,80 @@ def addScriptCode(hf, testruns): · 159 unmodified lines | |||
| 4002 | ' }\n'\ | 3979 | ' }\n'\ |
| 4003 | ' }\n'\ | 3980 | ' }\n'\ |
| 4004 | ' }\n'\ | 3981 | ' }\n'\ |
| 3982 | ' if(typeof devstats !== \'undefined\')\n'\ | ||
| 3983 | ' callDetail(this.id, this.title);\n'\ | ||
| 4005 | ' var cglist = document.getElementById("callgraphs");\n'\ | 3984 | ' var cglist = document.getElementById("callgraphs");\n'\ |
| 4006 | ' if(!cglist) return;\n'\ | 3985 | ' if(!cglist) return;\n'\ |
| 4007 | ' var cg = cglist.getElementsByClassName("atop");\n'\ | 3986 | ' var cg = cglist.getElementsByClassName("atop");\n'\ |
| 4008 | ' if(cg.length < 10) return;\n'\ | 3987 | ' if(cg.length < 10) return;\n'\ |
| 4009 | ' for (var i = 0; i < cg.length; i++) {\n'\ | 3988 | ' for (var i = 0; i < cg.length; i++) {\n'\ |
| 4010 | ' if(idlist.indexOf(cg[i].id) >= 0) {\n'\ | 3989 | ' cgid = cg[i].id.split("x")[0]\n'\ |
| 3990 | ' if(idlist.indexOf(cgid) >= 0) {\n'\ | ||
| 3991 | ' cg[i].style.display = "block";\n'\ | ||
| 3992 | ' } else {\n'\ | ||
| 3993 | ' cg[i].style.display = "none";\n'\ | ||
| 3994 | ' }\n'\ | ||
| 3995 | ' }\n'\ | ||
| 3996 | ' }\n'\ | ||
| 3997 | ' function callDetail(devid, devtitle) {\n'\ | ||
| 3998 | ' if(!(devid in devstats) || devstats[devid].length < 1)\n'\ | ||
| 3999 | ' return;\n'\ | ||
| 4000 | ' var list = devstats[devid];\n'\ | ||
| 4001 | ' var tmp = devtitle.split(" ");\n'\ | ||
| 4002 | ' var name = tmp[0], phase = tmp[tmp.length-1];\n'\ | ||
| 4003 | ' var dd = document.getElementById(phase);\n'\ | ||
| 4004 | ' var total = parseFloat(tmp[1].slice(1));\n'\ | ||
| 4005 | ' var mlist = [];\n'\ | ||
| 4006 | ' var maxlen = 0;\n'\ | ||
| 4007 | ' var info = []\n'\ | ||
| 4008 | ' for(var i in list) {\n'\ | ||
| 4009 | ' if(list[i][0] == "@") {\n'\ | ||
| 4010 | ' info = list[i].split("|");\n'\ | ||
| 4011 | ' continue;\n'\ | ||
| 4012 | ' }\n'\ | ||
| 4013 | ' var tmp = list[i].split("|");\n'\ | ||
| 4014 | ' var t = parseFloat(tmp[0]), f = tmp[1], c = parseInt(tmp[2]);\n'\ | ||
| 4015 | ' var p = (t*100.0/total).toFixed(2);\n'\ | ||
| 4016 | ' mlist[mlist.length] = [f, c, t.toFixed(2), p+"%"];\n'\ | ||
| 4017 | ' if(f.length > maxlen)\n'\ | ||
| 4018 | ' maxlen = f.length;\n'\ | ||
| 4019 | ' }\n'\ | ||
| 4020 | ' var pad = 5;\n'\ | ||
| 4021 | ' if(mlist.length == 0) pad = 30;\n'\ | ||
| 4022 | ' var html = \'<div style="padding-top:\'+pad+\'px"><t3> <b>\'+name+\':</b>\';\n'\ | ||
| 4023 | ' if(info.length > 2)\n'\ | ||
| 4024 | ' html += " start=<b>"+info[1]+"</b>, end=<b>"+info[2]+"</b>";\n'\ | ||
| 4025 | ' if(info.length > 3)\n'\ | ||
| 4026 | ' html += ", length<i>(w/o overhead)</i>=<b>"+info[3]+" ms</b>";\n'\ | ||
| 4027 | ' if(info.length > 4)\n'\ | ||
| 4028 | ' html += ", return=<b>"+info[4]+"</b>";\n'\ | ||
| 4029 | ' html += "</t3></div>";\n'\ | ||
| 4030 | ' if(mlist.length > 0) {\n'\ | ||
| 4031 | ' html += \'<table class=fstat style="padding-top:\'+(maxlen*5)+\'px;"><tr><th>Function</th>\';\n'\ | ||
| 4032 | ' for(var i in mlist)\n'\ | ||
| 4033 | ' html += "<td class=vt>"+mlist[i][0]+"</td>";\n'\ | ||
| 4034 | ' html += "</tr><tr><th>Calls</th>";\n'\ | ||
| 4035 | ' for(var i in mlist)\n'\ | ||
| 4036 | ' html += "<td>"+mlist[i][1]+"</td>";\n'\ | ||
| 4037 | ' html += "</tr><tr><th>Time(ms)</th>";\n'\ | ||
| 4038 | ' for(var i in mlist)\n'\ | ||
| 4039 | ' html += "<td>"+mlist[i][2]+"</td>";\n'\ | ||
| 4040 | ' html += "</tr><tr><th>Percent</th>";\n'\ | ||
| 4041 | ' for(var i in mlist)\n'\ | ||
| 4042 | ' html += "<td>"+mlist[i][3]+"</td>";\n'\ | ||
| 4043 | ' html += "</tr></table>";\n'\ | ||
| 4044 | ' }\n'\ | ||
| 4045 | ' dd.innerHTML = html;\n'\ | ||
| 4046 | ' var height = (maxlen*5)+100;\n'\ | ||
| 4047 | ' dd.style.height = height+"px";\n'\ | ||
| 4048 | ' document.getElementById("devicedetail").style.height = height+"px";\n'\ | ||
| 4049 | ' }\n'\ | ||
| 4050 | ' function callSelect() {\n'\ | ||
| 4051 | ' var cglist = document.getElementById("callgraphs");\n'\ | ||
| 4052 | ' if(!cglist) return;\n'\ | ||
| 4053 | ' var cg = cglist.getElementsByClassName("atop");\n'\ | ||
| 4054 | ' for (var i = 0; i < cg.length; i++) {\n'\ | ||
| 4055 | ' if(this.id == cg[i].id) {\n'\ | ||
| 4011 | ' cg[i].style.display = "block";\n'\ | 4056 | ' cg[i].style.display = "block";\n'\ |
| 4012 | ' } else {\n'\ | 4057 | ' } else {\n'\ |
| 4013 | ' cg[i].style.display = "none";\n'\ | 4058 | ' cg[i].style.display = "none";\n'\ |
| @@ -4093,6 +4138,9 @@ def addScriptCode(hf, testruns): · 79 unmodified lines | |||
| 4093 | ' dev[i].onmouseover = deviceHover;\n'\ | 4138 | ' dev[i].onmouseover = deviceHover;\n'\ |
| 4094 | ' dev[i].onmouseout = deviceUnhover;\n'\ | 4139 | ' dev[i].onmouseout = deviceUnhover;\n'\ |
| 4095 | ' }\n'\ | 4140 | ' }\n'\ |
| 4141 | ' var dev = dmesg.getElementsByClassName("srccall");\n'\ | ||
| 4142 | ' for (var i = 0; i < dev.length; i++)\n'\ | ||
| 4143 | ' dev[i].onclick = callSelect;\n'\ | ||
| 4096 | ' zoomTimeline();\n'\ | 4144 | ' zoomTimeline();\n'\ |
| 4097 | ' });\n'\ | 4145 | ' });\n'\ |
| 4098 | '</script>\n' | 4146 | '</script>\n' |
| @@ -4675,7 +4723,7 @@ def rootCheck(fatal): · 576 unmodified lines | |||
| 4675 | if(os.access(sysvals.powerfile, os.W_OK)): | 4723 | if(os.access(sysvals.powerfile, os.W_OK)): |
| 4676 | return True | 4724 | return True |
| 4677 | if fatal: | 4725 | if fatal: |
| 4678 | doError('This command must be run as root') | 4726 | doError('This command requires sysfs mount and root access') |
| 4679 | return False | 4727 | return False |
| 4680 | 4728 | ||
| 4681 | # Function: getArgInt | 4729 | # Function: getArgInt |
| @@ -4767,51 +4815,62 @@ def runTest(subdir, testpath=''): · 85 unmodified lines | |||
| 4767 | cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1' | 4815 | cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1' |
| 4768 | call(cmd.format(os.environ['SUDO_USER'], sysvals.testdir), shell=True) | 4816 | call(cmd.format(os.environ['SUDO_USER'], sysvals.testdir), shell=True) |
| 4769 | 4817 | ||
| 4818 | def find_in_html(html, strs, div=False): | ||
| 4819 | for str in strs: | ||
| 4820 | l = len(str) | ||
| 4821 | i = html.find(str) | ||
| 4822 | if i >= 0: | ||
| 4823 | break | ||
| 4824 | if i < 0: | ||
| 4825 | return '' | ||
| 4826 | if not div: | ||
| 4827 | return re.search(r'[-+]?\d*\.\d+|\d+', html[i+l:i+l+50]).group() | ||
| 4828 | n = html[i+l:].find('</div>') | ||
| 4829 | if n < 0: | ||
| 4830 | return '' | ||
| 4831 | return html[i+l:i+l+n] | ||
| 4832 | |||
| 4770 | # Function: runSummary | 4833 | # Function: runSummary |
| 4771 | # Description: | 4834 | # Description: |
| 4772 | # create a summary of tests in a sub-directory | 4835 | # create a summary of tests in a sub-directory |
| 4773 | def runSummary(subdir, output): | 4836 | def runSummary(subdir, local=True): |
| 4774 | # get a list of ftrace output files | 4837 | inpath = os.path.abspath(subdir) |
| 4775 | files = [] | 4838 | outpath = inpath |
| 4839 | if local: | ||
| 4840 | outpath = os.path.abspath('.') | ||
| 4841 | print('Generating a summary of folder "%s"' % inpath) | ||
| 4842 | testruns = [] | ||
| 4776 | for dirname, dirnames, filenames in os.walk(subdir): | 4843 | for dirname, dirnames, filenames in os.walk(subdir): |
| 4777 | for filename in filenames: | 4844 | for filename in filenames: |
| 4778 | if(re.match('.*_ftrace.txt', filename)): | 4845 | if(not re.match('.*.html', filename)): |
| 4779 | files.append("%s/%s" % (dirname, filename)) | ||
| 4780 | |||
| 4781 | # process the files in order and get an array of data objects | ||
| 4782 | testruns = [] | ||
| 4783 | for file in sorted(files): | ||
| 4784 | if output: | ||
| 4785 | print("Test found in %s" % os.path.dirname(file)) | ||
| 4786 | sysvals.ftracefile = file | ||
| 4787 | sysvals.dmesgfile = file.replace('_ftrace.txt', '_dmesg.txt') | ||
| 4788 | doesTraceLogHaveTraceEvents() | ||
| 4789 | sysvals.usecallgraph = False | ||
| 4790 | if not sysvals.usetraceeventsonly: | ||
| 4791 | if(not os.path.exists(sysvals.dmesgfile)): | ||
| 4792 | print("Skipping %s: not a valid test input" % file) | ||
| 4793 | continue | 4846 | continue |
| 4794 | else: | 4847 | file = os.path.join(dirname, filename) |
| 4795 | if output: | 4848 | html = open(file, 'r').read(10000) |
| 4796 | f = os.path.basename(sysvals.ftracefile) | 4849 | suspend = find_in_html(html, |
| 4797 | d = os.path.basename(sysvals.dmesgfile) | 4850 | ['Kernel Suspend: ', 'Kernel Suspend Time: ']) |
| 4798 | print("\tInput files: %s and %s" % (f, d)) | 4851 | resume = find_in_html(html, |
| 4799 | testdata = loadKernelLog() | 4852 | ['Kernel Resume: ', 'Kernel Resume Time: ']) |
| 4800 | data = testdata[0] | 4853 | line = find_in_html(html, ['<div class="stamp">'], True) |
| 4801 | parseKernelLog(data) | 4854 | stmp = line.split() |
| 4802 | testdata = [data] | 4855 | if not suspend or not resume or len(stmp) < 4: |
| 4803 | appendIncompleteTraceLog(testdata) | 4856 | continue |
| 4804 | else: | 4857 | data = { |
| 4805 | if output: | 4858 | 'host': stmp[0], |
| 4806 | print("\tInput file: %s" % os.path.basename(sysvals.ftracefile)) | 4859 | 'kernel': stmp[1], |
| 4807 | testdata = parseTraceLog() | 4860 | 'mode': stmp[2], |
| 4808 | data = testdata[0] | 4861 | 'time': string.join(stmp[3:], ' '), |
| 4809 | data.normalizeTime(data.tSuspended) | 4862 | 'suspend': suspend, |
| 4810 | link = file.replace(subdir+'/', '').replace('_ftrace.txt', '.html') | 4863 | 'resume': resume, |
| 4811 | data.outfile = link | 4864 | 'url': os.path.relpath(file, outpath), |
| 4812 | testruns.append(data) | 4865 | } |
| 4813 | 4866 | if len(stmp) == 7: | |
| 4814 | createHTMLSummarySimple(testruns, subdir+'/summary.html') | 4867 | data['kernel'] = 'unknown' |
| 4868 | data['mode'] = stmp[1] | ||
| 4869 | data['time'] = string.join(stmp[2:], ' ') | ||
| 4870 | testruns.append(data) | ||
| 4871 | outfile = os.path.join(outpath, 'summary.html') | ||
| 4872 | print('Summary file: %s' % outfile) | ||
| 4873 | createHTMLSummarySimple(testruns, outfile, inpath) | ||
| 4815 | 4874 | ||
| 4816 | # Function: checkArgBool | 4875 | # Function: checkArgBool |
| 4817 | # Description: | 4876 | # Description: |
| @@ -4869,9 +4928,14 @@ def configFromFile(file): · 51 unmodified lines | |||
| 4869 | sysvals.predelay = getArgInt('-predelay', value, 0, 60000, False) | 4928 | sysvals.predelay = getArgInt('-predelay', value, 0, 60000, False) |
| 4870 | elif(opt.lower() == 'postdelay'): | 4929 | elif(opt.lower() == 'postdelay'): |
| 4871 | sysvals.postdelay = getArgInt('-postdelay', value, 0, 60000, False) | 4930 | sysvals.postdelay = getArgInt('-postdelay', value, 0, 60000, False) |
| 4931 | elif(opt.lower() == 'maxdepth'): | ||
| 4932 | sysvals.max_graph_depth = getArgInt('-maxdepth', value, 0, 1000, False) | ||
| 4872 | elif(opt.lower() == 'rtcwake'): | 4933 | elif(opt.lower() == 'rtcwake'): |
| 4873 | sysvals.rtcwake = True | 4934 | if value.lower() == 'off': |
| 4874 | sysvals.rtcwaketime = getArgInt('-rtcwake', value, 0, 3600, False) | 4935 | sysvals.rtcwake = False |
| 4936 | else: | ||
| 4937 | sysvals.rtcwake = True | ||
| 4938 | sysvals.rtcwaketime = getArgInt('-rtcwake', value, 0, 3600, False) | ||
| 4875 | elif(opt.lower() == 'timeprec'): | 4939 | elif(opt.lower() == 'timeprec'): |
| 4876 | sysvals.setPrecision(getArgInt('-timeprec', value, 0, 6, False)) | 4940 | sysvals.setPrecision(getArgInt('-timeprec', value, 0, 6, False)) |
| 4877 | elif(opt.lower() == 'mindev'): | 4941 | elif(opt.lower() == 'mindev'): |
| @@ -4969,8 +5033,8 @@ def printHelp(): · 91 unmodified lines | |||
| 4969 | modes = getModes() | 5033 | modes = getModes() |
| 4970 | 5034 | ||
| 4971 | print('') | 5035 | print('') |
| 4972 | print('AnalyzeSuspend v%s' % sysvals.version) | 5036 | print('%s v%s' % (sysvals.title, sysvals.version)) |
| 4973 | print('Usage: sudo analyze_suspend.py <options>') | 5037 | print('Usage: sudo sleepgraph <options> <commands>') |
| 4974 | print('') | 5038 | print('') |
| 4975 | print('Description:') | 5039 | print('Description:') |
| 4976 | print(' This tool is designed to assist kernel and OS developers in optimizing') | 5040 | print(' This tool is designed to assist kernel and OS developers in optimizing') |
| @@ -4981,22 +5045,22 @@ def printHelp(): · 4 unmodified lines | |||
| 4981 | print(' a detailed view of which devices/subsystems are taking the most') | 5045 | print(' a detailed view of which devices/subsystems are taking the most') |
| 4982 | print(' time in suspend/resume.') | 5046 | print(' time in suspend/resume.') |
| 4983 | print('') | 5047 | print('') |
| 5048 | print(' If no specific command is given, the default behavior is to initiate') | ||
| 5049 | print(' a suspend/resume and capture the dmesg/ftrace output as an html timeline.') | ||
| 5050 | print('') | ||
| 4984 | print(' Generates output files in subdirectory: suspend-mmddyy-HHMMSS') | 5051 | print(' Generates output files in subdirectory: suspend-mmddyy-HHMMSS') |
| 4985 | print(' HTML output: <hostname>_<mode>.html') | 5052 | print(' HTML output: <hostname>_<mode>.html') |
| 4986 | print(' raw dmesg output: <hostname>_<mode>_dmesg.txt') | 5053 | print(' raw dmesg output: <hostname>_<mode>_dmesg.txt') |
| 4987 | print(' raw ftrace output: <hostname>_<mode>_ftrace.txt') | 5054 | print(' raw ftrace output: <hostname>_<mode>_ftrace.txt') |
| 4988 | print('') | 5055 | print('') |
| 4989 | print('Options:') | 5056 | print('Options:') |
| 4990 | print(' [general]') | ||
| 4991 | print(' -h Print this help text') | 5057 | print(' -h Print this help text') |
| 4992 | print(' -v Print the current tool version') | 5058 | print(' -v Print the current tool version') |
| 4993 | print(' -config fn Pull arguments and config options from file fn') | 5059 | print(' -config fn Pull arguments and config options from file fn') |
| 4994 | print(' -verbose Print extra information during execution and analysis') | 5060 | print(' -verbose Print extra information during execution and analysis') |
| 4995 | print(' -status Test to see if the system is enabled to run this tool') | ||
| 4996 | print(' -modes List available suspend modes') | ||
| 4997 | print(' -m mode Mode to initiate for suspend %s (default: %s)') % (modes, sysvals.suspendmode) | 5061 | print(' -m mode Mode to initiate for suspend %s (default: %s)') % (modes, sysvals.suspendmode) |
| 4998 | print(' -o subdir Override the output subdirectory') | 5062 | print(' -o subdir Override the output subdirectory') |
| 4999 | print(' -rtcwake t Use rtcwake to autoresume after <t> seconds (default: disabled)') | 5063 | print(' -rtcwake t Wakeup t seconds after suspend, set t to "off" to disable (default: 15)') |
| 5000 | print(' -addlogs Add the dmesg and ftrace logs to the html output') | 5064 | print(' -addlogs Add the dmesg and ftrace logs to the html output') |
| 5001 | print(' -srgap Add a visible gap in the timeline between sus/res (default: disabled)') | 5065 | print(' -srgap Add a visible gap in the timeline between sus/res (default: disabled)') |
| 5002 | print(' [advanced]') | 5066 | print(' [advanced]') |
| @@ -5012,23 +5076,25 @@ def printHelp(): · 9 unmodified lines | |||
| 5012 | print(' be created in a new subdirectory with a summary page.') | 5076 | print(' be created in a new subdirectory with a summary page.') |
| 5013 | print(' [debug]') | 5077 | print(' [debug]') |
| 5014 | print(' -f Use ftrace to create device callgraphs (default: disabled)') | 5078 | print(' -f Use ftrace to create device callgraphs (default: disabled)') |
| 5079 | print(' -maxdepth N limit the callgraph data to N call levels (default: 0=all)') | ||
| 5015 | print(' -expandcg pre-expand the callgraph data in the html output (default: disabled)') | 5080 | print(' -expandcg pre-expand the callgraph data in the html output (default: disabled)') |
| 5016 | print(' -flist Print the list of functions currently being captured in ftrace') | ||
| 5017 | print(' -flistall Print all functions capable of being captured in ftrace') | ||
| 5018 | print(' -fadd file Add functions to be graphed in the timeline from a list in a text file') | 5081 | print(' -fadd file Add functions to be graphed in the timeline from a list in a text file') |
| 5019 | print(' -filter "d1,d2,..." Filter out all but this comma-delimited list of device names') | 5082 | print(' -filter "d1,d2,..." Filter out all but this comma-delimited list of device names') |
| 5020 | print(' -mincg ms Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)') | 5083 | print(' -mincg ms Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)') |
| 5021 | print(' -cgphase P Only show callgraph data for phase P (e.g. suspend_late)') | 5084 | print(' -cgphase P Only show callgraph data for phase P (e.g. suspend_late)') |
| 5022 | print(' -cgtest N Only show callgraph data for test N (e.g. 0 or 1 in an x2 run)') | 5085 | print(' -cgtest N Only show callgraph data for test N (e.g. 0 or 1 in an x2 run)') |
| 5023 | print(' -timeprec N Number of significant digits in timestamps (0:S, [3:ms], 6:us)') | 5086 | print(' -timeprec N Number of significant digits in timestamps (0:S, [3:ms], 6:us)') |
| 5024 | print(' [utilities]') | 5087 | print(' [commands]') |
| 5088 | print(' -ftrace ftracefile Create HTML output using ftrace input (used with -dmesg)') | ||
| 5089 | print(' -dmesg dmesgfile Create HTML output using dmesg (used with -ftrace)') | ||
| 5090 | print(' -summary directory Create a summary of all test in this dir') | ||
| 5091 | print(' -modes List available suspend modes') | ||
| 5092 | print(' -status Test to see if the system is enabled to run this tool') | ||
| 5025 | print(' -fpdt Print out the contents of the ACPI Firmware Performance Data Table') | 5093 | print(' -fpdt Print out the contents of the ACPI Firmware Performance Data Table') |
| 5026 | print(' -usbtopo Print out the current USB topology with power info') | 5094 | print(' -usbtopo Print out the current USB topology with power info') |
| 5027 | print(' -usbauto Enable autosuspend for all connected USB devices') | 5095 | print(' -usbauto Enable autosuspend for all connected USB devices') |
| 5028 | print(' [re-analyze data from previous runs]') | 5096 | print(' -flist Print the list of functions currently being captured in ftrace') |
| 5029 | print(' -ftrace ftracefile Create HTML output using ftrace input') | 5097 | print(' -flistall Print all functions capable of being captured in ftrace') |
| 5030 | print(' -dmesg dmesgfile Create HTML output using dmesg (not needed for kernel >= 3.15)') | ||
| 5031 | print(' -summary directory Create a summary of all test in this dir') | ||
| 5032 | print('') | 5098 | print('') |
| 5033 | return True | 5099 | return True |
| 5034 | 5100 | ||
| @@ -5076,9 +5142,18 @@ if __name__ == '__main__': · 41 unmodified lines | |||
| 5076 | sysvals.useprocmon = True | 5142 | sysvals.useprocmon = True |
| 5077 | elif(arg == '-dev'): | 5143 | elif(arg == '-dev'): |
| 5078 | sysvals.usedevsrc = True | 5144 | sysvals.usedevsrc = True |
| 5145 | elif(arg == '-maxdepth'): | ||
| 5146 | sysvals.max_graph_depth = getArgInt('-maxdepth', args, 0, 1000) | ||
| 5079 | elif(arg == '-rtcwake'): | 5147 | elif(arg == '-rtcwake'): |
| 5080 | sysvals.rtcwake = True | 5148 | try: |
| 5081 | sysvals.rtcwaketime = getArgInt('-rtcwake', args, 0, 3600) | 5149 | val = args.next() |
| 5150 | except: | ||
| 5151 | doError('No rtcwake time supplied', True) | ||
| 5152 | if val.lower() == 'off': | ||
| 5153 | sysvals.rtcwake = False | ||
| 5154 | else: | ||
| 5155 | sysvals.rtcwake = True | ||
| 5156 | sysvals.rtcwaketime = getArgInt('-rtcwake', val, 0, 3600, False) | ||
| 5082 | elif(arg == '-timeprec'): | 5157 | elif(arg == '-timeprec'): |
| 5083 | sysvals.setPrecision(getArgInt('-timeprec', args, 0, 6)) | 5158 | sysvals.setPrecision(getArgInt('-timeprec', args, 0, 6)) |
| 5084 | elif(arg == '-mindev'): | 5159 | elif(arg == '-mindev'): |
| @@ -5201,7 +5276,6 @@ if __name__ == '__main__': · 116 unmodified lines | |||
| 5201 | elif(cmd == 'usbauto'): | 5276 | elif(cmd == 'usbauto'): |
| 5202 | setUSBDevicesAuto() | 5277 | setUSBDevicesAuto() |
| 5203 | elif(cmd == 'summary'): | 5278 | elif(cmd == 'summary'): |
| 5204 | print("Generating a summary of folder \"%s\"" % cmdarg) | ||
| 5205 | runSummary(cmdarg, True) | 5279 | runSummary(cmdarg, True) |
| 5206 | sys.exit() | 5280 | sys.exit() |
| 5207 | 5281 | ||