aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/power/pm-graph/Makefile28
-rwxr-xr-xtools/power/pm-graph/analyze_boot.py824
-rwxr-xr-xtools/power/pm-graph/analyze_suspend.py (renamed from scripts/analyze_suspend.py)916
-rw-r--r--tools/power/pm-graph/bootgraph.8132
-rw-r--r--tools/power/pm-graph/sleepgraph.8243
-rwxr-xr-xtools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py17
6 files changed, 1739 insertions, 421 deletions
diff --git a/tools/power/pm-graph/Makefile b/tools/power/pm-graph/Makefile
new file mode 100644
index 000000000000..4d0ccc89e6c6
--- /dev/null
+++ b/tools/power/pm-graph/Makefile
@@ -0,0 +1,28 @@
1PREFIX ?= /usr
2DESTDIR ?=
3
4all:
5 @echo "Nothing to build"
6
7install :
8 install -d $(DESTDIR)$(PREFIX)/lib/pm-graph
9 install analyze_suspend.py $(DESTDIR)$(PREFIX)/lib/pm-graph
10 install analyze_boot.py $(DESTDIR)$(PREFIX)/lib/pm-graph
11
12 ln -s $(DESTDIR)$(PREFIX)/lib/pm-graph/analyze_boot.py $(DESTDIR)$(PREFIX)/bin/bootgraph
13 ln -s $(DESTDIR)$(PREFIX)/lib/pm-graph/analyze_suspend.py $(DESTDIR)$(PREFIX)/bin/sleepgraph
14
15 install -d $(DESTDIR)$(PREFIX)/share/man/man8
16 install bootgraph.8 $(DESTDIR)$(PREFIX)/share/man/man8
17 install sleepgraph.8 $(DESTDIR)$(PREFIX)/share/man/man8
18
19uninstall :
20 rm $(DESTDIR)$(PREFIX)/share/man/man8/bootgraph.8
21 rm $(DESTDIR)$(PREFIX)/share/man/man8/sleepgraph.8
22
23 rm $(DESTDIR)$(PREFIX)/bin/bootgraph
24 rm $(DESTDIR)$(PREFIX)/bin/sleepgraph
25
26 rm $(DESTDIR)$(PREFIX)/lib/pm-graph/analyze_boot.py
27 rm $(DESTDIR)$(PREFIX)/lib/pm-graph/analyze_suspend.py
28 rmdir $(DESTDIR)$(PREFIX)/lib/pm-graph
diff --git a/tools/power/pm-graph/analyze_boot.py b/tools/power/pm-graph/analyze_boot.py
new file mode 100755
index 000000000000..3e1dcbbf1adc
--- /dev/null
+++ b/tools/power/pm-graph/analyze_boot.py
@@ -0,0 +1,824 @@
1#!/usr/bin/python
2#
3# Tool for analyzing boot timing
4# Copyright (c) 2013, Intel Corporation.
5#
6# This program is free software; you can redistribute it and/or modify it
7# under the terms and conditions of the GNU General Public License,
8# version 2, as published by the Free Software Foundation.
9#
10# This program is distributed in the hope it will be useful, but WITHOUT
11# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13# more details.
14#
15# Authors:
16# Todd Brandt <todd.e.brandt@linux.intel.com>
17#
18# Description:
19# This tool is designed to assist kernel and OS developers in optimizing
20# their linux stack's boot time. It creates an html representation of
21# the kernel boot timeline up to the start of the init process.
22#
23
24# ----------------- LIBRARIES --------------------
25
26import sys
27import time
28import os
29import string
30import re
31import platform
32import shutil
33from datetime import datetime, timedelta
34from subprocess import call, Popen, PIPE
35import analyze_suspend as aslib
36
37# ----------------- CLASSES --------------------
38
39# Class: SystemValues
40# Description:
41# A global, single-instance container used to
42# store system values and test parameters
43class SystemValues(aslib.SystemValues):
44 title = 'BootGraph'
45 version = 2.0
46 hostname = 'localhost'
47 testtime = ''
48 kernel = ''
49 dmesgfile = ''
50 ftracefile = ''
51 htmlfile = 'bootgraph.html'
52 outfile = ''
53 phoronix = False
54 addlogs = False
55 useftrace = False
56 usedevsrc = True
57 suspendmode = 'boot'
58 max_graph_depth = 2
59 graph_filter = 'do_one_initcall'
60 reboot = False
61 manual = False
62 iscronjob = False
63 timeformat = '%.6f'
64 def __init__(self):
65 if('LOG_FILE' in os.environ and 'TEST_RESULTS_IDENTIFIER' in os.environ):
66 self.phoronix = True
67 self.addlogs = True
68 self.outfile = os.environ['LOG_FILE']
69 self.htmlfile = os.environ['LOG_FILE']
70 self.hostname = platform.node()
71 self.testtime = datetime.now().strftime('%Y-%m-%d_%H:%M:%S')
72 if os.path.exists('/proc/version'):
73 fp = open('/proc/version', 'r')
74 val = fp.read().strip()
75 fp.close()
76 self.kernel = self.kernelVersion(val)
77 else:
78 self.kernel = 'unknown'
79 def kernelVersion(self, msg):
80 return msg.split()[2]
81 def kernelParams(self):
82 cmdline = 'initcall_debug log_buf_len=32M'
83 if self.useftrace:
84 cmdline += ' trace_buf_size=128M trace_clock=global '\
85 'trace_options=nooverwrite,funcgraph-abstime,funcgraph-cpu,'\
86 'funcgraph-duration,funcgraph-proc,funcgraph-tail,'\
87 'nofuncgraph-overhead,context-info,graph-time '\
88 'ftrace=function_graph '\
89 'ftrace_graph_max_depth=%d '\
90 'ftrace_graph_filter=%s' % \
91 (self.max_graph_depth, self.graph_filter)
92 return cmdline
93 def setGraphFilter(self, val):
94 fp = open(self.tpath+'available_filter_functions')
95 master = fp.read().split('\n')
96 fp.close()
97 for i in val.split(','):
98 func = i.strip()
99 if func not in master:
100 doError('function "%s" not available for ftrace' % func)
101 self.graph_filter = val
102 def cronjobCmdString(self):
103 cmdline = '%s -cronjob' % os.path.abspath(sys.argv[0])
104 args = iter(sys.argv[1:])
105 for arg in args:
106 if arg in ['-h', '-v', '-cronjob', '-reboot']:
107 continue
108 elif arg in ['-o', '-dmesg', '-ftrace', '-filter']:
109 args.next()
110 continue
111 cmdline += ' '+arg
112 if self.graph_filter != 'do_one_initcall':
113 cmdline += ' -filter "%s"' % self.graph_filter
114 cmdline += ' -o "%s"' % os.path.abspath(self.htmlfile)
115 return cmdline
116 def manualRebootRequired(self):
117 cmdline = self.kernelParams()
118 print 'To generate a new timeline manually, follow these steps:\n'
119 print '1. Add the CMDLINE string to your kernel command line.'
120 print '2. Reboot the system.'
121 print '3. After reboot, re-run this tool with the same arguments but no command (w/o -reboot or -manual).\n'
122 print 'CMDLINE="%s"' % cmdline
123 sys.exit()
124
125sysvals = SystemValues()
126
127# Class: Data
128# Description:
129# The primary container for test data.
130class Data(aslib.Data):
131 dmesg = {} # root data structure
132 start = 0.0 # test start
133 end = 0.0 # test end
134 dmesgtext = [] # dmesg text file in memory
135 testnumber = 0
136 idstr = ''
137 html_device_id = 0
138 valid = False
139 initstart = 0.0
140 boottime = ''
141 phases = ['boot']
142 do_one_initcall = False
143 def __init__(self, num):
144 self.testnumber = num
145 self.idstr = 'a'
146 self.dmesgtext = []
147 self.dmesg = {
148 'boot': {'list': dict(), 'start': -1.0, 'end': -1.0, 'row': 0, 'color': '#dddddd'}
149 }
150 def deviceTopology(self):
151 return ''
152 def newAction(self, phase, name, start, end, ret, ulen):
153 # new device callback for a specific phase
154 self.html_device_id += 1
155 devid = '%s%d' % (self.idstr, self.html_device_id)
156 list = self.dmesg[phase]['list']
157 length = -1.0
158 if(start >= 0 and end >= 0):
159 length = end - start
160 i = 2
161 origname = name
162 while(name in list):
163 name = '%s[%d]' % (origname, i)
164 i += 1
165 list[name] = {'name': name, 'start': start, 'end': end,
166 'pid': 0, 'length': length, 'row': 0, 'id': devid,
167 'ret': ret, 'ulen': ulen }
168 return name
169 def deviceMatch(self, cg):
170 if cg.end - cg.start == 0:
171 return True
172 list = self.dmesg['boot']['list']
173 for devname in list:
174 dev = list[devname]
175 if cg.name == 'do_one_initcall':
176 if(cg.start <= dev['start'] and cg.end >= dev['end'] and dev['length'] > 0):
177 dev['ftrace'] = cg
178 self.do_one_initcall = True
179 return True
180 else:
181 if(cg.start > dev['start'] and cg.end < dev['end']):
182 if 'ftraces' not in dev:
183 dev['ftraces'] = []
184 dev['ftraces'].append(cg)
185 return True
186 return False
187
188# ----------------- FUNCTIONS --------------------
189
190# Function: loadKernelLog
191# Description:
192# Load a raw kernel log from dmesg
193def loadKernelLog():
194 data = Data(0)
195 data.dmesg['boot']['start'] = data.start = ktime = 0.0
196 sysvals.stamp = {
197 'time': datetime.now().strftime('%B %d %Y, %I:%M:%S %p'),
198 'host': sysvals.hostname,
199 'mode': 'boot', 'kernel': ''}
200
201 devtemp = dict()
202 if(sysvals.dmesgfile):
203 lf = open(sysvals.dmesgfile, 'r')
204 else:
205 lf = Popen('dmesg', stdout=PIPE).stdout
206 for line in lf:
207 line = line.replace('\r\n', '')
208 idx = line.find('[')
209 if idx > 1:
210 line = line[idx:]
211 m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
212 if(not m):
213 continue
214 ktime = float(m.group('ktime'))
215 if(ktime > 120):
216 break
217 msg = m.group('msg')
218 data.end = data.initstart = ktime
219 data.dmesgtext.append(line)
220 if(ktime == 0.0 and re.match('^Linux version .*', msg)):
221 if(not sysvals.stamp['kernel']):
222 sysvals.stamp['kernel'] = sysvals.kernelVersion(msg)
223 continue
224 m = re.match('.* setting system clock to (?P<t>.*) UTC.*', msg)
225 if(m):
226 bt = datetime.strptime(m.group('t'), '%Y-%m-%d %H:%M:%S')
227 bt = bt - timedelta(seconds=int(ktime))
228 data.boottime = bt.strftime('%Y-%m-%d_%H:%M:%S')
229 sysvals.stamp['time'] = bt.strftime('%B %d %Y, %I:%M:%S %p')
230 continue
231 m = re.match('^calling *(?P<f>.*)\+.*', msg)
232 if(m):
233 devtemp[m.group('f')] = ktime
234 continue
235 m = re.match('^initcall *(?P<f>.*)\+.* returned (?P<r>.*) after (?P<t>.*) usecs', msg)
236 if(m):
237 data.valid = True
238 f, r, t = m.group('f', 'r', 't')
239 if(f in devtemp):
240 data.newAction('boot', f, devtemp[f], ktime, int(r), int(t))
241 data.end = ktime
242 del devtemp[f]
243 continue
244 if(re.match('^Freeing unused kernel memory.*', msg)):
245 break
246
247 data.dmesg['boot']['end'] = data.end
248 lf.close()
249 return data
250
251# Function: loadTraceLog
252# Description:
253# Check if trace is available and copy to a temp file
254def loadTraceLog(data):
255 # load the data to a temp file if none given
256 if not sysvals.ftracefile:
257 lib = aslib.sysvals
258 aslib.rootCheck(True)
259 if not lib.verifyFtrace():
260 doError('ftrace not available')
261 if lib.fgetVal('current_tracer').strip() != 'function_graph':
262 doError('ftrace not configured for a boot callgraph')
263 sysvals.ftracefile = '/tmp/boot_ftrace.%s.txt' % os.getpid()
264 call('cat '+lib.tpath+'trace > '+sysvals.ftracefile, shell=True)
265 if not sysvals.ftracefile:
266 doError('No trace data available')
267
268 # parse the trace log
269 ftemp = dict()
270 tp = aslib.TestProps()
271 tp.setTracerType('function_graph')
272 tf = open(sysvals.ftracefile, 'r')
273 for line in tf:
274 if line[0] == '#':
275 continue
276 m = re.match(tp.ftrace_line_fmt, line.strip())
277 if(not m):
278 continue
279 m_time, m_proc, m_pid, m_msg, m_dur = \
280 m.group('time', 'proc', 'pid', 'msg', 'dur')
281 if float(m_time) > data.end:
282 break
283 if(m_time and m_pid and m_msg):
284 t = aslib.FTraceLine(m_time, m_msg, m_dur)
285 pid = int(m_pid)
286 else:
287 continue
288 if t.fevent or t.fkprobe:
289 continue
290 key = (m_proc, pid)
291 if(key not in ftemp):
292 ftemp[key] = []
293 ftemp[key].append(aslib.FTraceCallGraph(pid))
294 cg = ftemp[key][-1]
295 if(cg.addLine(t)):
296 ftemp[key].append(aslib.FTraceCallGraph(pid))
297 tf.close()
298
299 # add the callgraph data to the device hierarchy
300 for key in ftemp:
301 proc, pid = key
302 for cg in ftemp[key]:
303 if len(cg.list) < 1 or cg.invalid:
304 continue
305 if(not cg.postProcess()):
306 print('Sanity check failed for %s-%d' % (proc, pid))
307 continue
308 # match cg data to devices
309 if not data.deviceMatch(cg):
310 print ' BAD: %s %s-%d [%f - %f]' % (cg.name, proc, pid, cg.start, cg.end)
311
312# Function: colorForName
313# Description:
314# Generate a repeatable color from a list for a given name
315def colorForName(name):
316 list = [
317 ('c1', '#ec9999'),
318 ('c2', '#ffc1a6'),
319 ('c3', '#fff0a6'),
320 ('c4', '#adf199'),
321 ('c5', '#9fadea'),
322 ('c6', '#a699c1'),
323 ('c7', '#ad99b4'),
324 ('c8', '#eaffea'),
325 ('c9', '#dcecfb'),
326 ('c10', '#ffffea')
327 ]
328 i = 0
329 total = 0
330 count = len(list)
331 while i < len(name):
332 total += ord(name[i])
333 i += 1
334 return list[total % count]
335
336def cgOverview(cg, minlen):
337 stats = dict()
338 large = []
339 for l in cg.list:
340 if l.fcall and l.depth == 1:
341 if l.length >= minlen:
342 large.append(l)
343 if l.name not in stats:
344 stats[l.name] = [0, 0.0]
345 stats[l.name][0] += (l.length * 1000.0)
346 stats[l.name][1] += 1
347 return (large, stats)
348
349# Function: createBootGraph
350# Description:
351# Create the output html file from the resident test data
352# Arguments:
353# testruns: array of Data objects from parseKernelLog or parseTraceLog
354# Output:
355# True if the html file was created, false if it failed
356def createBootGraph(data, embedded):
357 # html function templates
358 html_srccall = '<div id={6} title="{5}" class="srccall" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;">{0}</div>\n'
359 html_timetotal = '<table class="time1">\n<tr>'\
360 '<td class="blue">Time from Kernel Boot to start of User Mode: <b>{0} ms</b></td>'\
361 '</tr>\n</table>\n'
362
363 # device timeline
364 devtl = aslib.Timeline(100, 20)
365
366 # write the test title and general info header
367 devtl.createHeader(sysvals, 'noftrace')
368
369 # Generate the header for this timeline
370 t0 = data.start
371 tMax = data.end
372 tTotal = tMax - t0
373 if(tTotal == 0):
374 print('ERROR: No timeline data')
375 return False
376 boot_time = '%.0f'%(tTotal*1000)
377 devtl.html += html_timetotal.format(boot_time)
378
379 # determine the maximum number of rows we need to draw
380 phase = 'boot'
381 list = data.dmesg[phase]['list']
382 devlist = []
383 for devname in list:
384 d = aslib.DevItem(0, phase, list[devname])
385 devlist.append(d)
386 devtl.getPhaseRows(devlist)
387 devtl.calcTotalRows()
388
389 # draw the timeline background
390 devtl.createZoomBox()
391 boot = data.dmesg[phase]
392 length = boot['end']-boot['start']
393 left = '%.3f' % (((boot['start']-t0)*100.0)/tTotal)
394 width = '%.3f' % ((length*100.0)/tTotal)
395 devtl.html += devtl.html_tblock.format(phase, left, width, devtl.scaleH)
396 devtl.html += devtl.html_phase.format('0', '100', \
397 '%.3f'%devtl.scaleH, '%.3f'%devtl.bodyH, \
398 'white', '')
399
400 # draw the device timeline
401 num = 0
402 devstats = dict()
403 for devname in sorted(list):
404 cls, color = colorForName(devname)
405 dev = list[devname]
406 info = '@|%.3f|%.3f|%.3f|%d' % (dev['start']*1000.0, dev['end']*1000.0,
407 dev['ulen']/1000.0, dev['ret'])
408 devstats[dev['id']] = {'info':info}
409 dev['color'] = color
410 height = devtl.phaseRowHeight(0, phase, dev['row'])
411 top = '%.6f' % ((dev['row']*height) + devtl.scaleH)
412 left = '%.6f' % (((dev['start']-t0)*100)/tTotal)
413 width = '%.6f' % (((dev['end']-dev['start'])*100)/tTotal)
414 length = ' (%0.3f ms) ' % ((dev['end']-dev['start'])*1000)
415 devtl.html += devtl.html_device.format(dev['id'],
416 devname+length+'kernel_mode', left, top, '%.3f'%height,
417 width, devname, ' '+cls, '')
418 rowtop = devtl.phaseRowTop(0, phase, dev['row'])
419 height = '%.6f' % (devtl.rowH / 2)
420 top = '%.6f' % (rowtop + devtl.scaleH + (devtl.rowH / 2))
421 if data.do_one_initcall:
422 if('ftrace' not in dev):
423 continue
424 cg = dev['ftrace']
425 large, stats = cgOverview(cg, 0.001)
426 devstats[dev['id']]['fstat'] = stats
427 for l in large:
428 left = '%f' % (((l.time-t0)*100)/tTotal)
429 width = '%f' % (l.length*100/tTotal)
430 title = '%s (%0.3fms)' % (l.name, l.length * 1000.0)
431 devtl.html += html_srccall.format(l.name, left,
432 top, height, width, title, 'x%d'%num)
433 num += 1
434 continue
435 if('ftraces' not in dev):
436 continue
437 for cg in dev['ftraces']:
438 left = '%f' % (((cg.start-t0)*100)/tTotal)
439 width = '%f' % ((cg.end-cg.start)*100/tTotal)
440 cglen = (cg.end - cg.start) * 1000.0
441 title = '%s (%0.3fms)' % (cg.name, cglen)
442 cg.id = 'x%d' % num
443 devtl.html += html_srccall.format(cg.name, left,
444 top, height, width, title, dev['id']+cg.id)
445 num += 1
446
447 # draw the time scale, try to make the number of labels readable
448 devtl.createTimeScale(t0, tMax, tTotal, phase)
449 devtl.html += '</div>\n'
450
451 # timeline is finished
452 devtl.html += '</div>\n</div>\n'
453
454 if(sysvals.outfile == sysvals.htmlfile):
455 hf = open(sysvals.htmlfile, 'a')
456 else:
457 hf = open(sysvals.htmlfile, 'w')
458
459 # add the css if this is not an embedded run
460 extra = '\
461 .c1 {background:rgba(209,0,0,0.4);}\n\
462 .c2 {background:rgba(255,102,34,0.4);}\n\
463 .c3 {background:rgba(255,218,33,0.4);}\n\
464 .c4 {background:rgba(51,221,0,0.4);}\n\
465 .c5 {background:rgba(17,51,204,0.4);}\n\
466 .c6 {background:rgba(34,0,102,0.4);}\n\
467 .c7 {background:rgba(51,0,68,0.4);}\n\
468 .c8 {background:rgba(204,255,204,0.4);}\n\
469 .c9 {background:rgba(169,208,245,0.4);}\n\
470 .c10 {background:rgba(255,255,204,0.4);}\n\
471 .vt {transform:rotate(-60deg);transform-origin:0 0;}\n\
472 table.fstat {table-layout:fixed;padding:150px 15px 0 0;font-size:10px;column-width:30px;}\n\
473 .fstat th {width:55px;}\n\
474 .fstat td {text-align:left;width:35px;}\n\
475 .srccall {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\
476 .srccall:hover {color:white;font-weight:bold;border:1px solid white;}\n'
477 if(not embedded):
478 aslib.addCSS(hf, sysvals, 1, False, extra)
479
480 # write the device timeline
481 hf.write(devtl.html)
482
483 # add boot specific html
484 statinfo = 'var devstats = {\n'
485 for n in sorted(devstats):
486 statinfo += '\t"%s": [\n\t\t"%s",\n' % (n, devstats[n]['info'])
487 if 'fstat' in devstats[n]:
488 funcs = devstats[n]['fstat']
489 for f in sorted(funcs, key=funcs.get, reverse=True):
490 if funcs[f][0] < 0.01 and len(funcs) > 10:
491 break
492 statinfo += '\t\t"%f|%s|%d",\n' % (funcs[f][0], f, funcs[f][1])
493 statinfo += '\t],\n'
494 statinfo += '};\n'
495 html = \
496 '<div id="devicedetailtitle"></div>\n'\
497 '<div id="devicedetail" style="display:none;">\n'\
498 '<div id="devicedetail0">\n'\
499 '<div id="kernel_mode" class="phaselet" style="left:0%;width:100%;background:#DDDDDD"></div>\n'\
500 '</div>\n</div>\n'\
501 '<script type="text/javascript">\n'+statinfo+\
502 '</script>\n'
503 hf.write(html)
504
505 # add the callgraph html
506 if(sysvals.usecallgraph):
507 aslib.addCallgraphs(sysvals, hf, data)
508
509 # add the dmesg log as a hidden div
510 if sysvals.addlogs:
511 hf.write('<div id="dmesglog" style="display:none;">\n')
512 for line in data.dmesgtext:
513 line = line.replace('<', '&lt').replace('>', '&gt')
514 hf.write(line)
515 hf.write('</div>\n')
516
517 if(not embedded):
518 # write the footer and close
519 aslib.addScriptCode(hf, [data])
520 hf.write('</body>\n</html>\n')
521 else:
522 # embedded out will be loaded in a page, skip the js
523 hf.write('<div id=bounds style=display:none>%f,%f</div>' % \
524 (data.start*1000, data.initstart*1000))
525 hf.close()
526 return True
527
528# Function: updateCron
529# Description:
530# (restore=False) Set the tool to run automatically on reboot
531# (restore=True) Restore the original crontab
532def updateCron(restore=False):
533 if not restore:
534 sysvals.rootUser(True)
535 crondir = '/var/spool/cron/crontabs/'
536 cronfile = crondir+'root'
537 backfile = crondir+'root-analyze_boot-backup'
538 if not os.path.exists(crondir):
539 doError('%s not found' % crondir)
540 out = Popen(['which', 'crontab'], stdout=PIPE).stdout.read()
541 if not out:
542 doError('crontab not found')
543 # on restore: move the backup cron back into place
544 if restore:
545 if os.path.exists(backfile):
546 shutil.move(backfile, cronfile)
547 return
548 # backup current cron and install new one with reboot
549 if os.path.exists(cronfile):
550 shutil.move(cronfile, backfile)
551 else:
552 fp = open(backfile, 'w')
553 fp.close()
554 res = -1
555 try:
556 fp = open(backfile, 'r')
557 op = open(cronfile, 'w')
558 for line in fp:
559 if '@reboot' not in line:
560 op.write(line)
561 continue
562 fp.close()
563 op.write('@reboot python %s\n' % sysvals.cronjobCmdString())
564 op.close()
565 res = call('crontab %s' % cronfile, shell=True)
566 except Exception, e:
567 print 'Exception: %s' % str(e)
568 shutil.move(backfile, cronfile)
569 res = -1
570 if res != 0:
571 doError('crontab failed')
572
573# Function: updateGrub
574# Description:
575# update grub.cfg for all kernels with our parameters
576def updateGrub(restore=False):
577 # call update-grub on restore
578 if restore:
579 try:
580 call(['update-grub'], stderr=PIPE, stdout=PIPE,
581 env={'PATH': '.:/sbin:/usr/sbin:/usr/bin:/sbin:/bin'})
582 except Exception, e:
583 print 'Exception: %s\n' % str(e)
584 return
585 # verify we can do this
586 sysvals.rootUser(True)
587 grubfile = '/etc/default/grub'
588 if not os.path.exists(grubfile):
589 print 'ERROR: Unable to set the kernel parameters via grub.\n'
590 sysvals.manualRebootRequired()
591 out = Popen(['which', 'update-grub'], stdout=PIPE).stdout.read()
592 if not out:
593 print 'ERROR: Unable to set the kernel parameters via grub.\n'
594 sysvals.manualRebootRequired()
595
596 # extract the option and create a grub config without it
597 tgtopt = 'GRUB_CMDLINE_LINUX_DEFAULT'
598 cmdline = ''
599 tempfile = '/etc/default/grub.analyze_boot'
600 shutil.move(grubfile, tempfile)
601 res = -1
602 try:
603 fp = open(tempfile, 'r')
604 op = open(grubfile, 'w')
605 cont = False
606 for line in fp:
607 line = line.strip()
608 if len(line) == 0 or line[0] == '#':
609 continue
610 opt = line.split('=')[0].strip()
611 if opt == tgtopt:
612 cmdline = line.split('=', 1)[1].strip('\\')
613 if line[-1] == '\\':
614 cont = True
615 elif cont:
616 cmdline += line.strip('\\')
617 if line[-1] != '\\':
618 cont = False
619 else:
620 op.write('%s\n' % line)
621 fp.close()
622 # if the target option value is in quotes, strip them
623 sp = '"'
624 val = cmdline.strip()
625 if val[0] == '\'' or val[0] == '"':
626 sp = val[0]
627 val = val.strip(sp)
628 cmdline = val
629 # append our cmd line options
630 if len(cmdline) > 0:
631 cmdline += ' '
632 cmdline += sysvals.kernelParams()
633 # write out the updated target option
634 op.write('\n%s=%s%s%s\n' % (tgtopt, sp, cmdline, sp))
635 op.close()
636 res = call('update-grub')
637 os.remove(grubfile)
638 except Exception, e:
639 print 'Exception: %s' % str(e)
640 res = -1
641 # cleanup
642 shutil.move(tempfile, grubfile)
643 if res != 0:
644 doError('update-grub failed')
645
646# Function: doError
647# Description:
648# generic error function for catastrphic failures
649# Arguments:
650# msg: the error message to print
651# help: True if printHelp should be called after, False otherwise
652def doError(msg, help=False):
653 if help == True:
654 printHelp()
655 print 'ERROR: %s\n' % msg
656 sys.exit()
657
658# Function: printHelp
659# Description:
660# print out the help text
661def printHelp():
662 print('')
663 print('%s v%.1f' % (sysvals.title, sysvals.version))
664 print('Usage: bootgraph <options> <command>')
665 print('')
666 print('Description:')
667 print(' This tool reads in a dmesg log of linux kernel boot and')
668 print(' creates an html representation of the boot timeline up to')
669 print(' the start of the init process.')
670 print('')
671 print(' If no specific command is given the tool reads the current dmesg')
672 print(' and/or ftrace log and outputs bootgraph.html')
673 print('')
674 print('Options:')
675 print(' -h Print this help text')
676 print(' -v Print the current tool version')
677 print(' -addlogs Add the dmesg log to the html output')
678 print(' -o file Html timeline name (default: bootgraph.html)')
679 print(' [advanced]')
680 print(' -f Use ftrace to add function detail (default: disabled)')
681 print(' -callgraph Add callgraph detail, can be very large (default: disabled)')
682 print(' -maxdepth N limit the callgraph data to N call levels (default: 2)')
683 print(' -mincg ms Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)')
684 print(' -timeprec N Number of significant digits in timestamps (0:S, 3:ms, [6:us])')
685 print(' -expandcg pre-expand the callgraph data in the html output (default: disabled)')
686 print(' -filter list Limit ftrace to comma-delimited list of functions (default: do_one_initcall)')
687 print(' [commands]')
688 print(' -reboot Reboot the machine automatically and generate a new timeline')
689 print(' -manual Show the requirements to generate a new timeline manually')
690 print(' -dmesg file Load a stored dmesg file (used with -ftrace)')
691 print(' -ftrace file Load a stored ftrace file (used with -dmesg)')
692 print(' -flistall Print all functions capable of being captured in ftrace')
693 print('')
694 return True
695
696# ----------------- MAIN --------------------
697# exec start (skipped if script is loaded as library)
698if __name__ == '__main__':
699 # loop through the command line arguments
700 cmd = ''
701 simplecmds = ['-updategrub', '-flistall']
702 args = iter(sys.argv[1:])
703 for arg in args:
704 if(arg == '-h'):
705 printHelp()
706 sys.exit()
707 elif(arg == '-v'):
708 print("Version %.1f" % sysvals.version)
709 sys.exit()
710 elif(arg in simplecmds):
711 cmd = arg[1:]
712 elif(arg == '-f'):
713 sysvals.useftrace = True
714 elif(arg == '-callgraph'):
715 sysvals.useftrace = True
716 sysvals.usecallgraph = True
717 elif(arg == '-mincg'):
718 sysvals.mincglen = aslib.getArgFloat('-mincg', args, 0.0, 10000.0)
719 elif(arg == '-timeprec'):
720 sysvals.setPrecision(aslib.getArgInt('-timeprec', args, 0, 6))
721 elif(arg == '-maxdepth'):
722 sysvals.max_graph_depth = aslib.getArgInt('-maxdepth', args, 0, 1000)
723 elif(arg == '-filter'):
724 try:
725 val = args.next()
726 except:
727 doError('No filter functions supplied', True)
728 aslib.rootCheck(True)
729 sysvals.setGraphFilter(val)
730 elif(arg == '-ftrace'):
731 try:
732 val = args.next()
733 except:
734 doError('No ftrace file supplied', True)
735 if(os.path.exists(val) == False):
736 doError('%s does not exist' % val)
737 sysvals.ftracefile = val
738 elif(arg == '-addlogs'):
739 sysvals.addlogs = True
740 elif(arg == '-expandcg'):
741 sysvals.cgexp = True
742 elif(arg == '-dmesg'):
743 try:
744 val = args.next()
745 except:
746 doError('No dmesg file supplied', True)
747 if(os.path.exists(val) == False):
748 doError('%s does not exist' % val)
749 if(sysvals.htmlfile == val or sysvals.outfile == val):
750 doError('Output filename collision')
751 sysvals.dmesgfile = val
752 elif(arg == '-o'):
753 try:
754 val = args.next()
755 except:
756 doError('No HTML filename supplied', True)
757 if(sysvals.dmesgfile == val or sysvals.ftracefile == val):
758 doError('Output filename collision')
759 sysvals.htmlfile = val
760 elif(arg == '-reboot'):
761 if sysvals.iscronjob:
762 doError('-reboot and -cronjob are incompatible')
763 sysvals.reboot = True
764 elif(arg == '-manual'):
765 sysvals.reboot = True
766 sysvals.manual = True
767 # remaining options are only for cron job use
768 elif(arg == '-cronjob'):
769 sysvals.iscronjob = True
770 if sysvals.reboot:
771 doError('-reboot and -cronjob are incompatible')
772 else:
773 doError('Invalid argument: '+arg, True)
774
775 if cmd != '':
776 if cmd == 'updategrub':
777 updateGrub()
778 elif cmd == 'flistall':
779 sysvals.getFtraceFilterFunctions(False)
780 sys.exit()
781
782 # update grub, setup a cronjob, and reboot
783 if sysvals.reboot:
784 if not sysvals.manual:
785 updateGrub()
786 updateCron()
787 call('reboot')
788 else:
789 sysvals.manualRebootRequired()
790 sys.exit()
791
792 # disable the cronjob
793 if sysvals.iscronjob:
794 updateCron(True)
795 updateGrub(True)
796
797 data = loadKernelLog()
798 if sysvals.useftrace:
799 loadTraceLog(data)
800 if sysvals.iscronjob:
801 try:
802 sysvals.fsetVal('0', 'tracing_on')
803 except:
804 pass
805
806 if(sysvals.outfile and sysvals.phoronix):
807 fp = open(sysvals.outfile, 'w')
808 fp.write('pass %s initstart %.3f end %.3f boot %s\n' %
809 (data.valid, data.initstart*1000, data.end*1000, data.boottime))
810 fp.close()
811 if(not data.valid):
812 if sysvals.dmesgfile:
813 doError('No initcall data found in %s' % sysvals.dmesgfile)
814 else:
815 doError('No initcall data found, is initcall_debug enabled?')
816
817 print(' Host: %s' % sysvals.hostname)
818 print(' Test time: %s' % sysvals.testtime)
819 print(' Boot time: %s' % data.boottime)
820 print('Kernel Version: %s' % sysvals.kernel)
821 print(' Kernel start: %.3f' % (data.start * 1000))
822 print(' init start: %.3f' % (data.initstart * 1000))
823
824 createBootGraph(data, sysvals.phoronix)
diff --git a/scripts/analyze_suspend.py b/tools/power/pm-graph/analyze_suspend.py
index 20cdb2bc1dae..a9206e67fc1f 100755
--- a/scripts/analyze_suspend.py
+++ b/tools/power/pm-graph/analyze_suspend.py
@@ -12,10 +12,6 @@
12# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13# more details. 13# more details.
14# 14#
15# You should have received a copy of the GNU General Public License along with
16# this program; if not, write to the Free Software Foundation, Inc.,
17# 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
18#
19# Authors: 15# Authors:
20# Todd Brandt <todd.e.brandt@linux.intel.com> 16# Todd Brandt <todd.e.brandt@linux.intel.com>
21# 17#
@@ -23,7 +19,7 @@
23# Home Page 19# Home Page
24# https://01.org/suspendresume 20# https://01.org/suspendresume
25# Source repo 21# Source repo
26# https://github.com/01org/suspendresume 22# https://github.com/01org/pm-graph
27# 23#
28# Description: 24# Description:
29# This tool is designed to assist kernel and OS developers in optimizing 25# This tool is designed to assist kernel and OS developers in optimizing
@@ -71,14 +67,16 @@ from subprocess import call, Popen, PIPE
71# A global, single-instance container used to 67# A global, single-instance container used to
72# store system values and test parameters 68# store system values and test parameters
73class SystemValues: 69class SystemValues:
70 title = 'SleepGraph'
71 version = '4.6'
74 ansi = False 72 ansi = False
75 version = '4.5'
76 verbose = False 73 verbose = False
77 addlogs = False 74 addlogs = False
78 mindevlen = 0.0 75 mindevlen = 0.0
79 mincglen = 0.0 76 mincglen = 0.0
80 cgphase = '' 77 cgphase = ''
81 cgtest = -1 78 cgtest = -1
79 max_graph_depth = 0
82 callloopmaxgap = 0.0001 80 callloopmaxgap = 0.0001
83 callloopmaxlen = 0.005 81 callloopmaxlen = 0.005
84 srgap = 0 82 srgap = 0
@@ -106,8 +104,8 @@ class SystemValues:
106 ftracefile = '' 104 ftracefile = ''
107 htmlfile = '' 105 htmlfile = ''
108 embedded = False 106 embedded = False
109 rtcwake = False 107 rtcwake = True
110 rtcwaketime = 10 108 rtcwaketime = 15
111 rtcpath = '' 109 rtcpath = ''
112 devicefilter = [] 110 devicefilter = []
113 stamp = 0 111 stamp = 0
@@ -235,6 +233,12 @@ class SystemValues:
235 self.rtcpath = rtc 233 self.rtcpath = rtc
236 if (hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()): 234 if (hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()):
237 self.ansi = True 235 self.ansi = True
236 def rootUser(self, fatal=False):
237 if 'USER' in os.environ and os.environ['USER'] == 'root':
238 return True
239 if fatal:
240 doError('This command must be run as root')
241 return False
238 def setPrecision(self, num): 242 def setPrecision(self, num):
239 if num < 0 or num > 6: 243 if num < 0 or num > 6:
240 return 244 return
@@ -564,7 +568,7 @@ class SystemValues:
564 self.fsetVal('global', 'trace_clock') 568 self.fsetVal('global', 'trace_clock')
565 # set trace buffer to a huge value 569 # set trace buffer to a huge value
566 self.fsetVal('nop', 'current_tracer') 570 self.fsetVal('nop', 'current_tracer')
567 self.fsetVal('100000', 'buffer_size_kb') 571 self.fsetVal('131073', 'buffer_size_kb')
568 # go no further if this is just a status check 572 # go no further if this is just a status check
569 if testing: 573 if testing:
570 return 574 return
@@ -583,7 +587,7 @@ class SystemValues:
583 self.fsetVal('nofuncgraph-overhead', 'trace_options') 587 self.fsetVal('nofuncgraph-overhead', 'trace_options')
584 self.fsetVal('context-info', 'trace_options') 588 self.fsetVal('context-info', 'trace_options')
585 self.fsetVal('graph-time', 'trace_options') 589 self.fsetVal('graph-time', 'trace_options')
586 self.fsetVal('0', 'max_graph_depth') 590 self.fsetVal('%d' % self.max_graph_depth, 'max_graph_depth')
587 cf = ['dpm_run_callback'] 591 cf = ['dpm_run_callback']
588 if(self.usetraceeventsonly): 592 if(self.usetraceeventsonly):
589 cf += ['dpm_prepare', 'dpm_complete'] 593 cf += ['dpm_prepare', 'dpm_complete']
@@ -639,6 +643,12 @@ class SystemValues:
639 return '\x1B[%d;40m%s\x1B[m' % (color, str) 643 return '\x1B[%d;40m%s\x1B[m' % (color, str)
640 644
641sysvals = SystemValues() 645sysvals = SystemValues()
646suspendmodename = {
647 'freeze': 'Freeze (S0)',
648 'standby': 'Standby (S1)',
649 'mem': 'Suspend (S3)',
650 'disk': 'Hibernate (S4)'
651}
642 652
643# Class: DevProps 653# Class: DevProps
644# Description: 654# Description:
@@ -1013,6 +1023,8 @@ class Data:
1013 tmp = dict() 1023 tmp = dict()
1014 for devname in list: 1024 for devname in list:
1015 dev = list[devname] 1025 dev = list[devname]
1026 if dev['length'] == 0:
1027 continue
1016 tmp[dev['start']] = devname 1028 tmp[dev['start']] = devname
1017 for t in sorted(tmp): 1029 for t in sorted(tmp):
1018 slist.append(tmp[t]) 1030 slist.append(tmp[t])
@@ -1477,12 +1489,14 @@ class FTraceLine:
1477# Each instance is tied to a single device in a single phase, and is 1489# Each instance is tied to a single device in a single phase, and is
1478# comprised of an ordered list of FTraceLine objects 1490# comprised of an ordered list of FTraceLine objects
1479class FTraceCallGraph: 1491class FTraceCallGraph:
1492 id = ''
1480 start = -1.0 1493 start = -1.0
1481 end = -1.0 1494 end = -1.0
1482 list = [] 1495 list = []
1483 invalid = False 1496 invalid = False
1484 depth = 0 1497 depth = 0
1485 pid = 0 1498 pid = 0
1499 name = ''
1486 def __init__(self, pid): 1500 def __init__(self, pid):
1487 self.start = -1.0 1501 self.start = -1.0
1488 self.end = -1.0 1502 self.end = -1.0
@@ -1631,9 +1645,17 @@ class FTraceCallGraph:
1631 return True 1645 return True
1632 return False 1646 return False
1633 def postProcess(self, debug=False): 1647 def postProcess(self, debug=False):
1648 if len(self.list) > 0:
1649 self.name = self.list[0].name
1634 stack = dict() 1650 stack = dict()
1635 cnt = 0 1651 cnt = 0
1652 last = 0
1636 for l in self.list: 1653 for l in self.list:
1654 # ftrace bug: reported duration is not reliable
1655 # check each leaf and clip it at max possible length
1656 if(last and last.freturn and last.fcall):
1657 if last.length > l.time - last.time:
1658 last.length = l.time - last.time
1637 if(l.fcall and not l.freturn): 1659 if(l.fcall and not l.freturn):
1638 stack[l.depth] = l 1660 stack[l.depth] = l
1639 cnt += 1 1661 cnt += 1
@@ -1643,11 +1665,12 @@ class FTraceCallGraph:
1643 print 'Post Process Error: Depth missing' 1665 print 'Post Process Error: Depth missing'
1644 l.debugPrint() 1666 l.debugPrint()
1645 return False 1667 return False
1646 # transfer total time from return line to call line 1668 # calculate call length from call/return lines
1647 stack[l.depth].length = l.length 1669 stack[l.depth].length = l.time - stack[l.depth].time
1648 stack.pop(l.depth) 1670 stack.pop(l.depth)
1649 l.length = 0 1671 l.length = 0
1650 cnt -= 1 1672 cnt -= 1
1673 last = l
1651 if(cnt == 0): 1674 if(cnt == 0):
1652 # trace caught the whole call tree 1675 # trace caught the whole call tree
1653 return True 1676 return True
@@ -1664,8 +1687,8 @@ class FTraceCallGraph:
1664 'dpm_prepare': 'suspend_prepare', 1687 'dpm_prepare': 'suspend_prepare',
1665 'dpm_complete': 'resume_complete' 1688 'dpm_complete': 'resume_complete'
1666 } 1689 }
1667 if(self.list[0].name in borderphase): 1690 if(self.name in borderphase):
1668 p = borderphase[self.list[0].name] 1691 p = borderphase[self.name]
1669 list = data.dmesg[p]['list'] 1692 list = data.dmesg[p]['list']
1670 for devname in list: 1693 for devname in list:
1671 dev = list[devname] 1694 dev = list[devname]
@@ -1690,7 +1713,7 @@ class FTraceCallGraph:
1690 break 1713 break
1691 return found 1714 return found
1692 def newActionFromFunction(self, data): 1715 def newActionFromFunction(self, data):
1693 name = self.list[0].name 1716 name = self.name
1694 if name in ['dpm_run_callback', 'dpm_prepare', 'dpm_complete']: 1717 if name in ['dpm_run_callback', 'dpm_prepare', 'dpm_complete']:
1695 return 1718 return
1696 fs = self.start 1719 fs = self.start
@@ -1710,7 +1733,7 @@ class FTraceCallGraph:
1710 phase, myname = out 1733 phase, myname = out
1711 data.dmesg[phase]['list'][myname]['ftrace'] = self 1734 data.dmesg[phase]['list'][myname]['ftrace'] = self
1712 def debugPrint(self): 1735 def debugPrint(self):
1713 print('[%f - %f] %s (%d)') % (self.start, self.end, self.list[0].name, self.pid) 1736 print('[%f - %f] %s (%d)') % (self.start, self.end, self.name, self.pid)
1714 for l in self.list: 1737 for l in self.list:
1715 if(l.freturn and l.fcall): 1738 if(l.freturn and l.fcall):
1716 print('%f (%02d): %s(); (%.3f us)' % (l.time, \ 1739 print('%f (%02d): %s(); (%.3f us)' % (l.time, \
@@ -1738,7 +1761,7 @@ class DevItem:
1738# A container for a device timeline which calculates 1761# A container for a device timeline which calculates
1739# all the html properties to display it correctly 1762# all the html properties to display it correctly
1740class Timeline: 1763class Timeline:
1741 html = {} 1764 html = ''
1742 height = 0 # total timeline height 1765 height = 0 # total timeline height
1743 scaleH = 20 # timescale (top) row height 1766 scaleH = 20 # timescale (top) row height
1744 rowH = 30 # device row height 1767 rowH = 30 # device row height
@@ -1746,14 +1769,28 @@ class Timeline:
1746 rows = 0 # total timeline rows 1769 rows = 0 # total timeline rows
1747 rowlines = dict() 1770 rowlines = dict()
1748 rowheight = dict() 1771 rowheight = dict()
1772 html_tblock = '<div id="block{0}" class="tblock" style="left:{1}%;width:{2}%;"><div class="tback" style="height:{3}px"></div>\n'
1773 html_device = '<div id="{0}" title="{1}" class="thread{7}" style="left:{2}%;top:{3}px;height:{4}px;width:{5}%;{8}">{6}</div>\n'
1774 html_phase = '<div class="phase" style="left:{0}%;width:{1}%;top:{2}px;height:{3}px;background:{4}">{5}</div>\n'
1775 html_phaselet = '<div id="{0}" class="phaselet" style="left:{1}%;width:{2}%;background:{3}"></div>\n'
1749 def __init__(self, rowheight, scaleheight): 1776 def __init__(self, rowheight, scaleheight):
1750 self.rowH = rowheight 1777 self.rowH = rowheight
1751 self.scaleH = scaleheight 1778 self.scaleH = scaleheight
1752 self.html = { 1779 self.html = ''
1753 'header': '', 1780 def createHeader(self, sv, suppress=''):
1754 'timeline': '', 1781 if(not sv.stamp['time']):
1755 'legend': '', 1782 return
1756 } 1783 self.html += '<div class="version"><a href="https://01.org/suspendresume">%s v%s</a></div>' \
1784 % (sv.title, sv.version)
1785 if sv.logmsg and 'log' not in suppress:
1786 self.html += '<button id="showtest" class="logbtn">log</button>'
1787 if sv.addlogs and 'dmesg' not in suppress:
1788 self.html += '<button id="showdmesg" class="logbtn">dmesg</button>'
1789 if sv.addlogs and sv.ftracefile and 'ftrace' not in suppress:
1790 self.html += '<button id="showftrace" class="logbtn">ftrace</button>'
1791 headline_stamp = '<div class="stamp">{0} {1} {2} {3}</div>\n'
1792 self.html += headline_stamp.format(sv.stamp['host'], sv.stamp['kernel'],
1793 sv.stamp['mode'], sv.stamp['time'])
1757 # Function: getDeviceRows 1794 # Function: getDeviceRows
1758 # Description: 1795 # Description:
1759 # determine how may rows the device funcs will take 1796 # determine how may rows the device funcs will take
@@ -1880,10 +1917,8 @@ class Timeline:
1880 break 1917 break
1881 top += self.rowheight[test][phase][i] 1918 top += self.rowheight[test][phase][i]
1882 return top 1919 return top
1883 # Function: calcTotalRows
1884 # Description:
1885 # Calculate the heights and offsets for the header and rows
1886 def calcTotalRows(self): 1920 def calcTotalRows(self):
1921 # Calculate the heights and offsets for the header and rows
1887 maxrows = 0 1922 maxrows = 0
1888 standardphases = [] 1923 standardphases = []
1889 for t in self.rowlines: 1924 for t in self.rowlines:
@@ -1901,6 +1936,20 @@ class Timeline:
1901 for t, p in standardphases: 1936 for t, p in standardphases:
1902 for i in sorted(self.rowheight[t][p]): 1937 for i in sorted(self.rowheight[t][p]):
1903 self.rowheight[t][p][i] = self.bodyH/len(self.rowlines[t][p]) 1938 self.rowheight[t][p][i] = self.bodyH/len(self.rowlines[t][p])
1939 def createZoomBox(self, mode='command', testcount=1):
1940 # Create bounding box, add buttons
1941 html_zoombox = '<center><button id="zoomin">ZOOM IN +</button><button id="zoomout">ZOOM OUT -</button><button id="zoomdef">ZOOM 1:1</button></center>\n'
1942 html_timeline = '<div id="dmesgzoombox" class="zoombox">\n<div id="{0}" class="timeline" style="height:{1}px">\n'
1943 html_devlist1 = '<button id="devlist1" class="devlist" style="float:left;">Device Detail{0}</button>'
1944 html_devlist2 = '<button id="devlist2" class="devlist" style="float:right;">Device Detail2</button>\n'
1945 if mode != 'command':
1946 if testcount > 1:
1947 self.html += html_devlist2
1948 self.html += html_devlist1.format('1')
1949 else:
1950 self.html += html_devlist1.format('')
1951 self.html += html_zoombox
1952 self.html += html_timeline.format('dmesg', self.height)
1904 # Function: createTimeScale 1953 # Function: createTimeScale
1905 # Description: 1954 # Description:
1906 # Create the timescale for a timeline block 1955 # Create the timescale for a timeline block
@@ -1913,7 +1962,7 @@ class Timeline:
1913 # The html code needed to display the time scale 1962 # The html code needed to display the time scale
1914 def createTimeScale(self, m0, mMax, tTotal, mode): 1963 def createTimeScale(self, m0, mMax, tTotal, mode):
1915 timescale = '<div class="t" style="right:{0}%">{1}</div>\n' 1964 timescale = '<div class="t" style="right:{0}%">{1}</div>\n'
1916 rline = '<div class="t" style="left:0;border-left:1px solid black;border-right:0;">Resume</div>\n' 1965 rline = '<div class="t" style="left:0;border-left:1px solid black;border-right:0;">{0}</div>\n'
1917 output = '<div class="timescale">\n' 1966 output = '<div class="timescale">\n'
1918 # set scale for timeline 1967 # set scale for timeline
1919 mTotal = mMax - m0 1968 mTotal = mMax - m0
@@ -1926,21 +1975,20 @@ class Timeline:
1926 divEdge = (mTotal - tS*(divTotal-1))*100/mTotal 1975 divEdge = (mTotal - tS*(divTotal-1))*100/mTotal
1927 for i in range(divTotal): 1976 for i in range(divTotal):
1928 htmlline = '' 1977 htmlline = ''
1929 if(mode == 'resume'): 1978 if(mode == 'suspend'):
1930 pos = '%0.3f' % (100 - ((float(i)*tS*100)/mTotal))
1931 val = '%0.fms' % (float(i)*tS*1000)
1932 htmlline = timescale.format(pos, val)
1933 if(i == 0):
1934 htmlline = rline
1935 else:
1936 pos = '%0.3f' % (100 - ((float(i)*tS*100)/mTotal) - divEdge) 1979 pos = '%0.3f' % (100 - ((float(i)*tS*100)/mTotal) - divEdge)
1937 val = '%0.fms' % (float(i-divTotal+1)*tS*1000) 1980 val = '%0.fms' % (float(i-divTotal+1)*tS*1000)
1938 if(i == divTotal - 1): 1981 if(i == divTotal - 1):
1939 val = 'Suspend' 1982 val = mode
1983 htmlline = timescale.format(pos, val)
1984 else:
1985 pos = '%0.3f' % (100 - ((float(i)*tS*100)/mTotal))
1986 val = '%0.fms' % (float(i)*tS*1000)
1940 htmlline = timescale.format(pos, val) 1987 htmlline = timescale.format(pos, val)
1988 if(i == 0):
1989 htmlline = rline.format(mode)
1941 output += htmlline 1990 output += htmlline
1942 output += '</div>\n' 1991 self.html += output+'</div>\n'
1943 return output
1944 1992
1945# Class: TestProps 1993# Class: TestProps
1946# Description: 1994# Description:
@@ -2009,7 +2057,7 @@ class ProcessMonitor:
2009 val['kern'] = kern 2057 val['kern'] = kern
2010 if ujiff > 0 or kjiff > 0: 2058 if ujiff > 0 or kjiff > 0:
2011 running[pid] = ujiff + kjiff 2059 running[pid] = ujiff + kjiff
2012 result = process.wait() 2060 process.wait()
2013 out = '' 2061 out = ''
2014 for pid in running: 2062 for pid in running:
2015 jiffies = running[pid] 2063 jiffies = running[pid]
@@ -2071,26 +2119,6 @@ def parseStamp(line, data):
2071 if not sysvals.stamp: 2119 if not sysvals.stamp:
2072 sysvals.stamp = data.stamp 2120 sysvals.stamp = data.stamp
2073 2121
2074# Function: diffStamp
2075# Description:
2076# compare the host, kernel, and mode fields in 3 stamps
2077# Arguments:
2078# stamp1: string array with mode, kernel, and host
2079# stamp2: string array with mode, kernel, and host
2080# Return:
2081# True if stamps differ, False if they're the same
2082def diffStamp(stamp1, stamp2):
2083 if 'host' in stamp1 and 'host' in stamp2:
2084 if stamp1['host'] != stamp2['host']:
2085 return True
2086 if 'kernel' in stamp1 and 'kernel' in stamp2:
2087 if stamp1['kernel'] != stamp2['kernel']:
2088 return True
2089 if 'mode' in stamp1 and 'mode' in stamp2:
2090 if stamp1['mode'] != stamp2['mode']:
2091 return True
2092 return False
2093
2094# Function: doesTraceLogHaveTraceEvents 2122# Function: doesTraceLogHaveTraceEvents
2095# Description: 2123# Description:
2096# Quickly determine if the ftrace log has some or all of the trace events 2124# Quickly determine if the ftrace log has some or all of the trace events
@@ -2722,7 +2750,7 @@ def parseTraceLog():
2722 # create blocks for orphan cg data 2750 # create blocks for orphan cg data
2723 for sortkey in sorted(sortlist): 2751 for sortkey in sorted(sortlist):
2724 cg = sortlist[sortkey] 2752 cg = sortlist[sortkey]
2725 name = cg.list[0].name 2753 name = cg.name
2726 if sysvals.isCallgraphFunc(name): 2754 if sysvals.isCallgraphFunc(name):
2727 vprint('Callgraph found for task %d: %.3fms, %s' % (cg.pid, (cg.end - cg.start)*1000, name)) 2755 vprint('Callgraph found for task %d: %.3fms, %s' % (cg.pid, (cg.end - cg.start)*1000, name))
2728 cg.newActionFromFunction(data) 2756 cg.newActionFromFunction(data)
@@ -3100,149 +3128,154 @@ def parseKernelLog(data):
3100 data.fixupInitcallsThatDidntReturn() 3128 data.fixupInitcallsThatDidntReturn()
3101 return True 3129 return True
3102 3130
3131def callgraphHTML(sv, hf, num, cg, title, color, devid):
3132 html_func_top = '<article id="{0}" class="atop" style="background:{1}">\n<input type="checkbox" class="pf" id="f{2}" checked/><label for="f{2}">{3} {4}</label>\n'
3133 html_func_start = '<article>\n<input type="checkbox" class="pf" id="f{0}" checked/><label for="f{0}">{1} {2}</label>\n'
3134 html_func_end = '</article>\n'
3135 html_func_leaf = '<article>{0} {1}</article>\n'
3136
3137 cgid = devid
3138 if cg.id:
3139 cgid += cg.id
3140 cglen = (cg.end - cg.start) * 1000
3141 if cglen < sv.mincglen:
3142 return num
3143
3144 fmt = '<r>(%.3f ms @ '+sv.timeformat+' to '+sv.timeformat+')</r>'
3145 flen = fmt % (cglen, cg.start, cg.end)
3146 hf.write(html_func_top.format(cgid, color, num, title, flen))
3147 num += 1
3148 for line in cg.list:
3149 if(line.length < 0.000000001):
3150 flen = ''
3151 else:
3152 fmt = '<n>(%.3f ms @ '+sv.timeformat+')</n>'
3153 flen = fmt % (line.length*1000, line.time)
3154 if(line.freturn and line.fcall):
3155 hf.write(html_func_leaf.format(line.name, flen))
3156 elif(line.freturn):
3157 hf.write(html_func_end)
3158 else:
3159 hf.write(html_func_start.format(num, line.name, flen))
3160 num += 1
3161 hf.write(html_func_end)
3162 return num
3163
3164def addCallgraphs(sv, hf, data):
3165 hf.write('<section id="callgraphs" class="callgraph">\n')
3166 # write out the ftrace data converted to html
3167 num = 0
3168 for p in data.phases:
3169 if sv.cgphase and p != sv.cgphase:
3170 continue
3171 list = data.dmesg[p]['list']
3172 for devname in data.sortedDevices(p):
3173 dev = list[devname]
3174 color = 'white'
3175 if 'color' in data.dmesg[p]:
3176 color = data.dmesg[p]['color']
3177 if 'color' in dev:
3178 color = dev['color']
3179 name = devname
3180 if(devname in sv.devprops):
3181 name = sv.devprops[devname].altName(devname)
3182 if sv.suspendmode in suspendmodename:
3183 name += ' '+p
3184 if('ftrace' in dev):
3185 cg = dev['ftrace']
3186 num = callgraphHTML(sv, hf, num, cg,
3187 name, color, dev['id'])
3188 if('ftraces' in dev):
3189 for cg in dev['ftraces']:
3190 num = callgraphHTML(sv, hf, num, cg,
3191 name+' &rarr; '+cg.name, color, dev['id'])
3192
3193 hf.write('\n\n </section>\n')
3194
3103# Function: createHTMLSummarySimple 3195# Function: createHTMLSummarySimple
3104# Description: 3196# Description:
3105# Create summary html file for a series of tests 3197# Create summary html file for a series of tests
3106# Arguments: 3198# Arguments:
3107# testruns: array of Data objects from parseTraceLog 3199# testruns: array of Data objects from parseTraceLog
3108def createHTMLSummarySimple(testruns, htmlfile): 3200def createHTMLSummarySimple(testruns, htmlfile, folder):
3109 # print out the basic summary of all the tests
3110 hf = open(htmlfile, 'w')
3111
3112 # write the html header first (html head, css code, up to body start) 3201 # write the html header first (html head, css code, up to body start)
3113 html = '<!DOCTYPE html>\n<html>\n<head>\n\ 3202 html = '<!DOCTYPE html>\n<html>\n<head>\n\
3114 <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\ 3203 <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\
3115 <title>AnalyzeSuspend Summary</title>\n\ 3204 <title>SleepGraph Summary</title>\n\
3116 <style type=\'text/css\'>\n\ 3205 <style type=\'text/css\'>\n\
3117 body {overflow-y: scroll;}\n\ 3206 .stamp {width: 100%;text-align:center;background:#888;line-height:30px;color:white;font: 25px Arial;}\n\
3118 .stamp {width: 100%;text-align:center;background-color:#495E09;line-height:30px;color:white;font: 25px Arial;}\n\
3119 table {width:100%;border-collapse: collapse;}\n\ 3207 table {width:100%;border-collapse: collapse;}\n\
3120 .summary {font: 22px Arial;border:1px solid;}\n\ 3208 .summary {border:1px solid;}\n\
3121 th {border: 1px solid black;background-color:#A7C942;color:white;}\n\ 3209 th {border: 1px solid black;background:#222;color:white;}\n\
3122 td {text-align: center;}\n\ 3210 td {font: 16px "Times New Roman";text-align: center;}\n\
3123 tr.alt td {background-color:#EAF2D3;}\n\ 3211 tr.alt td {background:#ddd;}\n\
3124 tr.avg td {background-color:#BDE34C;}\n\ 3212 tr.avg td {background:#aaa;}\n\
3125 a:link {color: #90B521;}\n\
3126 a:visited {color: #495E09;}\n\
3127 a:hover {color: #B1DF28;}\n\
3128 a:active {color: #FFFFFF;}\n\
3129 </style>\n</head>\n<body>\n' 3213 </style>\n</head>\n<body>\n'
3130 3214
3131 # group test header 3215 # group test header
3132 count = len(testruns) 3216 html += '<div class="stamp">%s (%d tests)</div>\n' % (folder, len(testruns))
3133 headline_stamp = '<div class="stamp">{0} {1} {2} {3} ({4} tests)</div>\n'
3134 html += headline_stamp.format(sysvals.stamp['host'],
3135 sysvals.stamp['kernel'], sysvals.stamp['mode'],
3136 sysvals.stamp['time'], count)
3137
3138 # check to see if all the tests have the same value
3139 stampcolumns = False
3140 for data in testruns:
3141 if diffStamp(sysvals.stamp, data.stamp):
3142 stampcolumns = True
3143 break
3144
3145 th = '\t<th>{0}</th>\n' 3217 th = '\t<th>{0}</th>\n'
3146 td = '\t<td>{0}</td>\n' 3218 td = '\t<td>{0}</td>\n'
3147 tdlink = '\t<td><a href="{0}">Click Here</a></td>\n' 3219 tdlink = '\t<td><a href="{0}">html</a></td>\n'
3148 3220
3149 # table header 3221 # table header
3150 html += '<table class="summary">\n<tr>\n' 3222 html += '<table class="summary">\n<tr>\n' + th.format('#') +\
3151 html += th.format("Test #") 3223 th.format('Mode') + th.format('Host') + th.format('Kernel') +\
3152 if stampcolumns: 3224 th.format('Test Time') + th.format('Suspend') + th.format('Resume') +\
3153 html += th.format("Hostname") 3225 th.format('Detail') + '</tr>\n'
3154 html += th.format("Kernel Version")
3155 html += th.format("Suspend Mode")
3156 html += th.format("Test Time")
3157 html += th.format("Suspend Time")
3158 html += th.format("Resume Time")
3159 html += th.format("Detail")
3160 html += '</tr>\n'
3161 3226
3162 # test data, 1 row per test 3227 # test data, 1 row per test
3163 sTimeAvg = 0.0 3228 avg = '<tr class="avg"><td></td><td></td><td></td><td></td>'+\
3164 rTimeAvg = 0.0 3229 '<td>Average of {0} {1} tests</td><td>{2}</td><td>{3}</td><td></td></tr>\n'
3165 num = 1 3230 sTimeAvg = rTimeAvg = 0.0
3166 for data in testruns: 3231 mode = ''
3167 # data.end is the end of post_resume 3232 num = 0
3168 resumeEnd = data.dmesg['resume_complete']['end'] 3233 for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'])):
3234 if mode != data['mode']:
3235 # test average line
3236 if(num > 0):
3237 sTimeAvg /= (num - 1)
3238 rTimeAvg /= (num - 1)
3239 html += avg.format('%d' % (num - 1), mode,
3240 '%3.3f ms' % sTimeAvg, '%3.3f ms' % rTimeAvg)
3241 sTimeAvg = rTimeAvg = 0.0
3242 mode = data['mode']
3243 num = 1
3244 # alternate row color
3169 if num % 2 == 1: 3245 if num % 2 == 1:
3170 html += '<tr class="alt">\n' 3246 html += '<tr class="alt">\n'
3171 else: 3247 else:
3172 html += '<tr>\n' 3248 html += '<tr>\n'
3173 3249 html += td.format("%d" % num)
3174 # test num
3175 html += td.format("test %d" % num)
3176 num += 1 3250 num += 1
3177 if stampcolumns: 3251 # basic info
3178 # host name 3252 for item in ['mode', 'host', 'kernel', 'time']:
3179 val = "unknown"
3180 if('host' in data.stamp):
3181 val = data.stamp['host']
3182 html += td.format(val)
3183 # host kernel
3184 val = "unknown" 3253 val = "unknown"
3185 if('kernel' in data.stamp): 3254 if(item in data):
3186 val = data.stamp['kernel'] 3255 val = data[item]
3187 html += td.format(val) 3256 html += td.format(val)
3188 # suspend mode
3189 val = "unknown"
3190 if('mode' in data.stamp):
3191 val = data.stamp['mode']
3192 html += td.format(val)
3193 # test time
3194 val = "unknown"
3195 if('time' in data.stamp):
3196 val = data.stamp['time']
3197 html += td.format(val)
3198 # suspend time 3257 # suspend time
3199 sTime = (data.tSuspended - data.start)*1000 3258 sTime = float(data['suspend'])
3200 sTimeAvg += sTime 3259 sTimeAvg += sTime
3201 html += td.format("%3.3f ms" % sTime) 3260 html += td.format('%.3f ms' % sTime)
3202 # resume time 3261 # resume time
3203 rTime = (resumeEnd - data.tResumed)*1000 3262 rTime = float(data['resume'])
3204 rTimeAvg += rTime 3263 rTimeAvg += rTime
3205 html += td.format("%3.3f ms" % rTime) 3264 html += td.format('%.3f ms' % rTime)
3206 # link to the output html 3265 # link to the output html
3207 html += tdlink.format(data.outfile) 3266 html += tdlink.format(data['url']) + '</tr>\n'
3208 3267 # last test average line
3209 html += '</tr>\n' 3268 if(num > 0):
3210 3269 sTimeAvg /= (num - 1)
3211 # last line: test average 3270 rTimeAvg /= (num - 1)
3212 if(count > 0): 3271 html += avg.format('%d' % (num - 1), mode,
3213 sTimeAvg /= count 3272 '%3.3f ms' % sTimeAvg, '%3.3f ms' % rTimeAvg)
3214 rTimeAvg /= count
3215 html += '<tr class="avg">\n'
3216 html += td.format('Average') # name
3217 if stampcolumns:
3218 html += td.format('') # host
3219 html += td.format('') # kernel
3220 html += td.format('') # mode
3221 html += td.format('') # time
3222 html += td.format("%3.3f ms" % sTimeAvg) # suspend time
3223 html += td.format("%3.3f ms" % rTimeAvg) # resume time
3224 html += td.format('') # output link
3225 html += '</tr>\n'
3226 3273
3227 # flush the data to file 3274 # flush the data to file
3228 hf.write(html+'</table>\n') 3275 hf = open(htmlfile, 'w')
3229 hf.write('</body>\n</html>\n') 3276 hf.write(html+'</table>\n</body>\n</html>\n')
3230 hf.close() 3277 hf.close()
3231 3278
3232def htmlTitle():
3233 modename = {
3234 'freeze': 'Freeze (S0)',
3235 'standby': 'Standby (S1)',
3236 'mem': 'Suspend (S3)',
3237 'disk': 'Hibernate (S4)'
3238 }
3239 kernel = sysvals.stamp['kernel']
3240 host = sysvals.hostname[0].upper()+sysvals.hostname[1:]
3241 mode = sysvals.suspendmode
3242 if sysvals.suspendmode in modename:
3243 mode = modename[sysvals.suspendmode]
3244 return host+' '+mode+' '+kernel
3245
3246def ordinal(value): 3279def ordinal(value):
3247 suffix = 'th' 3280 suffix = 'th'
3248 if value < 10 or value > 19: 3281 if value < 10 or value > 19:
@@ -3272,24 +3305,11 @@ def createHTML(testruns):
3272 kerror = True 3305 kerror = True
3273 data.normalizeTime(testruns[-1].tSuspended) 3306 data.normalizeTime(testruns[-1].tSuspended)
3274 3307
3275 x2changes = ['', 'absolute']
3276 if len(testruns) > 1:
3277 x2changes = ['1', 'relative']
3278 # html function templates 3308 # html function templates
3279 headline_version = '<div class="version"><a href="https://01.org/suspendresume">AnalyzeSuspend v%s</a></div>' % sysvals.version
3280 headline_stamp = '<div class="stamp">{0} {1} {2} {3}</div>\n'
3281 html_devlist1 = '<button id="devlist1" class="devlist" style="float:left;">Device Detail%s</button>' % x2changes[0]
3282 html_zoombox = '<center><button id="zoomin">ZOOM IN +</button><button id="zoomout">ZOOM OUT -</button><button id="zoomdef">ZOOM 1:1</button></center>\n'
3283 html_devlist2 = '<button id="devlist2" class="devlist" style="float:right;">Device Detail2</button>\n'
3284 html_timeline = '<div id="dmesgzoombox" class="zoombox">\n<div id="{0}" class="timeline" style="height:{1}px">\n'
3285 html_tblock = '<div id="block{0}" class="tblock" style="left:{1}%;width:{2}%;"><div class="tback" style="height:{3}px"></div>\n'
3286 html_device = '<div id="{0}" title="{1}" class="thread{7}" style="left:{2}%;top:{3}px;height:{4}px;width:{5}%;{8}">{6}</div>\n'
3287 html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">ERROR&rarr;</div>\n' 3309 html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">ERROR&rarr;</div>\n'
3288 html_traceevent = '<div title="{0}" class="traceevent{6}" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;{7}">{5}</div>\n' 3310 html_traceevent = '<div title="{0}" class="traceevent{6}" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;{7}">{5}</div>\n'
3289 html_cpuexec = '<div class="jiffie" style="left:{0}%;top:{1}px;height:{2}px;width:{3}%;background:{4};"></div>\n' 3311 html_cpuexec = '<div class="jiffie" style="left:{0}%;top:{1}px;height:{2}px;width:{3}%;background:{4};"></div>\n'
3290 html_phase = '<div class="phase" style="left:{0}%;width:{1}%;top:{2}px;height:{3}px;background-color:{4}">{5}</div>\n' 3312 html_legend = '<div id="p{3}" class="square" style="left:{0}%;background:{1}">&nbsp;{2}</div>\n'
3291 html_phaselet = '<div id="{0}" class="phaselet" style="left:{1}%;width:{2}%;background:{3}"></div>\n'
3292 html_legend = '<div id="p{3}" class="square" style="left:{0}%;background-color:{1}">&nbsp;{2}</div>\n'
3293 html_timetotal = '<table class="time1">\n<tr>'\ 3313 html_timetotal = '<table class="time1">\n<tr>'\
3294 '<td class="green" title="{3}">{2} Suspend Time: <b>{0} ms</b></td>'\ 3314 '<td class="green" title="{3}">{2} Suspend Time: <b>{0} ms</b></td>'\
3295 '<td class="yellow" title="{4}">{2} Resume Time: <b>{1} ms</b></td>'\ 3315 '<td class="yellow" title="{4}">{2} Resume Time: <b>{1} ms</b></td>'\
@@ -3311,20 +3331,18 @@ def createHTML(testruns):
3311 '</tr>\n</table>\n' 3331 '</tr>\n</table>\n'
3312 3332
3313 # html format variables 3333 # html format variables
3314 hoverZ = 'z-index:8;'
3315 if sysvals.usedevsrc:
3316 hoverZ = ''
3317 scaleH = 20 3334 scaleH = 20
3318 scaleTH = 20
3319 if kerror: 3335 if kerror:
3320 scaleH = 40 3336 scaleH = 40
3321 scaleTH = 60
3322 3337
3323 # device timeline 3338 # device timeline
3324 vprint('Creating Device Timeline...') 3339 vprint('Creating Device Timeline...')
3325 3340
3326 devtl = Timeline(30, scaleH) 3341 devtl = Timeline(30, scaleH)
3327 3342
3343 # write the test title and general info header
3344 devtl.createHeader(sysvals)
3345
3328 # Generate the header for this timeline 3346 # Generate the header for this timeline
3329 for data in testruns: 3347 for data in testruns:
3330 tTotal = data.end - data.start 3348 tTotal = data.end - data.start
@@ -3346,7 +3364,7 @@ def createHTML(testruns):
3346 if(len(testruns) > 1): 3364 if(len(testruns) > 1):
3347 testdesc = ordinal(data.testnumber+1)+' '+testdesc 3365 testdesc = ordinal(data.testnumber+1)+' '+testdesc
3348 thtml = html_timetotal3.format(run_time, testdesc) 3366 thtml = html_timetotal3.format(run_time, testdesc)
3349 devtl.html['header'] += thtml 3367 devtl.html += thtml
3350 elif data.fwValid: 3368 elif data.fwValid:
3351 suspend_time = '%.0f'%(sktime + (data.fwSuspend/1000000.0)) 3369 suspend_time = '%.0f'%(sktime + (data.fwSuspend/1000000.0))
3352 resume_time = '%.0f'%(rktime + (data.fwResume/1000000.0)) 3370 resume_time = '%.0f'%(rktime + (data.fwResume/1000000.0))
@@ -3363,10 +3381,10 @@ def createHTML(testruns):
3363 else: 3381 else:
3364 thtml = html_timetotal2.format(suspend_time, low_time, \ 3382 thtml = html_timetotal2.format(suspend_time, low_time, \
3365 resume_time, testdesc1, stitle, rtitle) 3383 resume_time, testdesc1, stitle, rtitle)
3366 devtl.html['header'] += thtml 3384 devtl.html += thtml
3367 sftime = '%.3f'%(data.fwSuspend / 1000000.0) 3385 sftime = '%.3f'%(data.fwSuspend / 1000000.0)
3368 rftime = '%.3f'%(data.fwResume / 1000000.0) 3386 rftime = '%.3f'%(data.fwResume / 1000000.0)
3369 devtl.html['header'] += html_timegroups.format('%.3f'%sktime, \ 3387 devtl.html += html_timegroups.format('%.3f'%sktime, \
3370 sftime, rftime, '%.3f'%rktime, testdesc2, sysvals.suspendmode) 3388 sftime, rftime, '%.3f'%rktime, testdesc2, sysvals.suspendmode)
3371 else: 3389 else:
3372 suspend_time = '%.3f' % sktime 3390 suspend_time = '%.3f' % sktime
@@ -3382,7 +3400,7 @@ def createHTML(testruns):
3382 else: 3400 else:
3383 thtml = html_timetotal2.format(suspend_time, low_time, \ 3401 thtml = html_timetotal2.format(suspend_time, low_time, \
3384 resume_time, testdesc, stitle, rtitle) 3402 resume_time, testdesc, stitle, rtitle)
3385 devtl.html['header'] += thtml 3403 devtl.html += thtml
3386 3404
3387 # time scale for potentially multiple datasets 3405 # time scale for potentially multiple datasets
3388 t0 = testruns[0].start 3406 t0 = testruns[0].start
@@ -3429,15 +3447,8 @@ def createHTML(testruns):
3429 devtl.getPhaseRows(threadlist, devtl.rows) 3447 devtl.getPhaseRows(threadlist, devtl.rows)
3430 devtl.calcTotalRows() 3448 devtl.calcTotalRows()
3431 3449
3432 # create bounding box, add buttons
3433 if sysvals.suspendmode != 'command':
3434 devtl.html['timeline'] += html_devlist1
3435 if len(testruns) > 1:
3436 devtl.html['timeline'] += html_devlist2
3437 devtl.html['timeline'] += html_zoombox
3438 devtl.html['timeline'] += html_timeline.format('dmesg', devtl.height)
3439
3440 # draw the full timeline 3450 # draw the full timeline
3451 devtl.createZoomBox(sysvals.suspendmode, len(testruns))
3441 phases = {'suspend':[],'resume':[]} 3452 phases = {'suspend':[],'resume':[]}
3442 for phase in data.dmesg: 3453 for phase in data.dmesg:
3443 if 'resume' in phase: 3454 if 'resume' in phase:
@@ -3452,37 +3463,36 @@ def createHTML(testruns):
3452 # draw suspend and resume blocks separately 3463 # draw suspend and resume blocks separately
3453 bname = '%s%d' % (dir[0], data.testnumber) 3464 bname = '%s%d' % (dir[0], data.testnumber)
3454 if dir == 'suspend': 3465 if dir == 'suspend':
3455 m0 = testruns[data.testnumber].start 3466 m0 = data.start
3456 mMax = testruns[data.testnumber].tSuspended 3467 mMax = data.tSuspended
3457 mTotal = mMax - m0
3458 left = '%f' % (((m0-t0)*100.0)/tTotal) 3468 left = '%f' % (((m0-t0)*100.0)/tTotal)
3459 else: 3469 else:
3460 m0 = testruns[data.testnumber].tSuspended 3470 m0 = data.tSuspended
3461 mMax = testruns[data.testnumber].end 3471 mMax = data.end
3462 # in an x2 run, remove any gap between blocks 3472 # in an x2 run, remove any gap between blocks
3463 if len(testruns) > 1 and data.testnumber == 0: 3473 if len(testruns) > 1 and data.testnumber == 0:
3464 mMax = testruns[1].start 3474 mMax = testruns[1].start
3465 mTotal = mMax - m0
3466 left = '%f' % ((((m0-t0)*100.0)+sysvals.srgap/2)/tTotal) 3475 left = '%f' % ((((m0-t0)*100.0)+sysvals.srgap/2)/tTotal)
3476 mTotal = mMax - m0
3467 # if a timeline block is 0 length, skip altogether 3477 # if a timeline block is 0 length, skip altogether
3468 if mTotal == 0: 3478 if mTotal == 0:
3469 continue 3479 continue
3470 width = '%f' % (((mTotal*100.0)-sysvals.srgap/2)/tTotal) 3480 width = '%f' % (((mTotal*100.0)-sysvals.srgap/2)/tTotal)
3471 devtl.html['timeline'] += html_tblock.format(bname, left, width, devtl.scaleH) 3481 devtl.html += devtl.html_tblock.format(bname, left, width, devtl.scaleH)
3472 for b in sorted(phases[dir]): 3482 for b in sorted(phases[dir]):
3473 # draw the phase color background 3483 # draw the phase color background
3474 phase = data.dmesg[b] 3484 phase = data.dmesg[b]
3475 length = phase['end']-phase['start'] 3485 length = phase['end']-phase['start']
3476 left = '%f' % (((phase['start']-m0)*100.0)/mTotal) 3486 left = '%f' % (((phase['start']-m0)*100.0)/mTotal)
3477 width = '%f' % ((length*100.0)/mTotal) 3487 width = '%f' % ((length*100.0)/mTotal)
3478 devtl.html['timeline'] += html_phase.format(left, width, \ 3488 devtl.html += devtl.html_phase.format(left, width, \
3479 '%.3f'%devtl.scaleH, '%.3f'%devtl.bodyH, \ 3489 '%.3f'%devtl.scaleH, '%.3f'%devtl.bodyH, \
3480 data.dmesg[b]['color'], '') 3490 data.dmesg[b]['color'], '')
3481 for e in data.errorinfo[dir]: 3491 for e in data.errorinfo[dir]:
3482 # draw red lines for any kernel errors found 3492 # draw red lines for any kernel errors found
3483 t, err = e 3493 t, err = e
3484 right = '%f' % (((mMax-t)*100.0)/mTotal) 3494 right = '%f' % (((mMax-t)*100.0)/mTotal)
3485 devtl.html['timeline'] += html_error.format(right, err) 3495 devtl.html += html_error.format(right, err)
3486 for b in sorted(phases[dir]): 3496 for b in sorted(phases[dir]):
3487 # draw the devices for this phase 3497 # draw the devices for this phase
3488 phaselist = data.dmesg[b]['list'] 3498 phaselist = data.dmesg[b]['list']
@@ -3496,7 +3506,7 @@ def createHTML(testruns):
3496 if 'htmlclass' in dev: 3506 if 'htmlclass' in dev:
3497 xtraclass = dev['htmlclass'] 3507 xtraclass = dev['htmlclass']
3498 if 'color' in dev: 3508 if 'color' in dev:
3499 xtrastyle = 'background-color:%s;' % dev['color'] 3509 xtrastyle = 'background:%s;' % dev['color']
3500 if(d in sysvals.devprops): 3510 if(d in sysvals.devprops):
3501 name = sysvals.devprops[d].altName(d) 3511 name = sysvals.devprops[d].altName(d)
3502 xtraclass = sysvals.devprops[d].xtraClass() 3512 xtraclass = sysvals.devprops[d].xtraClass()
@@ -3521,7 +3531,7 @@ def createHTML(testruns):
3521 title += 'post_resume_process' 3531 title += 'post_resume_process'
3522 else: 3532 else:
3523 title += b 3533 title += b
3524 devtl.html['timeline'] += html_device.format(dev['id'], \ 3534 devtl.html += devtl.html_device.format(dev['id'], \
3525 title, left, top, '%.3f'%rowheight, width, \ 3535 title, left, top, '%.3f'%rowheight, width, \
3526 d+drv, xtraclass, xtrastyle) 3536 d+drv, xtraclass, xtrastyle)
3527 if('cpuexec' in dev): 3537 if('cpuexec' in dev):
@@ -3535,7 +3545,7 @@ def createHTML(testruns):
3535 left = '%f' % (((start-m0)*100)/mTotal) 3545 left = '%f' % (((start-m0)*100)/mTotal)
3536 width = '%f' % ((end-start)*100/mTotal) 3546 width = '%f' % ((end-start)*100/mTotal)
3537 color = 'rgba(255, 0, 0, %f)' % j 3547 color = 'rgba(255, 0, 0, %f)' % j
3538 devtl.html['timeline'] += \ 3548 devtl.html += \
3539 html_cpuexec.format(left, top, height, width, color) 3549 html_cpuexec.format(left, top, height, width, color)
3540 if('src' not in dev): 3550 if('src' not in dev):
3541 continue 3551 continue
@@ -3548,20 +3558,20 @@ def createHTML(testruns):
3548 xtrastyle = '' 3558 xtrastyle = ''
3549 if e.color: 3559 if e.color:
3550 xtrastyle = 'background:%s;' % e.color 3560 xtrastyle = 'background:%s;' % e.color
3551 devtl.html['timeline'] += \ 3561 devtl.html += \
3552 html_traceevent.format(e.title(), \ 3562 html_traceevent.format(e.title(), \
3553 left, top, height, width, e.text(), '', xtrastyle) 3563 left, top, height, width, e.text(), '', xtrastyle)
3554 # draw the time scale, try to make the number of labels readable 3564 # draw the time scale, try to make the number of labels readable
3555 devtl.html['timeline'] += devtl.createTimeScale(m0, mMax, tTotal, dir) 3565 devtl.createTimeScale(m0, mMax, tTotal, dir)
3556 devtl.html['timeline'] += '</div>\n' 3566 devtl.html += '</div>\n'
3557 3567
3558 # timeline is finished 3568 # timeline is finished
3559 devtl.html['timeline'] += '</div>\n</div>\n' 3569 devtl.html += '</div>\n</div>\n'
3560 3570
3561 # draw a legend which describes the phases by color 3571 # draw a legend which describes the phases by color
3562 if sysvals.suspendmode != 'command': 3572 if sysvals.suspendmode != 'command':
3563 data = testruns[-1] 3573 data = testruns[-1]
3564 devtl.html['legend'] = '<div class="legend">\n' 3574 devtl.html += '<div class="legend">\n'
3565 pdelta = 100.0/len(data.phases) 3575 pdelta = 100.0/len(data.phases)
3566 pmargin = pdelta / 4.0 3576 pmargin = pdelta / 4.0
3567 for phase in data.phases: 3577 for phase in data.phases:
@@ -3571,127 +3581,41 @@ def createHTML(testruns):
3571 id += tmp[1][0] 3581 id += tmp[1][0]
3572 order = '%.2f' % ((data.dmesg[phase]['order'] * pdelta) + pmargin) 3582 order = '%.2f' % ((data.dmesg[phase]['order'] * pdelta) + pmargin)
3573 name = string.replace(phase, '_', ' &nbsp;') 3583 name = string.replace(phase, '_', ' &nbsp;')
3574 devtl.html['legend'] += html_legend.format(order, \ 3584 devtl.html += html_legend.format(order, \
3575 data.dmesg[phase]['color'], name, id) 3585 data.dmesg[phase]['color'], name, id)
3576 devtl.html['legend'] += '</div>\n' 3586 devtl.html += '</div>\n'
3577 3587
3578 hf = open(sysvals.htmlfile, 'w') 3588 hf = open(sysvals.htmlfile, 'w')
3579 3589
3580 if not sysvals.cgexp:
3581 cgchk = 'checked'
3582 cgnchk = 'not(:checked)'
3583 else:
3584 cgchk = 'not(:checked)'
3585 cgnchk = 'checked'
3586
3587 # write the html header first (html head, css code, up to body start)
3588 html_header = '<!DOCTYPE html>\n<html>\n<head>\n\
3589 <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\
3590 <title>'+htmlTitle()+'</title>\n\
3591 <style type=\'text/css\'>\n\
3592 body {overflow-y:scroll;}\n\
3593 .stamp {width:100%;text-align:center;background-color:gray;line-height:30px;color:white;font:25px Arial;}\n\
3594 .callgraph {margin-top:30px;box-shadow:5px 5px 20px black;}\n\
3595 .callgraph article * {padding-left:28px;}\n\
3596 h1 {color:black;font:bold 30px Times;}\n\
3597 t0 {color:black;font:bold 30px Times;}\n\
3598 t1 {color:black;font:30px Times;}\n\
3599 t2 {color:black;font:25px Times;}\n\
3600 t3 {color:black;font:20px Times;white-space:nowrap;}\n\
3601 t4 {color:black;font:bold 30px Times;line-height:60px;white-space:nowrap;}\n\
3602 cS {font:bold 13px Times;}\n\
3603 table {width:100%;}\n\
3604 .gray {background-color:rgba(80,80,80,0.1);}\n\
3605 .green {background-color:rgba(204,255,204,0.4);}\n\
3606 .purple {background-color:rgba(128,0,128,0.2);}\n\
3607 .yellow {background-color:rgba(255,255,204,0.4);}\n\
3608 .time1 {font:22px Arial;border:1px solid;}\n\
3609 .time2 {font:15px Arial;border-bottom:1px solid;border-left:1px solid;border-right:1px solid;}\n\
3610 td {text-align:center;}\n\
3611 r {color:#500000;font:15px Tahoma;}\n\
3612 n {color:#505050;font:15px Tahoma;}\n\
3613 .tdhl {color:red;}\n\
3614 .hide {display:none;}\n\
3615 .pf {display:none;}\n\
3616 .pf:'+cgchk+' + label {background:url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/><rect x="8" y="4" width="2" height="10" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\
3617 .pf:'+cgnchk+' ~ label {background:url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\
3618 .pf:'+cgchk+' ~ *:not(:nth-child(2)) {display:none;}\n\
3619 .zoombox {position:relative;width:100%;overflow-x:scroll;-webkit-user-select:none;-moz-user-select:none;user-select:none;}\n\
3620 .timeline {position:relative;font-size:14px;cursor:pointer;width:100%; overflow:hidden;background:linear-gradient(#cccccc, white);}\n\
3621 .thread {position:absolute;height:0%;overflow:hidden;z-index:7;line-height:30px;font-size:14px;border:1px solid;text-align:center;white-space:nowrap;}\n\
3622 .thread.ps {border-radius:3px;background:linear-gradient(to top, #ccc, #eee);}\n\
3623 .thread:hover {background-color:white;border:1px solid red;'+hoverZ+'}\n\
3624 .thread.sec,.thread.sec:hover {background-color:black;border:0;color:white;line-height:15px;font-size:10px;}\n\
3625 .hover {background-color:white;border:1px solid red;'+hoverZ+'}\n\
3626 .hover.sync {background-color:white;}\n\
3627 .hover.bg,.hover.kth,.hover.sync,.hover.ps {background-color:white;}\n\
3628 .jiffie {position:absolute;pointer-events: none;z-index:8;}\n\
3629 .traceevent {position:absolute;font-size:10px;z-index:7;overflow:hidden;color:black;text-align:center;white-space:nowrap;border-radius:5px;border:1px solid black;background:linear-gradient(to bottom right,#CCC,#969696);}\n\
3630 .traceevent:hover {color:white;font-weight:bold;border:1px solid white;}\n\
3631 .phase {position:absolute;overflow:hidden;border:0px;text-align:center;}\n\
3632 .phaselet {position:absolute;overflow:hidden;border:0px;text-align:center;height:100px;font-size:24px;}\n\
3633 .t {position:absolute;line-height:'+('%d'%scaleTH)+'px;pointer-events:none;top:0;height:100%;border-right:1px solid black;z-index:6;}\n\
3634 .err {position:absolute;top:0%;height:100%;border-right:3px solid red;color:red;font:bold 14px Times;line-height:18px;}\n\
3635 .legend {position:relative; width:100%; height:40px; text-align:center;margin-bottom:20px}\n\
3636 .legend .square {position:absolute;cursor:pointer;top:10px; width:0px;height:20px;border:1px solid;padding-left:20px;}\n\
3637 button {height:40px;width:200px;margin-bottom:20px;margin-top:20px;font-size:24px;}\n\
3638 .logbtn {position:relative;float:right;height:25px;width:50px;margin-top:3px;margin-bottom:0;font-size:10px;text-align:center;}\n\
3639 .devlist {position:'+x2changes[1]+';width:190px;}\n\
3640 a:link {color:white;text-decoration:none;}\n\
3641 a:visited {color:white;}\n\
3642 a:hover {color:white;}\n\
3643 a:active {color:white;}\n\
3644 .version {position:relative;float:left;color:white;font-size:10px;line-height:30px;margin-left:10px;}\n\
3645 #devicedetail {height:100px;box-shadow:5px 5px 20px black;}\n\
3646 .tblock {position:absolute;height:100%;background-color:#ddd;}\n\
3647 .tback {position:absolute;width:100%;background:linear-gradient(#ccc, #ddd);}\n\
3648 .bg {z-index:1;}\n\
3649 </style>\n</head>\n<body>\n'
3650
3651 # no header or css if its embedded 3590 # no header or css if its embedded
3652 if(sysvals.embedded): 3591 if(sysvals.embedded):
3653 hf.write('pass True tSus %.3f tRes %.3f tLow %.3f fwvalid %s tSus %.3f tRes %.3f\n' % 3592 hf.write('pass True tSus %.3f tRes %.3f tLow %.3f fwvalid %s tSus %.3f tRes %.3f\n' %
3654 (data.tSuspended-data.start, data.end-data.tSuspended, data.tLow, data.fwValid, \ 3593 (data.tSuspended-data.start, data.end-data.tSuspended, data.tLow, data.fwValid, \
3655 data.fwSuspend/1000000, data.fwResume/1000000)) 3594 data.fwSuspend/1000000, data.fwResume/1000000))
3656 else: 3595 else:
3657 hf.write(html_header) 3596 addCSS(hf, sysvals, len(testruns), kerror)
3658
3659 # write the test title and general info header
3660 if(sysvals.stamp['time'] != ""):
3661 hf.write(headline_version)
3662 if sysvals.logmsg:
3663 hf.write('<button id="showtest" class="logbtn">log</button>')
3664 if sysvals.addlogs and sysvals.dmesgfile:
3665 hf.write('<button id="showdmesg" class="logbtn">dmesg</button>')
3666 if sysvals.addlogs and sysvals.ftracefile:
3667 hf.write('<button id="showftrace" class="logbtn">ftrace</button>')
3668 hf.write(headline_stamp.format(sysvals.stamp['host'],
3669 sysvals.stamp['kernel'], sysvals.stamp['mode'], \
3670 sysvals.stamp['time']))
3671 3597
3672 # write the device timeline 3598 # write the device timeline
3673 hf.write(devtl.html['header']) 3599 hf.write(devtl.html)
3674 hf.write(devtl.html['timeline'])
3675 hf.write(devtl.html['legend'])
3676 hf.write('<div id="devicedetailtitle"></div>\n') 3600 hf.write('<div id="devicedetailtitle"></div>\n')
3677 hf.write('<div id="devicedetail" style="display:none;">\n') 3601 hf.write('<div id="devicedetail" style="display:none;">\n')
3678 # draw the colored boxes for the device detail section 3602 # draw the colored boxes for the device detail section
3679 for data in testruns: 3603 for data in testruns:
3680 hf.write('<div id="devicedetail%d">\n' % data.testnumber) 3604 hf.write('<div id="devicedetail%d">\n' % data.testnumber)
3681 pscolor = 'linear-gradient(to top left, #ccc, #eee)' 3605 pscolor = 'linear-gradient(to top left, #ccc, #eee)'
3682 hf.write(html_phaselet.format('pre_suspend_process', \ 3606 hf.write(devtl.html_phaselet.format('pre_suspend_process', \
3683 '0', '0', pscolor)) 3607 '0', '0', pscolor))
3684 for b in data.phases: 3608 for b in data.phases:
3685 phase = data.dmesg[b] 3609 phase = data.dmesg[b]
3686 length = phase['end']-phase['start'] 3610 length = phase['end']-phase['start']
3687 left = '%.3f' % (((phase['start']-t0)*100.0)/tTotal) 3611 left = '%.3f' % (((phase['start']-t0)*100.0)/tTotal)
3688 width = '%.3f' % ((length*100.0)/tTotal) 3612 width = '%.3f' % ((length*100.0)/tTotal)
3689 hf.write(html_phaselet.format(b, left, width, \ 3613 hf.write(devtl.html_phaselet.format(b, left, width, \
3690 data.dmesg[b]['color'])) 3614 data.dmesg[b]['color']))
3691 hf.write(html_phaselet.format('post_resume_process', \ 3615 hf.write(devtl.html_phaselet.format('post_resume_process', \
3692 '0', '0', pscolor)) 3616 '0', '0', pscolor))
3693 if sysvals.suspendmode == 'command': 3617 if sysvals.suspendmode == 'command':
3694 hf.write(html_phaselet.format('cmdexec', '0', '0', pscolor)) 3618 hf.write(devtl.html_phaselet.format('cmdexec', '0', '0', pscolor))
3695 hf.write('</div>\n') 3619 hf.write('</div>\n')
3696 hf.write('</div>\n') 3620 hf.write('</div>\n')
3697 3621
@@ -3701,52 +3625,7 @@ def createHTML(testruns):
3701 else: 3625 else:
3702 data = testruns[-1] 3626 data = testruns[-1]
3703 if(sysvals.usecallgraph and not sysvals.embedded): 3627 if(sysvals.usecallgraph and not sysvals.embedded):
3704 hf.write('<section id="callgraphs" class="callgraph">\n') 3628 addCallgraphs(sysvals, hf, data)
3705 # write out the ftrace data converted to html
3706 html_func_top = '<article id="{0}" class="atop" style="background-color:{1}">\n<input type="checkbox" class="pf" id="f{2}" checked/><label for="f{2}">{3} {4}</label>\n'
3707 html_func_start = '<article>\n<input type="checkbox" class="pf" id="f{0}" checked/><label for="f{0}">{1} {2}</label>\n'
3708 html_func_end = '</article>\n'
3709 html_func_leaf = '<article>{0} {1}</article>\n'
3710 num = 0
3711 for p in data.phases:
3712 if sysvals.cgphase and p != sysvals.cgphase:
3713 continue
3714 list = data.dmesg[p]['list']
3715 for devname in data.sortedDevices(p):
3716 if('ftrace' not in list[devname]):
3717 continue
3718 devid = list[devname]['id']
3719 cg = list[devname]['ftrace']
3720 clen = (cg.end - cg.start) * 1000
3721 if clen < sysvals.mincglen:
3722 continue
3723 fmt = '<r>(%.3f ms @ '+sysvals.timeformat+' to '+sysvals.timeformat+')</r>'
3724 flen = fmt % (clen, cg.start, cg.end)
3725 name = devname
3726 if(devname in sysvals.devprops):
3727 name = sysvals.devprops[devname].altName(devname)
3728 if sysvals.suspendmode == 'command':
3729 ftitle = name
3730 else:
3731 ftitle = name+' '+p
3732 hf.write(html_func_top.format(devid, data.dmesg[p]['color'], \
3733 num, ftitle, flen))
3734 num += 1
3735 for line in cg.list:
3736 if(line.length < 0.000000001):
3737 flen = ''
3738 else:
3739 fmt = '<n>(%.3f ms @ '+sysvals.timeformat+')</n>'
3740 flen = fmt % (line.length*1000, line.time)
3741 if(line.freturn and line.fcall):
3742 hf.write(html_func_leaf.format(line.name, flen))
3743 elif(line.freturn):
3744 hf.write(html_func_end)
3745 else:
3746 hf.write(html_func_start.format(num, line.name, flen))
3747 num += 1
3748 hf.write(html_func_end)
3749 hf.write('\n\n </section>\n')
3750 3629
3751 # add the test log as a hidden div 3630 # add the test log as a hidden div
3752 if sysvals.logmsg: 3631 if sysvals.logmsg:
@@ -3788,6 +3667,100 @@ def createHTML(testruns):
3788 hf.close() 3667 hf.close()
3789 return True 3668 return True
3790 3669
3670def addCSS(hf, sv, testcount=1, kerror=False, extra=''):
3671 kernel = sv.stamp['kernel']
3672 host = sv.hostname[0].upper()+sv.hostname[1:]
3673 mode = sv.suspendmode
3674 if sv.suspendmode in suspendmodename:
3675 mode = suspendmodename[sv.suspendmode]
3676 title = host+' '+mode+' '+kernel
3677
3678 # various format changes by flags
3679 cgchk = 'checked'
3680 cgnchk = 'not(:checked)'
3681 if sv.cgexp:
3682 cgchk = 'not(:checked)'
3683 cgnchk = 'checked'
3684
3685 hoverZ = 'z-index:8;'
3686 if sv.usedevsrc:
3687 hoverZ = ''
3688
3689 devlistpos = 'absolute'
3690 if testcount > 1:
3691 devlistpos = 'relative'
3692
3693 scaleTH = 20
3694 if kerror:
3695 scaleTH = 60
3696
3697 # write the html header first (html head, css code, up to body start)
3698 html_header = '<!DOCTYPE html>\n<html>\n<head>\n\
3699 <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\
3700 <title>'+title+'</title>\n\
3701 <style type=\'text/css\'>\n\
3702 body {overflow-y:scroll;}\n\
3703 .stamp {width:100%;text-align:center;background:gray;line-height:30px;color:white;font:25px Arial;}\n\
3704 .callgraph {margin-top:30px;box-shadow:5px 5px 20px black;}\n\
3705 .callgraph article * {padding-left:28px;}\n\
3706 h1 {color:black;font:bold 30px Times;}\n\
3707 t0 {color:black;font:bold 30px Times;}\n\
3708 t1 {color:black;font:30px Times;}\n\
3709 t2 {color:black;font:25px Times;}\n\
3710 t3 {color:black;font:20px Times;white-space:nowrap;}\n\
3711 t4 {color:black;font:bold 30px Times;line-height:60px;white-space:nowrap;}\n\
3712 cS {font:bold 13px Times;}\n\
3713 table {width:100%;}\n\
3714 .gray {background:rgba(80,80,80,0.1);}\n\
3715 .green {background:rgba(204,255,204,0.4);}\n\
3716 .purple {background:rgba(128,0,128,0.2);}\n\
3717 .yellow {background:rgba(255,255,204,0.4);}\n\
3718 .blue {background:rgba(169,208,245,0.4);}\n\
3719 .time1 {font:22px Arial;border:1px solid;}\n\
3720 .time2 {font:15px Arial;border-bottom:1px solid;border-left:1px solid;border-right:1px solid;}\n\
3721 td {text-align:center;}\n\
3722 r {color:#500000;font:15px Tahoma;}\n\
3723 n {color:#505050;font:15px Tahoma;}\n\
3724 .tdhl {color:red;}\n\
3725 .hide {display:none;}\n\
3726 .pf {display:none;}\n\
3727 .pf:'+cgchk+' + label {background:url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/><rect x="8" y="4" width="2" height="10" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\
3728 .pf:'+cgnchk+' ~ label {background:url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\
3729 .pf:'+cgchk+' ~ *:not(:nth-child(2)) {display:none;}\n\
3730 .zoombox {position:relative;width:100%;overflow-x:scroll;-webkit-user-select:none;-moz-user-select:none;user-select:none;}\n\
3731 .timeline {position:relative;font-size:14px;cursor:pointer;width:100%; overflow:hidden;background:linear-gradient(#cccccc, white);}\n\
3732 .thread {position:absolute;height:0%;overflow:hidden;z-index:7;line-height:30px;font-size:14px;border:1px solid;text-align:center;white-space:nowrap;}\n\
3733 .thread.ps {border-radius:3px;background:linear-gradient(to top, #ccc, #eee);}\n\
3734 .thread:hover {background:white;border:1px solid red;'+hoverZ+'}\n\
3735 .thread.sec,.thread.sec:hover {background:black;border:0;color:white;line-height:15px;font-size:10px;}\n\
3736 .hover {background:white;border:1px solid red;'+hoverZ+'}\n\
3737 .hover.sync {background:white;}\n\
3738 .hover.bg,.hover.kth,.hover.sync,.hover.ps {background:white;}\n\
3739 .jiffie {position:absolute;pointer-events: none;z-index:8;}\n\
3740 .traceevent {position:absolute;font-size:10px;z-index:7;overflow:hidden;color:black;text-align:center;white-space:nowrap;border-radius:5px;border:1px solid black;background:linear-gradient(to bottom right,#CCC,#969696);}\n\
3741 .traceevent:hover {color:white;font-weight:bold;border:1px solid white;}\n\
3742 .phase {position:absolute;overflow:hidden;border:0px;text-align:center;}\n\
3743 .phaselet {float:left;overflow:hidden;border:0px;text-align:center;min-height:100px;font-size:24px;}\n\
3744 .t {position:absolute;line-height:'+('%d'%scaleTH)+'px;pointer-events:none;top:0;height:100%;border-right:1px solid black;z-index:6;}\n\
3745 .err {position:absolute;top:0%;height:100%;border-right:3px solid red;color:red;font:bold 14px Times;line-height:18px;}\n\
3746 .legend {position:relative; width:100%; height:40px; text-align:center;margin-bottom:20px}\n\
3747 .legend .square {position:absolute;cursor:pointer;top:10px; width:0px;height:20px;border:1px solid;padding-left:20px;}\n\
3748 button {height:40px;width:200px;margin-bottom:20px;margin-top:20px;font-size:24px;}\n\
3749 .logbtn {position:relative;float:right;height:25px;width:50px;margin-top:3px;margin-bottom:0;font-size:10px;text-align:center;}\n\
3750 .devlist {position:'+devlistpos+';width:190px;}\n\
3751 a:link {color:white;text-decoration:none;}\n\
3752 a:visited {color:white;}\n\
3753 a:hover {color:white;}\n\
3754 a:active {color:white;}\n\
3755 .version {position:relative;float:left;color:white;font-size:10px;line-height:30px;margin-left:10px;}\n\
3756 #devicedetail {min-height:100px;box-shadow:5px 5px 20px black;}\n\
3757 .tblock {position:absolute;height:100%;background:#ddd;}\n\
3758 .tback {position:absolute;width:100%;background:linear-gradient(#ccc, #ddd);}\n\
3759 .bg {z-index:1;}\n\
3760'+extra+'\
3761 </style>\n</head>\n<body>\n'
3762 hf.write(html_header)
3763
3791# Function: addScriptCode 3764# Function: addScriptCode
3792# Description: 3765# Description:
3793# Adds the javascript code to the output html 3766# Adds the javascript code to the output html
@@ -3809,7 +3782,7 @@ def addScriptCode(hf, testruns):
3809 ' var resolution = -1;\n'\ 3782 ' var resolution = -1;\n'\
3810 ' var dragval = [0, 0];\n'\ 3783 ' var dragval = [0, 0];\n'\
3811 ' function redrawTimescale(t0, tMax, tS) {\n'\ 3784 ' function redrawTimescale(t0, tMax, tS) {\n'\
3812 ' var rline = \'<div class="t" style="left:0;border-left:1px solid black;border-right:0;"><cS>&larr;R</cS></div>\';\n'\ 3785 ' var rline = \'<div class="t" style="left:0;border-left:1px solid black;border-right:0;">\';\n'\
3813 ' var tTotal = tMax - t0;\n'\ 3786 ' var tTotal = tMax - t0;\n'\
3814 ' var list = document.getElementsByClassName("tblock");\n'\ 3787 ' var list = document.getElementsByClassName("tblock");\n'\
3815 ' for (var i = 0; i < list.length; i++) {\n'\ 3788 ' for (var i = 0; i < list.length; i++) {\n'\
@@ -3824,19 +3797,23 @@ def addScriptCode(hf, testruns):
3824 ' var pos = 0.0, val = 0.0;\n'\ 3797 ' var pos = 0.0, val = 0.0;\n'\
3825 ' for (var j = 0; j < divTotal; j++) {\n'\ 3798 ' for (var j = 0; j < divTotal; j++) {\n'\
3826 ' var htmlline = "";\n'\ 3799 ' var htmlline = "";\n'\
3827 ' if(list[i].id[5] == "r") {\n'\ 3800 ' var mode = list[i].id[5];\n'\
3828 ' pos = 100 - (((j)*tS*100)/mTotal);\n'\ 3801 ' if(mode == "s") {\n'\
3829 ' val = (j)*tS;\n'\
3830 ' htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\
3831 ' if(j == 0)\n'\
3832 ' htmlline = rline;\n'\
3833 ' } else {\n'\
3834 ' pos = 100 - (((j)*tS*100)/mTotal) - divEdge;\n'\ 3802 ' pos = 100 - (((j)*tS*100)/mTotal) - divEdge;\n'\
3835 ' val = (j-divTotal+1)*tS;\n'\ 3803 ' val = (j-divTotal+1)*tS;\n'\
3836 ' if(j == divTotal - 1)\n'\ 3804 ' if(j == divTotal - 1)\n'\
3837 ' htmlline = \'<div class="t" style="right:\'+pos+\'%"><cS>S&rarr;</cS></div>\';\n'\ 3805 ' htmlline = \'<div class="t" style="right:\'+pos+\'%"><cS>S&rarr;</cS></div>\';\n'\
3838 ' else\n'\ 3806 ' else\n'\
3839 ' htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\ 3807 ' htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\
3808 ' } else {\n'\
3809 ' pos = 100 - (((j)*tS*100)/mTotal);\n'\
3810 ' val = (j)*tS;\n'\
3811 ' htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\
3812 ' if(j == 0)\n'\
3813 ' if(mode == "r")\n'\
3814 ' htmlline = rline+"<cS>&larr;R</cS></div>";\n'\
3815 ' else\n'\
3816 ' htmlline = rline+"<cS>0ms</div>";\n'\
3840 ' }\n'\ 3817 ' }\n'\
3841 ' html += htmlline;\n'\ 3818 ' html += htmlline;\n'\
3842 ' }\n'\ 3819 ' }\n'\
@@ -4002,12 +3979,80 @@ def addScriptCode(hf, testruns):
4002 ' }\n'\ 3979 ' }\n'\
4003 ' }\n'\ 3980 ' }\n'\
4004 ' }\n'\ 3981 ' }\n'\
3982 ' if(typeof devstats !== \'undefined\')\n'\
3983 ' callDetail(this.id, this.title);\n'\
4005 ' var cglist = document.getElementById("callgraphs");\n'\ 3984 ' var cglist = document.getElementById("callgraphs");\n'\
4006 ' if(!cglist) return;\n'\ 3985 ' if(!cglist) return;\n'\
4007 ' var cg = cglist.getElementsByClassName("atop");\n'\ 3986 ' var cg = cglist.getElementsByClassName("atop");\n'\
4008 ' if(cg.length < 10) return;\n'\ 3987 ' if(cg.length < 10) return;\n'\
4009 ' for (var i = 0; i < cg.length; i++) {\n'\ 3988 ' for (var i = 0; i < cg.length; i++) {\n'\
4010 ' if(idlist.indexOf(cg[i].id) >= 0) {\n'\ 3989 ' cgid = cg[i].id.split("x")[0]\n'\
3990 ' if(idlist.indexOf(cgid) >= 0) {\n'\
3991 ' cg[i].style.display = "block";\n'\
3992 ' } else {\n'\
3993 ' cg[i].style.display = "none";\n'\
3994 ' }\n'\
3995 ' }\n'\
3996 ' }\n'\
3997 ' function callDetail(devid, devtitle) {\n'\
3998 ' if(!(devid in devstats) || devstats[devid].length < 1)\n'\
3999 ' return;\n'\
4000 ' var list = devstats[devid];\n'\
4001 ' var tmp = devtitle.split(" ");\n'\
4002 ' var name = tmp[0], phase = tmp[tmp.length-1];\n'\
4003 ' var dd = document.getElementById(phase);\n'\
4004 ' var total = parseFloat(tmp[1].slice(1));\n'\
4005 ' var mlist = [];\n'\
4006 ' var maxlen = 0;\n'\
4007 ' var info = []\n'\
4008 ' for(var i in list) {\n'\
4009 ' if(list[i][0] == "@") {\n'\
4010 ' info = list[i].split("|");\n'\
4011 ' continue;\n'\
4012 ' }\n'\
4013 ' var tmp = list[i].split("|");\n'\
4014 ' var t = parseFloat(tmp[0]), f = tmp[1], c = parseInt(tmp[2]);\n'\
4015 ' var p = (t*100.0/total).toFixed(2);\n'\
4016 ' mlist[mlist.length] = [f, c, t.toFixed(2), p+"%"];\n'\
4017 ' if(f.length > maxlen)\n'\
4018 ' maxlen = f.length;\n'\
4019 ' }\n'\
4020 ' var pad = 5;\n'\
4021 ' if(mlist.length == 0) pad = 30;\n'\
4022 ' var html = \'<div style="padding-top:\'+pad+\'px"><t3> <b>\'+name+\':</b>\';\n'\
4023 ' if(info.length > 2)\n'\
4024 ' html += " start=<b>"+info[1]+"</b>, end=<b>"+info[2]+"</b>";\n'\
4025 ' if(info.length > 3)\n'\
4026 ' html += ", length<i>(w/o overhead)</i>=<b>"+info[3]+" ms</b>";\n'\
4027 ' if(info.length > 4)\n'\
4028 ' html += ", return=<b>"+info[4]+"</b>";\n'\
4029 ' html += "</t3></div>";\n'\
4030 ' if(mlist.length > 0) {\n'\
4031 ' html += \'<table class=fstat style="padding-top:\'+(maxlen*5)+\'px;"><tr><th>Function</th>\';\n'\
4032 ' for(var i in mlist)\n'\
4033 ' html += "<td class=vt>"+mlist[i][0]+"</td>";\n'\
4034 ' html += "</tr><tr><th>Calls</th>";\n'\
4035 ' for(var i in mlist)\n'\
4036 ' html += "<td>"+mlist[i][1]+"</td>";\n'\
4037 ' html += "</tr><tr><th>Time(ms)</th>";\n'\
4038 ' for(var i in mlist)\n'\
4039 ' html += "<td>"+mlist[i][2]+"</td>";\n'\
4040 ' html += "</tr><tr><th>Percent</th>";\n'\
4041 ' for(var i in mlist)\n'\
4042 ' html += "<td>"+mlist[i][3]+"</td>";\n'\
4043 ' html += "</tr></table>";\n'\
4044 ' }\n'\
4045 ' dd.innerHTML = html;\n'\
4046 ' var height = (maxlen*5)+100;\n'\
4047 ' dd.style.height = height+"px";\n'\
4048 ' document.getElementById("devicedetail").style.height = height+"px";\n'\
4049 ' }\n'\
4050 ' function callSelect() {\n'\
4051 ' var cglist = document.getElementById("callgraphs");\n'\
4052 ' if(!cglist) return;\n'\
4053 ' var cg = cglist.getElementsByClassName("atop");\n'\
4054 ' for (var i = 0; i < cg.length; i++) {\n'\
4055 ' if(this.id == cg[i].id) {\n'\
4011 ' cg[i].style.display = "block";\n'\ 4056 ' cg[i].style.display = "block";\n'\
4012 ' } else {\n'\ 4057 ' } else {\n'\
4013 ' cg[i].style.display = "none";\n'\ 4058 ' cg[i].style.display = "none";\n'\
@@ -4093,6 +4138,9 @@ def addScriptCode(hf, testruns):
4093 ' dev[i].onmouseover = deviceHover;\n'\ 4138 ' dev[i].onmouseover = deviceHover;\n'\
4094 ' dev[i].onmouseout = deviceUnhover;\n'\ 4139 ' dev[i].onmouseout = deviceUnhover;\n'\
4095 ' }\n'\ 4140 ' }\n'\
4141 ' var dev = dmesg.getElementsByClassName("srccall");\n'\
4142 ' for (var i = 0; i < dev.length; i++)\n'\
4143 ' dev[i].onclick = callSelect;\n'\
4096 ' zoomTimeline();\n'\ 4144 ' zoomTimeline();\n'\
4097 ' });\n'\ 4145 ' });\n'\
4098 '</script>\n' 4146 '</script>\n'
@@ -4675,7 +4723,7 @@ def rootCheck(fatal):
4675 if(os.access(sysvals.powerfile, os.W_OK)): 4723 if(os.access(sysvals.powerfile, os.W_OK)):
4676 return True 4724 return True
4677 if fatal: 4725 if fatal:
4678 doError('This command must be run as root') 4726 doError('This command requires sysfs mount and root access')
4679 return False 4727 return False
4680 4728
4681# Function: getArgInt 4729# Function: getArgInt
@@ -4767,51 +4815,62 @@ def runTest(subdir, testpath=''):
4767 cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1' 4815 cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1'
4768 call(cmd.format(os.environ['SUDO_USER'], sysvals.testdir), shell=True) 4816 call(cmd.format(os.environ['SUDO_USER'], sysvals.testdir), shell=True)
4769 4817
4818def find_in_html(html, strs, div=False):
4819 for str in strs:
4820 l = len(str)
4821 i = html.find(str)
4822 if i >= 0:
4823 break
4824 if i < 0:
4825 return ''
4826 if not div:
4827 return re.search(r'[-+]?\d*\.\d+|\d+', html[i+l:i+l+50]).group()
4828 n = html[i+l:].find('</div>')
4829 if n < 0:
4830 return ''
4831 return html[i+l:i+l+n]
4832
4770# Function: runSummary 4833# Function: runSummary
4771# Description: 4834# Description:
4772# create a summary of tests in a sub-directory 4835# create a summary of tests in a sub-directory
4773def runSummary(subdir, output): 4836def runSummary(subdir, local=True):
4774 # get a list of ftrace output files 4837 inpath = os.path.abspath(subdir)
4775 files = [] 4838 outpath = inpath
4839 if local:
4840 outpath = os.path.abspath('.')
4841 print('Generating a summary of folder "%s"' % inpath)
4842 testruns = []
4776 for dirname, dirnames, filenames in os.walk(subdir): 4843 for dirname, dirnames, filenames in os.walk(subdir):
4777 for filename in filenames: 4844 for filename in filenames:
4778 if(re.match('.*_ftrace.txt', filename)): 4845 if(not re.match('.*.html', filename)):
4779 files.append("%s/%s" % (dirname, filename))
4780
4781 # process the files in order and get an array of data objects
4782 testruns = []
4783 for file in sorted(files):
4784 if output:
4785 print("Test found in %s" % os.path.dirname(file))
4786 sysvals.ftracefile = file
4787 sysvals.dmesgfile = file.replace('_ftrace.txt', '_dmesg.txt')
4788 doesTraceLogHaveTraceEvents()
4789 sysvals.usecallgraph = False
4790 if not sysvals.usetraceeventsonly:
4791 if(not os.path.exists(sysvals.dmesgfile)):
4792 print("Skipping %s: not a valid test input" % file)
4793 continue 4846 continue
4794 else: 4847 file = os.path.join(dirname, filename)
4795 if output: 4848 html = open(file, 'r').read(10000)
4796 f = os.path.basename(sysvals.ftracefile) 4849 suspend = find_in_html(html,
4797 d = os.path.basename(sysvals.dmesgfile) 4850 ['Kernel Suspend: ', 'Kernel Suspend Time: '])
4798 print("\tInput files: %s and %s" % (f, d)) 4851 resume = find_in_html(html,
4799 testdata = loadKernelLog() 4852 ['Kernel Resume: ', 'Kernel Resume Time: '])
4800 data = testdata[0] 4853 line = find_in_html(html, ['<div class="stamp">'], True)
4801 parseKernelLog(data) 4854 stmp = line.split()
4802 testdata = [data] 4855 if not suspend or not resume or len(stmp) < 4:
4803 appendIncompleteTraceLog(testdata) 4856 continue
4804 else: 4857 data = {
4805 if output: 4858 'host': stmp[0],
4806 print("\tInput file: %s" % os.path.basename(sysvals.ftracefile)) 4859 'kernel': stmp[1],
4807 testdata = parseTraceLog() 4860 'mode': stmp[2],
4808 data = testdata[0] 4861 'time': string.join(stmp[3:], ' '),
4809 data.normalizeTime(data.tSuspended) 4862 'suspend': suspend,
4810 link = file.replace(subdir+'/', '').replace('_ftrace.txt', '.html') 4863 'resume': resume,
4811 data.outfile = link 4864 'url': os.path.relpath(file, outpath),
4812 testruns.append(data) 4865 }
4813 4866 if len(stmp) == 7:
4814 createHTMLSummarySimple(testruns, subdir+'/summary.html') 4867 data['kernel'] = 'unknown'
4868 data['mode'] = stmp[1]
4869 data['time'] = string.join(stmp[2:], ' ')
4870 testruns.append(data)
4871 outfile = os.path.join(outpath, 'summary.html')
4872 print('Summary file: %s' % outfile)
4873 createHTMLSummarySimple(testruns, outfile, inpath)
4815 4874
4816# Function: checkArgBool 4875# Function: checkArgBool
4817# Description: 4876# Description:
@@ -4869,9 +4928,14 @@ def configFromFile(file):
4869 sysvals.predelay = getArgInt('-predelay', value, 0, 60000, False) 4928 sysvals.predelay = getArgInt('-predelay', value, 0, 60000, False)
4870 elif(opt.lower() == 'postdelay'): 4929 elif(opt.lower() == 'postdelay'):
4871 sysvals.postdelay = getArgInt('-postdelay', value, 0, 60000, False) 4930 sysvals.postdelay = getArgInt('-postdelay', value, 0, 60000, False)
4931 elif(opt.lower() == 'maxdepth'):
4932 sysvals.max_graph_depth = getArgInt('-maxdepth', value, 0, 1000, False)
4872 elif(opt.lower() == 'rtcwake'): 4933 elif(opt.lower() == 'rtcwake'):
4873 sysvals.rtcwake = True 4934 if value.lower() == 'off':
4874 sysvals.rtcwaketime = getArgInt('-rtcwake', value, 0, 3600, False) 4935 sysvals.rtcwake = False
4936 else:
4937 sysvals.rtcwake = True
4938 sysvals.rtcwaketime = getArgInt('-rtcwake', value, 0, 3600, False)
4875 elif(opt.lower() == 'timeprec'): 4939 elif(opt.lower() == 'timeprec'):
4876 sysvals.setPrecision(getArgInt('-timeprec', value, 0, 6, False)) 4940 sysvals.setPrecision(getArgInt('-timeprec', value, 0, 6, False))
4877 elif(opt.lower() == 'mindev'): 4941 elif(opt.lower() == 'mindev'):
@@ -4969,8 +5033,8 @@ def printHelp():
4969 modes = getModes() 5033 modes = getModes()
4970 5034
4971 print('') 5035 print('')
4972 print('AnalyzeSuspend v%s' % sysvals.version) 5036 print('%s v%s' % (sysvals.title, sysvals.version))
4973 print('Usage: sudo analyze_suspend.py <options>') 5037 print('Usage: sudo sleepgraph <options> <commands>')
4974 print('') 5038 print('')
4975 print('Description:') 5039 print('Description:')
4976 print(' This tool is designed to assist kernel and OS developers in optimizing') 5040 print(' This tool is designed to assist kernel and OS developers in optimizing')
@@ -4981,22 +5045,22 @@ def printHelp():
4981 print(' a detailed view of which devices/subsystems are taking the most') 5045 print(' a detailed view of which devices/subsystems are taking the most')
4982 print(' time in suspend/resume.') 5046 print(' time in suspend/resume.')
4983 print('') 5047 print('')
5048 print(' If no specific command is given, the default behavior is to initiate')
5049 print(' a suspend/resume and capture the dmesg/ftrace output as an html timeline.')
5050 print('')
4984 print(' Generates output files in subdirectory: suspend-mmddyy-HHMMSS') 5051 print(' Generates output files in subdirectory: suspend-mmddyy-HHMMSS')
4985 print(' HTML output: <hostname>_<mode>.html') 5052 print(' HTML output: <hostname>_<mode>.html')
4986 print(' raw dmesg output: <hostname>_<mode>_dmesg.txt') 5053 print(' raw dmesg output: <hostname>_<mode>_dmesg.txt')
4987 print(' raw ftrace output: <hostname>_<mode>_ftrace.txt') 5054 print(' raw ftrace output: <hostname>_<mode>_ftrace.txt')
4988 print('') 5055 print('')
4989 print('Options:') 5056 print('Options:')
4990 print(' [general]')
4991 print(' -h Print this help text') 5057 print(' -h Print this help text')
4992 print(' -v Print the current tool version') 5058 print(' -v Print the current tool version')
4993 print(' -config fn Pull arguments and config options from file fn') 5059 print(' -config fn Pull arguments and config options from file fn')
4994 print(' -verbose Print extra information during execution and analysis') 5060 print(' -verbose Print extra information during execution and analysis')
4995 print(' -status Test to see if the system is enabled to run this tool')
4996 print(' -modes List available suspend modes')
4997 print(' -m mode Mode to initiate for suspend %s (default: %s)') % (modes, sysvals.suspendmode) 5061 print(' -m mode Mode to initiate for suspend %s (default: %s)') % (modes, sysvals.suspendmode)
4998 print(' -o subdir Override the output subdirectory') 5062 print(' -o subdir Override the output subdirectory')
4999 print(' -rtcwake t Use rtcwake to autoresume after <t> seconds (default: disabled)') 5063 print(' -rtcwake t Wakeup t seconds after suspend, set t to "off" to disable (default: 15)')
5000 print(' -addlogs Add the dmesg and ftrace logs to the html output') 5064 print(' -addlogs Add the dmesg and ftrace logs to the html output')
5001 print(' -srgap Add a visible gap in the timeline between sus/res (default: disabled)') 5065 print(' -srgap Add a visible gap in the timeline between sus/res (default: disabled)')
5002 print(' [advanced]') 5066 print(' [advanced]')
@@ -5012,23 +5076,25 @@ def printHelp():
5012 print(' be created in a new subdirectory with a summary page.') 5076 print(' be created in a new subdirectory with a summary page.')
5013 print(' [debug]') 5077 print(' [debug]')
5014 print(' -f Use ftrace to create device callgraphs (default: disabled)') 5078 print(' -f Use ftrace to create device callgraphs (default: disabled)')
5079 print(' -maxdepth N limit the callgraph data to N call levels (default: 0=all)')
5015 print(' -expandcg pre-expand the callgraph data in the html output (default: disabled)') 5080 print(' -expandcg pre-expand the callgraph data in the html output (default: disabled)')
5016 print(' -flist Print the list of functions currently being captured in ftrace')
5017 print(' -flistall Print all functions capable of being captured in ftrace')
5018 print(' -fadd file Add functions to be graphed in the timeline from a list in a text file') 5081 print(' -fadd file Add functions to be graphed in the timeline from a list in a text file')
5019 print(' -filter "d1,d2,..." Filter out all but this comma-delimited list of device names') 5082 print(' -filter "d1,d2,..." Filter out all but this comma-delimited list of device names')
5020 print(' -mincg ms Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)') 5083 print(' -mincg ms Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)')
5021 print(' -cgphase P Only show callgraph data for phase P (e.g. suspend_late)') 5084 print(' -cgphase P Only show callgraph data for phase P (e.g. suspend_late)')
5022 print(' -cgtest N Only show callgraph data for test N (e.g. 0 or 1 in an x2 run)') 5085 print(' -cgtest N Only show callgraph data for test N (e.g. 0 or 1 in an x2 run)')
5023 print(' -timeprec N Number of significant digits in timestamps (0:S, [3:ms], 6:us)') 5086 print(' -timeprec N Number of significant digits in timestamps (0:S, [3:ms], 6:us)')
5024 print(' [utilities]') 5087 print(' [commands]')
5088 print(' -ftrace ftracefile Create HTML output using ftrace input (used with -dmesg)')
5089 print(' -dmesg dmesgfile Create HTML output using dmesg (used with -ftrace)')
5090 print(' -summary directory Create a summary of all test in this dir')
5091 print(' -modes List available suspend modes')
5092 print(' -status Test to see if the system is enabled to run this tool')
5025 print(' -fpdt Print out the contents of the ACPI Firmware Performance Data Table') 5093 print(' -fpdt Print out the contents of the ACPI Firmware Performance Data Table')
5026 print(' -usbtopo Print out the current USB topology with power info') 5094 print(' -usbtopo Print out the current USB topology with power info')
5027 print(' -usbauto Enable autosuspend for all connected USB devices') 5095 print(' -usbauto Enable autosuspend for all connected USB devices')
5028 print(' [re-analyze data from previous runs]') 5096 print(' -flist Print the list of functions currently being captured in ftrace')
5029 print(' -ftrace ftracefile Create HTML output using ftrace input') 5097 print(' -flistall Print all functions capable of being captured in ftrace')
5030 print(' -dmesg dmesgfile Create HTML output using dmesg (not needed for kernel >= 3.15)')
5031 print(' -summary directory Create a summary of all test in this dir')
5032 print('') 5098 print('')
5033 return True 5099 return True
5034 5100
@@ -5076,9 +5142,18 @@ if __name__ == '__main__':
5076 sysvals.useprocmon = True 5142 sysvals.useprocmon = True
5077 elif(arg == '-dev'): 5143 elif(arg == '-dev'):
5078 sysvals.usedevsrc = True 5144 sysvals.usedevsrc = True
5145 elif(arg == '-maxdepth'):
5146 sysvals.max_graph_depth = getArgInt('-maxdepth', args, 0, 1000)
5079 elif(arg == '-rtcwake'): 5147 elif(arg == '-rtcwake'):
5080 sysvals.rtcwake = True 5148 try:
5081 sysvals.rtcwaketime = getArgInt('-rtcwake', args, 0, 3600) 5149 val = args.next()
5150 except:
5151 doError('No rtcwake time supplied', True)
5152 if val.lower() == 'off':
5153 sysvals.rtcwake = False
5154 else:
5155 sysvals.rtcwake = True
5156 sysvals.rtcwaketime = getArgInt('-rtcwake', val, 0, 3600, False)
5082 elif(arg == '-timeprec'): 5157 elif(arg == '-timeprec'):
5083 sysvals.setPrecision(getArgInt('-timeprec', args, 0, 6)) 5158 sysvals.setPrecision(getArgInt('-timeprec', args, 0, 6))
5084 elif(arg == '-mindev'): 5159 elif(arg == '-mindev'):
@@ -5201,7 +5276,6 @@ if __name__ == '__main__':
5201 elif(cmd == 'usbauto'): 5276 elif(cmd == 'usbauto'):
5202 setUSBDevicesAuto() 5277 setUSBDevicesAuto()
5203 elif(cmd == 'summary'): 5278 elif(cmd == 'summary'):
5204 print("Generating a summary of folder \"%s\"" % cmdarg)
5205 runSummary(cmdarg, True) 5279 runSummary(cmdarg, True)
5206 sys.exit() 5280 sys.exit()
5207 5281
diff --git a/tools/power/pm-graph/bootgraph.8 b/tools/power/pm-graph/bootgraph.8
new file mode 100644
index 000000000000..55272a67b0e7
--- /dev/null
+++ b/tools/power/pm-graph/bootgraph.8
@@ -0,0 +1,132 @@
1.TH BOOTGRAPH 8
2.SH NAME
3bootgraph \- Kernel boot timing analysis
4.SH SYNOPSIS
5.ft B
6.B bootgraph
7.RB [ OPTIONS ]
8.RB [ COMMAND ]
9.SH DESCRIPTION
10\fBbootgraph \fP reads the dmesg log from kernel boot and
11creates an html representation of the initcall timeline up to the start
12of the init process.
13.PP
14If no specific command is given, the tool reads the current dmesg log and
15outputs bootgraph.html.
16.PP
17The tool can also augment the timeline with ftrace data on custom target
18functions as well as full trace callgraphs.
19.SH OPTIONS
20.TP
21\fB-h\fR
22Print this help text
23.TP
24\fB-v\fR
25Print the current tool version
26.TP
27\fB-addlogs\fR
28Add the dmesg log to the html output. It will be viewable by
29clicking a button in the timeline.
30.TP
31\fB-o \fIfile\fR
32Override the HTML output filename (default: bootgraph.html)
33.SS "Ftrace Debug"
34.TP
35\fB-f\fR
36Use ftrace to add function detail (default: disabled)
37.TP
38\fB-callgraph\fR
39Use ftrace to create initcall callgraphs (default: disabled). If -filter
40is not used there will be one callgraph per initcall. This can produce
41very large outputs, i.e. 10MB - 100MB.
42.TP
43\fB-maxdepth \fIlevel\fR
44limit the callgraph trace depth to \fIlevel\fR (default: 2). This is
45the best way to limit the output size when using -callgraph.
46.TP
47\fB-mincg \fIt\fR
48Discard all callgraphs shorter than \fIt\fR milliseconds (default: 0=all).
49This reduces the html file size as there can be many tiny callgraphs
50which are barely visible in the timeline.
51The value is a float: e.g. 0.001 represents 1 us.
52.TP
53\fB-timeprec \fIn\fR
54Number of significant digits in timestamps (0:S, 3:ms, [6:us])
55.TP
56\fB-expandcg\fR
57pre-expand the callgraph data in the html output (default: disabled)
58.TP
59\fB-filter \fI"func1,func2,..."\fR
60Instead of tracing each initcall, trace a custom list of functions (default: do_one_initcall)
61
62.SH COMMANDS
63.TP
64\fB-reboot\fR
65Reboot the machine and generate a new timeline automatically. Works in 4 steps.
66 1. updates grub with the required kernel parameters
67 2. installs a cron job which re-runs the tool after reboot
68 3. reboots the system
69 4. after startup, extracts the data and generates the timeline
70.TP
71\fB-manual\fR
72Show the requirements to generate a new timeline manually. Requires 3 steps.
73 1. append the string to the kernel command line via your native boot manager.
74 2. reboot the system
75 3. after startup, re-run the tool with the same arguments and no command
76.TP
77\fB-dmesg \fIfile\fR
78Create HTML output from an existing dmesg file.
79.TP
80\fB-ftrace \fIfile\fR
81Create HTML output from an existing ftrace file (used with -dmesg).
82.TP
83\fB-flistall\fR
84Print all ftrace functions capable of being captured. These are all the
85possible values you can add to trace via the -filter argument.
86
87.SH EXAMPLES
88Create a timeline using the current dmesg log.
89.IP
90\f(CW$ bootgraph\fR
91.PP
92Create a timeline using the current dmesg and ftrace log.
93.IP
94\f(CW$ bootgraph -callgraph\fR
95.PP
96Create a timeline using the current dmesg, add the log to the html and change the name.
97.IP
98\f(CW$ bootgraph -addlogs -o myboot.html\fR
99.PP
100Capture a new boot timeline by automatically rebooting the machine.
101.IP
102\f(CW$ sudo bootgraph -reboot -addlogs -o latestboot.html\fR
103.PP
104Capture a new boot timeline with function trace data.
105.IP
106\f(CW$ sudo bootgraph -reboot -f\fR
107.PP
108Capture a new boot timeline with trace & callgraph data. Skip callgraphs smaller than 5ms.
109.IP
110\f(CW$ sudo bootgraph -reboot -callgraph -mincg 5\fR
111.PP
112Capture a new boot timeline with callgraph data over custom functions.
113.IP
114\f(CW$ sudo bootgraph -reboot -callgraph -filter "acpi_ps_parse_aml,msleep"\fR
115.PP
116Capture a brand new boot timeline with manual reboot.
117.IP
118\f(CW$ sudo bootgraph -callgraph -manual\fR
119.IP
120\f(CW$ vi /etc/default/grub # add the CMDLINE string to your kernel params\fR
121.IP
122\f(CW$ sudo reboot # reboot the machine\fR
123.IP
124\f(CW$ sudo bootgraph -callgraph # re-run the tool after restart\fR
125.PP
126
127.SH "SEE ALSO"
128dmesg(1), update-grub(8), crontab(1), reboot(8)
129.PP
130.SH AUTHOR
131.nf
132Written by Todd Brandt <todd.e.brandt@linux.intel.com>
diff --git a/tools/power/pm-graph/sleepgraph.8 b/tools/power/pm-graph/sleepgraph.8
new file mode 100644
index 000000000000..610e72ebbc06
--- /dev/null
+++ b/tools/power/pm-graph/sleepgraph.8
@@ -0,0 +1,243 @@
1.TH SLEEPGRAPH 8
2.SH NAME
3sleepgraph \- Suspend/Resume timing analysis
4.SH SYNOPSIS
5.ft B
6.B sleepgraph
7.RB [ OPTIONS ]
8.RB [ COMMAND ]
9.SH DESCRIPTION
10\fBsleepgraph \fP is designed to assist kernel and OS developers
11in optimizing their linux stack's suspend/resume time. Using a kernel
12image built with a few extra options enabled, the tool will execute a
13suspend and capture dmesg and ftrace data until resume is complete.
14This data is transformed into a device timeline and an optional
15callgraph to give a detailed view of which devices/subsystems are
16taking the most time in suspend/resume.
17.PP
18If no specific command is given, the default behavior is to initiate
19a suspend/resume.
20.PP
21Generates output files in subdirectory: suspend-yymmdd-HHMMSS
22 html timeline : <hostname>_<mode>.html
23 raw dmesg file : <hostname>_<mode>_dmesg.txt
24 raw ftrace file : <hostname>_<mode>_ftrace.txt
25.SH OPTIONS
26.TP
27\fB-h\fR
28Print the help text.
29.TP
30\fB-v\fR
31Print the current tool version.
32.TP
33\fB-verbose\fR
34Print extra information during execution and analysis.
35.TP
36\fB-config \fIfile\fR
37Pull arguments and config options from a file.
38.TP
39\fB-m \fImode\fR
40Mode to initiate for suspend e.g. standby, freeze, mem (default: mem).
41.TP
42\fB-o \fIsubdir\fR
43Override the output subdirectory. Use {date}, {time}, {hostname} for current values.
44.sp
45e.g. suspend-{hostname}-{date}-{time}
46.TP
47\fB-rtcwake \fIt\fR | off
48Use rtcwake to autoresume after \fIt\fR seconds (default: 15). Set t to "off" to
49disable rtcwake and require a user keypress to resume.
50.TP
51\fB-addlogs\fR
52Add the dmesg and ftrace logs to the html output. They will be viewable by
53clicking buttons in the timeline.
54
55.SS "Advanced"
56.TP
57\fB-cmd \fIstr\fR
58Run the timeline over a custom suspend command, e.g. pm-suspend. By default
59the tool forces suspend via /sys/power/state so this allows testing over
60an OS's official suspend method. The output file will change to
61hostname_command.html and will autodetect which suspend mode was triggered.
62.TP
63\fB-filter \fI"d1,d2,..."\fR
64Filter out all but these device callbacks. These strings can be device names
65or module names. e.g. 0000:00:02.0, ata5, i915, usb, etc.
66.TP
67\fB-mindev \fIt\fR
68Discard all device callbacks shorter than \fIt\fR milliseconds (default: 0.0).
69This reduces the html file size as there can be many tiny callbacks which are barely
70visible. The value is a float: e.g. 0.001 represents 1 us.
71.TP
72\fB-proc\fR
73Add usermode process info into the timeline (default: disabled).
74.TP
75\fB-dev\fR
76Add kernel source calls and threads to the timeline (default: disabled).
77.TP
78\fB-x2\fR
79Run two suspend/resumes back to back (default: disabled).
80.TP
81\fB-x2delay \fIt\fR
82Include \fIt\fR ms delay between multiple test runs (default: 0 ms).
83.TP
84\fB-predelay \fIt\fR
85Include \fIt\fR ms delay before 1st suspend (default: 0 ms).
86.TP
87\fB-postdelay \fIt\fR
88Include \fIt\fR ms delay after last resume (default: 0 ms).
89.TP
90\fB-multi \fIn d\fR
91Execute \fIn\fR consecutive tests at \fId\fR seconds intervals. The outputs will
92be created in a new subdirectory with a summary page: suspend-xN-{date}-{time}.
93
94.SS "Ftrace Debug"
95.TP
96\fB-f\fR
97Use ftrace to create device callgraphs (default: disabled). This can produce
98very large outputs, i.e. 10MB - 100MB.
99.TP
100\fB-maxdepth \fIlevel\fR
101limit the callgraph trace depth to \fIlevel\fR (default: 0=all). This is
102the best way to limit the output size when using callgraphs via -f.
103.TP
104\fB-expandcg\fR
105pre-expand the callgraph data in the html output (default: disabled)
106.TP
107\fB-fadd \fIfile\fR
108Add functions to be graphed in the timeline from a list in a text file
109.TP
110\fB-mincg \fIt\fR
111Discard all callgraphs shorter than \fIt\fR milliseconds (default: 0.0).
112This reduces the html file size as there can be many tiny callgraphs
113which are barely visible in the timeline.
114The value is a float: e.g. 0.001 represents 1 us.
115.TP
116\fB-cgphase \fIp\fR
117Only show callgraph data for phase \fIp\fR (e.g. suspend_late).
118.TP
119\fB-cgtest \fIn\fR
120In an x2 run, only show callgraph data for test \fIn\fR (e.g. 0 or 1).
121.TP
122\fB-timeprec \fIn\fR
123Number of significant digits in timestamps (0:S, [3:ms], 6:us).
124
125.SH COMMANDS
126.TP
127\fB-ftrace \fIfile\fR
128Create HTML output from an existing ftrace file.
129.TP
130\fB-dmesg \fIfile\fR
131Create HTML output from an existing dmesg file.
132.TP
133\fB-summary \fIindir\fR
134Create a summary page of all tests in \fIindir\fR. Creates summary.html
135in the current folder. The output page is a table of tests with
136suspend and resume values sorted by suspend mode, host, and kernel.
137Includes test averages by mode and links to the test html files.
138.TP
139\fB-modes\fR
140List available suspend modes.
141.TP
142\fB-status\fR
143Test to see if the system is able to run this tool. Use this along
144with any options you intend to use to see if they will work.
145.TP
146\fB-fpdt\fR
147Print out the contents of the ACPI Firmware Performance Data Table.
148.TP
149\fB-usbtopo\fR
150Print out the current USB topology with power info.
151.TP
152\fB-usbauto\fR
153Enable autosuspend for all connected USB devices.
154.TP
155\fB-flist\fR
156Print the list of ftrace functions currently being captured. Functions
157that are not available as symbols in the current kernel are shown in red.
158By default, the tool traces a list of important suspend/resume functions
159in order to better fill out the timeline. If the user has added their own
160with -fadd they will also be checked.
161.TP
162\fB-flistall\fR
163Print all ftrace functions capable of being captured. These are all the
164possible values you can add to trace via the -fadd argument.
165
166.SH EXAMPLES
167.SS "Simple Commands"
168Check which suspend modes are currently supported.
169.IP
170\f(CW$ sleepgraph -modes\fR
171.PP
172Read the Firmware Performance Data Table (FPDT)
173.IP
174\f(CW$ sudo sleepgraph -fpdt\fR
175.PP
176Print out the current USB power topology
177.IP
178\f(CW$ sleepgraph -usbtopo
179.PP
180Verify that you can run a command with a set of arguments
181.IP
182\f(CW$ sudo sleepgraph -f -rtcwake 30 -status
183.PP
184Generate a summary of all timelines in a particular folder.
185.IP
186\f(CW$ sleepgraph -summary ~/workspace/myresults/\fR
187.PP
188Re-generate the html output from a previous run's dmesg and ftrace log.
189.IP
190\f(CW$ sleepgraph -dmesg myhost_mem_dmesg.txt -ftrace myhost_mem_ftrace.txt\fR
191.PP
192
193.SS "Capturing Simple Timelines"
194Execute a mem suspend with a 15 second wakeup. Include the logs in the html.
195.IP
196\f(CW$ sudo sleepgraph -rtcwake 15 -addlogs\fR
197.PP
198Execute a standby with a 15 second wakeup. Change the output folder name.
199.IP
200\f(CW$ sudo sleepgraph -m standby -rtcwake 15 -o "standby-{hostname}-{date}-{time}"\fR
201.PP
202Execute a freeze with no wakeup (require keypress). Change output folder name.
203.IP
204\f(CW$ sudo sleepgraph -m freeze -rtcwake off -o "freeze-{hostname}-{date}-{time}"\fR
205.PP
206
207.SS "Capturing Advanced Timelines"
208Execute a suspend & include dev mode source calls, limit callbacks to 5ms or larger.
209.IP
210\f(CW$ sudo sleepgraph -m mem -rtcwake 15 -dev -mindev 5\fR
211.PP
212Run two suspends back to back, include a 500ms delay before, after, and in between runs.
213.IP
214\f(CW$ sudo sleepgraph -m mem -rtcwake 15 -x2 -predelay 500 -x2delay 500 -postdelay 500\fR
215.PP
216Do a batch run of 10 freezes with 30 seconds delay between runs.
217.IP
218\f(CW$ sudo sleepgraph -m freeze -rtcwake 15 -multi 10 30\fR
219.PP
220Execute a suspend using a custom command.
221.IP
222\f(CW$ sudo sleepgraph -cmd "echo mem > /sys/power/state" -rtcwake 15\fR
223.PP
224
225
226.SS "Capturing Timelines with Callgraph Data"
227Add device callgraphs. Limit the trace depth and only show callgraphs 10ms or larger.
228.IP
229\f(CW$ sudo sleepgraph -m mem -rtcwake 15 -f -maxdepth 5 -mincg 10\fR
230.PP
231Capture a full callgraph across all suspend, then filter the html by a single phase.
232.IP
233\f(CW$ sudo sleepgraph -m mem -rtcwake 15 -f\fR
234.IP
235\f(CW$ sleepgraph -dmesg host_mem_dmesg.txt -ftrace host_mem_ftrace.txt -f -cgphase resume
236.PP
237
238.SH "SEE ALSO"
239dmesg(1)
240.PP
241.SH AUTHOR
242.nf
243Written by Todd Brandt <todd.e.brandt@linux.intel.com>
diff --git a/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py b/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py
index fd706ac0f347..0b24dd9d01ff 100755
--- a/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py
+++ b/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py
@@ -353,6 +353,14 @@ def split_csv():
353 os.system('grep -m 1 common_cpu cpu.csv > cpu{:0>3}.csv'.format(index)) 353 os.system('grep -m 1 common_cpu cpu.csv > cpu{:0>3}.csv'.format(index))
354 os.system('grep CPU_{:0>3} cpu.csv >> cpu{:0>3}.csv'.format(index, index)) 354 os.system('grep CPU_{:0>3} cpu.csv >> cpu{:0>3}.csv'.format(index, index))
355 355
356def fix_ownership(path):
357 """Change the owner of the file to SUDO_UID, if required"""
358
359 uid = os.environ.get('SUDO_UID')
360 gid = os.environ.get('SUDO_GID')
361 if uid is not None:
362 os.chown(path, int(uid), int(gid))
363
356def cleanup_data_files(): 364def cleanup_data_files():
357 """ clean up existing data files """ 365 """ clean up existing data files """
358 366
@@ -518,12 +526,16 @@ else:
518 526
519if not os.path.exists('results'): 527if not os.path.exists('results'):
520 os.mkdir('results') 528 os.mkdir('results')
529 # The regular user needs to own the directory, not root.
530 fix_ownership('results')
521 531
522os.chdir('results') 532os.chdir('results')
523if os.path.exists(testname): 533if os.path.exists(testname):
524 print('The test name directory already exists. Please provide a unique test name. Test re-run not supported, yet.') 534 print('The test name directory already exists. Please provide a unique test name. Test re-run not supported, yet.')
525 sys.exit() 535 sys.exit()
526os.mkdir(testname) 536os.mkdir(testname)
537# The regular user needs to own the directory, not root.
538fix_ownership(testname)
527os.chdir(testname) 539os.chdir(testname)
528 540
529# Temporary (or perhaps not) 541# Temporary (or perhaps not)
@@ -566,4 +578,9 @@ plot_scaled_cpu()
566plot_boost_cpu() 578plot_boost_cpu()
567plot_ghz_cpu() 579plot_ghz_cpu()
568 580
581# It is preferrable, but not necessary, that the regular user owns the files, not root.
582for root, dirs, files in os.walk('.'):
583 for f in files:
584 fix_ownership(f)
585
569os.chdir('../../') 586os.chdir('../../')