diff mbox series

pm-graph v5.8

Message ID 0ee042aef591d6d07d4a8e1c87ac2dd7b3ebcdd3.1605060723.git.todd.e.brandt@linux.intel.com (mailing list archive)
State Mainlined, archived
Headers show
Series pm-graph v5.8 | expand

Commit Message

Todd Brandt Nov. 11, 2020, 2:36 a.m. UTC
- if wakeups occur in s2idle: "freeze time: N (-x ms waking y times) ms"
- change FREEZELOOP and FREEZEWAKE to S2LOOP and S2WAKE for brevity
- returns all sysfs vals to their initial state after testing
- use the dmesg log for debugging until the test is completed,
  instrument the executeSuspend process to have a full trace,
  if test completes, formal dmesg log overwrites the debug log
- fix CPU_ON and CPU_OFF devices in the timeline, should include [n]

Signed-off-by: Todd Brandt <todd.e.brandt@linux.intel.com>
---
 tools/power/pm-graph/README        |   4 +-
 tools/power/pm-graph/sleepgraph.py | 387 +++++++++++++++++------------
 2 files changed, 227 insertions(+), 164 deletions(-)

Comments

Rafael J. Wysocki Nov. 23, 2020, 5:21 p.m. UTC | #1
On Wed, Nov 11, 2020 at 3:36 AM Todd Brandt
<todd.e.brandt@linux.intel.com> wrote:
>
> - if wakeups occur in s2idle: "freeze time: N (-x ms waking y times) ms"
> - change FREEZELOOP and FREEZEWAKE to S2LOOP and S2WAKE for brevity
> - returns all sysfs vals to their initial state after testing
> - use the dmesg log for debugging until the test is completed,
>   instrument the executeSuspend process to have a full trace,
>   if test completes, formal dmesg log overwrites the debug log
> - fix CPU_ON and CPU_OFF devices in the timeline, should include [n]
>
> Signed-off-by: Todd Brandt <todd.e.brandt@linux.intel.com>

Applied as 5.11 material, thanks!

