mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-15 16:24:13 +08:00
Update to pm-graph 5.3
sleepgraph: - add support for parsing kernel issues from timeline dmesg logs - with -summary, generate a summary-issues.html for kernel issues found - with -summary, generate a summary-devices.html for device callback times - when recreating a timeline, use -o to set the output html filename - capture mcelog data when hardware errors occur and store in log - add -turbostat option to capture power data during freeze Signed-off-by: Todd Brandt <todd.e.brandt@linux.intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
parent
cd6c84d8f0
commit
7673896a40
@ -61,6 +61,7 @@ import ConfigParser
|
|||||||
import gzip
|
import gzip
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from subprocess import call, Popen, PIPE
|
from subprocess import call, Popen, PIPE
|
||||||
|
import base64
|
||||||
|
|
||||||
def pprint(msg):
|
def pprint(msg):
|
||||||
print(msg)
|
print(msg)
|
||||||
@ -74,7 +75,7 @@ def pprint(msg):
|
|||||||
# store system values and test parameters
|
# store system values and test parameters
|
||||||
class SystemValues:
|
class SystemValues:
|
||||||
title = 'SleepGraph'
|
title = 'SleepGraph'
|
||||||
version = '5.2'
|
version = '5.3'
|
||||||
ansi = False
|
ansi = False
|
||||||
rs = 0
|
rs = 0
|
||||||
display = ''
|
display = ''
|
||||||
@ -199,6 +200,7 @@ class SystemValues:
|
|||||||
'usleep_range': { 'args_x86_64': {'min':'%di:s32', 'max':'%si:s32'}, 'ub': 1 },
|
'usleep_range': { 'args_x86_64': {'min':'%di:s32', 'max':'%si:s32'}, 'ub': 1 },
|
||||||
'mutex_lock_slowpath': { 'func':'__mutex_lock_slowpath', 'ub': 1 },
|
'mutex_lock_slowpath': { 'func':'__mutex_lock_slowpath', 'ub': 1 },
|
||||||
'acpi_os_stall': {'ub': 1},
|
'acpi_os_stall': {'ub': 1},
|
||||||
|
'rt_mutex_slowlock': {'ub': 1},
|
||||||
# ACPI
|
# ACPI
|
||||||
'acpi_resume_power_resources': {},
|
'acpi_resume_power_resources': {},
|
||||||
'acpi_ps_parse_aml': {},
|
'acpi_ps_parse_aml': {},
|
||||||
@ -344,10 +346,12 @@ class SystemValues:
|
|||||||
m = info['baseboard-manufacturer']
|
m = info['baseboard-manufacturer']
|
||||||
elif 'system-manufacturer' in info:
|
elif 'system-manufacturer' in info:
|
||||||
m = info['system-manufacturer']
|
m = info['system-manufacturer']
|
||||||
if 'baseboard-product-name' in info:
|
if 'system-product-name' in info:
|
||||||
p = info['baseboard-product-name']
|
|
||||||
elif 'system-product-name' in info:
|
|
||||||
p = info['system-product-name']
|
p = info['system-product-name']
|
||||||
|
elif 'baseboard-product-name' in info:
|
||||||
|
p = info['baseboard-product-name']
|
||||||
|
if m[:5].lower() == 'intel' and 'baseboard-product-name' in info:
|
||||||
|
p = info['baseboard-product-name']
|
||||||
if 'processor-version' in info:
|
if 'processor-version' in info:
|
||||||
c = info['processor-version']
|
c = info['processor-version']
|
||||||
if 'bios-version' in info:
|
if 'bios-version' in info:
|
||||||
@ -688,7 +692,8 @@ class SystemValues:
|
|||||||
if self.bufsize > 0:
|
if self.bufsize > 0:
|
||||||
tgtsize = self.bufsize
|
tgtsize = self.bufsize
|
||||||
elif self.usecallgraph or self.usedevsrc:
|
elif self.usecallgraph or self.usedevsrc:
|
||||||
bmax = (1*1024*1024) if self.suspendmode == 'disk' else (3*1024*1024)
|
bmax = (1*1024*1024) if self.suspendmode in ['disk', 'command'] \
|
||||||
|
else (3*1024*1024)
|
||||||
tgtsize = min(self.memfree, bmax)
|
tgtsize = min(self.memfree, bmax)
|
||||||
else:
|
else:
|
||||||
tgtsize = 65536
|
tgtsize = 65536
|
||||||
@ -776,6 +781,10 @@ class SystemValues:
|
|||||||
fw = test['fw']
|
fw = test['fw']
|
||||||
if(fw):
|
if(fw):
|
||||||
fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1]))
|
fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1]))
|
||||||
|
if 'mcelog' in test:
|
||||||
|
fp.write('# mcelog %s\n' % test['mcelog'])
|
||||||
|
if 'turbo' in test:
|
||||||
|
fp.write('# turbostat %s\n' % test['turbo'])
|
||||||
if 'bat' in test:
|
if 'bat' in test:
|
||||||
(a1, c1), (a2, c2) = test['bat']
|
(a1, c1), (a2, c2) = test['bat']
|
||||||
fp.write('# battery %s %d %s %d\n' % (a1, c1, a2, c2))
|
fp.write('# battery %s %d %s %d\n' % (a1, c1, a2, c2))
|
||||||
@ -829,6 +838,56 @@ class SystemValues:
|
|||||||
if isgz:
|
if isgz:
|
||||||
return gzip.open(filename, mode+'b')
|
return gzip.open(filename, mode+'b')
|
||||||
return open(filename, mode)
|
return open(filename, mode)
|
||||||
|
def mcelog(self, clear=False):
|
||||||
|
cmd = self.getExec('mcelog')
|
||||||
|
if not cmd:
|
||||||
|
return ''
|
||||||
|
if clear:
|
||||||
|
call(cmd+' > /dev/null 2>&1', shell=True)
|
||||||
|
return ''
|
||||||
|
fp = Popen([cmd], stdout=PIPE, stderr=PIPE).stdout
|
||||||
|
out = fp.read().strip()
|
||||||
|
fp.close()
|
||||||
|
if not out:
|
||||||
|
return ''
|
||||||
|
return base64.b64encode(out.encode('zlib'))
|
||||||
|
def haveTurbostat(self):
|
||||||
|
cmd = self.getExec('turbostat')
|
||||||
|
if not cmd:
|
||||||
|
return False
|
||||||
|
fp = Popen([cmd, '-v'], stdout=PIPE, stderr=PIPE).stderr
|
||||||
|
out = fp.read().strip()
|
||||||
|
fp.close()
|
||||||
|
return re.match('turbostat version [0-8.]* .*', out)
|
||||||
|
def turbostat(self):
|
||||||
|
cmd = self.getExec('turbostat')
|
||||||
|
if not cmd:
|
||||||
|
return 'missing turbostat executable'
|
||||||
|
outfile = '/tmp/pm-graph-turbostat.txt'
|
||||||
|
res = call('%s -o %s -q -S echo freeze > %s' % \
|
||||||
|
(cmd, outfile, self.powerfile), shell=True)
|
||||||
|
if res != 0:
|
||||||
|
return 'turbosat returned %d' % res
|
||||||
|
if not os.path.exists(outfile):
|
||||||
|
return 'turbostat output missing'
|
||||||
|
fp = open(outfile, 'r')
|
||||||
|
text = []
|
||||||
|
for line in fp:
|
||||||
|
if re.match('[0-9.]* sec', line):
|
||||||
|
continue
|
||||||
|
text.append(line.split())
|
||||||
|
fp.close()
|
||||||
|
if len(text) < 2:
|
||||||
|
return 'turbostat output format error'
|
||||||
|
out = []
|
||||||
|
for key in text[0]:
|
||||||
|
values = []
|
||||||
|
idx = text[0].index(key)
|
||||||
|
for line in text[1:]:
|
||||||
|
if len(line) > idx:
|
||||||
|
values.append(line[idx])
|
||||||
|
out.append('%s=%s' % (key, ','.join(values)))
|
||||||
|
return '|'.join(out)
|
||||||
|
|
||||||
sysvals = SystemValues()
|
sysvals = SystemValues()
|
||||||
switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0']
|
switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0']
|
||||||
@ -941,6 +1000,8 @@ class Data:
|
|||||||
self.outfile = ''
|
self.outfile = ''
|
||||||
self.kerror = False
|
self.kerror = False
|
||||||
self.battery = 0
|
self.battery = 0
|
||||||
|
self.turbostat = 0
|
||||||
|
self.mcelog = 0
|
||||||
self.enterfail = ''
|
self.enterfail = ''
|
||||||
self.currphase = ''
|
self.currphase = ''
|
||||||
self.pstl = dict() # process timeline
|
self.pstl = dict() # process timeline
|
||||||
@ -975,8 +1036,38 @@ class Data:
|
|||||||
if len(plist) < 1:
|
if len(plist) < 1:
|
||||||
return ''
|
return ''
|
||||||
return plist[-1]
|
return plist[-1]
|
||||||
def extractErrorInfo(self):
|
def errorSummary(self, errinfo, msg):
|
||||||
lf = sysvals.openlog(sysvals.dmesgfile, 'r')
|
found = False
|
||||||
|
for entry in errinfo:
|
||||||
|
if re.match(entry['match'], msg):
|
||||||
|
entry['count'] += 1
|
||||||
|
if sysvals.hostname not in entry['urls']:
|
||||||
|
entry['urls'][sysvals.hostname] = sysvals.htmlfile
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if found:
|
||||||
|
return
|
||||||
|
arr = msg.split()
|
||||||
|
for j in range(len(arr)):
|
||||||
|
if re.match('^[0-9\-\.]*$', arr[j]):
|
||||||
|
arr[j] = '[0-9\-\.]*'
|
||||||
|
else:
|
||||||
|
arr[j] = arr[j]\
|
||||||
|
.replace(']', '\]').replace('[', '\[').replace('.', '\.')\
|
||||||
|
.replace('+', '\+').replace('*', '\*').replace('(', '\(')\
|
||||||
|
.replace(')', '\)')
|
||||||
|
mstr = ' '.join(arr)
|
||||||
|
entry = {
|
||||||
|
'line': msg,
|
||||||
|
'match': mstr,
|
||||||
|
'count': 1,
|
||||||
|
'urls': {sysvals.hostname: sysvals.htmlfile}
|
||||||
|
}
|
||||||
|
errinfo.append(entry)
|
||||||
|
def extractErrorInfo(self, issues=0):
|
||||||
|
lf = self.dmesgtext
|
||||||
|
if len(self.dmesgtext) < 1 and sysvals.dmesgfile:
|
||||||
|
lf = sysvals.openlog(sysvals.dmesgfile, 'r')
|
||||||
i = 0
|
i = 0
|
||||||
list = []
|
list = []
|
||||||
for line in lf:
|
for line in lf:
|
||||||
@ -993,6 +1084,8 @@ class Data:
|
|||||||
if re.match(self.errlist[err], msg):
|
if re.match(self.errlist[err], msg):
|
||||||
list.append((err, dir, t, i, i))
|
list.append((err, dir, t, i, i))
|
||||||
self.kerror = True
|
self.kerror = True
|
||||||
|
if not isinstance(issues, int):
|
||||||
|
self.errorSummary(issues, msg)
|
||||||
break
|
break
|
||||||
for e in list:
|
for e in list:
|
||||||
type, dir, t, idx1, idx2 = e
|
type, dir, t, idx1, idx2 = e
|
||||||
@ -1000,7 +1093,8 @@ class Data:
|
|||||||
self.errorinfo[dir].append((type, t, idx1, idx2))
|
self.errorinfo[dir].append((type, t, idx1, idx2))
|
||||||
if self.kerror:
|
if self.kerror:
|
||||||
sysvals.dmesglog = True
|
sysvals.dmesglog = True
|
||||||
lf.close()
|
if len(self.dmesgtext) < 1 and sysvals.dmesgfile:
|
||||||
|
lf.close()
|
||||||
def setStart(self, time):
|
def setStart(self, time):
|
||||||
self.start = time
|
self.start = time
|
||||||
def setEnd(self, time):
|
def setEnd(self, time):
|
||||||
@ -2358,6 +2452,8 @@ class TestProps:
|
|||||||
'(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\
|
'(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\
|
||||||
' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$'
|
' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$'
|
||||||
batteryfmt = '^# battery (?P<a1>\w*) (?P<c1>\d*) (?P<a2>\w*) (?P<c2>\d*)'
|
batteryfmt = '^# battery (?P<a1>\w*) (?P<c1>\d*) (?P<a2>\w*) (?P<c2>\d*)'
|
||||||
|
tstatfmt = '^# turbostat (?P<t>\S*)'
|
||||||
|
mcelogfmt = '^# mcelog (?P<m>\S*)'
|
||||||
testerrfmt = '^# enter_sleep_error (?P<e>.*)'
|
testerrfmt = '^# enter_sleep_error (?P<e>.*)'
|
||||||
sysinfofmt = '^# sysinfo .*'
|
sysinfofmt = '^# sysinfo .*'
|
||||||
cmdlinefmt = '^# command \| (?P<cmd>.*)'
|
cmdlinefmt = '^# command \| (?P<cmd>.*)'
|
||||||
@ -2380,6 +2476,8 @@ class TestProps:
|
|||||||
self.cmdline = ''
|
self.cmdline = ''
|
||||||
self.kparams = ''
|
self.kparams = ''
|
||||||
self.testerror = []
|
self.testerror = []
|
||||||
|
self.mcelog = []
|
||||||
|
self.turbostat = []
|
||||||
self.battery = []
|
self.battery = []
|
||||||
self.fwdata = []
|
self.fwdata = []
|
||||||
self.ftrace_line_fmt = self.ftrace_line_fmt_nop
|
self.ftrace_line_fmt = self.ftrace_line_fmt_nop
|
||||||
@ -2394,6 +2492,38 @@ class TestProps:
|
|||||||
self.ftrace_line_fmt = self.ftrace_line_fmt_nop
|
self.ftrace_line_fmt = self.ftrace_line_fmt_nop
|
||||||
else:
|
else:
|
||||||
doError('Invalid tracer format: [%s]' % tracer)
|
doError('Invalid tracer format: [%s]' % tracer)
|
||||||
|
def decode(self, data):
|
||||||
|
try:
|
||||||
|
out = base64.b64decode(data).decode('zlib')
|
||||||
|
except:
|
||||||
|
out = data
|
||||||
|
return out
|
||||||
|
def stampInfo(self, line):
|
||||||
|
if re.match(self.stampfmt, line):
|
||||||
|
self.stamp = line
|
||||||
|
return True
|
||||||
|
elif re.match(self.sysinfofmt, line):
|
||||||
|
self.sysinfo = line
|
||||||
|
return True
|
||||||
|
elif re.match(self.cmdlinefmt, line):
|
||||||
|
self.cmdline = line
|
||||||
|
return True
|
||||||
|
elif re.match(self.mcelogfmt, line):
|
||||||
|
self.mcelog.append(line)
|
||||||
|
return True
|
||||||
|
elif re.match(self.tstatfmt, line):
|
||||||
|
self.turbostat.append(line)
|
||||||
|
return True
|
||||||
|
elif re.match(self.batteryfmt, line):
|
||||||
|
self.battery.append(line)
|
||||||
|
return True
|
||||||
|
elif re.match(self.testerrfmt, line):
|
||||||
|
self.testerror.append(line)
|
||||||
|
return True
|
||||||
|
elif re.match(self.firmwarefmt, line):
|
||||||
|
self.fwdata.append(line)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
def parseStamp(self, data, sv):
|
def parseStamp(self, data, sv):
|
||||||
# global test data
|
# global test data
|
||||||
m = re.match(self.stampfmt, self.stamp)
|
m = re.match(self.stampfmt, self.stamp)
|
||||||
@ -2436,9 +2566,21 @@ class TestProps:
|
|||||||
sv.stamp = data.stamp
|
sv.stamp = data.stamp
|
||||||
# firmware data
|
# firmware data
|
||||||
if sv.suspendmode == 'mem' and len(self.fwdata) > data.testnumber:
|
if sv.suspendmode == 'mem' and len(self.fwdata) > data.testnumber:
|
||||||
data.fwSuspend, data.fwResume = self.fwdata[data.testnumber]
|
m = re.match(self.firmwarefmt, self.fwdata[data.testnumber])
|
||||||
if(data.fwSuspend > 0 or data.fwResume > 0):
|
if m:
|
||||||
data.fwValid = True
|
data.fwSuspend, data.fwResume = int(m.group('s')), int(m.group('r'))
|
||||||
|
if(data.fwSuspend > 0 or data.fwResume > 0):
|
||||||
|
data.fwValid = True
|
||||||
|
# mcelog data
|
||||||
|
if len(self.mcelog) > data.testnumber:
|
||||||
|
m = re.match(self.mcelogfmt, self.mcelog[data.testnumber])
|
||||||
|
if m:
|
||||||
|
data.mcelog = self.decode(m.group('m'))
|
||||||
|
# turbostat data
|
||||||
|
if len(self.turbostat) > data.testnumber:
|
||||||
|
m = re.match(self.tstatfmt, self.turbostat[data.testnumber])
|
||||||
|
if m:
|
||||||
|
data.turbostat = m.group('t')
|
||||||
# battery data
|
# battery data
|
||||||
if len(self.battery) > data.testnumber:
|
if len(self.battery) > data.testnumber:
|
||||||
m = re.match(self.batteryfmt, self.battery[data.testnumber])
|
m = re.match(self.batteryfmt, self.battery[data.testnumber])
|
||||||
@ -2564,21 +2706,7 @@ def appendIncompleteTraceLog(testruns):
|
|||||||
for line in tf:
|
for line in tf:
|
||||||
# remove any latent carriage returns
|
# remove any latent carriage returns
|
||||||
line = line.replace('\r\n', '')
|
line = line.replace('\r\n', '')
|
||||||
# grab the stamp and sysinfo
|
if tp.stampInfo(line):
|
||||||
if re.match(tp.stampfmt, line):
|
|
||||||
tp.stamp = line
|
|
||||||
continue
|
|
||||||
elif re.match(tp.sysinfofmt, line):
|
|
||||||
tp.sysinfo = line
|
|
||||||
continue
|
|
||||||
elif re.match(tp.cmdlinefmt, line):
|
|
||||||
tp.cmdline = line
|
|
||||||
continue
|
|
||||||
elif re.match(tp.batteryfmt, line):
|
|
||||||
tp.battery.append(line)
|
|
||||||
continue
|
|
||||||
elif re.match(tp.testerrfmt, line):
|
|
||||||
tp.testerror.append(line)
|
|
||||||
continue
|
continue
|
||||||
# determine the trace data type (required for further parsing)
|
# determine the trace data type (required for further parsing)
|
||||||
m = re.match(tp.tracertypefmt, line)
|
m = re.match(tp.tracertypefmt, line)
|
||||||
@ -2701,26 +2829,7 @@ def parseTraceLog(live=False):
|
|||||||
for line in tf:
|
for line in tf:
|
||||||
# remove any latent carriage returns
|
# remove any latent carriage returns
|
||||||
line = line.replace('\r\n', '')
|
line = line.replace('\r\n', '')
|
||||||
# stamp and sysinfo lines
|
if tp.stampInfo(line):
|
||||||
if re.match(tp.stampfmt, line):
|
|
||||||
tp.stamp = line
|
|
||||||
continue
|
|
||||||
elif re.match(tp.sysinfofmt, line):
|
|
||||||
tp.sysinfo = line
|
|
||||||
continue
|
|
||||||
elif re.match(tp.cmdlinefmt, line):
|
|
||||||
tp.cmdline = line
|
|
||||||
continue
|
|
||||||
elif re.match(tp.batteryfmt, line):
|
|
||||||
tp.battery.append(line)
|
|
||||||
continue
|
|
||||||
elif re.match(tp.testerrfmt, line):
|
|
||||||
tp.testerror.append(line)
|
|
||||||
continue
|
|
||||||
# firmware line: pull out any firmware data
|
|
||||||
m = re.match(tp.firmwarefmt, line)
|
|
||||||
if(m):
|
|
||||||
tp.fwdata.append((int(m.group('s')), int(m.group('r'))))
|
|
||||||
continue
|
continue
|
||||||
# tracer type line: determine the trace data type
|
# tracer type line: determine the trace data type
|
||||||
m = re.match(tp.tracertypefmt, line)
|
m = re.match(tp.tracertypefmt, line)
|
||||||
@ -3141,25 +3250,7 @@ def loadKernelLog():
|
|||||||
idx = line.find('[')
|
idx = line.find('[')
|
||||||
if idx > 1:
|
if idx > 1:
|
||||||
line = line[idx:]
|
line = line[idx:]
|
||||||
# grab the stamp and sysinfo
|
if tp.stampInfo(line):
|
||||||
if re.match(tp.stampfmt, line):
|
|
||||||
tp.stamp = line
|
|
||||||
continue
|
|
||||||
elif re.match(tp.sysinfofmt, line):
|
|
||||||
tp.sysinfo = line
|
|
||||||
continue
|
|
||||||
elif re.match(tp.cmdlinefmt, line):
|
|
||||||
tp.cmdline = line
|
|
||||||
continue
|
|
||||||
elif re.match(tp.batteryfmt, line):
|
|
||||||
tp.battery.append(line)
|
|
||||||
continue
|
|
||||||
elif re.match(tp.testerrfmt, line):
|
|
||||||
tp.testerror.append(line)
|
|
||||||
continue
|
|
||||||
m = re.match(tp.firmwarefmt, line)
|
|
||||||
if(m):
|
|
||||||
tp.fwdata.append((int(m.group('s')), int(m.group('r'))))
|
|
||||||
continue
|
continue
|
||||||
m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
|
m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
|
||||||
if(not m):
|
if(not m):
|
||||||
@ -3531,22 +3622,16 @@ def addCallgraphs(sv, hf, data):
|
|||||||
name+' → '+cg.name, color, dev['id'])
|
name+' → '+cg.name, color, dev['id'])
|
||||||
hf.write('\n\n </section>\n')
|
hf.write('\n\n </section>\n')
|
||||||
|
|
||||||
# Function: createHTMLSummarySimple
|
def summaryCSS(title, center=True):
|
||||||
# Description:
|
tdcenter = 'text-align:center;' if center else ''
|
||||||
# Create summary html file for a series of tests
|
out = '<!DOCTYPE html>\n<html>\n<head>\n\
|
||||||
# Arguments:
|
|
||||||
# testruns: array of Data objects from parseTraceLog
|
|
||||||
def createHTMLSummarySimple(testruns, htmlfile, title):
|
|
||||||
# write the html header first (html head, css code, up to body start)
|
|
||||||
html = '<!DOCTYPE html>\n<html>\n<head>\n\
|
|
||||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\
|
<meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\
|
||||||
<title>SleepGraph Summary</title>\n\
|
<title>'+title+'</title>\n\
|
||||||
<style type=\'text/css\'>\n\
|
<style type=\'text/css\'>\n\
|
||||||
.stamp {width: 100%;text-align:center;background:#888;line-height:30px;color:white;font: 25px Arial;}\n\
|
.stamp {width: 100%;text-align:center;background:#888;line-height:30px;color:white;font: 25px Arial;}\n\
|
||||||
table {width:100%;border-collapse: collapse;}\n\
|
table {width:100%;border-collapse: collapse;border:1px solid;}\n\
|
||||||
.summary {border:1px solid;}\n\
|
|
||||||
th {border: 1px solid black;background:#222;color:white;}\n\
|
th {border: 1px solid black;background:#222;color:white;}\n\
|
||||||
td {font: 14px "Times New Roman";text-align: center;}\n\
|
td {font: 14px "Times New Roman";'+tdcenter+'}\n\
|
||||||
tr.head td {border: 1px solid black;background:#aaa;}\n\
|
tr.head td {border: 1px solid black;background:#aaa;}\n\
|
||||||
tr.alt {background-color:#ddd;}\n\
|
tr.alt {background-color:#ddd;}\n\
|
||||||
tr.notice {color:red;}\n\
|
tr.notice {color:red;}\n\
|
||||||
@ -3555,6 +3640,16 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
|
|||||||
.maxval {background-color:#FFBBBB;}\n\
|
.maxval {background-color:#FFBBBB;}\n\
|
||||||
.head a {color:#000;text-decoration: none;}\n\
|
.head a {color:#000;text-decoration: none;}\n\
|
||||||
</style>\n</head>\n<body>\n'
|
</style>\n</head>\n<body>\n'
|
||||||
|
return out
|
||||||
|
|
||||||
|
# Function: createHTMLSummarySimple
|
||||||
|
# Description:
|
||||||
|
# Create summary html file for a series of tests
|
||||||
|
# Arguments:
|
||||||
|
# testruns: array of Data objects from parseTraceLog
|
||||||
|
def createHTMLSummarySimple(testruns, htmlfile, title):
|
||||||
|
# write the html header first (html head, css code, up to body start)
|
||||||
|
html = summaryCSS('Summary - SleepGraph')
|
||||||
|
|
||||||
# extract the test data into list
|
# extract the test data into list
|
||||||
list = dict()
|
list = dict()
|
||||||
@ -3579,17 +3674,20 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
|
|||||||
tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [[], []]
|
tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [[], []]
|
||||||
iMin, iMed, iMax = [0, 0], [0, 0], [0, 0]
|
iMin, iMed, iMax = [0, 0], [0, 0], [0, 0]
|
||||||
num = 0
|
num = 0
|
||||||
|
res = data['result']
|
||||||
tVal = [float(data['suspend']), float(data['resume'])]
|
tVal = [float(data['suspend']), float(data['resume'])]
|
||||||
list[mode]['data'].append([data['host'], data['kernel'],
|
list[mode]['data'].append([data['host'], data['kernel'],
|
||||||
data['time'], tVal[0], tVal[1], data['url'], data['result'],
|
data['time'], tVal[0], tVal[1], data['url'], res,
|
||||||
data['issues'], data['sus_worst'], data['sus_worsttime'],
|
data['issues'], data['sus_worst'], data['sus_worsttime'],
|
||||||
data['res_worst'], data['res_worsttime']])
|
data['res_worst'], data['res_worsttime']])
|
||||||
idx = len(list[mode]['data']) - 1
|
idx = len(list[mode]['data']) - 1
|
||||||
if data['result'] not in cnt:
|
if res.startswith('fail in'):
|
||||||
cnt[data['result']] = 1
|
res = 'fail'
|
||||||
|
if res not in cnt:
|
||||||
|
cnt[res] = 1
|
||||||
else:
|
else:
|
||||||
cnt[data['result']] += 1
|
cnt[res] += 1
|
||||||
if data['result'] == 'pass':
|
if res == 'pass':
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
tMed[i].append(tVal[i])
|
tMed[i].append(tVal[i])
|
||||||
tAvg[i] += tVal[i]
|
tAvg[i] += tVal[i]
|
||||||
@ -3623,7 +3721,7 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
|
|||||||
tdlink = '\t<td><a href="{0}">html</a></td>\n'
|
tdlink = '\t<td><a href="{0}">html</a></td>\n'
|
||||||
|
|
||||||
# table header
|
# table header
|
||||||
html += '<table class="summary">\n<tr>\n' + th.format('#') +\
|
html += '<table>\n<tr>\n' + th.format('#') +\
|
||||||
th.format('Mode') + th.format('Host') + th.format('Kernel') +\
|
th.format('Mode') + th.format('Host') + th.format('Kernel') +\
|
||||||
th.format('Test Time') + th.format('Result') + th.format('Issues') +\
|
th.format('Test Time') + th.format('Result') + th.format('Issues') +\
|
||||||
th.format('Suspend') + th.format('Resume') +\
|
th.format('Suspend') + th.format('Resume') +\
|
||||||
@ -3698,6 +3796,104 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
|
|||||||
hf.write(html+'</table>\n</body>\n</html>\n')
|
hf.write(html+'</table>\n</body>\n</html>\n')
|
||||||
hf.close()
|
hf.close()
|
||||||
|
|
||||||
|
def createHTMLDeviceSummary(testruns, htmlfile, title):
|
||||||
|
html = summaryCSS('Device Summary - SleepGraph', False)
|
||||||
|
|
||||||
|
# create global device list from all tests
|
||||||
|
devall = dict()
|
||||||
|
for data in testruns:
|
||||||
|
host, url, devlist = data['host'], data['url'], data['devlist']
|
||||||
|
for type in devlist:
|
||||||
|
if type not in devall:
|
||||||
|
devall[type] = dict()
|
||||||
|
mdevlist, devlist = devall[type], data['devlist'][type]
|
||||||
|
for name in devlist:
|
||||||
|
length = devlist[name]
|
||||||
|
if name not in mdevlist:
|
||||||
|
mdevlist[name] = {'name': name, 'host': host,
|
||||||
|
'worst': length, 'total': length, 'count': 1,
|
||||||
|
'url': url}
|
||||||
|
else:
|
||||||
|
if length > mdevlist[name]['worst']:
|
||||||
|
mdevlist[name]['worst'] = length
|
||||||
|
mdevlist[name]['url'] = url
|
||||||
|
mdevlist[name]['host'] = host
|
||||||
|
mdevlist[name]['total'] += length
|
||||||
|
mdevlist[name]['count'] += 1
|
||||||
|
|
||||||
|
# generate the html
|
||||||
|
th = '\t<th>{0}</th>\n'
|
||||||
|
td = '\t<td align=center>{0}</td>\n'
|
||||||
|
tdr = '\t<td align=right>{0}</td>\n'
|
||||||
|
tdlink = '\t<td align=center><a href="{0}">html</a></td>\n'
|
||||||
|
limit = 1
|
||||||
|
for type in sorted(devall, reverse=True):
|
||||||
|
num = 0
|
||||||
|
devlist = devall[type]
|
||||||
|
# table header
|
||||||
|
html += '<div class="stamp">%s (%s devices > %d ms)</div><table>\n' % \
|
||||||
|
(title, type.upper(), limit)
|
||||||
|
html += '<tr>\n' + '<th align=right>Device Name</th>' +\
|
||||||
|
th.format('Average Time') + th.format('Count') +\
|
||||||
|
th.format('Worst Time') + th.format('Host (worst time)') +\
|
||||||
|
th.format('Link (worst time)') + '</tr>\n'
|
||||||
|
for name in sorted(devlist, key=lambda k:devlist[k]['worst'], reverse=True):
|
||||||
|
data = devall[type][name]
|
||||||
|
data['average'] = data['total'] / data['count']
|
||||||
|
if data['average'] < limit:
|
||||||
|
continue
|
||||||
|
# row classes - alternate row color
|
||||||
|
rcls = ['alt'] if num % 2 == 1 else []
|
||||||
|
html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n'
|
||||||
|
html += tdr.format(data['name']) # name
|
||||||
|
html += td.format('%.3f ms' % data['average']) # average
|
||||||
|
html += td.format(data['count']) # count
|
||||||
|
html += td.format('%.3f ms' % data['worst']) # worst
|
||||||
|
html += td.format(data['host']) # host
|
||||||
|
html += tdlink.format(data['url']) # url
|
||||||
|
html += '</tr>\n'
|
||||||
|
num += 1
|
||||||
|
html += '</table>\n'
|
||||||
|
|
||||||
|
# flush the data to file
|
||||||
|
hf = open(htmlfile, 'w')
|
||||||
|
hf.write(html+'</body>\n</html>\n')
|
||||||
|
hf.close()
|
||||||
|
return devall
|
||||||
|
|
||||||
|
def createHTMLIssuesSummary(issues, htmlfile, title):
|
||||||
|
html = summaryCSS('Issues Summary - SleepGraph', False)
|
||||||
|
|
||||||
|
# generate the html
|
||||||
|
th = '\t<th>{0}</th>\n'
|
||||||
|
td = '\t<td align={0}>{1}</td>\n'
|
||||||
|
tdlink = '<a href="{1}">{0}</a>'
|
||||||
|
subtitle = '%d issues' % len(issues) if len(issues) > 0 else 'no issues'
|
||||||
|
html += '<div class="stamp">%s (%s)</div><table>\n' % (title, subtitle)
|
||||||
|
html += '<tr>\n' + th.format('Count') + th.format('Issue') +\
|
||||||
|
th.format('Hosts') + th.format('First Instance') + '</tr>\n'
|
||||||
|
|
||||||
|
num = 0
|
||||||
|
for e in sorted(issues, key=lambda v:v['count'], reverse=True):
|
||||||
|
links = []
|
||||||
|
for host in sorted(e['urls']):
|
||||||
|
links.append(tdlink.format(host, e['urls'][host]))
|
||||||
|
# row classes - alternate row color
|
||||||
|
rcls = ['alt'] if num % 2 == 1 else []
|
||||||
|
html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n'
|
||||||
|
html += td.format('center', e['count']) # count
|
||||||
|
html += td.format('left', e['line']) # issue
|
||||||
|
html += td.format('center', len(e['urls'])) # hosts
|
||||||
|
html += td.format('center nowrap', '<br>'.join(links)) # links
|
||||||
|
html += '</tr>\n'
|
||||||
|
num += 1
|
||||||
|
|
||||||
|
# flush the data to file
|
||||||
|
hf = open(htmlfile, 'w')
|
||||||
|
hf.write(html+'</table>\n</body>\n</html>\n')
|
||||||
|
hf.close()
|
||||||
|
return issues
|
||||||
|
|
||||||
def ordinal(value):
|
def ordinal(value):
|
||||||
suffix = 'th'
|
suffix = 'th'
|
||||||
if value < 10 or value > 19:
|
if value < 10 or value > 19:
|
||||||
@ -4621,6 +4817,7 @@ def executeSuspend():
|
|||||||
pprint('SUSPEND START')
|
pprint('SUSPEND START')
|
||||||
else:
|
else:
|
||||||
pprint('SUSPEND START (press a key to resume)')
|
pprint('SUSPEND START (press a key to resume)')
|
||||||
|
sysvals.mcelog(True)
|
||||||
bat1 = getBattery() if battery else False
|
bat1 = getBattery() if battery else False
|
||||||
# set rtcwake
|
# set rtcwake
|
||||||
if(sysvals.rtcwake):
|
if(sysvals.rtcwake):
|
||||||
@ -4652,13 +4849,21 @@ def executeSuspend():
|
|||||||
pf = open(sysvals.diskpowerfile, 'w')
|
pf = open(sysvals.diskpowerfile, 'w')
|
||||||
pf.write(sysvals.diskmode)
|
pf.write(sysvals.diskmode)
|
||||||
pf.close()
|
pf.close()
|
||||||
pf = open(sysvals.powerfile, 'w')
|
if mode == 'freeze' and sysvals.haveTurbostat():
|
||||||
pf.write(mode)
|
# execution will pause here
|
||||||
# execution will pause here
|
turbo = sysvals.turbostat()
|
||||||
try:
|
if '|' in turbo:
|
||||||
pf.close()
|
tdata['turbo'] = turbo
|
||||||
except Exception as e:
|
else:
|
||||||
tdata['error'] = str(e)
|
tdata['error'] = turbo
|
||||||
|
else:
|
||||||
|
pf = open(sysvals.powerfile, 'w')
|
||||||
|
pf.write(mode)
|
||||||
|
# execution will pause here
|
||||||
|
try:
|
||||||
|
pf.close()
|
||||||
|
except Exception as e:
|
||||||
|
tdata['error'] = str(e)
|
||||||
if(sysvals.rtcwake):
|
if(sysvals.rtcwake):
|
||||||
sysvals.rtcWakeAlarmOff()
|
sysvals.rtcWakeAlarmOff()
|
||||||
# postdelay delay
|
# postdelay delay
|
||||||
@ -4672,6 +4877,9 @@ def executeSuspend():
|
|||||||
sysvals.fsetVal('RESUME COMPLETE', 'trace_marker')
|
sysvals.fsetVal('RESUME COMPLETE', 'trace_marker')
|
||||||
if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'):
|
if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'):
|
||||||
tdata['fw'] = getFPDT(False)
|
tdata['fw'] = getFPDT(False)
|
||||||
|
mcelog = sysvals.mcelog()
|
||||||
|
if mcelog:
|
||||||
|
tdata['mcelog'] = mcelog
|
||||||
bat2 = getBattery() if battery else False
|
bat2 = getBattery() if battery else False
|
||||||
if battery and bat1 and bat2:
|
if battery and bat1 and bat2:
|
||||||
tdata['bat'] = (bat1, bat2)
|
tdata['bat'] = (bat1, bat2)
|
||||||
@ -4694,6 +4902,7 @@ def executeSuspend():
|
|||||||
op.close()
|
op.close()
|
||||||
sysvals.fsetVal('', 'trace')
|
sysvals.fsetVal('', 'trace')
|
||||||
devProps()
|
devProps()
|
||||||
|
return testdata
|
||||||
|
|
||||||
def readFile(file):
|
def readFile(file):
|
||||||
if os.path.islink(file):
|
if os.path.islink(file):
|
||||||
@ -5398,6 +5607,12 @@ def processData(live=False):
|
|||||||
appendIncompleteTraceLog(testruns)
|
appendIncompleteTraceLog(testruns)
|
||||||
sysvals.vprint('Command:\n %s' % sysvals.cmdline)
|
sysvals.vprint('Command:\n %s' % sysvals.cmdline)
|
||||||
for data in testruns:
|
for data in testruns:
|
||||||
|
if data.mcelog:
|
||||||
|
sysvals.vprint('MCELOG Data:')
|
||||||
|
for line in data.mcelog.split('\n'):
|
||||||
|
sysvals.vprint(' %s' % line)
|
||||||
|
if data.turbostat:
|
||||||
|
sysvals.vprint('Turbostat:\n %s' % data.turbostat.replace('|', ' '))
|
||||||
if data.battery:
|
if data.battery:
|
||||||
a1, c1, a2, c2 = data.battery
|
a1, c1, a2, c2 = data.battery
|
||||||
s = 'Battery:\n Before - AC: %s, Charge: %d\n After - AC: %s, Charge: %d' % \
|
s = 'Battery:\n Before - AC: %s, Charge: %d\n After - AC: %s, Charge: %d' % \
|
||||||
@ -5431,7 +5646,10 @@ def rerunTest():
|
|||||||
doesTraceLogHaveTraceEvents()
|
doesTraceLogHaveTraceEvents()
|
||||||
if not sysvals.dmesgfile and not sysvals.usetraceevents:
|
if not sysvals.dmesgfile and not sysvals.usetraceevents:
|
||||||
doError('recreating this html output requires a dmesg file')
|
doError('recreating this html output requires a dmesg file')
|
||||||
sysvals.setOutputFile()
|
if sysvals.outdir:
|
||||||
|
sysvals.htmlfile = sysvals.outdir
|
||||||
|
else:
|
||||||
|
sysvals.setOutputFile()
|
||||||
if os.path.exists(sysvals.htmlfile):
|
if os.path.exists(sysvals.htmlfile):
|
||||||
if not os.path.isfile(sysvals.htmlfile):
|
if not os.path.isfile(sysvals.htmlfile):
|
||||||
doError('a directory already exists with this name: %s' % sysvals.htmlfile)
|
doError('a directory already exists with this name: %s' % sysvals.htmlfile)
|
||||||
@ -5450,14 +5668,18 @@ def runTest(n=0):
|
|||||||
sysvals.initTestOutput('suspend')
|
sysvals.initTestOutput('suspend')
|
||||||
|
|
||||||
# execute the test
|
# execute the test
|
||||||
executeSuspend()
|
testdata = executeSuspend()
|
||||||
sysvals.cleanupFtrace()
|
sysvals.cleanupFtrace()
|
||||||
if sysvals.skiphtml:
|
if sysvals.skiphtml:
|
||||||
sysvals.sudoUserchown(sysvals.testdir)
|
sysvals.sudoUserchown(sysvals.testdir)
|
||||||
return
|
return
|
||||||
testruns, stamp = processData(True)
|
if len(testdata) > 0 and not testdata[0]['error']:
|
||||||
for data in testruns:
|
testruns, stamp = processData(True)
|
||||||
del data
|
for data in testruns:
|
||||||
|
del data
|
||||||
|
else:
|
||||||
|
stamp = testdata[0]
|
||||||
|
|
||||||
sysvals.sudoUserchown(sysvals.testdir)
|
sysvals.sudoUserchown(sysvals.testdir)
|
||||||
sysvals.outputResult(stamp, n)
|
sysvals.outputResult(stamp, n)
|
||||||
if 'error' in stamp:
|
if 'error' in stamp:
|
||||||
@ -5487,8 +5709,13 @@ def find_in_html(html, start, end, firstonly=True):
|
|||||||
return ''
|
return ''
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def data_from_html(file, outpath, devlist=False):
|
def data_from_html(file, outpath, issues):
|
||||||
html = open(file, 'r').read()
|
if '<html>' not in file:
|
||||||
|
html = open(file, 'r').read()
|
||||||
|
sysvals.htmlfile = os.path.relpath(file, outpath)
|
||||||
|
else:
|
||||||
|
html = file
|
||||||
|
# extract general info
|
||||||
suspend = find_in_html(html, 'Kernel Suspend', 'ms')
|
suspend = find_in_html(html, 'Kernel Suspend', 'ms')
|
||||||
resume = find_in_html(html, 'Kernel Resume', 'ms')
|
resume = find_in_html(html, 'Kernel Resume', 'ms')
|
||||||
line = find_in_html(html, '<div class="stamp">', '</div>')
|
line = find_in_html(html, '<div class="stamp">', '</div>')
|
||||||
@ -5499,6 +5726,7 @@ def data_from_html(file, outpath, devlist=False):
|
|||||||
dt = datetime.strptime(' '.join(stmp[3:]), '%B %d %Y, %I:%M:%S %p')
|
dt = datetime.strptime(' '.join(stmp[3:]), '%B %d %Y, %I:%M:%S %p')
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
|
sysvals.hostname = stmp[0]
|
||||||
tstr = dt.strftime('%Y/%m/%d %H:%M:%S')
|
tstr = dt.strftime('%Y/%m/%d %H:%M:%S')
|
||||||
error = find_in_html(html, '<table class="testfail"><tr><td>', '</td>')
|
error = find_in_html(html, '<table class="testfail"><tr><td>', '</td>')
|
||||||
if error:
|
if error:
|
||||||
@ -5509,13 +5737,39 @@ def data_from_html(file, outpath, devlist=False):
|
|||||||
result = 'fail'
|
result = 'fail'
|
||||||
else:
|
else:
|
||||||
result = 'pass'
|
result = 'pass'
|
||||||
|
# extract error info
|
||||||
ilist = []
|
ilist = []
|
||||||
e = find_in_html(html, 'class="err"[\w=":;\.%\- ]*>', '→</div>', False)
|
log = find_in_html(html, '<div id="dmesglog" style="display:none;">',
|
||||||
for i in list(set(e)):
|
'</div>').strip()
|
||||||
ilist.append('%sx%d' % (i, e.count(i)) if e.count(i) > 1 else i)
|
if log:
|
||||||
|
d = Data(0)
|
||||||
|
d.end = 999999999
|
||||||
|
d.dmesgtext = log.split('\n')
|
||||||
|
d.extractErrorInfo(issues)
|
||||||
|
elist = dict()
|
||||||
|
for dir in d.errorinfo:
|
||||||
|
for err in d.errorinfo[dir]:
|
||||||
|
if err[0] not in elist:
|
||||||
|
elist[err[0]] = 0
|
||||||
|
elist[err[0]] += 1
|
||||||
|
for i in elist:
|
||||||
|
ilist.append('%sx%d' % (i, elist[i]) if elist[i] > 1 else i)
|
||||||
low = find_in_html(html, 'freeze time: <b>', ' ms</b>')
|
low = find_in_html(html, 'freeze time: <b>', ' ms</b>')
|
||||||
if low and '|' in low:
|
if low and '|' in low:
|
||||||
ilist.append('FREEZEx%d' % len(low.split('|')))
|
issue = 'FREEZEx%d' % len(low.split('|'))
|
||||||
|
match = [i for i in issues if i['match'] == issue]
|
||||||
|
if len(match) > 0:
|
||||||
|
match[0]['count'] += 1
|
||||||
|
if sysvals.hostname not in match[0]['urls']:
|
||||||
|
match[0]['urls'][sysvals.hostname] = sysvals.htmlfile
|
||||||
|
else:
|
||||||
|
issues.append({
|
||||||
|
'match': issue, 'count': 1, 'line': issue,
|
||||||
|
'urls': {sysvals.hostname: sysvals.htmlfile},
|
||||||
|
})
|
||||||
|
ilist.append(issue)
|
||||||
|
|
||||||
|
# extract device info
|
||||||
devices = dict()
|
devices = dict()
|
||||||
for line in html.split('\n'):
|
for line in html.split('\n'):
|
||||||
m = re.match(' *<div id=\"[a,0-9]*\" *title=\"(?P<title>.*)\" class=\"thread.*', line)
|
m = re.match(' *<div id=\"[a,0-9]*\" *title=\"(?P<title>.*)\" class=\"thread.*', line)
|
||||||
@ -5527,19 +5781,23 @@ def data_from_html(file, outpath, devlist=False):
|
|||||||
name, time, phase = m.group('n'), m.group('t'), m.group('p')
|
name, time, phase = m.group('n'), m.group('t'), m.group('p')
|
||||||
if ' async' in name or ' sync' in name:
|
if ' async' in name or ' sync' in name:
|
||||||
name = ' '.join(name.split(' ')[:-1])
|
name = ' '.join(name.split(' ')[:-1])
|
||||||
d = phase.split('_')[0]
|
if phase.startswith('suspend'):
|
||||||
|
d = 'suspend'
|
||||||
|
elif phase.startswith('resume'):
|
||||||
|
d = 'resume'
|
||||||
|
else:
|
||||||
|
continue
|
||||||
if d not in devices:
|
if d not in devices:
|
||||||
devices[d] = dict()
|
devices[d] = dict()
|
||||||
if name not in devices[d]:
|
if name not in devices[d]:
|
||||||
devices[d][name] = 0.0
|
devices[d][name] = 0.0
|
||||||
devices[d][name] += float(time)
|
devices[d][name] += float(time)
|
||||||
worst = {'suspend': {'name':'', 'time': 0.0},
|
# create worst device info
|
||||||
'resume': {'name':'', 'time': 0.0}}
|
worst = dict()
|
||||||
for d in devices:
|
for d in ['suspend', 'resume']:
|
||||||
if d not in worst:
|
worst[d] = {'name':'', 'time': 0.0}
|
||||||
worst[d] = dict()
|
dev = devices[d] if d in devices else 0
|
||||||
dev = devices[d]
|
if dev and len(dev.keys()) > 0:
|
||||||
if len(dev.keys()) > 0:
|
|
||||||
n = sorted(dev, key=dev.get, reverse=True)[0]
|
n = sorted(dev, key=dev.get, reverse=True)[0]
|
||||||
worst[d]['name'], worst[d]['time'] = n, dev[n]
|
worst[d]['name'], worst[d]['time'] = n, dev[n]
|
||||||
data = {
|
data = {
|
||||||
@ -5551,14 +5809,13 @@ def data_from_html(file, outpath, devlist=False):
|
|||||||
'issues': ' '.join(ilist),
|
'issues': ' '.join(ilist),
|
||||||
'suspend': suspend,
|
'suspend': suspend,
|
||||||
'resume': resume,
|
'resume': resume,
|
||||||
|
'devlist': devices,
|
||||||
'sus_worst': worst['suspend']['name'],
|
'sus_worst': worst['suspend']['name'],
|
||||||
'sus_worsttime': worst['suspend']['time'],
|
'sus_worsttime': worst['suspend']['time'],
|
||||||
'res_worst': worst['resume']['name'],
|
'res_worst': worst['resume']['name'],
|
||||||
'res_worsttime': worst['resume']['time'],
|
'res_worsttime': worst['resume']['time'],
|
||||||
'url': os.path.relpath(file, outpath),
|
'url': sysvals.htmlfile,
|
||||||
}
|
}
|
||||||
if devlist:
|
|
||||||
data['devlist'] = devices
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
# Function: runSummary
|
# Function: runSummary
|
||||||
@ -5567,7 +5824,7 @@ def data_from_html(file, outpath, devlist=False):
|
|||||||
def runSummary(subdir, local=True, genhtml=False):
|
def runSummary(subdir, local=True, genhtml=False):
|
||||||
inpath = os.path.abspath(subdir)
|
inpath = os.path.abspath(subdir)
|
||||||
outpath = os.path.abspath('.') if local else inpath
|
outpath = os.path.abspath('.') if local else inpath
|
||||||
pprint('Generating a summary of folder "%s"' % inpath)
|
pprint('Generating a summary of folder:\n %s' % inpath)
|
||||||
if genhtml:
|
if genhtml:
|
||||||
for dirname, dirnames, filenames in os.walk(subdir):
|
for dirname, dirnames, filenames in os.walk(subdir):
|
||||||
sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = ''
|
sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = ''
|
||||||
@ -5583,26 +5840,31 @@ def runSummary(subdir, local=True, genhtml=False):
|
|||||||
if sysvals.dmesgfile:
|
if sysvals.dmesgfile:
|
||||||
pprint('DMESG : %s' % sysvals.dmesgfile)
|
pprint('DMESG : %s' % sysvals.dmesgfile)
|
||||||
rerunTest()
|
rerunTest()
|
||||||
|
issues = []
|
||||||
testruns = []
|
testruns = []
|
||||||
desc = {'host':[],'mode':[],'kernel':[]}
|
desc = {'host':[],'mode':[],'kernel':[]}
|
||||||
for dirname, dirnames, filenames in os.walk(subdir):
|
for dirname, dirnames, filenames in os.walk(subdir):
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
if(not re.match('.*.html', filename)):
|
if(not re.match('.*.html', filename)):
|
||||||
continue
|
continue
|
||||||
data = data_from_html(os.path.join(dirname, filename), outpath)
|
data = data_from_html(os.path.join(dirname, filename), outpath, issues)
|
||||||
if(not data):
|
if(not data):
|
||||||
continue
|
continue
|
||||||
testruns.append(data)
|
testruns.append(data)
|
||||||
for key in desc:
|
for key in desc:
|
||||||
if data[key] not in desc[key]:
|
if data[key] not in desc[key]:
|
||||||
desc[key].append(data[key])
|
desc[key].append(data[key])
|
||||||
outfile = os.path.join(outpath, 'summary.html')
|
pprint('Summary files:')
|
||||||
pprint('Summary file: %s' % outfile)
|
|
||||||
if len(desc['host']) == len(desc['mode']) == len(desc['kernel']) == 1:
|
if len(desc['host']) == len(desc['mode']) == len(desc['kernel']) == 1:
|
||||||
title = '%s %s %s' % (desc['host'][0], desc['kernel'][0], desc['mode'][0])
|
title = '%s %s %s' % (desc['host'][0], desc['kernel'][0], desc['mode'][0])
|
||||||
else:
|
else:
|
||||||
title = inpath
|
title = inpath
|
||||||
createHTMLSummarySimple(testruns, outfile, title)
|
createHTMLSummarySimple(testruns, os.path.join(outpath, 'summary.html'), title)
|
||||||
|
pprint(' summary.html - tabular list of test data found')
|
||||||
|
createHTMLDeviceSummary(testruns, os.path.join(outpath, 'summary-devices.html'), title)
|
||||||
|
pprint(' summary-devices.html - kernel device list sorted by total execution time')
|
||||||
|
createHTMLIssuesSummary(issues, os.path.join(outpath, 'summary-issues.html'), title)
|
||||||
|
pprint(' summary-issues.html - kernel issues found sorted by frequency')
|
||||||
|
|
||||||
# Function: checkArgBool
|
# Function: checkArgBool
|
||||||
# Description:
|
# Description:
|
||||||
|
Loading…
Reference in New Issue
Block a user