Commit 8e9ccd0f authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'pm-5.7-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull power management fixes from Rafael Wysocki:
 "Restore an optimization related to asynchronous suspend and resume of
  devices during system-wide power transitions that was disabled by
  mistake (Kai-Heng Feng) and update the pm-graph suite of power
  management utilities (Todd Brandt)"

* tag 'pm-5.7-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  PM: sleep: core: Switch back to async_schedule_dev()
  pm-graph v5.6
parents 5be35f7f 4bee16d7
...@@ -730,7 +730,7 @@ static bool dpm_async_fn(struct device *dev, async_func_t func) ...@@ -730,7 +730,7 @@ static bool dpm_async_fn(struct device *dev, async_func_t func)
if (is_async(dev)) { if (is_async(dev)) {
get_device(dev); get_device(dev);
async_schedule(func, dev); async_schedule_dev(func, dev);
return true; return true;
} }
......
...@@ -41,6 +41,10 @@ uninstall : ...@@ -41,6 +41,10 @@ uninstall :
if [ -d $(DESTDIR)$(PREFIX)/lib/pm-graph/config ] ; then \ if [ -d $(DESTDIR)$(PREFIX)/lib/pm-graph/config ] ; then \
rmdir $(DESTDIR)$(PREFIX)/lib/pm-graph/config; \ rmdir $(DESTDIR)$(PREFIX)/lib/pm-graph/config; \
fi; fi;
rm -f $(DESTDIR)$(PREFIX)/lib/pm-graph/__pycache__/*
if [ -d $(DESTDIR)$(PREFIX)/lib/pm-graph/__pycache__ ] ; then \
rmdir $(DESTDIR)$(PREFIX)/lib/pm-graph/__pycache__; \
fi;
rm -f $(DESTDIR)$(PREFIX)/lib/pm-graph/* rm -f $(DESTDIR)$(PREFIX)/lib/pm-graph/*
if [ -d $(DESTDIR)$(PREFIX)/lib/pm-graph ] ; then \ if [ -d $(DESTDIR)$(PREFIX)/lib/pm-graph ] ; then \
rmdir $(DESTDIR)$(PREFIX)/lib/pm-graph; \ rmdir $(DESTDIR)$(PREFIX)/lib/pm-graph; \
......
p m - g r a p h _
_ __ _ __ ___ __ _ _ __ __ _ _ __ | |__
| '_ \| '_ ` _ \ _____ / _` | '__/ _` | '_ \| '_ \
| |_) | | | | | |_____| (_| | | | (_| | |_) | | | |
| .__/|_| |_| |_| \__, |_| \__,_| .__/|_| |_|
|_| |___/ |_|
pm-graph: suspend/resume/boot timing analysis tools pm-graph: suspend/resume/boot timing analysis tools
Version: 5.5 Version: 5.6
Author: Todd Brandt <todd.e.brandt@intel.com> Author: Todd Brandt <todd.e.brandt@intel.com>
Home Page: https://01.org/pm-graph Home Page: https://01.org/pm-graph
...@@ -18,10 +23,6 @@ ...@@ -18,10 +23,6 @@
- upstream version in git: - upstream version in git:
https://github.com/intel/pm-graph/ 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 Table of Contents
- Overview - Overview
- Setup - Setup
...@@ -29,6 +30,8 @@ ...@@ -29,6 +30,8 @@
- Basic Usage - Basic Usage
- Dev Mode Usage - Dev Mode Usage
- Proc Mode Usage - Proc Mode Usage
- Endurance Testing
- Usage Examples
- Configuration Files - Configuration Files
- Usage Examples - Usage Examples
- Config File Options - Config File Options
...@@ -54,15 +57,18 @@ ...@@ -54,15 +57,18 @@
| SETUP | | SETUP |
------------------------------------------------------------------ ------------------------------------------------------------------
These packages are required to execute the scripts Package Requirements
- runs with python2 or python3, choice is made by /usr/bin/python link
- python - python
- python-requests - python-configparser (for python2 sleepgraph)
- python-requests (for googlesheet.py)
- linux-tools-common (for turbostat usage in sleepgraph)
Ubuntu: Ubuntu:
sudo apt-get install python python-requests sudo apt-get install python python-configparser python-requests linux-tools-common
Fedora: Fedora:
sudo dnf install python python-requests sudo dnf install python python-configparser python-requests linux-tools-common
The tools can most easily be installed via git clone and make install The tools can most easily be installed via git clone and make install
...@@ -190,6 +196,104 @@ _______________ ...@@ -190,6 +196,104 @@ _______________
%> sudo ./sleepgraph.py -config config/suspend-proc.cfg %> sudo ./sleepgraph.py -config config/suspend-proc.cfg
------------------------------------------------------------------
| ENDURANCE TESTING |
------------------------------------------------------------------
The best way to gauge the health of a system is to run a series of
suspend/resumes over an extended period and analyze the behavior. This can be
accomplished with sleepgraph's -multi argument. You specify two numbers: the
number of tests to run OR the duration in days, hours, or minutes, and the
delay in seconds between them. For instance, -multi 20 5: execute 20 tests with
a 5 second delay between each, or -multi 24h 0: execute tests over a 24 hour
period with no delay between tests. You can include any other options you like
to generate the data you want. It's most useful to collect dev mode timelines
as the kprobes don't alter the performance much and you get more insight.
On completion, the output folder contains a series of folders for the
individual test data and a set of summary pages in the root. The summary.html
file is a tabular list of the tests with relevant info and links. The
summary-issue.html and summary-devices.html files include data taken from
all tests on kernel issues and device performance. The folder looks like this:
suspend-xN-{date}-{time}:
summary.html
summary-issues.html
summary-devices.html
suspend-{date}-{time} (1)
suspend-{date}-{time} (2)
...
These are the relevant arguments to use for testing:
-m mode
Mode to initiate for suspend e.g. mem, freeze, standby (default: mem).
-rtcwake t
Use rtcwake to autoresume after t seconds (default: 15).
-gzip (optional)
Gzip the trace and dmesg logs to save space. The tool can also read in
gzipped logs for processing. This reduces the multitest folder size.
-dev (optional)
Add kernel source calls and threads to the timeline (default: disabled).
-multi n d
Execute n consecutive tests at d seconds intervals. The outputs will be
created in a new subdirectory: suspend-xN-{date}-{time}. When the multitest
run is done, the -summary command is called automatically to create summary
html files for all the data (unless you use -skiphtml). -skiphtml will
speed up the testing by not creating timelines or summary html files. You
can then run the tool again at a later time with -summary and -genhtml to
create the timelines.
-skiphtml (optional)
Run the test and capture the trace logs, but skip the timeline and summary
html generation. This can greatly speed up overall testing. You can then
copy the data to a faster host machine and run -summary -genhtml to
generate the timelines and summary.
These are the relevant commands to use after testing is complete:
-summary indir
Generate or regenerate the summary for a -multi test run. Creates three
files: summary.html, summary-issues.html, and summary-devices.html in the
current folder. summary.html is a table of tests with relevant info sorted
by kernel/host/mode, and links to the test html files. summary-issues.html
is a list of kernel issues found in dmesg from all the tests.
summary-devices.html is a list of devices and times from all the tests.
-genhtml
Used with -summary to regenerate any missing html timelines from their
dmesg and ftrace logs. This will require a significant amount of time if
there are thousands of tests.
Usage Examples
_______________
A multitest is initiated like this:
%> sudo ./sleepgraph.py -m mem -rtcwake 10 -dev -gzip -multi 2000 0
or you can skip timeline generation in order to speed things up
%> sudo ./sleepgraph.py -m mem -rtcwake 10 -dev -gzip -multi 2000 0 -skiphtml
The tool will produce an output folder with all the test subfolders inside.
Each test subfolder contains the dmesg/ftrace logs and/or the html timeline
depending on whether you used the -skiphtml option. The root folder contains
the summary.html files.
The summary for an existing multitest is generated like this:
%> cd suspend-x2000-{date}-{time}
%> sleepgraph.py -summary .
or if you need to generate the html timelines you can use -genhtml
%> cd suspend-xN-{date}-{time}
%> sleepgraph.py -summary . -genhtml
------------------------------------------------------------------ ------------------------------------------------------------------
| CONFIGURATION FILES | | CONFIGURATION FILES |
......
#!/usr/bin/python #!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
# #
# Tool for analyzing boot timing # Tool for analyzing boot timing
......
...@@ -74,8 +74,10 @@ after the test is complete. ...@@ -74,8 +74,10 @@ after the test is complete.
Switch the display to the requested mode for the test using the xset command. Switch the display to the requested mode for the test using the xset command.
This helps maintain the consistency of test data for better comparison. This helps maintain the consistency of test data for better comparison.
.TP .TP
\fB-skiphtml\fR \fB-wifi\fR
Run the test and capture the trace logs, but skip the timeline generation. If a wifi connection is available, check that it reconnects after resume. Include
the reconnect time in the total resume time calculation and treat wifi timeouts
as resume failures.
.SS "advanced" .SS "advanced"
.TP .TP
...@@ -117,8 +119,24 @@ Include \fIt\fR ms delay before 1st suspend (default: 0 ms). ...@@ -117,8 +119,24 @@ Include \fIt\fR ms delay before 1st suspend (default: 0 ms).
Include \fIt\fR ms delay after last resume (default: 0 ms). Include \fIt\fR ms delay after last resume (default: 0 ms).
.TP .TP
\fB-multi \fIn d\fR \fB-multi \fIn d\fR
Execute \fIn\fR consecutive tests at \fId\fR seconds intervals. The outputs will Used for endurance testing. If \fIn\fR is entirely numeric, it's treated as a count:
be created in a new subdirectory with a summary page: suspend-xN-{date}-{time}. Execute \fIn\fR consecutive tests at \fId\fR second intervals.
If \fIn\fR is an integer followed by a "d", "h", or "m", it's treated as a duration:
Execute tests continuously over \fIn\fR days, hours, or minutes at \fId\fR second intervals.
The outputs will be created in a new subdirectory, for count: suspend-{date}-{time}-xN,
for duration: suspend-{date}-{time}-Nm. When the multitest run is done, the \fI-summary\fR
command is called automatically to create summary html files for all the data (unless you
use \fI-skiphtml\fR). \fI-skiphtml\fR will speed up the testing by not creating timelines
or summary html files. You can then run the tool again at a later time with \fI-summary\fR
and \fI-genhtml\fR to create the timelines.
.TP
\fB-maxfail \fIn\fR
Abort a -multi run after \fIn\fR consecutive fails. 0 means never abort (default = 0).
.TP
\fB-skiphtml\fR
Run the test and capture the trace logs, but skip the timeline generation.
You can generate the html timelines later with \fI-dmesg\fR & \fI-ftrace\fR, or
by running \fI-summary\fR and \fI-genhtml\fR.
.SS "ftrace debug" .SS "ftrace debug"
.TP .TP
...@@ -173,11 +191,20 @@ Set trace buffer size to N kilo-bytes (default: all of free memory up to 3GB) ...@@ -173,11 +191,20 @@ Set trace buffer size to N kilo-bytes (default: all of free memory up to 3GB)
.SH COMMANDS .SH COMMANDS
.TP .TP
\fB-summary \fIindir\fR \fB-summary \fIindir\fR
Create a summary page of all tests in \fIindir\fR. Creates summary.html Create a set of summary pages for all tests in \fIindir\fR recursively.
in the current folder. The output page is a table of tests with Creates summary.html, summary-issues.html, and summary-devices.html in the current folder.
suspend and resume values sorted by suspend mode, host, and kernel. summary.html is a table of tests with relevant info sorted by kernel/host/mode,
Includes test averages by mode and links to the test html files. and links to the test html files. It identifies the minimum, maximum, and median
Use -genhtml to include tests with missing html. suspend and resume times for you with highlights and links in the header.
summary-issues.html is a list of kernel issues found in dmesg from all the tests.
summary-devices.html is a list of devices and times from all the tests.
Use \fI-genhtml\fR to regenerate any tests with missing html.
.TP
\fB-genhtml\fR
Used with \fI-summary\fR to regenerate any missing html timelines from their
dmesg and ftrace logs. This will require a significant amount of time if there
are thousands of tests.
.TP .TP
\fB-modes\fR \fB-modes\fR
List available suspend modes. List available suspend modes.
...@@ -189,10 +216,7 @@ with any options you intend to use to see if they will work. ...@@ -189,10 +216,7 @@ with any options you intend to use to see if they will work.
\fB-fpdt\fR \fB-fpdt\fR
Print out the contents of the ACPI Firmware Performance Data Table. Print out the contents of the ACPI Firmware Performance Data Table.
.TP .TP
\fB-battery\fR \fB-wificheck\fR
Print out battery status and current charge.
.TP
\fB-wifi\fR
Print out wifi status and connection details. Print out wifi status and connection details.
.TP .TP
\fB-xon/-xoff/-xstandby/-xsuspend\fR \fB-xon/-xoff/-xstandby/-xsuspend\fR
...@@ -208,6 +232,9 @@ Print out system info extracted from BIOS. Reads /dev/mem directly instead of go ...@@ -208,6 +232,9 @@ Print out system info extracted from BIOS. Reads /dev/mem directly instead of go
\fB-devinfo\fR \fB-devinfo\fR
Print out the pm settings of all devices which support runtime suspend. Print out the pm settings of all devices which support runtime suspend.
.TP .TP
\fB-cmdinfo\fR
Print out all the platform data collected from the system that makes it into the logs.
.TP
\fB-flist\fR \fB-flist\fR
Print the list of ftrace functions currently being captured. Functions Print the list of ftrace functions currently being captured. Functions
that are not available as symbols in the current kernel are shown in red. that are not available as symbols in the current kernel are shown in red.
...@@ -272,14 +299,20 @@ Run two suspends back to back, include a 500ms delay before, after, and in betwe ...@@ -272,14 +299,20 @@ Run two suspends back to back, include a 500ms delay before, after, and in betwe
.IP .IP
\f(CW$ sudo sleepgraph -m mem -rtcwake 15 -x2 -predelay 500 -x2delay 500 -postdelay 500\fR \f(CW$ sudo sleepgraph -m mem -rtcwake 15 -x2 -predelay 500 -x2delay 500 -postdelay 500\fR
.PP .PP
Execute a suspend using a custom command.
.IP
\f(CW$ sudo sleepgraph -cmd "echo mem > /sys/power/state" -rtcwake 15\fR
.PP
.SS "endurance testing using -multi"
.PP
Do a batch run of 10 freezes with 30 seconds delay between runs. Do a batch run of 10 freezes with 30 seconds delay between runs.
.IP .IP
\f(CW$ sudo sleepgraph -m freeze -rtcwake 15 -multi 10 30\fR \f(CW$ sudo sleepgraph -m freeze -rtcwake 15 -multi 10 30\fR
.PP .PP
Execute a suspend using a custom command. Do a batch run of freezes for 24 hours.
.IP .IP
\f(CW$ sudo sleepgraph -cmd "echo mem > /sys/power/state" -rtcwake 15\fR \f(CW$ sudo sleepgraph -m freeze -rtcwake 15 -multi 24h 0\fR
.PP
.SS "adding callgraph data" .SS "adding callgraph data"
Add device callgraphs. Limit the trace depth and only show callgraphs 10ms or larger. Add device callgraphs. Limit the trace depth and only show callgraphs 10ms or larger.
......
#!/usr/bin/python #!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
# #
# Tool for analyzing suspend/resume timing # Tool for analyzing suspend/resume timing
...@@ -58,7 +58,7 @@ import re ...@@ -58,7 +58,7 @@ import re
import platform import platform
import signal import signal
import codecs import codecs
from datetime import datetime from datetime import datetime, timedelta
import struct import struct
import configparser import configparser
import gzip import gzip
...@@ -81,12 +81,13 @@ def ascii(text): ...@@ -81,12 +81,13 @@ def ascii(text):
# store system values and test parameters # store system values and test parameters
class SystemValues: class SystemValues:
title = 'SleepGraph' title = 'SleepGraph'
version = '5.5' version = '5.6'
ansi = False ansi = False
rs = 0 rs = 0
display = '' display = ''
gzip = False gzip = False
sync = False sync = False
wifi = False
verbose = False verbose = False
testlog = True testlog = True
dmesglog = True dmesglog = True
...@@ -97,7 +98,8 @@ class SystemValues: ...@@ -97,7 +98,8 @@ class SystemValues:
cgphase = '' cgphase = ''
cgtest = -1 cgtest = -1
cgskip = '' cgskip = ''
multitest = {'run': False, 'count': 0, 'delay': 0} maxfail = 0
multitest = {'run': False, 'count': 1000000, 'delay': 0}
max_graph_depth = 0 max_graph_depth = 0
callloopmaxgap = 0.0001 callloopmaxgap = 0.0001
callloopmaxlen = 0.005 callloopmaxlen = 0.005
...@@ -148,7 +150,7 @@ class SystemValues: ...@@ -148,7 +150,7 @@ class SystemValues:
x2delay = 0 x2delay = 0
skiphtml = False skiphtml = False
usecallgraph = False usecallgraph = False
ftopfunc = 'suspend_devices_and_enter' ftopfunc = 'pm_suspend'
ftop = False ftop = False
usetraceevents = False usetraceevents = False
usetracemarkers = True usetracemarkers = True
...@@ -164,6 +166,8 @@ class SystemValues: ...@@ -164,6 +166,8 @@ class SystemValues:
predelay = 0 predelay = 0
postdelay = 0 postdelay = 0
pmdebug = '' pmdebug = ''
tmstart = 'SUSPEND START %Y%m%d-%H:%M:%S.%f'
tmend = 'RESUME COMPLETE %Y%m%d-%H:%M:%S.%f'
tracefuncs = { tracefuncs = {
'sys_sync': {}, 'sys_sync': {},
'ksys_sync': {}, 'ksys_sync': {},
...@@ -183,9 +187,11 @@ class SystemValues: ...@@ -183,9 +187,11 @@ class SystemValues:
'acpi_s2idle_sync': {}, 'acpi_s2idle_sync': {},
'acpi_s2idle_begin': {}, 'acpi_s2idle_begin': {},
'acpi_s2idle_prepare': {}, 'acpi_s2idle_prepare': {},
'acpi_s2idle_prepare_late': {},
'acpi_s2idle_wake': {}, 'acpi_s2idle_wake': {},
'acpi_s2idle_wakeup': {}, 'acpi_s2idle_wakeup': {},
'acpi_s2idle_restore': {}, 'acpi_s2idle_restore': {},
'acpi_s2idle_restore_early': {},
'hibernate_preallocate_memory': {}, 'hibernate_preallocate_memory': {},
'create_basic_memory_bitmaps': {}, 'create_basic_memory_bitmaps': {},
'swsusp_write': {}, 'swsusp_write': {},
...@@ -270,12 +276,23 @@ class SystemValues: ...@@ -270,12 +276,23 @@ class SystemValues:
'intel_opregion_init': {}, 'intel_opregion_init': {},
'intel_fbdev_set_suspend': {}, 'intel_fbdev_set_suspend': {},
} }
infocmds = [
[0, 'kparams', 'cat', '/proc/cmdline'],
[0, 'mcelog', 'mcelog'],
[0, 'pcidevices', 'lspci', '-tv'],
[0, 'usbdevices', 'lsusb', '-t'],
[1, 'interrupts', 'cat', '/proc/interrupts'],
[1, 'wakeups', 'cat', '/sys/kernel/debug/wakeup_sources'],
[2, 'gpecounts', 'sh', '-c', 'grep -v invalid /sys/firmware/acpi/interrupts/*'],
[2, 'suspendstats', 'sh', '-c', 'grep -v invalid /sys/power/suspend_stats/*'],
[2, 'cpuidle', 'sh', '-c', 'grep -v invalid /sys/devices/system/cpu/cpu*/cpuidle/state*/s2idle/*'],
[2, 'battery', 'sh', '-c', 'grep -v invalid /sys/class/power_supply/*/*'],
]
cgblacklist = [] cgblacklist = []
kprobes = dict() kprobes = dict()
timeformat = '%.3f' timeformat = '%.3f'
cmdline = '%s %s' % \ cmdline = '%s %s' % \
(os.path.basename(sys.argv[0]), ' '.join(sys.argv[1:])) (os.path.basename(sys.argv[0]), ' '.join(sys.argv[1:]))
kparams = ''
sudouser = '' sudouser = ''
def __init__(self): def __init__(self):
self.archargs = 'args_'+platform.machine() self.archargs = 'args_'+platform.machine()
...@@ -295,6 +312,9 @@ class SystemValues: ...@@ -295,6 +312,9 @@ class SystemValues:
if os.getuid() == 0 and 'SUDO_USER' in os.environ and \ if os.getuid() == 0 and 'SUDO_USER' in os.environ and \
os.environ['SUDO_USER']: os.environ['SUDO_USER']:
self.sudouser = os.environ['SUDO_USER'] self.sudouser = os.environ['SUDO_USER']
def resetlog(self):
self.logmsg = ''
self.platinfo = []
def vprint(self, msg): def vprint(self, msg):
self.logmsg += msg+'\n' self.logmsg += msg+'\n'
if self.verbose or msg.startswith('WARNING:'): if self.verbose or msg.startswith('WARNING:'):
...@@ -304,11 +324,11 @@ class SystemValues: ...@@ -304,11 +324,11 @@ class SystemValues:
return return
signame = self.signames[signum] if signum in self.signames else 'UNKNOWN' signame = self.signames[signum] if signum in self.signames else 'UNKNOWN'
msg = 'Signal %s caused a tool exit, line %d' % (signame, frame.f_lineno) msg = 'Signal %s caused a tool exit, line %d' % (signame, frame.f_lineno)
sysvals.outputResult({'error':msg}) self.outputResult({'error':msg})
sys.exit(3) sys.exit(3)
def signalHandlerInit(self): def signalHandlerInit(self):
capture = ['BUS', 'SYS', 'XCPU', 'XFSZ', 'PWR', 'HUP', 'INT', 'QUIT', capture = ['BUS', 'SYS', 'XCPU', 'XFSZ', 'PWR', 'HUP', 'INT', 'QUIT',
'ILL', 'ABRT', 'FPE', 'SEGV', 'TERM', 'TSTP'] 'ILL', 'ABRT', 'FPE', 'SEGV', 'TERM']
self.signames = dict() self.signames = dict()
for i in capture: for i in capture:
s = 'SIG'+i s = 'SIG'+i
...@@ -336,6 +356,8 @@ class SystemValues: ...@@ -336,6 +356,8 @@ class SystemValues:
self.outputResult({'error':msg}) self.outputResult({'error':msg})
sys.exit(1) sys.exit(1)
return False return False
def usable(self, file):
return (os.path.exists(file) and os.path.getsize(file) > 0)
def getExec(self, cmd): def getExec(self, cmd):
try: try:
fp = Popen(['which', cmd], stdout=PIPE, stderr=PIPE).stdout fp = Popen(['which', cmd], stdout=PIPE, stderr=PIPE).stdout
...@@ -389,12 +411,6 @@ class SystemValues: ...@@ -389,12 +411,6 @@ class SystemValues:
r = info['bios-release-date'] if 'bios-release-date' in info else '' r = info['bios-release-date'] if 'bios-release-date' in info else ''
self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | biosdate:%s | numcpu:%d | memsz:%d | memfr:%d' % \ self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | biosdate:%s | numcpu:%d | memsz:%d | memfr:%d' % \
(m, p, c, b, r, self.cpucount, self.memtotal, self.memfree) (m, p, c, b, r, self.cpucount, self.memtotal, self.memfree)
try:
kcmd = open('/proc/cmdline', 'r').read().strip()
except:
kcmd = ''
if kcmd:
self.sysstamp += '\n# kparams | %s' % kcmd
def printSystemInfo(self, fatal=False): def printSystemInfo(self, fatal=False):
self.rootCheck(True) self.rootCheck(True)
out = dmidecode(self.mempath, fatal) out = dmidecode(self.mempath, fatal)
...@@ -441,6 +457,7 @@ class SystemValues: ...@@ -441,6 +457,7 @@ class SystemValues:
self.testdir+'/'+self.prefix+'_'+self.suspendmode+'.html' self.testdir+'/'+self.prefix+'_'+self.suspendmode+'.html'
if not os.path.isdir(self.testdir): if not os.path.isdir(self.testdir):
os.makedirs(self.testdir) os.makedirs(self.testdir)
self.sudoUserchown(self.testdir)
def getValueList(self, value): def getValueList(self, value):
out = [] out = []
for i in value.split(','): for i in value.split(','):
...@@ -486,7 +503,7 @@ class SystemValues: ...@@ -486,7 +503,7 @@ class SystemValues:
fp.close() fp.close()
self.dmesgstart = float(ktime) self.dmesgstart = float(ktime)
def getdmesg(self, testdata): def getdmesg(self, testdata):
op = self.writeDatafileHeader(sysvals.dmesgfile, testdata) op = self.writeDatafileHeader(self.dmesgfile, testdata)
# store all new dmesg lines since initdmesg was called # store all new dmesg lines since initdmesg was called
fp = Popen('dmesg', stdout=PIPE).stdout fp = Popen('dmesg', stdout=PIPE).stdout
for line in fp: for line in fp:
...@@ -716,8 +733,9 @@ class SystemValues: ...@@ -716,8 +733,9 @@ class SystemValues:
if name == f: if name == f:
return True return True
return False return False
def initFtrace(self): def initFtrace(self, quiet=False):
self.printSystemInfo(False) if not quiet:
sysvals.printSystemInfo(False)
pprint('INITIALIZING FTRACE...') pprint('INITIALIZING FTRACE...')
# turn trace off # turn trace off
self.fsetVal('0', 'tracing_on') self.fsetVal('0', 'tracing_on')
...@@ -746,7 +764,7 @@ class SystemValues: ...@@ -746,7 +764,7 @@ class SystemValues:
if tgtsize < 65536: if tgtsize < 65536:
tgtsize = int(self.fgetVal('buffer_size_kb')) * cpus tgtsize = int(self.fgetVal('buffer_size_kb')) * cpus
break break
pprint('Setting trace buffers to %d kB (%d kB per cpu)' % (tgtsize, tgtsize/cpus)) self.vprint('Setting trace buffers to %d kB (%d kB per cpu)' % (tgtsize, tgtsize/cpus))
# initialize the callgraph trace # initialize the callgraph trace
if(self.usecallgraph): if(self.usecallgraph):
# set trace type # set trace type
...@@ -782,6 +800,7 @@ class SystemValues: ...@@ -782,6 +800,7 @@ class SystemValues:
if self.usedevsrc: if self.usedevsrc:
for name in self.dev_tracefuncs: for name in self.dev_tracefuncs:
self.defaultKprobe(name, self.dev_tracefuncs[name]) self.defaultKprobe(name, self.dev_tracefuncs[name])
if not quiet:
pprint('INITIALIZING KPROBES...') pprint('INITIALIZING KPROBES...')
self.addKprobes(self.verbose) self.addKprobes(self.verbose)
if(self.usetraceevents): if(self.usetraceevents):
...@@ -827,21 +846,10 @@ class SystemValues: ...@@ -827,21 +846,10 @@ class SystemValues:
fw = test['fw'] fw = test['fw']
if(fw): if(fw):
fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1])) fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1]))
if 'mcelog' in test:
fp.write('# mcelog %s\n' % test['mcelog'])
if 'turbo' in test: if 'turbo' in test:
fp.write('# turbostat %s\n' % test['turbo']) fp.write('# turbostat %s\n' % test['turbo'])
if 'bat' in test:
(a1, c1), (a2, c2) = test['bat']
fp.write('# battery %s %d %s %d\n' % (a1, c1, a2, c2))
if 'wifi' in test: if 'wifi' in test:
wstr = [] fp.write('# wifi %s\n' % test['wifi'])
for wifi in test['wifi']:
tmp = []
for key in sorted(wifi):
tmp.append('%s:%s' % (key, wifi[key]))
wstr.append('|'.join(tmp))
fp.write('# wifi %s\n' % (','.join(wstr)))
if test['error'] or len(testdata) > 1: if test['error'] or len(testdata) > 1:
fp.write('# enter_sleep_error %s\n' % test['error']) fp.write('# enter_sleep_error %s\n' % test['error'])
return fp return fp
...@@ -901,23 +909,7 @@ class SystemValues: ...@@ -901,23 +909,7 @@ class SystemValues:
def b64zip(self, data): def b64zip(self, data):
out = base64.b64encode(codecs.encode(data.encode(), 'zlib')).decode() out = base64.b64encode(codecs.encode(data.encode(), 'zlib')).decode()
return out return out
def mcelog(self, clear=False): def platforminfo(self, cmdafter):
cmd = self.getExec('mcelog')
if not cmd:
return ''
if clear:
call(cmd+' > /dev/null 2>&1', shell=True)
return ''
try:
fp = Popen([cmd], stdout=PIPE, stderr=PIPE).stdout
out = ascii(fp.read()).strip()
fp.close()
except:
return ''
if not out:
return ''
return self.b64zip(out)
def platforminfo(self):
# add platform info on to a completed ftrace file # add platform info on to a completed ftrace file
if not os.path.exists(self.ftracefile): if not os.path.exists(self.ftracefile):
return False return False
...@@ -1001,31 +993,76 @@ class SystemValues: ...@@ -1001,31 +993,76 @@ class SystemValues:
footer += '# platform-devinfo: %s\n' % self.b64zip(out) footer += '# platform-devinfo: %s\n' % self.b64zip(out)
# add a line for each of these commands with their outputs # add a line for each of these commands with their outputs
cmds = [ for name, cmdline, info in cmdafter:
['pcidevices', 'lspci', '-tv'], footer += '# platform-%s: %s | %s\n' % (name, cmdline, self.b64zip(info))
['interrupts', 'cat', '/proc/interrupts'],
['gpecounts', 'sh', '-c', 'grep -v invalid /sys/firmware/acpi/interrupts/gpe*'], with self.openlog(self.ftracefile, 'a') as fp:
] fp.write(footer)
for cargs in cmds: return True
name = cargs[0] def commonPrefix(self, list):
cmdline = ' '.join(cargs[1:]) if len(list) < 2:
cmdpath = self.getExec(cargs[1]) return ''
if not cmdpath: prefix = list[0]
for s in list[1:]:
while s[:len(prefix)] != prefix and prefix:
prefix = prefix[:len(prefix)-1]
if not prefix:
break
if '/' in prefix and prefix[-1] != '/':
prefix = prefix[0:prefix.rfind('/')+1]
return prefix
def dictify(self, text, format):
out = dict()
header = True if format == 1 else False
delim = ' ' if format == 1 else ':'
for line in text.split('\n'):
if header:
header, out['@'] = False, line
continue
line = line.strip()
if delim in line:
data = line.split(delim, 1)
num = re.search(r'[\d]+', data[1])
if format == 2 and num:
out[data[0].strip()] = num.group()
else:
out[data[0].strip()] = data[1]
return out
def cmdinfo(self, begin, debug=False):
out = []
if begin:
self.cmd1 = dict()
for cargs in self.infocmds:
delta, name = cargs[0], cargs[1]
cmdline, cmdpath = ' '.join(cargs[2:]), self.getExec(cargs[2])
if not cmdpath or (begin and not delta):
continue continue
cmd = [cmdpath] + cargs[2:]
try: try:
fp = Popen(cmd, stdout=PIPE, stderr=PIPE).stdout fp = Popen([cmdpath]+cargs[3:], stdout=PIPE, stderr=PIPE).stdout
info = ascii(fp.read()).strip() info = ascii(fp.read()).strip()
fp.close() fp.close()
except: except:
continue continue
if not info: if not debug and begin:
continue self.cmd1[name] = self.dictify(info, delta)
footer += '# platform-%s: %s | %s\n' % (name, cmdline, self.b64zip(info)) elif not debug and delta and name in self.cmd1:
before, after = self.cmd1[name], self.dictify(info, delta)
with self.openlog(self.ftracefile, 'a') as fp: dinfo = ('\t%s\n' % before['@']) if '@' in before else ''
fp.write(footer) prefix = self.commonPrefix(list(before.keys()))
return True for key in sorted(before):
if key in after and before[key] != after[key]:
title = key.replace(prefix, '')
if delta == 2:
dinfo += '\t%s : %s -> %s\n' % \
(title, before[key].strip(), after[key].strip())
else:
dinfo += '%10s (start) : %s\n%10s (after) : %s\n' % \
(title, before[key], title, after[key])
dinfo = '\tnothing changed' if not dinfo else dinfo.rstrip()
out.append((name, cmdline, dinfo))
else:
out.append((name, cmdline, '\tnothing' if not info else info))
return out
def haveTurbostat(self): def haveTurbostat(self):
if not self.tstat: if not self.tstat:
return False return False
...@@ -1035,8 +1072,8 @@ class SystemValues: ...@@ -1035,8 +1072,8 @@ class SystemValues:
fp = Popen([cmd, '-v'], stdout=PIPE, stderr=PIPE).stderr fp = Popen([cmd, '-v'], stdout=PIPE, stderr=PIPE).stderr
out = ascii(fp.read()).strip() out = ascii(fp.read()).strip()
fp.close() fp.close()
if re.match('turbostat version [0-9\.]* .*', out): if re.match('turbostat version .*', out):
sysvals.vprint(out) self.vprint(out)
return True return True
return False return False
def turbostat(self): def turbostat(self):
...@@ -1056,11 +1093,11 @@ class SystemValues: ...@@ -1056,11 +1093,11 @@ class SystemValues:
fp.close() fp.close()
if not keyline or not valline or len(keyline) != len(valline): if not keyline or not valline or len(keyline) != len(valline):
errmsg = 'unrecognized turbostat output:\n'+rawout.strip() errmsg = 'unrecognized turbostat output:\n'+rawout.strip()
sysvals.vprint(errmsg) self.vprint(errmsg)
if not sysvals.verbose: if not self.verbose:
pprint(errmsg) pprint(errmsg)
return '' return ''
if sysvals.verbose: if self.verbose:
pprint(rawout.strip()) pprint(rawout.strip())
out = [] out = []
for key in keyline: for key in keyline:
...@@ -1068,30 +1105,36 @@ class SystemValues: ...@@ -1068,30 +1105,36 @@ class SystemValues:
val = valline[idx] val = valline[idx]
out.append('%s=%s' % (key, val)) out.append('%s=%s' % (key, val))
return '|'.join(out) return '|'.join(out)
def checkWifi(self): def wifiDetails(self, dev):
out = dict() try:
iwcmd, ifcmd = self.getExec('iwconfig'), self.getExec('ifconfig') info = open('/sys/class/net/%s/device/uevent' % dev, 'r').read().strip()
if not iwcmd or not ifcmd: except:
return out return dev
fp = Popen(iwcmd, stdout=PIPE, stderr=PIPE).stdout vals = [dev]
for line in fp: for prop in info.split('\n'):
m = re.match('(?P<dev>\S*) .* ESSID:(?P<ess>\S*)', ascii(line)) if prop.startswith('DRIVER=') or prop.startswith('PCI_ID='):
if not m: vals.append(prop.split('=')[-1])
return ':'.join(vals)
def checkWifi(self, dev=''):
try:
w = open('/proc/net/wireless', 'r').read().strip()
except:
return ''
for line in reversed(w.split('\n')):
m = re.match(' *(?P<dev>.*): (?P<stat>[0-9a-f]*) .*', w.split('\n')[-1])
if not m or (dev and dev != m.group('dev')):
continue continue
out['device'] = m.group('dev') return m.group('dev')
if '"' in m.group('ess'): return ''
out['essid'] = m.group('ess').strip('"') def pollWifi(self, dev, timeout=60):
break start = time.time()
fp.close() while (time.time() - start) < timeout:
if 'device' in out: w = self.checkWifi(dev)
fp = Popen([ifcmd, out['device']], stdout=PIPE, stderr=PIPE).stdout if w:
for line in fp: return '%s reconnected %.2f' % \
m = re.match('.* inet (?P<ip>[0-9\.]*)', ascii(line)) (self.wifiDetails(dev), max(0, time.time() - start))
if m: time.sleep(0.01)
out['ip'] = m.group('ip') return '%s timeout %d' % (self.wifiDetails(dev), timeout)
break
fp.close()
return out
def errorSummary(self, errinfo, msg): def errorSummary(self, errinfo, msg):
found = False found = False
for entry in errinfo: for entry in errinfo:
...@@ -1113,8 +1156,9 @@ class SystemValues: ...@@ -1113,8 +1156,9 @@ class SystemValues:
arr[j] = arr[j]\ arr[j] = arr[j]\
.replace('\\', '\\\\').replace(']', '\]').replace('[', '\[')\ .replace('\\', '\\\\').replace(']', '\]').replace('[', '\[')\
.replace('.', '\.').replace('+', '\+').replace('*', '\*')\ .replace('.', '\.').replace('+', '\+').replace('*', '\*')\
.replace('(', '\(').replace(')', '\)') .replace('(', '\(').replace(')', '\)').replace('}', '\}')\
mstr = ' '.join(arr) .replace('{', '\{')
mstr = ' *'.join(arr)
entry = { entry = {
'line': msg, 'line': msg,
'match': mstr, 'match': mstr,
...@@ -1122,6 +1166,44 @@ class SystemValues: ...@@ -1122,6 +1166,44 @@ class SystemValues:
'urls': {self.hostname: [self.htmlfile]} 'urls': {self.hostname: [self.htmlfile]}
} }
errinfo.append(entry) errinfo.append(entry)
def multistat(self, start, idx, finish):
if 'time' in self.multitest:
id = '%d Duration=%dmin' % (idx+1, self.multitest['time'])
else:
id = '%d/%d' % (idx+1, self.multitest['count'])
t = time.time()
if 'start' not in self.multitest:
self.multitest['start'] = self.multitest['last'] = t
self.multitest['total'] = 0.0
pprint('TEST (%s) START' % id)
return
dt = t - self.multitest['last']
if not start:
if idx == 0 and self.multitest['delay'] > 0:
self.multitest['total'] += self.multitest['delay']
pprint('TEST (%s) COMPLETE -- Duration %.1fs' % (id, dt))
return
self.multitest['total'] += dt
self.multitest['last'] = t
avg = self.multitest['total'] / idx
if 'time' in self.multitest:
left = finish - datetime.now()
left -= timedelta(microseconds=left.microseconds)
else:
left = timedelta(seconds=((self.multitest['count'] - idx) * int(avg)))
pprint('TEST (%s) START - Avg Duration %.1fs, Time left %s' % \
(id, avg, str(left)))
def multiinit(self, c, d):
sz, unit = 'count', 'm'
if c.endswith('d') or c.endswith('h') or c.endswith('m'):
sz, unit, c = 'time', c[-1], c[:-1]
self.multitest['run'] = True
self.multitest[sz] = getArgInt('multi: n d (exec count)', c, 1, 1000000, False)
self.multitest['delay'] = getArgInt('multi: n d (delay between tests)', d, 0, 3600, False)
if unit == 'd':
self.multitest[sz] *= 1440
elif unit == 'h':
self.multitest[sz] *= 60
sysvals = SystemValues() sysvals = SystemValues()
switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0'] switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0']
...@@ -1210,25 +1292,30 @@ class Data: ...@@ -1210,25 +1292,30 @@ class Data:
'resume_complete': {'order': 9, 'color': '#FFFFCC'}, 'resume_complete': {'order': 9, 'color': '#FFFFCC'},
} }
errlist = { errlist = {
'HWERROR' : '.*\[ *Hardware Error *\].*', 'HWERROR' : r'.*\[ *Hardware Error *\].*',
'FWBUG' : '.*\[ *Firmware Bug *\].*', 'FWBUG' : r'.*\[ *Firmware Bug *\].*',
'BUG' : '.*BUG.*', 'BUG' : r'(?i).*\bBUG\b.*',
'ERROR' : '.*ERROR.*', 'ERROR' : r'(?i).*\bERROR\b.*',
'WARNING' : '.*WARNING.*', 'WARNING' : r'(?i).*\bWARNING\b.*',
'IRQ' : '.*genirq: .*', 'FAULT' : r'(?i).*\bFAULT\b.*',
'TASKFAIL': '.*Freezing of tasks *.*', 'FAIL' : r'(?i).*\bFAILED\b.*',
'ACPI' : '.*ACPI *(?P<b>[A-Za-z]*) *Error[: ].*', 'INVALID' : r'(?i).*\bINVALID\b.*',
'DEVFAIL' : '.* failed to (?P<b>[a-z]*) async: .*', 'CRASH' : r'(?i).*\bCRASHED\b.*',
'DISKFULL': '.*No space left on device.*', 'IRQ' : r'.*\bgenirq: .*',
'USBERR' : '.*usb .*device .*, error [0-9-]*', 'TASKFAIL': r'.*Freezing of tasks *.*',
'ATAERR' : ' *ata[0-9\.]*: .*failed.*', 'ACPI' : r'.*\bACPI *(?P<b>[A-Za-z]*) *Error[: ].*',
'MEIERR' : ' *mei.*: .*failed.*', 'DISKFULL': r'.*\bNo space left on device.*',
'TPMERR' : '(?i) *tpm *tpm[0-9]*: .*error.*', 'USBERR' : r'.*usb .*device .*, error [0-9-]*',
'ATAERR' : r' *ata[0-9\.]*: .*failed.*',
'MEIERR' : r' *mei.*: .*failed.*',
'TPMERR' : r'(?i) *tpm *tpm[0-9]*: .*error.*',
} }
def __init__(self, num): def __init__(self, num):
idchar = 'abcdefghij' idchar = 'abcdefghij'
self.start = 0.0 # test start self.start = 0.0 # test start
self.end = 0.0 # test end self.end = 0.0 # test end
self.hwstart = 0 # rtc test start
self.hwend = 0 # rtc test end
self.tSuspended = 0.0 # low-level suspend start self.tSuspended = 0.0 # low-level suspend start
self.tResumed = 0.0 # low-level resume start self.tResumed = 0.0 # low-level resume start
self.tKernSus = 0.0 # kernel level suspend start self.tKernSus = 0.0 # kernel level suspend start
...@@ -1240,10 +1327,8 @@ class Data: ...@@ -1240,10 +1327,8 @@ class Data:
self.stamp = 0 self.stamp = 0
self.outfile = '' self.outfile = ''
self.kerror = False self.kerror = False
self.battery = 0 self.wifi = dict()
self.wifi = 0
self.turbostat = 0 self.turbostat = 0
self.mcelog = 0
self.enterfail = '' self.enterfail = ''
self.currphase = '' self.currphase = ''
self.pstl = dict() # process timeline self.pstl = dict() # process timeline
...@@ -1308,6 +1393,8 @@ class Data: ...@@ -1308,6 +1393,8 @@ class Data:
continue continue
dir = 'suspend' if t < self.tSuspended else 'resume' dir = 'suspend' if t < self.tSuspended else 'resume'
msg = m.group('msg') msg = m.group('msg')
if re.match('capability: warning: .*', msg):
continue
for err in self.errlist: for err in self.errlist:
if re.match(self.errlist[err], msg): if re.match(self.errlist[err], msg):
list.append((msg, err, dir, t, i, i)) list.append((msg, err, dir, t, i, i))
...@@ -1316,17 +1403,26 @@ class Data: ...@@ -1316,17 +1403,26 @@ class Data:
msglist = [] msglist = []
for msg, type, dir, t, idx1, idx2 in list: for msg, type, dir, t, idx1, idx2 in list:
msglist.append(msg) msglist.append(msg)
sysvals.vprint('kernel %s found in %s at %f' % (type, dir, t))
self.errorinfo[dir].append((type, t, idx1, idx2)) self.errorinfo[dir].append((type, t, idx1, idx2))
if self.kerror: if self.kerror:
sysvals.dmesglog = True sysvals.dmesglog = True
if len(self.dmesgtext) < 1 and sysvals.dmesgfile: if len(self.dmesgtext) < 1 and sysvals.dmesgfile:
lf.close() lf.close()
return msglist return msglist
def setStart(self, time): def setStart(self, time, msg=''):
self.start = time self.start = time
def setEnd(self, time): if msg:
try:
self.hwstart = datetime.strptime(msg, sysvals.tmstart)
except:
self.hwstart = 0
def setEnd(self, time, msg=''):
self.end = time self.end = time
if msg:
try:
self.hwend = datetime.strptime(msg, sysvals.tmend)
except:
self.hwend = 0
def isTraceEventOutsideDeviceCalls(self, pid, time): def isTraceEventOutsideDeviceCalls(self, pid, time):
for phase in self.sortedPhases(): for phase in self.sortedPhases():
list = self.dmesg[phase]['list'] list = self.dmesg[phase]['list']
...@@ -1546,6 +1642,14 @@ class Data: ...@@ -1546,6 +1642,14 @@ class Data:
self.trimTime(tS, tL, left) self.trimTime(tS, tL, left)
self.tLow.append('%.0f'%(tL*1000)) self.tLow.append('%.0f'%(tL*1000))
lp = phase lp = phase
def getMemTime(self):
if not self.hwstart or not self.hwend:
return
stime = (self.tSuspended - self.start) * 1000000
rtime = (self.end - self.tResumed) * 1000000
hws = self.hwstart + timedelta(microseconds=stime)
hwr = self.hwend - timedelta(microseconds=rtime)
self.tLow.append('%.0f'%((hwr - hws).total_seconds() * 1000))
def getTimeValues(self): def getTimeValues(self):
sktime = (self.tSuspended - self.tKernSus) * 1000 sktime = (self.tSuspended - self.tKernSus) * 1000
rktime = (self.tKernRes - self.tResumed) * 1000 rktime = (self.tKernRes - self.tResumed) * 1000
...@@ -1883,9 +1987,9 @@ class Data: ...@@ -1883,9 +1987,9 @@ class Data:
c = self.addProcessUsageEvent(ps, tres) c = self.addProcessUsageEvent(ps, tres)
if c > 0: if c > 0:
sysvals.vprint('%25s (res): %d' % (ps, c)) sysvals.vprint('%25s (res): %d' % (ps, c))
def handleEndMarker(self, time): def handleEndMarker(self, time, msg=''):
dm = self.dmesg dm = self.dmesg
self.setEnd(time) self.setEnd(time, msg)
self.initDevicegroups() self.initDevicegroups()
# give suspend_prepare an end if needed # give suspend_prepare an end if needed
if 'suspend_prepare' in dm and dm['suspend_prepare']['end'] < 0: if 'suspend_prepare' in dm and dm['suspend_prepare']['end'] < 0:
...@@ -2071,7 +2175,7 @@ class FTraceLine: ...@@ -2071,7 +2175,7 @@ class FTraceLine:
if not self.fevent: if not self.fevent:
return False return False
if sysvals.usetracemarkers: if sysvals.usetracemarkers:
if(self.name == 'SUSPEND START'): if(self.name.startswith('SUSPEND START')):
return True return True
return False return False
else: else:
...@@ -2084,7 +2188,7 @@ class FTraceLine: ...@@ -2084,7 +2188,7 @@ class FTraceLine:
if not self.fevent: if not self.fevent:
return False return False
if sysvals.usetracemarkers: if sysvals.usetracemarkers:
if(self.name == 'RESUME COMPLETE'): if(self.name.startswith('RESUME COMPLETE')):
return True return True
return False return False
else: else:
...@@ -2444,7 +2548,7 @@ class Timeline: ...@@ -2444,7 +2548,7 @@ class Timeline:
def createHeader(self, sv, stamp): def createHeader(self, sv, stamp):
if(not stamp['time']): if(not stamp['time']):
return return
self.html += '<div class="version"><a href="https://01.org/suspendresume">%s v%s</a></div>' \ self.html += '<div class="version"><a href="https://01.org/pm-graph">%s v%s</a></div>' \
% (sv.title, sv.version) % (sv.title, sv.version)
if sv.logmsg and sv.testlog: if sv.logmsg and sv.testlog:
self.html += '<button id="showtest" class="logbtn btnfmt">log</button>' self.html += '<button id="showtest" class="logbtn btnfmt">log</button>'
...@@ -2670,14 +2774,11 @@ class TestProps: ...@@ -2670,14 +2774,11 @@ class TestProps:
stampfmt = '# [a-z]*-(?P<m>[0-9]{2})(?P<d>[0-9]{2})(?P<y>[0-9]{2})-'+\ stampfmt = '# [a-z]*-(?P<m>[0-9]{2})(?P<d>[0-9]{2})(?P<y>[0-9]{2})-'+\
'(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\ '(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\
' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$' ' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$'
batteryfmt = '^# battery (?P<a1>\w*) (?P<c1>\d*) (?P<a2>\w*) (?P<c2>\d*)' wififmt = '^# wifi *(?P<d>\S*) *(?P<s>\S*) *(?P<t>[0-9\.]+).*'
wififmt = '^# wifi (?P<w>.*)'
tstatfmt = '^# turbostat (?P<t>\S*)' tstatfmt = '^# turbostat (?P<t>\S*)'
mcelogfmt = '^# mcelog (?P<m>\S*)'
testerrfmt = '^# enter_sleep_error (?P<e>.*)' testerrfmt = '^# enter_sleep_error (?P<e>.*)'
sysinfofmt = '^# sysinfo .*' sysinfofmt = '^# sysinfo .*'
cmdlinefmt = '^# command \| (?P<cmd>.*)' cmdlinefmt = '^# command \| (?P<cmd>.*)'
kparamsfmt = '^# kparams \| (?P<kp>.*)'
devpropfmt = '# Device Properties: .*' devpropfmt = '# Device Properties: .*'
pinfofmt = '# platform-(?P<val>[a-z,A-Z,0-9]*): (?P<info>.*)' pinfofmt = '# platform-(?P<val>[a-z,A-Z,0-9]*): (?P<info>.*)'
tracertypefmt = '# tracer: (?P<t>.*)' tracertypefmt = '# tracer: (?P<t>.*)'
...@@ -2695,11 +2796,8 @@ class TestProps: ...@@ -2695,11 +2796,8 @@ class TestProps:
self.stamp = '' self.stamp = ''
self.sysinfo = '' self.sysinfo = ''
self.cmdline = '' self.cmdline = ''
self.kparams = ''
self.testerror = [] self.testerror = []
self.mcelog = []
self.turbostat = [] self.turbostat = []
self.battery = []
self.wifi = [] self.wifi = []
self.fwdata = [] self.fwdata = []
self.ftrace_line_fmt = self.ftrace_line_fmt_nop self.ftrace_line_fmt = self.ftrace_line_fmt_nop
...@@ -2721,21 +2819,12 @@ class TestProps: ...@@ -2721,21 +2819,12 @@ class TestProps:
elif re.match(self.sysinfofmt, line): elif re.match(self.sysinfofmt, line):
self.sysinfo = line self.sysinfo = line
return True return True
elif re.match(self.kparamsfmt, line):
self.kparams = line
return True
elif re.match(self.cmdlinefmt, line): elif re.match(self.cmdlinefmt, line):
self.cmdline = line self.cmdline = line
return True return True
elif re.match(self.mcelogfmt, line):
self.mcelog.append(line)
return True
elif re.match(self.tstatfmt, line): elif re.match(self.tstatfmt, line):
self.turbostat.append(line) self.turbostat.append(line)
return True return True
elif re.match(self.batteryfmt, line):
self.battery.append(line)
return True
elif re.match(self.wififmt, line): elif re.match(self.wififmt, line):
self.wifi.append(line) self.wifi.append(line)
return True return True
...@@ -2749,6 +2838,8 @@ class TestProps: ...@@ -2749,6 +2838,8 @@ class TestProps:
def parseStamp(self, data, sv): def parseStamp(self, data, sv):
# global test data # global test data
m = re.match(self.stampfmt, self.stamp) m = re.match(self.stampfmt, self.stamp)
if not self.stamp or not m:
doError('data does not include the expected stamp')
data.stamp = {'time': '', 'host': '', 'mode': ''} data.stamp = {'time': '', 'host': '', 'mode': ''}
dt = datetime(int(m.group('y'))+2000, int(m.group('m')), dt = datetime(int(m.group('y'))+2000, int(m.group('m')),
int(m.group('d')), int(m.group('H')), int(m.group('M')), int(m.group('d')), int(m.group('H')), int(m.group('M')),
...@@ -2780,10 +2871,6 @@ class TestProps: ...@@ -2780,10 +2871,6 @@ class TestProps:
m = re.match(self.cmdlinefmt, self.cmdline) m = re.match(self.cmdlinefmt, self.cmdline)
if m: if m:
sv.cmdline = m.group('cmd') sv.cmdline = m.group('cmd')
if self.kparams:
m = re.match(self.kparamsfmt, self.kparams)
if m:
sv.kparams = m.group('kp')
if not sv.stamp: if not sv.stamp:
sv.stamp = data.stamp sv.stamp = data.stamp
# firmware data # firmware data
...@@ -2793,26 +2880,18 @@ class TestProps: ...@@ -2793,26 +2880,18 @@ class TestProps:
data.fwSuspend, data.fwResume = int(m.group('s')), int(m.group('r')) data.fwSuspend, data.fwResume = int(m.group('s')), int(m.group('r'))
if(data.fwSuspend > 0 or data.fwResume > 0): if(data.fwSuspend > 0 or data.fwResume > 0):
data.fwValid = True data.fwValid = True
# mcelog data
if len(self.mcelog) > data.testnumber:
m = re.match(self.mcelogfmt, self.mcelog[data.testnumber])
if m:
data.mcelog = sv.b64unzip(m.group('m'))
# turbostat data # turbostat data
if len(self.turbostat) > data.testnumber: if len(self.turbostat) > data.testnumber:
m = re.match(self.tstatfmt, self.turbostat[data.testnumber]) m = re.match(self.tstatfmt, self.turbostat[data.testnumber])
if m: if m:
data.turbostat = m.group('t') data.turbostat = m.group('t')
# battery data
if len(self.battery) > data.testnumber:
m = re.match(self.batteryfmt, self.battery[data.testnumber])
if m:
data.battery = m.groups()
# wifi data # wifi data
if len(self.wifi) > data.testnumber: if len(self.wifi) > data.testnumber:
m = re.match(self.wififmt, self.wifi[data.testnumber]) m = re.match(self.wififmt, self.wifi[data.testnumber])
if m: if m:
data.wifi = m.group('w') data.wifi = {'dev': m.group('d'), 'stat': m.group('s'),
'time': float(m.group('t'))}
data.stamp['wifi'] = m.group('d')
# sleep mode enter errors # sleep mode enter errors
if len(self.testerror) > data.testnumber: if len(self.testerror) > data.testnumber:
m = re.match(self.testerrfmt, self.testerror[data.testnumber]) m = re.match(self.testerrfmt, self.testerror[data.testnumber])
...@@ -3012,13 +3091,13 @@ def appendIncompleteTraceLog(testruns): ...@@ -3012,13 +3091,13 @@ def appendIncompleteTraceLog(testruns):
if(t.startMarker()): if(t.startMarker()):
data = testrun[testidx].data data = testrun[testidx].data
tp.parseStamp(data, sysvals) tp.parseStamp(data, sysvals)
data.setStart(t.time) data.setStart(t.time, t.name)
continue continue
if(not data): if(not data):
continue continue
# find the end of resume # find the end of resume
if(t.endMarker()): if(t.endMarker()):
data.setEnd(t.time) data.setEnd(t.time, t.name)
testidx += 1 testidx += 1
if(testidx >= testcnt): if(testidx >= testcnt):
break break
...@@ -3081,7 +3160,7 @@ def parseTraceLog(live=False): ...@@ -3081,7 +3160,7 @@ def parseTraceLog(live=False):
doError('%s does not exist' % sysvals.ftracefile) doError('%s does not exist' % sysvals.ftracefile)
if not live: if not live:
sysvals.setupAllKprobes() sysvals.setupAllKprobes()
ksuscalls = ['pm_prepare_console'] ksuscalls = ['ksys_sync', 'pm_prepare_console']
krescalls = ['pm_restore_console'] krescalls = ['pm_restore_console']
tracewatch = ['irq_wakeup'] tracewatch = ['irq_wakeup']
if sysvals.usekprobes: if sysvals.usekprobes:
...@@ -3094,7 +3173,7 @@ def parseTraceLog(live=False): ...@@ -3094,7 +3173,7 @@ def parseTraceLog(live=False):
testruns = [] testruns = []
testdata = [] testdata = []
testrun = 0 testrun = 0
data = 0 data, limbo = 0, True
tf = sysvals.openlog(sysvals.ftracefile, 'r') tf = sysvals.openlog(sysvals.ftracefile, 'r')
phase = 'suspend_prepare' phase = 'suspend_prepare'
for line in tf: for line in tf:
...@@ -3141,16 +3220,16 @@ def parseTraceLog(live=False): ...@@ -3141,16 +3220,16 @@ def parseTraceLog(live=False):
continue continue
# find the start of suspend # find the start of suspend
if(t.startMarker()): if(t.startMarker()):
data = Data(len(testdata)) data, limbo = Data(len(testdata)), False
testdata.append(data) testdata.append(data)
testrun = TestRun(data) testrun = TestRun(data)
testruns.append(testrun) testruns.append(testrun)
tp.parseStamp(data, sysvals) tp.parseStamp(data, sysvals)
data.setStart(t.time) data.setStart(t.time, t.name)
data.first_suspend_prepare = True data.first_suspend_prepare = True
phase = data.setPhase('suspend_prepare', t.time, True) phase = data.setPhase('suspend_prepare', t.time, True)
continue continue
if(not data): if(not data or limbo):
continue continue
# process cpu exec line # process cpu exec line
if t.type == 'tracing_mark_write': if t.type == 'tracing_mark_write':
...@@ -3167,14 +3246,16 @@ def parseTraceLog(live=False): ...@@ -3167,14 +3246,16 @@ def parseTraceLog(live=False):
continue continue
# find the end of resume # find the end of resume
if(t.endMarker()): if(t.endMarker()):
data.handleEndMarker(t.time) if data.tKernRes == 0:
data.tKernRes = t.time
data.handleEndMarker(t.time, t.name)
if(not sysvals.usetracemarkers): if(not sysvals.usetracemarkers):
# no trace markers? then quit and be sure to finish recording # no trace markers? then quit and be sure to finish recording
# the event we used to trigger resume end # the event we used to trigger resume end
if('thaw_processes' in testrun.ttemp and len(testrun.ttemp['thaw_processes']) > 0): if('thaw_processes' in testrun.ttemp and len(testrun.ttemp['thaw_processes']) > 0):
# if an entry exists, assume this is its end # if an entry exists, assume this is its end
testrun.ttemp['thaw_processes'][-1]['end'] = t.time testrun.ttemp['thaw_processes'][-1]['end'] = t.time
break limbo = True
continue continue
# trace event processing # trace event processing
if(t.fevent): if(t.fevent):
...@@ -3197,7 +3278,7 @@ def parseTraceLog(live=False): ...@@ -3197,7 +3278,7 @@ def parseTraceLog(live=False):
# -- phase changes -- # -- phase changes --
# start of kernel suspend # start of kernel suspend
if(re.match('suspend_enter\[.*', t.name)): if(re.match('suspend_enter\[.*', t.name)):
if(isbegin): if(isbegin and data.tKernSus == 0):
data.tKernSus = t.time data.tKernSus = t.time
continue continue
# suspend_prepare start # suspend_prepare start
...@@ -3225,7 +3306,7 @@ def parseTraceLog(live=False): ...@@ -3225,7 +3306,7 @@ def parseTraceLog(live=False):
elif(re.match('machine_suspend\[.*', t.name)): elif(re.match('machine_suspend\[.*', t.name)):
if(isbegin): if(isbegin):
lp = data.lastPhase() lp = data.lastPhase()
if lp == 'resume_machine': if lp.startswith('resume_machine'):
data.dmesg[lp]['end'] = t.time data.dmesg[lp]['end'] = t.time
phase = data.setPhase('suspend_machine', data.dmesg[lp]['end'], True) phase = data.setPhase('suspend_machine', data.dmesg[lp]['end'], True)
data.setPhase(phase, t.time, False) data.setPhase(phase, t.time, False)
...@@ -3320,7 +3401,8 @@ def parseTraceLog(live=False): ...@@ -3320,7 +3401,8 @@ def parseTraceLog(live=False):
'proc': m_proc, 'proc': m_proc,
}) })
# start of kernel resume # start of kernel resume
if(phase == 'suspend_prepare' and kprobename in ksuscalls): if(data.tKernSus == 0 and phase == 'suspend_prepare' \
and kprobename in ksuscalls):
data.tKernSus = t.time data.tKernSus = t.time
elif(t.freturn): elif(t.freturn):
if(key not in tp.ktemp) or len(tp.ktemp[key]) < 1: if(key not in tp.ktemp) or len(tp.ktemp[key]) < 1:
...@@ -3355,7 +3437,7 @@ def parseTraceLog(live=False): ...@@ -3355,7 +3437,7 @@ def parseTraceLog(live=False):
sysvals.vprint('WARNING: ftrace start marker is missing') sysvals.vprint('WARNING: ftrace start marker is missing')
if data and not data.devicegroups: if data and not data.devicegroups:
sysvals.vprint('WARNING: ftrace end marker is missing') sysvals.vprint('WARNING: ftrace end marker is missing')
data.handleEndMarker(t.time) data.handleEndMarker(t.time, t.name)
if sysvals.suspendmode == 'command': if sysvals.suspendmode == 'command':
for test in testruns: for test in testruns:
...@@ -3476,6 +3558,10 @@ def parseTraceLog(live=False): ...@@ -3476,6 +3558,10 @@ def parseTraceLog(live=False):
data.fwValid = False data.fwValid = False
sysvals.vprint('WARNING: phase "%s" is missing!' % p) sysvals.vprint('WARNING: phase "%s" is missing!' % p)
lp = p lp = p
if not terr and 'dev' in data.wifi and data.wifi['stat'] == 'timeout':
terr = '%s%s failed in wifi_resume <i>(%s %.0fs timeout)</i>' % \
(sysvals.suspendmode, tn, data.wifi['dev'], data.wifi['time'])
error.append(terr)
if not terr and data.enterfail: if not terr and data.enterfail:
pprint('test%s FAILED: enter %s failed with %s' % (tn, sysvals.suspendmode, data.enterfail)) pprint('test%s FAILED: enter %s failed with %s' % (tn, sysvals.suspendmode, data.enterfail))
terr = 'test%s failed to enter %s mode' % (tn, sysvals.suspendmode) terr = 'test%s failed to enter %s mode' % (tn, sysvals.suspendmode)
...@@ -3933,7 +4019,7 @@ def createHTMLSummarySimple(testruns, htmlfile, title): ...@@ -3933,7 +4019,7 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()] tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()]
iMin, iMed, iMax = [0, 0], [0, 0], [0, 0] iMin, iMed, iMax = [0, 0], [0, 0], [0, 0]
num = 0 num = 0
useturbo = False useturbo = usewifi = False
lastmode = '' lastmode = ''
cnt = dict() cnt = dict()
for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'], v['time'])): for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'], v['time'])):
...@@ -3952,17 +4038,17 @@ def createHTMLSummarySimple(testruns, htmlfile, title): ...@@ -3952,17 +4038,17 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()] tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()]
iMin, iMed, iMax = [0, 0], [0, 0], [0, 0] iMin, iMed, iMax = [0, 0], [0, 0], [0, 0]
num = 0 num = 0
pkgpc10 = syslpi = '' pkgpc10 = syslpi = wifi = ''
if 'pkgpc10' in data and 'syslpi' in data: if 'pkgpc10' in data and 'syslpi' in data:
pkgpc10 = data['pkgpc10'] pkgpc10, syslpi, useturbo = data['pkgpc10'], data['syslpi'], True
syslpi = data['syslpi'] if 'wifi' in data:
useturbo = True wifi, usewifi = data['wifi'], True
res = data['result'] res = data['result']
tVal = [float(data['suspend']), float(data['resume'])] tVal = [float(data['suspend']), float(data['resume'])]
list[mode]['data'].append([data['host'], data['kernel'], list[mode]['data'].append([data['host'], data['kernel'],
data['time'], tVal[0], tVal[1], data['url'], res, data['time'], tVal[0], tVal[1], data['url'], res,
data['issues'], data['sus_worst'], data['sus_worsttime'], data['issues'], data['sus_worst'], data['sus_worsttime'],
data['res_worst'], data['res_worsttime'], pkgpc10, syslpi]) data['res_worst'], data['res_worsttime'], pkgpc10, syslpi, wifi])
idx = len(list[mode]['data']) - 1 idx = len(list[mode]['data']) - 1
if res.startswith('fail in'): if res.startswith('fail in'):
res = 'fail' res = 'fail'
...@@ -4002,7 +4088,12 @@ def createHTMLSummarySimple(testruns, htmlfile, title): ...@@ -4002,7 +4088,12 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
td = '\t<td>{0}</td>\n' td = '\t<td>{0}</td>\n'
tdh = '\t<td{1}>{0}</td>\n' tdh = '\t<td{1}>{0}</td>\n'
tdlink = '\t<td><a href="{0}">html</a></td>\n' tdlink = '\t<td><a href="{0}">html</a></td>\n'
colspan = '14' if useturbo else '12' cols = 12
if useturbo:
cols += 2
if usewifi:
cols += 1
colspan = '%d' % cols
# table header # table header
html += '<table>\n<tr>\n' + th.format('#') +\ html += '<table>\n<tr>\n' + th.format('#') +\
...@@ -4013,6 +4104,8 @@ def createHTMLSummarySimple(testruns, htmlfile, title): ...@@ -4013,6 +4104,8 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
th.format('Worst Resume Device') + th.format('RD Time') th.format('Worst Resume Device') + th.format('RD Time')
if useturbo: if useturbo:
html += th.format('PkgPC10') + th.format('SysLPI') html += th.format('PkgPC10') + th.format('SysLPI')
if usewifi:
html += th.format('Wifi')
html += th.format('Detail')+'</tr>\n' html += th.format('Detail')+'</tr>\n'
# export list into html # export list into html
head = '<tr class="head"><td>{0}</td><td>{1}</td>'+\ head = '<tr class="head"><td>{0}</td><td>{1}</td>'+\
...@@ -4076,6 +4169,8 @@ def createHTMLSummarySimple(testruns, htmlfile, title): ...@@ -4076,6 +4169,8 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
if useturbo: if useturbo:
html += td.format(d[12]) # pkg_pc10 html += td.format(d[12]) # pkg_pc10
html += td.format(d[13]) # syslpi html += td.format(d[13]) # syslpi
if usewifi:
html += td.format(d[14]) # wifi
html += tdlink.format(d[5]) if d[5] else td.format('') # url html += tdlink.format(d[5]) if d[5] else td.format('') # url
html += '</tr>\n' html += '</tr>\n'
num += 1 num += 1
...@@ -4224,6 +4319,8 @@ def createHTML(testruns, testfail): ...@@ -4224,6 +4319,8 @@ def createHTML(testruns, testfail):
kerror = True kerror = True
if(sysvals.suspendmode in ['freeze', 'standby']): if(sysvals.suspendmode in ['freeze', 'standby']):
data.trimFreezeTime(testruns[-1].tSuspended) data.trimFreezeTime(testruns[-1].tSuspended)
else:
data.getMemTime()
# html function templates # html function templates
html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">{2}&rarr;</div>\n' html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">{2}&rarr;</div>\n'
...@@ -4242,13 +4339,10 @@ def createHTML(testruns, testfail): ...@@ -4242,13 +4339,10 @@ def createHTML(testruns, testfail):
'<td class="green">Execution Time: <b>{0} ms</b></td>'\ '<td class="green">Execution Time: <b>{0} ms</b></td>'\
'<td class="yellow">Command: <b>{1}</b></td>'\ '<td class="yellow">Command: <b>{1}</b></td>'\
'</tr>\n</table>\n' '</tr>\n</table>\n'
html_timegroups = '<table class="time2">\n<tr>'\
'<td class="green" title="time from kernel enter_state({5}) to firmware mode [kernel time only]">{4}Kernel Suspend: {0} ms</td>'\
'<td class="purple">{4}Firmware Suspend: {1} ms</td>'\
'<td class="purple">{4}Firmware Resume: {2} ms</td>'\
'<td class="yellow" title="time from firmware mode to return from kernel enter_state({5}) [kernel time only]">{4}Kernel Resume: {3} ms</td>'\
'</tr>\n</table>\n'
html_fail = '<table class="testfail"><tr><td>{0}</td></tr></table>\n' html_fail = '<table class="testfail"><tr><td>{0}</td></tr></table>\n'
html_kdesc = '<td class="{3}" title="time spent in kernel execution">{0}Kernel {2}: {1} ms</td>'
html_fwdesc = '<td class="{3}" title="time spent in firmware">{0}Firmware {2}: {1} ms</td>'
html_wifdesc = '<td class="yellow" title="time for wifi to reconnect after resume complete ({2})">{0}Wifi Resume: {1}</td>'
# html format variables # html format variables
scaleH = 20 scaleH = 20
...@@ -4264,13 +4358,10 @@ def createHTML(testruns, testfail): ...@@ -4264,13 +4358,10 @@ def createHTML(testruns, testfail):
# Generate the header for this timeline # Generate the header for this timeline
for data in testruns: for data in testruns:
tTotal = data.end - data.start tTotal = data.end - data.start
sktime, rktime = data.getTimeValues()
if(tTotal == 0): if(tTotal == 0):
doError('No timeline data') doError('No timeline data')
if(len(data.tLow) > 0):
low_time = '+'.join(data.tLow)
if sysvals.suspendmode == 'command': if sysvals.suspendmode == 'command':
run_time = '%.0f'%((data.end-data.start)*1000) run_time = '%.0f' % (tTotal * 1000)
if sysvals.testcommand: if sysvals.testcommand:
testdesc = sysvals.testcommand testdesc = sysvals.testcommand
else: else:
...@@ -4279,43 +4370,55 @@ def createHTML(testruns, testfail): ...@@ -4279,43 +4370,55 @@ def createHTML(testruns, testfail):
testdesc = ordinal(data.testnumber+1)+' '+testdesc testdesc = ordinal(data.testnumber+1)+' '+testdesc
thtml = html_timetotal3.format(run_time, testdesc) thtml = html_timetotal3.format(run_time, testdesc)
devtl.html += thtml devtl.html += thtml
elif data.fwValid: continue
suspend_time = '%.0f'%(sktime + (data.fwSuspend/1000000.0)) # typical full suspend/resume header
resume_time = '%.0f'%(rktime + (data.fwResume/1000000.0)) stot, rtot = sktime, rktime = data.getTimeValues()
testdesc1 = 'Total' ssrc, rsrc, testdesc, testdesc2 = ['kernel'], ['kernel'], 'Kernel', ''
testdesc2 = '' if data.fwValid:
stitle = 'time from kernel enter_state(%s) to low-power mode [kernel & firmware time]' % sysvals.suspendmode stot += (data.fwSuspend/1000000.0)
rtitle = 'time from low-power mode to return from kernel enter_state(%s) [firmware & kernel time]' % sysvals.suspendmode rtot += (data.fwResume/1000000.0)
ssrc.append('firmware')
rsrc.append('firmware')
testdesc = 'Total'
if 'time' in data.wifi and data.wifi['stat'] != 'timeout':
rtot += data.end - data.tKernRes + (data.wifi['time'] * 1000.0)
rsrc.append('wifi')
testdesc = 'Total'
suspend_time, resume_time = '%.3f' % stot, '%.3f' % rtot
stitle = 'time from kernel suspend start to %s mode [%s time]' % \
(sysvals.suspendmode, ' & '.join(ssrc))
rtitle = 'time from %s mode to kernel resume complete [%s time]' % \
(sysvals.suspendmode, ' & '.join(rsrc))
if(len(testruns) > 1): if(len(testruns) > 1):
testdesc1 = testdesc2 = ordinal(data.testnumber+1) testdesc = testdesc2 = ordinal(data.testnumber+1)
testdesc2 += ' ' testdesc2 += ' '
if(len(data.tLow) == 0): if(len(data.tLow) == 0):
thtml = html_timetotal.format(suspend_time, \ thtml = html_timetotal.format(suspend_time, \
resume_time, testdesc1, stitle, rtitle) resume_time, testdesc, stitle, rtitle)
else: else:
low_time = '+'.join(data.tLow)
thtml = html_timetotal2.format(suspend_time, low_time, \ thtml = html_timetotal2.format(suspend_time, low_time, \
resume_time, testdesc1, stitle, rtitle) resume_time, testdesc, stitle, rtitle)
devtl.html += thtml devtl.html += thtml
if not data.fwValid and 'dev' not in data.wifi:
continue
# extra detail when the times come from multiple sources
thtml = '<table class="time2">\n<tr>'
thtml += html_kdesc.format(testdesc2, '%.3f'%sktime, 'Suspend', 'green')
if data.fwValid:
sftime = '%.3f'%(data.fwSuspend / 1000000.0) sftime = '%.3f'%(data.fwSuspend / 1000000.0)
rftime = '%.3f'%(data.fwResume / 1000000.0) rftime = '%.3f'%(data.fwResume / 1000000.0)
devtl.html += html_timegroups.format('%.3f'%sktime, \ thtml += html_fwdesc.format(testdesc2, sftime, 'Suspend', 'green')
sftime, rftime, '%.3f'%rktime, testdesc2, sysvals.suspendmode) thtml += html_fwdesc.format(testdesc2, rftime, 'Resume', 'yellow')
else: thtml += html_kdesc.format(testdesc2, '%.3f'%rktime, 'Resume', 'yellow')
suspend_time = '%.3f' % sktime if 'time' in data.wifi:
resume_time = '%.3f' % rktime if data.wifi['stat'] != 'timeout':
testdesc = 'Kernel' wtime = '%.0f ms'%(data.end - data.tKernRes + (data.wifi['time'] * 1000.0))
stitle = 'time from kernel enter_state(%s) to firmware mode [kernel time only]' % sysvals.suspendmode
rtitle = 'time from firmware mode to return from kernel enter_state(%s) [kernel time only]' % sysvals.suspendmode
if(len(testruns) > 1):
testdesc = ordinal(data.testnumber+1)+' '+testdesc
if(len(data.tLow) == 0):
thtml = html_timetotal.format(suspend_time, \
resume_time, testdesc, stitle, rtitle)
else: else:
thtml = html_timetotal2.format(suspend_time, low_time, \ wtime = 'TIMEOUT'
resume_time, testdesc, stitle, rtitle) thtml += html_wifdesc.format(testdesc2, wtime, data.wifi['dev'])
thtml += '</tr>\n</table>\n'
devtl.html += thtml devtl.html += thtml
if testfail: if testfail:
devtl.html += html_fail.format(testfail) devtl.html += html_fail.format(testfail)
...@@ -5082,28 +5185,32 @@ def setRuntimeSuspend(before=True): ...@@ -5082,28 +5185,32 @@ def setRuntimeSuspend(before=True):
# Description: # Description:
# Execute system suspend through the sysfs interface, then copy the output # Execute system suspend through the sysfs interface, then copy the output
# dmesg and ftrace files to the test output directory. # dmesg and ftrace files to the test output directory.
def executeSuspend(): def executeSuspend(quiet=False):
pm = ProcessMonitor() pm = ProcessMonitor()
tp = sysvals.tpath tp = sysvals.tpath
if sysvals.wifi:
wifi = sysvals.checkWifi() wifi = sysvals.checkWifi()
testdata = [] testdata = []
battery = True if getBattery() else False
# run these commands to prepare the system for suspend # run these commands to prepare the system for suspend
if sysvals.display: if sysvals.display:
if not quiet:
pprint('SET DISPLAY TO %s' % sysvals.display.upper()) pprint('SET DISPLAY TO %s' % sysvals.display.upper())
displayControl(sysvals.display) displayControl(sysvals.display)
time.sleep(1) time.sleep(1)
if sysvals.sync: if sysvals.sync:
if not quiet:
pprint('SYNCING FILESYSTEMS') pprint('SYNCING FILESYSTEMS')
call('sync', shell=True) call('sync', shell=True)
# mark the start point in the kernel ring buffer just as we start # mark the start point in the kernel ring buffer just as we start
sysvals.initdmesg() sysvals.initdmesg()
# start ftrace # start ftrace
if(sysvals.usecallgraph or sysvals.usetraceevents): if(sysvals.usecallgraph or sysvals.usetraceevents):
if not quiet:
pprint('START TRACING') pprint('START TRACING')
sysvals.fsetVal('1', 'tracing_on') sysvals.fsetVal('1', 'tracing_on')
if sysvals.useprocmon: if sysvals.useprocmon:
pm.start() pm.start()
sysvals.cmdinfo(True)
# execute however many s/r runs requested # execute however many s/r runs requested
for count in range(1,sysvals.execcount+1): for count in range(1,sysvals.execcount+1):
# x2delay in between test runs # x2delay in between test runs
...@@ -5119,15 +5226,14 @@ def executeSuspend(): ...@@ -5119,15 +5226,14 @@ def executeSuspend():
pprint('SUSPEND START') pprint('SUSPEND START')
else: else:
pprint('SUSPEND START (press a key to resume)') pprint('SUSPEND START (press a key to resume)')
sysvals.mcelog(True)
bat1 = getBattery() if battery else False
# set rtcwake # set rtcwake
if(sysvals.rtcwake): if(sysvals.rtcwake):
if not quiet:
pprint('will issue an rtcwake in %d seconds' % sysvals.rtcwaketime) pprint('will issue an rtcwake in %d seconds' % sysvals.rtcwaketime)
sysvals.rtcWakeAlarmOn() sysvals.rtcWakeAlarmOn()
# start of suspend trace marker # start of suspend trace marker
if(sysvals.usecallgraph or sysvals.usetraceevents): if(sysvals.usecallgraph or sysvals.usetraceevents):
sysvals.fsetVal('SUSPEND START', 'trace_marker') sysvals.fsetVal(datetime.now().strftime(sysvals.tmstart), 'trace_marker')
# predelay delay # predelay delay
if(count == 1 and sysvals.predelay > 0): if(count == 1 and sysvals.predelay > 0):
sysvals.fsetVal('WAIT %d' % sysvals.predelay, 'trace_marker') sysvals.fsetVal('WAIT %d' % sysvals.predelay, 'trace_marker')
...@@ -5174,28 +5280,25 @@ def executeSuspend(): ...@@ -5174,28 +5280,25 @@ def executeSuspend():
# return from suspend # return from suspend
pprint('RESUME COMPLETE') pprint('RESUME COMPLETE')
if(sysvals.usecallgraph or sysvals.usetraceevents): if(sysvals.usecallgraph or sysvals.usetraceevents):
sysvals.fsetVal('RESUME COMPLETE', 'trace_marker') sysvals.fsetVal(datetime.now().strftime(sysvals.tmend), 'trace_marker')
if sysvals.wifi and wifi:
tdata['wifi'] = sysvals.pollWifi(wifi)
if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'): if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'):
tdata['fw'] = getFPDT(False) tdata['fw'] = getFPDT(False)
mcelog = sysvals.mcelog()
if mcelog:
tdata['mcelog'] = mcelog
bat2 = getBattery() if battery else False
if battery and bat1 and bat2:
tdata['bat'] = (bat1, bat2)
if 'device' in wifi and 'ip' in wifi:
tdata['wifi'] = (wifi, sysvals.checkWifi())
testdata.append(tdata) testdata.append(tdata)
cmdafter = sysvals.cmdinfo(False)
# stop ftrace # stop ftrace
if(sysvals.usecallgraph or sysvals.usetraceevents): if(sysvals.usecallgraph or sysvals.usetraceevents):
if sysvals.useprocmon: if sysvals.useprocmon:
pm.stop() pm.stop()
sysvals.fsetVal('0', 'tracing_on') sysvals.fsetVal('0', 'tracing_on')
# grab a copy of the dmesg output # grab a copy of the dmesg output
if not quiet:
pprint('CAPTURING DMESG') pprint('CAPTURING DMESG')
sysvals.getdmesg(testdata) sysvals.getdmesg(testdata)
# grab a copy of the ftrace output # grab a copy of the ftrace output
if(sysvals.usecallgraph or sysvals.usetraceevents): if(sysvals.usecallgraph or sysvals.usetraceevents):
if not quiet:
pprint('CAPTURING TRACE') pprint('CAPTURING TRACE')
op = sysvals.writeDatafileHeader(sysvals.ftracefile, testdata) op = sysvals.writeDatafileHeader(sysvals.ftracefile, testdata)
fp = open(tp+'trace', 'r') fp = open(tp+'trace', 'r')
...@@ -5203,8 +5306,7 @@ def executeSuspend(): ...@@ -5203,8 +5306,7 @@ def executeSuspend():
op.write(line) op.write(line)
op.close() op.close()
sysvals.fsetVal('', 'trace') sysvals.fsetVal('', 'trace')
sysvals.platforminfo() sysvals.platforminfo(cmdafter)
return testdata
def readFile(file): def readFile(file):
if os.path.islink(file): if os.path.islink(file):
...@@ -5447,25 +5549,6 @@ def dmidecode(mempath, fatal=False): ...@@ -5447,25 +5549,6 @@ def dmidecode(mempath, fatal=False):
count += 1 count += 1
return out return out
def getBattery():
p, charge, bat = '/sys/class/power_supply', 0, {}
if not os.path.exists(p):
return False
for d in os.listdir(p):
type = sysvals.getVal(os.path.join(p, d, 'type')).strip().lower()
if type != 'battery':
continue
for v in ['status', 'energy_now', 'capacity_now']:
bat[v] = sysvals.getVal(os.path.join(p, d, v)).strip().lower()
break
if 'status' not in bat:
return False
ac = False if 'discharging' in bat['status'] else True
for v in ['energy_now', 'capacity_now']:
if v in bat and bat[v]:
charge = int(bat[v])
return (ac, charge)
def displayControl(cmd): def displayControl(cmd):
xset, ret = 'timeout 10 xset -d :0.0 {0}', 0 xset, ret = 'timeout 10 xset -d :0.0 {0}', 0
if sysvals.sudouser: if sysvals.sudouser:
...@@ -5715,6 +5798,17 @@ def statusCheck(probecheck=False): ...@@ -5715,6 +5798,17 @@ def statusCheck(probecheck=False):
status = 'rtcwake is not properly supported' status = 'rtcwake is not properly supported'
pprint(' is rtcwake supported: %s' % res) pprint(' is rtcwake supported: %s' % res)
# check info commands
pprint(' optional commands this tool may use for info:')
no = sysvals.colorText('MISSING')
yes = sysvals.colorText('FOUND', 32)
for c in ['turbostat', 'mcelog', 'lspci', 'lsusb']:
if c == 'turbostat':
res = yes if sysvals.haveTurbostat() else no
else:
res = yes if sysvals.getExec(c) else no
pprint(' %s: %s' % (c, res))
if not probecheck: if not probecheck:
return status return status
...@@ -5780,7 +5874,8 @@ def getArgFloat(name, args, min, max, main=True): ...@@ -5780,7 +5874,8 @@ def getArgFloat(name, args, min, max, main=True):
doError(name+': value should be between %f and %f' % (min, max), True) doError(name+': value should be between %f and %f' % (min, max), True)
return val return val
def processData(live=False): def processData(live=False, quiet=False):
if not quiet:
pprint('PROCESSING DATA') pprint('PROCESSING DATA')
sysvals.vprint('usetraceevents=%s, usetracemarkers=%s, usekprobes=%s' % \ sysvals.vprint('usetraceevents=%s, usetracemarkers=%s, usekprobes=%s' % \
(sysvals.usetraceevents, sysvals.usetracemarkers, sysvals.usekprobes)) (sysvals.usetraceevents, sysvals.usetracemarkers, sysvals.usekprobes))
...@@ -5796,20 +5891,17 @@ def processData(live=False): ...@@ -5796,20 +5891,17 @@ def processData(live=False):
parseKernelLog(data) parseKernelLog(data)
if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)): if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)):
appendIncompleteTraceLog(testruns) appendIncompleteTraceLog(testruns)
if not sysvals.stamp:
pprint('ERROR: data does not include the expected stamp')
return (testruns, {'error': 'timeline generation failed'})
shown = ['bios', 'biosdate', 'cpu', 'host', 'kernel', 'man', 'memfr', shown = ['bios', 'biosdate', 'cpu', 'host', 'kernel', 'man', 'memfr',
'memsz', 'mode', 'numcpu', 'plat', 'time'] 'memsz', 'mode', 'numcpu', 'plat', 'time', 'wifi']
sysvals.vprint('System Info:') sysvals.vprint('System Info:')
for key in sorted(sysvals.stamp): for key in sorted(sysvals.stamp):
if key in shown: if key in shown:
sysvals.vprint(' %-8s : %s' % (key.upper(), sysvals.stamp[key])) 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) sysvals.vprint('Command:\n %s' % sysvals.cmdline)
for data in testruns: for data in testruns:
if data.mcelog:
sysvals.vprint('MCELOG Data:')
for line in data.mcelog.split('\n'):
sysvals.vprint(' %s' % line)
if data.turbostat: if data.turbostat:
idx, s = 0, 'Turbostat:\n ' idx, s = 0, 'Turbostat:\n '
for val in data.turbostat.split('|'): for val in data.turbostat.split('|'):
...@@ -5819,21 +5911,11 @@ def processData(live=False): ...@@ -5819,21 +5911,11 @@ def processData(live=False):
s += '\n ' s += '\n '
s += val + ' ' s += val + ' '
sysvals.vprint(s) sysvals.vprint(s)
if data.battery:
a1, c1, a2, c2 = data.battery
s = 'Battery:\n Before - AC: %s, Charge: %d\n After - AC: %s, Charge: %d' % \
(a1, int(c1), a2, int(c2))
sysvals.vprint(s)
if data.wifi:
w = data.wifi.replace('|', ' ').split(',')
s = 'Wifi:\n Before %s\n After %s' % \
(w[0], w[1])
sysvals.vprint(s)
data.printDetails() data.printDetails()
if len(sysvals.platinfo) > 0: if len(sysvals.platinfo) > 0:
sysvals.vprint('\nPlatform Info:') sysvals.vprint('\nPlatform Info:')
for info in sysvals.platinfo: for info in sysvals.platinfo:
sysvals.vprint(info[0]+' - '+info[1]) sysvals.vprint('[%s - %s]' % (info[0], info[1]))
sysvals.vprint(info[2]) sysvals.vprint(info[2])
sysvals.vprint('') sysvals.vprint('')
if sysvals.cgdump: if sysvals.cgdump:
...@@ -5845,6 +5927,7 @@ def processData(live=False): ...@@ -5845,6 +5927,7 @@ def processData(live=False):
return (testruns, {'error': 'timeline generation failed'}) return (testruns, {'error': 'timeline generation failed'})
sysvals.vprint('Creating the html timeline (%s)...' % sysvals.htmlfile) sysvals.vprint('Creating the html timeline (%s)...' % sysvals.htmlfile)
createHTML(testruns, error) createHTML(testruns, error)
if not quiet:
pprint('DONE') pprint('DONE')
data = testruns[0] data = testruns[0]
stamp = data.stamp stamp = data.stamp
...@@ -5872,31 +5955,28 @@ def rerunTest(htmlfile=''): ...@@ -5872,31 +5955,28 @@ def rerunTest(htmlfile=''):
doError('a directory already exists with this name: %s' % sysvals.htmlfile) doError('a directory already exists with this name: %s' % sysvals.htmlfile)
elif not os.access(sysvals.htmlfile, os.W_OK): elif not os.access(sysvals.htmlfile, os.W_OK):
doError('missing permission to write to %s' % sysvals.htmlfile) doError('missing permission to write to %s' % sysvals.htmlfile)
testruns, stamp = processData(False) testruns, stamp = processData()
sysvals.logmsg = '' sysvals.resetlog()
return stamp return stamp
# Function: runTest # Function: runTest
# Description: # Description:
# execute a suspend/resume, gather the logs, and generate the output # execute a suspend/resume, gather the logs, and generate the output
def runTest(n=0): def runTest(n=0, quiet=False):
# prepare for the test # prepare for the test
sysvals.initFtrace() sysvals.initFtrace(quiet)
sysvals.initTestOutput('suspend') sysvals.initTestOutput('suspend')
# execute the test # execute the test
testdata = executeSuspend() executeSuspend(quiet)
sysvals.cleanupFtrace() sysvals.cleanupFtrace()
if sysvals.skiphtml: if sysvals.skiphtml:
sysvals.outputResult({}, n)
sysvals.sudoUserchown(sysvals.testdir) sysvals.sudoUserchown(sysvals.testdir)
return return
if not testdata[0]['error']: testruns, stamp = processData(True, quiet)
testruns, stamp = processData(True)
for data in testruns: for data in testruns:
del data del data
else:
stamp = testdata[0]
sysvals.sudoUserchown(sysvals.testdir) sysvals.sudoUserchown(sysvals.testdir)
sysvals.outputResult(stamp, n) sysvals.outputResult(stamp, n)
if 'error' in stamp: if 'error' in stamp:
...@@ -5904,13 +5984,14 @@ def runTest(n=0): ...@@ -5904,13 +5984,14 @@ def runTest(n=0):
return 0 return 0
def find_in_html(html, start, end, firstonly=True): def find_in_html(html, start, end, firstonly=True):
n, out = 0, [] n, cnt, out = 0, len(html), []
while n < len(html): while n < cnt:
m = re.search(start, html[n:]) e = cnt if (n + 10000 > cnt or n == 0) else n + 10000
m = re.search(start, html[n:e])
if not m: if not m:
break break
i = m.end() i = m.end()
m = re.search(end, html[n+i:]) m = re.search(end, html[n+i:e])
if not m: if not m:
break break
j = m.start() j = m.start()
...@@ -5945,7 +6026,7 @@ def data_from_html(file, outpath, issues, fulldetail=False): ...@@ -5945,7 +6026,7 @@ def data_from_html(file, outpath, issues, fulldetail=False):
tstr = dt.strftime('%Y/%m/%d %H:%M:%S') tstr = dt.strftime('%Y/%m/%d %H:%M:%S')
error = find_in_html(html, '<table class="testfail"><tr><td>', '</td>') error = find_in_html(html, '<table class="testfail"><tr><td>', '</td>')
if error: if error:
m = re.match('[a-z]* failed in (?P<p>[a-z0-9_]*) phase', error) m = re.match('[a-z0-9]* failed in (?P<p>\S*).*', error)
if m: if m:
result = 'fail in %s' % m.group('p') result = 'fail in %s' % m.group('p')
else: else:
...@@ -5974,6 +6055,9 @@ def data_from_html(file, outpath, issues, fulldetail=False): ...@@ -5974,6 +6055,9 @@ def data_from_html(file, outpath, issues, fulldetail=False):
elist[err[0]] += 1 elist[err[0]] += 1
for i in elist: for i in elist:
ilist.append('%sx%d' % (i, elist[i]) if elist[i] > 1 else i) ilist.append('%sx%d' % (i, elist[i]) if elist[i] > 1 else i)
wifi = find_in_html(html, 'Wifi Resume: ', '</td>')
if wifi:
extra['wifi'] = wifi
low = find_in_html(html, 'freeze time: <b>', ' ms</b>') low = find_in_html(html, 'freeze time: <b>', ' ms</b>')
if low and '|' in low: if low and '|' in low:
issue = 'FREEZEx%d' % len(low.split('|')) issue = 'FREEZEx%d' % len(low.split('|'))
...@@ -6048,13 +6132,15 @@ def genHtml(subdir, force=False): ...@@ -6048,13 +6132,15 @@ def genHtml(subdir, force=False):
for dirname, dirnames, filenames in os.walk(subdir): for dirname, dirnames, filenames in os.walk(subdir):
sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = '' sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = ''
for filename in filenames: for filename in filenames:
file = os.path.join(dirname, filename)
if sysvals.usable(file):
if(re.match('.*_dmesg.txt', filename)): if(re.match('.*_dmesg.txt', filename)):
sysvals.dmesgfile = os.path.join(dirname, filename) sysvals.dmesgfile = file
elif(re.match('.*_ftrace.txt', filename)): elif(re.match('.*_ftrace.txt', filename)):
sysvals.ftracefile = os.path.join(dirname, filename) sysvals.ftracefile = file
sysvals.setOutputFile() sysvals.setOutputFile()
if sysvals.ftracefile and sysvals.htmlfile and \ if (sysvals.dmesgfile or sysvals.ftracefile) and sysvals.htmlfile and \
(force or not os.path.exists(sysvals.htmlfile)): (force or not sysvals.usable(sysvals.htmlfile)):
pprint('FTRACE: %s' % sysvals.ftracefile) pprint('FTRACE: %s' % sysvals.ftracefile)
if sysvals.dmesgfile: if sysvals.dmesgfile:
pprint('DMESG : %s' % sysvals.dmesgfile) pprint('DMESG : %s' % sysvals.dmesgfile)
...@@ -6169,9 +6255,9 @@ def configFromFile(file): ...@@ -6169,9 +6255,9 @@ def configFromFile(file):
sysvals.cgtest = getArgInt('cgtest', value, 0, 1, False) sysvals.cgtest = getArgInt('cgtest', value, 0, 1, False)
elif(option == 'cgphase'): elif(option == 'cgphase'):
d = Data(0) d = Data(0)
if value not in d.sortedPhases(): if value not in d.phasedef:
doError('invalid phase --> (%s: %s), valid phases are %s'\ doError('invalid phase --> (%s: %s), valid phases are %s'\
% (option, value, d.sortedPhases()), True) % (option, value, d.phasedef.keys()), True)
sysvals.cgphase = value sysvals.cgphase = value
elif(option == 'fadd'): elif(option == 'fadd'):
file = sysvals.configFile(value) file = sysvals.configFile(value)
...@@ -6184,9 +6270,7 @@ def configFromFile(file): ...@@ -6184,9 +6270,7 @@ def configFromFile(file):
nums = value.split() nums = value.split()
if len(nums) != 2: if len(nums) != 2:
doError('multi requires 2 integers (exec_count and delay)', True) doError('multi requires 2 integers (exec_count and delay)', True)
sysvals.multitest['run'] = True sysvals.multiinit(nums[0], nums[1])
sysvals.multitest['count'] = getArgInt('multi: n d (exec count)', nums[0], 2, 1000000, False)
sysvals.multitest['delay'] = getArgInt('multi: n d (delay between tests)', nums[1], 0, 3600, False)
elif(option == 'devicefilter'): elif(option == 'devicefilter'):
sysvals.setDeviceFilter(value) sysvals.setDeviceFilter(value)
elif(option == 'expandcg'): elif(option == 'expandcg'):
...@@ -6342,6 +6426,7 @@ def printHelp(): ...@@ -6342,6 +6426,7 @@ def printHelp():
' -srgap Add a visible gap in the timeline between sus/res (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'\ ' -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'\ ' -result fn Export a results table to a text file for parsing.\n'\
' -wifi If a wifi connection is available, check that it reconnects after resume.\n'\
' [testprep]\n'\ ' [testprep]\n'\
' -sync Sync the filesystems before starting the test\n'\ ' -sync Sync the filesystems before starting the test\n'\
' -rs on/off Enable/disable runtime suspend for all devices, restore all after test\n'\ ' -rs on/off Enable/disable runtime suspend for all devices, restore all after test\n'\
...@@ -6356,8 +6441,10 @@ def printHelp(): ...@@ -6356,8 +6441,10 @@ def printHelp():
' -predelay t Include t ms delay before 1st suspend (default: 0 ms)\n'\ ' -predelay t Include t ms delay before 1st suspend (default: 0 ms)\n'\
' -postdelay t Include t ms delay after last resume (default: 0 ms)\n'\ ' -postdelay t Include t ms delay after last resume (default: 0 ms)\n'\
' -mindev ms Discard all device blocks shorter than ms milliseconds (e.g. 0.001 for us)\n'\ ' -mindev ms Discard all device blocks shorter than ms milliseconds (e.g. 0.001 for us)\n'\
' -multi n d Execute <n> consecutive tests at <d> seconds intervals. The outputs will\n'\ ' -multi n d Execute <n> consecutive tests at <d> seconds intervals. If <n> is followed\n'\
' be created in a new subdirectory with a summary page.\n'\ ' by a "d", "h", or "m" execute for <n> days, hours, or mins instead.\n'\
' The outputs will be created in a new subdirectory with a summary page.\n'\
' -maxfail n Abort a -multi run after n consecutive fails (default is 0 = never abort)\n'\
' [debug]\n'\ ' [debug]\n'\
' -f Use ftrace to create device callgraphs (default: disabled)\n'\ ' -f Use ftrace to create device callgraphs (default: disabled)\n'\
' -ftop Use ftrace on the top level call: "%s" (default: disabled)\n'\ ' -ftop Use ftrace on the top level call: "%s" (default: disabled)\n'\
...@@ -6379,11 +6466,11 @@ def printHelp(): ...@@ -6379,11 +6466,11 @@ def printHelp():
' -modes List available suspend modes\n'\ ' -modes List available suspend modes\n'\
' -status Test to see if the system is enabled to run this tool\n'\ ' -status Test to see if the system is enabled to run this tool\n'\
' -fpdt Print out the contents of the ACPI Firmware Performance Data Table\n'\ ' -fpdt Print out the contents of the ACPI Firmware Performance Data Table\n'\
' -battery Print out battery info (if available)\n'\ ' -wificheck Print out wifi connection info\n'\
' -wifi Print out wifi connection info (if wireless-tools and device exists)\n'\
' -x<mode> Test xset by toggling the given mode (on/off/standby/suspend)\n'\ ' -x<mode> Test xset by toggling the given mode (on/off/standby/suspend)\n'\
' -sysinfo Print out system info extracted from BIOS\n'\ ' -sysinfo Print out system info extracted from BIOS\n'\
' -devinfo Print out the pm settings of all devices which support runtime suspend\n'\ ' -devinfo Print out the pm settings of all devices which support runtime suspend\n'\
' -cmdinfo Print out all the platform info collected before and after suspend/resume\n'\
' -flist Print the list of functions currently being captured in ftrace\n'\ ' -flist Print the list of functions currently being captured in ftrace\n'\
' -flistall Print all functions capable of being captured in ftrace\n'\ ' -flistall Print all functions capable of being captured in ftrace\n'\
' -summary dir Create a summary of tests in this dir [-genhtml builds missing html]\n'\ ' -summary dir Create a summary of tests in this dir [-genhtml builds missing html]\n'\
...@@ -6399,8 +6486,8 @@ if __name__ == '__main__': ...@@ -6399,8 +6486,8 @@ if __name__ == '__main__':
genhtml = False genhtml = False
cmd = '' cmd = ''
simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall', simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall',
'-devinfo', '-status', '-battery', '-xon', '-xoff', '-xstandby', '-devinfo', '-status', '-xon', '-xoff', '-xstandby', '-xsuspend',
'-xsuspend', '-xinit', '-xreset', '-xstat', '-wifi'] '-xinit', '-xreset', '-xstat', '-wificheck', '-cmdinfo']
if '-f' in sys.argv: if '-f' in sys.argv:
sysvals.cgskip = sysvals.configFile('cgskip.txt') sysvals.cgskip = sysvals.configFile('cgskip.txt')
# loop through the command line arguments # loop through the command line arguments
...@@ -6462,8 +6549,15 @@ if __name__ == '__main__': ...@@ -6462,8 +6549,15 @@ if __name__ == '__main__':
sysvals.usedevsrc = True sysvals.usedevsrc = True
elif(arg == '-sync'): elif(arg == '-sync'):
sysvals.sync = True sysvals.sync = True
elif(arg == '-wifi'):
sysvals.wifi = True
elif(arg == '-gzip'): elif(arg == '-gzip'):
sysvals.gzip = True sysvals.gzip = True
elif(arg == '-info'):
try:
val = next(args)
except:
doError('-info requires one string argument', True)
elif(arg == '-rs'): elif(arg == '-rs'):
try: try:
val = next(args) val = next(args)
...@@ -6555,10 +6649,14 @@ if __name__ == '__main__': ...@@ -6555,10 +6649,14 @@ if __name__ == '__main__':
sysvals.cgexp = True sysvals.cgexp = True
elif(arg == '-srgap'): elif(arg == '-srgap'):
sysvals.srgap = 5 sysvals.srgap = 5
elif(arg == '-maxfail'):
sysvals.maxfail = getArgInt('-maxfail', args, 0, 1000000)
elif(arg == '-multi'): elif(arg == '-multi'):
sysvals.multitest['run'] = True try:
sysvals.multitest['count'] = getArgInt('-multi n d (exec count)', args, 2, 1000000) c, d = next(args), next(args)
sysvals.multitest['delay'] = getArgInt('-multi n d (delay between tests)', args, 0, 3600) except:
doError('-multi requires two values', True)
sysvals.multiinit(c, d)
elif(arg == '-o'): elif(arg == '-o'):
try: try:
val = next(args) val = next(args)
...@@ -6655,13 +6753,6 @@ if __name__ == '__main__': ...@@ -6655,13 +6753,6 @@ if __name__ == '__main__':
elif(cmd == 'fpdt'): elif(cmd == 'fpdt'):
if not getFPDT(True): if not getFPDT(True):
ret = 1 ret = 1
elif(cmd == 'battery'):
out = getBattery()
if out:
pprint('AC Connect : %s\nBattery Charge: %d' % out)
else:
pprint('no battery found')
ret = 1
elif(cmd == 'sysinfo'): elif(cmd == 'sysinfo'):
sysvals.printSystemInfo(True) sysvals.printSystemInfo(True)
elif(cmd == 'devinfo'): elif(cmd == 'devinfo'):
...@@ -6679,13 +6770,15 @@ if __name__ == '__main__': ...@@ -6679,13 +6770,15 @@ if __name__ == '__main__':
ret = displayControl(cmd[1:]) ret = displayControl(cmd[1:])
elif(cmd == 'xstat'): elif(cmd == 'xstat'):
pprint('Display Status: %s' % displayControl('stat').upper()) pprint('Display Status: %s' % displayControl('stat').upper())
elif(cmd == 'wifi'): elif(cmd == 'wificheck'):
out = sysvals.checkWifi() dev = sysvals.checkWifi()
if 'device' not in out: if dev:
pprint('WIFI interface not found') print('%s is connected' % sysvals.wifiDetails(dev))
else: else:
for key in sorted(out): print('No wifi connection found')
pprint('%6s: %s' % (key.upper(), out[key])) elif(cmd == 'cmdinfo'):
for out in sysvals.cmdinfo(False, True):
print('[%s - %s]\n%s\n' % out)
sys.exit(ret) sys.exit(ret)
# if instructed, re-analyze existing data files # if instructed, re-analyze existing data files
...@@ -6720,24 +6813,38 @@ if __name__ == '__main__': ...@@ -6720,24 +6813,38 @@ if __name__ == '__main__':
setRuntimeSuspend(True) setRuntimeSuspend(True)
if sysvals.display: if sysvals.display:
displayControl('init') displayControl('init')
ret = 0 failcnt, ret = 0, 0
if sysvals.multitest['run']: if sysvals.multitest['run']:
# run multiple tests in a separate subdirectory # run multiple tests in a separate subdirectory
if not sysvals.outdir: if not sysvals.outdir:
s = 'suspend-x%d' % sysvals.multitest['count'] if 'time' in sysvals.multitest:
sysvals.outdir = datetime.now().strftime(s+'-%y%m%d-%H%M%S') s = '-%dm' % sysvals.multitest['time']
else:
s = '-x%d' % sysvals.multitest['count']
sysvals.outdir = datetime.now().strftime('suspend-%y%m%d-%H%M%S'+s)
if not os.path.isdir(sysvals.outdir): if not os.path.isdir(sysvals.outdir):
os.makedirs(sysvals.outdir) os.makedirs(sysvals.outdir)
sysvals.sudoUserchown(sysvals.outdir)
finish = datetime.now()
if 'time' in sysvals.multitest:
finish += timedelta(minutes=sysvals.multitest['time'])
for i in range(sysvals.multitest['count']): for i in range(sysvals.multitest['count']):
if(i != 0): sysvals.multistat(True, i, finish)
if i != 0 and sysvals.multitest['delay'] > 0:
pprint('Waiting %d seconds...' % (sysvals.multitest['delay'])) pprint('Waiting %d seconds...' % (sysvals.multitest['delay']))
time.sleep(sysvals.multitest['delay']) time.sleep(sysvals.multitest['delay'])
pprint('TEST (%d/%d) START' % (i+1, sysvals.multitest['count']))
fmt = 'suspend-%y%m%d-%H%M%S' fmt = 'suspend-%y%m%d-%H%M%S'
sysvals.testdir = os.path.join(sysvals.outdir, datetime.now().strftime(fmt)) sysvals.testdir = os.path.join(sysvals.outdir, datetime.now().strftime(fmt))
ret = runTest(i+1) ret = runTest(i+1, True)
pprint('TEST (%d/%d) COMPLETE' % (i+1, sysvals.multitest['count'])) failcnt = 0 if not ret else failcnt + 1
sysvals.logmsg = '' if sysvals.maxfail > 0 and failcnt >= sysvals.maxfail:
pprint('Maximum fail count of %d reached, aborting multitest' % (sysvals.maxfail))
break
time.sleep(5)
sysvals.resetlog()
sysvals.multistat(False, i, finish)
if 'time' in sysvals.multitest and datetime.now() >= finish:
break
if not sysvals.skiphtml: if not sysvals.skiphtml:
runSummary(sysvals.outdir, False, False) runSummary(sysvals.outdir, False, False)
sysvals.sudoUserchown(sysvals.outdir) sysvals.sudoUserchown(sysvals.outdir)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment