Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

Signed-off-by: Todd Brandt <todd.e.brandt@linux.intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Todd E Brandt and committed by
Rafael J. Wysocki
bc167c7d 010a522c

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