pm-graph v5.5

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>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
Todd Brandt 2019-08-12 14:08:44 -07:00 committed by Rafael J. Wysocki
parent d1abaeb3be
commit 1446794a89
4 changed files with 387 additions and 296 deletions

View file

@ -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)