pm-graph v5.9
authorTodd Brandt <todd.e.brandt@intel.com>
Thu, 7 Jul 2022 22:54:51 +0000 (15:54 -0700)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Fri, 15 Jul 2022 17:31:58 +0000 (19:31 +0200)
bootgraph:
 - fix parsing of /proc/version to be much more flexible
 - check kernel version to disallow ftrace on anything older than 4.10

sleepgraph:
 - include fix to bugzilla 212761 in case it regresses
 - fix for -proc bug: https://github.com/intel/pm-graph/pull/20
 - add -debugtiming arg to get timestamps on prints
 - allow use of the netfix tool hosted in the github repo
 - read s0ix data from pmc_core for better debug
 - include more system data in the output log
 - Do a better job testing input files useability
 - flag more error data from dmesg in the timeline
 - pre-parse the trace log to fix any ordering issues
 - add new parser to process dmesg only timelines
 - remove superflous sleep(5) in multitest mode

config/custom-timeline-functions.cfg:
 - change some names to keep up to date

README:
 - new version, small wording changes

Signed-off-by: Todd Brandt <todd.e.brandt@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
tools/power/pm-graph/README
tools/power/pm-graph/bootgraph.py
tools/power/pm-graph/config/custom-timeline-functions.cfg
tools/power/pm-graph/sleepgraph.py

index da468bd..e6020c0 100644 (file)
@@ -6,7 +6,7 @@
    |_|                    |___/          |_|
 
    pm-graph: suspend/resume/boot timing analysis tools
-    Version: 5.8
+    Version: 5.9
      Author: Todd Brandt <todd.e.brandt@intel.com>
   Home Page: https://01.org/pm-graph
 
@@ -97,8 +97,8 @@
         (kernel/pre-3.15/enable_trace_events_suspend_resume.patch)
         (kernel/pre-3.15/enable_trace_events_device_pm_callback.patch)
 
-       If you're using a kernel older than 3.15.0, the following
-        additional kernel parameters are required:
+       If you're using bootgraph, or sleepgraph with a kernel older than 3.15.0,
+               the following additional kernel parameters are required:
         (e.g. in file /etc/default/grub)
         GRUB_CMDLINE_LINUX_DEFAULT="... initcall_debug log_buf_len=32M ..."
 
index 2823cd3..f96f50e 100755 (executable)
@@ -69,22 +69,24 @@ class SystemValues(aslib.SystemValues):
        bootloader = 'grub'
        blexec = []
        def __init__(self):
-               self.hostname = platform.node()
+               self.kernel, self.hostname = 'unknown', platform.node()
                self.testtime = datetime.now().strftime('%Y-%m-%d_%H:%M:%S')
                if os.path.exists('/proc/version'):
                        fp = open('/proc/version', 'r')
-                       val = fp.read().strip()
+                       self.kernel = self.kernelVersion(fp.read().strip())
                        fp.close()
-                       self.kernel = self.kernelVersion(val)
-               else:
-                       self.kernel = 'unknown'
                self.testdir = datetime.now().strftime('boot-%y%m%d-%H%M%S')
        def kernelVersion(self, msg):
-               return msg.split()[2]
+               m = re.match('^[Ll]inux *[Vv]ersion *(?P<v>\S*) .*', msg)
+               if m:
+                       return m.group('v')
+               return 'unknown'
        def checkFtraceKernelVersion(self):
-               val = tuple(map(int, self.kernel.split('-')[0].split('.')))
-               if val >= (4, 10, 0):
-                       return True
+               m = re.match('^(?P<x>[0-9]*)\.(?P<y>[0-9]*)\.(?P<z>[0-9]*).*', self.kernel)
+               if m:
+                       val = tuple(map(int, m.groups()))
+                       if val >= (4, 10, 0):
+                               return True
                return False
        def kernelParams(self):
                cmdline = 'initcall_debug log_buf_len=32M'
index 962e576..4f80ad7 100644 (file)
@@ -125,7 +125,7 @@ acpi_suspend_begin:
 suspend_console:
 acpi_pm_prepare:
 syscore_suspend:
-arch_thaw_secondary_cpus_end:
+arch_enable_nonboot_cpus_end:
 syscore_resume:
 acpi_pm_finish:
 resume_console:
index ffd5095..33981ad 100755 (executable)
@@ -66,8 +66,13 @@ from threading import Thread
 from subprocess import call, Popen, PIPE
 import base64
 
+debugtiming = False
+mystarttime = time.time()
 def pprint(msg):
-       print(msg)
+       if debugtiming:
+               print('[%09.3f] %s' % (time.time()-mystarttime, msg))
+       else:
+               print(msg)
        sys.stdout.flush()
 
 def ascii(text):
@@ -81,13 +86,14 @@ def ascii(text):
 #       store system values and test parameters
 class SystemValues:
        title = 'SleepGraph'
-       version = '5.8'
+       version = '5.9'
        ansi = False
        rs = 0
        display = ''
        gzip = False
        sync = False
        wifi = False
+       netfix = False
        verbose = False
        testlog = True
        dmesglog = True
@@ -108,6 +114,7 @@ class SystemValues:
        cpucount = 0
        memtotal = 204800
        memfree = 204800
+       osversion = ''
        srgap = 0
        cgexp = False
        testdir = ''
@@ -116,6 +123,7 @@ class SystemValues:
        fpdtpath = '/sys/firmware/acpi/tables/FPDT'
        epath = '/sys/kernel/debug/tracing/events/power/'
        pmdpath = '/sys/power/pm_debug_messages'
