hc
2024-05-11 04dd17822334871b23ea2862f7798fb0e0007777
kernel/tools/power/pm-graph/sleepgraph.py
....@@ -1,4 +1,5 @@
1
-#!/usr/bin/python2
1
+#!/usr/bin/env python3
2
+# SPDX-License-Identifier: GPL-2.0-only
23 #
34 # Tool for analyzing suspend/resume timing
45 # Copyright (c) 2013, Intel Corporation.
....@@ -17,9 +18,9 @@
1718 #
1819 # Links:
1920 # Home Page
20
-# https://01.org/suspendresume
21
+# https://01.org/pm-graph
2122 # Source repo
22
-# git@github.com:01org/pm-graph
23
+# git@github.com:intel/pm-graph
2324 #
2425 # Description:
2526 # This tool is designed to assist kernel and OS developers in optimizing
....@@ -32,6 +33,7 @@
3233 # viewed in firefox or chrome.
3334 #
3435 # The following kernel build options are required:
36
+# CONFIG_DEVMEM=y
3537 # CONFIG_PM_DEBUG=y
3638 # CONFIG_PM_SLEEP_DEBUG=y
3739 # CONFIG_FTRACE=y
....@@ -54,12 +56,22 @@
5456 import string
5557 import re
5658 import platform
57
-from datetime import datetime
59
+import signal
60
+import codecs
61
+from datetime import datetime, timedelta
5862 import struct
59
-import ConfigParser
63
+import configparser
6064 import gzip
6165 from threading import Thread
6266 from subprocess import call, Popen, PIPE
67
+import base64
68
+
69
+def pprint(msg):
70
+ print(msg)
71
+ sys.stdout.flush()
72
+
73
+def ascii(text):
74
+ return text.decode('ascii', 'ignore')
6375
6476 # ----------------- CLASSES --------------------
6577
....@@ -69,22 +81,25 @@
6981 # store system values and test parameters
7082 class SystemValues:
7183 title = 'SleepGraph'
72
- version = '5.1'
84
+ version = '5.7'
7385 ansi = False
7486 rs = 0
75
- display = 0
87
+ display = ''
7688 gzip = False
7789 sync = False
90
+ wifi = False
7891 verbose = False
7992 testlog = True
80
- dmesglog = False
93
+ dmesglog = True
8194 ftracelog = False
95
+ tstat = True
8296 mindevlen = 0.0
8397 mincglen = 0.0
8498 cgphase = ''
8599 cgtest = -1
86100 cgskip = ''
87
- multitest = {'run': False, 'count': 0, 'delay': 0}
101
+ maxfail = 0
102
+ multitest = {'run': False, 'count': 1000000, 'delay': 0}
88103 max_graph_depth = 0
89104 callloopmaxgap = 0.0001
90105 callloopmaxlen = 0.005
....@@ -99,8 +114,11 @@
99114 tpath = '/sys/kernel/debug/tracing/'
100115 fpdtpath = '/sys/firmware/acpi/tables/FPDT'
101116 epath = '/sys/kernel/debug/tracing/events/power/'
117
+ pmdpath = '/sys/power/pm_debug_messages'
102118 traceevents = [
103119 'suspend_resume',
120
+ 'wakeup_source_activate',
121
+ 'wakeup_source_deactivate',
104122 'device_pm_callback_end',
105123 'device_pm_callback_start'
106124 ]
....@@ -109,8 +127,10 @@
109127 mempath = '/dev/mem'
110128 powerfile = '/sys/power/state'
111129 mempowerfile = '/sys/power/mem_sleep'
130
+ diskpowerfile = '/sys/power/disk'
112131 suspendmode = 'mem'
113132 memmode = ''
133
+ diskmode = ''
114134 hostname = 'localhost'
115135 prefix = 'test'
116136 teststamp = ''
....@@ -130,6 +150,8 @@
130150 x2delay = 0
131151 skiphtml = False
132152 usecallgraph = False
153
+ ftopfunc = 'pm_suspend'
154
+ ftop = False
133155 usetraceevents = False
134156 usetracemarkers = True
135157 usekprobes = True
....@@ -137,17 +159,19 @@
137159 useprocmon = False
138160 notestrun = False
139161 cgdump = False
162
+ devdump = False
140163 mixedphaseheight = True
141164 devprops = dict()
165
+ platinfo = []
142166 predelay = 0
143167 postdelay = 0
144
- procexecfmt = 'ps - (?P<ps>.*)$'
145
- devpropfmt = '# Device Properties: .*'
146
- tracertypefmt = '# tracer: (?P<t>.*)'
147
- firmwarefmt = '# fwsuspend (?P<s>[0-9]*) fwresume (?P<r>[0-9]*)$'
168
+ pmdebug = ''
169
+ tmstart = 'SUSPEND START %Y%m%d-%H:%M:%S.%f'
170
+ tmend = 'RESUME COMPLETE %Y%m%d-%H:%M:%S.%f'
148171 tracefuncs = {
149172 'sys_sync': {},
150
- '__pm_notifier_call_chain': {},
173
+ 'ksys_sync': {},
174
+ 'pm_notifier_call_chain_robust': {},
151175 'pm_prepare_console': {},
152176 'pm_notifier_call_chain': {},
153177 'freeze_processes': {},
....@@ -159,6 +183,15 @@
159183 'acpi_hibernation_leave': {},
160184 'acpi_pm_freeze': {},
161185 'acpi_pm_thaw': {},
186
+ 'acpi_s2idle_end': {},
187
+ 'acpi_s2idle_sync': {},
188
+ 'acpi_s2idle_begin': {},
189
+ 'acpi_s2idle_prepare': {},
190
+ 'acpi_s2idle_prepare_late': {},
191
+ 'acpi_s2idle_wake': {},
192
+ 'acpi_s2idle_wakeup': {},
193
+ 'acpi_s2idle_restore': {},
194
+ 'acpi_s2idle_restore_early': {},
162195 'hibernate_preallocate_memory': {},
163196 'create_basic_memory_bitmaps': {},
164197 'swsusp_write': {},
....@@ -187,18 +220,25 @@
187220 dev_tracefuncs = {
188221 # general wait/delay/sleep
189222 'msleep': { 'args_x86_64': {'time':'%di:s32'}, 'ub': 1 },
190
- 'schedule_timeout_uninterruptible': { 'args_x86_64': {'timeout':'%di:s32'}, 'ub': 1 },
191223 'schedule_timeout': { 'args_x86_64': {'timeout':'%di:s32'}, 'ub': 1 },
192224 'udelay': { 'func':'__const_udelay', 'args_x86_64': {'loops':'%di:s32'}, 'ub': 1 },
193225 'usleep_range': { 'args_x86_64': {'min':'%di:s32', 'max':'%si:s32'}, 'ub': 1 },
194226 'mutex_lock_slowpath': { 'func':'__mutex_lock_slowpath', 'ub': 1 },
195227 'acpi_os_stall': {'ub': 1},
228
+ 'rt_mutex_slowlock': {'ub': 1},
196229 # ACPI
197230 'acpi_resume_power_resources': {},
198
- 'acpi_ps_parse_aml': {},
231
+ 'acpi_ps_execute_method': { 'args_x86_64': {
232
+ 'fullpath':'+0(+40(%di)):string',
233
+ }},
234
+ # mei_me
235
+ 'mei_reset': {},
199236 # filesystem
200237 'ext4_sync_fs': {},
201238 # 80211
239
+ 'ath10k_bmi_read_memory': { 'args_x86_64': {'length':'%cx:s32'} },
240
+ 'ath10k_bmi_write_memory': { 'args_x86_64': {'length':'%cx:s32'} },
241
+ 'ath10k_bmi_fast_download': { 'args_x86_64': {'length':'%cx:s32'} },
202242 'iwlagn_mac_start': {},
203243 'iwlagn_alloc_bcast_station': {},
204244 'iwl_trans_pcie_start_hw': {},
....@@ -236,11 +276,24 @@
236276 'intel_opregion_init': {},
237277 'intel_fbdev_set_suspend': {},
238278 }
279
+ infocmds = [
280
+ [0, 'kparams', 'cat', '/proc/cmdline'],
281
+ [0, 'mcelog', 'mcelog'],
282
+ [0, 'pcidevices', 'lspci', '-tv'],
283
+ [0, 'usbdevices', 'lsusb', '-t'],
284
+ [1, 'interrupts', 'cat', '/proc/interrupts'],
285
+ [1, 'wakeups', 'cat', '/sys/kernel/debug/wakeup_sources'],
286
+ [2, 'gpecounts', 'sh', '-c', 'grep -v invalid /sys/firmware/acpi/interrupts/*'],
287
+ [2, 'suspendstats', 'sh', '-c', 'grep -v invalid /sys/power/suspend_stats/*'],
288
+ [2, 'cpuidle', 'sh', '-c', 'grep -v invalid /sys/devices/system/cpu/cpu*/cpuidle/state*/s2idle/*'],
289
+ [2, 'battery', 'sh', '-c', 'grep -v invalid /sys/class/power_supply/*/*'],
290
+ ]
239291 cgblacklist = []
240292 kprobes = dict()
241293 timeformat = '%.3f'
242294 cmdline = '%s %s' % \
243295 (os.path.basename(sys.argv[0]), ' '.join(sys.argv[1:]))
296
+ sudouser = ''
244297 def __init__(self):
245298 self.archargs = 'args_'+platform.machine()
246299 self.hostname = platform.node()
....@@ -256,36 +309,70 @@
256309 if (hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()):
257310 self.ansi = True
258311 self.testdir = datetime.now().strftime('suspend-%y%m%d-%H%M%S')
312
+ if os.getuid() == 0 and 'SUDO_USER' in os.environ and \
313
+ os.environ['SUDO_USER']:
314
+ self.sudouser = os.environ['SUDO_USER']
315
+ def resetlog(self):
316
+ self.logmsg = ''
317
+ self.platinfo = []
259318 def vprint(self, msg):
260319 self.logmsg += msg+'\n'
261
- if(self.verbose):
262
- print(msg)
320
+ if self.verbose or msg.startswith('WARNING:'):
321
+ pprint(msg)
322
+ def signalHandler(self, signum, frame):
323
+ if not self.result:
324
+ return
325
+ signame = self.signames[signum] if signum in self.signames else 'UNKNOWN'
326
+ msg = 'Signal %s caused a tool exit, line %d' % (signame, frame.f_lineno)
327
+ self.outputResult({'error':msg})
328
+ sys.exit(3)
329
+ def signalHandlerInit(self):
330
+ capture = ['BUS', 'SYS', 'XCPU', 'XFSZ', 'PWR', 'HUP', 'INT', 'QUIT',
331
+ 'ILL', 'ABRT', 'FPE', 'SEGV', 'TERM']
332
+ self.signames = dict()
333
+ for i in capture:
334
+ s = 'SIG'+i
335
+ try:
336
+ signum = getattr(signal, s)
337
+ signal.signal(signum, self.signalHandler)
338
+ except:
339
+ continue
340
+ self.signames[signum] = s
263341 def rootCheck(self, fatal=True):
264342 if(os.access(self.powerfile, os.W_OK)):
265343 return True
266344 if fatal:
267345 msg = 'This command requires sysfs mount and root access'
268
- print('ERROR: %s\n') % msg
346
+ pprint('ERROR: %s\n' % msg)
269347 self.outputResult({'error':msg})
270
- sys.exit()
348
+ sys.exit(1)
271349 return False
272350 def rootUser(self, fatal=False):
273351 if 'USER' in os.environ and os.environ['USER'] == 'root':
274352 return True
275353 if fatal:
276354 msg = 'This command must be run as root'
277
- print('ERROR: %s\n') % msg
355
+ pprint('ERROR: %s\n' % msg)
278356 self.outputResult({'error':msg})
279
- sys.exit()
357
+ sys.exit(1)
280358 return False
359
+ def usable(self, file):
360
+ return (os.path.exists(file) and os.path.getsize(file) > 0)
281361 def getExec(self, cmd):
282
- dirlist = ['/sbin', '/bin', '/usr/sbin', '/usr/bin',
283
- '/usr/local/sbin', '/usr/local/bin']
284
- for path in dirlist:
362
+ try:
363
+ fp = Popen(['which', cmd], stdout=PIPE, stderr=PIPE).stdout
364
+ out = ascii(fp.read()).strip()
365
+ fp.close()
366
+ except:
367
+ out = ''
368
+ if out:
369
+ return out
370
+ for path in ['/sbin', '/bin', '/usr/sbin', '/usr/bin',
371
+ '/usr/local/sbin', '/usr/local/bin']:
285372 cmdfull = os.path.join(path, cmd)
286373 if os.path.exists(cmdfull):
287374 return cmdfull
288
- return ''
375
+ return out
289376 def setPrecision(self, num):
290377 if num < 0 or num > 6:
291378 return
....@@ -296,6 +383,7 @@
296383 args['date'] = n.strftime('%y%m%d')
297384 args['time'] = n.strftime('%H%M%S')
298385 args['hostname'] = args['host'] = self.hostname
386
+ args['mode'] = self.suspendmode
299387 return value.format(**args)
300388 def setOutputFile(self):
301389 if self.dmesgfile != '':
....@@ -307,21 +395,22 @@
307395 if(m):
308396 self.htmlfile = m.group('name')+'.html'
309397 def systemInfo(self, info):
310
- p = c = m = b = ''
398
+ p = m = ''
311399 if 'baseboard-manufacturer' in info:
312400 m = info['baseboard-manufacturer']
313401 elif 'system-manufacturer' in info:
314402 m = info['system-manufacturer']
315
- if 'baseboard-product-name' in info:
316
- p = info['baseboard-product-name']
317
- elif 'system-product-name' in info:
403
+ if 'system-product-name' in info:
318404 p = info['system-product-name']
319
- if 'processor-version' in info:
320
- c = info['processor-version']
321
- if 'bios-version' in info:
322
- b = info['bios-version']
323
- self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | numcpu:%d | memsz:%d | memfr:%d' % \
324
- (m, p, c, b, self.cpucount, self.memtotal, self.memfree)
405
+ elif 'baseboard-product-name' in info:
406
+ p = info['baseboard-product-name']
407
+ if m[:5].lower() == 'intel' and 'baseboard-product-name' in info:
408
+ p = info['baseboard-product-name']
409
+ c = info['processor-version'] if 'processor-version' in info else ''
410
+ b = info['bios-version'] if 'bios-version' in info else ''
411
+ r = info['bios-release-date'] if 'bios-release-date' in info else ''
412
+ self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | biosdate:%s | numcpu:%d | memsz:%d | memfr:%d' % \
413
+ (m, p, c, b, r, self.cpucount, self.memtotal, self.memfree)
325414 def printSystemInfo(self, fatal=False):
326415 self.rootCheck(True)
327416 out = dmidecode(self.mempath, fatal)
....@@ -329,10 +418,10 @@
329418 return
330419 fmt = '%-24s: %s'
331420 for name in sorted(out):
332
- print fmt % (name, out[name])
333
- print fmt % ('cpucount', ('%d' % self.cpucount))
334
- print fmt % ('memtotal', ('%d kB' % self.memtotal))
335
- print fmt % ('memfree', ('%d kB' % self.memfree))
421
+ print(fmt % (name, out[name]))
422
+ print(fmt % ('cpucount', ('%d' % self.cpucount)))
423
+ print(fmt % ('memtotal', ('%d kB' % self.memtotal)))
424
+ print(fmt % ('memfree', ('%d kB' % self.memfree)))
336425 def cpuInfo(self):
337426 self.cpucount = 0
338427 fp = open('/proc/cpuinfo', 'r')
....@@ -352,7 +441,7 @@
352441 def initTestOutput(self, name):
353442 self.prefix = self.hostname
354443 v = open('/proc/version', 'r').read().strip()
355
- kver = string.split(v)[2]
444
+ kver = v.split()[2]
356445 fmt = name+'-%m%d%y-%H%M%S'
357446 testtime = datetime.now().strftime(fmt)
358447 self.teststamp = \
....@@ -367,7 +456,8 @@
367456 self.htmlfile = \
368457 self.testdir+'/'+self.prefix+'_'+self.suspendmode+'.html'
369458 if not os.path.isdir(self.testdir):
370
- os.mkdir(self.testdir)
459
+ os.makedirs(self.testdir)
460
+ self.sudoUserchown(self.testdir)
371461 def getValueList(self, value):
372462 out = []
373463 for i in value.split(','):
....@@ -378,6 +468,12 @@
378468 self.devicefilter = self.getValueList(value)
379469 def setCallgraphFilter(self, value):
380470 self.cgfilter = self.getValueList(value)
471
+ def skipKprobes(self, value):
472
+ for k in self.getValueList(value):
473
+ if k in self.tracefuncs:
474
+ del self.tracefuncs[k]
475
+ if k in self.dev_tracefuncs:
476
+ del self.dev_tracefuncs[k]
381477 def setCallgraphBlacklist(self, file):
382478 self.cgblacklist = self.listFromFile(file)
383479 def rtcWakeAlarmOn(self):
....@@ -397,7 +493,7 @@
397493 fp = Popen('dmesg', stdout=PIPE).stdout
398494 ktime = '0'
399495 for line in fp:
400
- line = line.replace('\r\n', '')
496
+ line = ascii(line).replace('\r\n', '')
401497 idx = line.find('[')
402498 if idx > 1:
403499 line = line[idx:]
....@@ -406,12 +502,12 @@
406502 ktime = m.group('ktime')
407503 fp.close()
408504 self.dmesgstart = float(ktime)
409
- def getdmesg(self, fwdata=[]):
410
- op = self.writeDatafileHeader(sysvals.dmesgfile, fwdata)
505
+ def getdmesg(self, testdata):
506
+ op = self.writeDatafileHeader(self.dmesgfile, testdata)
411507 # store all new dmesg lines since initdmesg was called
412508 fp = Popen('dmesg', stdout=PIPE).stdout
413509 for line in fp:
414
- line = line.replace('\r\n', '')
510
+ line = ascii(line).replace('\r\n', '')
415511 idx = line.find('[')
416512 if idx > 1:
417513 line = line[idx:]
....@@ -443,13 +539,13 @@
443539 call('cat '+self.tpath+'available_filter_functions', shell=True)
444540 return
445541 master = self.listFromFile(self.tpath+'available_filter_functions')
446
- for i in self.tracefuncs:
542
+ for i in sorted(self.tracefuncs):
447543 if 'func' in self.tracefuncs[i]:
448544 i = self.tracefuncs[i]['func']
449545 if i in master:
450
- print i
546
+ print(i)
451547 else:
452
- print self.colorText(i)
548
+ print(self.colorText(i))
453549 def setFtraceFilterFunctions(self, list):
454550 master = self.listFromFile(self.tpath+'available_filter_functions')
455551 flist = ''
....@@ -535,7 +631,7 @@
535631 if len(self.kprobes) < 1:
536632 return
537633 if output:
538
- print(' kprobe functions in this kernel:')
634
+ pprint(' kprobe functions in this kernel:')
539635 # first test each kprobe
540636 rejects = []
541637 # sort kprobes: trace, ub-dev, custom, dev
....@@ -557,7 +653,7 @@
557653 else:
558654 kpl[2].append(name)
559655 if output:
560
- print(' %s: %s' % (name, res))
656
+ pprint(' %s: %s' % (name, res))
561657 kplist = kpl[0] + kpl[1] + kpl[2] + kpl[3]
562658 # remove all failed ones from the list
563659 for name in rejects:
....@@ -570,8 +666,8 @@
570666 self.fsetVal(kprobeevents, 'kprobe_events')
571667 if output:
572668 check = self.fgetVal('kprobe_events')
573
- linesack = (len(check.split('\n')) - 1) / 2
574
- print(' kprobe functions enabled: %d/%d' % (linesack, linesout))
669
+ linesack = (len(check.split('\n')) - 1) // 2
670
+ pprint(' kprobe functions enabled: %d/%d' % (linesack, linesout))
575671 self.fsetVal('1', 'events/kprobes/enable')
576672 def testKprobe(self, kname, kprobe):
577673 self.fsetVal('0', 'events/kprobes/enable')
....@@ -588,19 +684,19 @@
588684 if linesack < linesout:
589685 return False
590686 return True
591
- def setVal(self, val, file, mode='w'):
687
+ def setVal(self, val, file):
592688 if not os.path.exists(file):
593689 return False
594690 try:
595
- fp = open(file, mode, 0)
596
- fp.write(val)
691
+ fp = open(file, 'wb', 0)
692
+ fp.write(val.encode())
597693 fp.flush()
598694 fp.close()
599695 except:
600696 return False
601697 return True
602
- def fsetVal(self, val, path, mode='w'):
603
- return self.setVal(val, self.tpath+path, mode)
698
+ def fsetVal(self, val, path):
699
+ return self.setVal(val, self.tpath+path)
604700 def getVal(self, file):
605701 res = ''
606702 if not os.path.exists(file):
....@@ -619,6 +715,8 @@
619715 self.fsetVal('0', 'events/kprobes/enable')
620716 self.fsetVal('', 'kprobe_events')
621717 self.fsetVal('1024', 'buffer_size_kb')
718
+ if self.pmdebug:
719
+ self.setVal(self.pmdebug, self.pmdpath)
622720 def setupAllKprobes(self):
623721 for name in self.tracefuncs:
624722 self.defaultKprobe(name, self.tracefuncs[name])
....@@ -635,12 +733,18 @@
635733 if name == f:
636734 return True
637735 return False
638
- def initFtrace(self):
639
- self.printSystemInfo(False)
640
- print('INITIALIZING FTRACE...')
736
+ def initFtrace(self, quiet=False):
737
+ if not quiet:
738
+ sysvals.printSystemInfo(False)
739
+ pprint('INITIALIZING FTRACE...')
641740 # turn trace off
642741 self.fsetVal('0', 'tracing_on')
643742 self.cleanupFtrace()
743
+ # pm debug messages
744
+ pv = self.getVal(self.pmdpath)
745
+ if pv != '1':
746
+ self.setVal('1', self.pmdpath)
747
+ self.pmdebug = pv
644748 # set the trace clock to global
645749 self.fsetVal('global', 'trace_clock')
646750 self.fsetVal('nop', 'current_tracer')
....@@ -649,16 +753,18 @@
649753 if self.bufsize > 0:
650754 tgtsize = self.bufsize
651755 elif self.usecallgraph or self.usedevsrc:
652
- tgtsize = min(self.memfree, 3*1024*1024)
756
+ bmax = (1*1024*1024) if self.suspendmode in ['disk', 'command'] \
757
+ else (3*1024*1024)
758
+ tgtsize = min(self.memfree, bmax)
653759 else:
654760 tgtsize = 65536
655
- while not self.fsetVal('%d' % (tgtsize / cpus), 'buffer_size_kb'):
761
+ while not self.fsetVal('%d' % (tgtsize // cpus), 'buffer_size_kb'):
656762 # if the size failed to set, lower it and keep trying
657763 tgtsize -= 65536
658764 if tgtsize < 65536:
659765 tgtsize = int(self.fgetVal('buffer_size_kb')) * cpus
660766 break
661
- print 'Setting trace buffers to %d kB (%d kB per cpu)' % (tgtsize, tgtsize/cpus)
767
+ self.vprint('Setting trace buffers to %d kB (%d kB per cpu)' % (tgtsize, tgtsize/cpus))
662768 # initialize the callgraph trace
663769 if(self.usecallgraph):
664770 # set trace type
....@@ -683,7 +789,10 @@
683789 cf.append(self.tracefuncs[fn]['func'])
684790 else:
685791 cf.append(fn)
686
- self.setFtraceFilterFunctions(cf)
792
+ if self.ftop:
793
+ self.setFtraceFilterFunctions([self.ftopfunc])
794
+ else:
795
+ self.setFtraceFilterFunctions(cf)
687796 # initialize the kprobe trace
688797 elif self.usekprobes:
689798 for name in self.tracefuncs:
....@@ -691,7 +800,8 @@
691800 if self.usedevsrc:
692801 for name in self.dev_tracefuncs:
693802 self.defaultKprobe(name, self.dev_tracefuncs[name])
694
- print('INITIALIZING KPROBES...')
803
+ if not quiet:
804
+ pprint('INITIALIZING KPROBES...')
695805 self.addKprobes(self.verbose)
696806 if(self.usetraceevents):
697807 # turn trace events on
....@@ -728,19 +838,25 @@
728838 if not self.ansi:
729839 return str
730840 return '\x1B[%d;40m%s\x1B[m' % (color, str)
731
- def writeDatafileHeader(self, filename, fwdata=[]):
841
+ def writeDatafileHeader(self, filename, testdata):
732842 fp = self.openlog(filename, 'w')
733843 fp.write('%s\n%s\n# command | %s\n' % (self.teststamp, self.sysstamp, self.cmdline))
734
- if(self.suspendmode == 'mem' or self.suspendmode == 'command'):
735
- for fw in fwdata:
844
+ for test in testdata:
845
+ if 'fw' in test:
846
+ fw = test['fw']
736847 if(fw):
737848 fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1]))
849
+ if 'turbo' in test:
850
+ fp.write('# turbostat %s\n' % test['turbo'])
851
+ if 'wifi' in test:
852
+ fp.write('# wifi %s\n' % test['wifi'])
853
+ if test['error'] or len(testdata) > 1:
854
+ fp.write('# enter_sleep_error %s\n' % test['error'])
738855 return fp
739
- def sudouser(self, dir):
740
- if os.path.exists(dir) and os.getuid() == 0 and \
741
- 'SUDO_USER' in os.environ:
856
+ def sudoUserchown(self, dir):
857
+ if os.path.exists(dir) and self.sudouser:
742858 cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1'
743
- call(cmd.format(os.environ['SUDO_USER'], dir), shell=True)
859
+ call(cmd.format(self.sudouser, dir), shell=True)
744860 def outputResult(self, testdata, num=0):
745861 if not self.result:
746862 return
....@@ -762,7 +878,7 @@
762878 if 'bugurl' in testdata:
763879 fp.write('url%s: %s\n' % (n, testdata['bugurl']))
764880 fp.close()
765
- self.sudouser(self.result)
881
+ self.sudoUserchown(self.result)
766882 def configFile(self, file):
767883 dir = os.path.dirname(os.path.realpath(__file__))
768884 if os.path.exists(file):
....@@ -776,14 +892,315 @@
776892 isgz = self.gzip
777893 if mode == 'r':
778894 try:
779
- with gzip.open(filename, mode+'b') as fp:
895
+ with gzip.open(filename, mode+'t') as fp:
780896 test = fp.read(64)
781897 isgz = True
782898 except:
783899 isgz = False
784900 if isgz:
785
- return gzip.open(filename, mode+'b')
901
+ return gzip.open(filename, mode+'t')
786902 return open(filename, mode)
903
+ def b64unzip(self, data):
904
+ try:
905
+ out = codecs.decode(base64.b64decode(data), 'zlib').decode()
906
+ except:
907
+ out = data
908
+ return out
909
+ def b64zip(self, data):
910
+ out = base64.b64encode(codecs.encode(data.encode(), 'zlib')).decode()
911
+ return out
912
+ def platforminfo(self, cmdafter):
913
+ # add platform info on to a completed ftrace file
914
+ if not os.path.exists(self.ftracefile):
915
+ return False
916
+ footer = '#\n'
917
+
918
+ # add test command string line if need be
919
+ if self.suspendmode == 'command' and self.testcommand:
920
+ footer += '# platform-testcmd: %s\n' % (self.testcommand)
921
+
922
+ # get a list of target devices from the ftrace file
923
+ props = dict()
924
+ tp = TestProps()
925
+ tf = self.openlog(self.ftracefile, 'r')
926
+ for line in tf:
927
+ if tp.stampInfo(line, self):
928
+ continue
929
+ # parse only valid lines, if this is not one move on
930
+ m = re.match(tp.ftrace_line_fmt, line)
931
+ if(not m or 'device_pm_callback_start' not in line):
932
+ continue
933
+ m = re.match('.*: (?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*', m.group('msg'));
934
+ if(not m):
935
+ continue
936
+ dev = m.group('d')
937
+ if dev not in props:
938
+ props[dev] = DevProps()
939
+ tf.close()
940
+
941
+ # now get the syspath for each target device
942
+ for dirname, dirnames, filenames in os.walk('/sys/devices'):
943
+ if(re.match('.*/power', dirname) and 'async' in filenames):
944
+ dev = dirname.split('/')[-2]
945
+ if dev in props and (not props[dev].syspath or len(dirname) < len(props[dev].syspath)):
946
+ props[dev].syspath = dirname[:-6]
947
+
948
+ # now fill in the properties for our target devices
949
+ for dev in sorted(props):
950
+ dirname = props[dev].syspath
951
+ if not dirname or not os.path.exists(dirname):
952
+ continue
953
+ with open(dirname+'/power/async') as fp:
954
+ text = fp.read()
955
+ props[dev].isasync = False
956
+ if 'enabled' in text:
957
+ props[dev].isasync = True
958
+ fields = os.listdir(dirname)
959
+ if 'product' in fields:
960
+ with open(dirname+'/product', 'rb') as fp:
961
+ props[dev].altname = ascii(fp.read())
962
+ elif 'name' in fields:
963
+ with open(dirname+'/name', 'rb') as fp:
964
+ props[dev].altname = ascii(fp.read())
965
+ elif 'model' in fields:
966
+ with open(dirname+'/model', 'rb') as fp:
967
+ props[dev].altname = ascii(fp.read())
968
+ elif 'description' in fields:
969
+ with open(dirname+'/description', 'rb') as fp:
970
+ props[dev].altname = ascii(fp.read())
971
+ elif 'id' in fields:
972
+ with open(dirname+'/id', 'rb') as fp:
973
+ props[dev].altname = ascii(fp.read())
974
+ elif 'idVendor' in fields and 'idProduct' in fields:
975
+ idv, idp = '', ''
976
+ with open(dirname+'/idVendor', 'rb') as fp:
977
+ idv = ascii(fp.read()).strip()
978
+ with open(dirname+'/idProduct', 'rb') as fp:
979
+ idp = ascii(fp.read()).strip()
980
+ props[dev].altname = '%s:%s' % (idv, idp)
981
+ if props[dev].altname:
982
+ out = props[dev].altname.strip().replace('\n', ' ')\
983
+ .replace(',', ' ').replace(';', ' ')
984
+ props[dev].altname = out
985
+
986
+ # add a devinfo line to the bottom of ftrace
987
+ out = ''
988
+ for dev in sorted(props):
989
+ out += props[dev].out(dev)
990
+ footer += '# platform-devinfo: %s\n' % self.b64zip(out)
991
+
992
+ # add a line for each of these commands with their outputs
993
+ for name, cmdline, info in cmdafter:
994
+ footer += '# platform-%s: %s | %s\n' % (name, cmdline, self.b64zip(info))
995
+
996
+ with self.openlog(self.ftracefile, 'a') as fp:
997
+ fp.write(footer)
998
+ return True
999
+ def commonPrefix(self, list):
1000
+ if len(list) < 2:
1001
+ return ''
1002
+ prefix = list[0]
1003
+ for s in list[1:]:
1004
+ while s[:len(prefix)] != prefix and prefix:
1005
+ prefix = prefix[:len(prefix)-1]
1006
+ if not prefix:
1007
+ break
1008
+ if '/' in prefix and prefix[-1] != '/':
1009
+ prefix = prefix[0:prefix.rfind('/')+1]
1010
+ return prefix
1011
+ def dictify(self, text, format):
1012
+ out = dict()
1013
+ header = True if format == 1 else False
1014
+ delim = ' ' if format == 1 else ':'
1015
+ for line in text.split('\n'):
1016
+ if header:
1017
+ header, out['@'] = False, line
1018
+ continue
1019
+ line = line.strip()
1020
+ if delim in line:
1021
+ data = line.split(delim, 1)
1022
+ num = re.search(r'[\d]+', data[1])
1023
+ if format == 2 and num:
1024
+ out[data[0].strip()] = num.group()
1025
+ else:
1026
+ out[data[0].strip()] = data[1]
1027
+ return out
1028
+ def cmdinfo(self, begin, debug=False):
1029
+ out = []
1030
+ if begin:
1031
+ self.cmd1 = dict()
1032
+ for cargs in self.infocmds:
1033
+ delta, name = cargs[0], cargs[1]
1034
+ cmdline, cmdpath = ' '.join(cargs[2:]), self.getExec(cargs[2])
1035
+ if not cmdpath or (begin and not delta):
1036
+ continue
1037
+ try:
1038
+ fp = Popen([cmdpath]+cargs[3:], stdout=PIPE, stderr=PIPE).stdout
1039
+ info = ascii(fp.read()).strip()
1040
+ fp.close()
1041
+ except:
1042
+ continue
1043
+ if not debug and begin:
1044
+ self.cmd1[name] = self.dictify(info, delta)
1045
+ elif not debug and delta and name in self.cmd1:
1046
+ before, after = self.cmd1[name], self.dictify(info, delta)
1047
+ dinfo = ('\t%s\n' % before['@']) if '@' in before else ''
1048
+ prefix = self.commonPrefix(list(before.keys()))
1049
+ for key in sorted(before):
1050
+ if key in after and before[key] != after[key]:
1051
+ title = key.replace(prefix, '')
1052
+ if delta == 2:
1053
+ dinfo += '\t%s : %s -> %s\n' % \
1054
+ (title, before[key].strip(), after[key].strip())
1055
+ else:
1056
+ dinfo += '%10s (start) : %s\n%10s (after) : %s\n' % \
1057
+ (title, before[key], title, after[key])
1058
+ dinfo = '\tnothing changed' if not dinfo else dinfo.rstrip()
1059
+ out.append((name, cmdline, dinfo))
1060
+ else:
1061
+ out.append((name, cmdline, '\tnothing' if not info else info))
1062
+ return out
1063
+ def haveTurbostat(self):
1064
+ if not self.tstat:
1065
+ return False
1066
+ cmd = self.getExec('turbostat')
1067
+ if not cmd:
1068
+ return False
1069
+ fp = Popen([cmd, '-v'], stdout=PIPE, stderr=PIPE).stderr
1070
+ out = ascii(fp.read()).strip()
1071
+ fp.close()
1072
+ if re.match('turbostat version .*', out):
1073
+ self.vprint(out)
1074
+ return True
1075
+ return False
1076
+ def turbostat(self):
1077
+ cmd = self.getExec('turbostat')
1078
+ rawout = keyline = valline = ''
1079
+ fullcmd = '%s -q -S echo freeze > %s' % (cmd, self.powerfile)
1080
+ fp = Popen(['sh', '-c', fullcmd], stdout=PIPE, stderr=PIPE).stderr
1081
+ for line in fp:
1082
+ line = ascii(line)
1083
+ rawout += line
1084
+ if keyline and valline:
1085
+ continue
1086
+ if re.match('(?i)Avg_MHz.*', line):
1087
+ keyline = line.strip().split()
1088
+ elif keyline:
1089
+ valline = line.strip().split()
1090
+ fp.close()
1091
+ if not keyline or not valline or len(keyline) != len(valline):
1092
+ errmsg = 'unrecognized turbostat output:\n'+rawout.strip()
1093
+ self.vprint(errmsg)
1094
+ if not self.verbose:
1095
+ pprint(errmsg)
1096
+ return ''
1097
+ if self.verbose:
1098
+ pprint(rawout.strip())
1099
+ out = []
1100
+ for key in keyline:
1101
+ idx = keyline.index(key)
1102
+ val = valline[idx]
1103
+ out.append('%s=%s' % (key, val))
1104
+ return '|'.join(out)
1105
+ def wifiDetails(self, dev):
1106
+ try:
1107
+ info = open('/sys/class/net/%s/device/uevent' % dev, 'r').read().strip()
1108
+ except:
1109
+ return dev
1110
+ vals = [dev]
1111
+ for prop in info.split('\n'):
1112
+ if prop.startswith('DRIVER=') or prop.startswith('PCI_ID='):
1113
+ vals.append(prop.split('=')[-1])
1114
+ return ':'.join(vals)
1115
+ def checkWifi(self, dev=''):
1116
+ try:
1117
+ w = open('/proc/net/wireless', 'r').read().strip()
1118
+ except:
1119
+ return ''
1120
+ for line in reversed(w.split('\n')):
1121
+ m = re.match(' *(?P<dev>.*): (?P<stat>[0-9a-f]*) .*', w.split('\n')[-1])
1122
+ if not m or (dev and dev != m.group('dev')):
1123
+ continue
1124
+ return m.group('dev')
1125
+ return ''
1126
+ def pollWifi(self, dev, timeout=60):
1127
+ start = time.time()
1128
+ while (time.time() - start) < timeout:
1129
+ w = self.checkWifi(dev)
1130
+ if w:
1131
+ return '%s reconnected %.2f' % \
1132
+ (self.wifiDetails(dev), max(0, time.time() - start))
1133
+ time.sleep(0.01)
1134
+ return '%s timeout %d' % (self.wifiDetails(dev), timeout)
1135
+ def errorSummary(self, errinfo, msg):
1136
+ found = False
1137
+ for entry in errinfo:
1138
+ if re.match(entry['match'], msg):
1139
+ entry['count'] += 1
1140
+ if self.hostname not in entry['urls']:
1141
+ entry['urls'][self.hostname] = [self.htmlfile]
1142
+ elif self.htmlfile not in entry['urls'][self.hostname]:
1143
+ entry['urls'][self.hostname].append(self.htmlfile)
1144
+ found = True
1145
+ break
1146
+ if found:
1147
+ return
1148
+ arr = msg.split()
1149
+ for j in range(len(arr)):
1150
+ if re.match('^[0-9,\-\.]*$', arr[j]):
1151
+ arr[j] = '[0-9,\-\.]*'
1152
+ else:
1153
+ arr[j] = arr[j]\
1154
+ .replace('\\', '\\\\').replace(']', '\]').replace('[', '\[')\
1155
+ .replace('.', '\.').replace('+', '\+').replace('*', '\*')\
1156
+ .replace('(', '\(').replace(')', '\)').replace('}', '\}')\
1157
+ .replace('{', '\{')
1158
+ mstr = ' *'.join(arr)
1159
+ entry = {
1160
+ 'line': msg,
1161
+ 'match': mstr,
1162
+ 'count': 1,
1163
+ 'urls': {self.hostname: [self.htmlfile]}
1164
+ }
1165
+ errinfo.append(entry)
1166
+ def multistat(self, start, idx, finish):
1167
+ if 'time' in self.multitest:
1168
+ id = '%d Duration=%dmin' % (idx+1, self.multitest['time'])
1169
+ else:
1170
+ id = '%d/%d' % (idx+1, self.multitest['count'])
1171
+ t = time.time()
1172
+ if 'start' not in self.multitest:
1173
+ self.multitest['start'] = self.multitest['last'] = t
1174
+ self.multitest['total'] = 0.0
1175
+ pprint('TEST (%s) START' % id)
1176
+ return
1177
+ dt = t - self.multitest['last']
1178
+ if not start:
1179
+ if idx == 0 and self.multitest['delay'] > 0:
1180
+ self.multitest['total'] += self.multitest['delay']
1181
+ pprint('TEST (%s) COMPLETE -- Duration %.1fs' % (id, dt))
1182
+ return
1183
+ self.multitest['total'] += dt
1184
+ self.multitest['last'] = t
1185
+ avg = self.multitest['total'] / idx
1186
+ if 'time' in self.multitest:
1187
+ left = finish - datetime.now()
1188
+ left -= timedelta(microseconds=left.microseconds)
1189
+ else:
1190
+ left = timedelta(seconds=((self.multitest['count'] - idx) * int(avg)))
1191
+ pprint('TEST (%s) START - Avg Duration %.1fs, Time left %s' % \
1192
+ (id, avg, str(left)))
1193
+ def multiinit(self, c, d):
1194
+ sz, unit = 'count', 'm'
1195
+ if c.endswith('d') or c.endswith('h') or c.endswith('m'):
1196
+ sz, unit, c = 'time', c[-1], c[:-1]
1197
+ self.multitest['run'] = True
1198
+ self.multitest[sz] = getArgInt('multi: n d (exec count)', c, 1, 1000000, False)
1199
+ self.multitest['delay'] = getArgInt('multi: n d (delay between tests)', d, 0, 3600, False)
1200
+ if unit == 'd':
1201
+ self.multitest[sz] *= 1440
1202
+ elif unit == 'h':
1203
+ self.multitest[sz] *= 60
7871204
7881205 sysvals = SystemValues()
7891206 switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0']
....@@ -800,15 +1217,16 @@
8001217 # Simple class which holds property values collected
8011218 # for all the devices used in the timeline.
8021219 class DevProps:
803
- syspath = ''
804
- altname = ''
805
- async = True
806
- xtraclass = ''
807
- xtrainfo = ''
1220
+ def __init__(self):
1221
+ self.syspath = ''
1222
+ self.altname = ''
1223
+ self.isasync = True
1224
+ self.xtraclass = ''
1225
+ self.xtrainfo = ''
8081226 def out(self, dev):
809
- return '%s,%s,%d;' % (dev, self.altname, self.async)
1227
+ return '%s,%s,%d;' % (dev, self.altname, self.isasync)
8101228 def debug(self, dev):
811
- print '%s:\n\taltname = %s\n\t async = %s' % (dev, self.altname, self.async)
1229
+ pprint('%s:\n\taltname = %s\n\t async = %s' % (dev, self.altname, self.isasync))
8121230 def altName(self, dev):
8131231 if not self.altname or self.altname == dev:
8141232 return dev
....@@ -816,24 +1234,21 @@
8161234 def xtraClass(self):
8171235 if self.xtraclass:
8181236 return ' '+self.xtraclass
819
- if not self.async:
1237
+ if not self.isasync:
8201238 return ' sync'
8211239 return ''
8221240 def xtraInfo(self):
8231241 if self.xtraclass:
8241242 return ' '+self.xtraclass
825
- if self.async:
826
- return ' async_device'
827
- return ' sync_device'
1243
+ if self.isasync:
1244
+ return ' (async)'
1245
+ return ' (sync)'
8281246
8291247 # Class: DeviceNode
8301248 # Description:
8311249 # A container used to create a device hierachy, with a single root node
8321250 # and a tree of child nodes. Used by Data.deviceTopology()
8331251 class DeviceNode:
834
- name = ''
835
- children = 0
836
- depth = 0
8371252 def __init__(self, nodename, nodedepth):
8381253 self.name = nodename
8391254 self.children = []
....@@ -861,76 +1276,116 @@
8611276 # }
8621277 #
8631278 class Data:
864
- dmesg = {} # root data structure
865
- phases = [] # ordered list of phases
866
- start = 0.0 # test start
867
- end = 0.0 # test end
868
- tSuspended = 0.0 # low-level suspend start
869
- tResumed = 0.0 # low-level resume start
870
- tKernSus = 0.0 # kernel level suspend start
871
- tKernRes = 0.0 # kernel level resume end
872
- tLow = 0.0 # time spent in low-level suspend (standby/freeze)
873
- fwValid = False # is firmware data available
874
- fwSuspend = 0 # time spent in firmware suspend
875
- fwResume = 0 # time spent in firmware resume
876
- dmesgtext = [] # dmesg text file in memory
877
- pstl = 0 # process timeline
878
- testnumber = 0
879
- idstr = ''
880
- html_device_id = 0
881
- stamp = 0
882
- outfile = ''
883
- devpids = []
884
- kerror = False
1279
+ phasedef = {
1280
+ 'suspend_prepare': {'order': 0, 'color': '#CCFFCC'},
1281
+ 'suspend': {'order': 1, 'color': '#88FF88'},
1282
+ 'suspend_late': {'order': 2, 'color': '#00AA00'},
1283
+ 'suspend_noirq': {'order': 3, 'color': '#008888'},
1284
+ 'suspend_machine': {'order': 4, 'color': '#0000FF'},
1285
+ 'resume_machine': {'order': 5, 'color': '#FF0000'},
1286
+ 'resume_noirq': {'order': 6, 'color': '#FF9900'},
1287
+ 'resume_early': {'order': 7, 'color': '#FFCC00'},
1288
+ 'resume': {'order': 8, 'color': '#FFFF88'},
1289
+ 'resume_complete': {'order': 9, 'color': '#FFFFCC'},
1290
+ }
1291
+ errlist = {
1292
+ 'HWERROR' : r'.*\[ *Hardware Error *\].*',
1293
+ 'FWBUG' : r'.*\[ *Firmware Bug *\].*',
1294
+ 'BUG' : r'(?i).*\bBUG\b.*',
1295
+ 'ERROR' : r'(?i).*\bERROR\b.*',
1296
+ 'WARNING' : r'(?i).*\bWARNING\b.*',
1297
+ 'FAULT' : r'(?i).*\bFAULT\b.*',
1298
+ 'FAIL' : r'(?i).*\bFAILED\b.*',
1299
+ 'INVALID' : r'(?i).*\bINVALID\b.*',
1300
+ 'CRASH' : r'(?i).*\bCRASHED\b.*',
1301
+ 'TIMEOUT' : r'(?i).*\bTIMEOUT\b.*',
1302
+ 'IRQ' : r'.*\bgenirq: .*',
1303
+ 'TASKFAIL': r'.*Freezing of tasks *.*',
1304
+ 'ACPI' : r'.*\bACPI *(?P<b>[A-Za-z]*) *Error[: ].*',
1305
+ 'DISKFULL': r'.*\bNo space left on device.*',
1306
+ 'USBERR' : r'.*usb .*device .*, error [0-9-]*',
1307
+ 'ATAERR' : r' *ata[0-9\.]*: .*failed.*',
1308
+ 'MEIERR' : r' *mei.*: .*failed.*',
1309
+ 'TPMERR' : r'(?i) *tpm *tpm[0-9]*: .*error.*',
1310
+ }
8851311 def __init__(self, num):
8861312 idchar = 'abcdefghij'
887
- self.pstl = dict()
1313
+ self.start = 0.0 # test start
1314
+ self.end = 0.0 # test end
1315
+ self.hwstart = 0 # rtc test start
1316
+ self.hwend = 0 # rtc test end
1317
+ self.tSuspended = 0.0 # low-level suspend start
1318
+ self.tResumed = 0.0 # low-level resume start
1319
+ self.tKernSus = 0.0 # kernel level suspend start
1320
+ self.tKernRes = 0.0 # kernel level resume end
1321
+ self.fwValid = False # is firmware data available
1322
+ self.fwSuspend = 0 # time spent in firmware suspend
1323
+ self.fwResume = 0 # time spent in firmware resume
1324
+ self.html_device_id = 0
1325
+ self.stamp = 0
1326
+ self.outfile = ''
1327
+ self.kerror = False
1328
+ self.wifi = dict()
1329
+ self.turbostat = 0
1330
+ self.enterfail = ''
1331
+ self.currphase = ''
1332
+ self.pstl = dict() # process timeline
8881333 self.testnumber = num
8891334 self.idstr = idchar[num]
890
- self.dmesgtext = []
891
- self.phases = []
892
- self.dmesg = { # fixed list of 10 phases
893
- 'suspend_prepare': {'list': dict(), 'start': -1.0, 'end': -1.0,
894
- 'row': 0, 'color': '#CCFFCC', 'order': 0},
895
- 'suspend': {'list': dict(), 'start': -1.0, 'end': -1.0,
896
- 'row': 0, 'color': '#88FF88', 'order': 1},
897
- 'suspend_late': {'list': dict(), 'start': -1.0, 'end': -1.0,
898
- 'row': 0, 'color': '#00AA00', 'order': 2},
899
- 'suspend_noirq': {'list': dict(), 'start': -1.0, 'end': -1.0,
900
- 'row': 0, 'color': '#008888', 'order': 3},
901
- 'suspend_machine': {'list': dict(), 'start': -1.0, 'end': -1.0,
902
- 'row': 0, 'color': '#0000FF', 'order': 4},
903
- 'resume_machine': {'list': dict(), 'start': -1.0, 'end': -1.0,
904
- 'row': 0, 'color': '#FF0000', 'order': 5},
905
- 'resume_noirq': {'list': dict(), 'start': -1.0, 'end': -1.0,
906
- 'row': 0, 'color': '#FF9900', 'order': 6},
907
- 'resume_early': {'list': dict(), 'start': -1.0, 'end': -1.0,
908
- 'row': 0, 'color': '#FFCC00', 'order': 7},
909
- 'resume': {'list': dict(), 'start': -1.0, 'end': -1.0,
910
- 'row': 0, 'color': '#FFFF88', 'order': 8},
911
- 'resume_complete': {'list': dict(), 'start': -1.0, 'end': -1.0,
912
- 'row': 0, 'color': '#FFFFCC', 'order': 9}
913
- }
914
- self.phases = self.sortedPhases()
915
- self.devicegroups = []
916
- for phase in self.phases:
917
- self.devicegroups.append([phase])
1335
+ self.dmesgtext = [] # dmesg text file in memory
1336
+ self.dmesg = dict() # root data structure
9181337 self.errorinfo = {'suspend':[],'resume':[]}
1338
+ self.tLow = [] # time spent in low-level suspends (standby/freeze)
1339
+ self.devpids = []
1340
+ self.devicegroups = 0
1341
+ def sortedPhases(self):
1342
+ return sorted(self.dmesg, key=lambda k:self.dmesg[k]['order'])
1343
+ def initDevicegroups(self):
1344
+ # called when phases are all finished being added
1345
+ for phase in sorted(self.dmesg.keys()):
1346
+ if '*' in phase:
1347
+ p = phase.split('*')
1348
+ pnew = '%s%d' % (p[0], len(p))
1349
+ self.dmesg[pnew] = self.dmesg.pop(phase)
1350
+ self.devicegroups = []
1351
+ for phase in self.sortedPhases():
1352
+ self.devicegroups.append([phase])
1353
+ def nextPhase(self, phase, offset):
1354
+ order = self.dmesg[phase]['order'] + offset
1355
+ for p in self.dmesg:
1356
+ if self.dmesg[p]['order'] == order:
1357
+ return p
1358
+ return ''
1359
+ def lastPhase(self, depth=1):
1360
+ plist = self.sortedPhases()
1361
+ if len(plist) < depth:
1362
+ return ''
1363
+ return plist[-1*depth]
1364
+ def turbostatInfo(self):
1365
+ tp = TestProps()
1366
+ out = {'syslpi':'N/A','pkgpc10':'N/A'}
1367
+ for line in self.dmesgtext:
1368
+ m = re.match(tp.tstatfmt, line)
1369
+ if not m:
1370
+ continue
1371
+ for i in m.group('t').split('|'):
1372
+ if 'SYS%LPI' in i:
1373
+ out['syslpi'] = i.split('=')[-1]+'%'
1374
+ elif 'pc10' in i:
1375
+ out['pkgpc10'] = i.split('=')[-1]+'%'
1376
+ break
1377
+ return out
9191378 def extractErrorInfo(self):
920
- elist = {
921
- 'HWERROR' : '.*\[ *Hardware Error *\].*',
922
- 'FWBUG' : '.*\[ *Firmware Bug *\].*',
923
- 'BUG' : '.*BUG.*',
924
- 'ERROR' : '.*ERROR.*',
925
- 'WARNING' : '.*WARNING.*',
926
- 'IRQ' : '.*genirq: .*',
927
- 'TASKFAIL': '.*Freezing of tasks failed.*',
928
- }
929
- lf = sysvals.openlog(sysvals.dmesgfile, 'r')
1379
+ lf = self.dmesgtext
1380
+ if len(self.dmesgtext) < 1 and sysvals.dmesgfile:
1381
+ lf = sysvals.openlog(sysvals.dmesgfile, 'r')
9301382 i = 0
1383
+ tp = TestProps()
9311384 list = []
9321385 for line in lf:
9331386 i += 1
1387
+ if tp.stampInfo(line, sysvals):
1388
+ continue
9341389 m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
9351390 if not m:
9361391 continue
....@@ -939,24 +1394,38 @@
9391394 continue
9401395 dir = 'suspend' if t < self.tSuspended else 'resume'
9411396 msg = m.group('msg')
942
- for err in elist:
943
- if re.match(elist[err], msg):
944
- list.append((err, dir, t, i, i))
1397
+ if re.match('capability: warning: .*', msg):
1398
+ continue
1399
+ for err in self.errlist:
1400
+ if re.match(self.errlist[err], msg):
1401
+ list.append((msg, err, dir, t, i, i))
9451402 self.kerror = True
9461403 break
947
- for e in list:
948
- type, dir, t, idx1, idx2 = e
949
- sysvals.vprint('kernel %s found in %s at %f' % (type, dir, t))
1404
+ tp.msglist = []
1405
+ for msg, type, dir, t, idx1, idx2 in list:
1406
+ tp.msglist.append(msg)
9501407 self.errorinfo[dir].append((type, t, idx1, idx2))
9511408 if self.kerror:
9521409 sysvals.dmesglog = True
953
- lf.close()
954
- def setStart(self, time):
1410
+ if len(self.dmesgtext) < 1 and sysvals.dmesgfile:
1411
+ lf.close()
1412
+ return tp
1413
+ def setStart(self, time, msg=''):
9551414 self.start = time
956
- def setEnd(self, time):
1415
+ if msg:
1416
+ try:
1417
+ self.hwstart = datetime.strptime(msg, sysvals.tmstart)
1418
+ except:
1419
+ self.hwstart = 0
1420
+ def setEnd(self, time, msg=''):
9571421 self.end = time
1422
+ if msg:
1423
+ try:
1424
+ self.hwend = datetime.strptime(msg, sysvals.tmend)
1425
+ except:
1426
+ self.hwend = 0
9581427 def isTraceEventOutsideDeviceCalls(self, pid, time):
959
- for phase in self.phases:
1428
+ for phase in self.sortedPhases():
9601429 list = self.dmesg[phase]['list']
9611430 for dev in list:
9621431 d = list[dev]
....@@ -964,16 +1433,10 @@
9641433 time < d['end']):
9651434 return False
9661435 return True
967
- def phaseCollision(self, phase, isbegin, line):
968
- key = 'end'
969
- if isbegin:
970
- key = 'start'
971
- if self.dmesg[phase][key] >= 0:
972
- sysvals.vprint('IGNORE: %s' % line.strip())
973
- return True
974
- return False
9751436 def sourcePhase(self, start):
976
- for phase in self.phases:
1437
+ for phase in self.sortedPhases():
1438
+ if 'machine' in phase:
1439
+ continue
9771440 pend = self.dmesg[phase]['end']
9781441 if start <= pend:
9791442 return phase
....@@ -1004,14 +1467,15 @@
10041467 return tgtdev
10051468 def addDeviceFunctionCall(self, displayname, kprobename, proc, pid, start, end, cdata, rdata):
10061469 # try to place the call in a device
1007
- tgtdev = self.sourceDevice(self.phases, start, end, pid, 'device')
1470
+ phases = self.sortedPhases()
1471
+ tgtdev = self.sourceDevice(phases, start, end, pid, 'device')
10081472 # calls with device pids that occur outside device bounds are dropped
10091473 # TODO: include these somehow
10101474 if not tgtdev and pid in self.devpids:
10111475 return False
10121476 # try to place the call in a thread
10131477 if not tgtdev:
1014
- tgtdev = self.sourceDevice(self.phases, start, end, pid, 'thread')
1478
+ tgtdev = self.sourceDevice(phases, start, end, pid, 'thread')
10151479 # create new thread blocks, expand as new calls are found
10161480 if not tgtdev:
10171481 if proc == '<...>':
....@@ -1053,7 +1517,7 @@
10531517 def overflowDevices(self):
10541518 # get a list of devices that extend beyond the end of this test run
10551519 devlist = []
1056
- for phase in self.phases:
1520
+ for phase in self.sortedPhases():
10571521 list = self.dmesg[phase]['list']
10581522 for devname in list:
10591523 dev = list[devname]
....@@ -1064,7 +1528,7 @@
10641528 # merge any devices that overlap devlist
10651529 for dev in devlist:
10661530 devname = dev['name']
1067
- for phase in self.phases:
1531
+ for phase in self.sortedPhases():
10681532 list = self.dmesg[phase]['list']
10691533 if devname not in list:
10701534 continue
....@@ -1079,7 +1543,7 @@
10791543 del list[devname]
10801544 def usurpTouchingThread(self, name, dev):
10811545 # the caller test has priority of this thread, give it to him
1082
- for phase in self.phases:
1546
+ for phase in self.sortedPhases():
10831547 list = self.dmesg[phase]['list']
10841548 if name in list:
10851549 tdev = list[name]
....@@ -1093,7 +1557,7 @@
10931557 break
10941558 def stitchTouchingThreads(self, testlist):
10951559 # merge any threads between tests that touch
1096
- for phase in self.phases:
1560
+ for phase in self.sortedPhases():
10971561 list = self.dmesg[phase]['list']
10981562 for devname in list:
10991563 dev = list[devname]
....@@ -1103,7 +1567,7 @@
11031567 data.usurpTouchingThread(devname, dev)
11041568 def optimizeDevSrc(self):
11051569 # merge any src call loops to reduce timeline size
1106
- for phase in self.phases:
1570
+ for phase in self.sortedPhases():
11071571 list = self.dmesg[phase]['list']
11081572 for dev in list:
11091573 if 'src' not in list[dev]:
....@@ -1141,7 +1605,7 @@
11411605 self.tKernSus = self.trimTimeVal(self.tKernSus, t0, dT, left)
11421606 self.tKernRes = self.trimTimeVal(self.tKernRes, t0, dT, left)
11431607 self.end = self.trimTimeVal(self.end, t0, dT, left)
1144
- for phase in self.phases:
1608
+ for phase in self.sortedPhases():
11451609 p = self.dmesg[phase]
11461610 p['start'] = self.trimTimeVal(p['start'], t0, dT, left)
11471611 p['end'] = self.trimTimeVal(p['end'], t0, dT, left)
....@@ -1150,6 +1614,7 @@
11501614 d = list[name]
11511615 d['start'] = self.trimTimeVal(d['start'], t0, dT, left)
11521616 d['end'] = self.trimTimeVal(d['end'], t0, dT, left)
1617
+ d['length'] = d['end'] - d['start']
11531618 if('ftrace' in d):
11541619 cg = d['ftrace']
11551620 cg.start = self.trimTimeVal(cg.start, t0, dT, left)
....@@ -1159,6 +1624,8 @@
11591624 if('src' in d):
11601625 for e in d['src']:
11611626 e.time = self.trimTimeVal(e.time, t0, dT, left)
1627
+ e.end = self.trimTimeVal(e.end, t0, dT, left)
1628
+ e.length = e.end - e.time
11621629 for dir in ['suspend', 'resume']:
11631630 list = []
11641631 for e in self.errorinfo[dir]:
....@@ -1166,55 +1633,80 @@
11661633 tm = self.trimTimeVal(tm, t0, dT, left)
11671634 list.append((type, tm, idx1, idx2))
11681635 self.errorinfo[dir] = list
1169
- def normalizeTime(self, tZero):
1636
+ def trimFreezeTime(self, tZero):
11701637 # trim out any standby or freeze clock time
1171
- if(self.tSuspended != self.tResumed):
1172
- if(self.tResumed > tZero):
1173
- self.trimTime(self.tSuspended, \
1174
- self.tResumed-self.tSuspended, True)
1175
- else:
1176
- self.trimTime(self.tSuspended, \
1177
- self.tResumed-self.tSuspended, False)
1638
+ lp = ''
1639
+ for phase in self.sortedPhases():
1640
+ if 'resume_machine' in phase and 'suspend_machine' in lp:
1641
+ tS, tR = self.dmesg[lp]['end'], self.dmesg[phase]['start']
1642
+ tL = tR - tS
1643
+ if tL > 0:
1644
+ left = True if tR > tZero else False
1645
+ self.trimTime(tS, tL, left)
1646
+ if 'trying' in self.dmesg[lp] and self.dmesg[lp]['trying'] >= 0.001:
1647
+ tTry = round(self.dmesg[lp]['trying'] * 1000)
1648
+ text = '%.0f (-%.0f waking)' % (tL * 1000, tTry)
1649
+ else:
1650
+ text = '%.0f' % (tL * 1000)
1651
+ self.tLow.append(text)
1652
+ lp = phase
1653
+ def getMemTime(self):
1654
+ if not self.hwstart or not self.hwend:
1655
+ return
1656
+ stime = (self.tSuspended - self.start) * 1000000
1657
+ rtime = (self.end - self.tResumed) * 1000000
1658
+ hws = self.hwstart + timedelta(microseconds=stime)
1659
+ hwr = self.hwend - timedelta(microseconds=rtime)
1660
+ self.tLow.append('%.0f'%((hwr - hws).total_seconds() * 1000))
11781661 def getTimeValues(self):
1179
- sktime = (self.dmesg['suspend_machine']['end'] - \
1180
- self.tKernSus) * 1000
1181
- rktime = (self.dmesg['resume_complete']['end'] - \
1182
- self.dmesg['resume_machine']['start']) * 1000
1662
+ sktime = (self.tSuspended - self.tKernSus) * 1000
1663
+ rktime = (self.tKernRes - self.tResumed) * 1000
11831664 return (sktime, rktime)
1184
- def setPhase(self, phase, ktime, isbegin):
1665
+ def setPhase(self, phase, ktime, isbegin, order=-1):
11851666 if(isbegin):
1667
+ # phase start over current phase
1668
+ if self.currphase:
1669
+ if 'resume_machine' not in self.currphase:
1670
+ sysvals.vprint('WARNING: phase %s failed to end' % self.currphase)
1671
+ self.dmesg[self.currphase]['end'] = ktime
1672
+ phases = self.dmesg.keys()
1673
+ color = self.phasedef[phase]['color']
1674
+ count = len(phases) if order < 0 else order
1675
+ # create unique name for every new phase
1676
+ while phase in phases:
1677
+ phase += '*'
1678
+ self.dmesg[phase] = {'list': dict(), 'start': -1.0, 'end': -1.0,
1679
+ 'row': 0, 'color': color, 'order': count}
11861680 self.dmesg[phase]['start'] = ktime
1681
+ self.currphase = phase
11871682 else:
1683
+ # phase end without a start
1684
+ if phase not in self.currphase:
1685
+ if self.currphase:
1686
+ sysvals.vprint('WARNING: %s ended instead of %s, ftrace corruption?' % (phase, self.currphase))
1687
+ else:
1688
+ sysvals.vprint('WARNING: %s ended without a start, ftrace corruption?' % phase)
1689
+ return phase
1690
+ phase = self.currphase
11881691 self.dmesg[phase]['end'] = ktime
1189
- def dmesgSortVal(self, phase):
1190
- return self.dmesg[phase]['order']
1191
- def sortedPhases(self):
1192
- return sorted(self.dmesg, key=self.dmesgSortVal)
1692
+ self.currphase = ''
1693
+ return phase
11931694 def sortedDevices(self, phase):
11941695 list = self.dmesg[phase]['list']
1195
- slist = []
1196
- tmp = dict()
1197
- for devname in list:
1198
- dev = list[devname]
1199
- if dev['length'] == 0:
1200
- continue
1201
- tmp[dev['start']] = devname
1202
- for t in sorted(tmp):
1203
- slist.append(tmp[t])
1204
- return slist
1696
+ return sorted(list, key=lambda k:list[k]['start'])
12051697 def fixupInitcalls(self, phase):
12061698 # if any calls never returned, clip them at system resume end
12071699 phaselist = self.dmesg[phase]['list']
12081700 for devname in phaselist:
12091701 dev = phaselist[devname]
12101702 if(dev['end'] < 0):
1211
- for p in self.phases:
1703
+ for p in self.sortedPhases():
12121704 if self.dmesg[p]['end'] > dev['start']:
12131705 dev['end'] = self.dmesg[p]['end']
12141706 break
12151707 sysvals.vprint('%s (%s): callback didnt return' % (devname, phase))
12161708 def deviceFilter(self, devicefilter):
1217
- for phase in self.phases:
1709
+ for phase in self.sortedPhases():
12181710 list = self.dmesg[phase]['list']
12191711 rmlist = []
12201712 for name in list:
....@@ -1229,7 +1721,7 @@
12291721 del list[name]
12301722 def fixupInitcallsThatDidntReturn(self):
12311723 # if any calls never returned, clip them at system resume end
1232
- for phase in self.phases:
1724
+ for phase in self.sortedPhases():
12331725 self.fixupInitcalls(phase)
12341726 def phaseOverlap(self, phases):
12351727 rmgroups = []
....@@ -1248,17 +1740,18 @@
12481740 self.devicegroups.append(newgroup)
12491741 def newActionGlobal(self, name, start, end, pid=-1, color=''):
12501742 # which phase is this device callback or action in
1743
+ phases = self.sortedPhases()
12511744 targetphase = 'none'
12521745 htmlclass = ''
12531746 overlap = 0.0
1254
- phases = []
1255
- for phase in self.phases:
1747
+ myphases = []
1748
+ for phase in phases:
12561749 pstart = self.dmesg[phase]['start']
12571750 pend = self.dmesg[phase]['end']
12581751 # see if the action overlaps this phase
12591752 o = max(0, min(end, pend) - max(start, pstart))
12601753 if o > 0:
1261
- phases.append(phase)
1754
+ myphases.append(phase)
12621755 # set the target phase to the one that overlaps most
12631756 if o > overlap:
12641757 if overlap > 0 and phase == 'post_resume':
....@@ -1267,19 +1760,19 @@
12671760 overlap = o
12681761 # if no target phase was found, pin it to the edge
12691762 if targetphase == 'none':
1270
- p0start = self.dmesg[self.phases[0]]['start']
1763
+ p0start = self.dmesg[phases[0]]['start']
12711764 if start <= p0start:
1272
- targetphase = self.phases[0]
1765
+ targetphase = phases[0]
12731766 else:
1274
- targetphase = self.phases[-1]
1767
+ targetphase = phases[-1]
12751768 if pid == -2:
12761769 htmlclass = ' bg'
12771770 elif pid == -3:
12781771 htmlclass = ' ps'
1279
- if len(phases) > 1:
1772
+ if len(myphases) > 1:
12801773 htmlclass = ' bg'
1281
- self.phaseOverlap(phases)
1282
- if targetphase in self.phases:
1774
+ self.phaseOverlap(myphases)
1775
+ if targetphase in phases:
12831776 newname = self.newAction(targetphase, name, pid, '', start, end, '', htmlclass, color)
12841777 return (targetphase, newname)
12851778 return False
....@@ -1291,7 +1784,7 @@
12911784 length = -1.0
12921785 if(start >= 0 and end >= 0):
12931786 length = end - start
1294
- if pid == -2:
1787
+ if pid == -2 or name not in sysvals.tracefuncs.keys():
12951788 i = 2
12961789 origname = name
12971790 while(name in list):
....@@ -1304,6 +1797,15 @@
13041797 if color:
13051798 list[name]['color'] = color
13061799 return name
1800
+ def findDevice(self, phase, name):
1801
+ list = self.dmesg[phase]['list']
1802
+ mydev = ''
1803
+ for devname in sorted(list):
1804
+ if name == devname or re.match('^%s\[(?P<num>[0-9]*)\]$' % name, devname):
1805
+ mydev = devname
1806
+ if mydev:
1807
+ return list[mydev]
1808
+ return False
13071809 def deviceChildren(self, devname, phase):
13081810 devlist = []
13091811 list = self.dmesg[phase]['list']
....@@ -1311,21 +1813,45 @@
13111813 if(list[child]['par'] == devname):
13121814 devlist.append(child)
13131815 return devlist
1816
+ def maxDeviceNameSize(self, phase):
1817
+ size = 0
1818
+ for name in self.dmesg[phase]['list']:
1819
+ if len(name) > size:
1820
+ size = len(name)
1821
+ return size
13141822 def printDetails(self):
13151823 sysvals.vprint('Timeline Details:')
13161824 sysvals.vprint(' test start: %f' % self.start)
13171825 sysvals.vprint('kernel suspend start: %f' % self.tKernSus)
1318
- for phase in self.phases:
1319
- dc = len(self.dmesg[phase]['list'])
1320
- sysvals.vprint(' %16s: %f - %f (%d devices)' % (phase, \
1321
- self.dmesg[phase]['start'], self.dmesg[phase]['end'], dc))
1826
+ tS = tR = False
1827
+ for phase in self.sortedPhases():
1828
+ devlist = self.dmesg[phase]['list']
1829
+ dc, ps, pe = len(devlist), self.dmesg[phase]['start'], self.dmesg[phase]['end']
1830
+ if not tS and ps >= self.tSuspended:
1831
+ sysvals.vprint(' machine suspended: %f' % self.tSuspended)
1832
+ tS = True
1833
+ if not tR and ps >= self.tResumed:
1834
+ sysvals.vprint(' machine resumed: %f' % self.tResumed)
1835
+ tR = True
1836
+ sysvals.vprint('%20s: %f - %f (%d devices)' % (phase, ps, pe, dc))
1837
+ if sysvals.devdump:
1838
+ sysvals.vprint(''.join('-' for i in range(80)))
1839
+ maxname = '%d' % self.maxDeviceNameSize(phase)
1840
+ fmt = '%3d) %'+maxname+'s - %f - %f'
1841
+ c = 1
1842
+ for name in sorted(devlist):
1843
+ s = devlist[name]['start']
1844
+ e = devlist[name]['end']
1845
+ sysvals.vprint(fmt % (c, name, s, e))
1846
+ c += 1
1847
+ sysvals.vprint(''.join('-' for i in range(80)))
13221848 sysvals.vprint(' kernel resume end: %f' % self.tKernRes)
13231849 sysvals.vprint(' test end: %f' % self.end)
13241850 def deviceChildrenAllPhases(self, devname):
13251851 devlist = []
1326
- for phase in self.phases:
1852
+ for phase in self.sortedPhases():
13271853 list = self.deviceChildren(devname, phase)
1328
- for dev in list:
1854
+ for dev in sorted(list):
13291855 if dev not in devlist:
13301856 devlist.append(dev)
13311857 return devlist
....@@ -1344,7 +1870,7 @@
13441870 if node.name:
13451871 info = ''
13461872 drv = ''
1347
- for phase in self.phases:
1873
+ for phase in self.sortedPhases():
13481874 list = self.dmesg[phase]['list']
13491875 if node.name in list:
13501876 s = list[node.name]['start']
....@@ -1365,16 +1891,16 @@
13651891 def rootDeviceList(self):
13661892 # list of devices graphed
13671893 real = []
1368
- for phase in self.dmesg:
1894
+ for phase in self.sortedPhases():
13691895 list = self.dmesg[phase]['list']
1370
- for dev in list:
1896
+ for dev in sorted(list):
13711897 if list[dev]['pid'] >= 0 and dev not in real:
13721898 real.append(dev)
13731899 # list of top-most root devices
13741900 rootlist = []
1375
- for phase in self.dmesg:
1901
+ for phase in self.sortedPhases():
13761902 list = self.dmesg[phase]['list']
1377
- for dev in list:
1903
+ for dev in sorted(list):
13781904 pdev = list[dev]['par']
13791905 pid = list[dev]['pid']
13801906 if(pid < 0 or re.match('[0-9]*-[0-9]*\.[0-9]*[\.0-9]*\:[\.0-9]*$', pdev)):
....@@ -1455,9 +1981,9 @@
14551981 def createProcessUsageEvents(self):
14561982 # get an array of process names
14571983 proclist = []
1458
- for t in self.pstl:
1984
+ for t in sorted(self.pstl):
14591985 pslist = self.pstl[t]
1460
- for ps in pslist:
1986
+ for ps in sorted(pslist):
14611987 if ps not in proclist:
14621988 proclist.append(ps)
14631989 # get a list of data points for suspend and resume
....@@ -1478,10 +2004,31 @@
14782004 c = self.addProcessUsageEvent(ps, tres)
14792005 if c > 0:
14802006 sysvals.vprint('%25s (res): %d' % (ps, c))
2007
+ def handleEndMarker(self, time, msg=''):
2008
+ dm = self.dmesg
2009
+ self.setEnd(time, msg)
2010
+ self.initDevicegroups()
2011
+ # give suspend_prepare an end if needed
2012
+ if 'suspend_prepare' in dm and dm['suspend_prepare']['end'] < 0:
2013
+ dm['suspend_prepare']['end'] = time
2014
+ # assume resume machine ends at next phase start
2015
+ if 'resume_machine' in dm and dm['resume_machine']['end'] < 0:
2016
+ np = self.nextPhase('resume_machine', 1)
2017
+ if np:
2018
+ dm['resume_machine']['end'] = dm[np]['start']
2019
+ # if kernel resume end not found, assume its the end marker
2020
+ if self.tKernRes == 0.0:
2021
+ self.tKernRes = time
2022
+ # if kernel suspend start not found, assume its the end marker
2023
+ if self.tKernSus == 0.0:
2024
+ self.tKernSus = time
2025
+ # set resume complete to end at end marker
2026
+ if 'resume_complete' in dm:
2027
+ dm['resume_complete']['end'] = time
14812028 def debugPrint(self):
1482
- for p in self.phases:
2029
+ for p in self.sortedPhases():
14832030 list = self.dmesg[p]['list']
1484
- for devname in list:
2031
+ for devname in sorted(list):
14852032 dev = list[devname]
14862033 if 'ftrace' in dev:
14872034 dev['ftrace'].debugPrint(' [%s]' % devname)
....@@ -1490,9 +2037,9 @@
14902037 # Description:
14912038 # A container for kprobe function data we want in the dev timeline
14922039 class DevFunction:
1493
- row = 0
1494
- count = 1
14952040 def __init__(self, name, args, caller, ret, start, end, u, proc, pid, color):
2041
+ self.row = 0
2042
+ self.count = 1
14962043 self.name = name
14972044 self.args = args
14982045 self.caller = caller
....@@ -1546,16 +2093,15 @@
15462093 # suspend_resume: phase or custom exec block data
15472094 # device_pm_callback: device callback info
15482095 class FTraceLine:
1549
- time = 0.0
1550
- length = 0.0
1551
- fcall = False
1552
- freturn = False
1553
- fevent = False
1554
- fkprobe = False
1555
- depth = 0
1556
- name = ''
1557
- type = ''
15582096 def __init__(self, t, m='', d=''):
2097
+ self.length = 0.0
2098
+ self.fcall = False
2099
+ self.freturn = False
2100
+ self.fevent = False
2101
+ self.fkprobe = False
2102
+ self.depth = 0
2103
+ self.name = ''
2104
+ self.type = ''
15592105 self.time = float(t)
15602106 if not m and not d:
15612107 return
....@@ -1633,20 +2179,20 @@
16332179 return len(str)/2
16342180 def debugPrint(self, info=''):
16352181 if self.isLeaf():
1636
- print(' -- %12.6f (depth=%02d): %s(); (%.3f us) %s' % (self.time, \
2182
+ pprint(' -- %12.6f (depth=%02d): %s(); (%.3f us) %s' % (self.time, \
16372183 self.depth, self.name, self.length*1000000, info))
16382184 elif self.freturn:
1639
- print(' -- %12.6f (depth=%02d): %s} (%.3f us) %s' % (self.time, \
2185
+ pprint(' -- %12.6f (depth=%02d): %s} (%.3f us) %s' % (self.time, \
16402186 self.depth, self.name, self.length*1000000, info))
16412187 else:
1642
- print(' -- %12.6f (depth=%02d): %s() { (%.3f us) %s' % (self.time, \
2188
+ pprint(' -- %12.6f (depth=%02d): %s() { (%.3f us) %s' % (self.time, \
16432189 self.depth, self.name, self.length*1000000, info))
16442190 def startMarker(self):
16452191 # Is this the starting line of a suspend?
16462192 if not self.fevent:
16472193 return False
16482194 if sysvals.usetracemarkers:
1649
- if(self.name == 'SUSPEND START'):
2195
+ if(self.name.startswith('SUSPEND START')):
16502196 return True
16512197 return False
16522198 else:
....@@ -1659,7 +2205,7 @@
16592205 if not self.fevent:
16602206 return False
16612207 if sysvals.usetracemarkers:
1662
- if(self.name == 'RESUME COMPLETE'):
2208
+ if(self.name.startswith('RESUME COMPLETE')):
16632209 return True
16642210 return False
16652211 else:
....@@ -1675,19 +2221,13 @@
16752221 # Each instance is tied to a single device in a single phase, and is
16762222 # comprised of an ordered list of FTraceLine objects
16772223 class FTraceCallGraph:
1678
- id = ''
1679
- start = -1.0
1680
- end = -1.0
1681
- list = []
1682
- invalid = False
1683
- depth = 0
1684
- pid = 0
1685
- name = ''
1686
- partial = False
16872224 vfname = 'missing_function_name'
1688
- ignore = False
1689
- sv = 0
16902225 def __init__(self, pid, sv):
2226
+ self.id = ''
2227
+ self.invalid = False
2228
+ self.name = ''
2229
+ self.partial = False
2230
+ self.ignore = False
16912231 self.start = -1.0
16922232 self.end = -1.0
16932233 self.list = []
....@@ -1786,7 +2326,7 @@
17862326 if warning and ('[make leaf]', line) not in info:
17872327 info.append(('', line))
17882328 if warning:
1789
- print 'WARNING: ftrace data missing, corrections made:'
2329
+ pprint('WARNING: ftrace data missing, corrections made:')
17902330 for i in info:
17912331 t, obj = i
17922332 if obj:
....@@ -1846,10 +2386,10 @@
18462386 id = 'task %s' % (self.pid)
18472387 window = '(%f - %f)' % (self.start, line.time)
18482388 if(self.depth < 0):
1849
- print('Data misalignment for '+id+\
2389
+ pprint('Data misalignment for '+id+\
18502390 ' (buffer overflow), ignoring this callback')
18512391 else:
1852
- print('Too much data for '+id+\
2392
+ pprint('Too much data for '+id+\
18532393 ' '+window+', ignoring this callback')
18542394 def slice(self, dev):
18552395 minicg = FTraceCallGraph(dev['pid'], self.sv)
....@@ -1902,7 +2442,7 @@
19022442 elif l.isReturn():
19032443 if(l.depth not in stack):
19042444 if self.sv.verbose:
1905
- print 'Post Process Error: Depth missing'
2445
+ pprint('Post Process Error: Depth missing')
19062446 l.debugPrint()
19072447 return False
19082448 # calculate call length from call/return lines
....@@ -1919,7 +2459,7 @@
19192459 return True
19202460 elif(cnt < 0):
19212461 if self.sv.verbose:
1922
- print 'Post Process Error: Depth is less than 0'
2462
+ pprint('Post Process Error: Depth is less than 0')
19232463 return False
19242464 # trace ended before call tree finished
19252465 return self.repair(cnt)
....@@ -1943,11 +2483,11 @@
19432483 dev['ftrace'] = cg
19442484 found = devname
19452485 return found
1946
- for p in data.phases:
2486
+ for p in data.sortedPhases():
19472487 if(data.dmesg[p]['start'] <= self.start and
19482488 self.start <= data.dmesg[p]['end']):
19492489 list = data.dmesg[p]['list']
1950
- for devname in list:
2490
+ for devname in sorted(list, key=lambda k:list[k]['start']):
19512491 dev = list[devname]
19522492 if(pid == dev['pid'] and
19532493 self.start <= dev['start'] and
....@@ -1966,7 +2506,7 @@
19662506 if fs < data.start or fe > data.end:
19672507 return
19682508 phase = ''
1969
- for p in data.phases:
2509
+ for p in data.sortedPhases():
19702510 if(data.dmesg[p]['start'] <= self.start and
19712511 self.start < data.dmesg[p]['end']):
19722512 phase = p
....@@ -1978,20 +2518,20 @@
19782518 phase, myname = out
19792519 data.dmesg[phase]['list'][myname]['ftrace'] = self
19802520 def debugPrint(self, info=''):
1981
- print('%s pid=%d [%f - %f] %.3f us') % \
2521
+ pprint('%s pid=%d [%f - %f] %.3f us' % \
19822522 (self.name, self.pid, self.start, self.end,
1983
- (self.end - self.start)*1000000)
2523
+ (self.end - self.start)*1000000))
19842524 for l in self.list:
19852525 if l.isLeaf():
1986
- print('%f (%02d): %s(); (%.3f us)%s' % (l.time, \
2526
+ pprint('%f (%02d): %s(); (%.3f us)%s' % (l.time, \
19872527 l.depth, l.name, l.length*1000000, info))
19882528 elif l.freturn:
1989
- print('%f (%02d): %s} (%.3f us)%s' % (l.time, \
2529
+ pprint('%f (%02d): %s} (%.3f us)%s' % (l.time, \
19902530 l.depth, l.name, l.length*1000000, info))
19912531 else:
1992
- print('%f (%02d): %s() { (%.3f us)%s' % (l.time, \
2532
+ pprint('%f (%02d): %s() { (%.3f us)%s' % (l.time, \
19932533 l.depth, l.name, l.length*1000000, info))
1994
- print(' ')
2534
+ pprint(' ')
19952535
19962536 class DevItem:
19972537 def __init__(self, test, phase, dev):
....@@ -2008,27 +2548,24 @@
20082548 # A container for a device timeline which calculates
20092549 # all the html properties to display it correctly
20102550 class Timeline:
2011
- html = ''
2012
- height = 0 # total timeline height
2013
- scaleH = 20 # timescale (top) row height
2014
- rowH = 30 # device row height
2015
- bodyH = 0 # body height
2016
- rows = 0 # total timeline rows
2017
- rowlines = dict()
2018
- rowheight = dict()
20192551 html_tblock = '<div id="block{0}" class="tblock" style="left:{1}%;width:{2}%;"><div class="tback" style="height:{3}px"></div>\n'
20202552 html_device = '<div id="{0}" title="{1}" class="thread{7}" style="left:{2}%;top:{3}px;height:{4}px;width:{5}%;{8}">{6}</div>\n'
20212553 html_phase = '<div class="phase" style="left:{0}%;width:{1}%;top:{2}px;height:{3}px;background:{4}">{5}</div>\n'
20222554 html_phaselet = '<div id="{0}" class="phaselet" style="left:{1}%;width:{2}%;background:{3}"></div>\n'
20232555 html_legend = '<div id="p{3}" class="square" style="left:{0}%;background:{1}">&nbsp;{2}</div>\n'
20242556 def __init__(self, rowheight, scaleheight):
2025
- self.rowH = rowheight
2026
- self.scaleH = scaleheight
20272557 self.html = ''
2558
+ self.height = 0 # total timeline height
2559
+ self.scaleH = scaleheight # timescale (top) row height
2560
+ self.rowH = rowheight # device row height
2561
+ self.bodyH = 0 # body height
2562
+ self.rows = 0 # total timeline rows
2563
+ self.rowlines = dict()
2564
+ self.rowheight = dict()
20282565 def createHeader(self, sv, stamp):
20292566 if(not stamp['time']):
20302567 return
2031
- self.html += '<div class="version"><a href="https://01.org/suspendresume">%s v%s</a></div>' \
2568
+ self.html += '<div class="version"><a href="https://01.org/pm-graph">%s v%s</a></div>' \
20322569 % (sv.title, sv.version)
20332570 if sv.logmsg and sv.testlog:
20342571 self.html += '<button id="showtest" class="logbtn btnfmt">log</button>'
....@@ -2192,7 +2729,7 @@
21922729 # if there is 1 line per row, draw them the standard way
21932730 for t, p in standardphases:
21942731 for i in sorted(self.rowheight[t][p]):
2195
- self.rowheight[t][p][i] = self.bodyH/len(self.rowlines[t][p])
2732
+ self.rowheight[t][p][i] = float(self.bodyH)/len(self.rowlines[t][p])
21962733 def createZoomBox(self, mode='command', testcount=1):
21972734 # Create bounding box, add buttons
21982735 html_zoombox = '<center><button id="zoomin">ZOOM IN +</button><button id="zoomout">ZOOM OUT -</button><button id="zoomdef">ZOOM 1:1</button></center>\n'
....@@ -2251,31 +2788,40 @@
22512788 # Description:
22522789 # A list of values describing the properties of these test runs
22532790 class TestProps:
2254
- stamp = ''
2255
- sysinfo = ''
2256
- cmdline = ''
2257
- kparams = ''
2258
- S0i3 = False
2259
- fwdata = []
22602791 stampfmt = '# [a-z]*-(?P<m>[0-9]{2})(?P<d>[0-9]{2})(?P<y>[0-9]{2})-'+\
22612792 '(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\
22622793 ' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$'
2794
+ wififmt = '^# wifi *(?P<d>\S*) *(?P<s>\S*) *(?P<t>[0-9\.]+).*'
2795
+ tstatfmt = '^# turbostat (?P<t>\S*)'
2796
+ testerrfmt = '^# enter_sleep_error (?P<e>.*)'
22632797 sysinfofmt = '^# sysinfo .*'
22642798 cmdlinefmt = '^# command \| (?P<cmd>.*)'
22652799 kparamsfmt = '^# kparams \| (?P<kp>.*)'
2800
+ devpropfmt = '# Device Properties: .*'
2801
+ pinfofmt = '# platform-(?P<val>[a-z,A-Z,0-9]*): (?P<info>.*)'
2802
+ tracertypefmt = '# tracer: (?P<t>.*)'
2803
+ firmwarefmt = '# fwsuspend (?P<s>[0-9]*) fwresume (?P<r>[0-9]*)$'
2804
+ procexecfmt = 'ps - (?P<ps>.*)$'
22662805 ftrace_line_fmt_fg = \
22672806 '^ *(?P<time>[0-9\.]*) *\| *(?P<cpu>[0-9]*)\)'+\
22682807 ' *(?P<proc>.*)-(?P<pid>[0-9]*) *\|'+\
22692808 '[ +!#\*@$]*(?P<dur>[0-9\.]*) .*\| (?P<msg>.*)'
22702809 ftrace_line_fmt_nop = \
22712810 ' *(?P<proc>.*)-(?P<pid>[0-9]*) *\[(?P<cpu>[0-9]*)\] *'+\
2272
- '(?P<flags>.{4}) *(?P<time>[0-9\.]*): *'+\
2811
+ '(?P<flags>\S*) *(?P<time>[0-9\.]*): *'+\
22732812 '(?P<msg>.*)'
2274
- ftrace_line_fmt = ftrace_line_fmt_nop
2275
- cgformat = False
2276
- data = 0
2277
- ktemp = dict()
2813
+ machinesuspend = 'machine_suspend\[.*'
22782814 def __init__(self):
2815
+ self.stamp = ''
2816
+ self.sysinfo = ''
2817
+ self.cmdline = ''
2818
+ self.testerror = []
2819
+ self.turbostat = []
2820
+ self.wifi = []
2821
+ self.fwdata = []
2822
+ self.ftrace_line_fmt = self.ftrace_line_fmt_nop
2823
+ self.cgformat = False
2824
+ self.data = 0
22792825 self.ktemp = dict()
22802826 def setTracerType(self, tracer):
22812827 if(tracer == 'function_graph'):
....@@ -2285,8 +2831,45 @@
22852831 self.ftrace_line_fmt = self.ftrace_line_fmt_nop
22862832 else:
22872833 doError('Invalid tracer format: [%s]' % tracer)
2834
+ def stampInfo(self, line, sv):
2835
+ if re.match(self.stampfmt, line):
2836
+ self.stamp = line
2837
+ return True
2838
+ elif re.match(self.sysinfofmt, line):
2839
+ self.sysinfo = line
2840
+ return True
2841
+ elif re.match(self.tstatfmt, line):
2842
+ self.turbostat.append(line)
2843
+ return True
2844
+ elif re.match(self.wififmt, line):
2845
+ self.wifi.append(line)
2846
+ return True
2847
+ elif re.match(self.testerrfmt, line):
2848
+ self.testerror.append(line)
2849
+ return True
2850
+ elif re.match(self.firmwarefmt, line):
2851
+ self.fwdata.append(line)
2852
+ return True
2853
+ elif(re.match(self.devpropfmt, line)):
2854
+ self.parseDevprops(line, sv)
2855
+ return True
2856
+ elif(re.match(self.pinfofmt, line)):
2857
+ self.parsePlatformInfo(line, sv)
2858
+ return True
2859
+ m = re.match(self.cmdlinefmt, line)
2860
+ if m:
2861
+ self.cmdline = m.group('cmd')
2862
+ return True
2863
+ m = re.match(self.tracertypefmt, line)
2864
+ if(m):
2865
+ self.setTracerType(m.group('t'))
2866
+ return True
2867
+ return False
22882868 def parseStamp(self, data, sv):
2869
+ # global test data
22892870 m = re.match(self.stampfmt, self.stamp)
2871
+ if not self.stamp or not m:
2872
+ doError('data does not include the expected stamp')
22902873 data.stamp = {'time': '', 'host': '', 'mode': ''}
22912874 dt = datetime(int(m.group('y'))+2000, int(m.group('m')),
22922875 int(m.group('d')), int(m.group('H')), int(m.group('M')),
....@@ -2305,9 +2888,13 @@
23052888 data.stamp[key] = val
23062889 sv.hostname = data.stamp['host']
23072890 sv.suspendmode = data.stamp['mode']
2891
+ if sv.suspendmode == 'freeze':
2892
+ self.machinesuspend = 'timekeeping_freeze\[.*'
2893
+ else:
2894
+ self.machinesuspend = 'machine_suspend\[.*'
23082895 if sv.suspendmode == 'command' and sv.ftracefile != '':
23092896 modes = ['on', 'freeze', 'standby', 'mem', 'disk']
2310
- fp = sysvals.openlog(sv.ftracefile, 'r')
2897
+ fp = sv.openlog(sv.ftracefile, 'r')
23112898 for line in fp:
23122899 m = re.match('.* machine_suspend\[(?P<mode>.*)\]', line)
23132900 if m and m.group('mode') in ['1', '2', '3', '4']:
....@@ -2315,38 +2902,94 @@
23152902 data.stamp['mode'] = sv.suspendmode
23162903 break
23172904 fp.close()
2318
- m = re.match(self.cmdlinefmt, self.cmdline)
2319
- if m:
2320
- sv.cmdline = m.group('cmd')
2321
- if self.kparams:
2322
- m = re.match(self.kparamsfmt, self.kparams)
2323
- if m:
2324
- sv.kparams = m.group('kp')
2905
+ sv.cmdline = self.cmdline
23252906 if not sv.stamp:
23262907 sv.stamp = data.stamp
2908
+ # firmware data
2909
+ if sv.suspendmode == 'mem' and len(self.fwdata) > data.testnumber:
2910
+ m = re.match(self.firmwarefmt, self.fwdata[data.testnumber])
2911
+ if m:
2912
+ data.fwSuspend, data.fwResume = int(m.group('s')), int(m.group('r'))
2913
+ if(data.fwSuspend > 0 or data.fwResume > 0):
2914
+ data.fwValid = True
2915
+ # turbostat data
2916
+ if len(self.turbostat) > data.testnumber:
2917
+ m = re.match(self.tstatfmt, self.turbostat[data.testnumber])
2918
+ if m:
2919
+ data.turbostat = m.group('t')
2920
+ # wifi data
2921
+ if len(self.wifi) > data.testnumber:
2922
+ m = re.match(self.wififmt, self.wifi[data.testnumber])
2923
+ if m:
2924
+ data.wifi = {'dev': m.group('d'), 'stat': m.group('s'),
2925
+ 'time': float(m.group('t'))}
2926
+ data.stamp['wifi'] = m.group('d')
2927
+ # sleep mode enter errors
2928
+ if len(self.testerror) > data.testnumber:
2929
+ m = re.match(self.testerrfmt, self.testerror[data.testnumber])
2930
+ if m:
2931
+ data.enterfail = m.group('e')
2932
+ def devprops(self, data):
2933
+ props = dict()
2934
+ devlist = data.split(';')
2935
+ for dev in devlist:
2936
+ f = dev.split(',')
2937
+ if len(f) < 3:
2938
+ continue
2939
+ dev = f[0]
2940
+ props[dev] = DevProps()
2941
+ props[dev].altname = f[1]
2942
+ if int(f[2]):
2943
+ props[dev].isasync = True
2944
+ else:
2945
+ props[dev].isasync = False
2946
+ return props
2947
+ def parseDevprops(self, line, sv):
2948
+ idx = line.index(': ') + 2
2949
+ if idx >= len(line):
2950
+ return
2951
+ props = self.devprops(line[idx:])
2952
+ if sv.suspendmode == 'command' and 'testcommandstring' in props:
2953
+ sv.testcommand = props['testcommandstring'].altname
2954
+ sv.devprops = props
2955
+ def parsePlatformInfo(self, line, sv):
2956
+ m = re.match(self.pinfofmt, line)
2957
+ if not m:
2958
+ return
2959
+ name, info = m.group('val'), m.group('info')
2960
+ if name == 'devinfo':
2961
+ sv.devprops = self.devprops(sv.b64unzip(info))
2962
+ return
2963
+ elif name == 'testcmd':
2964
+ sv.testcommand = info
2965
+ return
2966
+ field = info.split('|')
2967
+ if len(field) < 2:
2968
+ return
2969
+ cmdline = field[0].strip()
2970
+ output = sv.b64unzip(field[1].strip())
2971
+ sv.platinfo.append([name, cmdline, output])
23272972
23282973 # Class: TestRun
23292974 # Description:
23302975 # A container for a suspend/resume test run. This is necessary as
23312976 # there could be more than one, and they need to be separate.
23322977 class TestRun:
2333
- ftemp = dict()
2334
- ttemp = dict()
2335
- data = 0
23362978 def __init__(self, dataobj):
23372979 self.data = dataobj
23382980 self.ftemp = dict()
23392981 self.ttemp = dict()
23402982
23412983 class ProcessMonitor:
2342
- proclist = dict()
2343
- running = False
2984
+ def __init__(self):
2985
+ self.proclist = dict()
2986
+ self.running = False
23442987 def procstat(self):
23452988 c = ['cat /proc/[1-9]*/stat 2>/dev/null']
23462989 process = Popen(c, shell=True, stdout=PIPE)
23472990 running = dict()
23482991 for line in process.stdout:
2349
- data = line.split()
2992
+ data = ascii(line).split()
23502993 pid = data[0]
23512994 name = re.sub('[()]', '', data[1])
23522995 user = int(data[13])
....@@ -2390,8 +3033,8 @@
23903033 # Quickly determine if the ftrace log has all of the trace events,
23913034 # markers, and/or kprobes required for primary parsing.
23923035 def doesTraceLogHaveTraceEvents():
2393
- kpcheck = ['_cal: (', '_cpu_down()']
2394
- techeck = ['suspend_resume']
3036
+ kpcheck = ['_cal: (', '_ret: (']
3037
+ techeck = ['suspend_resume', 'device_pm_callback']
23953038 tmcheck = ['SUSPEND START', 'RESUME COMPLETE']
23963039 sysvals.usekprobes = False
23973040 fp = sysvals.openlog(sysvals.ftracefile, 'r')
....@@ -2414,23 +3057,14 @@
24143057 check.remove(i)
24153058 tmcheck = check
24163059 fp.close()
2417
- if len(techeck) == 0:
2418
- sysvals.usetraceevents = True
2419
- else:
2420
- sysvals.usetraceevents = False
2421
- if len(tmcheck) == 0:
2422
- sysvals.usetracemarkers = True
2423
- else:
2424
- sysvals.usetracemarkers = False
3060
+ sysvals.usetraceevents = True if len(techeck) < 2 else False
3061
+ sysvals.usetracemarkers = True if len(tmcheck) == 0 else False
24253062
24263063 # Function: appendIncompleteTraceLog
24273064 # Description:
24283065 # [deprecated for kernel 3.15 or newer]
2429
-# Legacy support of ftrace outputs that lack the device_pm_callback
2430
-# and/or suspend_resume trace events. The primary data should be
2431
-# taken from dmesg, and this ftrace is used only for callgraph data
2432
-# or custom actions in the timeline. The data is appended to the Data
2433
-# objects provided.
3066
+# Adds callgraph data which lacks trace event data. This is only
3067
+# for timelines generated from 3.15 or older
24343068 # Arguments:
24353069 # testruns: the array of Data objects obtained from parseKernelLog
24363070 def appendIncompleteTraceLog(testruns):
....@@ -2450,24 +3084,7 @@
24503084 for line in tf:
24513085 # remove any latent carriage returns
24523086 line = line.replace('\r\n', '')
2453
- # grab the stamp and sysinfo
2454
- if re.match(tp.stampfmt, line):
2455
- tp.stamp = line
2456
- continue
2457
- elif re.match(tp.sysinfofmt, line):
2458
- tp.sysinfo = line
2459
- continue
2460
- elif re.match(tp.cmdlinefmt, line):
2461
- tp.cmdline = line
2462
- continue
2463
- # determine the trace data type (required for further parsing)
2464
- m = re.match(sysvals.tracertypefmt, line)
2465
- if(m):
2466
- tp.setTracerType(m.group('t'))
2467
- continue
2468
- # device properties line
2469
- if(re.match(sysvals.devpropfmt, line)):
2470
- devProps(line)
3087
+ if tp.stampInfo(line, sysvals):
24713088 continue
24723089 # parse only valid lines, if this is not one move on
24733090 m = re.match(tp.ftrace_line_fmt, line)
....@@ -2493,100 +3110,20 @@
24933110 if(t.startMarker()):
24943111 data = testrun[testidx].data
24953112 tp.parseStamp(data, sysvals)
2496
- data.setStart(t.time)
3113
+ data.setStart(t.time, t.name)
24973114 continue
24983115 if(not data):
24993116 continue
25003117 # find the end of resume
25013118 if(t.endMarker()):
2502
- data.setEnd(t.time)
3119
+ data.setEnd(t.time, t.name)
25033120 testidx += 1
25043121 if(testidx >= testcnt):
25053122 break
25063123 continue
25073124 # trace event processing
25083125 if(t.fevent):
2509
- # general trace events have two types, begin and end
2510
- if(re.match('(?P<name>.*) begin$', t.name)):
2511
- isbegin = True
2512
- elif(re.match('(?P<name>.*) end$', t.name)):
2513
- isbegin = False
2514
- else:
2515
- continue
2516
- m = re.match('(?P<name>.*)\[(?P<val>[0-9]*)\] .*', t.name)
2517
- if(m):
2518
- val = m.group('val')
2519
- if val == '0':
2520
- name = m.group('name')
2521
- else:
2522
- name = m.group('name')+'['+val+']'
2523
- else:
2524
- m = re.match('(?P<name>.*) .*', t.name)
2525
- name = m.group('name')
2526
- # special processing for trace events
2527
- if re.match('dpm_prepare\[.*', name):
2528
- continue
2529
- elif re.match('machine_suspend.*', name):
2530
- continue
2531
- elif re.match('suspend_enter\[.*', name):
2532
- if(not isbegin):
2533
- data.dmesg['suspend_prepare']['end'] = t.time
2534
- continue
2535
- elif re.match('dpm_suspend\[.*', name):
2536
- if(not isbegin):
2537
- data.dmesg['suspend']['end'] = t.time
2538
- continue
2539
- elif re.match('dpm_suspend_late\[.*', name):
2540
- if(isbegin):
2541
- data.dmesg['suspend_late']['start'] = t.time
2542
- else:
2543
- data.dmesg['suspend_late']['end'] = t.time
2544
- continue
2545
- elif re.match('dpm_suspend_noirq\[.*', name):
2546
- if(isbegin):
2547
- data.dmesg['suspend_noirq']['start'] = t.time
2548
- else:
2549
- data.dmesg['suspend_noirq']['end'] = t.time
2550
- continue
2551
- elif re.match('dpm_resume_noirq\[.*', name):
2552
- if(isbegin):
2553
- data.dmesg['resume_machine']['end'] = t.time
2554
- data.dmesg['resume_noirq']['start'] = t.time
2555
- else:
2556
- data.dmesg['resume_noirq']['end'] = t.time
2557
- continue
2558
- elif re.match('dpm_resume_early\[.*', name):
2559
- if(isbegin):
2560
- data.dmesg['resume_early']['start'] = t.time
2561
- else:
2562
- data.dmesg['resume_early']['end'] = t.time
2563
- continue
2564
- elif re.match('dpm_resume\[.*', name):
2565
- if(isbegin):
2566
- data.dmesg['resume']['start'] = t.time
2567
- else:
2568
- data.dmesg['resume']['end'] = t.time
2569
- continue
2570
- elif re.match('dpm_complete\[.*', name):
2571
- if(isbegin):
2572
- data.dmesg['resume_complete']['start'] = t.time
2573
- else:
2574
- data.dmesg['resume_complete']['end'] = t.time
2575
- continue
2576
- # skip trace events inside devices calls
2577
- if(not data.isTraceEventOutsideDeviceCalls(pid, t.time)):
2578
- continue
2579
- # global events (outside device calls) are simply graphed
2580
- if(isbegin):
2581
- # store each trace event in ttemp
2582
- if(name not in testrun[testidx].ttemp):
2583
- testrun[testidx].ttemp[name] = []
2584
- testrun[testidx].ttemp[name].append(\
2585
- {'begin': t.time, 'end': t.time})
2586
- else:
2587
- # finish off matching trace event in ttemp
2588
- if(name in testrun[testidx].ttemp):
2589
- testrun[testidx].ttemp[name][-1]['end'] = t.time
3126
+ continue
25903127 # call/return processing
25913128 elif sysvals.usecallgraph:
25923129 # create a callgraph object for the data
....@@ -2603,12 +3140,6 @@
26033140 tf.close()
26043141
26053142 for test in testrun:
2606
- # add the traceevent data to the device hierarchy
2607
- if(sysvals.usetraceevents):
2608
- for name in test.ttemp:
2609
- for event in test.ttemp[name]:
2610
- test.data.newActionGlobal(name, event['begin'], event['end'])
2611
-
26123143 # add the callgraph data to the device hierarchy
26133144 for pid in test.ftemp:
26143145 for cg in test.ftemp[pid]:
....@@ -2621,7 +3152,7 @@
26213152 continue
26223153 callstart = cg.start
26233154 callend = cg.end
2624
- for p in test.data.phases:
3155
+ for p in test.data.sortedPhases():
26253156 if(test.data.dmesg[p]['start'] <= callstart and
26263157 callstart <= test.data.dmesg[p]['end']):
26273158 list = test.data.dmesg[p]['list']
....@@ -2648,45 +3179,25 @@
26483179 doError('%s does not exist' % sysvals.ftracefile)
26493180 if not live:
26503181 sysvals.setupAllKprobes()
2651
- tracewatch = []
3182
+ ksuscalls = ['ksys_sync', 'pm_prepare_console']
3183
+ krescalls = ['pm_restore_console']
3184
+ tracewatch = ['irq_wakeup']
26523185 if sysvals.usekprobes:
26533186 tracewatch += ['sync_filesystems', 'freeze_processes', 'syscore_suspend',
2654
- 'syscore_resume', 'resume_console', 'thaw_processes', 'CPU_ON', 'CPU_OFF']
3187
+ 'syscore_resume', 'resume_console', 'thaw_processes', 'CPU_ON',
3188
+ 'CPU_OFF', 'acpi_suspend']
26553189
26563190 # extract the callgraph and traceevent data
3191
+ s2idle_enter = hwsus = False
26573192 tp = TestProps()
2658
- testruns = []
2659
- testdata = []
2660
- testrun = 0
2661
- data = 0
3193
+ testruns, testdata = [], []
3194
+ testrun, data, limbo = 0, 0, True
26623195 tf = sysvals.openlog(sysvals.ftracefile, 'r')
26633196 phase = 'suspend_prepare'
26643197 for line in tf:
26653198 # remove any latent carriage returns
26663199 line = line.replace('\r\n', '')
2667
- # stamp and sysinfo lines
2668
- if re.match(tp.stampfmt, line):
2669
- tp.stamp = line
2670
- continue
2671
- elif re.match(tp.sysinfofmt, line):
2672
- tp.sysinfo = line
2673
- continue
2674
- elif re.match(tp.cmdlinefmt, line):
2675
- tp.cmdline = line
2676
- continue
2677
- # firmware line: pull out any firmware data
2678
- m = re.match(sysvals.firmwarefmt, line)
2679
- if(m):
2680
- tp.fwdata.append((int(m.group('s')), int(m.group('r'))))
2681
- continue
2682
- # tracer type line: determine the trace data type
2683
- m = re.match(sysvals.tracertypefmt, line)
2684
- if(m):
2685
- tp.setTracerType(m.group('t'))
2686
- continue
2687
- # device properties line
2688
- if(re.match(sysvals.devpropfmt, line)):
2689
- devProps(line)
3200
+ if tp.stampInfo(line, sysvals):
26903201 continue
26913202 # ignore all other commented lines
26923203 if line[0] == '#':
....@@ -2714,20 +3225,20 @@
27143225 continue
27153226 # find the start of suspend
27163227 if(t.startMarker()):
2717
- phase = 'suspend_prepare'
2718
- data = Data(len(testdata))
3228
+ data, limbo = Data(len(testdata)), False
27193229 testdata.append(data)
27203230 testrun = TestRun(data)
27213231 testruns.append(testrun)
27223232 tp.parseStamp(data, sysvals)
2723
- data.setStart(t.time)
2724
- data.tKernSus = t.time
3233
+ data.setStart(t.time, t.name)
3234
+ data.first_suspend_prepare = True
3235
+ phase = data.setPhase('suspend_prepare', t.time, True)
27253236 continue
2726
- if(not data):
3237
+ if(not data or limbo):
27273238 continue
27283239 # process cpu exec line
27293240 if t.type == 'tracing_mark_write':
2730
- m = re.match(sysvals.procexecfmt, t.name)
3241
+ m = re.match(tp.procexecfmt, t.name)
27313242 if(m):
27323243 proclist = dict()
27333244 for ps in m.group('ps').split(','):
....@@ -2740,28 +3251,19 @@
27403251 continue
27413252 # find the end of resume
27423253 if(t.endMarker()):
2743
- data.setEnd(t.time)
2744
- if data.tKernRes == 0.0:
3254
+ if data.tKernRes == 0:
27453255 data.tKernRes = t.time
2746
- if data.dmesg['resume_complete']['end'] < 0:
2747
- data.dmesg['resume_complete']['end'] = t.time
2748
- if sysvals.suspendmode == 'mem' and len(tp.fwdata) > data.testnumber:
2749
- data.fwSuspend, data.fwResume = tp.fwdata[data.testnumber]
2750
- if(data.tSuspended != 0 and data.tResumed != 0 and \
2751
- (data.fwSuspend > 0 or data.fwResume > 0)):
2752
- data.fwValid = True
3256
+ data.handleEndMarker(t.time, t.name)
27533257 if(not sysvals.usetracemarkers):
27543258 # no trace markers? then quit and be sure to finish recording
27553259 # the event we used to trigger resume end
2756
- if(len(testrun.ttemp['thaw_processes']) > 0):
3260
+ if('thaw_processes' in testrun.ttemp and len(testrun.ttemp['thaw_processes']) > 0):
27573261 # if an entry exists, assume this is its end
27583262 testrun.ttemp['thaw_processes'][-1]['end'] = t.time
2759
- break
3263
+ limbo = True
27603264 continue
27613265 # trace event processing
27623266 if(t.fevent):
2763
- if(phase == 'post_resume'):
2764
- data.setEnd(t.time)
27653267 if(t.type == 'suspend_resume'):
27663268 # suspend_resume trace events have two types, begin and end
27673269 if(re.match('(?P<name>.*) begin$', t.name)):
....@@ -2770,102 +3272,88 @@
27703272 isbegin = False
27713273 else:
27723274 continue
2773
- m = re.match('(?P<name>.*)\[(?P<val>[0-9]*)\] .*', t.name)
2774
- if(m):
2775
- val = m.group('val')
2776
- if val == '0':
2777
- name = m.group('name')
2778
- else:
2779
- name = m.group('name')+'['+val+']'
3275
+ if '[' in t.name:
3276
+ m = re.match('(?P<name>.*)\[.*', t.name)
27803277 else:
27813278 m = re.match('(?P<name>.*) .*', t.name)
2782
- name = m.group('name')
3279
+ name = m.group('name')
27833280 # ignore these events
27843281 if(name.split('[')[0] in tracewatch):
27853282 continue
27863283 # -- phase changes --
27873284 # start of kernel suspend
27883285 if(re.match('suspend_enter\[.*', t.name)):
2789
- if(isbegin and data.start == data.tKernSus):
2790
- data.dmesg[phase]['start'] = t.time
3286
+ if(isbegin and data.tKernSus == 0):
27913287 data.tKernSus = t.time
27923288 continue
27933289 # suspend_prepare start
27943290 elif(re.match('dpm_prepare\[.*', t.name)):
2795
- phase = 'suspend_prepare'
2796
- if(not isbegin):
2797
- data.dmesg[phase]['end'] = t.time
2798
- if data.dmesg[phase]['start'] < 0:
2799
- data.dmesg[phase]['start'] = data.start
3291
+ if isbegin and data.first_suspend_prepare:
3292
+ data.first_suspend_prepare = False
3293
+ if data.tKernSus == 0:
3294
+ data.tKernSus = t.time
3295
+ continue
3296
+ phase = data.setPhase('suspend_prepare', t.time, isbegin)
28003297 continue
28013298 # suspend start
28023299 elif(re.match('dpm_suspend\[.*', t.name)):
2803
- phase = 'suspend'
2804
- data.setPhase(phase, t.time, isbegin)
3300
+ phase = data.setPhase('suspend', t.time, isbegin)
28053301 continue
28063302 # suspend_late start
28073303 elif(re.match('dpm_suspend_late\[.*', t.name)):
2808
- phase = 'suspend_late'
2809
- data.setPhase(phase, t.time, isbegin)
3304
+ phase = data.setPhase('suspend_late', t.time, isbegin)
28103305 continue
28113306 # suspend_noirq start
28123307 elif(re.match('dpm_suspend_noirq\[.*', t.name)):
2813
- if data.phaseCollision('suspend_noirq', isbegin, line):
2814
- continue
2815
- phase = 'suspend_noirq'
2816
- data.setPhase(phase, t.time, isbegin)
2817
- if(not isbegin):
2818
- phase = 'suspend_machine'
2819
- data.dmesg[phase]['start'] = t.time
3308
+ phase = data.setPhase('suspend_noirq', t.time, isbegin)
28203309 continue
28213310 # suspend_machine/resume_machine
2822
- elif(re.match('machine_suspend\[.*', t.name)):
3311
+ elif(re.match(tp.machinesuspend, t.name)):
3312
+ lp = data.lastPhase()
28233313 if(isbegin):
2824
- phase = 'suspend_machine'
2825
- data.dmesg[phase]['end'] = t.time
2826
- data.tSuspended = t.time
3314
+ hwsus = True
3315
+ if lp.startswith('resume_machine'):
3316
+ # trim out s2idle loops, track time trying to freeze
3317
+ llp = data.lastPhase(2)
3318
+ if llp.startswith('suspend_machine'):
3319
+ if 'trying' not in data.dmesg[llp]:
3320
+ data.dmesg[llp]['trying'] = 0
3321
+ data.dmesg[llp]['trying'] += \
3322
+ t.time - data.dmesg[lp]['start']
3323
+ data.currphase = ''
3324
+ del data.dmesg[lp]
3325
+ continue
3326
+ phase = data.setPhase('suspend_machine', data.dmesg[lp]['end'], True)
3327
+ data.setPhase(phase, t.time, False)
3328
+ if data.tSuspended == 0:
3329
+ data.tSuspended = t.time
28273330 else:
2828
- if(sysvals.suspendmode in ['mem', 'disk'] and not tp.S0i3):
2829
- data.dmesg['suspend_machine']['end'] = t.time
3331
+ if lp.startswith('resume_machine'):
3332
+ data.dmesg[lp]['end'] = t.time
3333
+ continue
3334
+ phase = data.setPhase('resume_machine', t.time, True)
3335
+ if(sysvals.suspendmode in ['mem', 'disk']):
3336
+ susp = phase.replace('resume', 'suspend')
3337
+ if susp in data.dmesg:
3338
+ data.dmesg[susp]['end'] = t.time
28303339 data.tSuspended = t.time
2831
- phase = 'resume_machine'
2832
- data.dmesg[phase]['start'] = t.time
28333340 data.tResumed = t.time
2834
- data.tLow = data.tResumed - data.tSuspended
2835
- continue
2836
- # acpi_suspend
2837
- elif(re.match('acpi_suspend\[.*', t.name)):
2838
- # acpi_suspend[0] S0i3
2839
- if(re.match('acpi_suspend\[0\] begin', t.name)):
2840
- if(sysvals.suspendmode == 'mem'):
2841
- tp.S0i3 = True
2842
- data.dmesg['suspend_machine']['end'] = t.time
2843
- data.tSuspended = t.time
28443341 continue
28453342 # resume_noirq start
28463343 elif(re.match('dpm_resume_noirq\[.*', t.name)):
2847
- if data.phaseCollision('resume_noirq', isbegin, line):
2848
- continue
2849
- phase = 'resume_noirq'
2850
- data.setPhase(phase, t.time, isbegin)
2851
- if(isbegin):
2852
- data.dmesg['resume_machine']['end'] = t.time
3344
+ phase = data.setPhase('resume_noirq', t.time, isbegin)
28533345 continue
28543346 # resume_early start
28553347 elif(re.match('dpm_resume_early\[.*', t.name)):
2856
- phase = 'resume_early'
2857
- data.setPhase(phase, t.time, isbegin)
3348
+ phase = data.setPhase('resume_early', t.time, isbegin)
28583349 continue
28593350 # resume start
28603351 elif(re.match('dpm_resume\[.*', t.name)):
2861
- phase = 'resume'
2862
- data.setPhase(phase, t.time, isbegin)
3352
+ phase = data.setPhase('resume', t.time, isbegin)
28633353 continue
28643354 # resume complete start
28653355 elif(re.match('dpm_complete\[.*', t.name)):
2866
- phase = 'resume_complete'
2867
- if(isbegin):
2868
- data.dmesg[phase]['start'] = t.time
3356
+ phase = data.setPhase('resume_complete', t.time, isbegin)
28693357 continue
28703358 # skip trace events inside devices calls
28713359 if(not data.isTraceEventOutsideDeviceCalls(pid, t.time)):
....@@ -2873,6 +3361,19 @@
28733361 # global events (outside device calls) are graphed
28743362 if(name not in testrun.ttemp):
28753363 testrun.ttemp[name] = []
3364
+ # special handling for s2idle_enter
3365
+ if name == 'machine_suspend':
3366
+ if hwsus:
3367
+ s2idle_enter = hwsus = False
3368
+ elif s2idle_enter and not isbegin:
3369
+ if(len(testrun.ttemp[name]) > 0):
3370
+ testrun.ttemp[name][-1]['end'] = t.time
3371
+ testrun.ttemp[name][-1]['loop'] += 1
3372
+ elif not s2idle_enter and isbegin:
3373
+ s2idle_enter = True
3374
+ testrun.ttemp[name].append({'begin': t.time,
3375
+ 'end': t.time, 'pid': pid, 'loop': 0})
3376
+ continue
28763377 if(isbegin):
28773378 # create a new list entry
28783379 testrun.ttemp[name].append(\
....@@ -2881,13 +3382,10 @@
28813382 if(len(testrun.ttemp[name]) > 0):
28823383 # if an entry exists, assume this is its end
28833384 testrun.ttemp[name][-1]['end'] = t.time
2884
- elif(phase == 'post_resume'):
2885
- # post resume events can just have ends
2886
- testrun.ttemp[name].append({
2887
- 'begin': data.dmesg[phase]['start'],
2888
- 'end': t.time})
28893385 # device callback start
28903386 elif(t.type == 'device_pm_callback_start'):
3387
+ if phase not in data.dmesg:
3388
+ continue
28913389 m = re.match('(?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*',\
28923390 t.name);
28933391 if(not m):
....@@ -2901,13 +3399,14 @@
29013399 data.devpids.append(pid)
29023400 # device callback finish
29033401 elif(t.type == 'device_pm_callback_end'):
3402
+ if phase not in data.dmesg:
3403
+ continue
29043404 m = re.match('(?P<drv>.*) (?P<d>.*), err.*', t.name);
29053405 if(not m):
29063406 continue
29073407 n = m.group('d')
2908
- list = data.dmesg[phase]['list']
2909
- if(n in list):
2910
- dev = list[n]
3408
+ dev = data.findDevice(phase, n)
3409
+ if dev:
29113410 dev['length'] = t.time - dev['start']
29123411 dev['end'] = t.time
29133412 # kprobe event processing
....@@ -2926,24 +3425,27 @@
29263425 tp.ktemp[key].append({
29273426 'pid': pid,
29283427 'begin': t.time,
2929
- 'end': t.time,
3428
+ 'end': -1,
29303429 'name': displayname,
29313430 'cdata': kprobedata,
29323431 'proc': m_proc,
29333432 })
3433
+ # start of kernel resume
3434
+ if(data.tKernSus == 0 and phase == 'suspend_prepare' \
3435
+ and kprobename in ksuscalls):
3436
+ data.tKernSus = t.time
29343437 elif(t.freturn):
29353438 if(key not in tp.ktemp) or len(tp.ktemp[key]) < 1:
29363439 continue
2937
- e = tp.ktemp[key][-1]
2938
- if e['begin'] < 0.0 or t.time - e['begin'] < 0.000001:
2939
- tp.ktemp[key].pop()
2940
- else:
2941
- e['end'] = t.time
2942
- e['rdata'] = kprobedata
3440
+ e = next((x for x in reversed(tp.ktemp[key]) if x['end'] < 0), 0)
3441
+ if not e:
3442
+ continue
3443
+ e['end'] = t.time
3444
+ e['rdata'] = kprobedata
29433445 # end of kernel resume
2944
- if(kprobename == 'pm_notifier_call_chain' or \
2945
- kprobename == 'pm_restore_console'):
2946
- data.dmesg[phase]['end'] = t.time
3446
+ if(phase != 'suspend_prepare' and kprobename in krescalls):
3447
+ if phase in data.dmesg:
3448
+ data.dmesg[phase]['end'] = t.time
29473449 data.tKernRes = t.time
29483450
29493451 # callgraph processing
....@@ -2961,10 +3463,15 @@
29613463 if(res == -1):
29623464 testrun.ftemp[key][-1].addLine(t)
29633465 tf.close()
3466
+ if len(testdata) < 1:
3467
+ sysvals.vprint('WARNING: ftrace start marker is missing')
3468
+ if data and not data.devicegroups:
3469
+ sysvals.vprint('WARNING: ftrace end marker is missing')
3470
+ data.handleEndMarker(t.time, t.name)
29643471
29653472 if sysvals.suspendmode == 'command':
29663473 for test in testruns:
2967
- for p in test.data.phases:
3474
+ for p in test.data.sortedPhases():
29683475 if p == 'suspend_prepare':
29693476 test.data.dmesg[p]['start'] = test.data.start
29703477 test.data.dmesg[p]['end'] = test.data.end
....@@ -2973,12 +3480,19 @@
29733480 test.data.dmesg[p]['end'] = test.data.end
29743481 test.data.tSuspended = test.data.end
29753482 test.data.tResumed = test.data.end
2976
- test.data.tLow = 0
29773483 test.data.fwValid = False
29783484
29793485 # dev source and procmon events can be unreadable with mixed phase height
29803486 if sysvals.usedevsrc or sysvals.useprocmon:
29813487 sysvals.mixedphaseheight = False
3488
+
3489
+ # expand phase boundaries so there are no gaps
3490
+ for data in testdata:
3491
+ lp = data.sortedPhases()[0]
3492
+ for p in data.sortedPhases():
3493
+ if(p != lp and not ('machine' in p and 'machine' in lp)):
3494
+ data.dmesg[lp]['end'] = data.dmesg[p]['start']
3495
+ lp = p
29823496
29833497 for i in range(len(testruns)):
29843498 test = testruns[i]
....@@ -2993,36 +3507,43 @@
29933507 # add the traceevent data to the device hierarchy
29943508 if(sysvals.usetraceevents):
29953509 # add actual trace funcs
2996
- for name in test.ttemp:
3510
+ for name in sorted(test.ttemp):
29973511 for event in test.ttemp[name]:
2998
- data.newActionGlobal(name, event['begin'], event['end'], event['pid'])
3512
+ if event['end'] - event['begin'] <= 0:
3513
+ continue
3514
+ title = name
3515
+ if name == 'machine_suspend' and 'loop' in event:
3516
+ title = 's2idle_enter_%dx' % event['loop']
3517
+ data.newActionGlobal(title, event['begin'], event['end'], event['pid'])
29993518 # add the kprobe based virtual tracefuncs as actual devices
3000
- for key in tp.ktemp:
3519
+ for key in sorted(tp.ktemp):
30013520 name, pid = key
30023521 if name not in sysvals.tracefuncs:
30033522 continue
3523
+ if pid not in data.devpids:
3524
+ data.devpids.append(pid)
30043525 for e in tp.ktemp[key]:
30053526 kb, ke = e['begin'], e['end']
3006
- if kb == ke or tlb > kb or tle <= kb:
3527
+ if ke - kb < 0.000001 or tlb > kb or tle <= kb:
30073528 continue
30083529 color = sysvals.kprobeColor(name)
30093530 data.newActionGlobal(e['name'], kb, ke, pid, color)
30103531 # add config base kprobes and dev kprobes
30113532 if sysvals.usedevsrc:
3012
- for key in tp.ktemp:
3533
+ for key in sorted(tp.ktemp):
30133534 name, pid = key
30143535 if name in sysvals.tracefuncs or name not in sysvals.dev_tracefuncs:
30153536 continue
30163537 for e in tp.ktemp[key]:
30173538 kb, ke = e['begin'], e['end']
3018
- if kb == ke or tlb > kb or tle <= kb:
3539
+ if ke - kb < 0.000001 or tlb > kb or tle <= kb:
30193540 continue
30203541 data.addDeviceFunctionCall(e['name'], name, e['proc'], pid, kb,
30213542 ke, e['cdata'], e['rdata'])
30223543 if sysvals.usecallgraph:
30233544 # add the callgraph data to the device hierarchy
30243545 sortlist = dict()
3025
- for key in test.ftemp:
3546
+ for key in sorted(test.ftemp):
30263547 proc, pid = key
30273548 for cg in test.ftemp[key]:
30283549 if len(cg.list) < 1 or cg.invalid or (cg.end - cg.start == 0):
....@@ -3039,9 +3560,9 @@
30393560 if not devname:
30403561 sortkey = '%f%f%d' % (cg.start, cg.end, pid)
30413562 sortlist[sortkey] = cg
3042
- elif len(cg.list) > 1000000:
3043
- print 'WARNING: the callgraph for %s is massive (%d lines)' %\
3044
- (devname, len(cg.list))
3563
+ elif len(cg.list) > 1000000 and cg.name != sysvals.ftopfunc:
3564
+ sysvals.vprint('WARNING: the callgraph for %s is massive (%d lines)' %\
3565
+ (devname, len(cg.list)))
30453566 # create blocks for orphan cg data
30463567 for sortkey in sorted(sortlist):
30473568 cg = sortlist[sortkey]
....@@ -3057,25 +3578,34 @@
30573578 for data in testdata:
30583579 tn = '' if len(testdata) == 1 else ('%d' % (data.testnumber + 1))
30593580 terr = ''
3060
- lp = data.phases[0]
3061
- for p in data.phases:
3062
- if(data.dmesg[p]['start'] < 0 and data.dmesg[p]['end'] < 0):
3581
+ phasedef = data.phasedef
3582
+ lp = 'suspend_prepare'
3583
+ for p in sorted(phasedef, key=lambda k:phasedef[k]['order']):
3584
+ if p not in data.dmesg:
30633585 if not terr:
3064
- print 'TEST%s FAILED: %s failed in %s phase' % (tn, sysvals.suspendmode, lp)
3065
- terr = '%s%s failed in %s phase' % (sysvals.suspendmode, tn, lp)
3586
+ ph = p if 'machine' in p else lp
3587
+ terr = '%s%s failed in %s phase' % (sysvals.suspendmode, tn, ph)
3588
+ pprint('TEST%s FAILED: %s' % (tn, terr))
30663589 error.append(terr)
3590
+ if data.tSuspended == 0:
3591
+ data.tSuspended = data.dmesg[lp]['end']
3592
+ if data.tResumed == 0:
3593
+ data.tResumed = data.dmesg[lp]['end']
3594
+ data.fwValid = False
30673595 sysvals.vprint('WARNING: phase "%s" is missing!' % p)
3068
- if(data.dmesg[p]['start'] < 0):
3069
- data.dmesg[p]['start'] = data.dmesg[lp]['end']
3070
- if(p == 'resume_machine'):
3071
- data.tSuspended = data.dmesg[lp]['end']
3072
- data.tResumed = data.dmesg[lp]['end']
3073
- data.tLow = 0
3074
- if(data.dmesg[p]['end'] < 0):
3075
- data.dmesg[p]['end'] = data.dmesg[p]['start']
3076
- if(p != lp and not ('machine' in p and 'machine' in lp)):
3077
- data.dmesg[lp]['end'] = data.dmesg[p]['start']
30783596 lp = p
3597
+ if not terr and 'dev' in data.wifi and data.wifi['stat'] == 'timeout':
3598
+ terr = '%s%s failed in wifi_resume <i>(%s %.0fs timeout)</i>' % \
3599
+ (sysvals.suspendmode, tn, data.wifi['dev'], data.wifi['time'])
3600
+ error.append(terr)
3601
+ if not terr and data.enterfail:
3602
+ pprint('test%s FAILED: enter %s failed with %s' % (tn, sysvals.suspendmode, data.enterfail))
3603
+ terr = 'test%s failed to enter %s mode' % (tn, sysvals.suspendmode)
3604
+ error.append(terr)
3605
+ if data.tSuspended == 0:
3606
+ data.tSuspended = data.tKernRes
3607
+ if data.tResumed == 0:
3608
+ data.tResumed = data.tSuspended
30793609
30803610 if(len(sysvals.devicefilter) > 0):
30813611 data.deviceFilter(sysvals.devicefilter)
....@@ -3117,19 +3647,7 @@
31173647 idx = line.find('[')
31183648 if idx > 1:
31193649 line = line[idx:]
3120
- # grab the stamp and sysinfo
3121
- if re.match(tp.stampfmt, line):
3122
- tp.stamp = line
3123
- continue
3124
- elif re.match(tp.sysinfofmt, line):
3125
- tp.sysinfo = line
3126
- continue
3127
- elif re.match(tp.cmdlinefmt, line):
3128
- tp.cmdline = line
3129
- continue
3130
- m = re.match(sysvals.firmwarefmt, line)
3131
- if(m):
3132
- tp.fwdata.append((int(m.group('s')), int(m.group('r'))))
3650
+ if tp.stampInfo(line, sysvals):
31333651 continue
31343652 m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
31353653 if(not m):
....@@ -3140,10 +3658,6 @@
31403658 testruns.append(data)
31413659 data = Data(len(testruns))
31423660 tp.parseStamp(data, sysvals)
3143
- if len(tp.fwdata) > data.testnumber:
3144
- data.fwSuspend, data.fwResume = tp.fwdata[data.testnumber]
3145
- if(data.fwSuspend > 0 or data.fwResume > 0):
3146
- data.fwValid = True
31473661 if(not data):
31483662 continue
31493663 m = re.match('.* *(?P<k>[0-9]\.[0-9]{2}\.[0-9]-.*) .*', msg)
....@@ -3158,7 +3672,7 @@
31583672 if data:
31593673 testruns.append(data)
31603674 if len(testruns) < 1:
3161
- print('ERROR: dmesg log has no suspend/resume data: %s' \
3675
+ doError('dmesg log has no suspend/resume data: %s' \
31623676 % sysvals.dmesgfile)
31633677
31643678 # fix lines with same timestamp/function with the call and return swapped
....@@ -3199,30 +3713,30 @@
31993713
32003714 # dmesg phase match table
32013715 dm = {
3202
- 'suspend_prepare': 'PM: Syncing filesystems.*',
3203
- 'suspend': 'PM: Entering [a-z]* sleep.*',
3204
- 'suspend_late': 'PM: suspend of devices complete after.*',
3205
- 'suspend_noirq': 'PM: late suspend of devices complete after.*',
3206
- 'suspend_machine': 'PM: noirq suspend of devices complete after.*',
3207
- 'resume_machine': 'ACPI: Low-level resume complete.*',
3208
- 'resume_noirq': 'ACPI: Waking up from system sleep state.*',
3209
- 'resume_early': 'PM: noirq resume of devices complete after.*',
3210
- 'resume': 'PM: early resume of devices complete after.*',
3211
- 'resume_complete': 'PM: resume of devices complete after.*',
3212
- 'post_resume': '.*Restarting tasks \.\.\..*',
3716
+ 'suspend_prepare': ['PM: Syncing filesystems.*'],
3717
+ 'suspend': ['PM: Entering [a-z]* sleep.*', 'Suspending console.*'],
3718
+ 'suspend_late': ['PM: suspend of devices complete after.*'],
3719
+ 'suspend_noirq': ['PM: late suspend of devices complete after.*'],
3720
+ 'suspend_machine': ['PM: noirq suspend of devices complete after.*'],
3721
+ 'resume_machine': ['ACPI: Low-level resume complete.*'],
3722
+ 'resume_noirq': ['ACPI: Waking up from system sleep state.*'],
3723
+ 'resume_early': ['PM: noirq resume of devices complete after.*'],
3724
+ 'resume': ['PM: early resume of devices complete after.*'],
3725
+ 'resume_complete': ['PM: resume of devices complete after.*'],
3726
+ 'post_resume': ['.*Restarting tasks \.\.\..*'],
32133727 }
32143728 if(sysvals.suspendmode == 'standby'):
3215
- dm['resume_machine'] = 'PM: Restoring platform NVS memory'
3729
+ dm['resume_machine'] = ['PM: Restoring platform NVS memory']
32163730 elif(sysvals.suspendmode == 'disk'):
3217
- dm['suspend_late'] = 'PM: freeze of devices complete after.*'
3218
- dm['suspend_noirq'] = 'PM: late freeze of devices complete after.*'
3219
- dm['suspend_machine'] = 'PM: noirq freeze of devices complete after.*'
3220
- dm['resume_machine'] = 'PM: Restoring platform NVS memory'
3221
- dm['resume_early'] = 'PM: noirq restore of devices complete after.*'
3222
- dm['resume'] = 'PM: early restore of devices complete after.*'
3223
- dm['resume_complete'] = 'PM: restore of devices complete after.*'
3731
+ dm['suspend_late'] = ['PM: freeze of devices complete after.*']
3732
+ dm['suspend_noirq'] = ['PM: late freeze of devices complete after.*']
3733
+ dm['suspend_machine'] = ['PM: noirq freeze of devices complete after.*']
3734
+ dm['resume_machine'] = ['PM: Restoring platform NVS memory']
3735
+ dm['resume_early'] = ['PM: noirq restore of devices complete after.*']
3736
+ dm['resume'] = ['PM: early restore of devices complete after.*']
3737
+ dm['resume_complete'] = ['PM: restore of devices complete after.*']
32243738 elif(sysvals.suspendmode == 'freeze'):
3225
- dm['resume_machine'] = 'ACPI: resume from mwait'
3739
+ dm['resume_machine'] = ['ACPI: resume from mwait']
32263740
32273741 # action table (expected events that occur and show up in dmesg)
32283742 at = {
....@@ -3264,81 +3778,89 @@
32643778 else:
32653779 continue
32663780
3781
+ # check for a phase change line
3782
+ phasechange = False
3783
+ for p in dm:
3784
+ for s in dm[p]:
3785
+ if(re.match(s, msg)):
3786
+ phasechange, phase = True, p
3787
+ break
3788
+
32673789 # hack for determining resume_machine end for freeze
32683790 if(not sysvals.usetraceevents and sysvals.suspendmode == 'freeze' \
32693791 and phase == 'resume_machine' and \
32703792 re.match('calling (?P<f>.*)\+ @ .*, parent: .*', msg)):
3271
- data.dmesg['resume_machine']['end'] = ktime
3793
+ data.setPhase(phase, ktime, False)
32723794 phase = 'resume_noirq'
3273
- data.dmesg[phase]['start'] = ktime
3795
+ data.setPhase(phase, ktime, True)
32743796
3275
- # suspend start
3276
- if(re.match(dm['suspend_prepare'], msg)):
3277
- phase = 'suspend_prepare'
3278
- data.dmesg[phase]['start'] = ktime
3279
- data.setStart(ktime)
3280
- data.tKernSus = ktime
3281
- # suspend start
3282
- elif(re.match(dm['suspend'], msg)):
3283
- data.dmesg['suspend_prepare']['end'] = ktime
3284
- phase = 'suspend'
3285
- data.dmesg[phase]['start'] = ktime
3286
- # suspend_late start
3287
- elif(re.match(dm['suspend_late'], msg)):
3288
- data.dmesg['suspend']['end'] = ktime
3289
- phase = 'suspend_late'
3290
- data.dmesg[phase]['start'] = ktime
3291
- # suspend_noirq start
3292
- elif(re.match(dm['suspend_noirq'], msg)):
3293
- data.dmesg['suspend_late']['end'] = ktime
3294
- phase = 'suspend_noirq'
3295
- data.dmesg[phase]['start'] = ktime
3296
- # suspend_machine start
3297
- elif(re.match(dm['suspend_machine'], msg)):
3298
- data.dmesg['suspend_noirq']['end'] = ktime
3299
- phase = 'suspend_machine'
3300
- data.dmesg[phase]['start'] = ktime
3301
- # resume_machine start
3302
- elif(re.match(dm['resume_machine'], msg)):
3303
- if(sysvals.suspendmode in ['freeze', 'standby']):
3304
- data.tSuspended = prevktime
3305
- data.dmesg['suspend_machine']['end'] = prevktime
3306
- else:
3307
- data.tSuspended = ktime
3308
- data.dmesg['suspend_machine']['end'] = ktime
3309
- phase = 'resume_machine'
3310
- data.tResumed = ktime
3311
- data.tLow = data.tResumed - data.tSuspended
3312
- data.dmesg[phase]['start'] = ktime
3313
- # resume_noirq start
3314
- elif(re.match(dm['resume_noirq'], msg)):
3315
- data.dmesg['resume_machine']['end'] = ktime
3316
- phase = 'resume_noirq'
3317
- data.dmesg[phase]['start'] = ktime
3318
- # resume_early start
3319
- elif(re.match(dm['resume_early'], msg)):
3320
- data.dmesg['resume_noirq']['end'] = ktime
3321
- phase = 'resume_early'
3322
- data.dmesg[phase]['start'] = ktime
3323
- # resume start
3324
- elif(re.match(dm['resume'], msg)):
3325
- data.dmesg['resume_early']['end'] = ktime
3326
- phase = 'resume'
3327
- data.dmesg[phase]['start'] = ktime
3328
- # resume complete start
3329
- elif(re.match(dm['resume_complete'], msg)):
3330
- data.dmesg['resume']['end'] = ktime
3331
- phase = 'resume_complete'
3332
- data.dmesg[phase]['start'] = ktime
3333
- # post resume start
3334
- elif(re.match(dm['post_resume'], msg)):
3335
- data.dmesg['resume_complete']['end'] = ktime
3336
- data.setEnd(ktime)
3337
- data.tKernRes = ktime
3338
- break
3797
+ if phasechange:
3798
+ if phase == 'suspend_prepare':
3799
+ data.setPhase(phase, ktime, True)
3800
+ data.setStart(ktime)
3801
+ data.tKernSus = ktime
3802
+ elif phase == 'suspend':
3803
+ lp = data.lastPhase()
3804
+ if lp:
3805
+ data.setPhase(lp, ktime, False)
3806
+ data.setPhase(phase, ktime, True)
3807
+ elif phase == 'suspend_late':
3808
+ lp = data.lastPhase()
3809
+ if lp:
3810
+ data.setPhase(lp, ktime, False)
3811
+ data.setPhase(phase, ktime, True)
3812
+ elif phase == 'suspend_noirq':
3813
+ lp = data.lastPhase()
3814
+ if lp:
3815
+ data.setPhase(lp, ktime, False)
3816
+ data.setPhase(phase, ktime, True)
3817
+ elif phase == 'suspend_machine':
3818
+ lp = data.lastPhase()
3819
+ if lp:
3820
+ data.setPhase(lp, ktime, False)
3821
+ data.setPhase(phase, ktime, True)
3822
+ elif phase == 'resume_machine':
3823
+ lp = data.lastPhase()
3824
+ if(sysvals.suspendmode in ['freeze', 'standby']):
3825
+ data.tSuspended = prevktime
3826
+ if lp:
3827
+ data.setPhase(lp, prevktime, False)
3828
+ else:
3829
+ data.tSuspended = ktime
3830
+ if lp:
3831
+ data.setPhase(lp, prevktime, False)
3832
+ data.tResumed = ktime
3833
+ data.setPhase(phase, ktime, True)
3834
+ elif phase == 'resume_noirq':
3835
+ lp = data.lastPhase()
3836
+ if lp:
3837
+ data.setPhase(lp, ktime, False)
3838
+ data.setPhase(phase, ktime, True)
3839
+ elif phase == 'resume_early':
3840
+ lp = data.lastPhase()
3841
+ if lp:
3842
+ data.setPhase(lp, ktime, False)
3843
+ data.setPhase(phase, ktime, True)
3844
+ elif phase == 'resume':
3845
+ lp = data.lastPhase()
3846
+ if lp:
3847
+ data.setPhase(lp, ktime, False)
3848
+ data.setPhase(phase, ktime, True)
3849
+ elif phase == 'resume_complete':
3850
+ lp = data.lastPhase()
3851
+ if lp:
3852
+ data.setPhase(lp, ktime, False)
3853
+ data.setPhase(phase, ktime, True)
3854
+ elif phase == 'post_resume':
3855
+ lp = data.lastPhase()
3856
+ if lp:
3857
+ data.setPhase(lp, ktime, False)
3858
+ data.setEnd(ktime)
3859
+ data.tKernRes = ktime
3860
+ break
33393861
33403862 # -- device callbacks --
3341
- if(phase in data.phases):
3863
+ if(phase in data.sortedPhases()):
33423864 # device init call
33433865 if(re.match('calling (?P<f>.*)\+ @ .*, parent: .*', msg)):
33443866 sm = re.match('calling (?P<f>.*)\+ @ '+\
....@@ -3364,7 +3886,7 @@
33643886 # if trace events are not available, these are better than nothing
33653887 if(not sysvals.usetraceevents):
33663888 # look for known actions
3367
- for a in at:
3889
+ for a in sorted(at):
33683890 if(re.match(at[a]['smsg'], msg)):
33693891 if(a not in actions):
33703892 actions[a] = []
....@@ -3396,27 +3918,34 @@
33963918 actions[cpu].append({'begin': cpu_start, 'end': ktime})
33973919 cpu_start = ktime
33983920 prevktime = ktime
3921
+ data.initDevicegroups()
33993922
34003923 # fill in any missing phases
3401
- lp = data.phases[0]
3402
- for p in data.phases:
3403
- if(data.dmesg[p]['start'] < 0 and data.dmesg[p]['end'] < 0):
3404
- print('WARNING: phase "%s" is missing, something went wrong!' % p)
3405
- print(' In %s, this dmesg line denotes the start of %s:' % \
3406
- (sysvals.suspendmode, p))
3407
- print(' "%s"' % dm[p])
3408
- if(data.dmesg[p]['start'] < 0):
3409
- data.dmesg[p]['start'] = data.dmesg[lp]['end']
3410
- if(p == 'resume_machine'):
3411
- data.tSuspended = data.dmesg[lp]['end']
3412
- data.tResumed = data.dmesg[lp]['end']
3413
- data.tLow = 0
3414
- if(data.dmesg[p]['end'] < 0):
3415
- data.dmesg[p]['end'] = data.dmesg[p]['start']
3924
+ phasedef = data.phasedef
3925
+ terr, lp = '', 'suspend_prepare'
3926
+ for p in sorted(phasedef, key=lambda k:phasedef[k]['order']):
3927
+ if p not in data.dmesg:
3928
+ if not terr:
3929
+ pprint('TEST FAILED: %s failed in %s phase' % (sysvals.suspendmode, lp))
3930
+ terr = '%s failed in %s phase' % (sysvals.suspendmode, lp)
3931
+ if data.tSuspended == 0:
3932
+ data.tSuspended = data.dmesg[lp]['end']
3933
+ if data.tResumed == 0:
3934
+ data.tResumed = data.dmesg[lp]['end']
3935
+ sysvals.vprint('WARNING: phase "%s" is missing!' % p)
34163936 lp = p
3937
+ lp = data.sortedPhases()[0]
3938
+ for p in data.sortedPhases():
3939
+ if(p != lp and not ('machine' in p and 'machine' in lp)):
3940
+ data.dmesg[lp]['end'] = data.dmesg[p]['start']
3941
+ lp = p
3942
+ if data.tSuspended == 0:
3943
+ data.tSuspended = data.tKernRes
3944
+ if data.tResumed == 0:
3945
+ data.tResumed = data.tSuspended
34173946
34183947 # fill in any actions we've found
3419
- for name in actions:
3948
+ for name in sorted(actions):
34203949 for event in actions[name]:
34213950 data.newActionGlobal(name, event['begin'], event['end'])
34223951
....@@ -3462,26 +3991,30 @@
34623991 hf.write('<section id="callgraphs" class="callgraph">\n')
34633992 # write out the ftrace data converted to html
34643993 num = 0
3465
- for p in data.phases:
3994
+ for p in data.sortedPhases():
34663995 if sv.cgphase and p != sv.cgphase:
34673996 continue
34683997 list = data.dmesg[p]['list']
3469
- for devname in data.sortedDevices(p):
3470
- if len(sv.cgfilter) > 0 and devname not in sv.cgfilter:
3998
+ for d in data.sortedDevices(p):
3999
+ if len(sv.cgfilter) > 0 and d not in sv.cgfilter:
34714000 continue
3472
- dev = list[devname]
4001
+ dev = list[d]
34734002 color = 'white'
34744003 if 'color' in data.dmesg[p]:
34754004 color = data.dmesg[p]['color']
34764005 if 'color' in dev:
34774006 color = dev['color']
3478
- name = devname
3479
- if(devname in sv.devprops):
3480
- name = sv.devprops[devname].altName(devname)
4007
+ name = d if '[' not in d else d.split('[')[0]
4008
+ if(d in sv.devprops):
4009
+ name = sv.devprops[d].altName(d)
4010
+ if 'drv' in dev and dev['drv']:
4011
+ name += ' {%s}' % dev['drv']
34814012 if sv.suspendmode in suspendmodename:
34824013 name += ' '+p
34834014 if('ftrace' in dev):
34844015 cg = dev['ftrace']
4016
+ if cg.name == sv.ftopfunc:
4017
+ name = 'top level suspend/resume call'
34854018 num = callgraphHTML(sv, hf, num, cg,
34864019 name, color, dev['id'])
34874020 if('ftraces' in dev):
....@@ -3490,22 +4023,16 @@
34904023 name+' &rarr; '+cg.name, color, dev['id'])
34914024 hf.write('\n\n </section>\n')
34924025
3493
-# Function: createHTMLSummarySimple
3494
-# Description:
3495
-# Create summary html file for a series of tests
3496
-# Arguments:
3497
-# testruns: array of Data objects from parseTraceLog
3498
-def createHTMLSummarySimple(testruns, htmlfile, folder):
3499
- # write the html header first (html head, css code, up to body start)
3500
- html = '<!DOCTYPE html>\n<html>\n<head>\n\
4026
+def summaryCSS(title, center=True):
4027
+ tdcenter = 'text-align:center;' if center else ''
4028
+ out = '<!DOCTYPE html>\n<html>\n<head>\n\
35014029 <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\
3502
- <title>SleepGraph Summary</title>\n\
4030
+ <title>'+title+'</title>\n\
35034031 <style type=\'text/css\'>\n\
35044032 .stamp {width: 100%;text-align:center;background:#888;line-height:30px;color:white;font: 25px Arial;}\n\
3505
- table {width:100%;border-collapse: collapse;}\n\
3506
- .summary {border:1px solid;}\n\
4033
+ table {width:100%;border-collapse: collapse;border:1px solid;}\n\
35074034 th {border: 1px solid black;background:#222;color:white;}\n\
3508
- td {font: 16px "Times New Roman";text-align: center;}\n\
4035
+ td {font: 14px "Times New Roman";'+tdcenter+'}\n\
35094036 tr.head td {border: 1px solid black;background:#aaa;}\n\
35104037 tr.alt {background-color:#ddd;}\n\
35114038 tr.notice {color:red;}\n\
....@@ -3514,14 +4041,25 @@
35144041 .maxval {background-color:#FFBBBB;}\n\
35154042 .head a {color:#000;text-decoration: none;}\n\
35164043 </style>\n</head>\n<body>\n'
4044
+ return out
4045
+
4046
+# Function: createHTMLSummarySimple
4047
+# Description:
4048
+# Create summary html file for a series of tests
4049
+# Arguments:
4050
+# testruns: array of Data objects from parseTraceLog
4051
+def createHTMLSummarySimple(testruns, htmlfile, title):
4052
+ # write the html header first (html head, css code, up to body start)
4053
+ html = summaryCSS('Summary - SleepGraph')
35174054
35184055 # extract the test data into list
35194056 list = dict()
3520
- tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [[], []]
4057
+ tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()]
35214058 iMin, iMed, iMax = [0, 0], [0, 0], [0, 0]
35224059 num = 0
4060
+ useturbo = usewifi = False
35234061 lastmode = ''
3524
- cnt = {'pass':0, 'fail':0, 'hang':0}
4062
+ cnt = dict()
35254063 for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'], v['time'])):
35264064 mode = data['mode']
35274065 if mode not in list:
....@@ -3529,24 +4067,36 @@
35294067 if lastmode and lastmode != mode and num > 0:
35304068 for i in range(2):
35314069 s = sorted(tMed[i])
3532
- list[lastmode]['med'][i] = s[int(len(s)/2)]
3533
- iMed[i] = tMed[i].index(list[lastmode]['med'][i])
4070
+ list[lastmode]['med'][i] = s[int(len(s)//2)]
4071
+ iMed[i] = tMed[i][list[lastmode]['med'][i]]
35344072 list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num]
35354073 list[lastmode]['min'] = tMin
35364074 list[lastmode]['max'] = tMax
35374075 list[lastmode]['idx'] = (iMin, iMed, iMax)
3538
- tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [[], []]
4076
+ tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()]
35394077 iMin, iMed, iMax = [0, 0], [0, 0], [0, 0]
35404078 num = 0
4079
+ pkgpc10 = syslpi = wifi = ''
4080
+ if 'pkgpc10' in data and 'syslpi' in data:
4081
+ pkgpc10, syslpi, useturbo = data['pkgpc10'], data['syslpi'], True
4082
+ if 'wifi' in data:
4083
+ wifi, usewifi = data['wifi'], True
4084
+ res = data['result']
35414085 tVal = [float(data['suspend']), float(data['resume'])]
35424086 list[mode]['data'].append([data['host'], data['kernel'],
3543
- data['time'], tVal[0], tVal[1], data['url'], data['result'],
3544
- data['issues']])
4087
+ data['time'], tVal[0], tVal[1], data['url'], res,
4088
+ data['issues'], data['sus_worst'], data['sus_worsttime'],
4089
+ data['res_worst'], data['res_worsttime'], pkgpc10, syslpi, wifi])
35454090 idx = len(list[mode]['data']) - 1
3546
- if data['result'] == 'pass':
3547
- cnt['pass'] += 1
4091
+ if res.startswith('fail in'):
4092
+ res = 'fail'
4093
+ if res not in cnt:
4094
+ cnt[res] = 1
4095
+ else:
4096
+ cnt[res] += 1
4097
+ if res == 'pass':
35484098 for i in range(2):
3549
- tMed[i].append(tVal[i])
4099
+ tMed[i][tVal[i]] = idx
35504100 tAvg[i] += tVal[i]
35514101 if tMin[i] == 0 or tVal[i] < tMin[i]:
35524102 iMin[i] = idx
....@@ -3555,16 +4105,12 @@
35554105 iMax[i] = idx
35564106 tMax[i] = tVal[i]
35574107 num += 1
3558
- elif data['result'] == 'hang':
3559
- cnt['hang'] += 1
3560
- elif data['result'] == 'fail':
3561
- cnt['fail'] += 1
35624108 lastmode = mode
35634109 if lastmode and num > 0:
35644110 for i in range(2):
35654111 s = sorted(tMed[i])
3566
- list[lastmode]['med'][i] = s[int(len(s)/2)]
3567
- iMed[i] = tMed[i].index(list[lastmode]['med'][i])
4112
+ list[lastmode]['med'][i] = s[int(len(s)//2)]
4113
+ iMed[i] = tMed[i][list[lastmode]['med'][i]]
35684114 list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num]
35694115 list[lastmode]['min'] = tMin
35704116 list[lastmode]['max'] = tMax
....@@ -3575,21 +4121,33 @@
35754121 for ilk in sorted(cnt, reverse=True):
35764122 if cnt[ilk] > 0:
35774123 desc.append('%d %s' % (cnt[ilk], ilk))
3578
- html += '<div class="stamp">%s (%d tests: %s)</div>\n' % (folder, len(testruns), ', '.join(desc))
4124
+ html += '<div class="stamp">%s (%d tests: %s)</div>\n' % (title, len(testruns), ', '.join(desc))
35794125 th = '\t<th>{0}</th>\n'
35804126 td = '\t<td>{0}</td>\n'
35814127 tdh = '\t<td{1}>{0}</td>\n'
35824128 tdlink = '\t<td><a href="{0}">html</a></td>\n'
4129
+ cols = 12
4130
+ if useturbo:
4131
+ cols += 2
4132
+ if usewifi:
4133
+ cols += 1
4134
+ colspan = '%d' % cols
35834135
35844136 # table header
3585
- html += '<table class="summary">\n<tr>\n' + th.format('#') +\
4137
+ html += '<table>\n<tr>\n' + th.format('#') +\
35864138 th.format('Mode') + th.format('Host') + th.format('Kernel') +\
35874139 th.format('Test Time') + th.format('Result') + th.format('Issues') +\
3588
- th.format('Suspend') + th.format('Resume') + th.format('Detail') + '</tr>\n'
3589
-
4140
+ th.format('Suspend') + th.format('Resume') +\
4141
+ th.format('Worst Suspend Device') + th.format('SD Time') +\
4142
+ th.format('Worst Resume Device') + th.format('RD Time')
4143
+ if useturbo:
4144
+ html += th.format('PkgPC10') + th.format('SysLPI')
4145
+ if usewifi:
4146
+ html += th.format('Wifi')
4147
+ html += th.format('Detail')+'</tr>\n'
35904148 # export list into html
35914149 head = '<tr class="head"><td>{0}</td><td>{1}</td>'+\
3592
- '<td colspan=8 class="sus">Suspend Avg={2} '+\
4150
+ '<td colspan='+colspan+' class="sus">Suspend Avg={2} '+\
35934151 '<span class=minval><a href="#s{10}min">Min={3}</a></span> '+\
35944152 '<span class=medval><a href="#s{10}med">Med={4}</a></span> '+\
35954153 '<span class=maxval><a href="#s{10}max">Max={5}</a></span> '+\
....@@ -3598,8 +4156,9 @@
35984156 '<span class=medval><a href="#r{10}med">Med={8}</a></span> '+\
35994157 '<span class=maxval><a href="#r{10}max">Max={9}</a></span></td>'+\
36004158 '</tr>\n'
3601
- headnone = '<tr class="head"><td>{0}</td><td>{1}</td><td colspan=8></td></tr>\n'
3602
- for mode in list:
4159
+ headnone = '<tr class="head"><td>{0}</td><td>{1}</td><td colspan='+\
4160
+ colspan+'></td></tr>\n'
4161
+ for mode in sorted(list):
36034162 # header line for each suspend mode
36044163 num = 0
36054164 tAvg, tMin, tMax, tMed = list[mode]['avg'], list[mode]['min'],\
....@@ -3641,6 +4200,15 @@
36414200 html += td.format(d[7]) # issues
36424201 html += tdh.format('%.3f ms' % d[3], tHigh[0]) if d[3] else td.format('') # suspend
36434202 html += tdh.format('%.3f ms' % d[4], tHigh[1]) if d[4] else td.format('') # resume
4203
+ html += td.format(d[8]) # sus_worst
4204
+ html += td.format('%.3f ms' % d[9]) if d[9] else td.format('') # sus_worst time
4205
+ html += td.format(d[10]) # res_worst
4206
+ html += td.format('%.3f ms' % d[11]) if d[11] else td.format('') # res_worst time
4207
+ if useturbo:
4208
+ html += td.format(d[12]) # pkg_pc10
4209
+ html += td.format(d[13]) # syslpi
4210
+ if usewifi:
4211
+ html += td.format(d[14]) # wifi
36444212 html += tdlink.format(d[5]) if d[5] else td.format('') # url
36454213 html += '</tr>\n'
36464214 num += 1
....@@ -3649,6 +4217,116 @@
36494217 hf = open(htmlfile, 'w')
36504218 hf.write(html+'</table>\n</body>\n</html>\n')
36514219 hf.close()
4220
+
4221
+def createHTMLDeviceSummary(testruns, htmlfile, title):
4222
+ html = summaryCSS('Device Summary - SleepGraph', False)
4223
+
4224
+ # create global device list from all tests
4225
+ devall = dict()
4226
+ for data in testruns:
4227
+ host, url, devlist = data['host'], data['url'], data['devlist']
4228
+ for type in devlist:
4229
+ if type not in devall:
4230
+ devall[type] = dict()
4231
+ mdevlist, devlist = devall[type], data['devlist'][type]
4232
+ for name in devlist:
4233
+ length = devlist[name]
4234
+ if name not in mdevlist:
4235
+ mdevlist[name] = {'name': name, 'host': host,
4236
+ 'worst': length, 'total': length, 'count': 1,
4237
+ 'url': url}
4238
+ else:
4239
+ if length > mdevlist[name]['worst']:
4240
+ mdevlist[name]['worst'] = length
4241
+ mdevlist[name]['url'] = url
4242
+ mdevlist[name]['host'] = host
4243
+ mdevlist[name]['total'] += length
4244
+ mdevlist[name]['count'] += 1
4245
+
4246
+ # generate the html
4247
+ th = '\t<th>{0}</th>\n'
4248
+ td = '\t<td align=center>{0}</td>\n'
4249
+ tdr = '\t<td align=right>{0}</td>\n'
4250
+ tdlink = '\t<td align=center><a href="{0}">html</a></td>\n'
4251
+ limit = 1
4252
+ for type in sorted(devall, reverse=True):
4253
+ num = 0
4254
+ devlist = devall[type]
4255
+ # table header
4256
+ html += '<div class="stamp">%s (%s devices > %d ms)</div><table>\n' % \
4257
+ (title, type.upper(), limit)
4258
+ html += '<tr>\n' + '<th align=right>Device Name</th>' +\
4259
+ th.format('Average Time') + th.format('Count') +\
4260
+ th.format('Worst Time') + th.format('Host (worst time)') +\
4261
+ th.format('Link (worst time)') + '</tr>\n'
4262
+ for name in sorted(devlist, key=lambda k:(devlist[k]['worst'], \
4263
+ devlist[k]['total'], devlist[k]['name']), reverse=True):
4264
+ data = devall[type][name]
4265
+ data['average'] = data['total'] / data['count']
4266
+ if data['average'] < limit:
4267
+ continue
4268
+ # row classes - alternate row color
4269
+ rcls = ['alt'] if num % 2 == 1 else []
4270
+ html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n'
4271
+ html += tdr.format(data['name']) # name
4272
+ html += td.format('%.3f ms' % data['average']) # average
4273
+ html += td.format(data['count']) # count
4274
+ html += td.format('%.3f ms' % data['worst']) # worst
4275
+ html += td.format(data['host']) # host
4276
+ html += tdlink.format(data['url']) # url
4277
+ html += '</tr>\n'
4278
+ num += 1
4279
+ html += '</table>\n'
4280
+
4281
+ # flush the data to file
4282
+ hf = open(htmlfile, 'w')
4283
+ hf.write(html+'</body>\n</html>\n')
4284
+ hf.close()
4285
+ return devall
4286
+
4287
+def createHTMLIssuesSummary(testruns, issues, htmlfile, title, extra=''):
4288
+ multihost = len([e for e in issues if len(e['urls']) > 1]) > 0
4289
+ html = summaryCSS('Issues Summary - SleepGraph', False)
4290
+ total = len(testruns)
4291
+
4292
+ # generate the html
4293
+ th = '\t<th>{0}</th>\n'
4294
+ td = '\t<td align={0}>{1}</td>\n'
4295
+ tdlink = '<a href="{1}">{0}</a>'
4296
+ subtitle = '%d issues' % len(issues) if len(issues) > 0 else 'no issues'
4297
+ html += '<div class="stamp">%s (%s)</div><table>\n' % (title, subtitle)
4298
+ html += '<tr>\n' + th.format('Issue') + th.format('Count')
4299
+ if multihost:
4300
+ html += th.format('Hosts')
4301
+ html += th.format('Tests') + th.format('Fail Rate') +\
4302
+ th.format('First Instance') + '</tr>\n'
4303
+
4304
+ num = 0
4305
+ for e in sorted(issues, key=lambda v:v['count'], reverse=True):
4306
+ testtotal = 0
4307
+ links = []
4308
+ for host in sorted(e['urls']):
4309
+ links.append(tdlink.format(host, e['urls'][host][0]))
4310
+ testtotal += len(e['urls'][host])
4311
+ rate = '%d/%d (%.2f%%)' % (testtotal, total, 100*float(testtotal)/float(total))
4312
+ # row classes - alternate row color
4313
+ rcls = ['alt'] if num % 2 == 1 else []
4314
+ html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n'
4315
+ html += td.format('left', e['line']) # issue
4316
+ html += td.format('center', e['count']) # count
4317
+ if multihost:
4318
+ html += td.format('center', len(e['urls'])) # hosts
4319
+ html += td.format('center', testtotal) # test count
4320
+ html += td.format('center', rate) # test rate
4321
+ html += td.format('center nowrap', '<br>'.join(links)) # links
4322
+ html += '</tr>\n'
4323
+ num += 1
4324
+
4325
+ # flush the data to file
4326
+ hf = open(htmlfile, 'w')
4327
+ hf.write(html+'</table>\n'+extra+'</body>\n</html>\n')
4328
+ hf.close()
4329
+ return issues
36524330
36534331 def ordinal(value):
36544332 suffix = 'th'
....@@ -3670,14 +4348,17 @@
36704348 # True if the html file was created, false if it failed
36714349 def createHTML(testruns, testfail):
36724350 if len(testruns) < 1:
3673
- print('ERROR: Not enough test data to build a timeline')
4351
+ pprint('ERROR: Not enough test data to build a timeline')
36744352 return
36754353
36764354 kerror = False
36774355 for data in testruns:
36784356 if data.kerror:
36794357 kerror = True
3680
- data.normalizeTime(testruns[-1].tSuspended)
4358
+ if(sysvals.suspendmode in ['freeze', 'standby']):
4359
+ data.trimFreezeTime(testruns[-1].tSuspended)
4360
+ else:
4361
+ data.getMemTime()
36814362
36824363 # html function templates
36834364 html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">{2}&rarr;</div>\n'
....@@ -3696,13 +4377,10 @@
36964377 '<td class="green">Execution Time: <b>{0} ms</b></td>'\
36974378 '<td class="yellow">Command: <b>{1}</b></td>'\
36984379 '</tr>\n</table>\n'
3699
- html_timegroups = '<table class="time2">\n<tr>'\
3700
- '<td class="green" title="time from kernel enter_state({5}) to firmware mode [kernel time only]">{4}Kernel Suspend: {0} ms</td>'\
3701
- '<td class="purple">{4}Firmware Suspend: {1} ms</td>'\
3702
- '<td class="purple">{4}Firmware Resume: {2} ms</td>'\
3703
- '<td class="yellow" title="time from firmware mode to return from kernel enter_state({5}) [kernel time only]">{4}Kernel Resume: {3} ms</td>'\
3704
- '</tr>\n</table>\n'
37054380 html_fail = '<table class="testfail"><tr><td>{0}</td></tr></table>\n'
4381
+ html_kdesc = '<td class="{3}" title="time spent in kernel execution">{0}Kernel {2}: {1} ms</td>'
4382
+ html_fwdesc = '<td class="{3}" title="time spent in firmware">{0}Firmware {2}: {1} ms</td>'
4383
+ html_wifdesc = '<td class="yellow" title="time for wifi to reconnect after resume complete ({2})">{0}Wifi Resume: {1}</td>'
37064384
37074385 # html format variables
37084386 scaleH = 20
....@@ -3718,13 +4396,10 @@
37184396 # Generate the header for this timeline
37194397 for data in testruns:
37204398 tTotal = data.end - data.start
3721
- sktime, rktime = data.getTimeValues()
37224399 if(tTotal == 0):
37234400 doError('No timeline data')
3724
- if(data.tLow > 0):
3725
- low_time = '%.0f'%(data.tLow*1000)
37264401 if sysvals.suspendmode == 'command':
3727
- run_time = '%.0f'%((data.end-data.start)*1000)
4402
+ run_time = '%.0f' % (tTotal * 1000)
37284403 if sysvals.testcommand:
37294404 testdesc = sysvals.testcommand
37304405 else:
....@@ -3733,43 +4408,55 @@
37334408 testdesc = ordinal(data.testnumber+1)+' '+testdesc
37344409 thtml = html_timetotal3.format(run_time, testdesc)
37354410 devtl.html += thtml
3736
- elif data.fwValid:
3737
- suspend_time = '%.0f'%(sktime + (data.fwSuspend/1000000.0))
3738
- resume_time = '%.0f'%(rktime + (data.fwResume/1000000.0))
3739
- testdesc1 = 'Total'
3740
- testdesc2 = ''
3741
- stitle = 'time from kernel enter_state(%s) to low-power mode [kernel & firmware time]' % sysvals.suspendmode
3742
- rtitle = 'time from low-power mode to return from kernel enter_state(%s) [firmware & kernel time]' % sysvals.suspendmode
3743
- if(len(testruns) > 1):
3744
- testdesc1 = testdesc2 = ordinal(data.testnumber+1)
3745
- testdesc2 += ' '
3746
- if(data.tLow == 0):
3747
- thtml = html_timetotal.format(suspend_time, \
3748
- resume_time, testdesc1, stitle, rtitle)
3749
- else:
3750
- thtml = html_timetotal2.format(suspend_time, low_time, \
3751
- resume_time, testdesc1, stitle, rtitle)
3752
- devtl.html += thtml
4411
+ continue
4412
+ # typical full suspend/resume header
4413
+ stot, rtot = sktime, rktime = data.getTimeValues()
4414
+ ssrc, rsrc, testdesc, testdesc2 = ['kernel'], ['kernel'], 'Kernel', ''
4415
+ if data.fwValid:
4416
+ stot += (data.fwSuspend/1000000.0)
4417
+ rtot += (data.fwResume/1000000.0)
4418
+ ssrc.append('firmware')
4419
+ rsrc.append('firmware')
4420
+ testdesc = 'Total'
4421
+ if 'time' in data.wifi and data.wifi['stat'] != 'timeout':
4422
+ rtot += data.end - data.tKernRes + (data.wifi['time'] * 1000.0)
4423
+ rsrc.append('wifi')
4424
+ testdesc = 'Total'
4425
+ suspend_time, resume_time = '%.3f' % stot, '%.3f' % rtot
4426
+ stitle = 'time from kernel suspend start to %s mode [%s time]' % \
4427
+ (sysvals.suspendmode, ' & '.join(ssrc))
4428
+ rtitle = 'time from %s mode to kernel resume complete [%s time]' % \
4429
+ (sysvals.suspendmode, ' & '.join(rsrc))
4430
+ if(len(testruns) > 1):
4431
+ testdesc = testdesc2 = ordinal(data.testnumber+1)
4432
+ testdesc2 += ' '
4433
+ if(len(data.tLow) == 0):
4434
+ thtml = html_timetotal.format(suspend_time, \
4435
+ resume_time, testdesc, stitle, rtitle)
4436
+ else:
4437
+ low_time = '+'.join(data.tLow)
4438
+ thtml = html_timetotal2.format(suspend_time, low_time, \
4439
+ resume_time, testdesc, stitle, rtitle)
4440
+ devtl.html += thtml
4441
+ if not data.fwValid and 'dev' not in data.wifi:
4442
+ continue
4443
+ # extra detail when the times come from multiple sources
4444
+ thtml = '<table class="time2">\n<tr>'
4445
+ thtml += html_kdesc.format(testdesc2, '%.3f'%sktime, 'Suspend', 'green')
4446
+ if data.fwValid:
37534447 sftime = '%.3f'%(data.fwSuspend / 1000000.0)
37544448 rftime = '%.3f'%(data.fwResume / 1000000.0)
3755
- devtl.html += html_timegroups.format('%.3f'%sktime, \
3756
- sftime, rftime, '%.3f'%rktime, testdesc2, sysvals.suspendmode)
3757
- else:
3758
- suspend_time = '%.3f' % sktime
3759
- resume_time = '%.3f' % rktime
3760
- testdesc = 'Kernel'
3761
- stitle = 'time from kernel enter_state(%s) to firmware mode [kernel time only]' % sysvals.suspendmode
3762
- rtitle = 'time from firmware mode to return from kernel enter_state(%s) [kernel time only]' % sysvals.suspendmode
3763
- if(len(testruns) > 1):
3764
- testdesc = ordinal(data.testnumber+1)+' '+testdesc
3765
- if(data.tLow == 0):
3766
- thtml = html_timetotal.format(suspend_time, \
3767
- resume_time, testdesc, stitle, rtitle)
4449
+ thtml += html_fwdesc.format(testdesc2, sftime, 'Suspend', 'green')
4450
+ thtml += html_fwdesc.format(testdesc2, rftime, 'Resume', 'yellow')
4451
+ thtml += html_kdesc.format(testdesc2, '%.3f'%rktime, 'Resume', 'yellow')
4452
+ if 'time' in data.wifi:
4453
+ if data.wifi['stat'] != 'timeout':
4454
+ wtime = '%.0f ms'%(data.end - data.tKernRes + (data.wifi['time'] * 1000.0))
37684455 else:
3769
- thtml = html_timetotal2.format(suspend_time, low_time, \
3770
- resume_time, testdesc, stitle, rtitle)
3771
- devtl.html += thtml
3772
-
4456
+ wtime = 'TIMEOUT'
4457
+ thtml += html_wifdesc.format(testdesc2, wtime, data.wifi['dev'])
4458
+ thtml += '</tr>\n</table>\n'
4459
+ devtl.html += thtml
37734460 if testfail:
37744461 devtl.html += html_fail.format(testfail)
37754462
....@@ -3788,7 +4475,7 @@
37884475 for group in data.devicegroups:
37894476 devlist = []
37904477 for phase in group:
3791
- for devname in data.tdevlist[phase]:
4478
+ for devname in sorted(data.tdevlist[phase]):
37924479 d = DevItem(data.testnumber, phase, data.dmesg[phase]['list'][devname])
37934480 devlist.append(d)
37944481 if d.isa('kth'):
....@@ -3820,15 +4507,14 @@
38204507
38214508 # draw the full timeline
38224509 devtl.createZoomBox(sysvals.suspendmode, len(testruns))
3823
- phases = {'suspend':[],'resume':[]}
3824
- for phase in data.dmesg:
3825
- if 'resume' in phase:
3826
- phases['resume'].append(phase)
3827
- else:
3828
- phases['suspend'].append(phase)
3829
-
3830
- # draw each test run chronologically
38314510 for data in testruns:
4511
+ # draw each test run and block chronologically
4512
+ phases = {'suspend':[],'resume':[]}
4513
+ for phase in data.sortedPhases():
4514
+ if data.dmesg[phase]['start'] >= data.tSuspended:
4515
+ phases['resume'].append(phase)
4516
+ else:
4517
+ phases['suspend'].append(phase)
38324518 # now draw the actual timeline blocks
38334519 for dir in phases:
38344520 # draw suspend and resume blocks separately
....@@ -3850,7 +4536,7 @@
38504536 continue
38514537 width = '%f' % (((mTotal*100.0)-sysvals.srgap/2)/tTotal)
38524538 devtl.html += devtl.html_tblock.format(bname, left, width, devtl.scaleH)
3853
- for b in sorted(phases[dir]):
4539
+ for b in phases[dir]:
38544540 # draw the phase color background
38554541 phase = data.dmesg[b]
38564542 length = phase['end']-phase['start']
....@@ -3865,16 +4551,13 @@
38654551 id = '%d_%d' % (idx1, idx2)
38664552 right = '%f' % (((mMax-t)*100.0)/mTotal)
38674553 devtl.html += html_error.format(right, id, type)
3868
- for b in sorted(phases[dir]):
4554
+ for b in phases[dir]:
38694555 # draw the devices for this phase
38704556 phaselist = data.dmesg[b]['list']
3871
- for d in data.tdevlist[b]:
3872
- name = d
3873
- drv = ''
3874
- dev = phaselist[d]
3875
- xtraclass = ''
3876
- xtrainfo = ''
3877
- xtrastyle = ''
4557
+ for d in sorted(data.tdevlist[b]):
4558
+ dname = d if '[' not in d else d.split('[')[0]
4559
+ name, dev = dname, phaselist[d]
4560
+ drv = xtraclass = xtrainfo = xtrastyle = ''
38784561 if 'htmlclass' in dev:
38794562 xtraclass = dev['htmlclass']
38804563 if 'color' in dev:
....@@ -3905,7 +4588,7 @@
39054588 title += b
39064589 devtl.html += devtl.html_device.format(dev['id'], \
39074590 title, left, top, '%.3f'%rowheight, width, \
3908
- d+drv, xtraclass, xtrastyle)
4591
+ dname+drv, xtraclass, xtrastyle)
39094592 if('cpuexec' in dev):
39104593 for t in sorted(dev['cpuexec']):
39114594 start, end = t
....@@ -3923,6 +4606,8 @@
39234606 continue
39244607 # draw any trace events for this device
39254608 for e in dev['src']:
4609
+ if e.length == 0:
4610
+ continue
39264611 height = '%.3f' % devtl.rowH
39274612 top = '%.3f' % (rowtop + devtl.scaleH + (e.row*devtl.rowH))
39284613 left = '%f' % (((e.time-m0)*100)/mTotal)
....@@ -3942,19 +4627,17 @@
39424627
39434628 # draw a legend which describes the phases by color
39444629 if sysvals.suspendmode != 'command':
3945
- data = testruns[-1]
4630
+ phasedef = testruns[-1].phasedef
39464631 devtl.html += '<div class="legend">\n'
3947
- pdelta = 100.0/len(data.phases)
4632
+ pdelta = 100.0/len(phasedef.keys())
39484633 pmargin = pdelta / 4.0
3949
- for phase in data.phases:
3950
- tmp = phase.split('_')
3951
- id = tmp[0][0]
3952
- if(len(tmp) > 1):
3953
- id += tmp[1][0]
3954
- order = '%.2f' % ((data.dmesg[phase]['order'] * pdelta) + pmargin)
3955
- name = string.replace(phase, '_', ' &nbsp;')
3956
- devtl.html += devtl.html_legend.format(order, \
3957
- data.dmesg[phase]['color'], name, id)
4634
+ for phase in sorted(phasedef, key=lambda k:phasedef[k]['order']):
4635
+ id, p = '', phasedef[phase]
4636
+ for word in phase.split('_'):
4637
+ id += word[0]
4638
+ order = '%.2f' % ((p['order'] * pdelta) + pmargin)
4639
+ name = phase.replace('_', ' &nbsp;')
4640
+ devtl.html += devtl.html_legend.format(order, p['color'], name, id)
39584641 devtl.html += '</div>\n'
39594642
39604643 hf = open(sysvals.htmlfile, 'w')
....@@ -3970,7 +4653,7 @@
39704653 pscolor = 'linear-gradient(to top left, #ccc, #eee)'
39714654 hf.write(devtl.html_phaselet.format('pre_suspend_process', \
39724655 '0', '0', pscolor))
3973
- for b in data.phases:
4656
+ for b in data.sortedPhases():
39744657 phase = data.dmesg[b]
39754658 length = phase['end']-phase['start']
39764659 left = '%.3f' % (((phase['start']-t0)*100.0)/tTotal)
....@@ -4522,48 +5205,49 @@
45225205 sv.rstgt, sv.rsval, sv.rsdir = 'on', 'auto', 'enabled'
45235206 else:
45245207 sv.rstgt, sv.rsval, sv.rsdir = 'auto', 'on', 'disabled'
4525
- print('CONFIGURING RUNTIME SUSPEND...')
5208
+ pprint('CONFIGURING RUNTIME SUSPEND...')
45265209 sv.rslist = deviceInfo(sv.rstgt)
45275210 for i in sv.rslist:
45285211 sv.setVal(sv.rsval, i)
4529
- print('runtime suspend %s on all devices (%d changed)' % (sv.rsdir, len(sv.rslist)))
4530
- print('waiting 5 seconds...')
5212
+ pprint('runtime suspend %s on all devices (%d changed)' % (sv.rsdir, len(sv.rslist)))
5213
+ pprint('waiting 5 seconds...')
45315214 time.sleep(5)
45325215 else:
45335216 # runtime suspend re-enable or re-disable
45345217 for i in sv.rslist:
45355218 sv.setVal(sv.rstgt, i)
4536
- print('runtime suspend settings restored on %d devices' % len(sv.rslist))
5219
+ pprint('runtime suspend settings restored on %d devices' % len(sv.rslist))
45375220
45385221 # Function: executeSuspend
45395222 # Description:
45405223 # Execute system suspend through the sysfs interface, then copy the output
45415224 # dmesg and ftrace files to the test output directory.
4542
-def executeSuspend():
5225
+def executeSuspend(quiet=False):
45435226 pm = ProcessMonitor()
45445227 tp = sysvals.tpath
4545
- fwdata = []
5228
+ if sysvals.wifi:
5229
+ wifi = sysvals.checkWifi()
5230
+ testdata = []
45465231 # run these commands to prepare the system for suspend
45475232 if sysvals.display:
4548
- if sysvals.display > 0:
4549
- print('TURN DISPLAY ON')
4550
- call('xset -d :0.0 dpms force suspend', shell=True)
4551
- call('xset -d :0.0 dpms force on', shell=True)
4552
- else:
4553
- print('TURN DISPLAY OFF')
4554
- call('xset -d :0.0 dpms force suspend', shell=True)
5233
+ if not quiet:
5234
+ pprint('SET DISPLAY TO %s' % sysvals.display.upper())
5235
+ displayControl(sysvals.display)
45555236 time.sleep(1)
45565237 if sysvals.sync:
4557
- print('SYNCING FILESYSTEMS')
5238
+ if not quiet:
5239
+ pprint('SYNCING FILESYSTEMS')
45585240 call('sync', shell=True)
45595241 # mark the start point in the kernel ring buffer just as we start
45605242 sysvals.initdmesg()
45615243 # start ftrace
45625244 if(sysvals.usecallgraph or sysvals.usetraceevents):
4563
- print('START TRACING')
5245
+ if not quiet:
5246
+ pprint('START TRACING')
45645247 sysvals.fsetVal('1', 'tracing_on')
45655248 if sysvals.useprocmon:
45665249 pm.start()
5250
+ sysvals.cmdinfo(True)
45675251 # execute however many s/r runs requested
45685252 for count in range(1,sysvals.execcount+1):
45695253 # x2delay in between test runs
....@@ -4573,27 +5257,31 @@
45735257 sysvals.fsetVal('WAIT END', 'trace_marker')
45745258 # start message
45755259 if sysvals.testcommand != '':
4576
- print('COMMAND START')
5260
+ pprint('COMMAND START')
45775261 else:
45785262 if(sysvals.rtcwake):
4579
- print('SUSPEND START')
5263
+ pprint('SUSPEND START')
45805264 else:
4581
- print('SUSPEND START (press a key to resume)')
5265
+ pprint('SUSPEND START (press a key to resume)')
45825266 # set rtcwake
45835267 if(sysvals.rtcwake):
4584
- print('will issue an rtcwake in %d seconds' % sysvals.rtcwaketime)
5268
+ if not quiet:
5269
+ pprint('will issue an rtcwake in %d seconds' % sysvals.rtcwaketime)
45855270 sysvals.rtcWakeAlarmOn()
45865271 # start of suspend trace marker
45875272 if(sysvals.usecallgraph or sysvals.usetraceevents):
4588
- sysvals.fsetVal('SUSPEND START', 'trace_marker')
5273
+ sysvals.fsetVal(datetime.now().strftime(sysvals.tmstart), 'trace_marker')
45895274 # predelay delay
45905275 if(count == 1 and sysvals.predelay > 0):
45915276 sysvals.fsetVal('WAIT %d' % sysvals.predelay, 'trace_marker')
45925277 time.sleep(sysvals.predelay/1000.0)
45935278 sysvals.fsetVal('WAIT END', 'trace_marker')
45945279 # initiate suspend or command
5280
+ tdata = {'error': ''}
45955281 if sysvals.testcommand != '':
4596
- call(sysvals.testcommand+' 2>&1', shell=True);
5282
+ res = call(sysvals.testcommand+' 2>&1', shell=True);
5283
+ if res != 0:
5284
+ tdata['error'] = 'cmd returned %d' % res
45975285 else:
45985286 mode = sysvals.suspendmode
45995287 if sysvals.memmode and os.path.exists(sysvals.mempowerfile):
....@@ -4601,13 +5289,24 @@
46015289 pf = open(sysvals.mempowerfile, 'w')
46025290 pf.write(sysvals.memmode)
46035291 pf.close()
4604
- pf = open(sysvals.powerfile, 'w')
4605
- pf.write(mode)
4606
- # execution will pause here
4607
- try:
5292
+ if sysvals.diskmode and os.path.exists(sysvals.diskpowerfile):
5293
+ mode = 'disk'
5294
+ pf = open(sysvals.diskpowerfile, 'w')
5295
+ pf.write(sysvals.diskmode)
46085296 pf.close()
4609
- except:
4610
- pass
5297
+ if mode == 'freeze' and sysvals.haveTurbostat():
5298
+ # execution will pause here
5299
+ turbo = sysvals.turbostat()
5300
+ if turbo:
5301
+ tdata['turbo'] = turbo
5302
+ else:
5303
+ pf = open(sysvals.powerfile, 'w')
5304
+ pf.write(mode)
5305
+ # execution will pause here
5306
+ try:
5307
+ pf.close()
5308
+ except Exception as e:
5309
+ tdata['error'] = str(e)
46115310 if(sysvals.rtcwake):
46125311 sysvals.rtcWakeAlarmOff()
46135312 # postdelay delay
....@@ -4616,27 +5315,35 @@
46165315 time.sleep(sysvals.postdelay/1000.0)
46175316 sysvals.fsetVal('WAIT END', 'trace_marker')
46185317 # return from suspend
4619
- print('RESUME COMPLETE')
5318
+ pprint('RESUME COMPLETE')
46205319 if(sysvals.usecallgraph or sysvals.usetraceevents):
4621
- sysvals.fsetVal('RESUME COMPLETE', 'trace_marker')
5320
+ sysvals.fsetVal(datetime.now().strftime(sysvals.tmend), 'trace_marker')
5321
+ if sysvals.wifi and wifi:
5322
+ tdata['wifi'] = sysvals.pollWifi(wifi)
46225323 if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'):
4623
- fwdata.append(getFPDT(False))
5324
+ tdata['fw'] = getFPDT(False)
5325
+ testdata.append(tdata)
5326
+ cmdafter = sysvals.cmdinfo(False)
46245327 # stop ftrace
46255328 if(sysvals.usecallgraph or sysvals.usetraceevents):
46265329 if sysvals.useprocmon:
46275330 pm.stop()
46285331 sysvals.fsetVal('0', 'tracing_on')
4629
- print('CAPTURING TRACE')
4630
- op = sysvals.writeDatafileHeader(sysvals.ftracefile, fwdata)
5332
+ # grab a copy of the dmesg output
5333
+ if not quiet:
5334
+ pprint('CAPTURING DMESG')
5335
+ sysvals.getdmesg(testdata)
5336
+ # grab a copy of the ftrace output
5337
+ if(sysvals.usecallgraph or sysvals.usetraceevents):
5338
+ if not quiet:
5339
+ pprint('CAPTURING TRACE')
5340
+ op = sysvals.writeDatafileHeader(sysvals.ftracefile, testdata)
46315341 fp = open(tp+'trace', 'r')
46325342 for line in fp:
46335343 op.write(line)
46345344 op.close()
46355345 sysvals.fsetVal('', 'trace')
4636
- devProps()
4637
- # grab a copy of the dmesg output
4638
- print('CAPTURING DMESG')
4639
- sysvals.getdmesg(fwdata)
5346
+ sysvals.platforminfo(cmdafter)
46405347
46415348 def readFile(file):
46425349 if os.path.islink(file):
....@@ -4651,9 +5358,9 @@
46515358 # The time string, e.g. "1901m16s"
46525359 def ms2nice(val):
46535360 val = int(val)
4654
- h = val / 3600000
4655
- m = (val / 60000) % 60
4656
- s = (val / 1000) % 60
5361
+ h = val // 3600000
5362
+ m = (val // 60000) % 60
5363
+ s = (val // 1000) % 60
46575364 if h > 0:
46585365 return '%d:%02d:%02d' % (h, m, s)
46595366 if m > 0:
....@@ -4673,15 +5380,15 @@
46735380 # a list of USB device names to sysvals for better timeline readability
46745381 def deviceInfo(output=''):
46755382 if not output:
4676
- print('LEGEND')
4677
- print('---------------------------------------------------------------------------------------------')
4678
- print(' A = async/sync PM queue (A/S) C = runtime active children')
4679
- print(' R = runtime suspend enabled/disabled (E/D) rACTIVE = runtime active (min/sec)')
4680
- print(' S = runtime status active/suspended (A/S) rSUSPEND = runtime suspend (min/sec)')
4681
- print(' U = runtime usage count')
4682
- print('---------------------------------------------------------------------------------------------')
4683
- print('DEVICE NAME A R S U C rACTIVE rSUSPEND')
4684
- print('---------------------------------------------------------------------------------------------')
5383
+ pprint('LEGEND\n'\
5384
+ '---------------------------------------------------------------------------------------------\n'\
5385
+ ' A = async/sync PM queue (A/S) C = runtime active children\n'\
5386
+ ' R = runtime suspend enabled/disabled (E/D) rACTIVE = runtime active (min/sec)\n'\
5387
+ ' S = runtime status active/suspended (A/S) rSUSPEND = runtime suspend (min/sec)\n'\
5388
+ ' U = runtime usage count\n'\
5389
+ '---------------------------------------------------------------------------------------------\n'\
5390
+ 'DEVICE NAME A R S U C rACTIVE rSUSPEND\n'\
5391
+ '---------------------------------------------------------------------------------------------')
46855392
46865393 res = []
46875394 tgtval = 'runtime_status'
....@@ -4723,129 +5430,8 @@
47235430 ms2nice(power['runtime_active_time']), \
47245431 ms2nice(power['runtime_suspended_time']))
47255432 for i in sorted(lines):
4726
- print lines[i]
5433
+ print(lines[i])
47275434 return res
4728
-
4729
-# Function: devProps
4730
-# Description:
4731
-# Retrieve a list of properties for all devices in the trace log
4732
-def devProps(data=0):
4733
- props = dict()
4734
-
4735
- if data:
4736
- idx = data.index(': ') + 2
4737
- if idx >= len(data):
4738
- return
4739
- devlist = data[idx:].split(';')
4740
- for dev in devlist:
4741
- f = dev.split(',')
4742
- if len(f) < 3:
4743
- continue
4744
- dev = f[0]
4745
- props[dev] = DevProps()
4746
- props[dev].altname = f[1]
4747
- if int(f[2]):
4748
- props[dev].async = True
4749
- else:
4750
- props[dev].async = False
4751
- sysvals.devprops = props
4752
- if sysvals.suspendmode == 'command' and 'testcommandstring' in props:
4753
- sysvals.testcommand = props['testcommandstring'].altname
4754
- return
4755
-
4756
- if(os.path.exists(sysvals.ftracefile) == False):
4757
- doError('%s does not exist' % sysvals.ftracefile)
4758
-
4759
- # first get the list of devices we need properties for
4760
- msghead = 'Additional data added by AnalyzeSuspend'
4761
- alreadystamped = False
4762
- tp = TestProps()
4763
- tf = sysvals.openlog(sysvals.ftracefile, 'r')
4764
- for line in tf:
4765
- if msghead in line:
4766
- alreadystamped = True
4767
- continue
4768
- # determine the trace data type (required for further parsing)
4769
- m = re.match(sysvals.tracertypefmt, line)
4770
- if(m):
4771
- tp.setTracerType(m.group('t'))
4772
- continue
4773
- # parse only valid lines, if this is not one move on
4774
- m = re.match(tp.ftrace_line_fmt, line)
4775
- if(not m or 'device_pm_callback_start' not in line):
4776
- continue
4777
- m = re.match('.*: (?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*', m.group('msg'));
4778
- if(not m):
4779
- continue
4780
- dev = m.group('d')
4781
- if dev not in props:
4782
- props[dev] = DevProps()
4783
- tf.close()
4784
-
4785
- if not alreadystamped and sysvals.suspendmode == 'command':
4786
- out = '#\n# '+msghead+'\n# Device Properties: '
4787
- out += 'testcommandstring,%s,0;' % (sysvals.testcommand)
4788
- with sysvals.openlog(sysvals.ftracefile, 'a') as fp:
4789
- fp.write(out+'\n')
4790
- sysvals.devprops = props
4791
- return
4792
-
4793
- # now get the syspath for each of our target devices
4794
- for dirname, dirnames, filenames in os.walk('/sys/devices'):
4795
- if(re.match('.*/power', dirname) and 'async' in filenames):
4796
- dev = dirname.split('/')[-2]
4797
- if dev in props and (not props[dev].syspath or len(dirname) < len(props[dev].syspath)):
4798
- props[dev].syspath = dirname[:-6]
4799
-
4800
- # now fill in the properties for our target devices
4801
- for dev in props:
4802
- dirname = props[dev].syspath
4803
- if not dirname or not os.path.exists(dirname):
4804
- continue
4805
- with open(dirname+'/power/async') as fp:
4806
- text = fp.read()
4807
- props[dev].async = False
4808
- if 'enabled' in text:
4809
- props[dev].async = True
4810
- fields = os.listdir(dirname)
4811
- if 'product' in fields:
4812
- with open(dirname+'/product') as fp:
4813
- props[dev].altname = fp.read()
4814
- elif 'name' in fields:
4815
- with open(dirname+'/name') as fp:
4816
- props[dev].altname = fp.read()
4817
- elif 'model' in fields:
4818
- with open(dirname+'/model') as fp:
4819
- props[dev].altname = fp.read()
4820
- elif 'description' in fields:
4821
- with open(dirname+'/description') as fp:
4822
- props[dev].altname = fp.read()
4823
- elif 'id' in fields:
4824
- with open(dirname+'/id') as fp:
4825
- props[dev].altname = fp.read()
4826
- elif 'idVendor' in fields and 'idProduct' in fields:
4827
- idv, idp = '', ''
4828
- with open(dirname+'/idVendor') as fp:
4829
- idv = fp.read().strip()
4830
- with open(dirname+'/idProduct') as fp:
4831
- idp = fp.read().strip()
4832
- props[dev].altname = '%s:%s' % (idv, idp)
4833
-
4834
- if props[dev].altname:
4835
- out = props[dev].altname.strip().replace('\n', ' ')
4836
- out = out.replace(',', ' ')
4837
- out = out.replace(';', ' ')
4838
- props[dev].altname = out
4839
-
4840
- # and now write the data to the ftrace file
4841
- if not alreadystamped:
4842
- out = '#\n# '+msghead+'\n# Device Properties: '
4843
- for dev in sorted(props):
4844
- out += props[dev].out(dev)
4845
- with sysvals.openlog(sysvals.ftracefile, 'a') as fp:
4846
- fp.write(out+'\n')
4847
-
4848
- sysvals.devprops = props
48495435
48505436 # Function: getModes
48515437 # Description:
....@@ -4856,12 +5442,12 @@
48565442 modes = []
48575443 if(os.path.exists(sysvals.powerfile)):
48585444 fp = open(sysvals.powerfile, 'r')
4859
- modes = string.split(fp.read())
5445
+ modes = fp.read().split()
48605446 fp.close()
48615447 if(os.path.exists(sysvals.mempowerfile)):
48625448 deep = False
48635449 fp = open(sysvals.mempowerfile, 'r')
4864
- for m in string.split(fp.read()):
5450
+ for m in fp.read().split():
48655451 memmode = m.strip('[]')
48665452 if memmode == 'deep':
48675453 deep = True
....@@ -4870,6 +5456,11 @@
48705456 fp.close()
48715457 if 'mem' in modes and not deep:
48725458 modes.remove('mem')
5459
+ if('disk' in modes and os.path.exists(sysvals.diskpowerfile)):
5460
+ fp = open(sysvals.diskpowerfile, 'r')
5461
+ for m in fp.read().split():
5462
+ modes.append('disk-%s' % m.strip('[]'))
5463
+ fp.close()
48735464 return modes
48745465
48755466 # Function: dmidecode
....@@ -4930,25 +5521,26 @@
49305521 continue
49315522
49325523 # read in the memory for scanning
4933
- fp = open(mempath, 'rb')
49345524 try:
5525
+ fp = open(mempath, 'rb')
49355526 fp.seek(memaddr)
49365527 buf = fp.read(memsize)
49375528 except:
49385529 if(fatal):
49395530 doError('DMI table is unreachable, sorry')
49405531 else:
5532
+ pprint('WARNING: /dev/mem is not readable, ignoring DMI data')
49415533 return out
49425534 fp.close()
49435535
49445536 # search for either an SM table or DMI table
49455537 i = base = length = num = 0
49465538 while(i < memsize):
4947
- if buf[i:i+4] == '_SM_' and i < memsize - 16:
5539
+ if buf[i:i+4] == b'_SM_' and i < memsize - 16:
49485540 length = struct.unpack('H', buf[i+22:i+24])[0]
49495541 base, num = struct.unpack('IH', buf[i+24:i+30])
49505542 break
4951
- elif buf[i:i+5] == '_DMI_':
5543
+ elif buf[i:i+5] == b'_DMI_':
49525544 length = struct.unpack('H', buf[i+6:i+8])[0]
49535545 base, num = struct.unpack('IH', buf[i+8:i+14])
49545546 break
....@@ -4960,14 +5552,15 @@
49605552 return out
49615553
49625554 # read in the SM or DMI table
4963
- fp = open(mempath, 'rb')
49645555 try:
5556
+ fp = open(mempath, 'rb')
49655557 fp.seek(base)
49665558 buf = fp.read(length)
49675559 except:
49685560 if(fatal):
49695561 doError('DMI table is unreachable, sorry')
49705562 else:
5563
+ pprint('WARNING: /dev/mem is not readable, ignoring DMI data')
49715564 return out
49725565 fp.close()
49735566
....@@ -4980,37 +5573,51 @@
49805573 if 0 == struct.unpack('H', buf[n:n+2])[0]:
49815574 break
49825575 n += 1
4983
- data = buf[i+size:n+2].split('\0')
5576
+ data = buf[i+size:n+2].split(b'\0')
49845577 for name in info:
49855578 itype, idxadr = info[name]
49865579 if itype == type:
4987
- idx = struct.unpack('B', buf[i+idxadr])[0]
5580
+ idx = struct.unpack('B', buf[i+idxadr:i+idxadr+1])[0]
49885581 if idx > 0 and idx < len(data) - 1:
4989
- s = data[idx-1].strip()
4990
- if s and s.lower() != 'to be filled by o.e.m.':
4991
- out[name] = data[idx-1]
5582
+ s = data[idx-1].decode('utf-8')
5583
+ if s.strip() and s.strip().lower() != 'to be filled by o.e.m.':
5584
+ out[name] = s
49925585 i = n + 2
49935586 count += 1
49945587 return out
49955588
4996
-def getBattery():
4997
- p = '/sys/class/power_supply'
4998
- bat = dict()
4999
- for d in os.listdir(p):
5000
- type = sysvals.getVal(os.path.join(p, d, 'type')).strip().lower()
5001
- if type != 'battery':
5002
- continue
5003
- for v in ['status', 'energy_now', 'capacity_now']:
5004
- bat[v] = sysvals.getVal(os.path.join(p, d, v)).strip().lower()
5005
- break
5006
- ac = True
5007
- if 'status' in bat and 'discharging' in bat['status']:
5008
- ac = False
5009
- charge = 0
5010
- for v in ['energy_now', 'capacity_now']:
5011
- if v in bat and bat[v]:
5012
- charge = int(bat[v])
5013
- return (ac, charge)
5589
+def displayControl(cmd):
5590
+ xset, ret = 'timeout 10 xset -d :0.0 {0}', 0
5591
+ if sysvals.sudouser:
5592
+ xset = 'sudo -u %s %s' % (sysvals.sudouser, xset)
5593
+ if cmd == 'init':
5594
+ ret = call(xset.format('dpms 0 0 0'), shell=True)
5595
+ if not ret:
5596
+ ret = call(xset.format('s off'), shell=True)
5597
+ elif cmd == 'reset':
5598
+ ret = call(xset.format('s reset'), shell=True)
5599
+ elif cmd in ['on', 'off', 'standby', 'suspend']:
5600
+ b4 = displayControl('stat')
5601
+ ret = call(xset.format('dpms force %s' % cmd), shell=True)
5602
+ if not ret:
5603
+ curr = displayControl('stat')
5604
+ sysvals.vprint('Display Switched: %s -> %s' % (b4, curr))
5605
+ if curr != cmd:
5606
+ sysvals.vprint('WARNING: Display failed to change to %s' % cmd)
5607
+ if ret:
5608
+ sysvals.vprint('WARNING: Display failed to change to %s with xset' % cmd)
5609
+ return ret
5610
+ elif cmd == 'stat':
5611
+ fp = Popen(xset.format('q').split(' '), stdout=PIPE).stdout
5612
+ ret = 'unknown'
5613
+ for line in fp:
5614
+ m = re.match('[\s]*Monitor is (?P<m>.*)', ascii(line))
5615
+ if(m and len(m.group('m')) >= 2):
5616
+ out = m.group('m').lower()
5617
+ ret = out[3:] if out[0:2] == 'in' else out
5618
+ break
5619
+ fp.close()
5620
+ return ret
50145621
50155622 # Function: getFPDT
50165623 # Description:
....@@ -5055,20 +5662,22 @@
50555662
50565663 table = struct.unpack('4sIBB6s8sI4sI', buf[0:36])
50575664 if(output):
5058
- print('')
5059
- print('Firmware Performance Data Table (%s)' % table[0])
5060
- print(' Signature : %s' % table[0])
5061
- print(' Table Length : %u' % table[1])
5062
- print(' Revision : %u' % table[2])
5063
- print(' Checksum : 0x%x' % table[3])
5064
- print(' OEM ID : %s' % table[4])
5065
- print(' OEM Table ID : %s' % table[5])
5066
- print(' OEM Revision : %u' % table[6])
5067
- print(' Creator ID : %s' % table[7])
5068
- print(' Creator Revision : 0x%x' % table[8])
5069
- print('')
5665
+ pprint('\n'\
5666
+ 'Firmware Performance Data Table (%s)\n'\
5667
+ ' Signature : %s\n'\
5668
+ ' Table Length : %u\n'\
5669
+ ' Revision : %u\n'\
5670
+ ' Checksum : 0x%x\n'\
5671
+ ' OEM ID : %s\n'\
5672
+ ' OEM Table ID : %s\n'\
5673
+ ' OEM Revision : %u\n'\
5674
+ ' Creator ID : %s\n'\
5675
+ ' Creator Revision : 0x%x\n'\
5676
+ '' % (ascii(table[0]), ascii(table[0]), table[1], table[2],
5677
+ table[3], ascii(table[4]), ascii(table[5]), table[6],
5678
+ ascii(table[7]), table[8]))
50705679
5071
- if(table[0] != 'FPDT'):
5680
+ if(table[0] != b'FPDT'):
50725681 if(output):
50735682 doError('Invalid FPDT table')
50745683 return False
....@@ -5077,7 +5686,11 @@
50775686 i = 0
50785687 fwData = [0, 0]
50795688 records = buf[36:]
5080
- fp = open(sysvals.mempath, 'rb')
5689
+ try:
5690
+ fp = open(sysvals.mempath, 'rb')
5691
+ except:
5692
+ pprint('WARNING: /dev/mem is not readable, ignoring the FPDT data')
5693
+ return False
50815694 while(i < len(records)):
50825695 header = struct.unpack('HBB', records[i:i+4])
50835696 if(header[0] not in rectype):
....@@ -5092,22 +5705,24 @@
50925705 first = fp.read(8)
50935706 except:
50945707 if(output):
5095
- print('Bad address 0x%x in %s' % (addr, sysvals.mempath))
5708
+ pprint('Bad address 0x%x in %s' % (addr, sysvals.mempath))
50965709 return [0, 0]
50975710 rechead = struct.unpack('4sI', first)
50985711 recdata = fp.read(rechead[1]-8)
5099
- if(rechead[0] == 'FBPT'):
5100
- record = struct.unpack('HBBIQQQQQ', recdata)
5712
+ if(rechead[0] == b'FBPT'):
5713
+ record = struct.unpack('HBBIQQQQQ', recdata[:48])
51015714 if(output):
5102
- print('%s (%s)' % (rectype[header[0]], rechead[0]))
5103
- print(' Reset END : %u ns' % record[4])
5104
- print(' OS Loader LoadImage Start : %u ns' % record[5])
5105
- print(' OS Loader StartImage Start : %u ns' % record[6])
5106
- print(' ExitBootServices Entry : %u ns' % record[7])
5107
- print(' ExitBootServices Exit : %u ns' % record[8])
5108
- elif(rechead[0] == 'S3PT'):
5715
+ pprint('%s (%s)\n'\
5716
+ ' Reset END : %u ns\n'\
5717
+ ' OS Loader LoadImage Start : %u ns\n'\
5718
+ ' OS Loader StartImage Start : %u ns\n'\
5719
+ ' ExitBootServices Entry : %u ns\n'\
5720
+ ' ExitBootServices Exit : %u ns'\
5721
+ '' % (rectype[header[0]], ascii(rechead[0]), record[4], record[5],
5722
+ record[6], record[7], record[8]))
5723
+ elif(rechead[0] == b'S3PT'):
51095724 if(output):
5110
- print('%s (%s)' % (rectype[header[0]], rechead[0]))
5725
+ pprint('%s (%s)' % (rectype[header[0]], ascii(rechead[0])))
51115726 j = 0
51125727 while(j < len(recdata)):
51135728 prechead = struct.unpack('HBB', recdata[j:j+4])
....@@ -5117,27 +5732,26 @@
51175732 record = struct.unpack('IIQQ', recdata[j:j+prechead[1]])
51185733 fwData[1] = record[2]
51195734 if(output):
5120
- print(' %s' % prectype[prechead[0]])
5121
- print(' Resume Count : %u' % \
5122
- record[1])
5123
- print(' FullResume : %u ns' % \
5124
- record[2])
5125
- print(' AverageResume : %u ns' % \
5126
- record[3])
5735
+ pprint(' %s\n'\
5736
+ ' Resume Count : %u\n'\
5737
+ ' FullResume : %u ns\n'\
5738
+ ' AverageResume : %u ns'\
5739
+ '' % (prectype[prechead[0]], record[1],
5740
+ record[2], record[3]))
51275741 elif(prechead[0] == 1):
51285742 record = struct.unpack('QQ', recdata[j+4:j+prechead[1]])
51295743 fwData[0] = record[1] - record[0]
51305744 if(output):
5131
- print(' %s' % prectype[prechead[0]])
5132
- print(' SuspendStart : %u ns' % \
5133
- record[0])
5134
- print(' SuspendEnd : %u ns' % \
5135
- record[1])
5136
- print(' SuspendTime : %u ns' % \
5137
- fwData[0])
5745
+ pprint(' %s\n'\
5746
+ ' SuspendStart : %u ns\n'\
5747
+ ' SuspendEnd : %u ns\n'\
5748
+ ' SuspendTime : %u ns'\
5749
+ '' % (prectype[prechead[0]], record[0],
5750
+ record[1], fwData[0]))
5751
+
51385752 j += prechead[1]
51395753 if(output):
5140
- print('')
5754
+ pprint('')
51415755 i += header[1]
51425756 fp.close()
51435757 return fwData
....@@ -5149,26 +5763,26 @@
51495763 # Output:
51505764 # True if the test will work, False if not
51515765 def statusCheck(probecheck=False):
5152
- status = True
5766
+ status = ''
51535767
5154
- print('Checking this system (%s)...' % platform.node())
5768
+ pprint('Checking this system (%s)...' % platform.node())
51555769
51565770 # check we have root access
51575771 res = sysvals.colorText('NO (No features of this tool will work!)')
51585772 if(sysvals.rootCheck(False)):
51595773 res = 'YES'
5160
- print(' have root access: %s' % res)
5774
+ pprint(' have root access: %s' % res)
51615775 if(res != 'YES'):
5162
- print(' Try running this script with sudo')
5163
- return False
5776
+ pprint(' Try running this script with sudo')
5777
+ return 'missing root access'
51645778
51655779 # check sysfs is mounted
51665780 res = sysvals.colorText('NO (No features of this tool will work!)')
51675781 if(os.path.exists(sysvals.powerfile)):
51685782 res = 'YES'
5169
- print(' is sysfs mounted: %s' % res)
5783
+ pprint(' is sysfs mounted: %s' % res)
51705784 if(res != 'YES'):
5171
- return False
5785
+ return 'sysfs is missing'
51725786
51735787 # check target mode is a valid mode
51745788 if sysvals.suspendmode != 'command':
....@@ -5177,11 +5791,11 @@
51775791 if(sysvals.suspendmode in modes):
51785792 res = 'YES'
51795793 else:
5180
- status = False
5181
- print(' is "%s" a valid power mode: %s' % (sysvals.suspendmode, res))
5794
+ status = '%s mode is not supported' % sysvals.suspendmode
5795
+ pprint(' is "%s" a valid power mode: %s' % (sysvals.suspendmode, res))
51825796 if(res == 'NO'):
5183
- print(' valid power modes are: %s' % modes)
5184
- print(' please choose one with -m')
5797
+ pprint(' valid power modes are: %s' % modes)
5798
+ pprint(' please choose one with -m')
51855799
51865800 # check if ftrace is available
51875801 res = sysvals.colorText('NO')
....@@ -5189,17 +5803,18 @@
51895803 if(ftgood):
51905804 res = 'YES'
51915805 elif(sysvals.usecallgraph):
5192
- status = False
5193
- print(' is ftrace supported: %s' % res)
5806
+ status = 'ftrace is not properly supported'
5807
+ pprint(' is ftrace supported: %s' % res)
51945808
51955809 # check if kprobes are available
5196
- res = sysvals.colorText('NO')
5197
- sysvals.usekprobes = sysvals.verifyKprobes()
5198
- if(sysvals.usekprobes):
5199
- res = 'YES'
5200
- else:
5201
- sysvals.usedevsrc = False
5202
- print(' are kprobes supported: %s' % res)
5810
+ if sysvals.usekprobes:
5811
+ res = sysvals.colorText('NO')
5812
+ sysvals.usekprobes = sysvals.verifyKprobes()
5813
+ if(sysvals.usekprobes):
5814
+ res = 'YES'
5815
+ else:
5816
+ sysvals.usedevsrc = False
5817
+ pprint(' are kprobes supported: %s' % res)
52035818
52045819 # what data source are we using
52055820 res = 'DMESG'
....@@ -5210,15 +5825,26 @@
52105825 sysvals.usetraceevents = False
52115826 if(sysvals.usetraceevents):
52125827 res = 'FTRACE (all trace events found)'
5213
- print(' timeline data source: %s' % res)
5828
+ pprint(' timeline data source: %s' % res)
52145829
52155830 # check if rtcwake
52165831 res = sysvals.colorText('NO')
52175832 if(sysvals.rtcpath != ''):
52185833 res = 'YES'
52195834 elif(sysvals.rtcwake):
5220
- status = False
5221
- print(' is rtcwake supported: %s' % res)
5835
+ status = 'rtcwake is not properly supported'
5836
+ pprint(' is rtcwake supported: %s' % res)
5837
+
5838
+ # check info commands
5839
+ pprint(' optional commands this tool may use for info:')
5840
+ no = sysvals.colorText('MISSING')
5841
+ yes = sysvals.colorText('FOUND', 32)
5842
+ for c in ['turbostat', 'mcelog', 'lspci', 'lsusb']:
5843
+ if c == 'turbostat':
5844
+ res = yes if sysvals.haveTurbostat() else no
5845
+ else:
5846
+ res = yes if sysvals.getExec(c) else no
5847
+ pprint(' %s: %s' % (c, res))
52225848
52235849 if not probecheck:
52245850 return status
....@@ -5243,9 +5869,9 @@
52435869 def doError(msg, help=False):
52445870 if(help == True):
52455871 printHelp()
5246
- print('ERROR: %s\n') % msg
5872
+ pprint('ERROR: %s\n' % msg)
52475873 sysvals.outputResult({'error':msg})
5248
- sys.exit()
5874
+ sys.exit(1)
52495875
52505876 # Function: getArgInt
52515877 # Description:
....@@ -5253,7 +5879,7 @@
52535879 def getArgInt(name, args, min, max, main=True):
52545880 if main:
52555881 try:
5256
- arg = args.next()
5882
+ arg = next(args)
52575883 except:
52585884 doError(name+': no argument supplied', True)
52595885 else:
....@@ -5272,7 +5898,7 @@
52725898 def getArgFloat(name, args, min, max, main=True):
52735899 if main:
52745900 try:
5275
- arg = args.next()
5901
+ arg = next(args)
52765902 except:
52775903 doError(name+': no argument supplied', True)
52785904 else:
....@@ -5285,8 +5911,11 @@
52855911 doError(name+': value should be between %f and %f' % (min, max), True)
52865912 return val
52875913
5288
-def processData(live=False):
5289
- print('PROCESSING DATA')
5914
+def processData(live=False, quiet=False):
5915
+ if not quiet:
5916
+ pprint('PROCESSING: %s' % sysvals.htmlfile)
5917
+ sysvals.vprint('usetraceevents=%s, usetracemarkers=%s, usekprobes=%s' % \
5918
+ (sysvals.usetraceevents, sysvals.usetracemarkers, sysvals.usekprobes))
52905919 error = ''
52915920 if(sysvals.usetraceevents):
52925921 testruns, error = parseTraceLog(live)
....@@ -5299,18 +5928,44 @@
52995928 parseKernelLog(data)
53005929 if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)):
53015930 appendIncompleteTraceLog(testruns)
5931
+ if not sysvals.stamp:
5932
+ pprint('ERROR: data does not include the expected stamp')
5933
+ return (testruns, {'error': 'timeline generation failed'})
5934
+ shown = ['bios', 'biosdate', 'cpu', 'host', 'kernel', 'man', 'memfr',
5935
+ 'memsz', 'mode', 'numcpu', 'plat', 'time', 'wifi']
5936
+ sysvals.vprint('System Info:')
5937
+ for key in sorted(sysvals.stamp):
5938
+ if key in shown:
5939
+ sysvals.vprint(' %-8s : %s' % (key.upper(), sysvals.stamp[key]))
53025940 sysvals.vprint('Command:\n %s' % sysvals.cmdline)
53035941 for data in testruns:
5942
+ if data.turbostat:
5943
+ idx, s = 0, 'Turbostat:\n '
5944
+ for val in data.turbostat.split('|'):
5945
+ idx += len(val) + 1
5946
+ if idx >= 80:
5947
+ idx = 0
5948
+ s += '\n '
5949
+ s += val + ' '
5950
+ sysvals.vprint(s)
53045951 data.printDetails()
5952
+ if len(sysvals.platinfo) > 0:
5953
+ sysvals.vprint('\nPlatform Info:')
5954
+ for info in sysvals.platinfo:
5955
+ sysvals.vprint('[%s - %s]' % (info[0], info[1]))
5956
+ sysvals.vprint(info[2])
5957
+ sysvals.vprint('')
53055958 if sysvals.cgdump:
53065959 for data in testruns:
53075960 data.debugPrint()
5308
- sys.exit()
5961
+ sys.exit(0)
53095962 if len(testruns) < 1:
5963
+ pprint('ERROR: Not enough test data to build a timeline')
53105964 return (testruns, {'error': 'timeline generation failed'})
53115965 sysvals.vprint('Creating the html timeline (%s)...' % sysvals.htmlfile)
53125966 createHTML(testruns, error)
5313
- print('DONE')
5967
+ if not quiet:
5968
+ pprint('DONE: %s' % sysvals.htmlfile)
53145969 data = testruns[0]
53155970 stamp = data.stamp
53165971 stamp['suspend'], stamp['resume'] = data.getTimeValues()
....@@ -5323,126 +5978,255 @@
53235978 # Function: rerunTest
53245979 # Description:
53255980 # generate an output from an existing set of ftrace/dmesg logs
5326
-def rerunTest():
5981
+def rerunTest(htmlfile=''):
53275982 if sysvals.ftracefile:
53285983 doesTraceLogHaveTraceEvents()
53295984 if not sysvals.dmesgfile and not sysvals.usetraceevents:
53305985 doError('recreating this html output requires a dmesg file')
5331
- sysvals.setOutputFile()
5986
+ if htmlfile:
5987
+ sysvals.htmlfile = htmlfile
5988
+ else:
5989
+ sysvals.setOutputFile()
53325990 if os.path.exists(sysvals.htmlfile):
53335991 if not os.path.isfile(sysvals.htmlfile):
53345992 doError('a directory already exists with this name: %s' % sysvals.htmlfile)
53355993 elif not os.access(sysvals.htmlfile, os.W_OK):
53365994 doError('missing permission to write to %s' % sysvals.htmlfile)
5337
- testruns, stamp = processData(False)
5995
+ testruns, stamp = processData()
5996
+ sysvals.resetlog()
53385997 return stamp
53395998
53405999 # Function: runTest
53416000 # Description:
53426001 # execute a suspend/resume, gather the logs, and generate the output
5343
-def runTest(n=0):
6002
+def runTest(n=0, quiet=False):
53446003 # prepare for the test
5345
- sysvals.initFtrace()
6004
+ sysvals.initFtrace(quiet)
53466005 sysvals.initTestOutput('suspend')
53476006
53486007 # execute the test
5349
- executeSuspend()
6008
+ executeSuspend(quiet)
53506009 sysvals.cleanupFtrace()
53516010 if sysvals.skiphtml:
5352
- sysvals.sudouser(sysvals.testdir)
6011
+ sysvals.outputResult({}, n)
6012
+ sysvals.sudoUserchown(sysvals.testdir)
53536013 return
5354
- testruns, stamp = processData(True)
6014
+ testruns, stamp = processData(True, quiet)
53556015 for data in testruns:
53566016 del data
5357
- sysvals.sudouser(sysvals.testdir)
6017
+ sysvals.sudoUserchown(sysvals.testdir)
53586018 sysvals.outputResult(stamp, n)
6019
+ if 'error' in stamp:
6020
+ return 2
6021
+ return 0
53596022
53606023 def find_in_html(html, start, end, firstonly=True):
5361
- n, out = 0, []
5362
- while n < len(html):
5363
- m = re.search(start, html[n:])
6024
+ cnt, out, list = len(html), [], []
6025
+ if firstonly:
6026
+ m = re.search(start, html)
6027
+ if m:
6028
+ list.append(m)
6029
+ else:
6030
+ list = re.finditer(start, html)
6031
+ for match in list:
6032
+ s = match.end()
6033
+ e = cnt if (len(out) < 1 or s + 10000 > cnt) else s + 10000
6034
+ m = re.search(end, html[s:e])
53646035 if not m:
53656036 break
5366
- i = m.end()
5367
- m = re.search(end, html[n+i:])
5368
- if not m:
5369
- break
5370
- j = m.start()
5371
- str = html[n+i:n+i+j]
6037
+ e = s + m.start()
6038
+ str = html[s:e]
53726039 if end == 'ms':
53736040 num = re.search(r'[-+]?\d*\.\d+|\d+', str)
53746041 str = num.group() if num else 'NaN'
53756042 if firstonly:
53766043 return str
53776044 out.append(str)
5378
- n += i+j
53796045 if firstonly:
53806046 return ''
53816047 return out
6048
+
6049
+def data_from_html(file, outpath, issues, fulldetail=False):
6050
+ html = open(file, 'r').read()
6051
+ sysvals.htmlfile = os.path.relpath(file, outpath)
6052
+ # extract general info
6053
+ suspend = find_in_html(html, 'Kernel Suspend', 'ms')
6054
+ resume = find_in_html(html, 'Kernel Resume', 'ms')
6055
+ sysinfo = find_in_html(html, '<div class="stamp sysinfo">', '</div>')
6056
+ line = find_in_html(html, '<div class="stamp">', '</div>')
6057
+ stmp = line.split()
6058
+ if not suspend or not resume or len(stmp) != 8:
6059
+ return False
6060
+ try:
6061
+ dt = datetime.strptime(' '.join(stmp[3:]), '%B %d %Y, %I:%M:%S %p')
6062
+ except:
6063
+ return False
6064
+ sysvals.hostname = stmp[0]
6065
+ tstr = dt.strftime('%Y/%m/%d %H:%M:%S')
6066
+ error = find_in_html(html, '<table class="testfail"><tr><td>', '</td>')
6067
+ if error:
6068
+ m = re.match('[a-z0-9]* failed in (?P<p>\S*).*', error)
6069
+ if m:
6070
+ result = 'fail in %s' % m.group('p')
6071
+ else:
6072
+ result = 'fail'
6073
+ else:
6074
+ result = 'pass'
6075
+ # extract error info
6076
+ tp, ilist = False, []
6077
+ extra = dict()
6078
+ log = find_in_html(html, '<div id="dmesglog" style="display:none;">',
6079
+ '</div>').strip()
6080
+ if log:
6081
+ d = Data(0)
6082
+ d.end = 999999999
6083
+ d.dmesgtext = log.split('\n')
6084
+ tp = d.extractErrorInfo()
6085
+ for msg in tp.msglist:
6086
+ sysvals.errorSummary(issues, msg)
6087
+ if stmp[2] == 'freeze':
6088
+ extra = d.turbostatInfo()
6089
+ elist = dict()
6090
+ for dir in d.errorinfo:
6091
+ for err in d.errorinfo[dir]:
6092
+ if err[0] not in elist:
6093
+ elist[err[0]] = 0
6094
+ elist[err[0]] += 1
6095
+ for i in elist:
6096
+ ilist.append('%sx%d' % (i, elist[i]) if elist[i] > 1 else i)
6097
+ wifi = find_in_html(html, 'Wifi Resume: ', '</td>')
6098
+ if wifi:
6099
+ extra['wifi'] = wifi
6100
+ low = find_in_html(html, 'freeze time: <b>', ' ms</b>')
6101
+ if low and 'waking' in low:
6102
+ issue = 'FREEZEWAKE'
6103
+ match = [i for i in issues if i['match'] == issue]
6104
+ if len(match) > 0:
6105
+ match[0]['count'] += 1
6106
+ if sysvals.hostname not in match[0]['urls']:
6107
+ match[0]['urls'][sysvals.hostname] = [sysvals.htmlfile]
6108
+ elif sysvals.htmlfile not in match[0]['urls'][sysvals.hostname]:
6109
+ match[0]['urls'][sysvals.hostname].append(sysvals.htmlfile)
6110
+ else:
6111
+ issues.append({
6112
+ 'match': issue, 'count': 1, 'line': issue,
6113
+ 'urls': {sysvals.hostname: [sysvals.htmlfile]},
6114
+ })
6115
+ ilist.append(issue)
6116
+ # extract device info
6117
+ devices = dict()
6118
+ for line in html.split('\n'):
6119
+ m = re.match(' *<div id=\"[a,0-9]*\" *title=\"(?P<title>.*)\" class=\"thread.*', line)
6120
+ if not m or 'thread kth' in line or 'thread sec' in line:
6121
+ continue
6122
+ m = re.match('(?P<n>.*) \((?P<t>[0-9,\.]*) ms\) (?P<p>.*)', m.group('title'))
6123
+ if not m:
6124
+ continue
6125
+ name, time, phase = m.group('n'), m.group('t'), m.group('p')
6126
+ if ' async' in name or ' sync' in name:
6127
+ name = ' '.join(name.split(' ')[:-1])
6128
+ if phase.startswith('suspend'):
6129
+ d = 'suspend'
6130
+ elif phase.startswith('resume'):
6131
+ d = 'resume'
6132
+ else:
6133
+ continue
6134
+ if d not in devices:
6135
+ devices[d] = dict()
6136
+ if name not in devices[d]:
6137
+ devices[d][name] = 0.0
6138
+ devices[d][name] += float(time)
6139
+ # create worst device info
6140
+ worst = dict()
6141
+ for d in ['suspend', 'resume']:
6142
+ worst[d] = {'name':'', 'time': 0.0}
6143
+ dev = devices[d] if d in devices else 0
6144
+ if dev and len(dev.keys()) > 0:
6145
+ n = sorted(dev, key=lambda k:(dev[k], k), reverse=True)[0]
6146
+ worst[d]['name'], worst[d]['time'] = n, dev[n]
6147
+ data = {
6148
+ 'mode': stmp[2],
6149
+ 'host': stmp[0],
6150
+ 'kernel': stmp[1],
6151
+ 'sysinfo': sysinfo,
6152
+ 'time': tstr,
6153
+ 'result': result,
6154
+ 'issues': ' '.join(ilist),
6155
+ 'suspend': suspend,
6156
+ 'resume': resume,
6157
+ 'devlist': devices,
6158
+ 'sus_worst': worst['suspend']['name'],
6159
+ 'sus_worsttime': worst['suspend']['time'],
6160
+ 'res_worst': worst['resume']['name'],
6161
+ 'res_worsttime': worst['resume']['time'],
6162
+ 'url': sysvals.htmlfile,
6163
+ }
6164
+ for key in extra:
6165
+ data[key] = extra[key]
6166
+ if fulldetail:
6167
+ data['funclist'] = find_in_html(html, '<div title="', '" class="traceevent"', False)
6168
+ if tp:
6169
+ for arg in ['-multi ', '-info ']:
6170
+ if arg in tp.cmdline:
6171
+ data['target'] = tp.cmdline[tp.cmdline.find(arg):].split()[1]
6172
+ break
6173
+ return data
6174
+
6175
+def genHtml(subdir, force=False):
6176
+ for dirname, dirnames, filenames in os.walk(subdir):
6177
+ sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = ''
6178
+ for filename in filenames:
6179
+ file = os.path.join(dirname, filename)
6180
+ if sysvals.usable(file):
6181
+ if(re.match('.*_dmesg.txt', filename)):
6182
+ sysvals.dmesgfile = file
6183
+ elif(re.match('.*_ftrace.txt', filename)):
6184
+ sysvals.ftracefile = file
6185
+ sysvals.setOutputFile()
6186
+ if (sysvals.dmesgfile or sysvals.ftracefile) and sysvals.htmlfile and \
6187
+ (force or not sysvals.usable(sysvals.htmlfile)):
6188
+ pprint('FTRACE: %s' % sysvals.ftracefile)
6189
+ if sysvals.dmesgfile:
6190
+ pprint('DMESG : %s' % sysvals.dmesgfile)
6191
+ rerunTest()
53826192
53836193 # Function: runSummary
53846194 # Description:
53856195 # create a summary of tests in a sub-directory
53866196 def runSummary(subdir, local=True, genhtml=False):
53876197 inpath = os.path.abspath(subdir)
5388
- outpath = inpath
5389
- if local:
5390
- outpath = os.path.abspath('.')
5391
- print('Generating a summary of folder "%s"' % inpath)
6198
+ outpath = os.path.abspath('.') if local else inpath
6199
+ pprint('Generating a summary of folder:\n %s' % inpath)
53926200 if genhtml:
5393
- for dirname, dirnames, filenames in os.walk(subdir):
5394
- sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = ''
5395
- for filename in filenames:
5396
- if(re.match('.*_dmesg.txt', filename)):
5397
- sysvals.dmesgfile = os.path.join(dirname, filename)
5398
- elif(re.match('.*_ftrace.txt', filename)):
5399
- sysvals.ftracefile = os.path.join(dirname, filename)
5400
- sysvals.setOutputFile()
5401
- if sysvals.ftracefile and sysvals.htmlfile and \
5402
- not os.path.exists(sysvals.htmlfile):
5403
- print('FTRACE: %s' % sysvals.ftracefile)
5404
- if sysvals.dmesgfile:
5405
- print('DMESG : %s' % sysvals.dmesgfile)
5406
- rerunTest()
5407
- testruns = []
6201
+ genHtml(subdir)
6202
+ target, issues, testruns = '', [], []
6203
+ desc = {'host':[],'mode':[],'kernel':[]}
54086204 for dirname, dirnames, filenames in os.walk(subdir):
54096205 for filename in filenames:
54106206 if(not re.match('.*.html', filename)):
54116207 continue
5412
- file = os.path.join(dirname, filename)
5413
- html = open(file, 'r').read()
5414
- suspend = find_in_html(html, 'Kernel Suspend', 'ms')
5415
- resume = find_in_html(html, 'Kernel Resume', 'ms')
5416
- line = find_in_html(html, '<div class="stamp">', '</div>')
5417
- stmp = line.split()
5418
- if not suspend or not resume or len(stmp) != 8:
6208
+ data = data_from_html(os.path.join(dirname, filename), outpath, issues)
6209
+ if(not data):
54196210 continue
5420
- try:
5421
- dt = datetime.strptime(' '.join(stmp[3:]), '%B %d %Y, %I:%M:%S %p')
5422
- except:
5423
- continue
5424
- tstr = dt.strftime('%Y/%m/%d %H:%M:%S')
5425
- error = find_in_html(html, '<table class="testfail"><tr><td>', '</td>')
5426
- result = 'fail' if error else 'pass'
5427
- ilist = []
5428
- e = find_in_html(html, 'class="err"[\w=":;\.%\- ]*>', '&rarr;</div>', False)
5429
- for i in list(set(e)):
5430
- ilist.append('%sx%d' % (i, e.count(i)) if e.count(i) > 1 else i)
5431
- data = {
5432
- 'mode': stmp[2],
5433
- 'host': stmp[0],
5434
- 'kernel': stmp[1],
5435
- 'time': tstr,
5436
- 'result': result,
5437
- 'issues': ','.join(ilist),
5438
- 'suspend': suspend,
5439
- 'resume': resume,
5440
- 'url': os.path.relpath(file, outpath),
5441
- }
6211
+ if 'target' in data:
6212
+ target = data['target']
54426213 testruns.append(data)
5443
- outfile = os.path.join(outpath, 'summary.html')
5444
- print('Summary file: %s' % outfile)
5445
- createHTMLSummarySimple(testruns, outfile, inpath)
6214
+ for key in desc:
6215
+ if data[key] not in desc[key]:
6216
+ desc[key].append(data[key])
6217
+ pprint('Summary files:')
6218
+ if len(desc['host']) == len(desc['mode']) == len(desc['kernel']) == 1:
6219
+ title = '%s %s %s' % (desc['host'][0], desc['kernel'][0], desc['mode'][0])
6220
+ if target:
6221
+ title += ' %s' % target
6222
+ else:
6223
+ title = inpath
6224
+ createHTMLSummarySimple(testruns, os.path.join(outpath, 'summary.html'), title)
6225
+ pprint(' summary.html - tabular list of test data found')
6226
+ createHTMLDeviceSummary(testruns, os.path.join(outpath, 'summary-devices.html'), title)
6227
+ pprint(' summary-devices.html - kernel device list sorted by total execution time')
6228
+ createHTMLIssuesSummary(testruns, issues, os.path.join(outpath, 'summary-issues.html'), title)
6229
+ pprint(' summary-issues.html - kernel issues found sorted by frequency')
54466230
54476231 # Function: checkArgBool
54486232 # Description:
....@@ -5459,7 +6243,7 @@
54596243 # Description:
54606244 # Configure the script via the info in a config file
54616245 def configFromFile(file):
5462
- Config = ConfigParser.ConfigParser()
6246
+ Config = configparser.ConfigParser()
54636247
54646248 Config.read(file)
54656249 sections = Config.sections()
....@@ -5499,13 +6283,10 @@
54996283 else:
55006284 doError('invalid value --> (%s: %s), use "enable/disable"' % (option, value), True)
55016285 elif(option == 'display'):
5502
- if value in switchvalues:
5503
- if value in switchoff:
5504
- sysvals.display = -1
5505
- else:
5506
- sysvals.display = 1
5507
- else:
5508
- doError('invalid value --> (%s: %s), use "on/off"' % (option, value), True)
6286
+ disopt = ['on', 'off', 'standby', 'suspend']
6287
+ if value not in disopt:
6288
+ doError('invalid value --> (%s: %s), use %s' % (option, value, disopt), True)
6289
+ sysvals.display = value
55096290 elif(option == 'gzip'):
55106291 sysvals.gzip = checkArgBool(option, value)
55116292 elif(option == 'cgfilter'):
....@@ -5521,9 +6302,9 @@
55216302 sysvals.cgtest = getArgInt('cgtest', value, 0, 1, False)
55226303 elif(option == 'cgphase'):
55236304 d = Data(0)
5524
- if value not in d.phases:
6305
+ if value not in d.phasedef:
55256306 doError('invalid phase --> (%s: %s), valid phases are %s'\
5526
- % (option, value, d.phases), True)
6307
+ % (option, value, d.phasedef.keys()), True)
55276308 sysvals.cgphase = value
55286309 elif(option == 'fadd'):
55296310 file = sysvals.configFile(value)
....@@ -5536,9 +6317,7 @@
55366317 nums = value.split()
55376318 if len(nums) != 2:
55386319 doError('multi requires 2 integers (exec_count and delay)', True)
5539
- sysvals.multitest['run'] = True
5540
- sysvals.multitest['count'] = getArgInt('multi: n d (exec count)', nums[0], 2, 1000000, False)
5541
- sysvals.multitest['delay'] = getArgInt('multi: n d (delay between tests)', nums[1], 0, 3600, False)
6320
+ sysvals.multiinit(nums[0], nums[1])
55426321 elif(option == 'devicefilter'):
55436322 sysvals.setDeviceFilter(value)
55446323 elif(option == 'expandcg'):
....@@ -5660,84 +6439,92 @@
56606439 # Description:
56616440 # print out the help text
56626441 def printHelp():
5663
- print('')
5664
- print('%s v%s' % (sysvals.title, sysvals.version))
5665
- print('Usage: sudo sleepgraph <options> <commands>')
5666
- print('')
5667
- print('Description:')
5668
- print(' This tool is designed to assist kernel and OS developers in optimizing')
5669
- print(' their linux stack\'s suspend/resume time. Using a kernel image built')
5670
- print(' with a few extra options enabled, the tool will execute a suspend and')
5671
- print(' capture dmesg and ftrace data until resume is complete. This data is')
5672
- print(' transformed into a device timeline and an optional callgraph to give')
5673
- print(' a detailed view of which devices/subsystems are taking the most')
5674
- print(' time in suspend/resume.')
5675
- print('')
5676
- print(' If no specific command is given, the default behavior is to initiate')
5677
- print(' a suspend/resume and capture the dmesg/ftrace output as an html timeline.')
5678
- print('')
5679
- print(' Generates output files in subdirectory: suspend-yymmdd-HHMMSS')
5680
- print(' HTML output: <hostname>_<mode>.html')
5681
- print(' raw dmesg output: <hostname>_<mode>_dmesg.txt')
5682
- print(' raw ftrace output: <hostname>_<mode>_ftrace.txt')
5683
- print('')
5684
- print('Options:')
5685
- print(' -h Print this help text')
5686
- print(' -v Print the current tool version')
5687
- print(' -config fn Pull arguments and config options from file fn')
5688
- print(' -verbose Print extra information during execution and analysis')
5689
- print(' -m mode Mode to initiate for suspend (default: %s)') % (sysvals.suspendmode)
5690
- print(' -o name Overrides the output subdirectory name when running a new test')
5691
- print(' default: suspend-{date}-{time}')
5692
- print(' -rtcwake t Wakeup t seconds after suspend, set t to "off" to disable (default: 15)')
5693
- print(' -addlogs Add the dmesg and ftrace logs to the html output')
5694
- print(' -srgap Add a visible gap in the timeline between sus/res (default: disabled)')
5695
- print(' -skiphtml Run the test and capture the trace logs, but skip the timeline (default: disabled)')
5696
- print(' -result fn Export a results table to a text file for parsing.')
5697
- print(' [testprep]')
5698
- print(' -sync Sync the filesystems before starting the test')
5699
- print(' -rs on/off Enable/disable runtime suspend for all devices, restore all after test')
5700
- print(' -display on/off Turn the display on or off for the test')
5701
- print(' [advanced]')
5702
- print(' -gzip Gzip the trace and dmesg logs to save space')
5703
- print(' -cmd {s} Run the timeline over a custom command, e.g. "sync -d"')
5704
- print(' -proc Add usermode process info into the timeline (default: disabled)')
5705
- print(' -dev Add kernel function calls and threads to the timeline (default: disabled)')
5706
- print(' -x2 Run two suspend/resumes back to back (default: disabled)')
5707
- print(' -x2delay t Include t ms delay between multiple test runs (default: 0 ms)')
5708
- print(' -predelay t Include t ms delay before 1st suspend (default: 0 ms)')
5709
- print(' -postdelay t Include t ms delay after last resume (default: 0 ms)')
5710
- print(' -mindev ms Discard all device blocks shorter than ms milliseconds (e.g. 0.001 for us)')
5711
- print(' -multi n d Execute <n> consecutive tests at <d> seconds intervals. The outputs will')
5712
- print(' be created in a new subdirectory with a summary page.')
5713
- print(' [debug]')
5714
- print(' -f Use ftrace to create device callgraphs (default: disabled)')
5715
- print(' -maxdepth N limit the callgraph data to N call levels (default: 0=all)')
5716
- print(' -expandcg pre-expand the callgraph data in the html output (default: disabled)')
5717
- print(' -fadd file Add functions to be graphed in the timeline from a list in a text file')
5718
- print(' -filter "d1,d2,..." Filter out all but this comma-delimited list of device names')
5719
- print(' -mincg ms Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)')
5720
- print(' -cgphase P Only show callgraph data for phase P (e.g. suspend_late)')
5721
- print(' -cgtest N Only show callgraph data for test N (e.g. 0 or 1 in an x2 run)')
5722
- print(' -timeprec N Number of significant digits in timestamps (0:S, [3:ms], 6:us)')
5723
- print(' -cgfilter S Filter the callgraph output in the timeline')
5724
- print(' -cgskip file Callgraph functions to skip, off to disable (default: cgskip.txt)')
5725
- print(' -bufsize N Set trace buffer size to N kilo-bytes (default: all of free memory)')
5726
- print('')
5727
- print('Other commands:')
5728
- print(' -modes List available suspend modes')
5729
- print(' -status Test to see if the system is enabled to run this tool')
5730
- print(' -fpdt Print out the contents of the ACPI Firmware Performance Data Table')
5731
- print(' -battery Print out battery info (if available)')
5732
- print(' -sysinfo Print out system info extracted from BIOS')
5733
- print(' -devinfo Print out the pm settings of all devices which support runtime suspend')
5734
- print(' -flist Print the list of functions currently being captured in ftrace')
5735
- print(' -flistall Print all functions capable of being captured in ftrace')
5736
- print(' -summary dir Create a summary of tests in this dir [-genhtml builds missing html]')
5737
- print(' [redo]')
5738
- print(' -ftrace ftracefile Create HTML output using ftrace input (used with -dmesg)')
5739
- print(' -dmesg dmesgfile Create HTML output using dmesg (used with -ftrace)')
5740
- print('')
6442
+ pprint('\n%s v%s\n'\
6443
+ 'Usage: sudo sleepgraph <options> <commands>\n'\
6444
+ '\n'\
6445
+ 'Description:\n'\
6446
+ ' This tool is designed to assist kernel and OS developers in optimizing\n'\
6447
+ ' their linux stack\'s suspend/resume time. Using a kernel image built\n'\
6448
+ ' with a few extra options enabled, the tool will execute a suspend and\n'\
6449
+ ' capture dmesg and ftrace data until resume is complete. This data is\n'\
6450
+ ' transformed into a device timeline and an optional callgraph to give\n'\
6451
+ ' a detailed view of which devices/subsystems are taking the most\n'\
6452
+ ' time in suspend/resume.\n'\
6453
+ '\n'\
6454
+ ' If no specific command is given, the default behavior is to initiate\n'\
6455
+ ' a suspend/resume and capture the dmesg/ftrace output as an html timeline.\n'\
6456
+ '\n'\
6457
+ ' Generates output files in subdirectory: suspend-yymmdd-HHMMSS\n'\
6458
+ ' HTML output: <hostname>_<mode>.html\n'\
6459
+ ' raw dmesg output: <hostname>_<mode>_dmesg.txt\n'\
6460
+ ' raw ftrace output: <hostname>_<mode>_ftrace.txt\n'\
6461
+ '\n'\
6462
+ 'Options:\n'\
6463
+ ' -h Print this help text\n'\
6464
+ ' -v Print the current tool version\n'\
6465
+ ' -config fn Pull arguments and config options from file fn\n'\
6466
+ ' -verbose Print extra information during execution and analysis\n'\
6467
+ ' -m mode Mode to initiate for suspend (default: %s)\n'\
6468
+ ' -o name Overrides the output subdirectory name when running a new test\n'\
6469
+ ' default: suspend-{date}-{time}\n'\
6470
+ ' -rtcwake t Wakeup t seconds after suspend, set t to "off" to disable (default: 15)\n'\
6471
+ ' -addlogs Add the dmesg and ftrace logs to the html output\n'\
6472
+ ' -noturbostat Dont use turbostat in freeze mode (default: disabled)\n'\
6473
+ ' -srgap Add a visible gap in the timeline between sus/res (default: disabled)\n'\
6474
+ ' -skiphtml Run the test and capture the trace logs, but skip the timeline (default: disabled)\n'\
6475
+ ' -result fn Export a results table to a text file for parsing.\n'\
6476
+ ' -wifi If a wifi connection is available, check that it reconnects after resume.\n'\
6477
+ ' [testprep]\n'\
6478
+ ' -sync Sync the filesystems before starting the test\n'\
6479
+ ' -rs on/off Enable/disable runtime suspend for all devices, restore all after test\n'\
6480
+ ' -display m Change the display mode to m for the test (on/off/standby/suspend)\n'\
6481
+ ' [advanced]\n'\
6482
+ ' -gzip Gzip the trace and dmesg logs to save space\n'\
6483
+ ' -cmd {s} Run the timeline over a custom command, e.g. "sync -d"\n'\
6484
+ ' -proc Add usermode process info into the timeline (default: disabled)\n'\
6485
+ ' -dev Add kernel function calls and threads to the timeline (default: disabled)\n'\
6486
+ ' -x2 Run two suspend/resumes back to back (default: disabled)\n'\
6487
+ ' -x2delay t Include t ms delay between multiple test runs (default: 0 ms)\n'\
6488
+ ' -predelay t Include t ms delay before 1st suspend (default: 0 ms)\n'\
6489
+ ' -postdelay t Include t ms delay after last resume (default: 0 ms)\n'\
6490
+ ' -mindev ms Discard all device blocks shorter than ms milliseconds (e.g. 0.001 for us)\n'\
6491
+ ' -multi n d Execute <n> consecutive tests at <d> seconds intervals. If <n> is followed\n'\
6492
+ ' by a "d", "h", or "m" execute for <n> days, hours, or mins instead.\n'\
6493
+ ' The outputs will be created in a new subdirectory with a summary page.\n'\
6494
+ ' -maxfail n Abort a -multi run after n consecutive fails (default is 0 = never abort)\n'\
6495
+ ' [debug]\n'\
6496
+ ' -f Use ftrace to create device callgraphs (default: disabled)\n'\
6497
+ ' -ftop Use ftrace on the top level call: "%s" (default: disabled)\n'\
6498
+ ' -maxdepth N limit the callgraph data to N call levels (default: 0=all)\n'\
6499
+ ' -expandcg pre-expand the callgraph data in the html output (default: disabled)\n'\
6500
+ ' -fadd file Add functions to be graphed in the timeline from a list in a text file\n'\
6501
+ ' -filter "d1,d2,..." Filter out all but this comma-delimited list of device names\n'\
6502
+ ' -mincg ms Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)\n'\
6503
+ ' -cgphase P Only show callgraph data for phase P (e.g. suspend_late)\n'\
6504
+ ' -cgtest N Only show callgraph data for test N (e.g. 0 or 1 in an x2 run)\n'\
6505
+ ' -timeprec N Number of significant digits in timestamps (0:S, [3:ms], 6:us)\n'\
6506
+ ' -cgfilter S Filter the callgraph output in the timeline\n'\
6507
+ ' -cgskip file Callgraph functions to skip, off to disable (default: cgskip.txt)\n'\
6508
+ ' -bufsize N Set trace buffer size to N kilo-bytes (default: all of free memory)\n'\
6509
+ ' -devdump Print out all the raw device data for each phase\n'\
6510
+ ' -cgdump Print out all the raw callgraph data\n'\
6511
+ '\n'\
6512
+ 'Other commands:\n'\
6513
+ ' -modes List available suspend modes\n'\
6514
+ ' -status Test to see if the system is enabled to run this tool\n'\
6515
+ ' -fpdt Print out the contents of the ACPI Firmware Performance Data Table\n'\
6516
+ ' -wificheck Print out wifi connection info\n'\
6517
+ ' -x<mode> Test xset by toggling the given mode (on/off/standby/suspend)\n'\
6518
+ ' -sysinfo Print out system info extracted from BIOS\n'\
6519
+ ' -devinfo Print out the pm settings of all devices which support runtime suspend\n'\
6520
+ ' -cmdinfo Print out all the platform info collected before and after suspend/resume\n'\
6521
+ ' -flist Print the list of functions currently being captured in ftrace\n'\
6522
+ ' -flistall Print all functions capable of being captured in ftrace\n'\
6523
+ ' -summary dir Create a summary of tests in this dir [-genhtml builds missing html]\n'\
6524
+ ' [redo]\n'\
6525
+ ' -ftrace ftracefile Create HTML output using ftrace input (used with -dmesg)\n'\
6526
+ ' -dmesg dmesgfile Create HTML output using dmesg (used with -ftrace)\n'\
6527
+ '' % (sysvals.title, sysvals.version, sysvals.suspendmode, sysvals.ftopfunc))
57416528 return True
57426529
57436530 # ----------------- MAIN --------------------
....@@ -5745,7 +6532,9 @@
57456532 if __name__ == '__main__':
57466533 genhtml = False
57476534 cmd = ''
5748
- simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall', '-devinfo', '-status', '-battery']
6535
+ simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall',
6536
+ '-devinfo', '-status', '-xon', '-xoff', '-xstandby', '-xsuspend',
6537
+ '-xinit', '-xreset', '-xstat', '-wificheck', '-cmdinfo']
57496538 if '-f' in sys.argv:
57506539 sysvals.cgskip = sysvals.configFile('cgskip.txt')
57516540 # loop through the command line arguments
....@@ -5753,7 +6542,7 @@
57536542 for arg in args:
57546543 if(arg == '-m'):
57556544 try:
5756
- val = args.next()
6545
+ val = next(args)
57576546 except:
57586547 doError('No mode supplied', True)
57596548 if val == 'command' and not sysvals.testcommand:
....@@ -5763,10 +6552,10 @@
57636552 cmd = arg[1:]
57646553 elif(arg == '-h'):
57656554 printHelp()
5766
- sys.exit()
6555
+ sys.exit(0)
57676556 elif(arg == '-v'):
5768
- print("Version %s" % sysvals.version)
5769
- sys.exit()
6557
+ pprint("Version %s" % sysvals.version)
6558
+ sys.exit(0)
57706559 elif(arg == '-x2'):
57716560 sysvals.execcount = 2
57726561 elif(arg == '-x2delay'):
....@@ -5777,14 +6566,28 @@
57776566 sysvals.postdelay = getArgInt('-postdelay', args, 0, 60000)
57786567 elif(arg == '-f'):
57796568 sysvals.usecallgraph = True
6569
+ elif(arg == '-ftop'):
6570
+ sysvals.usecallgraph = True
6571
+ sysvals.ftop = True
6572
+ sysvals.usekprobes = False
57806573 elif(arg == '-skiphtml'):
57816574 sysvals.skiphtml = True
57826575 elif(arg == '-cgdump'):
57836576 sysvals.cgdump = True
6577
+ elif(arg == '-devdump'):
6578
+ sysvals.devdump = True
57846579 elif(arg == '-genhtml'):
57856580 genhtml = True
57866581 elif(arg == '-addlogs'):
57876582 sysvals.dmesglog = sysvals.ftracelog = True
6583
+ elif(arg == '-nologs'):
6584
+ sysvals.dmesglog = sysvals.ftracelog = False
6585
+ elif(arg == '-addlogdmesg'):
6586
+ sysvals.dmesglog = True
6587
+ elif(arg == '-addlogftrace'):
6588
+ sysvals.ftracelog = True
6589
+ elif(arg == '-noturbostat'):
6590
+ sysvals.tstat = False
57886591 elif(arg == '-verbose'):
57896592 sysvals.verbose = True
57906593 elif(arg == '-proc'):
....@@ -5793,11 +6596,18 @@
57936596 sysvals.usedevsrc = True
57946597 elif(arg == '-sync'):
57956598 sysvals.sync = True
6599
+ elif(arg == '-wifi'):
6600
+ sysvals.wifi = True
57966601 elif(arg == '-gzip'):
57976602 sysvals.gzip = True
6603
+ elif(arg == '-info'):
6604
+ try:
6605
+ val = next(args)
6606
+ except:
6607
+ doError('-info requires one string argument', True)
57986608 elif(arg == '-rs'):
57996609 try:
5800
- val = args.next()
6610
+ val = next(args)
58016611 except:
58026612 doError('-rs requires "enable" or "disable"', True)
58036613 if val.lower() in switchvalues:
....@@ -5809,21 +6619,18 @@
58096619 doError('invalid option: %s, use "enable/disable" or "on/off"' % val, True)
58106620 elif(arg == '-display'):
58116621 try:
5812
- val = args.next()
6622
+ val = next(args)
58136623 except:
5814
- doError('-display requires "on" or "off"', True)
5815
- if val.lower() in switchvalues:
5816
- if val.lower() in switchoff:
5817
- sysvals.display = -1
5818
- else:
5819
- sysvals.display = 1
5820
- else:
5821
- doError('invalid option: %s, use "on/off"' % val, True)
6624
+ doError('-display requires an mode value', True)
6625
+ disopt = ['on', 'off', 'standby', 'suspend']
6626
+ if val.lower() not in disopt:
6627
+ doError('valid display mode values are %s' % disopt, True)
6628
+ sysvals.display = val.lower()
58226629 elif(arg == '-maxdepth'):
58236630 sysvals.max_graph_depth = getArgInt('-maxdepth', args, 0, 1000)
58246631 elif(arg == '-rtcwake'):
58256632 try:
5826
- val = args.next()
6633
+ val = next(args)
58276634 except:
58286635 doError('No rtcwake time supplied', True)
58296636 if val.lower() in switchoff:
....@@ -5843,23 +6650,29 @@
58436650 sysvals.cgtest = getArgInt('-cgtest', args, 0, 1)
58446651 elif(arg == '-cgphase'):
58456652 try:
5846
- val = args.next()
6653
+ val = next(args)
58476654 except:
58486655 doError('No phase name supplied', True)
58496656 d = Data(0)
5850
- if val not in d.phases:
6657
+ if val not in d.phasedef:
58516658 doError('invalid phase --> (%s: %s), valid phases are %s'\
5852
- % (arg, val, d.phases), True)
6659
+ % (arg, val, d.phasedef.keys()), True)
58536660 sysvals.cgphase = val
58546661 elif(arg == '-cgfilter'):
58556662 try:
5856
- val = args.next()
6663
+ val = next(args)
58576664 except:
58586665 doError('No callgraph functions supplied', True)
58596666 sysvals.setCallgraphFilter(val)
6667
+ elif(arg == '-skipkprobe'):
6668
+ try:
6669
+ val = next(args)
6670
+ except:
6671
+ doError('No kprobe functions supplied', True)
6672
+ sysvals.skipKprobes(val)
58606673 elif(arg == '-cgskip'):
58616674 try:
5862
- val = args.next()
6675
+ val = next(args)
58636676 except:
58646677 doError('No file supplied', True)
58656678 if val.lower() in switchoff:
....@@ -5874,7 +6687,7 @@
58746687 sysvals.callloopmaxlen = getArgFloat('-callloop-maxlen', args, 0.0, 1.0)
58756688 elif(arg == '-cmd'):
58766689 try:
5877
- val = args.next()
6690
+ val = next(args)
58786691 except:
58796692 doError('No command string supplied', True)
58806693 sysvals.testcommand = val
....@@ -5883,19 +6696,23 @@
58836696 sysvals.cgexp = True
58846697 elif(arg == '-srgap'):
58856698 sysvals.srgap = 5
6699
+ elif(arg == '-maxfail'):
6700
+ sysvals.maxfail = getArgInt('-maxfail', args, 0, 1000000)
58866701 elif(arg == '-multi'):
5887
- sysvals.multitest['run'] = True
5888
- sysvals.multitest['count'] = getArgInt('-multi n d (exec count)', args, 2, 1000000)
5889
- sysvals.multitest['delay'] = getArgInt('-multi n d (delay between tests)', args, 0, 3600)
6702
+ try:
6703
+ c, d = next(args), next(args)
6704
+ except:
6705
+ doError('-multi requires two values', True)
6706
+ sysvals.multiinit(c, d)
58906707 elif(arg == '-o'):
58916708 try:
5892
- val = args.next()
6709
+ val = next(args)
58936710 except:
58946711 doError('No subdirectory name supplied', True)
58956712 sysvals.outdir = sysvals.setOutputFolder(val)
58966713 elif(arg == '-config'):
58976714 try:
5898
- val = args.next()
6715
+ val = next(args)
58996716 except:
59006717 doError('No text file supplied', True)
59016718 file = sysvals.configFile(val)
....@@ -5904,7 +6721,7 @@
59046721 configFromFile(file)
59056722 elif(arg == '-fadd'):
59066723 try:
5907
- val = args.next()
6724
+ val = next(args)
59086725 except:
59096726 doError('No text file supplied', True)
59106727 file = sysvals.configFile(val)
....@@ -5913,7 +6730,7 @@
59136730 sysvals.addFtraceFilterFunctions(file)
59146731 elif(arg == '-dmesg'):
59156732 try:
5916
- val = args.next()
6733
+ val = next(args)
59176734 except:
59186735 doError('No dmesg file supplied', True)
59196736 sysvals.notestrun = True
....@@ -5922,7 +6739,7 @@
59226739 doError('%s does not exist' % sysvals.dmesgfile)
59236740 elif(arg == '-ftrace'):
59246741 try:
5925
- val = args.next()
6742
+ val = next(args)
59266743 except:
59276744 doError('No ftrace file supplied', True)
59286745 sysvals.notestrun = True
....@@ -5931,7 +6748,7 @@
59316748 doError('%s does not exist' % sysvals.ftracefile)
59326749 elif(arg == '-summary'):
59336750 try:
5934
- val = args.next()
6751
+ val = next(args)
59356752 except:
59366753 doError('No directory supplied', True)
59376754 cmd = 'summary'
....@@ -5941,16 +6758,17 @@
59416758 doError('%s is not accesible' % val)
59426759 elif(arg == '-filter'):
59436760 try:
5944
- val = args.next()
6761
+ val = next(args)
59456762 except:
59466763 doError('No devnames supplied', True)
59476764 sysvals.setDeviceFilter(val)
59486765 elif(arg == '-result'):
59496766 try:
5950
- val = args.next()
6767
+ val = next(args)
59516768 except:
59526769 doError('No result file supplied', True)
59536770 sysvals.result = val
6771
+ sysvals.signalHandlerInit()
59546772 else:
59556773 doError('Invalid argument: '+arg, True)
59566774
....@@ -5975,43 +6793,56 @@
59756793
59766794 # just run a utility command and exit
59776795 if(cmd != ''):
6796
+ ret = 0
59786797 if(cmd == 'status'):
5979
- statusCheck(True)
6798
+ if not statusCheck(True):
6799
+ ret = 1
59806800 elif(cmd == 'fpdt'):
5981
- getFPDT(True)
5982
- elif(cmd == 'battery'):
5983
- print 'AC Connect: %s\nCharge: %d' % getBattery()
6801
+ if not getFPDT(True):
6802
+ ret = 1
59846803 elif(cmd == 'sysinfo'):
59856804 sysvals.printSystemInfo(True)
59866805 elif(cmd == 'devinfo'):
59876806 deviceInfo()
59886807 elif(cmd == 'modes'):
5989
- print getModes()
6808
+ pprint(getModes())
59906809 elif(cmd == 'flist'):
59916810 sysvals.getFtraceFilterFunctions(True)
59926811 elif(cmd == 'flistall'):
59936812 sysvals.getFtraceFilterFunctions(False)
59946813 elif(cmd == 'summary'):
59956814 runSummary(sysvals.outdir, True, genhtml)
5996
- sys.exit()
6815
+ elif(cmd in ['xon', 'xoff', 'xstandby', 'xsuspend', 'xinit', 'xreset']):
6816
+ sysvals.verbose = True
6817
+ ret = displayControl(cmd[1:])
6818
+ elif(cmd == 'xstat'):
6819
+ pprint('Display Status: %s' % displayControl('stat').upper())
6820
+ elif(cmd == 'wificheck'):
6821
+ dev = sysvals.checkWifi()
6822
+ if dev:
6823
+ print('%s is connected' % sysvals.wifiDetails(dev))
6824
+ else:
6825
+ print('No wifi connection found')
6826
+ elif(cmd == 'cmdinfo'):
6827
+ for out in sysvals.cmdinfo(False, True):
6828
+ print('[%s - %s]\n%s\n' % out)
6829
+ sys.exit(ret)
59976830
59986831 # if instructed, re-analyze existing data files
59996832 if(sysvals.notestrun):
6000
- stamp = rerunTest()
6833
+ stamp = rerunTest(sysvals.outdir)
60016834 sysvals.outputResult(stamp)
6002
- sys.exit()
6835
+ sys.exit(0)
60036836
60046837 # verify that we can run a test
6005
- if(not statusCheck()):
6006
- doError('Check FAILED, aborting the test run!')
6838
+ error = statusCheck()
6839
+ if(error):
6840
+ doError(error)
60076841
6008
- # extract mem modes and convert
6842
+ # extract mem/disk extra modes and convert
60096843 mode = sysvals.suspendmode
6010
- if 'mem' == mode[:3]:
6011
- if '-' in mode:
6012
- memmode = mode.split('-')[-1]
6013
- else:
6014
- memmode = 'deep'
6844
+ if mode.startswith('mem'):
6845
+ memmode = mode.split('-', 1)[-1] if '-' in mode else 'deep'
60156846 if memmode == 'shallow':
60166847 mode = 'standby'
60176848 elif memmode == 's2idle':
....@@ -6020,38 +6851,56 @@
60206851 mode = 'mem'
60216852 sysvals.memmode = memmode
60226853 sysvals.suspendmode = mode
6854
+ if mode.startswith('disk-'):
6855
+ sysvals.diskmode = mode.split('-', 1)[-1]
6856
+ sysvals.suspendmode = 'disk'
60236857
60246858 sysvals.systemInfo(dmidecode(sysvals.mempath))
60256859
60266860 setRuntimeSuspend(True)
60276861 if sysvals.display:
6028
- call('xset -d :0.0 dpms 0 0 0', shell=True)
6029
- call('xset -d :0.0 s off', shell=True)
6862
+ displayControl('init')
6863
+ failcnt, ret = 0, 0
60306864 if sysvals.multitest['run']:
60316865 # run multiple tests in a separate subdirectory
60326866 if not sysvals.outdir:
6033
- s = 'suspend-x%d' % sysvals.multitest['count']
6034
- sysvals.outdir = datetime.now().strftime(s+'-%y%m%d-%H%M%S')
6867
+ if 'time' in sysvals.multitest:
6868
+ s = '-%dm' % sysvals.multitest['time']
6869
+ else:
6870
+ s = '-x%d' % sysvals.multitest['count']
6871
+ sysvals.outdir = datetime.now().strftime('suspend-%y%m%d-%H%M%S'+s)
60356872 if not os.path.isdir(sysvals.outdir):
6036
- os.mkdir(sysvals.outdir)
6873
+ os.makedirs(sysvals.outdir)
6874
+ sysvals.sudoUserchown(sysvals.outdir)
6875
+ finish = datetime.now()
6876
+ if 'time' in sysvals.multitest:
6877
+ finish += timedelta(minutes=sysvals.multitest['time'])
60376878 for i in range(sysvals.multitest['count']):
6038
- if(i != 0):
6039
- print('Waiting %d seconds...' % (sysvals.multitest['delay']))
6879
+ sysvals.multistat(True, i, finish)
6880
+ if i != 0 and sysvals.multitest['delay'] > 0:
6881
+ pprint('Waiting %d seconds...' % (sysvals.multitest['delay']))
60406882 time.sleep(sysvals.multitest['delay'])
6041
- print('TEST (%d/%d) START' % (i+1, sysvals.multitest['count']))
60426883 fmt = 'suspend-%y%m%d-%H%M%S'
60436884 sysvals.testdir = os.path.join(sysvals.outdir, datetime.now().strftime(fmt))
6044
- runTest(i+1)
6045
- print('TEST (%d/%d) COMPLETE' % (i+1, sysvals.multitest['count']))
6046
- sysvals.logmsg = ''
6885
+ ret = runTest(i+1, True)
6886
+ failcnt = 0 if not ret else failcnt + 1
6887
+ if sysvals.maxfail > 0 and failcnt >= sysvals.maxfail:
6888
+ pprint('Maximum fail count of %d reached, aborting multitest' % (sysvals.maxfail))
6889
+ break
6890
+ time.sleep(5)
6891
+ sysvals.resetlog()
6892
+ sysvals.multistat(False, i, finish)
6893
+ if 'time' in sysvals.multitest and datetime.now() >= finish:
6894
+ break
60476895 if not sysvals.skiphtml:
60486896 runSummary(sysvals.outdir, False, False)
6049
- sysvals.sudouser(sysvals.outdir)
6897
+ sysvals.sudoUserchown(sysvals.outdir)
60506898 else:
60516899 if sysvals.outdir:
60526900 sysvals.testdir = sysvals.outdir
60536901 # run the test in the current directory
6054
- runTest()
6902
+ ret = runTest()
60556903 if sysvals.display:
6056
- call('xset -d :0.0 s reset', shell=True)
6904
+ displayControl('reset')
60576905 setRuntimeSuspend(False)
6906
+ sys.exit(ret)