* [PATCH] pm-graph v5.5
@ 2019-08-12 21:08 Todd Brandt
2019-08-26 9:19 ` Rafael J. Wysocki
0 siblings, 1 reply; 2+ messages in thread
From: Todd Brandt @ 2019-08-12 21:08 UTC (permalink / raw)
To: linux-pm, rafael.j.wysocki, rjw; +Cc: todd.e.brandt, todd.e.brandt
Upgrade bootgraph/sleepgraph to be able to run on python2 and python3.
Both now simply require python, the system can choose which to use.
bootgraph python3 update:
- add floor function to handle integer arithmetic
- change argument loop to use next() instead of args.next()
- open dmesg log and popen in binary, use decode(ascii, ignore)
- sort all html data to allow diff between python versions
- change exception handler to use python3 as instead of comma
sleepgraph python3 update:
- import configparser not ConfigParser (p2 needs python-configparser)
- add floor function to handle integer arithmetic
- change argument loop to use next() instead of args.next()
- handle popen output in binary, use decode(ascii, ignore)
- sort all html/output data to allow diff between python versions
- force gzip open to use text mode, same for file open
- ensure no binary data is written to logs (ascii convert devprops info)
- use codecs library to handle zlib encoding for mcelog data
- remove all uses of python3.7 keyword "async" as members or vars
- assume all FPDT and DMI data is in binary string form
sleepgraph:
- turbostat will be used by default if it's found & the mode is freeze
- a new option "-noturbostat" will disable its use
- fix bug where two callgraphs with the same start time overwrite.
- fix s2idle processing where two suspend/resume_machines occur back2back
- update getexec function to use which first (assuming PATH exists)
- new platforminfo data in log with: lspci, gpe counts, /proc/interrupts
- new data is zipped, b64 encoded, and tacked on the end of ftrace
Signed-off-by: Todd Brandt <todd.e.brandt@linux.intel.com>
---
tools/power/pm-graph/README | 6 +-
tools/power/pm-graph/bootgraph.py | 59 ++-
tools/power/pm-graph/sleepgraph.8 | 8 +-
tools/power/pm-graph/sleepgraph.py | 610 ++++++++++++++++-------------
4 files changed, 387 insertions(+), 296 deletions(-)
diff --git a/tools/power/pm-graph/README b/tools/power/pm-graph/README
index 58a5591e3951..96259f6e5715 100644
--- a/tools/power/pm-graph/README
+++ b/tools/power/pm-graph/README
@@ -1,7 +1,7 @@
p m - g r a p h
pm-graph: suspend/resume/boot timing analysis tools
- Version: 5.4
+ Version: 5.5
Author: Todd Brandt <todd.e.brandt@intel.com>
Home Page: https://01.org/pm-graph
@@ -18,6 +18,10 @@
- upstream version in git:
https://github.com/intel/pm-graph/
+ Requirements:
+ - runs with python2 or python3, choice is made by /usr/bin/python link
+ - python2 now requires python-configparser be installed
+
Table of Contents
- Overview
- Setup
diff --git a/tools/power/pm-graph/bootgraph.py b/tools/power/pm-graph/bootgraph.py
index 666bcbda648d..d3b99a1e92d6 100755
--- a/tools/power/pm-graph/bootgraph.py
+++ b/tools/power/pm-graph/bootgraph.py
@@ -1,9 +1,18 @@
-#!/usr/bin/python2
+#!/usr/bin/python
# SPDX-License-Identifier: GPL-2.0-only
#
# Tool for analyzing boot timing
# Copyright (c) 2013, Intel Corporation.
#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU General Public License,
+# version 2, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+#
# Authors:
# Todd Brandt <todd.e.brandt@linux.intel.com>
#
@@ -81,7 +90,7 @@ class SystemValues(aslib.SystemValues):
cmdline = 'initcall_debug log_buf_len=32M'
if self.useftrace:
if self.cpucount > 0:
- bs = min(self.memtotal / 2, 2*1024*1024) / self.cpucount
+ bs = min(self.memtotal // 2, 2*1024*1024) // self.cpucount
else:
bs = 131072
cmdline += ' trace_buf_size=%dK trace_clock=global '\
@@ -137,13 +146,13 @@ class SystemValues(aslib.SystemValues):
if arg in ['-h', '-v', '-cronjob', '-reboot', '-verbose']:
continue
elif arg in ['-o', '-dmesg', '-ftrace', '-func']:
- args.next()
+ next(args)
continue
elif arg == '-result':
- cmdline += ' %s "%s"' % (arg, os.path.abspath(args.next()))
+ cmdline += ' %s "%s"' % (arg, os.path.abspath(next(args)))
continue
elif arg == '-cgskip':
- file = self.configFile(args.next())
+ file = self.configFile(next(args))
cmdline += ' %s "%s"' % (arg, os.path.abspath(file))
continue
cmdline += ' '+arg
@@ -292,11 +301,11 @@ def parseKernelLog():
tp = aslib.TestProps()
devtemp = dict()
if(sysvals.dmesgfile):
- lf = open(sysvals.dmesgfile, 'r')
+ lf = open(sysvals.dmesgfile, 'rb')
else:
lf = Popen('dmesg', stdout=PIPE).stdout
for line in lf:
- line = line.replace('\r\n', '')
+ line = aslib.ascii(line).replace('\r\n', '')
# grab the stamp and sysinfo
if re.match(tp.stampfmt, line):
tp.stamp = line
@@ -649,7 +658,7 @@ def createBootGraph(data):
statinfo += '\t"%s": [\n\t\t"%s",\n' % (n, devstats[n]['info'])
if 'fstat' in devstats[n]:
funcs = devstats[n]['fstat']
- for f in sorted(funcs, key=funcs.get, reverse=True):
+ for f in sorted(funcs, key=lambda k:(funcs[k], k), reverse=True):
if funcs[f][0] < 0.01 and len(funcs) > 10:
break
statinfo += '\t\t"%f|%s|%d",\n' % (funcs[f][0], f, funcs[f][1])
@@ -729,7 +738,7 @@ def updateCron(restore=False):
op.write('@reboot python %s\n' % sysvals.cronjobCmdString())
op.close()
res = call([cmd, cronfile])
- except Exception, e:
+ except Exception as e:
pprint('Exception: %s' % str(e))
shutil.move(backfile, cronfile)
res = -1
@@ -745,7 +754,7 @@ def updateGrub(restore=False):
try:
call(sysvals.blexec, stderr=PIPE, stdout=PIPE,
env={'PATH': '.:/sbin:/usr/sbin:/usr/bin:/sbin:/bin'})
- except Exception, e:
+ except Exception as e:
pprint('Exception: %s\n' % str(e))
return
# extract the option and create a grub config without it
@@ -792,7 +801,7 @@ def updateGrub(restore=False):
op.close()
res = call(sysvals.blexec)
os.remove(grubfile)
- except Exception, e:
+ except Exception as e:
pprint('Exception: %s' % str(e))
res = -1
# cleanup
@@ -866,6 +875,7 @@ def printHelp():
'Other commands:\n'\
' -flistall Print all functions capable of being captured in ftrace\n'\
' -sysinfo Print out system info extracted from BIOS\n'\
+ ' -which exec Print an executable path, should function even without PATH\n'\
' [redo]\n'\
' -dmesg file Create HTML output using dmesg input (used with -ftrace)\n'\
' -ftrace file Create HTML output using ftrace input (used with -dmesg)\n'\
@@ -907,13 +917,13 @@ if __name__ == '__main__':
sysvals.mincglen = aslib.getArgFloat('-mincg', args, 0.0, 10000.0)
elif(arg == '-cgfilter'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No callgraph functions supplied', True)
sysvals.setCallgraphFilter(val)
elif(arg == '-cgskip'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No file supplied', True)
if val.lower() in switchoff:
@@ -924,7 +934,7 @@ if __name__ == '__main__':
doError('%s does not exist' % cgskip)
elif(arg == '-bl'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No boot loader name supplied', True)
if val.lower() not in ['grub']:
@@ -937,7 +947,7 @@ if __name__ == '__main__':
sysvals.max_graph_depth = aslib.getArgInt('-maxdepth', args, 0, 1000)
elif(arg == '-func'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No filter functions supplied', True)
sysvals.useftrace = True
@@ -946,7 +956,7 @@ if __name__ == '__main__':
sysvals.setGraphFilter(val)
elif(arg == '-ftrace'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No ftrace file supplied', True)
if(os.path.exists(val) == False):
@@ -959,7 +969,7 @@ if __name__ == '__main__':
sysvals.cgexp = True
elif(arg == '-dmesg'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No dmesg file supplied', True)
if(os.path.exists(val) == False):
@@ -968,13 +978,13 @@ if __name__ == '__main__':
sysvals.dmesgfile = val
elif(arg == '-o'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No subdirectory name supplied', True)
sysvals.testdir = sysvals.setOutputFolder(val)
elif(arg == '-result'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No result file supplied', True)
sysvals.result = val
@@ -986,6 +996,17 @@ if __name__ == '__main__':
# remaining options are only for cron job use
elif(arg == '-cronjob'):
sysvals.iscronjob = True
+ elif(arg == '-which'):
+ try:
+ val = next(args)
+ except:
+ doError('No executable supplied', True)
+ out = sysvals.getExec(val)
+ if not out:
+ print('%s not found' % val)
+ sys.exit(1)
+ print(out)
+ sys.exit(0)
else:
doError('Invalid argument: '+arg, True)
diff --git a/tools/power/pm-graph/sleepgraph.8 b/tools/power/pm-graph/sleepgraph.8
index 9648be644d5f..43aee64316df 100644
--- a/tools/power/pm-graph/sleepgraph.8
+++ b/tools/power/pm-graph/sleepgraph.8
@@ -53,10 +53,10 @@ disable rtcwake and require a user keypress to resume.
Add the dmesg and ftrace logs to the html output. They will be viewable by
clicking buttons in the timeline.
.TP
-\fB-turbostat\fR
-Use turbostat to execute the command in freeze mode (default: disabled). This
-will provide turbostat output in the log which will tell you which actual
-power modes were entered.
+\fB-noturbostat\fR
+By default, if turbostat is found and the requested mode is freeze, sleepgraph
+will execute the suspend via turbostat and collect data in the timeline log.
+This option disables the use of turbostat.
.TP
\fB-result \fIfile\fR
Export a results table to a text file for parsing.
diff --git a/tools/power/pm-graph/sleepgraph.py b/tools/power/pm-graph/sleepgraph.py
index 4f46a7a1feb6..1794c79a7d1b 100755
--- a/tools/power/pm-graph/sleepgraph.py
+++ b/tools/power/pm-graph/sleepgraph.py
@@ -1,9 +1,18 @@
-#!/usr/bin/python2
+#!/usr/bin/python
# SPDX-License-Identifier: GPL-2.0-only
#
# Tool for analyzing suspend/resume timing
# Copyright (c) 2013, Intel Corporation.
#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU General Public License,
+# version 2, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+#
# Authors:
# Todd Brandt <todd.e.brandt@linux.intel.com>
#
@@ -48,9 +57,10 @@ import string
import re
import platform
import signal
+import codecs
from datetime import datetime
import struct
-import ConfigParser
+import configparser
import gzip
from threading import Thread
from subprocess import call, Popen, PIPE
@@ -60,6 +70,9 @@ def pprint(msg):
print(msg)
sys.stdout.flush()
+def ascii(text):
+ return text.decode('ascii', 'ignore')
+
# ----------------- CLASSES --------------------
# Class: SystemValues
@@ -68,7 +81,7 @@ def pprint(msg):
# store system values and test parameters
class SystemValues:
title = 'SleepGraph'
- version = '5.4'
+ version = '5.5'
ansi = False
rs = 0
display = ''
@@ -78,7 +91,7 @@ class SystemValues:
testlog = True
dmesglog = True
ftracelog = False
- tstat = False
+ tstat = True
mindevlen = 0.0
mincglen = 0.0
cgphase = ''
@@ -147,6 +160,7 @@ class SystemValues:
devdump = False
mixedphaseheight = True
devprops = dict()
+ platinfo = []
predelay = 0
postdelay = 0
pmdebug = ''
@@ -323,13 +337,20 @@ class SystemValues:
sys.exit(1)
return False
def getExec(self, cmd):
- dirlist = ['/sbin', '/bin', '/usr/sbin', '/usr/bin',
- '/usr/local/sbin', '/usr/local/bin']
- for path in dirlist:
+ try:
+ fp = Popen(['which', cmd], stdout=PIPE, stderr=PIPE).stdout
+ out = ascii(fp.read()).strip()
+ fp.close()
+ except:
+ out = ''
+ if out:
+ return out
+ for path in ['/sbin', '/bin', '/usr/sbin', '/usr/bin',
+ '/usr/local/sbin', '/usr/local/bin']:
cmdfull = os.path.join(path, cmd)
if os.path.exists(cmdfull):
return cmdfull
- return ''
+ return out
def setPrecision(self, num):
if num < 0 or num > 6:
return
@@ -455,7 +476,7 @@ class SystemValues:
fp = Popen('dmesg', stdout=PIPE).stdout
ktime = '0'
for line in fp:
- line = line.replace('\r\n', '')
+ line = ascii(line).replace('\r\n', '')
idx = line.find('[')
if idx > 1:
line = line[idx:]
@@ -469,7 +490,7 @@ class SystemValues:
# store all new dmesg lines since initdmesg was called
fp = Popen('dmesg', stdout=PIPE).stdout
for line in fp:
- line = line.replace('\r\n', '')
+ line = ascii(line).replace('\r\n', '')
idx = line.find('[')
if idx > 1:
line = line[idx:]
@@ -501,7 +522,7 @@ class SystemValues:
call('cat '+self.tpath+'available_filter_functions', shell=True)
return
master = self.listFromFile(self.tpath+'available_filter_functions')
- for i in self.tracefuncs:
+ for i in sorted(self.tracefuncs):
if 'func' in self.tracefuncs[i]:
i = self.tracefuncs[i]['func']
if i in master:
@@ -628,7 +649,7 @@ class SystemValues:
self.fsetVal(kprobeevents, 'kprobe_events')
if output:
check = self.fgetVal('kprobe_events')
- linesack = (len(check.split('\n')) - 1) / 2
+ linesack = (len(check.split('\n')) - 1) // 2
pprint(' kprobe functions enabled: %d/%d' % (linesack, linesout))
self.fsetVal('1', 'events/kprobes/enable')
def testKprobe(self, kname, kprobe):
@@ -650,7 +671,7 @@ class SystemValues:
if not os.path.exists(file):
return False
try:
- fp = open(file, mode, 0)
+ fp = open(file, mode)
fp.write(val)
fp.flush()
fp.close()
@@ -719,7 +740,7 @@ class SystemValues:
tgtsize = min(self.memfree, bmax)
else:
tgtsize = 65536
- while not self.fsetVal('%d' % (tgtsize / cpus), 'buffer_size_kb'):
+ while not self.fsetVal('%d' % (tgtsize // cpus), 'buffer_size_kb'):
# if the size failed to set, lower it and keep trying
tgtsize -= 65536
if tgtsize < 65536:
@@ -863,14 +884,23 @@ class SystemValues:
isgz = self.gzip
if mode == 'r':
try:
- with gzip.open(filename, mode+'b') as fp:
+ with gzip.open(filename, mode+'t') as fp:
test = fp.read(64)
isgz = True
except:
isgz = False
if isgz:
- return gzip.open(filename, mode+'b')
+ return gzip.open(filename, mode+'t')
return open(filename, mode)
+ def b64unzip(self, data):
+ try:
+ out = codecs.decode(base64.b64decode(data), 'zlib').decode()
+ except:
+ out = data
+ return out
+ def b64zip(self, data):
+ out = base64.b64encode(codecs.encode(data.encode(), 'zlib')).decode()
+ return out
def mcelog(self, clear=False):
cmd = self.getExec('mcelog')
if not cmd:
@@ -878,12 +908,124 @@ class SystemValues:
if clear:
call(cmd+' > /dev/null 2>&1', shell=True)
return ''
- fp = Popen([cmd], stdout=PIPE, stderr=PIPE).stdout
- out = fp.read().strip()
- fp.close()
+ try:
+ fp = Popen([cmd], stdout=PIPE, stderr=PIPE).stdout
+ out = ascii(fp.read()).strip()
+ fp.close()
+ except:
+ return ''
if not out:
return ''
- return base64.b64encode(out.encode('zlib'))
+ return self.b64zip(out)
+ def platforminfo(self):
+ # add platform info on to a completed ftrace file
+ if not os.path.exists(self.ftracefile):
+ return False
+ footer = '#\n'
+
+ # add test command string line if need be
+ if self.suspendmode == 'command' and self.testcommand:
+ footer += '# platform-testcmd: %s\n' % (self.testcommand)
+
+ # get a list of target devices from the ftrace file
+ props = dict()
+ tp = TestProps()
+ tf = self.openlog(self.ftracefile, 'r')
+ for line in tf:
+ # determine the trace data type (required for further parsing)
+ m = re.match(tp.tracertypefmt, line)
+ if(m):
+ tp.setTracerType(m.group('t'))
+ continue
+ # parse only valid lines, if this is not one move on
+ m = re.match(tp.ftrace_line_fmt, line)
+ if(not m or 'device_pm_callback_start' not in line):
+ continue
+ m = re.match('.*: (?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*', m.group('msg'));
+ if(not m):
+ continue
+ dev = m.group('d')
+ if dev not in props:
+ props[dev] = DevProps()
+ tf.close()
+
+ # now get the syspath for each target device
+ for dirname, dirnames, filenames in os.walk('/sys/devices'):
+ if(re.match('.*/power', dirname) and 'async' in filenames):
+ dev = dirname.split('/')[-2]
+ if dev in props and (not props[dev].syspath or len(dirname) < len(props[dev].syspath)):
+ props[dev].syspath = dirname[:-6]
+
+ # now fill in the properties for our target devices
+ for dev in sorted(props):
+ dirname = props[dev].syspath
+ if not dirname or not os.path.exists(dirname):
+ continue
+ with open(dirname+'/power/async') as fp:
+ text = fp.read()
+ props[dev].isasync = False
+ if 'enabled' in text:
+ props[dev].isasync = True
+ fields = os.listdir(dirname)
+ if 'product' in fields:
+ with open(dirname+'/product', 'rb') as fp:
+ props[dev].altname = ascii(fp.read())
+ elif 'name' in fields:
+ with open(dirname+'/name', 'rb') as fp:
+ props[dev].altname = ascii(fp.read())
+ elif 'model' in fields:
+ with open(dirname+'/model', 'rb') as fp:
+ props[dev].altname = ascii(fp.read())
+ elif 'description' in fields:
+ with open(dirname+'/description', 'rb') as fp:
+ props[dev].altname = ascii(fp.read())
+ elif 'id' in fields:
+ with open(dirname+'/id', 'rb') as fp:
+ props[dev].altname = ascii(fp.read())
+ elif 'idVendor' in fields and 'idProduct' in fields:
+ idv, idp = '', ''
+ with open(dirname+'/idVendor', 'rb') as fp:
+ idv = ascii(fp.read()).strip()
+ with open(dirname+'/idProduct', 'rb') as fp:
+ idp = ascii(fp.read()).strip()
+ props[dev].altname = '%s:%s' % (idv, idp)
+ if props[dev].altname:
+ out = props[dev].altname.strip().replace('\n', ' ')\
+ .replace(',', ' ').replace(';', ' ')
+ props[dev].altname = out
+
+ # add a devinfo line to the bottom of ftrace
+ out = ''
+ for dev in sorted(props):
+ out += props[dev].out(dev)
+ footer += '# platform-devinfo: %s\n' % self.b64zip(out)
+
+ # add a line for each of these commands with their outputs
+ cmds = [
+ ['pcidevices', 'lspci', '-tv'],
+ ['interrupts', 'cat', '/proc/interrupts'],
+ ['gpecounts', 'sh', '-c', 'grep -v invalid /sys/firmware/acpi/interrupts/gpe*'],
+ ]
+ for cargs in cmds:
+ name = cargs[0]
+ cmdline = ' '.join(cargs[1:])
+ cmdpath = self.getExec(cargs[1])
+ if not cmdpath:
+ continue
+ cmd = [cmdpath] + cargs[2:]
+ try:
+ fp = Popen(cmd, stdout=PIPE, stderr=PIPE).stdout
+ info = ascii(fp.read()).strip()
+ fp.close()
+ except:
+ continue
+ if not info:
+ continue
+ footer += '# platform-%s: %s | %s\n' % (name, cmdline, self.b64zip(info))
+
+ with self.openlog(self.ftracefile, 'a') as fp:
+ fp.write(footer)
+ return True
def haveTurbostat(self):
if not self.tstat:
return False
@@ -891,31 +1033,40 @@ class SystemValues:
if not cmd:
return False
fp = Popen([cmd, '-v'], stdout=PIPE, stderr=PIPE).stderr
- out = fp.read().strip()
+ out = ascii(fp.read()).strip()
fp.close()
- return re.match('turbostat version [0-9\.]* .*', out)
+ if re.match('turbostat version [0-9\.]* .*', out):
+ sysvals.vprint(out)
+ return True
+ return False
def turbostat(self):
cmd = self.getExec('turbostat')
- if not cmd:
- return 'missing turbostat executable'
- text = []
+ rawout = keyline = valline = ''
fullcmd = '%s -q -S echo freeze > %s' % (cmd, self.powerfile)
fp = Popen(['sh', '-c', fullcmd], stdout=PIPE, stderr=PIPE).stderr
for line in fp:
- if re.match('[0-9.]* sec', line):
+ line = ascii(line)
+ rawout += line
+ if keyline and valline:
continue
- text.append(line.split())
+ if re.match('(?i)Avg_MHz.*', line):
+ keyline = line.strip().split()
+ elif keyline:
+ valline = line.strip().split()
fp.close()
- if len(text) < 2:
- return 'turbostat output format error'
+ if not keyline or not valline or len(keyline) != len(valline):
+ errmsg = 'unrecognized turbostat output:\n'+rawout.strip()
+ sysvals.vprint(errmsg)
+ if not sysvals.verbose:
+ pprint(errmsg)
+ return ''
+ if sysvals.verbose:
+ pprint(rawout.strip())
out = []
- for key in text[0]:
- values = []
- idx = text[0].index(key)
- for line in text[1:]:
- if len(line) > idx:
- values.append(line[idx])
- out.append('%s=%s' % (key, ','.join(values)))
+ for key in keyline:
+ idx = keyline.index(key)
+ val = valline[idx]
+ out.append('%s=%s' % (key, val))
return '|'.join(out)
def checkWifi(self):
out = dict()
@@ -924,7 +1075,7 @@ class SystemValues:
return out
fp = Popen(iwcmd, stdout=PIPE, stderr=PIPE).stdout
for line in fp:
- m = re.match('(?P<dev>\S*) .* ESSID:(?P<ess>\S*)', line)
+ m = re.match('(?P<dev>\S*) .* ESSID:(?P<ess>\S*)', ascii(line))
if not m:
continue
out['device'] = m.group('dev')
@@ -935,7 +1086,7 @@ class SystemValues:
if 'device' in out:
fp = Popen([ifcmd, out['device']], stdout=PIPE, stderr=PIPE).stdout
for line in fp:
- m = re.match('.* inet (?P<ip>[0-9\.]*)', line)
+ m = re.match('.* inet (?P<ip>[0-9\.]*)', ascii(line))
if m:
out['ip'] = m.group('ip')
break
@@ -990,13 +1141,13 @@ class DevProps:
def __init__(self):
self.syspath = ''
self.altname = ''
- self.async = True
+ self.isasync = True
self.xtraclass = ''
self.xtrainfo = ''
def out(self, dev):
- return '%s,%s,%d;' % (dev, self.altname, self.async)
+ return '%s,%s,%d;' % (dev, self.altname, self.isasync)
def debug(self, dev):
- pprint('%s:\n\taltname = %s\n\t async = %s' % (dev, self.altname, self.async))
+ pprint('%s:\n\taltname = %s\n\t async = %s' % (dev, self.altname, self.isasync))
def altName(self, dev):
if not self.altname or self.altname == dev:
return dev
@@ -1004,13 +1155,13 @@ class DevProps:
def xtraClass(self):
if self.xtraclass:
return ' '+self.xtraclass
- if not self.async:
+ if not self.isasync:
return ' sync'
return ''
def xtraInfo(self):
if self.xtraclass:
return ' '+self.xtraclass
- if self.async:
+ if self.isasync:
return ' async_device'
return ' sync_device'
@@ -1108,7 +1259,7 @@ class Data:
return sorted(self.dmesg, key=lambda k:self.dmesg[k]['order'])
def initDevicegroups(self):
# called when phases are all finished being added
- for phase in self.dmesg.keys():
+ for phase in sorted(self.dmesg.keys()):
if '*' in phase:
p = phase.split('*')
pnew = '%s%d' % (p[0], len(p))
@@ -1430,16 +1581,7 @@ class Data:
return phase
def sortedDevices(self, phase):
list = self.dmesg[phase]['list']
- slist = []
- tmp = dict()
- for devname in list:
- dev = list[devname]
- if dev['length'] == 0:
- continue
- tmp[dev['start']] = devname
- for t in sorted(tmp):
- slist.append(tmp[t])
- return slist
+ return sorted(list, key=lambda k:list[k]['start'])
def fixupInitcalls(self, phase):
# if any calls never returned, clip them at system resume end
phaselist = self.dmesg[phase]['list']
@@ -1576,7 +1718,7 @@ class Data:
maxname = '%d' % self.maxDeviceNameSize(phase)
fmt = '%3d) %'+maxname+'s - %f - %f'
c = 1
- for name in devlist:
+ for name in sorted(devlist):
s = devlist[name]['start']
e = devlist[name]['end']
sysvals.vprint(fmt % (c, name, s, e))
@@ -1588,7 +1730,7 @@ class Data:
devlist = []
for phase in self.sortedPhases():
list = self.deviceChildren(devname, phase)
- for dev in list:
+ for dev in sorted(list):
if dev not in devlist:
devlist.append(dev)
return devlist
@@ -1628,16 +1770,16 @@ class Data:
def rootDeviceList(self):
# list of devices graphed
real = []
- for phase in self.dmesg:
+ for phase in self.sortedPhases():
list = self.dmesg[phase]['list']
- for dev in list:
+ for dev in sorted(list):
if list[dev]['pid'] >= 0 and dev not in real:
real.append(dev)
# list of top-most root devices
rootlist = []
- for phase in self.dmesg:
+ for phase in self.sortedPhases():
list = self.dmesg[phase]['list']
- for dev in list:
+ for dev in sorted(list):
pdev = list[dev]['par']
pid = list[dev]['pid']
if(pid < 0 or re.match('[0-9]*-[0-9]*\.[0-9]*[\.0-9]*\:[\.0-9]*$', pdev)):
@@ -1718,9 +1860,9 @@ class Data:
def createProcessUsageEvents(self):
# get an array of process names
proclist = []
- for t in self.pstl:
+ for t in sorted(self.pstl):
pslist = self.pstl[t]
- for ps in pslist:
+ for ps in sorted(pslist):
if ps not in proclist:
proclist.append(ps)
# get a list of data points for suspend and resume
@@ -1765,7 +1907,7 @@ class Data:
def debugPrint(self):
for p in self.sortedPhases():
list = self.dmesg[p]['list']
- for devname in list:
+ for devname in sorted(list):
dev = list[devname]
if 'ftrace' in dev:
dev['ftrace'].debugPrint(' [%s]' % devname)
@@ -2466,7 +2608,7 @@ class Timeline:
# if there is 1 line per row, draw them the standard way
for t, p in standardphases:
for i in sorted(self.rowheight[t][p]):
- self.rowheight[t][p][i] = self.bodyH/len(self.rowlines[t][p])
+ self.rowheight[t][p][i] = float(self.bodyH)/len(self.rowlines[t][p])
def createZoomBox(self, mode='command', testcount=1):
# Create bounding box, add buttons
html_zoombox = '<center><button id="zoomin">ZOOM IN +</button><button id="zoomout">ZOOM OUT -</button><button id="zoomdef">ZOOM 1:1</button></center>\n'
@@ -2537,6 +2679,7 @@ class TestProps:
cmdlinefmt = '^# command \| (?P<cmd>.*)'
kparamsfmt = '^# kparams \| (?P<kp>.*)'
devpropfmt = '# Device Properties: .*'
+ pinfofmt = '# platform-(?P<val>[a-z,A-Z,0-9]*): (?P<info>.*)'
tracertypefmt = '# tracer: (?P<t>.*)'
firmwarefmt = '# fwsuspend (?P<s>[0-9]*) fwresume (?P<r>[0-9]*)$'
procexecfmt = 'ps - (?P<ps>.*)$'
@@ -2571,12 +2714,6 @@ class TestProps:
self.ftrace_line_fmt = self.ftrace_line_fmt_nop
else:
doError('Invalid tracer format: [%s]' % tracer)
- def decode(self, data):
- try:
- out = base64.b64decode(data).decode('zlib')
- except:
- out = data
- return out
def stampInfo(self, line):
if re.match(self.stampfmt, line):
self.stamp = line
@@ -2660,7 +2797,7 @@ class TestProps:
if len(self.mcelog) > data.testnumber:
m = re.match(self.mcelogfmt, self.mcelog[data.testnumber])
if m:
- data.mcelog = self.decode(m.group('m'))
+ data.mcelog = sv.b64unzip(m.group('m'))
# turbostat data
if len(self.turbostat) > data.testnumber:
m = re.match(self.tstatfmt, self.turbostat[data.testnumber])
@@ -2681,6 +2818,46 @@ class TestProps:
m = re.match(self.testerrfmt, self.testerror[data.testnumber])
if m:
data.enterfail = m.group('e')
+ def devprops(self, data):
+ props = dict()
+ devlist = data.split(';')
+ for dev in devlist:
+ f = dev.split(',')
+ if len(f) < 3:
+ continue
+ dev = f[0]
+ props[dev] = DevProps()
+ props[dev].altname = f[1]
+ if int(f[2]):
+ props[dev].isasync = True
+ else:
+ props[dev].isasync = False
+ return props
+ def parseDevprops(self, line, sv):
+ idx = line.index(': ') + 2
+ if idx >= len(line):
+ return
+ props = self.devprops(line[idx:])
+ if sv.suspendmode == 'command' and 'testcommandstring' in props:
+ sv.testcommand = props['testcommandstring'].altname
+ sv.devprops = props
+ def parsePlatformInfo(self, line, sv):
+ m = re.match(self.pinfofmt, line)
+ if not m:
+ return
+ name, info = m.group('val'), m.group('info')
+ if name == 'devinfo':
+ sv.devprops = self.devprops(sv.b64unzip(info))
+ return
+ elif name == 'testcmd':
+ sv.testcommand = info
+ return
+ field = info.split('|')
+ if len(field) < 2:
+ return
+ cmdline = field[0].strip()
+ output = sv.b64unzip(field[1].strip())
+ sv.platinfo.append([name, cmdline, output])
# Class: TestRun
# Description:
@@ -2701,7 +2878,7 @@ class ProcessMonitor:
process = Popen(c, shell=True, stdout=PIPE)
running = dict()
for line in process.stdout:
- data = line.split()
+ data = ascii(line).split()
pid = data[0]
name = re.sub('[()]', '', data[1])
user = int(data[13])
@@ -2805,7 +2982,11 @@ def appendIncompleteTraceLog(testruns):
continue
# device properties line
if(re.match(tp.devpropfmt, line)):
- devProps(line)
+ tp.parseDevprops(line, sysvals)
+ continue
+ # platform info line
+ if(re.match(tp.pinfofmt, line)):
+ tp.parsePlatformInfo(line, sysvals)
continue
# parse only valid lines, if this is not one move on
m = re.match(tp.ftrace_line_fmt, line)
@@ -2902,7 +3083,7 @@ def parseTraceLog(live=False):
sysvals.setupAllKprobes()
ksuscalls = ['pm_prepare_console']
krescalls = ['pm_restore_console']
- tracewatch = []
+ tracewatch = ['irq_wakeup']
if sysvals.usekprobes:
tracewatch += ['sync_filesystems', 'freeze_processes', 'syscore_suspend',
'syscore_resume', 'resume_console', 'thaw_processes', 'CPU_ON',
@@ -2928,7 +3109,11 @@ def parseTraceLog(live=False):
continue
# device properties line
if(re.match(tp.devpropfmt, line)):
- devProps(line)
+ tp.parseDevprops(line, sysvals)
+ continue
+ # platform info line
+ if(re.match(tp.pinfofmt, line)):
+ tp.parsePlatformInfo(line, sysvals)
continue
# ignore all other commented lines
if line[0] == '#':
@@ -3001,16 +3186,11 @@ def parseTraceLog(live=False):
isbegin = False
else:
continue
- m = re.match('(?P<name>.*)\[(?P<val>[0-9]*)\] .*', t.name)
- if(m):
- val = m.group('val')
- if val == '0':
- name = m.group('name')
- else:
- name = m.group('name')+'['+val+']'
+ if '[' in t.name:
+ m = re.match('(?P<name>.*)\[.*', t.name)
else:
m = re.match('(?P<name>.*) .*', t.name)
- name = m.group('name')
+ name = m.group('name')
# ignore these events
if(name.split('[')[0] in tracewatch):
continue
@@ -3045,6 +3225,8 @@ def parseTraceLog(live=False):
elif(re.match('machine_suspend\[.*', t.name)):
if(isbegin):
lp = data.lastPhase()
+ if lp == 'resume_machine':
+ data.dmesg[lp]['end'] = t.time
phase = data.setPhase('suspend_machine', data.dmesg[lp]['end'], True)
data.setPhase(phase, t.time, False)
if data.tSuspended == 0:
@@ -3213,11 +3395,11 @@ def parseTraceLog(live=False):
# add the traceevent data to the device hierarchy
if(sysvals.usetraceevents):
# add actual trace funcs
- for name in test.ttemp:
+ for name in sorted(test.ttemp):
for event in test.ttemp[name]:
data.newActionGlobal(name, event['begin'], event['end'], event['pid'])
# add the kprobe based virtual tracefuncs as actual devices
- for key in tp.ktemp:
+ for key in sorted(tp.ktemp):
name, pid = key
if name not in sysvals.tracefuncs:
continue
@@ -3231,7 +3413,7 @@ def parseTraceLog(live=False):
data.newActionGlobal(e['name'], kb, ke, pid, color)
# add config base kprobes and dev kprobes
if sysvals.usedevsrc:
- for key in tp.ktemp:
+ for key in sorted(tp.ktemp):
name, pid = key
if name in sysvals.tracefuncs or name not in sysvals.dev_tracefuncs:
continue
@@ -3244,7 +3426,7 @@ def parseTraceLog(live=False):
if sysvals.usecallgraph:
# add the callgraph data to the device hierarchy
sortlist = dict()
- for key in test.ftemp:
+ for key in sorted(test.ftemp):
proc, pid = key
for cg in test.ftemp[key]:
if len(cg.list) < 1 or cg.invalid or (cg.end - cg.start == 0):
@@ -3582,7 +3764,7 @@ def parseKernelLog(data):
# if trace events are not available, these are better than nothing
if(not sysvals.usetraceevents):
# look for known actions
- for a in at:
+ for a in sorted(at):
if(re.match(at[a]['smsg'], msg)):
if(a not in actions):
actions[a] = []
@@ -3641,7 +3823,7 @@ def parseKernelLog(data):
data.tResumed = data.tSuspended
# fill in any actions we've found
- for name in actions:
+ for name in sorted(actions):
for event in actions[name]:
data.newActionGlobal(name, event['begin'], event['end'])
@@ -3761,7 +3943,7 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
if lastmode and lastmode != mode and num > 0:
for i in range(2):
s = sorted(tMed[i])
- list[lastmode]['med'][i] = s[int(len(s)/2)]
+ list[lastmode]['med'][i] = s[int(len(s)//2)]
iMed[i] = tMed[i][list[lastmode]['med'][i]]
list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num]
list[lastmode]['min'] = tMin
@@ -3803,7 +3985,7 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
if lastmode and num > 0:
for i in range(2):
s = sorted(tMed[i])
- list[lastmode]['med'][i] = s[int(len(s)/2)]
+ list[lastmode]['med'][i] = s[int(len(s)//2)]
iMed[i] = tMed[i][list[lastmode]['med'][i]]
list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num]
list[lastmode]['min'] = tMin
@@ -3845,7 +4027,7 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
'</tr>\n'
headnone = '<tr class="head"><td>{0}</td><td>{1}</td><td colspan='+\
colspan+'></td></tr>\n'
- for mode in list:
+ for mode in sorted(list):
# header line for each suspend mode
num = 0
tAvg, tMin, tMax, tMed = list[mode]['avg'], list[mode]['min'],\
@@ -3944,7 +4126,8 @@ def createHTMLDeviceSummary(testruns, htmlfile, title):
th.format('Average Time') + th.format('Count') +\
th.format('Worst Time') + th.format('Host (worst time)') +\
th.format('Link (worst time)') + '</tr>\n'
- for name in sorted(devlist, key=lambda k:devlist[k]['worst'], reverse=True):
+ for name in sorted(devlist, key=lambda k:(devlist[k]['worst'], \
+ devlist[k]['total'], devlist[k]['name']), reverse=True):
data = devall[type][name]
data['average'] = data['total'] / data['count']
if data['average'] < limit:
@@ -4085,7 +4268,7 @@ def createHTML(testruns, testfail):
if(tTotal == 0):
doError('No timeline data')
if(len(data.tLow) > 0):
- low_time = '|'.join(data.tLow)
+ low_time = '+'.join(data.tLow)
if sysvals.suspendmode == 'command':
run_time = '%.0f'%((data.end-data.start)*1000)
if sysvals.testcommand:
@@ -4151,7 +4334,7 @@ def createHTML(testruns, testfail):
for group in data.devicegroups:
devlist = []
for phase in group:
- for devname in data.tdevlist[phase]:
+ for devname in sorted(data.tdevlist[phase]):
d = DevItem(data.testnumber, phase, data.dmesg[phase]['list'][devname])
devlist.append(d)
if d.isa('kth'):
@@ -4230,7 +4413,7 @@ def createHTML(testruns, testfail):
for b in phases[dir]:
# draw the devices for this phase
phaselist = data.dmesg[b]['list']
- for d in data.tdevlist[b]:
+ for d in sorted(data.tdevlist[b]):
name = d
drv = ''
dev = phaselist[d]
@@ -4971,13 +5154,9 @@ def executeSuspend():
if mode == 'freeze' and sysvals.haveTurbostat():
# execution will pause here
turbo = sysvals.turbostat()
- if '|' in turbo:
+ if turbo:
tdata['turbo'] = turbo
- else:
- tdata['error'] = turbo
else:
- if sysvals.haveTurbostat():
- sysvals.vprint('WARNING: ignoring turbostat in mode "%s"' % mode)
pf = open(sysvals.powerfile, 'w')
pf.write(mode)
# execution will pause here
@@ -5024,7 +5203,7 @@ def executeSuspend():
op.write(line)
op.close()
sysvals.fsetVal('', 'trace')
- devProps()
+ sysvals.platforminfo()
return testdata
def readFile(file):
@@ -5040,9 +5219,9 @@ def readFile(file):
# The time string, e.g. "1901m16s"
def ms2nice(val):
val = int(val)
- h = val / 3600000
- m = (val / 60000) % 60
- s = (val / 1000) % 60
+ h = val // 3600000
+ m = (val // 60000) % 60
+ s = (val // 1000) % 60
if h > 0:
return '%d:%02d:%02d' % (h, m, s)
if m > 0:
@@ -5115,127 +5294,6 @@ def deviceInfo(output=''):
print(lines[i])
return res
-# Function: devProps
-# Description:
-# Retrieve a list of properties for all devices in the trace log
-def devProps(data=0):
- props = dict()
-
- if data:
- idx = data.index(': ') + 2
- if idx >= len(data):
- return
- devlist = data[idx:].split(';')
- for dev in devlist:
- f = dev.split(',')
- if len(f) < 3:
- continue
- dev = f[0]
- props[dev] = DevProps()
- props[dev].altname = f[1]
- if int(f[2]):
- props[dev].async = True
- else:
- props[dev].async = False
- sysvals.devprops = props
- if sysvals.suspendmode == 'command' and 'testcommandstring' in props:
- sysvals.testcommand = props['testcommandstring'].altname
- return
-
- if(os.path.exists(sysvals.ftracefile) == False):
- doError('%s does not exist' % sysvals.ftracefile)
-
- # first get the list of devices we need properties for
- msghead = 'Additional data added by AnalyzeSuspend'
- alreadystamped = False
- tp = TestProps()
- tf = sysvals.openlog(sysvals.ftracefile, 'r')
- for line in tf:
- if msghead in line:
- alreadystamped = True
- continue
- # determine the trace data type (required for further parsing)
- m = re.match(tp.tracertypefmt, line)
- if(m):
- tp.setTracerType(m.group('t'))
- continue
- # parse only valid lines, if this is not one move on
- m = re.match(tp.ftrace_line_fmt, line)
- if(not m or 'device_pm_callback_start' not in line):
- continue
- m = re.match('.*: (?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*', m.group('msg'));
- if(not m):
- continue
- dev = m.group('d')
- if dev not in props:
- props[dev] = DevProps()
- tf.close()
-
- if not alreadystamped and sysvals.suspendmode == 'command':
- out = '#\n# '+msghead+'\n# Device Properties: '
- out += 'testcommandstring,%s,0;' % (sysvals.testcommand)
- with sysvals.openlog(sysvals.ftracefile, 'a') as fp:
- fp.write(out+'\n')
- sysvals.devprops = props
- return
-
- # now get the syspath for each of our target devices
- for dirname, dirnames, filenames in os.walk('/sys/devices'):
- if(re.match('.*/power', dirname) and 'async' in filenames):
- dev = dirname.split('/')[-2]
- if dev in props and (not props[dev].syspath or len(dirname) < len(props[dev].syspath)):
- props[dev].syspath = dirname[:-6]
-
- # now fill in the properties for our target devices
- for dev in props:
- dirname = props[dev].syspath
- if not dirname or not os.path.exists(dirname):
- continue
- with open(dirname+'/power/async') as fp:
- text = fp.read()
- props[dev].async = False
- if 'enabled' in text:
- props[dev].async = True
- fields = os.listdir(dirname)
- if 'product' in fields:
- with open(dirname+'/product') as fp:
- props[dev].altname = fp.read()
- elif 'name' in fields:
- with open(dirname+'/name') as fp:
- props[dev].altname = fp.read()
- elif 'model' in fields:
- with open(dirname+'/model') as fp:
- props[dev].altname = fp.read()
- elif 'description' in fields:
- with open(dirname+'/description') as fp:
- props[dev].altname = fp.read()
- elif 'id' in fields:
- with open(dirname+'/id') as fp:
- props[dev].altname = fp.read()
- elif 'idVendor' in fields and 'idProduct' in fields:
- idv, idp = '', ''
- with open(dirname+'/idVendor') as fp:
- idv = fp.read().strip()
- with open(dirname+'/idProduct') as fp:
- idp = fp.read().strip()
- props[dev].altname = '%s:%s' % (idv, idp)
-
- if props[dev].altname:
- out = props[dev].altname.strip().replace('\n', ' ')
- out = out.replace(',', ' ')
- out = out.replace(';', ' ')
- props[dev].altname = out
-
- # and now write the data to the ftrace file
- if not alreadystamped:
- out = '#\n# '+msghead+'\n# Device Properties: '
- for dev in sorted(props):
- out += props[dev].out(dev)
- with sysvals.openlog(sysvals.ftracefile, 'a') as fp:
- fp.write(out+'\n')
-
- sysvals.devprops = props
-
# Function: getModes
# Description:
# Determine the supported power modes on this system
@@ -5339,11 +5397,11 @@ def dmidecode(mempath, fatal=False):
# search for either an SM table or DMI table
i = base = length = num = 0
while(i < memsize):
- if buf[i:i+4] == '_SM_' and i < memsize - 16:
+ if buf[i:i+4] == b'_SM_' and i < memsize - 16:
length = struct.unpack('H', buf[i+22:i+24])[0]
base, num = struct.unpack('IH', buf[i+24:i+30])
break
- elif buf[i:i+5] == '_DMI_':
+ elif buf[i:i+5] == b'_DMI_':
length = struct.unpack('H', buf[i+6:i+8])[0]
base, num = struct.unpack('IH', buf[i+8:i+14])
break
@@ -5376,15 +5434,15 @@ def dmidecode(mempath, fatal=False):
if 0 == struct.unpack('H', buf[n:n+2])[0]:
break
n += 1
- data = buf[i+size:n+2].split('\0')
+ data = buf[i+size:n+2].split(b'\0')
for name in info:
itype, idxadr = info[name]
if itype == type:
- idx = struct.unpack('B', buf[i+idxadr])[0]
+ idx = struct.unpack('B', buf[i+idxadr:i+idxadr+1])[0]
if idx > 0 and idx < len(data) - 1:
- s = data[idx-1].strip()
- if s and s.lower() != 'to be filled by o.e.m.':
- out[name] = data[idx-1]
+ s = data[idx-1].decode('utf-8')
+ if s.strip() and s.strip().lower() != 'to be filled by o.e.m.':
+ out[name] = s
i = n + 2
count += 1
return out
@@ -5409,7 +5467,7 @@ def getBattery():
return (ac, charge)
def displayControl(cmd):
- xset, ret = 'xset -d :0.0 {0}', 0
+ xset, ret = 'timeout 10 xset -d :0.0 {0}', 0
if sysvals.sudouser:
xset = 'sudo -u %s %s' % (sysvals.sudouser, xset)
if cmd == 'init':
@@ -5433,7 +5491,7 @@ def displayControl(cmd):
fp = Popen(xset.format('q').split(' '), stdout=PIPE).stdout
ret = 'unknown'
for line in fp:
- m = re.match('[\s]*Monitor is (?P<m>.*)', line)
+ m = re.match('[\s]*Monitor is (?P<m>.*)', ascii(line))
if(m and len(m.group('m')) >= 2):
out = m.group('m').lower()
ret = out[3:] if out[0:2] == 'in' else out
@@ -5495,10 +5553,11 @@ def getFPDT(output):
' OEM Revision : %u\n'\
' Creator ID : %s\n'\
' Creator Revision : 0x%x\n'\
- '' % (table[0], table[0], table[1], table[2], table[3],
- table[4], table[5], table[6], table[7], table[8]))
+ '' % (ascii(table[0]), ascii(table[0]), table[1], table[2],
+ table[3], ascii(table[4]), ascii(table[5]), table[6],
+ ascii(table[7]), table[8]))
- if(table[0] != 'FPDT'):
+ if(table[0] != b'FPDT'):
if(output):
doError('Invalid FPDT table')
return False
@@ -5530,8 +5589,8 @@ def getFPDT(output):
return [0, 0]
rechead = struct.unpack('4sI', first)
recdata = fp.read(rechead[1]-8)
- if(rechead[0] == 'FBPT'):
- record = struct.unpack('HBBIQQQQQ', recdata)
+ if(rechead[0] == b'FBPT'):
+ record = struct.unpack('HBBIQQQQQ', recdata[:48])
if(output):
pprint('%s (%s)\n'\
' Reset END : %u ns\n'\
@@ -5539,11 +5598,11 @@ def getFPDT(output):
' OS Loader StartImage Start : %u ns\n'\
' ExitBootServices Entry : %u ns\n'\
' ExitBootServices Exit : %u ns'\
- '' % (rectype[header[0]], rechead[0], record[4], record[5],
+ '' % (rectype[header[0]], ascii(rechead[0]), record[4], record[5],
record[6], record[7], record[8]))
- elif(rechead[0] == 'S3PT'):
+ elif(rechead[0] == b'S3PT'):
if(output):
- pprint('%s (%s)' % (rectype[header[0]], rechead[0]))
+ pprint('%s (%s)' % (rectype[header[0]], ascii(rechead[0])))
j = 0
while(j < len(recdata)):
prechead = struct.unpack('HBB', recdata[j:j+4])
@@ -5689,7 +5748,7 @@ def doError(msg, help=False):
def getArgInt(name, args, min, max, main=True):
if main:
try:
- arg = args.next()
+ arg = next(args)
except:
doError(name+': no argument supplied', True)
else:
@@ -5708,7 +5767,7 @@ def getArgInt(name, args, min, max, main=True):
def getArgFloat(name, args, min, max, main=True):
if main:
try:
- arg = args.next()
+ arg = next(args)
except:
doError(name+': no argument supplied', True)
else:
@@ -5737,9 +5796,12 @@ def processData(live=False):
parseKernelLog(data)
if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)):
appendIncompleteTraceLog(testruns)
+ shown = ['bios', 'biosdate', 'cpu', 'host', 'kernel', 'man', 'memfr',
+ 'memsz', 'mode', 'numcpu', 'plat', 'time']
sysvals.vprint('System Info:')
for key in sorted(sysvals.stamp):
- sysvals.vprint(' %-8s : %s' % (key.upper(), sysvals.stamp[key]))
+ if key in shown:
+ sysvals.vprint(' %-8s : %s' % (key.upper(), sysvals.stamp[key]))
if sysvals.kparams:
sysvals.vprint('Kparams:\n %s' % sysvals.kparams)
sysvals.vprint('Command:\n %s' % sysvals.cmdline)
@@ -5768,6 +5830,12 @@ def processData(live=False):
(w[0], w[1])
sysvals.vprint(s)
data.printDetails()
+ if len(sysvals.platinfo) > 0:
+ sysvals.vprint('\nPlatform Info:')
+ for info in sysvals.platinfo:
+ sysvals.vprint(info[0]+' - '+info[1])
+ sysvals.vprint(info[2])
+ sysvals.vprint('')
if sysvals.cgdump:
for data in testruns:
data.debugPrint()
@@ -5951,7 +6019,7 @@ def data_from_html(file, outpath, issues, fulldetail=False):
worst[d] = {'name':'', 'time': 0.0}
dev = devices[d] if d in devices else 0
if dev and len(dev.keys()) > 0:
- n = sorted(dev, key=dev.get, reverse=True)[0]
+ n = sorted(dev, key=lambda k:(dev[k], k), reverse=True)[0]
worst[d]['name'], worst[d]['time'] = n, dev[n]
data = {
'mode': stmp[2],
@@ -5976,7 +6044,7 @@ def data_from_html(file, outpath, issues, fulldetail=False):
data['funclist'] = find_in_html(html, '<div title="', '" class="traceevent"', False)
return data
-def genHtml(subdir):
+def genHtml(subdir, force=False):
for dirname, dirnames, filenames in os.walk(subdir):
sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = ''
for filename in filenames:
@@ -5986,7 +6054,7 @@ def genHtml(subdir):
sysvals.ftracefile = os.path.join(dirname, filename)
sysvals.setOutputFile()
if sysvals.ftracefile and sysvals.htmlfile and \
- not os.path.exists(sysvals.htmlfile):
+ (force or not os.path.exists(sysvals.htmlfile)):
pprint('FTRACE: %s' % sysvals.ftracefile)
if sysvals.dmesgfile:
pprint('DMESG : %s' % sysvals.dmesgfile)
@@ -6042,7 +6110,7 @@ def checkArgBool(name, value):
# Description:
# Configure the script via the info in a config file
def configFromFile(file):
- Config = ConfigParser.ConfigParser()
+ Config = configparser.ConfigParser()
Config.read(file)
sections = Config.sections()
@@ -6270,7 +6338,7 @@ def printHelp():
' default: suspend-{date}-{time}\n'\
' -rtcwake t Wakeup t seconds after suspend, set t to "off" to disable (default: 15)\n'\
' -addlogs Add the dmesg and ftrace logs to the html output\n'\
- ' -turbostat Use turbostat to execute the command in freeze mode (default: disabled)\n'\
+ ' -noturbostat Dont use turbostat in freeze mode (default: disabled)\n'\
' -srgap Add a visible gap in the timeline between sus/res (default: disabled)\n'\
' -skiphtml Run the test and capture the trace logs, but skip the timeline (default: disabled)\n'\
' -result fn Export a results table to a text file for parsing.\n'\
@@ -6340,7 +6408,7 @@ if __name__ == '__main__':
for arg in args:
if(arg == '-m'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No mode supplied', True)
if val == 'command' and not sysvals.testcommand:
@@ -6384,10 +6452,8 @@ if __name__ == '__main__':
sysvals.dmesglog = True
elif(arg == '-addlogftrace'):
sysvals.ftracelog = True
- elif(arg == '-turbostat'):
- sysvals.tstat = True
- if not sysvals.haveTurbostat():
- doError('Turbostat command not found')
+ elif(arg == '-noturbostat'):
+ sysvals.tstat = False
elif(arg == '-verbose'):
sysvals.verbose = True
elif(arg == '-proc'):
@@ -6400,7 +6466,7 @@ if __name__ == '__main__':
sysvals.gzip = True
elif(arg == '-rs'):
try:
- val = args.next()
+ val = next(args)
except:
doError('-rs requires "enable" or "disable"', True)
if val.lower() in switchvalues:
@@ -6412,7 +6478,7 @@ if __name__ == '__main__':
doError('invalid option: %s, use "enable/disable" or "on/off"' % val, True)
elif(arg == '-display'):
try:
- val = args.next()
+ val = next(args)
except:
doError('-display requires an mode value', True)
disopt = ['on', 'off', 'standby', 'suspend']
@@ -6423,7 +6489,7 @@ if __name__ == '__main__':
sysvals.max_graph_depth = getArgInt('-maxdepth', args, 0, 1000)
elif(arg == '-rtcwake'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No rtcwake time supplied', True)
if val.lower() in switchoff:
@@ -6443,7 +6509,7 @@ if __name__ == '__main__':
sysvals.cgtest = getArgInt('-cgtest', args, 0, 1)
elif(arg == '-cgphase'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No phase name supplied', True)
d = Data(0)
@@ -6453,19 +6519,19 @@ if __name__ == '__main__':
sysvals.cgphase = val
elif(arg == '-cgfilter'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No callgraph functions supplied', True)
sysvals.setCallgraphFilter(val)
elif(arg == '-skipkprobe'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No kprobe functions supplied', True)
sysvals.skipKprobes(val)
elif(arg == '-cgskip'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No file supplied', True)
if val.lower() in switchoff:
@@ -6480,7 +6546,7 @@ if __name__ == '__main__':
sysvals.callloopmaxlen = getArgFloat('-callloop-maxlen', args, 0.0, 1.0)
elif(arg == '-cmd'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No command string supplied', True)
sysvals.testcommand = val
@@ -6495,13 +6561,13 @@ if __name__ == '__main__':
sysvals.multitest['delay'] = getArgInt('-multi n d (delay between tests)', args, 0, 3600)
elif(arg == '-o'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No subdirectory name supplied', True)
sysvals.outdir = sysvals.setOutputFolder(val)
elif(arg == '-config'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No text file supplied', True)
file = sysvals.configFile(val)
@@ -6510,7 +6576,7 @@ if __name__ == '__main__':
configFromFile(file)
elif(arg == '-fadd'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No text file supplied', True)
file = sysvals.configFile(val)
@@ -6519,7 +6585,7 @@ if __name__ == '__main__':
sysvals.addFtraceFilterFunctions(file)
elif(arg == '-dmesg'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No dmesg file supplied', True)
sysvals.notestrun = True
@@ -6528,7 +6594,7 @@ if __name__ == '__main__':
doError('%s does not exist' % sysvals.dmesgfile)
elif(arg == '-ftrace'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No ftrace file supplied', True)
sysvals.notestrun = True
@@ -6537,7 +6603,7 @@ if __name__ == '__main__':
doError('%s does not exist' % sysvals.ftracefile)
elif(arg == '-summary'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No directory supplied', True)
cmd = 'summary'
@@ -6547,13 +6613,13 @@ if __name__ == '__main__':
doError('%s is not accesible' % val)
elif(arg == '-filter'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No devnames supplied', True)
sysvals.setDeviceFilter(val)
elif(arg == '-result'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No result file supplied', True)
sysvals.result = val
--
2.17.1
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH] pm-graph v5.5
2019-08-12 21:08 [PATCH] pm-graph v5.5 Todd Brandt
@ 2019-08-26 9:19 ` Rafael J. Wysocki
0 siblings, 0 replies; 2+ messages in thread
From: Rafael J. Wysocki @ 2019-08-26 9:19 UTC (permalink / raw)
To: Todd Brandt; +Cc: linux-pm, rafael.j.wysocki, todd.e.brandt
On Monday, August 12, 2019 11:08:44 PM CEST Todd Brandt wrote:
> Upgrade bootgraph/sleepgraph to be able to run on python2 and python3.
> Both now simply require python, the system can choose which to use.
>
> bootgraph python3 update:
> - add floor function to handle integer arithmetic
> - change argument loop to use next() instead of args.next()
> - open dmesg log and popen in binary, use decode(ascii, ignore)
> - sort all html data to allow diff between python versions
> - change exception handler to use python3 as instead of comma
>
> sleepgraph python3 update:
> - import configparser not ConfigParser (p2 needs python-configparser)
> - add floor function to handle integer arithmetic
> - change argument loop to use next() instead of args.next()
> - handle popen output in binary, use decode(ascii, ignore)
> - sort all html/output data to allow diff between python versions
> - force gzip open to use text mode, same for file open
> - ensure no binary data is written to logs (ascii convert devprops info)
> - use codecs library to handle zlib encoding for mcelog data
> - remove all uses of python3.7 keyword "async" as members or vars
> - assume all FPDT and DMI data is in binary string form
>
> sleepgraph:
> - turbostat will be used by default if it's found & the mode is freeze
> - a new option "-noturbostat" will disable its use
> - fix bug where two callgraphs with the same start time overwrite.
> - fix s2idle processing where two suspend/resume_machines occur back2back
> - update getexec function to use which first (assuming PATH exists)
> - new platforminfo data in log with: lspci, gpe counts, /proc/interrupts
> - new data is zipped, b64 encoded, and tacked on the end of ftrace
>
> Signed-off-by: Todd Brandt <todd.e.brandt@linux.intel.com>
> ---
> tools/power/pm-graph/README | 6 +-
> tools/power/pm-graph/bootgraph.py | 59 ++-
> tools/power/pm-graph/sleepgraph.8 | 8 +-
> tools/power/pm-graph/sleepgraph.py | 610 ++++++++++++++++-------------
> 4 files changed, 387 insertions(+), 296 deletions(-)
>
> diff --git a/tools/power/pm-graph/README b/tools/power/pm-graph/README
> index 58a5591e3951..96259f6e5715 100644
> --- a/tools/power/pm-graph/README
> +++ b/tools/power/pm-graph/README
> @@ -1,7 +1,7 @@
> p m - g r a p h
>
> pm-graph: suspend/resume/boot timing analysis tools
> - Version: 5.4
> + Version: 5.5
> Author: Todd Brandt <todd.e.brandt@intel.com>
> Home Page: https://01.org/pm-graph
>
> @@ -18,6 +18,10 @@
> - upstream version in git:
> https://github.com/intel/pm-graph/
>
> + Requirements:
> + - runs with python2 or python3, choice is made by /usr/bin/python link
> + - python2 now requires python-configparser be installed
> +
> Table of Contents
> - Overview
> - Setup
> diff --git a/tools/power/pm-graph/bootgraph.py b/tools/power/pm-graph/bootgraph.py
> index 666bcbda648d..d3b99a1e92d6 100755
> --- a/tools/power/pm-graph/bootgraph.py
> +++ b/tools/power/pm-graph/bootgraph.py
> @@ -1,9 +1,18 @@
> -#!/usr/bin/python2
> +#!/usr/bin/python
> # SPDX-License-Identifier: GPL-2.0-only
> #
> # Tool for analyzing boot timing
> # Copyright (c) 2013, Intel Corporation.
> #
> +# This program is free software; you can redistribute it and/or modify it
> +# under the terms and conditions of the GNU General Public License,
> +# version 2, as published by the Free Software Foundation.
> +#
> +# This program is distributed in the hope it will be useful, but WITHOUT
> +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> +# more details.
> +#
> # Authors:
> # Todd Brandt <todd.e.brandt@linux.intel.com>
> #
> @@ -81,7 +90,7 @@ class SystemValues(aslib.SystemValues):
> cmdline = 'initcall_debug log_buf_len=32M'
> if self.useftrace:
> if self.cpucount > 0:
> - bs = min(self.memtotal / 2, 2*1024*1024) / self.cpucount
> + bs = min(self.memtotal // 2, 2*1024*1024) // self.cpucount
> else:
> bs = 131072
> cmdline += ' trace_buf_size=%dK trace_clock=global '\
> @@ -137,13 +146,13 @@ class SystemValues(aslib.SystemValues):
> if arg in ['-h', '-v', '-cronjob', '-reboot', '-verbose']:
> continue
> elif arg in ['-o', '-dmesg', '-ftrace', '-func']:
> - args.next()
> + next(args)
> continue
> elif arg == '-result':
> - cmdline += ' %s "%s"' % (arg, os.path.abspath(args.next()))
> + cmdline += ' %s "%s"' % (arg, os.path.abspath(next(args)))
> continue
> elif arg == '-cgskip':
> - file = self.configFile(args.next())
> + file = self.configFile(next(args))
> cmdline += ' %s "%s"' % (arg, os.path.abspath(file))
> continue
> cmdline += ' '+arg
> @@ -292,11 +301,11 @@ def parseKernelLog():
> tp = aslib.TestProps()
> devtemp = dict()
> if(sysvals.dmesgfile):
> - lf = open(sysvals.dmesgfile, 'r')
> + lf = open(sysvals.dmesgfile, 'rb')
> else:
> lf = Popen('dmesg', stdout=PIPE).stdout
> for line in lf:
> - line = line.replace('\r\n', '')
> + line = aslib.ascii(line).replace('\r\n', '')
> # grab the stamp and sysinfo
> if re.match(tp.stampfmt, line):
> tp.stamp = line
> @@ -649,7 +658,7 @@ def createBootGraph(data):
> statinfo += '\t"%s": [\n\t\t"%s",\n' % (n, devstats[n]['info'])
> if 'fstat' in devstats[n]:
> funcs = devstats[n]['fstat']
> - for f in sorted(funcs, key=funcs.get, reverse=True):
> + for f in sorted(funcs, key=lambda k:(funcs[k], k), reverse=True):
> if funcs[f][0] < 0.01 and len(funcs) > 10:
> break
> statinfo += '\t\t"%f|%s|%d",\n' % (funcs[f][0], f, funcs[f][1])
> @@ -729,7 +738,7 @@ def updateCron(restore=False):
> op.write('@reboot python %s\n' % sysvals.cronjobCmdString())
> op.close()
> res = call([cmd, cronfile])
> - except Exception, e:
> + except Exception as e:
> pprint('Exception: %s' % str(e))
> shutil.move(backfile, cronfile)
> res = -1
> @@ -745,7 +754,7 @@ def updateGrub(restore=False):
> try:
> call(sysvals.blexec, stderr=PIPE, stdout=PIPE,
> env={'PATH': '.:/sbin:/usr/sbin:/usr/bin:/sbin:/bin'})
> - except Exception, e:
> + except Exception as e:
> pprint('Exception: %s\n' % str(e))
> return
> # extract the option and create a grub config without it
> @@ -792,7 +801,7 @@ def updateGrub(restore=False):
> op.close()
> res = call(sysvals.blexec)
> os.remove(grubfile)
> - except Exception, e:
> + except Exception as e:
> pprint('Exception: %s' % str(e))
> res = -1
> # cleanup
> @@ -866,6 +875,7 @@ def printHelp():
> 'Other commands:\n'\
> ' -flistall Print all functions capable of being captured in ftrace\n'\
> ' -sysinfo Print out system info extracted from BIOS\n'\
> + ' -which exec Print an executable path, should function even without PATH\n'\
> ' [redo]\n'\
> ' -dmesg file Create HTML output using dmesg input (used with -ftrace)\n'\
> ' -ftrace file Create HTML output using ftrace input (used with -dmesg)\n'\
> @@ -907,13 +917,13 @@ if __name__ == '__main__':
> sysvals.mincglen = aslib.getArgFloat('-mincg', args, 0.0, 10000.0)
> elif(arg == '-cgfilter'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No callgraph functions supplied', True)
> sysvals.setCallgraphFilter(val)
> elif(arg == '-cgskip'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No file supplied', True)
> if val.lower() in switchoff:
> @@ -924,7 +934,7 @@ if __name__ == '__main__':
> doError('%s does not exist' % cgskip)
> elif(arg == '-bl'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No boot loader name supplied', True)
> if val.lower() not in ['grub']:
> @@ -937,7 +947,7 @@ if __name__ == '__main__':
> sysvals.max_graph_depth = aslib.getArgInt('-maxdepth', args, 0, 1000)
> elif(arg == '-func'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No filter functions supplied', True)
> sysvals.useftrace = True
> @@ -946,7 +956,7 @@ if __name__ == '__main__':
> sysvals.setGraphFilter(val)
> elif(arg == '-ftrace'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No ftrace file supplied', True)
> if(os.path.exists(val) == False):
> @@ -959,7 +969,7 @@ if __name__ == '__main__':
> sysvals.cgexp = True
> elif(arg == '-dmesg'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No dmesg file supplied', True)
> if(os.path.exists(val) == False):
> @@ -968,13 +978,13 @@ if __name__ == '__main__':
> sysvals.dmesgfile = val
> elif(arg == '-o'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No subdirectory name supplied', True)
> sysvals.testdir = sysvals.setOutputFolder(val)
> elif(arg == '-result'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No result file supplied', True)
> sysvals.result = val
> @@ -986,6 +996,17 @@ if __name__ == '__main__':
> # remaining options are only for cron job use
> elif(arg == '-cronjob'):
> sysvals.iscronjob = True
> + elif(arg == '-which'):
> + try:
> + val = next(args)
> + except:
> + doError('No executable supplied', True)
> + out = sysvals.getExec(val)
> + if not out:
> + print('%s not found' % val)
> + sys.exit(1)
> + print(out)
> + sys.exit(0)
> else:
> doError('Invalid argument: '+arg, True)
>
> diff --git a/tools/power/pm-graph/sleepgraph.8 b/tools/power/pm-graph/sleepgraph.8
> index 9648be644d5f..43aee64316df 100644
> --- a/tools/power/pm-graph/sleepgraph.8
> +++ b/tools/power/pm-graph/sleepgraph.8
> @@ -53,10 +53,10 @@ disable rtcwake and require a user keypress to resume.
> Add the dmesg and ftrace logs to the html output. They will be viewable by
> clicking buttons in the timeline.
> .TP
> -\fB-turbostat\fR
> -Use turbostat to execute the command in freeze mode (default: disabled). This
> -will provide turbostat output in the log which will tell you which actual
> -power modes were entered.
> +\fB-noturbostat\fR
> +By default, if turbostat is found and the requested mode is freeze, sleepgraph
> +will execute the suspend via turbostat and collect data in the timeline log.
> +This option disables the use of turbostat.
> .TP
> \fB-result \fIfile\fR
> Export a results table to a text file for parsing.
> diff --git a/tools/power/pm-graph/sleepgraph.py b/tools/power/pm-graph/sleepgraph.py
> index 4f46a7a1feb6..1794c79a7d1b 100755
> --- a/tools/power/pm-graph/sleepgraph.py
> +++ b/tools/power/pm-graph/sleepgraph.py
> @@ -1,9 +1,18 @@
> -#!/usr/bin/python2
> +#!/usr/bin/python
> # SPDX-License-Identifier: GPL-2.0-only
> #
> # Tool for analyzing suspend/resume timing
> # Copyright (c) 2013, Intel Corporation.
> #
> +# This program is free software; you can redistribute it and/or modify it
> +# under the terms and conditions of the GNU General Public License,
> +# version 2, as published by the Free Software Foundation.
> +#
> +# This program is distributed in the hope it will be useful, but WITHOUT
> +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> +# more details.
> +#
> # Authors:
> # Todd Brandt <todd.e.brandt@linux.intel.com>
> #
> @@ -48,9 +57,10 @@ import string
> import re
> import platform
> import signal
> +import codecs
> from datetime import datetime
> import struct
> -import ConfigParser
> +import configparser
> import gzip
> from threading import Thread
> from subprocess import call, Popen, PIPE
> @@ -60,6 +70,9 @@ def pprint(msg):
> print(msg)
> sys.stdout.flush()
>
> +def ascii(text):
> + return text.decode('ascii', 'ignore')
> +
> # ----------------- CLASSES --------------------
>
> # Class: SystemValues
> @@ -68,7 +81,7 @@ def pprint(msg):
> # store system values and test parameters
> class SystemValues:
> title = 'SleepGraph'
> - version = '5.4'
> + version = '5.5'
> ansi = False
> rs = 0
> display = ''
> @@ -78,7 +91,7 @@ class SystemValues:
> testlog = True
> dmesglog = True
> ftracelog = False
> - tstat = False
> + tstat = True
> mindevlen = 0.0
> mincglen = 0.0
> cgphase = ''
> @@ -147,6 +160,7 @@ class SystemValues:
> devdump = False
> mixedphaseheight = True
> devprops = dict()
> + platinfo = []
> predelay = 0
> postdelay = 0
> pmdebug = ''
> @@ -323,13 +337,20 @@ class SystemValues:
> sys.exit(1)
> return False
> def getExec(self, cmd):
> - dirlist = ['/sbin', '/bin', '/usr/sbin', '/usr/bin',
> - '/usr/local/sbin', '/usr/local/bin']
> - for path in dirlist:
> + try:
> + fp = Popen(['which', cmd], stdout=PIPE, stderr=PIPE).stdout
> + out = ascii(fp.read()).strip()
> + fp.close()
> + except:
> + out = ''
> + if out:
> + return out
> + for path in ['/sbin', '/bin', '/usr/sbin', '/usr/bin',
> + '/usr/local/sbin', '/usr/local/bin']:
> cmdfull = os.path.join(path, cmd)
> if os.path.exists(cmdfull):
> return cmdfull
> - return ''
> + return out
> def setPrecision(self, num):
> if num < 0 or num > 6:
> return
> @@ -455,7 +476,7 @@ class SystemValues:
> fp = Popen('dmesg', stdout=PIPE).stdout
> ktime = '0'
> for line in fp:
> - line = line.replace('\r\n', '')
> + line = ascii(line).replace('\r\n', '')
> idx = line.find('[')
> if idx > 1:
> line = line[idx:]
> @@ -469,7 +490,7 @@ class SystemValues:
> # store all new dmesg lines since initdmesg was called
> fp = Popen('dmesg', stdout=PIPE).stdout
> for line in fp:
> - line = line.replace('\r\n', '')
> + line = ascii(line).replace('\r\n', '')
> idx = line.find('[')
> if idx > 1:
> line = line[idx:]
> @@ -501,7 +522,7 @@ class SystemValues:
> call('cat '+self.tpath+'available_filter_functions', shell=True)
> return
> master = self.listFromFile(self.tpath+'available_filter_functions')
> - for i in self.tracefuncs:
> + for i in sorted(self.tracefuncs):
> if 'func' in self.tracefuncs[i]:
> i = self.tracefuncs[i]['func']
> if i in master:
> @@ -628,7 +649,7 @@ class SystemValues:
> self.fsetVal(kprobeevents, 'kprobe_events')
> if output:
> check = self.fgetVal('kprobe_events')
> - linesack = (len(check.split('\n')) - 1) / 2
> + linesack = (len(check.split('\n')) - 1) // 2
> pprint(' kprobe functions enabled: %d/%d' % (linesack, linesout))
> self.fsetVal('1', 'events/kprobes/enable')
> def testKprobe(self, kname, kprobe):
> @@ -650,7 +671,7 @@ class SystemValues:
> if not os.path.exists(file):
> return False
> try:
> - fp = open(file, mode, 0)
> + fp = open(file, mode)
> fp.write(val)
> fp.flush()
> fp.close()
> @@ -719,7 +740,7 @@ class SystemValues:
> tgtsize = min(self.memfree, bmax)
> else:
> tgtsize = 65536
> - while not self.fsetVal('%d' % (tgtsize / cpus), 'buffer_size_kb'):
> + while not self.fsetVal('%d' % (tgtsize // cpus), 'buffer_size_kb'):
> # if the size failed to set, lower it and keep trying
> tgtsize -= 65536
> if tgtsize < 65536:
> @@ -863,14 +884,23 @@ class SystemValues:
> isgz = self.gzip
> if mode == 'r':
> try:
> - with gzip.open(filename, mode+'b') as fp:
> + with gzip.open(filename, mode+'t') as fp:
> test = fp.read(64)
> isgz = True
> except:
> isgz = False
> if isgz:
> - return gzip.open(filename, mode+'b')
> + return gzip.open(filename, mode+'t')
> return open(filename, mode)
> + def b64unzip(self, data):
> + try:
> + out = codecs.decode(base64.b64decode(data), 'zlib').decode()
> + except:
> + out = data
> + return out
> + def b64zip(self, data):
> + out = base64.b64encode(codecs.encode(data.encode(), 'zlib')).decode()
> + return out
> def mcelog(self, clear=False):
> cmd = self.getExec('mcelog')
> if not cmd:
> @@ -878,12 +908,124 @@ class SystemValues:
> if clear:
> call(cmd+' > /dev/null 2>&1', shell=True)
> return ''
> - fp = Popen([cmd], stdout=PIPE, stderr=PIPE).stdout
> - out = fp.read().strip()
> - fp.close()
> + try:
> + fp = Popen([cmd], stdout=PIPE, stderr=PIPE).stdout
> + out = ascii(fp.read()).strip()
> + fp.close()
> + except:
> + return ''
> if not out:
> return ''
> - return base64.b64encode(out.encode('zlib'))
> + return self.b64zip(out)
> + def platforminfo(self):
> + # add platform info on to a completed ftrace file
> + if not os.path.exists(self.ftracefile):
> + return False
> + footer = '#\n'
> +
> + # add test command string line if need be
> + if self.suspendmode == 'command' and self.testcommand:
> + footer += '# platform-testcmd: %s\n' % (self.testcommand)
> +
> + # get a list of target devices from the ftrace file
> + props = dict()
> + tp = TestProps()
> + tf = self.openlog(self.ftracefile, 'r')
> + for line in tf:
> + # determine the trace data type (required for further parsing)
> + m = re.match(tp.tracertypefmt, line)
> + if(m):
> + tp.setTracerType(m.group('t'))
> + continue
> + # parse only valid lines, if this is not one move on
> + m = re.match(tp.ftrace_line_fmt, line)
> + if(not m or 'device_pm_callback_start' not in line):
> + continue
> + m = re.match('.*: (?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*', m.group('msg'));
> + if(not m):
> + continue
> + dev = m.group('d')
> + if dev not in props:
> + props[dev] = DevProps()
> + tf.close()
> +
> + # now get the syspath for each target device
> + for dirname, dirnames, filenames in os.walk('/sys/devices'):
> + if(re.match('.*/power', dirname) and 'async' in filenames):
> + dev = dirname.split('/')[-2]
> + if dev in props and (not props[dev].syspath or len(dirname) < len(props[dev].syspath)):
> + props[dev].syspath = dirname[:-6]
> +
> + # now fill in the properties for our target devices
> + for dev in sorted(props):
> + dirname = props[dev].syspath
> + if not dirname or not os.path.exists(dirname):
> + continue
> + with open(dirname+'/power/async') as fp:
> + text = fp.read()
> + props[dev].isasync = False
> + if 'enabled' in text:
> + props[dev].isasync = True
> + fields = os.listdir(dirname)
> + if 'product' in fields:
> + with open(dirname+'/product', 'rb') as fp:
> + props[dev].altname = ascii(fp.read())
> + elif 'name' in fields:
> + with open(dirname+'/name', 'rb') as fp:
> + props[dev].altname = ascii(fp.read())
> + elif 'model' in fields:
> + with open(dirname+'/model', 'rb') as fp:
> + props[dev].altname = ascii(fp.read())
> + elif 'description' in fields:
> + with open(dirname+'/description', 'rb') as fp:
> + props[dev].altname = ascii(fp.read())
> + elif 'id' in fields:
> + with open(dirname+'/id', 'rb') as fp:
> + props[dev].altname = ascii(fp.read())
> + elif 'idVendor' in fields and 'idProduct' in fields:
> + idv, idp = '', ''
> + with open(dirname+'/idVendor', 'rb') as fp:
> + idv = ascii(fp.read()).strip()
> + with open(dirname+'/idProduct', 'rb') as fp:
> + idp = ascii(fp.read()).strip()
> + props[dev].altname = '%s:%s' % (idv, idp)
> + if props[dev].altname:
> + out = props[dev].altname.strip().replace('\n', ' ')\
> + .replace(',', ' ').replace(';', ' ')
> + props[dev].altname = out
> +
> + # add a devinfo line to the bottom of ftrace
> + out = ''
> + for dev in sorted(props):
> + out += props[dev].out(dev)
> + footer += '# platform-devinfo: %s\n' % self.b64zip(out)
> +
> + # add a line for each of these commands with their outputs
> + cmds = [
> + ['pcidevices', 'lspci', '-tv'],
> + ['interrupts', 'cat', '/proc/interrupts'],
> + ['gpecounts', 'sh', '-c', 'grep -v invalid /sys/firmware/acpi/interrupts/gpe*'],
> + ]
> + for cargs in cmds:
> + name = cargs[0]
> + cmdline = ' '.join(cargs[1:])
> + cmdpath = self.getExec(cargs[1])
> + if not cmdpath:
> + continue
> + cmd = [cmdpath] + cargs[2:]
> + try:
> + fp = Popen(cmd, stdout=PIPE, stderr=PIPE).stdout
> + info = ascii(fp.read()).strip()
> + fp.close()
> + except:
> + continue
> + if not info:
> + continue
> + footer += '# platform-%s: %s | %s\n' % (name, cmdline, self.b64zip(info))
> +
> + with self.openlog(self.ftracefile, 'a') as fp:
> + fp.write(footer)
> + return True
> def haveTurbostat(self):
> if not self.tstat:
> return False
> @@ -891,31 +1033,40 @@ class SystemValues:
> if not cmd:
> return False
> fp = Popen([cmd, '-v'], stdout=PIPE, stderr=PIPE).stderr
> - out = fp.read().strip()
> + out = ascii(fp.read()).strip()
> fp.close()
> - return re.match('turbostat version [0-9\.]* .*', out)
> + if re.match('turbostat version [0-9\.]* .*', out):
> + sysvals.vprint(out)
> + return True
> + return False
> def turbostat(self):
> cmd = self.getExec('turbostat')
> - if not cmd:
> - return 'missing turbostat executable'
> - text = []
> + rawout = keyline = valline = ''
> fullcmd = '%s -q -S echo freeze > %s' % (cmd, self.powerfile)
> fp = Popen(['sh', '-c', fullcmd], stdout=PIPE, stderr=PIPE).stderr
> for line in fp:
> - if re.match('[0-9.]* sec', line):
> + line = ascii(line)
> + rawout += line
> + if keyline and valline:
> continue
> - text.append(line.split())
> + if re.match('(?i)Avg_MHz.*', line):
> + keyline = line.strip().split()
> + elif keyline:
> + valline = line.strip().split()
> fp.close()
> - if len(text) < 2:
> - return 'turbostat output format error'
> + if not keyline or not valline or len(keyline) != len(valline):
> + errmsg = 'unrecognized turbostat output:\n'+rawout.strip()
> + sysvals.vprint(errmsg)
> + if not sysvals.verbose:
> + pprint(errmsg)
> + return ''
> + if sysvals.verbose:
> + pprint(rawout.strip())
> out = []
> - for key in text[0]:
> - values = []
> - idx = text[0].index(key)
> - for line in text[1:]:
> - if len(line) > idx:
> - values.append(line[idx])
> - out.append('%s=%s' % (key, ','.join(values)))
> + for key in keyline:
> + idx = keyline.index(key)
> + val = valline[idx]
> + out.append('%s=%s' % (key, val))
> return '|'.join(out)
> def checkWifi(self):
> out = dict()
> @@ -924,7 +1075,7 @@ class SystemValues:
> return out
> fp = Popen(iwcmd, stdout=PIPE, stderr=PIPE).stdout
> for line in fp:
> - m = re.match('(?P<dev>\S*) .* ESSID:(?P<ess>\S*)', line)
> + m = re.match('(?P<dev>\S*) .* ESSID:(?P<ess>\S*)', ascii(line))
> if not m:
> continue
> out['device'] = m.group('dev')
> @@ -935,7 +1086,7 @@ class SystemValues:
> if 'device' in out:
> fp = Popen([ifcmd, out['device']], stdout=PIPE, stderr=PIPE).stdout
> for line in fp:
> - m = re.match('.* inet (?P<ip>[0-9\.]*)', line)
> + m = re.match('.* inet (?P<ip>[0-9\.]*)', ascii(line))
> if m:
> out['ip'] = m.group('ip')
> break
> @@ -990,13 +1141,13 @@ class DevProps:
> def __init__(self):
> self.syspath = ''
> self.altname = ''
> - self.async = True
> + self.isasync = True
> self.xtraclass = ''
> self.xtrainfo = ''
> def out(self, dev):
> - return '%s,%s,%d;' % (dev, self.altname, self.async)
> + return '%s,%s,%d;' % (dev, self.altname, self.isasync)
> def debug(self, dev):
> - pprint('%s:\n\taltname = %s\n\t async = %s' % (dev, self.altname, self.async))
> + pprint('%s:\n\taltname = %s\n\t async = %s' % (dev, self.altname, self.isasync))
> def altName(self, dev):
> if not self.altname or self.altname == dev:
> return dev
> @@ -1004,13 +1155,13 @@ class DevProps:
> def xtraClass(self):
> if self.xtraclass:
> return ' '+self.xtraclass
> - if not self.async:
> + if not self.isasync:
> return ' sync'
> return ''
> def xtraInfo(self):
> if self.xtraclass:
> return ' '+self.xtraclass
> - if self.async:
> + if self.isasync:
> return ' async_device'
> return ' sync_device'
>
> @@ -1108,7 +1259,7 @@ class Data:
> return sorted(self.dmesg, key=lambda k:self.dmesg[k]['order'])
> def initDevicegroups(self):
> # called when phases are all finished being added
> - for phase in self.dmesg.keys():
> + for phase in sorted(self.dmesg.keys()):
> if '*' in phase:
> p = phase.split('*')
> pnew = '%s%d' % (p[0], len(p))
> @@ -1430,16 +1581,7 @@ class Data:
> return phase
> def sortedDevices(self, phase):
> list = self.dmesg[phase]['list']
> - slist = []
> - tmp = dict()
> - for devname in list:
> - dev = list[devname]
> - if dev['length'] == 0:
> - continue
> - tmp[dev['start']] = devname
> - for t in sorted(tmp):
> - slist.append(tmp[t])
> - return slist
> + return sorted(list, key=lambda k:list[k]['start'])
> def fixupInitcalls(self, phase):
> # if any calls never returned, clip them at system resume end
> phaselist = self.dmesg[phase]['list']
> @@ -1576,7 +1718,7 @@ class Data:
> maxname = '%d' % self.maxDeviceNameSize(phase)
> fmt = '%3d) %'+maxname+'s - %f - %f'
> c = 1
> - for name in devlist:
> + for name in sorted(devlist):
> s = devlist[name]['start']
> e = devlist[name]['end']
> sysvals.vprint(fmt % (c, name, s, e))
> @@ -1588,7 +1730,7 @@ class Data:
> devlist = []
> for phase in self.sortedPhases():
> list = self.deviceChildren(devname, phase)
> - for dev in list:
> + for dev in sorted(list):
> if dev not in devlist:
> devlist.append(dev)
> return devlist
> @@ -1628,16 +1770,16 @@ class Data:
> def rootDeviceList(self):
> # list of devices graphed
> real = []
> - for phase in self.dmesg:
> + for phase in self.sortedPhases():
> list = self.dmesg[phase]['list']
> - for dev in list:
> + for dev in sorted(list):
> if list[dev]['pid'] >= 0 and dev not in real:
> real.append(dev)
> # list of top-most root devices
> rootlist = []
> - for phase in self.dmesg:
> + for phase in self.sortedPhases():
> list = self.dmesg[phase]['list']
> - for dev in list:
> + for dev in sorted(list):
> pdev = list[dev]['par']
> pid = list[dev]['pid']
> if(pid < 0 or re.match('[0-9]*-[0-9]*\.[0-9]*[\.0-9]*\:[\.0-9]*$', pdev)):
> @@ -1718,9 +1860,9 @@ class Data:
> def createProcessUsageEvents(self):
> # get an array of process names
> proclist = []
> - for t in self.pstl:
> + for t in sorted(self.pstl):
> pslist = self.pstl[t]
> - for ps in pslist:
> + for ps in sorted(pslist):
> if ps not in proclist:
> proclist.append(ps)
> # get a list of data points for suspend and resume
> @@ -1765,7 +1907,7 @@ class Data:
> def debugPrint(self):
> for p in self.sortedPhases():
> list = self.dmesg[p]['list']
> - for devname in list:
> + for devname in sorted(list):
> dev = list[devname]
> if 'ftrace' in dev:
> dev['ftrace'].debugPrint(' [%s]' % devname)
> @@ -2466,7 +2608,7 @@ class Timeline:
> # if there is 1 line per row, draw them the standard way
> for t, p in standardphases:
> for i in sorted(self.rowheight[t][p]):
> - self.rowheight[t][p][i] = self.bodyH/len(self.rowlines[t][p])
> + self.rowheight[t][p][i] = float(self.bodyH)/len(self.rowlines[t][p])
> def createZoomBox(self, mode='command', testcount=1):
> # Create bounding box, add buttons
> html_zoombox = '<center><button id="zoomin">ZOOM IN +</button><button id="zoomout">ZOOM OUT -</button><button id="zoomdef">ZOOM 1:1</button></center>\n'
> @@ -2537,6 +2679,7 @@ class TestProps:
> cmdlinefmt = '^# command \| (?P<cmd>.*)'
> kparamsfmt = '^# kparams \| (?P<kp>.*)'
> devpropfmt = '# Device Properties: .*'
> + pinfofmt = '# platform-(?P<val>[a-z,A-Z,0-9]*): (?P<info>.*)'
> tracertypefmt = '# tracer: (?P<t>.*)'
> firmwarefmt = '# fwsuspend (?P<s>[0-9]*) fwresume (?P<r>[0-9]*)$'
> procexecfmt = 'ps - (?P<ps>.*)$'
> @@ -2571,12 +2714,6 @@ class TestProps:
> self.ftrace_line_fmt = self.ftrace_line_fmt_nop
> else:
> doError('Invalid tracer format: [%s]' % tracer)
> - def decode(self, data):
> - try:
> - out = base64.b64decode(data).decode('zlib')
> - except:
> - out = data
> - return out
> def stampInfo(self, line):
> if re.match(self.stampfmt, line):
> self.stamp = line
> @@ -2660,7 +2797,7 @@ class TestProps:
> if len(self.mcelog) > data.testnumber:
> m = re.match(self.mcelogfmt, self.mcelog[data.testnumber])
> if m:
> - data.mcelog = self.decode(m.group('m'))
> + data.mcelog = sv.b64unzip(m.group('m'))
> # turbostat data
> if len(self.turbostat) > data.testnumber:
> m = re.match(self.tstatfmt, self.turbostat[data.testnumber])
> @@ -2681,6 +2818,46 @@ class TestProps:
> m = re.match(self.testerrfmt, self.testerror[data.testnumber])
> if m:
> data.enterfail = m.group('e')
> + def devprops(self, data):
> + props = dict()
> + devlist = data.split(';')
> + for dev in devlist:
> + f = dev.split(',')
> + if len(f) < 3:
> + continue
> + dev = f[0]
> + props[dev] = DevProps()
> + props[dev].altname = f[1]
> + if int(f[2]):
> + props[dev].isasync = True
> + else:
> + props[dev].isasync = False
> + return props
> + def parseDevprops(self, line, sv):
> + idx = line.index(': ') + 2
> + if idx >= len(line):
> + return
> + props = self.devprops(line[idx:])
> + if sv.suspendmode == 'command' and 'testcommandstring' in props:
> + sv.testcommand = props['testcommandstring'].altname
> + sv.devprops = props
> + def parsePlatformInfo(self, line, sv):
> + m = re.match(self.pinfofmt, line)
> + if not m:
> + return
> + name, info = m.group('val'), m.group('info')
> + if name == 'devinfo':
> + sv.devprops = self.devprops(sv.b64unzip(info))
> + return
> + elif name == 'testcmd':
> + sv.testcommand = info
> + return
> + field = info.split('|')
> + if len(field) < 2:
> + return
> + cmdline = field[0].strip()
> + output = sv.b64unzip(field[1].strip())
> + sv.platinfo.append([name, cmdline, output])
>
> # Class: TestRun
> # Description:
> @@ -2701,7 +2878,7 @@ class ProcessMonitor:
> process = Popen(c, shell=True, stdout=PIPE)
> running = dict()
> for line in process.stdout:
> - data = line.split()
> + data = ascii(line).split()
> pid = data[0]
> name = re.sub('[()]', '', data[1])
> user = int(data[13])
> @@ -2805,7 +2982,11 @@ def appendIncompleteTraceLog(testruns):
> continue
> # device properties line
> if(re.match(tp.devpropfmt, line)):
> - devProps(line)
> + tp.parseDevprops(line, sysvals)
> + continue
> + # platform info line
> + if(re.match(tp.pinfofmt, line)):
> + tp.parsePlatformInfo(line, sysvals)
> continue
> # parse only valid lines, if this is not one move on
> m = re.match(tp.ftrace_line_fmt, line)
> @@ -2902,7 +3083,7 @@ def parseTraceLog(live=False):
> sysvals.setupAllKprobes()
> ksuscalls = ['pm_prepare_console']
> krescalls = ['pm_restore_console']
> - tracewatch = []
> + tracewatch = ['irq_wakeup']
> if sysvals.usekprobes:
> tracewatch += ['sync_filesystems', 'freeze_processes', 'syscore_suspend',
> 'syscore_resume', 'resume_console', 'thaw_processes', 'CPU_ON',
> @@ -2928,7 +3109,11 @@ def parseTraceLog(live=False):
> continue
> # device properties line
> if(re.match(tp.devpropfmt, line)):
> - devProps(line)
> + tp.parseDevprops(line, sysvals)
> + continue
> + # platform info line
> + if(re.match(tp.pinfofmt, line)):
> + tp.parsePlatformInfo(line, sysvals)
> continue
> # ignore all other commented lines
> if line[0] == '#':
> @@ -3001,16 +3186,11 @@ def parseTraceLog(live=False):
> isbegin = False
> else:
> continue
> - m = re.match('(?P<name>.*)\[(?P<val>[0-9]*)\] .*', t.name)
> - if(m):
> - val = m.group('val')
> - if val == '0':
> - name = m.group('name')
> - else:
> - name = m.group('name')+'['+val+']'
> + if '[' in t.name:
> + m = re.match('(?P<name>.*)\[.*', t.name)
> else:
> m = re.match('(?P<name>.*) .*', t.name)
> - name = m.group('name')
> + name = m.group('name')
> # ignore these events
> if(name.split('[')[0] in tracewatch):
> continue
> @@ -3045,6 +3225,8 @@ def parseTraceLog(live=False):
> elif(re.match('machine_suspend\[.*', t.name)):
> if(isbegin):
> lp = data.lastPhase()
> + if lp == 'resume_machine':
> + data.dmesg[lp]['end'] = t.time
> phase = data.setPhase('suspend_machine', data.dmesg[lp]['end'], True)
> data.setPhase(phase, t.time, False)
> if data.tSuspended == 0:
> @@ -3213,11 +3395,11 @@ def parseTraceLog(live=False):
> # add the traceevent data to the device hierarchy
> if(sysvals.usetraceevents):
> # add actual trace funcs
> - for name in test.ttemp:
> + for name in sorted(test.ttemp):
> for event in test.ttemp[name]:
> data.newActionGlobal(name, event['begin'], event['end'], event['pid'])
> # add the kprobe based virtual tracefuncs as actual devices
> - for key in tp.ktemp:
> + for key in sorted(tp.ktemp):
> name, pid = key
> if name not in sysvals.tracefuncs:
> continue
> @@ -3231,7 +3413,7 @@ def parseTraceLog(live=False):
> data.newActionGlobal(e['name'], kb, ke, pid, color)
> # add config base kprobes and dev kprobes
> if sysvals.usedevsrc:
> - for key in tp.ktemp:
> + for key in sorted(tp.ktemp):
> name, pid = key
> if name in sysvals.tracefuncs or name not in sysvals.dev_tracefuncs:
> continue
> @@ -3244,7 +3426,7 @@ def parseTraceLog(live=False):
> if sysvals.usecallgraph:
> # add the callgraph data to the device hierarchy
> sortlist = dict()
> - for key in test.ftemp:
> + for key in sorted(test.ftemp):
> proc, pid = key
> for cg in test.ftemp[key]:
> if len(cg.list) < 1 or cg.invalid or (cg.end - cg.start == 0):
> @@ -3582,7 +3764,7 @@ def parseKernelLog(data):
> # if trace events are not available, these are better than nothing
> if(not sysvals.usetraceevents):
> # look for known actions
> - for a in at:
> + for a in sorted(at):
> if(re.match(at[a]['smsg'], msg)):
> if(a not in actions):
> actions[a] = []
> @@ -3641,7 +3823,7 @@ def parseKernelLog(data):
> data.tResumed = data.tSuspended
>
> # fill in any actions we've found
> - for name in actions:
> + for name in sorted(actions):
> for event in actions[name]:
> data.newActionGlobal(name, event['begin'], event['end'])
>
> @@ -3761,7 +3943,7 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
> if lastmode and lastmode != mode and num > 0:
> for i in range(2):
> s = sorted(tMed[i])
> - list[lastmode]['med'][i] = s[int(len(s)/2)]
> + list[lastmode]['med'][i] = s[int(len(s)//2)]
> iMed[i] = tMed[i][list[lastmode]['med'][i]]
> list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num]
> list[lastmode]['min'] = tMin
> @@ -3803,7 +3985,7 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
> if lastmode and num > 0:
> for i in range(2):
> s = sorted(tMed[i])
> - list[lastmode]['med'][i] = s[int(len(s)/2)]
> + list[lastmode]['med'][i] = s[int(len(s)//2)]
> iMed[i] = tMed[i][list[lastmode]['med'][i]]
> list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num]
> list[lastmode]['min'] = tMin
> @@ -3845,7 +4027,7 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
> '</tr>\n'
> headnone = '<tr class="head"><td>{0}</td><td>{1}</td><td colspan='+\
> colspan+'></td></tr>\n'
> - for mode in list:
> + for mode in sorted(list):
> # header line for each suspend mode
> num = 0
> tAvg, tMin, tMax, tMed = list[mode]['avg'], list[mode]['min'],\
> @@ -3944,7 +4126,8 @@ def createHTMLDeviceSummary(testruns, htmlfile, title):
> th.format('Average Time') + th.format('Count') +\
> th.format('Worst Time') + th.format('Host (worst time)') +\
> th.format('Link (worst time)') + '</tr>\n'
> - for name in sorted(devlist, key=lambda k:devlist[k]['worst'], reverse=True):
> + for name in sorted(devlist, key=lambda k:(devlist[k]['worst'], \
> + devlist[k]['total'], devlist[k]['name']), reverse=True):
> data = devall[type][name]
> data['average'] = data['total'] / data['count']
> if data['average'] < limit:
> @@ -4085,7 +4268,7 @@ def createHTML(testruns, testfail):
> if(tTotal == 0):
> doError('No timeline data')
> if(len(data.tLow) > 0):
> - low_time = '|'.join(data.tLow)
> + low_time = '+'.join(data.tLow)
> if sysvals.suspendmode == 'command':
> run_time = '%.0f'%((data.end-data.start)*1000)
> if sysvals.testcommand:
> @@ -4151,7 +4334,7 @@ def createHTML(testruns, testfail):
> for group in data.devicegroups:
> devlist = []
> for phase in group:
> - for devname in data.tdevlist[phase]:
> + for devname in sorted(data.tdevlist[phase]):
> d = DevItem(data.testnumber, phase, data.dmesg[phase]['list'][devname])
> devlist.append(d)
> if d.isa('kth'):
> @@ -4230,7 +4413,7 @@ def createHTML(testruns, testfail):
> for b in phases[dir]:
> # draw the devices for this phase
> phaselist = data.dmesg[b]['list']
> - for d in data.tdevlist[b]:
> + for d in sorted(data.tdevlist[b]):
> name = d
> drv = ''
> dev = phaselist[d]
> @@ -4971,13 +5154,9 @@ def executeSuspend():
> if mode == 'freeze' and sysvals.haveTurbostat():
> # execution will pause here
> turbo = sysvals.turbostat()
> - if '|' in turbo:
> + if turbo:
> tdata['turbo'] = turbo
> - else:
> - tdata['error'] = turbo
> else:
> - if sysvals.haveTurbostat():
> - sysvals.vprint('WARNING: ignoring turbostat in mode "%s"' % mode)
> pf = open(sysvals.powerfile, 'w')
> pf.write(mode)
> # execution will pause here
> @@ -5024,7 +5203,7 @@ def executeSuspend():
> op.write(line)
> op.close()
> sysvals.fsetVal('', 'trace')
> - devProps()
> + sysvals.platforminfo()
> return testdata
>
> def readFile(file):
> @@ -5040,9 +5219,9 @@ def readFile(file):
> # The time string, e.g. "1901m16s"
> def ms2nice(val):
> val = int(val)
> - h = val / 3600000
> - m = (val / 60000) % 60
> - s = (val / 1000) % 60
> + h = val // 3600000
> + m = (val // 60000) % 60
> + s = (val // 1000) % 60
> if h > 0:
> return '%d:%02d:%02d' % (h, m, s)
> if m > 0:
> @@ -5115,127 +5294,6 @@ def deviceInfo(output=''):
> print(lines[i])
> return res
>
> -# Function: devProps
> -# Description:
> -# Retrieve a list of properties for all devices in the trace log
> -def devProps(data=0):
> - props = dict()
> -
> - if data:
> - idx = data.index(': ') + 2
> - if idx >= len(data):
> - return
> - devlist = data[idx:].split(';')
> - for dev in devlist:
> - f = dev.split(',')
> - if len(f) < 3:
> - continue
> - dev = f[0]
> - props[dev] = DevProps()
> - props[dev].altname = f[1]
> - if int(f[2]):
> - props[dev].async = True
> - else:
> - props[dev].async = False
> - sysvals.devprops = props
> - if sysvals.suspendmode == 'command' and 'testcommandstring' in props:
> - sysvals.testcommand = props['testcommandstring'].altname
> - return
> -
> - if(os.path.exists(sysvals.ftracefile) == False):
> - doError('%s does not exist' % sysvals.ftracefile)
> -
> - # first get the list of devices we need properties for
> - msghead = 'Additional data added by AnalyzeSuspend'
> - alreadystamped = False
> - tp = TestProps()
> - tf = sysvals.openlog(sysvals.ftracefile, 'r')
> - for line in tf:
> - if msghead in line:
> - alreadystamped = True
> - continue
> - # determine the trace data type (required for further parsing)
> - m = re.match(tp.tracertypefmt, line)
> - if(m):
> - tp.setTracerType(m.group('t'))
> - continue
> - # parse only valid lines, if this is not one move on
> - m = re.match(tp.ftrace_line_fmt, line)
> - if(not m or 'device_pm_callback_start' not in line):
> - continue
> - m = re.match('.*: (?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*', m.group('msg'));
> - if(not m):
> - continue
> - dev = m.group('d')
> - if dev not in props:
> - props[dev] = DevProps()
> - tf.close()
> -
> - if not alreadystamped and sysvals.suspendmode == 'command':
> - out = '#\n# '+msghead+'\n# Device Properties: '
> - out += 'testcommandstring,%s,0;' % (sysvals.testcommand)
> - with sysvals.openlog(sysvals.ftracefile, 'a') as fp:
> - fp.write(out+'\n')
> - sysvals.devprops = props
> - return
> -
> - # now get the syspath for each of our target devices
> - for dirname, dirnames, filenames in os.walk('/sys/devices'):
> - if(re.match('.*/power', dirname) and 'async' in filenames):
> - dev = dirname.split('/')[-2]
> - if dev in props and (not props[dev].syspath or len(dirname) < len(props[dev].syspath)):
> - props[dev].syspath = dirname[:-6]
> -
> - # now fill in the properties for our target devices
> - for dev in props:
> - dirname = props[dev].syspath
> - if not dirname or not os.path.exists(dirname):
> - continue
> - with open(dirname+'/power/async') as fp:
> - text = fp.read()
> - props[dev].async = False
> - if 'enabled' in text:
> - props[dev].async = True
> - fields = os.listdir(dirname)
> - if 'product' in fields:
> - with open(dirname+'/product') as fp:
> - props[dev].altname = fp.read()
> - elif 'name' in fields:
> - with open(dirname+'/name') as fp:
> - props[dev].altname = fp.read()
> - elif 'model' in fields:
> - with open(dirname+'/model') as fp:
> - props[dev].altname = fp.read()
> - elif 'description' in fields:
> - with open(dirname+'/description') as fp:
> - props[dev].altname = fp.read()
> - elif 'id' in fields:
> - with open(dirname+'/id') as fp:
> - props[dev].altname = fp.read()
> - elif 'idVendor' in fields and 'idProduct' in fields:
> - idv, idp = '', ''
> - with open(dirname+'/idVendor') as fp:
> - idv = fp.read().strip()
> - with open(dirname+'/idProduct') as fp:
> - idp = fp.read().strip()
> - props[dev].altname = '%s:%s' % (idv, idp)
> -
> - if props[dev].altname:
> - out = props[dev].altname.strip().replace('\n', ' ')
> - out = out.replace(',', ' ')
> - out = out.replace(';', ' ')
> - props[dev].altname = out
> -
> - # and now write the data to the ftrace file
> - if not alreadystamped:
> - out = '#\n# '+msghead+'\n# Device Properties: '
> - for dev in sorted(props):
> - out += props[dev].out(dev)
> - with sysvals.openlog(sysvals.ftracefile, 'a') as fp:
> - fp.write(out+'\n')
> -
> - sysvals.devprops = props
> -
> # Function: getModes
> # Description:
> # Determine the supported power modes on this system
> @@ -5339,11 +5397,11 @@ def dmidecode(mempath, fatal=False):
> # search for either an SM table or DMI table
> i = base = length = num = 0
> while(i < memsize):
> - if buf[i:i+4] == '_SM_' and i < memsize - 16:
> + if buf[i:i+4] == b'_SM_' and i < memsize - 16:
> length = struct.unpack('H', buf[i+22:i+24])[0]
> base, num = struct.unpack('IH', buf[i+24:i+30])
> break
> - elif buf[i:i+5] == '_DMI_':
> + elif buf[i:i+5] == b'_DMI_':
> length = struct.unpack('H', buf[i+6:i+8])[0]
> base, num = struct.unpack('IH', buf[i+8:i+14])
> break
> @@ -5376,15 +5434,15 @@ def dmidecode(mempath, fatal=False):
> if 0 == struct.unpack('H', buf[n:n+2])[0]:
> break
> n += 1
> - data = buf[i+size:n+2].split('\0')
> + data = buf[i+size:n+2].split(b'\0')
> for name in info:
> itype, idxadr = info[name]
> if itype == type:
> - idx = struct.unpack('B', buf[i+idxadr])[0]
> + idx = struct.unpack('B', buf[i+idxadr:i+idxadr+1])[0]
> if idx > 0 and idx < len(data) - 1:
> - s = data[idx-1].strip()
> - if s and s.lower() != 'to be filled by o.e.m.':
> - out[name] = data[idx-1]
> + s = data[idx-1].decode('utf-8')
> + if s.strip() and s.strip().lower() != 'to be filled by o.e.m.':
> + out[name] = s
> i = n + 2
> count += 1
> return out
> @@ -5409,7 +5467,7 @@ def getBattery():
> return (ac, charge)
>
> def displayControl(cmd):
> - xset, ret = 'xset -d :0.0 {0}', 0
> + xset, ret = 'timeout 10 xset -d :0.0 {0}', 0
> if sysvals.sudouser:
> xset = 'sudo -u %s %s' % (sysvals.sudouser, xset)
> if cmd == 'init':
> @@ -5433,7 +5491,7 @@ def displayControl(cmd):
> fp = Popen(xset.format('q').split(' '), stdout=PIPE).stdout
> ret = 'unknown'
> for line in fp:
> - m = re.match('[\s]*Monitor is (?P<m>.*)', line)
> + m = re.match('[\s]*Monitor is (?P<m>.*)', ascii(line))
> if(m and len(m.group('m')) >= 2):
> out = m.group('m').lower()
> ret = out[3:] if out[0:2] == 'in' else out
> @@ -5495,10 +5553,11 @@ def getFPDT(output):
> ' OEM Revision : %u\n'\
> ' Creator ID : %s\n'\
> ' Creator Revision : 0x%x\n'\
> - '' % (table[0], table[0], table[1], table[2], table[3],
> - table[4], table[5], table[6], table[7], table[8]))
> + '' % (ascii(table[0]), ascii(table[0]), table[1], table[2],
> + table[3], ascii(table[4]), ascii(table[5]), table[6],
> + ascii(table[7]), table[8]))
>
> - if(table[0] != 'FPDT'):
> + if(table[0] != b'FPDT'):
> if(output):
> doError('Invalid FPDT table')
> return False
> @@ -5530,8 +5589,8 @@ def getFPDT(output):
> return [0, 0]
> rechead = struct.unpack('4sI', first)
> recdata = fp.read(rechead[1]-8)
> - if(rechead[0] == 'FBPT'):
> - record = struct.unpack('HBBIQQQQQ', recdata)
> + if(rechead[0] == b'FBPT'):
> + record = struct.unpack('HBBIQQQQQ', recdata[:48])
> if(output):
> pprint('%s (%s)\n'\
> ' Reset END : %u ns\n'\
> @@ -5539,11 +5598,11 @@ def getFPDT(output):
> ' OS Loader StartImage Start : %u ns\n'\
> ' ExitBootServices Entry : %u ns\n'\
> ' ExitBootServices Exit : %u ns'\
> - '' % (rectype[header[0]], rechead[0], record[4], record[5],
> + '' % (rectype[header[0]], ascii(rechead[0]), record[4], record[5],
> record[6], record[7], record[8]))
> - elif(rechead[0] == 'S3PT'):
> + elif(rechead[0] == b'S3PT'):
> if(output):
> - pprint('%s (%s)' % (rectype[header[0]], rechead[0]))
> + pprint('%s (%s)' % (rectype[header[0]], ascii(rechead[0])))
> j = 0
> while(j < len(recdata)):
> prechead = struct.unpack('HBB', recdata[j:j+4])
> @@ -5689,7 +5748,7 @@ def doError(msg, help=False):
> def getArgInt(name, args, min, max, main=True):
> if main:
> try:
> - arg = args.next()
> + arg = next(args)
> except:
> doError(name+': no argument supplied', True)
> else:
> @@ -5708,7 +5767,7 @@ def getArgInt(name, args, min, max, main=True):
> def getArgFloat(name, args, min, max, main=True):
> if main:
> try:
> - arg = args.next()
> + arg = next(args)
> except:
> doError(name+': no argument supplied', True)
> else:
> @@ -5737,9 +5796,12 @@ def processData(live=False):
> parseKernelLog(data)
> if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)):
> appendIncompleteTraceLog(testruns)
> + shown = ['bios', 'biosdate', 'cpu', 'host', 'kernel', 'man', 'memfr',
> + 'memsz', 'mode', 'numcpu', 'plat', 'time']
> sysvals.vprint('System Info:')
> for key in sorted(sysvals.stamp):
> - sysvals.vprint(' %-8s : %s' % (key.upper(), sysvals.stamp[key]))
> + if key in shown:
> + sysvals.vprint(' %-8s : %s' % (key.upper(), sysvals.stamp[key]))
> if sysvals.kparams:
> sysvals.vprint('Kparams:\n %s' % sysvals.kparams)
> sysvals.vprint('Command:\n %s' % sysvals.cmdline)
> @@ -5768,6 +5830,12 @@ def processData(live=False):
> (w[0], w[1])
> sysvals.vprint(s)
> data.printDetails()
> + if len(sysvals.platinfo) > 0:
> + sysvals.vprint('\nPlatform Info:')
> + for info in sysvals.platinfo:
> + sysvals.vprint(info[0]+' - '+info[1])
> + sysvals.vprint(info[2])
> + sysvals.vprint('')
> if sysvals.cgdump:
> for data in testruns:
> data.debugPrint()
> @@ -5951,7 +6019,7 @@ def data_from_html(file, outpath, issues, fulldetail=False):
> worst[d] = {'name':'', 'time': 0.0}
> dev = devices[d] if d in devices else 0
> if dev and len(dev.keys()) > 0:
> - n = sorted(dev, key=dev.get, reverse=True)[0]
> + n = sorted(dev, key=lambda k:(dev[k], k), reverse=True)[0]
> worst[d]['name'], worst[d]['time'] = n, dev[n]
> data = {
> 'mode': stmp[2],
> @@ -5976,7 +6044,7 @@ def data_from_html(file, outpath, issues, fulldetail=False):
> data['funclist'] = find_in_html(html, '<div title="', '" class="traceevent"', False)
> return data
>
> -def genHtml(subdir):
> +def genHtml(subdir, force=False):
> for dirname, dirnames, filenames in os.walk(subdir):
> sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = ''
> for filename in filenames:
> @@ -5986,7 +6054,7 @@ def genHtml(subdir):
> sysvals.ftracefile = os.path.join(dirname, filename)
> sysvals.setOutputFile()
> if sysvals.ftracefile and sysvals.htmlfile and \
> - not os.path.exists(sysvals.htmlfile):
> + (force or not os.path.exists(sysvals.htmlfile)):
> pprint('FTRACE: %s' % sysvals.ftracefile)
> if sysvals.dmesgfile:
> pprint('DMESG : %s' % sysvals.dmesgfile)
> @@ -6042,7 +6110,7 @@ def checkArgBool(name, value):
> # Description:
> # Configure the script via the info in a config file
> def configFromFile(file):
> - Config = ConfigParser.ConfigParser()
> + Config = configparser.ConfigParser()
>
> Config.read(file)
> sections = Config.sections()
> @@ -6270,7 +6338,7 @@ def printHelp():
> ' default: suspend-{date}-{time}\n'\
> ' -rtcwake t Wakeup t seconds after suspend, set t to "off" to disable (default: 15)\n'\
> ' -addlogs Add the dmesg and ftrace logs to the html output\n'\
> - ' -turbostat Use turbostat to execute the command in freeze mode (default: disabled)\n'\
> + ' -noturbostat Dont use turbostat in freeze mode (default: disabled)\n'\
> ' -srgap Add a visible gap in the timeline between sus/res (default: disabled)\n'\
> ' -skiphtml Run the test and capture the trace logs, but skip the timeline (default: disabled)\n'\
> ' -result fn Export a results table to a text file for parsing.\n'\
> @@ -6340,7 +6408,7 @@ if __name__ == '__main__':
> for arg in args:
> if(arg == '-m'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No mode supplied', True)
> if val == 'command' and not sysvals.testcommand:
> @@ -6384,10 +6452,8 @@ if __name__ == '__main__':
> sysvals.dmesglog = True
> elif(arg == '-addlogftrace'):
> sysvals.ftracelog = True
> - elif(arg == '-turbostat'):
> - sysvals.tstat = True
> - if not sysvals.haveTurbostat():
> - doError('Turbostat command not found')
> + elif(arg == '-noturbostat'):
> + sysvals.tstat = False
> elif(arg == '-verbose'):
> sysvals.verbose = True
> elif(arg == '-proc'):
> @@ -6400,7 +6466,7 @@ if __name__ == '__main__':
> sysvals.gzip = True
> elif(arg == '-rs'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('-rs requires "enable" or "disable"', True)
> if val.lower() in switchvalues:
> @@ -6412,7 +6478,7 @@ if __name__ == '__main__':
> doError('invalid option: %s, use "enable/disable" or "on/off"' % val, True)
> elif(arg == '-display'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('-display requires an mode value', True)
> disopt = ['on', 'off', 'standby', 'suspend']
> @@ -6423,7 +6489,7 @@ if __name__ == '__main__':
> sysvals.max_graph_depth = getArgInt('-maxdepth', args, 0, 1000)
> elif(arg == '-rtcwake'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No rtcwake time supplied', True)
> if val.lower() in switchoff:
> @@ -6443,7 +6509,7 @@ if __name__ == '__main__':
> sysvals.cgtest = getArgInt('-cgtest', args, 0, 1)
> elif(arg == '-cgphase'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No phase name supplied', True)
> d = Data(0)
> @@ -6453,19 +6519,19 @@ if __name__ == '__main__':
> sysvals.cgphase = val
> elif(arg == '-cgfilter'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No callgraph functions supplied', True)
> sysvals.setCallgraphFilter(val)
> elif(arg == '-skipkprobe'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No kprobe functions supplied', True)
> sysvals.skipKprobes(val)
> elif(arg == '-cgskip'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No file supplied', True)
> if val.lower() in switchoff:
> @@ -6480,7 +6546,7 @@ if __name__ == '__main__':
> sysvals.callloopmaxlen = getArgFloat('-callloop-maxlen', args, 0.0, 1.0)
> elif(arg == '-cmd'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No command string supplied', True)
> sysvals.testcommand = val
> @@ -6495,13 +6561,13 @@ if __name__ == '__main__':
> sysvals.multitest['delay'] = getArgInt('-multi n d (delay between tests)', args, 0, 3600)
> elif(arg == '-o'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No subdirectory name supplied', True)
> sysvals.outdir = sysvals.setOutputFolder(val)
> elif(arg == '-config'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No text file supplied', True)
> file = sysvals.configFile(val)
> @@ -6510,7 +6576,7 @@ if __name__ == '__main__':
> configFromFile(file)
> elif(arg == '-fadd'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No text file supplied', True)
> file = sysvals.configFile(val)
> @@ -6519,7 +6585,7 @@ if __name__ == '__main__':
> sysvals.addFtraceFilterFunctions(file)
> elif(arg == '-dmesg'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No dmesg file supplied', True)
> sysvals.notestrun = True
> @@ -6528,7 +6594,7 @@ if __name__ == '__main__':
> doError('%s does not exist' % sysvals.dmesgfile)
> elif(arg == '-ftrace'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No ftrace file supplied', True)
> sysvals.notestrun = True
> @@ -6537,7 +6603,7 @@ if __name__ == '__main__':
> doError('%s does not exist' % sysvals.ftracefile)
> elif(arg == '-summary'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No directory supplied', True)
> cmd = 'summary'
> @@ -6547,13 +6613,13 @@ if __name__ == '__main__':
> doError('%s is not accesible' % val)
> elif(arg == '-filter'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No devnames supplied', True)
> sysvals.setDeviceFilter(val)
> elif(arg == '-result'):
> try:
> - val = args.next()
> + val = next(args)
> except:
> doError('No result file supplied', True)
> sysvals.result = val
>
Applied, thanks!
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2019-08-26 9:19 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-08-12 21:08 [PATCH] pm-graph v5.5 Todd Brandt
2019-08-26 9:19 ` Rafael J. Wysocki
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).