Commit 1ea39643 authored by Todd E Brandt's avatar Todd E Brandt Committed by Rafael J. Wysocki

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

- when running with sudo, change output dir back to SUDO_USER ownership
- replace all os.system/popen instances with subprocess.call/Popen
- graph pm device callbacks and async threads in separate sections
- remove kprobe config section and replaced it with timeline_functions
- added new kprobe config section for dev mode: dev_timeline_functions
- merge call loops in dev mode to create a single event with a count
- added hover text to all header entries to explain what they mean
- changed the -filter option to grep device driver/name for a string
- added new options for tuning the dev mode timeline/custom kprobes
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 203f1f98
...@@ -67,7 +67,7 @@ from datetime import datetime ...@@ -67,7 +67,7 @@ from datetime import datetime
import struct import struct
import ConfigParser import ConfigParser
from threading import Thread from threading import Thread
import subprocess from subprocess import call, Popen, PIPE
# ----------------- CLASSES -------------------- # ----------------- CLASSES --------------------
...@@ -77,11 +77,13 @@ import subprocess ...@@ -77,11 +77,13 @@ import subprocess
# store system values and test parameters # store system values and test parameters
class SystemValues: class SystemValues:
ansi = False ansi = False
version = '4.3' version = '4.4'
verbose = False verbose = False
addlogs = False addlogs = False
mindevlen = 0.001 mindevlen = 0.001
mincglen = 1.0 mincglen = 1.0
callloopmaxgap = 0.0001
callloopmaxlen = 0.005
srgap = 0 srgap = 0
cgexp = False cgexp = False
outdir = '' outdir = ''
...@@ -132,9 +134,6 @@ class SystemValues: ...@@ -132,9 +134,6 @@ class SystemValues:
stampfmt = '# suspend-(?P<m>[0-9]{2})(?P<d>[0-9]{2})(?P<y>[0-9]{2})-'+\ stampfmt = '# suspend-(?P<m>[0-9]{2})(?P<d>[0-9]{2})(?P<y>[0-9]{2})-'+\
'(?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>.*)$'
kprobecolor = 'rgba(204,204,204,0.5)'
synccolor = 'rgba(204,204,204,0.5)'
debugfuncs = []
tracefuncs = { tracefuncs = {
'sys_sync': dict(), 'sys_sync': dict(),
'pm_prepare_console': dict(), 'pm_prepare_console': dict(),
...@@ -213,6 +212,7 @@ class SystemValues: ...@@ -213,6 +212,7 @@ class SystemValues:
self.embedded = True self.embedded = True
self.addlogs = True self.addlogs = True
self.htmlfile = os.environ['LOG_FILE'] self.htmlfile = os.environ['LOG_FILE']
self.archargs = 'args_'+platform.machine()
self.hostname = platform.node() self.hostname = platform.node()
if(self.hostname == ''): if(self.hostname == ''):
self.hostname = 'localhost' self.hostname = 'localhost'
...@@ -282,7 +282,7 @@ class SystemValues: ...@@ -282,7 +282,7 @@ class SystemValues:
for i in value: for i in value:
self.devicefilter.append(i.strip()) self.devicefilter.append(i.strip())
def rtcWakeAlarmOn(self): def rtcWakeAlarmOn(self):
os.system('echo 0 > '+self.rtcpath+'/wakealarm') call('echo 0 > '+self.rtcpath+'/wakealarm', shell=True)
outD = open(self.rtcpath+'/date', 'r').read().strip() outD = open(self.rtcpath+'/date', 'r').read().strip()
outT = open(self.rtcpath+'/time', 'r').read().strip() outT = open(self.rtcpath+'/time', 'r').read().strip()
mD = re.match('^(?P<y>[0-9]*)-(?P<m>[0-9]*)-(?P<d>[0-9]*)', outD) mD = re.match('^(?P<y>[0-9]*)-(?P<m>[0-9]*)-(?P<d>[0-9]*)', outD)
...@@ -298,12 +298,12 @@ class SystemValues: ...@@ -298,12 +298,12 @@ class SystemValues:
# if hardware time fails, use the software time # if hardware time fails, use the software time
nowtime = int(datetime.now().strftime('%s')) nowtime = int(datetime.now().strftime('%s'))
alarm = nowtime + self.rtcwaketime alarm = nowtime + self.rtcwaketime
os.system('echo %d > %s/wakealarm' % (alarm, self.rtcpath)) call('echo %d > %s/wakealarm' % (alarm, self.rtcpath), shell=True)
def rtcWakeAlarmOff(self): def rtcWakeAlarmOff(self):
os.system('echo 0 > %s/wakealarm' % self.rtcpath) call('echo 0 > %s/wakealarm' % self.rtcpath, shell=True)
def initdmesg(self): def initdmesg(self):
# get the latest time stamp from the dmesg log # get the latest time stamp from the dmesg log
fp = os.popen('dmesg') fp = Popen('dmesg', stdout=PIPE).stdout
ktime = '0' ktime = '0'
for line in fp: for line in fp:
line = line.replace('\r\n', '') line = line.replace('\r\n', '')
...@@ -317,7 +317,7 @@ class SystemValues: ...@@ -317,7 +317,7 @@ class SystemValues:
self.dmesgstart = float(ktime) self.dmesgstart = float(ktime)
def getdmesg(self): def getdmesg(self):
# store all new dmesg lines since initdmesg was called # store all new dmesg lines since initdmesg was called
fp = os.popen('dmesg') fp = Popen('dmesg', stdout=PIPE).stdout
op = open(self.dmesgfile, 'a') op = open(self.dmesgfile, 'a')
for line in fp: for line in fp:
line = line.replace('\r\n', '') line = line.replace('\r\n', '')
...@@ -343,25 +343,18 @@ class SystemValues: ...@@ -343,25 +343,18 @@ class SystemValues:
def getFtraceFilterFunctions(self, current): def getFtraceFilterFunctions(self, current):
rootCheck(True) rootCheck(True)
if not current: if not current:
os.system('cat '+self.tpath+'available_filter_functions') call('cat '+self.tpath+'available_filter_functions', shell=True)
return return
fp = open(self.tpath+'available_filter_functions') fp = open(self.tpath+'available_filter_functions')
master = fp.read().split('\n') master = fp.read().split('\n')
fp.close() fp.close()
if len(self.debugfuncs) > 0: for i in self.tracefuncs:
for i in self.debugfuncs: if 'func' in self.tracefuncs[i]:
if i in master: i = self.tracefuncs[i]['func']
print i if i in master:
else: print i
print self.colorText(i) else:
else: print self.colorText(i)
for i in self.tracefuncs:
if 'func' in self.tracefuncs[i]:
i = self.tracefuncs[i]['func']
if i in master:
print i
else:
print self.colorText(i)
def setFtraceFilterFunctions(self, list): def setFtraceFilterFunctions(self, list):
fp = open(self.tpath+'available_filter_functions') fp = open(self.tpath+'available_filter_functions')
master = fp.read().split('\n') master = fp.read().split('\n')
...@@ -384,9 +377,8 @@ class SystemValues: ...@@ -384,9 +377,8 @@ class SystemValues:
for field in ['name', 'format', 'func']: for field in ['name', 'format', 'func']:
if field not in k: if field not in k:
k[field] = name k[field] = name
archargs = 'args_'+platform.machine() if self.archargs in k:
if archargs in k: k['args'] = k[self.archargs]
k['args'] = k[archargs]
else: else:
k['args'] = dict() k['args'] = dict()
k['format'] = name k['format'] = name
...@@ -423,8 +415,19 @@ class SystemValues: ...@@ -423,8 +415,19 @@ class SystemValues:
out = fmt.format(**arglist) out = fmt.format(**arglist)
out = out.replace(' ', '_').replace('"', '') out = out.replace(' ', '_').replace('"', '')
return out return out
def kprobeText(self, kprobe): def kprobeText(self, kname, kprobe):
name, fmt, func, args = kprobe['name'], kprobe['format'], kprobe['func'], kprobe['args'] name = fmt = func = kname
args = dict()
if 'name' in kprobe:
name = kprobe['name']
if 'format' in kprobe:
fmt = kprobe['format']
if 'func' in kprobe:
func = kprobe['func']
if self.archargs in kprobe:
args = kprobe[self.archargs]
if 'args' in kprobe:
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), False)
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):
...@@ -440,7 +443,7 @@ class SystemValues: ...@@ -440,7 +443,7 @@ class SystemValues:
print('INITIALIZING KPROBES...') print('INITIALIZING KPROBES...')
rejects = [] rejects = []
for name in sorted(self.kprobes): for name in sorted(self.kprobes):
if not self.testKprobe(self.kprobes[name]): if not self.testKprobe(name, self.kprobes[name]):
rejects.append(name) rejects.append(name)
# remove all failed ones from the list # remove all failed ones from the list
for name in rejects: for name in rejects:
...@@ -450,9 +453,9 @@ class SystemValues: ...@@ -450,9 +453,9 @@ class SystemValues:
kprobeevents = '' kprobeevents = ''
# set the kprobes all at once # set the kprobes all at once
for kp in self.kprobes: for kp in self.kprobes:
val = self.kprobeText(self.kprobes[kp]) val = self.kprobeText(kp, self.kprobes[kp])
vprint('Adding KPROBE: %s\n%s' % (kp, val.strip())) vprint('Adding KPROBE: %s\n%s' % (kp, val.strip()))
kprobeevents += self.kprobeText(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')
...@@ -461,11 +464,11 @@ class SystemValues: ...@@ -461,11 +464,11 @@ class SystemValues:
if linesack < linesout: if linesack < linesout:
# if not, try appending the kprobes 1 by 1 # if not, try appending the kprobes 1 by 1
for kp in self.kprobes: for kp in self.kprobes:
kprobeevents = self.kprobeText(self.kprobes[kp]) kprobeevents = self.kprobeText(kp, self.kprobes[kp])
self.fsetVal(kprobeevents, 'kprobe_events', 'a') self.fsetVal(kprobeevents, 'kprobe_events', 'a')
self.fsetVal('1', 'events/kprobes/enable') self.fsetVal('1', 'events/kprobes/enable')
def testKprobe(self, kprobe): def testKprobe(self, kname, kprobe):
kprobeevents = self.kprobeText(kprobe) kprobeevents = self.kprobeText(kname, kprobe)
if not kprobeevents: if not kprobeevents:
return False return False
try: try:
...@@ -511,18 +514,15 @@ class SystemValues: ...@@ -511,18 +514,15 @@ class SystemValues:
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])
def isCallgraphFunc(self, name): def isCallgraphFunc(self, name):
if len(self.debugfuncs) < 1 and self.suspendmode == 'command': if len(self.tracefuncs) < 1 and self.suspendmode == 'command':
return True
if name in self.debugfuncs:
return True return True
funclist = []
for i in self.tracefuncs: for i in self.tracefuncs:
if 'func' in self.tracefuncs[i]: if 'func' in self.tracefuncs[i]:
funclist.append(self.tracefuncs[i]['func']) f = self.tracefuncs[i]['func']
else: else:
funclist.append(i) f = i
if name in funclist: if name == f:
return True return True
return False return False
def initFtrace(self, testing=False): def initFtrace(self, testing=False):
tp = self.tpath tp = self.tpath
...@@ -538,18 +538,7 @@ class SystemValues: ...@@ -538,18 +538,7 @@ class SystemValues:
# go no further if this is just a status check # go no further if this is just a status check
if testing: if testing:
return return
if self.usekprobes: # initialize the callgraph trace
# add tracefunc kprobes so long as were not using full callgraph
if(not self.usecallgraph or len(self.debugfuncs) > 0):
for name in self.tracefuncs:
self.defaultKprobe(name, self.tracefuncs[name])
if self.usedevsrc:
for name in self.dev_tracefuncs:
self.defaultKprobe(name, self.dev_tracefuncs[name])
else:
self.usedevsrc = False
self.addKprobes()
# initialize the callgraph trace, unless this is an x2 run
if(self.usecallgraph): if(self.usecallgraph):
# set trace type # set trace type
self.fsetVal('function_graph', 'current_tracer') self.fsetVal('function_graph', 'current_tracer')
...@@ -565,20 +554,23 @@ class SystemValues: ...@@ -565,20 +554,23 @@ class SystemValues:
self.fsetVal('context-info', 'trace_options') self.fsetVal('context-info', 'trace_options')
self.fsetVal('graph-time', 'trace_options') self.fsetVal('graph-time', 'trace_options')
self.fsetVal('0', 'max_graph_depth') self.fsetVal('0', 'max_graph_depth')
if len(self.debugfuncs) > 0: cf = ['dpm_run_callback']
self.setFtraceFilterFunctions(self.debugfuncs) if(self.usetraceeventsonly):
elif self.suspendmode == 'command': cf += ['dpm_prepare', 'dpm_complete']
self.fsetVal('', 'set_graph_function') for fn in self.tracefuncs:
else: if 'func' in self.tracefuncs[fn]:
cf = ['dpm_run_callback'] cf.append(self.tracefuncs[fn]['func'])
if(self.usetraceeventsonly): else:
cf += ['dpm_prepare', 'dpm_complete'] cf.append(fn)
for fn in self.tracefuncs: self.setFtraceFilterFunctions(cf)
if 'func' in self.tracefuncs[fn]: # initialize the kprobe trace
cf.append(self.tracefuncs[fn]['func']) elif self.usekprobes:
else: for name in self.tracefuncs:
cf.append(fn) self.defaultKprobe(name, self.tracefuncs[name])
self.setFtraceFilterFunctions(cf) if self.usedevsrc:
for name in self.dev_tracefuncs:
self.defaultKprobe(name, self.dev_tracefuncs[name])
self.addKprobes()
if(self.usetraceevents): if(self.usetraceevents):
# turn trace events on # turn trace events on
events = iter(self.traceevents) events = iter(self.traceevents)
...@@ -791,25 +783,22 @@ class Data: ...@@ -791,25 +783,22 @@ class Data:
break break
return tgtdev return tgtdev
def addDeviceFunctionCall(self, displayname, kprobename, proc, pid, start, end, cdata, rdata): def addDeviceFunctionCall(self, displayname, kprobename, proc, pid, start, end, cdata, rdata):
tgtphase = self.sourcePhase(start, end)
# try to place the call in a device # try to place the call in a device
tgtdev = self.sourceDevice(self.phases, start, end, pid, 'device') tgtdev = self.sourceDevice(self.phases, start, end, pid, 'device')
# calls with device pids that occur outside dev bounds are dropped # calls with device pids that occur outside device bounds are dropped
# TODO: include these somehow
if not tgtdev and pid in self.devpids: if not tgtdev and pid in self.devpids:
return False return False
# try to place the call in a thread # try to place the call in a thread
if not tgtdev: if not tgtdev:
tgtdev = self.sourceDevice([tgtphase], start, end, pid, 'thread') tgtdev = self.sourceDevice(self.phases, start, end, pid, 'thread')
# create new thread blocks, expand as new calls are found # create new thread blocks, expand as new calls are found
if not tgtdev: if not tgtdev:
if proc == '<...>': if proc == '<...>':
threadname = 'kthread-%d' % (pid) threadname = 'kthread-%d' % (pid)
else: else:
threadname = '%s-%d' % (proc, pid) threadname = '%s-%d' % (proc, pid)
if(start < self.start): tgtphase = self.sourcePhase(start, end)
self.setStart(start)
if(end > self.end):
self.setEnd(end)
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
...@@ -836,7 +825,8 @@ class Data: ...@@ -836,7 +825,8 @@ class Data:
r = 'ret=%s ' % r r = 'ret=%s ' % r
if ubiquitous and c in self.dev_ubiquitous: if ubiquitous and c in self.dev_ubiquitous:
return False return False
e = DevFunction(displayname, a, c, r, start, end, ubiquitous, proc, pid) color = sysvals.kprobeColor(kprobename)
e = DevFunction(displayname, a, c, r, start, end, ubiquitous, proc, pid, color)
tgtdev['src'].append(e) tgtdev['src'].append(e)
return True return True
def overflowDevices(self): def overflowDevices(self):
...@@ -866,6 +856,30 @@ class Data: ...@@ -866,6 +856,30 @@ class Data:
continue continue
dev['src'] += tdev['src'] dev['src'] += tdev['src']
del list[devname] del list[devname]
def usurpTouchingThread(self, name, dev):
# the caller test has priority of this thread, give it to him
for phase in self.phases:
list = self.dmesg[phase]['list']
if name in list:
tdev = list[name]
if tdev['start'] - dev['end'] < 0.1:
dev['end'] = tdev['end']
if 'src' not in dev:
dev['src'] = []
if 'src' in tdev:
dev['src'] += tdev['src']
del list[name]
break
def stitchTouchingThreads(self, testlist):
# merge any threads between tests that touch
for phase in self.phases:
list = self.dmesg[phase]['list']
for devname in list:
dev = list[devname]
if 'htmlclass' not in dev or 'kth' not in dev['htmlclass']:
continue
for data in testlist:
data.usurpTouchingThread(devname, dev)
def optimizeDevSrc(self): def optimizeDevSrc(self):
# merge any src call loops to reduce timeline size # merge any src call loops to reduce timeline size
for phase in self.phases: for phase in self.phases:
...@@ -962,29 +976,18 @@ class Data: ...@@ -962,29 +976,18 @@ 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):
# get a list of all devices related to the filter devices # check each device name & driver name
filter = [] # remove it if it does not include one of the filter strings
for phase in self.phases:
list = self.dmesg[phase]['list']
for name in devicefilter:
dev = name
# loop up to the top level parent of this dev
while(dev in list):
if(dev not in filter):
filter.append(dev)
dev = list[dev]['par']
# now get the full tree of related devices
children = self.deviceDescendants(name, phase)
for dev in children:
if(dev not in filter):
filter.append(dev)
# remove all devices not in the filter list
for phase in self.phases: for phase in self.phases:
list = self.dmesg[phase]['list'] list = self.dmesg[phase]['list']
rmlist = [] rmlist = []
for name in list: for name in list:
pid = list[name]['pid'] keep = False
if(name not in filter): for filter in devicefilter:
if filter in name or \
('drv' in list[name] and filter in list[name]['drv']):
keep = True
if not keep:
rmlist.append(name) rmlist.append(name)
for name in rmlist: for name in rmlist:
del list[name] del list[name]
...@@ -992,10 +995,6 @@ class Data: ...@@ -992,10 +995,6 @@ class Data:
# 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, self.end)
def isInsideTimeline(self, start, end):
if(self.start <= start and self.end > start):
return True
return False
def phaseOverlap(self, phases): def phaseOverlap(self, phases):
rmgroups = [] rmgroups = []
newgroup = [] newgroup = []
...@@ -1012,12 +1011,6 @@ class Data: ...@@ -1012,12 +1011,6 @@ class Data:
self.devicegroups.remove(group) self.devicegroups.remove(group)
self.devicegroups.append(newgroup) self.devicegroups.append(newgroup)
def newActionGlobal(self, name, start, end, pid=-1, color=''): def newActionGlobal(self, name, start, end, pid=-1, color=''):
# if event starts before timeline start, expand timeline
if(start < self.start):
self.setStart(start)
# if event ends after timeline end, expand the timeline
if(end > self.end):
self.setEnd(end)
# which phase is this device callback or action in # which phase is this device callback or action in
targetphase = 'none' targetphase = 'none'
htmlclass = '' htmlclass = ''
...@@ -1036,13 +1029,12 @@ class Data: ...@@ -1036,13 +1029,12 @@ class Data:
continue continue
targetphase = phase targetphase = phase
overlap = o overlap = o
# if no target phase was found, check for pre/post resume # if no target phase was found, pin it to the edge
if targetphase == 'none': if targetphase == 'none':
o = max(0, min(end, self.tKernSus) - max(start, self.start)) p0start = self.dmesg[self.phases[0]]['start']
if o > 0: if start <= p0start:
targetphase = self.phases[0] targetphase = self.phases[0]
o = max(0, min(end, self.end) - max(start, self.tKernRes)) else:
if o > 0:
targetphase = self.phases[-1] targetphase = self.phases[-1]
if pid == -2: if pid == -2:
htmlclass = ' bg' htmlclass = ' bg'
...@@ -1195,6 +1187,15 @@ class Data: ...@@ -1195,6 +1187,15 @@ class Data:
if width != '0.000000' and length >= mindevlen: if width != '0.000000' and length >= mindevlen:
devlist.append(dev) devlist.append(dev)
self.tdevlist[phase] = devlist self.tdevlist[phase] = devlist
def addHorizontalDivider(self, devname, devend):
phase = 'suspend_prepare'
self.newAction(phase, devname, -2, '', \
self.start, devend, '', ' sec', '')
if phase not in self.tdevlist:
self.tdevlist[phase] = []
self.tdevlist[phase].append(devname)
d = DevItem(0, phase, self.dmesg[phase]['list'][devname])
return d
def addProcessUsageEvent(self, name, times): def addProcessUsageEvent(self, name, times):
# get the start and end times for this process # get the start and end times for this process
maxC = 0 maxC = 0
...@@ -1273,7 +1274,7 @@ class Data: ...@@ -1273,7 +1274,7 @@ class Data:
class DevFunction: class DevFunction:
row = 0 row = 0
count = 1 count = 1
def __init__(self, name, args, caller, ret, start, end, u, proc, pid): def __init__(self, name, args, caller, ret, start, end, u, proc, pid, color):
self.name = name self.name = name
self.args = args self.args = args
self.caller = caller self.caller = caller
...@@ -1284,17 +1285,18 @@ class DevFunction: ...@@ -1284,17 +1285,18 @@ class DevFunction:
self.ubiquitous = u self.ubiquitous = u
self.proc = proc self.proc = proc
self.pid = pid self.pid = pid
self.color = color
def title(self): def title(self):
cnt = '' cnt = ''
if self.count > 1: if self.count > 1:
cnt = '(x%d)' % self.count cnt = '(x%d)' % self.count
l = '%0.3fms' % (self.length * 1000) l = '%0.3fms' % (self.length * 1000)
if self.ubiquitous: if self.ubiquitous:
title = '%s(%s) <- %s, %s%s(%s)' % \ title = '%s(%s)%s <- %s, %s(%s)' % \
(self.name, self.args, self.caller, self.ret, cnt, l) (self.name, self.args, cnt, self.caller, self.ret, l)
else: else:
title = '%s(%s) %s%s(%s)' % (self.name, self.args, self.ret, cnt, l) title = '%s(%s) %s%s(%s)' % (self.name, self.args, self.ret, cnt, l)
return title return title.replace('"', '')
def text(self): def text(self):
if self.count > 1: if self.count > 1:
text = '%s(x%d)' % (self.name, self.count) text = '%s(x%d)' % (self.name, self.count)
...@@ -1302,12 +1304,15 @@ class DevFunction: ...@@ -1302,12 +1304,15 @@ class DevFunction:
text = self.name text = self.name
return text return text
def repeat(self, tgt): def repeat(self, tgt):
# is the tgt call just a repeat of this call (e.g. are we in a loop)
dt = self.time - tgt.end dt = self.time - tgt.end
# only combine calls if -all- attributes are identical
if tgt.caller == self.caller and \ if tgt.caller == self.caller and \
tgt.name == self.name and tgt.args == self.args and \ tgt.name == self.name and tgt.args == self.args and \
tgt.proc == self.proc and tgt.pid == self.pid and \ tgt.proc == self.proc and tgt.pid == self.pid and \
tgt.ret == self.ret and dt >= 0 and dt <= 0.000100 and \ tgt.ret == self.ret and dt >= 0 and \
self.length < 0.001: dt <= sysvals.callloopmaxgap and \
self.length < sysvals.callloopmaxlen:
return True return True
return False return False
...@@ -1698,6 +1703,10 @@ class DevItem: ...@@ -1698,6 +1703,10 @@ class DevItem:
self.test = test self.test = test
self.phase = phase self.phase = phase
self.dev = dev self.dev = dev
def isa(self, cls):
if 'htmlclass' in self.dev and cls in self.dev['htmlclass']:
return True
return False
# Class: Timeline # Class: Timeline
# Description: # Description:
...@@ -1767,11 +1776,10 @@ class Timeline: ...@@ -1767,11 +1776,10 @@ class Timeline:
# devlist: the list of devices/actions in a group of contiguous phases # devlist: the list of devices/actions in a group of contiguous phases
# Output: # Output:
# The total number of rows needed to display this phase of the timeline # The total number of rows needed to display this phase of the timeline
def getPhaseRows(self, devlist): def getPhaseRows(self, devlist, row=0):
# clear all rows and set them to undefined # clear all rows and set them to undefined
remaining = len(devlist) remaining = len(devlist)
rowdata = dict() rowdata = dict()
row = 0
sortdict = dict() sortdict = dict()
myphases = [] myphases = []
# initialize all device rows to -1 and calculate devrows # initialize all device rows to -1 and calculate devrows
...@@ -1825,8 +1833,14 @@ class Timeline: ...@@ -1825,8 +1833,14 @@ class Timeline:
if p not in self.rowlines[t] or p not in self.rowheight[t]: if p not in self.rowlines[t] or p not in self.rowheight[t]:
self.rowlines[t][p] = dict() self.rowlines[t][p] = dict()
self.rowheight[t][p] = dict() self.rowheight[t][p] = dict()
rh = self.rowH
# section headers should use a different row height
if len(rowdata[row]) == 1 and \
'htmlclass' in rowdata[row][0].dev and \
'sec' in rowdata[row][0].dev['htmlclass']:
rh = 15
self.rowlines[t][p][row] = rowheight self.rowlines[t][p][row] = rowheight
self.rowheight[t][p][row] = rowheight * self.rowH self.rowheight[t][p][row] = rowheight * rh
row += 1 row += 1
if(row > self.rows): if(row > self.rows):
self.rows = int(row) self.rows = int(row)
...@@ -1952,7 +1966,7 @@ class ProcessMonitor: ...@@ -1952,7 +1966,7 @@ class ProcessMonitor:
running = False running = False
def procstat(self): def procstat(self):
c = ['cat /proc/[1-9]*/stat 2>/dev/null'] c = ['cat /proc/[1-9]*/stat 2>/dev/null']
process = subprocess.Popen(c, shell=True, stdout=subprocess.PIPE) process = Popen(c, shell=True, stdout=PIPE)
running = dict() running = dict()
for line in process.stdout: for line in process.stdout:
data = line.split() data = line.split()
...@@ -2027,7 +2041,8 @@ def parseStamp(line, data): ...@@ -2027,7 +2041,8 @@ def parseStamp(line, data):
sysvals.suspendmode = data.stamp['mode'] sysvals.suspendmode = data.stamp['mode']
if sysvals.suspendmode == 'command' and sysvals.ftracefile != '': if sysvals.suspendmode == 'command' and sysvals.ftracefile != '':
modes = ['on', 'freeze', 'standby', 'mem'] modes = ['on', 'freeze', 'standby', 'mem']
out = os.popen('grep suspend_enter '+sysvals.ftracefile).read() out = Popen(['grep', 'suspend_enter', sysvals.ftracefile],
stderr=PIPE, stdout=PIPE).stdout.read()
m = re.match('.* suspend_enter\[(?P<mode>.*)\]', out) m = re.match('.* suspend_enter\[(?P<mode>.*)\]', out)
if m and m.group('mode') in ['1', '2', '3']: if m and m.group('mode') in ['1', '2', '3']:
sysvals.suspendmode = modes[int(m.group('mode'))] sysvals.suspendmode = modes[int(m.group('mode'))]
...@@ -2065,14 +2080,15 @@ def doesTraceLogHaveTraceEvents(): ...@@ -2065,14 +2080,15 @@ def doesTraceLogHaveTraceEvents():
# check for kprobes # check for kprobes
sysvals.usekprobes = False sysvals.usekprobes = False
out = os.system('grep -q "_cal: (" '+sysvals.ftracefile) out = call('grep -q "_cal: (" '+sysvals.ftracefile, shell=True)
if(out == 0): if(out == 0):
sysvals.usekprobes = True sysvals.usekprobes = True
# check for callgraph data on trace event blocks # check for callgraph data on trace event blocks
out = os.system('grep -q "_cpu_down()" '+sysvals.ftracefile) out = call('grep -q "_cpu_down()" '+sysvals.ftracefile, shell=True)
if(out == 0): if(out == 0):
sysvals.usekprobes = True sysvals.usekprobes = True
out = os.popen('head -1 '+sysvals.ftracefile).read().replace('\n', '') out = Popen(['head', '-1', sysvals.ftracefile],
stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '')
m = re.match(sysvals.stampfmt, out) m = re.match(sysvals.stampfmt, out)
if m and m.group('mode') == 'command': if m and m.group('mode') == 'command':
sysvals.usetraceeventsonly = True sysvals.usetraceeventsonly = True
...@@ -2082,14 +2098,14 @@ def doesTraceLogHaveTraceEvents(): ...@@ -2082,14 +2098,14 @@ def doesTraceLogHaveTraceEvents():
sysvals.usetraceeventsonly = True sysvals.usetraceeventsonly = True
sysvals.usetraceevents = False sysvals.usetraceevents = False
for e in sysvals.traceevents: for e in sysvals.traceevents:
out = os.system('grep -q "'+e+': " '+sysvals.ftracefile) out = call('grep -q "'+e+': " '+sysvals.ftracefile, shell=True)
if(out != 0): if(out != 0):
sysvals.usetraceeventsonly = False sysvals.usetraceeventsonly = False
if(e == 'suspend_resume' and out == 0): if(e == 'suspend_resume' and out == 0):
sysvals.usetraceevents = True sysvals.usetraceevents = True
# determine is this log is properly formatted # determine is this log is properly formatted
for e in ['SUSPEND START', 'RESUME COMPLETE']: for e in ['SUSPEND START', 'RESUME COMPLETE']:
out = os.system('grep -q "'+e+'" '+sysvals.ftracefile) out = call('grep -q "'+e+'" '+sysvals.ftracefile, shell=True)
if(out != 0): if(out != 0):
sysvals.usetracemarkers = False sysvals.usetracemarkers = False
...@@ -2617,14 +2633,14 @@ def parseTraceLog(): ...@@ -2617,14 +2633,14 @@ def parseTraceLog():
if sysvals.suspendmode == 'command': if sysvals.suspendmode == 'command':
for test in testruns: for test in testruns:
for p in test.data.phases: for p in test.data.phases:
if p == 'resume_complete': if p == 'suspend_prepare':
test.data.dmesg[p]['start'] = test.data.start test.data.dmesg[p]['start'] = test.data.start
test.data.dmesg[p]['end'] = test.data.end test.data.dmesg[p]['end'] = test.data.end
else: else:
test.data.dmesg[p]['start'] = test.data.start test.data.dmesg[p]['start'] = test.data.end
test.data.dmesg[p]['end'] = test.data.start test.data.dmesg[p]['end'] = test.data.end
test.data.tSuspended = test.data.start test.data.tSuspended = test.data.end
test.data.tResumed = test.data.start test.data.tResumed = test.data.end
test.data.tLow = 0 test.data.tLow = 0
test.data.fwValid = False test.data.fwValid = False
...@@ -2632,16 +2648,22 @@ def parseTraceLog(): ...@@ -2632,16 +2648,22 @@ def parseTraceLog():
if sysvals.usedevsrc or sysvals.useprocmon: if sysvals.usedevsrc or sysvals.useprocmon:
sysvals.mixedphaseheight = False sysvals.mixedphaseheight = False
for test in testruns: for i in range(len(testruns)):
test = testruns[i]
data = test.data
# find the total time range for this test (begin, end)
tlb, tle = data.start, data.end
if i < len(testruns) - 1:
tle = testruns[i+1].data.start
# add the process usage data to the timeline # add the process usage data to the timeline
if sysvals.useprocmon: if sysvals.useprocmon:
test.data.createProcessUsageEvents() data.createProcessUsageEvents()
# add the traceevent data to the device hierarchy # add the traceevent data to the device hierarchy
if(sysvals.usetraceevents): if(sysvals.usetraceevents):
# add actual trace funcs # add actual trace funcs
for name in test.ttemp: for name in test.ttemp:
for event in test.ttemp[name]: for event in test.ttemp[name]:
test.data.newActionGlobal(name, event['begin'], event['end'], event['pid']) data.newActionGlobal(name, event['begin'], event['end'], event['pid'])
# add the kprobe based virtual tracefuncs as actual devices # add the kprobe based virtual tracefuncs as actual devices
for key in tp.ktemp: for key in tp.ktemp:
name, pid = key name, pid = key
...@@ -2649,25 +2671,21 @@ def parseTraceLog(): ...@@ -2649,25 +2671,21 @@ def parseTraceLog():
continue continue
for e in tp.ktemp[key]: for e in tp.ktemp[key]:
kb, ke = e['begin'], e['end'] kb, ke = e['begin'], e['end']
if kb == ke or not test.data.isInsideTimeline(kb, ke): if kb == ke or tlb > kb or tle <= kb:
continue continue
test.data.newActionGlobal(e['name'], kb, ke, pid) color = sysvals.kprobeColor(name)
data.newActionGlobal(e['name'], kb, ke, pid, color)
# add config base kprobes and dev kprobes # add config base kprobes and dev kprobes
for key in tp.ktemp: if sysvals.usedevsrc:
name, pid = key for key in tp.ktemp:
if name in sysvals.tracefuncs: name, pid = key
continue if name in sysvals.tracefuncs or name not in sysvals.dev_tracefuncs:
for e in tp.ktemp[key]:
kb, ke = e['begin'], e['end']
if kb == ke or not test.data.isInsideTimeline(kb, ke):
continue continue
color = sysvals.kprobeColor(e['name']) for e in tp.ktemp[key]:
if name not in sysvals.dev_tracefuncs: kb, ke = e['begin'], e['end']
# custom user kprobe from the config if kb == ke or tlb > kb or tle <= kb:
test.data.newActionGlobal(e['name'], kb, ke, -2, color) continue
elif sysvals.usedevsrc: data.addDeviceFunctionCall(e['name'], name, e['proc'], pid, kb,
# dev kprobe
test.data.addDeviceFunctionCall(e['name'], name, e['proc'], pid, kb,
ke, e['cdata'], e['rdata']) ke, e['cdata'], e['rdata'])
if sysvals.usecallgraph: if sysvals.usecallgraph:
# add the callgraph data to the device hierarchy # add the callgraph data to the device hierarchy
...@@ -2683,7 +2701,7 @@ def parseTraceLog(): ...@@ -2683,7 +2701,7 @@ def parseTraceLog():
id+', ignoring this callback') id+', ignoring this callback')
continue continue
# match cg data to devices # match cg data to devices
if sysvals.suspendmode == 'command' or not cg.deviceMatch(pid, test.data): if sysvals.suspendmode == 'command' or not cg.deviceMatch(pid, data):
sortkey = '%f%f%d' % (cg.start, cg.end, pid) sortkey = '%f%f%d' % (cg.start, cg.end, pid)
sortlist[sortkey] = cg sortlist[sortkey] = cg
# create blocks for orphan cg data # create blocks for orphan cg data
...@@ -2692,7 +2710,7 @@ def parseTraceLog(): ...@@ -2692,7 +2710,7 @@ def parseTraceLog():
name = cg.list[0].name name = cg.list[0].name
if sysvals.isCallgraphFunc(name): if sysvals.isCallgraphFunc(name):
vprint('Callgraph found for task %d: %.3fms, %s' % (cg.pid, (cg.end - cg.start)*1000, name)) vprint('Callgraph found for task %d: %.3fms, %s' % (cg.pid, (cg.end - cg.start)*1000, name))
cg.newActionFromFunction(test.data) cg.newActionFromFunction(data)
if sysvals.suspendmode == 'command': if sysvals.suspendmode == 'command':
if(sysvals.verbose): if(sysvals.verbose):
...@@ -2724,14 +2742,14 @@ def parseTraceLog(): ...@@ -2724,14 +2742,14 @@ def parseTraceLog():
if sysvals.verbose: if sysvals.verbose:
data.printDetails() data.printDetails()
# 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:
tc = len(testdata) tc = len(testdata)
for i in range(tc - 1): for i in range(tc - 1):
devlist = testdata[i].overflowDevices() devlist = testdata[i].overflowDevices()
for j in range(i + 1, tc): for j in range(i + 1, tc):
testdata[j].mergeOverlapDevices(devlist) testdata[j].mergeOverlapDevices(devlist)
testdata[0].stitchTouchingThreads(testdata[1:])
return testdata return testdata
# Function: loadRawKernelLog # Function: loadRawKernelLog
...@@ -3300,29 +3318,29 @@ def createHTML(testruns): ...@@ -3300,29 +3318,29 @@ 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_traceevent = '<div title="{0}" class="traceevent{6}" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;">{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'
html_phaselet = '<div id="{0}" class="phaselet" style="left:{1}%;width:{2}%;background:{3}"></div>\n' html_phaselet = '<div id="{0}" class="phaselet" style="left:{1}%;width:{2}%;background:{3}"></div>\n'
html_legend = '<div id="p{3}" class="square" style="left:{0}%;background-color:{1}">&nbsp;{2}</div>\n' html_legend = '<div id="p{3}" class="square" style="left:{0}%;background-color:{1}">&nbsp;{2}</div>\n'
html_timetotal = '<table class="time1">\n<tr>'\ html_timetotal = '<table class="time1">\n<tr>'\
'<td class="green">{2} Suspend Time: <b>{0} ms</b></td>'\ '<td class="green" title="{3}">{2} Suspend Time: <b>{0} ms</b></td>'\
'<td class="yellow">{2} Resume Time: <b>{1} ms</b></td>'\ '<td class="yellow" title="{4}">{2} Resume Time: <b>{1} ms</b></td>'\
'</tr>\n</table>\n' '</tr>\n</table>\n'
html_timetotal2 = '<table class="time1">\n<tr>'\ html_timetotal2 = '<table class="time1">\n<tr>'\
'<td class="green">{3} Suspend Time: <b>{0} ms</b></td>'\ '<td class="green" title="{4}">{3} Suspend Time: <b>{0} ms</b></td>'\
'<td class="gray">'+sysvals.suspendmode+' time: <b>{1} ms</b></td>'\ '<td class="gray" title="time spent in low-power mode with clock running">'+sysvals.suspendmode+' time: <b>{1} ms</b></td>'\
'<td class="yellow">{3} Resume Time: <b>{2} ms</b></td>'\ '<td class="yellow" title="{5}">{3} Resume Time: <b>{2} ms</b></td>'\
'</tr>\n</table>\n' '</tr>\n</table>\n'
html_timetotal3 = '<table class="time1">\n<tr>'\ html_timetotal3 = '<table class="time1">\n<tr>'\
'<td class="green">Execution Time: <b>{0} ms</b></td>'\ '<td class="green">Execution Time: <b>{0} ms</b></td>'\
'<td class="yellow">Command: <b>{1}</b></td>'\ '<td class="yellow">Command: <b>{1}</b></td>'\
'</tr>\n</table>\n' '</tr>\n</table>\n'
html_timegroups = '<table class="time2">\n<tr>'\ html_timegroups = '<table class="time2">\n<tr>'\
'<td class="green">{4}Kernel Suspend: {0} ms</td>'\ '<td class="green" title="time from kernel enter_state({5}) to firmware mode [kernel time only]">{4}Kernel Suspend: {0} ms</td>'\
'<td class="purple">{4}Firmware Suspend: {1} ms</td>'\ '<td class="purple">{4}Firmware Suspend: {1} ms</td>'\
'<td class="purple">{4}Firmware Resume: {2} ms</td>'\ '<td class="purple">{4}Firmware Resume: {2} ms</td>'\
'<td class="yellow">{4}Kernel Resume: {3} ms</td>'\ '<td class="yellow" title="time from firmware mode to return from kernel enter_state({5}) [kernel time only]">{4}Kernel Resume: {3} ms</td>'\
'</tr>\n</table>\n' '</tr>\n</table>\n'
# html format variables # html format variables
...@@ -3338,6 +3356,10 @@ def createHTML(testruns): ...@@ -3338,6 +3356,10 @@ def createHTML(testruns):
# Generate the header for this timeline # Generate the header for this timeline
for data in testruns: for data in testruns:
tTotal = data.end - data.start tTotal = data.end - data.start
sktime = (data.dmesg['suspend_machine']['end'] - \
data.tKernSus) * 1000
rktime = (data.dmesg['resume_complete']['end'] - \
data.dmesg['resume_machine']['start']) * 1000
if(tTotal == 0): if(tTotal == 0):
print('ERROR: No timeline data') print('ERROR: No timeline data')
sys.exit() sys.exit()
...@@ -3354,42 +3376,40 @@ def createHTML(testruns): ...@@ -3354,42 +3376,40 @@ def createHTML(testruns):
thtml = html_timetotal3.format(run_time, testdesc) thtml = html_timetotal3.format(run_time, testdesc)
devtl.html['header'] += thtml devtl.html['header'] += thtml
elif data.fwValid: elif data.fwValid:
suspend_time = '%.0f'%((data.tSuspended-data.start)*1000 + \ suspend_time = '%.0f'%(sktime + (data.fwSuspend/1000000.0))
(data.fwSuspend/1000000.0)) resume_time = '%.0f'%(rktime + (data.fwResume/1000000.0))
resume_time = '%.0f'%((data.end-data.tSuspended)*1000 + \
(data.fwResume/1000000.0))
testdesc1 = 'Total' testdesc1 = 'Total'
testdesc2 = '' testdesc2 = ''
stitle = 'time from kernel enter_state(%s) to low-power mode [kernel & firmware time]' % sysvals.suspendmode
rtitle = 'time from low-power mode to return from kernel enter_state(%s) [firmware & kernel time]' % sysvals.suspendmode
if(len(testruns) > 1): if(len(testruns) > 1):
testdesc1 = testdesc2 = ordinal(data.testnumber+1) testdesc1 = testdesc2 = ordinal(data.testnumber+1)
testdesc2 += ' ' testdesc2 += ' '
if(data.tLow == 0): if(data.tLow == 0):
thtml = html_timetotal.format(suspend_time, \ thtml = html_timetotal.format(suspend_time, \
resume_time, testdesc1) resume_time, testdesc1, stitle, rtitle)
else: else:
thtml = html_timetotal2.format(suspend_time, low_time, \ thtml = html_timetotal2.format(suspend_time, low_time, \
resume_time, testdesc1) resume_time, testdesc1, stitle, rtitle)
devtl.html['header'] += thtml devtl.html['header'] += thtml
sktime = '%.3f'%((data.dmesg['suspend_machine']['end'] - \
data.tKernSus)*1000)
sftime = '%.3f'%(data.fwSuspend / 1000000.0) sftime = '%.3f'%(data.fwSuspend / 1000000.0)
rftime = '%.3f'%(data.fwResume / 1000000.0) rftime = '%.3f'%(data.fwResume / 1000000.0)
rktime = '%.3f'%((data.dmesg['resume_complete']['end'] - \ devtl.html['header'] += html_timegroups.format('%.3f'%sktime, \
data.dmesg['resume_machine']['start'])*1000) sftime, rftime, '%.3f'%rktime, testdesc2, sysvals.suspendmode)
devtl.html['header'] += html_timegroups.format(sktime, \
sftime, rftime, rktime, testdesc2)
else: else:
suspend_time = '%.0f'%((data.tSuspended-data.start)*1000) suspend_time = '%.3f' % sktime
resume_time = '%.0f'%((data.end-data.tSuspended)*1000) resume_time = '%.3f' % rktime
testdesc = 'Kernel' testdesc = 'Kernel'
stitle = 'time from kernel enter_state(%s) to firmware mode [kernel time only]' % sysvals.suspendmode
rtitle = 'time from firmware mode to return from kernel enter_state(%s) [kernel time only]' % sysvals.suspendmode
if(len(testruns) > 1): if(len(testruns) > 1):
testdesc = ordinal(data.testnumber+1)+' '+testdesc testdesc = ordinal(data.testnumber+1)+' '+testdesc
if(data.tLow == 0): if(data.tLow == 0):
thtml = html_timetotal.format(suspend_time, \ thtml = html_timetotal.format(suspend_time, \
resume_time, testdesc) resume_time, testdesc, stitle, rtitle)
else: else:
thtml = html_timetotal2.format(suspend_time, low_time, \ thtml = html_timetotal2.format(suspend_time, low_time, \
resume_time, testdesc) resume_time, testdesc, stitle, rtitle)
devtl.html['header'] += thtml devtl.html['header'] += thtml
# time scale for potentially multiple datasets # time scale for potentially multiple datasets
...@@ -3400,6 +3420,9 @@ def createHTML(testruns): ...@@ -3400,6 +3420,9 @@ def createHTML(testruns):
# determine the maximum number of rows we need to draw # determine the maximum number of rows we need to draw
fulllist = [] fulllist = []
threadlist = []
pscnt = 0
devcnt = 0
for data in testruns: for data in testruns:
data.selectTimelineDevices('%f', tTotal, sysvals.mindevlen) data.selectTimelineDevices('%f', tTotal, sysvals.mindevlen)
for group in data.devicegroups: for group in data.devicegroups:
...@@ -3408,11 +3431,31 @@ def createHTML(testruns): ...@@ -3408,11 +3431,31 @@ def createHTML(testruns):
for devname in data.tdevlist[phase]: for devname in data.tdevlist[phase]:
d = DevItem(data.testnumber, phase, data.dmesg[phase]['list'][devname]) d = DevItem(data.testnumber, phase, data.dmesg[phase]['list'][devname])
devlist.append(d) devlist.append(d)
fulllist.append(d) if d.isa('kth'):
threadlist.append(d)
else:
if d.isa('ps'):
pscnt += 1
else:
devcnt += 1
fulllist.append(d)
if sysvals.mixedphaseheight: if sysvals.mixedphaseheight:
devtl.getPhaseRows(devlist) devtl.getPhaseRows(devlist)
if not sysvals.mixedphaseheight: if not sysvals.mixedphaseheight:
if len(threadlist) > 0 and len(fulllist) > 0:
if pscnt > 0 and devcnt > 0:
msg = 'user processes & device pm callbacks'
elif pscnt > 0:
msg = 'user processes'
else:
msg = 'device pm callbacks'
d = testruns[0].addHorizontalDivider(msg, testruns[-1].end)
fulllist.insert(0, d)
devtl.getPhaseRows(fulllist) devtl.getPhaseRows(fulllist)
if len(threadlist) > 0:
d = testruns[0].addHorizontalDivider('asynchronous kernel threads', testruns[-1].end)
threadlist.insert(0, d)
devtl.getPhaseRows(threadlist, devtl.rows)
devtl.calcTotalRows() devtl.calcTotalRows()
# create bounding box, add buttons # create bounding box, add buttons
...@@ -3494,7 +3537,7 @@ def createHTML(testruns): ...@@ -3494,7 +3537,7 @@ def createHTML(testruns):
length = ' (%0.3f ms) ' % ((dev['end']-dev['start'])*1000) length = ' (%0.3f ms) ' % ((dev['end']-dev['start'])*1000)
title = name+drv+xtrainfo+length title = name+drv+xtrainfo+length
if sysvals.suspendmode == 'command': if sysvals.suspendmode == 'command':
title += 'cmdexec' title += sysvals.testcommand
elif xtraclass == ' ps': elif xtraclass == ' ps':
if 'suspend' in b: if 'suspend' in b:
title += 'pre_suspend_process' title += 'pre_suspend_process'
...@@ -3526,12 +3569,12 @@ def createHTML(testruns): ...@@ -3526,12 +3569,12 @@ def createHTML(testruns):
top = '%.3f' % (rowtop + devtl.scaleH + (e.row*devtl.rowH)) top = '%.3f' % (rowtop + devtl.scaleH + (e.row*devtl.rowH))
left = '%f' % (((e.time-m0)*100)/mTotal) left = '%f' % (((e.time-m0)*100)/mTotal)
width = '%f' % (e.length*100/mTotal) width = '%f' % (e.length*100/mTotal)
sleepclass = '' xtrastyle = ''
if e.ubiquitous: if e.color:
sleepclass = ' sleep' xtrastyle = 'background:%s;' % e.color
devtl.html['timeline'] += \ devtl.html['timeline'] += \
html_traceevent.format(e.title(), \ html_traceevent.format(e.title(), \
left, top, height, width, e.text(), sleepclass) left, top, height, width, e.text(), '', xtrastyle)
# draw the time scale, try to make the number of labels readable # draw the time scale, try to make the number of labels readable
devtl.html['timeline'] += devtl.createTimeScale(m0, mMax, tTotal, dir) devtl.html['timeline'] += devtl.createTimeScale(m0, mMax, tTotal, dir)
devtl.html['timeline'] += '</div>\n' devtl.html['timeline'] += '</div>\n'
...@@ -3601,17 +3644,15 @@ def createHTML(testruns): ...@@ -3601,17 +3644,15 @@ def createHTML(testruns):
.zoombox {position:relative;width:100%;overflow-x:scroll;-webkit-user-select:none;-moz-user-select:none;user-select:none;}\n\ .zoombox {position:relative;width:100%;overflow-x:scroll;-webkit-user-select:none;-moz-user-select:none;user-select:none;}\n\
.timeline {position:relative;font-size:14px;cursor:pointer;width:100%; overflow:hidden;background:linear-gradient(#cccccc, white);}\n\ .timeline {position:relative;font-size:14px;cursor:pointer;width:100%; overflow:hidden;background:linear-gradient(#cccccc, white);}\n\
.thread {position:absolute;height:0%;overflow:hidden;z-index:7;line-height:30px;font-size:14px;border:1px solid;text-align:center;white-space:nowrap;}\n\ .thread {position:absolute;height:0%;overflow:hidden;z-index:7;line-height:30px;font-size:14px;border:1px solid;text-align:center;white-space:nowrap;}\n\
.thread.sync {background-color:'+sysvals.synccolor+';}\n\
.thread.bg {background-color:'+sysvals.kprobecolor+';}\n\
.thread.ps {border-radius:3px;background:linear-gradient(to top, #ccc, #eee);}\n\ .thread.ps {border-radius:3px;background:linear-gradient(to top, #ccc, #eee);}\n\
.thread:hover {background-color:white;border:1px solid red;'+hoverZ+'}\n\ .thread:hover {background-color:white;border:1px solid red;'+hoverZ+'}\n\
.thread.sec,.thread.sec:hover {background-color:black;border:0;color:white;line-height:15px;font-size:10px;}\n\
.hover {background-color:white;border:1px solid red;'+hoverZ+'}\n\ .hover {background-color:white;border:1px solid red;'+hoverZ+'}\n\
.hover.sync {background-color:white;}\n\ .hover.sync {background-color:white;}\n\
.hover.bg,.hover.kth,.hover.sync,.hover.ps {background-color:white;}\n\ .hover.bg,.hover.kth,.hover.sync,.hover.ps {background-color:white;}\n\
.jiffie {position:absolute;pointer-events: none;z-index:8;}\n\ .jiffie {position:absolute;pointer-events: none;z-index:8;}\n\
.traceevent {position:absolute;font-size:10px;z-index:7;overflow:hidden;color:black;text-align:center;white-space:nowrap;border-radius:5px;border:1px solid black;background:linear-gradient(to bottom right,rgb(204,204,204),rgb(150,150,150));}\n\ .traceevent {position:absolute;font-size:10px;z-index:7;overflow:hidden;color:black;text-align:center;white-space:nowrap;border-radius:5px;border:1px solid black;background:linear-gradient(to bottom right,#CCC,#969696);}\n\
.traceevent.sleep {font-style:normal;}\n\ .traceevent:hover {color:white;font-weight:bold;border:1px solid white;}\n\
.traceevent:hover {background: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;pointer-events:none;top:0%;height:100%;border-right:1px solid black;z-index:6;}\n\
...@@ -4108,7 +4149,7 @@ def executeSuspend(): ...@@ -4108,7 +4149,7 @@ def executeSuspend():
time.sleep(sysvals.predelay/1000.0) time.sleep(sysvals.predelay/1000.0)
# initiate suspend or command # initiate suspend or command
if sysvals.testcommand != '': if sysvals.testcommand != '':
os.system(sysvals.testcommand+' 2>&1'); call(sysvals.testcommand+' 2>&1', shell=True);
else: else:
pf = open(sysvals.powerfile, 'w') pf = open(sysvals.powerfile, 'w')
pf.write(sysvals.suspendmode) pf.write(sysvals.suspendmode)
...@@ -4135,7 +4176,7 @@ def executeSuspend(): ...@@ -4135,7 +4176,7 @@ def executeSuspend():
sysvals.fsetVal('0', 'tracing_on') sysvals.fsetVal('0', 'tracing_on')
print('CAPTURING TRACE') print('CAPTURING TRACE')
writeDatafileHeader(sysvals.ftracefile, fwdata) writeDatafileHeader(sysvals.ftracefile, fwdata)
os.system('cat '+tp+'trace >> '+sysvals.ftracefile) call('cat '+tp+'trace >> '+sysvals.ftracefile, shell=True)
sysvals.fsetVal('', 'trace') sysvals.fsetVal('', 'trace')
devProps() devProps()
# grab a copy of the dmesg output # grab a copy of the dmesg output
...@@ -4167,12 +4208,12 @@ def setUSBDevicesAuto(): ...@@ -4167,12 +4208,12 @@ def setUSBDevicesAuto():
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
'idVendor' in filenames and 'idProduct' in filenames): 'idVendor' in filenames and 'idProduct' in filenames):
os.system('echo auto > %s/power/control' % dirname) call('echo auto > %s/power/control' % dirname, shell=True)
name = dirname.split('/')[-1] name = dirname.split('/')[-1]
desc = os.popen('cat %s/product 2>/dev/null' % \ desc = Popen(['cat', '%s/product' % dirname],
dirname).read().replace('\n', '') stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '')
ctrl = os.popen('cat %s/power/control 2>/dev/null' % \ ctrl = Popen(['cat', '%s/power/control' % dirname],
dirname).read().replace('\n', '') stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '')
print('control is %s for %6s: %s' % (ctrl, name, desc)) print('control is %s for %6s: %s' % (ctrl, name, desc))
# Function: yesno # Function: yesno
...@@ -4236,12 +4277,12 @@ def detectUSB(): ...@@ -4236,12 +4277,12 @@ def detectUSB():
if(re.match('.*/usb[0-9]*.*', dirname) and if(re.match('.*/usb[0-9]*.*', dirname) and
'idVendor' in filenames and 'idProduct' in filenames): 'idVendor' in filenames and 'idProduct' in filenames):
for i in field: for i in field:
field[i] = os.popen('cat %s/%s 2>/dev/null' % \ field[i] = Popen(['cat', '%s/%s' % (dirname, i)],
(dirname, i)).read().replace('\n', '') stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '')
name = dirname.split('/')[-1] name = dirname.split('/')[-1]
for i in power: for i in power:
power[i] = os.popen('cat %s/power/%s 2>/dev/null' % \ power[i] = Popen(['cat', '%s/power/%s' % (dirname, i)],
(dirname, i)).read().replace('\n', '') stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '')
if(re.match('usb[0-9]*', name)): if(re.match('usb[0-9]*', name)):
first = '%-8s' % name first = '%-8s' % name
else: else:
...@@ -4619,35 +4660,19 @@ def statusCheck(probecheck=False): ...@@ -4619,35 +4660,19 @@ def statusCheck(probecheck=False):
if not probecheck: if not probecheck:
return status return status
if (sysvals.usecallgraph and len(sysvals.debugfuncs) > 0) or len(sysvals.kprobes) > 0: # verify kprobes
sysvals.initFtrace(True) if sysvals.usekprobes and len(sysvals.tracefuncs) > 0:
print(' verifying timeline kprobes work:')
# verify callgraph debugfuncs for name in sorted(sysvals.tracefuncs):
if sysvals.usecallgraph and len(sysvals.debugfuncs) > 0:
print(' verifying these ftrace callgraph functions work:')
sysvals.setFtraceFilterFunctions(sysvals.debugfuncs)
fp = open(sysvals.tpath+'set_graph_function', 'r')
flist = fp.read().split('\n')
fp.close()
for func in sysvals.debugfuncs:
res = sysvals.colorText('NO') res = sysvals.colorText('NO')
if func in flist: if sysvals.testKprobe(name, sysvals.tracefuncs[name]):
res = 'YES' res = 'YES'
else: print(' %s: %s' % (name, res))
for i in flist: if sysvals.usedevsrc and sysvals.usekprobes and len(sysvals.dev_tracefuncs) > 0:
if ' [' in i and func == i.split(' ')[0]: print(' verifying dev kprobes work:')
res = 'YES' for name in sorted(sysvals.dev_tracefuncs):
break
print(' %s: %s' % (func, res))
# verify kprobes
if len(sysvals.kprobes) > 0:
print(' verifying these kprobes work:')
for name in sorted(sysvals.kprobes):
if name in sysvals.tracefuncs:
continue
res = sysvals.colorText('NO') res = sysvals.colorText('NO')
if sysvals.testKprobe(sysvals.kprobes[name]): if sysvals.testKprobe(name, sysvals.dev_tracefuncs[name]):
res = 'YES' res = 'YES'
print(' %s: %s' % (name, res)) print(' %s: %s' % (name, res))
...@@ -4786,6 +4811,11 @@ def runTest(subdir, testpath=''): ...@@ -4786,6 +4811,11 @@ def runTest(subdir, testpath=''):
if(sysvals.usecallgraph or sysvals.usetraceevents): if(sysvals.usecallgraph or sysvals.usetraceevents):
appendIncompleteTraceLog(testruns) appendIncompleteTraceLog(testruns)
createHTML(testruns) createHTML(testruns)
# if running as root, change output dir owner to sudo_user
if os.path.isdir(sysvals.testdir) and os.getuid() == 0 and \
'SUDO_USER' in os.environ:
cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1'
call(cmd.format(os.environ['SUDO_USER'], sysvals.testdir), shell=True)
# Function: runSummary # Function: runSummary
# Description: # Description:
...@@ -4851,9 +4881,10 @@ def configFromFile(file): ...@@ -4851,9 +4881,10 @@ def configFromFile(file):
global sysvals global sysvals
Config = ConfigParser.ConfigParser() Config = ConfigParser.ConfigParser()
ignorekprobes = False
Config.read(file) Config.read(file)
sections = Config.sections() sections = Config.sections()
overridekprobes = False
overridedevkprobes = False
if 'Settings' in sections: if 'Settings' in sections:
for opt in Config.options('Settings'): for opt in Config.options('Settings'):
value = Config.get('Settings', opt).lower() value = Config.get('Settings', opt).lower()
...@@ -4865,19 +4896,15 @@ def configFromFile(file): ...@@ -4865,19 +4896,15 @@ def configFromFile(file):
sysvals.usedevsrc = checkArgBool(value) sysvals.usedevsrc = checkArgBool(value)
elif(opt.lower() == 'proc'): elif(opt.lower() == 'proc'):
sysvals.useprocmon = checkArgBool(value) sysvals.useprocmon = checkArgBool(value)
elif(opt.lower() == 'ignorekprobes'):
ignorekprobes = checkArgBool(value)
elif(opt.lower() == 'x2'): elif(opt.lower() == 'x2'):
if checkArgBool(value): if checkArgBool(value):
sysvals.execcount = 2 sysvals.execcount = 2
elif(opt.lower() == 'callgraph'): elif(opt.lower() == 'callgraph'):
sysvals.usecallgraph = checkArgBool(value) sysvals.usecallgraph = checkArgBool(value)
elif(opt.lower() == 'callgraphfunc'): elif(opt.lower() == 'override-timeline-functions'):
sysvals.debugfuncs = [] overridekprobes = checkArgBool(value)
if value: elif(opt.lower() == 'override-dev-timeline-functions'):
value = value.split(',') overridedevkprobes = checkArgBool(value)
for i in value:
sysvals.debugfuncs.append(i.strip())
elif(opt.lower() == 'devicefilter'): elif(opt.lower() == 'devicefilter'):
sysvals.setDeviceFilter(value) sysvals.setDeviceFilter(value)
elif(opt.lower() == 'expandcg'): elif(opt.lower() == 'expandcg'):
...@@ -4902,20 +4929,12 @@ def configFromFile(file): ...@@ -4902,20 +4929,12 @@ def configFromFile(file):
sysvals.setPrecision(getArgInt('-timeprec', value, 0, 6, False)) sysvals.setPrecision(getArgInt('-timeprec', value, 0, 6, False))
elif(opt.lower() == 'mindev'): elif(opt.lower() == 'mindev'):
sysvals.mindevlen = getArgFloat('-mindev', value, 0.0, 10000.0, False) sysvals.mindevlen = getArgFloat('-mindev', value, 0.0, 10000.0, False)
elif(opt.lower() == 'callloop-maxgap'):
sysvals.callloopmaxgap = getArgFloat('-callloop-maxgap', value, 0.0, 1.0, False)
elif(opt.lower() == 'callloop-maxlen'):
sysvals.callloopmaxgap = getArgFloat('-callloop-maxlen', value, 0.0, 1.0, False)
elif(opt.lower() == 'mincg'): elif(opt.lower() == 'mincg'):
sysvals.mincglen = getArgFloat('-mincg', value, 0.0, 10000.0, False) sysvals.mincglen = getArgFloat('-mincg', value, 0.0, 10000.0, False)
elif(opt.lower() == 'kprobecolor'):
try:
val = int(value, 16)
sysvals.kprobecolor = '#'+value
except:
sysvals.kprobecolor = value
elif(opt.lower() == 'synccolor'):
try:
val = int(value, 16)
sysvals.synccolor = '#'+value
except:
sysvals.synccolor = value
elif(opt.lower() == 'output-dir'): elif(opt.lower() == 'output-dir'):
sysvals.setOutputFolder(value) sysvals.setOutputFolder(value)
...@@ -4930,24 +4949,32 @@ def configFromFile(file): ...@@ -4930,24 +4949,32 @@ def configFromFile(file):
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', False)
if ignorekprobes: if overridekprobes:
return sysvals.tracefuncs = dict()
if overridedevkprobes:
sysvals.dev_tracefuncs = dict()
kprobes = dict() kprobes = dict()
archkprobe = 'Kprobe_'+platform.machine() kprobesec = 'dev_timeline_functions_'+platform.machine()
if archkprobe in sections: if kprobesec in sections:
for name in Config.options(archkprobe): for name in Config.options(kprobesec):
kprobes[name] = Config.get(archkprobe, name) text = Config.get(kprobesec, name)
if 'Kprobe' in sections: kprobes[name] = (text, True)
for name in Config.options('Kprobe'): kprobesec = 'timeline_functions_'+platform.machine()
kprobes[name] = Config.get('Kprobe', name) if kprobesec in sections:
for name in Config.options(kprobesec):
if name in kprobes:
doError('Duplicate timeline function found "%s"' % (name), False)
text = Config.get(kprobesec, name)
kprobes[name] = (text, False)
for name in kprobes: for name in kprobes:
function = name function = name
format = name format = name
color = '' color = ''
args = dict() args = dict()
data = kprobes[name].split() text, dev = kprobes[name]
data = text.split()
i = 0 i = 0
for val in data: for val in data:
# bracketted strings are special formatting, read them separately # bracketted strings are special formatting, read them separately
...@@ -4974,17 +5001,21 @@ def configFromFile(file): ...@@ -4974,17 +5001,21 @@ def configFromFile(file):
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), False)
if name in sysvals.kprobes: if (dev and name in sysvals.dev_tracefuncs) or (not dev and name in sysvals.tracefuncs):
doError('Duplicate kprobe found "%s"' % (name), False) doError('Duplicate timeline function found "%s"' % (name), False)
vprint('Adding KPROBE: %s %s %s %s' % (name, function, format, args))
sysvals.kprobes[name] = { kp = {
'name': name, 'name': name,
'func': function, 'func': function,
'format': format, 'format': format,
'args': args sysvals.archargs: args
} }
if color: if color:
sysvals.kprobes[name]['color'] = color kp['color'] = color
if dev:
sysvals.dev_tracefuncs[name] = kp
else:
sysvals.tracefuncs[name] = kp
# Function: printHelp # Function: printHelp
# Description: # Description:
...@@ -5108,6 +5139,10 @@ if __name__ == '__main__': ...@@ -5108,6 +5139,10 @@ 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 == '-callloop-maxgap'):
sysvals.callloopmaxgap = getArgFloat('-callloop-maxgap', args, 0.0, 1.0)
elif(arg == '-callloop-maxlen'):
sysvals.callloopmaxlen = getArgFloat('-callloop-maxlen', args, 0.0, 1.0)
elif(arg == '-cmd'): elif(arg == '-cmd'):
try: try:
val = args.next() val = args.next()
......
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