1
0
Fork 0

pm-graph: AnalyzeBoot v2.2

- add -cgskip option to reduce callgraph output size
- add -cgfilter option to focus on a list of devices
- add -result option for exporting batch test results
- removed all phoronix hooks, use -result to enable batch testing
- changed argument -f to match sleegraph, -f = -callgraph
- use -fstat for function status instead of -f
- add -verbose option to print out timeline stats and kernel options
- include command string and kernel params in timeline output header

Signed-off-by: Todd Brandt <todd.e.brandt@linux.intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
hifive-unleashed-5.1
Todd E Brandt 2018-01-30 00:17:19 -08:00 committed by Rafael J. Wysocki
parent a6fbdbb2b8
commit d83a76a8ec
2 changed files with 160 additions and 77 deletions

View File

@ -37,6 +37,9 @@ Print the current tool version
Add the dmesg log to the html output. It will be viewable by Add the dmesg log to the html output. It will be viewable by
clicking a button in the timeline. clicking a button in the timeline.
.TP .TP
\fB-result \fIfile\fR
Export a results table to a text file for parsing.
.TP
\fB-o \fIname\fR \fB-o \fIname\fR
Overrides the output subdirectory name when running a new test. Overrides the output subdirectory name when running a new test.
Use {date}, {time}, {hostname} for current values. Use {date}, {time}, {hostname} for current values.
@ -44,14 +47,14 @@ Use {date}, {time}, {hostname} for current values.
e.g. boot-{hostname}-{date}-{time} e.g. boot-{hostname}-{date}-{time}
.SS "advanced" .SS "advanced"
.TP .TP
\fB-f\fR \fB-f or -callgraph\fR
Use ftrace to add function detail (default: disabled)
.TP
\fB-callgraph\fR
Use ftrace to create initcall callgraphs (default: disabled). If -func Use ftrace to create initcall callgraphs (default: disabled). If -func
is not used there will be one callgraph per initcall. This can produce is not used there will be one callgraph per initcall. This can produce
very large outputs, i.e. 10MB - 100MB. very large outputs, i.e. 10MB - 100MB.
.TP .TP
\fB-fstat\fR
Use ftrace to add function detail (default: disabled)
.TP
\fB-maxdepth \fIlevel\fR \fB-maxdepth \fIlevel\fR
limit the callgraph trace depth to \fIlevel\fR (default: 2). This is limit the callgraph trace depth to \fIlevel\fR (default: 2). This is
the best way to limit the output size when using -callgraph. the best way to limit the output size when using -callgraph.
@ -67,6 +70,13 @@ Reduce callgraph output in the timeline by limiting it to a list of calls. The
argument can be a single function name or a comma delimited list. argument can be a single function name or a comma delimited list.
(default: none) (default: none)
.TP .TP
\fB-cgskip \fIfile\fR
Reduce callgraph output in the timeline by skipping over uninteresting
functions in the trace, e.g. printk or console_unlock. The functions listed
in this file will show up as empty leaves in the callgraph with only the start/end
times displayed.
(default: none)
.TP
\fB-timeprec \fIn\fR \fB-timeprec \fIn\fR
Number of significant digits in timestamps (0:S, 3:ms, [6:us]) Number of significant digits in timestamps (0:S, 3:ms, [6:us])
.TP .TP

View File

