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
1212 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
1313 # more details.
1414 #
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−#
1915 # Authors:
2016 # Todd Brandt <todd.e.brandt@linux.intel.com>
2117 #
@@ -23,7 +19,7 @@ · 1 unmodified line
2319 # Home Page
2420 # https://01.org/suspendresume
2521 # Source repo
26−# https://github.com/01org/suspendresume
22+# https://github.com/01org/pm-graph
2723 #
2824 # Description:
2925 # 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
7167 # A global, single-instance container used to
7268 # store system values and test parameters
7369 class SystemValues:
70+ title = 'SleepGraph'
71+ version = '4.6'
7472 ansi = False
75− version = '4.5'
7673 verbose = False
7774 addlogs = False
7875 mindevlen = 0.0
7976 mincglen = 0.0
8077 cgphase = ''
8178 cgtest = -1
79+ max_graph_depth = 0
8280 callloopmaxgap = 0.0001
8381 callloopmaxlen = 0.005
8482 srgap = 0
@@ -106,8 +104,8 @@ class SystemValues: · 21 unmodified lines
106104 ftracefile = ''
107105 htmlfile = ''
108106 embedded = False
109− rtcwake = False
110− rtcwaketime = 10
107+ rtcwake = True
108+ rtcwaketime = 15
111109 rtcpath = ''
112110 devicefilter = []
113111 stamp = 0
@@ -235,6 +233,12 @@ class SystemValues: · 121 unmodified lines
235233 self.rtcpath = rtc
236234 if (hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()):
237235 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
238242 def setPrecision(self, num):
239243 if num < 0 or num > 6:
240244 return
@@ -564,7 +568,7 @@ class SystemValues: · 323 unmodified lines
564568 self.fsetVal('global', 'trace_clock')
565569 # set trace buffer to a huge value
566570 self.fsetVal('nop', 'current_tracer')
567− self.fsetVal('100000', 'buffer_size_kb')
571+ self.fsetVal('131073', 'buffer_size_kb')
568572 # go no further if this is just a status check
569573 if testing:
570574 return
@@ -583,7 +587,7 @@ class SystemValues: · 12 unmodified lines
583587 self.fsetVal('nofuncgraph-overhead', 'trace_options')
584588 self.fsetVal('context-info', 'trace_options')
585589 self.fsetVal('graph-time', 'trace_options')
586− self.fsetVal('0', 'max_graph_depth')
590+ self.fsetVal('%d' % self.max_graph_depth, 'max_graph_depth')
587591 cf = ['dpm_run_callback']
588592 if(self.usetraceeventsonly):
589593 cf += ['dpm_prepare', 'dpm_complete']
@@ -639,6 +643,12 @@ class SystemValues: · 49 unmodified lines
639643 return '\x1B[%d;40m%s\x1B[m' % (color, str)
640644
641645 sysvals = SystemValues()
646+suspendmodename = {
647+ 'freeze': 'Freeze (S0)',
648+ 'standby': 'Standby (S1)',
649+ 'mem': 'Suspend (S3)',
650+ 'disk': 'Hibernate (S4)'
651+}
642652
643653 # Class: DevProps
644654 # Description:
@@ -1013,6 +1023,8 @@ class Data: · 368 unmodified lines
10131023 tmp = dict()
10141024 for devname in list:
10151025 dev = list[devname]
1026+ if dev['length'] == 0:
1027+ continue
10161028 tmp[dev['start']] = devname
10171029 for t in sorted(tmp):
10181030 slist.append(tmp[t])
@@ -1477,12 +1489,14 @@ class FTraceLine: · 458 unmodified lines
14771489 # Each instance is tied to a single device in a single phase, and is
14781490 # comprised of an ordered list of FTraceLine objects
14791491 class FTraceCallGraph:
1492+ id = ''
14801493 start = -1.0
14811494 end = -1.0
14821495 list = []
14831496 invalid = False
14841497 depth = 0
14851498 pid = 0
1499+ name = ''
14861500 def __init__(self, pid):
14871501 self.start = -1.0
14881502 self.end = -1.0
@@ -1631,9 +1645,17 @@ class FTraceCallGraph: · 142 unmodified lines
16311645 return True
16321646 return False
16331647 def postProcess(self, debug=False):
1648+ if len(self.list) > 0:
1649+ self.name = self.list[0].name
16341650 stack = dict()
16351651 cnt = 0
1652+ last = 0
16361653 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
16371659 if(l.fcall and not l.freturn):
16381660 stack[l.depth] = l
16391661 cnt += 1
@@ -1643,11 +1665,12 @@ class FTraceCallGraph: · 3 unmodified lines
16431665 print 'Post Process Error: Depth missing'
16441666 l.debugPrint()
16451667 return False
1646− # transfer total time from return line to call line
1647− stack[l.depth].length = l.length
1668+ # calculate call length from call/return lines
1669+ stack[l.depth].length = l.time - stack[l.depth].time
16481670 stack.pop(l.depth)
16491671 l.length = 0
16501672 cnt -= 1
1673+ last = l
16511674 if(cnt == 0):
16521675 # trace caught the whole call tree
16531676 return True
@@ -1664,8 +1687,8 @@ class FTraceCallGraph: · 10 unmodified lines
16641687 'dpm_prepare': 'suspend_prepare',
16651688 'dpm_complete': 'resume_complete'
16661689 }
1667− if(self.list[0].name in borderphase):
1668− p = borderphase[self.list[0].name]
1690+ if(self.name in borderphase):
1691+ p = borderphase[self.name]
16691692 list = data.dmesg[p]['list']
16701693 for devname in list:
16711694 dev = list[devname]
@@ -1690,7 +1713,7 @@ class FTraceCallGraph: · 18 unmodified lines
16901713 break
16911714 return found
16921715 def newActionFromFunction(self, data):
1693− name = self.list[0].name
1716+ name = self.name
16941717 if name in ['dpm_run_callback', 'dpm_prepare', 'dpm_complete']:
16951718 return
16961719 fs = self.start
@@ -1710,7 +1733,7 @@ class FTraceCallGraph: · 13 unmodified lines
17101733 phase, myname = out
17111734 data.dmesg[phase]['list'][myname]['ftrace'] = self
17121735 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)
17141737 for l in self.list:
17151738 if(l.freturn and l.fcall):
17161739 print('%f (%02d): %s(); (%.3f us)' % (l.time, \
@@ -1738,7 +1761,7 @@ class DevItem: · 21 unmodified lines
17381761 # A container for a device timeline which calculates
17391762 # all the html properties to display it correctly
17401763 class Timeline:
1741− html = {}
1764+ html = ''
17421765 height = 0 # total timeline height
17431766 scaleH = 20 # timescale (top) row height
17441767 rowH = 30 # device row height
@@ -1746,14 +1769,28 @@ class Timeline: · 1 unmodified line
17461769 rows = 0 # total timeline rows
17471770 rowlines = dict()
17481771 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'
17491776 def __init__(self, rowheight, scaleheight):
17501777 self.rowH = rowheight
17511778 self.scaleH = scaleheight
1752− self.html = {
1753− 'header': '',
1754− 'timeline': '',
1755− 'legend': '',
1756− }
1779+ self.html = ''
1780+ def createHeader(self, sv, suppress=''):
1781+ if(not sv.stamp['time']):
1782+ return
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'])
17571794 # Function: getDeviceRows
17581795 # Description:
17591796 # determine how may rows the device funcs will take
@@ -1880,10 +1917,8 @@ class Timeline: · 120 unmodified lines
18801917 break
18811918 top += self.rowheight[test][phase][i]
18821919 return top
1883− # Function: calcTotalRows
1884− # Description:
1885− # Calculate the heights and offsets for the header and rows
18861920 def calcTotalRows(self):
1921+ # Calculate the heights and offsets for the header and rows
18871922 maxrows = 0
18881923 standardphases = []
18891924 for t in self.rowlines:
@@ -1901,6 +1936,20 @@ class Timeline: · 11 unmodified lines
19011936 for t, p in standardphases:
19021937 for i in sorted(self.rowheight[t][p]):
19031938 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)
19041953 # Function: createTimeScale
19051954 # Description:
19061955 # Create the timescale for a timeline block
@@ -1913,7 +1962,7 @@ class Timeline: · 6 unmodified lines
19131962 # The html code needed to display the time scale
19141963 def createTimeScale(self, m0, mMax, tTotal, mode):
19151964 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'
19171966 output = '<div class="timescale">\n'
19181967 # set scale for timeline
19191968 mTotal = mMax - m0
@@ -1926,21 +1975,20 @@ class Timeline: · 6 unmodified lines
19261975 divEdge = (mTotal - tS*(divTotal-1))*100/mTotal
19271976 for i in range(divTotal):
19281977 htmlline = ''
1929− if(mode == 'resume'):
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:
1978+ if(mode == 'suspend'):
19361979 pos = '%0.3f' % (100 - ((float(i)*tS*100)/mTotal) - divEdge)
19371980 val = '%0.fms' % (float(i-divTotal+1)*tS*1000)
19381981 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)
19401987 htmlline = timescale.format(pos, val)
1988+ if(i == 0):
1989+ htmlline = rline.format(mode)
19411990 output += htmlline
1942− output += '</div>\n'
1943− return output
1991+ self.html += output+'</div>\n'
19441992
19451993 # Class: TestProps
19461994 # Description:
@@ -2009,7 +2057,7 @@ class ProcessMonitor: · 62 unmodified lines
20092057 val['kern'] = kern
20102058 if ujiff > 0 or kjiff > 0:
20112059 running[pid] = ujiff + kjiff
2012− result = process.wait()
2060+ process.wait()
20132061 out = ''
20142062 for pid in running:
20152063 jiffies = running[pid]
@@ -2071,26 +2119,6 @@ def parseStamp(line, data): · 55 unmodified lines
20712119 if not sysvals.stamp:
20722120 sysvals.stamp = data.stamp
20732121
2074−# Function: diffStamp
2075−# Description:
2076−# compare the host, kernel, and mode fields in 3 stamps
2077−# Arguments:
2078−# stamp1: string array with mode, kernel, and host
2079−# stamp2: string array with mode, kernel, and host
2080−# Return:
2081−# True if stamps differ, False if they're the same
2082−def diffStamp(stamp1, stamp2):
2083− if 'host' in stamp1 and 'host' in stamp2:
2084− if stamp1['host'] != stamp2['host']:
2085− return True
2086− if 'kernel' in stamp1 and 'kernel' in stamp2:
2087− if stamp1['kernel'] != stamp2['kernel']:
2088− return True
2089− if 'mode' in stamp1 and 'mode' in stamp2:
2090− if stamp1['mode'] != stamp2['mode']:
2091− return True
2092− return False
2093−
20942122 # Function: doesTraceLogHaveTraceEvents
20952123 # Description:
20962124 # Quickly determine if the ftrace log has some or all of the trace events
@@ -2722,7 +2750,7 @@ def parseTraceLog(): · 625 unmodified lines
27222750 # create blocks for orphan cg data
27232751 for sortkey in sorted(sortlist):
27242752 cg = sortlist[sortkey]
2725− name = cg.list[0].name
2753+ name = cg.name
27262754 if sysvals.isCallgraphFunc(name):
27272755 vprint('Callgraph found for task %d: %.3fms, %s' % (cg.pid, (cg.end - cg.start)*1000, name))
27282756 cg.newActionFromFunction(data)
@@ -3100,149 +3128,154 @@ def parseKernelLog(data): · 371 unmodified lines
31003128 data.fixupInitcallsThatDidntReturn()
31013129 return True
31023130
3131+def callgraphHTML(sv, hf, num, cg, title, color, devid):
3132+ html_func_top = '<article id="{0}" class="atop" style="background:{1}">\n<input type="checkbox" class="pf" id="f{2}" checked/><label for="f{2}">{3} {4}</label>\n'
3133+ html_func_start = '<article>\n<input type="checkbox" class="pf" id="f{0}" checked/><label for="f{0}">{1} {2}</label>\n'
3134+ html_func_end = '</article>\n'
3135+ html_func_leaf = '<article>{0} {1}</article>\n'
3136+
3137+ cgid = devid
3138+ if cg.id:
3139+ cgid += cg.id
3140+ cglen = (cg.end - cg.start) * 1000
3141+ if cglen < sv.mincglen:
3142+ return num
3143+
3144+ fmt = '<r>(%.3f ms @ '+sv.timeformat+' to '+sv.timeformat+')</r>'
3145+ flen = fmt % (cglen, cg.start, cg.end)
3146+ hf.write(html_func_top.format(cgid, color, num, title, flen))
3147+ num += 1
3148+ for line in cg.list:
3149+ if(line.length < 0.000000001):
3150+ flen = ''
3151+ else:
3152+ fmt = '<n>(%.3f ms @ '+sv.timeformat+')</n>'
3153+ flen = fmt % (line.length*1000, line.time)
3154+ if(line.freturn and line.fcall):
3155+ hf.write(html_func_leaf.format(line.name, flen))
3156+ elif(line.freturn):
3157+ hf.write(html_func_end)
3158+ else:
3159+ hf.write(html_func_start.format(num, line.name, flen))
3160+ num += 1
3161+ hf.write(html_func_end)
3162+ return num
3163+
3164+def addCallgraphs(sv, hf, data):
3165+ hf.write('<section id="callgraphs" class="callgraph">\n')
3166+ # write out the ftrace data converted to html
3167+ num = 0
3168+ for p in data.phases:
3169+ if sv.cgphase and p != sv.cgphase:
3170+ continue
3171+ list = data.dmesg[p]['list']
3172+ for devname in data.sortedDevices(p):
3173+ dev = list[devname]
3174+ color = 'white'
3175+ if 'color' in data.dmesg[p]:
3176+ color = data.dmesg[p]['color']
3177+ if 'color' in dev:
3178+ color = dev['color']
3179+ name = devname
3180+ if(devname in sv.devprops):
3181+ name = sv.devprops[devname].altName(devname)
3182+ if sv.suspendmode in suspendmodename:
3183+ name += ' '+p
3184+ if('ftrace' in dev):
3185+ cg = dev['ftrace']
3186+ num = callgraphHTML(sv, hf, num, cg,
3187+ name, color, dev['id'])
3188+ if('ftraces' in dev):
3189+ for cg in dev['ftraces']:
3190+ num = callgraphHTML(sv, hf, num, cg,
3191+ name+' &rarr; '+cg.name, color, dev['id'])
3192+
3193+ hf.write('\n\n </section>\n')
3194+
31033195 # Function: createHTMLSummarySimple
31043196 # Description:
31053197 # Create summary html file for a series of tests
31063198 # Arguments:
31073199 # testruns: array of Data objects from parseTraceLog
3108−def createHTMLSummarySimple(testruns, htmlfile):
3109− # print out the basic summary of all the tests
3110− hf = open(htmlfile, 'w')
3111−
3200+def createHTMLSummarySimple(testruns, htmlfile, folder):
31123201 # write the html header first (html head, css code, up to body start)
31133202 html = '<!DOCTYPE html>\n<html>\n<head>\n\
31143203 <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\
3115− <title>AnalyzeSuspend Summary</title>\n\
3204+ <title>SleepGraph Summary</title>\n\
31163205 <style type=\'text/css\'>\n\
3117− body {overflow-y: scroll;}\n\
3118− .stamp {width: 100%;text-align:center;background-color:#495E09;line-height:30px;color:white;font: 25px Arial;}\n\
3206+ .stamp {width: 100%;text-align:center;background:#888;line-height:30px;color:white;font: 25px Arial;}\n\
31193207 table {width:100%;border-collapse: collapse;}\n\
3120− .summary {font: 22px Arial;border:1px solid;}\n\
3121− th {border: 1px solid black;background-color:#A7C942;color:white;}\n\
3122− td {text-align: center;}\n\
3123− tr.alt td {background-color:#EAF2D3;}\n\
3124− tr.avg td {background-color:#BDE34C;}\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\
3208+ .summary {border:1px solid;}\n\
3209+ th {border: 1px solid black;background:#222;color:white;}\n\
3210+ td {font: 16px "Times New Roman";text-align: center;}\n\
3211+ tr.alt td {background:#ddd;}\n\
3212+ tr.avg td {background:#aaa;}\n\
31293213 </style>\n</head>\n<body>\n'
31303214
31313215 # group test header
3132− count = 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−
3216+ html += '<div class="stamp">%s (%d tests)</div>\n' % (folder, len(testruns))
31453217 th = '\t<th>{0}</th>\n'
31463218 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'
31483220
31493221 # table header
3150− html += '<table class="summary">\n<tr>\n'
3151− html += th.format("Test #")
3152− if stampcolumns:
3153− html += th.format("Hostname")
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'
3222+ html += '<table class="summary">\n<tr>\n' + th.format('#') +\
3223+ th.format('Mode') + th.format('Host') + th.format('Kernel') +\
3224+ th.format('Test Time') + th.format('Suspend') + th.format('Resume') +\
3225+ th.format('Detail') + '</tr>\n'
31613226
31623227 # test data, 1 row per test
3163− sTimeAvg = 0.0
3164− rTimeAvg = 0.0
3165− num = 1
3166− for data in testruns:
3167− # data.end is the end of post_resume
3168− resumeEnd = data.dmesg['resume_complete']['end']
3228+ avg = '<tr class="avg"><td></td><td></td><td></td><td></td>'+\
3229+ '<td>Average of {0} {1} tests</td><td>{2}</td><td>{3}</td><td></td></tr>\n'
3230+ sTimeAvg = rTimeAvg = 0.0
3231+ mode = ''
3232+ num = 0
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
31693245 if num % 2 == 1:
31703246 html += '<tr class="alt">\n'
31713247 else:
31723248 html += '<tr>\n'
3173−
3174− # test num
3175− html += td.format("test %d" % num)
3249+ html += td.format("%d" % num)
31763250 num += 1
3177− if stampcolumns:
3178− # host name
3179− val = "unknown"
3180− if('host' in data.stamp):
3181− val = data.stamp['host']
3182− html += td.format(val)
3183− # host kernel
3251+ # basic info
3252+ for item in ['mode', 'host', 'kernel', 'time']:
31843253 val = "unknown"
3185− if('kernel' in data.stamp):
3186− val = data.stamp['kernel']
3254+ if(item in data):
3255+ val = data[item]
31873256 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)
31983257 # suspend time
3199− sTime = (data.tSuspended - data.start)*1000
3258+ sTime = float(data['suspend'])
32003259 sTimeAvg += sTime
3201− html += td.format("%3.3f ms" % sTime)
3260+ html += td.format('%.3f ms' % sTime)
32023261 # resume time
3203− rTime = (resumeEnd - data.tResumed)*1000
3262+ rTime = float(data['resume'])
32043263 rTimeAvg += rTime
3205− html += td.format("%3.3f ms" % rTime)
3264+ html += td.format('%.3f ms' % rTime)
32063265 # link to the output html
3207− html += tdlink.format(data.outfile)
3208−
3209− html += '</tr>\n'
3210−
3211− # last line: test average
3212− if(count > 0):
3213− sTimeAvg /= count
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'
3266+ html += tdlink.format(data['url']) + '</tr>\n'
3267+ # last test average line
3268+ if(num > 0):
3269+ sTimeAvg /= (num - 1)
3270+ rTimeAvg /= (num - 1)
3271+ html += avg.format('%d' % (num - 1), mode,
3272+ '%3.3f ms' % sTimeAvg, '%3.3f ms' % rTimeAvg)
32263273
32273274 # flush the data to file
3228− hf.write(html+'</table>\n')
3229− hf.write('</body>\n</html>\n')
3275+ hf = open(htmlfile, 'w')
3276+ hf.write(html+'</table>\n</body>\n</html>\n')
32303277 hf.close()
32313278
3232−def htmlTitle():
3233− modename = {
3234− 'freeze': 'Freeze (S0)',
3235− 'standby': 'Standby (S1)',
3236− 'mem': 'Suspend (S3)',
3237− 'disk': 'Hibernate (S4)'
3238− }
3239− kernel = sysvals.stamp['kernel']
3240− host = sysvals.hostname[0].upper()+sysvals.hostname[1:]
3241− mode = sysvals.suspendmode
3242− if sysvals.suspendmode in modename:
3243− mode = modename[sysvals.suspendmode]
3244− return host+' '+mode+' '+kernel
3245−
32463279 def ordinal(value):
32473280 suffix = 'th'
32483281 if value < 10 or value > 19:
@@ -3272,24 +3305,11 @@ def createHTML(testruns): · 23 unmodified lines
32723305 kerror = True
32733306 data.normalizeTime(testruns[-1].tSuspended)
32743307
3275− x2changes = ['', 'absolute']
3276− if len(testruns) > 1:
3277− x2changes = ['1', 'relative']
32783308 # 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'
32873309 html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">ERROR&rarr;</div>\n'
32883310 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'
32893311 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'
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'
3312+ html_legend = '<div id="p{3}" class="square" style="left:{0}%;background:{1}">&nbsp;{2}</div>\n'
32933313 html_timetotal = '<table class="time1">\n<tr>'\
32943314 '<td class="green" title="{3}">{2} Suspend Time: <b>{0} ms</b></td>'\
32953315 '<td class="yellow" title="{4}">{2} Resume Time: <b>{1} ms</b></td>'\
@@ -3311,20 +3331,18 @@ def createHTML(testruns): · 15 unmodified lines
33113331 '</tr>\n</table>\n'
33123332
33133333 # html format variables
3314− hoverZ = 'z-index:8;'
3315− if sysvals.usedevsrc:
3316− hoverZ = ''
33173334 scaleH = 20
3318− scaleTH = 20
33193335 if kerror:
33203336 scaleH = 40
3321− scaleTH = 60
33223337
33233338 # device timeline
33243339 vprint('Creating Device Timeline...')
33253340
33263341 devtl = Timeline(30, scaleH)
33273342
3343+ # write the test title and general info header
3344+ devtl.createHeader(sysvals)
3345+
33283346 # Generate the header for this timeline
33293347 for data in testruns:
33303348 tTotal = data.end - data.start
@@ -3346,7 +3364,7 @@ def createHTML(testruns): · 15 unmodified lines
33463364 if(len(testruns) > 1):
33473365 testdesc = ordinal(data.testnumber+1)+' '+testdesc
33483366 thtml = html_timetotal3.format(run_time, testdesc)
3349− devtl.html['header'] += thtml
3367+ devtl.html += thtml
33503368 elif data.fwValid:
33513369 suspend_time = '%.0f'%(sktime + (data.fwSuspend/1000000.0))
33523370 resume_time = '%.0f'%(rktime + (data.fwResume/1000000.0))
@@ -3363,10 +3381,10 @@ def createHTML(testruns): · 10 unmodified lines
33633381 else:
33643382 thtml = html_timetotal2.format(suspend_time, low_time, \
33653383 resume_time, testdesc1, stitle, rtitle)
3366− devtl.html['header'] += thtml
3384+ devtl.html += thtml
33673385 sftime = '%.3f'%(data.fwSuspend / 1000000.0)
33683386 rftime = '%.3f'%(data.fwResume / 1000000.0)
3369− devtl.html['header'] += html_timegroups.format('%.3f'%sktime, \
3387+ devtl.html += html_timegroups.format('%.3f'%sktime, \
33703388 sftime, rftime, '%.3f'%rktime, testdesc2, sysvals.suspendmode)
33713389 else:
33723390 suspend_time = '%.3f' % sktime
@@ -3382,7 +3400,7 @@ def createHTML(testruns): · 9 unmodified lines
33823400 else:
33833401 thtml = html_timetotal2.format(suspend_time, low_time, \
33843402 resume_time, testdesc, stitle, rtitle)
3385− devtl.html['header'] += thtml
3403+ devtl.html += thtml
33863404
33873405 # time scale for potentially multiple datasets
33883406 t0 = testruns[0].start
@@ -3429,15 +3447,8 @@ def createHTML(testruns): · 40 unmodified lines
34293447 devtl.getPhaseRows(threadlist, devtl.rows)
34303448 devtl.calcTotalRows()
34313449
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−
34403450 # draw the full timeline
3451+ devtl.createZoomBox(sysvals.suspendmode, len(testruns))
34413452 phases = {'suspend':[],'resume':[]}
34423453 for phase in data.dmesg:
34433454 if 'resume' in phase:
@@ -3452,37 +3463,36 @@ def createHTML(testruns): · 8 unmodified lines
34523463 # draw suspend and resume blocks separately
34533464 bname = '%s%d' % (dir[0], data.testnumber)
34543465 if dir == 'suspend':
3455− m0 = testruns[data.testnumber].start
3456− mMax = testruns[data.testnumber].tSuspended
3457− mTotal = mMax - m0
3466+ m0 = data.start
3467+ mMax = data.tSuspended
34583468 left = '%f' % (((m0-t0)*100.0)/tTotal)
34593469 else:
3460− m0 = testruns[data.testnumber].tSuspended
3461− mMax = testruns[data.testnumber].end
3470+ m0 = data.tSuspended
3471+ mMax = data.end
34623472 # in an x2 run, remove any gap between blocks
34633473 if len(testruns) > 1 and data.testnumber == 0:
34643474 mMax = testruns[1].start
3465− mTotal = mMax - m0
34663475 left = '%f' % ((((m0-t0)*100.0)+sysvals.srgap/2)/tTotal)
3476+ mTotal = mMax - m0
34673477 # if a timeline block is 0 length, skip altogether
34683478 if mTotal == 0:
34693479 continue
34703480 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)
34723482 for b in sorted(phases[dir]):
34733483 # draw the phase color background
34743484 phase = data.dmesg[b]
34753485 length = phase['end']-phase['start']
34763486 left = '%f' % (((phase['start']-m0)*100.0)/mTotal)
34773487 width = '%f' % ((length*100.0)/mTotal)
3478− devtl.html['timeline'] += html_phase.format(left, width, \
3488+ devtl.html += devtl.html_phase.format(left, width, \
34793489 '%.3f'%devtl.scaleH, '%.3f'%devtl.bodyH, \
34803490 data.dmesg[b]['color'], '')
34813491 for e in data.errorinfo[dir]:
34823492 # draw red lines for any kernel errors found
34833493 t, err = e
34843494 right = '%f' % (((mMax-t)*100.0)/mTotal)
3485− devtl.html['timeline'] += html_error.format(right, err)
3495+ devtl.html += html_error.format(right, err)
34863496 for b in sorted(phases[dir]):
34873497 # draw the devices for this phase
34883498 phaselist = data.dmesg[b]['list']
@@ -3496,7 +3506,7 @@ def createHTML(testruns): · 7 unmodified lines
34963506 if 'htmlclass' in dev:
34973507 xtraclass = dev['htmlclass']
34983508 if 'color' in dev:
3499− xtrastyle = 'background-color:%s;' % dev['color']
3509+ xtrastyle = 'background:%s;' % dev['color']
35003510 if(d in sysvals.devprops):
35013511 name = sysvals.devprops[d].altName(d)
35023512 xtraclass = sysvals.devprops[d].xtraClass()
@@ -3521,7 +3531,7 @@ def createHTML(testruns): · 18 unmodified lines
35213531 title += 'post_resume_process'
35223532 else:
35233533 title += b
3524− devtl.html['timeline'] += html_device.format(dev['id'], \
3534+ devtl.html += devtl.html_device.format(dev['id'], \
35253535 title, left, top, '%.3f'%rowheight, width, \
35263536 d+drv, xtraclass, xtrastyle)
35273537 if('cpuexec' in dev):
@@ -3535,7 +3545,7 @@ def createHTML(testruns): · 7 unmodified lines
35353545 left = '%f' % (((start-m0)*100)/mTotal)
35363546 width = '%f' % ((end-start)*100/mTotal)
35373547 color = 'rgba(255, 0, 0, %f)' % j
3538− devtl.html['timeline'] += \
3548+ devtl.html += \
35393549 html_cpuexec.format(left, top, height, width, color)
35403550 if('src' not in dev):
35413551 continue
@@ -3548,20 +3558,20 @@ def createHTML(testruns): · 6 unmodified lines
35483558 xtrastyle = ''
35493559 if e.color:
35503560 xtrastyle = 'background:%s;' % e.color
3551− devtl.html['timeline'] += \
3561+ devtl.html += \
35523562 html_traceevent.format(e.title(), \
35533563 left, top, height, width, e.text(), '', xtrastyle)
35543564 # draw the time scale, try to make the number of labels readable
3555− devtl.html['timeline'] += devtl.createTimeScale(m0, mMax, tTotal, dir)
3556− devtl.html['timeline'] += '</div>\n'
3565+ devtl.createTimeScale(m0, mMax, tTotal, dir)
3566+ devtl.html += '</div>\n'
35573567
35583568 # timeline is finished
3559− devtl.html['timeline'] += '</div>\n</div>\n'
3569+ devtl.html += '</div>\n</div>\n'
35603570
35613571 # draw a legend which describes the phases by color
35623572 if sysvals.suspendmode != 'command':
35633573 data = testruns[-1]
3564− devtl.html['legend'] = '<div class="legend">\n'
3574+ devtl.html += '<div class="legend">\n'
35653575 pdelta = 100.0/len(data.phases)
35663576 pmargin = pdelta / 4.0
35673577 for phase in data.phases:
@@ -3571,127 +3581,41 @@ def createHTML(testruns): · 3 unmodified lines
35713581 id += tmp[1][0]
35723582 order = '%.2f' % ((data.dmesg[phase]['order'] * pdelta) + pmargin)
35733583 name = string.replace(phase, '_', ' &nbsp;')
3574− devtl.html['legend'] += html_legend.format(order, \
3584+ devtl.html += html_legend.format(order, \
35753585 data.dmesg[phase]['color'], name, id)
3576− devtl.html['legend'] += '</div>\n'
3586+ devtl.html += '</div>\n'
35773587
35783588 hf = open(sysvals.htmlfile, 'w')
35793589
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−
36513590 # no header or css if its embedded
36523591 if(sysvals.embedded):
36533592 hf.write('pass True tSus %.3f tRes %.3f tLow %.3f fwvalid %s tSus %.3f tRes %.3f\n' %
36543593 (data.tSuspended-data.start, data.end-data.tSuspended, data.tLow, data.fwValid, \
36553594 data.fwSuspend/1000000, data.fwResume/1000000))
36563595 else:
3657− hf.write(html_header)
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']))
3596+ addCSS(hf, sysvals, len(testruns), kerror)
36713597
36723598 # write the device timeline
3673− hf.write(devtl.html['header'])
3674− hf.write(devtl.html['timeline'])
3675− hf.write(devtl.html['legend'])
3599+ hf.write(devtl.html)
36763600 hf.write('<div id="devicedetailtitle"></div>\n')
36773601 hf.write('<div id="devicedetail" style="display:none;">\n')
36783602 # draw the colored boxes for the device detail section
36793603 for data in testruns:
36803604 hf.write('<div id="devicedetail%d">\n' % data.testnumber)
36813605 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', \
36833607 '0', '0', pscolor))
36843608 for b in data.phases:
36853609 phase = data.dmesg[b]
36863610 length = phase['end']-phase['start']
36873611 left = '%.3f' % (((phase['start']-t0)*100.0)/tTotal)
36883612 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, \
36903614 data.dmesg[b]['color']))
3691− hf.write(html_phaselet.format('post_resume_process', \
3615+ hf.write(devtl.html_phaselet.format('post_resume_process', \
36923616 '0', '0', pscolor))
36933617 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))
36953619 hf.write('</div>\n')
36963620 hf.write('</div>\n')
36973621
@@ -3701,52 +3625,7 @@ def createHTML(testruns): · 3 unmodified lines
37013625 else:
37023626 data = testruns[-1]
37033627 if(sysvals.usecallgraph and not sysvals.embedded):
3704− hf.write('<section id="callgraphs" class="callgraph">\n')
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')
3628+ addCallgraphs(sysvals, hf, data)
37503629
37513630 # add the test log as a hidden div
37523631 if sysvals.logmsg:
@@ -3788,6 +3667,100 @@ def createHTML(testruns): · 35 unmodified lines
37883667 hf.close()
37893668 return True
37903669
3670+def addCSS(hf, sv, testcount=1, kerror=False, extra=''):
3671+ kernel = sv.stamp['kernel']
3672+ host = sv.hostname[0].upper()+sv.hostname[1:]
3673+ mode = sv.suspendmode
3674+ if sv.suspendmode in suspendmodename:
3675+ mode = suspendmodename[sv.suspendmode]
3676+ title = host+' '+mode+' '+kernel
3677+
3678+ # various format changes by flags
3679+ cgchk = 'checked'
3680+ cgnchk = 'not(:checked)'
3681+ if sv.cgexp:
3682+ cgchk = 'not(:checked)'
3683+ cgnchk = 'checked'
3684+
3685+ hoverZ = 'z-index:8;'
3686+ if sv.usedevsrc:
3687+ hoverZ = ''
3688+
3689+ devlistpos = 'absolute'
3690+ if testcount > 1:
3691+ devlistpos = 'relative'
3692+
3693+ scaleTH = 20
3694+ if kerror:
3695+ scaleTH = 60
3696+
3697+ # write the html header first (html head, css code, up to body start)
3698+ html_header = '<!DOCTYPE html>\n<html>\n<head>\n\
3699+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\
3700+ <title>'+title+'</title>\n\
3701+ <style type=\'text/css\'>\n\
3702+ body {overflow-y:scroll;}\n\
3703+ .stamp {width:100%;text-align:center;background:gray;line-height:30px;color:white;font:25px Arial;}\n\
3704+ .callgraph {margin-top:30px;box-shadow:5px 5px 20px black;}\n\
3705+ .callgraph article * {padding-left:28px;}\n\
3706+ h1 {color:black;font:bold 30px Times;}\n\
3707+ t0 {color:black;font:bold 30px Times;}\n\
3708+ t1 {color:black;font:30px Times;}\n\
3709+ t2 {color:black;font:25px Times;}\n\
3710+ t3 {color:black;font:20px Times;white-space:nowrap;}\n\
3711+ t4 {color:black;font:bold 30px Times;line-height:60px;white-space:nowrap;}\n\
3712+ cS {font:bold 13px Times;}\n\
3713+ table {width:100%;}\n\
3714+ .gray {background:rgba(80,80,80,0.1);}\n\
3715+ .green {background:rgba(204,255,204,0.4);}\n\
3716+ .purple {background:rgba(128,0,128,0.2);}\n\
3717+ .yellow {background:rgba(255,255,204,0.4);}\n\
3718+ .blue {background:rgba(169,208,245,0.4);}\n\
3719+ .time1 {font:22px Arial;border:1px solid;}\n\
3720+ .time2 {font:15px Arial;border-bottom:1px solid;border-left:1px solid;border-right:1px solid;}\n\
3721+ td {text-align:center;}\n\
3722+ r {color:#500000;font:15px Tahoma;}\n\
3723+ n {color:#505050;font:15px Tahoma;}\n\
3724+ .tdhl {color:red;}\n\
3725+ .hide {display:none;}\n\
3726+ .pf {display:none;}\n\
3727+ .pf:'+cgchk+' + label {background:url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/><rect x="8" y="4" width="2" height="10" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\
3728+ .pf:'+cgnchk+' ~ label {background:url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\
3729+ .pf:'+cgchk+' ~ *:not(:nth-child(2)) {display:none;}\n\
3730+ .zoombox {position:relative;width:100%;overflow-x:scroll;-webkit-user-select:none;-moz-user-select:none;user-select:none;}\n\
3731+ .timeline {position:relative;font-size:14px;cursor:pointer;width:100%; overflow:hidden;background:linear-gradient(#cccccc, white);}\n\
3732+ .thread {position:absolute;height:0%;overflow:hidden;z-index:7;line-height:30px;font-size:14px;border:1px solid;text-align:center;white-space:nowrap;}\n\
3733+ .thread.ps {border-radius:3px;background:linear-gradient(to top, #ccc, #eee);}\n\
3734+ .thread:hover {background:white;border:1px solid red;'+hoverZ+'}\n\
3735+ .thread.sec,.thread.sec:hover {background:black;border:0;color:white;line-height:15px;font-size:10px;}\n\
3736+ .hover {background:white;border:1px solid red;'+hoverZ+'}\n\
3737+ .hover.sync {background:white;}\n\
3738+ .hover.bg,.hover.kth,.hover.sync,.hover.ps {background:white;}\n\
3739+ .jiffie {position:absolute;pointer-events: none;z-index:8;}\n\
3740+ .traceevent {position:absolute;font-size:10px;z-index:7;overflow:hidden;color:black;text-align:center;white-space:nowrap;border-radius:5px;border:1px solid black;background:linear-gradient(to bottom right,#CCC,#969696);}\n\
3741+ .traceevent:hover {color:white;font-weight:bold;border:1px solid white;}\n\
3742+ .phase {position:absolute;overflow:hidden;border:0px;text-align:center;}\n\
3743+ .phaselet {float:left;overflow:hidden;border:0px;text-align:center;min-height:100px;font-size:24px;}\n\
3744+ .t {position:absolute;line-height:'+('%d'%scaleTH)+'px;pointer-events:none;top:0;height:100%;border-right:1px solid black;z-index:6;}\n\
3745+ .err {position:absolute;top:0%;height:100%;border-right:3px solid red;color:red;font:bold 14px Times;line-height:18px;}\n\
3746+ .legend {position:relative; width:100%; height:40px; text-align:center;margin-bottom:20px}\n\
3747+ .legend .square {position:absolute;cursor:pointer;top:10px; width:0px;height:20px;border:1px solid;padding-left:20px;}\n\
3748+ button {height:40px;width:200px;margin-bottom:20px;margin-top:20px;font-size:24px;}\n\
3749+ .logbtn {position:relative;float:right;height:25px;width:50px;margin-top:3px;margin-bottom:0;font-size:10px;text-align:center;}\n\
3750+ .devlist {position:'+devlistpos+';width:190px;}\n\
3751+ a:link {color:white;text-decoration:none;}\n\
3752+ a:visited {color:white;}\n\
3753+ a:hover {color:white;}\n\
3754+ a:active {color:white;}\n\
3755+ .version {position:relative;float:left;color:white;font-size:10px;line-height:30px;margin-left:10px;}\n\
3756+ #devicedetail {min-height:100px;box-shadow:5px 5px 20px black;}\n\
3757+ .tblock {position:absolute;height:100%;background:#ddd;}\n\
3758+ .tback {position:absolute;width:100%;background:linear-gradient(#ccc, #ddd);}\n\
3759+ .bg {z-index:1;}\n\
3760+'+extra+'\
3761+ </style>\n</head>\n<body>\n'
3762+ hf.write(html_header)
3763+
37913764 # Function: addScriptCode
37923765 # Description:
37933766 # Adds the javascript code to the output html
@@ -3809,7 +3782,7 @@ def addScriptCode(hf, testruns): · 15 unmodified lines
38093782 ' var resolution = -1;\n'\
38103783 ' var dragval = [0, 0];\n'\
38113784 ' 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'\
38133786 ' var tTotal = tMax - t0;\n'\
38143787 ' var list = document.getElementsByClassName("tblock");\n'\
38153788 ' for (var i = 0; i < list.length; i++) {\n'\
@@ -3824,19 +3797,23 @@ def addScriptCode(hf, testruns): · 8 unmodified lines
38243797 ' var pos = 0.0, val = 0.0;\n'\
38253798 ' for (var j = 0; j < divTotal; j++) {\n'\
38263799 ' var htmlline = "";\n'\
3827− ' if(list[i].id[5] == "r") {\n'\
3828− ' pos = 100 - (((j)*tS*100)/mTotal);\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'\
3800+ ' var mode = list[i].id[5];\n'\
3801+ ' if(mode == "s") {\n'\
38343802 ' pos = 100 - (((j)*tS*100)/mTotal) - divEdge;\n'\
38353803 ' val = (j-divTotal+1)*tS;\n'\
38363804 ' if(j == divTotal - 1)\n'\
38373805 ' htmlline = \'<div class="t" style="right:\'+pos+\'%"><cS>S&rarr;</cS></div>\';\n'\
38383806 ' else\n'\
38393807 ' 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'\
38403817 ' }\n'\
38413818 ' html += htmlline;\n'\
38423819 ' }\n'\
@@ -4002,12 +3979,80 @@ def addScriptCode(hf, testruns): · 159 unmodified lines
40023979 ' }\n'\
40033980 ' }\n'\
40043981 ' }\n'\
3982+ ' if(typeof devstats !== \'undefined\')\n'\
3983+ ' callDetail(this.id, this.title);\n'\
40053984 ' var cglist = document.getElementById("callgraphs");\n'\
40063985 ' if(!cglist) return;\n'\
40073986 ' var cg = cglist.getElementsByClassName("atop");\n'\
40083987 ' if(cg.length < 10) return;\n'\
40093988 ' 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'\
40114056 ' cg[i].style.display = "block";\n'\
40124057 ' } else {\n'\
40134058 ' cg[i].style.display = "none";\n'\
@@ -4093,6 +4138,9 @@ def addScriptCode(hf, testruns): · 79 unmodified lines
40934138 ' dev[i].onmouseover = deviceHover;\n'\
40944139 ' dev[i].onmouseout = deviceUnhover;\n'\
40954140 ' }\n'\
4141+ ' var dev = dmesg.getElementsByClassName("srccall");\n'\
4142+ ' for (var i = 0; i < dev.length; i++)\n'\
4143+ ' dev[i].onclick = callSelect;\n'\
40964144 ' zoomTimeline();\n'\
40974145 ' });\n'\
40984146 '</script>\n'
@@ -4675,7 +4723,7 @@ def rootCheck(fatal): · 576 unmodified lines
46754723 if(os.access(sysvals.powerfile, os.W_OK)):
46764724 return True
46774725 if fatal:
4678− doError('This command must be run as root')
4726+ doError('This command requires sysfs mount and root access')
46794727 return False
46804728
46814729 # Function: getArgInt
@@ -4767,51 +4815,62 @@ def runTest(subdir, testpath=''): · 85 unmodified lines
47674815 cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1'
47684816 call(cmd.format(os.environ['SUDO_USER'], sysvals.testdir), shell=True)
47694817
4818+def find_in_html(html, strs, div=False):
4819+ for str in strs:
4820+ l = len(str)
4821+ i = html.find(str)
4822+ if i >= 0:
4823+ break
4824+ if i < 0:
4825+ return ''
4826+ if not div:
4827+ return re.search(r'[-+]?\d*\.\d+|\d+', html[i+l:i+l+50]).group()
4828+ n = html[i+l:].find('</div>')
4829+ if n < 0:
4830+ return ''
4831+ return html[i+l:i+l+n]
4832+
47704833 # Function: runSummary
47714834 # Description:
47724835 # create a summary of tests in a sub-directory
4773−def runSummary(subdir, output):
4774− # get a list of ftrace output files
4775− files = []
4836+def runSummary(subdir, local=True):
4837+ inpath = os.path.abspath(subdir)
4838+ outpath = inpath
4839+ if local:
4840+ outpath = os.path.abspath('.')
4841+ print('Generating a summary of folder "%s"' % inpath)
4842+ testruns = []
47764843 for dirname, dirnames, filenames in os.walk(subdir):
47774844 for filename in filenames:
4778− if(re.match('.*_ftrace.txt', 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)
4845+ if(not re.match('.*.html', filename)):
47934846 continue
4794− else:
4795− if output:
4796− f = os.path.basename(sysvals.ftracefile)
4797− d = os.path.basename(sysvals.dmesgfile)
4798− print("\tInput files: %s and %s" % (f, d))
4799− testdata = loadKernelLog()
4800− data = testdata[0]
4801− parseKernelLog(data)
4802− testdata = [data]
4803− appendIncompleteTraceLog(testdata)
4804− else:
4805− if output:
4806− print("\tInput file: %s" % os.path.basename(sysvals.ftracefile))
4807− testdata = parseTraceLog()
4808− data = testdata[0]
4809− data.normalizeTime(data.tSuspended)
4810− link = file.replace(subdir+'/', '').replace('_ftrace.txt', '.html')
4811− data.outfile = link
4812− testruns.append(data)
4813−
4814− createHTMLSummarySimple(testruns, subdir+'/summary.html')
4847+ file = os.path.join(dirname, filename)
4848+ html = open(file, 'r').read(10000)
4849+ suspend = find_in_html(html,
4850+ ['Kernel Suspend: ', 'Kernel Suspend Time: '])
4851+ resume = find_in_html(html,
4852+ ['Kernel Resume: ', 'Kernel Resume Time: '])
4853+ line = find_in_html(html, ['<div class="stamp">'], True)
4854+ stmp = line.split()
4855+ if not suspend or not resume or len(stmp) < 4:
4856+ continue
4857+ data = {
4858+ 'host': stmp[0],
4859+ 'kernel': stmp[1],
4860+ 'mode': stmp[2],
4861+ 'time': string.join(stmp[3:], ' '),
4862+ 'suspend': suspend,
4863+ 'resume': resume,
4864+ 'url': os.path.relpath(file, outpath),
4865+ }
4866+ if len(stmp) == 7:
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)
48154874
48164875 # Function: checkArgBool
48174876 # Description:
@@ -4869,9 +4928,14 @@ def configFromFile(file): · 51 unmodified lines
48694928 sysvals.predelay = getArgInt('-predelay', value, 0, 60000, False)
48704929 elif(opt.lower() == 'postdelay'):
48714930 sysvals.postdelay = getArgInt('-postdelay', value, 0, 60000, False)
4931+ elif(opt.lower() == 'maxdepth'):
4932+ sysvals.max_graph_depth = getArgInt('-maxdepth', value, 0, 1000, False)
48724933 elif(opt.lower() == 'rtcwake'):
4873− sysvals.rtcwake = True
4874− sysvals.rtcwaketime = getArgInt('-rtcwake', value, 0, 3600, False)
4934+ if value.lower() == 'off':
4935+ sysvals.rtcwake = False
4936+ else:
4937+ sysvals.rtcwake = True
4938+ sysvals.rtcwaketime = getArgInt('-rtcwake', value, 0, 3600, False)
48754939 elif(opt.lower() == 'timeprec'):
48764940 sysvals.setPrecision(getArgInt('-timeprec', value, 0, 6, False))
48774941 elif(opt.lower() == 'mindev'):
@@ -4969,8 +5033,8 @@ def printHelp(): · 91 unmodified lines
49695033 modes = getModes()
49705034
49715035 print('')
4972− print('AnalyzeSuspend v%s' % sysvals.version)
4973− print('Usage: sudo analyze_suspend.py <options>')
5036+ print('%s v%s' % (sysvals.title, sysvals.version))
5037+ print('Usage: sudo sleepgraph <options> <commands>')
49745038 print('')
49755039 print('Description:')
49765040 print(' This tool is designed to assist kernel and OS developers in optimizing')
@@ -4981,22 +5045,22 @@ def printHelp(): · 4 unmodified lines
49815045 print(' a detailed view of which devices/subsystems are taking the most')
49825046 print(' time in suspend/resume.')
49835047 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('')
49845051 print(' Generates output files in subdirectory: suspend-mmddyy-HHMMSS')
49855052 print(' HTML output: <hostname>_<mode>.html')
49865053 print(' raw dmesg output: <hostname>_<mode>_dmesg.txt')
49875054 print(' raw ftrace output: <hostname>_<mode>_ftrace.txt')
49885055 print('')
49895056 print('Options:')
4990− print(' [general]')
49915057 print(' -h Print this help text')
49925058 print(' -v Print the current tool version')
49935059 print(' -config fn Pull arguments and config options from file fn')
49945060 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')
49975061 print(' -m mode Mode to initiate for suspend %s (default: %s)') % (modes, sysvals.suspendmode)
49985062 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)')
50005064 print(' -addlogs Add the dmesg and ftrace logs to the html output')
50015065 print(' -srgap Add a visible gap in the timeline between sus/res (default: disabled)')
50025066 print(' [advanced]')
@@ -5012,23 +5076,25 @@ def printHelp(): · 9 unmodified lines
50125076 print(' be created in a new subdirectory with a summary page.')
50135077 print(' [debug]')
50145078 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)')
50155080 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')
50185081 print(' -fadd file Add functions to be graphed in the timeline from a list in a text file')
50195082 print(' -filter "d1,d2,..." Filter out all but this comma-delimited list of device names')
50205083 print(' -mincg ms Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)')
50215084 print(' -cgphase P Only show callgraph data for phase P (e.g. suspend_late)')
50225085 print(' -cgtest N Only show callgraph data for test N (e.g. 0 or 1 in an x2 run)')
50235086 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')
50255093 print(' -fpdt Print out the contents of the ACPI Firmware Performance Data Table')
50265094 print(' -usbtopo Print out the current USB topology with power info')
50275095 print(' -usbauto Enable autosuspend for all connected USB devices')
5028− print(' [re-analyze data from previous runs]')
5029− print(' -ftrace ftracefile Create HTML output using ftrace input')
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')
5096+ print(' -flist Print the list of functions currently being captured in ftrace')
5097+ print(' -flistall Print all functions capable of being captured in ftrace')
50325098 print('')
50335099 return True
50345100
@@ -5076,9 +5142,18 @@ if __name__ == '__main__': · 41 unmodified lines
50765142 sysvals.useprocmon = True
50775143 elif(arg == '-dev'):
50785144 sysvals.usedevsrc = True
5145+ elif(arg == '-maxdepth'):
5146+ sysvals.max_graph_depth = getArgInt('-maxdepth', args, 0, 1000)
50795147 elif(arg == '-rtcwake'):
5080− sysvals.rtcwake = True
5081− sysvals.rtcwaketime = getArgInt('-rtcwake', args, 0, 3600)
5148+ try:
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)
50825157 elif(arg == '-timeprec'):
50835158 sysvals.setPrecision(getArgInt('-timeprec', args, 0, 6))
50845159 elif(arg == '-mindev'):
@@ -5201,7 +5276,6 @@ if __name__ == '__main__': · 116 unmodified lines
52015276 elif(cmd == 'usbauto'):
52025277 setUSBDevicesAuto()
52035278 elif(cmd == 'summary'):
5204− print("Generating a summary of folder \"%s\"" % cmdarg)
52055279 runSummary(cmdarg, True)
52065280 sys.exit()
52075281