+       s0ixpath = '/sys/module/intel_pmc_core/parameters/warn_on_s0ix_failures'
        acpipath='/sys/module/acpi/parameters/debug_level'
        traceevents = [
                'suspend_resume',
@@ -156,6 +164,7 @@ class SystemValues:
        ftop = False
        usetraceevents = False
        usetracemarkers = True
+       useftrace = True
        usekprobes = True
        usedevsrc = False
        useprocmon = False
@@ -279,10 +288,16 @@ class SystemValues:
                'intel_fbdev_set_suspend': {},
        }
        infocmds = [
+               [0, 'sysinfo', 'uname', '-a'],
+               [0, 'cpuinfo', 'head', '-7', '/proc/cpuinfo'],
                [0, 'kparams', 'cat', '/proc/cmdline'],
                [0, 'mcelog', 'mcelog'],
                [0, 'pcidevices', 'lspci', '-tv'],
-               [0, 'usbdevices', 'lsusb', '-t'],
+               [0, 'usbdevices', 'lsusb', '-tv'],
+               [0, 'acpidevices', 'sh', '-c', 'ls -l /sys/bus/acpi/devices/*/physical_node'],
+               [0, 's0ix_require', 'cat', '/sys/kernel/debug/pmc_core/substate_requirements'],
+               [0, 's0ix_debug', 'cat', '/sys/kernel/debug/pmc_core/slp_s0_debug_status'],
+               [1, 's0ix_residency', 'cat', '/sys/kernel/debug/pmc_core/slp_s0_residency_usec'],
                [1, 'interrupts', 'cat', '/proc/interrupts'],
                [1, 'wakeups', 'cat', '/sys/kernel/debug/wakeup_sources'],
                [2, 'gpecounts', 'sh', '-c', 'grep -v invalid /sys/firmware/acpi/interrupts/*'],
@@ -358,8 +373,19 @@ class SystemValues:
                        self.outputResult({'error':msg})
                        sys.exit(1)
                return False
-       def usable(self, file):
-               return (os.path.exists(file) and os.path.getsize(file) > 0)
+       def usable(self, file, ishtml=False):
+               if not os.path.exists(file) or os.path.getsize(file) < 1:
+                       return False
+               if ishtml:
+                       try:
+                               fp = open(file, 'r')
+                               res = fp.read(1000)
+                               fp.close()
+                       except:
+                               return False
+                       if '<html>' not in res:
+                               return False
+               return True
        def getExec(self, cmd):
                try:
                        fp = Popen(['which', cmd], stdout=PIPE, stderr=PIPE).stdout
@@ -413,12 +439,16 @@ class SystemValues:
                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' % \
                        (m, p, c, b, r, self.cpucount, self.memtotal, self.memfree)
+               if self.osversion:
+                       self.sysstamp += ' | os:%s' % self.osversion
        def printSystemInfo(self, fatal=False):
                self.rootCheck(True)
                out = dmidecode(self.mempath, fatal)
                if len(out) < 1:
                        return
                fmt = '%-24s: %s'
+               if self.osversion:
+                       print(fmt % ('os-version', self.osversion))
                for name in sorted(out):
                        print(fmt % (name, out[name]))
                print(fmt % ('cpucount', ('%d' % self.cpucount)))
@@ -426,20 +456,25 @@ class SystemValues:
                print(fmt % ('memfree', ('%d kB' % self.memfree)))
        def cpuInfo(self):
                self.cpucount = 0
-               fp = open('/proc/cpuinfo', 'r')
-               for line in fp:
-                       if re.match('^processor[ \t]*:[ \t]*[0-9]*', line):
-                               self.cpucount += 1
-               fp.close()
-               fp = open('/proc/meminfo', 'r')
-               for line in fp:
-                       m = re.match('^MemTotal:[ \t]*(?P<sz>[0-9]*) *kB', line)
-                       if m:
-                               self.memtotal = int(m.group('sz'))
-                       m = re.match('^MemFree:[ \t]*(?P<sz>[0-9]*) *kB', line)
-                       if m:
-                               self.memfree = int(m.group('sz'))
-               fp.close()
+               if os.path.exists('/proc/cpuinfo'):
+                       with open('/proc/cpuinfo', 'r') as fp:
+                               for line in fp:
+                                       if re.match('^processor[ \t]*:[ \t]*[0-9]*', line):
+                                               self.cpucount += 1
+               if os.path.exists('/proc/meminfo'):
+                       with open('/proc/meminfo', 'r') as fp:
+                               for line in fp:
+                                       m = re.match('^MemTotal:[ \t]*(?P<sz>[0-9]*) *kB', line)
+                                       if m:
+                                               self.memtotal = int(m.group('sz'))
+                                       m = re.match('^MemFree:[ \t]*(?P<sz>[0-9]*) *kB', line)
+                                       if m:
+                                               self.memfree = int(m.group('sz'))
+               if os.path.exists('/etc/os-release'):
+                       with open('/etc/os-release', 'r') as fp:
+                               for line in fp:
+                                       if line.startswith('PRETTY_NAME='):
+                                               self.osversion = line[12:].strip().replace('"', '')
        def initTestOutput(self, name):
                self.prefix = self.hostname
                v = open('/proc/version', 'r').read().strip()
@@ -698,6 +733,8 @@ class SystemValues:
                        return False
                return True
        def fsetVal(self, val, path):
+               if not self.useftrace:
+                       return False
                return self.setVal(val, self.tpath+path)
        def getVal(self, file):
                res = ''
@@ -711,9 +748,11 @@ class SystemValues:
                        pass
                return res
        def fgetVal(self, path):
+               if not self.useftrace:
+                       return ''
                return self.getVal(self.tpath+path)
        def cleanupFtrace(self):
-               if(self.usecallgraph or self.usetraceevents or self.usedevsrc):
+               if self.useftrace:
                        self.fsetVal('0', 'events/kprobes/enable')
                        self.fsetVal('', 'kprobe_events')
                        self.fsetVal('1024', 'buffer_size_kb')
@@ -734,13 +773,14 @@ class SystemValues:
                                return True
                return False
        def initFtrace(self, quiet=False):
+               if not self.useftrace:
+                       return
                if not quiet:
                        sysvals.printSystemInfo(False)
                        pprint('INITIALIZING FTRACE...')
                # turn trace off
                self.fsetVal('0', 'tracing_on')
                self.cleanupFtrace()
-               self.testVal(self.pmdpath, 'basic', '1')
                # set the trace clock to global
                self.fsetVal('global', 'trace_clock')
                self.fsetVal('nop', 'current_tracer')
@@ -766,6 +806,10 @@ class SystemValues:
                        # set trace type
                        self.fsetVal('function_graph', 'current_tracer')
                        self.fsetVal('', 'set_ftrace_filter')
+                       # temporary hack to fix https://bugzilla.kernel.org/show_bug.cgi?id=212761
+                       fp = open(self.tpath+'set_ftrace_notrace', 'w')
+                       fp.write('native_queued_spin_lock_slowpath\ndev_driver_string')
+                       fp.close()
                        # set trace format options
                        self.fsetVal('print-parent', 'trace_options')
                        self.fsetVal('funcgraph-abstime', 'trace_options')
@@ -846,6 +890,8 @@ class SystemValues:
                                fp.write('# turbostat %s\n' % test['turbo'])
                        if 'wifi' in test:
                                fp.write('# wifi %s\n' % test['wifi'])
+                       if 'netfix' in test:
+                               fp.write('# netfix %s\n' % test['netfix'])
                        if test['error'] or len(testdata) > 1:
                                fp.write('# enter_sleep_error %s\n' % test['error'])
                return fp
@@ -865,6 +911,8 @@ class SystemValues:
                        fp.write('error%s: %s\n' % (n, testdata['error']))
                else:
                        fp.write('result%s: pass\n' % n)
+               if 'mode' in testdata:
+                       fp.write('mode%s: %s\n' % (n, testdata['mode']))
                for v in ['suspend', 'resume', 'boot', 'lastinit']:
                        if v in testdata:
                                fp.write('%s%s: %.3f\n' % (v, n, testdata[v]))
@@ -901,6 +949,8 @@ class SystemValues:
                        fp.write(text)
                        fp.close()
        def dlog(self, text):
+               if not self.dmesgfile:
+                       return
                self.putlog(self.dmesgfile, '# %s\n' % text)
        def flog(self, text):
                self.putlog(self.ftracefile, text)
@@ -954,34 +1004,31 @@ class SystemValues:
                        dirname = props[dev].syspath
                        if not dirname or not os.path.exists(dirname):
                                continue
-                       with open(dirname+'/power/async') as fp:
-                               text = fp.read()
-                               props[dev].isasync = False
-                               if 'enabled' in text:
+                       props[dev].isasync = False
+                       if os.path.exists(dirname+'/power/async'):
+                               fp = open(dirname+'/power/async')
+                               if 'enabled' in fp.read():
                                        props[dev].isasync = True
+                               fp.close()
                        fields = os.listdir(dirname)
-                       if 'product' in fields:
-                               with open(dirname+'/product', 'rb') as fp:
-                                       props[dev].altname = ascii(fp.read())
-                       elif 'name' in fields:
-                               with open(dirname+'/name', 'rb') as fp:
-                                       props[dev].altname = ascii(fp.read())
-                       elif 'model' in fields:
-                               with open(dirname+'/model', 'rb') as fp:
-                                       props[dev].altname = ascii(fp.read())
-                       elif 'description' in fields:
-                               with open(dirname+'/description', 'rb') as fp:
-                                       props[dev].altname = ascii(fp.read())
-                       elif 'id' in fields:
-                               with open(dirname+'/id', 'rb') as fp:
-                                       props[dev].altname = ascii(fp.read())
-                       elif 'idVendor' in fields and 'idProduct' in fields:
-                               idv, idp = '', ''
-                               with open(dirname+'/idVendor', 'rb') as fp:
-                                       idv = ascii(fp.read()).strip()
-                               with open(dirname+'/idProduct', 'rb') as fp:
-                                       idp = ascii(fp.read()).strip()
-                               props[dev].altname = '%s:%s' % (idv, idp)
+                       for file in ['product', 'name', 'model', 'description', 'id', 'idVendor']:
+                               if file not in fields:
+                                       continue
+                               try:
+                                       with open(os.path.join(dirname, file), 'rb') as fp:
+                                               props[dev].altname = ascii(fp.read())
+                               except:
+                                       continue
+                               if file == 'idVendor':
+                                       idv, idp = props[dev].altname.strip(), ''
+                                       try:
+                                               with open(os.path.join(dirname, 'idProduct'), 'rb') as fp:
+                                                       idp = ascii(fp.read()).strip()
+                                       except:
+                                               props[dev].altname = ''
+                                               break
+                                       props[dev].altname = '%s:%s' % (idv, idp)
+                               break
                        if props[dev].altname:
                                out = props[dev].altname.strip().replace('\n', ' ')\
                                        .replace(',', ' ').replace(';', ' ')
@@ -1047,7 +1094,7 @@ class SystemValues:
                                self.cmd1[name] = self.dictify(info, delta)
                        elif not debug and delta and name in self.cmd1:
                                before, after = self.cmd1[name], self.dictify(info, delta)
-                               dinfo = ('\t%s\n' % before['@']) if '@' in before else ''
+                               dinfo = ('\t%s\n' % before['@']) if '@' in before and len(before) > 1 else ''
                                prefix = self.commonPrefix(list(before.keys()))
                                for key in sorted(before):
                                        if key in after and before[key] != after[key]:
@@ -1128,6 +1175,22 @@ class SystemValues:
                        val = valline[idx]
                        out.append('%s=%s' % (key, val))
                return '|'.join(out)
+       def netfixon(self, net='both'):
+               cmd = self.getExec('netfix')
+               if not cmd:
+                       return ''
+               fp = Popen([cmd, '-s', net, 'on'], stdout=PIPE, stderr=PIPE).stdout
+               out = ascii(fp.read()).strip()
+               fp.close()
+               return out
+       def wifiRepair(self):
+               out = self.netfixon('wifi')
+               if not out or 'error' in out.lower():
+                       return ''
+               m = re.match('WIFI \S* ONLINE (?P<action>\S*)', out)
+               if not m:
+                       return 'dead'
+               return m.group('action')
        def wifiDetails(self, dev):
                try:
                        info = open('/sys/class/net/%s/device/uevent' % dev, 'r').read().strip()
@@ -1144,12 +1207,12 @@ class SystemValues:
                except:
                        return ''
                for line in reversed(w.split('\n')):
-                       m = re.match(' *(?P<dev>.*): (?P<stat>[0-9a-f]*) .*', w.split('\n')[-1])
+                       m = re.match(' *(?P<dev>.*): (?P<stat>[0-9a-f]*) .*', line)
                        if not m or (dev and dev != m.group('dev')):
                                continue
                        return m.group('dev')
                return ''
-       def pollWifi(self, dev, timeout=60):
+       def pollWifi(self, dev, timeout=10):
                start = time.time()
                while (time.time() - start) < timeout:
                        w = self.checkWifi(dev)
@@ -1157,6 +1220,11 @@ class SystemValues:
                                return '%s reconnected %.2f' % \
                                        (self.wifiDetails(dev), max(0, time.time() - start))
                        time.sleep(0.01)
+               if self.netfix:
+                       res = self.wifiRepair()
+                       if res:
+                               timeout = max(0, time.time() - start)
+                               return '%s %s %d' % (self.wifiDetails(dev), res, timeout)
                return '%s timeout %d' % (self.wifiDetails(dev), timeout)
        def errorSummary(self, errinfo, msg):
                found = False
@@ -1283,10 +1351,10 @@ sysvals = SystemValues()
 switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0']
 switchoff = ['disable', 'off', 'false', '0']
 suspendmodename = {
-       'freeze': 'Freeze (S0)',
-       'standby': 'Standby (S1)',
-       'mem': 'Suspend (S3)',
-       'disk': 'Hibernate (S4)'
+       'standby': 'standby (S1)',
+       'freeze': 'freeze (S2idle)',
+       'mem': 'suspend (S3)',
+       'disk': 'hibernate (S4)'
 }
 
 # Class: DevProps
@@ -1376,6 +1444,7 @@ class Data:
                'INVALID' : r'(?i).*\bINVALID\b.*',
                'CRASH'   : r'(?i).*\bCRASHED\b.*',
                'TIMEOUT' : r'(?i).*\bTIMEOUT\b.*',
+               'ABORT'   : r'(?i).*\bABORT\b.*',
                'IRQ'     : r'.*\bgenirq: .*',
                'TASKFAIL': r'.*Freezing of tasks *.*',
                'ACPI'    : r'.*\bACPI *(?P<b>[A-Za-z]*) *Error[: ].*',
@@ -1724,9 +1793,9 @@ class Data:
                                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))