@ -32,7 +32,7 @@ import platform
import shutil import shutil
from datetime import datetime, timedelta from datetime import datetime, timedelta
from subprocess import call, Popen, PIPE from subprocess import call, Popen, PIPE
import analyze_suspend as aslib import sleepgraph as aslib
# ----------------- CLASSES -------------------- # ----------------- CLASSES --------------------
@ -42,23 +42,18 @@ import analyze_suspend as aslib
# store system values and test parameters # store system values and test parameters
class SystemValues(aslib.SystemValues): class SystemValues(aslib.SystemValues):
title = 'BootGraph' title = 'BootGraph'
version = '2.1' version = '2.2'
hostname = 'localhost' hostname = 'localhost'
testtime = '' testtime = ''
kernel = '' kernel = ''
dmesgfile = '' dmesgfile = ''
ftracefile = '' ftracefile = ''
htmlfile = 'bootgraph.html' htmlfile = 'bootgraph.html'
outfile = ''
testdir = '' testdir = ''
testdirprefix = 'boot' kparams = ''
embedded = False result = ''
testlog = False
dmesglog = False
ftracelog = False
useftrace = False useftrace = False
usecallgraph = False usecallgraph = False
usedevsrc = True
suspendmode = 'boot' suspendmode = 'boot'
max_graph_depth = 2 max_graph_depth = 2
graph_filter = 'do_one_initcall' graph_filter = 'do_one_initcall'
@ -69,11 +64,6 @@ class SystemValues(aslib.SystemValues):
bootloader = 'grub' bootloader = 'grub'
blexec = [] blexec = []
def __init__(self): def __init__(self):
if('LOG_FILE' in os.environ and 'TEST_RESULTS_IDENTIFIER' in os.environ):
self.embedded = True
self.dmesglog = True
self.outfile = os.environ['LOG_FILE']
self.htmlfile = os.environ['LOG_FILE']
self.hostname = platform.node() self.hostname = platform.node()
self.testtime = datetime.now().strftime('%Y-%m-%d_%H:%M:%S') self.testtime = datetime.now().strftime('%Y-%m-%d_%H:%M:%S')
if os.path.exists('/proc/version'): if os.path.exists('/proc/version'):
@ -148,11 +138,18 @@ class SystemValues(aslib.SystemValues):
cmdline = '%s -cronjob' % os.path.abspath(sys.argv[0]) cmdline = '%s -cronjob' % os.path.abspath(sys.argv[0])
args = iter(sys.argv[1:]) args = iter(sys.argv[1:])
for arg in args: for arg in args:
if arg in ['-h', '-v', '-cronjob', '-reboot']: if arg in ['-h', '-v', '-cronjob', '-reboot', '-verbose']:
continue continue
elif arg in ['-o', '-dmesg', '-ftrace', '-func']: elif arg in ['-o', '-dmesg', '-ftrace', '-func']:
args.next() args.next()
continue continue
elif arg == '-result':
cmdline += ' %s "%s"' % (arg, os.path.abspath(args.next()))
continue
elif arg == '-cgskip':
file = self.configFile(args.next())
cmdline += ' %s "%s"' % (arg, os.path.abspath(file))
continue
cmdline += ' '+arg cmdline += ' '+arg
if self.graph_filter != 'do_one_initcall': if self.graph_filter != 'do_one_initcall':
cmdline += ' -func "%s"' % self.graph_filter cmdline += ' -func "%s"' % self.graph_filter
@ -166,14 +163,6 @@ class SystemValues(aslib.SystemValues):
print '3. After reboot, re-run this tool with the same arguments but no command (w/o -reboot or -manual).\n' print '3. After reboot, re-run this tool with the same arguments but no command (w/o -reboot or -manual).\n'
print 'CMDLINE="%s"' % cmdline print 'CMDLINE="%s"' % cmdline
sys.exit() sys.exit()
def getExec(self, cmd):
dirlist = ['/sbin', '/bin', '/usr/sbin', '/usr/bin',
'/usr/local/sbin', '/usr/local/bin']
for path in dirlist:
cmdfull = os.path.join(path, cmd)
if os.path.exists(cmdfull):
return cmdfull
return ''
def blGrub(self): def blGrub(self):
blcmd = '' blcmd = ''
for cmd in ['update-grub', 'grub-mkconfig', 'grub2-mkconfig']: for cmd in ['update-grub', 'grub-mkconfig', 'grub2-mkconfig']:
@ -199,6 +188,14 @@ class SystemValues(aslib.SystemValues):
self.blGrub() self.blGrub()
else: else:
doError('unknown boot loader: %s' % self.bootloader) doError('unknown boot loader: %s' % self.bootloader)
def writeDatafileHeader(self, filename):
self.kparams = open('/proc/cmdline', 'r').read().strip()
fp = open(filename, 'w')
fp.write(self.teststamp+'\n')
fp.write(self.sysstamp+'\n')
fp.write('# command | %s\n' % self.cmdline)
fp.write('# kparams | %s\n' % self.kparams)
fp.close()
sysvals = SystemValues() sysvals = SystemValues()
@ -249,7 +246,7 @@ class Data(aslib.Data):
return name return name
def deviceMatch(self, pid, cg): def deviceMatch(self, pid, cg):
if cg.end - cg.start == 0: if cg.end - cg.start == 0:
return True return ''
for p in data.phases: for p in data.phases:
list = self.dmesg[p]['list'] list = self.dmesg[p]['list']
for devname in list: for devname in list:
@ -260,14 +257,25 @@ class Data(aslib.Data):
if(cg.start <= dev['start'] and cg.end >= dev['end'] and dev['length'] > 0): if(cg.start <= dev['start'] and cg.end >= dev['end'] and dev['length'] > 0):
dev['ftrace'] = cg dev['ftrace'] = cg
self.do_one_initcall = True self.do_one_initcall = True
return True return devname
else: else:
if(cg.start > dev['start'] and cg.end < dev['end']): if(cg.start > dev['start'] and cg.end < dev['end']):
if 'ftraces' not in dev: if 'ftraces' not in dev:
dev['ftraces'] = [] dev['ftraces'] = []
dev['ftraces'].append(cg) dev['ftraces'].append(cg)
return True return devname
return False return ''
def printDetails(self):
sysvals.vprint('Timeline Details:')
sysvals.vprint(' Host: %s' % sysvals.hostname)
sysvals.vprint(' Kernel: %s' % sysvals.kernel)
sysvals.vprint(' Test time: %s' % sysvals.testtime)
sysvals.vprint(' Boot time: %s' % self.boottime)
for phase in self.phases:
dc = len(self.dmesg[phase]['list'])
sysvals.vprint('%9s mode: %.3f - %.3f (%d initcalls)' % (phase,
self.dmesg[phase]['start']*1000,
self.dmesg[phase]['end']*1000, dc))
# ----------------- FUNCTIONS -------------------- # ----------------- FUNCTIONS --------------------
@ -275,6 +283,8 @@ class Data(aslib.Data):
# Description: # Description:
# parse a kernel log for boot data # parse a kernel log for boot data
def parseKernelLog(): def parseKernelLog():
sysvals.vprint('Analyzing the dmesg data (%s)...' % \
os.path.basename(sysvals.dmesgfile))
phase = 'kernel' phase = 'kernel'
data = Data(0) data = Data(0)
data.dmesg['kernel']['start'] = data.start = ktime = 0.0 data.dmesg['kernel']['start'] = data.start = ktime = 0.0
@ -298,6 +308,12 @@ def parseKernelLog():
elif re.match(tp.sysinfofmt, line): elif re.match(tp.sysinfofmt, line):
tp.sysinfo = line tp.sysinfo = line
continue continue
elif re.match(tp.cmdlinefmt, line):
tp.cmdline = line
continue
elif re.match(tp.kparamsfmt, line):
tp.kparams = line
continue
idx = line.find('[') idx = line.find('[')
if idx > 1: if idx > 1:
line = line[idx:] line = line[idx:]
@ -353,6 +369,17 @@ def parseKernelLog():
# Description: # Description:
# Check if trace is available and copy to a temp file # Check if trace is available and copy to a temp file
def parseTraceLog(data): def parseTraceLog(data):
sysvals.vprint('Analyzing the ftrace data (%s)...' % \
os.path.basename(sysvals.ftracefile))
# if available, calculate cgfilter allowable ranges
cgfilter = []
if len(sysvals.cgfilter) > 0:
for p in data.phases:
list = data.dmesg[p]['list']
for i in sysvals.cgfilter:
if i in list:
cgfilter.append([list[i]['start']-0.0001,
list[i]['end']+0.0001])
# parse the trace log # parse the trace log
ftemp = dict() ftemp = dict()
tp = aslib.TestProps() tp = aslib.TestProps()
@ -366,7 +393,16 @@ def parseTraceLog(data):
continue continue
m_time, m_proc, m_pid, m_msg, m_dur = \ m_time, m_proc, m_pid, m_msg, m_dur = \
m.group('time', 'proc', 'pid', 'msg', 'dur') m.group('time', 'proc', 'pid', 'msg', 'dur')
if float(m_time) > data.end: t = float(m_time)
if len(cgfilter) > 0:
allow = False
for r in cgfilter:
if t >= r[0] and t < r[1]:
allow = True
break
if not allow:
continue
if t > data.end:
break break
if(m_time and m_pid and m_msg): if(m_time and m_pid and m_msg):
t = aslib.FTraceLine(m_time, m_msg, m_dur) t = aslib.FTraceLine(m_time, m_msg, m_dur)
@ -378,24 +414,36 @@ def parseTraceLog(data):
key = (m_proc, pid) key = (m_proc, pid)
if(key not in ftemp): if(key not in ftemp):
ftemp[key] = [] ftemp[key] = []
ftemp[key].append(aslib.FTraceCallGraph(pid)) ftemp[key].append(aslib.FTraceCallGraph(pid, sysvals))
cg = ftemp[key][-1] cg = ftemp[key][-1]
if(cg.addLine(t)): res = cg.addLine(t)
ftemp[key].append(aslib.FTraceCallGraph(pid)) if(res != 0):
ftemp[key].append(aslib.FTraceCallGraph(pid, sysvals))
if(res == -1):
ftemp[key][-1].addLine(t)
tf.close() tf.close()
# add the callgraph data to the device hierarchy # add the callgraph data to the device hierarchy
for key in ftemp: for key in ftemp:
proc, pid = key proc, pid = key
for cg in ftemp[key]: for cg in ftemp[key]:
if len(cg.list) < 1 or cg.invalid: if len(cg.list) < 1 or cg.invalid or (cg.end - cg.start == 0):
continue continue
if(not cg.postProcess()): if(not cg.postProcess()):
print('Sanity check failed for %s-%d' % (proc, pid)) print('Sanity check failed for %s-%d' % (proc, pid))
continue continue
# match cg data to devices # match cg data to devices
if not data.deviceMatch(pid, cg): devname = data.deviceMatch(pid, cg)
print ' BAD: %s %s-%d [%f - %f]' % (cg.name, proc, pid, cg.start, cg.end) if not devname:
kind = 'Orphan'
if cg.partial:
kind = 'Partial'
sysvals.vprint('%s callgraph found for %s %s-%d [%f - %f]' %\
(kind, cg.name, proc, pid, cg.start, cg.end))
elif len(cg.list) > 1000000:
print 'WARNING: the callgraph found for %s is massive! (%d lines)' %\
(devname, len(cg.list))
# Function: retrieveLogs # Function: retrieveLogs
# Description: # Description:
@ -473,7 +521,7 @@ def createBootGraph(data):
devtl = aslib.Timeline(100, 20) devtl = aslib.Timeline(100, 20)
# write the test title and general info header # write the test title and general info header
devtl.createHeader(sysvals) devtl.createHeader(sysvals, sysvals.stamp)
# Generate the header for this timeline # Generate the header for this timeline
t0 = data.start t0 = data.start
@ -574,12 +622,9 @@ def createBootGraph(data):
data.dmesg[phase]['color'], phase+'_mode', phase[0]) data.dmesg[phase]['color'], phase+'_mode', phase[0])
devtl.html += '</div>\n' devtl.html += '</div>\n'
if(sysvals.outfile == sysvals.htmlfile): hf = open(sysvals.htmlfile, 'w')
hf = open(sysvals.htmlfile, 'a')
else:
hf = open(sysvals.htmlfile, 'w')
# add the css if this is not an embedded run # add the css
extra = '\ extra = '\
.c1 {background:rgba(209,0,0,0.4);}\n\ .c1 {background:rgba(209,0,0,0.4);}\n\
.c2 {background:rgba(255,102,34,0.4);}\n\ .c2 {background:rgba(255,102,34,0.4);}\n\
@ -597,8 +642,7 @@ def createBootGraph(data):
.fstat td {text-align:left;width:35px;}\n\ .fstat td {text-align:left;width:35px;}\n\
.srccall {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\ .srccall {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\
.srccall:hover {color:white;font-weight:bold;border:1px solid white;}\n' .srccall:hover {color:white;font-weight:bold;border:1px solid white;}\n'
if(not sysvals.embedded): aslib.addCSS(hf, sysvals, 1, False, extra)
aslib.addCSS(hf, sysvals, 1, False, extra)
# write the device timeline # write the device timeline
hf.write(devtl.html) hf.write(devtl.html)
@ -631,6 +675,9 @@ def createBootGraph(data):
if(sysvals.usecallgraph): if(sysvals.usecallgraph):
aslib.addCallgraphs(sysvals, hf, data) aslib.addCallgraphs(sysvals, hf, data)
# add the test log as a hidden div
if sysvals.testlog and 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.dmesglog: if sysvals.dmesglog:
hf.write('<div id="dmesglog" style="display:none;">\n') hf.write('<div id="dmesglog" style="display:none;">\n')
@ -639,14 +686,9 @@ def createBootGraph(data):
hf.write(line) hf.write(line)
hf.write('</div>\n') hf.write('</div>\n')
if(not sysvals.embedded): # write the footer and close
# write the footer and close aslib.addScriptCode(hf, [data])
aslib.addScriptCode(hf, [data]) hf.write('</body>\n</html>\n')
hf.write('</body>\n</html>\n')
else:
# embedded out will be loaded in a page, skip the js
hf.write('<div id=bounds style=display:none>%f,%f</div>' % \
(data.start*1000, data.end*1000))
hf.close() hf.close()
return True return True
@ -780,6 +822,7 @@ def doError(msg, help=False):
if help == True: if help == True:
printHelp() printHelp()
print 'ERROR: %s\n' % msg print 'ERROR: %s\n' % msg
sysvals.outputResult({'error':msg})
sys.exit() sys.exit()
# Function: printHelp # Function: printHelp
@ -806,18 +849,21 @@ def printHelp():
print('Options:') print('Options:')
print(' -h Print this help text') print(' -h Print this help text')
print(' -v Print the current tool version') print(' -v Print the current tool version')
print(' -verbose Print extra information during execution and analysis')
print(' -addlogs Add the dmesg log to the html output') print(' -addlogs Add the dmesg log to the html output')
print(' -result fn Export a results table to a text file for parsing.')
print(' -o name Overrides the output subdirectory name when running a new test') print(' -o name Overrides the output subdirectory name when running a new test')
print(' default: boot-{date}-{time}') print(' default: boot-{date}-{time}')
print(' [advanced]') print(' [advanced]')
print(' -f Use ftrace to add function detail (default: disabled)') print(' -fstat Use ftrace to add function detail and statistics (default: disabled)')
print(' -callgraph Add callgraph detail, can be very large (default: disabled)') print(' -f/-callgraph Add callgraph detail, can be very large (default: disabled)')
print(' -maxdepth N limit the callgraph data to N call levels (default: 2)') print(' -maxdepth N limit the callgraph data to N call levels (default: 2)')
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(' -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(' -expandcg pre-expand the callgraph data in the html output (default: disabled)') print(' -expandcg pre-expand the callgraph data in the html output (default: disabled)')
print(' -func list Limit ftrace to comma-delimited list of functions (default: do_one_initcall)') print(' -func list Limit ftrace to comma-delimited list of functions (default: do_one_initcall)')
print(' -cgfilter S Filter the callgraph output in the timeline') print(' -cgfilter S Filter the callgraph output in the timeline')
print(' -cgskip file Callgraph functions to skip, off to disable (default: cgskip.txt)')
print(' -bl name Use the following boot loader for kernel params (default: grub)') print(' -bl name Use the following boot loader for kernel params (default: grub)')
print(' -reboot Reboot the machine automatically and generate a new timeline') print(' -reboot Reboot the machine automatically and generate a new timeline')
print(' -manual Show the steps to generate a new timeline manually (used with -reboot)') print(' -manual Show the steps to generate a new timeline manually (used with -reboot)')
@ -837,8 +883,13 @@ if __name__ == '__main__':
# loop through the command line arguments # loop through the command line arguments
cmd = '' cmd = ''
testrun = True testrun = True
switchoff = ['disable', 'off', 'false', '0']
simplecmds = ['-sysinfo', '-kpupdate', '-flistall', '-checkbl'] simplecmds = ['-sysinfo', '-kpupdate', '-flistall', '-checkbl']
cgskip = ''
if '-f' in sys.argv:
cgskip = sysvals.configFile('cgskip.txt')
args = iter(sys.argv[1:]) args = iter(sys.argv[1:])
mdset = False
for arg in args: for arg in args:
if(arg == '-h'): if(arg == '-h'):
printHelp() printHelp()
@ -846,13 +897,17 @@ if __name__ == '__main__':
elif(arg == '-v'): elif(arg == '-v'):
print("Version %s" % sysvals.version) print("Version %s" % sysvals.version)
sys.exit() sys.exit()
elif(arg == '-verbose'):
sysvals.verbose = True
elif(arg in simplecmds): elif(arg in simplecmds):
cmd = arg[1:] cmd = arg[1:]
elif(arg == '-f'): elif(arg == '-fstat'):
sysvals.useftrace = True sysvals.useftrace = True
elif(arg == '-callgraph'): elif(arg == '-callgraph' or arg == '-f'):
sysvals.useftrace = True sysvals.useftrace = True
sysvals.usecallgraph = True sysvals.usecallgraph = True
elif(arg == '-cgdump'):
sysvals.cgdump = True
elif(arg == '-mincg'): elif(arg == '-mincg'):
sysvals.mincglen = aslib.getArgFloat('-mincg', args, 0.0, 10000.0) sysvals.mincglen = aslib.getArgFloat('-mincg', args, 0.0, 10000.0)
elif(arg == '-cgfilter'): elif(arg == '-cgfilter'):
@ -860,7 +915,18 @@ if __name__ == '__main__':
val = args.next() val = args.next()
except: except:
doError('No callgraph functions supplied', True) doError('No callgraph functions supplied', True)
sysvals.setDeviceFilter(val) sysvals.setCallgraphFilter(val)
elif(arg == '-cgskip'):
try:
val = args.next()
except:
doError('No file supplied', True)
if val.lower() in switchoff:
cgskip = ''
else:
cgskip = sysvals.configFile(val)
if(not cgskip):
doError('%s does not exist' % cgskip)
elif(arg == '-bl'): elif(arg == '-bl'):
try: try:
val = args.next() val = args.next()
@ -872,6 +938,7 @@ if __name__ == '__main__':
elif(arg == '-timeprec'): elif(arg == '-timeprec'):
sysvals.setPrecision(aslib.getArgInt('-timeprec', args, 0, 6)) sysvals.setPrecision(aslib.getArgInt('-timeprec', args, 0, 6))
elif(arg == '-maxdepth'): elif(arg == '-maxdepth'):
mdset = True
sysvals.max_graph_depth = aslib.getArgInt('-maxdepth', args, 0, 1000) sysvals.max_graph_depth = aslib.getArgInt('-maxdepth', args, 0, 1000)
elif(arg == '-func'): elif(arg == '-func'):
try: try:
@ -902,8 +969,6 @@ if __name__ == '__main__':
doError('No dmesg file supplied', True) doError('No dmesg file supplied', True)
if(os.path.exists(val) == False): if(os.path.exists(val) == False):
doError('%s does not exist' % val) doError('%s does not exist' % val)
if(sysvals.htmlfile == val or sysvals.outfile == val):
doError('Output filename collision')
testrun = False testrun = False
sysvals.dmesgfile = val sysvals.dmesgfile = val
elif(arg == '-o'): elif(arg == '-o'):
@ -912,6 +977,12 @@ if __name__ == '__main__':
except: except:
doError('No subdirectory name supplied', True) doError('No subdirectory name supplied', True)
sysvals.testdir = sysvals.setOutputFolder(val) sysvals.testdir = sysvals.setOutputFolder(val)
elif(arg == '-result'):
try:
val = args.next()
except:
doError('No result file supplied', True)
sysvals.result = val
elif(arg == '-reboot'): elif(arg == '-reboot'):
sysvals.reboot = True sysvals.reboot = True
elif(arg == '-manual'): elif(arg == '-manual'):
@ -947,7 +1018,7 @@ if __name__ == '__main__':
sysvals.getBootLoader() sysvals.getBootLoader()
print 'Boot Loader: %s\n%s' % (sysvals.bootloader, sysvals.blexec) print 'Boot Loader: %s\n%s' % (sysvals.bootloader, sysvals.blexec)
elif(cmd == 'sysinfo'): elif(cmd == 'sysinfo'):
sysvals.printSystemInfo() sysvals.printSystemInfo(True)
sys.exit() sys.exit()
# reboot: update grub, setup a cronjob, and reboot # reboot: update grub, setup a cronjob, and reboot
@ -963,6 +1034,10 @@ if __name__ == '__main__':
sysvals.manualRebootRequired() sysvals.manualRebootRequired()
sys.exit() sys.exit()
if sysvals.usecallgraph and cgskip:
sysvals.vprint('Using cgskip file: %s' % cgskip)
sysvals.setCallgraphBlacklist(cgskip)
# cronjob: remove the cronjob, grub changes, and disable ftrace # cronjob: remove the cronjob, grub changes, and disable ftrace
if sysvals.iscronjob: if sysvals.iscronjob:
updateCron(True) updateCron(True)
@ -980,29 +1055,23 @@ if __name__ == '__main__':
# process the log data # process the log data
if sysvals.dmesgfile: if sysvals.dmesgfile:
if not mdset:
sysvals.max_graph_depth = 0
data = parseKernelLog() data = parseKernelLog()
if(not data.valid): if(not data.valid):
doError('No initcall data found in %s' % sysvals.dmesgfile) doError('No initcall data found in %s' % sysvals.dmesgfile)
if sysvals.useftrace and sysvals.ftracefile: if sysvals.useftrace and sysvals.ftracefile:
parseTraceLog(data) parseTraceLog(data)
if sysvals.cgdump:
data.debugPrint()
sys.exit()
else: else:
doError('dmesg file required') doError('dmesg file required')
print(' Host: %s' % sysvals.hostname) sysvals.vprint('Creating the html timeline (%s)...' % sysvals.htmlfile)
print(' Test time: %s' % sysvals.testtime) sysvals.vprint('Command:\n %s' % sysvals.cmdline)
print(' Boot time: %s' % data.boottime) sysvals.vprint('Kernel parameters:\n %s' % sysvals.kparams)
print('Kernel Version: %s' % sysvals.kernel) data.printDetails()
print(' Kernel start: %.3f' % (data.start * 1000))
print('Usermode start: %.3f' % (data.tUserMode * 1000))
print('Last Init Call: %.3f' % (data.end * 1000))
# handle embedded output logs
if(sysvals.outfile and sysvals.embedded):
fp = open(sysvals.outfile, 'w')
fp.write('pass %s initstart %.3f end %.3f boot %s\n' %
(data.valid, data.tUserMode*1000, data.end*1000, data.boottime))
fp.close()
createBootGraph(data) createBootGraph(data)
# if running as root, change output dir owner to sudo_user # if running as root, change output dir owner to sudo_user
@ -1010,3 +1079,7 @@ if __name__ == '__main__':
os.getuid() == 0 and 'SUDO_USER' in os.environ: os.getuid() == 0 and 'SUDO_USER' in os.environ:
cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1' cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1'
call(cmd.format(os.environ['SUDO_USER'], sysvals.testdir), shell=True) call(cmd.format(os.environ['SUDO_USER'], sysvals.testdir), shell=True)
sysvals.stamp['boot'] = (data.tUserMode - data.start) * 1000
sysvals.stamp['lastinit'] = data.end * 1000
sysvals.outputResult(sysvals.stamp)