aboutsummaryrefslogtreecommitdiffstats
path: root/tools/power/pm-graph/sleepgraph.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/power/pm-graph/sleepgraph.py')
-rwxr-xr-xtools/power/pm-graph/sleepgraph.py857
1 files changed, 657 insertions, 200 deletions
diff --git a/tools/power/pm-graph/sleepgraph.py b/tools/power/pm-graph/sleepgraph.py
index d1a88d05e976..4f46a7a1feb6 100755
--- a/tools/power/pm-graph/sleepgraph.py
+++ b/tools/power/pm-graph/sleepgraph.py
@@ -9,9 +9,9 @@
9# 9#
10# Links: 10# Links:
11# Home Page 11# Home Page
12# https://01.org/suspendresume 12# https://01.org/pm-graph
13# Source repo 13# Source repo
14# git@github.com:01org/pm-graph 14# git@github.com:intel/pm-graph
15# 15#
16# Description: 16# Description:
17# This tool is designed to assist kernel and OS developers in optimizing 17# This tool is designed to assist kernel and OS developers in optimizing
@@ -24,6 +24,7 @@
24# viewed in firefox or chrome. 24# viewed in firefox or chrome.
25# 25#
26# The following kernel build options are required: 26# The following kernel build options are required:
27# CONFIG_DEVMEM=y
27# CONFIG_PM_DEBUG=y 28# CONFIG_PM_DEBUG=y
28# CONFIG_PM_SLEEP_DEBUG=y 29# CONFIG_PM_SLEEP_DEBUG=y
29# CONFIG_FTRACE=y 30# CONFIG_FTRACE=y
@@ -53,6 +54,7 @@ import ConfigParser
53import gzip 54import gzip
54from threading import Thread 55from threading import Thread
55from subprocess import call, Popen, PIPE 56from subprocess import call, Popen, PIPE
57import base64
56 58
57def pprint(msg): 59def pprint(msg):
58 print(msg) 60 print(msg)
@@ -66,7 +68,7 @@ def pprint(msg):
66# store system values and test parameters 68# store system values and test parameters
67class SystemValues: 69class SystemValues:
68 title = 'SleepGraph' 70 title = 'SleepGraph'
69 version = '5.2' 71 version = '5.4'
70 ansi = False 72 ansi = False
71 rs = 0 73 rs = 0
72 display = '' 74 display = ''
@@ -74,8 +76,9 @@ class SystemValues:
74 sync = False 76 sync = False
75 verbose = False 77 verbose = False
76 testlog = True 78 testlog = True
77 dmesglog = False 79 dmesglog = True
78 ftracelog = False 80 ftracelog = False
81 tstat = False
79 mindevlen = 0.0 82 mindevlen = 0.0
80 mincglen = 0.0 83 mincglen = 0.0
81 cgphase = '' 84 cgphase = ''
@@ -99,6 +102,8 @@ class SystemValues:
99 pmdpath = '/sys/power/pm_debug_messages' 102 pmdpath = '/sys/power/pm_debug_messages'
100 traceevents = [ 103 traceevents = [
101 'suspend_resume', 104 'suspend_resume',
105 'wakeup_source_activate',
106 'wakeup_source_deactivate',
102 'device_pm_callback_end', 107 'device_pm_callback_end',
103 'device_pm_callback_start' 108 'device_pm_callback_start'
104 ] 109 ]
@@ -130,6 +135,8 @@ class SystemValues:
130 x2delay = 0 135 x2delay = 0
131 skiphtml = False 136 skiphtml = False
132 usecallgraph = False 137 usecallgraph = False
138 ftopfunc = 'suspend_devices_and_enter'
139 ftop = False
133 usetraceevents = False 140 usetraceevents = False
134 usetracemarkers = True 141 usetracemarkers = True
135 usekprobes = True 142 usekprobes = True
@@ -158,6 +165,13 @@ class SystemValues:
158 'acpi_hibernation_leave': {}, 165 'acpi_hibernation_leave': {},
159 'acpi_pm_freeze': {}, 166 'acpi_pm_freeze': {},
160 'acpi_pm_thaw': {}, 167 'acpi_pm_thaw': {},
168 'acpi_s2idle_end': {},
169 'acpi_s2idle_sync': {},
170 'acpi_s2idle_begin': {},
171 'acpi_s2idle_prepare': {},
172 'acpi_s2idle_wake': {},
173 'acpi_s2idle_wakeup': {},
174 'acpi_s2idle_restore': {},
161 'hibernate_preallocate_memory': {}, 175 'hibernate_preallocate_memory': {},
162 'create_basic_memory_bitmaps': {}, 176 'create_basic_memory_bitmaps': {},
163 'swsusp_write': {}, 177 'swsusp_write': {},
@@ -191,9 +205,14 @@ class SystemValues:
191 'usleep_range': { 'args_x86_64': {'min':'%di:s32', 'max':'%si:s32'}, 'ub': 1 }, 205 'usleep_range': { 'args_x86_64': {'min':'%di:s32', 'max':'%si:s32'}, 'ub': 1 },
192 'mutex_lock_slowpath': { 'func':'__mutex_lock_slowpath', 'ub': 1 }, 206 'mutex_lock_slowpath': { 'func':'__mutex_lock_slowpath', 'ub': 1 },
193 'acpi_os_stall': {'ub': 1}, 207 'acpi_os_stall': {'ub': 1},
208 'rt_mutex_slowlock': {'ub': 1},
194 # ACPI 209 # ACPI
195 'acpi_resume_power_resources': {}, 210 'acpi_resume_power_resources': {},
196 'acpi_ps_parse_aml': {}, 211 'acpi_ps_execute_method': { 'args_x86_64': {
212 'fullpath':'+0(+40(%di)):string',
213 }},
214 # mei_me
215 'mei_reset': {},
197 # filesystem 216 # filesystem
198 'ext4_sync_fs': {}, 217 'ext4_sync_fs': {},
199 # 80211 218 # 80211
@@ -242,6 +261,7 @@ class SystemValues:
242 timeformat = '%.3f' 261 timeformat = '%.3f'
243 cmdline = '%s %s' % \ 262 cmdline = '%s %s' % \
244 (os.path.basename(sys.argv[0]), ' '.join(sys.argv[1:])) 263 (os.path.basename(sys.argv[0]), ' '.join(sys.argv[1:]))
264 kparams = ''
245 sudouser = '' 265 sudouser = ''
246 def __init__(self): 266 def __init__(self):
247 self.archargs = 'args_'+platform.machine() 267 self.archargs = 'args_'+platform.machine()
@@ -320,6 +340,7 @@ class SystemValues:
320 args['date'] = n.strftime('%y%m%d') 340 args['date'] = n.strftime('%y%m%d')
321 args['time'] = n.strftime('%H%M%S') 341 args['time'] = n.strftime('%H%M%S')
322 args['hostname'] = args['host'] = self.hostname 342 args['hostname'] = args['host'] = self.hostname
343 args['mode'] = self.suspendmode
323 return value.format(**args) 344 return value.format(**args)
324 def setOutputFile(self): 345 def setOutputFile(self):
325 if self.dmesgfile != '': 346 if self.dmesgfile != '':
@@ -331,21 +352,28 @@ class SystemValues:
331 if(m): 352 if(m):
332 self.htmlfile = m.group('name')+'.html' 353 self.htmlfile = m.group('name')+'.html'
333 def systemInfo(self, info): 354 def systemInfo(self, info):
334 p = c = m = b = '' 355 p = m = ''
335 if 'baseboard-manufacturer' in info: 356 if 'baseboard-manufacturer' in info:
336 m = info['baseboard-manufacturer'] 357 m = info['baseboard-manufacturer']
337 elif 'system-manufacturer' in info: 358 elif 'system-manufacturer' in info:
338 m = info['system-manufacturer'] 359 m = info['system-manufacturer']
339 if 'baseboard-product-name' in info: 360 if 'system-product-name' in info:
340 p = info['baseboard-product-name']
341 elif 'system-product-name' in info:
342 p = info['system-product-name'] 361 p = info['system-product-name']
343 if 'processor-version' in info: 362 elif 'baseboard-product-name' in info:
344 c = info['processor-version'] 363 p = info['baseboard-product-name']
345 if 'bios-version' in info: 364 if m[:5].lower() == 'intel' and 'baseboard-product-name' in info:
346 b = info['bios-version'] 365 p = info['baseboard-product-name']
347 self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | numcpu:%d | memsz:%d | memfr:%d' % \ 366 c = info['processor-version'] if 'processor-version' in info else ''
348 (m, p, c, b, self.cpucount, self.memtotal, self.memfree) 367 b = info['bios-version'] if 'bios-version' in info else ''
368 r = info['bios-release-date'] if 'bios-release-date' in info else ''
369 self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | biosdate:%s | numcpu:%d | memsz:%d | memfr:%d' % \
370 (m, p, c, b, r, self.cpucount, self.memtotal, self.memfree)
371 try:
372 kcmd = open('/proc/cmdline', 'r').read().strip()
373 except:
374 kcmd = ''
375 if kcmd:
376 self.sysstamp += '\n# kparams | %s' % kcmd
349 def printSystemInfo(self, fatal=False): 377 def printSystemInfo(self, fatal=False):
350 self.rootCheck(True) 378 self.rootCheck(True)
351 out = dmidecode(self.mempath, fatal) 379 out = dmidecode(self.mempath, fatal)
@@ -353,10 +381,10 @@ class SystemValues:
353 return 381 return
354 fmt = '%-24s: %s' 382 fmt = '%-24s: %s'
355 for name in sorted(out): 383 for name in sorted(out):
356 print fmt % (name, out[name]) 384 print(fmt % (name, out[name]))
357 print fmt % ('cpucount', ('%d' % self.cpucount)) 385 print(fmt % ('cpucount', ('%d' % self.cpucount)))
358 print fmt % ('memtotal', ('%d kB' % self.memtotal)) 386 print(fmt % ('memtotal', ('%d kB' % self.memtotal)))
359 print fmt % ('memfree', ('%d kB' % self.memfree)) 387 print(fmt % ('memfree', ('%d kB' % self.memfree)))
360 def cpuInfo(self): 388 def cpuInfo(self):
361 self.cpucount = 0 389 self.cpucount = 0
362 fp = open('/proc/cpuinfo', 'r') 390 fp = open('/proc/cpuinfo', 'r')
@@ -376,7 +404,7 @@ class SystemValues:
376 def initTestOutput(self, name): 404 def initTestOutput(self, name):
377 self.prefix = self.hostname 405 self.prefix = self.hostname
378 v = open('/proc/version', 'r').read().strip() 406 v = open('/proc/version', 'r').read().strip()
379 kver = string.split(v)[2] 407 kver = v.split()[2]
380 fmt = name+'-%m%d%y-%H%M%S' 408 fmt = name+'-%m%d%y-%H%M%S'
381 testtime = datetime.now().strftime(fmt) 409 testtime = datetime.now().strftime(fmt)
382 self.teststamp = \ 410 self.teststamp = \
@@ -391,7 +419,7 @@ class SystemValues:
391 self.htmlfile = \ 419 self.htmlfile = \
392 self.testdir+'/'+self.prefix+'_'+self.suspendmode+'.html' 420 self.testdir+'/'+self.prefix+'_'+self.suspendmode+'.html'
393 if not os.path.isdir(self.testdir): 421 if not os.path.isdir(self.testdir):
394 os.mkdir(self.testdir) 422 os.makedirs(self.testdir)
395 def getValueList(self, value): 423 def getValueList(self, value):
396 out = [] 424 out = []
397 for i in value.split(','): 425 for i in value.split(','):
@@ -402,6 +430,12 @@ class SystemValues:
402 self.devicefilter = self.getValueList(value) 430 self.devicefilter = self.getValueList(value)
403 def setCallgraphFilter(self, value): 431 def setCallgraphFilter(self, value):
404 self.cgfilter = self.getValueList(value) 432 self.cgfilter = self.getValueList(value)
433 def skipKprobes(self, value):
434 for k in self.getValueList(value):
435 if k in self.tracefuncs:
436 del self.tracefuncs[k]
437 if k in self.dev_tracefuncs:
438 del self.dev_tracefuncs[k]
405 def setCallgraphBlacklist(self, file): 439 def setCallgraphBlacklist(self, file):
406 self.cgblacklist = self.listFromFile(file) 440 self.cgblacklist = self.listFromFile(file)
407 def rtcWakeAlarmOn(self): 441 def rtcWakeAlarmOn(self):
@@ -471,9 +505,9 @@ class SystemValues:
471 if 'func' in self.tracefuncs[i]: 505 if 'func' in self.tracefuncs[i]:
472 i = self.tracefuncs[i]['func'] 506 i = self.tracefuncs[i]['func']
473 if i in master: 507 if i in master:
474 print i 508 print(i)
475 else: 509 else:
476 print self.colorText(i) 510 print(self.colorText(i))
477 def setFtraceFilterFunctions(self, list): 511 def setFtraceFilterFunctions(self, list):
478 master = self.listFromFile(self.tpath+'available_filter_functions') 512 master = self.listFromFile(self.tpath+'available_filter_functions')
479 flist = '' 513 flist = ''
@@ -680,7 +714,8 @@ class SystemValues:
680 if self.bufsize > 0: 714 if self.bufsize > 0:
681 tgtsize = self.bufsize 715 tgtsize = self.bufsize
682 elif self.usecallgraph or self.usedevsrc: 716 elif self.usecallgraph or self.usedevsrc:
683 bmax = (1*1024*1024) if self.suspendmode == 'disk' else (3*1024*1024) 717 bmax = (1*1024*1024) if self.suspendmode in ['disk', 'command'] \
718 else (3*1024*1024)
684 tgtsize = min(self.memfree, bmax) 719 tgtsize = min(self.memfree, bmax)
685 else: 720 else:
686 tgtsize = 65536 721 tgtsize = 65536
@@ -715,7 +750,10 @@ class SystemValues:
715 cf.append(self.tracefuncs[fn]['func']) 750 cf.append(self.tracefuncs[fn]['func'])
716 else: 751 else:
717 cf.append(fn) 752 cf.append(fn)
718 self.setFtraceFilterFunctions(cf) 753 if self.ftop:
754 self.setFtraceFilterFunctions([self.ftopfunc])
755 else:
756 self.setFtraceFilterFunctions(cf)
719 # initialize the kprobe trace 757 # initialize the kprobe trace
720 elif self.usekprobes: 758 elif self.usekprobes:
721 for name in self.tracefuncs: 759 for name in self.tracefuncs:
@@ -768,9 +806,21 @@ class SystemValues:
768 fw = test['fw'] 806 fw = test['fw']
769 if(fw): 807 if(fw):
770 fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1])) 808 fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1]))
809 if 'mcelog' in test:
810 fp.write('# mcelog %s\n' % test['mcelog'])
811 if 'turbo' in test:
812 fp.write('# turbostat %s\n' % test['turbo'])
771 if 'bat' in test: 813 if 'bat' in test:
772 (a1, c1), (a2, c2) = test['bat'] 814 (a1, c1), (a2, c2) = test['bat']
773 fp.write('# battery %s %d %s %d\n' % (a1, c1, a2, c2)) 815 fp.write('# battery %s %d %s %d\n' % (a1, c1, a2, c2))
816 if 'wifi' in test:
817 wstr = []
818 for wifi in test['wifi']:
819 tmp = []
820 for key in sorted(wifi):
821 tmp.append('%s:%s' % (key, wifi[key]))
822 wstr.append('|'.join(tmp))
823 fp.write('# wifi %s\n' % (','.join(wstr)))
774 if test['error'] or len(testdata) > 1: 824 if test['error'] or len(testdata) > 1:
775 fp.write('# enter_sleep_error %s\n' % test['error']) 825 fp.write('# enter_sleep_error %s\n' % test['error'])
776 return fp 826 return fp
@@ -821,6 +871,106 @@ class SystemValues:
821 if isgz: 871 if isgz:
822 return gzip.open(filename, mode+'b') 872 return gzip.open(filename, mode+'b')
823 return open(filename, mode) 873 return open(filename, mode)
874 def mcelog(self, clear=False):
875 cmd = self.getExec('mcelog')
876 if not cmd:
877 return ''
878 if clear:
879 call(cmd+' > /dev/null 2>&1', shell=True)
880 return ''
881 fp = Popen([cmd], stdout=PIPE, stderr=PIPE).stdout
882 out = fp.read().strip()
883 fp.close()
884 if not out:
885 return ''
886 return base64.b64encode(out.encode('zlib'))
887 def haveTurbostat(self):
888 if not self.tstat:
889 return False
890 cmd = self.getExec('turbostat')
891 if not cmd:
892 return False
893 fp = Popen([cmd, '-v'], stdout=PIPE, stderr=PIPE).stderr
894 out = fp.read().strip()
895 fp.close()
896 return re.match('turbostat version [0-9\.]* .*', out)
897 def turbostat(self):
898 cmd = self.getExec('turbostat')
899 if not cmd:
900 return 'missing turbostat executable'
901 text = []
902 fullcmd = '%s -q -S echo freeze > %s' % (cmd, self.powerfile)
903 fp = Popen(['sh', '-c', fullcmd], stdout=PIPE, stderr=PIPE).stderr
904 for line in fp:
905 if re.match('[0-9.]* sec', line):
906 continue
907 text.append(line.split())
908 fp.close()
909 if len(text) < 2:
910 return 'turbostat output format error'
911 out = []
912 for key in text[0]:
913 values = []
914 idx = text[0].index(key)
915 for line in text[1:]:
916 if len(line) > idx:
917 values.append(line[idx])
918 out.append('%s=%s' % (key, ','.join(values)))
919 return '|'.join(out)
920 def checkWifi(self):
921 out = dict()
922 iwcmd, ifcmd = self.getExec('iwconfig'), self.getExec('ifconfig')
923 if not iwcmd or not ifcmd:
924 return out
925 fp = Popen(iwcmd, stdout=PIPE, stderr=PIPE).stdout
926 for line in fp:
927 m = re.match('(?P<dev>\S*) .* ESSID:(?P<ess>\S*)', line)
928 if not m:
929 continue
930 out['device'] = m.group('dev')
931 if '"' in m.group('ess'):
932 out['essid'] = m.group('ess').strip('"')
933 break
934 fp.close()
935 if 'device' in out:
936 fp = Popen([ifcmd, out['device']], stdout=PIPE, stderr=PIPE).stdout
937 for line in fp:
938 m = re.match('.* inet (?P<ip>[0-9\.]*)', line)
939 if m:
940 out['ip'] = m.group('ip')
941 break
942 fp.close()
943 return out
944 def errorSummary(self, errinfo, msg):
945 found = False
946 for entry in errinfo:
947 if re.match(entry['match'], msg):
948 entry['count'] += 1
949 if self.hostname not in entry['urls']:
950 entry['urls'][self.hostname] = [self.htmlfile]
951 elif self.htmlfile not in entry['urls'][self.hostname]:
952 entry['urls'][self.hostname].append(self.htmlfile)
953 found = True
954 break
955 if found:
956 return
957 arr = msg.split()
958 for j in range(len(arr)):
959 if re.match('^[0-9,\-\.]*$', arr[j]):
960 arr[j] = '[0-9,\-\.]*'
961 else:
962 arr[j] = arr[j]\
963 .replace('\\', '\\\\').replace(']', '\]').replace('[', '\[')\
964 .replace('.', '\.').replace('+', '\+').replace('*', '\*')\
965 .replace('(', '\(').replace(')', '\)')
966 mstr = ' '.join(arr)
967 entry = {
968 'line': msg,
969 'match': mstr,
970 'count': 1,
971 'urls': {self.hostname: [self.htmlfile]}
972 }
973 errinfo.append(entry)
824 974
825sysvals = SystemValues() 975sysvals = SystemValues()
826switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0'] 976switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0']
@@ -915,7 +1065,14 @@ class Data:
915 'ERROR' : '.*ERROR.*', 1065 'ERROR' : '.*ERROR.*',
916 'WARNING' : '.*WARNING.*', 1066 'WARNING' : '.*WARNING.*',
917 'IRQ' : '.*genirq: .*', 1067 'IRQ' : '.*genirq: .*',
918 'TASKFAIL': '.*Freezing of tasks failed.*', 1068 'TASKFAIL': '.*Freezing of tasks *.*',
1069 'ACPI' : '.*ACPI *(?P<b>[A-Za-z]*) *Error[: ].*',
1070 'DEVFAIL' : '.* failed to (?P<b>[a-z]*) async: .*',
1071 'DISKFULL': '.*No space left on device.*',
1072 'USBERR' : '.*usb .*device .*, error [0-9-]*',
1073 'ATAERR' : ' *ata[0-9\.]*: .*failed.*',
1074 'MEIERR' : ' *mei.*: .*failed.*',
1075 'TPMERR' : '(?i) *tpm *tpm[0-9]*: .*error.*',
919 } 1076 }
920 def __init__(self, num): 1077 def __init__(self, num):
921 idchar = 'abcdefghij' 1078 idchar = 'abcdefghij'
@@ -933,6 +1090,9 @@ class Data:
933 self.outfile = '' 1090 self.outfile = ''
934 self.kerror = False 1091 self.kerror = False
935 self.battery = 0 1092 self.battery = 0
1093 self.wifi = 0
1094 self.turbostat = 0
1095 self.mcelog = 0
936 self.enterfail = '' 1096 self.enterfail = ''
937 self.currphase = '' 1097 self.currphase = ''
938 self.pstl = dict() # process timeline 1098 self.pstl = dict() # process timeline
@@ -967,8 +1127,24 @@ class Data:
967 if len(plist) < 1: 1127 if len(plist) < 1:
968 return '' 1128 return ''
969 return plist[-1] 1129 return plist[-1]
1130 def turbostatInfo(self):
1131 tp = TestProps()
1132 out = {'syslpi':'N/A','pkgpc10':'N/A'}
1133 for line in self.dmesgtext:
1134 m = re.match(tp.tstatfmt, line)
1135 if not m:
1136 continue
1137 for i in m.group('t').split('|'):
1138 if 'SYS%LPI' in i:
1139 out['syslpi'] = i.split('=')[-1]+'%'
1140 elif 'pc10' in i:
1141 out['pkgpc10'] = i.split('=')[-1]+'%'
1142 break
1143 return out
970 def extractErrorInfo(self): 1144 def extractErrorInfo(self):
971 lf = sysvals.openlog(sysvals.dmesgfile, 'r') 1145 lf = self.dmesgtext
1146 if len(self.dmesgtext) < 1 and sysvals.dmesgfile:
1147 lf = sysvals.openlog(sysvals.dmesgfile, 'r')
972 i = 0 1148 i = 0
973 list = [] 1149 list = []
974 for line in lf: 1150 for line in lf:
@@ -983,16 +1159,19 @@ class Data:
983 msg = m.group('msg') 1159 msg = m.group('msg')
984 for err in self.errlist: 1160 for err in self.errlist:
985 if re.match(self.errlist[err], msg): 1161 if re.match(self.errlist[err], msg):
986 list.append((err, dir, t, i, i)) 1162 list.append((msg, err, dir, t, i, i))
987 self.kerror = True 1163 self.kerror = True
988 break 1164 break
989 for e in list: 1165 msglist = []
990 type, dir, t, idx1, idx2 = e 1166 for msg, type, dir, t, idx1, idx2 in list:
1167 msglist.append(msg)
991 sysvals.vprint('kernel %s found in %s at %f' % (type, dir, t)) 1168 sysvals.vprint('kernel %s found in %s at %f' % (type, dir, t))
992 self.errorinfo[dir].append((type, t, idx1, idx2)) 1169 self.errorinfo[dir].append((type, t, idx1, idx2))
993 if self.kerror: 1170 if self.kerror:
994 sysvals.dmesglog = True 1171 sysvals.dmesglog = True
995 lf.close() 1172 if len(self.dmesgtext) < 1 and sysvals.dmesgfile:
1173 lf.close()
1174 return msglist
996 def setStart(self, time): 1175 def setStart(self, time):
997 self.start = time 1176 self.start = time
998 def setEnd(self, time): 1177 def setEnd(self, time):
@@ -2045,7 +2224,7 @@ class FTraceCallGraph:
2045 if(data.dmesg[p]['start'] <= self.start and 2224 if(data.dmesg[p]['start'] <= self.start and
2046 self.start <= data.dmesg[p]['end']): 2225 self.start <= data.dmesg[p]['end']):
2047 list = data.dmesg[p]['list'] 2226 list = data.dmesg[p]['list']
2048 for devname in list: 2227 for devname in sorted(list, key=lambda k:list[k]['start']):
2049 dev = list[devname] 2228 dev = list[devname]
2050 if(pid == dev['pid'] and 2229 if(pid == dev['pid'] and
2051 self.start <= dev['start'] and 2230 self.start <= dev['start'] and
@@ -2350,6 +2529,9 @@ class TestProps:
2350 '(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\ 2529 '(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\
2351 ' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$' 2530 ' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$'
2352 batteryfmt = '^# battery (?P<a1>\w*) (?P<c1>\d*) (?P<a2>\w*) (?P<c2>\d*)' 2531 batteryfmt = '^# battery (?P<a1>\w*) (?P<c1>\d*) (?P<a2>\w*) (?P<c2>\d*)'
2532 wififmt = '^# wifi (?P<w>.*)'
2533 tstatfmt = '^# turbostat (?P<t>\S*)'
2534 mcelogfmt = '^# mcelog (?P<m>\S*)'
2353 testerrfmt = '^# enter_sleep_error (?P<e>.*)' 2535 testerrfmt = '^# enter_sleep_error (?P<e>.*)'
2354 sysinfofmt = '^# sysinfo .*' 2536 sysinfofmt = '^# sysinfo .*'
2355 cmdlinefmt = '^# command \| (?P<cmd>.*)' 2537 cmdlinefmt = '^# command \| (?P<cmd>.*)'
@@ -2372,7 +2554,10 @@ class TestProps:
2372 self.cmdline = '' 2554 self.cmdline = ''
2373 self.kparams = '' 2555 self.kparams = ''
2374 self.testerror = [] 2556 self.testerror = []
2557 self.mcelog = []
2558 self.turbostat = []
2375 self.battery = [] 2559 self.battery = []
2560 self.wifi = []
2376 self.fwdata = [] 2561 self.fwdata = []
2377 self.ftrace_line_fmt = self.ftrace_line_fmt_nop 2562 self.ftrace_line_fmt = self.ftrace_line_fmt_nop
2378 self.cgformat = False 2563 self.cgformat = False
@@ -2386,6 +2571,44 @@ class TestProps:
2386 self.ftrace_line_fmt = self.ftrace_line_fmt_nop 2571 self.ftrace_line_fmt = self.ftrace_line_fmt_nop
2387 else: 2572 else:
2388 doError('Invalid tracer format: [%s]' % tracer) 2573 doError('Invalid tracer format: [%s]' % tracer)
2574 def decode(self, data):
2575 try:
2576 out = base64.b64decode(data).decode('zlib')
2577 except:
2578 out = data
2579 return out
2580 def stampInfo(self, line):
2581 if re.match(self.stampfmt, line):
2582 self.stamp = line
2583 return True
2584 elif re.match(self.sysinfofmt, line):
2585 self.sysinfo = line
2586 return True
2587 elif re.match(self.kparamsfmt, line):
2588 self.kparams = line
2589 return True
2590 elif re.match(self.cmdlinefmt, line):
2591 self.cmdline = line
2592 return True
2593 elif re.match(self.mcelogfmt, line):
2594 self.mcelog.append(line)
2595 return True
2596 elif re.match(self.tstatfmt, line):
2597 self.turbostat.append(line)
2598 return True
2599 elif re.match(self.batteryfmt, line):
2600 self.battery.append(line)
2601 return True
2602 elif re.match(self.wififmt, line):
2603 self.wifi.append(line)
2604 return True
2605 elif re.match(self.testerrfmt, line):
2606 self.testerror.append(line)
2607 return True
2608 elif re.match(self.firmwarefmt, line):
2609 self.fwdata.append(line)
2610 return True
2611 return False
2389 def parseStamp(self, data, sv): 2612 def parseStamp(self, data, sv):
2390 # global test data 2613 # global test data
2391 m = re.match(self.stampfmt, self.stamp) 2614 m = re.match(self.stampfmt, self.stamp)
@@ -2428,14 +2651,31 @@ class TestProps:
2428 sv.stamp = data.stamp 2651 sv.stamp = data.stamp
2429 # firmware data 2652 # firmware data
2430 if sv.suspendmode == 'mem' and len(self.fwdata) > data.testnumber: 2653 if sv.suspendmode == 'mem' and len(self.fwdata) > data.testnumber:
2431 data.fwSuspend, data.fwResume = self.fwdata[data.testnumber] 2654 m = re.match(self.firmwarefmt, self.fwdata[data.testnumber])
2432 if(data.fwSuspend > 0 or data.fwResume > 0): 2655 if m:
2433 data.fwValid = True 2656 data.fwSuspend, data.fwResume = int(m.group('s')), int(m.group('r'))
2657 if(data.fwSuspend > 0 or data.fwResume > 0):
2658 data.fwValid = True
2659 # mcelog data
2660 if len(self.mcelog) > data.testnumber:
2661 m = re.match(self.mcelogfmt, self.mcelog[data.testnumber])
2662 if m:
2663 data.mcelog = self.decode(m.group('m'))
2664 # turbostat data
2665 if len(self.turbostat) > data.testnumber:
2666 m = re.match(self.tstatfmt, self.turbostat[data.testnumber])
2667 if m:
2668 data.turbostat = m.group('t')
2434 # battery data 2669 # battery data
2435 if len(self.battery) > data.testnumber: 2670 if len(self.battery) > data.testnumber:
2436 m = re.match(self.batteryfmt, self.battery[data.testnumber]) 2671 m = re.match(self.batteryfmt, self.battery[data.testnumber])
2437 if m: 2672 if m:
2438 data.battery = m.groups() 2673 data.battery = m.groups()
2674 # wifi data
2675 if len(self.wifi) > data.testnumber:
2676 m = re.match(self.wififmt, self.wifi[data.testnumber])
2677 if m:
2678 data.wifi = m.group('w')
2439 # sleep mode enter errors 2679 # sleep mode enter errors
2440 if len(self.testerror) > data.testnumber: 2680 if len(self.testerror) > data.testnumber:
2441 m = re.match(self.testerrfmt, self.testerror[data.testnumber]) 2681 m = re.match(self.testerrfmt, self.testerror[data.testnumber])
@@ -2505,9 +2745,9 @@ class ProcessMonitor:
2505# Quickly determine if the ftrace log has all of the trace events, 2745# Quickly determine if the ftrace log has all of the trace events,
2506# markers, and/or kprobes required for primary parsing. 2746# markers, and/or kprobes required for primary parsing.
2507def doesTraceLogHaveTraceEvents(): 2747def doesTraceLogHaveTraceEvents():
2508 kpcheck = ['_cal: (', '_cpu_down()'] 2748 kpcheck = ['_cal: (', '_ret: (']
2509 techeck = ['suspend_resume', 'device_pm_callback'] 2749 techeck = ['suspend_resume', 'device_pm_callback']
2510 tmcheck = ['tracing_mark_write'] 2750 tmcheck = ['SUSPEND START', 'RESUME COMPLETE']
2511 sysvals.usekprobes = False 2751 sysvals.usekprobes = False
2512 fp = sysvals.openlog(sysvals.ftracefile, 'r') 2752 fp = sysvals.openlog(sysvals.ftracefile, 'r')
2513 for line in fp: 2753 for line in fp:
@@ -2556,21 +2796,7 @@ def appendIncompleteTraceLog(testruns):
2556 for line in tf: 2796 for line in tf:
2557 # remove any latent carriage returns 2797 # remove any latent carriage returns
2558 line = line.replace('\r\n', '') 2798 line = line.replace('\r\n', '')
2559 # grab the stamp and sysinfo 2799 if tp.stampInfo(line):
2560 if re.match(tp.stampfmt, line):
2561 tp.stamp = line
2562 continue
2563 elif re.match(tp.sysinfofmt, line):
2564 tp.sysinfo = line
2565 continue
2566 elif re.match(tp.cmdlinefmt, line):
2567 tp.cmdline = line
2568 continue
2569 elif re.match(tp.batteryfmt, line):
2570 tp.battery.append(line)
2571 continue
2572 elif re.match(tp.testerrfmt, line):
2573 tp.testerror.append(line)
2574 continue 2800 continue
2575 # determine the trace data type (required for further parsing) 2801 # determine the trace data type (required for further parsing)
2576 m = re.match(tp.tracertypefmt, line) 2802 m = re.match(tp.tracertypefmt, line)
@@ -2693,26 +2919,7 @@ def parseTraceLog(live=False):
2693 for line in tf: 2919 for line in tf:
2694 # remove any latent carriage returns 2920 # remove any latent carriage returns
2695 line = line.replace('\r\n', '') 2921 line = line.replace('\r\n', '')
2696 # stamp and sysinfo lines 2922 if tp.stampInfo(line):
2697 if re.match(tp.stampfmt, line):
2698 tp.stamp = line
2699 continue
2700 elif re.match(tp.sysinfofmt, line):
2701 tp.sysinfo = line
2702 continue
2703 elif re.match(tp.cmdlinefmt, line):
2704 tp.cmdline = line
2705 continue
2706 elif re.match(tp.batteryfmt, line):
2707 tp.battery.append(line)
2708 continue
2709 elif re.match(tp.testerrfmt, line):
2710 tp.testerror.append(line)
2711 continue
2712 # firmware line: pull out any firmware data
2713 m = re.match(tp.firmwarefmt, line)
2714 if(m):
2715 tp.fwdata.append((int(m.group('s')), int(m.group('r'))))
2716 continue 2923 continue
2717 # tracer type line: determine the trace data type 2924 # tracer type line: determine the trace data type
2718 m = re.match(tp.tracertypefmt, line) 2925 m = re.match(tp.tracertypefmt, line)
@@ -2925,7 +3132,7 @@ def parseTraceLog(live=False):
2925 tp.ktemp[key].append({ 3132 tp.ktemp[key].append({
2926 'pid': pid, 3133 'pid': pid,
2927 'begin': t.time, 3134 'begin': t.time,
2928 'end': t.time, 3135 'end': -1,
2929 'name': displayname, 3136 'name': displayname,
2930 'cdata': kprobedata, 3137 'cdata': kprobedata,
2931 'proc': m_proc, 3138 'proc': m_proc,
@@ -2936,12 +3143,11 @@ def parseTraceLog(live=False):
2936 elif(t.freturn): 3143 elif(t.freturn):
2937 if(key not in tp.ktemp) or len(tp.ktemp[key]) < 1: 3144 if(key not in tp.ktemp) or len(tp.ktemp[key]) < 1:
2938 continue 3145 continue
2939 e = tp.ktemp[key][-1] 3146 e = next((x for x in reversed(tp.ktemp[key]) if x['end'] < 0), 0)
2940 if e['begin'] < 0.0 or t.time - e['begin'] < 0.000001: 3147 if not e:
2941 tp.ktemp[key].pop() 3148 continue
2942 else: 3149 e['end'] = t.time
2943 e['end'] = t.time 3150 e['rdata'] = kprobedata
2944 e['rdata'] = kprobedata
2945 # end of kernel resume 3151 # end of kernel resume
2946 if(phase != 'suspend_prepare' and kprobename in krescalls): 3152 if(phase != 'suspend_prepare' and kprobename in krescalls):
2947 if phase in data.dmesg: 3153 if phase in data.dmesg:
@@ -2963,8 +3169,10 @@ def parseTraceLog(live=False):
2963 if(res == -1): 3169 if(res == -1):
2964 testrun.ftemp[key][-1].addLine(t) 3170 testrun.ftemp[key][-1].addLine(t)
2965 tf.close() 3171 tf.close()
3172 if len(testdata) < 1:
3173 sysvals.vprint('WARNING: ftrace start marker is missing')
2966 if data and not data.devicegroups: 3174 if data and not data.devicegroups:
2967 sysvals.vprint('WARNING: end marker is missing') 3175 sysvals.vprint('WARNING: ftrace end marker is missing')
2968 data.handleEndMarker(t.time) 3176 data.handleEndMarker(t.time)
2969 3177
2970 if sysvals.suspendmode == 'command': 3178 if sysvals.suspendmode == 'command':
@@ -3013,9 +3221,11 @@ def parseTraceLog(live=False):
3013 name, pid = key 3221 name, pid = key
3014 if name not in sysvals.tracefuncs: 3222 if name not in sysvals.tracefuncs:
3015 continue 3223 continue
3224 if pid not in data.devpids:
3225 data.devpids.append(pid)
3016 for e in tp.ktemp[key]: 3226 for e in tp.ktemp[key]:
3017 kb, ke = e['begin'], e['end'] 3227 kb, ke = e['begin'], e['end']
3018 if kb == ke or tlb > kb or tle <= kb: 3228 if ke - kb < 0.000001 or tlb > kb or tle <= kb:
3019 continue 3229 continue
3020 color = sysvals.kprobeColor(name) 3230 color = sysvals.kprobeColor(name)
3021 data.newActionGlobal(e['name'], kb, ke, pid, color) 3231 data.newActionGlobal(e['name'], kb, ke, pid, color)
@@ -3027,7 +3237,7 @@ def parseTraceLog(live=False):
3027 continue 3237 continue
3028 for e in tp.ktemp[key]: 3238 for e in tp.ktemp[key]:
3029 kb, ke = e['begin'], e['end'] 3239 kb, ke = e['begin'], e['end']
3030 if kb == ke or tlb > kb or tle <= kb: 3240 if ke - kb < 0.000001 or tlb > kb or tle <= kb:
3031 continue 3241 continue
3032 data.addDeviceFunctionCall(e['name'], name, e['proc'], pid, kb, 3242 data.addDeviceFunctionCall(e['name'], name, e['proc'], pid, kb,
3033 ke, e['cdata'], e['rdata']) 3243 ke, e['cdata'], e['rdata'])
@@ -3051,7 +3261,7 @@ def parseTraceLog(live=False):
3051 if not devname: 3261 if not devname:
3052 sortkey = '%f%f%d' % (cg.start, cg.end, pid) 3262 sortkey = '%f%f%d' % (cg.start, cg.end, pid)
3053 sortlist[sortkey] = cg 3263 sortlist[sortkey] = cg
3054 elif len(cg.list) > 1000000: 3264 elif len(cg.list) > 1000000 and cg.name != sysvals.ftopfunc:
3055 sysvals.vprint('WARNING: the callgraph for %s is massive (%d lines)' %\ 3265 sysvals.vprint('WARNING: the callgraph for %s is massive (%d lines)' %\
3056 (devname, len(cg.list))) 3266 (devname, len(cg.list)))
3057 # create blocks for orphan cg data 3267 # create blocks for orphan cg data
@@ -3133,25 +3343,7 @@ def loadKernelLog():
3133 idx = line.find('[') 3343 idx = line.find('[')
3134 if idx > 1: 3344 if idx > 1:
3135 line = line[idx:] 3345 line = line[idx:]
3136 # grab the stamp and sysinfo 3346 if tp.stampInfo(line):
3137 if re.match(tp.stampfmt, line):
3138 tp.stamp = line
3139 continue
3140 elif re.match(tp.sysinfofmt, line):
3141 tp.sysinfo = line
3142 continue
3143 elif re.match(tp.cmdlinefmt, line):
3144 tp.cmdline = line
3145 continue
3146 elif re.match(tp.batteryfmt, line):
3147 tp.battery.append(line)
3148 continue
3149 elif re.match(tp.testerrfmt, line):
3150 tp.testerror.append(line)
3151 continue
3152 m = re.match(tp.firmwarefmt, line)
3153 if(m):
3154 tp.fwdata.append((int(m.group('s')), int(m.group('r'))))
3155 continue 3347 continue
3156 m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line) 3348 m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
3157 if(not m): 3349 if(not m):
@@ -3176,7 +3368,7 @@ def loadKernelLog():
3176 if data: 3368 if data:
3177 testruns.append(data) 3369 testruns.append(data)
3178 if len(testruns) < 1: 3370 if len(testruns) < 1:
3179 pprint('ERROR: dmesg log has no suspend/resume data: %s' \ 3371 doError('dmesg log has no suspend/resume data: %s' \
3180 % sysvals.dmesgfile) 3372 % sysvals.dmesgfile)
3181 3373
3182 # fix lines with same timestamp/function with the call and return swapped 3374 # fix lines with same timestamp/function with the call and return swapped
@@ -3515,6 +3707,8 @@ def addCallgraphs(sv, hf, data):
3515 name += ' '+p 3707 name += ' '+p
3516 if('ftrace' in dev): 3708 if('ftrace' in dev):
3517 cg = dev['ftrace'] 3709 cg = dev['ftrace']
3710 if cg.name == sv.ftopfunc:
3711 name = 'top level suspend/resume call'
3518 num = callgraphHTML(sv, hf, num, cg, 3712 num = callgraphHTML(sv, hf, num, cg,
3519 name, color, dev['id']) 3713 name, color, dev['id'])
3520 if('ftraces' in dev): 3714 if('ftraces' in dev):
@@ -3523,22 +3717,16 @@ def addCallgraphs(sv, hf, data):
3523 name+' &rarr; '+cg.name, color, dev['id']) 3717 name+' &rarr; '+cg.name, color, dev['id'])
3524 hf.write('\n\n </section>\n') 3718 hf.write('\n\n </section>\n')
3525 3719
3526# Function: createHTMLSummarySimple 3720def summaryCSS(title, center=True):
3527# Description: 3721 tdcenter = 'text-align:center;' if center else ''
3528# Create summary html file for a series of tests 3722 out = '<!DOCTYPE html>\n<html>\n<head>\n\
3529# Arguments:
3530# testruns: array of Data objects from parseTraceLog
3531def createHTMLSummarySimple(testruns, htmlfile, title):
3532 # write the html header first (html head, css code, up to body start)
3533 html = '<!DOCTYPE html>\n<html>\n<head>\n\
3534 <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\ 3723 <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\
3535 <title>SleepGraph Summary</title>\n\ 3724 <title>'+title+'</title>\n\
3536 <style type=\'text/css\'>\n\ 3725 <style type=\'text/css\'>\n\
3537 .stamp {width: 100%;text-align:center;background:#888;line-height:30px;color:white;font: 25px Arial;}\n\ 3726 .stamp {width: 100%;text-align:center;background:#888;line-height:30px;color:white;font: 25px Arial;}\n\
3538 table {width:100%;border-collapse: collapse;}\n\ 3727 table {width:100%;border-collapse: collapse;border:1px solid;}\n\
3539 .summary {border:1px solid;}\n\
3540 th {border: 1px solid black;background:#222;color:white;}\n\ 3728 th {border: 1px solid black;background:#222;color:white;}\n\
3541 td {font: 14px "Times New Roman";text-align: center;}\n\ 3729 td {font: 14px "Times New Roman";'+tdcenter+'}\n\
3542 tr.head td {border: 1px solid black;background:#aaa;}\n\ 3730 tr.head td {border: 1px solid black;background:#aaa;}\n\
3543 tr.alt {background-color:#ddd;}\n\ 3731 tr.alt {background-color:#ddd;}\n\
3544 tr.notice {color:red;}\n\ 3732 tr.notice {color:red;}\n\
@@ -3547,12 +3735,23 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
3547 .maxval {background-color:#FFBBBB;}\n\ 3735 .maxval {background-color:#FFBBBB;}\n\
3548 .head a {color:#000;text-decoration: none;}\n\ 3736 .head a {color:#000;text-decoration: none;}\n\
3549 </style>\n</head>\n<body>\n' 3737 </style>\n</head>\n<body>\n'
3738 return out
3739
3740# Function: createHTMLSummarySimple
3741# Description:
3742# Create summary html file for a series of tests
3743# Arguments:
3744# testruns: array of Data objects from parseTraceLog
3745def createHTMLSummarySimple(testruns, htmlfile, title):
3746 # write the html header first (html head, css code, up to body start)
3747 html = summaryCSS('Summary - SleepGraph')
3550 3748
3551 # extract the test data into list 3749 # extract the test data into list
3552 list = dict() 3750 list = dict()
3553 tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [[], []] 3751 tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()]
3554 iMin, iMed, iMax = [0, 0], [0, 0], [0, 0] 3752 iMin, iMed, iMax = [0, 0], [0, 0], [0, 0]
3555 num = 0 3753 num = 0
3754 useturbo = False
3556 lastmode = '' 3755 lastmode = ''
3557 cnt = dict() 3756 cnt = dict()
3558 for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'], v['time'])): 3757 for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'], v['time'])):
@@ -3563,27 +3762,35 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
3563 for i in range(2): 3762 for i in range(2):
3564 s = sorted(tMed[i]) 3763 s = sorted(tMed[i])
3565 list[lastmode]['med'][i] = s[int(len(s)/2)] 3764 list[lastmode]['med'][i] = s[int(len(s)/2)]
3566 iMed[i] = tMed[i].index(list[lastmode]['med'][i]) 3765 iMed[i] = tMed[i][list[lastmode]['med'][i]]
3567 list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num] 3766 list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num]
3568 list[lastmode]['min'] = tMin 3767 list[lastmode]['min'] = tMin
3569 list[lastmode]['max'] = tMax 3768 list[lastmode]['max'] = tMax
3570 list[lastmode]['idx'] = (iMin, iMed, iMax) 3769 list[lastmode]['idx'] = (iMin, iMed, iMax)
3571 tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [[], []] 3770 tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()]
3572 iMin, iMed, iMax = [0, 0], [0, 0], [0, 0] 3771 iMin, iMed, iMax = [0, 0], [0, 0], [0, 0]
3573 num = 0 3772 num = 0
3773 pkgpc10 = syslpi = ''
3774 if 'pkgpc10' in data and 'syslpi' in data:
3775 pkgpc10 = data['pkgpc10']
3776 syslpi = data['syslpi']
3777 useturbo = True
3778 res = data['result']
3574 tVal = [float(data['suspend']), float(data['resume'])] 3779 tVal = [float(data['suspend']), float(data['resume'])]
3575 list[mode]['data'].append([data['host'], data['kernel'], 3780 list[mode]['data'].append([data['host'], data['kernel'],
3576 data['time'], tVal[0], tVal[1], data['url'], data['result'], 3781 data['time'], tVal[0], tVal[1], data['url'], res,
3577 data['issues'], data['sus_worst'], data['sus_worsttime'], 3782 data['issues'], data['sus_worst'], data['sus_worsttime'],
3578 data['res_worst'], data['res_worsttime']]) 3783 data['res_worst'], data['res_worsttime'], pkgpc10, syslpi])
3579 idx = len(list[mode]['data']) - 1 3784 idx = len(list[mode]['data']) - 1
3580 if data['result'] not in cnt: 3785 if res.startswith('fail in'):
3581 cnt[data['result']] = 1 3786 res = 'fail'
3787 if res not in cnt:
3788 cnt[res] = 1
3582 else: 3789 else:
3583 cnt[data['result']] += 1 3790 cnt[res] += 1
3584 if data['result'] == 'pass': 3791 if res == 'pass':
3585 for i in range(2): 3792 for i in range(2):
3586 tMed[i].append(tVal[i]) 3793 tMed[i][tVal[i]] = idx
3587 tAvg[i] += tVal[i] 3794 tAvg[i] += tVal[i]
3588 if tMin[i] == 0 or tVal[i] < tMin[i]: 3795 if tMin[i] == 0 or tVal[i] < tMin[i]:
3589 iMin[i] = idx 3796 iMin[i] = idx
@@ -3597,7 +3804,7 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
3597 for i in range(2): 3804 for i in range(2):
3598 s = sorted(tMed[i]) 3805 s = sorted(tMed[i])
3599 list[lastmode]['med'][i] = s[int(len(s)/2)] 3806 list[lastmode]['med'][i] = s[int(len(s)/2)]
3600 iMed[i] = tMed[i].index(list[lastmode]['med'][i]) 3807 iMed[i] = tMed[i][list[lastmode]['med'][i]]
3601 list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num] 3808 list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num]
3602 list[lastmode]['min'] = tMin 3809 list[lastmode]['min'] = tMin
3603 list[lastmode]['max'] = tMax 3810 list[lastmode]['max'] = tMax
@@ -3613,19 +3820,21 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
3613 td = '\t<td>{0}</td>\n' 3820 td = '\t<td>{0}</td>\n'
3614 tdh = '\t<td{1}>{0}</td>\n' 3821 tdh = '\t<td{1}>{0}</td>\n'
3615 tdlink = '\t<td><a href="{0}">html</a></td>\n' 3822 tdlink = '\t<td><a href="{0}">html</a></td>\n'
3823 colspan = '14' if useturbo else '12'
3616 3824
3617 # table header 3825 # table header
3618 html += '<table class="summary">\n<tr>\n' + th.format('#') +\ 3826 html += '<table>\n<tr>\n' + th.format('#') +\
3619 th.format('Mode') + th.format('Host') + th.format('Kernel') +\ 3827 th.format('Mode') + th.format('Host') + th.format('Kernel') +\
3620 th.format('Test Time') + th.format('Result') + th.format('Issues') +\ 3828 th.format('Test Time') + th.format('Result') + th.format('Issues') +\
3621 th.format('Suspend') + th.format('Resume') +\ 3829 th.format('Suspend') + th.format('Resume') +\
3622 th.format('Worst Suspend Device') + th.format('SD Time') +\ 3830 th.format('Worst Suspend Device') + th.format('SD Time') +\
3623 th.format('Worst Resume Device') + th.format('RD Time') +\ 3831 th.format('Worst Resume Device') + th.format('RD Time')
3624 th.format('Detail') + '</tr>\n' 3832 if useturbo:
3625 3833 html += th.format('PkgPC10') + th.format('SysLPI')
3834 html += th.format('Detail')+'</tr>\n'
3626 # export list into html 3835 # export list into html
3627 head = '<tr class="head"><td>{0}</td><td>{1}</td>'+\ 3836 head = '<tr class="head"><td>{0}</td><td>{1}</td>'+\
3628 '<td colspan=12 class="sus">Suspend Avg={2} '+\ 3837 '<td colspan='+colspan+' class="sus">Suspend Avg={2} '+\
3629 '<span class=minval><a href="#s{10}min">Min={3}</a></span> '+\ 3838 '<span class=minval><a href="#s{10}min">Min={3}</a></span> '+\
3630 '<span class=medval><a href="#s{10}med">Med={4}</a></span> '+\ 3839 '<span class=medval><a href="#s{10}med">Med={4}</a></span> '+\
3631 '<span class=maxval><a href="#s{10}max">Max={5}</a></span> '+\ 3840 '<span class=maxval><a href="#s{10}max">Max={5}</a></span> '+\
@@ -3634,7 +3843,8 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
3634 '<span class=medval><a href="#r{10}med">Med={8}</a></span> '+\ 3843 '<span class=medval><a href="#r{10}med">Med={8}</a></span> '+\
3635 '<span class=maxval><a href="#r{10}max">Max={9}</a></span></td>'+\ 3844 '<span class=maxval><a href="#r{10}max">Max={9}</a></span></td>'+\
3636 '</tr>\n' 3845 '</tr>\n'
3637 headnone = '<tr class="head"><td>{0}</td><td>{1}</td><td colspan=12></td></tr>\n' 3846 headnone = '<tr class="head"><td>{0}</td><td>{1}</td><td colspan='+\
3847 colspan+'></td></tr>\n'
3638 for mode in list: 3848 for mode in list:
3639 # header line for each suspend mode 3849 # header line for each suspend mode
3640 num = 0 3850 num = 0
@@ -3681,6 +3891,9 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
3681 html += td.format('%.3f ms' % d[9]) if d[9] else td.format('') # sus_worst time 3891 html += td.format('%.3f ms' % d[9]) if d[9] else td.format('') # sus_worst time
3682 html += td.format(d[10]) # res_worst 3892 html += td.format(d[10]) # res_worst
3683 html += td.format('%.3f ms' % d[11]) if d[11] else td.format('') # res_worst time 3893 html += td.format('%.3f ms' % d[11]) if d[11] else td.format('') # res_worst time
3894 if useturbo:
3895 html += td.format(d[12]) # pkg_pc10
3896 html += td.format(d[13]) # syslpi
3684 html += tdlink.format(d[5]) if d[5] else td.format('') # url 3897 html += tdlink.format(d[5]) if d[5] else td.format('') # url
3685 html += '</tr>\n' 3898 html += '</tr>\n'
3686 num += 1 3899 num += 1
@@ -3690,6 +3903,115 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
3690 hf.write(html+'</table>\n</body>\n</html>\n') 3903 hf.write(html+'</table>\n</body>\n</html>\n')
3691 hf.close() 3904 hf.close()
3692 3905
3906def createHTMLDeviceSummary(testruns, htmlfile, title):
3907 html = summaryCSS('Device Summary - SleepGraph', False)
3908
3909 # create global device list from all tests
3910 devall = dict()
3911 for data in testruns:
3912 host, url, devlist = data['host'], data['url'], data['devlist']
3913 for type in devlist:
3914 if type not in devall:
3915 devall[type] = dict()
3916 mdevlist, devlist = devall[type], data['devlist'][type]
3917 for name in devlist:
3918 length = devlist[name]
3919 if name not in mdevlist:
3920 mdevlist[name] = {'name': name, 'host': host,
3921 'worst': length, 'total': length, 'count': 1,
3922 'url': url}
3923 else:
3924 if length > mdevlist[name]['worst']:
3925 mdevlist[name]['worst'] = length
3926 mdevlist[name]['url'] = url
3927 mdevlist[name]['host'] = host
3928 mdevlist[name]['total'] += length
3929 mdevlist[name]['count'] += 1
3930
3931 # generate the html
3932 th = '\t<th>{0}</th>\n'
3933 td = '\t<td align=center>{0}</td>\n'
3934 tdr = '\t<td align=right>{0}</td>\n'
3935 tdlink = '\t<td align=center><a href="{0}">html</a></td>\n'
3936 limit = 1
3937 for type in sorted(devall, reverse=True):
3938 num = 0
3939 devlist = devall[type]
3940 # table header
3941 html += '<div class="stamp">%s (%s devices > %d ms)</div><table>\n' % \
3942 (title, type.upper(), limit)
3943 html += '<tr>\n' + '<th align=right>Device Name</th>' +\
3944 th.format('Average Time') + th.format('Count') +\
3945 th.format('Worst Time') + th.format('Host (worst time)') +\
3946 th.format('Link (worst time)') + '</tr>\n'
3947 for name in sorted(devlist, key=lambda k:devlist[k]['worst'], reverse=True):
3948 data = devall[type][name]
3949 data['average'] = data['total'] / data['count']
3950 if data['average'] < limit:
3951 continue
3952 # row classes - alternate row color
3953 rcls = ['alt'] if num % 2 == 1 else []
3954 html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n'
3955 html += tdr.format(data['name']) # name
3956 html += td.format('%.3f ms' % data['average']) # average
3957 html += td.format(data['count']) # count
3958 html += td.format('%.3f ms' % data['worst']) # worst
3959 html += td.format(data['host']) # host
3960 html += tdlink.format(data['url']) # url
3961 html += '</tr>\n'
3962 num += 1
3963 html += '</table>\n'
3964
3965 # flush the data to file
3966 hf = open(htmlfile, 'w')
3967 hf.write(html+'</body>\n</html>\n')
3968 hf.close()
3969 return devall
3970
3971def createHTMLIssuesSummary(testruns, issues, htmlfile, title, extra=''):
3972 multihost = len([e for e in issues if len(e['urls']) > 1]) > 0
3973 html = summaryCSS('Issues Summary - SleepGraph', False)
3974 total = len(testruns)
3975
3976 # generate the html
3977 th = '\t<th>{0}</th>\n'
3978 td = '\t<td align={0}>{1}</td>\n'
3979 tdlink = '<a href="{1}">{0}</a>'
3980 subtitle = '%d issues' % len(issues) if len(issues) > 0 else 'no issues'
3981 html += '<div class="stamp">%s (%s)</div><table>\n' % (title, subtitle)
3982 html += '<tr>\n' + th.format('Issue') + th.format('Count')
3983 if multihost:
3984 html += th.format('Hosts')
3985 html += th.format('Tests') + th.format('Fail Rate') +\
3986 th.format('First Instance') + '</tr>\n'
3987
3988 num = 0
3989 for e in sorted(issues, key=lambda v:v['count'], reverse=True):
3990 testtotal = 0
3991 links = []
3992 for host in sorted(e['urls']):
3993 links.append(tdlink.format(host, e['urls'][host][0]))
3994 testtotal += len(e['urls'][host])
3995 rate = '%d/%d (%.2f%%)' % (testtotal, total, 100*float(testtotal)/float(total))
3996 # row classes - alternate row color
3997 rcls = ['alt'] if num % 2 == 1 else []
3998 html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n'
3999 html += td.format('left', e['line']) # issue
4000 html += td.format('center', e['count']) # count
4001 if multihost:
4002 html += td.format('center', len(e['urls'])) # hosts
4003 html += td.format('center', testtotal) # test count
4004 html += td.format('center', rate) # test rate
4005 html += td.format('center nowrap', '<br>'.join(links)) # links
4006 html += '</tr>\n'
4007 num += 1
4008
4009 # flush the data to file
4010 hf = open(htmlfile, 'w')
4011 hf.write(html+'</table>\n'+extra+'</body>\n</html>\n')
4012 hf.close()
4013 return issues
4014
3693def ordinal(value): 4015def ordinal(value):
3694 suffix = 'th' 4016 suffix = 'th'
3695 if value < 10 or value > 19: 4017 if value < 10 or value > 19:
@@ -3991,7 +4313,7 @@ def createHTML(testruns, testfail):
3991 for word in phase.split('_'): 4313 for word in phase.split('_'):
3992 id += word[0] 4314 id += word[0]
3993 order = '%.2f' % ((p['order'] * pdelta) + pmargin) 4315 order = '%.2f' % ((p['order'] * pdelta) + pmargin)
3994 name = string.replace(phase, '_', ' &nbsp;') 4316 name = phase.replace('_', ' &nbsp;')
3995 devtl.html += devtl.html_legend.format(order, p['color'], name, id) 4317 devtl.html += devtl.html_legend.format(order, p['color'], name, id)
3996 devtl.html += '</div>\n' 4318 devtl.html += '</div>\n'
3997 4319
@@ -4580,6 +4902,7 @@ def setRuntimeSuspend(before=True):
4580def executeSuspend(): 4902def executeSuspend():
4581 pm = ProcessMonitor() 4903 pm = ProcessMonitor()
4582 tp = sysvals.tpath 4904 tp = sysvals.tpath
4905 wifi = sysvals.checkWifi()
4583 testdata = [] 4906 testdata = []
4584 battery = True if getBattery() else False 4907 battery = True if getBattery() else False
4585 # run these commands to prepare the system for suspend 4908 # run these commands to prepare the system for suspend
@@ -4613,6 +4936,7 @@ def executeSuspend():
4613 pprint('SUSPEND START') 4936 pprint('SUSPEND START')
4614 else: 4937 else:
4615 pprint('SUSPEND START (press a key to resume)') 4938 pprint('SUSPEND START (press a key to resume)')
4939 sysvals.mcelog(True)
4616 bat1 = getBattery() if battery else False 4940 bat1 = getBattery() if battery else False
4617 # set rtcwake 4941 # set rtcwake
4618 if(sysvals.rtcwake): 4942 if(sysvals.rtcwake):
@@ -4644,13 +4968,23 @@ def executeSuspend():
4644 pf = open(sysvals.diskpowerfile, 'w') 4968 pf = open(sysvals.diskpowerfile, 'w')
4645 pf.write(sysvals.diskmode) 4969 pf.write(sysvals.diskmode)
4646 pf.close() 4970 pf.close()
4647 pf = open(sysvals.powerfile, 'w') 4971 if mode == 'freeze' and sysvals.haveTurbostat():
4648 pf.write(mode) 4972 # execution will pause here
4649 # execution will pause here 4973 turbo = sysvals.turbostat()
4650 try: 4974 if '|' in turbo:
4651 pf.close() 4975 tdata['turbo'] = turbo
4652 except Exception as e: 4976 else:
4653 tdata['error'] = str(e) 4977 tdata['error'] = turbo
4978 else:
4979 if sysvals.haveTurbostat():
4980 sysvals.vprint('WARNING: ignoring turbostat in mode "%s"' % mode)
4981 pf = open(sysvals.powerfile, 'w')
4982 pf.write(mode)
4983 # execution will pause here
4984 try:
4985 pf.close()
4986 except Exception as e:
4987 tdata['error'] = str(e)
4654 if(sysvals.rtcwake): 4988 if(sysvals.rtcwake):
4655 sysvals.rtcWakeAlarmOff() 4989 sysvals.rtcWakeAlarmOff()
4656 # postdelay delay 4990 # postdelay delay
@@ -4664,9 +4998,14 @@ def executeSuspend():
4664 sysvals.fsetVal('RESUME COMPLETE', 'trace_marker') 4998 sysvals.fsetVal('RESUME COMPLETE', 'trace_marker')
4665 if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'): 4999 if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'):
4666 tdata['fw'] = getFPDT(False) 5000 tdata['fw'] = getFPDT(False)
5001 mcelog = sysvals.mcelog()
5002 if mcelog:
5003 tdata['mcelog'] = mcelog
4667 bat2 = getBattery() if battery else False 5004 bat2 = getBattery() if battery else False
4668 if battery and bat1 and bat2: 5005 if battery and bat1 and bat2:
4669 tdata['bat'] = (bat1, bat2) 5006 tdata['bat'] = (bat1, bat2)
5007 if 'device' in wifi and 'ip' in wifi:
5008 tdata['wifi'] = (wifi, sysvals.checkWifi())
4670 testdata.append(tdata) 5009 testdata.append(tdata)
4671 # stop ftrace 5010 # stop ftrace
4672 if(sysvals.usecallgraph or sysvals.usetraceevents): 5011 if(sysvals.usecallgraph or sysvals.usetraceevents):
@@ -4686,6 +5025,7 @@ def executeSuspend():
4686 op.close() 5025 op.close()
4687 sysvals.fsetVal('', 'trace') 5026 sysvals.fsetVal('', 'trace')
4688 devProps() 5027 devProps()
5028 return testdata
4689 5029
4690def readFile(file): 5030def readFile(file):
4691 if os.path.islink(file): 5031 if os.path.islink(file):
@@ -4772,7 +5112,7 @@ def deviceInfo(output=''):
4772 ms2nice(power['runtime_active_time']), \ 5112 ms2nice(power['runtime_active_time']), \
4773 ms2nice(power['runtime_suspended_time'])) 5113 ms2nice(power['runtime_suspended_time']))
4774 for i in sorted(lines): 5114 for i in sorted(lines):
4775 print lines[i] 5115 print(lines[i])
4776 return res 5116 return res
4777 5117
4778# Function: devProps 5118# Function: devProps
@@ -4905,12 +5245,12 @@ def getModes():
4905 modes = [] 5245 modes = []
4906 if(os.path.exists(sysvals.powerfile)): 5246 if(os.path.exists(sysvals.powerfile)):
4907 fp = open(sysvals.powerfile, 'r') 5247 fp = open(sysvals.powerfile, 'r')
4908 modes = string.split(fp.read()) 5248 modes = fp.read().split()
4909 fp.close() 5249 fp.close()
4910 if(os.path.exists(sysvals.mempowerfile)): 5250 if(os.path.exists(sysvals.mempowerfile)):
4911 deep = False 5251 deep = False
4912 fp = open(sysvals.mempowerfile, 'r') 5252 fp = open(sysvals.mempowerfile, 'r')
4913 for m in string.split(fp.read()): 5253 for m in fp.read().split():
4914 memmode = m.strip('[]') 5254 memmode = m.strip('[]')
4915 if memmode == 'deep': 5255 if memmode == 'deep':
4916 deep = True 5256 deep = True
@@ -4921,7 +5261,7 @@ def getModes():
4921 modes.remove('mem') 5261 modes.remove('mem')
4922 if('disk' in modes and os.path.exists(sysvals.diskpowerfile)): 5262 if('disk' in modes and os.path.exists(sysvals.diskpowerfile)):
4923 fp = open(sysvals.diskpowerfile, 'r') 5263 fp = open(sysvals.diskpowerfile, 'r')
4924 for m in string.split(fp.read()): 5264 for m in fp.read().split():
4925 modes.append('disk-%s' % m.strip('[]')) 5265 modes.append('disk-%s' % m.strip('[]'))
4926 fp.close() 5266 fp.close()
4927 return modes 5267 return modes
@@ -4984,14 +5324,15 @@ def dmidecode(mempath, fatal=False):
4984 continue 5324 continue
4985 5325
4986 # read in the memory for scanning 5326 # read in the memory for scanning
4987 fp = open(mempath, 'rb')
4988 try: 5327 try:
5328 fp = open(mempath, 'rb')
4989 fp.seek(memaddr) 5329 fp.seek(memaddr)
4990 buf = fp.read(memsize) 5330 buf = fp.read(memsize)
4991 except: 5331 except:
4992 if(fatal): 5332 if(fatal):
4993 doError('DMI table is unreachable, sorry') 5333 doError('DMI table is unreachable, sorry')
4994 else: 5334 else:
5335 pprint('WARNING: /dev/mem is not readable, ignoring DMI data')
4995 return out 5336 return out
4996 fp.close() 5337 fp.close()
4997 5338
@@ -5014,14 +5355,15 @@ def dmidecode(mempath, fatal=False):
5014 return out 5355 return out
5015 5356
5016 # read in the SM or DMI table 5357 # read in the SM or DMI table
5017 fp = open(mempath, 'rb')
5018 try: 5358 try:
5359 fp = open(mempath, 'rb')
5019 fp.seek(base) 5360 fp.seek(base)
5020 buf = fp.read(length) 5361 buf = fp.read(length)
5021 except: 5362 except:
5022 if(fatal): 5363 if(fatal):
5023 doError('DMI table is unreachable, sorry') 5364 doError('DMI table is unreachable, sorry')
5024 else: 5365 else:
5366 pprint('WARNING: /dev/mem is not readable, ignoring DMI data')
5025 return out 5367 return out
5026 fp.close() 5368 fp.close()
5027 5369
@@ -5165,7 +5507,11 @@ def getFPDT(output):
5165 i = 0 5507 i = 0
5166 fwData = [0, 0] 5508 fwData = [0, 0]
5167 records = buf[36:] 5509 records = buf[36:]
5168 fp = open(sysvals.mempath, 'rb') 5510 try:
5511 fp = open(sysvals.mempath, 'rb')
5512 except:
5513 pprint('WARNING: /dev/mem is not readable, ignoring the FPDT data')
5514 return False
5169 while(i < len(records)): 5515 while(i < len(records)):
5170 header = struct.unpack('HBB', records[i:i+4]) 5516 header = struct.unpack('HBB', records[i:i+4])
5171 if(header[0] not in rectype): 5517 if(header[0] not in rectype):
@@ -5282,13 +5628,14 @@ def statusCheck(probecheck=False):
5282 pprint(' is ftrace supported: %s' % res) 5628 pprint(' is ftrace supported: %s' % res)
5283 5629
5284 # check if kprobes are available 5630 # check if kprobes are available
5285 res = sysvals.colorText('NO') 5631 if sysvals.usekprobes:
5286 sysvals.usekprobes = sysvals.verifyKprobes() 5632 res = sysvals.colorText('NO')
5287 if(sysvals.usekprobes): 5633 sysvals.usekprobes = sysvals.verifyKprobes()
5288 res = 'YES' 5634 if(sysvals.usekprobes):
5289 else: 5635 res = 'YES'
5290 sysvals.usedevsrc = False 5636 else:
5291 pprint(' are kprobes supported: %s' % res) 5637 sysvals.usedevsrc = False
5638 pprint(' are kprobes supported: %s' % res)
5292 5639
5293 # what data source are we using 5640 # what data source are we using
5294 res = 'DMESG' 5641 res = 'DMESG'
@@ -5376,6 +5723,8 @@ def getArgFloat(name, args, min, max, main=True):
5376 5723
5377def processData(live=False): 5724def processData(live=False):
5378 pprint('PROCESSING DATA') 5725 pprint('PROCESSING DATA')
5726 sysvals.vprint('usetraceevents=%s, usetracemarkers=%s, usekprobes=%s' % \
5727 (sysvals.usetraceevents, sysvals.usetracemarkers, sysvals.usekprobes))
5379 error = '' 5728 error = ''
5380 if(sysvals.usetraceevents): 5729 if(sysvals.usetraceevents):
5381 testruns, error = parseTraceLog(live) 5730 testruns, error = parseTraceLog(live)
@@ -5388,13 +5737,36 @@ def processData(live=False):
5388 parseKernelLog(data) 5737 parseKernelLog(data)
5389 if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)): 5738 if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)):
5390 appendIncompleteTraceLog(testruns) 5739 appendIncompleteTraceLog(testruns)
5740 sysvals.vprint('System Info:')
5741 for key in sorted(sysvals.stamp):
5742 sysvals.vprint(' %-8s : %s' % (key.upper(), sysvals.stamp[key]))
5743 if sysvals.kparams:
5744 sysvals.vprint('Kparams:\n %s' % sysvals.kparams)
5391 sysvals.vprint('Command:\n %s' % sysvals.cmdline) 5745 sysvals.vprint('Command:\n %s' % sysvals.cmdline)
5392 for data in testruns: 5746 for data in testruns:
5747 if data.mcelog:
5748 sysvals.vprint('MCELOG Data:')
5749 for line in data.mcelog.split('\n'):
5750 sysvals.vprint(' %s' % line)
5751 if data.turbostat:
5752 idx, s = 0, 'Turbostat:\n '
5753 for val in data.turbostat.split('|'):
5754 idx += len(val) + 1
5755 if idx >= 80:
5756 idx = 0
5757 s += '\n '
5758 s += val + ' '
5759 sysvals.vprint(s)
5393 if data.battery: 5760 if data.battery:
5394 a1, c1, a2, c2 = data.battery 5761 a1, c1, a2, c2 = data.battery
5395 s = 'Battery:\n Before - AC: %s, Charge: %d\n After - AC: %s, Charge: %d' % \ 5762 s = 'Battery:\n Before - AC: %s, Charge: %d\n After - AC: %s, Charge: %d' % \
5396 (a1, int(c1), a2, int(c2)) 5763 (a1, int(c1), a2, int(c2))
5397 sysvals.vprint(s) 5764 sysvals.vprint(s)
5765 if data.wifi:
5766 w = data.wifi.replace('|', ' ').split(',')
5767 s = 'Wifi:\n Before %s\n After %s' % \
5768 (w[0], w[1])
5769 sysvals.vprint(s)
5398 data.printDetails() 5770 data.printDetails()
5399 if sysvals.cgdump: 5771 if sysvals.cgdump:
5400 for data in testruns: 5772 for data in testruns:
@@ -5418,12 +5790,15 @@ def processData(live=False):
5418# Function: rerunTest 5790# Function: rerunTest
5419# Description: 5791# Description:
5420# generate an output from an existing set of ftrace/dmesg logs 5792# generate an output from an existing set of ftrace/dmesg logs
5421def rerunTest(): 5793def rerunTest(htmlfile=''):
5422 if sysvals.ftracefile: 5794 if sysvals.ftracefile:
5423 doesTraceLogHaveTraceEvents() 5795 doesTraceLogHaveTraceEvents()
5424 if not sysvals.dmesgfile and not sysvals.usetraceevents: 5796 if not sysvals.dmesgfile and not sysvals.usetraceevents:
5425 doError('recreating this html output requires a dmesg file') 5797 doError('recreating this html output requires a dmesg file')
5426 sysvals.setOutputFile() 5798 if htmlfile:
5799 sysvals.htmlfile = htmlfile
5800 else:
5801 sysvals.setOutputFile()
5427 if os.path.exists(sysvals.htmlfile): 5802 if os.path.exists(sysvals.htmlfile):
5428 if not os.path.isfile(sysvals.htmlfile): 5803 if not os.path.isfile(sysvals.htmlfile):
5429 doError('a directory already exists with this name: %s' % sysvals.htmlfile) 5804 doError('a directory already exists with this name: %s' % sysvals.htmlfile)
@@ -5442,14 +5817,18 @@ def runTest(n=0):
5442 sysvals.initTestOutput('suspend') 5817 sysvals.initTestOutput('suspend')
5443 5818
5444 # execute the test 5819 # execute the test
5445 executeSuspend() 5820 testdata = executeSuspend()
5446 sysvals.cleanupFtrace() 5821 sysvals.cleanupFtrace()
5447 if sysvals.skiphtml: 5822 if sysvals.skiphtml:
5448 sysvals.sudoUserchown(sysvals.testdir) 5823 sysvals.sudoUserchown(sysvals.testdir)
5449 return 5824 return
5450 testruns, stamp = processData(True) 5825 if not testdata[0]['error']:
5451 for data in testruns: 5826 testruns, stamp = processData(True)
5452 del data 5827 for data in testruns:
5828 del data
5829 else:
5830 stamp = testdata[0]
5831
5453 sysvals.sudoUserchown(sysvals.testdir) 5832 sysvals.sudoUserchown(sysvals.testdir)
5454 sysvals.outputResult(stamp, n) 5833 sysvals.outputResult(stamp, n)
5455 if 'error' in stamp: 5834 if 'error' in stamp:
@@ -5479,10 +5858,13 @@ def find_in_html(html, start, end, firstonly=True):
5479 return '' 5858 return ''
5480 return out 5859 return out
5481 5860
5482def data_from_html(file, outpath, devlist=False): 5861def data_from_html(file, outpath, issues, fulldetail=False):
5483 html = open(file, 'r').read() 5862 html = open(file, 'r').read()
5863 sysvals.htmlfile = os.path.relpath(file, outpath)
5864 # extract general info
5484 suspend = find_in_html(html, 'Kernel Suspend', 'ms') 5865 suspend = find_in_html(html, 'Kernel Suspend', 'ms')
5485 resume = find_in_html(html, 'Kernel Resume', 'ms') 5866 resume = find_in_html(html, 'Kernel Resume', 'ms')
5867 sysinfo = find_in_html(html, '<div class="stamp sysinfo">', '</div>')
5486 line = find_in_html(html, '<div class="stamp">', '</div>') 5868 line = find_in_html(html, '<div class="stamp">', '</div>')
5487 stmp = line.split() 5869 stmp = line.split()
5488 if not suspend or not resume or len(stmp) != 8: 5870 if not suspend or not resume or len(stmp) != 8:
@@ -5491,6 +5873,7 @@ def data_from_html(file, outpath, devlist=False):
5491 dt = datetime.strptime(' '.join(stmp[3:]), '%B %d %Y, %I:%M:%S %p') 5873 dt = datetime.strptime(' '.join(stmp[3:]), '%B %d %Y, %I:%M:%S %p')
5492 except: 5874 except:
5493 return False 5875 return False
5876 sysvals.hostname = stmp[0]
5494 tstr = dt.strftime('%Y/%m/%d %H:%M:%S') 5877 tstr = dt.strftime('%Y/%m/%d %H:%M:%S')
5495 error = find_in_html(html, '<table class="testfail"><tr><td>', '</td>') 5878 error = find_in_html(html, '<table class="testfail"><tr><td>', '</td>')
5496 if error: 5879 if error:
@@ -5501,13 +5884,45 @@ def data_from_html(file, outpath, devlist=False):
5501 result = 'fail' 5884 result = 'fail'
5502 else: 5885 else:
5503 result = 'pass' 5886 result = 'pass'
5887 # extract error info
5504 ilist = [] 5888 ilist = []
5505 e = find_in_html(html, 'class="err"[\w=":;\.%\- ]*>', '&rarr;</div>', False) 5889 extra = dict()
5506 for i in list(set(e)): 5890 log = find_in_html(html, '<div id="dmesglog" style="display:none;">',
5507 ilist.append('%sx%d' % (i, e.count(i)) if e.count(i) > 1 else i) 5891 '</div>').strip()
5892 if log:
5893 d = Data(0)
5894 d.end = 999999999
5895 d.dmesgtext = log.split('\n')
5896 msglist = d.extractErrorInfo()
5897 for msg in msglist:
5898 sysvals.errorSummary(issues, msg)
5899 if stmp[2] == 'freeze':
5900 extra = d.turbostatInfo()
5901 elist = dict()
5902 for dir in d.errorinfo:
5903 for err in d.errorinfo[dir]:
5904 if err[0] not in elist:
5905 elist[err[0]] = 0
5906 elist[err[0]] += 1
5907 for i in elist:
5908 ilist.append('%sx%d' % (i, elist[i]) if elist[i] > 1 else i)
5508 low = find_in_html(html, 'freeze time: <b>', ' ms</b>') 5909 low = find_in_html(html, 'freeze time: <b>', ' ms</b>')
5509 if low and '|' in low: 5910 if low and '|' in low:
5510 ilist.append('FREEZEx%d' % len(low.split('|'))) 5911 issue = 'FREEZEx%d' % len(low.split('|'))
5912 match = [i for i in issues if i['match'] == issue]
5913 if len(match) > 0:
5914 match[0]['count'] += 1
5915 if sysvals.hostname not in match[0]['urls']:
5916 match[0]['urls'][sysvals.hostname] = [sysvals.htmlfile]
5917 elif sysvals.htmlfile not in match[0]['urls'][sysvals.hostname]:
5918 match[0]['urls'][sysvals.hostname].append(sysvals.htmlfile)
5919 else:
5920 issues.append({
5921 'match': issue, 'count': 1, 'line': issue,
5922 'urls': {sysvals.hostname: [sysvals.htmlfile]},
5923 })
5924 ilist.append(issue)
5925 # extract device info
5511 devices = dict() 5926 devices = dict()
5512 for line in html.split('\n'): 5927 for line in html.split('\n'):
5513 m = re.match(' *<div id=\"[a,0-9]*\" *title=\"(?P<title>.*)\" class=\"thread.*', line) 5928 m = re.match(' *<div id=\"[a,0-9]*\" *title=\"(?P<title>.*)\" class=\"thread.*', line)
@@ -5519,82 +5934,98 @@ def data_from_html(file, outpath, devlist=False):
5519 name, time, phase = m.group('n'), m.group('t'), m.group('p') 5934 name, time, phase = m.group('n'), m.group('t'), m.group('p')
5520 if ' async' in name or ' sync' in name: 5935 if ' async' in name or ' sync' in name:
5521 name = ' '.join(name.split(' ')[:-1]) 5936 name = ' '.join(name.split(' ')[:-1])
5522 d = phase.split('_')[0] 5937 if phase.startswith('suspend'):
5938 d = 'suspend'
5939 elif phase.startswith('resume'):
5940 d = 'resume'
5941 else:
5942 continue
5523 if d not in devices: 5943 if d not in devices:
5524 devices[d] = dict() 5944 devices[d] = dict()
5525 if name not in devices[d]: 5945 if name not in devices[d]:
5526 devices[d][name] = 0.0 5946 devices[d][name] = 0.0
5527 devices[d][name] += float(time) 5947 devices[d][name] += float(time)
5528 worst = {'suspend': {'name':'', 'time': 0.0}, 5948 # create worst device info
5529 'resume': {'name':'', 'time': 0.0}} 5949 worst = dict()
5530 for d in devices: 5950 for d in ['suspend', 'resume']:
5531 if d not in worst: 5951 worst[d] = {'name':'', 'time': 0.0}
5532 worst[d] = dict() 5952 dev = devices[d] if d in devices else 0
5533 dev = devices[d] 5953 if dev and len(dev.keys()) > 0:
5534 if len(dev.keys()) > 0:
5535 n = sorted(dev, key=dev.get, reverse=True)[0] 5954 n = sorted(dev, key=dev.get, reverse=True)[0]
5536 worst[d]['name'], worst[d]['time'] = n, dev[n] 5955 worst[d]['name'], worst[d]['time'] = n, dev[n]
5537 data = { 5956 data = {
5538 'mode': stmp[2], 5957 'mode': stmp[2],
5539 'host': stmp[0], 5958 'host': stmp[0],
5540 'kernel': stmp[1], 5959 'kernel': stmp[1],
5960 'sysinfo': sysinfo,
5541 'time': tstr, 5961 'time': tstr,
5542 'result': result, 5962 'result': result,
5543 'issues': ' '.join(ilist), 5963 'issues': ' '.join(ilist),
5544 'suspend': suspend, 5964 'suspend': suspend,
5545 'resume': resume, 5965 'resume': resume,
5966 'devlist': devices,
5546 'sus_worst': worst['suspend']['name'], 5967 'sus_worst': worst['suspend']['name'],
5547 'sus_worsttime': worst['suspend']['time'], 5968 'sus_worsttime': worst['suspend']['time'],
5548 'res_worst': worst['resume']['name'], 5969 'res_worst': worst['resume']['name'],
5549 'res_worsttime': worst['resume']['time'], 5970 'res_worsttime': worst['resume']['time'],
5550 'url': os.path.relpath(file, outpath), 5971 'url': sysvals.htmlfile,
5551 } 5972 }
5552 if devlist: 5973 for key in extra:
5553 data['devlist'] = devices 5974 data[key] = extra[key]
5975 if fulldetail:
5976 data['funclist'] = find_in_html(html, '<div title="', '" class="traceevent"', False)
5554 return data 5977 return data
5555 5978
5979def genHtml(subdir):
5980 for dirname, dirnames, filenames in os.walk(subdir):
5981 sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = ''
5982 for filename in filenames:
5983 if(re.match('.*_dmesg.txt', filename)):
5984 sysvals.dmesgfile = os.path.join(dirname, filename)
5985 elif(re.match('.*_ftrace.txt', filename)):
5986 sysvals.ftracefile = os.path.join(dirname, filename)
5987 sysvals.setOutputFile()
5988 if sysvals.ftracefile and sysvals.htmlfile and \
5989 not os.path.exists(sysvals.htmlfile):
5990 pprint('FTRACE: %s' % sysvals.ftracefile)
5991 if sysvals.dmesgfile:
5992 pprint('DMESG : %s' % sysvals.dmesgfile)
5993 rerunTest()
5994
5556# Function: runSummary 5995# Function: runSummary
5557# Description: 5996# Description:
5558# create a summary of tests in a sub-directory 5997# create a summary of tests in a sub-directory
5559def runSummary(subdir, local=True, genhtml=False): 5998def runSummary(subdir, local=True, genhtml=False):
5560 inpath = os.path.abspath(subdir) 5999 inpath = os.path.abspath(subdir)
5561 outpath = os.path.abspath('.') if local else inpath 6000 outpath = os.path.abspath('.') if local else inpath
5562 pprint('Generating a summary of folder "%s"' % inpath) 6001 pprint('Generating a summary of folder:\n %s' % inpath)
5563 if genhtml: 6002 if genhtml:
5564 for dirname, dirnames, filenames in os.walk(subdir): 6003 genHtml(subdir)
5565 sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = '' 6004 issues = []
5566 for filename in filenames:
5567 if(re.match('.*_dmesg.txt', filename)):
5568 sysvals.dmesgfile = os.path.join(dirname, filename)
5569 elif(re.match('.*_ftrace.txt', filename)):
5570 sysvals.ftracefile = os.path.join(dirname, filename)
5571 sysvals.setOutputFile()
5572 if sysvals.ftracefile and sysvals.htmlfile and \
5573 not os.path.exists(sysvals.htmlfile):
5574 pprint('FTRACE: %s' % sysvals.ftracefile)
5575 if sysvals.dmesgfile:
5576 pprint('DMESG : %s' % sysvals.dmesgfile)
5577 rerunTest()
5578 testruns = [] 6005 testruns = []
5579 desc = {'host':[],'mode':[],'kernel':[]} 6006 desc = {'host':[],'mode':[],'kernel':[]}
5580 for dirname, dirnames, filenames in os.walk(subdir): 6007 for dirname, dirnames, filenames in os.walk(subdir):
5581 for filename in filenames: 6008 for filename in filenames:
5582 if(not re.match('.*.html', filename)): 6009 if(not re.match('.*.html', filename)):
5583 continue 6010 continue
5584 data = data_from_html(os.path.join(dirname, filename), outpath) 6011 data = data_from_html(os.path.join(dirname, filename), outpath, issues)
5585 if(not data): 6012 if(not data):
5586 continue 6013 continue
5587 testruns.append(data) 6014 testruns.append(data)
5588 for key in desc: 6015 for key in desc:
5589 if data[key] not in desc[key]: 6016 if data[key] not in desc[key]:
5590 desc[key].append(data[key]) 6017 desc[key].append(data[key])
5591 outfile = os.path.join(outpath, 'summary.html') 6018 pprint('Summary files:')
5592 pprint('Summary file: %s' % outfile)
5593 if len(desc['host']) == len(desc['mode']) == len(desc['kernel']) == 1: 6019 if len(desc['host']) == len(desc['mode']) == len(desc['kernel']) == 1:
5594 title = '%s %s %s' % (desc['host'][0], desc['kernel'][0], desc['mode'][0]) 6020 title = '%s %s %s' % (desc['host'][0], desc['kernel'][0], desc['mode'][0])
5595 else: 6021 else:
5596 title = inpath 6022 title = inpath
5597 createHTMLSummarySimple(testruns, outfile, title) 6023 createHTMLSummarySimple(testruns, os.path.join(outpath, 'summary.html'), title)
6024 pprint(' summary.html - tabular list of test data found')
6025 createHTMLDeviceSummary(testruns, os.path.join(outpath, 'summary-devices.html'), title)
6026 pprint(' summary-devices.html - kernel device list sorted by total execution time')
6027 createHTMLIssuesSummary(testruns, issues, os.path.join(outpath, 'summary-issues.html'), title)
6028 pprint(' summary-issues.html - kernel issues found sorted by frequency')
5598 6029
5599# Function: checkArgBool 6030# Function: checkArgBool
5600# Description: 6031# Description:
@@ -5839,6 +6270,7 @@ def printHelp():
5839 ' default: suspend-{date}-{time}\n'\ 6270 ' default: suspend-{date}-{time}\n'\
5840 ' -rtcwake t Wakeup t seconds after suspend, set t to "off" to disable (default: 15)\n'\ 6271 ' -rtcwake t Wakeup t seconds after suspend, set t to "off" to disable (default: 15)\n'\
5841 ' -addlogs Add the dmesg and ftrace logs to the html output\n'\ 6272 ' -addlogs Add the dmesg and ftrace logs to the html output\n'\
6273 ' -turbostat Use turbostat to execute the command in freeze mode (default: disabled)\n'\
5842 ' -srgap Add a visible gap in the timeline between sus/res (default: disabled)\n'\ 6274 ' -srgap Add a visible gap in the timeline between sus/res (default: disabled)\n'\
5843 ' -skiphtml Run the test and capture the trace logs, but skip the timeline (default: disabled)\n'\ 6275 ' -skiphtml Run the test and capture the trace logs, but skip the timeline (default: disabled)\n'\
5844 ' -result fn Export a results table to a text file for parsing.\n'\ 6276 ' -result fn Export a results table to a text file for parsing.\n'\
@@ -5860,6 +6292,7 @@ def printHelp():
5860 ' be created in a new subdirectory with a summary page.\n'\ 6292 ' be created in a new subdirectory with a summary page.\n'\
5861 ' [debug]\n'\ 6293 ' [debug]\n'\
5862 ' -f Use ftrace to create device callgraphs (default: disabled)\n'\ 6294 ' -f Use ftrace to create device callgraphs (default: disabled)\n'\
6295 ' -ftop Use ftrace on the top level call: "%s" (default: disabled)\n'\
5863 ' -maxdepth N limit the callgraph data to N call levels (default: 0=all)\n'\ 6296 ' -maxdepth N limit the callgraph data to N call levels (default: 0=all)\n'\
5864 ' -expandcg pre-expand the callgraph data in the html output (default: disabled)\n'\ 6297 ' -expandcg pre-expand the callgraph data in the html output (default: disabled)\n'\
5865 ' -fadd file Add functions to be graphed in the timeline from a list in a text file\n'\ 6298 ' -fadd file Add functions to be graphed in the timeline from a list in a text file\n'\
@@ -5879,6 +6312,7 @@ def printHelp():
5879 ' -status Test to see if the system is enabled to run this tool\n'\ 6312 ' -status Test to see if the system is enabled to run this tool\n'\
5880 ' -fpdt Print out the contents of the ACPI Firmware Performance Data Table\n'\ 6313 ' -fpdt Print out the contents of the ACPI Firmware Performance Data Table\n'\
5881 ' -battery Print out battery info (if available)\n'\ 6314 ' -battery Print out battery info (if available)\n'\
6315 ' -wifi Print out wifi connection info (if wireless-tools and device exists)\n'\
5882 ' -x<mode> Test xset by toggling the given mode (on/off/standby/suspend)\n'\ 6316 ' -x<mode> Test xset by toggling the given mode (on/off/standby/suspend)\n'\
5883 ' -sysinfo Print out system info extracted from BIOS\n'\ 6317 ' -sysinfo Print out system info extracted from BIOS\n'\
5884 ' -devinfo Print out the pm settings of all devices which support runtime suspend\n'\ 6318 ' -devinfo Print out the pm settings of all devices which support runtime suspend\n'\
@@ -5888,7 +6322,7 @@ def printHelp():
5888 ' [redo]\n'\ 6322 ' [redo]\n'\
5889 ' -ftrace ftracefile Create HTML output using ftrace input (used with -dmesg)\n'\ 6323 ' -ftrace ftracefile Create HTML output using ftrace input (used with -dmesg)\n'\
5890 ' -dmesg dmesgfile Create HTML output using dmesg (used with -ftrace)\n'\ 6324 ' -dmesg dmesgfile Create HTML output using dmesg (used with -ftrace)\n'\
5891 '' % (sysvals.title, sysvals.version, sysvals.suspendmode)) 6325 '' % (sysvals.title, sysvals.version, sysvals.suspendmode, sysvals.ftopfunc))
5892 return True 6326 return True
5893 6327
5894# ----------------- MAIN -------------------- 6328# ----------------- MAIN --------------------
@@ -5898,7 +6332,7 @@ if __name__ == '__main__':
5898 cmd = '' 6332 cmd = ''
5899 simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall', 6333 simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall',
5900 '-devinfo', '-status', '-battery', '-xon', '-xoff', '-xstandby', 6334 '-devinfo', '-status', '-battery', '-xon', '-xoff', '-xstandby',
5901 '-xsuspend', '-xinit', '-xreset', '-xstat'] 6335 '-xsuspend', '-xinit', '-xreset', '-xstat', '-wifi']
5902 if '-f' in sys.argv: 6336 if '-f' in sys.argv:
5903 sysvals.cgskip = sysvals.configFile('cgskip.txt') 6337 sysvals.cgskip = sysvals.configFile('cgskip.txt')
5904 # loop through the command line arguments 6338 # loop through the command line arguments
@@ -5930,6 +6364,10 @@ if __name__ == '__main__':
5930 sysvals.postdelay = getArgInt('-postdelay', args, 0, 60000) 6364 sysvals.postdelay = getArgInt('-postdelay', args, 0, 60000)
5931 elif(arg == '-f'): 6365 elif(arg == '-f'):
5932 sysvals.usecallgraph = True 6366 sysvals.usecallgraph = True
6367 elif(arg == '-ftop'):
6368 sysvals.usecallgraph = True
6369 sysvals.ftop = True
6370 sysvals.usekprobes = False
5933 elif(arg == '-skiphtml'): 6371 elif(arg == '-skiphtml'):
5934 sysvals.skiphtml = True 6372 sysvals.skiphtml = True
5935 elif(arg == '-cgdump'): 6373 elif(arg == '-cgdump'):
@@ -5940,10 +6378,16 @@ if __name__ == '__main__':
5940 genhtml = True 6378 genhtml = True
5941 elif(arg == '-addlogs'): 6379 elif(arg == '-addlogs'):
5942 sysvals.dmesglog = sysvals.ftracelog = True 6380 sysvals.dmesglog = sysvals.ftracelog = True
6381 elif(arg == '-nologs'):
6382 sysvals.dmesglog = sysvals.ftracelog = False
5943 elif(arg == '-addlogdmesg'): 6383 elif(arg == '-addlogdmesg'):
5944 sysvals.dmesglog = True 6384 sysvals.dmesglog = True
5945 elif(arg == '-addlogftrace'): 6385 elif(arg == '-addlogftrace'):
5946 sysvals.ftracelog = True 6386 sysvals.ftracelog = True
6387 elif(arg == '-turbostat'):
6388 sysvals.tstat = True
6389 if not sysvals.haveTurbostat():
6390 doError('Turbostat command not found')
5947 elif(arg == '-verbose'): 6391 elif(arg == '-verbose'):
5948 sysvals.verbose = True 6392 sysvals.verbose = True
5949 elif(arg == '-proc'): 6393 elif(arg == '-proc'):
@@ -6013,6 +6457,12 @@ if __name__ == '__main__':
6013 except: 6457 except:
6014 doError('No callgraph functions supplied', True) 6458 doError('No callgraph functions supplied', True)
6015 sysvals.setCallgraphFilter(val) 6459 sysvals.setCallgraphFilter(val)
6460 elif(arg == '-skipkprobe'):
6461 try:
6462 val = args.next()
6463 except:
6464 doError('No kprobe functions supplied', True)
6465 sysvals.skipKprobes(val)
6016 elif(arg == '-cgskip'): 6466 elif(arg == '-cgskip'):
6017 try: 6467 try:
6018 val = args.next() 6468 val = args.next()
@@ -6151,7 +6601,7 @@ if __name__ == '__main__':
6151 elif(cmd == 'devinfo'): 6601 elif(cmd == 'devinfo'):
6152 deviceInfo() 6602 deviceInfo()
6153 elif(cmd == 'modes'): 6603 elif(cmd == 'modes'):
6154 print getModes() 6604 pprint(getModes())
6155 elif(cmd == 'flist'): 6605 elif(cmd == 'flist'):
6156 sysvals.getFtraceFilterFunctions(True) 6606 sysvals.getFtraceFilterFunctions(True)
6157 elif(cmd == 'flistall'): 6607 elif(cmd == 'flistall'):
@@ -6163,11 +6613,18 @@ if __name__ == '__main__':
6163 ret = displayControl(cmd[1:]) 6613 ret = displayControl(cmd[1:])
6164 elif(cmd == 'xstat'): 6614 elif(cmd == 'xstat'):
6165 pprint('Display Status: %s' % displayControl('stat').upper()) 6615 pprint('Display Status: %s' % displayControl('stat').upper())
6616 elif(cmd == 'wifi'):
6617 out = sysvals.checkWifi()
6618 if 'device' not in out:
6619 pprint('WIFI interface not found')
6620 else:
6621 for key in sorted(out):
6622 pprint('%6s: %s' % (key.upper(), out[key]))
6166 sys.exit(ret) 6623 sys.exit(ret)
6167 6624
6168 # if instructed, re-analyze existing data files 6625 # if instructed, re-analyze existing data files
6169 if(sysvals.notestrun): 6626 if(sysvals.notestrun):
6170 stamp = rerunTest() 6627 stamp = rerunTest(sysvals.outdir)
6171 sysvals.outputResult(stamp) 6628 sysvals.outputResult(stamp)
6172 sys.exit(0) 6629 sys.exit(0)
6173 6630
@@ -6204,7 +6661,7 @@ if __name__ == '__main__':
6204 s = 'suspend-x%d' % sysvals.multitest['count'] 6661 s = 'suspend-x%d' % sysvals.multitest['count']
6205 sysvals.outdir = datetime.now().strftime(s+'-%y%m%d-%H%M%S') 6662 sysvals.outdir = datetime.now().strftime(s+'-%y%m%d-%H%M%S')
6206 if not os.path.isdir(sysvals.outdir): 6663 if not os.path.isdir(sysvals.outdir):
6207 os.mkdir(sysvals.outdir) 6664 os.makedirs(sysvals.outdir)
6208 for i in range(sysvals.multitest['count']): 6665 for i in range(sysvals.multitest['count']):
6209 if(i != 0): 6666 if(i != 0):
6210 pprint('Waiting %d seconds...' % (sysvals.multitest['delay'])) 6667 pprint('Waiting %d seconds...' % (sysvals.multitest['delay']))