+                                               tTry = '%.0f' % (round(self.dmesg[lp]['waking'][1] * 1000))
                                        else:
-                                               tTry = '-%.3f' % (self.dmesg[lp]['waking'][1] * 1000)
+                                               tTry = '%.3f' % (self.dmesg[lp]['waking'][1] * 1000)
                                        text = '%.0f (%s ms waking %d times)' % (tL * 1000, tTry, tCnt)
                                else:
                                        text = '%.0f' % (tL * 1000)
@@ -2107,6 +2176,30 @@ class Data:
                # set resume complete to end at end marker
                if 'resume_complete' in dm:
                        dm['resume_complete']['end'] = time
+       def initcall_debug_call(self, line, quick=False):
+               m = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) .* (?P<f>.*)\: '+\
+                       'PM: *calling .* @ (?P<n>.*), parent: (?P<p>.*)', line)
+               if not m:
+                       m = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) .* (?P<f>.*)\: '+\
+                               'calling .* @ (?P<n>.*), parent: (?P<p>.*)', line)
+               if not m:
+                       m = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) calling  '+\
+                               '(?P<f>.*)\+ @ (?P<n>.*), parent: (?P<p>.*)', line)
+               if m:
+                       return True if quick else m.group('t', 'f', 'n', 'p')
+               return False if quick else ('', '', '', '')
+       def initcall_debug_return(self, line, quick=False):
+               m = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) .* (?P<f>.*)\: PM: '+\
+                       '.* returned (?P<r>[0-9]*) after (?P<dt>[0-9]*) usecs', line)
+               if not m:
+                       m = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) .* (?P<f>.*)\: '+\
+                               '.* returned (?P<r>[0-9]*) after (?P<dt>[0-9]*) usecs', line)
+               if not m:
+                       m = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) call '+\
+                               '(?P<f>.*)\+ returned .* after (?P<dt>.*) usecs', line)
+               if m:
+                       return True if quick else m.group('t', 'f', 'dt')
+               return False if quick else ('', '', '')
        def debugPrint(self):
                for p in self.sortedPhases():
                        list = self.dmesg[p]['list']