> ---
>  tools/power/pm-graph/README        |   4 +-
>  tools/power/pm-graph/sleepgraph.py | 387 +++++++++++++++++------------
>  2 files changed, 227 insertions(+), 164 deletions(-)
>
> diff --git a/tools/power/pm-graph/README b/tools/power/pm-graph/README
> index 89d0a7dab4bc..da468bd510ca 100644
> --- a/tools/power/pm-graph/README
> +++ b/tools/power/pm-graph/README
> @@ -6,7 +6,7 @@
>     |_|                    |___/          |_|
>
>     pm-graph: suspend/resume/boot timing analysis tools
> -    Version: 5.7
> +    Version: 5.8
>       Author: Todd Brandt <todd.e.brandt@intel.com>
>    Home Page: https://01.org/pm-graph
>
> @@ -61,7 +61,7 @@
>         - runs with python2 or python3, choice is made by /usr/bin/python link
>         - python
>         - python-configparser (for python2 sleepgraph)
> -       - python-requests (for googlesheet.py)
> +       - python-requests (for stresstester.py)
>         - linux-tools-common (for turbostat usage in sleepgraph)
>
>         Ubuntu:
> diff --git a/tools/power/pm-graph/sleepgraph.py b/tools/power/pm-graph/sleepgraph.py
> index 1bc36a1db14f..81f4b8abbdf7 100755
> --- a/tools/power/pm-graph/sleepgraph.py
> +++ b/tools/power/pm-graph/sleepgraph.py
> @@ -81,7 +81,7 @@ def ascii(text):
>  #       store system values and test parameters
>  class SystemValues:
>         title = 'SleepGraph'
> -       version = '5.7'
> +       version = '5.8'
>         ansi = False
>         rs = 0
>         display = ''
> @@ -92,8 +92,9 @@ class SystemValues:
>         testlog = True
>         dmesglog = True
>         ftracelog = False
> +       acpidebug = True
>         tstat = True
> -       mindevlen = 0.0
> +       mindevlen = 0.0001
>         mincglen = 0.0
>         cgphase = ''
>         cgtest = -1
> @@ -115,6 +116,7 @@ class SystemValues:
>         fpdtpath = '/sys/firmware/acpi/tables/FPDT'
>         epath = '/sys/kernel/debug/tracing/events/power/'
>         pmdpath = '/sys/power/pm_debug_messages'
> +       acpipath='/sys/module/acpi/parameters/debug_level'
>         traceevents = [
>                 'suspend_resume',
>                 'wakeup_source_activate',
> @@ -162,16 +164,16 @@ class SystemValues:
>         devdump = False
>         mixedphaseheight = True
>         devprops = dict()
> +       cfgdef = dict()
>         platinfo = []
>         predelay = 0
>         postdelay = 0
> -       pmdebug = ''
>         tmstart = 'SUSPEND START %Y%m%d-%H:%M:%S.%f'
>         tmend = 'RESUME COMPLETE %Y%m%d-%H:%M:%S.%f'
>         tracefuncs = {
>                 'sys_sync': {},
>                 'ksys_sync': {},
> -               'pm_notifier_call_chain_robust': {},
> +               '__pm_notifier_call_chain': {},
>                 'pm_prepare_console': {},
>                 'pm_notifier_call_chain': {},
>                 'freeze_processes': {},
> @@ -490,9 +492,9 @@ class SystemValues:
>                 call('echo 0 > %s/wakealarm' % self.rtcpath, shell=True)
>         def initdmesg(self):
>                 # get the latest time stamp from the dmesg log
> -               fp = Popen('dmesg', stdout=PIPE).stdout
> +               lines = Popen('dmesg', stdout=PIPE).stdout.readlines()
>                 ktime = '0'
> -               for line in fp:
> +               for line in reversed(lines):
>                         line = ascii(line).replace('\r\n', '')
>                         idx = line.find('[')
>                         if idx > 1:
> @@ -500,7 +502,7 @@ class SystemValues:
>                         m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
>                         if(m):
>                                 ktime = m.group('ktime')
> -               fp.close()
> +                               break
>                 self.dmesgstart = float(ktime)
>         def getdmesg(self, testdata):
>                 op = self.writeDatafileHeader(self.dmesgfile, testdata)
> @@ -715,8 +717,6 @@ class SystemValues:
>                         self.fsetVal('0', 'events/kprobes/enable')
>                         self.fsetVal('', 'kprobe_events')
>                         self.fsetVal('1024', 'buffer_size_kb')
> -               if self.pmdebug:
> -                       self.setVal(self.pmdebug, self.pmdpath)
>         def setupAllKprobes(self):
>                 for name in self.tracefuncs:
>                         self.defaultKprobe(name, self.tracefuncs[name])
> @@ -740,11 +740,7 @@ class SystemValues:
>                 # turn trace off
>                 self.fsetVal('0', 'tracing_on')
>                 self.cleanupFtrace()
> -               # pm debug messages
> -               pv = self.getVal(self.pmdpath)
> -               if pv != '1':
> -                       self.setVal('1', self.pmdpath)
> -                       self.pmdebug = pv
> +               self.testVal(self.pmdpath, 'basic', '1')
>                 # set the trace clock to global
>                 self.fsetVal('global', 'trace_clock')
>                 self.fsetVal('nop', 'current_tracer')
> @@ -900,6 +896,14 @@ class SystemValues:
>                 if isgz:
>                         return gzip.open(filename, mode+'t')
>                 return open(filename, mode)
> +       def putlog(self, filename, text):
> +               with self.openlog(filename, 'a') as fp:
> +                       fp.write(text)
> +                       fp.close()
> +       def dlog(self, text):
> +               self.putlog(self.dmesgfile, '# %s\n' % text)
> +       def flog(self, text):
> +               self.putlog(self.ftracefile, text)
>         def b64unzip(self, data):
>                 try:
>                         out = codecs.decode(base64.b64decode(data), 'zlib').decode()
> @@ -992,9 +996,7 @@ class SystemValues:
>                 # add a line for each of these commands with their outputs
>                 for name, cmdline, info in cmdafter:
>                         footer += '# platform-%s: %s | %s\n' % (name, cmdline, self.b64zip(info))
> -
> -               with self.openlog(self.ftracefile, 'a') as fp:
> -                       fp.write(footer)
> +               self.flog(footer)
>                 return True
>         def commonPrefix(self, list):
>                 if len(list) < 2:
> @@ -1034,6 +1036,7 @@ class SystemValues:
>                         cmdline, cmdpath = ' '.join(cargs[2:]), self.getExec(cargs[2])
>                         if not cmdpath or (begin and not delta):
>                                 continue
> +                       self.dlog('[%s]' % cmdline)
>                         try:
>                                 fp = Popen([cmdpath]+cargs[3:], stdout=PIPE, stderr=PIPE).stdout
>                                 info = ascii(fp.read()).strip()
> @@ -1060,6 +1063,29 @@ class SystemValues:
>                         else:
>                                 out.append((name, cmdline, '\tnothing' if not info else info))
>                 return out
> +       def testVal(self, file, fmt='basic', value=''):
> +               if file == 'restoreall':
> +                       for f in self.cfgdef:
> +                               if os.path.exists(f):
> +                                       fp = open(f, 'w')
> +                                       fp.write(self.cfgdef[f])
> +                                       fp.close()
> +                       self.cfgdef = dict()
> +               elif value and os.path.exists(file):
> +                       fp = open(file, 'r+')
> +                       if fmt == 'radio':
> +                               m = re.match('.*\[(?P<v>.*)\].*', fp.read())
> +                               if m:
> +                                       self.cfgdef[file] = m.group('v')
> +                       elif fmt == 'acpi':
> +                               line = fp.read().strip().split('\n')[-1]
> +                               m = re.match('.* (?P<v>[0-9A-Fx]*) .*', line)
> +                               if m:
> +                                       self.cfgdef[file] = m.group('v')
> +                       else:
> +                               self.cfgdef[file] = fp.read().strip()
> +                       fp.write(value)
> +                       fp.close()
>         def haveTurbostat(self):
>                 if not self.tstat:
>                         return False
> @@ -1201,6 +1227,57 @@ class SystemValues:
>                         self.multitest[sz] *= 1440
>                 elif unit == 'h':
>                         self.multitest[sz] *= 60
> +       def displayControl(self, cmd):
> +               xset, ret = 'timeout 10 xset -d :0.0 {0}', 0
> +               if self.sudouser:
> +                       xset = 'sudo -u %s %s' % (self.sudouser, xset)
> +               if cmd == 'init':
> +                       ret = call(xset.format('dpms 0 0 0'), shell=True)
> +                       if not ret:
> +                               ret = call(xset.format('s off'), shell=True)
> +               elif cmd == 'reset':
> +                       ret = call(xset.format('s reset'), shell=True)
> +               elif cmd in ['on', 'off', 'standby', 'suspend']:
> +                       b4 = self.displayControl('stat')
> +                       ret = call(xset.format('dpms force %s' % cmd), shell=True)
> +                       if not ret:
> +                               curr = self.displayControl('stat')
> +                               self.vprint('Display Switched: %s -> %s' % (b4, curr))
> +                               if curr != cmd:
> +                                       self.vprint('WARNING: Display failed to change to %s' % cmd)
> +                       if ret:
> +                               self.vprint('WARNING: Display failed to change to %s with xset' % cmd)
> +                               return ret
> +               elif cmd == 'stat':
> +                       fp = Popen(xset.format('q').split(' '), stdout=PIPE).stdout
> +                       ret = 'unknown'
> +                       for line in fp:
> +                               m = re.match('[\s]*Monitor is (?P<m>.*)', ascii(line))
> +                               if(m and len(m.group('m')) >= 2):
> +                                       out = m.group('m').lower()
> +                                       ret = out[3:] if out[0:2] == 'in' else out
> +                                       break
> +                       fp.close()
> +               return ret
> +       def setRuntimeSuspend(self, before=True):
> +               if before:
> +                       # runtime suspend disable or enable
> +                       if self.rs > 0:
> +                               self.rstgt, self.rsval, self.rsdir = 'on', 'auto', 'enabled'
> +                       else:
> +                               self.rstgt, self.rsval, self.rsdir = 'auto', 'on', 'disabled'
> +                       pprint('CONFIGURING RUNTIME SUSPEND...')
> +                       self.rslist = deviceInfo(self.rstgt)
> +                       for i in self.rslist:
> +                               self.setVal(self.rsval, i)
> +                       pprint('runtime suspend %s on all devices (%d changed)' % (self.rsdir, len(self.rslist)))
> +                       pprint('waiting 5 seconds...')
> +                       time.sleep(5)
> +               else:
> +                       # runtime suspend re-enable or re-disable
> +                       for i in self.rslist:
> +                               self.setVal(self.rstgt, i)
> +                       pprint('runtime suspend settings restored on %d devices' % len(self.rslist))
>
>  sysvals = SystemValues()
>  switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0']
> @@ -1640,15 +1717,20 @@ class Data:
>                         if 'resume_machine' in phase and 'suspend_machine' in lp:
>                                 tS, tR = self.dmesg[lp]['end'], self.dmesg[phase]['start']
>                                 tL = tR - tS
> -                               if tL > 0:
> -                                       left = True if tR > tZero else False
> -                                       self.trimTime(tS, tL, left)
> -                                       if 'trying' in self.dmesg[lp] and self.dmesg[lp]['trying'] >= 0.001:
> -                                               tTry = round(self.dmesg[lp]['trying'] * 1000)
> -                                               text = '%.0f (-%.0f waking)' % (tL * 1000, tTry)
> +                               if tL <= 0:
> +                                       continue
> +                               left = True if tR > tZero else False
> +                               self.trimTime(tS, tL, left)
> +                               if 'waking' in self.dmesg[lp]:
> +                                       tCnt = self.dmesg[lp]['waking'][0]
> +                                       if self.dmesg[lp]['waking'][1] >= 0.001:
> +                                               tTry = '-%.0f' % (round(self.dmesg[lp]['waking'][1] * 1000))
>                                         else:
> -                                               text = '%.0f' % (tL * 1000)
> -                                       self.tLow.append(text)
> +                                               tTry = '-%.3f' % (self.dmesg[lp]['waking'][1] * 1000)
> +                                       text = '%.0f (%s ms waking %d times)' % (tL * 1000, tTry, tCnt)
> +                               else:
> +                                       text = '%.0f' % (tL * 1000)
> +                               self.tLow.append(text)
>                         lp = phase
>         def getMemTime(self):
>                 if not self.hwstart or not self.hwend:
> @@ -1921,7 +2003,7 @@ class Data:
>                         for dev in list:
>                                 length = (list[dev]['end'] - list[dev]['start']) * 1000
>                                 width = widfmt % (((list[dev]['end']-list[dev]['start'])*100)/tTotal)
> -                               if width != '0.000000' and length >= mindevlen:
> +                               if length >= mindevlen:
>                                         devlist.append(dev)
>                         self.tdevlist[phase] = devlist
>         def addHorizontalDivider(self, devname, devend):
> @@ -3316,9 +3398,10 @@ def parseTraceLog(live=False):
>                                                         # trim out s2idle loops, track time trying to freeze
>                                                         llp = data.lastPhase(2)
>                                                         if llp.startswith('suspend_machine'):
> -                                                               if 'trying' not in data.dmesg[llp]:
> -                                                                       data.dmesg[llp]['trying'] = 0
> -                                                               data.dmesg[llp]['trying'] += \
> +                                                               if 'waking' not in data.dmesg[llp]:
> +                                                                       data.dmesg[llp]['waking'] = [0, 0.0]
> +                                                               data.dmesg[llp]['waking'][0] += 1
> +                                                               data.dmesg[llp]['waking'][1] += \
>                                                                         t.time - data.dmesg[lp]['start']
>                                                         data.currphase = ''
>                                                         del data.dmesg[lp]
> @@ -4555,7 +4638,7 @@ def createHTML(testruns, testfail):
>                                 # draw the devices for this phase
>                                 phaselist = data.dmesg[b]['list']
>                                 for d in sorted(data.tdevlist[b]):
> -                                       dname = d if '[' not in d else d.split('[')[0]
> +                                       dname = d if ('[' not in d or 'CPU' in d) else d.split('[')[0]
>                                         name, dev = dname, phaselist[d]
>                                         drv = xtraclass = xtrainfo = xtrastyle = ''
>                                         if 'htmlclass' in dev:
> @@ -5194,156 +5277,146 @@ def addScriptCode(hf, testruns):
>         '</script>\n'
>         hf.write(script_code);
>
> -def setRuntimeSuspend(before=True):
> -       global sysvals
> -       sv = sysvals
> -       if sv.rs == 0:
> -               return
> -       if before:
> -               # runtime suspend disable or enable
> -               if sv.rs > 0:
> -                       sv.rstgt, sv.rsval, sv.rsdir = 'on', 'auto', 'enabled'
> -               else:
> -                       sv.rstgt, sv.rsval, sv.rsdir = 'auto', 'on', 'disabled'
> -               pprint('CONFIGURING RUNTIME SUSPEND...')
> -               sv.rslist = deviceInfo(sv.rstgt)
> -               for i in sv.rslist:
> -                       sv.setVal(sv.rsval, i)
> -               pprint('runtime suspend %s on all devices (%d changed)' % (sv.rsdir, len(sv.rslist)))
> -               pprint('waiting 5 seconds...')
> -               time.sleep(5)
> -       else:
> -               # runtime suspend re-enable or re-disable
> -               for i in sv.rslist:
> -                       sv.setVal(sv.rstgt, i)
> -               pprint('runtime suspend settings restored on %d devices' % len(sv.rslist))
> -
>  # Function: executeSuspend
>  # Description:
>  #       Execute system suspend through the sysfs interface, then copy the output
>  #       dmesg and ftrace files to the test output directory.
>  def executeSuspend(quiet=False):
> -       pm = ProcessMonitor()
> -       tp = sysvals.tpath
> -       if sysvals.wifi:
> -               wifi = sysvals.checkWifi()
> +       sv, tp, pm = sysvals, sysvals.tpath, ProcessMonitor()
> +       if sv.wifi:
> +               wifi = sv.checkWifi()
> +               sv.dlog('wifi check, connected device is "%s"' % wifi)
>         testdata = []
>         # run these commands to prepare the system for suspend
> -       if sysvals.display:
> +       if sv.display:
>                 if not quiet:
> -                       pprint('SET DISPLAY TO %s' % sysvals.display.upper())
> -               displayControl(sysvals.display)
> +                       pprint('SET DISPLAY TO %s' % sv.display.upper())
> +               ret = sv.displayControl(sv.display)
> +               sv.dlog('xset display %s, ret = %d' % (sv.display, ret))
>                 time.sleep(1)
> -       if sysvals.sync:
> +       if sv.sync:
>                 if not quiet:
>                         pprint('SYNCING FILESYSTEMS')
> +               sv.dlog('syncing filesystems')
>                 call('sync', shell=True)
> -       # mark the start point in the kernel ring buffer just as we start
> -       sysvals.initdmesg()
> +       sv.dlog('read dmesg')
> +       sv.initdmesg()
>         # start ftrace
> -       if(sysvals.usecallgraph or sysvals.usetraceevents):
> +       if(sv.usecallgraph or sv.usetraceevents):
>                 if not quiet:
>                         pprint('START TRACING')
> -               sysvals.fsetVal('1', 'tracing_on')
> -               if sysvals.useprocmon:
> +               sv.dlog('start ftrace tracing')
> +               sv.fsetVal('1', 'tracing_on')
> +               if sv.useprocmon:
> +                       sv.dlog('start the process monitor')
>                         pm.start()
> -       sysvals.cmdinfo(True)
> +       sv.dlog('run the cmdinfo list before')
> +       sv.cmdinfo(True)
>         # execute however many s/r runs requested
> -       for count in range(1,sysvals.execcount+1):
> +       for count in range(1,sv.execcount+1):
>                 # x2delay in between test runs
> -               if(count > 1 and sysvals.x2delay > 0):
> -                       sysvals.fsetVal('WAIT %d' % sysvals.x2delay, 'trace_marker')
> -                       time.sleep(sysvals.x2delay/1000.0)
> -                       sysvals.fsetVal('WAIT END', 'trace_marker')
> +               if(count > 1 and sv.x2delay > 0):
> +                       sv.fsetVal('WAIT %d' % sv.x2delay, 'trace_marker')
> +                       time.sleep(sv.x2delay/1000.0)
> +                       sv.fsetVal('WAIT END', 'trace_marker')
>                 # start message
> -               if sysvals.testcommand != '':
> +               if sv.testcommand != '':
>                         pprint('COMMAND START')
>                 else:
> -                       if(sysvals.rtcwake):
> +                       if(sv.rtcwake):
>                                 pprint('SUSPEND START')
>                         else:
>                                 pprint('SUSPEND START (press a key to resume)')
>                 # set rtcwake
> -               if(sysvals.rtcwake):
> +               if(sv.rtcwake):
>                         if not quiet:
> -                               pprint('will issue an rtcwake in %d seconds' % sysvals.rtcwaketime)
> -                       sysvals.rtcWakeAlarmOn()
> +                               pprint('will issue an rtcwake in %d seconds' % sv.rtcwaketime)
> +                       sv.dlog('enable RTC wake alarm')
> +                       sv.rtcWakeAlarmOn()
>                 # start of suspend trace marker
> -               if(sysvals.usecallgraph or sysvals.usetraceevents):
> -                       sysvals.fsetVal(datetime.now().strftime(sysvals.tmstart), 'trace_marker')
> +               if(sv.usecallgraph or sv.usetraceevents):
> +                       sv.fsetVal(datetime.now().strftime(sv.tmstart), 'trace_marker')
>                 # predelay delay
> -               if(count == 1 and sysvals.predelay > 0):
> -                       sysvals.fsetVal('WAIT %d' % sysvals.predelay, 'trace_marker')
> -                       time.sleep(sysvals.predelay/1000.0)
> -                       sysvals.fsetVal('WAIT END', 'trace_marker')
> +               if(count == 1 and sv.predelay > 0):
> +                       sv.fsetVal('WAIT %d' % sv.predelay, 'trace_marker')
> +                       time.sleep(sv.predelay/1000.0)
> +                       sv.fsetVal('WAIT END', 'trace_marker')
>                 # initiate suspend or command
> +               sv.dlog('system executing a suspend')
>                 tdata = {'error': ''}
> -               if sysvals.testcommand != '':
> -                       res = call(sysvals.testcommand+' 2>&1', shell=True);
> +               if sv.testcommand != '':
> +                       res = call(sv.testcommand+' 2>&1', shell=True);
>                         if res != 0:
>                                 tdata['error'] = 'cmd returned %d' % res
>                 else:
> -                       mode = sysvals.suspendmode
> -                       if sysvals.memmode and os.path.exists(sysvals.mempowerfile):
> +                       mode = sv.suspendmode
> +                       if sv.memmode and os.path.exists(sv.mempowerfile):
>                                 mode = 'mem'
> -                               pf = open(sysvals.mempowerfile, 'w')
> -                               pf.write(sysvals.memmode)
> -                               pf.close()
> -                       if sysvals.diskmode and os.path.exists(sysvals.diskpowerfile):
> +                               sv.testVal(sv.mempowerfile, 'radio', sv.memmode)
> +                       if sv.diskmode and os.path.exists(sv.diskpowerfile):
>                                 mode = 'disk'
> -                               pf = open(sysvals.diskpowerfile, 'w')
> -                               pf.write(sysvals.diskmode)
> -                               pf.close()
> -                       if mode == 'freeze' and sysvals.haveTurbostat():
> +                               sv.testVal(sv.diskpowerfile, 'radio', sv.diskmode)
> +                       if sv.acpidebug:
> +                               sv.testVal(sv.acpipath, 'acpi', '0xe')
> +                       if mode == 'freeze' and sv.haveTurbostat():
>                                 # execution will pause here
> -                               turbo = sysvals.turbostat()
> +                               turbo = sv.turbostat()
>                                 if turbo:
>                                         tdata['turbo'] = turbo
>                         else:
> -                               pf = open(sysvals.powerfile, 'w')
> +                               pf = open(sv.powerfile, 'w')
>                                 pf.write(mode)
>                                 # execution will pause here
>                                 try:
>                                         pf.close()
>                                 except Exception as e:
>                                         tdata['error'] = str(e)
> -               if(sysvals.rtcwake):
> -                       sysvals.rtcWakeAlarmOff()
> +               sv.dlog('system returned from resume')
> +               # reset everything
> +               sv.testVal('restoreall')
> +               if(sv.rtcwake):
> +                       sv.dlog('disable RTC wake alarm')
> +                       sv.rtcWakeAlarmOff()
>                 # postdelay delay
> -               if(count == sysvals.execcount and sysvals.postdelay > 0):
> -                       sysvals.fsetVal('WAIT %d' % sysvals.postdelay, 'trace_marker')
> -                       time.sleep(sysvals.postdelay/1000.0)
> -                       sysvals.fsetVal('WAIT END', 'trace_marker')
> +               if(count == sv.execcount and sv.postdelay > 0):
> +                       sv.fsetVal('WAIT %d' % sv.postdelay, 'trace_marker')
> +                       time.sleep(sv.postdelay/1000.0)
> +                       sv.fsetVal('WAIT END', 'trace_marker')
>                 # return from suspend
>                 pprint('RESUME COMPLETE')
> -               if(sysvals.usecallgraph or sysvals.usetraceevents):
> -                       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(sv.usecallgraph or sv.usetraceevents):
> +                       sv.fsetVal(datetime.now().strftime(sv.tmend), 'trace_marker')
> +               if sv.wifi and wifi:
> +                       tdata['wifi'] = sv.pollWifi(wifi)
> +                       sv.dlog('wifi check, %s' % tdata['wifi'])
> +               if(sv.suspendmode == 'mem' or sv.suspendmode == 'command'):
> +                       sv.dlog('read the ACPI FPDT')
>                         tdata['fw'] = getFPDT(False)
>                 testdata.append(tdata)
> -       cmdafter = sysvals.cmdinfo(False)
> +       sv.dlog('run the cmdinfo list after')
> +       cmdafter = sv.cmdinfo(False)
>         # stop ftrace
> -       if(sysvals.usecallgraph or sysvals.usetraceevents):
> -               if sysvals.useprocmon:
> +       if(sv.usecallgraph or sv.usetraceevents):
> +               if sv.useprocmon:
> +                       sv.dlog('stop the process monitor')
>                         pm.stop()
> -               sysvals.fsetVal('0', 'tracing_on')
> +               sv.fsetVal('0', 'tracing_on')
>         # grab a copy of the dmesg output
>         if not quiet:
>                 pprint('CAPTURING DMESG')
> -       sysvals.getdmesg(testdata)
> +       sysvals.dlog('EXECUTION TRACE END')
> +       sv.getdmesg(testdata)
>         # grab a copy of the ftrace output
> -       if(sysvals.usecallgraph or sysvals.usetraceevents):
> +       if(sv.usecallgraph or sv.usetraceevents):
>                 if not quiet:
>                         pprint('CAPTURING TRACE')
> -               op = sysvals.writeDatafileHeader(sysvals.ftracefile, testdata)
> +               op = sv.writeDatafileHeader(sv.ftracefile, testdata)
>                 fp = open(tp+'trace', 'r')
>                 for line in fp:
>                         op.write(line)
>                 op.close()
> -               sysvals.fsetVal('', 'trace')
> -               sysvals.platforminfo(cmdafter)
> +               sv.fsetVal('', 'trace')
> +               sv.platforminfo(cmdafter)
>
>  def readFile(file):
>         if os.path.islink(file):
> @@ -5586,39 +5659,6 @@ def dmidecode(mempath, fatal=False):
>                 count += 1
>         return out
>
> -def displayControl(cmd):
> -       xset, ret = 'timeout 10 xset -d :0.0 {0}', 0
> -       if sysvals.sudouser:
> -               xset = 'sudo -u %s %s' % (sysvals.sudouser, xset)
> -       if cmd == 'init':
> -               ret = call(xset.format('dpms 0 0 0'), shell=True)
> -               if not ret:
> -                       ret = call(xset.format('s off'), shell=True)
> -       elif cmd == 'reset':
> -               ret = call(xset.format('s reset'), shell=True)
> -       elif cmd in ['on', 'off', 'standby', 'suspend']:
> -               b4 = displayControl('stat')
> -               ret = call(xset.format('dpms force %s' % cmd), shell=True)
> -               if not ret:
> -                       curr = displayControl('stat')
> -                       sysvals.vprint('Display Switched: %s -> %s' % (b4, curr))
> -                       if curr != cmd:
> -                               sysvals.vprint('WARNING: Display failed to change to %s' % cmd)
> -               if ret:
> -                       sysvals.vprint('WARNING: Display failed to change to %s with xset' % cmd)
> -                       return ret
> -       elif cmd == 'stat':
> -               fp = Popen(xset.format('q').split(' '), stdout=PIPE).stdout
> -               ret = 'unknown'
> -               for line in fp:
> -                       m = re.match('[\s]*Monitor is (?P<m>.*)', ascii(line))
> -                       if(m and len(m.group('m')) >= 2):
> -                               out = m.group('m').lower()
> -                               ret = out[3:] if out[0:2] == 'in' else out
> -                               break
> -               fp.close()
> -       return ret
> -
>  # Function: getFPDT
>  # Description:
>  #       Read the acpi bios tables and pull out FPDT, the firmware data
> @@ -6001,8 +6041,19 @@ def rerunTest(htmlfile=''):
>  #       execute a suspend/resume, gather the logs, and generate the output
>  def runTest(n=0, quiet=False):
>         # prepare for the test
> -       sysvals.initFtrace(quiet)
>         sysvals.initTestOutput('suspend')
> +       op = sysvals.writeDatafileHeader(sysvals.dmesgfile, [])
> +       op.write('# EXECUTION TRACE START\n')
> +       op.close()
> +       if n <= 1:
> +               if sysvals.rs != 0:
> +                       sysvals.dlog('%sabling runtime suspend' % ('en' if sysvals.rs > 0 else 'dis'))
> +                       sysvals.setRuntimeSuspend(True)
> +               if sysvals.display:
> +                       ret = sysvals.displayControl('init')
> +                       sysvals.dlog('xset display init, ret = %d' % ret)
> +       sysvals.dlog('initialize ftrace')
> +       sysvals.initFtrace(quiet)
>
>         # execute the test
>         executeSuspend(quiet)
> @@ -6098,8 +6149,16 @@ def data_from_html(file, outpath, issues, fulldetail=False):
>         if wifi:
>                 extra['wifi'] = wifi
>         low = find_in_html(html, 'freeze time: <b>', ' ms</b>')
> -       if low and 'waking' in low:
> -               issue = 'FREEZEWAKE'
> +       for lowstr in ['waking', '+']:
> +               if not low:
> +                       break
> +               if lowstr not in low:
> +                       continue
> +               if lowstr == '+':
> +                       issue = 'S2LOOPx%d' % len(low.split('+'))
> +               else:
> +                       m = re.match('.*waking *(?P<n>[0-9]*) *times.*', low)
> +                       issue = 'S2WAKEx%s' % m.group('n') if m else 'S2WAKExNaN'
>                 match = [i for i in issues if i['match'] == issue]
>                 if len(match) > 0:
>                         match[0]['count'] += 1
> @@ -6605,6 +6664,11 @@ if __name__ == '__main__':
>                                 val = next(args)
>                         except:
>                                 doError('-info requires one string argument', True)
> +               elif(arg == '-desc'):
> +                       try:
> +                               val = next(args)
> +                       except:
> +                               doError('-desc requires one string argument', True)
>                 elif(arg == '-rs'):
>                         try:
>                                 val = next(args)
> @@ -6814,9 +6878,9 @@ if __name__ == '__main__':
>                         runSummary(sysvals.outdir, True, genhtml)
>                 elif(cmd in ['xon', 'xoff', 'xstandby', 'xsuspend', 'xinit', 'xreset']):
>                         sysvals.verbose = True
> -                       ret = displayControl(cmd[1:])
> +                       ret = sysvals.displayControl(cmd[1:])
>                 elif(cmd == 'xstat'):
> -                       pprint('Display Status: %s' % displayControl('stat').upper())
> +                       pprint('Display Status: %s' % sysvals.displayControl('stat').upper())
>                 elif(cmd == 'wificheck'):
>                         dev = sysvals.checkWifi()
>                         if dev:
> @@ -6854,12 +6918,8 @@ if __name__ == '__main__':
>         if mode.startswith('disk-'):
>                 sysvals.diskmode = mode.split('-', 1)[-1]
>                 sysvals.suspendmode = 'disk'
> -
>         sysvals.systemInfo(dmidecode(sysvals.mempath))
>
> -       setRuntimeSuspend(True)
> -       if sysvals.display:
> -               displayControl('init')
>         failcnt, ret = 0, 0
>         if sysvals.multitest['run']:
>                 # run multiple tests in a separate subdirectory
> @@ -6900,7 +6960,10 @@ if __name__ == '__main__':
>                         sysvals.testdir = sysvals.outdir
>                 # run the test in the current directory
>                 ret = runTest()
> +
> +       # reset to default values after testing
>         if sysvals.display:
> -               displayControl('reset')
> -       setRuntimeSuspend(False)
> +               sysvals.displayControl('reset')
> +       if sysvals.rs != 0:
> +               sysvals.setRuntimeSuspend(False)
>         sys.exit(ret)
> --
> 2.25.1
>
diff mbox series

Patch

diff --git a/tools/power/pm-graph/README b/tools/power/pm-graph/README
index 89d0a7dab4bc..da468bd510ca 100644
--- a/tools/power/pm-graph/README
+++ b/tools/power/pm-graph/README
@@ -6,7 +6,7 @@ 
    |_|                    |___/          |_|
 
    pm-graph: suspend/resume/boot timing analysis tools
-    Version: 5.7
+    Version: 5.8
      Author: Todd Brandt <todd.e.brandt@intel.com>
   Home Page: https://01.org/pm-graph
 
@@ -61,7 +61,7 @@ 
        - runs with python2 or python3, choice is made by /usr/bin/python link
        - python
        - python-configparser (for python2 sleepgraph)
-       - python-requests (for googlesheet.py)
+       - python-requests (for stresstester.py)
        - linux-tools-common (for turbostat usage in sleepgraph)
 
        Ubuntu:
diff --git a/tools/power/pm-graph/sleepgraph.py b/tools/power/pm-graph/sleepgraph.py
index 1bc36a1db14f..81f4b8abbdf7 100755
--- a/tools/power/pm-graph/sleepgraph.py
+++ b/tools/power/pm-graph/sleepgraph.py
@@ -81,7 +81,7 @@  def ascii(text):
 #	 store system values and test parameters
 class SystemValues:
 	title = 'SleepGraph'
-	version = '5.7'
+	version = '5.8'
 	ansi = False
 	rs = 0
 	display = ''
@@ -92,8 +92,9 @@  class SystemValues:
 	testlog = True
 	dmesglog = True
 	ftracelog = False
+	acpidebug = True
 	tstat = True
-	mindevlen = 0.0
+	mindevlen = 0.0001
 	mincglen = 0.0
 	cgphase = ''
 	cgtest = -1
@@ -115,6 +116,7 @@  class SystemValues:
 	fpdtpath = '/sys/firmware/acpi/tables/FPDT'
 	epath = '/sys/kernel/debug/tracing/events/power/'
 	pmdpath = '/sys/power/pm_debug_messages'
+	acpipath='/sys/module/acpi/parameters/debug_level'
 	traceevents = [
 		'suspend_resume',
 		'wakeup_source_activate',
@@ -162,16 +164,16 @@  class SystemValues:
 	devdump = False
 	mixedphaseheight = True
 	devprops = dict()
+	cfgdef = dict()
 	platinfo = []
 	predelay = 0
 	postdelay = 0
-	pmdebug = ''
 	tmstart = 'SUSPEND START %Y%m%d-%H:%M:%S.%f'
 	tmend = 'RESUME COMPLETE %Y%m%d-%H:%M:%S.%f'
 	tracefuncs = {
 		'sys_sync': {},
 		'ksys_sync': {},
-		'pm_notifier_call_chain_robust': {},
+		'__pm_notifier_call_chain': {},
 		'pm_prepare_console': {},
 		'pm_notifier_call_chain': {},
 		'freeze_processes': {},
@@ -490,9 +492,9 @@  class SystemValues:
 		call('echo 0 > %s/wakealarm' % self.rtcpath, shell=True)
 	def initdmesg(self):
 		# get the latest time stamp from the dmesg log
-		fp = Popen('dmesg', stdout=PIPE).stdout
+		lines = Popen('dmesg', stdout=PIPE).stdout.readlines()
 		ktime = '0'
-		for line in fp:
+		for line in reversed(lines):
 			line = ascii(line).replace('\r\n', '')
 			idx = line.find('[')
 			if idx > 1:
@@ -500,7 +502,7 @@  class SystemValues:
 			m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
 			if(m):
 				ktime = m.group('ktime')
-		fp.close()
+				break
 		self.dmesgstart = float(ktime)
 	def getdmesg(self, testdata):
 		op = self.writeDatafileHeader(self.dmesgfile, testdata)
@@ -715,8 +717,6 @@  class SystemValues:
 			self.fsetVal('0', 'events/kprobes/enable')
 			self.fsetVal('', 'kprobe_events')
 			self.fsetVal('1024', 'buffer_size_kb')
-		if self.pmdebug:
-			self.setVal(self.pmdebug, self.pmdpath)
 	def setupAllKprobes(self):
 		for name in self.tracefuncs:
 			self.defaultKprobe(name, self.tracefuncs[name])
@@ -740,11 +740,7 @@  class SystemValues:
 		# turn trace off
 		self.fsetVal('0', 'tracing_on')
 		self.cleanupFtrace()
-		# pm debug messages
-		pv = self.getVal(self.pmdpath)
-		if pv != '1':
-			self.setVal('1', self.pmdpath)
-			self.pmdebug = pv
+		self.testVal(self.pmdpath, 'basic', '1')
 		# set the trace clock to global
 		self.fsetVal('global', 'trace_clock')
 		self.fsetVal('nop', 'current_tracer')
@@ -900,6 +896,14 @@  class SystemValues:
 		if isgz:
 			return gzip.open(filename, mode+'t')
 		return open(filename, mode)
+	def putlog(self, filename, text):
+		with self.openlog(filename, 'a') as fp:
+			fp.write(text)
+			fp.close()
+	def dlog(self, text):
+		self.putlog(self.dmesgfile, '# %s\n' % text)
+	def flog(self, text):
+		self.putlog(self.ftracefile, text)
 	def b64unzip(self, data):
 		try:
 			out = codecs.decode(base64.b64decode(data), 'zlib').decode()
@@ -992,9 +996,7 @@  class SystemValues:
 		# add a line for each of these commands with their outputs
 		for name, cmdline, info in cmdafter:
 			footer += '# platform-%s: %s | %s\n' % (name, cmdline, self.b64zip(info))
-
-		with self.openlog(self.ftracefile, 'a') as fp:
-			fp.write(footer)
+		self.flog(footer)
 		return True
 	def commonPrefix(self, list):
 		if len(list) < 2:
@@ -1034,6 +1036,7 @@  class SystemValues:
 			cmdline, cmdpath = ' '.join(cargs[2:]), self.getExec(cargs[2])
 			if not cmdpath or (begin and not delta):
 				continue
+			self.dlog('[%s]' % cmdline)
 			try:
 				fp = Popen([cmdpath]+cargs[3:], stdout=PIPE, stderr=PIPE).stdout
 				info = ascii(fp.read()).strip()
@@ -1060,6 +1063,29 @@  class SystemValues:
 			else:
 				out.append((name, cmdline, '\tnothing' if not info else info))
 		return out
+	def testVal(self, file, fmt='basic', value=''):
+		if file == 'restoreall':
+			for f in self.cfgdef:
+				if os.path.exists(f):
+					fp = open(f, 'w')
+					fp.write(self.cfgdef[f])
+					fp.close()
+			self.cfgdef = dict()
+		elif value and os.path.exists(file):
+			fp = open(file, 'r+')
+			if fmt == 'radio':
+				m = re.match('.*\[(?P<v>.*)\].*', fp.read())
+				if m:
+					self.cfgdef[file] = m.group('v')
+			elif fmt == 'acpi':
+				line = fp.read().strip().split('\n')[-1]
+				m = re.match('.* (?P<v>[0-9A-Fx]*) .*', line)
+				if m:
+					self.cfgdef[file] = m.group('v')
+			else:
+				self.cfgdef[file] = fp.read().strip()
+			fp.write(value)
+			fp.close()
 	def haveTurbostat(self):
 		if not self.tstat:
 			return False
@@ -1201,6 +1227,57 @@  class SystemValues:
 			self.multitest[sz] *= 1440
 		elif unit == 'h':
 			self.multitest[sz] *= 60
+	def displayControl(self, cmd):
+		xset, ret = 'timeout 10 xset -d :0.0 {0}', 0
+		if self.sudouser:
+			xset = 'sudo -u %s %s' % (self.sudouser, xset)
+		if cmd == 'init':
+			ret = call(xset.format('dpms 0 0 0'), shell=True)
+			if not ret:
+				ret = call(xset.format('s off'), shell=True)
+		elif cmd == 'reset':
+			ret = call(xset.format('s reset'), shell=True)
+		elif cmd in ['on', 'off', 'standby', 'suspend']:
+			b4 = self.displayControl('stat')
+			ret = call(xset.format('dpms force %s' % cmd), shell=True)
+			if not ret:
+				curr = self.displayControl('stat')
+				self.vprint('Display Switched: %s -> %s' % (b4, curr))
+				if curr != cmd:
+					self.vprint('WARNING: Display failed to change to %s' % cmd)
+			if ret:
+				self.vprint('WARNING: Display failed to change to %s with xset' % cmd)
+				return ret
+		elif cmd == 'stat':
+			fp = Popen(xset.format('q').split(' '), stdout=PIPE).stdout
+			ret = 'unknown'
+			for line in fp:
+				m = re.match('[\s]*Monitor is (?P<m>.*)', ascii(line))
+				if(m and len(m.group('m')) >= 2):
+					out = m.group('m').lower()
+					ret = out[3:] if out[0:2] == 'in' else out
+					break
+			fp.close()
+		return ret
+	def setRuntimeSuspend(self, before=True):
+		if before:
+			# runtime suspend disable or enable
+			if self.rs > 0:
+				self.rstgt, self.rsval, self.rsdir = 'on', 'auto', 'enabled'
+			else:
+				self.rstgt, self.rsval, self.rsdir = 'auto', 'on', 'disabled'
+			pprint('CONFIGURING RUNTIME SUSPEND...')
+			self.rslist = deviceInfo(self.rstgt)
+			for i in self.rslist:
+				self.setVal(self.rsval, i)
+			pprint('runtime suspend %s on all devices (%d changed)' % (self.rsdir, len(self.rslist)))
+			pprint('waiting 5 seconds...')
+			time.sleep(5)
+		else:
+			# runtime suspend re-enable or re-disable
+			for i in self.rslist:
+				self.setVal(self.rstgt, i)
+			pprint('runtime suspend settings restored on %d devices' % len(self.rslist))
 
 sysvals = SystemValues()
 switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0']
@@ -1640,15 +1717,20 @@  class Data:
 			if 'resume_machine' in phase and 'suspend_machine' in lp:
 				tS, tR = self.dmesg[lp]['end'], self.dmesg[phase]['start']
 				tL = tR - tS
-				if tL > 0:
-					left = True if tR > tZero else False
-					self.trimTime(tS, tL, left)
-					if 'trying' in self.dmesg[lp] and self.dmesg[lp]['trying'] >= 0.001:
-						tTry = round(self.dmesg[lp]['trying'] * 1000)
-						text = '%.0f (-%.0f waking)' % (tL * 1000, tTry)
+				if tL <= 0:
+					continue
+				left = True if tR > tZero else False
+				self.trimTime(tS, tL, left)
+				if 'waking' in self.dmesg[lp]:
+					tCnt = self.dmesg[lp]['waking'][0]
+					if self.dmesg[lp]['waking'][1] >= 0.001:
+						tTry = '-%.0f' % (round(self.dmesg[lp]['waking'][1] * 1000))
 					else:
-						text = '%.0f' % (tL * 1000)
-					self.tLow.append(text)
+						tTry = '-%.3f' % (self.dmesg[lp]['waking'][1] * 1000)
+					text = '%.0f (%s ms waking %d times)' % (tL * 1000, tTry, tCnt)
+				else:
+					text = '%.0f' % (tL * 1000)
+				self.tLow.append(text)
 			lp = phase
 	def getMemTime(self):
 		if not self.hwstart or not self.hwend:
@@ -1921,7 +2003,7 @@  class Data:
 			for dev in list:
 				length = (list[dev]['end'] - list[dev]['start']) * 1000
 				width = widfmt % (((list[dev]['end']-list[dev]['start'])*100)/tTotal)
-				if width != '0.000000' and length >= mindevlen:
+				if length >= mindevlen:
 					devlist.append(dev)
 			self.tdevlist[phase] = devlist
 	def addHorizontalDivider(self, devname, devend):
@@ -3316,9 +3398,10 @@  def parseTraceLog(live=False):
 							# trim out s2idle loops, track time trying to freeze
 							llp = data.lastPhase(2)
 							if llp.startswith('suspend_machine'):
-								if 'trying' not in data.dmesg[llp]:
-									data.dmesg[llp]['trying'] = 0
-								data.dmesg[llp]['trying'] += \
+								if 'waking' not in data.dmesg[llp]:
+									data.dmesg[llp]['waking'] = [0, 0.0]
+								data.dmesg[llp]['waking'][0] += 1
+								data.dmesg[llp]['waking'][1] += \
 									t.time - data.dmesg[lp]['start']
 							data.currphase = ''
 							del data.dmesg[lp]
@@ -4555,7 +4638,7 @@  def createHTML(testruns, testfail):
 				# draw the devices for this phase
 				phaselist = data.dmesg[b]['list']
 				for d in sorted(data.tdevlist[b]):
-					dname = d if '[' not in d else d.split('[')[0]
+					dname = d if ('[' not in d or 'CPU' in d) else d.split('[')[0]
 					name, dev = dname, phaselist[d]
 					drv = xtraclass = xtrainfo = xtrastyle = ''
 					if 'htmlclass' in dev:
@@ -5194,156 +5277,146 @@  def addScriptCode(hf, testruns):
 	'</script>\n'
 	hf.write(script_code);
 
-def setRuntimeSuspend(before=True):
-	global sysvals
-	sv = sysvals
-	if sv.rs == 0:
-		return
-	if before:
-		# runtime suspend disable or enable
-		if sv.rs > 0:
-			sv.rstgt, sv.rsval, sv.rsdir = 'on', 'auto', 'enabled'
-		else:
-			sv.rstgt, sv.rsval, sv.rsdir = 'auto', 'on', 'disabled'
-		pprint('CONFIGURING RUNTIME SUSPEND...')
-		sv.rslist = deviceInfo(sv.rstgt)
-		for i in sv.rslist:
-			sv.setVal(sv.rsval, i)
-		pprint('runtime suspend %s on all devices (%d changed)' % (sv.rsdir, len(sv.rslist)))
-		pprint('waiting 5 seconds...')
-		time.sleep(5)
-	else:
-		# runtime suspend re-enable or re-disable
-		for i in sv.rslist:
-			sv.setVal(sv.rstgt, i)
-		pprint('runtime suspend settings restored on %d devices' % len(sv.rslist))
-
 # Function: executeSuspend
 # Description:
 #	 Execute system suspend through the sysfs interface, then copy the output
 #	 dmesg and ftrace files to the test output directory.
 def executeSuspend(quiet=False):
-	pm = ProcessMonitor()
-	tp = sysvals.tpath
-	if sysvals.wifi:
-		wifi = sysvals.checkWifi()
+	sv, tp, pm = sysvals, sysvals.tpath, ProcessMonitor()
+	if sv.wifi:
+		wifi = sv.checkWifi()
+		sv.dlog('wifi check, connected device is "%s"' % wifi)
 	testdata = []
 	# run these commands to prepare the system for suspend
-	if sysvals.display:
+	if sv.display:
 		if not quiet:
-			pprint('SET DISPLAY TO %s' % sysvals.display.upper())
-		displayControl(sysvals.display)
+			pprint('SET DISPLAY TO %s' % sv.display.upper())
+		ret = sv.displayControl(sv.display)
+		sv.dlog('xset display %s, ret = %d' % (sv.display, ret))
 		time.sleep(1)
-	if sysvals.sync:
+	if sv.sync:
 		if not quiet:
 			pprint('SYNCING FILESYSTEMS')
+		sv.dlog('syncing filesystems')
 		call('sync', shell=True)
-	# mark the start point in the kernel ring buffer just as we start
-	sysvals.initdmesg()
+	sv.dlog('read dmesg')
+	sv.initdmesg()
 	# start ftrace
-	if(sysvals.usecallgraph or sysvals.usetraceevents):
+	if(sv.usecallgraph or sv.usetraceevents):
 		if not quiet:
 			pprint('START TRACING')
-		sysvals.fsetVal('1', 'tracing_on')
-		if sysvals.useprocmon:
+		sv.dlog('start ftrace tracing')
+		sv.fsetVal('1', 'tracing_on')
+		if sv.useprocmon:
+			sv.dlog('start the process monitor')
 			pm.start()
-	sysvals.cmdinfo(True)
+	sv.dlog('run the cmdinfo list before')
+	sv.cmdinfo(True)
 	# execute however many s/r runs requested
-	for count in range(1,sysvals.execcount+1):
+	for count in range(1,sv.execcount+1):
 		# x2delay in between test runs
-		if(count > 1 and sysvals.x2delay > 0):
-			sysvals.fsetVal('WAIT %d' % sysvals.x2delay, 'trace_marker')
-			time.sleep(sysvals.x2delay/1000.0)
-			sysvals.fsetVal('WAIT END', 'trace_marker')
+		if(count > 1 and sv.x2delay > 0):
+			sv.fsetVal('WAIT %d' % sv.x2delay, 'trace_marker')
+			time.sleep(sv.x2delay/1000.0)
+			sv.fsetVal('WAIT END', 'trace_marker')
 		# start message
-		if sysvals.testcommand != '':
+		if sv.testcommand != '':
 			pprint('COMMAND START')
 		else:
-			if(sysvals.rtcwake):
+			if(sv.rtcwake):
 				pprint('SUSPEND START')
 			else:
 				pprint('SUSPEND START (press a key to resume)')
 		# set rtcwake
-		if(sysvals.rtcwake):
+		if(sv.rtcwake):
 			if not quiet:
-				pprint('will issue an rtcwake in %d seconds' % sysvals.rtcwaketime)
-			sysvals.rtcWakeAlarmOn()
+				pprint('will issue an rtcwake in %d seconds' % sv.rtcwaketime)
+			sv.dlog('enable RTC wake alarm')
+			sv.rtcWakeAlarmOn()
 		# start of suspend trace marker
-		if(sysvals.usecallgraph or sysvals.usetraceevents):
-			sysvals.fsetVal(datetime.now().strftime(sysvals.tmstart), 'trace_marker')
+		if(sv.usecallgraph or sv.usetraceevents):
+			sv.fsetVal(datetime.now().strftime(sv.tmstart), 'trace_marker')
 		# predelay delay
-		if(count == 1 and sysvals.predelay > 0):
-			sysvals.fsetVal('WAIT %d' % sysvals.predelay, 'trace_marker')
-			time.sleep(sysvals.predelay/1000.0)
-			sysvals.fsetVal('WAIT END', 'trace_marker')
+		if(count == 1 and sv.predelay > 0):
+			sv.fsetVal('WAIT %d' % sv.predelay, 'trace_marker')
+			time.sleep(sv.predelay/1000.0)
+			sv.fsetVal('WAIT END', 'trace_marker')
 		# initiate suspend or command
+		sv.dlog('system executing a suspend')
 		tdata = {'error': ''}
-		if sysvals.testcommand != '':
-			res = call(sysvals.testcommand+' 2>&1', shell=True);
+		if sv.testcommand != '':
+			res = call(sv.testcommand+' 2>&1', shell=True);
 			if res != 0:
 				tdata['error'] = 'cmd returned %d' % res
 		else:
-			mode = sysvals.suspendmode
-			if sysvals.memmode and os.path.exists(sysvals.mempowerfile):
+			mode = sv.suspendmode
+			if sv.memmode and os.path.exists(sv.mempowerfile):
 				mode = 'mem'
-				pf = open(sysvals.mempowerfile, 'w')
-				pf.write(sysvals.memmode)
-				pf.close()
-			if sysvals.diskmode and os.path.exists(sysvals.diskpowerfile):
+				sv.testVal(sv.mempowerfile, 'radio', sv.memmode)
+			if sv.diskmode and os.path.exists(sv.diskpowerfile):
 				mode = 'disk'
-				pf = open(sysvals.diskpowerfile, 'w')
-				pf.write(sysvals.diskmode)
-				pf.close()
-			if mode == 'freeze' and sysvals.haveTurbostat():
+				sv.testVal(sv.diskpowerfile, 'radio', sv.diskmode)
+			if sv.acpidebug:
+				sv.testVal(sv.acpipath, 'acpi', '0xe')
+			if mode == 'freeze' and sv.haveTurbostat():
 				# execution will pause here
-				turbo = sysvals.turbostat()
+				turbo = sv.turbostat()
 				if turbo:
 					tdata['turbo'] = turbo
 			else:
-				pf = open(sysvals.powerfile, 'w')
+				pf = open(sv.powerfile, 'w')
 				pf.write(mode)
 				# execution will pause here
 				try:
 					pf.close()
 				except Exception as e:
 					tdata['error'] = str(e)
-		if(sysvals.rtcwake):
-			sysvals.rtcWakeAlarmOff()
+		sv.dlog('system returned from resume')
+		# reset everything
+		sv.testVal('restoreall')
+		if(sv.rtcwake):
+			sv.dlog('disable RTC wake alarm')
+			sv.rtcWakeAlarmOff()
 		# postdelay delay
-		if(count == sysvals.execcount and sysvals.postdelay > 0):
-			sysvals.fsetVal('WAIT %d' % sysvals.postdelay, 'trace_marker')
-			time.sleep(sysvals.postdelay/1000.0)
-			sysvals.fsetVal('WAIT END', 'trace_marker')
+		if(count == sv.execcount and sv.postdelay > 0):
+			sv.fsetVal('WAIT %d' % sv.postdelay, 'trace_marker')
+			time.sleep(sv.postdelay/1000.0)
+			sv.fsetVal('WAIT END', 'trace_marker')
 		# return from suspend
 		pprint('RESUME COMPLETE')
-		if(sysvals.usecallgraph or sysvals.usetraceevents):
-			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(sv.usecallgraph or sv.usetraceevents):
+			sv.fsetVal(datetime.now().strftime(sv.tmend), 'trace_marker')
+		if sv.wifi and wifi:
+			tdata['wifi'] = sv.pollWifi(wifi)
+			sv.dlog('wifi check, %s' % tdata['wifi'])
+		if(sv.suspendmode == 'mem' or sv.suspendmode == 'command'):
+			sv.dlog('read the ACPI FPDT')
 			tdata['fw'] = getFPDT(False)
 		testdata.append(tdata)
-	cmdafter = sysvals.cmdinfo(False)
+	sv.dlog('run the cmdinfo list after')
+	cmdafter = sv.cmdinfo(False)
 	# stop ftrace
-	if(sysvals.usecallgraph or sysvals.usetraceevents):
-		if sysvals.useprocmon:
+	if(sv.usecallgraph or sv.usetraceevents):
+		if sv.useprocmon:
+			sv.dlog('stop the process monitor')
 			pm.stop()
-		sysvals.fsetVal('0', 'tracing_on')
+		sv.fsetVal('0', 'tracing_on')
 	# grab a copy of the dmesg output
 	if not quiet:
 		pprint('CAPTURING DMESG')
-	sysvals.getdmesg(testdata)
+	sysvals.dlog('EXECUTION TRACE END')
+	sv.getdmesg(testdata)
 	# grab a copy of the ftrace output
-	if(sysvals.usecallgraph or sysvals.usetraceevents):
+	if(sv.usecallgraph or sv.usetraceevents):
 		if not quiet:
 			pprint('CAPTURING TRACE')
-		op = sysvals.writeDatafileHeader(sysvals.ftracefile, testdata)
+		op = sv.writeDatafileHeader(sv.ftracefile, testdata)
 		fp = open(tp+'trace', 'r')
 		for line in fp:
 			op.write(line)
 		op.close()
-		sysvals.fsetVal('', 'trace')
-		sysvals.platforminfo(cmdafter)
+		sv.fsetVal('', 'trace')
+		sv.platforminfo(cmdafter)
 
 def readFile(file):
 	if os.path.islink(file):
@@ -5586,39 +5659,6 @@  def dmidecode(mempath, fatal=False):
 		count += 1
 	return out
 
-def displayControl(cmd):
-	xset, ret = 'timeout 10 xset -d :0.0 {0}', 0
-	if sysvals.sudouser:
-		xset = 'sudo -u %s %s' % (sysvals.sudouser, xset)
-	if cmd == 'init':
-		ret = call(xset.format('dpms 0 0 0'), shell=True)
-		if not ret:
-			ret = call(xset.format('s off'), shell=True)
-	elif cmd == 'reset':
-		ret = call(xset.format('s reset'), shell=True)
-	elif cmd in ['on', 'off', 'standby', 'suspend']:
-		b4 = displayControl('stat')
-		ret = call(xset.format('dpms force %s' % cmd), shell=True)
-		if not ret:
-			curr = displayControl('stat')
-			sysvals.vprint('Display Switched: %s -> %s' % (b4, curr))
-			if curr != cmd:
-				sysvals.vprint('WARNING: Display failed to change to %s' % cmd)
-		if ret:
-			sysvals.vprint('WARNING: Display failed to change to %s with xset' % cmd)
-			return ret
-	elif cmd == 'stat':
-		fp = Popen(xset.format('q').split(' '), stdout=PIPE).stdout
-		ret = 'unknown'
-		for line in fp:
-			m = re.match('[\s]*Monitor is (?P<m>.*)', ascii(line))
-			if(m and len(m.group('m')) >= 2):
-				out = m.group('m').lower()
-				ret = out[3:] if out[0:2] == 'in' else out
-				break
-		fp.close()
-	return ret
-
 # Function: getFPDT
 # Description:
 #	 Read the acpi bios tables and pull out FPDT, the firmware data
@@ -6001,8 +6041,19 @@  def rerunTest(htmlfile=''):
 #	 execute a suspend/resume, gather the logs, and generate the output
 def runTest(n=0, quiet=False):
 	# prepare for the test
-	sysvals.initFtrace(quiet)
 	sysvals.initTestOutput('suspend')
+	op = sysvals.writeDatafileHeader(sysvals.dmesgfile, [])
+	op.write('# EXECUTION TRACE START\n')
+	op.close()
+	if n <= 1:
+		if sysvals.rs != 0:
+			sysvals.dlog('%sabling runtime suspend' % ('en' if sysvals.rs > 0 else 'dis'))
+			sysvals.setRuntimeSuspend(True)
+		if sysvals.display:
+			ret = sysvals.displayControl('init')
+			sysvals.dlog('xset display init, ret = %d' % ret)
+	sysvals.dlog('initialize ftrace')
+	sysvals.initFtrace(quiet)
 
 	# execute the test
 	executeSuspend(quiet)
@@ -6098,8 +6149,16 @@  def data_from_html(file, outpath, issues, fulldetail=False):
 	if wifi:
 		extra['wifi'] = wifi
 	low = find_in_html(html, 'freeze time: <b>', ' ms</b>')
-	if low and 'waking' in low:
-		issue = 'FREEZEWAKE'
+	for lowstr in ['waking', '+']:
+		if not low:
+			break
+		if lowstr not in low:
+			continue
+		if lowstr == '+':
+			issue = 'S2LOOPx%d' % len(low.split('+'))
+		else:
+			m = re.match('.*waking *(?P<n>[0-9]*) *times.*', low)
+			issue = 'S2WAKEx%s' % m.group('n') if m else 'S2WAKExNaN'
 		match = [i for i in issues if i['match'] == issue]
 		if len(match) > 0:
 			match[0]['count'] += 1
@@ -6605,6 +6664,11 @@  if __name__ == '__main__':
 				val = next(args)
 			except:
 				doError('-info requires one string argument', True)
+		elif(arg == '-desc'):
+			try:
+				val = next(args)
+			except:
+				doError('-desc requires one string argument', True)
 		elif(arg == '-rs'):
 			try:
 				val = next(args)
@@ -6814,9 +6878,9 @@  if __name__ == '__main__':
 			runSummary(sysvals.outdir, True, genhtml)
 		elif(cmd in ['xon', 'xoff', 'xstandby', 'xsuspend', 'xinit', 'xreset']):
 			sysvals.verbose = True
-			ret = displayControl(cmd[1:])
+			ret = sysvals.displayControl(cmd[1:])
 		elif(cmd == 'xstat'):
-			pprint('Display Status: %s' % displayControl('stat').upper())
+			pprint('Display Status: %s' % sysvals.displayControl('stat').upper())
 		elif(cmd == 'wificheck'):
 			dev = sysvals.checkWifi()
 			if dev:
@@ -6854,12 +6918,8 @@  if __name__ == '__main__':
 	if mode.startswith('disk-'):
 		sysvals.diskmode = mode.split('-', 1)[-1]
 		sysvals.suspendmode = 'disk'
-
 	sysvals.systemInfo(dmidecode(sysvals.mempath))
 
-	setRuntimeSuspend(True)
-	if sysvals.display:
-		displayControl('init')
 	failcnt, ret = 0, 0
 	if sysvals.multitest['run']:
 		# run multiple tests in a separate subdirectory
@@ -6900,7 +6960,10 @@  if __name__ == '__main__':
 			sysvals.testdir = sysvals.outdir
 		# run the test in the current directory
 		ret = runTest()
+
+	# reset to default values after testing
 	if sysvals.display:
-		displayControl('reset')
-	setRuntimeSuspend(False)
+		sysvals.displayControl('reset')
+	if sysvals.rs != 0:
+		sysvals.setRuntimeSuspend(False)
 	sys.exit(ret)