162306a36Sopenharmony_ci#!/usr/bin/env python3
262306a36Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0-only
362306a36Sopenharmony_ci#
462306a36Sopenharmony_ci# Tool for analyzing boot timing
562306a36Sopenharmony_ci# Copyright (c) 2013, Intel Corporation.
662306a36Sopenharmony_ci#
762306a36Sopenharmony_ci# This program is free software; you can redistribute it and/or modify it
862306a36Sopenharmony_ci# under the terms and conditions of the GNU General Public License,
962306a36Sopenharmony_ci# version 2, as published by the Free Software Foundation.
1062306a36Sopenharmony_ci#
1162306a36Sopenharmony_ci# This program is distributed in the hope it will be useful, but WITHOUT
1262306a36Sopenharmony_ci# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1362306a36Sopenharmony_ci# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
1462306a36Sopenharmony_ci# more details.
1562306a36Sopenharmony_ci#
1662306a36Sopenharmony_ci# Authors:
1762306a36Sopenharmony_ci#	 Todd Brandt <todd.e.brandt@linux.intel.com>
1862306a36Sopenharmony_ci#
1962306a36Sopenharmony_ci# Description:
2062306a36Sopenharmony_ci#	 This tool is designed to assist kernel and OS developers in optimizing
2162306a36Sopenharmony_ci#	 their linux stack's boot time. It creates an html representation of
2262306a36Sopenharmony_ci#	 the kernel boot timeline up to the start of the init process.
2362306a36Sopenharmony_ci#
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci# ----------------- LIBRARIES --------------------
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ciimport sys
2862306a36Sopenharmony_ciimport time
2962306a36Sopenharmony_ciimport os
3062306a36Sopenharmony_ciimport string
3162306a36Sopenharmony_ciimport re
3262306a36Sopenharmony_ciimport platform
3362306a36Sopenharmony_ciimport shutil
3462306a36Sopenharmony_cifrom datetime import datetime, timedelta
3562306a36Sopenharmony_cifrom subprocess import call, Popen, PIPE
3662306a36Sopenharmony_ciimport sleepgraph as aslib
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cidef pprint(msg):
3962306a36Sopenharmony_ci	print(msg)
4062306a36Sopenharmony_ci	sys.stdout.flush()
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci# ----------------- CLASSES --------------------
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci# Class: SystemValues
4562306a36Sopenharmony_ci# Description:
4662306a36Sopenharmony_ci#	 A global, single-instance container used to
4762306a36Sopenharmony_ci#	 store system values and test parameters
4862306a36Sopenharmony_ciclass SystemValues(aslib.SystemValues):
4962306a36Sopenharmony_ci	title = 'BootGraph'
5062306a36Sopenharmony_ci	version = '2.2'
5162306a36Sopenharmony_ci	hostname = 'localhost'
5262306a36Sopenharmony_ci	testtime = ''
5362306a36Sopenharmony_ci	kernel = ''
5462306a36Sopenharmony_ci	dmesgfile = ''
5562306a36Sopenharmony_ci	ftracefile = ''
5662306a36Sopenharmony_ci	htmlfile = 'bootgraph.html'
5762306a36Sopenharmony_ci	testdir = ''
5862306a36Sopenharmony_ci	kparams = ''
5962306a36Sopenharmony_ci	result = ''
6062306a36Sopenharmony_ci	useftrace = False
6162306a36Sopenharmony_ci	usecallgraph = False
6262306a36Sopenharmony_ci	suspendmode = 'boot'
6362306a36Sopenharmony_ci	max_graph_depth = 2
6462306a36Sopenharmony_ci	graph_filter = 'do_one_initcall'
6562306a36Sopenharmony_ci	reboot = False
6662306a36Sopenharmony_ci	manual = False
6762306a36Sopenharmony_ci	iscronjob = False
6862306a36Sopenharmony_ci	timeformat = '%.6f'
6962306a36Sopenharmony_ci	bootloader = 'grub'
7062306a36Sopenharmony_ci	blexec = []
7162306a36Sopenharmony_ci	def __init__(self):
7262306a36Sopenharmony_ci		self.kernel, self.hostname = 'unknown', platform.node()
7362306a36Sopenharmony_ci		self.testtime = datetime.now().strftime('%Y-%m-%d_%H:%M:%S')
7462306a36Sopenharmony_ci		if os.path.exists('/proc/version'):
7562306a36Sopenharmony_ci			fp = open('/proc/version', 'r')
7662306a36Sopenharmony_ci			self.kernel = self.kernelVersion(fp.read().strip())
7762306a36Sopenharmony_ci			fp.close()
7862306a36Sopenharmony_ci		self.testdir = datetime.now().strftime('boot-%y%m%d-%H%M%S')
7962306a36Sopenharmony_ci	def kernelVersion(self, msg):
8062306a36Sopenharmony_ci		m = re.match('^[Ll]inux *[Vv]ersion *(?P<v>\S*) .*', msg)
8162306a36Sopenharmony_ci		if m:
8262306a36Sopenharmony_ci			return m.group('v')
8362306a36Sopenharmony_ci		return 'unknown'
8462306a36Sopenharmony_ci	def checkFtraceKernelVersion(self):
8562306a36Sopenharmony_ci		m = re.match('^(?P<x>[0-9]*)\.(?P<y>[0-9]*)\.(?P<z>[0-9]*).*', self.kernel)
8662306a36Sopenharmony_ci		if m:
8762306a36Sopenharmony_ci			val = tuple(map(int, m.groups()))
8862306a36Sopenharmony_ci			if val >= (4, 10, 0):
8962306a36Sopenharmony_ci				return True
9062306a36Sopenharmony_ci		return False
9162306a36Sopenharmony_ci	def kernelParams(self):
9262306a36Sopenharmony_ci		cmdline = 'initcall_debug log_buf_len=32M'
9362306a36Sopenharmony_ci		if self.useftrace:
9462306a36Sopenharmony_ci			if self.cpucount > 0:
9562306a36Sopenharmony_ci				bs = min(self.memtotal // 2, 2*1024*1024) // self.cpucount
9662306a36Sopenharmony_ci			else:
9762306a36Sopenharmony_ci				bs = 131072
9862306a36Sopenharmony_ci			cmdline += ' trace_buf_size=%dK trace_clock=global '\
9962306a36Sopenharmony_ci			'trace_options=nooverwrite,funcgraph-abstime,funcgraph-cpu,'\
10062306a36Sopenharmony_ci			'funcgraph-duration,funcgraph-proc,funcgraph-tail,'\
10162306a36Sopenharmony_ci			'nofuncgraph-overhead,context-info,graph-time '\
10262306a36Sopenharmony_ci			'ftrace=function_graph '\
10362306a36Sopenharmony_ci			'ftrace_graph_max_depth=%d '\
10462306a36Sopenharmony_ci			'ftrace_graph_filter=%s' % \
10562306a36Sopenharmony_ci				(bs, self.max_graph_depth, self.graph_filter)
10662306a36Sopenharmony_ci		return cmdline
10762306a36Sopenharmony_ci	def setGraphFilter(self, val):
10862306a36Sopenharmony_ci		master = self.getBootFtraceFilterFunctions()
10962306a36Sopenharmony_ci		fs = ''
11062306a36Sopenharmony_ci		for i in val.split(','):
11162306a36Sopenharmony_ci			func = i.strip()
11262306a36Sopenharmony_ci			if func == '':
11362306a36Sopenharmony_ci				doError('badly formatted filter function string')
11462306a36Sopenharmony_ci			if '[' in func or ']' in func:
11562306a36Sopenharmony_ci				doError('loadable module functions not allowed - "%s"' % func)
11662306a36Sopenharmony_ci			if ' ' in func:
11762306a36Sopenharmony_ci				doError('spaces found in filter functions - "%s"' % func)
11862306a36Sopenharmony_ci			if func not in master:
11962306a36Sopenharmony_ci				doError('function "%s" not available for ftrace' % func)
12062306a36Sopenharmony_ci			if not fs:
12162306a36Sopenharmony_ci				fs = func
12262306a36Sopenharmony_ci			else:
12362306a36Sopenharmony_ci				fs += ','+func
12462306a36Sopenharmony_ci		if not fs:
12562306a36Sopenharmony_ci			doError('badly formatted filter function string')
12662306a36Sopenharmony_ci		self.graph_filter = fs
12762306a36Sopenharmony_ci	def getBootFtraceFilterFunctions(self):
12862306a36Sopenharmony_ci		self.rootCheck(True)
12962306a36Sopenharmony_ci		fp = open(self.tpath+'available_filter_functions')
13062306a36Sopenharmony_ci		fulllist = fp.read().split('\n')
13162306a36Sopenharmony_ci		fp.close()
13262306a36Sopenharmony_ci		list = []
13362306a36Sopenharmony_ci		for i in fulllist:
13462306a36Sopenharmony_ci			if not i or ' ' in i or '[' in i or ']' in i:
13562306a36Sopenharmony_ci				continue
13662306a36Sopenharmony_ci			list.append(i)
13762306a36Sopenharmony_ci		return list
13862306a36Sopenharmony_ci	def myCronJob(self, line):
13962306a36Sopenharmony_ci		if '@reboot' not in line:
14062306a36Sopenharmony_ci			return False
14162306a36Sopenharmony_ci		if 'bootgraph' in line or 'analyze_boot.py' in line or '-cronjob' in line:
14262306a36Sopenharmony_ci			return True
14362306a36Sopenharmony_ci		return False
14462306a36Sopenharmony_ci	def cronjobCmdString(self):
14562306a36Sopenharmony_ci		cmdline = '%s -cronjob' % os.path.abspath(sys.argv[0])
14662306a36Sopenharmony_ci		args = iter(sys.argv[1:])
14762306a36Sopenharmony_ci		for arg in args:
14862306a36Sopenharmony_ci			if arg in ['-h', '-v', '-cronjob', '-reboot', '-verbose']:
14962306a36Sopenharmony_ci				continue
15062306a36Sopenharmony_ci			elif arg in ['-o', '-dmesg', '-ftrace', '-func']:
15162306a36Sopenharmony_ci				next(args)
15262306a36Sopenharmony_ci				continue
15362306a36Sopenharmony_ci			elif arg == '-result':
15462306a36Sopenharmony_ci				cmdline += ' %s "%s"' % (arg, os.path.abspath(next(args)))
15562306a36Sopenharmony_ci				continue
15662306a36Sopenharmony_ci			elif arg == '-cgskip':
15762306a36Sopenharmony_ci				file = self.configFile(next(args))
15862306a36Sopenharmony_ci				cmdline += ' %s "%s"' % (arg, os.path.abspath(file))
15962306a36Sopenharmony_ci				continue
16062306a36Sopenharmony_ci			cmdline += ' '+arg
16162306a36Sopenharmony_ci		if self.graph_filter != 'do_one_initcall':
16262306a36Sopenharmony_ci			cmdline += ' -func "%s"' % self.graph_filter
16362306a36Sopenharmony_ci		cmdline += ' -o "%s"' % os.path.abspath(self.testdir)
16462306a36Sopenharmony_ci		return cmdline
16562306a36Sopenharmony_ci	def manualRebootRequired(self):
16662306a36Sopenharmony_ci		cmdline = self.kernelParams()
16762306a36Sopenharmony_ci		pprint('To generate a new timeline manually, follow these steps:\n\n'\
16862306a36Sopenharmony_ci		'1. Add the CMDLINE string to your kernel command line.\n'\
16962306a36Sopenharmony_ci		'2. Reboot the system.\n'\
17062306a36Sopenharmony_ci		'3. After reboot, re-run this tool with the same arguments but no command (w/o -reboot or -manual).\n\n'\
17162306a36Sopenharmony_ci		'CMDLINE="%s"' % cmdline)
17262306a36Sopenharmony_ci		sys.exit()
17362306a36Sopenharmony_ci	def blGrub(self):
17462306a36Sopenharmony_ci		blcmd = ''
17562306a36Sopenharmony_ci		for cmd in ['update-grub', 'grub-mkconfig', 'grub2-mkconfig']:
17662306a36Sopenharmony_ci			if blcmd:
17762306a36Sopenharmony_ci				break
17862306a36Sopenharmony_ci			blcmd = self.getExec(cmd)
17962306a36Sopenharmony_ci		if not blcmd:
18062306a36Sopenharmony_ci			doError('[GRUB] missing update command')
18162306a36Sopenharmony_ci		if not os.path.exists('/etc/default/grub'):
18262306a36Sopenharmony_ci			doError('[GRUB] missing /etc/default/grub')
18362306a36Sopenharmony_ci		if 'grub2' in blcmd:
18462306a36Sopenharmony_ci			cfg = '/boot/grub2/grub.cfg'
18562306a36Sopenharmony_ci		else:
18662306a36Sopenharmony_ci			cfg = '/boot/grub/grub.cfg'
18762306a36Sopenharmony_ci		if not os.path.exists(cfg):
18862306a36Sopenharmony_ci			doError('[GRUB] missing %s' % cfg)
18962306a36Sopenharmony_ci		if 'update-grub' in blcmd:
19062306a36Sopenharmony_ci			self.blexec = [blcmd]
19162306a36Sopenharmony_ci		else:
19262306a36Sopenharmony_ci			self.blexec = [blcmd, '-o', cfg]
19362306a36Sopenharmony_ci	def getBootLoader(self):
19462306a36Sopenharmony_ci		if self.bootloader == 'grub':
19562306a36Sopenharmony_ci			self.blGrub()
19662306a36Sopenharmony_ci		else:
19762306a36Sopenharmony_ci			doError('unknown boot loader: %s' % self.bootloader)
19862306a36Sopenharmony_ci	def writeDatafileHeader(self, filename):
19962306a36Sopenharmony_ci		self.kparams = open('/proc/cmdline', 'r').read().strip()
20062306a36Sopenharmony_ci		fp = open(filename, 'w')
20162306a36Sopenharmony_ci		fp.write(self.teststamp+'\n')
20262306a36Sopenharmony_ci		fp.write(self.sysstamp+'\n')
20362306a36Sopenharmony_ci		fp.write('# command | %s\n' % self.cmdline)
20462306a36Sopenharmony_ci		fp.write('# kparams | %s\n' % self.kparams)
20562306a36Sopenharmony_ci		fp.close()
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cisysvals = SystemValues()
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci# Class: Data
21062306a36Sopenharmony_ci# Description:
21162306a36Sopenharmony_ci#	 The primary container for test data.
21262306a36Sopenharmony_ciclass Data(aslib.Data):
21362306a36Sopenharmony_ci	dmesg = {}  # root data structure
21462306a36Sopenharmony_ci	start = 0.0 # test start
21562306a36Sopenharmony_ci	end = 0.0   # test end
21662306a36Sopenharmony_ci	dmesgtext = []   # dmesg text file in memory
21762306a36Sopenharmony_ci	testnumber = 0
21862306a36Sopenharmony_ci	idstr = ''
21962306a36Sopenharmony_ci	html_device_id = 0
22062306a36Sopenharmony_ci	valid = False
22162306a36Sopenharmony_ci	tUserMode = 0.0
22262306a36Sopenharmony_ci	boottime = ''
22362306a36Sopenharmony_ci	phases = ['kernel', 'user']
22462306a36Sopenharmony_ci	do_one_initcall = False
22562306a36Sopenharmony_ci	def __init__(self, num):
22662306a36Sopenharmony_ci		self.testnumber = num
22762306a36Sopenharmony_ci		self.idstr = 'a'
22862306a36Sopenharmony_ci		self.dmesgtext = []
22962306a36Sopenharmony_ci		self.dmesg = {
23062306a36Sopenharmony_ci			'kernel': {'list': dict(), 'start': -1.0, 'end': -1.0, 'row': 0,
23162306a36Sopenharmony_ci				'order': 0, 'color': 'linear-gradient(to bottom, #fff, #bcf)'},
23262306a36Sopenharmony_ci			'user': {'list': dict(), 'start': -1.0, 'end': -1.0, 'row': 0,
23362306a36Sopenharmony_ci				'order': 1, 'color': '#fff'}
23462306a36Sopenharmony_ci		}
23562306a36Sopenharmony_ci	def deviceTopology(self):
23662306a36Sopenharmony_ci		return ''
23762306a36Sopenharmony_ci	def newAction(self, phase, name, pid, start, end, ret, ulen):
23862306a36Sopenharmony_ci		# new device callback for a specific phase
23962306a36Sopenharmony_ci		self.html_device_id += 1
24062306a36Sopenharmony_ci		devid = '%s%d' % (self.idstr, self.html_device_id)
24162306a36Sopenharmony_ci		list = self.dmesg[phase]['list']
24262306a36Sopenharmony_ci		length = -1.0
24362306a36Sopenharmony_ci		if(start >= 0 and end >= 0):
24462306a36Sopenharmony_ci			length = end - start
24562306a36Sopenharmony_ci		i = 2
24662306a36Sopenharmony_ci		origname = name
24762306a36Sopenharmony_ci		while(name in list):
24862306a36Sopenharmony_ci			name = '%s[%d]' % (origname, i)
24962306a36Sopenharmony_ci			i += 1
25062306a36Sopenharmony_ci		list[name] = {'name': name, 'start': start, 'end': end,
25162306a36Sopenharmony_ci			'pid': pid, 'length': length, 'row': 0, 'id': devid,
25262306a36Sopenharmony_ci			'ret': ret, 'ulen': ulen }
25362306a36Sopenharmony_ci		return name
25462306a36Sopenharmony_ci	def deviceMatch(self, pid, cg):
25562306a36Sopenharmony_ci		if cg.end - cg.start == 0:
25662306a36Sopenharmony_ci			return ''
25762306a36Sopenharmony_ci		for p in data.phases:
25862306a36Sopenharmony_ci			list = self.dmesg[p]['list']
25962306a36Sopenharmony_ci			for devname in list:
26062306a36Sopenharmony_ci				dev = list[devname]
26162306a36Sopenharmony_ci				if pid != dev['pid']:
26262306a36Sopenharmony_ci					continue
26362306a36Sopenharmony_ci				if cg.name == 'do_one_initcall':
26462306a36Sopenharmony_ci					if(cg.start <= dev['start'] and cg.end >= dev['end'] and dev['length'] > 0):
26562306a36Sopenharmony_ci						dev['ftrace'] = cg
26662306a36Sopenharmony_ci						self.do_one_initcall = True
26762306a36Sopenharmony_ci						return devname
26862306a36Sopenharmony_ci				else:
26962306a36Sopenharmony_ci					if(cg.start > dev['start'] and cg.end < dev['end']):
27062306a36Sopenharmony_ci						if 'ftraces' not in dev:
27162306a36Sopenharmony_ci							dev['ftraces'] = []
27262306a36Sopenharmony_ci						dev['ftraces'].append(cg)
27362306a36Sopenharmony_ci						return devname
27462306a36Sopenharmony_ci		return ''
27562306a36Sopenharmony_ci	def printDetails(self):
27662306a36Sopenharmony_ci		sysvals.vprint('Timeline Details:')
27762306a36Sopenharmony_ci		sysvals.vprint('          Host: %s' % sysvals.hostname)
27862306a36Sopenharmony_ci		sysvals.vprint('        Kernel: %s' % sysvals.kernel)
27962306a36Sopenharmony_ci		sysvals.vprint('     Test time: %s' % sysvals.testtime)
28062306a36Sopenharmony_ci		sysvals.vprint('     Boot time: %s' % self.boottime)
28162306a36Sopenharmony_ci		for phase in self.phases:
28262306a36Sopenharmony_ci			dc = len(self.dmesg[phase]['list'])
28362306a36Sopenharmony_ci			sysvals.vprint('%9s mode: %.3f - %.3f (%d initcalls)' % (phase,
28462306a36Sopenharmony_ci				self.dmesg[phase]['start']*1000,
28562306a36Sopenharmony_ci				self.dmesg[phase]['end']*1000, dc))
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci# ----------------- FUNCTIONS --------------------
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci# Function: parseKernelLog
29062306a36Sopenharmony_ci# Description:
29162306a36Sopenharmony_ci#	 parse a kernel log for boot data
29262306a36Sopenharmony_cidef parseKernelLog():
29362306a36Sopenharmony_ci	sysvals.vprint('Analyzing the dmesg data (%s)...' % \
29462306a36Sopenharmony_ci		os.path.basename(sysvals.dmesgfile))
29562306a36Sopenharmony_ci	phase = 'kernel'
29662306a36Sopenharmony_ci	data = Data(0)
29762306a36Sopenharmony_ci	data.dmesg['kernel']['start'] = data.start = ktime = 0.0
29862306a36Sopenharmony_ci	sysvals.stamp = {
29962306a36Sopenharmony_ci		'time': datetime.now().strftime('%B %d %Y, %I:%M:%S %p'),
30062306a36Sopenharmony_ci		'host': sysvals.hostname,
30162306a36Sopenharmony_ci		'mode': 'boot', 'kernel': ''}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	tp = aslib.TestProps()
30462306a36Sopenharmony_ci	devtemp = dict()
30562306a36Sopenharmony_ci	if(sysvals.dmesgfile):
30662306a36Sopenharmony_ci		lf = open(sysvals.dmesgfile, 'rb')
30762306a36Sopenharmony_ci	else:
30862306a36Sopenharmony_ci		lf = Popen('dmesg', stdout=PIPE).stdout
30962306a36Sopenharmony_ci	for line in lf:
31062306a36Sopenharmony_ci		line = aslib.ascii(line).replace('\r\n', '')
31162306a36Sopenharmony_ci		# grab the stamp and sysinfo
31262306a36Sopenharmony_ci		if re.match(tp.stampfmt, line):
31362306a36Sopenharmony_ci			tp.stamp = line
31462306a36Sopenharmony_ci			continue
31562306a36Sopenharmony_ci		elif re.match(tp.sysinfofmt, line):
31662306a36Sopenharmony_ci			tp.sysinfo = line
31762306a36Sopenharmony_ci			continue
31862306a36Sopenharmony_ci		elif re.match(tp.cmdlinefmt, line):
31962306a36Sopenharmony_ci			tp.cmdline = line
32062306a36Sopenharmony_ci			continue
32162306a36Sopenharmony_ci		elif re.match(tp.kparamsfmt, line):
32262306a36Sopenharmony_ci			tp.kparams = line
32362306a36Sopenharmony_ci			continue
32462306a36Sopenharmony_ci		idx = line.find('[')
32562306a36Sopenharmony_ci		if idx > 1:
32662306a36Sopenharmony_ci			line = line[idx:]
32762306a36Sopenharmony_ci		m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
32862306a36Sopenharmony_ci		if(not m):
32962306a36Sopenharmony_ci			continue
33062306a36Sopenharmony_ci		ktime = float(m.group('ktime'))
33162306a36Sopenharmony_ci		if(ktime > 120):
33262306a36Sopenharmony_ci			break
33362306a36Sopenharmony_ci		msg = m.group('msg')
33462306a36Sopenharmony_ci		data.dmesgtext.append(line)
33562306a36Sopenharmony_ci		if(ktime == 0.0 and re.match('^Linux version .*', msg)):
33662306a36Sopenharmony_ci			if(not sysvals.stamp['kernel']):
33762306a36Sopenharmony_ci				sysvals.stamp['kernel'] = sysvals.kernelVersion(msg)
33862306a36Sopenharmony_ci			continue
33962306a36Sopenharmony_ci		m = re.match('.* setting system clock to (?P<d>[0-9\-]*)[ A-Z](?P<t>[0-9:]*) UTC.*', msg)
34062306a36Sopenharmony_ci		if(m):
34162306a36Sopenharmony_ci			bt = datetime.strptime(m.group('d')+' '+m.group('t'), '%Y-%m-%d %H:%M:%S')
34262306a36Sopenharmony_ci			bt = bt - timedelta(seconds=int(ktime))
34362306a36Sopenharmony_ci			data.boottime = bt.strftime('%Y-%m-%d_%H:%M:%S')
34462306a36Sopenharmony_ci			sysvals.stamp['time'] = bt.strftime('%B %d %Y, %I:%M:%S %p')
34562306a36Sopenharmony_ci			continue
34662306a36Sopenharmony_ci		m = re.match('^calling *(?P<f>.*)\+.* @ (?P<p>[0-9]*)', msg)
34762306a36Sopenharmony_ci		if(m):
34862306a36Sopenharmony_ci			func = m.group('f')
34962306a36Sopenharmony_ci			pid = int(m.group('p'))
35062306a36Sopenharmony_ci			devtemp[func] = (ktime, pid)
35162306a36Sopenharmony_ci			continue
35262306a36Sopenharmony_ci		m = re.match('^initcall *(?P<f>.*)\+.* returned (?P<r>.*) after (?P<t>.*) usecs', msg)
35362306a36Sopenharmony_ci		if(m):
35462306a36Sopenharmony_ci			data.valid = True
35562306a36Sopenharmony_ci			data.end = ktime
35662306a36Sopenharmony_ci			f, r, t = m.group('f', 'r', 't')
35762306a36Sopenharmony_ci			if(f in devtemp):
35862306a36Sopenharmony_ci				start, pid = devtemp[f]
35962306a36Sopenharmony_ci				data.newAction(phase, f, pid, start, ktime, int(r), int(t))
36062306a36Sopenharmony_ci				del devtemp[f]
36162306a36Sopenharmony_ci			continue
36262306a36Sopenharmony_ci		if(re.match('^Freeing unused kernel .*', msg)):
36362306a36Sopenharmony_ci			data.tUserMode = ktime
36462306a36Sopenharmony_ci			data.dmesg['kernel']['end'] = ktime
36562306a36Sopenharmony_ci			data.dmesg['user']['start'] = ktime
36662306a36Sopenharmony_ci			phase = 'user'
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	if tp.stamp:
36962306a36Sopenharmony_ci		sysvals.stamp = 0
37062306a36Sopenharmony_ci		tp.parseStamp(data, sysvals)
37162306a36Sopenharmony_ci	data.dmesg['user']['end'] = data.end
37262306a36Sopenharmony_ci	lf.close()
37362306a36Sopenharmony_ci	return data
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci# Function: parseTraceLog
37662306a36Sopenharmony_ci# Description:
37762306a36Sopenharmony_ci#	 Check if trace is available and copy to a temp file
37862306a36Sopenharmony_cidef parseTraceLog(data):
37962306a36Sopenharmony_ci	sysvals.vprint('Analyzing the ftrace data (%s)...' % \
38062306a36Sopenharmony_ci		os.path.basename(sysvals.ftracefile))
38162306a36Sopenharmony_ci	# if available, calculate cgfilter allowable ranges
38262306a36Sopenharmony_ci	cgfilter = []
38362306a36Sopenharmony_ci	if len(sysvals.cgfilter) > 0:
38462306a36Sopenharmony_ci		for p in data.phases:
38562306a36Sopenharmony_ci			list = data.dmesg[p]['list']
38662306a36Sopenharmony_ci			for i in sysvals.cgfilter:
38762306a36Sopenharmony_ci				if i in list:
38862306a36Sopenharmony_ci					cgfilter.append([list[i]['start']-0.0001,
38962306a36Sopenharmony_ci						list[i]['end']+0.0001])
39062306a36Sopenharmony_ci	# parse the trace log
39162306a36Sopenharmony_ci	ftemp = dict()
39262306a36Sopenharmony_ci	tp = aslib.TestProps()
39362306a36Sopenharmony_ci	tp.setTracerType('function_graph')
39462306a36Sopenharmony_ci	tf = open(sysvals.ftracefile, 'r')
39562306a36Sopenharmony_ci	for line in tf:
39662306a36Sopenharmony_ci		if line[0] == '#':
39762306a36Sopenharmony_ci			continue
39862306a36Sopenharmony_ci		m = re.match(tp.ftrace_line_fmt, line.strip())
39962306a36Sopenharmony_ci		if(not m):
40062306a36Sopenharmony_ci			continue
40162306a36Sopenharmony_ci		m_time, m_proc, m_pid, m_msg, m_dur = \
40262306a36Sopenharmony_ci			m.group('time', 'proc', 'pid', 'msg', 'dur')
40362306a36Sopenharmony_ci		t = float(m_time)
40462306a36Sopenharmony_ci		if len(cgfilter) > 0:
40562306a36Sopenharmony_ci			allow = False
40662306a36Sopenharmony_ci			for r in cgfilter:
40762306a36Sopenharmony_ci				if t >= r[0] and t < r[1]:
40862306a36Sopenharmony_ci					allow = True
40962306a36Sopenharmony_ci					break
41062306a36Sopenharmony_ci			if not allow:
41162306a36Sopenharmony_ci				continue
41262306a36Sopenharmony_ci		if t > data.end:
41362306a36Sopenharmony_ci			break
41462306a36Sopenharmony_ci		if(m_time and m_pid and m_msg):
41562306a36Sopenharmony_ci			t = aslib.FTraceLine(m_time, m_msg, m_dur)
41662306a36Sopenharmony_ci			pid = int(m_pid)
41762306a36Sopenharmony_ci		else:
41862306a36Sopenharmony_ci			continue
41962306a36Sopenharmony_ci		if t.fevent or t.fkprobe:
42062306a36Sopenharmony_ci			continue
42162306a36Sopenharmony_ci		key = (m_proc, pid)
42262306a36Sopenharmony_ci		if(key not in ftemp):
42362306a36Sopenharmony_ci			ftemp[key] = []
42462306a36Sopenharmony_ci			ftemp[key].append(aslib.FTraceCallGraph(pid, sysvals))
42562306a36Sopenharmony_ci		cg = ftemp[key][-1]
42662306a36Sopenharmony_ci		res = cg.addLine(t)
42762306a36Sopenharmony_ci		if(res != 0):
42862306a36Sopenharmony_ci			ftemp[key].append(aslib.FTraceCallGraph(pid, sysvals))
42962306a36Sopenharmony_ci		if(res == -1):
43062306a36Sopenharmony_ci			ftemp[key][-1].addLine(t)
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	tf.close()
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	# add the callgraph data to the device hierarchy
43562306a36Sopenharmony_ci	for key in ftemp:
43662306a36Sopenharmony_ci		proc, pid = key
43762306a36Sopenharmony_ci		for cg in ftemp[key]:
43862306a36Sopenharmony_ci			if len(cg.list) < 1 or cg.invalid or (cg.end - cg.start == 0):
43962306a36Sopenharmony_ci				continue
44062306a36Sopenharmony_ci			if(not cg.postProcess()):
44162306a36Sopenharmony_ci				pprint('Sanity check failed for %s-%d' % (proc, pid))
44262306a36Sopenharmony_ci				continue
44362306a36Sopenharmony_ci			# match cg data to devices
44462306a36Sopenharmony_ci			devname = data.deviceMatch(pid, cg)
44562306a36Sopenharmony_ci			if not devname:
44662306a36Sopenharmony_ci				kind = 'Orphan'
44762306a36Sopenharmony_ci				if cg.partial:
44862306a36Sopenharmony_ci					kind = 'Partial'
44962306a36Sopenharmony_ci				sysvals.vprint('%s callgraph found for %s %s-%d [%f - %f]' %\
45062306a36Sopenharmony_ci					(kind, cg.name, proc, pid, cg.start, cg.end))
45162306a36Sopenharmony_ci			elif len(cg.list) > 1000000:
45262306a36Sopenharmony_ci				pprint('WARNING: the callgraph found for %s is massive! (%d lines)' %\
45362306a36Sopenharmony_ci					(devname, len(cg.list)))
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci# Function: retrieveLogs
45662306a36Sopenharmony_ci# Description:
45762306a36Sopenharmony_ci#	 Create copies of dmesg and/or ftrace for later processing
45862306a36Sopenharmony_cidef retrieveLogs():
45962306a36Sopenharmony_ci	# check ftrace is configured first
46062306a36Sopenharmony_ci	if sysvals.useftrace:
46162306a36Sopenharmony_ci		tracer = sysvals.fgetVal('current_tracer').strip()
46262306a36Sopenharmony_ci		if tracer != 'function_graph':
46362306a36Sopenharmony_ci			doError('ftrace not configured for a boot callgraph')
46462306a36Sopenharmony_ci	# create the folder and get dmesg
46562306a36Sopenharmony_ci	sysvals.systemInfo(aslib.dmidecode(sysvals.mempath))
46662306a36Sopenharmony_ci	sysvals.initTestOutput('boot')
46762306a36Sopenharmony_ci	sysvals.writeDatafileHeader(sysvals.dmesgfile)
46862306a36Sopenharmony_ci	call('dmesg >> '+sysvals.dmesgfile, shell=True)
46962306a36Sopenharmony_ci	if not sysvals.useftrace:
47062306a36Sopenharmony_ci		return
47162306a36Sopenharmony_ci	# get ftrace
47262306a36Sopenharmony_ci	sysvals.writeDatafileHeader(sysvals.ftracefile)
47362306a36Sopenharmony_ci	call('cat '+sysvals.tpath+'trace >> '+sysvals.ftracefile, shell=True)
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci# Function: colorForName
47662306a36Sopenharmony_ci# Description:
47762306a36Sopenharmony_ci#	 Generate a repeatable color from a list for a given name
47862306a36Sopenharmony_cidef colorForName(name):
47962306a36Sopenharmony_ci	list = [
48062306a36Sopenharmony_ci		('c1', '#ec9999'),
48162306a36Sopenharmony_ci		('c2', '#ffc1a6'),
48262306a36Sopenharmony_ci		('c3', '#fff0a6'),
48362306a36Sopenharmony_ci		('c4', '#adf199'),
48462306a36Sopenharmony_ci		('c5', '#9fadea'),
48562306a36Sopenharmony_ci		('c6', '#a699c1'),
48662306a36Sopenharmony_ci		('c7', '#ad99b4'),
48762306a36Sopenharmony_ci		('c8', '#eaffea'),
48862306a36Sopenharmony_ci		('c9', '#dcecfb'),
48962306a36Sopenharmony_ci		('c10', '#ffffea')
49062306a36Sopenharmony_ci	]
49162306a36Sopenharmony_ci	i = 0
49262306a36Sopenharmony_ci	total = 0
49362306a36Sopenharmony_ci	count = len(list)
49462306a36Sopenharmony_ci	while i < len(name):
49562306a36Sopenharmony_ci		total += ord(name[i])
49662306a36Sopenharmony_ci		i += 1
49762306a36Sopenharmony_ci	return list[total % count]
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cidef cgOverview(cg, minlen):
50062306a36Sopenharmony_ci	stats = dict()
50162306a36Sopenharmony_ci	large = []
50262306a36Sopenharmony_ci	for l in cg.list:
50362306a36Sopenharmony_ci		if l.fcall and l.depth == 1:
50462306a36Sopenharmony_ci			if l.length >= minlen:
50562306a36Sopenharmony_ci				large.append(l)
50662306a36Sopenharmony_ci			if l.name not in stats:
50762306a36Sopenharmony_ci				stats[l.name] = [0, 0.0]
50862306a36Sopenharmony_ci			stats[l.name][0] += (l.length * 1000.0)
50962306a36Sopenharmony_ci			stats[l.name][1] += 1
51062306a36Sopenharmony_ci	return (large, stats)
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci# Function: createBootGraph
51362306a36Sopenharmony_ci# Description:
51462306a36Sopenharmony_ci#	 Create the output html file from the resident test data
51562306a36Sopenharmony_ci# Arguments:
51662306a36Sopenharmony_ci#	 testruns: array of Data objects from parseKernelLog or parseTraceLog
51762306a36Sopenharmony_ci# Output:
51862306a36Sopenharmony_ci#	 True if the html file was created, false if it failed
51962306a36Sopenharmony_cidef createBootGraph(data):
52062306a36Sopenharmony_ci	# html function templates
52162306a36Sopenharmony_ci	html_srccall = '<div id={6} title="{5}" class="srccall" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;">{0}</div>\n'
52262306a36Sopenharmony_ci	html_timetotal = '<table class="time1">\n<tr>'\
52362306a36Sopenharmony_ci		'<td class="blue">Init process starts @ <b>{0} ms</b></td>'\
52462306a36Sopenharmony_ci		'<td class="blue">Last initcall ends @ <b>{1} ms</b></td>'\
52562306a36Sopenharmony_ci		'</tr>\n</table>\n'
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	# device timeline
52862306a36Sopenharmony_ci	devtl = aslib.Timeline(100, 20)
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	# write the test title and general info header
53162306a36Sopenharmony_ci	devtl.createHeader(sysvals, sysvals.stamp)
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	# Generate the header for this timeline
53462306a36Sopenharmony_ci	t0 = data.start
53562306a36Sopenharmony_ci	tMax = data.end
53662306a36Sopenharmony_ci	tTotal = tMax - t0
53762306a36Sopenharmony_ci	if(tTotal == 0):
53862306a36Sopenharmony_ci		pprint('ERROR: No timeline data')
53962306a36Sopenharmony_ci		return False
54062306a36Sopenharmony_ci	user_mode = '%.0f'%(data.tUserMode*1000)
54162306a36Sopenharmony_ci	last_init = '%.0f'%(tTotal*1000)
54262306a36Sopenharmony_ci	devtl.html += html_timetotal.format(user_mode, last_init)
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	# determine the maximum number of rows we need to draw
54562306a36Sopenharmony_ci	devlist = []
54662306a36Sopenharmony_ci	for p in data.phases:
54762306a36Sopenharmony_ci		list = data.dmesg[p]['list']
54862306a36Sopenharmony_ci		for devname in list:
54962306a36Sopenharmony_ci			d = aslib.DevItem(0, p, list[devname])
55062306a36Sopenharmony_ci			devlist.append(d)
55162306a36Sopenharmony_ci		devtl.getPhaseRows(devlist, 0, 'start')
55262306a36Sopenharmony_ci	devtl.calcTotalRows()
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	# draw the timeline background
55562306a36Sopenharmony_ci	devtl.createZoomBox()
55662306a36Sopenharmony_ci	devtl.html += devtl.html_tblock.format('boot', '0', '100', devtl.scaleH)
55762306a36Sopenharmony_ci	for p in data.phases:
55862306a36Sopenharmony_ci		phase = data.dmesg[p]
55962306a36Sopenharmony_ci		length = phase['end']-phase['start']
56062306a36Sopenharmony_ci		left = '%.3f' % (((phase['start']-t0)*100.0)/tTotal)
56162306a36Sopenharmony_ci		width = '%.3f' % ((length*100.0)/tTotal)
56262306a36Sopenharmony_ci		devtl.html += devtl.html_phase.format(left, width, \
56362306a36Sopenharmony_ci			'%.3f'%devtl.scaleH, '%.3f'%devtl.bodyH, \
56462306a36Sopenharmony_ci			phase['color'], '')
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	# draw the device timeline
56762306a36Sopenharmony_ci	num = 0
56862306a36Sopenharmony_ci	devstats = dict()
56962306a36Sopenharmony_ci	for phase in data.phases:
57062306a36Sopenharmony_ci		list = data.dmesg[phase]['list']
57162306a36Sopenharmony_ci		for devname in sorted(list):
57262306a36Sopenharmony_ci			cls, color = colorForName(devname)
57362306a36Sopenharmony_ci			dev = list[devname]
57462306a36Sopenharmony_ci			info = '@|%.3f|%.3f|%.3f|%d' % (dev['start']*1000.0, dev['end']*1000.0,
57562306a36Sopenharmony_ci				dev['ulen']/1000.0, dev['ret'])
57662306a36Sopenharmony_ci			devstats[dev['id']] = {'info':info}
57762306a36Sopenharmony_ci			dev['color'] = color
57862306a36Sopenharmony_ci			height = devtl.phaseRowHeight(0, phase, dev['row'])
57962306a36Sopenharmony_ci			top = '%.6f' % ((dev['row']*height) + devtl.scaleH)
58062306a36Sopenharmony_ci			left = '%.6f' % (((dev['start']-t0)*100)/tTotal)
58162306a36Sopenharmony_ci			width = '%.6f' % (((dev['end']-dev['start'])*100)/tTotal)
58262306a36Sopenharmony_ci			length = ' (%0.3f ms) ' % ((dev['end']-dev['start'])*1000)
58362306a36Sopenharmony_ci			devtl.html += devtl.html_device.format(dev['id'],
58462306a36Sopenharmony_ci				devname+length+phase+'_mode', left, top, '%.3f'%height,
58562306a36Sopenharmony_ci				width, devname, ' '+cls, '')
58662306a36Sopenharmony_ci			rowtop = devtl.phaseRowTop(0, phase, dev['row'])
58762306a36Sopenharmony_ci			height = '%.6f' % (devtl.rowH / 2)
58862306a36Sopenharmony_ci			top = '%.6f' % (rowtop + devtl.scaleH + (devtl.rowH / 2))
58962306a36Sopenharmony_ci			if data.do_one_initcall:
59062306a36Sopenharmony_ci				if('ftrace' not in dev):
59162306a36Sopenharmony_ci					continue
59262306a36Sopenharmony_ci				cg = dev['ftrace']
59362306a36Sopenharmony_ci				large, stats = cgOverview(cg, 0.001)
59462306a36Sopenharmony_ci				devstats[dev['id']]['fstat'] = stats
59562306a36Sopenharmony_ci				for l in large:
59662306a36Sopenharmony_ci					left = '%f' % (((l.time-t0)*100)/tTotal)
59762306a36Sopenharmony_ci					width = '%f' % (l.length*100/tTotal)
59862306a36Sopenharmony_ci					title = '%s (%0.3fms)' % (l.name, l.length * 1000.0)
59962306a36Sopenharmony_ci					devtl.html += html_srccall.format(l.name, left,
60062306a36Sopenharmony_ci						top, height, width, title, 'x%d'%num)
60162306a36Sopenharmony_ci					num += 1
60262306a36Sopenharmony_ci				continue
60362306a36Sopenharmony_ci			if('ftraces' not in dev):
60462306a36Sopenharmony_ci				continue
60562306a36Sopenharmony_ci			for cg in dev['ftraces']:
60662306a36Sopenharmony_ci				left = '%f' % (((cg.start-t0)*100)/tTotal)
60762306a36Sopenharmony_ci				width = '%f' % ((cg.end-cg.start)*100/tTotal)
60862306a36Sopenharmony_ci				cglen = (cg.end - cg.start) * 1000.0
60962306a36Sopenharmony_ci				title = '%s (%0.3fms)' % (cg.name, cglen)
61062306a36Sopenharmony_ci				cg.id = 'x%d' % num
61162306a36Sopenharmony_ci				devtl.html += html_srccall.format(cg.name, left,
61262306a36Sopenharmony_ci					top, height, width, title, dev['id']+cg.id)
61362306a36Sopenharmony_ci				num += 1
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	# draw the time scale, try to make the number of labels readable
61662306a36Sopenharmony_ci	devtl.createTimeScale(t0, tMax, tTotal, 'boot')
61762306a36Sopenharmony_ci	devtl.html += '</div>\n'
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	# timeline is finished
62062306a36Sopenharmony_ci	devtl.html += '</div>\n</div>\n'
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	# draw a legend which describes the phases by color
62362306a36Sopenharmony_ci	devtl.html += '<div class="legend">\n'
62462306a36Sopenharmony_ci	pdelta = 20.0
62562306a36Sopenharmony_ci	pmargin = 36.0
62662306a36Sopenharmony_ci	for phase in data.phases:
62762306a36Sopenharmony_ci		order = '%.2f' % ((data.dmesg[phase]['order'] * pdelta) + pmargin)
62862306a36Sopenharmony_ci		devtl.html += devtl.html_legend.format(order, \
62962306a36Sopenharmony_ci			data.dmesg[phase]['color'], phase+'_mode', phase[0])
63062306a36Sopenharmony_ci	devtl.html += '</div>\n'
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	hf = open(sysvals.htmlfile, 'w')
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	# add the css
63562306a36Sopenharmony_ci	extra = '\
63662306a36Sopenharmony_ci		.c1 {background:rgba(209,0,0,0.4);}\n\
63762306a36Sopenharmony_ci		.c2 {background:rgba(255,102,34,0.4);}\n\
63862306a36Sopenharmony_ci		.c3 {background:rgba(255,218,33,0.4);}\n\
63962306a36Sopenharmony_ci		.c4 {background:rgba(51,221,0,0.4);}\n\
64062306a36Sopenharmony_ci		.c5 {background:rgba(17,51,204,0.4);}\n\
64162306a36Sopenharmony_ci		.c6 {background:rgba(34,0,102,0.4);}\n\
64262306a36Sopenharmony_ci		.c7 {background:rgba(51,0,68,0.4);}\n\
64362306a36Sopenharmony_ci		.c8 {background:rgba(204,255,204,0.4);}\n\
64462306a36Sopenharmony_ci		.c9 {background:rgba(169,208,245,0.4);}\n\
64562306a36Sopenharmony_ci		.c10 {background:rgba(255,255,204,0.4);}\n\
64662306a36Sopenharmony_ci		.vt {transform:rotate(-60deg);transform-origin:0 0;}\n\
64762306a36Sopenharmony_ci		table.fstat {table-layout:fixed;padding:150px 15px 0 0;font-size:10px;column-width:30px;}\n\
64862306a36Sopenharmony_ci		.fstat th {width:55px;}\n\
64962306a36Sopenharmony_ci		.fstat td {text-align:left;width:35px;}\n\
65062306a36Sopenharmony_ci		.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\
65162306a36Sopenharmony_ci		.srccall:hover {color:white;font-weight:bold;border:1px solid white;}\n'
65262306a36Sopenharmony_ci	aslib.addCSS(hf, sysvals, 1, False, extra)
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	# write the device timeline
65562306a36Sopenharmony_ci	hf.write(devtl.html)
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	# add boot specific html
65862306a36Sopenharmony_ci	statinfo = 'var devstats = {\n'
65962306a36Sopenharmony_ci	for n in sorted(devstats):
66062306a36Sopenharmony_ci		statinfo += '\t"%s": [\n\t\t"%s",\n' % (n, devstats[n]['info'])
66162306a36Sopenharmony_ci		if 'fstat' in devstats[n]:
66262306a36Sopenharmony_ci			funcs = devstats[n]['fstat']
66362306a36Sopenharmony_ci			for f in sorted(funcs, key=lambda k:(funcs[k], k), reverse=True):
66462306a36Sopenharmony_ci				if funcs[f][0] < 0.01 and len(funcs) > 10:
66562306a36Sopenharmony_ci					break
66662306a36Sopenharmony_ci				statinfo += '\t\t"%f|%s|%d",\n' % (funcs[f][0], f, funcs[f][1])
66762306a36Sopenharmony_ci		statinfo += '\t],\n'
66862306a36Sopenharmony_ci	statinfo += '};\n'
66962306a36Sopenharmony_ci	html = \
67062306a36Sopenharmony_ci		'<div id="devicedetailtitle"></div>\n'\
67162306a36Sopenharmony_ci		'<div id="devicedetail" style="display:none;">\n'\
67262306a36Sopenharmony_ci		'<div id="devicedetail0">\n'
67362306a36Sopenharmony_ci	for p in data.phases:
67462306a36Sopenharmony_ci		phase = data.dmesg[p]
67562306a36Sopenharmony_ci		html += devtl.html_phaselet.format(p+'_mode', '0', '100', phase['color'])
67662306a36Sopenharmony_ci	html += '</div>\n</div>\n'\
67762306a36Sopenharmony_ci		'<script type="text/javascript">\n'+statinfo+\
67862306a36Sopenharmony_ci		'</script>\n'
67962306a36Sopenharmony_ci	hf.write(html)
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	# add the callgraph html
68262306a36Sopenharmony_ci	if(sysvals.usecallgraph):
68362306a36Sopenharmony_ci		aslib.addCallgraphs(sysvals, hf, data)
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	# add the test log as a hidden div
68662306a36Sopenharmony_ci	if sysvals.testlog and sysvals.logmsg:
68762306a36Sopenharmony_ci		hf.write('<div id="testlog" style="display:none;">\n'+sysvals.logmsg+'</div>\n')
68862306a36Sopenharmony_ci	# add the dmesg log as a hidden div
68962306a36Sopenharmony_ci	if sysvals.dmesglog:
69062306a36Sopenharmony_ci		hf.write('<div id="dmesglog" style="display:none;">\n')
69162306a36Sopenharmony_ci		for line in data.dmesgtext:
69262306a36Sopenharmony_ci			line = line.replace('<', '&lt').replace('>', '&gt')
69362306a36Sopenharmony_ci			hf.write(line)
69462306a36Sopenharmony_ci		hf.write('</div>\n')
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	# write the footer and close
69762306a36Sopenharmony_ci	aslib.addScriptCode(hf, [data])
69862306a36Sopenharmony_ci	hf.write('</body>\n</html>\n')
69962306a36Sopenharmony_ci	hf.close()
70062306a36Sopenharmony_ci	return True
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci# Function: updateCron
70362306a36Sopenharmony_ci# Description:
70462306a36Sopenharmony_ci#    (restore=False) Set the tool to run automatically on reboot
70562306a36Sopenharmony_ci#    (restore=True) Restore the original crontab
70662306a36Sopenharmony_cidef updateCron(restore=False):
70762306a36Sopenharmony_ci	if not restore:
70862306a36Sopenharmony_ci		sysvals.rootUser(True)
70962306a36Sopenharmony_ci	crondir = '/var/spool/cron/crontabs/'
71062306a36Sopenharmony_ci	if not os.path.exists(crondir):
71162306a36Sopenharmony_ci		crondir = '/var/spool/cron/'
71262306a36Sopenharmony_ci	if not os.path.exists(crondir):
71362306a36Sopenharmony_ci		doError('%s not found' % crondir)
71462306a36Sopenharmony_ci	cronfile = crondir+'root'
71562306a36Sopenharmony_ci	backfile = crondir+'root-analyze_boot-backup'
71662306a36Sopenharmony_ci	cmd = sysvals.getExec('crontab')
71762306a36Sopenharmony_ci	if not cmd:
71862306a36Sopenharmony_ci		doError('crontab not found')
71962306a36Sopenharmony_ci	# on restore: move the backup cron back into place
72062306a36Sopenharmony_ci	if restore:
72162306a36Sopenharmony_ci		if os.path.exists(backfile):
72262306a36Sopenharmony_ci			shutil.move(backfile, cronfile)
72362306a36Sopenharmony_ci			call([cmd, cronfile])
72462306a36Sopenharmony_ci		return
72562306a36Sopenharmony_ci	# backup current cron and install new one with reboot
72662306a36Sopenharmony_ci	if os.path.exists(cronfile):
72762306a36Sopenharmony_ci		shutil.move(cronfile, backfile)
72862306a36Sopenharmony_ci	else:
72962306a36Sopenharmony_ci		fp = open(backfile, 'w')
73062306a36Sopenharmony_ci		fp.close()
73162306a36Sopenharmony_ci	res = -1
73262306a36Sopenharmony_ci	try:
73362306a36Sopenharmony_ci		fp = open(backfile, 'r')
73462306a36Sopenharmony_ci		op = open(cronfile, 'w')
73562306a36Sopenharmony_ci		for line in fp:
73662306a36Sopenharmony_ci			if not sysvals.myCronJob(line):
73762306a36Sopenharmony_ci				op.write(line)
73862306a36Sopenharmony_ci				continue
73962306a36Sopenharmony_ci		fp.close()
74062306a36Sopenharmony_ci		op.write('@reboot python %s\n' % sysvals.cronjobCmdString())
74162306a36Sopenharmony_ci		op.close()
74262306a36Sopenharmony_ci		res = call([cmd, cronfile])
74362306a36Sopenharmony_ci	except Exception as e:
74462306a36Sopenharmony_ci		pprint('Exception: %s' % str(e))
74562306a36Sopenharmony_ci		shutil.move(backfile, cronfile)
74662306a36Sopenharmony_ci		res = -1
74762306a36Sopenharmony_ci	if res != 0:
74862306a36Sopenharmony_ci		doError('crontab failed')
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci# Function: updateGrub
75162306a36Sopenharmony_ci# Description:
75262306a36Sopenharmony_ci#	 update grub.cfg for all kernels with our parameters
75362306a36Sopenharmony_cidef updateGrub(restore=False):
75462306a36Sopenharmony_ci	# call update-grub on restore
75562306a36Sopenharmony_ci	if restore:
75662306a36Sopenharmony_ci		try:
75762306a36Sopenharmony_ci			call(sysvals.blexec, stderr=PIPE, stdout=PIPE,
75862306a36Sopenharmony_ci				env={'PATH': '.:/sbin:/usr/sbin:/usr/bin:/sbin:/bin'})
75962306a36Sopenharmony_ci		except Exception as e:
76062306a36Sopenharmony_ci			pprint('Exception: %s\n' % str(e))
76162306a36Sopenharmony_ci		return
76262306a36Sopenharmony_ci	# extract the option and create a grub config without it
76362306a36Sopenharmony_ci	sysvals.rootUser(True)
76462306a36Sopenharmony_ci	tgtopt = 'GRUB_CMDLINE_LINUX_DEFAULT'
76562306a36Sopenharmony_ci	cmdline = ''
76662306a36Sopenharmony_ci	grubfile = '/etc/default/grub'
76762306a36Sopenharmony_ci	tempfile = '/etc/default/grub.analyze_boot'
76862306a36Sopenharmony_ci	shutil.move(grubfile, tempfile)
76962306a36Sopenharmony_ci	res = -1
77062306a36Sopenharmony_ci	try:
77162306a36Sopenharmony_ci		fp = open(tempfile, 'r')
77262306a36Sopenharmony_ci		op = open(grubfile, 'w')
77362306a36Sopenharmony_ci		cont = False
77462306a36Sopenharmony_ci		for line in fp:
77562306a36Sopenharmony_ci			line = line.strip()
77662306a36Sopenharmony_ci			if len(line) == 0 or line[0] == '#':
77762306a36Sopenharmony_ci				continue
77862306a36Sopenharmony_ci			opt = line.split('=')[0].strip()
77962306a36Sopenharmony_ci			if opt == tgtopt:
78062306a36Sopenharmony_ci				cmdline = line.split('=', 1)[1].strip('\\')
78162306a36Sopenharmony_ci				if line[-1] == '\\':
78262306a36Sopenharmony_ci					cont = True
78362306a36Sopenharmony_ci			elif cont:
78462306a36Sopenharmony_ci				cmdline += line.strip('\\')
78562306a36Sopenharmony_ci				if line[-1] != '\\':
78662306a36Sopenharmony_ci					cont = False
78762306a36Sopenharmony_ci			else:
78862306a36Sopenharmony_ci				op.write('%s\n' % line)
78962306a36Sopenharmony_ci		fp.close()
79062306a36Sopenharmony_ci		# if the target option value is in quotes, strip them
79162306a36Sopenharmony_ci		sp = '"'
79262306a36Sopenharmony_ci		val = cmdline.strip()
79362306a36Sopenharmony_ci		if val and (val[0] == '\'' or val[0] == '"'):
79462306a36Sopenharmony_ci			sp = val[0]
79562306a36Sopenharmony_ci			val = val.strip(sp)
79662306a36Sopenharmony_ci		cmdline = val
79762306a36Sopenharmony_ci		# append our cmd line options
79862306a36Sopenharmony_ci		if len(cmdline) > 0:
79962306a36Sopenharmony_ci			cmdline += ' '
80062306a36Sopenharmony_ci		cmdline += sysvals.kernelParams()
80162306a36Sopenharmony_ci		# write out the updated target option
80262306a36Sopenharmony_ci		op.write('\n%s=%s%s%s\n' % (tgtopt, sp, cmdline, sp))
80362306a36Sopenharmony_ci		op.close()
80462306a36Sopenharmony_ci		res = call(sysvals.blexec)
80562306a36Sopenharmony_ci		os.remove(grubfile)
80662306a36Sopenharmony_ci	except Exception as e:
80762306a36Sopenharmony_ci		pprint('Exception: %s' % str(e))
80862306a36Sopenharmony_ci		res = -1
80962306a36Sopenharmony_ci	# cleanup
81062306a36Sopenharmony_ci	shutil.move(tempfile, grubfile)
81162306a36Sopenharmony_ci	if res != 0:
81262306a36Sopenharmony_ci		doError('update grub failed')
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci# Function: updateKernelParams
81562306a36Sopenharmony_ci# Description:
81662306a36Sopenharmony_ci#	 update boot conf for all kernels with our parameters
81762306a36Sopenharmony_cidef updateKernelParams(restore=False):
81862306a36Sopenharmony_ci	# find the boot loader
81962306a36Sopenharmony_ci	sysvals.getBootLoader()
82062306a36Sopenharmony_ci	if sysvals.bootloader == 'grub':
82162306a36Sopenharmony_ci		updateGrub(restore)
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci# Function: doError Description:
82462306a36Sopenharmony_ci#	 generic error function for catastrphic failures
82562306a36Sopenharmony_ci# Arguments:
82662306a36Sopenharmony_ci#	 msg: the error message to print
82762306a36Sopenharmony_ci#	 help: True if printHelp should be called after, False otherwise
82862306a36Sopenharmony_cidef doError(msg, help=False):
82962306a36Sopenharmony_ci	if help == True:
83062306a36Sopenharmony_ci		printHelp()
83162306a36Sopenharmony_ci	pprint('ERROR: %s\n' % msg)
83262306a36Sopenharmony_ci	sysvals.outputResult({'error':msg})
83362306a36Sopenharmony_ci	sys.exit()
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci# Function: printHelp
83662306a36Sopenharmony_ci# Description:
83762306a36Sopenharmony_ci#	 print out the help text
83862306a36Sopenharmony_cidef printHelp():
83962306a36Sopenharmony_ci	pprint('\n%s v%s\n'\
84062306a36Sopenharmony_ci	'Usage: bootgraph <options> <command>\n'\
84162306a36Sopenharmony_ci	'\n'\
84262306a36Sopenharmony_ci	'Description:\n'\
84362306a36Sopenharmony_ci	'  This tool reads in a dmesg log of linux kernel boot and\n'\
84462306a36Sopenharmony_ci	'  creates an html representation of the boot timeline up to\n'\
84562306a36Sopenharmony_ci	'  the start of the init process.\n'\
84662306a36Sopenharmony_ci	'\n'\
84762306a36Sopenharmony_ci	'  If no specific command is given the tool reads the current dmesg\n'\
84862306a36Sopenharmony_ci	'  and/or ftrace log and creates a timeline\n'\
84962306a36Sopenharmony_ci	'\n'\
85062306a36Sopenharmony_ci	'  Generates output files in subdirectory: boot-yymmdd-HHMMSS\n'\
85162306a36Sopenharmony_ci	'   HTML output:                    <hostname>_boot.html\n'\
85262306a36Sopenharmony_ci	'   raw dmesg output:               <hostname>_boot_dmesg.txt\n'\
85362306a36Sopenharmony_ci	'   raw ftrace output:              <hostname>_boot_ftrace.txt\n'\
85462306a36Sopenharmony_ci	'\n'\
85562306a36Sopenharmony_ci	'Options:\n'\
85662306a36Sopenharmony_ci	'  -h            Print this help text\n'\
85762306a36Sopenharmony_ci	'  -v            Print the current tool version\n'\
85862306a36Sopenharmony_ci	'  -verbose      Print extra information during execution and analysis\n'\
85962306a36Sopenharmony_ci	'  -addlogs      Add the dmesg log to the html output\n'\
86062306a36Sopenharmony_ci	'  -result fn    Export a results table to a text file for parsing.\n'\
86162306a36Sopenharmony_ci	'  -o name       Overrides the output subdirectory name when running a new test\n'\
86262306a36Sopenharmony_ci	'                default: boot-{date}-{time}\n'\
86362306a36Sopenharmony_ci	' [advanced]\n'\
86462306a36Sopenharmony_ci	'  -fstat        Use ftrace to add function detail and statistics (default: disabled)\n'\
86562306a36Sopenharmony_ci	'  -f/-callgraph Add callgraph detail, can be very large (default: disabled)\n'\
86662306a36Sopenharmony_ci	'  -maxdepth N   limit the callgraph data to N call levels (default: 2)\n'\
86762306a36Sopenharmony_ci	'  -mincg ms     Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)\n'\
86862306a36Sopenharmony_ci	'  -timeprec N   Number of significant digits in timestamps (0:S, 3:ms, [6:us])\n'\
86962306a36Sopenharmony_ci	'  -expandcg     pre-expand the callgraph data in the html output (default: disabled)\n'\
87062306a36Sopenharmony_ci	'  -func list    Limit ftrace to comma-delimited list of functions (default: do_one_initcall)\n'\
87162306a36Sopenharmony_ci	'  -cgfilter S   Filter the callgraph output in the timeline\n'\
87262306a36Sopenharmony_ci	'  -cgskip file  Callgraph functions to skip, off to disable (default: cgskip.txt)\n'\
87362306a36Sopenharmony_ci	'  -bl name      Use the following boot loader for kernel params (default: grub)\n'\
87462306a36Sopenharmony_ci	'  -reboot       Reboot the machine automatically and generate a new timeline\n'\
87562306a36Sopenharmony_ci	'  -manual       Show the steps to generate a new timeline manually (used with -reboot)\n'\
87662306a36Sopenharmony_ci	'\n'\
87762306a36Sopenharmony_ci	'Other commands:\n'\
87862306a36Sopenharmony_ci	'  -flistall     Print all functions capable of being captured in ftrace\n'\
87962306a36Sopenharmony_ci	'  -sysinfo      Print out system info extracted from BIOS\n'\
88062306a36Sopenharmony_ci	'  -which exec   Print an executable path, should function even without PATH\n'\
88162306a36Sopenharmony_ci	' [redo]\n'\
88262306a36Sopenharmony_ci	'  -dmesg file   Create HTML output using dmesg input (used with -ftrace)\n'\
88362306a36Sopenharmony_ci	'  -ftrace file  Create HTML output using ftrace input (used with -dmesg)\n'\
88462306a36Sopenharmony_ci	'' % (sysvals.title, sysvals.version))
88562306a36Sopenharmony_ci	return True
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci# ----------------- MAIN --------------------
88862306a36Sopenharmony_ci# exec start (skipped if script is loaded as library)
88962306a36Sopenharmony_ciif __name__ == '__main__':
89062306a36Sopenharmony_ci	# loop through the command line arguments
89162306a36Sopenharmony_ci	cmd = ''
89262306a36Sopenharmony_ci	testrun = True
89362306a36Sopenharmony_ci	switchoff = ['disable', 'off', 'false', '0']
89462306a36Sopenharmony_ci	simplecmds = ['-sysinfo', '-kpupdate', '-flistall', '-checkbl']
89562306a36Sopenharmony_ci	cgskip = ''
89662306a36Sopenharmony_ci	if '-f' in sys.argv:
89762306a36Sopenharmony_ci		cgskip = sysvals.configFile('cgskip.txt')
89862306a36Sopenharmony_ci	args = iter(sys.argv[1:])
89962306a36Sopenharmony_ci	mdset = False
90062306a36Sopenharmony_ci	for arg in args:
90162306a36Sopenharmony_ci		if(arg == '-h'):
90262306a36Sopenharmony_ci			printHelp()
90362306a36Sopenharmony_ci			sys.exit()
90462306a36Sopenharmony_ci		elif(arg == '-v'):
90562306a36Sopenharmony_ci			pprint("Version %s" % sysvals.version)
90662306a36Sopenharmony_ci			sys.exit()
90762306a36Sopenharmony_ci		elif(arg == '-verbose'):
90862306a36Sopenharmony_ci			sysvals.verbose = True
90962306a36Sopenharmony_ci		elif(arg in simplecmds):
91062306a36Sopenharmony_ci			cmd = arg[1:]
91162306a36Sopenharmony_ci		elif(arg == '-fstat'):
91262306a36Sopenharmony_ci			sysvals.useftrace = True
91362306a36Sopenharmony_ci		elif(arg == '-callgraph' or arg == '-f'):
91462306a36Sopenharmony_ci			sysvals.useftrace = True
91562306a36Sopenharmony_ci			sysvals.usecallgraph = True
91662306a36Sopenharmony_ci		elif(arg == '-cgdump'):
91762306a36Sopenharmony_ci			sysvals.cgdump = True
91862306a36Sopenharmony_ci		elif(arg == '-mincg'):
91962306a36Sopenharmony_ci			sysvals.mincglen = aslib.getArgFloat('-mincg', args, 0.0, 10000.0)
92062306a36Sopenharmony_ci		elif(arg == '-cgfilter'):
92162306a36Sopenharmony_ci			try:
92262306a36Sopenharmony_ci				val = next(args)
92362306a36Sopenharmony_ci			except:
92462306a36Sopenharmony_ci				doError('No callgraph functions supplied', True)
92562306a36Sopenharmony_ci			sysvals.setCallgraphFilter(val)
92662306a36Sopenharmony_ci		elif(arg == '-cgskip'):
92762306a36Sopenharmony_ci			try:
92862306a36Sopenharmony_ci				val = next(args)
92962306a36Sopenharmony_ci			except:
93062306a36Sopenharmony_ci				doError('No file supplied', True)
93162306a36Sopenharmony_ci			if val.lower() in switchoff:
93262306a36Sopenharmony_ci				cgskip = ''
93362306a36Sopenharmony_ci			else:
93462306a36Sopenharmony_ci				cgskip = sysvals.configFile(val)
93562306a36Sopenharmony_ci				if(not cgskip):
93662306a36Sopenharmony_ci					doError('%s does not exist' % cgskip)
93762306a36Sopenharmony_ci		elif(arg == '-bl'):
93862306a36Sopenharmony_ci			try:
93962306a36Sopenharmony_ci				val = next(args)
94062306a36Sopenharmony_ci			except:
94162306a36Sopenharmony_ci				doError('No boot loader name supplied', True)
94262306a36Sopenharmony_ci			if val.lower() not in ['grub']:
94362306a36Sopenharmony_ci				doError('Unknown boot loader: %s' % val, True)
94462306a36Sopenharmony_ci			sysvals.bootloader = val.lower()
94562306a36Sopenharmony_ci		elif(arg == '-timeprec'):
94662306a36Sopenharmony_ci			sysvals.setPrecision(aslib.getArgInt('-timeprec', args, 0, 6))
94762306a36Sopenharmony_ci		elif(arg == '-maxdepth'):
94862306a36Sopenharmony_ci			mdset = True
94962306a36Sopenharmony_ci			sysvals.max_graph_depth = aslib.getArgInt('-maxdepth', args, 0, 1000)
95062306a36Sopenharmony_ci		elif(arg == '-func'):
95162306a36Sopenharmony_ci			try:
95262306a36Sopenharmony_ci				val = next(args)
95362306a36Sopenharmony_ci			except:
95462306a36Sopenharmony_ci				doError('No filter functions supplied', True)
95562306a36Sopenharmony_ci			sysvals.useftrace = True
95662306a36Sopenharmony_ci			sysvals.usecallgraph = True
95762306a36Sopenharmony_ci			sysvals.rootCheck(True)
95862306a36Sopenharmony_ci			sysvals.setGraphFilter(val)
95962306a36Sopenharmony_ci		elif(arg == '-ftrace'):
96062306a36Sopenharmony_ci			try:
96162306a36Sopenharmony_ci				val = next(args)
96262306a36Sopenharmony_ci			except:
96362306a36Sopenharmony_ci				doError('No ftrace file supplied', True)
96462306a36Sopenharmony_ci			if(os.path.exists(val) == False):
96562306a36Sopenharmony_ci				doError('%s does not exist' % val)
96662306a36Sopenharmony_ci			testrun = False
96762306a36Sopenharmony_ci			sysvals.ftracefile = val
96862306a36Sopenharmony_ci		elif(arg == '-addlogs'):
96962306a36Sopenharmony_ci			sysvals.dmesglog = True
97062306a36Sopenharmony_ci		elif(arg == '-expandcg'):
97162306a36Sopenharmony_ci			sysvals.cgexp = True
97262306a36Sopenharmony_ci		elif(arg == '-dmesg'):
97362306a36Sopenharmony_ci			try:
97462306a36Sopenharmony_ci				val = next(args)
97562306a36Sopenharmony_ci			except:
97662306a36Sopenharmony_ci				doError('No dmesg file supplied', True)
97762306a36Sopenharmony_ci			if(os.path.exists(val) == False):
97862306a36Sopenharmony_ci				doError('%s does not exist' % val)
97962306a36Sopenharmony_ci			testrun = False
98062306a36Sopenharmony_ci			sysvals.dmesgfile = val
98162306a36Sopenharmony_ci		elif(arg == '-o'):
98262306a36Sopenharmony_ci			try:
98362306a36Sopenharmony_ci				val = next(args)
98462306a36Sopenharmony_ci			except:
98562306a36Sopenharmony_ci				doError('No subdirectory name supplied', True)
98662306a36Sopenharmony_ci			sysvals.testdir = sysvals.setOutputFolder(val)
98762306a36Sopenharmony_ci		elif(arg == '-result'):
98862306a36Sopenharmony_ci			try:
98962306a36Sopenharmony_ci				val = next(args)
99062306a36Sopenharmony_ci			except:
99162306a36Sopenharmony_ci				doError('No result file supplied', True)
99262306a36Sopenharmony_ci			sysvals.result = val
99362306a36Sopenharmony_ci		elif(arg == '-reboot'):
99462306a36Sopenharmony_ci			sysvals.reboot = True
99562306a36Sopenharmony_ci		elif(arg == '-manual'):
99662306a36Sopenharmony_ci			sysvals.reboot = True
99762306a36Sopenharmony_ci			sysvals.manual = True
99862306a36Sopenharmony_ci		# remaining options are only for cron job use
99962306a36Sopenharmony_ci		elif(arg == '-cronjob'):
100062306a36Sopenharmony_ci			sysvals.iscronjob = True
100162306a36Sopenharmony_ci		elif(arg == '-which'):
100262306a36Sopenharmony_ci			try:
100362306a36Sopenharmony_ci				val = next(args)
100462306a36Sopenharmony_ci			except:
100562306a36Sopenharmony_ci				doError('No executable supplied', True)
100662306a36Sopenharmony_ci			out = sysvals.getExec(val)
100762306a36Sopenharmony_ci			if not out:
100862306a36Sopenharmony_ci				print('%s not found' % val)
100962306a36Sopenharmony_ci				sys.exit(1)
101062306a36Sopenharmony_ci			print(out)
101162306a36Sopenharmony_ci			sys.exit(0)
101262306a36Sopenharmony_ci		else:
101362306a36Sopenharmony_ci			doError('Invalid argument: '+arg, True)
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	# compatibility errors and access checks
101662306a36Sopenharmony_ci	if(sysvals.iscronjob and (sysvals.reboot or \
101762306a36Sopenharmony_ci		sysvals.dmesgfile or sysvals.ftracefile or cmd)):
101862306a36Sopenharmony_ci		doError('-cronjob is meant for batch purposes only')
101962306a36Sopenharmony_ci	if(sysvals.reboot and (sysvals.dmesgfile or sysvals.ftracefile)):
102062306a36Sopenharmony_ci		doError('-reboot and -dmesg/-ftrace are incompatible')
102162306a36Sopenharmony_ci	if cmd or sysvals.reboot or sysvals.iscronjob or testrun:
102262306a36Sopenharmony_ci		sysvals.rootCheck(True)
102362306a36Sopenharmony_ci	if (testrun and sysvals.useftrace) or cmd == 'flistall':
102462306a36Sopenharmony_ci		if not sysvals.verifyFtrace():
102562306a36Sopenharmony_ci			doError('Ftrace is not properly enabled')
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	# run utility commands
102862306a36Sopenharmony_ci	sysvals.cpuInfo()
102962306a36Sopenharmony_ci	if cmd != '':
103062306a36Sopenharmony_ci		if cmd == 'kpupdate':
103162306a36Sopenharmony_ci			updateKernelParams()
103262306a36Sopenharmony_ci		elif cmd == 'flistall':
103362306a36Sopenharmony_ci			for f in sysvals.getBootFtraceFilterFunctions():
103462306a36Sopenharmony_ci				print(f)
103562306a36Sopenharmony_ci		elif cmd == 'checkbl':
103662306a36Sopenharmony_ci			sysvals.getBootLoader()
103762306a36Sopenharmony_ci			pprint('Boot Loader: %s\n%s' % (sysvals.bootloader, sysvals.blexec))
103862306a36Sopenharmony_ci		elif(cmd == 'sysinfo'):
103962306a36Sopenharmony_ci			sysvals.printSystemInfo(True)
104062306a36Sopenharmony_ci		sys.exit()
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	# reboot: update grub, setup a cronjob, and reboot
104362306a36Sopenharmony_ci	if sysvals.reboot:
104462306a36Sopenharmony_ci		if (sysvals.useftrace or sysvals.usecallgraph) and \
104562306a36Sopenharmony_ci			not sysvals.checkFtraceKernelVersion():
104662306a36Sopenharmony_ci			doError('Ftrace functionality requires kernel v4.10 or newer')
104762306a36Sopenharmony_ci		if not sysvals.manual:
104862306a36Sopenharmony_ci			updateKernelParams()
104962306a36Sopenharmony_ci			updateCron()
105062306a36Sopenharmony_ci			call('reboot')
105162306a36Sopenharmony_ci		else:
105262306a36Sopenharmony_ci			sysvals.manualRebootRequired()
105362306a36Sopenharmony_ci		sys.exit()
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	if sysvals.usecallgraph and cgskip:
105662306a36Sopenharmony_ci		sysvals.vprint('Using cgskip file: %s' % cgskip)
105762306a36Sopenharmony_ci		sysvals.setCallgraphBlacklist(cgskip)
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	# cronjob: remove the cronjob, grub changes, and disable ftrace
106062306a36Sopenharmony_ci	if sysvals.iscronjob:
106162306a36Sopenharmony_ci		updateCron(True)
106262306a36Sopenharmony_ci		updateKernelParams(True)
106362306a36Sopenharmony_ci		try:
106462306a36Sopenharmony_ci			sysvals.fsetVal('0', 'tracing_on')
106562306a36Sopenharmony_ci		except:
106662306a36Sopenharmony_ci			pass
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	# testrun: generate copies of the logs
106962306a36Sopenharmony_ci	if testrun:
107062306a36Sopenharmony_ci		retrieveLogs()
107162306a36Sopenharmony_ci	else:
107262306a36Sopenharmony_ci		sysvals.setOutputFile()
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	# process the log data
107562306a36Sopenharmony_ci	if sysvals.dmesgfile:
107662306a36Sopenharmony_ci		if not mdset:
107762306a36Sopenharmony_ci			sysvals.max_graph_depth = 0
107862306a36Sopenharmony_ci		data = parseKernelLog()
107962306a36Sopenharmony_ci		if(not data.valid):
108062306a36Sopenharmony_ci			doError('No initcall data found in %s' % sysvals.dmesgfile)
108162306a36Sopenharmony_ci		if sysvals.useftrace and sysvals.ftracefile:
108262306a36Sopenharmony_ci			parseTraceLog(data)
108362306a36Sopenharmony_ci		if sysvals.cgdump:
108462306a36Sopenharmony_ci			data.debugPrint()
108562306a36Sopenharmony_ci			sys.exit()
108662306a36Sopenharmony_ci	else:
108762306a36Sopenharmony_ci		doError('dmesg file required')
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	sysvals.vprint('Creating the html timeline (%s)...' % sysvals.htmlfile)
109062306a36Sopenharmony_ci	sysvals.vprint('Command:\n    %s' % sysvals.cmdline)
109162306a36Sopenharmony_ci	sysvals.vprint('Kernel parameters:\n    %s' % sysvals.kparams)
109262306a36Sopenharmony_ci	data.printDetails()
109362306a36Sopenharmony_ci	createBootGraph(data)
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	# if running as root, change output dir owner to sudo_user
109662306a36Sopenharmony_ci	if testrun and os.path.isdir(sysvals.testdir) and \
109762306a36Sopenharmony_ci		os.getuid() == 0 and 'SUDO_USER' in os.environ:
109862306a36Sopenharmony_ci		cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1'
109962306a36Sopenharmony_ci		call(cmd.format(os.environ['SUDO_USER'], sysvals.testdir), shell=True)
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	sysvals.stamp['boot'] = (data.tUserMode - data.start) * 1000
110262306a36Sopenharmony_ci	sysvals.stamp['lastinit'] = data.end * 1000
110362306a36Sopenharmony_ci	sysvals.outputResult(sysvals.stamp)
1104