@@ -2880,10 +2973,11 @@ class TestProps:
        cmdlinefmt = '^# command \| (?P<cmd>.*)'
        kparamsfmt = '^# kparams \| (?P<kp>.*)'
        devpropfmt = '# Device Properties: .*'
-       pinfofmt   = '# platform-(?P<val>[a-z,A-Z,0-9]*): (?P<info>.*)'
+       pinfofmt   = '# platform-(?P<val>[a-z,A-Z,0-9,_]*): (?P<info>.*)'
        tracertypefmt = '# tracer: (?P<t>.*)'
        firmwarefmt = '# fwsuspend (?P<s>[0-9]*) fwresume (?P<r>[0-9]*)$'
        procexecfmt = 'ps - (?P<ps>.*)$'
+       procmultifmt = '@(?P<n>[0-9]*)\|(?P<ps>.*)$'
        ftrace_line_fmt_fg = \
                '^ *(?P<time>[0-9\.]*) *\| *(?P<cpu>[0-9]*)\)'+\
                ' *(?P<proc>.*)-(?P<pid>[0-9]*) *\|'+\
@@ -2893,6 +2987,9 @@ class TestProps:
                '(?P<flags>\S*) *(?P<time>[0-9\.]*): *'+\
                '(?P<msg>.*)'
        machinesuspend = 'machine_suspend\[.*'
+       multiproclist = dict()
+       multiproctime = 0.0
+       multiproccnt = 0
        def __init__(self):
                self.stamp = ''
                self.sysinfo = ''
@@ -3063,6 +3160,7 @@ class TestRun:
                self.ttemp = dict()
 
 class ProcessMonitor:
+       maxchars = 512
        def __init__(self):
                self.proclist = dict()
                self.running = False
@@ -3088,19 +3186,23 @@ class ProcessMonitor:
                        if ujiff > 0 or kjiff > 0:
                                running[pid] = ujiff + kjiff
                process.wait()
-               out = ''
+               out = ['']
                for pid in running:
                        jiffies = running[pid]
                        val = self.proclist[pid]
