Commit 03bc39be authored by Todd E Brandt's avatar Todd E Brandt Committed by Rafael J. Wysocki

scripts: analyze_suspend.py: Update to upstream v4.5

- Kernel errors shown in timeline
- Tool log: The tool output log is now available in the html timeline
- Selective ftrace filter: can choose phase and test run (for x2)
- further instrumentation of dev mode to cover wifi
Signed-off-by: default avatarTodd Brandt <todd.e.brandt@linux.intel.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 1ea39643
...@@ -24,11 +24,6 @@ ...@@ -24,11 +24,6 @@
# https://01.org/suspendresume # https://01.org/suspendresume
# Source repo # Source repo
# https://github.com/01org/suspendresume # https://github.com/01org/suspendresume
# Documentation
# Getting Started
# https://01.org/suspendresume/documentation/getting-started
# Command List:
# https://01.org/suspendresume/documentation/command-list
# #
# Description: # Description:
# This tool is designed to assist kernel and OS developers in optimizing # This tool is designed to assist kernel and OS developers in optimizing
...@@ -77,11 +72,13 @@ from subprocess import call, Popen, PIPE ...@@ -77,11 +72,13 @@ from subprocess import call, Popen, PIPE
# store system values and test parameters # store system values and test parameters
class SystemValues: class SystemValues:
ansi = False ansi = False
version = '4.4' version = '4.5'
verbose = False verbose = False
addlogs = False addlogs = False
mindevlen = 0.001 mindevlen = 0.0
mincglen = 1.0 mincglen = 0.0
cgphase = ''
cgtest = -1
callloopmaxgap = 0.0001 callloopmaxgap = 0.0001
callloopmaxlen = 0.005 callloopmaxlen = 0.005
srgap = 0 srgap = 0
...@@ -96,6 +93,7 @@ class SystemValues: ...@@ -96,6 +93,7 @@ class SystemValues:
'device_pm_callback_end', 'device_pm_callback_end',
'device_pm_callback_start' 'device_pm_callback_start'
] ]
logmsg = ''
testcommand = '' testcommand = ''
mempath = '/dev/mem' mempath = '/dev/mem'
powerfile = '/sys/power/state' powerfile = '/sys/power/state'
...@@ -166,18 +164,38 @@ class SystemValues: ...@@ -166,18 +164,38 @@ class SystemValues:
} }
dev_tracefuncs = { dev_tracefuncs = {
# general wait/delay/sleep # general wait/delay/sleep
'msleep': { 'args_x86_64': {'time':'%di:s32'} }, 'msleep': { 'args_x86_64': {'time':'%di:s32'}, 'ub': 1 },
'schedule_timeout_uninterruptible': { 'args_x86_64': {'timeout':'%di:s32'} }, 'schedule_timeout_uninterruptible': { 'args_x86_64': {'timeout':'%di:s32'}, 'ub': 1 },
'schedule_timeout': { 'args_x86_64': {'timeout':'%di:s32'} }, 'schedule_timeout': { 'args_x86_64': {'timeout':'%di:s32'}, 'ub': 1 },
'udelay': { 'func':'__const_udelay', 'args_x86_64': {'loops':'%di:s32'} }, 'udelay': { 'func':'__const_udelay', 'args_x86_64': {'loops':'%di:s32'}, 'ub': 1 },
'usleep_range': { 'args_x86_64': {'min':'%di:s32', 'max':'%si:s32'} }, 'usleep_range': { 'args_x86_64': {'min':'%di:s32', 'max':'%si:s32'}, 'ub': 1 },
'mutex_lock_slowpath': { 'func':'__mutex_lock_slowpath' }, 'mutex_lock_slowpath': { 'func':'__mutex_lock_slowpath', 'ub': 1 },
'acpi_os_stall': dict(), 'acpi_os_stall': {'ub': 1},
# ACPI # ACPI
'acpi_resume_power_resources': dict(), 'acpi_resume_power_resources': dict(),
'acpi_ps_parse_aml': dict(), 'acpi_ps_parse_aml': dict(),
# filesystem # filesystem
'ext4_sync_fs': dict(), 'ext4_sync_fs': dict(),
# 80211
'iwlagn_mac_start': dict(),
'iwlagn_alloc_bcast_station': dict(),
'iwl_trans_pcie_start_hw': dict(),
'iwl_trans_pcie_start_fw': dict(),
'iwl_run_init_ucode': dict(),
'iwl_load_ucode_wait_alive': dict(),
'iwl_alive_start': dict(),
'iwlagn_mac_stop': dict(),
'iwlagn_mac_suspend': dict(),
'iwlagn_mac_resume': dict(),
'iwlagn_mac_add_interface': dict(),
'iwlagn_mac_remove_interface': dict(),
'iwlagn_mac_change_interface': dict(),
'iwlagn_mac_config': dict(),
'iwlagn_configure_filter': dict(),
'iwlagn_mac_hw_scan': dict(),
'iwlagn_bss_info_changed': dict(),
'iwlagn_mac_channel_switch': dict(),
'iwlagn_mac_flush': dict(),
# ATA # ATA
'ata_eh_recover': { 'args_x86_64': {'port':'+36(%di):s32'} }, 'ata_eh_recover': { 'args_x86_64': {'port':'+36(%di):s32'} },
# i915 # i915
...@@ -196,14 +214,6 @@ class SystemValues: ...@@ -196,14 +214,6 @@ class SystemValues:
'intel_opregion_init': dict(), 'intel_opregion_init': dict(),
'intel_fbdev_set_suspend': dict(), 'intel_fbdev_set_suspend': dict(),
} }
kprobes_postresume = [
{
'name': 'ataportrst',
'func': 'ata_eh_recover',
'args': {'port':'+36(%di):s32'},
'format': 'ata{port}_port_reset'
}
]
kprobes = dict() kprobes = dict()
timeformat = '%.3f' timeformat = '%.3f'
def __init__(self): def __init__(self):
...@@ -429,45 +439,65 @@ class SystemValues: ...@@ -429,45 +439,65 @@ class SystemValues:
if 'args' in kprobe: if 'args' in kprobe:
args = kprobe['args'] args = kprobe['args']
if re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', func): if re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', func):
doError('Kprobe "%s" has format info in the function name "%s"' % (name, func), False) doError('Kprobe "%s" has format info in the function name "%s"' % (name, func))
for arg in re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', fmt): for arg in re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', fmt):
if arg not in args: if arg not in args:
doError('Kprobe "%s" is missing argument "%s"' % (name, arg), False) doError('Kprobe "%s" is missing argument "%s"' % (name, arg))
val = 'p:%s_cal %s' % (name, func) val = 'p:%s_cal %s' % (name, func)
for i in sorted(args): for i in sorted(args):
val += ' %s=%s' % (i, args[i]) val += ' %s=%s' % (i, args[i])
val += '\nr:%s_ret %s $retval\n' % (name, func) val += '\nr:%s_ret %s $retval\n' % (name, func)
return val return val
def addKprobes(self): def addKprobes(self, output=False):
if len(sysvals.kprobes) < 1:
return
if output:
print(' kprobe functions in this kernel:')
# first test each kprobe # first test each kprobe
print('INITIALIZING KPROBES...')
rejects = [] rejects = []
# sort kprobes: trace, ub-dev, custom, dev
kpl = [[], [], [], []]
for name in sorted(self.kprobes): for name in sorted(self.kprobes):
res = self.colorText('YES', 32)
if not self.testKprobe(name, self.kprobes[name]): if not self.testKprobe(name, self.kprobes[name]):
res = self.colorText('NO')
rejects.append(name) rejects.append(name)
else:
if name in self.tracefuncs:
kpl[0].append(name)
elif name in self.dev_tracefuncs:
if 'ub' in self.dev_tracefuncs[name]:
kpl[1].append(name)
else:
kpl[3].append(name)
else:
kpl[2].append(name)
if output:
print(' %s: %s' % (name, res))
kplist = kpl[0] + kpl[1] + kpl[2] + kpl[3]
# remove all failed ones from the list # remove all failed ones from the list
for name in rejects: for name in rejects:
vprint('Skipping KPROBE: %s' % name)
self.kprobes.pop(name) self.kprobes.pop(name)
# set the kprobes all at once
self.fsetVal('', 'kprobe_events') self.fsetVal('', 'kprobe_events')
kprobeevents = '' kprobeevents = ''
# set the kprobes all at once for kp in kplist:
for kp in self.kprobes:
val = self.kprobeText(kp, self.kprobes[kp])
vprint('Adding KPROBE: %s\n%s' % (kp, val.strip()))
kprobeevents += self.kprobeText(kp, self.kprobes[kp]) kprobeevents += self.kprobeText(kp, self.kprobes[kp])
self.fsetVal(kprobeevents, 'kprobe_events') self.fsetVal(kprobeevents, 'kprobe_events')
# verify that the kprobes were set as ordered # verify that the kprobes were set as ordered
check = self.fgetVal('kprobe_events') check = self.fgetVal('kprobe_events')
linesout = len(kprobeevents.split('\n')) linesout = len(kprobeevents.split('\n')) - 1
linesack = len(check.split('\n')) linesack = len(check.split('\n')) - 1
if linesack < linesout: if output:
# if not, try appending the kprobes 1 by 1 res = '%d/%d' % (linesack, linesout)
for kp in self.kprobes: if linesack < linesout:
kprobeevents = self.kprobeText(kp, self.kprobes[kp]) res = self.colorText(res, 31)
self.fsetVal(kprobeevents, 'kprobe_events', 'a') else:
res = self.colorText(res, 32)
print(' working kprobe functions enabled: %s' % res)
self.fsetVal('1', 'events/kprobes/enable') self.fsetVal('1', 'events/kprobes/enable')
def testKprobe(self, kname, kprobe): def testKprobe(self, kname, kprobe):
self.fsetVal('0', 'events/kprobes/enable')
kprobeevents = self.kprobeText(kname, kprobe) kprobeevents = self.kprobeText(kname, kprobe)
if not kprobeevents: if not kprobeevents:
return False return False
...@@ -486,8 +516,9 @@ class SystemValues: ...@@ -486,8 +516,9 @@ class SystemValues:
if not os.path.exists(file): if not os.path.exists(file):
return False return False
try: try:
fp = open(file, mode) fp = open(file, mode, 0)
fp.write(val) fp.write(val)
fp.flush()
fp.close() fp.close()
except: except:
pass pass
...@@ -525,7 +556,6 @@ class SystemValues: ...@@ -525,7 +556,6 @@ class SystemValues:
return True return True
return False return False
def initFtrace(self, testing=False): def initFtrace(self, testing=False):
tp = self.tpath
print('INITIALIZING FTRACE...') print('INITIALIZING FTRACE...')
# turn trace off # turn trace off
self.fsetVal('0', 'tracing_on') self.fsetVal('0', 'tracing_on')
...@@ -570,7 +600,8 @@ class SystemValues: ...@@ -570,7 +600,8 @@ class SystemValues:
if self.usedevsrc: if self.usedevsrc:
for name in self.dev_tracefuncs: for name in self.dev_tracefuncs:
self.defaultKprobe(name, self.dev_tracefuncs[name]) self.defaultKprobe(name, self.dev_tracefuncs[name])
self.addKprobes() print('INITIALIZING KPROBES...')
self.addKprobes(self.verbose)
if(self.usetraceevents): if(self.usetraceevents):
# turn trace events on # turn trace events on
events = iter(self.traceevents) events = iter(self.traceevents)
...@@ -602,10 +633,10 @@ class SystemValues: ...@@ -602,10 +633,10 @@ class SystemValues:
if(os.path.exists(tp+f) == False): if(os.path.exists(tp+f) == False):
return False return False
return True return True
def colorText(self, str): def colorText(self, str, color=31):
if not self.ansi: if not self.ansi:
return str return str
return '\x1B[31;40m'+str+'\x1B[m' return '\x1B[%d;40m%s\x1B[m' % (color, str)
sysvals = SystemValues() sysvals = SystemValues()
...@@ -658,8 +689,6 @@ class DeviceNode: ...@@ -658,8 +689,6 @@ class DeviceNode:
# The primary container for suspend/resume test data. There is one for # The primary container for suspend/resume test data. There is one for
# each test run. The data is organized into a cronological hierarchy: # each test run. The data is organized into a cronological hierarchy:
# Data.dmesg { # Data.dmesg {
# root structure, started as dmesg & ftrace, but now only ftrace
# contents: times for suspend start/end, resume start/end, fwdata
# phases { # phases {
# 10 sequential, non-overlapping phases of S/R # 10 sequential, non-overlapping phases of S/R
# contents: times for phase start/end, order/color data for html # contents: times for phase start/end, order/color data for html
...@@ -670,7 +699,7 @@ class DeviceNode: ...@@ -670,7 +699,7 @@ class DeviceNode:
# contents: start/stop times, pid/cpu/driver info # contents: start/stop times, pid/cpu/driver info
# parents/children, html id for timeline/callgraph # parents/children, html id for timeline/callgraph
# optionally includes an ftrace callgraph # optionally includes an ftrace callgraph
# optionally includes intradev trace events # optionally includes dev/ps data
# } # }
# } # }
# } # }
...@@ -697,16 +726,9 @@ class Data: ...@@ -697,16 +726,9 @@ class Data:
stamp = 0 stamp = 0
outfile = '' outfile = ''
devpids = [] devpids = []
dev_ubiquitous = [ kerror = False
'msleep',
'schedule_timeout_uninterruptible',
'schedule_timeout',
'udelay',
'usleep_range',
'mutex_lock_slowpath'
]
def __init__(self, num): def __init__(self, num):
idchar = 'abcdefghijklmnopqrstuvwxyz' idchar = 'abcdefghij'
self.pstl = dict() self.pstl = dict()
self.testnumber = num self.testnumber = num
self.idstr = idchar[num] self.idstr = idchar[num]
...@@ -738,6 +760,35 @@ class Data: ...@@ -738,6 +760,35 @@ class Data:
self.devicegroups = [] self.devicegroups = []
for phase in self.phases: for phase in self.phases:
self.devicegroups.append([phase]) self.devicegroups.append([phase])
self.errorinfo = {'suspend':[],'resume':[]}
def extractErrorInfo(self, dmesg):
error = ''
tm = 0.0
for i in range(len(dmesg)):
if 'Call Trace:' in dmesg[i]:
m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) .*', dmesg[i])
if not m:
continue
tm = float(m.group('ktime'))
if tm < self.start or tm > self.end:
continue
for j in range(i-10, i+1):
error += dmesg[j]
continue
if error:
m = re.match('[ \t]*\[ *[0-9\.]*\] \[\<[0-9a-fA-F]*\>\] .*', dmesg[i])
if m:
error += dmesg[i]
else:
if tm < self.tSuspended:
dir = 'suspend'
else:
dir = 'resume'
error = error.replace('<', '&lt').replace('>', '&gt')
vprint('kernel error found in %s at %f' % (dir, tm))
self.errorinfo[dir].append((tm, error))
self.kerror = True
error = ''
def setStart(self, time): def setStart(self, time):
self.start = time self.start = time
def setEnd(self, time): def setEnd(self, time):
...@@ -751,9 +802,8 @@ class Data: ...@@ -751,9 +802,8 @@ class Data:
time < d['end']): time < d['end']):
return False return False
return True return True
def sourcePhase(self, start, end): def sourcePhase(self, start):
for phase in self.phases: for phase in self.phases:
pstart = self.dmesg[phase]['start']
pend = self.dmesg[phase]['end'] pend = self.dmesg[phase]['end']
if start <= pend: if start <= pend:
return phase return phase
...@@ -798,7 +848,7 @@ class Data: ...@@ -798,7 +848,7 @@ class Data:
threadname = 'kthread-%d' % (pid) threadname = 'kthread-%d' % (pid)
else: else:
threadname = '%s-%d' % (proc, pid) threadname = '%s-%d' % (proc, pid)
tgtphase = self.sourcePhase(start, end) tgtphase = self.sourcePhase(start)
self.newAction(tgtphase, threadname, pid, '', start, end, '', ' kth', '') self.newAction(tgtphase, threadname, pid, '', start, end, '', ' kth', '')
return self.addDeviceFunctionCall(displayname, kprobename, proc, pid, start, end, cdata, rdata) return self.addDeviceFunctionCall(displayname, kprobename, proc, pid, start, end, cdata, rdata)
# this should not happen # this should not happen
...@@ -809,8 +859,9 @@ class Data: ...@@ -809,8 +859,9 @@ class Data:
# place the call data inside the src element of the tgtdev # place the call data inside the src element of the tgtdev
if('src' not in tgtdev): if('src' not in tgtdev):
tgtdev['src'] = [] tgtdev['src'] = []
dtf = sysvals.dev_tracefuncs
ubiquitous = False ubiquitous = False
if kprobename in self.dev_ubiquitous: if kprobename in dtf and 'ub' in dtf[kprobename]:
ubiquitous = True ubiquitous = True
title = cdata+' '+rdata title = cdata+' '+rdata
mstr = '\(.*\) *(?P<args>.*) *\((?P<caller>.*)\+.* arg1=(?P<ret>.*)' mstr = '\(.*\) *(?P<args>.*) *\((?P<caller>.*)\+.* arg1=(?P<ret>.*)'
...@@ -823,7 +874,7 @@ class Data: ...@@ -823,7 +874,7 @@ class Data:
r = '' r = ''
else: else:
r = 'ret=%s ' % r r = 'ret=%s ' % r
if ubiquitous and c in self.dev_ubiquitous: if ubiquitous and c in dtf and 'ub' in dtf[c]:
return False return False
color = sysvals.kprobeColor(kprobename) color = sysvals.kprobeColor(kprobename)
e = DevFunction(displayname, a, c, r, start, end, ubiquitous, proc, pid, color) e = DevFunction(displayname, a, c, r, start, end, ubiquitous, proc, pid, color)
...@@ -917,6 +968,8 @@ class Data: ...@@ -917,6 +968,8 @@ class Data:
self.tSuspended = self.trimTimeVal(self.tSuspended, t0, dT, left) self.tSuspended = self.trimTimeVal(self.tSuspended, t0, dT, left)
self.tResumed = self.trimTimeVal(self.tResumed, t0, dT, left) self.tResumed = self.trimTimeVal(self.tResumed, t0, dT, left)
self.start = self.trimTimeVal(self.start, t0, dT, left) self.start = self.trimTimeVal(self.start, t0, dT, left)
self.tKernSus = self.trimTimeVal(self.tKernSus, t0, dT, left)
self.tKernRes = self.trimTimeVal(self.tKernRes, t0, dT, left)
self.end = self.trimTimeVal(self.end, t0, dT, left) self.end = self.trimTimeVal(self.end, t0, dT, left)
for phase in self.phases: for phase in self.phases:
p = self.dmesg[phase] p = self.dmesg[phase]
...@@ -964,7 +1017,7 @@ class Data: ...@@ -964,7 +1017,7 @@ class Data:
for t in sorted(tmp): for t in sorted(tmp):
slist.append(tmp[t]) slist.append(tmp[t])
return slist return slist
def fixupInitcalls(self, phase, end): def fixupInitcalls(self, phase):
# if any calls never returned, clip them at system resume end # if any calls never returned, clip them at system resume end
phaselist = self.dmesg[phase]['list'] phaselist = self.dmesg[phase]['list']
for devname in phaselist: for devname in phaselist:
...@@ -976,8 +1029,6 @@ class Data: ...@@ -976,8 +1029,6 @@ class Data:
break break
vprint('%s (%s): callback didnt return' % (devname, phase)) vprint('%s (%s): callback didnt return' % (devname, phase))
def deviceFilter(self, devicefilter): def deviceFilter(self, devicefilter):
# check each device name & driver name
# remove it if it does not include one of the filter strings
for phase in self.phases: for phase in self.phases:
list = self.dmesg[phase]['list'] list = self.dmesg[phase]['list']
rmlist = [] rmlist = []
...@@ -994,7 +1045,7 @@ class Data: ...@@ -994,7 +1045,7 @@ class Data:
def fixupInitcallsThatDidntReturn(self): def fixupInitcallsThatDidntReturn(self):
# if any calls never returned, clip them at system resume end # if any calls never returned, clip them at system resume end
for phase in self.phases: for phase in self.phases:
self.fixupInitcalls(phase, self.end) self.fixupInitcalls(phase)
def phaseOverlap(self, phases): def phaseOverlap(self, phases):
rmgroups = [] rmgroups = []
newgroup = [] newgroup = []
...@@ -1068,22 +1119,6 @@ class Data: ...@@ -1068,22 +1119,6 @@ class Data:
if color: if color:
list[name]['color'] = color list[name]['color'] = color
return name return name
def deviceIDs(self, devlist, phase):
idlist = []
list = self.dmesg[phase]['list']
for devname in list:
if devname in devlist:
idlist.append(list[devname]['id'])
return idlist
def deviceParentID(self, devname, phase):
pdev = ''
pdevid = ''
list = self.dmesg[phase]['list']
if devname in list:
pdev = list[devname]['par']
if pdev in list:
return list[pdev]['id']
return pdev
def deviceChildren(self, devname, phase): def deviceChildren(self, devname, phase):
devlist = [] devlist = []
list = self.dmesg[phase]['list'] list = self.dmesg[phase]['list']
...@@ -1091,15 +1126,6 @@ class Data: ...@@ -1091,15 +1126,6 @@ class Data:
if(list[child]['par'] == devname): if(list[child]['par'] == devname):
devlist.append(child) devlist.append(child)
return devlist return devlist
def deviceDescendants(self, devname, phase):
children = self.deviceChildren(devname, phase)
family = children
for child in children:
family += self.deviceDescendants(child, phase)
return family
def deviceChildrenIDs(self, devname, phase):
devlist = self.deviceChildren(devname, phase)
return self.deviceIDs(devlist, phase)
def printDetails(self): def printDetails(self):
vprint('Timeline Details:') vprint('Timeline Details:')
vprint(' test start: %f' % self.start) vprint(' test start: %f' % self.start)
...@@ -1418,7 +1444,6 @@ class FTraceLine: ...@@ -1418,7 +1444,6 @@ class FTraceLine:
print('%s -- %f (%02d): %s() { (%.3f us)' % (dev, self.time, \ print('%s -- %f (%02d): %s() { (%.3f us)' % (dev, self.time, \
self.depth, self.name, self.length*1000000)) self.depth, self.name, self.length*1000000))
def startMarker(self): def startMarker(self):
global sysvals
# Is this the starting line of a suspend? # Is this the starting line of a suspend?
if not self.fevent: if not self.fevent:
return False return False
...@@ -1721,8 +1746,9 @@ class Timeline: ...@@ -1721,8 +1746,9 @@ class Timeline:
rows = 0 # total timeline rows rows = 0 # total timeline rows
rowlines = dict() rowlines = dict()
rowheight = dict() rowheight = dict()
def __init__(self, rowheight): def __init__(self, rowheight, scaleheight):
self.rowH = rowheight self.rowH = rowheight
self.scaleH = scaleheight
self.html = { self.html = {
'header': '', 'header': '',
'timeline': '', 'timeline': '',
...@@ -1921,7 +1947,6 @@ class Timeline: ...@@ -1921,7 +1947,6 @@ class Timeline:
# A list of values describing the properties of these test runs # A list of values describing the properties of these test runs
class TestProps: class TestProps:
stamp = '' stamp = ''
tracertype = ''
S0i3 = False S0i3 = False
fwdata = [] fwdata = []
ftrace_line_fmt_fg = \ ftrace_line_fmt_fg = \
...@@ -1939,14 +1964,13 @@ class TestProps: ...@@ -1939,14 +1964,13 @@ class TestProps:
def __init__(self): def __init__(self):
self.ktemp = dict() self.ktemp = dict()
def setTracerType(self, tracer): def setTracerType(self, tracer):
self.tracertype = tracer
if(tracer == 'function_graph'): if(tracer == 'function_graph'):
self.cgformat = True self.cgformat = True
self.ftrace_line_fmt = self.ftrace_line_fmt_fg self.ftrace_line_fmt = self.ftrace_line_fmt_fg
elif(tracer == 'nop'): elif(tracer == 'nop'):
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, False) doError('Invalid tracer format: [%s]' % tracer)
# Class: TestRun # Class: TestRun
# Description: # Description:
...@@ -1995,7 +2019,6 @@ class ProcessMonitor: ...@@ -1995,7 +2019,6 @@ class ProcessMonitor:
out += '%s-%s %d' % (val['name'], pid, jiffies) out += '%s-%s %d' % (val['name'], pid, jiffies)
return 'ps - '+out return 'ps - '+out
def processMonitor(self, tid): def processMonitor(self, tid):
global sysvals
while self.running: while self.running:
out = self.procstat() out = self.procstat()
if out: if out:
...@@ -2015,7 +2038,7 @@ class ProcessMonitor: ...@@ -2015,7 +2038,7 @@ class ProcessMonitor:
# Arguments: # Arguments:
# msg: the debug/log message to print # msg: the debug/log message to print
def vprint(msg): def vprint(msg):
global sysvals sysvals.logmsg += msg+'\n'
if(sysvals.verbose): if(sysvals.verbose):
print(msg) print(msg)
...@@ -2026,8 +2049,6 @@ def vprint(msg): ...@@ -2026,8 +2049,6 @@ def vprint(msg):
# Arguments: # Arguments:
# m: the valid re.match output for the stamp line # m: the valid re.match output for the stamp line
def parseStamp(line, data): def parseStamp(line, data):
global sysvals
m = re.match(sysvals.stampfmt, line) m = re.match(sysvals.stampfmt, line)
data.stamp = {'time': '', 'host': '', 'mode': ''} data.stamp = {'time': '', 'host': '', 'mode': ''}
dt = datetime(int(m.group('y'))+2000, int(m.group('m')), dt = datetime(int(m.group('y'))+2000, int(m.group('m')),
...@@ -2076,8 +2097,6 @@ def diffStamp(stamp1, stamp2): ...@@ -2076,8 +2097,6 @@ def diffStamp(stamp1, stamp2):
# required for primary parsing. Set the usetraceevents and/or # required for primary parsing. Set the usetraceevents and/or
# usetraceeventsonly flags in the global sysvals object # usetraceeventsonly flags in the global sysvals object
def doesTraceLogHaveTraceEvents(): def doesTraceLogHaveTraceEvents():
global sysvals
# check for kprobes # check for kprobes
sysvals.usekprobes = False sysvals.usekprobes = False
out = call('grep -q "_cal: (" '+sysvals.ftracefile, shell=True) out = call('grep -q "_cal: (" '+sysvals.ftracefile, shell=True)
...@@ -2120,8 +2139,6 @@ def doesTraceLogHaveTraceEvents(): ...@@ -2120,8 +2139,6 @@ def doesTraceLogHaveTraceEvents():
# Arguments: # Arguments:
# testruns: the array of Data objects obtained from parseKernelLog # testruns: the array of Data objects obtained from parseKernelLog
def appendIncompleteTraceLog(testruns): def appendIncompleteTraceLog(testruns):
global sysvals
# create TestRun vessels for ftrace parsing # create TestRun vessels for ftrace parsing
testcnt = len(testruns) testcnt = len(testruns)
testidx = 0 testidx = 0
...@@ -2312,8 +2329,7 @@ def appendIncompleteTraceLog(testruns): ...@@ -2312,8 +2329,7 @@ def appendIncompleteTraceLog(testruns):
dev['ftrace'] = cg dev['ftrace'] = cg
break break
if(sysvals.verbose): test.data.printDetails()
test.data.printDetails()
# Function: parseTraceLog # Function: parseTraceLog
# Description: # Description:
...@@ -2324,11 +2340,9 @@ def appendIncompleteTraceLog(testruns): ...@@ -2324,11 +2340,9 @@ def appendIncompleteTraceLog(testruns):
# Output: # Output:
# An array of Data objects # An array of Data objects
def parseTraceLog(): def parseTraceLog():
global sysvals
vprint('Analyzing the ftrace data...') vprint('Analyzing the ftrace data...')
if(os.path.exists(sysvals.ftracefile) == False): if(os.path.exists(sysvals.ftracefile) == False):
doError('%s does not exist' % sysvals.ftracefile, False) doError('%s does not exist' % sysvals.ftracefile)
sysvals.setupAllKprobes() sysvals.setupAllKprobes()
tracewatch = [] tracewatch = []
...@@ -2398,10 +2412,6 @@ def parseTraceLog(): ...@@ -2398,10 +2412,6 @@ def parseTraceLog():
testrun = TestRun(data) testrun = TestRun(data)
testruns.append(testrun) testruns.append(testrun)
parseStamp(tp.stamp, data) parseStamp(tp.stamp, data)
if sysvals.suspendmode == 'mem' and len(tp.fwdata) > data.testnumber:
data.fwSuspend, data.fwResume = tp.fwdata[data.testnumber]
if(data.fwSuspend > 0 or data.fwResume > 0):
data.fwValid = True
data.setStart(t.time) data.setStart(t.time)
data.tKernSus = t.time data.tKernSus = t.time
continue continue
...@@ -2427,6 +2437,11 @@ def parseTraceLog(): ...@@ -2427,6 +2437,11 @@ def parseTraceLog():
data.tKernRes = t.time data.tKernRes = t.time
if data.dmesg['resume_complete']['end'] < 0: if data.dmesg['resume_complete']['end'] < 0:
data.dmesg['resume_complete']['end'] = t.time data.dmesg['resume_complete']['end'] = t.time
if sysvals.suspendmode == 'mem' and len(tp.fwdata) > data.testnumber:
data.fwSuspend, data.fwResume = tp.fwdata[data.testnumber]
if(data.tSuspended != 0 and data.tResumed != 0 and \
(data.fwSuspend > 0 or data.fwResume > 0)):
data.fwValid = True
if(not sysvals.usetracemarkers): if(not sysvals.usetracemarkers):
# no trace markers? then quit and be sure to finish recording # no trace markers? then quit and be sure to finish recording
# the event we used to trigger resume end # the event we used to trigger resume end
...@@ -2713,9 +2728,8 @@ def parseTraceLog(): ...@@ -2713,9 +2728,8 @@ def parseTraceLog():
cg.newActionFromFunction(data) cg.newActionFromFunction(data)
if sysvals.suspendmode == 'command': if sysvals.suspendmode == 'command':
if(sysvals.verbose): for data in testdata:
for data in testdata: data.printDetails()
data.printDetails()
return testdata return testdata
# fill in any missing phases # fill in any missing phases
...@@ -2723,7 +2737,7 @@ def parseTraceLog(): ...@@ -2723,7 +2737,7 @@ def parseTraceLog():
lp = data.phases[0] lp = data.phases[0]
for p in data.phases: for p in data.phases:
if(data.dmesg[p]['start'] < 0 and data.dmesg[p]['end'] < 0): if(data.dmesg[p]['start'] < 0 and data.dmesg[p]['end'] < 0):
print('WARNING: phase "%s" is missing!' % p) vprint('WARNING: phase "%s" is missing!' % p)
if(data.dmesg[p]['start'] < 0): if(data.dmesg[p]['start'] < 0):
data.dmesg[p]['start'] = data.dmesg[lp]['end'] data.dmesg[p]['start'] = data.dmesg[lp]['end']
if(p == 'resume_machine'): if(p == 'resume_machine'):
...@@ -2732,6 +2746,8 @@ def parseTraceLog(): ...@@ -2732,6 +2746,8 @@ def parseTraceLog():
data.tLow = 0 data.tLow = 0
if(data.dmesg[p]['end'] < 0): if(data.dmesg[p]['end'] < 0):
data.dmesg[p]['end'] = data.dmesg[p]['start'] data.dmesg[p]['end'] = data.dmesg[p]['start']
if(p != lp and not ('machine' in p and 'machine' in lp)):
data.dmesg[lp]['end'] = data.dmesg[p]['start']
lp = p lp = p
if(len(sysvals.devicefilter) > 0): if(len(sysvals.devicefilter) > 0):
...@@ -2739,8 +2755,7 @@ def parseTraceLog(): ...@@ -2739,8 +2755,7 @@ def parseTraceLog():
data.fixupInitcallsThatDidntReturn() data.fixupInitcallsThatDidntReturn()
if sysvals.usedevsrc: if sysvals.usedevsrc:
data.optimizeDevSrc() data.optimizeDevSrc()
if sysvals.verbose: data.printDetails()
data.printDetails()
# x2: merge any overlapping devices between test runs # x2: merge any overlapping devices between test runs
if sysvals.usedevsrc and len(testdata) > 1: if sysvals.usedevsrc and len(testdata) > 1:
...@@ -2752,50 +2767,6 @@ def parseTraceLog(): ...@@ -2752,50 +2767,6 @@ def parseTraceLog():
testdata[0].stitchTouchingThreads(testdata[1:]) testdata[0].stitchTouchingThreads(testdata[1:])
return testdata return testdata
# Function: loadRawKernelLog
# Description:
# Load a raw kernel log that wasn't created by this tool, it might be
# possible to extract a valid suspend/resume log
def loadRawKernelLog(dmesgfile):
global sysvals
stamp = {'time': '', 'host': '', 'mode': 'mem', 'kernel': ''}
stamp['time'] = datetime.now().strftime('%B %d %Y, %I:%M:%S %p')
stamp['host'] = sysvals.hostname
testruns = []
data = 0
lf = open(dmesgfile, 'r')
for line in lf:
line = line.replace('\r\n', '')
idx = line.find('[')
if idx > 1:
line = line[idx:]
m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
if(not m):
continue
msg = m.group("msg")
m = re.match('PM: Syncing filesystems.*', msg)
if(m):
if(data):
testruns.append(data)
data = Data(len(testruns))
data.stamp = stamp
if(data):
m = re.match('.* *(?P<k>[0-9]\.[0-9]{2}\.[0-9]-.*) .*', msg)
if(m):
stamp['kernel'] = m.group('k')
m = re.match('PM: Preparing system for (?P<m>.*) sleep', msg)
if(m):
stamp['mode'] = m.group('m')
data.dmesgtext.append(line)
if(data):
testruns.append(data)
sysvals.stamp = stamp
sysvals.suspendmode = stamp['mode']
lf.close()
return testruns
# Function: loadKernelLog # Function: loadKernelLog
# Description: # Description:
# [deprecated for kernel 3.15.0 or newer] # [deprecated for kernel 3.15.0 or newer]
...@@ -2803,15 +2774,16 @@ def loadRawKernelLog(dmesgfile): ...@@ -2803,15 +2774,16 @@ def loadRawKernelLog(dmesgfile):
# The dmesg filename is taken from sysvals # The dmesg filename is taken from sysvals
# Output: # Output:
# An array of empty Data objects with only their dmesgtext attributes set # An array of empty Data objects with only their dmesgtext attributes set
def loadKernelLog(): def loadKernelLog(justtext=False):
global sysvals
vprint('Analyzing the dmesg data...') vprint('Analyzing the dmesg data...')
if(os.path.exists(sysvals.dmesgfile) == False): if(os.path.exists(sysvals.dmesgfile) == False):
doError('%s does not exist' % sysvals.dmesgfile, False) doError('%s does not exist' % sysvals.dmesgfile)
if justtext:
dmesgtext = []
# there can be multiple test runs in a single file # there can be multiple test runs in a single file
tp = TestProps() tp = TestProps()
tp.stamp = datetime.now().strftime('# suspend-%m%d%y-%H%M%S localhost mem unknown')
testruns = [] testruns = []
data = 0 data = 0
lf = open(sysvals.dmesgfile, 'r') lf = open(sysvals.dmesgfile, 'r')
...@@ -2832,6 +2804,9 @@ def loadKernelLog(): ...@@ -2832,6 +2804,9 @@ def loadKernelLog():
if(not m): if(not m):
continue continue
msg = m.group("msg") msg = m.group("msg")
if justtext:
dmesgtext.append(line)
continue
if(re.match('PM: Syncing filesystems.*', msg)): if(re.match('PM: Syncing filesystems.*', msg)):
if(data): if(data):
testruns.append(data) testruns.append(data)
...@@ -2841,24 +2816,24 @@ def loadKernelLog(): ...@@ -2841,24 +2816,24 @@ def loadKernelLog():
data.fwSuspend, data.fwResume = tp.fwdata[data.testnumber] data.fwSuspend, data.fwResume = tp.fwdata[data.testnumber]
if(data.fwSuspend > 0 or data.fwResume > 0): if(data.fwSuspend > 0 or data.fwResume > 0):
data.fwValid = True data.fwValid = True
if(re.match('ACPI: resume from mwait', msg)):
print('NOTE: This suspend appears to be freeze rather than'+\
' %s, it will be treated as such' % sysvals.suspendmode)
sysvals.suspendmode = 'freeze'
if(not data): if(not data):
continue continue
m = re.match('.* *(?P<k>[0-9]\.[0-9]{2}\.[0-9]-.*) .*', msg)
if(m):
sysvals.stamp['kernel'] = m.group('k')
m = re.match('PM: Preparing system for (?P<m>.*) sleep', msg)
if(m):
sysvals.stamp['mode'] = sysvals.suspendmode = m.group('m')
data.dmesgtext.append(line) data.dmesgtext.append(line)
if(data):
testruns.append(data)
lf.close() lf.close()
if(len(testruns) < 1): if justtext:
# bad log, but see if you can extract something meaningful anyway return dmesgtext
testruns = loadRawKernelLog(sysvals.dmesgfile) if data:
testruns.append(data)
if(len(testruns) < 1): if len(testruns) < 1:
doError(' dmesg log is completely unreadable: %s' \ doError(' dmesg log has no suspend/resume data: %s' \
% sysvals.dmesgfile, False) % sysvals.dmesgfile)
# fix lines with same timestamp/function with the call and return swapped # fix lines with same timestamp/function with the call and return swapped
for data in testruns: for data in testruns:
...@@ -2890,8 +2865,6 @@ def loadKernelLog(): ...@@ -2890,8 +2865,6 @@ def loadKernelLog():
# Output: # Output:
# The filled Data object # The filled Data object
def parseKernelLog(data): def parseKernelLog(data):
global sysvals
phase = 'suspend_runtime' phase = 'suspend_runtime'
if(data.fwValid): if(data.fwValid):
...@@ -2949,7 +2922,6 @@ def parseKernelLog(data): ...@@ -2949,7 +2922,6 @@ def parseKernelLog(data):
prevktime = -1.0 prevktime = -1.0
actions = dict() actions = dict()
for line in data.dmesgtext: for line in data.dmesgtext:
# -- preprocessing --
# parse each dmesg line into the time and message # parse each dmesg line into the time and message
m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line) m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
if(m): if(m):
...@@ -2957,8 +2929,6 @@ def parseKernelLog(data): ...@@ -2957,8 +2929,6 @@ def parseKernelLog(data):
try: try:
ktime = float(val) ktime = float(val)
except: except:
doWarning('INVALID DMESG LINE: '+\
line.replace('\n', ''), 'dmesg')
continue continue
msg = m.group('msg') msg = m.group('msg')
# initialize data start to first line time # initialize data start to first line time
...@@ -2976,12 +2946,12 @@ def parseKernelLog(data): ...@@ -2976,12 +2946,12 @@ def parseKernelLog(data):
phase = 'resume_noirq' phase = 'resume_noirq'
data.dmesg[phase]['start'] = ktime data.dmesg[phase]['start'] = ktime
# -- phase changes --
# suspend start # suspend start
if(re.match(dm['suspend_prepare'], msg)): if(re.match(dm['suspend_prepare'], msg)):
phase = 'suspend_prepare' phase = 'suspend_prepare'
data.dmesg[phase]['start'] = ktime data.dmesg[phase]['start'] = ktime
data.setStart(ktime) data.setStart(ktime)
data.tKernSus = ktime
# suspend start # suspend start
elif(re.match(dm['suspend'], msg)): elif(re.match(dm['suspend'], msg)):
data.dmesg['suspend_prepare']['end'] = ktime data.dmesg['suspend_prepare']['end'] = ktime
...@@ -3038,7 +3008,7 @@ def parseKernelLog(data): ...@@ -3038,7 +3008,7 @@ def parseKernelLog(data):
elif(re.match(dm['post_resume'], msg)): elif(re.match(dm['post_resume'], msg)):
data.dmesg['resume_complete']['end'] = ktime data.dmesg['resume_complete']['end'] = ktime
data.setEnd(ktime) data.setEnd(ktime)
phase = 'post_resume' data.tKernRes = ktime
break break
# -- device callbacks -- # -- device callbacks --
...@@ -3065,7 +3035,6 @@ def parseKernelLog(data): ...@@ -3065,7 +3035,6 @@ def parseKernelLog(data):
dev['length'] = int(t) dev['length'] = int(t)
dev['end'] = ktime dev['end'] = ktime
# -- non-devicecallback actions --
# if trace events are not available, these are better than nothing # if trace events are not available, these are better than nothing
if(not sysvals.usetraceevents): if(not sysvals.usetraceevents):
# look for known actions # look for known actions
...@@ -3125,8 +3094,7 @@ def parseKernelLog(data): ...@@ -3125,8 +3094,7 @@ def parseKernelLog(data):
for event in actions[name]: for event in actions[name]:
data.newActionGlobal(name, event['begin'], event['end']) data.newActionGlobal(name, event['begin'], event['end'])
if(sysvals.verbose): data.printDetails()
data.printDetails()
if(len(sysvals.devicefilter) > 0): if(len(sysvals.devicefilter) > 0):
data.deviceFilter(sysvals.devicefilter) data.deviceFilter(sysvals.devicefilter)
data.fixupInitcallsThatDidntReturn() data.fixupInitcallsThatDidntReturn()
...@@ -3138,8 +3106,6 @@ def parseKernelLog(data): ...@@ -3138,8 +3106,6 @@ def parseKernelLog(data):
# Arguments: # Arguments:
# testruns: array of Data objects from parseTraceLog # testruns: array of Data objects from parseTraceLog
def createHTMLSummarySimple(testruns, htmlfile): def createHTMLSummarySimple(testruns, htmlfile):
global sysvals
# print out the basic summary of all the tests # print out the basic summary of all the tests
hf = open(htmlfile, 'w') hf = open(htmlfile, 'w')
...@@ -3264,7 +3230,6 @@ def createHTMLSummarySimple(testruns, htmlfile): ...@@ -3264,7 +3230,6 @@ def createHTMLSummarySimple(testruns, htmlfile):
hf.close() hf.close()
def htmlTitle(): def htmlTitle():
global sysvals
modename = { modename = {
'freeze': 'Freeze (S0)', 'freeze': 'Freeze (S0)',
'standby': 'Standby (S1)', 'standby': 'Standby (S1)',
...@@ -3297,13 +3262,14 @@ def ordinal(value): ...@@ -3297,13 +3262,14 @@ def ordinal(value):
# Output: # Output:
# True if the html file was created, false if it failed # True if the html file was created, false if it failed
def createHTML(testruns): def createHTML(testruns):
global sysvals
if len(testruns) < 1: if len(testruns) < 1:
print('ERROR: Not enough test data to build a timeline') print('ERROR: Not enough test data to build a timeline')
return return
kerror = False
for data in testruns: for data in testruns:
if data.kerror:
kerror = True
data.normalizeTime(testruns[-1].tSuspended) data.normalizeTime(testruns[-1].tSuspended)
x2changes = ['', 'absolute'] x2changes = ['', 'absolute']
...@@ -3318,6 +3284,7 @@ def createHTML(testruns): ...@@ -3318,6 +3284,7 @@ def createHTML(testruns):
html_timeline = '<div id="dmesgzoombox" class="zoombox">\n<div id="{0}" class="timeline" style="height:{1}px">\n' html_timeline = '<div id="dmesgzoombox" class="zoombox">\n<div id="{0}" class="timeline" style="height:{1}px">\n'
html_tblock = '<div id="block{0}" class="tblock" style="left:{1}%;width:{2}%;"><div class="tback" style="height:{3}px"></div>\n' html_tblock = '<div id="block{0}" class="tblock" style="left:{1}%;width:{2}%;"><div class="tback" style="height:{3}px"></div>\n'
html_device = '<div id="{0}" title="{1}" class="thread{7}" style="left:{2}%;top:{3}px;height:{4}px;width:{5}%;{8}">{6}</div>\n' html_device = '<div id="{0}" title="{1}" class="thread{7}" style="left:{2}%;top:{3}px;height:{4}px;width:{5}%;{8}">{6}</div>\n'
html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">ERROR&rarr;</div>\n'
html_traceevent = '<div title="{0}" class="traceevent{6}" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;{7}">{5}</div>\n' html_traceevent = '<div title="{0}" class="traceevent{6}" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;{7}">{5}</div>\n'
html_cpuexec = '<div class="jiffie" style="left:{0}%;top:{1}px;height:{2}px;width:{3}%;background:{4};"></div>\n' html_cpuexec = '<div class="jiffie" style="left:{0}%;top:{1}px;height:{2}px;width:{3}%;background:{4};"></div>\n'
html_phase = '<div class="phase" style="left:{0}%;width:{1}%;top:{2}px;height:{3}px;background-color:{4}">{5}</div>\n' html_phase = '<div class="phase" style="left:{0}%;width:{1}%;top:{2}px;height:{3}px;background-color:{4}">{5}</div>\n'
...@@ -3347,11 +3314,16 @@ def createHTML(testruns): ...@@ -3347,11 +3314,16 @@ def createHTML(testruns):
hoverZ = 'z-index:8;' hoverZ = 'z-index:8;'
if sysvals.usedevsrc: if sysvals.usedevsrc:
hoverZ = '' hoverZ = ''
scaleH = 20
scaleTH = 20
if kerror:
scaleH = 40
scaleTH = 60
# device timeline # device timeline
vprint('Creating Device Timeline...') vprint('Creating Device Timeline...')
devtl = Timeline(30) devtl = Timeline(30, scaleH)
# Generate the header for this timeline # Generate the header for this timeline
for data in testruns: for data in testruns:
...@@ -3415,7 +3387,6 @@ def createHTML(testruns): ...@@ -3415,7 +3387,6 @@ def createHTML(testruns):
# time scale for potentially multiple datasets # time scale for potentially multiple datasets
t0 = testruns[0].start t0 = testruns[0].start
tMax = testruns[-1].end tMax = testruns[-1].end
tSuspended = testruns[-1].tSuspended
tTotal = tMax - t0 tTotal = tMax - t0
# determine the maximum number of rows we need to draw # determine the maximum number of rows we need to draw
...@@ -3507,6 +3478,11 @@ def createHTML(testruns): ...@@ -3507,6 +3478,11 @@ def createHTML(testruns):
devtl.html['timeline'] += html_phase.format(left, width, \ devtl.html['timeline'] += html_phase.format(left, width, \
'%.3f'%devtl.scaleH, '%.3f'%devtl.bodyH, \ '%.3f'%devtl.scaleH, '%.3f'%devtl.bodyH, \
data.dmesg[b]['color'], '') data.dmesg[b]['color'], '')
for e in data.errorinfo[dir]:
# draw red lines for any kernel errors found
t, err = e
right = '%f' % (((mMax-t)*100.0)/mTotal)
devtl.html['timeline'] += html_error.format(right, err)
for b in sorted(phases[dir]): for b in sorted(phases[dir]):
# draw the devices for this phase # draw the devices for this phase
phaselist = data.dmesg[b]['list'] phaselist = data.dmesg[b]['list']
...@@ -3623,8 +3599,7 @@ def createHTML(testruns): ...@@ -3623,8 +3599,7 @@ def createHTML(testruns):
t2 {color:black;font:25px Times;}\n\ t2 {color:black;font:25px Times;}\n\
t3 {color:black;font:20px Times;white-space:nowrap;}\n\ t3 {color:black;font:20px Times;white-space:nowrap;}\n\
t4 {color:black;font:bold 30px Times;line-height:60px;white-space:nowrap;}\n\ t4 {color:black;font:bold 30px Times;line-height:60px;white-space:nowrap;}\n\
cS {color:blue;font:bold 11px Times;}\n\ cS {font:bold 13px Times;}\n\
cR {color:red;font:bold 11px Times;}\n\
table {width:100%;}\n\ table {width:100%;}\n\
.gray {background-color:rgba(80,80,80,0.1);}\n\ .gray {background-color:rgba(80,80,80,0.1);}\n\
.green {background-color:rgba(204,255,204,0.4);}\n\ .green {background-color:rgba(204,255,204,0.4);}\n\
...@@ -3655,7 +3630,8 @@ def createHTML(testruns): ...@@ -3655,7 +3630,8 @@ def createHTML(testruns):
.traceevent:hover {color:white;font-weight:bold;border:1px solid white;}\n\ .traceevent:hover {color:white;font-weight:bold;border:1px solid white;}\n\
.phase {position:absolute;overflow:hidden;border:0px;text-align:center;}\n\ .phase {position:absolute;overflow:hidden;border:0px;text-align:center;}\n\
.phaselet {position:absolute;overflow:hidden;border:0px;text-align:center;height:100px;font-size:24px;}\n\ .phaselet {position:absolute;overflow:hidden;border:0px;text-align:center;height:100px;font-size:24px;}\n\
.t {position:absolute;pointer-events:none;top:0%;height:100%;border-right:1px solid black;z-index:6;}\n\ .t {position:absolute;line-height:'+('%d'%scaleTH)+'px;pointer-events:none;top:0;height:100%;border-right:1px solid black;z-index:6;}\n\
.err {position:absolute;top:0%;height:100%;border-right:3px solid red;color:red;font:bold 14px Times;line-height:18px;}\n\
.legend {position:relative; width:100%; height:40px; text-align:center;margin-bottom:20px}\n\ .legend {position:relative; width:100%; height:40px; text-align:center;margin-bottom:20px}\n\
.legend .square {position:absolute;cursor:pointer;top:10px; width:0px;height:20px;border:1px solid;padding-left:20px;}\n\ .legend .square {position:absolute;cursor:pointer;top:10px; width:0px;height:20px;border:1px solid;padding-left:20px;}\n\
button {height:40px;width:200px;margin-bottom:20px;margin-top:20px;font-size:24px;}\n\ button {height:40px;width:200px;margin-bottom:20px;margin-top:20px;font-size:24px;}\n\
...@@ -3683,6 +3659,8 @@ def createHTML(testruns): ...@@ -3683,6 +3659,8 @@ def createHTML(testruns):
# write the test title and general info header # write the test title and general info header
if(sysvals.stamp['time'] != ""): if(sysvals.stamp['time'] != ""):
hf.write(headline_version) hf.write(headline_version)
if sysvals.logmsg:
hf.write('<button id="showtest" class="logbtn">log</button>')
if sysvals.addlogs and sysvals.dmesgfile: if sysvals.addlogs and sysvals.dmesgfile:
hf.write('<button id="showdmesg" class="logbtn">dmesg</button>') hf.write('<button id="showdmesg" class="logbtn">dmesg</button>')
if sysvals.addlogs and sysvals.ftracefile: if sysvals.addlogs and sysvals.ftracefile:
...@@ -3718,7 +3696,10 @@ def createHTML(testruns): ...@@ -3718,7 +3696,10 @@ def createHTML(testruns):
hf.write('</div>\n') hf.write('</div>\n')
# write the ftrace data (callgraph) # write the ftrace data (callgraph)
data = testruns[-1] if sysvals.cgtest >= 0 and len(testruns) > sysvals.cgtest:
data = testruns[sysvals.cgtest]
else:
data = testruns[-1]
if(sysvals.usecallgraph and not sysvals.embedded): if(sysvals.usecallgraph and not sysvals.embedded):
hf.write('<section id="callgraphs" class="callgraph">\n') hf.write('<section id="callgraphs" class="callgraph">\n')
# write out the ftrace data converted to html # write out the ftrace data converted to html
...@@ -3728,6 +3709,8 @@ def createHTML(testruns): ...@@ -3728,6 +3709,8 @@ def createHTML(testruns):
html_func_leaf = '<article>{0} {1}</article>\n' html_func_leaf = '<article>{0} {1}</article>\n'
num = 0 num = 0
for p in data.phases: for p in data.phases:
if sysvals.cgphase and p != sysvals.cgphase:
continue
list = data.dmesg[p]['list'] list = data.dmesg[p]['list']
for devname in data.sortedDevices(p): for devname in data.sortedDevices(p):
if('ftrace' not in list[devname]): if('ftrace' not in list[devname]):
...@@ -3765,11 +3748,15 @@ def createHTML(testruns): ...@@ -3765,11 +3748,15 @@ def createHTML(testruns):
hf.write(html_func_end) hf.write(html_func_end)
hf.write('\n\n </section>\n') hf.write('\n\n </section>\n')
# add the test log as a hidden div
if sysvals.logmsg:
hf.write('<div id="testlog" style="display:none;">\n'+sysvals.logmsg+'</div>\n')
# add the dmesg log as a hidden div # add the dmesg log as a hidden div
if sysvals.addlogs and sysvals.dmesgfile: if sysvals.addlogs and sysvals.dmesgfile:
hf.write('<div id="dmesglog" style="display:none;">\n') hf.write('<div id="dmesglog" style="display:none;">\n')
lf = open(sysvals.dmesgfile, 'r') lf = open(sysvals.dmesgfile, 'r')
for line in lf: for line in lf:
line = line.replace('<', '&lt').replace('>', '&gt')
hf.write(line) hf.write(line)
lf.close() lf.close()
hf.write('</div>\n') hf.write('</div>\n')
...@@ -3822,7 +3809,7 @@ def addScriptCode(hf, testruns): ...@@ -3822,7 +3809,7 @@ def addScriptCode(hf, testruns):
' var resolution = -1;\n'\ ' var resolution = -1;\n'\
' var dragval = [0, 0];\n'\ ' var dragval = [0, 0];\n'\
' function redrawTimescale(t0, tMax, tS) {\n'\ ' function redrawTimescale(t0, tMax, tS) {\n'\
' var rline = \'<div class="t" style="left:0;border-left:1px solid black;border-right:0;"><cR><-R</cR></div>\';\n'\ ' var rline = \'<div class="t" style="left:0;border-left:1px solid black;border-right:0;"><cS>&larr;R</cS></div>\';\n'\
' var tTotal = tMax - t0;\n'\ ' var tTotal = tMax - t0;\n'\
' var list = document.getElementsByClassName("tblock");\n'\ ' var list = document.getElementsByClassName("tblock");\n'\
' for (var i = 0; i < list.length; i++) {\n'\ ' for (var i = 0; i < list.length; i++) {\n'\
...@@ -3847,7 +3834,7 @@ def addScriptCode(hf, testruns): ...@@ -3847,7 +3834,7 @@ def addScriptCode(hf, testruns):
' pos = 100 - (((j)*tS*100)/mTotal) - divEdge;\n'\ ' pos = 100 - (((j)*tS*100)/mTotal) - divEdge;\n'\
' val = (j-divTotal+1)*tS;\n'\ ' val = (j-divTotal+1)*tS;\n'\
' if(j == divTotal - 1)\n'\ ' if(j == divTotal - 1)\n'\
' htmlline = \'<div class="t" style="right:\'+pos+\'%"><cS>S-></cS></div>\';\n'\ ' htmlline = \'<div class="t" style="right:\'+pos+\'%"><cS>S&rarr;</cS></div>\';\n'\
' else\n'\ ' else\n'\
' htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\ ' htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\
' }\n'\ ' }\n'\
...@@ -4027,12 +4014,7 @@ def addScriptCode(hf, testruns): ...@@ -4027,12 +4014,7 @@ def addScriptCode(hf, testruns):
' }\n'\ ' }\n'\
' }\n'\ ' }\n'\
' function devListWindow(e) {\n'\ ' function devListWindow(e) {\n'\
' var sx = e.clientX;\n'\ ' var win = window.open();\n'\
' if(sx > window.innerWidth - 440)\n'\
' sx = window.innerWidth - 440;\n'\
' var cfg="top="+e.screenY+", left="+sx+", width=440, height=720, scrollbars=yes";\n'\
' var win = window.open("", "_blank", cfg);\n'\
' if(window.chrome) win.moveBy(sx, 0);\n'\
' var html = "<title>"+e.target.innerHTML+"</title>"+\n'\ ' var html = "<title>"+e.target.innerHTML+"</title>"+\n'\
' "<style type=\\"text/css\\">"+\n'\ ' "<style type=\\"text/css\\">"+\n'\
' " ul {list-style-type:circle;padding-left:10px;margin-left:10px;}"+\n'\ ' " ul {list-style-type:circle;padding-left:10px;margin-left:10px;}"+\n'\
...@@ -4042,6 +4024,12 @@ def addScriptCode(hf, testruns): ...@@ -4042,6 +4024,12 @@ def addScriptCode(hf, testruns):
' dt = devtable[1];\n'\ ' dt = devtable[1];\n'\
' win.document.write(html+dt);\n'\ ' win.document.write(html+dt);\n'\
' }\n'\ ' }\n'\
' function errWindow() {\n'\
' var text = this.id;\n'\
' var win = window.open();\n'\
' win.document.write("<pre>"+text+"</pre>");\n'\
' win.document.close();\n'\
' }\n'\
' function logWindow(e) {\n'\ ' function logWindow(e) {\n'\
' var name = e.target.id.slice(4);\n'\ ' var name = e.target.id.slice(4);\n'\
' var win = window.open();\n'\ ' var win = window.open();\n'\
...@@ -4089,6 +4077,9 @@ def addScriptCode(hf, testruns): ...@@ -4089,6 +4077,9 @@ def addScriptCode(hf, testruns):
' var list = document.getElementsByClassName("square");\n'\ ' var list = document.getElementsByClassName("square");\n'\
' for (var i = 0; i < list.length; i++)\n'\ ' for (var i = 0; i < list.length; i++)\n'\
' list[i].onclick = onClickPhase;\n'\ ' list[i].onclick = onClickPhase;\n'\
' var list = document.getElementsByClassName("err");\n'\
' for (var i = 0; i < list.length; i++)\n'\
' list[i].onclick = errWindow;\n'\
' var list = document.getElementsByClassName("logbtn");\n'\ ' var list = document.getElementsByClassName("logbtn");\n'\
' for (var i = 0; i < list.length; i++)\n'\ ' for (var i = 0; i < list.length; i++)\n'\
' list[i].onclick = logWindow;\n'\ ' list[i].onclick = logWindow;\n'\
...@@ -4111,8 +4102,6 @@ def addScriptCode(hf, testruns): ...@@ -4111,8 +4102,6 @@ def addScriptCode(hf, testruns):
# Execute system suspend through the sysfs interface, then copy the output # Execute system suspend through the sysfs interface, then copy the output
# dmesg and ftrace files to the test output directory. # dmesg and ftrace files to the test output directory.
def executeSuspend(): def executeSuspend():
global sysvals
pm = ProcessMonitor() pm = ProcessMonitor()
tp = sysvals.tpath tp = sysvals.tpath
fwdata = [] fwdata = []
...@@ -4128,7 +4117,9 @@ def executeSuspend(): ...@@ -4128,7 +4117,9 @@ def executeSuspend():
for count in range(1,sysvals.execcount+1): for count in range(1,sysvals.execcount+1):
# x2delay in between test runs # x2delay in between test runs
if(count > 1 and sysvals.x2delay > 0): if(count > 1 and sysvals.x2delay > 0):
sysvals.fsetVal('WAIT %d' % sysvals.x2delay, 'trace_marker')
time.sleep(sysvals.x2delay/1000.0) time.sleep(sysvals.x2delay/1000.0)
sysvals.fsetVal('WAIT END', 'trace_marker')
# start message # start message
if sysvals.testcommand != '': if sysvals.testcommand != '':
print('COMMAND START') print('COMMAND START')
...@@ -4146,7 +4137,9 @@ def executeSuspend(): ...@@ -4146,7 +4137,9 @@ def executeSuspend():
sysvals.fsetVal('SUSPEND START', 'trace_marker') sysvals.fsetVal('SUSPEND START', 'trace_marker')
# predelay delay # predelay delay
if(count == 1 and sysvals.predelay > 0): if(count == 1 and sysvals.predelay > 0):
sysvals.fsetVal('WAIT %d' % sysvals.predelay, 'trace_marker')
time.sleep(sysvals.predelay/1000.0) time.sleep(sysvals.predelay/1000.0)
sysvals.fsetVal('WAIT END', 'trace_marker')
# initiate suspend or command # initiate suspend or command
if sysvals.testcommand != '': if sysvals.testcommand != '':
call(sysvals.testcommand+' 2>&1', shell=True); call(sysvals.testcommand+' 2>&1', shell=True);
...@@ -4162,7 +4155,9 @@ def executeSuspend(): ...@@ -4162,7 +4155,9 @@ def executeSuspend():
sysvals.rtcWakeAlarmOff() sysvals.rtcWakeAlarmOff()
# postdelay delay # postdelay delay
if(count == sysvals.execcount and sysvals.postdelay > 0): if(count == sysvals.execcount and sysvals.postdelay > 0):
sysvals.fsetVal('WAIT %d' % sysvals.postdelay, 'trace_marker')
time.sleep(sysvals.postdelay/1000.0) time.sleep(sysvals.postdelay/1000.0)
sysvals.fsetVal('WAIT END', 'trace_marker')
# return from suspend # return from suspend
print('RESUME COMPLETE') print('RESUME COMPLETE')
if(sysvals.usecallgraph or sysvals.usetraceevents): if(sysvals.usecallgraph or sysvals.usetraceevents):
...@@ -4185,8 +4180,6 @@ def executeSuspend(): ...@@ -4185,8 +4180,6 @@ def executeSuspend():
sysvals.getdmesg() sysvals.getdmesg()
def writeDatafileHeader(filename, fwdata): def writeDatafileHeader(filename, fwdata):
global sysvals
fp = open(filename, 'a') fp = open(filename, 'a')
fp.write(sysvals.teststamp+'\n') fp.write(sysvals.teststamp+'\n')
if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'): if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'):
...@@ -4202,8 +4195,6 @@ def writeDatafileHeader(filename, fwdata): ...@@ -4202,8 +4195,6 @@ def writeDatafileHeader(filename, fwdata):
# to always-on since the kernel cant determine if the device can # to always-on since the kernel cant determine if the device can
# properly autosuspend # properly autosuspend
def setUSBDevicesAuto(): def setUSBDevicesAuto():
global sysvals
rootCheck(True) rootCheck(True)
for dirname, dirnames, filenames in os.walk('/sys/devices'): for dirname, dirnames, filenames in os.walk('/sys/devices'):
if(re.match('.*/usb[0-9]*.*', dirname) and if(re.match('.*/usb[0-9]*.*', dirname) and
...@@ -4250,8 +4241,6 @@ def ms2nice(val): ...@@ -4250,8 +4241,6 @@ def ms2nice(val):
# Detect all the USB hosts and devices currently connected and add # Detect all the USB hosts and devices currently connected and add
# a list of USB device names to sysvals for better timeline readability # a list of USB device names to sysvals for better timeline readability
def detectUSB(): def detectUSB():
global sysvals
field = {'idVendor':'', 'idProduct':'', 'product':'', 'speed':''} field = {'idVendor':'', 'idProduct':'', 'product':'', 'speed':''}
power = {'async':'', 'autosuspend':'', 'autosuspend_delay_ms':'', power = {'async':'', 'autosuspend':'', 'autosuspend_delay_ms':'',
'control':'', 'persist':'', 'runtime_enabled':'', 'control':'', 'persist':'', 'runtime_enabled':'',
...@@ -4306,7 +4295,6 @@ def detectUSB(): ...@@ -4306,7 +4295,6 @@ def detectUSB():
# Description: # Description:
# Retrieve a list of properties for all devices in the trace log # Retrieve a list of properties for all devices in the trace log
def devProps(data=0): def devProps(data=0):
global sysvals
props = dict() props = dict()
if data: if data:
...@@ -4331,7 +4319,7 @@ def devProps(data=0): ...@@ -4331,7 +4319,7 @@ def devProps(data=0):
return return
if(os.path.exists(sysvals.ftracefile) == False): if(os.path.exists(sysvals.ftracefile) == False):
doError('%s does not exist' % sysvals.ftracefile, False) doError('%s does not exist' % sysvals.ftracefile)
# first get the list of devices we need properties for # first get the list of devices we need properties for
msghead = 'Additional data added by AnalyzeSuspend' msghead = 'Additional data added by AnalyzeSuspend'
...@@ -4354,7 +4342,7 @@ def devProps(data=0): ...@@ -4354,7 +4342,7 @@ def devProps(data=0):
m = re.match('.*: (?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*', m.group('msg')); m = re.match('.*: (?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*', m.group('msg'));
if(not m): if(not m):
continue continue
drv, dev, par = m.group('drv'), m.group('d'), m.group('p') dev = m.group('d')
if dev not in props: if dev not in props:
props[dev] = DevProps() props[dev] = DevProps()
tf.close() tf.close()
...@@ -4430,7 +4418,6 @@ def devProps(data=0): ...@@ -4430,7 +4418,6 @@ def devProps(data=0):
# Output: # Output:
# A string list of the available modes # A string list of the available modes
def getModes(): def getModes():
global sysvals
modes = '' modes = ''
if(os.path.exists(sysvals.powerfile)): if(os.path.exists(sysvals.powerfile)):
fp = open(sysvals.powerfile, 'r') fp = open(sysvals.powerfile, 'r')
...@@ -4444,8 +4431,6 @@ def getModes(): ...@@ -4444,8 +4431,6 @@ def getModes():
# Arguments: # Arguments:
# output: True to output the info to stdout, False otherwise # output: True to output the info to stdout, False otherwise
def getFPDT(output): def getFPDT(output):
global sysvals
rectype = {} rectype = {}
rectype[0] = 'Firmware Basic Boot Performance Record' rectype[0] = 'Firmware Basic Boot Performance Record'
rectype[1] = 'S3 Performance Table Record' rectype[1] = 'S3 Performance Table Record'
...@@ -4456,19 +4441,19 @@ def getFPDT(output): ...@@ -4456,19 +4441,19 @@ def getFPDT(output):
rootCheck(True) rootCheck(True)
if(not os.path.exists(sysvals.fpdtpath)): if(not os.path.exists(sysvals.fpdtpath)):
if(output): if(output):
doError('file does not exist: %s' % sysvals.fpdtpath, False) doError('file does not exist: %s' % sysvals.fpdtpath)
return False return False
if(not os.access(sysvals.fpdtpath, os.R_OK)): if(not os.access(sysvals.fpdtpath, os.R_OK)):
if(output): if(output):
doError('file is not readable: %s' % sysvals.fpdtpath, False) doError('file is not readable: %s' % sysvals.fpdtpath)
return False return False
if(not os.path.exists(sysvals.mempath)): if(not os.path.exists(sysvals.mempath)):
if(output): if(output):
doError('file does not exist: %s' % sysvals.mempath, False) doError('file does not exist: %s' % sysvals.mempath)
return False return False
if(not os.access(sysvals.mempath, os.R_OK)): if(not os.access(sysvals.mempath, os.R_OK)):
if(output): if(output):
doError('file is not readable: %s' % sysvals.mempath, False) doError('file is not readable: %s' % sysvals.mempath)
return False return False
fp = open(sysvals.fpdtpath, 'rb') fp = open(sysvals.fpdtpath, 'rb')
...@@ -4478,7 +4463,7 @@ def getFPDT(output): ...@@ -4478,7 +4463,7 @@ def getFPDT(output):
if(len(buf) < 36): if(len(buf) < 36):
if(output): if(output):
doError('Invalid FPDT table data, should '+\ doError('Invalid FPDT table data, should '+\
'be at least 36 bytes', False) 'be at least 36 bytes')
return False return False
table = struct.unpack('4sIBB6s8sI4sI', buf[0:36]) table = struct.unpack('4sIBB6s8sI4sI', buf[0:36])
...@@ -4577,7 +4562,6 @@ def getFPDT(output): ...@@ -4577,7 +4562,6 @@ def getFPDT(output):
# Output: # Output:
# True if the test will work, False if not # True if the test will work, False if not
def statusCheck(probecheck=False): def statusCheck(probecheck=False):
global sysvals
status = True status = True
print('Checking this system (%s)...' % platform.node()) print('Checking this system (%s)...' % platform.node())
...@@ -4661,20 +4645,13 @@ def statusCheck(probecheck=False): ...@@ -4661,20 +4645,13 @@ def statusCheck(probecheck=False):
return status return status
# verify kprobes # verify kprobes
if sysvals.usekprobes and len(sysvals.tracefuncs) > 0: if sysvals.usekprobes:
print(' verifying timeline kprobes work:') for name in sysvals.tracefuncs:
for name in sorted(sysvals.tracefuncs): sysvals.defaultKprobe(name, sysvals.tracefuncs[name])
res = sysvals.colorText('NO') if sysvals.usedevsrc:
if sysvals.testKprobe(name, sysvals.tracefuncs[name]): for name in sysvals.dev_tracefuncs:
res = 'YES' sysvals.defaultKprobe(name, sysvals.dev_tracefuncs[name])
print(' %s: %s' % (name, res)) sysvals.addKprobes(True)
if sysvals.usedevsrc and sysvals.usekprobes and len(sysvals.dev_tracefuncs) > 0:
print(' verifying dev kprobes work:')
for name in sorted(sysvals.dev_tracefuncs):
res = sysvals.colorText('NO')
if sysvals.testKprobe(name, sysvals.dev_tracefuncs[name]):
res = 'YES'
print(' %s: %s' % (name, res))
return status return status
...@@ -4684,33 +4661,20 @@ def statusCheck(probecheck=False): ...@@ -4684,33 +4661,20 @@ def statusCheck(probecheck=False):
# Arguments: # Arguments:
# msg: the error message to print # msg: the error message to print
# help: True if printHelp should be called after, False otherwise # help: True if printHelp should be called after, False otherwise
def doError(msg, help): def doError(msg, help=False):
if(help == True): if(help == True):
printHelp() printHelp()
print('ERROR: %s\n') % msg print('ERROR: %s\n') % msg
sys.exit() sys.exit()
# Function: doWarning
# Description:
# generic warning function for non-catastrophic anomalies
# Arguments:
# msg: the warning message to print
# file: If not empty, a filename to request be sent to the owner for debug
def doWarning(msg, file=''):
print('/* %s */') % msg
if(file):
print('/* For a fix, please send this'+\
' %s file to <todd.e.brandt@intel.com> */' % file)
# Function: rootCheck # Function: rootCheck
# Description: # Description:
# quick check to see if we have root access # quick check to see if we have root access
def rootCheck(fatal): def rootCheck(fatal):
global sysvals
if(os.access(sysvals.powerfile, os.W_OK)): if(os.access(sysvals.powerfile, os.W_OK)):
return True return True
if fatal: if fatal:
doError('This command must be run as root', False) doError('This command must be run as root')
return False return False
# Function: getArgInt # Function: getArgInt
...@@ -4751,66 +4715,51 @@ def getArgFloat(name, args, min, max, main=True): ...@@ -4751,66 +4715,51 @@ def getArgFloat(name, args, min, max, main=True):
doError(name+': value should be between %f and %f' % (min, max), True) doError(name+': value should be between %f and %f' % (min, max), True)
return val return val
# Function: rerunTest def processData():
# Description:
# generate an output from an existing set of ftrace/dmesg logs
def rerunTest():
global sysvals
if(sysvals.ftracefile != ''):
doesTraceLogHaveTraceEvents()
if(sysvals.dmesgfile == '' and not sysvals.usetraceeventsonly):
doError('recreating this html output '+\
'requires a dmesg file', False)
sysvals.setOutputFile()
vprint('Output file: %s' % sysvals.htmlfile)
if(os.path.exists(sysvals.htmlfile) and not os.access(sysvals.htmlfile, os.W_OK)):
doError('missing permission to write to %s' % sysvals.htmlfile, False)
print('PROCESSING DATA') print('PROCESSING DATA')
if(sysvals.usetraceeventsonly): if(sysvals.usetraceeventsonly):
testruns = parseTraceLog() testruns = parseTraceLog()
if sysvals.dmesgfile:
dmesgtext = loadKernelLog(True)
for data in testruns:
data.extractErrorInfo(dmesgtext)
else: else:
testruns = loadKernelLog() testruns = loadKernelLog()
for data in testruns: for data in testruns:
parseKernelLog(data) parseKernelLog(data)
if(sysvals.ftracefile != ''): if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)):
appendIncompleteTraceLog(testruns) appendIncompleteTraceLog(testruns)
createHTML(testruns) createHTML(testruns)
# Function: rerunTest
# Description:
# generate an output from an existing set of ftrace/dmesg logs
def rerunTest():
if sysvals.ftracefile:
doesTraceLogHaveTraceEvents()
if not sysvals.dmesgfile and not sysvals.usetraceeventsonly:
doError('recreating this html output requires a dmesg file')
sysvals.setOutputFile()
vprint('Output file: %s' % sysvals.htmlfile)
if(os.path.exists(sysvals.htmlfile) and not os.access(sysvals.htmlfile, os.W_OK)):
doError('missing permission to write to %s' % sysvals.htmlfile)
processData()
# Function: runTest # Function: runTest
# Description: # Description:
# execute a suspend/resume, gather the logs, and generate the output # execute a suspend/resume, gather the logs, and generate the output
def runTest(subdir, testpath=''): def runTest(subdir, testpath=''):
global sysvals
# prepare for the test # prepare for the test
sysvals.initFtrace() sysvals.initFtrace()
sysvals.initTestOutput(subdir, testpath) sysvals.initTestOutput(subdir, testpath)
vprint('Output files:\n\t%s\n\t%s\n\t%s' % \
vprint('Output files:\n %s' % sysvals.dmesgfile) (sysvals.dmesgfile, sysvals.ftracefile, sysvals.htmlfile))
if(sysvals.usecallgraph or
sysvals.usetraceevents or
sysvals.usetraceeventsonly):
vprint(' %s' % sysvals.ftracefile)
vprint(' %s' % sysvals.htmlfile)
# execute the test # execute the test
executeSuspend() executeSuspend()
sysvals.cleanupFtrace() sysvals.cleanupFtrace()
processData()
# analyze the data and create the html output
print('PROCESSING DATA')
if(sysvals.usetraceeventsonly):
# data for kernels 3.15 or newer is entirely in ftrace
testruns = parseTraceLog()
else:
# data for kernels older than 3.15 is primarily in dmesg
testruns = loadKernelLog()
for data in testruns:
parseKernelLog(data)
if(sysvals.usecallgraph or sysvals.usetraceevents):
appendIncompleteTraceLog(testruns)
createHTML(testruns)
# if running as root, change output dir owner to sudo_user # if running as root, change output dir owner to sudo_user
if os.path.isdir(sysvals.testdir) and os.getuid() == 0 and \ if os.path.isdir(sysvals.testdir) and os.getuid() == 0 and \
'SUDO_USER' in os.environ: 'SUDO_USER' in os.environ:
...@@ -4821,8 +4770,6 @@ def runTest(subdir, testpath=''): ...@@ -4821,8 +4770,6 @@ def runTest(subdir, testpath=''):
# Description: # Description:
# create a summary of tests in a sub-directory # create a summary of tests in a sub-directory
def runSummary(subdir, output): def runSummary(subdir, output):
global sysvals
# get a list of ftrace output files # get a list of ftrace output files
files = [] files = []
for dirname, dirnames, filenames in os.walk(subdir): for dirname, dirnames, filenames in os.walk(subdir):
...@@ -4878,7 +4825,6 @@ def checkArgBool(value): ...@@ -4878,7 +4825,6 @@ def checkArgBool(value):
# Description: # Description:
# Configure the script via the info in a config file # Configure the script via the info in a config file
def configFromFile(file): def configFromFile(file):
global sysvals
Config = ConfigParser.ConfigParser() Config = ConfigParser.ConfigParser()
Config.read(file) Config.read(file)
...@@ -4939,15 +4885,13 @@ def configFromFile(file): ...@@ -4939,15 +4885,13 @@ def configFromFile(file):
sysvals.setOutputFolder(value) sysvals.setOutputFolder(value)
if sysvals.suspendmode == 'command' and not sysvals.testcommand: if sysvals.suspendmode == 'command' and not sysvals.testcommand:
doError('No command supplied for mode "command"', False) doError('No command supplied for mode "command"')
# compatibility errors # compatibility errors
if sysvals.usedevsrc and sysvals.usecallgraph: if sysvals.usedevsrc and sysvals.usecallgraph:
doError('-dev is not compatible with -f', False) doError('-dev is not compatible with -f')
if sysvals.usecallgraph and sysvals.execcount > 1:
doError('-x2 is not compatible with -f', False)
if sysvals.usecallgraph and sysvals.useprocmon: if sysvals.usecallgraph and sysvals.useprocmon:
doError('-proc is not compatible with -f', False) doError('-proc is not compatible with -f')
if overridekprobes: if overridekprobes:
sysvals.tracefuncs = dict() sysvals.tracefuncs = dict()
...@@ -4964,7 +4908,7 @@ def configFromFile(file): ...@@ -4964,7 +4908,7 @@ def configFromFile(file):
if kprobesec in sections: if kprobesec in sections:
for name in Config.options(kprobesec): for name in Config.options(kprobesec):
if name in kprobes: if name in kprobes:
doError('Duplicate timeline function found "%s"' % (name), False) doError('Duplicate timeline function found "%s"' % (name))
text = Config.get(kprobesec, name) text = Config.get(kprobesec, name)
kprobes[name] = (text, False) kprobes[name] = (text, False)
...@@ -4997,12 +4941,12 @@ def configFromFile(file): ...@@ -4997,12 +4941,12 @@ def configFromFile(file):
args[d[0]] = d[1] args[d[0]] = d[1]
i += 1 i += 1
if not function or not format: if not function or not format:
doError('Invalid kprobe: %s' % name, False) doError('Invalid kprobe: %s' % name)
for arg in re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', format): for arg in re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', format):
if arg not in args: if arg not in args:
doError('Kprobe "%s" is missing argument "%s"' % (name, arg), False) doError('Kprobe "%s" is missing argument "%s"' % (name, arg))
if (dev and name in sysvals.dev_tracefuncs) or (not dev and name in sysvals.tracefuncs): if (dev and name in sysvals.dev_tracefuncs) or (not dev and name in sysvals.tracefuncs):
doError('Duplicate timeline function found "%s"' % (name), False) doError('Duplicate timeline function found "%s"' % (name))
kp = { kp = {
'name': name, 'name': name,
...@@ -5021,7 +4965,6 @@ def configFromFile(file): ...@@ -5021,7 +4965,6 @@ def configFromFile(file):
# Description: # Description:
# print out the help text # print out the help text
def printHelp(): def printHelp():
global sysvals
modes = getModes() modes = getModes()
print('') print('')
...@@ -5074,6 +5017,8 @@ def printHelp(): ...@@ -5074,6 +5017,8 @@ def printHelp():
print(' -fadd file Add functions to be graphed in the timeline from a list in a text file') print(' -fadd file Add functions to be graphed in the timeline from a list in a text file')
print(' -filter "d1,d2,..." Filter out all but this comma-delimited list of device names') print(' -filter "d1,d2,..." Filter out all but this comma-delimited list of device names')
print(' -mincg ms Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)') print(' -mincg ms Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)')
print(' -cgphase P Only show callgraph data for phase P (e.g. suspend_late)')
print(' -cgtest N Only show callgraph data for test N (e.g. 0 or 1 in an x2 run)')
print(' -timeprec N Number of significant digits in timestamps (0:S, [3:ms], 6:us)') print(' -timeprec N Number of significant digits in timestamps (0:S, [3:ms], 6:us)')
print(' [utilities]') print(' [utilities]')
print(' -fpdt Print out the contents of the ACPI Firmware Performance Data Table') print(' -fpdt Print out the contents of the ACPI Firmware Performance Data Table')
...@@ -5139,6 +5084,17 @@ if __name__ == '__main__': ...@@ -5139,6 +5084,17 @@ if __name__ == '__main__':
sysvals.mindevlen = getArgFloat('-mindev', args, 0.0, 10000.0) sysvals.mindevlen = getArgFloat('-mindev', args, 0.0, 10000.0)
elif(arg == '-mincg'): elif(arg == '-mincg'):
sysvals.mincglen = getArgFloat('-mincg', args, 0.0, 10000.0) sysvals.mincglen = getArgFloat('-mincg', args, 0.0, 10000.0)
elif(arg == '-cgtest'):
sysvals.cgtest = getArgInt('-cgtest', args, 0, 1)
elif(arg == '-cgphase'):
try:
val = args.next()
except:
doError('No phase name supplied', True)
d = Data(0)
if val not in d.phases:
doError('Invalid phase, valid phaess are %s' % d.phases, True)
sysvals.cgphase = val
elif(arg == '-callloop-maxgap'): elif(arg == '-callloop-maxgap'):
sysvals.callloopmaxgap = getArgFloat('-callloop-maxgap', args, 0.0, 1.0) sysvals.callloopmaxgap = getArgFloat('-callloop-maxgap', args, 0.0, 1.0)
elif(arg == '-callloop-maxlen'): elif(arg == '-callloop-maxlen'):
...@@ -5170,7 +5126,7 @@ if __name__ == '__main__': ...@@ -5170,7 +5126,7 @@ if __name__ == '__main__':
except: except:
doError('No text file supplied', True) doError('No text file supplied', True)
if(os.path.exists(val) == False): if(os.path.exists(val) == False):
doError('%s does not exist' % val, False) doError('%s does not exist' % val)
configFromFile(val) configFromFile(val)
elif(arg == '-fadd'): elif(arg == '-fadd'):
try: try:
...@@ -5178,7 +5134,7 @@ if __name__ == '__main__': ...@@ -5178,7 +5134,7 @@ if __name__ == '__main__':
except: except:
doError('No text file supplied', True) doError('No text file supplied', True)
if(os.path.exists(val) == False): if(os.path.exists(val) == False):
doError('%s does not exist' % val, False) doError('%s does not exist' % val)
sysvals.addFtraceFilterFunctions(val) sysvals.addFtraceFilterFunctions(val)
elif(arg == '-dmesg'): elif(arg == '-dmesg'):
try: try:
...@@ -5188,7 +5144,7 @@ if __name__ == '__main__': ...@@ -5188,7 +5144,7 @@ if __name__ == '__main__':
sysvals.notestrun = True sysvals.notestrun = True
sysvals.dmesgfile = val sysvals.dmesgfile = val
if(os.path.exists(sysvals.dmesgfile) == False): if(os.path.exists(sysvals.dmesgfile) == False):
doError('%s does not exist' % sysvals.dmesgfile, False) doError('%s does not exist' % sysvals.dmesgfile)
elif(arg == '-ftrace'): elif(arg == '-ftrace'):
try: try:
val = args.next() val = args.next()
...@@ -5197,7 +5153,7 @@ if __name__ == '__main__': ...@@ -5197,7 +5153,7 @@ if __name__ == '__main__':
sysvals.notestrun = True sysvals.notestrun = True
sysvals.ftracefile = val sysvals.ftracefile = val
if(os.path.exists(sysvals.ftracefile) == False): if(os.path.exists(sysvals.ftracefile) == False):
doError('%s does not exist' % sysvals.ftracefile, False) doError('%s does not exist' % sysvals.ftracefile)
elif(arg == '-summary'): elif(arg == '-summary'):
try: try:
val = args.next() val = args.next()
...@@ -5207,7 +5163,7 @@ if __name__ == '__main__': ...@@ -5207,7 +5163,7 @@ if __name__ == '__main__':
cmdarg = val cmdarg = val
sysvals.notestrun = True sysvals.notestrun = True
if(os.path.isdir(val) == False): if(os.path.isdir(val) == False):
doError('%s is not accesible' % val, False) doError('%s is not accesible' % val)
elif(arg == '-filter'): elif(arg == '-filter'):
try: try:
val = args.next() val = args.next()
...@@ -5218,12 +5174,10 @@ if __name__ == '__main__': ...@@ -5218,12 +5174,10 @@ if __name__ == '__main__':
doError('Invalid argument: '+arg, True) doError('Invalid argument: '+arg, True)
# compatibility errors # compatibility errors
if(sysvals.usecallgraph and sysvals.execcount > 1):
doError('-x2 is not compatible with -f', False)
if(sysvals.usecallgraph and sysvals.usedevsrc): if(sysvals.usecallgraph and sysvals.usedevsrc):
doError('-dev is not compatible with -f', False) doError('-dev is not compatible with -f')
if(sysvals.usecallgraph and sysvals.useprocmon): if(sysvals.usecallgraph and sysvals.useprocmon):
doError('-proc is not compatible with -f', False) doError('-proc is not compatible with -f')
# callgraph size cannot exceed device size # callgraph size cannot exceed device size
if sysvals.mincglen < sysvals.mindevlen: if sysvals.mincglen < sysvals.mindevlen:
...@@ -5238,8 +5192,7 @@ if __name__ == '__main__': ...@@ -5238,8 +5192,7 @@ if __name__ == '__main__':
elif(cmd == 'usbtopo'): elif(cmd == 'usbtopo'):
detectUSB() detectUSB()
elif(cmd == 'modes'): elif(cmd == 'modes'):
modes = getModes() print getModes()
print modes
elif(cmd == 'flist'): elif(cmd == 'flist'):
sysvals.getFtraceFilterFunctions(True) sysvals.getFtraceFilterFunctions(True)
elif(cmd == 'flistall'): elif(cmd == 'flistall'):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment