History

tools: power: pm-graph: AnalyzeSuspend v4.6

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

Todd E Brandt committed 9 years ago

Commit bc167c71 parent 010a5221 file · +495 −421Browse files

Unified · Split

tools/power/pm-graph/analyze_suspend.pyrenamed from scripts/analyze_suspend.py+495−421
@@ -12,10 +12,6 @@ · 11 unmodified lines
12# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for12# 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 Page19# Home Page
24# https://01.org/suspendresume20# https://01.org/suspendresume
25# Source repo21# Source repo
26# https://github.com/01org/suspendresume22# https://github.com/01org/pm-graph
27#23#
28# Description:24# Description:
29# This tool is designed to assist kernel and OS developers in optimizing25# 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 to67# A global, single-instance container used to
72# store system values and test parameters68# store system values and test parameters
73class SystemValues:69class SystemValues:
70 title = 'SleepGraph'
71 version = '4.6'
74 ansi = False72 ansi = False
75 version = '4.5'
76 verbose = False73 verbose = False
77 addlogs = False74 addlogs = False
78 mindevlen = 0.075 mindevlen = 0.0
79 mincglen = 0.076 mincglen = 0.0
80 cgphase = ''77 cgphase = ''
81 cgtest = -178 cgtest = -1
79 max_graph_depth = 0
82 callloopmaxgap = 0.000180 callloopmaxgap = 0.0001
83 callloopmaxlen = 0.00581 callloopmaxlen = 0.005
84 srgap = 082 srgap = 0
@@ -106,8 +104,8 @@ class SystemValues: · 21 unmodified lines
106 ftracefile = ''104 ftracefile = ''
107 htmlfile = ''105 htmlfile = ''
108 embedded = False106 embedded = False
109 rtcwake = False107 rtcwake = True
110 rtcwaketime = 10108 rtcwaketime = 15
111 rtcpath = ''109 rtcpath = ''
112 devicefilter = []110 devicefilter = []
113 stamp = 0111 stamp = 0
@@ -235,6 +233,12 @@ class SystemValues: · 121 unmodified lines
235 self.rtcpath = rtc233 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 = True235 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 return244 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 value569 # 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 check572 # go no further if this is just a status check
569 if testing:573 if testing:
570 return574 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
641sysvals = SystemValues()645sysvals = SystemValues()
646suspendmodename = {
647 'freeze': 'Freeze (S0)',
648 'standby': 'Standby (S1)',
649 'mem': 'Suspend (S3)',
650 'disk': 'Hibernate (S4)'
651}
642 652
643# Class: DevProps653# 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']] = devname1028 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 is1489# Each instance is tied to a single device in a single phase, and is
1478# comprised of an ordered list of FTraceLine objects1490# comprised of an ordered list of FTraceLine objects
1479class FTraceCallGraph:1491class FTraceCallGraph:
1492 id = ''
1480 start = -1.01493 start = -1.0
1481 end = -1.01494 end = -1.0
1482 list = []1495 list = []
1483 invalid = False1496 invalid = False
1484 depth = 01497 depth = 0
1485 pid = 01498 pid = 0
1499 name = ''
1486 def __init__(self, pid):1500 def __init__(self, pid):
1487 self.start = -1.01501 self.start = -1.0
1488 self.end = -1.01502 self.end = -1.0
@@ -1631,9 +1645,17 @@ class FTraceCallGraph: · 142 unmodified lines
1631 return True1645 return True
1632 return False1646 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 = 01651 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] = l1660 stack[l.depth] = l
1639 cnt += 11661 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 False1667 return False
1646 # transfer total time from return line to call line1668 # calculate call length from call/return lines
1647 stack[l.depth].length = l.length1669 stack[l.depth].length = l.time - stack[l.depth].time
1648 stack.pop(l.depth)1670 stack.pop(l.depth)
1649 l.length = 01671 l.length = 0
1650 cnt -= 11672 cnt -= 1
1673 last = l
1651 if(cnt == 0):1674 if(cnt == 0):
1652 # trace caught the whole call tree1675 # trace caught the whole call tree
1653 return True1676 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 break1713 break
1691 return found1714 return found
1692 def newActionFromFunction(self, data):1715 def newActionFromFunction(self, data):
1693 name = self.list[0].name1716 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 return1718 return
1696 fs = self.start1719 fs = self.start
@@ -1710,7 +1733,7 @@ class FTraceCallGraph: · 13 unmodified lines
1710 phase, myname = out1733 phase, myname = out
1711 data.dmesg[phase]['list'][myname]['ftrace'] = self1734 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 calculates1761# A container for a device timeline which calculates
1739# all the html properties to display it correctly1762# all the html properties to display it correctly
1740class Timeline:1763class Timeline:
1741 html = {}1764 html = ''
1742 height = 0 # total timeline height1765 height = 0 # total timeline height
1743 scaleH = 20 # timescale (top) row height1766 scaleH = 20 # timescale (top) row height
1744 rowH = 30 # device row height1767 rowH = 30 # device row height
@@ -1746,14 +1769,28 @@ class Timeline: · 1 unmodified line
1746 rows = 0 # total timeline rows1769 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 = rowheight1777 self.rowH = rowheight
1751 self.scaleH = scaleheight1778 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: getDeviceRows1794 # Function: getDeviceRows
1758 # Description:1795 # Description:
1759 # determine how may rows the device funcs will take1796 # determine how may rows the device funcs will take
@@ -1880,10 +1917,8 @@ class Timeline: · 120 unmodified lines
1880 break1917 break
1881 top += self.rowheight[test][phase][i]1918 top += self.rowheight[test][phase][i]
1882 return top1919 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 = 01922 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: createTimeScale1953 # Function: createTimeScale
1905 # Description:1954 # Description:
1906 # Create the timescale for a timeline block1955 # 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 scale1962 # 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 timeline1967 # set scale for timeline
1919 mTotal = mMax - m01968 mTotal = mMax - m0
@@ -1926,21 +1975,20 @@ class Timeline: · 6 unmodified lines
1926 divEdge = (mTotal - tS*(divTotal-1))*100/mTotal1975 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 += htmlline1990 output += htmlline
1942 output += '</div>\n'1991 self.html += output+'</div>\n'
1943 return output
1944 1992
1945# Class: TestProps1993# Class: TestProps
1946# Description:1994# Description:
@@ -2009,7 +2057,7 @@ class ProcessMonitor: · 62 unmodified lines
2009 val['kern'] = kern2057 val['kern'] = kern
2010 if ujiff > 0 or kjiff > 0:2058 if ujiff > 0 or kjiff > 0:
2011 running[pid] = ujiff + kjiff2059 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.stamp2120 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
2082def 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: doesTraceLogHaveTraceEvents2122# Function: doesTraceLogHaveTraceEvents
2095# Description:2123# Description:
2096# Quickly determine if the ftrace log has some or all of the trace events2124# 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 data2750 # 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].name2753 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 True3129 return True
3102 3130
3131def 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
3164def 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+' &rarr; '+cg.name, color, dev['id'])
3192
3193 hf.write('\n\n </section>\n')
3194
3103# Function: createHTMLSummarySimple3195# Function: createHTMLSummarySimple
3104# Description:3196# Description:
3105# Create summary html file for a series of tests3197# Create summary html file for a series of tests
3106# Arguments:3198# Arguments:
3107# testruns: array of Data objects from parseTraceLog3199# testruns: array of Data objects from parseTraceLog
3108def createHTMLSummarySimple(testruns, htmlfile):3200def 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 header3215 # 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 header3221 # 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 test3227 # test data, 1 row per test
3163 sTimeAvg = 0.03228 avg = '<tr class="avg"><td></td><td></td><td></td><td></td>'+\
3164 rTimeAvg = 0.03229 '<td>Average of {0} {1} tests</td><td>{2}</td><td>{3}</td><td></td></tr>\n'
3165 num = 13230 sTimeAvg = rTimeAvg = 0.0
3166 for data in testruns:3231 mode = ''
3167 # data.end is the end of post_resume3232 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 += 13250 num += 1
3177 if stampcolumns:3251 # basic info
3178 # host name3252 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 time3257 # suspend time
3199 sTime = (data.tSuspended - data.start)*10003258 sTime = float(data['suspend'])
3200 sTimeAvg += sTime3259 sTimeAvg += sTime
3201 html += td.format("%3.3f ms" % sTime)3260 html += td.format('%.3f ms' % sTime)
3202 # resume time3261 # resume time
3203 rTime = (resumeEnd - data.tResumed)*10003262 rTime = float(data['resume'])
3204 rTimeAvg += rTime3263 rTimeAvg += rTime
3205 html += td.format("%3.3f ms" % rTime)3264 html += td.format('%.3f ms' % rTime)
3206 # link to the output html3265 # 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 average3270 rTimeAvg /= (num - 1)
3212 if(count > 0):3271 html += avg.format('%d' % (num - 1), mode,
3213 sTimeAvg /= count3272 '%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 file3274 # 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
3232def 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
3246def ordinal(value):3279def 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 = True3305 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 templates3308 # 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&rarr;</div>\n'3309 html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">ERROR&rarr;</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}">&nbsp;{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}">&nbsp;{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 variables3333 # html format variables
3314 hoverZ = 'z-index:8;'
3315 if sysvals.usedevsrc:
3316 hoverZ = ''
3317 scaleH = 203334 scaleH = 20
3318 scaleTH = 20
3319 if kerror:3335 if kerror:
3320 scaleH = 403336 scaleH = 40
3321 scaleTH = 60
3322 3337
3323 # device timeline3338 # 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 timeline3346 # Generate the header for this timeline
3329 for data in testruns:3347 for data in testruns:
3330 tTotal = data.end - data.start3348 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)+' '+testdesc3365 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'] += thtml3367 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'] += thtml3384 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' % sktime3390 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'] += thtml3403 devtl.html += thtml
3386 3404
3387 # time scale for potentially multiple datasets3405 # time scale for potentially multiple datasets
3388 t0 = testruns[0].start3406 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 timeline3450 # 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 separately3463 # 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].start3466 m0 = data.start
3456 mMax = testruns[data.testnumber].tSuspended3467 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].tSuspended3470 m0 = data.tSuspended
3461 mMax = testruns[data.testnumber].end3471 mMax = data.end
3462 # in an x2 run, remove any gap between blocks3472 # 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].start3474 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 altogether3477 # if a timeline block is 0 length, skip altogether
3468 if mTotal == 0:3478 if mTotal == 0:
3469 continue3479 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 background3483 # 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 found3492 # draw red lines for any kernel errors found
3483 t, err = e3493 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 phase3497 # 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 += b3533 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)' % j3547 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 continue3551 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.color3560 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 readable3564 # 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 finished3568 # 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 color3571 # 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.03576 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, '_', ' &nbsp;')3583 name = string.replace(phase, '_', ' &nbsp;')
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 embedded3590 # 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 timeline3598 # 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 section3602 # 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 div3630 # 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 True3668 return True
3790 3669
3670def 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: addScriptCode3764# Function: addScriptCode
3792# Description:3765# Description:
3793# Adds the javascript code to the output html3766# 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>&larr;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&rarr;</cS></div>\';\n'\3805 ' htmlline = \'<div class="t" style="right:\'+pos+\'%"><cS>S&rarr;</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>&larr;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 True4724 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 False4727 return False
4680 4728
4681# Function: getArgInt4729# 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
4818def 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: runSummary4833# Function: runSummary
4771# Description:4834# Description:
4772# create a summary of tests in a sub-directory4835# create a summary of tests in a sub-directory
4773def runSummary(subdir, output):4836def runSummary(subdir, local=True):
4774 # get a list of ftrace output files4837 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 continue4846 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 = link4864 '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: checkArgBool4875# 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 = True4934 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 True5099 return True
5034 5100
@@ -5076,9 +5142,18 @@ if __name__ == '__main__': · 41 unmodified lines
5076 sysvals.useprocmon = True5142 sysvals.useprocmon = True
5077 elif(arg == '-dev'):5143 elif(arg == '-dev'):
5078 sysvals.usedevsrc = True5144 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 = True5148 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