-                       if out:
-                               out += ','
-                       out += '%s-%s %d' % (val['name'], pid, jiffies)
-               return 'ps - '+out
+                       if len(out[-1]) > self.maxchars:
+                               out.append('')
+                       elif len(out[-1]) > 0:
+                               out[-1] += ','
+                       out[-1] += '%s-%s %d' % (val['name'], pid, jiffies)
+               if len(out) > 1:
+                       for line in out:
+                               sysvals.fsetVal('ps - @%d|%s' % (len(out), line), 'trace_marker')
+               else:
+                       sysvals.fsetVal('ps - %s' % out[0], 'trace_marker')
        def processMonitor(self, tid):
                while self.running:
-                       out = self.procstat()
-                       if out:
-                               sysvals.fsetVal(out, 'trace_marker')
+                       self.procstat()
        def start(self):
                self.thread = Thread(target=self.processMonitor, args=(0,))
                self.running = True
@@ -3144,7 +3246,6 @@ def doesTraceLogHaveTraceEvents():
 
 # Function: appendIncompleteTraceLog
 # Description:
-#       [deprecated for kernel 3.15 or newer]
 #       Adds callgraph data which lacks trace event data. This is only
 #       for timelines generated from 3.15 or older
 # Arguments:
@@ -3246,6 +3347,61 @@ def appendIncompleteTraceLog(testruns):
                                                                dev['ftrace'] = cg
                                                break
 
+# Function: loadTraceLog
+# Description:
+#       load the ftrace file into memory and fix up any ordering issues
+# Output:
+#       TestProps instance and an array of lines in proper order
+def loadTraceLog():
+       tp, data, lines, trace = TestProps(), dict(), [], []
+       tf = sysvals.openlog(sysvals.ftracefile, 'r')
+       for line in tf:
+               # remove any latent carriage returns
+               line = line.replace('\r\n', '')
+               if tp.stampInfo(line, sysvals):
+                       continue
+               # ignore all other commented lines
+               if line[0] == '#':
+                       continue
+               # ftrace line: parse only valid lines
+               m = re.match(tp.ftrace_line_fmt, line)
+               if(not m):
+                       continue
+               dur = m.group('dur') if tp.cgformat else 'traceevent'
+               info = (m.group('time'), m.group('proc'), m.group('pid'),
+                       m.group('msg'), dur)
+               # group the data by timestamp
+               t = float(info[0])
+               if t in data:
+                       data[t].append(info)
+               else:
+                       data[t] = [info]
+               # we only care about trace event ordering
+               if (info[3].startswith('suspend_resume:') or \
+                       info[3].startswith('tracing_mark_write:')) and t not in trace:
+                               trace.append(t)
+       tf.close()
+       for t in sorted(data):
+               first, last, blk = [], [], data[t]
+               if len(blk) > 1 and t in trace:
+                       # move certain lines to the start or end of a timestamp block
+                       for i in range(len(blk)):
+                               if 'SUSPEND START' in blk[i][3]:
+                                       first.append(i)
+                               elif re.match('.* timekeeping_freeze.*begin', blk[i][3]):
+                                       last.append(i)
+                               elif re.match('.* timekeeping_freeze.*end', blk[i][3]):
+                                       first.append(i)
+                               elif 'RESUME COMPLETE' in blk[i][3]:
+                                       last.append(i)
+                       if len(first) == 1 and len(last) == 0:
+                               blk.insert(0, blk.pop(first[0]))
+                       elif len(last) == 1 and len(first) == 0:
+                               blk.append(blk.pop(last[0]))
+               for info in blk:
+                       lines.append(info)
+       return (tp, lines)
+
 # Function: parseTraceLog
 # Description:
 #       Analyze an ftrace log output file generated from this app during
@@ -3271,32 +3427,12 @@ def parseTraceLog(live=False):
 
        # extract the callgraph and traceevent data
        s2idle_enter = hwsus = False
-       tp = TestProps()
        testruns, testdata = [], []
        testrun, data, limbo = 0, 0, True
-       tf = sysvals.openlog(sysvals.ftracefile, 'r')
        phase = 'suspend_prepare'
-       for line in tf:
-               # remove any latent carriage returns
-               line = line.replace('\r\n', '')
-               if tp.stampInfo(line, sysvals):
-                       continue
-               # ignore all other commented lines
-               if line[0] == '#':
-                       continue
-               # ftrace line: parse only valid lines
-               m = re.match(tp.ftrace_line_fmt, line)
-               if(not m):
-                       continue
+       tp, tf = loadTraceLog()
+       for m_time, m_proc, m_pid, m_msg, m_param3 in tf:
                # gather the basic message data from the line
-               m_time = m.group('time')
-               m_proc = m.group('proc')
-               m_pid = m.group('pid')
-               m_msg = m.group('msg')
-               if(tp.cgformat):
-                       m_param3 = m.group('dur')
-               else:
-                       m_param3 = 'traceevent'
                if(m_time and m_pid and m_msg):
                        t = FTraceLine(m_time, m_msg, m_param3)
                        pid = int(m_pid)
@@ -3322,14 +3458,29 @@ def parseTraceLog(live=False):
                if t.type == 'tracing_mark_write':
                        m = re.match(tp.procexecfmt, t.name)
                        if(m):
-                               proclist = dict()
-                               for ps in m.group('ps').split(','):
+                               parts, msg = 1, m.group('ps')
+                               m = re.match(tp.procmultifmt, msg)
+                               if(m):
+                                       parts, msg = int(m.group('n')), m.group('ps')
+                                       if tp.multiproccnt == 0:
+                                               tp.multiproctime = t.time
+                                               tp.multiproclist = dict()
+                                       proclist = tp.multiproclist
+                                       tp.multiproccnt += 1
+                               else:
+                                       proclist = dict()
+                                       tp.multiproccnt = 0
+                               for ps in msg.split(','):
                                        val = ps.split()
-                                       if not val:
+                                       if not val or len(val) != 2:
                                                continue
                                        name = val[0].replace('--', '-')
                                        proclist[name] = int(val[1])
-                               data.pstl[t.time] = proclist
+                               if parts == 1:
+                                       data.pstl[t.time] = proclist
+                               elif parts == tp.multiproccnt:
+                                       data.pstl[tp.multiproctime] = proclist
+                                       tp.multiproccnt = 0
                                continue
                # find the end of resume
                if(t.endMarker()):
