diff options
Diffstat (limited to 'tools/power/pm-graph/sleepgraph.py')
-rwxr-xr-x | tools/power/pm-graph/sleepgraph.py | 857 |
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 | |||
53 | import gzip | 54 | import gzip |
54 | from threading import Thread | 55 | from threading import Thread |
55 | from subprocess import call, Popen, PIPE | 56 | from subprocess import call, Popen, PIPE |
57 | import base64 | ||
56 | 58 | ||
57 | def pprint(msg): | 59 | def 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 |
67 | class SystemValues: | 69 | class 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 | ||
825 | sysvals = SystemValues() | 975 | sysvals = SystemValues() |
826 | switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0'] | 976 | switchvalues = ['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. |
2507 | def doesTraceLogHaveTraceEvents(): | 2747 | def 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+' → '+cg.name, color, dev['id']) | 3717 | name+' → '+cg.name, color, dev['id']) |
3524 | hf.write('\n\n </section>\n') | 3718 | hf.write('\n\n </section>\n') |
3525 | 3719 | ||
3526 | # Function: createHTMLSummarySimple | 3720 | def 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 | ||
3531 | def 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 | ||
3745 | def 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 | ||
3906 | def 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 | |||
3971 | def 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 | |||
3693 | def ordinal(value): | 4015 | def 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, '_', ' ') | 4316 | name = phase.replace('_', ' ') |
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): | |||
4580 | def executeSuspend(): | 4902 | def 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 | ||
4690 | def readFile(file): | 5030 | def 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 | ||
5377 | def processData(live=False): | 5724 | def 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 |
5421 | def rerunTest(): | 5793 | def 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 | ||
5482 | def data_from_html(file, outpath, devlist=False): | 5861 | def 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=":;\.%\- ]*>', '→</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 | ||
5979 | def 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 |
5559 | def runSummary(subdir, local=True, genhtml=False): | 5998 | def 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'])) |