@@ -3545,7 +3696,6 @@ def parseTraceLog(live=False):
                                testrun.ftemp[key].append(FTraceCallGraph(pid, sysvals))
                        if(res == -1):
                                testrun.ftemp[key][-1].addLine(t)
-       tf.close()
        if len(testdata) < 1:
                sysvals.vprint('WARNING: ftrace start marker is missing')
        if data and not data.devicegroups:
@@ -3667,7 +3817,13 @@ def parseTraceLog(live=False):
                        if p not in data.dmesg:
                                if not terr:
                                        ph = p if 'machine' in p else lp
-                                       terr = '%s%s failed in %s phase' % (sysvals.suspendmode, tn, ph)
+                                       if p == 'suspend_machine':
+                                               sm = sysvals.suspendmode
+                                               if sm in suspendmodename:
+                                                       sm = suspendmodename[sm]
+                                               terr = 'test%s did not enter %s power mode' % (tn, sm)
+                                       else:
+                                               terr = '%s%s failed in %s phase' % (sysvals.suspendmode, tn, ph)
                                        pprint('TEST%s FAILED: %s' % (tn, terr))
                                        error.append(terr)
                                        if data.tSuspended == 0:
@@ -3708,9 +3864,7 @@ def parseTraceLog(live=False):
 
 # Function: loadKernelLog
 # Description:
-#       [deprecated for kernel 3.15.0 or newer]
 #       load the dmesg file into memory and fix up any ordering issues
-#       The dmesg filename is taken from sysvals
 # Output:
 #       An array of empty Data objects with only their dmesgtext attributes set
 def loadKernelLog():
@@ -3736,7 +3890,8 @@ def loadKernelLog():
                if(not m):
                        continue
                msg = m.group("msg")
-               if(re.match('PM: Syncing filesystems.*', msg)):
+               if re.match('PM: Syncing filesystems.*', msg) or \
+                       re.match('PM: suspend entry.*', msg):
                        if(data):
                                testruns.append(data)
                        data = Data(len(testruns))
@@ -3747,11 +3902,17 @@ def loadKernelLog():
                if(m):
                        sysvals.stamp['kernel'] = m.group('k')
                m = re.match('PM: Preparing system for (?P<m>.*) sleep', msg)
-               if(m):
+               if not m:
+                       m = re.match('PM: Preparing system for sleep \((?P<m>.*)\)', msg)
+               if m:
                        sysvals.stamp['mode'] = sysvals.suspendmode = m.group('m')
                data.dmesgtext.append(line)
        lf.close()
 
+       if sysvals.suspendmode == 's2idle':
+               sysvals.suspendmode = 'freeze'
+       elif sysvals.suspendmode == 'deep':
+               sysvals.suspendmode = 'mem'
        if data:
                testruns.append(data)
        if len(testruns) < 1:
@@ -3762,12 +3923,9 @@ def loadKernelLog():
        for data in testruns:
                last = ''
                for line in data.dmesgtext:
-                       mc = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) calling  '+\
-                               '(?P<f>.*)\+ @ .*, parent: .*', line)
-                       mr = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) call '+\
-                               '(?P<f>.*)\+ returned .* after (?P<dt>.*) usecs', last)
-                       if(mc and mr and (mc.group('t') == mr.group('t')) and
-                               (mc.group('f') == mr.group('f'))):
+                       ct, cf, n, p = data.initcall_debug_call(line)
+                       rt, rf, l = data.initcall_debug_return(last)
+                       if ct and rt and ct == rt and cf == rf:
                                i = data.dmesgtext.index(last)
                                j = data.dmesgtext.index(line)
                                data.dmesgtext[i] = line
@@ -3777,7 +3935,6 @@ def loadKernelLog():
 
 # Function: parseKernelLog
 # Description:
-#       [deprecated for kernel 3.15.0 or newer]
 #       Analyse a dmesg log output file generated from this app during
 #       the execution phase. Create a set of device structures in memory
 #       for subsequent formatting in the html output file
@@ -3796,30 +3953,30 @@ def parseKernelLog(data):
 
        # dmesg phase match table
        dm = {
-               'suspend_prepare': ['PM: Syncing filesystems.*'],
-                       'suspend': ['PM: Entering [a-z]* sleep.*', 'Suspending console.*'],
-                  'suspend_late': ['PM: suspend of devices complete after.*'],
-                 'suspend_noirq': ['PM: late suspend of devices complete after.*'],
-               'suspend_machine': ['PM: noirq suspend of devices complete after.*'],
-                'resume_machine': ['ACPI: Low-level resume complete.*'],
-                  'resume_noirq': ['ACPI: Waking up from system sleep state.*'],
-                  'resume_early': ['PM: noirq resume of devices complete after.*'],
-                        'resume': ['PM: early resume of devices complete after.*'],
-               'resume_complete': ['PM: resume of devices complete after.*'],
+               'suspend_prepare': ['PM: Syncing filesystems.*', 'PM: suspend entry.*'],
+                       'suspend': ['PM: Entering [a-z]* sleep.*', 'Suspending console.*',
+                                   'PM: Suspending system .*'],
+                  'suspend_late': ['PM: suspend of devices complete after.*',
+                                                       'PM: freeze of devices complete after.*'],
+                 'suspend_noirq': ['PM: late suspend of devices complete after.*',
+                                                       'PM: late freeze of devices complete after.*'],
+               'suspend_machine': ['PM: suspend-to-idle',
+                                                       'PM: noirq suspend of devices complete after.*',
+                                                       'PM: noirq freeze of devices complete after.*'],
+                'resume_machine': ['PM: Timekeeping suspended for.*',
+                                                       'ACPI: Low-level resume complete.*',
+                                                       'ACPI: resume from mwait',
+                                                       'Suspended for [0-9\.]* seconds'],
+                  'resume_noirq': ['PM: resume from suspend-to-idle',
+                                                       'ACPI: Waking up from system sleep state.*'],
+                  'resume_early': ['PM: noirq resume of devices complete after.*',
+                                                       'PM: noirq restore of devices complete after.*'],
+                        'resume': ['PM: early resume of devices complete after.*',
+                                                       'PM: early restore of devices complete after.*'],
+               'resume_complete': ['PM: resume of devices complete after.*',
+                                                       'PM: restore of devices complete after.*'],
                    'post_resume': ['.*Restarting tasks \.\.\..*'],
        }
-       if(sysvals.suspendmode == 'standby'):
-               dm['resume_machine'] = ['PM: Restoring platform NVS memory']
-       elif(sysvals.suspendmode == 'disk'):
-               dm['suspend_late'] = ['PM: freeze of devices complete after.*']
-               dm['suspend_noirq'] = ['PM: late freeze of devices complete after.*']
-               dm['suspend_machine'] = ['PM: noirq freeze of devices complete after.*']
-               dm['resume_machine'] = ['PM: Restoring platform NVS memory']
-               dm['resume_early'] = ['PM: noirq restore of devices complete after.*']
-               dm['resume'] = ['PM: early restore of devices complete after.*']
-               dm['resume_complete'] = ['PM: restore of devices complete after.*']
-       elif(sysvals.suspendmode == 'freeze'):
-               dm['resume_machine'] = ['ACPI: resume from mwait']
 
        # action table (expected events that occur and show up in dmesg)
        at = {
@@ -3867,12 +4024,13 @@ def parseKernelLog(data):
                        for s in dm[p]:
                                if(re.match(s, msg)):
                                        phasechange, phase = True, p
+                                       dm[p] = [s]
                                        break
 
                # hack for determining resume_machine end for freeze
                if(not sysvals.usetraceevents and sysvals.suspendmode == 'freeze' \
                        and phase == 'resume_machine' and \
-                       re.match('calling  (?P<f>.*)\+ @ .*, parent: .*', msg)):
+                       data.initcall_debug_call(line, True)):
                        data.setPhase(phase, ktime, False)
                        phase = 'resume_noirq'
                        data.setPhase(phase, ktime, True)
@@ -3945,26 +4103,18 @@ def parseKernelLog(data):
                # -- device callbacks --
                if(phase in data.sortedPhases()):
                        # device init call
-                       if(re.match('calling  (?P<f>.*)\+ @ .*, parent: .*', msg)):
-                               sm = re.match('calling  (?P<f>.*)\+ @ '+\
-                                       '(?P<n>.*), parent: (?P<p>.*)', msg);
-                               f = sm.group('f')
-                               n = sm.group('n')
-                               p = sm.group('p')
-                               if(f and n and p):
-                                       data.newAction(phase, f, int(n), p, ktime, -1, '')
-                       # device init return
-                       elif(re.match('call (?P<f>.*)\+ returned .* after '+\
-                               '(?P<t>.*) usecs', msg)):
-                               sm = re.match('call (?P<f>.*)\+ returned .* after '+\
-                                       '(?P<t>.*) usecs(?P<a>.*)', msg);
-                               f = sm.group('f')
-                               t = sm.group('t')
-                               list = data.dmesg[phase]['list']
-                               if(f in list):
-                                       dev = list[f]
-                                       dev['length'] = int(t)
-                                       dev['end'] = ktime
+                       t, f, n, p = data.initcall_debug_call(line)
+                       if t and f and n and p:
+                               data.newAction(phase, f, int(n), p, ktime, -1, '')
+                       else:
+                               # device init return
+                               t, f, l = data.initcall_debug_return(line)
+                               if t and f and l:
+                                       list = data.dmesg[phase]['list']
+                                       if(f in list):
+                                               dev = list[f]
+                                               dev['length'] = int(l)
+                                               dev['end'] = ktime
 
                # if trace events are not available, these are better than nothing
                if(not sysvals.usetraceevents):
@@ -4006,6 +4156,8 @@ def parseKernelLog(data):
        # fill in any missing phases
        phasedef = data.phasedef
        terr, lp = '', 'suspend_prepare'
+       if lp not in data.dmesg:
+               doError('dmesg log format has changed, could not find start of suspend')
        for p in sorted(phasedef, key=lambda k:phasedef[k]['order']):
                if p not in data.dmesg:
                        if not terr:
@@ -5302,7 +5454,7 @@ def executeSuspend(quiet=False):
        sv.dlog('read dmesg')
        sv.initdmesg()
        # start ftrace
-       if(sv.usecallgraph or sv.usetraceevents):
+       if sv.useftrace:
                if not quiet:
                        pprint('START TRACING')
                sv.dlog('start ftrace tracing')
@@ -5334,8 +5486,7 @@ def executeSuspend(quiet=False):
                        sv.dlog('enable RTC wake alarm')
                        sv.rtcWakeAlarmOn()
                # start of suspend trace marker
-               if(sv.usecallgraph or sv.usetraceevents):
-                       sv.fsetVal(datetime.now().strftime(sv.tmstart), 'trace_marker')
+               sv.fsetVal(datetime.now().strftime(sv.tmstart), 'trace_marker')
                # predelay delay
                if(count == 1 and sv.predelay > 0):
                        sv.fsetVal('WAIT %d' % sv.predelay, 'trace_marker')
@@ -5384,11 +5535,17 @@ def executeSuspend(quiet=False):
                        sv.fsetVal('WAIT END', 'trace_marker')
                # return from suspend
                pprint('RESUME COMPLETE')
-               if(sv.usecallgraph or sv.usetraceevents):
-                       sv.fsetVal(datetime.now().strftime(sv.tmend), 'trace_marker')
+               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.netfix:
+                               netfixout = sv.netfixon('wired')
+               elif sv.netfix:
+                       netfixout = sv.netfixon()
+               if sv.netfix and netfixout:
+                       tdata['netfix'] = netfixout
+                       sv.dlog('netfix, %s' % tdata['netfix'])
                if(sv.suspendmode == 'mem' or sv.suspendmode == 'command'):
                        sv.dlog('read the ACPI FPDT')
                        tdata['fw'] = getFPDT(False)
@@ -5396,7 +5553,7 @@ def executeSuspend(quiet=False):
        sv.dlog('run the cmdinfo list after')
        cmdafter = sv.cmdinfo(False)
        # stop ftrace
-       if(sv.usecallgraph or sv.usetraceevents):
+       if sv.useftrace:
                if sv.useprocmon:
                        sv.dlog('stop the process monitor')
                        pm.stop()
@@ -5407,7 +5564,7 @@ def executeSuspend(quiet=False):
        sysvals.dlog('EXECUTION TRACE END')
        sv.getdmesg(testdata)
        # grab a copy of the ftrace output
-       if(sv.usecallgraph or sv.usetraceevents):
+       if sv.useftrace:
                if not quiet:
                        pprint('CAPTURING TRACE')
                op = sv.writeDatafileHeader(sv.ftracefile, testdata)
@@ -5838,13 +5995,19 @@ def statusCheck(probecheck=False):
                        pprint('      please choose one with -m')
 
        # check if ftrace is available
-       res = sysvals.colorText('NO')
-       ftgood = sysvals.verifyFtrace()
-       if(ftgood):
-               res = 'YES'
-       elif(sysvals.usecallgraph):
-               status = 'ftrace is not properly supported'
-       pprint('    is ftrace supported: %s' % res)
+       if sysvals.useftrace:
+               res = sysvals.colorText('NO')
+               sysvals.useftrace = sysvals.verifyFtrace()
+               efmt = '"{0}" uses ftrace, and it is not properly supported'
+               if sysvals.useftrace:
+                       res = 'YES'
+               elif sysvals.usecallgraph:
+                       status = efmt.format('-f')
+               elif sysvals.usedevsrc:
+                       status = efmt.format('-dev')
+               elif sysvals.useprocmon:
+                       status = efmt.format('-proc')
+               pprint('    is ftrace supported: %s' % res)
 
        # check if kprobes are available
        if sysvals.usekprobes:
@@ -5857,8 +6020,8 @@ def statusCheck(probecheck=False):
                pprint('    are kprobes supported: %s' % res)
 
        # what data source are we using
-       res = 'DMESG'
-       if(ftgood):
+       res = 'DMESG (very limited, ftrace is preferred)'
+       if sysvals.useftrace:
                sysvals.usetraceevents = True
                for e in sysvals.traceevents:
                        if not os.path.exists(sysvals.epath+e):
@@ -5879,7 +6042,7 @@ def statusCheck(probecheck=False):
        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']:
+       for c in ['turbostat', 'mcelog', 'lspci', 'lsusb', 'netfix']:
                if c == 'turbostat':
                        res = yes if sysvals.haveTurbostat() else no
                else:
@@ -5971,7 +6134,7 @@ def processData(live=False, quiet=False):
        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 = ['os', 'bios', 'biosdate', 'cpu', 'host', 'kernel', 'man', 'memfr',
                        'memsz', 'mode', 'numcpu', 'plat', 'time', 'wifi']
        sysvals.vprint('System Info:')
        for key in sorted(sysvals.stamp):
@@ -6052,6 +6215,8 @@ def runTest(n=0, quiet=False):
                if sysvals.display:
                        ret = sysvals.displayControl('init')
                        sysvals.dlog('xset display init, ret = %d' % ret)
+       sysvals.testVal(sysvals.pmdpath, 'basic', '1')
+       sysvals.testVal(sysvals.s0ixpath, 'basic', 'Y')
        sysvals.dlog('initialize ftrace')
        sysvals.initFtrace(quiet)
 
@@ -6145,9 +6310,12 @@ def data_from_html(file, outpath, issues, fulldetail=False):
                                elist[err[0]] += 1
                for i in elist:
                        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
+               line = find_in_html(log, '# wifi ', '\n')
+               if line:
+                       extra['wifi'] = line
+               line = find_in_html(log, '# netfix ', '\n')
+               if line:
+                       extra['netfix'] = line
        low = find_in_html(html, 'freeze time: <b>', ' ms</b>')
        for lowstr in ['waking', '+']:
                if not low:
@@ -6243,7 +6411,7 @@ def genHtml(subdir, force=False):
                                        sysvals.ftracefile = file
                sysvals.setOutputFile()
                if (sysvals.dmesgfile or sysvals.ftracefile) and sysvals.htmlfile and \
-                       (force or not sysvals.usable(sysvals.htmlfile)):
+                       (force or not sysvals.usable(sysvals.htmlfile, True)):
                        pprint('FTRACE: %s' % sysvals.ftracefile)
                        if sysvals.dmesgfile:
                                pprint('DMESG : %s' % sysvals.dmesgfile)
@@ -6533,6 +6701,7 @@ def printHelp():
        '   -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'\
        '   -wifi        If a wifi connection is available, check that it reconnects after resume.\n'\
+       '   -netfix      Use netfix to reset the network in the event it fails to resume.\n'\
        '  [testprep]\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'\
@@ -6615,6 +6784,8 @@ if __name__ == '__main__':
                elif(arg == '-v'):
                        pprint("Version %s" % sysvals.version)
                        sys.exit(0)
+               elif(arg == '-debugtiming'):
+                       debugtiming = True
                elif(arg == '-x2'):
                        sysvals.execcount = 2
                elif(arg == '-x2delay'):
@@ -6657,6 +6828,8 @@ if __name__ == '__main__':
                        sysvals.sync = True
                elif(arg == '-wifi'):
                        sysvals.wifi = True
+               elif(arg == '-netfix'):
+                       sysvals.netfix = True
                elif(arg == '-gzip'):
                        sysvals.gzip = True
                elif(arg == '-info'):
@@ -6819,7 +6992,7 @@ if __name__ == '__main__':
                        sysvals.outdir = val
                        sysvals.notestrun = True
                        if(os.path.isdir(val) == False):
-                               doError('%s is not accessible' % val)
+                               doError('%s is not accesible' % val)
                elif(arg == '-filter'):
                        try:
                                val = next(args)
@@ -6942,12 +7115,11 @@ if __name__ == '__main__':
                                time.sleep(sysvals.multitest['delay'])
                        fmt = 'suspend-%y%m%d-%H%M%S'
                        sysvals.testdir = os.path.join(sysvals.outdir, datetime.now().strftime(fmt))
-                       ret = runTest(i+1, True)
+                       ret = runTest(i+1, not sysvals.verbose)
                        failcnt = 0 if not ret else failcnt + 1
                        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: