18c2ecf20Sopenharmony_ci#!/usr/bin/env python3
28c2ecf20Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0-only
38c2ecf20Sopenharmony_ci#
48c2ecf20Sopenharmony_ci# Tool for analyzing suspend/resume timing
58c2ecf20Sopenharmony_ci# Copyright (c) 2013, Intel Corporation.
68c2ecf20Sopenharmony_ci#
78c2ecf20Sopenharmony_ci# This program is free software; you can redistribute it and/or modify it
88c2ecf20Sopenharmony_ci# under the terms and conditions of the GNU General Public License,
98c2ecf20Sopenharmony_ci# version 2, as published by the Free Software Foundation.
108c2ecf20Sopenharmony_ci#
118c2ecf20Sopenharmony_ci# This program is distributed in the hope it will be useful, but WITHOUT
128c2ecf20Sopenharmony_ci# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
138c2ecf20Sopenharmony_ci# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
148c2ecf20Sopenharmony_ci# more details.
158c2ecf20Sopenharmony_ci#
168c2ecf20Sopenharmony_ci# Authors:
178c2ecf20Sopenharmony_ci#	 Todd Brandt <todd.e.brandt@linux.intel.com>
188c2ecf20Sopenharmony_ci#
198c2ecf20Sopenharmony_ci# Links:
208c2ecf20Sopenharmony_ci#	 Home Page
218c2ecf20Sopenharmony_ci#	   https://01.org/pm-graph
228c2ecf20Sopenharmony_ci#	 Source repo
238c2ecf20Sopenharmony_ci#	   git@github.com:intel/pm-graph
248c2ecf20Sopenharmony_ci#
258c2ecf20Sopenharmony_ci# Description:
268c2ecf20Sopenharmony_ci#	 This tool is designed to assist kernel and OS developers in optimizing
278c2ecf20Sopenharmony_ci#	 their linux stack's suspend/resume time. Using a kernel image built
288c2ecf20Sopenharmony_ci#	 with a few extra options enabled, the tool will execute a suspend and
298c2ecf20Sopenharmony_ci#	 will capture dmesg and ftrace data until resume is complete. This data
308c2ecf20Sopenharmony_ci#	 is transformed into a device timeline and a callgraph to give a quick
318c2ecf20Sopenharmony_ci#	 and detailed view of which devices and callbacks are taking the most
328c2ecf20Sopenharmony_ci#	 time in suspend/resume. The output is a single html file which can be
338c2ecf20Sopenharmony_ci#	 viewed in firefox or chrome.
348c2ecf20Sopenharmony_ci#
358c2ecf20Sopenharmony_ci#	 The following kernel build options are required:
368c2ecf20Sopenharmony_ci#		 CONFIG_DEVMEM=y
378c2ecf20Sopenharmony_ci#		 CONFIG_PM_DEBUG=y
388c2ecf20Sopenharmony_ci#		 CONFIG_PM_SLEEP_DEBUG=y
398c2ecf20Sopenharmony_ci#		 CONFIG_FTRACE=y
408c2ecf20Sopenharmony_ci#		 CONFIG_FUNCTION_TRACER=y
418c2ecf20Sopenharmony_ci#		 CONFIG_FUNCTION_GRAPH_TRACER=y
428c2ecf20Sopenharmony_ci#		 CONFIG_KPROBES=y
438c2ecf20Sopenharmony_ci#		 CONFIG_KPROBES_ON_FTRACE=y
448c2ecf20Sopenharmony_ci#
458c2ecf20Sopenharmony_ci#	 For kernel versions older than 3.15:
468c2ecf20Sopenharmony_ci#	 The following additional kernel parameters are required:
478c2ecf20Sopenharmony_ci#		 (e.g. in file /etc/default/grub)
488c2ecf20Sopenharmony_ci#		 GRUB_CMDLINE_LINUX_DEFAULT="... initcall_debug log_buf_len=16M ..."
498c2ecf20Sopenharmony_ci#
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci# ----------------- LIBRARIES --------------------
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ciimport sys
548c2ecf20Sopenharmony_ciimport time
558c2ecf20Sopenharmony_ciimport os
568c2ecf20Sopenharmony_ciimport string
578c2ecf20Sopenharmony_ciimport re
588c2ecf20Sopenharmony_ciimport platform
598c2ecf20Sopenharmony_ciimport signal
608c2ecf20Sopenharmony_ciimport codecs
618c2ecf20Sopenharmony_cifrom datetime import datetime, timedelta
628c2ecf20Sopenharmony_ciimport struct
638c2ecf20Sopenharmony_ciimport configparser
648c2ecf20Sopenharmony_ciimport gzip
658c2ecf20Sopenharmony_cifrom threading import Thread
668c2ecf20Sopenharmony_cifrom subprocess import call, Popen, PIPE
678c2ecf20Sopenharmony_ciimport base64
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cidef pprint(msg):
708c2ecf20Sopenharmony_ci	print(msg)
718c2ecf20Sopenharmony_ci	sys.stdout.flush()
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cidef ascii(text):
748c2ecf20Sopenharmony_ci	return text.decode('ascii', 'ignore')
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci# ----------------- CLASSES --------------------
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci# Class: SystemValues
798c2ecf20Sopenharmony_ci# Description:
808c2ecf20Sopenharmony_ci#	 A global, single-instance container used to
818c2ecf20Sopenharmony_ci#	 store system values and test parameters
828c2ecf20Sopenharmony_ciclass SystemValues:
838c2ecf20Sopenharmony_ci	title = 'SleepGraph'
848c2ecf20Sopenharmony_ci	version = '5.7'
858c2ecf20Sopenharmony_ci	ansi = False
868c2ecf20Sopenharmony_ci	rs = 0
878c2ecf20Sopenharmony_ci	display = ''
888c2ecf20Sopenharmony_ci	gzip = False
898c2ecf20Sopenharmony_ci	sync = False
908c2ecf20Sopenharmony_ci	wifi = False
918c2ecf20Sopenharmony_ci	verbose = False
928c2ecf20Sopenharmony_ci	testlog = True
938c2ecf20Sopenharmony_ci	dmesglog = True
948c2ecf20Sopenharmony_ci	ftracelog = False
958c2ecf20Sopenharmony_ci	tstat = True
968c2ecf20Sopenharmony_ci	mindevlen = 0.0
978c2ecf20Sopenharmony_ci	mincglen = 0.0
988c2ecf20Sopenharmony_ci	cgphase = ''
998c2ecf20Sopenharmony_ci	cgtest = -1
1008c2ecf20Sopenharmony_ci	cgskip = ''
1018c2ecf20Sopenharmony_ci	maxfail = 0
1028c2ecf20Sopenharmony_ci	multitest = {'run': False, 'count': 1000000, 'delay': 0}
1038c2ecf20Sopenharmony_ci	max_graph_depth = 0
1048c2ecf20Sopenharmony_ci	callloopmaxgap = 0.0001
1058c2ecf20Sopenharmony_ci	callloopmaxlen = 0.005
1068c2ecf20Sopenharmony_ci	bufsize = 0
1078c2ecf20Sopenharmony_ci	cpucount = 0
1088c2ecf20Sopenharmony_ci	memtotal = 204800
1098c2ecf20Sopenharmony_ci	memfree = 204800
1108c2ecf20Sopenharmony_ci	srgap = 0
1118c2ecf20Sopenharmony_ci	cgexp = False
1128c2ecf20Sopenharmony_ci	testdir = ''
1138c2ecf20Sopenharmony_ci	outdir = ''
1148c2ecf20Sopenharmony_ci	tpath = '/sys/kernel/debug/tracing/'
1158c2ecf20Sopenharmony_ci	fpdtpath = '/sys/firmware/acpi/tables/FPDT'
1168c2ecf20Sopenharmony_ci	epath = '/sys/kernel/debug/tracing/events/power/'
1178c2ecf20Sopenharmony_ci	pmdpath = '/sys/power/pm_debug_messages'
1188c2ecf20Sopenharmony_ci	traceevents = [
1198c2ecf20Sopenharmony_ci		'suspend_resume',
1208c2ecf20Sopenharmony_ci		'wakeup_source_activate',
1218c2ecf20Sopenharmony_ci		'wakeup_source_deactivate',
1228c2ecf20Sopenharmony_ci		'device_pm_callback_end',
1238c2ecf20Sopenharmony_ci		'device_pm_callback_start'
1248c2ecf20Sopenharmony_ci	]
1258c2ecf20Sopenharmony_ci	logmsg = ''
1268c2ecf20Sopenharmony_ci	testcommand = ''
1278c2ecf20Sopenharmony_ci	mempath = '/dev/mem'
1288c2ecf20Sopenharmony_ci	powerfile = '/sys/power/state'
1298c2ecf20Sopenharmony_ci	mempowerfile = '/sys/power/mem_sleep'
1308c2ecf20Sopenharmony_ci	diskpowerfile = '/sys/power/disk'
1318c2ecf20Sopenharmony_ci	suspendmode = 'mem'
1328c2ecf20Sopenharmony_ci	memmode = ''
1338c2ecf20Sopenharmony_ci	diskmode = ''
1348c2ecf20Sopenharmony_ci	hostname = 'localhost'
1358c2ecf20Sopenharmony_ci	prefix = 'test'
1368c2ecf20Sopenharmony_ci	teststamp = ''
1378c2ecf20Sopenharmony_ci	sysstamp = ''
1388c2ecf20Sopenharmony_ci	dmesgstart = 0.0
1398c2ecf20Sopenharmony_ci	dmesgfile = ''
1408c2ecf20Sopenharmony_ci	ftracefile = ''
1418c2ecf20Sopenharmony_ci	htmlfile = 'output.html'
1428c2ecf20Sopenharmony_ci	result = ''
1438c2ecf20Sopenharmony_ci	rtcwake = True
1448c2ecf20Sopenharmony_ci	rtcwaketime = 15
1458c2ecf20Sopenharmony_ci	rtcpath = ''
1468c2ecf20Sopenharmony_ci	devicefilter = []
1478c2ecf20Sopenharmony_ci	cgfilter = []
1488c2ecf20Sopenharmony_ci	stamp = 0
1498c2ecf20Sopenharmony_ci	execcount = 1
1508c2ecf20Sopenharmony_ci	x2delay = 0
1518c2ecf20Sopenharmony_ci	skiphtml = False
1528c2ecf20Sopenharmony_ci	usecallgraph = False
1538c2ecf20Sopenharmony_ci	ftopfunc = 'pm_suspend'
1548c2ecf20Sopenharmony_ci	ftop = False
1558c2ecf20Sopenharmony_ci	usetraceevents = False
1568c2ecf20Sopenharmony_ci	usetracemarkers = True
1578c2ecf20Sopenharmony_ci	usekprobes = True
1588c2ecf20Sopenharmony_ci	usedevsrc = False
1598c2ecf20Sopenharmony_ci	useprocmon = False
1608c2ecf20Sopenharmony_ci	notestrun = False
1618c2ecf20Sopenharmony_ci	cgdump = False
1628c2ecf20Sopenharmony_ci	devdump = False
1638c2ecf20Sopenharmony_ci	mixedphaseheight = True
1648c2ecf20Sopenharmony_ci	devprops = dict()
1658c2ecf20Sopenharmony_ci	platinfo = []
1668c2ecf20Sopenharmony_ci	predelay = 0
1678c2ecf20Sopenharmony_ci	postdelay = 0
1688c2ecf20Sopenharmony_ci	pmdebug = ''
1698c2ecf20Sopenharmony_ci	tmstart = 'SUSPEND START %Y%m%d-%H:%M:%S.%f'
1708c2ecf20Sopenharmony_ci	tmend = 'RESUME COMPLETE %Y%m%d-%H:%M:%S.%f'
1718c2ecf20Sopenharmony_ci	tracefuncs = {
1728c2ecf20Sopenharmony_ci		'sys_sync': {},
1738c2ecf20Sopenharmony_ci		'ksys_sync': {},
1748c2ecf20Sopenharmony_ci		'pm_notifier_call_chain_robust': {},
1758c2ecf20Sopenharmony_ci		'pm_prepare_console': {},
1768c2ecf20Sopenharmony_ci		'pm_notifier_call_chain': {},
1778c2ecf20Sopenharmony_ci		'freeze_processes': {},
1788c2ecf20Sopenharmony_ci		'freeze_kernel_threads': {},
1798c2ecf20Sopenharmony_ci		'pm_restrict_gfp_mask': {},
1808c2ecf20Sopenharmony_ci		'acpi_suspend_begin': {},
1818c2ecf20Sopenharmony_ci		'acpi_hibernation_begin': {},
1828c2ecf20Sopenharmony_ci		'acpi_hibernation_enter': {},
1838c2ecf20Sopenharmony_ci		'acpi_hibernation_leave': {},
1848c2ecf20Sopenharmony_ci		'acpi_pm_freeze': {},
1858c2ecf20Sopenharmony_ci		'acpi_pm_thaw': {},
1868c2ecf20Sopenharmony_ci		'acpi_s2idle_end': {},
1878c2ecf20Sopenharmony_ci		'acpi_s2idle_sync': {},
1888c2ecf20Sopenharmony_ci		'acpi_s2idle_begin': {},
1898c2ecf20Sopenharmony_ci		'acpi_s2idle_prepare': {},
1908c2ecf20Sopenharmony_ci		'acpi_s2idle_prepare_late': {},
1918c2ecf20Sopenharmony_ci		'acpi_s2idle_wake': {},
1928c2ecf20Sopenharmony_ci		'acpi_s2idle_wakeup': {},
1938c2ecf20Sopenharmony_ci		'acpi_s2idle_restore': {},
1948c2ecf20Sopenharmony_ci		'acpi_s2idle_restore_early': {},
1958c2ecf20Sopenharmony_ci		'hibernate_preallocate_memory': {},
1968c2ecf20Sopenharmony_ci		'create_basic_memory_bitmaps': {},
1978c2ecf20Sopenharmony_ci		'swsusp_write': {},
1988c2ecf20Sopenharmony_ci		'suspend_console': {},
1998c2ecf20Sopenharmony_ci		'acpi_pm_prepare': {},
2008c2ecf20Sopenharmony_ci		'syscore_suspend': {},
2018c2ecf20Sopenharmony_ci		'arch_enable_nonboot_cpus_end': {},
2028c2ecf20Sopenharmony_ci		'syscore_resume': {},
2038c2ecf20Sopenharmony_ci		'acpi_pm_finish': {},
2048c2ecf20Sopenharmony_ci		'resume_console': {},
2058c2ecf20Sopenharmony_ci		'acpi_pm_end': {},
2068c2ecf20Sopenharmony_ci		'pm_restore_gfp_mask': {},
2078c2ecf20Sopenharmony_ci		'thaw_processes': {},
2088c2ecf20Sopenharmony_ci		'pm_restore_console': {},
2098c2ecf20Sopenharmony_ci		'CPU_OFF': {
2108c2ecf20Sopenharmony_ci			'func':'_cpu_down',
2118c2ecf20Sopenharmony_ci			'args_x86_64': {'cpu':'%di:s32'},
2128c2ecf20Sopenharmony_ci			'format': 'CPU_OFF[{cpu}]'
2138c2ecf20Sopenharmony_ci		},
2148c2ecf20Sopenharmony_ci		'CPU_ON': {
2158c2ecf20Sopenharmony_ci			'func':'_cpu_up',
2168c2ecf20Sopenharmony_ci			'args_x86_64': {'cpu':'%di:s32'},
2178c2ecf20Sopenharmony_ci			'format': 'CPU_ON[{cpu}]'
2188c2ecf20Sopenharmony_ci		},
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci	dev_tracefuncs = {
2218c2ecf20Sopenharmony_ci		# general wait/delay/sleep
2228c2ecf20Sopenharmony_ci		'msleep': { 'args_x86_64': {'time':'%di:s32'}, 'ub': 1 },
2238c2ecf20Sopenharmony_ci		'schedule_timeout': { 'args_x86_64': {'timeout':'%di:s32'}, 'ub': 1 },
2248c2ecf20Sopenharmony_ci		'udelay': { 'func':'__const_udelay', 'args_x86_64': {'loops':'%di:s32'}, 'ub': 1 },
2258c2ecf20Sopenharmony_ci		'usleep_range': { 'args_x86_64': {'min':'%di:s32', 'max':'%si:s32'}, 'ub': 1 },
2268c2ecf20Sopenharmony_ci		'mutex_lock_slowpath': { 'func':'__mutex_lock_slowpath', 'ub': 1 },
2278c2ecf20Sopenharmony_ci		'acpi_os_stall': {'ub': 1},
2288c2ecf20Sopenharmony_ci		'rt_mutex_slowlock': {'ub': 1},
2298c2ecf20Sopenharmony_ci		# ACPI
2308c2ecf20Sopenharmony_ci		'acpi_resume_power_resources': {},
2318c2ecf20Sopenharmony_ci		'acpi_ps_execute_method': { 'args_x86_64': {
2328c2ecf20Sopenharmony_ci			'fullpath':'+0(+40(%di)):string',
2338c2ecf20Sopenharmony_ci		}},
2348c2ecf20Sopenharmony_ci		# mei_me
2358c2ecf20Sopenharmony_ci		'mei_reset': {},
2368c2ecf20Sopenharmony_ci		# filesystem
2378c2ecf20Sopenharmony_ci		'ext4_sync_fs': {},
2388c2ecf20Sopenharmony_ci		# 80211
2398c2ecf20Sopenharmony_ci		'ath10k_bmi_read_memory': { 'args_x86_64': {'length':'%cx:s32'} },
2408c2ecf20Sopenharmony_ci		'ath10k_bmi_write_memory': { 'args_x86_64': {'length':'%cx:s32'} },
2418c2ecf20Sopenharmony_ci		'ath10k_bmi_fast_download': { 'args_x86_64': {'length':'%cx:s32'} },
2428c2ecf20Sopenharmony_ci		'iwlagn_mac_start': {},
2438c2ecf20Sopenharmony_ci		'iwlagn_alloc_bcast_station': {},
2448c2ecf20Sopenharmony_ci		'iwl_trans_pcie_start_hw': {},
2458c2ecf20Sopenharmony_ci		'iwl_trans_pcie_start_fw': {},
2468c2ecf20Sopenharmony_ci		'iwl_run_init_ucode': {},
2478c2ecf20Sopenharmony_ci		'iwl_load_ucode_wait_alive': {},
2488c2ecf20Sopenharmony_ci		'iwl_alive_start': {},
2498c2ecf20Sopenharmony_ci		'iwlagn_mac_stop': {},
2508c2ecf20Sopenharmony_ci		'iwlagn_mac_suspend': {},
2518c2ecf20Sopenharmony_ci		'iwlagn_mac_resume': {},
2528c2ecf20Sopenharmony_ci		'iwlagn_mac_add_interface': {},
2538c2ecf20Sopenharmony_ci		'iwlagn_mac_remove_interface': {},
2548c2ecf20Sopenharmony_ci		'iwlagn_mac_change_interface': {},
2558c2ecf20Sopenharmony_ci		'iwlagn_mac_config': {},
2568c2ecf20Sopenharmony_ci		'iwlagn_configure_filter': {},
2578c2ecf20Sopenharmony_ci		'iwlagn_mac_hw_scan': {},
2588c2ecf20Sopenharmony_ci		'iwlagn_bss_info_changed': {},
2598c2ecf20Sopenharmony_ci		'iwlagn_mac_channel_switch': {},
2608c2ecf20Sopenharmony_ci		'iwlagn_mac_flush': {},
2618c2ecf20Sopenharmony_ci		# ATA
2628c2ecf20Sopenharmony_ci		'ata_eh_recover': { 'args_x86_64': {'port':'+36(%di):s32'} },
2638c2ecf20Sopenharmony_ci		# i915
2648c2ecf20Sopenharmony_ci		'i915_gem_resume': {},
2658c2ecf20Sopenharmony_ci		'i915_restore_state': {},
2668c2ecf20Sopenharmony_ci		'intel_opregion_setup': {},
2678c2ecf20Sopenharmony_ci		'g4x_pre_enable_dp': {},
2688c2ecf20Sopenharmony_ci		'vlv_pre_enable_dp': {},
2698c2ecf20Sopenharmony_ci		'chv_pre_enable_dp': {},
2708c2ecf20Sopenharmony_ci		'g4x_enable_dp': {},
2718c2ecf20Sopenharmony_ci		'vlv_enable_dp': {},
2728c2ecf20Sopenharmony_ci		'intel_hpd_init': {},
2738c2ecf20Sopenharmony_ci		'intel_opregion_register': {},
2748c2ecf20Sopenharmony_ci		'intel_dp_detect': {},
2758c2ecf20Sopenharmony_ci		'intel_hdmi_detect': {},
2768c2ecf20Sopenharmony_ci		'intel_opregion_init': {},
2778c2ecf20Sopenharmony_ci		'intel_fbdev_set_suspend': {},
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci	infocmds = [
2808c2ecf20Sopenharmony_ci		[0, 'kparams', 'cat', '/proc/cmdline'],
2818c2ecf20Sopenharmony_ci		[0, 'mcelog', 'mcelog'],
2828c2ecf20Sopenharmony_ci		[0, 'pcidevices', 'lspci', '-tv'],
2838c2ecf20Sopenharmony_ci		[0, 'usbdevices', 'lsusb', '-t'],
2848c2ecf20Sopenharmony_ci		[1, 'interrupts', 'cat', '/proc/interrupts'],
2858c2ecf20Sopenharmony_ci		[1, 'wakeups', 'cat', '/sys/kernel/debug/wakeup_sources'],
2868c2ecf20Sopenharmony_ci		[2, 'gpecounts', 'sh', '-c', 'grep -v invalid /sys/firmware/acpi/interrupts/*'],
2878c2ecf20Sopenharmony_ci		[2, 'suspendstats', 'sh', '-c', 'grep -v invalid /sys/power/suspend_stats/*'],
2888c2ecf20Sopenharmony_ci		[2, 'cpuidle', 'sh', '-c', 'grep -v invalid /sys/devices/system/cpu/cpu*/cpuidle/state*/s2idle/*'],
2898c2ecf20Sopenharmony_ci		[2, 'battery', 'sh', '-c', 'grep -v invalid /sys/class/power_supply/*/*'],
2908c2ecf20Sopenharmony_ci	]
2918c2ecf20Sopenharmony_ci	cgblacklist = []
2928c2ecf20Sopenharmony_ci	kprobes = dict()
2938c2ecf20Sopenharmony_ci	timeformat = '%.3f'
2948c2ecf20Sopenharmony_ci	cmdline = '%s %s' % \
2958c2ecf20Sopenharmony_ci			(os.path.basename(sys.argv[0]), ' '.join(sys.argv[1:]))
2968c2ecf20Sopenharmony_ci	sudouser = ''
2978c2ecf20Sopenharmony_ci	def __init__(self):
2988c2ecf20Sopenharmony_ci		self.archargs = 'args_'+platform.machine()
2998c2ecf20Sopenharmony_ci		self.hostname = platform.node()
3008c2ecf20Sopenharmony_ci		if(self.hostname == ''):
3018c2ecf20Sopenharmony_ci			self.hostname = 'localhost'
3028c2ecf20Sopenharmony_ci		rtc = "rtc0"
3038c2ecf20Sopenharmony_ci		if os.path.exists('/dev/rtc'):
3048c2ecf20Sopenharmony_ci			rtc = os.readlink('/dev/rtc')
3058c2ecf20Sopenharmony_ci		rtc = '/sys/class/rtc/'+rtc
3068c2ecf20Sopenharmony_ci		if os.path.exists(rtc) and os.path.exists(rtc+'/date') and \
3078c2ecf20Sopenharmony_ci			os.path.exists(rtc+'/time') and os.path.exists(rtc+'/wakealarm'):
3088c2ecf20Sopenharmony_ci			self.rtcpath = rtc
3098c2ecf20Sopenharmony_ci		if (hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()):
3108c2ecf20Sopenharmony_ci			self.ansi = True
3118c2ecf20Sopenharmony_ci		self.testdir = datetime.now().strftime('suspend-%y%m%d-%H%M%S')
3128c2ecf20Sopenharmony_ci		if os.getuid() == 0 and 'SUDO_USER' in os.environ and \
3138c2ecf20Sopenharmony_ci			os.environ['SUDO_USER']:
3148c2ecf20Sopenharmony_ci			self.sudouser = os.environ['SUDO_USER']
3158c2ecf20Sopenharmony_ci	def resetlog(self):
3168c2ecf20Sopenharmony_ci		self.logmsg = ''
3178c2ecf20Sopenharmony_ci		self.platinfo = []
3188c2ecf20Sopenharmony_ci	def vprint(self, msg):
3198c2ecf20Sopenharmony_ci		self.logmsg += msg+'\n'
3208c2ecf20Sopenharmony_ci		if self.verbose or msg.startswith('WARNING:'):
3218c2ecf20Sopenharmony_ci			pprint(msg)
3228c2ecf20Sopenharmony_ci	def signalHandler(self, signum, frame):
3238c2ecf20Sopenharmony_ci		if not self.result:
3248c2ecf20Sopenharmony_ci			return
3258c2ecf20Sopenharmony_ci		signame = self.signames[signum] if signum in self.signames else 'UNKNOWN'
3268c2ecf20Sopenharmony_ci		msg = 'Signal %s caused a tool exit, line %d' % (signame, frame.f_lineno)
3278c2ecf20Sopenharmony_ci		self.outputResult({'error':msg})
3288c2ecf20Sopenharmony_ci		sys.exit(3)
3298c2ecf20Sopenharmony_ci	def signalHandlerInit(self):
3308c2ecf20Sopenharmony_ci		capture = ['BUS', 'SYS', 'XCPU', 'XFSZ', 'PWR', 'HUP', 'INT', 'QUIT',
3318c2ecf20Sopenharmony_ci			'ILL', 'ABRT', 'FPE', 'SEGV', 'TERM']
3328c2ecf20Sopenharmony_ci		self.signames = dict()
3338c2ecf20Sopenharmony_ci		for i in capture:
3348c2ecf20Sopenharmony_ci			s = 'SIG'+i
3358c2ecf20Sopenharmony_ci			try:
3368c2ecf20Sopenharmony_ci				signum = getattr(signal, s)
3378c2ecf20Sopenharmony_ci				signal.signal(signum, self.signalHandler)
3388c2ecf20Sopenharmony_ci			except:
3398c2ecf20Sopenharmony_ci				continue
3408c2ecf20Sopenharmony_ci			self.signames[signum] = s
3418c2ecf20Sopenharmony_ci	def rootCheck(self, fatal=True):
3428c2ecf20Sopenharmony_ci		if(os.access(self.powerfile, os.W_OK)):
3438c2ecf20Sopenharmony_ci			return True
3448c2ecf20Sopenharmony_ci		if fatal:
3458c2ecf20Sopenharmony_ci			msg = 'This command requires sysfs mount and root access'
3468c2ecf20Sopenharmony_ci			pprint('ERROR: %s\n' % msg)
3478c2ecf20Sopenharmony_ci			self.outputResult({'error':msg})
3488c2ecf20Sopenharmony_ci			sys.exit(1)
3498c2ecf20Sopenharmony_ci		return False
3508c2ecf20Sopenharmony_ci	def rootUser(self, fatal=False):
3518c2ecf20Sopenharmony_ci		if 'USER' in os.environ and os.environ['USER'] == 'root':
3528c2ecf20Sopenharmony_ci			return True
3538c2ecf20Sopenharmony_ci		if fatal:
3548c2ecf20Sopenharmony_ci			msg = 'This command must be run as root'
3558c2ecf20Sopenharmony_ci			pprint('ERROR: %s\n' % msg)
3568c2ecf20Sopenharmony_ci			self.outputResult({'error':msg})
3578c2ecf20Sopenharmony_ci			sys.exit(1)
3588c2ecf20Sopenharmony_ci		return False
3598c2ecf20Sopenharmony_ci	def usable(self, file):
3608c2ecf20Sopenharmony_ci		return (os.path.exists(file) and os.path.getsize(file) > 0)
3618c2ecf20Sopenharmony_ci	def getExec(self, cmd):
3628c2ecf20Sopenharmony_ci		try:
3638c2ecf20Sopenharmony_ci			fp = Popen(['which', cmd], stdout=PIPE, stderr=PIPE).stdout
3648c2ecf20Sopenharmony_ci			out = ascii(fp.read()).strip()
3658c2ecf20Sopenharmony_ci			fp.close()
3668c2ecf20Sopenharmony_ci		except:
3678c2ecf20Sopenharmony_ci			out = ''
3688c2ecf20Sopenharmony_ci		if out:
3698c2ecf20Sopenharmony_ci			return out
3708c2ecf20Sopenharmony_ci		for path in ['/sbin', '/bin', '/usr/sbin', '/usr/bin',
3718c2ecf20Sopenharmony_ci			'/usr/local/sbin', '/usr/local/bin']:
3728c2ecf20Sopenharmony_ci			cmdfull = os.path.join(path, cmd)
3738c2ecf20Sopenharmony_ci			if os.path.exists(cmdfull):
3748c2ecf20Sopenharmony_ci				return cmdfull
3758c2ecf20Sopenharmony_ci		return out
3768c2ecf20Sopenharmony_ci	def setPrecision(self, num):
3778c2ecf20Sopenharmony_ci		if num < 0 or num > 6:
3788c2ecf20Sopenharmony_ci			return
3798c2ecf20Sopenharmony_ci		self.timeformat = '%.{0}f'.format(num)
3808c2ecf20Sopenharmony_ci	def setOutputFolder(self, value):
3818c2ecf20Sopenharmony_ci		args = dict()
3828c2ecf20Sopenharmony_ci		n = datetime.now()
3838c2ecf20Sopenharmony_ci		args['date'] = n.strftime('%y%m%d')
3848c2ecf20Sopenharmony_ci		args['time'] = n.strftime('%H%M%S')
3858c2ecf20Sopenharmony_ci		args['hostname'] = args['host'] = self.hostname
3868c2ecf20Sopenharmony_ci		args['mode'] = self.suspendmode
3878c2ecf20Sopenharmony_ci		return value.format(**args)
3888c2ecf20Sopenharmony_ci	def setOutputFile(self):
3898c2ecf20Sopenharmony_ci		if self.dmesgfile != '':
3908c2ecf20Sopenharmony_ci			m = re.match('(?P<name>.*)_dmesg\.txt.*', self.dmesgfile)
3918c2ecf20Sopenharmony_ci			if(m):
3928c2ecf20Sopenharmony_ci				self.htmlfile = m.group('name')+'.html'
3938c2ecf20Sopenharmony_ci		if self.ftracefile != '':
3948c2ecf20Sopenharmony_ci			m = re.match('(?P<name>.*)_ftrace\.txt.*', self.ftracefile)
3958c2ecf20Sopenharmony_ci			if(m):
3968c2ecf20Sopenharmony_ci				self.htmlfile = m.group('name')+'.html'
3978c2ecf20Sopenharmony_ci	def systemInfo(self, info):
3988c2ecf20Sopenharmony_ci		p = m = ''
3998c2ecf20Sopenharmony_ci		if 'baseboard-manufacturer' in info:
4008c2ecf20Sopenharmony_ci			m = info['baseboard-manufacturer']
4018c2ecf20Sopenharmony_ci		elif 'system-manufacturer' in info:
4028c2ecf20Sopenharmony_ci			m = info['system-manufacturer']
4038c2ecf20Sopenharmony_ci		if 'system-product-name' in info:
4048c2ecf20Sopenharmony_ci			p = info['system-product-name']
4058c2ecf20Sopenharmony_ci		elif 'baseboard-product-name' in info:
4068c2ecf20Sopenharmony_ci			p = info['baseboard-product-name']
4078c2ecf20Sopenharmony_ci		if m[:5].lower() == 'intel' and 'baseboard-product-name' in info:
4088c2ecf20Sopenharmony_ci			p = info['baseboard-product-name']
4098c2ecf20Sopenharmony_ci		c = info['processor-version'] if 'processor-version' in info else ''
4108c2ecf20Sopenharmony_ci		b = info['bios-version'] if 'bios-version' in info else ''
4118c2ecf20Sopenharmony_ci		r = info['bios-release-date'] if 'bios-release-date' in info else ''
4128c2ecf20Sopenharmony_ci		self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | biosdate:%s | numcpu:%d | memsz:%d | memfr:%d' % \
4138c2ecf20Sopenharmony_ci			(m, p, c, b, r, self.cpucount, self.memtotal, self.memfree)
4148c2ecf20Sopenharmony_ci	def printSystemInfo(self, fatal=False):
4158c2ecf20Sopenharmony_ci		self.rootCheck(True)
4168c2ecf20Sopenharmony_ci		out = dmidecode(self.mempath, fatal)
4178c2ecf20Sopenharmony_ci		if len(out) < 1:
4188c2ecf20Sopenharmony_ci			return
4198c2ecf20Sopenharmony_ci		fmt = '%-24s: %s'
4208c2ecf20Sopenharmony_ci		for name in sorted(out):
4218c2ecf20Sopenharmony_ci			print(fmt % (name, out[name]))
4228c2ecf20Sopenharmony_ci		print(fmt % ('cpucount', ('%d' % self.cpucount)))
4238c2ecf20Sopenharmony_ci		print(fmt % ('memtotal', ('%d kB' % self.memtotal)))
4248c2ecf20Sopenharmony_ci		print(fmt % ('memfree', ('%d kB' % self.memfree)))
4258c2ecf20Sopenharmony_ci	def cpuInfo(self):
4268c2ecf20Sopenharmony_ci		self.cpucount = 0
4278c2ecf20Sopenharmony_ci		fp = open('/proc/cpuinfo', 'r')
4288c2ecf20Sopenharmony_ci		for line in fp:
4298c2ecf20Sopenharmony_ci			if re.match('^processor[ \t]*:[ \t]*[0-9]*', line):
4308c2ecf20Sopenharmony_ci				self.cpucount += 1
4318c2ecf20Sopenharmony_ci		fp.close()
4328c2ecf20Sopenharmony_ci		fp = open('/proc/meminfo', 'r')
4338c2ecf20Sopenharmony_ci		for line in fp:
4348c2ecf20Sopenharmony_ci			m = re.match('^MemTotal:[ \t]*(?P<sz>[0-9]*) *kB', line)
4358c2ecf20Sopenharmony_ci			if m:
4368c2ecf20Sopenharmony_ci				self.memtotal = int(m.group('sz'))
4378c2ecf20Sopenharmony_ci			m = re.match('^MemFree:[ \t]*(?P<sz>[0-9]*) *kB', line)
4388c2ecf20Sopenharmony_ci			if m:
4398c2ecf20Sopenharmony_ci				self.memfree = int(m.group('sz'))
4408c2ecf20Sopenharmony_ci		fp.close()
4418c2ecf20Sopenharmony_ci	def initTestOutput(self, name):
4428c2ecf20Sopenharmony_ci		self.prefix = self.hostname
4438c2ecf20Sopenharmony_ci		v = open('/proc/version', 'r').read().strip()
4448c2ecf20Sopenharmony_ci		kver = v.split()[2]
4458c2ecf20Sopenharmony_ci		fmt = name+'-%m%d%y-%H%M%S'
4468c2ecf20Sopenharmony_ci		testtime = datetime.now().strftime(fmt)
4478c2ecf20Sopenharmony_ci		self.teststamp = \
4488c2ecf20Sopenharmony_ci			'# '+testtime+' '+self.prefix+' '+self.suspendmode+' '+kver
4498c2ecf20Sopenharmony_ci		ext = ''
4508c2ecf20Sopenharmony_ci		if self.gzip:
4518c2ecf20Sopenharmony_ci			ext = '.gz'
4528c2ecf20Sopenharmony_ci		self.dmesgfile = \
4538c2ecf20Sopenharmony_ci			self.testdir+'/'+self.prefix+'_'+self.suspendmode+'_dmesg.txt'+ext
4548c2ecf20Sopenharmony_ci		self.ftracefile = \
4558c2ecf20Sopenharmony_ci			self.testdir+'/'+self.prefix+'_'+self.suspendmode+'_ftrace.txt'+ext
4568c2ecf20Sopenharmony_ci		self.htmlfile = \
4578c2ecf20Sopenharmony_ci			self.testdir+'/'+self.prefix+'_'+self.suspendmode+'.html'
4588c2ecf20Sopenharmony_ci		if not os.path.isdir(self.testdir):
4598c2ecf20Sopenharmony_ci			os.makedirs(self.testdir)
4608c2ecf20Sopenharmony_ci		self.sudoUserchown(self.testdir)
4618c2ecf20Sopenharmony_ci	def getValueList(self, value):
4628c2ecf20Sopenharmony_ci		out = []
4638c2ecf20Sopenharmony_ci		for i in value.split(','):
4648c2ecf20Sopenharmony_ci			if i.strip():
4658c2ecf20Sopenharmony_ci				out.append(i.strip())
4668c2ecf20Sopenharmony_ci		return out
4678c2ecf20Sopenharmony_ci	def setDeviceFilter(self, value):
4688c2ecf20Sopenharmony_ci		self.devicefilter = self.getValueList(value)
4698c2ecf20Sopenharmony_ci	def setCallgraphFilter(self, value):
4708c2ecf20Sopenharmony_ci		self.cgfilter = self.getValueList(value)
4718c2ecf20Sopenharmony_ci	def skipKprobes(self, value):
4728c2ecf20Sopenharmony_ci		for k in self.getValueList(value):
4738c2ecf20Sopenharmony_ci			if k in self.tracefuncs:
4748c2ecf20Sopenharmony_ci				del self.tracefuncs[k]
4758c2ecf20Sopenharmony_ci			if k in self.dev_tracefuncs:
4768c2ecf20Sopenharmony_ci				del self.dev_tracefuncs[k]
4778c2ecf20Sopenharmony_ci	def setCallgraphBlacklist(self, file):
4788c2ecf20Sopenharmony_ci		self.cgblacklist = self.listFromFile(file)
4798c2ecf20Sopenharmony_ci	def rtcWakeAlarmOn(self):
4808c2ecf20Sopenharmony_ci		call('echo 0 > '+self.rtcpath+'/wakealarm', shell=True)
4818c2ecf20Sopenharmony_ci		nowtime = open(self.rtcpath+'/since_epoch', 'r').read().strip()
4828c2ecf20Sopenharmony_ci		if nowtime:
4838c2ecf20Sopenharmony_ci			nowtime = int(nowtime)
4848c2ecf20Sopenharmony_ci		else:
4858c2ecf20Sopenharmony_ci			# if hardware time fails, use the software time
4868c2ecf20Sopenharmony_ci			nowtime = int(datetime.now().strftime('%s'))
4878c2ecf20Sopenharmony_ci		alarm = nowtime + self.rtcwaketime
4888c2ecf20Sopenharmony_ci		call('echo %d > %s/wakealarm' % (alarm, self.rtcpath), shell=True)
4898c2ecf20Sopenharmony_ci	def rtcWakeAlarmOff(self):
4908c2ecf20Sopenharmony_ci		call('echo 0 > %s/wakealarm' % self.rtcpath, shell=True)
4918c2ecf20Sopenharmony_ci	def initdmesg(self):
4928c2ecf20Sopenharmony_ci		# get the latest time stamp from the dmesg log
4938c2ecf20Sopenharmony_ci		fp = Popen('dmesg', stdout=PIPE).stdout
4948c2ecf20Sopenharmony_ci		ktime = '0'
4958c2ecf20Sopenharmony_ci		for line in fp:
4968c2ecf20Sopenharmony_ci			line = ascii(line).replace('\r\n', '')
4978c2ecf20Sopenharmony_ci			idx = line.find('[')
4988c2ecf20Sopenharmony_ci			if idx > 1:
4998c2ecf20Sopenharmony_ci				line = line[idx:]
5008c2ecf20Sopenharmony_ci			m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
5018c2ecf20Sopenharmony_ci			if(m):
5028c2ecf20Sopenharmony_ci				ktime = m.group('ktime')
5038c2ecf20Sopenharmony_ci		fp.close()
5048c2ecf20Sopenharmony_ci		self.dmesgstart = float(ktime)
5058c2ecf20Sopenharmony_ci	def getdmesg(self, testdata):
5068c2ecf20Sopenharmony_ci		op = self.writeDatafileHeader(self.dmesgfile, testdata)
5078c2ecf20Sopenharmony_ci		# store all new dmesg lines since initdmesg was called
5088c2ecf20Sopenharmony_ci		fp = Popen('dmesg', stdout=PIPE).stdout
5098c2ecf20Sopenharmony_ci		for line in fp:
5108c2ecf20Sopenharmony_ci			line = ascii(line).replace('\r\n', '')
5118c2ecf20Sopenharmony_ci			idx = line.find('[')
5128c2ecf20Sopenharmony_ci			if idx > 1:
5138c2ecf20Sopenharmony_ci				line = line[idx:]
5148c2ecf20Sopenharmony_ci			m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
5158c2ecf20Sopenharmony_ci			if(not m):
5168c2ecf20Sopenharmony_ci				continue
5178c2ecf20Sopenharmony_ci			ktime = float(m.group('ktime'))
5188c2ecf20Sopenharmony_ci			if ktime > self.dmesgstart:
5198c2ecf20Sopenharmony_ci				op.write(line)
5208c2ecf20Sopenharmony_ci		fp.close()
5218c2ecf20Sopenharmony_ci		op.close()
5228c2ecf20Sopenharmony_ci	def listFromFile(self, file):
5238c2ecf20Sopenharmony_ci		list = []
5248c2ecf20Sopenharmony_ci		fp = open(file)
5258c2ecf20Sopenharmony_ci		for i in fp.read().split('\n'):
5268c2ecf20Sopenharmony_ci			i = i.strip()
5278c2ecf20Sopenharmony_ci			if i and i[0] != '#':
5288c2ecf20Sopenharmony_ci				list.append(i)
5298c2ecf20Sopenharmony_ci		fp.close()
5308c2ecf20Sopenharmony_ci		return list
5318c2ecf20Sopenharmony_ci	def addFtraceFilterFunctions(self, file):
5328c2ecf20Sopenharmony_ci		for i in self.listFromFile(file):
5338c2ecf20Sopenharmony_ci			if len(i) < 2:
5348c2ecf20Sopenharmony_ci				continue
5358c2ecf20Sopenharmony_ci			self.tracefuncs[i] = dict()
5368c2ecf20Sopenharmony_ci	def getFtraceFilterFunctions(self, current):
5378c2ecf20Sopenharmony_ci		self.rootCheck(True)
5388c2ecf20Sopenharmony_ci		if not current:
5398c2ecf20Sopenharmony_ci			call('cat '+self.tpath+'available_filter_functions', shell=True)
5408c2ecf20Sopenharmony_ci			return
5418c2ecf20Sopenharmony_ci		master = self.listFromFile(self.tpath+'available_filter_functions')
5428c2ecf20Sopenharmony_ci		for i in sorted(self.tracefuncs):
5438c2ecf20Sopenharmony_ci			if 'func' in self.tracefuncs[i]:
5448c2ecf20Sopenharmony_ci				i = self.tracefuncs[i]['func']
5458c2ecf20Sopenharmony_ci			if i in master:
5468c2ecf20Sopenharmony_ci				print(i)
5478c2ecf20Sopenharmony_ci			else:
5488c2ecf20Sopenharmony_ci				print(self.colorText(i))
5498c2ecf20Sopenharmony_ci	def setFtraceFilterFunctions(self, list):
5508c2ecf20Sopenharmony_ci		master = self.listFromFile(self.tpath+'available_filter_functions')
5518c2ecf20Sopenharmony_ci		flist = ''
5528c2ecf20Sopenharmony_ci		for i in list:
5538c2ecf20Sopenharmony_ci			if i not in master:
5548c2ecf20Sopenharmony_ci				continue
5558c2ecf20Sopenharmony_ci			if ' [' in i:
5568c2ecf20Sopenharmony_ci				flist += i.split(' ')[0]+'\n'
5578c2ecf20Sopenharmony_ci			else:
5588c2ecf20Sopenharmony_ci				flist += i+'\n'
5598c2ecf20Sopenharmony_ci		fp = open(self.tpath+'set_graph_function', 'w')
5608c2ecf20Sopenharmony_ci		fp.write(flist)
5618c2ecf20Sopenharmony_ci		fp.close()
5628c2ecf20Sopenharmony_ci	def basicKprobe(self, name):
5638c2ecf20Sopenharmony_ci		self.kprobes[name] = {'name': name,'func': name,'args': dict(),'format': name}
5648c2ecf20Sopenharmony_ci	def defaultKprobe(self, name, kdata):
5658c2ecf20Sopenharmony_ci		k = kdata
5668c2ecf20Sopenharmony_ci		for field in ['name', 'format', 'func']:
5678c2ecf20Sopenharmony_ci			if field not in k:
5688c2ecf20Sopenharmony_ci				k[field] = name
5698c2ecf20Sopenharmony_ci		if self.archargs in k:
5708c2ecf20Sopenharmony_ci			k['args'] = k[self.archargs]
5718c2ecf20Sopenharmony_ci		else:
5728c2ecf20Sopenharmony_ci			k['args'] = dict()
5738c2ecf20Sopenharmony_ci			k['format'] = name
5748c2ecf20Sopenharmony_ci		self.kprobes[name] = k
5758c2ecf20Sopenharmony_ci	def kprobeColor(self, name):
5768c2ecf20Sopenharmony_ci		if name not in self.kprobes or 'color' not in self.kprobes[name]:
5778c2ecf20Sopenharmony_ci			return ''
5788c2ecf20Sopenharmony_ci		return self.kprobes[name]['color']
5798c2ecf20Sopenharmony_ci	def kprobeDisplayName(self, name, dataraw):
5808c2ecf20Sopenharmony_ci		if name not in self.kprobes:
5818c2ecf20Sopenharmony_ci			self.basicKprobe(name)
5828c2ecf20Sopenharmony_ci		data = ''
5838c2ecf20Sopenharmony_ci		quote=0
5848c2ecf20Sopenharmony_ci		# first remvoe any spaces inside quotes, and the quotes
5858c2ecf20Sopenharmony_ci		for c in dataraw:
5868c2ecf20Sopenharmony_ci			if c == '"':
5878c2ecf20Sopenharmony_ci				quote = (quote + 1) % 2
5888c2ecf20Sopenharmony_ci			if quote and c == ' ':
5898c2ecf20Sopenharmony_ci				data += '_'
5908c2ecf20Sopenharmony_ci			elif c != '"':
5918c2ecf20Sopenharmony_ci				data += c
5928c2ecf20Sopenharmony_ci		fmt, args = self.kprobes[name]['format'], self.kprobes[name]['args']
5938c2ecf20Sopenharmony_ci		arglist = dict()
5948c2ecf20Sopenharmony_ci		# now process the args
5958c2ecf20Sopenharmony_ci		for arg in sorted(args):
5968c2ecf20Sopenharmony_ci			arglist[arg] = ''
5978c2ecf20Sopenharmony_ci			m = re.match('.* '+arg+'=(?P<arg>.*) ', data);
5988c2ecf20Sopenharmony_ci			if m:
5998c2ecf20Sopenharmony_ci				arglist[arg] = m.group('arg')
6008c2ecf20Sopenharmony_ci			else:
6018c2ecf20Sopenharmony_ci				m = re.match('.* '+arg+'=(?P<arg>.*)', data);
6028c2ecf20Sopenharmony_ci				if m:
6038c2ecf20Sopenharmony_ci					arglist[arg] = m.group('arg')
6048c2ecf20Sopenharmony_ci		out = fmt.format(**arglist)
6058c2ecf20Sopenharmony_ci		out = out.replace(' ', '_').replace('"', '')
6068c2ecf20Sopenharmony_ci		return out
6078c2ecf20Sopenharmony_ci	def kprobeText(self, kname, kprobe):
6088c2ecf20Sopenharmony_ci		name = fmt = func = kname
6098c2ecf20Sopenharmony_ci		args = dict()
6108c2ecf20Sopenharmony_ci		if 'name' in kprobe:
6118c2ecf20Sopenharmony_ci			name = kprobe['name']
6128c2ecf20Sopenharmony_ci		if 'format' in kprobe:
6138c2ecf20Sopenharmony_ci			fmt = kprobe['format']
6148c2ecf20Sopenharmony_ci		if 'func' in kprobe:
6158c2ecf20Sopenharmony_ci			func = kprobe['func']
6168c2ecf20Sopenharmony_ci		if self.archargs in kprobe:
6178c2ecf20Sopenharmony_ci			args = kprobe[self.archargs]
6188c2ecf20Sopenharmony_ci		if 'args' in kprobe:
6198c2ecf20Sopenharmony_ci			args = kprobe['args']
6208c2ecf20Sopenharmony_ci		if re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', func):
6218c2ecf20Sopenharmony_ci			doError('Kprobe "%s" has format info in the function name "%s"' % (name, func))
6228c2ecf20Sopenharmony_ci		for arg in re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', fmt):
6238c2ecf20Sopenharmony_ci			if arg not in args:
6248c2ecf20Sopenharmony_ci				doError('Kprobe "%s" is missing argument "%s"' % (name, arg))
6258c2ecf20Sopenharmony_ci		val = 'p:%s_cal %s' % (name, func)
6268c2ecf20Sopenharmony_ci		for i in sorted(args):
6278c2ecf20Sopenharmony_ci			val += ' %s=%s' % (i, args[i])
6288c2ecf20Sopenharmony_ci		val += '\nr:%s_ret %s $retval\n' % (name, func)
6298c2ecf20Sopenharmony_ci		return val
6308c2ecf20Sopenharmony_ci	def addKprobes(self, output=False):
6318c2ecf20Sopenharmony_ci		if len(self.kprobes) < 1:
6328c2ecf20Sopenharmony_ci			return
6338c2ecf20Sopenharmony_ci		if output:
6348c2ecf20Sopenharmony_ci			pprint('    kprobe functions in this kernel:')
6358c2ecf20Sopenharmony_ci		# first test each kprobe
6368c2ecf20Sopenharmony_ci		rejects = []
6378c2ecf20Sopenharmony_ci		# sort kprobes: trace, ub-dev, custom, dev
6388c2ecf20Sopenharmony_ci		kpl = [[], [], [], []]
6398c2ecf20Sopenharmony_ci		linesout = len(self.kprobes)
6408c2ecf20Sopenharmony_ci		for name in sorted(self.kprobes):
6418c2ecf20Sopenharmony_ci			res = self.colorText('YES', 32)
6428c2ecf20Sopenharmony_ci			if not self.testKprobe(name, self.kprobes[name]):
6438c2ecf20Sopenharmony_ci				res = self.colorText('NO')
6448c2ecf20Sopenharmony_ci				rejects.append(name)
6458c2ecf20Sopenharmony_ci			else:
6468c2ecf20Sopenharmony_ci				if name in self.tracefuncs:
6478c2ecf20Sopenharmony_ci					kpl[0].append(name)
6488c2ecf20Sopenharmony_ci				elif name in self.dev_tracefuncs:
6498c2ecf20Sopenharmony_ci					if 'ub' in self.dev_tracefuncs[name]:
6508c2ecf20Sopenharmony_ci						kpl[1].append(name)
6518c2ecf20Sopenharmony_ci					else:
6528c2ecf20Sopenharmony_ci						kpl[3].append(name)
6538c2ecf20Sopenharmony_ci				else:
6548c2ecf20Sopenharmony_ci					kpl[2].append(name)
6558c2ecf20Sopenharmony_ci			if output:
6568c2ecf20Sopenharmony_ci				pprint('         %s: %s' % (name, res))
6578c2ecf20Sopenharmony_ci		kplist = kpl[0] + kpl[1] + kpl[2] + kpl[3]
6588c2ecf20Sopenharmony_ci		# remove all failed ones from the list
6598c2ecf20Sopenharmony_ci		for name in rejects:
6608c2ecf20Sopenharmony_ci			self.kprobes.pop(name)
6618c2ecf20Sopenharmony_ci		# set the kprobes all at once
6628c2ecf20Sopenharmony_ci		self.fsetVal('', 'kprobe_events')
6638c2ecf20Sopenharmony_ci		kprobeevents = ''
6648c2ecf20Sopenharmony_ci		for kp in kplist:
6658c2ecf20Sopenharmony_ci			kprobeevents += self.kprobeText(kp, self.kprobes[kp])
6668c2ecf20Sopenharmony_ci		self.fsetVal(kprobeevents, 'kprobe_events')
6678c2ecf20Sopenharmony_ci		if output:
6688c2ecf20Sopenharmony_ci			check = self.fgetVal('kprobe_events')
6698c2ecf20Sopenharmony_ci			linesack = (len(check.split('\n')) - 1) // 2
6708c2ecf20Sopenharmony_ci			pprint('    kprobe functions enabled: %d/%d' % (linesack, linesout))
6718c2ecf20Sopenharmony_ci		self.fsetVal('1', 'events/kprobes/enable')
6728c2ecf20Sopenharmony_ci	def testKprobe(self, kname, kprobe):
6738c2ecf20Sopenharmony_ci		self.fsetVal('0', 'events/kprobes/enable')
6748c2ecf20Sopenharmony_ci		kprobeevents = self.kprobeText(kname, kprobe)
6758c2ecf20Sopenharmony_ci		if not kprobeevents:
6768c2ecf20Sopenharmony_ci			return False
6778c2ecf20Sopenharmony_ci		try:
6788c2ecf20Sopenharmony_ci			self.fsetVal(kprobeevents, 'kprobe_events')
6798c2ecf20Sopenharmony_ci			check = self.fgetVal('kprobe_events')
6808c2ecf20Sopenharmony_ci		except:
6818c2ecf20Sopenharmony_ci			return False
6828c2ecf20Sopenharmony_ci		linesout = len(kprobeevents.split('\n'))
6838c2ecf20Sopenharmony_ci		linesack = len(check.split('\n'))
6848c2ecf20Sopenharmony_ci		if linesack < linesout:
6858c2ecf20Sopenharmony_ci			return False
6868c2ecf20Sopenharmony_ci		return True
6878c2ecf20Sopenharmony_ci	def setVal(self, val, file):
6888c2ecf20Sopenharmony_ci		if not os.path.exists(file):
6898c2ecf20Sopenharmony_ci			return False
6908c2ecf20Sopenharmony_ci		try:
6918c2ecf20Sopenharmony_ci			fp = open(file, 'wb', 0)
6928c2ecf20Sopenharmony_ci			fp.write(val.encode())
6938c2ecf20Sopenharmony_ci			fp.flush()
6948c2ecf20Sopenharmony_ci			fp.close()
6958c2ecf20Sopenharmony_ci		except:
6968c2ecf20Sopenharmony_ci			return False
6978c2ecf20Sopenharmony_ci		return True
6988c2ecf20Sopenharmony_ci	def fsetVal(self, val, path):
6998c2ecf20Sopenharmony_ci		return self.setVal(val, self.tpath+path)
7008c2ecf20Sopenharmony_ci	def getVal(self, file):
7018c2ecf20Sopenharmony_ci		res = ''
7028c2ecf20Sopenharmony_ci		if not os.path.exists(file):
7038c2ecf20Sopenharmony_ci			return res
7048c2ecf20Sopenharmony_ci		try:
7058c2ecf20Sopenharmony_ci			fp = open(file, 'r')
7068c2ecf20Sopenharmony_ci			res = fp.read()
7078c2ecf20Sopenharmony_ci			fp.close()
7088c2ecf20Sopenharmony_ci		except:
7098c2ecf20Sopenharmony_ci			pass
7108c2ecf20Sopenharmony_ci		return res
7118c2ecf20Sopenharmony_ci	def fgetVal(self, path):
7128c2ecf20Sopenharmony_ci		return self.getVal(self.tpath+path)
7138c2ecf20Sopenharmony_ci	def cleanupFtrace(self):
7148c2ecf20Sopenharmony_ci		if(self.usecallgraph or self.usetraceevents or self.usedevsrc):
7158c2ecf20Sopenharmony_ci			self.fsetVal('0', 'events/kprobes/enable')
7168c2ecf20Sopenharmony_ci			self.fsetVal('', 'kprobe_events')
7178c2ecf20Sopenharmony_ci			self.fsetVal('1024', 'buffer_size_kb')
7188c2ecf20Sopenharmony_ci		if self.pmdebug:
7198c2ecf20Sopenharmony_ci			self.setVal(self.pmdebug, self.pmdpath)
7208c2ecf20Sopenharmony_ci	def setupAllKprobes(self):
7218c2ecf20Sopenharmony_ci		for name in self.tracefuncs:
7228c2ecf20Sopenharmony_ci			self.defaultKprobe(name, self.tracefuncs[name])
7238c2ecf20Sopenharmony_ci		for name in self.dev_tracefuncs:
7248c2ecf20Sopenharmony_ci			self.defaultKprobe(name, self.dev_tracefuncs[name])
7258c2ecf20Sopenharmony_ci	def isCallgraphFunc(self, name):
7268c2ecf20Sopenharmony_ci		if len(self.tracefuncs) < 1 and self.suspendmode == 'command':
7278c2ecf20Sopenharmony_ci			return True
7288c2ecf20Sopenharmony_ci		for i in self.tracefuncs:
7298c2ecf20Sopenharmony_ci			if 'func' in self.tracefuncs[i]:
7308c2ecf20Sopenharmony_ci				f = self.tracefuncs[i]['func']
7318c2ecf20Sopenharmony_ci			else:
7328c2ecf20Sopenharmony_ci				f = i
7338c2ecf20Sopenharmony_ci			if name == f:
7348c2ecf20Sopenharmony_ci				return True
7358c2ecf20Sopenharmony_ci		return False
7368c2ecf20Sopenharmony_ci	def initFtrace(self, quiet=False):
7378c2ecf20Sopenharmony_ci		if not quiet:
7388c2ecf20Sopenharmony_ci			sysvals.printSystemInfo(False)
7398c2ecf20Sopenharmony_ci			pprint('INITIALIZING FTRACE...')
7408c2ecf20Sopenharmony_ci		# turn trace off
7418c2ecf20Sopenharmony_ci		self.fsetVal('0', 'tracing_on')
7428c2ecf20Sopenharmony_ci		self.cleanupFtrace()
7438c2ecf20Sopenharmony_ci		# pm debug messages
7448c2ecf20Sopenharmony_ci		pv = self.getVal(self.pmdpath)
7458c2ecf20Sopenharmony_ci		if pv != '1':
7468c2ecf20Sopenharmony_ci			self.setVal('1', self.pmdpath)
7478c2ecf20Sopenharmony_ci			self.pmdebug = pv
7488c2ecf20Sopenharmony_ci		# set the trace clock to global
7498c2ecf20Sopenharmony_ci		self.fsetVal('global', 'trace_clock')
7508c2ecf20Sopenharmony_ci		self.fsetVal('nop', 'current_tracer')
7518c2ecf20Sopenharmony_ci		# set trace buffer to an appropriate value
7528c2ecf20Sopenharmony_ci		cpus = max(1, self.cpucount)
7538c2ecf20Sopenharmony_ci		if self.bufsize > 0:
7548c2ecf20Sopenharmony_ci			tgtsize = self.bufsize
7558c2ecf20Sopenharmony_ci		elif self.usecallgraph or self.usedevsrc:
7568c2ecf20Sopenharmony_ci			bmax = (1*1024*1024) if self.suspendmode in ['disk', 'command'] \
7578c2ecf20Sopenharmony_ci				else (3*1024*1024)
7588c2ecf20Sopenharmony_ci			tgtsize = min(self.memfree, bmax)
7598c2ecf20Sopenharmony_ci		else:
7608c2ecf20Sopenharmony_ci			tgtsize = 65536
7618c2ecf20Sopenharmony_ci		while not self.fsetVal('%d' % (tgtsize // cpus), 'buffer_size_kb'):
7628c2ecf20Sopenharmony_ci			# if the size failed to set, lower it and keep trying
7638c2ecf20Sopenharmony_ci			tgtsize -= 65536
7648c2ecf20Sopenharmony_ci			if tgtsize < 65536:
7658c2ecf20Sopenharmony_ci				tgtsize = int(self.fgetVal('buffer_size_kb')) * cpus
7668c2ecf20Sopenharmony_ci				break
7678c2ecf20Sopenharmony_ci		self.vprint('Setting trace buffers to %d kB (%d kB per cpu)' % (tgtsize, tgtsize/cpus))
7688c2ecf20Sopenharmony_ci		# initialize the callgraph trace
7698c2ecf20Sopenharmony_ci		if(self.usecallgraph):
7708c2ecf20Sopenharmony_ci			# set trace type
7718c2ecf20Sopenharmony_ci			self.fsetVal('function_graph', 'current_tracer')
7728c2ecf20Sopenharmony_ci			self.fsetVal('', 'set_ftrace_filter')
7738c2ecf20Sopenharmony_ci			# set trace format options
7748c2ecf20Sopenharmony_ci			self.fsetVal('print-parent', 'trace_options')
7758c2ecf20Sopenharmony_ci			self.fsetVal('funcgraph-abstime', 'trace_options')
7768c2ecf20Sopenharmony_ci			self.fsetVal('funcgraph-cpu', 'trace_options')
7778c2ecf20Sopenharmony_ci			self.fsetVal('funcgraph-duration', 'trace_options')
7788c2ecf20Sopenharmony_ci			self.fsetVal('funcgraph-proc', 'trace_options')
7798c2ecf20Sopenharmony_ci			self.fsetVal('funcgraph-tail', 'trace_options')
7808c2ecf20Sopenharmony_ci			self.fsetVal('nofuncgraph-overhead', 'trace_options')
7818c2ecf20Sopenharmony_ci			self.fsetVal('context-info', 'trace_options')
7828c2ecf20Sopenharmony_ci			self.fsetVal('graph-time', 'trace_options')
7838c2ecf20Sopenharmony_ci			self.fsetVal('%d' % self.max_graph_depth, 'max_graph_depth')
7848c2ecf20Sopenharmony_ci			cf = ['dpm_run_callback']
7858c2ecf20Sopenharmony_ci			if(self.usetraceevents):
7868c2ecf20Sopenharmony_ci				cf += ['dpm_prepare', 'dpm_complete']
7878c2ecf20Sopenharmony_ci			for fn in self.tracefuncs:
7888c2ecf20Sopenharmony_ci				if 'func' in self.tracefuncs[fn]:
7898c2ecf20Sopenharmony_ci					cf.append(self.tracefuncs[fn]['func'])
7908c2ecf20Sopenharmony_ci				else:
7918c2ecf20Sopenharmony_ci					cf.append(fn)
7928c2ecf20Sopenharmony_ci			if self.ftop:
7938c2ecf20Sopenharmony_ci				self.setFtraceFilterFunctions([self.ftopfunc])
7948c2ecf20Sopenharmony_ci			else:
7958c2ecf20Sopenharmony_ci				self.setFtraceFilterFunctions(cf)
7968c2ecf20Sopenharmony_ci		# initialize the kprobe trace
7978c2ecf20Sopenharmony_ci		elif self.usekprobes:
7988c2ecf20Sopenharmony_ci			for name in self.tracefuncs:
7998c2ecf20Sopenharmony_ci				self.defaultKprobe(name, self.tracefuncs[name])
8008c2ecf20Sopenharmony_ci			if self.usedevsrc:
8018c2ecf20Sopenharmony_ci				for name in self.dev_tracefuncs:
8028c2ecf20Sopenharmony_ci					self.defaultKprobe(name, self.dev_tracefuncs[name])
8038c2ecf20Sopenharmony_ci			if not quiet:
8048c2ecf20Sopenharmony_ci				pprint('INITIALIZING KPROBES...')
8058c2ecf20Sopenharmony_ci			self.addKprobes(self.verbose)
8068c2ecf20Sopenharmony_ci		if(self.usetraceevents):
8078c2ecf20Sopenharmony_ci			# turn trace events on
8088c2ecf20Sopenharmony_ci			events = iter(self.traceevents)
8098c2ecf20Sopenharmony_ci			for e in events:
8108c2ecf20Sopenharmony_ci				self.fsetVal('1', 'events/power/'+e+'/enable')
8118c2ecf20Sopenharmony_ci		# clear the trace buffer
8128c2ecf20Sopenharmony_ci		self.fsetVal('', 'trace')
8138c2ecf20Sopenharmony_ci	def verifyFtrace(self):
8148c2ecf20Sopenharmony_ci		# files needed for any trace data
8158c2ecf20Sopenharmony_ci		files = ['buffer_size_kb', 'current_tracer', 'trace', 'trace_clock',
8168c2ecf20Sopenharmony_ci				 'trace_marker', 'trace_options', 'tracing_on']
8178c2ecf20Sopenharmony_ci		# files needed for callgraph trace data
8188c2ecf20Sopenharmony_ci		tp = self.tpath
8198c2ecf20Sopenharmony_ci		if(self.usecallgraph):
8208c2ecf20Sopenharmony_ci			files += [
8218c2ecf20Sopenharmony_ci				'available_filter_functions',
8228c2ecf20Sopenharmony_ci				'set_ftrace_filter',
8238c2ecf20Sopenharmony_ci				'set_graph_function'
8248c2ecf20Sopenharmony_ci			]
8258c2ecf20Sopenharmony_ci		for f in files:
8268c2ecf20Sopenharmony_ci			if(os.path.exists(tp+f) == False):
8278c2ecf20Sopenharmony_ci				return False
8288c2ecf20Sopenharmony_ci		return True
8298c2ecf20Sopenharmony_ci	def verifyKprobes(self):
8308c2ecf20Sopenharmony_ci		# files needed for kprobes to work
8318c2ecf20Sopenharmony_ci		files = ['kprobe_events', 'events']
8328c2ecf20Sopenharmony_ci		tp = self.tpath
8338c2ecf20Sopenharmony_ci		for f in files:
8348c2ecf20Sopenharmony_ci			if(os.path.exists(tp+f) == False):
8358c2ecf20Sopenharmony_ci				return False
8368c2ecf20Sopenharmony_ci		return True
8378c2ecf20Sopenharmony_ci	def colorText(self, str, color=31):
8388c2ecf20Sopenharmony_ci		if not self.ansi:
8398c2ecf20Sopenharmony_ci			return str
8408c2ecf20Sopenharmony_ci		return '\x1B[%d;40m%s\x1B[m' % (color, str)
8418c2ecf20Sopenharmony_ci	def writeDatafileHeader(self, filename, testdata):
8428c2ecf20Sopenharmony_ci		fp = self.openlog(filename, 'w')
8438c2ecf20Sopenharmony_ci		fp.write('%s\n%s\n# command | %s\n' % (self.teststamp, self.sysstamp, self.cmdline))
8448c2ecf20Sopenharmony_ci		for test in testdata:
8458c2ecf20Sopenharmony_ci			if 'fw' in test:
8468c2ecf20Sopenharmony_ci				fw = test['fw']
8478c2ecf20Sopenharmony_ci				if(fw):
8488c2ecf20Sopenharmony_ci					fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1]))
8498c2ecf20Sopenharmony_ci			if 'turbo' in test:
8508c2ecf20Sopenharmony_ci				fp.write('# turbostat %s\n' % test['turbo'])
8518c2ecf20Sopenharmony_ci			if 'wifi' in test:
8528c2ecf20Sopenharmony_ci				fp.write('# wifi %s\n' % test['wifi'])
8538c2ecf20Sopenharmony_ci			if test['error'] or len(testdata) > 1:
8548c2ecf20Sopenharmony_ci				fp.write('# enter_sleep_error %s\n' % test['error'])
8558c2ecf20Sopenharmony_ci		return fp
8568c2ecf20Sopenharmony_ci	def sudoUserchown(self, dir):
8578c2ecf20Sopenharmony_ci		if os.path.exists(dir) and self.sudouser:
8588c2ecf20Sopenharmony_ci			cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1'
8598c2ecf20Sopenharmony_ci			call(cmd.format(self.sudouser, dir), shell=True)
8608c2ecf20Sopenharmony_ci	def outputResult(self, testdata, num=0):
8618c2ecf20Sopenharmony_ci		if not self.result:
8628c2ecf20Sopenharmony_ci			return
8638c2ecf20Sopenharmony_ci		n = ''
8648c2ecf20Sopenharmony_ci		if num > 0:
8658c2ecf20Sopenharmony_ci			n = '%d' % num
8668c2ecf20Sopenharmony_ci		fp = open(self.result, 'a')
8678c2ecf20Sopenharmony_ci		if 'error' in testdata:
8688c2ecf20Sopenharmony_ci			fp.write('result%s: fail\n' % n)
8698c2ecf20Sopenharmony_ci			fp.write('error%s: %s\n' % (n, testdata['error']))
8708c2ecf20Sopenharmony_ci		else:
8718c2ecf20Sopenharmony_ci			fp.write('result%s: pass\n' % n)
8728c2ecf20Sopenharmony_ci		for v in ['suspend', 'resume', 'boot', 'lastinit']:
8738c2ecf20Sopenharmony_ci			if v in testdata:
8748c2ecf20Sopenharmony_ci				fp.write('%s%s: %.3f\n' % (v, n, testdata[v]))
8758c2ecf20Sopenharmony_ci		for v in ['fwsuspend', 'fwresume']:
8768c2ecf20Sopenharmony_ci			if v in testdata:
8778c2ecf20Sopenharmony_ci				fp.write('%s%s: %.3f\n' % (v, n, testdata[v] / 1000000.0))
8788c2ecf20Sopenharmony_ci		if 'bugurl' in testdata:
8798c2ecf20Sopenharmony_ci			fp.write('url%s: %s\n' % (n, testdata['bugurl']))
8808c2ecf20Sopenharmony_ci		fp.close()
8818c2ecf20Sopenharmony_ci		self.sudoUserchown(self.result)
8828c2ecf20Sopenharmony_ci	def configFile(self, file):
8838c2ecf20Sopenharmony_ci		dir = os.path.dirname(os.path.realpath(__file__))
8848c2ecf20Sopenharmony_ci		if os.path.exists(file):
8858c2ecf20Sopenharmony_ci			return file
8868c2ecf20Sopenharmony_ci		elif os.path.exists(dir+'/'+file):
8878c2ecf20Sopenharmony_ci			return dir+'/'+file
8888c2ecf20Sopenharmony_ci		elif os.path.exists(dir+'/config/'+file):
8898c2ecf20Sopenharmony_ci			return dir+'/config/'+file
8908c2ecf20Sopenharmony_ci		return ''
8918c2ecf20Sopenharmony_ci	def openlog(self, filename, mode):
8928c2ecf20Sopenharmony_ci		isgz = self.gzip
8938c2ecf20Sopenharmony_ci		if mode == 'r':
8948c2ecf20Sopenharmony_ci			try:
8958c2ecf20Sopenharmony_ci				with gzip.open(filename, mode+'t') as fp:
8968c2ecf20Sopenharmony_ci					test = fp.read(64)
8978c2ecf20Sopenharmony_ci				isgz = True
8988c2ecf20Sopenharmony_ci			except:
8998c2ecf20Sopenharmony_ci				isgz = False
9008c2ecf20Sopenharmony_ci		if isgz:
9018c2ecf20Sopenharmony_ci			return gzip.open(filename, mode+'t')
9028c2ecf20Sopenharmony_ci		return open(filename, mode)
9038c2ecf20Sopenharmony_ci	def b64unzip(self, data):
9048c2ecf20Sopenharmony_ci		try:
9058c2ecf20Sopenharmony_ci			out = codecs.decode(base64.b64decode(data), 'zlib').decode()
9068c2ecf20Sopenharmony_ci		except:
9078c2ecf20Sopenharmony_ci			out = data
9088c2ecf20Sopenharmony_ci		return out
9098c2ecf20Sopenharmony_ci	def b64zip(self, data):
9108c2ecf20Sopenharmony_ci		out = base64.b64encode(codecs.encode(data.encode(), 'zlib')).decode()
9118c2ecf20Sopenharmony_ci		return out
9128c2ecf20Sopenharmony_ci	def platforminfo(self, cmdafter):
9138c2ecf20Sopenharmony_ci		# add platform info on to a completed ftrace file
9148c2ecf20Sopenharmony_ci		if not os.path.exists(self.ftracefile):
9158c2ecf20Sopenharmony_ci			return False
9168c2ecf20Sopenharmony_ci		footer = '#\n'
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci		# add test command string line if need be
9198c2ecf20Sopenharmony_ci		if self.suspendmode == 'command' and self.testcommand:
9208c2ecf20Sopenharmony_ci			footer += '# platform-testcmd: %s\n' % (self.testcommand)
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci		# get a list of target devices from the ftrace file
9238c2ecf20Sopenharmony_ci		props = dict()
9248c2ecf20Sopenharmony_ci		tp = TestProps()
9258c2ecf20Sopenharmony_ci		tf = self.openlog(self.ftracefile, 'r')
9268c2ecf20Sopenharmony_ci		for line in tf:
9278c2ecf20Sopenharmony_ci			if tp.stampInfo(line, self):
9288c2ecf20Sopenharmony_ci				continue
9298c2ecf20Sopenharmony_ci			# parse only valid lines, if this is not one move on
9308c2ecf20Sopenharmony_ci			m = re.match(tp.ftrace_line_fmt, line)
9318c2ecf20Sopenharmony_ci			if(not m or 'device_pm_callback_start' not in line):
9328c2ecf20Sopenharmony_ci				continue
9338c2ecf20Sopenharmony_ci			m = re.match('.*: (?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*', m.group('msg'));
9348c2ecf20Sopenharmony_ci			if(not m):
9358c2ecf20Sopenharmony_ci				continue
9368c2ecf20Sopenharmony_ci			dev = m.group('d')
9378c2ecf20Sopenharmony_ci			if dev not in props:
9388c2ecf20Sopenharmony_ci				props[dev] = DevProps()
9398c2ecf20Sopenharmony_ci		tf.close()
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci		# now get the syspath for each target device
9428c2ecf20Sopenharmony_ci		for dirname, dirnames, filenames in os.walk('/sys/devices'):
9438c2ecf20Sopenharmony_ci			if(re.match('.*/power', dirname) and 'async' in filenames):
9448c2ecf20Sopenharmony_ci				dev = dirname.split('/')[-2]
9458c2ecf20Sopenharmony_ci				if dev in props and (not props[dev].syspath or len(dirname) < len(props[dev].syspath)):
9468c2ecf20Sopenharmony_ci					props[dev].syspath = dirname[:-6]
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci		# now fill in the properties for our target devices
9498c2ecf20Sopenharmony_ci		for dev in sorted(props):
9508c2ecf20Sopenharmony_ci			dirname = props[dev].syspath
9518c2ecf20Sopenharmony_ci			if not dirname or not os.path.exists(dirname):
9528c2ecf20Sopenharmony_ci				continue
9538c2ecf20Sopenharmony_ci			with open(dirname+'/power/async') as fp:
9548c2ecf20Sopenharmony_ci				text = fp.read()
9558c2ecf20Sopenharmony_ci				props[dev].isasync = False
9568c2ecf20Sopenharmony_ci				if 'enabled' in text:
9578c2ecf20Sopenharmony_ci					props[dev].isasync = True
9588c2ecf20Sopenharmony_ci			fields = os.listdir(dirname)
9598c2ecf20Sopenharmony_ci			if 'product' in fields:
9608c2ecf20Sopenharmony_ci				with open(dirname+'/product', 'rb') as fp:
9618c2ecf20Sopenharmony_ci					props[dev].altname = ascii(fp.read())
9628c2ecf20Sopenharmony_ci			elif 'name' in fields:
9638c2ecf20Sopenharmony_ci				with open(dirname+'/name', 'rb') as fp:
9648c2ecf20Sopenharmony_ci					props[dev].altname = ascii(fp.read())
9658c2ecf20Sopenharmony_ci			elif 'model' in fields:
9668c2ecf20Sopenharmony_ci				with open(dirname+'/model', 'rb') as fp:
9678c2ecf20Sopenharmony_ci					props[dev].altname = ascii(fp.read())
9688c2ecf20Sopenharmony_ci			elif 'description' in fields:
9698c2ecf20Sopenharmony_ci				with open(dirname+'/description', 'rb') as fp:
9708c2ecf20Sopenharmony_ci					props[dev].altname = ascii(fp.read())
9718c2ecf20Sopenharmony_ci			elif 'id' in fields:
9728c2ecf20Sopenharmony_ci				with open(dirname+'/id', 'rb') as fp:
9738c2ecf20Sopenharmony_ci					props[dev].altname = ascii(fp.read())
9748c2ecf20Sopenharmony_ci			elif 'idVendor' in fields and 'idProduct' in fields:
9758c2ecf20Sopenharmony_ci				idv, idp = '', ''
9768c2ecf20Sopenharmony_ci				with open(dirname+'/idVendor', 'rb') as fp:
9778c2ecf20Sopenharmony_ci					idv = ascii(fp.read()).strip()
9788c2ecf20Sopenharmony_ci				with open(dirname+'/idProduct', 'rb') as fp:
9798c2ecf20Sopenharmony_ci					idp = ascii(fp.read()).strip()
9808c2ecf20Sopenharmony_ci				props[dev].altname = '%s:%s' % (idv, idp)
9818c2ecf20Sopenharmony_ci			if props[dev].altname:
9828c2ecf20Sopenharmony_ci				out = props[dev].altname.strip().replace('\n', ' ')\
9838c2ecf20Sopenharmony_ci					.replace(',', ' ').replace(';', ' ')
9848c2ecf20Sopenharmony_ci				props[dev].altname = out
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci		# add a devinfo line to the bottom of ftrace
9878c2ecf20Sopenharmony_ci		out = ''
9888c2ecf20Sopenharmony_ci		for dev in sorted(props):
9898c2ecf20Sopenharmony_ci			out += props[dev].out(dev)
9908c2ecf20Sopenharmony_ci		footer += '# platform-devinfo: %s\n' % self.b64zip(out)
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci		# add a line for each of these commands with their outputs
9938c2ecf20Sopenharmony_ci		for name, cmdline, info in cmdafter:
9948c2ecf20Sopenharmony_ci			footer += '# platform-%s: %s | %s\n' % (name, cmdline, self.b64zip(info))
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci		with self.openlog(self.ftracefile, 'a') as fp:
9978c2ecf20Sopenharmony_ci			fp.write(footer)
9988c2ecf20Sopenharmony_ci		return True
9998c2ecf20Sopenharmony_ci	def commonPrefix(self, list):
10008c2ecf20Sopenharmony_ci		if len(list) < 2:
10018c2ecf20Sopenharmony_ci			return ''
10028c2ecf20Sopenharmony_ci		prefix = list[0]
10038c2ecf20Sopenharmony_ci		for s in list[1:]:
10048c2ecf20Sopenharmony_ci			while s[:len(prefix)] != prefix and prefix:
10058c2ecf20Sopenharmony_ci				prefix = prefix[:len(prefix)-1]
10068c2ecf20Sopenharmony_ci			if not prefix:
10078c2ecf20Sopenharmony_ci				break
10088c2ecf20Sopenharmony_ci		if '/' in prefix and prefix[-1] != '/':
10098c2ecf20Sopenharmony_ci			prefix = prefix[0:prefix.rfind('/')+1]
10108c2ecf20Sopenharmony_ci		return prefix
10118c2ecf20Sopenharmony_ci	def dictify(self, text, format):
10128c2ecf20Sopenharmony_ci		out = dict()
10138c2ecf20Sopenharmony_ci		header = True if format == 1 else False
10148c2ecf20Sopenharmony_ci		delim = ' ' if format == 1 else ':'
10158c2ecf20Sopenharmony_ci		for line in text.split('\n'):
10168c2ecf20Sopenharmony_ci			if header:
10178c2ecf20Sopenharmony_ci				header, out['@'] = False, line
10188c2ecf20Sopenharmony_ci				continue
10198c2ecf20Sopenharmony_ci			line = line.strip()
10208c2ecf20Sopenharmony_ci			if delim in line:
10218c2ecf20Sopenharmony_ci				data = line.split(delim, 1)
10228c2ecf20Sopenharmony_ci				num = re.search(r'[\d]+', data[1])
10238c2ecf20Sopenharmony_ci				if format == 2 and num:
10248c2ecf20Sopenharmony_ci					out[data[0].strip()] = num.group()
10258c2ecf20Sopenharmony_ci				else:
10268c2ecf20Sopenharmony_ci					out[data[0].strip()] = data[1]
10278c2ecf20Sopenharmony_ci		return out
10288c2ecf20Sopenharmony_ci	def cmdinfo(self, begin, debug=False):
10298c2ecf20Sopenharmony_ci		out = []
10308c2ecf20Sopenharmony_ci		if begin:
10318c2ecf20Sopenharmony_ci			self.cmd1 = dict()
10328c2ecf20Sopenharmony_ci		for cargs in self.infocmds:
10338c2ecf20Sopenharmony_ci			delta, name = cargs[0], cargs[1]
10348c2ecf20Sopenharmony_ci			cmdline, cmdpath = ' '.join(cargs[2:]), self.getExec(cargs[2])
10358c2ecf20Sopenharmony_ci			if not cmdpath or (begin and not delta):
10368c2ecf20Sopenharmony_ci				continue
10378c2ecf20Sopenharmony_ci			try:
10388c2ecf20Sopenharmony_ci				fp = Popen([cmdpath]+cargs[3:], stdout=PIPE, stderr=PIPE).stdout
10398c2ecf20Sopenharmony_ci				info = ascii(fp.read()).strip()
10408c2ecf20Sopenharmony_ci				fp.close()
10418c2ecf20Sopenharmony_ci			except:
10428c2ecf20Sopenharmony_ci				continue
10438c2ecf20Sopenharmony_ci			if not debug and begin:
10448c2ecf20Sopenharmony_ci				self.cmd1[name] = self.dictify(info, delta)
10458c2ecf20Sopenharmony_ci			elif not debug and delta and name in self.cmd1:
10468c2ecf20Sopenharmony_ci				before, after = self.cmd1[name], self.dictify(info, delta)
10478c2ecf20Sopenharmony_ci				dinfo = ('\t%s\n' % before['@']) if '@' in before else ''
10488c2ecf20Sopenharmony_ci				prefix = self.commonPrefix(list(before.keys()))
10498c2ecf20Sopenharmony_ci				for key in sorted(before):
10508c2ecf20Sopenharmony_ci					if key in after and before[key] != after[key]:
10518c2ecf20Sopenharmony_ci						title = key.replace(prefix, '')
10528c2ecf20Sopenharmony_ci						if delta == 2:
10538c2ecf20Sopenharmony_ci							dinfo += '\t%s : %s -> %s\n' % \
10548c2ecf20Sopenharmony_ci								(title, before[key].strip(), after[key].strip())
10558c2ecf20Sopenharmony_ci						else:
10568c2ecf20Sopenharmony_ci							dinfo += '%10s (start) : %s\n%10s (after) : %s\n' % \
10578c2ecf20Sopenharmony_ci								(title, before[key], title, after[key])
10588c2ecf20Sopenharmony_ci				dinfo = '\tnothing changed' if not dinfo else dinfo.rstrip()
10598c2ecf20Sopenharmony_ci				out.append((name, cmdline, dinfo))
10608c2ecf20Sopenharmony_ci			else:
10618c2ecf20Sopenharmony_ci				out.append((name, cmdline, '\tnothing' if not info else info))
10628c2ecf20Sopenharmony_ci		return out
10638c2ecf20Sopenharmony_ci	def haveTurbostat(self):
10648c2ecf20Sopenharmony_ci		if not self.tstat:
10658c2ecf20Sopenharmony_ci			return False
10668c2ecf20Sopenharmony_ci		cmd = self.getExec('turbostat')
10678c2ecf20Sopenharmony_ci		if not cmd:
10688c2ecf20Sopenharmony_ci			return False
10698c2ecf20Sopenharmony_ci		fp = Popen([cmd, '-v'], stdout=PIPE, stderr=PIPE).stderr
10708c2ecf20Sopenharmony_ci		out = ascii(fp.read()).strip()
10718c2ecf20Sopenharmony_ci		fp.close()
10728c2ecf20Sopenharmony_ci		if re.match('turbostat version .*', out):
10738c2ecf20Sopenharmony_ci			self.vprint(out)
10748c2ecf20Sopenharmony_ci			return True
10758c2ecf20Sopenharmony_ci		return False
10768c2ecf20Sopenharmony_ci	def turbostat(self):
10778c2ecf20Sopenharmony_ci		cmd = self.getExec('turbostat')
10788c2ecf20Sopenharmony_ci		rawout = keyline = valline = ''
10798c2ecf20Sopenharmony_ci		fullcmd = '%s -q -S echo freeze > %s' % (cmd, self.powerfile)
10808c2ecf20Sopenharmony_ci		fp = Popen(['sh', '-c', fullcmd], stdout=PIPE, stderr=PIPE).stderr
10818c2ecf20Sopenharmony_ci		for line in fp:
10828c2ecf20Sopenharmony_ci			line = ascii(line)
10838c2ecf20Sopenharmony_ci			rawout += line
10848c2ecf20Sopenharmony_ci			if keyline and valline:
10858c2ecf20Sopenharmony_ci				continue
10868c2ecf20Sopenharmony_ci			if re.match('(?i)Avg_MHz.*', line):
10878c2ecf20Sopenharmony_ci				keyline = line.strip().split()
10888c2ecf20Sopenharmony_ci			elif keyline:
10898c2ecf20Sopenharmony_ci				valline = line.strip().split()
10908c2ecf20Sopenharmony_ci		fp.close()
10918c2ecf20Sopenharmony_ci		if not keyline or not valline or len(keyline) != len(valline):
10928c2ecf20Sopenharmony_ci			errmsg = 'unrecognized turbostat output:\n'+rawout.strip()
10938c2ecf20Sopenharmony_ci			self.vprint(errmsg)
10948c2ecf20Sopenharmony_ci			if not self.verbose:
10958c2ecf20Sopenharmony_ci				pprint(errmsg)
10968c2ecf20Sopenharmony_ci			return ''
10978c2ecf20Sopenharmony_ci		if self.verbose:
10988c2ecf20Sopenharmony_ci			pprint(rawout.strip())
10998c2ecf20Sopenharmony_ci		out = []
11008c2ecf20Sopenharmony_ci		for key in keyline:
11018c2ecf20Sopenharmony_ci			idx = keyline.index(key)
11028c2ecf20Sopenharmony_ci			val = valline[idx]
11038c2ecf20Sopenharmony_ci			out.append('%s=%s' % (key, val))
11048c2ecf20Sopenharmony_ci		return '|'.join(out)
11058c2ecf20Sopenharmony_ci	def wifiDetails(self, dev):
11068c2ecf20Sopenharmony_ci		try:
11078c2ecf20Sopenharmony_ci			info = open('/sys/class/net/%s/device/uevent' % dev, 'r').read().strip()
11088c2ecf20Sopenharmony_ci		except:
11098c2ecf20Sopenharmony_ci			return dev
11108c2ecf20Sopenharmony_ci		vals = [dev]
11118c2ecf20Sopenharmony_ci		for prop in info.split('\n'):
11128c2ecf20Sopenharmony_ci			if prop.startswith('DRIVER=') or prop.startswith('PCI_ID='):
11138c2ecf20Sopenharmony_ci				vals.append(prop.split('=')[-1])
11148c2ecf20Sopenharmony_ci		return ':'.join(vals)
11158c2ecf20Sopenharmony_ci	def checkWifi(self, dev=''):
11168c2ecf20Sopenharmony_ci		try:
11178c2ecf20Sopenharmony_ci			w = open('/proc/net/wireless', 'r').read().strip()
11188c2ecf20Sopenharmony_ci		except:
11198c2ecf20Sopenharmony_ci			return ''
11208c2ecf20Sopenharmony_ci		for line in reversed(w.split('\n')):
11218c2ecf20Sopenharmony_ci			m = re.match(' *(?P<dev>.*): (?P<stat>[0-9a-f]*) .*', w.split('\n')[-1])
11228c2ecf20Sopenharmony_ci			if not m or (dev and dev != m.group('dev')):
11238c2ecf20Sopenharmony_ci				continue
11248c2ecf20Sopenharmony_ci			return m.group('dev')
11258c2ecf20Sopenharmony_ci		return ''
11268c2ecf20Sopenharmony_ci	def pollWifi(self, dev, timeout=60):
11278c2ecf20Sopenharmony_ci		start = time.time()
11288c2ecf20Sopenharmony_ci		while (time.time() - start) < timeout:
11298c2ecf20Sopenharmony_ci			w = self.checkWifi(dev)
11308c2ecf20Sopenharmony_ci			if w:
11318c2ecf20Sopenharmony_ci				return '%s reconnected %.2f' % \
11328c2ecf20Sopenharmony_ci					(self.wifiDetails(dev), max(0, time.time() - start))
11338c2ecf20Sopenharmony_ci			time.sleep(0.01)
11348c2ecf20Sopenharmony_ci		return '%s timeout %d' % (self.wifiDetails(dev), timeout)
11358c2ecf20Sopenharmony_ci	def errorSummary(self, errinfo, msg):
11368c2ecf20Sopenharmony_ci		found = False
11378c2ecf20Sopenharmony_ci		for entry in errinfo:
11388c2ecf20Sopenharmony_ci			if re.match(entry['match'], msg):
11398c2ecf20Sopenharmony_ci				entry['count'] += 1
11408c2ecf20Sopenharmony_ci				if self.hostname not in entry['urls']:
11418c2ecf20Sopenharmony_ci					entry['urls'][self.hostname] = [self.htmlfile]
11428c2ecf20Sopenharmony_ci				elif self.htmlfile not in entry['urls'][self.hostname]:
11438c2ecf20Sopenharmony_ci					entry['urls'][self.hostname].append(self.htmlfile)
11448c2ecf20Sopenharmony_ci				found = True
11458c2ecf20Sopenharmony_ci				break
11468c2ecf20Sopenharmony_ci		if found:
11478c2ecf20Sopenharmony_ci			return
11488c2ecf20Sopenharmony_ci		arr = msg.split()
11498c2ecf20Sopenharmony_ci		for j in range(len(arr)):
11508c2ecf20Sopenharmony_ci			if re.match('^[0-9,\-\.]*$', arr[j]):
11518c2ecf20Sopenharmony_ci				arr[j] = '[0-9,\-\.]*'
11528c2ecf20Sopenharmony_ci			else:
11538c2ecf20Sopenharmony_ci				arr[j] = arr[j]\
11548c2ecf20Sopenharmony_ci					.replace('\\', '\\\\').replace(']', '\]').replace('[', '\[')\
11558c2ecf20Sopenharmony_ci					.replace('.', '\.').replace('+', '\+').replace('*', '\*')\
11568c2ecf20Sopenharmony_ci					.replace('(', '\(').replace(')', '\)').replace('}', '\}')\
11578c2ecf20Sopenharmony_ci					.replace('{', '\{')
11588c2ecf20Sopenharmony_ci		mstr = ' *'.join(arr)
11598c2ecf20Sopenharmony_ci		entry = {
11608c2ecf20Sopenharmony_ci			'line': msg,
11618c2ecf20Sopenharmony_ci			'match': mstr,
11628c2ecf20Sopenharmony_ci			'count': 1,
11638c2ecf20Sopenharmony_ci			'urls': {self.hostname: [self.htmlfile]}
11648c2ecf20Sopenharmony_ci		}
11658c2ecf20Sopenharmony_ci		errinfo.append(entry)
11668c2ecf20Sopenharmony_ci	def multistat(self, start, idx, finish):
11678c2ecf20Sopenharmony_ci		if 'time' in self.multitest:
11688c2ecf20Sopenharmony_ci			id = '%d Duration=%dmin' % (idx+1, self.multitest['time'])
11698c2ecf20Sopenharmony_ci		else:
11708c2ecf20Sopenharmony_ci			id = '%d/%d' % (idx+1, self.multitest['count'])
11718c2ecf20Sopenharmony_ci		t = time.time()
11728c2ecf20Sopenharmony_ci		if 'start' not in self.multitest:
11738c2ecf20Sopenharmony_ci			self.multitest['start'] = self.multitest['last'] = t
11748c2ecf20Sopenharmony_ci			self.multitest['total'] = 0.0
11758c2ecf20Sopenharmony_ci			pprint('TEST (%s) START' % id)
11768c2ecf20Sopenharmony_ci			return
11778c2ecf20Sopenharmony_ci		dt = t - self.multitest['last']
11788c2ecf20Sopenharmony_ci		if not start:
11798c2ecf20Sopenharmony_ci			if idx == 0 and self.multitest['delay'] > 0:
11808c2ecf20Sopenharmony_ci				self.multitest['total'] += self.multitest['delay']
11818c2ecf20Sopenharmony_ci			pprint('TEST (%s) COMPLETE -- Duration %.1fs' % (id, dt))
11828c2ecf20Sopenharmony_ci			return
11838c2ecf20Sopenharmony_ci		self.multitest['total'] += dt
11848c2ecf20Sopenharmony_ci		self.multitest['last'] = t
11858c2ecf20Sopenharmony_ci		avg = self.multitest['total'] / idx
11868c2ecf20Sopenharmony_ci		if 'time' in self.multitest:
11878c2ecf20Sopenharmony_ci			left = finish - datetime.now()
11888c2ecf20Sopenharmony_ci			left -= timedelta(microseconds=left.microseconds)
11898c2ecf20Sopenharmony_ci		else:
11908c2ecf20Sopenharmony_ci			left = timedelta(seconds=((self.multitest['count'] - idx) * int(avg)))
11918c2ecf20Sopenharmony_ci		pprint('TEST (%s) START - Avg Duration %.1fs, Time left %s' % \
11928c2ecf20Sopenharmony_ci			(id, avg, str(left)))
11938c2ecf20Sopenharmony_ci	def multiinit(self, c, d):
11948c2ecf20Sopenharmony_ci		sz, unit = 'count', 'm'
11958c2ecf20Sopenharmony_ci		if c.endswith('d') or c.endswith('h') or c.endswith('m'):
11968c2ecf20Sopenharmony_ci			sz, unit, c = 'time', c[-1], c[:-1]
11978c2ecf20Sopenharmony_ci		self.multitest['run'] = True
11988c2ecf20Sopenharmony_ci		self.multitest[sz] = getArgInt('multi: n d (exec count)', c, 1, 1000000, False)
11998c2ecf20Sopenharmony_ci		self.multitest['delay'] = getArgInt('multi: n d (delay between tests)', d, 0, 3600, False)
12008c2ecf20Sopenharmony_ci		if unit == 'd':
12018c2ecf20Sopenharmony_ci			self.multitest[sz] *= 1440
12028c2ecf20Sopenharmony_ci		elif unit == 'h':
12038c2ecf20Sopenharmony_ci			self.multitest[sz] *= 60
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_cisysvals = SystemValues()
12068c2ecf20Sopenharmony_ciswitchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0']
12078c2ecf20Sopenharmony_ciswitchoff = ['disable', 'off', 'false', '0']
12088c2ecf20Sopenharmony_cisuspendmodename = {
12098c2ecf20Sopenharmony_ci	'freeze': 'Freeze (S0)',
12108c2ecf20Sopenharmony_ci	'standby': 'Standby (S1)',
12118c2ecf20Sopenharmony_ci	'mem': 'Suspend (S3)',
12128c2ecf20Sopenharmony_ci	'disk': 'Hibernate (S4)'
12138c2ecf20Sopenharmony_ci}
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci# Class: DevProps
12168c2ecf20Sopenharmony_ci# Description:
12178c2ecf20Sopenharmony_ci#	 Simple class which holds property values collected
12188c2ecf20Sopenharmony_ci#	 for all the devices used in the timeline.
12198c2ecf20Sopenharmony_ciclass DevProps:
12208c2ecf20Sopenharmony_ci	def __init__(self):
12218c2ecf20Sopenharmony_ci		self.syspath = ''
12228c2ecf20Sopenharmony_ci		self.altname = ''
12238c2ecf20Sopenharmony_ci		self.isasync = True
12248c2ecf20Sopenharmony_ci		self.xtraclass = ''
12258c2ecf20Sopenharmony_ci		self.xtrainfo = ''
12268c2ecf20Sopenharmony_ci	def out(self, dev):
12278c2ecf20Sopenharmony_ci		return '%s,%s,%d;' % (dev, self.altname, self.isasync)
12288c2ecf20Sopenharmony_ci	def debug(self, dev):
12298c2ecf20Sopenharmony_ci		pprint('%s:\n\taltname = %s\n\t  async = %s' % (dev, self.altname, self.isasync))
12308c2ecf20Sopenharmony_ci	def altName(self, dev):
12318c2ecf20Sopenharmony_ci		if not self.altname or self.altname == dev:
12328c2ecf20Sopenharmony_ci			return dev
12338c2ecf20Sopenharmony_ci		return '%s [%s]' % (self.altname, dev)
12348c2ecf20Sopenharmony_ci	def xtraClass(self):
12358c2ecf20Sopenharmony_ci		if self.xtraclass:
12368c2ecf20Sopenharmony_ci			return ' '+self.xtraclass
12378c2ecf20Sopenharmony_ci		if not self.isasync:
12388c2ecf20Sopenharmony_ci			return ' sync'
12398c2ecf20Sopenharmony_ci		return ''
12408c2ecf20Sopenharmony_ci	def xtraInfo(self):
12418c2ecf20Sopenharmony_ci		if self.xtraclass:
12428c2ecf20Sopenharmony_ci			return ' '+self.xtraclass
12438c2ecf20Sopenharmony_ci		if self.isasync:
12448c2ecf20Sopenharmony_ci			return ' (async)'
12458c2ecf20Sopenharmony_ci		return ' (sync)'
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci# Class: DeviceNode
12488c2ecf20Sopenharmony_ci# Description:
12498c2ecf20Sopenharmony_ci#	 A container used to create a device hierachy, with a single root node
12508c2ecf20Sopenharmony_ci#	 and a tree of child nodes. Used by Data.deviceTopology()
12518c2ecf20Sopenharmony_ciclass DeviceNode:
12528c2ecf20Sopenharmony_ci	def __init__(self, nodename, nodedepth):
12538c2ecf20Sopenharmony_ci		self.name = nodename
12548c2ecf20Sopenharmony_ci		self.children = []
12558c2ecf20Sopenharmony_ci		self.depth = nodedepth
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci# Class: Data
12588c2ecf20Sopenharmony_ci# Description:
12598c2ecf20Sopenharmony_ci#	 The primary container for suspend/resume test data. There is one for
12608c2ecf20Sopenharmony_ci#	 each test run. The data is organized into a cronological hierarchy:
12618c2ecf20Sopenharmony_ci#	 Data.dmesg {
12628c2ecf20Sopenharmony_ci#		phases {
12638c2ecf20Sopenharmony_ci#			10 sequential, non-overlapping phases of S/R
12648c2ecf20Sopenharmony_ci#			contents: times for phase start/end, order/color data for html
12658c2ecf20Sopenharmony_ci#			devlist {
12668c2ecf20Sopenharmony_ci#				device callback or action list for this phase
12678c2ecf20Sopenharmony_ci#				device {
12688c2ecf20Sopenharmony_ci#					a single device callback or generic action
12698c2ecf20Sopenharmony_ci#					contents: start/stop times, pid/cpu/driver info
12708c2ecf20Sopenharmony_ci#						parents/children, html id for timeline/callgraph
12718c2ecf20Sopenharmony_ci#						optionally includes an ftrace callgraph
12728c2ecf20Sopenharmony_ci#						optionally includes dev/ps data
12738c2ecf20Sopenharmony_ci#				}
12748c2ecf20Sopenharmony_ci#			}
12758c2ecf20Sopenharmony_ci#		}
12768c2ecf20Sopenharmony_ci#	}
12778c2ecf20Sopenharmony_ci#
12788c2ecf20Sopenharmony_ciclass Data:
12798c2ecf20Sopenharmony_ci	phasedef = {
12808c2ecf20Sopenharmony_ci		'suspend_prepare': {'order': 0, 'color': '#CCFFCC'},
12818c2ecf20Sopenharmony_ci		        'suspend': {'order': 1, 'color': '#88FF88'},
12828c2ecf20Sopenharmony_ci		   'suspend_late': {'order': 2, 'color': '#00AA00'},
12838c2ecf20Sopenharmony_ci		  'suspend_noirq': {'order': 3, 'color': '#008888'},
12848c2ecf20Sopenharmony_ci		'suspend_machine': {'order': 4, 'color': '#0000FF'},
12858c2ecf20Sopenharmony_ci		 'resume_machine': {'order': 5, 'color': '#FF0000'},
12868c2ecf20Sopenharmony_ci		   'resume_noirq': {'order': 6, 'color': '#FF9900'},
12878c2ecf20Sopenharmony_ci		   'resume_early': {'order': 7, 'color': '#FFCC00'},
12888c2ecf20Sopenharmony_ci		         'resume': {'order': 8, 'color': '#FFFF88'},
12898c2ecf20Sopenharmony_ci		'resume_complete': {'order': 9, 'color': '#FFFFCC'},
12908c2ecf20Sopenharmony_ci	}
12918c2ecf20Sopenharmony_ci	errlist = {
12928c2ecf20Sopenharmony_ci		'HWERROR' : r'.*\[ *Hardware Error *\].*',
12938c2ecf20Sopenharmony_ci		'FWBUG'   : r'.*\[ *Firmware Bug *\].*',
12948c2ecf20Sopenharmony_ci		'BUG'     : r'(?i).*\bBUG\b.*',
12958c2ecf20Sopenharmony_ci		'ERROR'   : r'(?i).*\bERROR\b.*',
12968c2ecf20Sopenharmony_ci		'WARNING' : r'(?i).*\bWARNING\b.*',
12978c2ecf20Sopenharmony_ci		'FAULT'   : r'(?i).*\bFAULT\b.*',
12988c2ecf20Sopenharmony_ci		'FAIL'    : r'(?i).*\bFAILED\b.*',
12998c2ecf20Sopenharmony_ci		'INVALID' : r'(?i).*\bINVALID\b.*',
13008c2ecf20Sopenharmony_ci		'CRASH'   : r'(?i).*\bCRASHED\b.*',
13018c2ecf20Sopenharmony_ci		'TIMEOUT' : r'(?i).*\bTIMEOUT\b.*',
13028c2ecf20Sopenharmony_ci		'IRQ'     : r'.*\bgenirq: .*',
13038c2ecf20Sopenharmony_ci		'TASKFAIL': r'.*Freezing of tasks *.*',
13048c2ecf20Sopenharmony_ci		'ACPI'    : r'.*\bACPI *(?P<b>[A-Za-z]*) *Error[: ].*',
13058c2ecf20Sopenharmony_ci		'DISKFULL': r'.*\bNo space left on device.*',
13068c2ecf20Sopenharmony_ci		'USBERR'  : r'.*usb .*device .*, error [0-9-]*',
13078c2ecf20Sopenharmony_ci		'ATAERR'  : r' *ata[0-9\.]*: .*failed.*',
13088c2ecf20Sopenharmony_ci		'MEIERR'  : r' *mei.*: .*failed.*',
13098c2ecf20Sopenharmony_ci		'TPMERR'  : r'(?i) *tpm *tpm[0-9]*: .*error.*',
13108c2ecf20Sopenharmony_ci	}
13118c2ecf20Sopenharmony_ci	def __init__(self, num):
13128c2ecf20Sopenharmony_ci		idchar = 'abcdefghij'
13138c2ecf20Sopenharmony_ci		self.start = 0.0 # test start
13148c2ecf20Sopenharmony_ci		self.end = 0.0   # test end
13158c2ecf20Sopenharmony_ci		self.hwstart = 0 # rtc test start
13168c2ecf20Sopenharmony_ci		self.hwend = 0   # rtc test end
13178c2ecf20Sopenharmony_ci		self.tSuspended = 0.0 # low-level suspend start
13188c2ecf20Sopenharmony_ci		self.tResumed = 0.0   # low-level resume start
13198c2ecf20Sopenharmony_ci		self.tKernSus = 0.0   # kernel level suspend start
13208c2ecf20Sopenharmony_ci		self.tKernRes = 0.0   # kernel level resume end
13218c2ecf20Sopenharmony_ci		self.fwValid = False  # is firmware data available
13228c2ecf20Sopenharmony_ci		self.fwSuspend = 0    # time spent in firmware suspend
13238c2ecf20Sopenharmony_ci		self.fwResume = 0     # time spent in firmware resume
13248c2ecf20Sopenharmony_ci		self.html_device_id = 0
13258c2ecf20Sopenharmony_ci		self.stamp = 0
13268c2ecf20Sopenharmony_ci		self.outfile = ''
13278c2ecf20Sopenharmony_ci		self.kerror = False
13288c2ecf20Sopenharmony_ci		self.wifi = dict()
13298c2ecf20Sopenharmony_ci		self.turbostat = 0
13308c2ecf20Sopenharmony_ci		self.enterfail = ''
13318c2ecf20Sopenharmony_ci		self.currphase = ''
13328c2ecf20Sopenharmony_ci		self.pstl = dict()    # process timeline
13338c2ecf20Sopenharmony_ci		self.testnumber = num
13348c2ecf20Sopenharmony_ci		self.idstr = idchar[num]
13358c2ecf20Sopenharmony_ci		self.dmesgtext = []   # dmesg text file in memory
13368c2ecf20Sopenharmony_ci		self.dmesg = dict()   # root data structure
13378c2ecf20Sopenharmony_ci		self.errorinfo = {'suspend':[],'resume':[]}
13388c2ecf20Sopenharmony_ci		self.tLow = []        # time spent in low-level suspends (standby/freeze)
13398c2ecf20Sopenharmony_ci		self.devpids = []
13408c2ecf20Sopenharmony_ci		self.devicegroups = 0
13418c2ecf20Sopenharmony_ci	def sortedPhases(self):
13428c2ecf20Sopenharmony_ci		return sorted(self.dmesg, key=lambda k:self.dmesg[k]['order'])
13438c2ecf20Sopenharmony_ci	def initDevicegroups(self):
13448c2ecf20Sopenharmony_ci		# called when phases are all finished being added
13458c2ecf20Sopenharmony_ci		for phase in sorted(self.dmesg.keys()):
13468c2ecf20Sopenharmony_ci			if '*' in phase:
13478c2ecf20Sopenharmony_ci				p = phase.split('*')
13488c2ecf20Sopenharmony_ci				pnew = '%s%d' % (p[0], len(p))
13498c2ecf20Sopenharmony_ci				self.dmesg[pnew] = self.dmesg.pop(phase)
13508c2ecf20Sopenharmony_ci		self.devicegroups = []
13518c2ecf20Sopenharmony_ci		for phase in self.sortedPhases():
13528c2ecf20Sopenharmony_ci			self.devicegroups.append([phase])
13538c2ecf20Sopenharmony_ci	def nextPhase(self, phase, offset):
13548c2ecf20Sopenharmony_ci		order = self.dmesg[phase]['order'] + offset
13558c2ecf20Sopenharmony_ci		for p in self.dmesg:
13568c2ecf20Sopenharmony_ci			if self.dmesg[p]['order'] == order:
13578c2ecf20Sopenharmony_ci				return p
13588c2ecf20Sopenharmony_ci		return ''
13598c2ecf20Sopenharmony_ci	def lastPhase(self, depth=1):
13608c2ecf20Sopenharmony_ci		plist = self.sortedPhases()
13618c2ecf20Sopenharmony_ci		if len(plist) < depth:
13628c2ecf20Sopenharmony_ci			return ''
13638c2ecf20Sopenharmony_ci		return plist[-1*depth]
13648c2ecf20Sopenharmony_ci	def turbostatInfo(self):
13658c2ecf20Sopenharmony_ci		tp = TestProps()
13668c2ecf20Sopenharmony_ci		out = {'syslpi':'N/A','pkgpc10':'N/A'}
13678c2ecf20Sopenharmony_ci		for line in self.dmesgtext:
13688c2ecf20Sopenharmony_ci			m = re.match(tp.tstatfmt, line)
13698c2ecf20Sopenharmony_ci			if not m:
13708c2ecf20Sopenharmony_ci				continue
13718c2ecf20Sopenharmony_ci			for i in m.group('t').split('|'):
13728c2ecf20Sopenharmony_ci				if 'SYS%LPI' in i:
13738c2ecf20Sopenharmony_ci					out['syslpi'] = i.split('=')[-1]+'%'
13748c2ecf20Sopenharmony_ci				elif 'pc10' in i:
13758c2ecf20Sopenharmony_ci					out['pkgpc10'] = i.split('=')[-1]+'%'
13768c2ecf20Sopenharmony_ci			break
13778c2ecf20Sopenharmony_ci		return out
13788c2ecf20Sopenharmony_ci	def extractErrorInfo(self):
13798c2ecf20Sopenharmony_ci		lf = self.dmesgtext
13808c2ecf20Sopenharmony_ci		if len(self.dmesgtext) < 1 and sysvals.dmesgfile:
13818c2ecf20Sopenharmony_ci			lf = sysvals.openlog(sysvals.dmesgfile, 'r')
13828c2ecf20Sopenharmony_ci		i = 0
13838c2ecf20Sopenharmony_ci		tp = TestProps()
13848c2ecf20Sopenharmony_ci		list = []
13858c2ecf20Sopenharmony_ci		for line in lf:
13868c2ecf20Sopenharmony_ci			i += 1
13878c2ecf20Sopenharmony_ci			if tp.stampInfo(line, sysvals):
13888c2ecf20Sopenharmony_ci				continue
13898c2ecf20Sopenharmony_ci			m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
13908c2ecf20Sopenharmony_ci			if not m:
13918c2ecf20Sopenharmony_ci				continue
13928c2ecf20Sopenharmony_ci			t = float(m.group('ktime'))
13938c2ecf20Sopenharmony_ci			if t < self.start or t > self.end:
13948c2ecf20Sopenharmony_ci				continue
13958c2ecf20Sopenharmony_ci			dir = 'suspend' if t < self.tSuspended else 'resume'
13968c2ecf20Sopenharmony_ci			msg = m.group('msg')
13978c2ecf20Sopenharmony_ci			if re.match('capability: warning: .*', msg):
13988c2ecf20Sopenharmony_ci				continue
13998c2ecf20Sopenharmony_ci			for err in self.errlist:
14008c2ecf20Sopenharmony_ci				if re.match(self.errlist[err], msg):
14018c2ecf20Sopenharmony_ci					list.append((msg, err, dir, t, i, i))
14028c2ecf20Sopenharmony_ci					self.kerror = True
14038c2ecf20Sopenharmony_ci					break
14048c2ecf20Sopenharmony_ci		tp.msglist = []
14058c2ecf20Sopenharmony_ci		for msg, type, dir, t, idx1, idx2 in list:
14068c2ecf20Sopenharmony_ci			tp.msglist.append(msg)
14078c2ecf20Sopenharmony_ci			self.errorinfo[dir].append((type, t, idx1, idx2))
14088c2ecf20Sopenharmony_ci		if self.kerror:
14098c2ecf20Sopenharmony_ci			sysvals.dmesglog = True
14108c2ecf20Sopenharmony_ci		if len(self.dmesgtext) < 1 and sysvals.dmesgfile:
14118c2ecf20Sopenharmony_ci			lf.close()
14128c2ecf20Sopenharmony_ci		return tp
14138c2ecf20Sopenharmony_ci	def setStart(self, time, msg=''):
14148c2ecf20Sopenharmony_ci		self.start = time
14158c2ecf20Sopenharmony_ci		if msg:
14168c2ecf20Sopenharmony_ci			try:
14178c2ecf20Sopenharmony_ci				self.hwstart = datetime.strptime(msg, sysvals.tmstart)
14188c2ecf20Sopenharmony_ci			except:
14198c2ecf20Sopenharmony_ci				self.hwstart = 0
14208c2ecf20Sopenharmony_ci	def setEnd(self, time, msg=''):
14218c2ecf20Sopenharmony_ci		self.end = time
14228c2ecf20Sopenharmony_ci		if msg:
14238c2ecf20Sopenharmony_ci			try:
14248c2ecf20Sopenharmony_ci				self.hwend = datetime.strptime(msg, sysvals.tmend)
14258c2ecf20Sopenharmony_ci			except:
14268c2ecf20Sopenharmony_ci				self.hwend = 0
14278c2ecf20Sopenharmony_ci	def isTraceEventOutsideDeviceCalls(self, pid, time):
14288c2ecf20Sopenharmony_ci		for phase in self.sortedPhases():
14298c2ecf20Sopenharmony_ci			list = self.dmesg[phase]['list']
14308c2ecf20Sopenharmony_ci			for dev in list:
14318c2ecf20Sopenharmony_ci				d = list[dev]
14328c2ecf20Sopenharmony_ci				if(d['pid'] == pid and time >= d['start'] and
14338c2ecf20Sopenharmony_ci					time < d['end']):
14348c2ecf20Sopenharmony_ci					return False
14358c2ecf20Sopenharmony_ci		return True
14368c2ecf20Sopenharmony_ci	def sourcePhase(self, start):
14378c2ecf20Sopenharmony_ci		for phase in self.sortedPhases():
14388c2ecf20Sopenharmony_ci			if 'machine' in phase:
14398c2ecf20Sopenharmony_ci				continue
14408c2ecf20Sopenharmony_ci			pend = self.dmesg[phase]['end']
14418c2ecf20Sopenharmony_ci			if start <= pend:
14428c2ecf20Sopenharmony_ci				return phase
14438c2ecf20Sopenharmony_ci		return 'resume_complete'
14448c2ecf20Sopenharmony_ci	def sourceDevice(self, phaselist, start, end, pid, type):
14458c2ecf20Sopenharmony_ci		tgtdev = ''
14468c2ecf20Sopenharmony_ci		for phase in phaselist:
14478c2ecf20Sopenharmony_ci			list = self.dmesg[phase]['list']
14488c2ecf20Sopenharmony_ci			for devname in list:
14498c2ecf20Sopenharmony_ci				dev = list[devname]
14508c2ecf20Sopenharmony_ci				# pid must match
14518c2ecf20Sopenharmony_ci				if dev['pid'] != pid:
14528c2ecf20Sopenharmony_ci					continue
14538c2ecf20Sopenharmony_ci				devS = dev['start']
14548c2ecf20Sopenharmony_ci				devE = dev['end']
14558c2ecf20Sopenharmony_ci				if type == 'device':
14568c2ecf20Sopenharmony_ci					# device target event is entirely inside the source boundary
14578c2ecf20Sopenharmony_ci					if(start < devS or start >= devE or end <= devS or end > devE):
14588c2ecf20Sopenharmony_ci						continue
14598c2ecf20Sopenharmony_ci				elif type == 'thread':
14608c2ecf20Sopenharmony_ci					# thread target event will expand the source boundary
14618c2ecf20Sopenharmony_ci					if start < devS:
14628c2ecf20Sopenharmony_ci						dev['start'] = start
14638c2ecf20Sopenharmony_ci					if end > devE:
14648c2ecf20Sopenharmony_ci						dev['end'] = end
14658c2ecf20Sopenharmony_ci				tgtdev = dev
14668c2ecf20Sopenharmony_ci				break
14678c2ecf20Sopenharmony_ci		return tgtdev
14688c2ecf20Sopenharmony_ci	def addDeviceFunctionCall(self, displayname, kprobename, proc, pid, start, end, cdata, rdata):
14698c2ecf20Sopenharmony_ci		# try to place the call in a device
14708c2ecf20Sopenharmony_ci		phases = self.sortedPhases()
14718c2ecf20Sopenharmony_ci		tgtdev = self.sourceDevice(phases, start, end, pid, 'device')
14728c2ecf20Sopenharmony_ci		# calls with device pids that occur outside device bounds are dropped
14738c2ecf20Sopenharmony_ci		# TODO: include these somehow
14748c2ecf20Sopenharmony_ci		if not tgtdev and pid in self.devpids:
14758c2ecf20Sopenharmony_ci			return False
14768c2ecf20Sopenharmony_ci		# try to place the call in a thread
14778c2ecf20Sopenharmony_ci		if not tgtdev:
14788c2ecf20Sopenharmony_ci			tgtdev = self.sourceDevice(phases, start, end, pid, 'thread')
14798c2ecf20Sopenharmony_ci		# create new thread blocks, expand as new calls are found
14808c2ecf20Sopenharmony_ci		if not tgtdev:
14818c2ecf20Sopenharmony_ci			if proc == '<...>':
14828c2ecf20Sopenharmony_ci				threadname = 'kthread-%d' % (pid)
14838c2ecf20Sopenharmony_ci			else:
14848c2ecf20Sopenharmony_ci				threadname = '%s-%d' % (proc, pid)
14858c2ecf20Sopenharmony_ci			tgtphase = self.sourcePhase(start)
14868c2ecf20Sopenharmony_ci			self.newAction(tgtphase, threadname, pid, '', start, end, '', ' kth', '')
14878c2ecf20Sopenharmony_ci			return self.addDeviceFunctionCall(displayname, kprobename, proc, pid, start, end, cdata, rdata)
14888c2ecf20Sopenharmony_ci		# this should not happen
14898c2ecf20Sopenharmony_ci		if not tgtdev:
14908c2ecf20Sopenharmony_ci			sysvals.vprint('[%f - %f] %s-%d %s %s %s' % \
14918c2ecf20Sopenharmony_ci				(start, end, proc, pid, kprobename, cdata, rdata))
14928c2ecf20Sopenharmony_ci			return False
14938c2ecf20Sopenharmony_ci		# place the call data inside the src element of the tgtdev
14948c2ecf20Sopenharmony_ci		if('src' not in tgtdev):
14958c2ecf20Sopenharmony_ci			tgtdev['src'] = []
14968c2ecf20Sopenharmony_ci		dtf = sysvals.dev_tracefuncs
14978c2ecf20Sopenharmony_ci		ubiquitous = False
14988c2ecf20Sopenharmony_ci		if kprobename in dtf and 'ub' in dtf[kprobename]:
14998c2ecf20Sopenharmony_ci			ubiquitous = True
15008c2ecf20Sopenharmony_ci		title = cdata+' '+rdata
15018c2ecf20Sopenharmony_ci		mstr = '\(.*\) *(?P<args>.*) *\((?P<caller>.*)\+.* arg1=(?P<ret>.*)'
15028c2ecf20Sopenharmony_ci		m = re.match(mstr, title)
15038c2ecf20Sopenharmony_ci		if m:
15048c2ecf20Sopenharmony_ci			c = m.group('caller')
15058c2ecf20Sopenharmony_ci			a = m.group('args').strip()
15068c2ecf20Sopenharmony_ci			r = m.group('ret')
15078c2ecf20Sopenharmony_ci			if len(r) > 6:
15088c2ecf20Sopenharmony_ci				r = ''
15098c2ecf20Sopenharmony_ci			else:
15108c2ecf20Sopenharmony_ci				r = 'ret=%s ' % r
15118c2ecf20Sopenharmony_ci			if ubiquitous and c in dtf and 'ub' in dtf[c]:
15128c2ecf20Sopenharmony_ci				return False
15138c2ecf20Sopenharmony_ci		color = sysvals.kprobeColor(kprobename)
15148c2ecf20Sopenharmony_ci		e = DevFunction(displayname, a, c, r, start, end, ubiquitous, proc, pid, color)
15158c2ecf20Sopenharmony_ci		tgtdev['src'].append(e)
15168c2ecf20Sopenharmony_ci		return True
15178c2ecf20Sopenharmony_ci	def overflowDevices(self):
15188c2ecf20Sopenharmony_ci		# get a list of devices that extend beyond the end of this test run
15198c2ecf20Sopenharmony_ci		devlist = []
15208c2ecf20Sopenharmony_ci		for phase in self.sortedPhases():
15218c2ecf20Sopenharmony_ci			list = self.dmesg[phase]['list']
15228c2ecf20Sopenharmony_ci			for devname in list:
15238c2ecf20Sopenharmony_ci				dev = list[devname]
15248c2ecf20Sopenharmony_ci				if dev['end'] > self.end:
15258c2ecf20Sopenharmony_ci					devlist.append(dev)
15268c2ecf20Sopenharmony_ci		return devlist
15278c2ecf20Sopenharmony_ci	def mergeOverlapDevices(self, devlist):
15288c2ecf20Sopenharmony_ci		# merge any devices that overlap devlist
15298c2ecf20Sopenharmony_ci		for dev in devlist:
15308c2ecf20Sopenharmony_ci			devname = dev['name']
15318c2ecf20Sopenharmony_ci			for phase in self.sortedPhases():
15328c2ecf20Sopenharmony_ci				list = self.dmesg[phase]['list']
15338c2ecf20Sopenharmony_ci				if devname not in list:
15348c2ecf20Sopenharmony_ci					continue
15358c2ecf20Sopenharmony_ci				tdev = list[devname]
15368c2ecf20Sopenharmony_ci				o = min(dev['end'], tdev['end']) - max(dev['start'], tdev['start'])
15378c2ecf20Sopenharmony_ci				if o <= 0:
15388c2ecf20Sopenharmony_ci					continue
15398c2ecf20Sopenharmony_ci				dev['end'] = tdev['end']
15408c2ecf20Sopenharmony_ci				if 'src' not in dev or 'src' not in tdev:
15418c2ecf20Sopenharmony_ci					continue
15428c2ecf20Sopenharmony_ci				dev['src'] += tdev['src']
15438c2ecf20Sopenharmony_ci				del list[devname]
15448c2ecf20Sopenharmony_ci	def usurpTouchingThread(self, name, dev):
15458c2ecf20Sopenharmony_ci		# the caller test has priority of this thread, give it to him
15468c2ecf20Sopenharmony_ci		for phase in self.sortedPhases():
15478c2ecf20Sopenharmony_ci			list = self.dmesg[phase]['list']
15488c2ecf20Sopenharmony_ci			if name in list:
15498c2ecf20Sopenharmony_ci				tdev = list[name]
15508c2ecf20Sopenharmony_ci				if tdev['start'] - dev['end'] < 0.1:
15518c2ecf20Sopenharmony_ci					dev['end'] = tdev['end']
15528c2ecf20Sopenharmony_ci					if 'src' not in dev:
15538c2ecf20Sopenharmony_ci						dev['src'] = []
15548c2ecf20Sopenharmony_ci					if 'src' in tdev:
15558c2ecf20Sopenharmony_ci						dev['src'] += tdev['src']
15568c2ecf20Sopenharmony_ci					del list[name]
15578c2ecf20Sopenharmony_ci				break
15588c2ecf20Sopenharmony_ci	def stitchTouchingThreads(self, testlist):
15598c2ecf20Sopenharmony_ci		# merge any threads between tests that touch
15608c2ecf20Sopenharmony_ci		for phase in self.sortedPhases():
15618c2ecf20Sopenharmony_ci			list = self.dmesg[phase]['list']
15628c2ecf20Sopenharmony_ci			for devname in list:
15638c2ecf20Sopenharmony_ci				dev = list[devname]
15648c2ecf20Sopenharmony_ci				if 'htmlclass' not in dev or 'kth' not in dev['htmlclass']:
15658c2ecf20Sopenharmony_ci					continue
15668c2ecf20Sopenharmony_ci				for data in testlist:
15678c2ecf20Sopenharmony_ci					data.usurpTouchingThread(devname, dev)
15688c2ecf20Sopenharmony_ci	def optimizeDevSrc(self):
15698c2ecf20Sopenharmony_ci		# merge any src call loops to reduce timeline size
15708c2ecf20Sopenharmony_ci		for phase in self.sortedPhases():
15718c2ecf20Sopenharmony_ci			list = self.dmesg[phase]['list']
15728c2ecf20Sopenharmony_ci			for dev in list:
15738c2ecf20Sopenharmony_ci				if 'src' not in list[dev]:
15748c2ecf20Sopenharmony_ci					continue
15758c2ecf20Sopenharmony_ci				src = list[dev]['src']
15768c2ecf20Sopenharmony_ci				p = 0
15778c2ecf20Sopenharmony_ci				for e in sorted(src, key=lambda event: event.time):
15788c2ecf20Sopenharmony_ci					if not p or not e.repeat(p):
15798c2ecf20Sopenharmony_ci						p = e
15808c2ecf20Sopenharmony_ci						continue
15818c2ecf20Sopenharmony_ci					# e is another iteration of p, move it into p
15828c2ecf20Sopenharmony_ci					p.end = e.end
15838c2ecf20Sopenharmony_ci					p.length = p.end - p.time
15848c2ecf20Sopenharmony_ci					p.count += 1
15858c2ecf20Sopenharmony_ci					src.remove(e)
15868c2ecf20Sopenharmony_ci	def trimTimeVal(self, t, t0, dT, left):
15878c2ecf20Sopenharmony_ci		if left:
15888c2ecf20Sopenharmony_ci			if(t > t0):
15898c2ecf20Sopenharmony_ci				if(t - dT < t0):
15908c2ecf20Sopenharmony_ci					return t0
15918c2ecf20Sopenharmony_ci				return t - dT
15928c2ecf20Sopenharmony_ci			else:
15938c2ecf20Sopenharmony_ci				return t
15948c2ecf20Sopenharmony_ci		else:
15958c2ecf20Sopenharmony_ci			if(t < t0 + dT):
15968c2ecf20Sopenharmony_ci				if(t > t0):
15978c2ecf20Sopenharmony_ci					return t0 + dT
15988c2ecf20Sopenharmony_ci				return t + dT
15998c2ecf20Sopenharmony_ci			else:
16008c2ecf20Sopenharmony_ci				return t
16018c2ecf20Sopenharmony_ci	def trimTime(self, t0, dT, left):
16028c2ecf20Sopenharmony_ci		self.tSuspended = self.trimTimeVal(self.tSuspended, t0, dT, left)
16038c2ecf20Sopenharmony_ci		self.tResumed = self.trimTimeVal(self.tResumed, t0, dT, left)
16048c2ecf20Sopenharmony_ci		self.start = self.trimTimeVal(self.start, t0, dT, left)
16058c2ecf20Sopenharmony_ci		self.tKernSus = self.trimTimeVal(self.tKernSus, t0, dT, left)
16068c2ecf20Sopenharmony_ci		self.tKernRes = self.trimTimeVal(self.tKernRes, t0, dT, left)
16078c2ecf20Sopenharmony_ci		self.end = self.trimTimeVal(self.end, t0, dT, left)
16088c2ecf20Sopenharmony_ci		for phase in self.sortedPhases():
16098c2ecf20Sopenharmony_ci			p = self.dmesg[phase]
16108c2ecf20Sopenharmony_ci			p['start'] = self.trimTimeVal(p['start'], t0, dT, left)
16118c2ecf20Sopenharmony_ci			p['end'] = self.trimTimeVal(p['end'], t0, dT, left)
16128c2ecf20Sopenharmony_ci			list = p['list']
16138c2ecf20Sopenharmony_ci			for name in list:
16148c2ecf20Sopenharmony_ci				d = list[name]
16158c2ecf20Sopenharmony_ci				d['start'] = self.trimTimeVal(d['start'], t0, dT, left)
16168c2ecf20Sopenharmony_ci				d['end'] = self.trimTimeVal(d['end'], t0, dT, left)
16178c2ecf20Sopenharmony_ci				d['length'] = d['end'] - d['start']
16188c2ecf20Sopenharmony_ci				if('ftrace' in d):
16198c2ecf20Sopenharmony_ci					cg = d['ftrace']
16208c2ecf20Sopenharmony_ci					cg.start = self.trimTimeVal(cg.start, t0, dT, left)
16218c2ecf20Sopenharmony_ci					cg.end = self.trimTimeVal(cg.end, t0, dT, left)
16228c2ecf20Sopenharmony_ci					for line in cg.list:
16238c2ecf20Sopenharmony_ci						line.time = self.trimTimeVal(line.time, t0, dT, left)
16248c2ecf20Sopenharmony_ci				if('src' in d):
16258c2ecf20Sopenharmony_ci					for e in d['src']:
16268c2ecf20Sopenharmony_ci						e.time = self.trimTimeVal(e.time, t0, dT, left)
16278c2ecf20Sopenharmony_ci						e.end = self.trimTimeVal(e.end, t0, dT, left)
16288c2ecf20Sopenharmony_ci						e.length = e.end - e.time
16298c2ecf20Sopenharmony_ci		for dir in ['suspend', 'resume']:
16308c2ecf20Sopenharmony_ci			list = []
16318c2ecf20Sopenharmony_ci			for e in self.errorinfo[dir]:
16328c2ecf20Sopenharmony_ci				type, tm, idx1, idx2 = e
16338c2ecf20Sopenharmony_ci				tm = self.trimTimeVal(tm, t0, dT, left)
16348c2ecf20Sopenharmony_ci				list.append((type, tm, idx1, idx2))
16358c2ecf20Sopenharmony_ci			self.errorinfo[dir] = list
16368c2ecf20Sopenharmony_ci	def trimFreezeTime(self, tZero):
16378c2ecf20Sopenharmony_ci		# trim out any standby or freeze clock time
16388c2ecf20Sopenharmony_ci		lp = ''
16398c2ecf20Sopenharmony_ci		for phase in self.sortedPhases():
16408c2ecf20Sopenharmony_ci			if 'resume_machine' in phase and 'suspend_machine' in lp:
16418c2ecf20Sopenharmony_ci				tS, tR = self.dmesg[lp]['end'], self.dmesg[phase]['start']
16428c2ecf20Sopenharmony_ci				tL = tR - tS
16438c2ecf20Sopenharmony_ci				if tL > 0:
16448c2ecf20Sopenharmony_ci					left = True if tR > tZero else False
16458c2ecf20Sopenharmony_ci					self.trimTime(tS, tL, left)
16468c2ecf20Sopenharmony_ci					if 'trying' in self.dmesg[lp] and self.dmesg[lp]['trying'] >= 0.001:
16478c2ecf20Sopenharmony_ci						tTry = round(self.dmesg[lp]['trying'] * 1000)
16488c2ecf20Sopenharmony_ci						text = '%.0f (-%.0f waking)' % (tL * 1000, tTry)
16498c2ecf20Sopenharmony_ci					else:
16508c2ecf20Sopenharmony_ci						text = '%.0f' % (tL * 1000)
16518c2ecf20Sopenharmony_ci					self.tLow.append(text)
16528c2ecf20Sopenharmony_ci			lp = phase
16538c2ecf20Sopenharmony_ci	def getMemTime(self):
16548c2ecf20Sopenharmony_ci		if not self.hwstart or not self.hwend:
16558c2ecf20Sopenharmony_ci			return
16568c2ecf20Sopenharmony_ci		stime = (self.tSuspended - self.start) * 1000000
16578c2ecf20Sopenharmony_ci		rtime = (self.end - self.tResumed) * 1000000
16588c2ecf20Sopenharmony_ci		hws = self.hwstart + timedelta(microseconds=stime)
16598c2ecf20Sopenharmony_ci		hwr = self.hwend - timedelta(microseconds=rtime)
16608c2ecf20Sopenharmony_ci		self.tLow.append('%.0f'%((hwr - hws).total_seconds() * 1000))
16618c2ecf20Sopenharmony_ci	def getTimeValues(self):
16628c2ecf20Sopenharmony_ci		sktime = (self.tSuspended - self.tKernSus) * 1000
16638c2ecf20Sopenharmony_ci		rktime = (self.tKernRes - self.tResumed) * 1000
16648c2ecf20Sopenharmony_ci		return (sktime, rktime)
16658c2ecf20Sopenharmony_ci	def setPhase(self, phase, ktime, isbegin, order=-1):
16668c2ecf20Sopenharmony_ci		if(isbegin):
16678c2ecf20Sopenharmony_ci			# phase start over current phase
16688c2ecf20Sopenharmony_ci			if self.currphase:
16698c2ecf20Sopenharmony_ci				if 'resume_machine' not in self.currphase:
16708c2ecf20Sopenharmony_ci					sysvals.vprint('WARNING: phase %s failed to end' % self.currphase)
16718c2ecf20Sopenharmony_ci				self.dmesg[self.currphase]['end'] = ktime
16728c2ecf20Sopenharmony_ci			phases = self.dmesg.keys()
16738c2ecf20Sopenharmony_ci			color = self.phasedef[phase]['color']
16748c2ecf20Sopenharmony_ci			count = len(phases) if order < 0 else order
16758c2ecf20Sopenharmony_ci			# create unique name for every new phase
16768c2ecf20Sopenharmony_ci			while phase in phases:
16778c2ecf20Sopenharmony_ci				phase += '*'
16788c2ecf20Sopenharmony_ci			self.dmesg[phase] = {'list': dict(), 'start': -1.0, 'end': -1.0,
16798c2ecf20Sopenharmony_ci				'row': 0, 'color': color, 'order': count}
16808c2ecf20Sopenharmony_ci			self.dmesg[phase]['start'] = ktime
16818c2ecf20Sopenharmony_ci			self.currphase = phase
16828c2ecf20Sopenharmony_ci		else:
16838c2ecf20Sopenharmony_ci			# phase end without a start
16848c2ecf20Sopenharmony_ci			if phase not in self.currphase:
16858c2ecf20Sopenharmony_ci				if self.currphase:
16868c2ecf20Sopenharmony_ci					sysvals.vprint('WARNING: %s ended instead of %s, ftrace corruption?' % (phase, self.currphase))
16878c2ecf20Sopenharmony_ci				else:
16888c2ecf20Sopenharmony_ci					sysvals.vprint('WARNING: %s ended without a start, ftrace corruption?' % phase)
16898c2ecf20Sopenharmony_ci					return phase
16908c2ecf20Sopenharmony_ci			phase = self.currphase
16918c2ecf20Sopenharmony_ci			self.dmesg[phase]['end'] = ktime
16928c2ecf20Sopenharmony_ci			self.currphase = ''
16938c2ecf20Sopenharmony_ci		return phase
16948c2ecf20Sopenharmony_ci	def sortedDevices(self, phase):
16958c2ecf20Sopenharmony_ci		list = self.dmesg[phase]['list']
16968c2ecf20Sopenharmony_ci		return sorted(list, key=lambda k:list[k]['start'])
16978c2ecf20Sopenharmony_ci	def fixupInitcalls(self, phase):
16988c2ecf20Sopenharmony_ci		# if any calls never returned, clip them at system resume end
16998c2ecf20Sopenharmony_ci		phaselist = self.dmesg[phase]['list']
17008c2ecf20Sopenharmony_ci		for devname in phaselist:
17018c2ecf20Sopenharmony_ci			dev = phaselist[devname]
17028c2ecf20Sopenharmony_ci			if(dev['end'] < 0):
17038c2ecf20Sopenharmony_ci				for p in self.sortedPhases():
17048c2ecf20Sopenharmony_ci					if self.dmesg[p]['end'] > dev['start']:
17058c2ecf20Sopenharmony_ci						dev['end'] = self.dmesg[p]['end']
17068c2ecf20Sopenharmony_ci						break
17078c2ecf20Sopenharmony_ci				sysvals.vprint('%s (%s): callback didnt return' % (devname, phase))
17088c2ecf20Sopenharmony_ci	def deviceFilter(self, devicefilter):
17098c2ecf20Sopenharmony_ci		for phase in self.sortedPhases():
17108c2ecf20Sopenharmony_ci			list = self.dmesg[phase]['list']
17118c2ecf20Sopenharmony_ci			rmlist = []
17128c2ecf20Sopenharmony_ci			for name in list:
17138c2ecf20Sopenharmony_ci				keep = False
17148c2ecf20Sopenharmony_ci				for filter in devicefilter:
17158c2ecf20Sopenharmony_ci					if filter in name or \
17168c2ecf20Sopenharmony_ci						('drv' in list[name] and filter in list[name]['drv']):
17178c2ecf20Sopenharmony_ci						keep = True
17188c2ecf20Sopenharmony_ci				if not keep:
17198c2ecf20Sopenharmony_ci					rmlist.append(name)
17208c2ecf20Sopenharmony_ci			for name in rmlist:
17218c2ecf20Sopenharmony_ci				del list[name]
17228c2ecf20Sopenharmony_ci	def fixupInitcallsThatDidntReturn(self):
17238c2ecf20Sopenharmony_ci		# if any calls never returned, clip them at system resume end
17248c2ecf20Sopenharmony_ci		for phase in self.sortedPhases():
17258c2ecf20Sopenharmony_ci			self.fixupInitcalls(phase)
17268c2ecf20Sopenharmony_ci	def phaseOverlap(self, phases):
17278c2ecf20Sopenharmony_ci		rmgroups = []
17288c2ecf20Sopenharmony_ci		newgroup = []
17298c2ecf20Sopenharmony_ci		for group in self.devicegroups:
17308c2ecf20Sopenharmony_ci			for phase in phases:
17318c2ecf20Sopenharmony_ci				if phase not in group:
17328c2ecf20Sopenharmony_ci					continue
17338c2ecf20Sopenharmony_ci				for p in group:
17348c2ecf20Sopenharmony_ci					if p not in newgroup:
17358c2ecf20Sopenharmony_ci						newgroup.append(p)
17368c2ecf20Sopenharmony_ci				if group not in rmgroups:
17378c2ecf20Sopenharmony_ci					rmgroups.append(group)
17388c2ecf20Sopenharmony_ci		for group in rmgroups:
17398c2ecf20Sopenharmony_ci			self.devicegroups.remove(group)
17408c2ecf20Sopenharmony_ci		self.devicegroups.append(newgroup)
17418c2ecf20Sopenharmony_ci	def newActionGlobal(self, name, start, end, pid=-1, color=''):
17428c2ecf20Sopenharmony_ci		# which phase is this device callback or action in
17438c2ecf20Sopenharmony_ci		phases = self.sortedPhases()
17448c2ecf20Sopenharmony_ci		targetphase = 'none'
17458c2ecf20Sopenharmony_ci		htmlclass = ''
17468c2ecf20Sopenharmony_ci		overlap = 0.0
17478c2ecf20Sopenharmony_ci		myphases = []
17488c2ecf20Sopenharmony_ci		for phase in phases:
17498c2ecf20Sopenharmony_ci			pstart = self.dmesg[phase]['start']
17508c2ecf20Sopenharmony_ci			pend = self.dmesg[phase]['end']
17518c2ecf20Sopenharmony_ci			# see if the action overlaps this phase
17528c2ecf20Sopenharmony_ci			o = max(0, min(end, pend) - max(start, pstart))
17538c2ecf20Sopenharmony_ci			if o > 0:
17548c2ecf20Sopenharmony_ci				myphases.append(phase)
17558c2ecf20Sopenharmony_ci			# set the target phase to the one that overlaps most
17568c2ecf20Sopenharmony_ci			if o > overlap:
17578c2ecf20Sopenharmony_ci				if overlap > 0 and phase == 'post_resume':
17588c2ecf20Sopenharmony_ci					continue
17598c2ecf20Sopenharmony_ci				targetphase = phase
17608c2ecf20Sopenharmony_ci				overlap = o
17618c2ecf20Sopenharmony_ci		# if no target phase was found, pin it to the edge
17628c2ecf20Sopenharmony_ci		if targetphase == 'none':
17638c2ecf20Sopenharmony_ci			p0start = self.dmesg[phases[0]]['start']
17648c2ecf20Sopenharmony_ci			if start <= p0start:
17658c2ecf20Sopenharmony_ci				targetphase = phases[0]
17668c2ecf20Sopenharmony_ci			else:
17678c2ecf20Sopenharmony_ci				targetphase = phases[-1]
17688c2ecf20Sopenharmony_ci		if pid == -2:
17698c2ecf20Sopenharmony_ci			htmlclass = ' bg'
17708c2ecf20Sopenharmony_ci		elif pid == -3:
17718c2ecf20Sopenharmony_ci			htmlclass = ' ps'
17728c2ecf20Sopenharmony_ci		if len(myphases) > 1:
17738c2ecf20Sopenharmony_ci			htmlclass = ' bg'
17748c2ecf20Sopenharmony_ci			self.phaseOverlap(myphases)
17758c2ecf20Sopenharmony_ci		if targetphase in phases:
17768c2ecf20Sopenharmony_ci			newname = self.newAction(targetphase, name, pid, '', start, end, '', htmlclass, color)
17778c2ecf20Sopenharmony_ci			return (targetphase, newname)
17788c2ecf20Sopenharmony_ci		return False
17798c2ecf20Sopenharmony_ci	def newAction(self, phase, name, pid, parent, start, end, drv, htmlclass='', color=''):
17808c2ecf20Sopenharmony_ci		# new device callback for a specific phase
17818c2ecf20Sopenharmony_ci		self.html_device_id += 1
17828c2ecf20Sopenharmony_ci		devid = '%s%d' % (self.idstr, self.html_device_id)
17838c2ecf20Sopenharmony_ci		list = self.dmesg[phase]['list']
17848c2ecf20Sopenharmony_ci		length = -1.0
17858c2ecf20Sopenharmony_ci		if(start >= 0 and end >= 0):
17868c2ecf20Sopenharmony_ci			length = end - start
17878c2ecf20Sopenharmony_ci		if pid == -2 or name not in sysvals.tracefuncs.keys():
17888c2ecf20Sopenharmony_ci			i = 2
17898c2ecf20Sopenharmony_ci			origname = name
17908c2ecf20Sopenharmony_ci			while(name in list):
17918c2ecf20Sopenharmony_ci				name = '%s[%d]' % (origname, i)
17928c2ecf20Sopenharmony_ci				i += 1
17938c2ecf20Sopenharmony_ci		list[name] = {'name': name, 'start': start, 'end': end, 'pid': pid,
17948c2ecf20Sopenharmony_ci			'par': parent, 'length': length, 'row': 0, 'id': devid, 'drv': drv }
17958c2ecf20Sopenharmony_ci		if htmlclass:
17968c2ecf20Sopenharmony_ci			list[name]['htmlclass'] = htmlclass
17978c2ecf20Sopenharmony_ci		if color:
17988c2ecf20Sopenharmony_ci			list[name]['color'] = color
17998c2ecf20Sopenharmony_ci		return name
18008c2ecf20Sopenharmony_ci	def findDevice(self, phase, name):
18018c2ecf20Sopenharmony_ci		list = self.dmesg[phase]['list']
18028c2ecf20Sopenharmony_ci		mydev = ''
18038c2ecf20Sopenharmony_ci		for devname in sorted(list):
18048c2ecf20Sopenharmony_ci			if name == devname or re.match('^%s\[(?P<num>[0-9]*)\]$' % name, devname):
18058c2ecf20Sopenharmony_ci				mydev = devname
18068c2ecf20Sopenharmony_ci		if mydev:
18078c2ecf20Sopenharmony_ci			return list[mydev]
18088c2ecf20Sopenharmony_ci		return False
18098c2ecf20Sopenharmony_ci	def deviceChildren(self, devname, phase):
18108c2ecf20Sopenharmony_ci		devlist = []
18118c2ecf20Sopenharmony_ci		list = self.dmesg[phase]['list']
18128c2ecf20Sopenharmony_ci		for child in list:
18138c2ecf20Sopenharmony_ci			if(list[child]['par'] == devname):
18148c2ecf20Sopenharmony_ci				devlist.append(child)
18158c2ecf20Sopenharmony_ci		return devlist
18168c2ecf20Sopenharmony_ci	def maxDeviceNameSize(self, phase):
18178c2ecf20Sopenharmony_ci		size = 0
18188c2ecf20Sopenharmony_ci		for name in self.dmesg[phase]['list']:
18198c2ecf20Sopenharmony_ci			if len(name) > size:
18208c2ecf20Sopenharmony_ci				size = len(name)
18218c2ecf20Sopenharmony_ci		return size
18228c2ecf20Sopenharmony_ci	def printDetails(self):
18238c2ecf20Sopenharmony_ci		sysvals.vprint('Timeline Details:')
18248c2ecf20Sopenharmony_ci		sysvals.vprint('          test start: %f' % self.start)
18258c2ecf20Sopenharmony_ci		sysvals.vprint('kernel suspend start: %f' % self.tKernSus)
18268c2ecf20Sopenharmony_ci		tS = tR = False
18278c2ecf20Sopenharmony_ci		for phase in self.sortedPhases():
18288c2ecf20Sopenharmony_ci			devlist = self.dmesg[phase]['list']
18298c2ecf20Sopenharmony_ci			dc, ps, pe = len(devlist), self.dmesg[phase]['start'], self.dmesg[phase]['end']
18308c2ecf20Sopenharmony_ci			if not tS and ps >= self.tSuspended:
18318c2ecf20Sopenharmony_ci				sysvals.vprint('   machine suspended: %f' % self.tSuspended)
18328c2ecf20Sopenharmony_ci				tS = True
18338c2ecf20Sopenharmony_ci			if not tR and ps >= self.tResumed:
18348c2ecf20Sopenharmony_ci				sysvals.vprint('     machine resumed: %f' % self.tResumed)
18358c2ecf20Sopenharmony_ci				tR = True
18368c2ecf20Sopenharmony_ci			sysvals.vprint('%20s: %f - %f (%d devices)' % (phase, ps, pe, dc))
18378c2ecf20Sopenharmony_ci			if sysvals.devdump:
18388c2ecf20Sopenharmony_ci				sysvals.vprint(''.join('-' for i in range(80)))
18398c2ecf20Sopenharmony_ci				maxname = '%d' % self.maxDeviceNameSize(phase)
18408c2ecf20Sopenharmony_ci				fmt = '%3d) %'+maxname+'s - %f - %f'
18418c2ecf20Sopenharmony_ci				c = 1
18428c2ecf20Sopenharmony_ci				for name in sorted(devlist):
18438c2ecf20Sopenharmony_ci					s = devlist[name]['start']
18448c2ecf20Sopenharmony_ci					e = devlist[name]['end']
18458c2ecf20Sopenharmony_ci					sysvals.vprint(fmt % (c, name, s, e))
18468c2ecf20Sopenharmony_ci					c += 1
18478c2ecf20Sopenharmony_ci				sysvals.vprint(''.join('-' for i in range(80)))
18488c2ecf20Sopenharmony_ci		sysvals.vprint('   kernel resume end: %f' % self.tKernRes)
18498c2ecf20Sopenharmony_ci		sysvals.vprint('            test end: %f' % self.end)
18508c2ecf20Sopenharmony_ci	def deviceChildrenAllPhases(self, devname):
18518c2ecf20Sopenharmony_ci		devlist = []
18528c2ecf20Sopenharmony_ci		for phase in self.sortedPhases():
18538c2ecf20Sopenharmony_ci			list = self.deviceChildren(devname, phase)
18548c2ecf20Sopenharmony_ci			for dev in sorted(list):
18558c2ecf20Sopenharmony_ci				if dev not in devlist:
18568c2ecf20Sopenharmony_ci					devlist.append(dev)
18578c2ecf20Sopenharmony_ci		return devlist
18588c2ecf20Sopenharmony_ci	def masterTopology(self, name, list, depth):
18598c2ecf20Sopenharmony_ci		node = DeviceNode(name, depth)
18608c2ecf20Sopenharmony_ci		for cname in list:
18618c2ecf20Sopenharmony_ci			# avoid recursions
18628c2ecf20Sopenharmony_ci			if name == cname:
18638c2ecf20Sopenharmony_ci				continue
18648c2ecf20Sopenharmony_ci			clist = self.deviceChildrenAllPhases(cname)
18658c2ecf20Sopenharmony_ci			cnode = self.masterTopology(cname, clist, depth+1)
18668c2ecf20Sopenharmony_ci			node.children.append(cnode)
18678c2ecf20Sopenharmony_ci		return node
18688c2ecf20Sopenharmony_ci	def printTopology(self, node):
18698c2ecf20Sopenharmony_ci		html = ''
18708c2ecf20Sopenharmony_ci		if node.name:
18718c2ecf20Sopenharmony_ci			info = ''
18728c2ecf20Sopenharmony_ci			drv = ''
18738c2ecf20Sopenharmony_ci			for phase in self.sortedPhases():
18748c2ecf20Sopenharmony_ci				list = self.dmesg[phase]['list']
18758c2ecf20Sopenharmony_ci				if node.name in list:
18768c2ecf20Sopenharmony_ci					s = list[node.name]['start']
18778c2ecf20Sopenharmony_ci					e = list[node.name]['end']
18788c2ecf20Sopenharmony_ci					if list[node.name]['drv']:
18798c2ecf20Sopenharmony_ci						drv = ' {'+list[node.name]['drv']+'}'
18808c2ecf20Sopenharmony_ci					info += ('<li>%s: %.3fms</li>' % (phase, (e-s)*1000))
18818c2ecf20Sopenharmony_ci			html += '<li><b>'+node.name+drv+'</b>'
18828c2ecf20Sopenharmony_ci			if info:
18838c2ecf20Sopenharmony_ci				html += '<ul>'+info+'</ul>'
18848c2ecf20Sopenharmony_ci			html += '</li>'
18858c2ecf20Sopenharmony_ci		if len(node.children) > 0:
18868c2ecf20Sopenharmony_ci			html += '<ul>'
18878c2ecf20Sopenharmony_ci			for cnode in node.children:
18888c2ecf20Sopenharmony_ci				html += self.printTopology(cnode)
18898c2ecf20Sopenharmony_ci			html += '</ul>'
18908c2ecf20Sopenharmony_ci		return html
18918c2ecf20Sopenharmony_ci	def rootDeviceList(self):
18928c2ecf20Sopenharmony_ci		# list of devices graphed
18938c2ecf20Sopenharmony_ci		real = []
18948c2ecf20Sopenharmony_ci		for phase in self.sortedPhases():
18958c2ecf20Sopenharmony_ci			list = self.dmesg[phase]['list']
18968c2ecf20Sopenharmony_ci			for dev in sorted(list):
18978c2ecf20Sopenharmony_ci				if list[dev]['pid'] >= 0 and dev not in real:
18988c2ecf20Sopenharmony_ci					real.append(dev)
18998c2ecf20Sopenharmony_ci		# list of top-most root devices
19008c2ecf20Sopenharmony_ci		rootlist = []
19018c2ecf20Sopenharmony_ci		for phase in self.sortedPhases():
19028c2ecf20Sopenharmony_ci			list = self.dmesg[phase]['list']
19038c2ecf20Sopenharmony_ci			for dev in sorted(list):
19048c2ecf20Sopenharmony_ci				pdev = list[dev]['par']
19058c2ecf20Sopenharmony_ci				pid = list[dev]['pid']
19068c2ecf20Sopenharmony_ci				if(pid < 0 or re.match('[0-9]*-[0-9]*\.[0-9]*[\.0-9]*\:[\.0-9]*$', pdev)):
19078c2ecf20Sopenharmony_ci					continue
19088c2ecf20Sopenharmony_ci				if pdev and pdev not in real and pdev not in rootlist:
19098c2ecf20Sopenharmony_ci					rootlist.append(pdev)
19108c2ecf20Sopenharmony_ci		return rootlist
19118c2ecf20Sopenharmony_ci	def deviceTopology(self):
19128c2ecf20Sopenharmony_ci		rootlist = self.rootDeviceList()
19138c2ecf20Sopenharmony_ci		master = self.masterTopology('', rootlist, 0)
19148c2ecf20Sopenharmony_ci		return self.printTopology(master)
19158c2ecf20Sopenharmony_ci	def selectTimelineDevices(self, widfmt, tTotal, mindevlen):
19168c2ecf20Sopenharmony_ci		# only select devices that will actually show up in html
19178c2ecf20Sopenharmony_ci		self.tdevlist = dict()
19188c2ecf20Sopenharmony_ci		for phase in self.dmesg:
19198c2ecf20Sopenharmony_ci			devlist = []
19208c2ecf20Sopenharmony_ci			list = self.dmesg[phase]['list']
19218c2ecf20Sopenharmony_ci			for dev in list:
19228c2ecf20Sopenharmony_ci				length = (list[dev]['end'] - list[dev]['start']) * 1000
19238c2ecf20Sopenharmony_ci				width = widfmt % (((list[dev]['end']-list[dev]['start'])*100)/tTotal)
19248c2ecf20Sopenharmony_ci				if width != '0.000000' and length >= mindevlen:
19258c2ecf20Sopenharmony_ci					devlist.append(dev)
19268c2ecf20Sopenharmony_ci			self.tdevlist[phase] = devlist
19278c2ecf20Sopenharmony_ci	def addHorizontalDivider(self, devname, devend):
19288c2ecf20Sopenharmony_ci		phase = 'suspend_prepare'
19298c2ecf20Sopenharmony_ci		self.newAction(phase, devname, -2, '', \
19308c2ecf20Sopenharmony_ci			self.start, devend, '', ' sec', '')
19318c2ecf20Sopenharmony_ci		if phase not in self.tdevlist:
19328c2ecf20Sopenharmony_ci			self.tdevlist[phase] = []
19338c2ecf20Sopenharmony_ci		self.tdevlist[phase].append(devname)
19348c2ecf20Sopenharmony_ci		d = DevItem(0, phase, self.dmesg[phase]['list'][devname])
19358c2ecf20Sopenharmony_ci		return d
19368c2ecf20Sopenharmony_ci	def addProcessUsageEvent(self, name, times):
19378c2ecf20Sopenharmony_ci		# get the start and end times for this process
19388c2ecf20Sopenharmony_ci		maxC = 0
19398c2ecf20Sopenharmony_ci		tlast = 0
19408c2ecf20Sopenharmony_ci		start = -1
19418c2ecf20Sopenharmony_ci		end = -1
19428c2ecf20Sopenharmony_ci		for t in sorted(times):
19438c2ecf20Sopenharmony_ci			if tlast == 0:
19448c2ecf20Sopenharmony_ci				tlast = t
19458c2ecf20Sopenharmony_ci				continue
19468c2ecf20Sopenharmony_ci			if name in self.pstl[t]:
19478c2ecf20Sopenharmony_ci				if start == -1 or tlast < start:
19488c2ecf20Sopenharmony_ci					start = tlast
19498c2ecf20Sopenharmony_ci				if end == -1 or t > end:
19508c2ecf20Sopenharmony_ci					end = t
19518c2ecf20Sopenharmony_ci			tlast = t
19528c2ecf20Sopenharmony_ci		if start == -1 or end == -1:
19538c2ecf20Sopenharmony_ci			return 0
19548c2ecf20Sopenharmony_ci		# add a new action for this process and get the object
19558c2ecf20Sopenharmony_ci		out = self.newActionGlobal(name, start, end, -3)
19568c2ecf20Sopenharmony_ci		if not out:
19578c2ecf20Sopenharmony_ci			return 0
19588c2ecf20Sopenharmony_ci		phase, devname = out
19598c2ecf20Sopenharmony_ci		dev = self.dmesg[phase]['list'][devname]
19608c2ecf20Sopenharmony_ci		# get the cpu exec data
19618c2ecf20Sopenharmony_ci		tlast = 0
19628c2ecf20Sopenharmony_ci		clast = 0
19638c2ecf20Sopenharmony_ci		cpuexec = dict()
19648c2ecf20Sopenharmony_ci		for t in sorted(times):
19658c2ecf20Sopenharmony_ci			if tlast == 0 or t <= start or t > end:
19668c2ecf20Sopenharmony_ci				tlast = t
19678c2ecf20Sopenharmony_ci				continue
19688c2ecf20Sopenharmony_ci			list = self.pstl[t]
19698c2ecf20Sopenharmony_ci			c = 0
19708c2ecf20Sopenharmony_ci			if name in list:
19718c2ecf20Sopenharmony_ci				c = list[name]
19728c2ecf20Sopenharmony_ci			if c > maxC:
19738c2ecf20Sopenharmony_ci				maxC = c
19748c2ecf20Sopenharmony_ci			if c != clast:
19758c2ecf20Sopenharmony_ci				key = (tlast, t)
19768c2ecf20Sopenharmony_ci				cpuexec[key] = c
19778c2ecf20Sopenharmony_ci				tlast = t
19788c2ecf20Sopenharmony_ci				clast = c
19798c2ecf20Sopenharmony_ci		dev['cpuexec'] = cpuexec
19808c2ecf20Sopenharmony_ci		return maxC
19818c2ecf20Sopenharmony_ci	def createProcessUsageEvents(self):
19828c2ecf20Sopenharmony_ci		# get an array of process names
19838c2ecf20Sopenharmony_ci		proclist = []
19848c2ecf20Sopenharmony_ci		for t in sorted(self.pstl):
19858c2ecf20Sopenharmony_ci			pslist = self.pstl[t]
19868c2ecf20Sopenharmony_ci			for ps in sorted(pslist):
19878c2ecf20Sopenharmony_ci				if ps not in proclist:
19888c2ecf20Sopenharmony_ci					proclist.append(ps)
19898c2ecf20Sopenharmony_ci		# get a list of data points for suspend and resume
19908c2ecf20Sopenharmony_ci		tsus = []
19918c2ecf20Sopenharmony_ci		tres = []
19928c2ecf20Sopenharmony_ci		for t in sorted(self.pstl):
19938c2ecf20Sopenharmony_ci			if t < self.tSuspended:
19948c2ecf20Sopenharmony_ci				tsus.append(t)
19958c2ecf20Sopenharmony_ci			else:
19968c2ecf20Sopenharmony_ci				tres.append(t)
19978c2ecf20Sopenharmony_ci		# process the events for suspend and resume
19988c2ecf20Sopenharmony_ci		if len(proclist) > 0:
19998c2ecf20Sopenharmony_ci			sysvals.vprint('Process Execution:')
20008c2ecf20Sopenharmony_ci		for ps in proclist:
20018c2ecf20Sopenharmony_ci			c = self.addProcessUsageEvent(ps, tsus)
20028c2ecf20Sopenharmony_ci			if c > 0:
20038c2ecf20Sopenharmony_ci				sysvals.vprint('%25s (sus): %d' % (ps, c))
20048c2ecf20Sopenharmony_ci			c = self.addProcessUsageEvent(ps, tres)
20058c2ecf20Sopenharmony_ci			if c > 0:
20068c2ecf20Sopenharmony_ci				sysvals.vprint('%25s (res): %d' % (ps, c))
20078c2ecf20Sopenharmony_ci	def handleEndMarker(self, time, msg=''):
20088c2ecf20Sopenharmony_ci		dm = self.dmesg
20098c2ecf20Sopenharmony_ci		self.setEnd(time, msg)
20108c2ecf20Sopenharmony_ci		self.initDevicegroups()
20118c2ecf20Sopenharmony_ci		# give suspend_prepare an end if needed
20128c2ecf20Sopenharmony_ci		if 'suspend_prepare' in dm and dm['suspend_prepare']['end'] < 0:
20138c2ecf20Sopenharmony_ci			dm['suspend_prepare']['end'] = time
20148c2ecf20Sopenharmony_ci		# assume resume machine ends at next phase start
20158c2ecf20Sopenharmony_ci		if 'resume_machine' in dm and dm['resume_machine']['end'] < 0:
20168c2ecf20Sopenharmony_ci			np = self.nextPhase('resume_machine', 1)
20178c2ecf20Sopenharmony_ci			if np:
20188c2ecf20Sopenharmony_ci				dm['resume_machine']['end'] = dm[np]['start']
20198c2ecf20Sopenharmony_ci		# if kernel resume end not found, assume its the end marker
20208c2ecf20Sopenharmony_ci		if self.tKernRes == 0.0:
20218c2ecf20Sopenharmony_ci			self.tKernRes = time
20228c2ecf20Sopenharmony_ci		# if kernel suspend start not found, assume its the end marker
20238c2ecf20Sopenharmony_ci		if self.tKernSus == 0.0:
20248c2ecf20Sopenharmony_ci			self.tKernSus = time
20258c2ecf20Sopenharmony_ci		# set resume complete to end at end marker
20268c2ecf20Sopenharmony_ci		if 'resume_complete' in dm:
20278c2ecf20Sopenharmony_ci			dm['resume_complete']['end'] = time
20288c2ecf20Sopenharmony_ci	def debugPrint(self):
20298c2ecf20Sopenharmony_ci		for p in self.sortedPhases():
20308c2ecf20Sopenharmony_ci			list = self.dmesg[p]['list']
20318c2ecf20Sopenharmony_ci			for devname in sorted(list):
20328c2ecf20Sopenharmony_ci				dev = list[devname]
20338c2ecf20Sopenharmony_ci				if 'ftrace' in dev:
20348c2ecf20Sopenharmony_ci					dev['ftrace'].debugPrint(' [%s]' % devname)
20358c2ecf20Sopenharmony_ci
20368c2ecf20Sopenharmony_ci# Class: DevFunction
20378c2ecf20Sopenharmony_ci# Description:
20388c2ecf20Sopenharmony_ci#	 A container for kprobe function data we want in the dev timeline
20398c2ecf20Sopenharmony_ciclass DevFunction:
20408c2ecf20Sopenharmony_ci	def __init__(self, name, args, caller, ret, start, end, u, proc, pid, color):
20418c2ecf20Sopenharmony_ci		self.row = 0
20428c2ecf20Sopenharmony_ci		self.count = 1
20438c2ecf20Sopenharmony_ci		self.name = name
20448c2ecf20Sopenharmony_ci		self.args = args
20458c2ecf20Sopenharmony_ci		self.caller = caller
20468c2ecf20Sopenharmony_ci		self.ret = ret
20478c2ecf20Sopenharmony_ci		self.time = start
20488c2ecf20Sopenharmony_ci		self.length = end - start
20498c2ecf20Sopenharmony_ci		self.end = end
20508c2ecf20Sopenharmony_ci		self.ubiquitous = u
20518c2ecf20Sopenharmony_ci		self.proc = proc
20528c2ecf20Sopenharmony_ci		self.pid = pid
20538c2ecf20Sopenharmony_ci		self.color = color
20548c2ecf20Sopenharmony_ci	def title(self):
20558c2ecf20Sopenharmony_ci		cnt = ''
20568c2ecf20Sopenharmony_ci		if self.count > 1:
20578c2ecf20Sopenharmony_ci			cnt = '(x%d)' % self.count
20588c2ecf20Sopenharmony_ci		l = '%0.3fms' % (self.length * 1000)
20598c2ecf20Sopenharmony_ci		if self.ubiquitous:
20608c2ecf20Sopenharmony_ci			title = '%s(%s)%s <- %s, %s(%s)' % \
20618c2ecf20Sopenharmony_ci				(self.name, self.args, cnt, self.caller, self.ret, l)
20628c2ecf20Sopenharmony_ci		else:
20638c2ecf20Sopenharmony_ci			title = '%s(%s) %s%s(%s)' % (self.name, self.args, self.ret, cnt, l)
20648c2ecf20Sopenharmony_ci		return title.replace('"', '')
20658c2ecf20Sopenharmony_ci	def text(self):
20668c2ecf20Sopenharmony_ci		if self.count > 1:
20678c2ecf20Sopenharmony_ci			text = '%s(x%d)' % (self.name, self.count)
20688c2ecf20Sopenharmony_ci		else:
20698c2ecf20Sopenharmony_ci			text = self.name
20708c2ecf20Sopenharmony_ci		return text
20718c2ecf20Sopenharmony_ci	def repeat(self, tgt):
20728c2ecf20Sopenharmony_ci		# is the tgt call just a repeat of this call (e.g. are we in a loop)
20738c2ecf20Sopenharmony_ci		dt = self.time - tgt.end
20748c2ecf20Sopenharmony_ci		# only combine calls if -all- attributes are identical
20758c2ecf20Sopenharmony_ci		if tgt.caller == self.caller and \
20768c2ecf20Sopenharmony_ci			tgt.name == self.name and tgt.args == self.args and \
20778c2ecf20Sopenharmony_ci			tgt.proc == self.proc and tgt.pid == self.pid and \
20788c2ecf20Sopenharmony_ci			tgt.ret == self.ret and dt >= 0 and \
20798c2ecf20Sopenharmony_ci			dt <= sysvals.callloopmaxgap and \
20808c2ecf20Sopenharmony_ci			self.length < sysvals.callloopmaxlen:
20818c2ecf20Sopenharmony_ci			return True
20828c2ecf20Sopenharmony_ci		return False
20838c2ecf20Sopenharmony_ci
20848c2ecf20Sopenharmony_ci# Class: FTraceLine
20858c2ecf20Sopenharmony_ci# Description:
20868c2ecf20Sopenharmony_ci#	 A container for a single line of ftrace data. There are six basic types:
20878c2ecf20Sopenharmony_ci#		 callgraph line:
20888c2ecf20Sopenharmony_ci#			  call: "  dpm_run_callback() {"
20898c2ecf20Sopenharmony_ci#			return: "  }"
20908c2ecf20Sopenharmony_ci#			  leaf: " dpm_run_callback();"
20918c2ecf20Sopenharmony_ci#		 trace event:
20928c2ecf20Sopenharmony_ci#			 tracing_mark_write: SUSPEND START or RESUME COMPLETE
20938c2ecf20Sopenharmony_ci#			 suspend_resume: phase or custom exec block data
20948c2ecf20Sopenharmony_ci#			 device_pm_callback: device callback info
20958c2ecf20Sopenharmony_ciclass FTraceLine:
20968c2ecf20Sopenharmony_ci	def __init__(self, t, m='', d=''):
20978c2ecf20Sopenharmony_ci		self.length = 0.0
20988c2ecf20Sopenharmony_ci		self.fcall = False
20998c2ecf20Sopenharmony_ci		self.freturn = False
21008c2ecf20Sopenharmony_ci		self.fevent = False
21018c2ecf20Sopenharmony_ci		self.fkprobe = False
21028c2ecf20Sopenharmony_ci		self.depth = 0
21038c2ecf20Sopenharmony_ci		self.name = ''
21048c2ecf20Sopenharmony_ci		self.type = ''
21058c2ecf20Sopenharmony_ci		self.time = float(t)
21068c2ecf20Sopenharmony_ci		if not m and not d:
21078c2ecf20Sopenharmony_ci			return
21088c2ecf20Sopenharmony_ci		# is this a trace event
21098c2ecf20Sopenharmony_ci		if(d == 'traceevent' or re.match('^ *\/\* *(?P<msg>.*) \*\/ *$', m)):
21108c2ecf20Sopenharmony_ci			if(d == 'traceevent'):
21118c2ecf20Sopenharmony_ci				# nop format trace event
21128c2ecf20Sopenharmony_ci				msg = m
21138c2ecf20Sopenharmony_ci			else:
21148c2ecf20Sopenharmony_ci				# function_graph format trace event
21158c2ecf20Sopenharmony_ci				em = re.match('^ *\/\* *(?P<msg>.*) \*\/ *$', m)
21168c2ecf20Sopenharmony_ci				msg = em.group('msg')
21178c2ecf20Sopenharmony_ci
21188c2ecf20Sopenharmony_ci			emm = re.match('^(?P<call>.*?): (?P<msg>.*)', msg)
21198c2ecf20Sopenharmony_ci			if(emm):
21208c2ecf20Sopenharmony_ci				self.name = emm.group('msg')
21218c2ecf20Sopenharmony_ci				self.type = emm.group('call')
21228c2ecf20Sopenharmony_ci			else:
21238c2ecf20Sopenharmony_ci				self.name = msg
21248c2ecf20Sopenharmony_ci			km = re.match('^(?P<n>.*)_cal$', self.type)
21258c2ecf20Sopenharmony_ci			if km:
21268c2ecf20Sopenharmony_ci				self.fcall = True
21278c2ecf20Sopenharmony_ci				self.fkprobe = True
21288c2ecf20Sopenharmony_ci				self.type = km.group('n')
21298c2ecf20Sopenharmony_ci				return
21308c2ecf20Sopenharmony_ci			km = re.match('^(?P<n>.*)_ret$', self.type)
21318c2ecf20Sopenharmony_ci			if km:
21328c2ecf20Sopenharmony_ci				self.freturn = True
21338c2ecf20Sopenharmony_ci				self.fkprobe = True
21348c2ecf20Sopenharmony_ci				self.type = km.group('n')
21358c2ecf20Sopenharmony_ci				return
21368c2ecf20Sopenharmony_ci			self.fevent = True
21378c2ecf20Sopenharmony_ci			return
21388c2ecf20Sopenharmony_ci		# convert the duration to seconds
21398c2ecf20Sopenharmony_ci		if(d):
21408c2ecf20Sopenharmony_ci			self.length = float(d)/1000000
21418c2ecf20Sopenharmony_ci		# the indentation determines the depth
21428c2ecf20Sopenharmony_ci		match = re.match('^(?P<d> *)(?P<o>.*)$', m)
21438c2ecf20Sopenharmony_ci		if(not match):
21448c2ecf20Sopenharmony_ci			return
21458c2ecf20Sopenharmony_ci		self.depth = self.getDepth(match.group('d'))
21468c2ecf20Sopenharmony_ci		m = match.group('o')
21478c2ecf20Sopenharmony_ci		# function return
21488c2ecf20Sopenharmony_ci		if(m[0] == '}'):
21498c2ecf20Sopenharmony_ci			self.freturn = True
21508c2ecf20Sopenharmony_ci			if(len(m) > 1):
21518c2ecf20Sopenharmony_ci				# includes comment with function name
21528c2ecf20Sopenharmony_ci				match = re.match('^} *\/\* *(?P<n>.*) *\*\/$', m)
21538c2ecf20Sopenharmony_ci				if(match):
21548c2ecf20Sopenharmony_ci					self.name = match.group('n').strip()
21558c2ecf20Sopenharmony_ci		# function call
21568c2ecf20Sopenharmony_ci		else:
21578c2ecf20Sopenharmony_ci			self.fcall = True
21588c2ecf20Sopenharmony_ci			# function call with children
21598c2ecf20Sopenharmony_ci			if(m[-1] == '{'):
21608c2ecf20Sopenharmony_ci				match = re.match('^(?P<n>.*) *\(.*', m)
21618c2ecf20Sopenharmony_ci				if(match):
21628c2ecf20Sopenharmony_ci					self.name = match.group('n').strip()
21638c2ecf20Sopenharmony_ci			# function call with no children (leaf)
21648c2ecf20Sopenharmony_ci			elif(m[-1] == ';'):
21658c2ecf20Sopenharmony_ci				self.freturn = True
21668c2ecf20Sopenharmony_ci				match = re.match('^(?P<n>.*) *\(.*', m)
21678c2ecf20Sopenharmony_ci				if(match):
21688c2ecf20Sopenharmony_ci					self.name = match.group('n').strip()
21698c2ecf20Sopenharmony_ci			# something else (possibly a trace marker)
21708c2ecf20Sopenharmony_ci			else:
21718c2ecf20Sopenharmony_ci				self.name = m
21728c2ecf20Sopenharmony_ci	def isCall(self):
21738c2ecf20Sopenharmony_ci		return self.fcall and not self.freturn
21748c2ecf20Sopenharmony_ci	def isReturn(self):
21758c2ecf20Sopenharmony_ci		return self.freturn and not self.fcall
21768c2ecf20Sopenharmony_ci	def isLeaf(self):
21778c2ecf20Sopenharmony_ci		return self.fcall and self.freturn
21788c2ecf20Sopenharmony_ci	def getDepth(self, str):
21798c2ecf20Sopenharmony_ci		return len(str)/2
21808c2ecf20Sopenharmony_ci	def debugPrint(self, info=''):
21818c2ecf20Sopenharmony_ci		if self.isLeaf():
21828c2ecf20Sopenharmony_ci			pprint(' -- %12.6f (depth=%02d): %s(); (%.3f us) %s' % (self.time, \
21838c2ecf20Sopenharmony_ci				self.depth, self.name, self.length*1000000, info))
21848c2ecf20Sopenharmony_ci		elif self.freturn:
21858c2ecf20Sopenharmony_ci			pprint(' -- %12.6f (depth=%02d): %s} (%.3f us) %s' % (self.time, \
21868c2ecf20Sopenharmony_ci				self.depth, self.name, self.length*1000000, info))
21878c2ecf20Sopenharmony_ci		else:
21888c2ecf20Sopenharmony_ci			pprint(' -- %12.6f (depth=%02d): %s() { (%.3f us) %s' % (self.time, \
21898c2ecf20Sopenharmony_ci				self.depth, self.name, self.length*1000000, info))
21908c2ecf20Sopenharmony_ci	def startMarker(self):
21918c2ecf20Sopenharmony_ci		# Is this the starting line of a suspend?
21928c2ecf20Sopenharmony_ci		if not self.fevent:
21938c2ecf20Sopenharmony_ci			return False
21948c2ecf20Sopenharmony_ci		if sysvals.usetracemarkers:
21958c2ecf20Sopenharmony_ci			if(self.name.startswith('SUSPEND START')):
21968c2ecf20Sopenharmony_ci				return True
21978c2ecf20Sopenharmony_ci			return False
21988c2ecf20Sopenharmony_ci		else:
21998c2ecf20Sopenharmony_ci			if(self.type == 'suspend_resume' and
22008c2ecf20Sopenharmony_ci				re.match('suspend_enter\[.*\] begin', self.name)):
22018c2ecf20Sopenharmony_ci				return True
22028c2ecf20Sopenharmony_ci			return False
22038c2ecf20Sopenharmony_ci	def endMarker(self):
22048c2ecf20Sopenharmony_ci		# Is this the ending line of a resume?
22058c2ecf20Sopenharmony_ci		if not self.fevent:
22068c2ecf20Sopenharmony_ci			return False
22078c2ecf20Sopenharmony_ci		if sysvals.usetracemarkers:
22088c2ecf20Sopenharmony_ci			if(self.name.startswith('RESUME COMPLETE')):
22098c2ecf20Sopenharmony_ci				return True
22108c2ecf20Sopenharmony_ci			return False
22118c2ecf20Sopenharmony_ci		else:
22128c2ecf20Sopenharmony_ci			if(self.type == 'suspend_resume' and
22138c2ecf20Sopenharmony_ci				re.match('thaw_processes\[.*\] end', self.name)):
22148c2ecf20Sopenharmony_ci				return True
22158c2ecf20Sopenharmony_ci			return False
22168c2ecf20Sopenharmony_ci
22178c2ecf20Sopenharmony_ci# Class: FTraceCallGraph
22188c2ecf20Sopenharmony_ci# Description:
22198c2ecf20Sopenharmony_ci#	 A container for the ftrace callgraph of a single recursive function.
22208c2ecf20Sopenharmony_ci#	 This can be a dpm_run_callback, dpm_prepare, or dpm_complete callgraph
22218c2ecf20Sopenharmony_ci#	 Each instance is tied to a single device in a single phase, and is
22228c2ecf20Sopenharmony_ci#	 comprised of an ordered list of FTraceLine objects
22238c2ecf20Sopenharmony_ciclass FTraceCallGraph:
22248c2ecf20Sopenharmony_ci	vfname = 'missing_function_name'
22258c2ecf20Sopenharmony_ci	def __init__(self, pid, sv):
22268c2ecf20Sopenharmony_ci		self.id = ''
22278c2ecf20Sopenharmony_ci		self.invalid = False
22288c2ecf20Sopenharmony_ci		self.name = ''
22298c2ecf20Sopenharmony_ci		self.partial = False
22308c2ecf20Sopenharmony_ci		self.ignore = False
22318c2ecf20Sopenharmony_ci		self.start = -1.0
22328c2ecf20Sopenharmony_ci		self.end = -1.0
22338c2ecf20Sopenharmony_ci		self.list = []
22348c2ecf20Sopenharmony_ci		self.depth = 0
22358c2ecf20Sopenharmony_ci		self.pid = pid
22368c2ecf20Sopenharmony_ci		self.sv = sv
22378c2ecf20Sopenharmony_ci	def addLine(self, line):
22388c2ecf20Sopenharmony_ci		# if this is already invalid, just leave
22398c2ecf20Sopenharmony_ci		if(self.invalid):
22408c2ecf20Sopenharmony_ci			if(line.depth == 0 and line.freturn):
22418c2ecf20Sopenharmony_ci				return 1
22428c2ecf20Sopenharmony_ci			return 0
22438c2ecf20Sopenharmony_ci		# invalidate on bad depth
22448c2ecf20Sopenharmony_ci		if(self.depth < 0):
22458c2ecf20Sopenharmony_ci			self.invalidate(line)
22468c2ecf20Sopenharmony_ci			return 0
22478c2ecf20Sopenharmony_ci		# ignore data til we return to the current depth
22488c2ecf20Sopenharmony_ci		if self.ignore:
22498c2ecf20Sopenharmony_ci			if line.depth > self.depth:
22508c2ecf20Sopenharmony_ci				return 0
22518c2ecf20Sopenharmony_ci			else:
22528c2ecf20Sopenharmony_ci				self.list[-1].freturn = True
22538c2ecf20Sopenharmony_ci				self.list[-1].length = line.time - self.list[-1].time
22548c2ecf20Sopenharmony_ci				self.ignore = False
22558c2ecf20Sopenharmony_ci				# if this is a return at self.depth, no more work is needed
22568c2ecf20Sopenharmony_ci				if line.depth == self.depth and line.isReturn():
22578c2ecf20Sopenharmony_ci					if line.depth == 0:
22588c2ecf20Sopenharmony_ci						self.end = line.time
22598c2ecf20Sopenharmony_ci						return 1
22608c2ecf20Sopenharmony_ci					return 0
22618c2ecf20Sopenharmony_ci		# compare current depth with this lines pre-call depth
22628c2ecf20Sopenharmony_ci		prelinedep = line.depth
22638c2ecf20Sopenharmony_ci		if line.isReturn():
22648c2ecf20Sopenharmony_ci			prelinedep += 1
22658c2ecf20Sopenharmony_ci		last = 0
22668c2ecf20Sopenharmony_ci		lasttime = line.time
22678c2ecf20Sopenharmony_ci		if len(self.list) > 0:
22688c2ecf20Sopenharmony_ci			last = self.list[-1]
22698c2ecf20Sopenharmony_ci			lasttime = last.time
22708c2ecf20Sopenharmony_ci			if last.isLeaf():
22718c2ecf20Sopenharmony_ci				lasttime += last.length
22728c2ecf20Sopenharmony_ci		# handle low misalignments by inserting returns
22738c2ecf20Sopenharmony_ci		mismatch = prelinedep - self.depth
22748c2ecf20Sopenharmony_ci		warning = self.sv.verbose and abs(mismatch) > 1
22758c2ecf20Sopenharmony_ci		info = []
22768c2ecf20Sopenharmony_ci		if mismatch < 0:
22778c2ecf20Sopenharmony_ci			idx = 0
22788c2ecf20Sopenharmony_ci			# add return calls to get the depth down
22798c2ecf20Sopenharmony_ci			while prelinedep < self.depth:
22808c2ecf20Sopenharmony_ci				self.depth -= 1
22818c2ecf20Sopenharmony_ci				if idx == 0 and last and last.isCall():
22828c2ecf20Sopenharmony_ci					# special case, turn last call into a leaf
22838c2ecf20Sopenharmony_ci					last.depth = self.depth
22848c2ecf20Sopenharmony_ci					last.freturn = True
22858c2ecf20Sopenharmony_ci					last.length = line.time - last.time
22868c2ecf20Sopenharmony_ci					if warning:
22878c2ecf20Sopenharmony_ci						info.append(('[make leaf]', last))
22888c2ecf20Sopenharmony_ci				else:
22898c2ecf20Sopenharmony_ci					vline = FTraceLine(lasttime)
22908c2ecf20Sopenharmony_ci					vline.depth = self.depth
22918c2ecf20Sopenharmony_ci					vline.name = self.vfname
22928c2ecf20Sopenharmony_ci					vline.freturn = True
22938c2ecf20Sopenharmony_ci					self.list.append(vline)
22948c2ecf20Sopenharmony_ci					if warning:
22958c2ecf20Sopenharmony_ci						if idx == 0:
22968c2ecf20Sopenharmony_ci							info.append(('', last))
22978c2ecf20Sopenharmony_ci						info.append(('[add return]', vline))
22988c2ecf20Sopenharmony_ci				idx += 1
22998c2ecf20Sopenharmony_ci			if warning:
23008c2ecf20Sopenharmony_ci				info.append(('', line))
23018c2ecf20Sopenharmony_ci		# handle high misalignments by inserting calls
23028c2ecf20Sopenharmony_ci		elif mismatch > 0:
23038c2ecf20Sopenharmony_ci			idx = 0
23048c2ecf20Sopenharmony_ci			if warning:
23058c2ecf20Sopenharmony_ci				info.append(('', last))
23068c2ecf20Sopenharmony_ci			# add calls to get the depth up
23078c2ecf20Sopenharmony_ci			while prelinedep > self.depth:
23088c2ecf20Sopenharmony_ci				if idx == 0 and line.isReturn():
23098c2ecf20Sopenharmony_ci					# special case, turn this return into a leaf
23108c2ecf20Sopenharmony_ci					line.fcall = True
23118c2ecf20Sopenharmony_ci					prelinedep -= 1
23128c2ecf20Sopenharmony_ci					if warning:
23138c2ecf20Sopenharmony_ci						info.append(('[make leaf]', line))
23148c2ecf20Sopenharmony_ci				else:
23158c2ecf20Sopenharmony_ci					vline = FTraceLine(lasttime)
23168c2ecf20Sopenharmony_ci					vline.depth = self.depth
23178c2ecf20Sopenharmony_ci					vline.name = self.vfname
23188c2ecf20Sopenharmony_ci					vline.fcall = True
23198c2ecf20Sopenharmony_ci					self.list.append(vline)
23208c2ecf20Sopenharmony_ci					self.depth += 1
23218c2ecf20Sopenharmony_ci					if not last:
23228c2ecf20Sopenharmony_ci						self.start = vline.time
23238c2ecf20Sopenharmony_ci					if warning:
23248c2ecf20Sopenharmony_ci						info.append(('[add call]', vline))
23258c2ecf20Sopenharmony_ci				idx += 1
23268c2ecf20Sopenharmony_ci			if warning and ('[make leaf]', line) not in info:
23278c2ecf20Sopenharmony_ci				info.append(('', line))
23288c2ecf20Sopenharmony_ci		if warning:
23298c2ecf20Sopenharmony_ci			pprint('WARNING: ftrace data missing, corrections made:')
23308c2ecf20Sopenharmony_ci			for i in info:
23318c2ecf20Sopenharmony_ci				t, obj = i
23328c2ecf20Sopenharmony_ci				if obj:
23338c2ecf20Sopenharmony_ci					obj.debugPrint(t)
23348c2ecf20Sopenharmony_ci		# process the call and set the new depth
23358c2ecf20Sopenharmony_ci		skipadd = False
23368c2ecf20Sopenharmony_ci		md = self.sv.max_graph_depth
23378c2ecf20Sopenharmony_ci		if line.isCall():
23388c2ecf20Sopenharmony_ci			# ignore blacklisted/overdepth funcs
23398c2ecf20Sopenharmony_ci			if (md and self.depth >= md - 1) or (line.name in self.sv.cgblacklist):
23408c2ecf20Sopenharmony_ci				self.ignore = True
23418c2ecf20Sopenharmony_ci			else:
23428c2ecf20Sopenharmony_ci				self.depth += 1
23438c2ecf20Sopenharmony_ci		elif line.isReturn():
23448c2ecf20Sopenharmony_ci			self.depth -= 1
23458c2ecf20Sopenharmony_ci			# remove blacklisted/overdepth/empty funcs that slipped through
23468c2ecf20Sopenharmony_ci			if (last and last.isCall() and last.depth == line.depth) or \
23478c2ecf20Sopenharmony_ci				(md and last and last.depth >= md) or \
23488c2ecf20Sopenharmony_ci				(line.name in self.sv.cgblacklist):
23498c2ecf20Sopenharmony_ci				while len(self.list) > 0 and self.list[-1].depth > line.depth:
23508c2ecf20Sopenharmony_ci					self.list.pop(-1)
23518c2ecf20Sopenharmony_ci				if len(self.list) == 0:
23528c2ecf20Sopenharmony_ci					self.invalid = True
23538c2ecf20Sopenharmony_ci					return 1
23548c2ecf20Sopenharmony_ci				self.list[-1].freturn = True
23558c2ecf20Sopenharmony_ci				self.list[-1].length = line.time - self.list[-1].time
23568c2ecf20Sopenharmony_ci				self.list[-1].name = line.name
23578c2ecf20Sopenharmony_ci				skipadd = True
23588c2ecf20Sopenharmony_ci		if len(self.list) < 1:
23598c2ecf20Sopenharmony_ci			self.start = line.time
23608c2ecf20Sopenharmony_ci		# check for a mismatch that returned all the way to callgraph end
23618c2ecf20Sopenharmony_ci		res = 1
23628c2ecf20Sopenharmony_ci		if mismatch < 0 and self.list[-1].depth == 0 and self.list[-1].freturn:
23638c2ecf20Sopenharmony_ci			line = self.list[-1]
23648c2ecf20Sopenharmony_ci			skipadd = True
23658c2ecf20Sopenharmony_ci			res = -1
23668c2ecf20Sopenharmony_ci		if not skipadd:
23678c2ecf20Sopenharmony_ci			self.list.append(line)
23688c2ecf20Sopenharmony_ci		if(line.depth == 0 and line.freturn):
23698c2ecf20Sopenharmony_ci			if(self.start < 0):
23708c2ecf20Sopenharmony_ci				self.start = line.time
23718c2ecf20Sopenharmony_ci			self.end = line.time
23728c2ecf20Sopenharmony_ci			if line.fcall:
23738c2ecf20Sopenharmony_ci				self.end += line.length
23748c2ecf20Sopenharmony_ci			if self.list[0].name == self.vfname:
23758c2ecf20Sopenharmony_ci				self.invalid = True
23768c2ecf20Sopenharmony_ci			if res == -1:
23778c2ecf20Sopenharmony_ci				self.partial = True
23788c2ecf20Sopenharmony_ci			return res
23798c2ecf20Sopenharmony_ci		return 0
23808c2ecf20Sopenharmony_ci	def invalidate(self, line):
23818c2ecf20Sopenharmony_ci		if(len(self.list) > 0):
23828c2ecf20Sopenharmony_ci			first = self.list[0]
23838c2ecf20Sopenharmony_ci			self.list = []
23848c2ecf20Sopenharmony_ci			self.list.append(first)
23858c2ecf20Sopenharmony_ci		self.invalid = True
23868c2ecf20Sopenharmony_ci		id = 'task %s' % (self.pid)
23878c2ecf20Sopenharmony_ci		window = '(%f - %f)' % (self.start, line.time)
23888c2ecf20Sopenharmony_ci		if(self.depth < 0):
23898c2ecf20Sopenharmony_ci			pprint('Data misalignment for '+id+\
23908c2ecf20Sopenharmony_ci				' (buffer overflow), ignoring this callback')
23918c2ecf20Sopenharmony_ci		else:
23928c2ecf20Sopenharmony_ci			pprint('Too much data for '+id+\
23938c2ecf20Sopenharmony_ci				' '+window+', ignoring this callback')
23948c2ecf20Sopenharmony_ci	def slice(self, dev):
23958c2ecf20Sopenharmony_ci		minicg = FTraceCallGraph(dev['pid'], self.sv)
23968c2ecf20Sopenharmony_ci		minicg.name = self.name
23978c2ecf20Sopenharmony_ci		mydepth = -1
23988c2ecf20Sopenharmony_ci		good = False
23998c2ecf20Sopenharmony_ci		for l in self.list:
24008c2ecf20Sopenharmony_ci			if(l.time < dev['start'] or l.time > dev['end']):
24018c2ecf20Sopenharmony_ci				continue
24028c2ecf20Sopenharmony_ci			if mydepth < 0:
24038c2ecf20Sopenharmony_ci				if l.name == 'mutex_lock' and l.freturn:
24048c2ecf20Sopenharmony_ci					mydepth = l.depth
24058c2ecf20Sopenharmony_ci				continue
24068c2ecf20Sopenharmony_ci			elif l.depth == mydepth and l.name == 'mutex_unlock' and l.fcall:
24078c2ecf20Sopenharmony_ci				good = True
24088c2ecf20Sopenharmony_ci				break
24098c2ecf20Sopenharmony_ci			l.depth -= mydepth
24108c2ecf20Sopenharmony_ci			minicg.addLine(l)
24118c2ecf20Sopenharmony_ci		if not good or len(minicg.list) < 1:
24128c2ecf20Sopenharmony_ci			return 0
24138c2ecf20Sopenharmony_ci		return minicg
24148c2ecf20Sopenharmony_ci	def repair(self, enddepth):
24158c2ecf20Sopenharmony_ci		# bring the depth back to 0 with additional returns
24168c2ecf20Sopenharmony_ci		fixed = False
24178c2ecf20Sopenharmony_ci		last = self.list[-1]
24188c2ecf20Sopenharmony_ci		for i in reversed(range(enddepth)):
24198c2ecf20Sopenharmony_ci			t = FTraceLine(last.time)
24208c2ecf20Sopenharmony_ci			t.depth = i
24218c2ecf20Sopenharmony_ci			t.freturn = True
24228c2ecf20Sopenharmony_ci			fixed = self.addLine(t)
24238c2ecf20Sopenharmony_ci			if fixed != 0:
24248c2ecf20Sopenharmony_ci				self.end = last.time
24258c2ecf20Sopenharmony_ci				return True
24268c2ecf20Sopenharmony_ci		return False
24278c2ecf20Sopenharmony_ci	def postProcess(self):
24288c2ecf20Sopenharmony_ci		if len(self.list) > 0:
24298c2ecf20Sopenharmony_ci			self.name = self.list[0].name
24308c2ecf20Sopenharmony_ci		stack = dict()
24318c2ecf20Sopenharmony_ci		cnt = 0
24328c2ecf20Sopenharmony_ci		last = 0
24338c2ecf20Sopenharmony_ci		for l in self.list:
24348c2ecf20Sopenharmony_ci			# ftrace bug: reported duration is not reliable
24358c2ecf20Sopenharmony_ci			# check each leaf and clip it at max possible length
24368c2ecf20Sopenharmony_ci			if last and last.isLeaf():
24378c2ecf20Sopenharmony_ci				if last.length > l.time - last.time:
24388c2ecf20Sopenharmony_ci					last.length = l.time - last.time
24398c2ecf20Sopenharmony_ci			if l.isCall():
24408c2ecf20Sopenharmony_ci				stack[l.depth] = l
24418c2ecf20Sopenharmony_ci				cnt += 1
24428c2ecf20Sopenharmony_ci			elif l.isReturn():
24438c2ecf20Sopenharmony_ci				if(l.depth not in stack):
24448c2ecf20Sopenharmony_ci					if self.sv.verbose:
24458c2ecf20Sopenharmony_ci						pprint('Post Process Error: Depth missing')
24468c2ecf20Sopenharmony_ci						l.debugPrint()
24478c2ecf20Sopenharmony_ci					return False
24488c2ecf20Sopenharmony_ci				# calculate call length from call/return lines
24498c2ecf20Sopenharmony_ci				cl = stack[l.depth]
24508c2ecf20Sopenharmony_ci				cl.length = l.time - cl.time
24518c2ecf20Sopenharmony_ci				if cl.name == self.vfname:
24528c2ecf20Sopenharmony_ci					cl.name = l.name
24538c2ecf20Sopenharmony_ci				stack.pop(l.depth)
24548c2ecf20Sopenharmony_ci				l.length = 0
24558c2ecf20Sopenharmony_ci				cnt -= 1
24568c2ecf20Sopenharmony_ci			last = l
24578c2ecf20Sopenharmony_ci		if(cnt == 0):
24588c2ecf20Sopenharmony_ci			# trace caught the whole call tree
24598c2ecf20Sopenharmony_ci			return True
24608c2ecf20Sopenharmony_ci		elif(cnt < 0):
24618c2ecf20Sopenharmony_ci			if self.sv.verbose:
24628c2ecf20Sopenharmony_ci				pprint('Post Process Error: Depth is less than 0')
24638c2ecf20Sopenharmony_ci			return False
24648c2ecf20Sopenharmony_ci		# trace ended before call tree finished
24658c2ecf20Sopenharmony_ci		return self.repair(cnt)
24668c2ecf20Sopenharmony_ci	def deviceMatch(self, pid, data):
24678c2ecf20Sopenharmony_ci		found = ''
24688c2ecf20Sopenharmony_ci		# add the callgraph data to the device hierarchy
24698c2ecf20Sopenharmony_ci		borderphase = {
24708c2ecf20Sopenharmony_ci			'dpm_prepare': 'suspend_prepare',
24718c2ecf20Sopenharmony_ci			'dpm_complete': 'resume_complete'
24728c2ecf20Sopenharmony_ci		}
24738c2ecf20Sopenharmony_ci		if(self.name in borderphase):
24748c2ecf20Sopenharmony_ci			p = borderphase[self.name]
24758c2ecf20Sopenharmony_ci			list = data.dmesg[p]['list']
24768c2ecf20Sopenharmony_ci			for devname in list:
24778c2ecf20Sopenharmony_ci				dev = list[devname]
24788c2ecf20Sopenharmony_ci				if(pid == dev['pid'] and
24798c2ecf20Sopenharmony_ci					self.start <= dev['start'] and
24808c2ecf20Sopenharmony_ci					self.end >= dev['end']):
24818c2ecf20Sopenharmony_ci					cg = self.slice(dev)
24828c2ecf20Sopenharmony_ci					if cg:
24838c2ecf20Sopenharmony_ci						dev['ftrace'] = cg
24848c2ecf20Sopenharmony_ci					found = devname
24858c2ecf20Sopenharmony_ci			return found
24868c2ecf20Sopenharmony_ci		for p in data.sortedPhases():
24878c2ecf20Sopenharmony_ci			if(data.dmesg[p]['start'] <= self.start and
24888c2ecf20Sopenharmony_ci				self.start <= data.dmesg[p]['end']):
24898c2ecf20Sopenharmony_ci				list = data.dmesg[p]['list']
24908c2ecf20Sopenharmony_ci				for devname in sorted(list, key=lambda k:list[k]['start']):
24918c2ecf20Sopenharmony_ci					dev = list[devname]
24928c2ecf20Sopenharmony_ci					if(pid == dev['pid'] and
24938c2ecf20Sopenharmony_ci						self.start <= dev['start'] and
24948c2ecf20Sopenharmony_ci						self.end >= dev['end']):
24958c2ecf20Sopenharmony_ci						dev['ftrace'] = self
24968c2ecf20Sopenharmony_ci						found = devname
24978c2ecf20Sopenharmony_ci						break
24988c2ecf20Sopenharmony_ci				break
24998c2ecf20Sopenharmony_ci		return found
25008c2ecf20Sopenharmony_ci	def newActionFromFunction(self, data):
25018c2ecf20Sopenharmony_ci		name = self.name
25028c2ecf20Sopenharmony_ci		if name in ['dpm_run_callback', 'dpm_prepare', 'dpm_complete']:
25038c2ecf20Sopenharmony_ci			return
25048c2ecf20Sopenharmony_ci		fs = self.start
25058c2ecf20Sopenharmony_ci		fe = self.end
25068c2ecf20Sopenharmony_ci		if fs < data.start or fe > data.end:
25078c2ecf20Sopenharmony_ci			return
25088c2ecf20Sopenharmony_ci		phase = ''
25098c2ecf20Sopenharmony_ci		for p in data.sortedPhases():
25108c2ecf20Sopenharmony_ci			if(data.dmesg[p]['start'] <= self.start and
25118c2ecf20Sopenharmony_ci				self.start < data.dmesg[p]['end']):
25128c2ecf20Sopenharmony_ci				phase = p
25138c2ecf20Sopenharmony_ci				break
25148c2ecf20Sopenharmony_ci		if not phase:
25158c2ecf20Sopenharmony_ci			return
25168c2ecf20Sopenharmony_ci		out = data.newActionGlobal(name, fs, fe, -2)
25178c2ecf20Sopenharmony_ci		if out:
25188c2ecf20Sopenharmony_ci			phase, myname = out
25198c2ecf20Sopenharmony_ci			data.dmesg[phase]['list'][myname]['ftrace'] = self
25208c2ecf20Sopenharmony_ci	def debugPrint(self, info=''):
25218c2ecf20Sopenharmony_ci		pprint('%s pid=%d [%f - %f] %.3f us' % \
25228c2ecf20Sopenharmony_ci			(self.name, self.pid, self.start, self.end,
25238c2ecf20Sopenharmony_ci			(self.end - self.start)*1000000))
25248c2ecf20Sopenharmony_ci		for l in self.list:
25258c2ecf20Sopenharmony_ci			if l.isLeaf():
25268c2ecf20Sopenharmony_ci				pprint('%f (%02d): %s(); (%.3f us)%s' % (l.time, \
25278c2ecf20Sopenharmony_ci					l.depth, l.name, l.length*1000000, info))
25288c2ecf20Sopenharmony_ci			elif l.freturn:
25298c2ecf20Sopenharmony_ci				pprint('%f (%02d): %s} (%.3f us)%s' % (l.time, \
25308c2ecf20Sopenharmony_ci					l.depth, l.name, l.length*1000000, info))
25318c2ecf20Sopenharmony_ci			else:
25328c2ecf20Sopenharmony_ci				pprint('%f (%02d): %s() { (%.3f us)%s' % (l.time, \
25338c2ecf20Sopenharmony_ci					l.depth, l.name, l.length*1000000, info))
25348c2ecf20Sopenharmony_ci		pprint(' ')
25358c2ecf20Sopenharmony_ci
25368c2ecf20Sopenharmony_ciclass DevItem:
25378c2ecf20Sopenharmony_ci	def __init__(self, test, phase, dev):
25388c2ecf20Sopenharmony_ci		self.test = test
25398c2ecf20Sopenharmony_ci		self.phase = phase
25408c2ecf20Sopenharmony_ci		self.dev = dev
25418c2ecf20Sopenharmony_ci	def isa(self, cls):
25428c2ecf20Sopenharmony_ci		if 'htmlclass' in self.dev and cls in self.dev['htmlclass']:
25438c2ecf20Sopenharmony_ci			return True
25448c2ecf20Sopenharmony_ci		return False
25458c2ecf20Sopenharmony_ci
25468c2ecf20Sopenharmony_ci# Class: Timeline
25478c2ecf20Sopenharmony_ci# Description:
25488c2ecf20Sopenharmony_ci#	 A container for a device timeline which calculates
25498c2ecf20Sopenharmony_ci#	 all the html properties to display it correctly
25508c2ecf20Sopenharmony_ciclass Timeline:
25518c2ecf20Sopenharmony_ci	html_tblock = '<div id="block{0}" class="tblock" style="left:{1}%;width:{2}%;"><div class="tback" style="height:{3}px"></div>\n'
25528c2ecf20Sopenharmony_ci	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'
25538c2ecf20Sopenharmony_ci	html_phase = '<div class="phase" style="left:{0}%;width:{1}%;top:{2}px;height:{3}px;background:{4}">{5}</div>\n'
25548c2ecf20Sopenharmony_ci	html_phaselet = '<div id="{0}" class="phaselet" style="left:{1}%;width:{2}%;background:{3}"></div>\n'
25558c2ecf20Sopenharmony_ci	html_legend = '<div id="p{3}" class="square" style="left:{0}%;background:{1}">&nbsp;{2}</div>\n'
25568c2ecf20Sopenharmony_ci	def __init__(self, rowheight, scaleheight):
25578c2ecf20Sopenharmony_ci		self.html = ''
25588c2ecf20Sopenharmony_ci		self.height = 0  # total timeline height
25598c2ecf20Sopenharmony_ci		self.scaleH = scaleheight # timescale (top) row height
25608c2ecf20Sopenharmony_ci		self.rowH = rowheight     # device row height
25618c2ecf20Sopenharmony_ci		self.bodyH = 0   # body height
25628c2ecf20Sopenharmony_ci		self.rows = 0    # total timeline rows
25638c2ecf20Sopenharmony_ci		self.rowlines = dict()
25648c2ecf20Sopenharmony_ci		self.rowheight = dict()
25658c2ecf20Sopenharmony_ci	def createHeader(self, sv, stamp):
25668c2ecf20Sopenharmony_ci		if(not stamp['time']):
25678c2ecf20Sopenharmony_ci			return
25688c2ecf20Sopenharmony_ci		self.html += '<div class="version"><a href="https://01.org/pm-graph">%s v%s</a></div>' \
25698c2ecf20Sopenharmony_ci			% (sv.title, sv.version)
25708c2ecf20Sopenharmony_ci		if sv.logmsg and sv.testlog:
25718c2ecf20Sopenharmony_ci			self.html += '<button id="showtest" class="logbtn btnfmt">log</button>'
25728c2ecf20Sopenharmony_ci		if sv.dmesglog:
25738c2ecf20Sopenharmony_ci			self.html += '<button id="showdmesg" class="logbtn btnfmt">dmesg</button>'
25748c2ecf20Sopenharmony_ci		if sv.ftracelog:
25758c2ecf20Sopenharmony_ci			self.html += '<button id="showftrace" class="logbtn btnfmt">ftrace</button>'
25768c2ecf20Sopenharmony_ci		headline_stamp = '<div class="stamp">{0} {1} {2} {3}</div>\n'
25778c2ecf20Sopenharmony_ci		self.html += headline_stamp.format(stamp['host'], stamp['kernel'],
25788c2ecf20Sopenharmony_ci			stamp['mode'], stamp['time'])
25798c2ecf20Sopenharmony_ci		if 'man' in stamp and 'plat' in stamp and 'cpu' in stamp and \
25808c2ecf20Sopenharmony_ci			stamp['man'] and stamp['plat'] and stamp['cpu']:
25818c2ecf20Sopenharmony_ci			headline_sysinfo = '<div class="stamp sysinfo">{0} {1} <i>with</i> {2}</div>\n'
25828c2ecf20Sopenharmony_ci			self.html += headline_sysinfo.format(stamp['man'], stamp['plat'], stamp['cpu'])
25838c2ecf20Sopenharmony_ci
25848c2ecf20Sopenharmony_ci	# Function: getDeviceRows
25858c2ecf20Sopenharmony_ci	# Description:
25868c2ecf20Sopenharmony_ci	#    determine how may rows the device funcs will take
25878c2ecf20Sopenharmony_ci	# Arguments:
25888c2ecf20Sopenharmony_ci	#	 rawlist: the list of devices/actions for a single phase
25898c2ecf20Sopenharmony_ci	# Output:
25908c2ecf20Sopenharmony_ci	#	 The total number of rows needed to display this phase of the timeline
25918c2ecf20Sopenharmony_ci	def getDeviceRows(self, rawlist):
25928c2ecf20Sopenharmony_ci		# clear all rows and set them to undefined
25938c2ecf20Sopenharmony_ci		sortdict = dict()
25948c2ecf20Sopenharmony_ci		for item in rawlist:
25958c2ecf20Sopenharmony_ci			item.row = -1
25968c2ecf20Sopenharmony_ci			sortdict[item] = item.length
25978c2ecf20Sopenharmony_ci		sortlist = sorted(sortdict, key=sortdict.get, reverse=True)
25988c2ecf20Sopenharmony_ci		remaining = len(sortlist)
25998c2ecf20Sopenharmony_ci		rowdata = dict()
26008c2ecf20Sopenharmony_ci		row = 1
26018c2ecf20Sopenharmony_ci		# try to pack each row with as many ranges as possible
26028c2ecf20Sopenharmony_ci		while(remaining > 0):
26038c2ecf20Sopenharmony_ci			if(row not in rowdata):
26048c2ecf20Sopenharmony_ci				rowdata[row] = []
26058c2ecf20Sopenharmony_ci			for i in sortlist:
26068c2ecf20Sopenharmony_ci				if(i.row >= 0):
26078c2ecf20Sopenharmony_ci					continue
26088c2ecf20Sopenharmony_ci				s = i.time
26098c2ecf20Sopenharmony_ci				e = i.time + i.length
26108c2ecf20Sopenharmony_ci				valid = True
26118c2ecf20Sopenharmony_ci				for ritem in rowdata[row]:
26128c2ecf20Sopenharmony_ci					rs = ritem.time
26138c2ecf20Sopenharmony_ci					re = ritem.time + ritem.length
26148c2ecf20Sopenharmony_ci					if(not (((s <= rs) and (e <= rs)) or
26158c2ecf20Sopenharmony_ci						((s >= re) and (e >= re)))):
26168c2ecf20Sopenharmony_ci						valid = False
26178c2ecf20Sopenharmony_ci						break
26188c2ecf20Sopenharmony_ci				if(valid):
26198c2ecf20Sopenharmony_ci					rowdata[row].append(i)
26208c2ecf20Sopenharmony_ci					i.row = row
26218c2ecf20Sopenharmony_ci					remaining -= 1
26228c2ecf20Sopenharmony_ci			row += 1
26238c2ecf20Sopenharmony_ci		return row
26248c2ecf20Sopenharmony_ci	# Function: getPhaseRows
26258c2ecf20Sopenharmony_ci	# Description:
26268c2ecf20Sopenharmony_ci	#	 Organize the timeline entries into the smallest
26278c2ecf20Sopenharmony_ci	#	 number of rows possible, with no entry overlapping
26288c2ecf20Sopenharmony_ci	# Arguments:
26298c2ecf20Sopenharmony_ci	#	 devlist: the list of devices/actions in a group of contiguous phases
26308c2ecf20Sopenharmony_ci	# Output:
26318c2ecf20Sopenharmony_ci	#	 The total number of rows needed to display this phase of the timeline
26328c2ecf20Sopenharmony_ci	def getPhaseRows(self, devlist, row=0, sortby='length'):
26338c2ecf20Sopenharmony_ci		# clear all rows and set them to undefined
26348c2ecf20Sopenharmony_ci		remaining = len(devlist)
26358c2ecf20Sopenharmony_ci		rowdata = dict()
26368c2ecf20Sopenharmony_ci		sortdict = dict()
26378c2ecf20Sopenharmony_ci		myphases = []
26388c2ecf20Sopenharmony_ci		# initialize all device rows to -1 and calculate devrows
26398c2ecf20Sopenharmony_ci		for item in devlist:
26408c2ecf20Sopenharmony_ci			dev = item.dev
26418c2ecf20Sopenharmony_ci			tp = (item.test, item.phase)
26428c2ecf20Sopenharmony_ci			if tp not in myphases:
26438c2ecf20Sopenharmony_ci				myphases.append(tp)
26448c2ecf20Sopenharmony_ci			dev['row'] = -1
26458c2ecf20Sopenharmony_ci			if sortby == 'start':
26468c2ecf20Sopenharmony_ci				# sort by start 1st, then length 2nd
26478c2ecf20Sopenharmony_ci				sortdict[item] = (-1*float(dev['start']), float(dev['end']) - float(dev['start']))
26488c2ecf20Sopenharmony_ci			else:
26498c2ecf20Sopenharmony_ci				# sort by length 1st, then name 2nd
26508c2ecf20Sopenharmony_ci				sortdict[item] = (float(dev['end']) - float(dev['start']), item.dev['name'])
26518c2ecf20Sopenharmony_ci			if 'src' in dev:
26528c2ecf20Sopenharmony_ci				dev['devrows'] = self.getDeviceRows(dev['src'])
26538c2ecf20Sopenharmony_ci		# sort the devlist by length so that large items graph on top
26548c2ecf20Sopenharmony_ci		sortlist = sorted(sortdict, key=sortdict.get, reverse=True)
26558c2ecf20Sopenharmony_ci		orderedlist = []
26568c2ecf20Sopenharmony_ci		for item in sortlist:
26578c2ecf20Sopenharmony_ci			if item.dev['pid'] == -2:
26588c2ecf20Sopenharmony_ci				orderedlist.append(item)
26598c2ecf20Sopenharmony_ci		for item in sortlist:
26608c2ecf20Sopenharmony_ci			if item not in orderedlist:
26618c2ecf20Sopenharmony_ci				orderedlist.append(item)
26628c2ecf20Sopenharmony_ci		# try to pack each row with as many devices as possible
26638c2ecf20Sopenharmony_ci		while(remaining > 0):
26648c2ecf20Sopenharmony_ci			rowheight = 1
26658c2ecf20Sopenharmony_ci			if(row not in rowdata):
26668c2ecf20Sopenharmony_ci				rowdata[row] = []
26678c2ecf20Sopenharmony_ci			for item in orderedlist:
26688c2ecf20Sopenharmony_ci				dev = item.dev
26698c2ecf20Sopenharmony_ci				if(dev['row'] < 0):
26708c2ecf20Sopenharmony_ci					s = dev['start']
26718c2ecf20Sopenharmony_ci					e = dev['end']
26728c2ecf20Sopenharmony_ci					valid = True
26738c2ecf20Sopenharmony_ci					for ritem in rowdata[row]:
26748c2ecf20Sopenharmony_ci						rs = ritem.dev['start']
26758c2ecf20Sopenharmony_ci						re = ritem.dev['end']
26768c2ecf20Sopenharmony_ci						if(not (((s <= rs) and (e <= rs)) or
26778c2ecf20Sopenharmony_ci							((s >= re) and (e >= re)))):
26788c2ecf20Sopenharmony_ci							valid = False
26798c2ecf20Sopenharmony_ci							break
26808c2ecf20Sopenharmony_ci					if(valid):
26818c2ecf20Sopenharmony_ci						rowdata[row].append(item)
26828c2ecf20Sopenharmony_ci						dev['row'] = row
26838c2ecf20Sopenharmony_ci						remaining -= 1
26848c2ecf20Sopenharmony_ci						if 'devrows' in dev and dev['devrows'] > rowheight:
26858c2ecf20Sopenharmony_ci							rowheight = dev['devrows']
26868c2ecf20Sopenharmony_ci			for t, p in myphases:
26878c2ecf20Sopenharmony_ci				if t not in self.rowlines or t not in self.rowheight:
26888c2ecf20Sopenharmony_ci					self.rowlines[t] = dict()
26898c2ecf20Sopenharmony_ci					self.rowheight[t] = dict()
26908c2ecf20Sopenharmony_ci				if p not in self.rowlines[t] or p not in self.rowheight[t]:
26918c2ecf20Sopenharmony_ci					self.rowlines[t][p] = dict()
26928c2ecf20Sopenharmony_ci					self.rowheight[t][p] = dict()
26938c2ecf20Sopenharmony_ci				rh = self.rowH
26948c2ecf20Sopenharmony_ci				# section headers should use a different row height
26958c2ecf20Sopenharmony_ci				if len(rowdata[row]) == 1 and \
26968c2ecf20Sopenharmony_ci					'htmlclass' in rowdata[row][0].dev and \
26978c2ecf20Sopenharmony_ci					'sec' in rowdata[row][0].dev['htmlclass']:
26988c2ecf20Sopenharmony_ci					rh = 15
26998c2ecf20Sopenharmony_ci				self.rowlines[t][p][row] = rowheight
27008c2ecf20Sopenharmony_ci				self.rowheight[t][p][row] = rowheight * rh
27018c2ecf20Sopenharmony_ci			row += 1
27028c2ecf20Sopenharmony_ci		if(row > self.rows):
27038c2ecf20Sopenharmony_ci			self.rows = int(row)
27048c2ecf20Sopenharmony_ci		return row
27058c2ecf20Sopenharmony_ci	def phaseRowHeight(self, test, phase, row):
27068c2ecf20Sopenharmony_ci		return self.rowheight[test][phase][row]
27078c2ecf20Sopenharmony_ci	def phaseRowTop(self, test, phase, row):
27088c2ecf20Sopenharmony_ci		top = 0
27098c2ecf20Sopenharmony_ci		for i in sorted(self.rowheight[test][phase]):
27108c2ecf20Sopenharmony_ci			if i >= row:
27118c2ecf20Sopenharmony_ci				break
27128c2ecf20Sopenharmony_ci			top += self.rowheight[test][phase][i]
27138c2ecf20Sopenharmony_ci		return top
27148c2ecf20Sopenharmony_ci	def calcTotalRows(self):
27158c2ecf20Sopenharmony_ci		# Calculate the heights and offsets for the header and rows
27168c2ecf20Sopenharmony_ci		maxrows = 0
27178c2ecf20Sopenharmony_ci		standardphases = []
27188c2ecf20Sopenharmony_ci		for t in self.rowlines:
27198c2ecf20Sopenharmony_ci			for p in self.rowlines[t]:
27208c2ecf20Sopenharmony_ci				total = 0
27218c2ecf20Sopenharmony_ci				for i in sorted(self.rowlines[t][p]):
27228c2ecf20Sopenharmony_ci					total += self.rowlines[t][p][i]
27238c2ecf20Sopenharmony_ci				if total > maxrows:
27248c2ecf20Sopenharmony_ci					maxrows = total
27258c2ecf20Sopenharmony_ci				if total == len(self.rowlines[t][p]):
27268c2ecf20Sopenharmony_ci					standardphases.append((t, p))
27278c2ecf20Sopenharmony_ci		self.height = self.scaleH + (maxrows*self.rowH)
27288c2ecf20Sopenharmony_ci		self.bodyH = self.height - self.scaleH
27298c2ecf20Sopenharmony_ci		# if there is 1 line per row, draw them the standard way
27308c2ecf20Sopenharmony_ci		for t, p in standardphases:
27318c2ecf20Sopenharmony_ci			for i in sorted(self.rowheight[t][p]):
27328c2ecf20Sopenharmony_ci				self.rowheight[t][p][i] = float(self.bodyH)/len(self.rowlines[t][p])
27338c2ecf20Sopenharmony_ci	def createZoomBox(self, mode='command', testcount=1):
27348c2ecf20Sopenharmony_ci		# Create bounding box, add buttons
27358c2ecf20Sopenharmony_ci		html_zoombox = '<center><button id="zoomin">ZOOM IN +</button><button id="zoomout">ZOOM OUT -</button><button id="zoomdef">ZOOM 1:1</button></center>\n'
27368c2ecf20Sopenharmony_ci		html_timeline = '<div id="dmesgzoombox" class="zoombox">\n<div id="{0}" class="timeline" style="height:{1}px">\n'
27378c2ecf20Sopenharmony_ci		html_devlist1 = '<button id="devlist1" class="devlist" style="float:left;">Device Detail{0}</button>'
27388c2ecf20Sopenharmony_ci		html_devlist2 = '<button id="devlist2" class="devlist" style="float:right;">Device Detail2</button>\n'
27398c2ecf20Sopenharmony_ci		if mode != 'command':
27408c2ecf20Sopenharmony_ci			if testcount > 1:
27418c2ecf20Sopenharmony_ci				self.html += html_devlist2
27428c2ecf20Sopenharmony_ci				self.html += html_devlist1.format('1')
27438c2ecf20Sopenharmony_ci			else:
27448c2ecf20Sopenharmony_ci				self.html += html_devlist1.format('')
27458c2ecf20Sopenharmony_ci		self.html += html_zoombox
27468c2ecf20Sopenharmony_ci		self.html += html_timeline.format('dmesg', self.height)
27478c2ecf20Sopenharmony_ci	# Function: createTimeScale
27488c2ecf20Sopenharmony_ci	# Description:
27498c2ecf20Sopenharmony_ci	#	 Create the timescale for a timeline block
27508c2ecf20Sopenharmony_ci	# Arguments:
27518c2ecf20Sopenharmony_ci	#	 m0: start time (mode begin)
27528c2ecf20Sopenharmony_ci	#	 mMax: end time (mode end)
27538c2ecf20Sopenharmony_ci	#	 tTotal: total timeline time
27548c2ecf20Sopenharmony_ci	#	 mode: suspend or resume
27558c2ecf20Sopenharmony_ci	# Output:
27568c2ecf20Sopenharmony_ci	#	 The html code needed to display the time scale
27578c2ecf20Sopenharmony_ci	def createTimeScale(self, m0, mMax, tTotal, mode):
27588c2ecf20Sopenharmony_ci		timescale = '<div class="t" style="right:{0}%">{1}</div>\n'
27598c2ecf20Sopenharmony_ci		rline = '<div class="t" style="left:0;border-left:1px solid black;border-right:0;">{0}</div>\n'
27608c2ecf20Sopenharmony_ci		output = '<div class="timescale">\n'
27618c2ecf20Sopenharmony_ci		# set scale for timeline
27628c2ecf20Sopenharmony_ci		mTotal = mMax - m0
27638c2ecf20Sopenharmony_ci		tS = 0.1
27648c2ecf20Sopenharmony_ci		if(tTotal <= 0):
27658c2ecf20Sopenharmony_ci			return output+'</div>\n'
27668c2ecf20Sopenharmony_ci		if(tTotal > 4):
27678c2ecf20Sopenharmony_ci			tS = 1
27688c2ecf20Sopenharmony_ci		divTotal = int(mTotal/tS) + 1
27698c2ecf20Sopenharmony_ci		divEdge = (mTotal - tS*(divTotal-1))*100/mTotal
27708c2ecf20Sopenharmony_ci		for i in range(divTotal):
27718c2ecf20Sopenharmony_ci			htmlline = ''
27728c2ecf20Sopenharmony_ci			if(mode == 'suspend'):
27738c2ecf20Sopenharmony_ci				pos = '%0.3f' % (100 - ((float(i)*tS*100)/mTotal) - divEdge)
27748c2ecf20Sopenharmony_ci				val = '%0.fms' % (float(i-divTotal+1)*tS*1000)
27758c2ecf20Sopenharmony_ci				if(i == divTotal - 1):
27768c2ecf20Sopenharmony_ci					val = mode
27778c2ecf20Sopenharmony_ci				htmlline = timescale.format(pos, val)
27788c2ecf20Sopenharmony_ci			else:
27798c2ecf20Sopenharmony_ci				pos = '%0.3f' % (100 - ((float(i)*tS*100)/mTotal))
27808c2ecf20Sopenharmony_ci				val = '%0.fms' % (float(i)*tS*1000)
27818c2ecf20Sopenharmony_ci				htmlline = timescale.format(pos, val)
27828c2ecf20Sopenharmony_ci				if(i == 0):
27838c2ecf20Sopenharmony_ci					htmlline = rline.format(mode)
27848c2ecf20Sopenharmony_ci			output += htmlline
27858c2ecf20Sopenharmony_ci		self.html += output+'</div>\n'
27868c2ecf20Sopenharmony_ci
27878c2ecf20Sopenharmony_ci# Class: TestProps
27888c2ecf20Sopenharmony_ci# Description:
27898c2ecf20Sopenharmony_ci#	 A list of values describing the properties of these test runs
27908c2ecf20Sopenharmony_ciclass TestProps:
27918c2ecf20Sopenharmony_ci	stampfmt = '# [a-z]*-(?P<m>[0-9]{2})(?P<d>[0-9]{2})(?P<y>[0-9]{2})-'+\
27928c2ecf20Sopenharmony_ci				'(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\
27938c2ecf20Sopenharmony_ci				' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$'
27948c2ecf20Sopenharmony_ci	wififmt    = '^# wifi *(?P<d>\S*) *(?P<s>\S*) *(?P<t>[0-9\.]+).*'
27958c2ecf20Sopenharmony_ci	tstatfmt   = '^# turbostat (?P<t>\S*)'
27968c2ecf20Sopenharmony_ci	testerrfmt = '^# enter_sleep_error (?P<e>.*)'
27978c2ecf20Sopenharmony_ci	sysinfofmt = '^# sysinfo .*'
27988c2ecf20Sopenharmony_ci	cmdlinefmt = '^# command \| (?P<cmd>.*)'
27998c2ecf20Sopenharmony_ci	kparamsfmt = '^# kparams \| (?P<kp>.*)'
28008c2ecf20Sopenharmony_ci	devpropfmt = '# Device Properties: .*'
28018c2ecf20Sopenharmony_ci	pinfofmt   = '# platform-(?P<val>[a-z,A-Z,0-9]*): (?P<info>.*)'
28028c2ecf20Sopenharmony_ci	tracertypefmt = '# tracer: (?P<t>.*)'
28038c2ecf20Sopenharmony_ci	firmwarefmt = '# fwsuspend (?P<s>[0-9]*) fwresume (?P<r>[0-9]*)$'
28048c2ecf20Sopenharmony_ci	procexecfmt = 'ps - (?P<ps>.*)$'
28058c2ecf20Sopenharmony_ci	ftrace_line_fmt_fg = \
28068c2ecf20Sopenharmony_ci		'^ *(?P<time>[0-9\.]*) *\| *(?P<cpu>[0-9]*)\)'+\
28078c2ecf20Sopenharmony_ci		' *(?P<proc>.*)-(?P<pid>[0-9]*) *\|'+\
28088c2ecf20Sopenharmony_ci		'[ +!#\*@$]*(?P<dur>[0-9\.]*) .*\|  (?P<msg>.*)'
28098c2ecf20Sopenharmony_ci	ftrace_line_fmt_nop = \
28108c2ecf20Sopenharmony_ci		' *(?P<proc>.*)-(?P<pid>[0-9]*) *\[(?P<cpu>[0-9]*)\] *'+\
28118c2ecf20Sopenharmony_ci		'(?P<flags>\S*) *(?P<time>[0-9\.]*): *'+\
28128c2ecf20Sopenharmony_ci		'(?P<msg>.*)'
28138c2ecf20Sopenharmony_ci	machinesuspend = 'machine_suspend\[.*'
28148c2ecf20Sopenharmony_ci	def __init__(self):
28158c2ecf20Sopenharmony_ci		self.stamp = ''
28168c2ecf20Sopenharmony_ci		self.sysinfo = ''
28178c2ecf20Sopenharmony_ci		self.cmdline = ''
28188c2ecf20Sopenharmony_ci		self.testerror = []
28198c2ecf20Sopenharmony_ci		self.turbostat = []
28208c2ecf20Sopenharmony_ci		self.wifi = []
28218c2ecf20Sopenharmony_ci		self.fwdata = []
28228c2ecf20Sopenharmony_ci		self.ftrace_line_fmt = self.ftrace_line_fmt_nop
28238c2ecf20Sopenharmony_ci		self.cgformat = False
28248c2ecf20Sopenharmony_ci		self.data = 0
28258c2ecf20Sopenharmony_ci		self.ktemp = dict()
28268c2ecf20Sopenharmony_ci	def setTracerType(self, tracer):
28278c2ecf20Sopenharmony_ci		if(tracer == 'function_graph'):
28288c2ecf20Sopenharmony_ci			self.cgformat = True
28298c2ecf20Sopenharmony_ci			self.ftrace_line_fmt = self.ftrace_line_fmt_fg
28308c2ecf20Sopenharmony_ci		elif(tracer == 'nop'):
28318c2ecf20Sopenharmony_ci			self.ftrace_line_fmt = self.ftrace_line_fmt_nop
28328c2ecf20Sopenharmony_ci		else:
28338c2ecf20Sopenharmony_ci			doError('Invalid tracer format: [%s]' % tracer)
28348c2ecf20Sopenharmony_ci	def stampInfo(self, line, sv):
28358c2ecf20Sopenharmony_ci		if re.match(self.stampfmt, line):
28368c2ecf20Sopenharmony_ci			self.stamp = line
28378c2ecf20Sopenharmony_ci			return True
28388c2ecf20Sopenharmony_ci		elif re.match(self.sysinfofmt, line):
28398c2ecf20Sopenharmony_ci			self.sysinfo = line
28408c2ecf20Sopenharmony_ci			return True
28418c2ecf20Sopenharmony_ci		elif re.match(self.tstatfmt, line):
28428c2ecf20Sopenharmony_ci			self.turbostat.append(line)
28438c2ecf20Sopenharmony_ci			return True
28448c2ecf20Sopenharmony_ci		elif re.match(self.wififmt, line):
28458c2ecf20Sopenharmony_ci			self.wifi.append(line)
28468c2ecf20Sopenharmony_ci			return True
28478c2ecf20Sopenharmony_ci		elif re.match(self.testerrfmt, line):
28488c2ecf20Sopenharmony_ci			self.testerror.append(line)
28498c2ecf20Sopenharmony_ci			return True
28508c2ecf20Sopenharmony_ci		elif re.match(self.firmwarefmt, line):
28518c2ecf20Sopenharmony_ci			self.fwdata.append(line)
28528c2ecf20Sopenharmony_ci			return True
28538c2ecf20Sopenharmony_ci		elif(re.match(self.devpropfmt, line)):
28548c2ecf20Sopenharmony_ci			self.parseDevprops(line, sv)
28558c2ecf20Sopenharmony_ci			return True
28568c2ecf20Sopenharmony_ci		elif(re.match(self.pinfofmt, line)):
28578c2ecf20Sopenharmony_ci			self.parsePlatformInfo(line, sv)
28588c2ecf20Sopenharmony_ci			return True
28598c2ecf20Sopenharmony_ci		m = re.match(self.cmdlinefmt, line)
28608c2ecf20Sopenharmony_ci		if m:
28618c2ecf20Sopenharmony_ci			self.cmdline = m.group('cmd')
28628c2ecf20Sopenharmony_ci			return True
28638c2ecf20Sopenharmony_ci		m = re.match(self.tracertypefmt, line)
28648c2ecf20Sopenharmony_ci		if(m):
28658c2ecf20Sopenharmony_ci			self.setTracerType(m.group('t'))
28668c2ecf20Sopenharmony_ci			return True
28678c2ecf20Sopenharmony_ci		return False
28688c2ecf20Sopenharmony_ci	def parseStamp(self, data, sv):
28698c2ecf20Sopenharmony_ci		# global test data
28708c2ecf20Sopenharmony_ci		m = re.match(self.stampfmt, self.stamp)
28718c2ecf20Sopenharmony_ci		if not self.stamp or not m:
28728c2ecf20Sopenharmony_ci			doError('data does not include the expected stamp')
28738c2ecf20Sopenharmony_ci		data.stamp = {'time': '', 'host': '', 'mode': ''}
28748c2ecf20Sopenharmony_ci		dt = datetime(int(m.group('y'))+2000, int(m.group('m')),
28758c2ecf20Sopenharmony_ci			int(m.group('d')), int(m.group('H')), int(m.group('M')),
28768c2ecf20Sopenharmony_ci			int(m.group('S')))
28778c2ecf20Sopenharmony_ci		data.stamp['time'] = dt.strftime('%B %d %Y, %I:%M:%S %p')
28788c2ecf20Sopenharmony_ci		data.stamp['host'] = m.group('host')
28798c2ecf20Sopenharmony_ci		data.stamp['mode'] = m.group('mode')
28808c2ecf20Sopenharmony_ci		data.stamp['kernel'] = m.group('kernel')
28818c2ecf20Sopenharmony_ci		if re.match(self.sysinfofmt, self.sysinfo):
28828c2ecf20Sopenharmony_ci			for f in self.sysinfo.split('|'):
28838c2ecf20Sopenharmony_ci				if '#' in f:
28848c2ecf20Sopenharmony_ci					continue
28858c2ecf20Sopenharmony_ci				tmp = f.strip().split(':', 1)
28868c2ecf20Sopenharmony_ci				key = tmp[0]
28878c2ecf20Sopenharmony_ci				val = tmp[1]
28888c2ecf20Sopenharmony_ci				data.stamp[key] = val
28898c2ecf20Sopenharmony_ci		sv.hostname = data.stamp['host']
28908c2ecf20Sopenharmony_ci		sv.suspendmode = data.stamp['mode']
28918c2ecf20Sopenharmony_ci		if sv.suspendmode == 'freeze':
28928c2ecf20Sopenharmony_ci			self.machinesuspend = 'timekeeping_freeze\[.*'
28938c2ecf20Sopenharmony_ci		else:
28948c2ecf20Sopenharmony_ci			self.machinesuspend = 'machine_suspend\[.*'
28958c2ecf20Sopenharmony_ci		if sv.suspendmode == 'command' and sv.ftracefile != '':
28968c2ecf20Sopenharmony_ci			modes = ['on', 'freeze', 'standby', 'mem', 'disk']
28978c2ecf20Sopenharmony_ci			fp = sv.openlog(sv.ftracefile, 'r')
28988c2ecf20Sopenharmony_ci			for line in fp:
28998c2ecf20Sopenharmony_ci				m = re.match('.* machine_suspend\[(?P<mode>.*)\]', line)
29008c2ecf20Sopenharmony_ci				if m and m.group('mode') in ['1', '2', '3', '4']:
29018c2ecf20Sopenharmony_ci					sv.suspendmode = modes[int(m.group('mode'))]
29028c2ecf20Sopenharmony_ci					data.stamp['mode'] = sv.suspendmode
29038c2ecf20Sopenharmony_ci					break
29048c2ecf20Sopenharmony_ci			fp.close()
29058c2ecf20Sopenharmony_ci		sv.cmdline = self.cmdline
29068c2ecf20Sopenharmony_ci		if not sv.stamp:
29078c2ecf20Sopenharmony_ci			sv.stamp = data.stamp
29088c2ecf20Sopenharmony_ci		# firmware data
29098c2ecf20Sopenharmony_ci		if sv.suspendmode == 'mem' and len(self.fwdata) > data.testnumber:
29108c2ecf20Sopenharmony_ci			m = re.match(self.firmwarefmt, self.fwdata[data.testnumber])
29118c2ecf20Sopenharmony_ci			if m:
29128c2ecf20Sopenharmony_ci				data.fwSuspend, data.fwResume = int(m.group('s')), int(m.group('r'))
29138c2ecf20Sopenharmony_ci				if(data.fwSuspend > 0 or data.fwResume > 0):
29148c2ecf20Sopenharmony_ci					data.fwValid = True
29158c2ecf20Sopenharmony_ci		# turbostat data
29168c2ecf20Sopenharmony_ci		if len(self.turbostat) > data.testnumber:
29178c2ecf20Sopenharmony_ci			m = re.match(self.tstatfmt, self.turbostat[data.testnumber])
29188c2ecf20Sopenharmony_ci			if m:
29198c2ecf20Sopenharmony_ci				data.turbostat = m.group('t')
29208c2ecf20Sopenharmony_ci		# wifi data
29218c2ecf20Sopenharmony_ci		if len(self.wifi) > data.testnumber:
29228c2ecf20Sopenharmony_ci			m = re.match(self.wififmt, self.wifi[data.testnumber])
29238c2ecf20Sopenharmony_ci			if m:
29248c2ecf20Sopenharmony_ci				data.wifi = {'dev': m.group('d'), 'stat': m.group('s'),
29258c2ecf20Sopenharmony_ci					'time': float(m.group('t'))}
29268c2ecf20Sopenharmony_ci				data.stamp['wifi'] = m.group('d')
29278c2ecf20Sopenharmony_ci		# sleep mode enter errors
29288c2ecf20Sopenharmony_ci		if len(self.testerror) > data.testnumber:
29298c2ecf20Sopenharmony_ci			m = re.match(self.testerrfmt, self.testerror[data.testnumber])
29308c2ecf20Sopenharmony_ci			if m:
29318c2ecf20Sopenharmony_ci				data.enterfail = m.group('e')
29328c2ecf20Sopenharmony_ci	def devprops(self, data):
29338c2ecf20Sopenharmony_ci		props = dict()
29348c2ecf20Sopenharmony_ci		devlist = data.split(';')
29358c2ecf20Sopenharmony_ci		for dev in devlist:
29368c2ecf20Sopenharmony_ci			f = dev.split(',')
29378c2ecf20Sopenharmony_ci			if len(f) < 3:
29388c2ecf20Sopenharmony_ci				continue
29398c2ecf20Sopenharmony_ci			dev = f[0]
29408c2ecf20Sopenharmony_ci			props[dev] = DevProps()
29418c2ecf20Sopenharmony_ci			props[dev].altname = f[1]
29428c2ecf20Sopenharmony_ci			if int(f[2]):
29438c2ecf20Sopenharmony_ci				props[dev].isasync = True
29448c2ecf20Sopenharmony_ci			else:
29458c2ecf20Sopenharmony_ci				props[dev].isasync = False
29468c2ecf20Sopenharmony_ci		return props
29478c2ecf20Sopenharmony_ci	def parseDevprops(self, line, sv):
29488c2ecf20Sopenharmony_ci		idx = line.index(': ') + 2
29498c2ecf20Sopenharmony_ci		if idx >= len(line):
29508c2ecf20Sopenharmony_ci			return
29518c2ecf20Sopenharmony_ci		props = self.devprops(line[idx:])
29528c2ecf20Sopenharmony_ci		if sv.suspendmode == 'command' and 'testcommandstring' in props:
29538c2ecf20Sopenharmony_ci			sv.testcommand = props['testcommandstring'].altname
29548c2ecf20Sopenharmony_ci		sv.devprops = props
29558c2ecf20Sopenharmony_ci	def parsePlatformInfo(self, line, sv):
29568c2ecf20Sopenharmony_ci		m = re.match(self.pinfofmt, line)
29578c2ecf20Sopenharmony_ci		if not m:
29588c2ecf20Sopenharmony_ci			return
29598c2ecf20Sopenharmony_ci		name, info = m.group('val'), m.group('info')
29608c2ecf20Sopenharmony_ci		if name == 'devinfo':
29618c2ecf20Sopenharmony_ci			sv.devprops = self.devprops(sv.b64unzip(info))
29628c2ecf20Sopenharmony_ci			return
29638c2ecf20Sopenharmony_ci		elif name == 'testcmd':
29648c2ecf20Sopenharmony_ci			sv.testcommand = info
29658c2ecf20Sopenharmony_ci			return
29668c2ecf20Sopenharmony_ci		field = info.split('|')
29678c2ecf20Sopenharmony_ci		if len(field) < 2:
29688c2ecf20Sopenharmony_ci			return
29698c2ecf20Sopenharmony_ci		cmdline = field[0].strip()
29708c2ecf20Sopenharmony_ci		output = sv.b64unzip(field[1].strip())
29718c2ecf20Sopenharmony_ci		sv.platinfo.append([name, cmdline, output])
29728c2ecf20Sopenharmony_ci
29738c2ecf20Sopenharmony_ci# Class: TestRun
29748c2ecf20Sopenharmony_ci# Description:
29758c2ecf20Sopenharmony_ci#	 A container for a suspend/resume test run. This is necessary as
29768c2ecf20Sopenharmony_ci#	 there could be more than one, and they need to be separate.
29778c2ecf20Sopenharmony_ciclass TestRun:
29788c2ecf20Sopenharmony_ci	def __init__(self, dataobj):
29798c2ecf20Sopenharmony_ci		self.data = dataobj
29808c2ecf20Sopenharmony_ci		self.ftemp = dict()
29818c2ecf20Sopenharmony_ci		self.ttemp = dict()
29828c2ecf20Sopenharmony_ci
29838c2ecf20Sopenharmony_ciclass ProcessMonitor:
29848c2ecf20Sopenharmony_ci	def __init__(self):
29858c2ecf20Sopenharmony_ci		self.proclist = dict()
29868c2ecf20Sopenharmony_ci		self.running = False
29878c2ecf20Sopenharmony_ci	def procstat(self):
29888c2ecf20Sopenharmony_ci		c = ['cat /proc/[1-9]*/stat 2>/dev/null']
29898c2ecf20Sopenharmony_ci		process = Popen(c, shell=True, stdout=PIPE)
29908c2ecf20Sopenharmony_ci		running = dict()
29918c2ecf20Sopenharmony_ci		for line in process.stdout:
29928c2ecf20Sopenharmony_ci			data = ascii(line).split()
29938c2ecf20Sopenharmony_ci			pid = data[0]
29948c2ecf20Sopenharmony_ci			name = re.sub('[()]', '', data[1])
29958c2ecf20Sopenharmony_ci			user = int(data[13])
29968c2ecf20Sopenharmony_ci			kern = int(data[14])
29978c2ecf20Sopenharmony_ci			kjiff = ujiff = 0
29988c2ecf20Sopenharmony_ci			if pid not in self.proclist:
29998c2ecf20Sopenharmony_ci				self.proclist[pid] = {'name' : name, 'user' : user, 'kern' : kern}
30008c2ecf20Sopenharmony_ci			else:
30018c2ecf20Sopenharmony_ci				val = self.proclist[pid]
30028c2ecf20Sopenharmony_ci				ujiff = user - val['user']
30038c2ecf20Sopenharmony_ci				kjiff = kern - val['kern']
30048c2ecf20Sopenharmony_ci				val['user'] = user
30058c2ecf20Sopenharmony_ci				val['kern'] = kern
30068c2ecf20Sopenharmony_ci			if ujiff > 0 or kjiff > 0:
30078c2ecf20Sopenharmony_ci				running[pid] = ujiff + kjiff
30088c2ecf20Sopenharmony_ci		process.wait()
30098c2ecf20Sopenharmony_ci		out = ''
30108c2ecf20Sopenharmony_ci		for pid in running:
30118c2ecf20Sopenharmony_ci			jiffies = running[pid]
30128c2ecf20Sopenharmony_ci			val = self.proclist[pid]
30138c2ecf20Sopenharmony_ci			if out:
30148c2ecf20Sopenharmony_ci				out += ','
30158c2ecf20Sopenharmony_ci			out += '%s-%s %d' % (val['name'], pid, jiffies)
30168c2ecf20Sopenharmony_ci		return 'ps - '+out
30178c2ecf20Sopenharmony_ci	def processMonitor(self, tid):
30188c2ecf20Sopenharmony_ci		while self.running:
30198c2ecf20Sopenharmony_ci			out = self.procstat()
30208c2ecf20Sopenharmony_ci			if out:
30218c2ecf20Sopenharmony_ci				sysvals.fsetVal(out, 'trace_marker')
30228c2ecf20Sopenharmony_ci	def start(self):
30238c2ecf20Sopenharmony_ci		self.thread = Thread(target=self.processMonitor, args=(0,))
30248c2ecf20Sopenharmony_ci		self.running = True
30258c2ecf20Sopenharmony_ci		self.thread.start()
30268c2ecf20Sopenharmony_ci	def stop(self):
30278c2ecf20Sopenharmony_ci		self.running = False
30288c2ecf20Sopenharmony_ci
30298c2ecf20Sopenharmony_ci# ----------------- FUNCTIONS --------------------
30308c2ecf20Sopenharmony_ci
30318c2ecf20Sopenharmony_ci# Function: doesTraceLogHaveTraceEvents
30328c2ecf20Sopenharmony_ci# Description:
30338c2ecf20Sopenharmony_ci#	 Quickly determine if the ftrace log has all of the trace events,
30348c2ecf20Sopenharmony_ci#	 markers, and/or kprobes required for primary parsing.
30358c2ecf20Sopenharmony_cidef doesTraceLogHaveTraceEvents():
30368c2ecf20Sopenharmony_ci	kpcheck = ['_cal: (', '_ret: (']
30378c2ecf20Sopenharmony_ci	techeck = ['suspend_resume', 'device_pm_callback']
30388c2ecf20Sopenharmony_ci	tmcheck = ['SUSPEND START', 'RESUME COMPLETE']
30398c2ecf20Sopenharmony_ci	sysvals.usekprobes = False
30408c2ecf20Sopenharmony_ci	fp = sysvals.openlog(sysvals.ftracefile, 'r')
30418c2ecf20Sopenharmony_ci	for line in fp:
30428c2ecf20Sopenharmony_ci		# check for kprobes
30438c2ecf20Sopenharmony_ci		if not sysvals.usekprobes:
30448c2ecf20Sopenharmony_ci			for i in kpcheck:
30458c2ecf20Sopenharmony_ci				if i in line:
30468c2ecf20Sopenharmony_ci					sysvals.usekprobes = True
30478c2ecf20Sopenharmony_ci		# check for all necessary trace events
30488c2ecf20Sopenharmony_ci		check = techeck[:]
30498c2ecf20Sopenharmony_ci		for i in techeck:
30508c2ecf20Sopenharmony_ci			if i in line:
30518c2ecf20Sopenharmony_ci				check.remove(i)
30528c2ecf20Sopenharmony_ci		techeck = check
30538c2ecf20Sopenharmony_ci		# check for all necessary trace markers
30548c2ecf20Sopenharmony_ci		check = tmcheck[:]
30558c2ecf20Sopenharmony_ci		for i in tmcheck:
30568c2ecf20Sopenharmony_ci			if i in line:
30578c2ecf20Sopenharmony_ci				check.remove(i)
30588c2ecf20Sopenharmony_ci		tmcheck = check
30598c2ecf20Sopenharmony_ci	fp.close()
30608c2ecf20Sopenharmony_ci	sysvals.usetraceevents = True if len(techeck) < 2 else False
30618c2ecf20Sopenharmony_ci	sysvals.usetracemarkers = True if len(tmcheck) == 0 else False
30628c2ecf20Sopenharmony_ci
30638c2ecf20Sopenharmony_ci# Function: appendIncompleteTraceLog
30648c2ecf20Sopenharmony_ci# Description:
30658c2ecf20Sopenharmony_ci#	 [deprecated for kernel 3.15 or newer]
30668c2ecf20Sopenharmony_ci#	 Adds callgraph data which lacks trace event data. This is only
30678c2ecf20Sopenharmony_ci#	 for timelines generated from 3.15 or older
30688c2ecf20Sopenharmony_ci# Arguments:
30698c2ecf20Sopenharmony_ci#	 testruns: the array of Data objects obtained from parseKernelLog
30708c2ecf20Sopenharmony_cidef appendIncompleteTraceLog(testruns):
30718c2ecf20Sopenharmony_ci	# create TestRun vessels for ftrace parsing
30728c2ecf20Sopenharmony_ci	testcnt = len(testruns)
30738c2ecf20Sopenharmony_ci	testidx = 0
30748c2ecf20Sopenharmony_ci	testrun = []
30758c2ecf20Sopenharmony_ci	for data in testruns:
30768c2ecf20Sopenharmony_ci		testrun.append(TestRun(data))
30778c2ecf20Sopenharmony_ci
30788c2ecf20Sopenharmony_ci	# extract the callgraph and traceevent data
30798c2ecf20Sopenharmony_ci	sysvals.vprint('Analyzing the ftrace data (%s)...' % \
30808c2ecf20Sopenharmony_ci		os.path.basename(sysvals.ftracefile))
30818c2ecf20Sopenharmony_ci	tp = TestProps()
30828c2ecf20Sopenharmony_ci	tf = sysvals.openlog(sysvals.ftracefile, 'r')
30838c2ecf20Sopenharmony_ci	data = 0
30848c2ecf20Sopenharmony_ci	for line in tf:
30858c2ecf20Sopenharmony_ci		# remove any latent carriage returns
30868c2ecf20Sopenharmony_ci		line = line.replace('\r\n', '')
30878c2ecf20Sopenharmony_ci		if tp.stampInfo(line, sysvals):
30888c2ecf20Sopenharmony_ci			continue
30898c2ecf20Sopenharmony_ci		# parse only valid lines, if this is not one move on
30908c2ecf20Sopenharmony_ci		m = re.match(tp.ftrace_line_fmt, line)
30918c2ecf20Sopenharmony_ci		if(not m):
30928c2ecf20Sopenharmony_ci			continue
30938c2ecf20Sopenharmony_ci		# gather the basic message data from the line
30948c2ecf20Sopenharmony_ci		m_time = m.group('time')
30958c2ecf20Sopenharmony_ci		m_pid = m.group('pid')
30968c2ecf20Sopenharmony_ci		m_msg = m.group('msg')
30978c2ecf20Sopenharmony_ci		if(tp.cgformat):
30988c2ecf20Sopenharmony_ci			m_param3 = m.group('dur')
30998c2ecf20Sopenharmony_ci		else:
31008c2ecf20Sopenharmony_ci			m_param3 = 'traceevent'
31018c2ecf20Sopenharmony_ci		if(m_time and m_pid and m_msg):
31028c2ecf20Sopenharmony_ci			t = FTraceLine(m_time, m_msg, m_param3)
31038c2ecf20Sopenharmony_ci			pid = int(m_pid)
31048c2ecf20Sopenharmony_ci		else:
31058c2ecf20Sopenharmony_ci			continue
31068c2ecf20Sopenharmony_ci		# the line should be a call, return, or event
31078c2ecf20Sopenharmony_ci		if(not t.fcall and not t.freturn and not t.fevent):
31088c2ecf20Sopenharmony_ci			continue
31098c2ecf20Sopenharmony_ci		# look for the suspend start marker
31108c2ecf20Sopenharmony_ci		if(t.startMarker()):
31118c2ecf20Sopenharmony_ci			data = testrun[testidx].data
31128c2ecf20Sopenharmony_ci			tp.parseStamp(data, sysvals)
31138c2ecf20Sopenharmony_ci			data.setStart(t.time, t.name)
31148c2ecf20Sopenharmony_ci			continue
31158c2ecf20Sopenharmony_ci		if(not data):
31168c2ecf20Sopenharmony_ci			continue
31178c2ecf20Sopenharmony_ci		# find the end of resume
31188c2ecf20Sopenharmony_ci		if(t.endMarker()):
31198c2ecf20Sopenharmony_ci			data.setEnd(t.time, t.name)
31208c2ecf20Sopenharmony_ci			testidx += 1
31218c2ecf20Sopenharmony_ci			if(testidx >= testcnt):
31228c2ecf20Sopenharmony_ci				break
31238c2ecf20Sopenharmony_ci			continue
31248c2ecf20Sopenharmony_ci		# trace event processing
31258c2ecf20Sopenharmony_ci		if(t.fevent):
31268c2ecf20Sopenharmony_ci			continue
31278c2ecf20Sopenharmony_ci		# call/return processing
31288c2ecf20Sopenharmony_ci		elif sysvals.usecallgraph:
31298c2ecf20Sopenharmony_ci			# create a callgraph object for the data
31308c2ecf20Sopenharmony_ci			if(pid not in testrun[testidx].ftemp):
31318c2ecf20Sopenharmony_ci				testrun[testidx].ftemp[pid] = []
31328c2ecf20Sopenharmony_ci				testrun[testidx].ftemp[pid].append(FTraceCallGraph(pid, sysvals))
31338c2ecf20Sopenharmony_ci			# when the call is finished, see which device matches it
31348c2ecf20Sopenharmony_ci			cg = testrun[testidx].ftemp[pid][-1]
31358c2ecf20Sopenharmony_ci			res = cg.addLine(t)
31368c2ecf20Sopenharmony_ci			if(res != 0):
31378c2ecf20Sopenharmony_ci				testrun[testidx].ftemp[pid].append(FTraceCallGraph(pid, sysvals))
31388c2ecf20Sopenharmony_ci			if(res == -1):
31398c2ecf20Sopenharmony_ci				testrun[testidx].ftemp[pid][-1].addLine(t)
31408c2ecf20Sopenharmony_ci	tf.close()
31418c2ecf20Sopenharmony_ci
31428c2ecf20Sopenharmony_ci	for test in testrun:
31438c2ecf20Sopenharmony_ci		# add the callgraph data to the device hierarchy
31448c2ecf20Sopenharmony_ci		for pid in test.ftemp:
31458c2ecf20Sopenharmony_ci			for cg in test.ftemp[pid]:
31468c2ecf20Sopenharmony_ci				if len(cg.list) < 1 or cg.invalid or (cg.end - cg.start == 0):
31478c2ecf20Sopenharmony_ci					continue
31488c2ecf20Sopenharmony_ci				if(not cg.postProcess()):
31498c2ecf20Sopenharmony_ci					id = 'task %s cpu %s' % (pid, m.group('cpu'))
31508c2ecf20Sopenharmony_ci					sysvals.vprint('Sanity check failed for '+\
31518c2ecf20Sopenharmony_ci						id+', ignoring this callback')
31528c2ecf20Sopenharmony_ci					continue
31538c2ecf20Sopenharmony_ci				callstart = cg.start
31548c2ecf20Sopenharmony_ci				callend = cg.end
31558c2ecf20Sopenharmony_ci				for p in test.data.sortedPhases():
31568c2ecf20Sopenharmony_ci					if(test.data.dmesg[p]['start'] <= callstart and
31578c2ecf20Sopenharmony_ci						callstart <= test.data.dmesg[p]['end']):
31588c2ecf20Sopenharmony_ci						list = test.data.dmesg[p]['list']
31598c2ecf20Sopenharmony_ci						for devname in list:
31608c2ecf20Sopenharmony_ci							dev = list[devname]
31618c2ecf20Sopenharmony_ci							if(pid == dev['pid'] and
31628c2ecf20Sopenharmony_ci								callstart <= dev['start'] and
31638c2ecf20Sopenharmony_ci								callend >= dev['end']):
31648c2ecf20Sopenharmony_ci								dev['ftrace'] = cg
31658c2ecf20Sopenharmony_ci						break
31668c2ecf20Sopenharmony_ci
31678c2ecf20Sopenharmony_ci# Function: parseTraceLog
31688c2ecf20Sopenharmony_ci# Description:
31698c2ecf20Sopenharmony_ci#	 Analyze an ftrace log output file generated from this app during
31708c2ecf20Sopenharmony_ci#	 the execution phase. Used when the ftrace log is the primary data source
31718c2ecf20Sopenharmony_ci#	 and includes the suspend_resume and device_pm_callback trace events
31728c2ecf20Sopenharmony_ci#	 The ftrace filename is taken from sysvals
31738c2ecf20Sopenharmony_ci# Output:
31748c2ecf20Sopenharmony_ci#	 An array of Data objects
31758c2ecf20Sopenharmony_cidef parseTraceLog(live=False):
31768c2ecf20Sopenharmony_ci	sysvals.vprint('Analyzing the ftrace data (%s)...' % \
31778c2ecf20Sopenharmony_ci		os.path.basename(sysvals.ftracefile))
31788c2ecf20Sopenharmony_ci	if(os.path.exists(sysvals.ftracefile) == False):
31798c2ecf20Sopenharmony_ci		doError('%s does not exist' % sysvals.ftracefile)
31808c2ecf20Sopenharmony_ci	if not live:
31818c2ecf20Sopenharmony_ci		sysvals.setupAllKprobes()
31828c2ecf20Sopenharmony_ci	ksuscalls = ['ksys_sync', 'pm_prepare_console']
31838c2ecf20Sopenharmony_ci	krescalls = ['pm_restore_console']
31848c2ecf20Sopenharmony_ci	tracewatch = ['irq_wakeup']
31858c2ecf20Sopenharmony_ci	if sysvals.usekprobes:
31868c2ecf20Sopenharmony_ci		tracewatch += ['sync_filesystems', 'freeze_processes', 'syscore_suspend',
31878c2ecf20Sopenharmony_ci			'syscore_resume', 'resume_console', 'thaw_processes', 'CPU_ON',
31888c2ecf20Sopenharmony_ci			'CPU_OFF', 'acpi_suspend']
31898c2ecf20Sopenharmony_ci
31908c2ecf20Sopenharmony_ci	# extract the callgraph and traceevent data
31918c2ecf20Sopenharmony_ci	s2idle_enter = hwsus = False
31928c2ecf20Sopenharmony_ci	tp = TestProps()
31938c2ecf20Sopenharmony_ci	testruns, testdata = [], []
31948c2ecf20Sopenharmony_ci	testrun, data, limbo = 0, 0, True
31958c2ecf20Sopenharmony_ci	tf = sysvals.openlog(sysvals.ftracefile, 'r')
31968c2ecf20Sopenharmony_ci	phase = 'suspend_prepare'
31978c2ecf20Sopenharmony_ci	for line in tf:
31988c2ecf20Sopenharmony_ci		# remove any latent carriage returns
31998c2ecf20Sopenharmony_ci		line = line.replace('\r\n', '')
32008c2ecf20Sopenharmony_ci		if tp.stampInfo(line, sysvals):
32018c2ecf20Sopenharmony_ci			continue
32028c2ecf20Sopenharmony_ci		# ignore all other commented lines
32038c2ecf20Sopenharmony_ci		if line[0] == '#':
32048c2ecf20Sopenharmony_ci			continue
32058c2ecf20Sopenharmony_ci		# ftrace line: parse only valid lines
32068c2ecf20Sopenharmony_ci		m = re.match(tp.ftrace_line_fmt, line)
32078c2ecf20Sopenharmony_ci		if(not m):
32088c2ecf20Sopenharmony_ci			continue
32098c2ecf20Sopenharmony_ci		# gather the basic message data from the line
32108c2ecf20Sopenharmony_ci		m_time = m.group('time')
32118c2ecf20Sopenharmony_ci		m_proc = m.group('proc')
32128c2ecf20Sopenharmony_ci		m_pid = m.group('pid')
32138c2ecf20Sopenharmony_ci		m_msg = m.group('msg')
32148c2ecf20Sopenharmony_ci		if(tp.cgformat):
32158c2ecf20Sopenharmony_ci			m_param3 = m.group('dur')
32168c2ecf20Sopenharmony_ci		else:
32178c2ecf20Sopenharmony_ci			m_param3 = 'traceevent'
32188c2ecf20Sopenharmony_ci		if(m_time and m_pid and m_msg):
32198c2ecf20Sopenharmony_ci			t = FTraceLine(m_time, m_msg, m_param3)
32208c2ecf20Sopenharmony_ci			pid = int(m_pid)
32218c2ecf20Sopenharmony_ci		else:
32228c2ecf20Sopenharmony_ci			continue
32238c2ecf20Sopenharmony_ci		# the line should be a call, return, or event
32248c2ecf20Sopenharmony_ci		if(not t.fcall and not t.freturn and not t.fevent):
32258c2ecf20Sopenharmony_ci			continue
32268c2ecf20Sopenharmony_ci		# find the start of suspend
32278c2ecf20Sopenharmony_ci		if(t.startMarker()):
32288c2ecf20Sopenharmony_ci			data, limbo = Data(len(testdata)), False
32298c2ecf20Sopenharmony_ci			testdata.append(data)
32308c2ecf20Sopenharmony_ci			testrun = TestRun(data)
32318c2ecf20Sopenharmony_ci			testruns.append(testrun)
32328c2ecf20Sopenharmony_ci			tp.parseStamp(data, sysvals)
32338c2ecf20Sopenharmony_ci			data.setStart(t.time, t.name)
32348c2ecf20Sopenharmony_ci			data.first_suspend_prepare = True
32358c2ecf20Sopenharmony_ci			phase = data.setPhase('suspend_prepare', t.time, True)
32368c2ecf20Sopenharmony_ci			continue
32378c2ecf20Sopenharmony_ci		if(not data or limbo):
32388c2ecf20Sopenharmony_ci			continue
32398c2ecf20Sopenharmony_ci		# process cpu exec line
32408c2ecf20Sopenharmony_ci		if t.type == 'tracing_mark_write':
32418c2ecf20Sopenharmony_ci			m = re.match(tp.procexecfmt, t.name)
32428c2ecf20Sopenharmony_ci			if(m):
32438c2ecf20Sopenharmony_ci				proclist = dict()
32448c2ecf20Sopenharmony_ci				for ps in m.group('ps').split(','):
32458c2ecf20Sopenharmony_ci					val = ps.split()
32468c2ecf20Sopenharmony_ci					if not val:
32478c2ecf20Sopenharmony_ci						continue
32488c2ecf20Sopenharmony_ci					name = val[0].replace('--', '-')
32498c2ecf20Sopenharmony_ci					proclist[name] = int(val[1])
32508c2ecf20Sopenharmony_ci				data.pstl[t.time] = proclist
32518c2ecf20Sopenharmony_ci				continue
32528c2ecf20Sopenharmony_ci		# find the end of resume
32538c2ecf20Sopenharmony_ci		if(t.endMarker()):
32548c2ecf20Sopenharmony_ci			if data.tKernRes == 0:
32558c2ecf20Sopenharmony_ci				data.tKernRes = t.time
32568c2ecf20Sopenharmony_ci			data.handleEndMarker(t.time, t.name)
32578c2ecf20Sopenharmony_ci			if(not sysvals.usetracemarkers):
32588c2ecf20Sopenharmony_ci				# no trace markers? then quit and be sure to finish recording
32598c2ecf20Sopenharmony_ci				# the event we used to trigger resume end
32608c2ecf20Sopenharmony_ci				if('thaw_processes' in testrun.ttemp and len(testrun.ttemp['thaw_processes']) > 0):
32618c2ecf20Sopenharmony_ci					# if an entry exists, assume this is its end
32628c2ecf20Sopenharmony_ci					testrun.ttemp['thaw_processes'][-1]['end'] = t.time
32638c2ecf20Sopenharmony_ci			limbo = True
32648c2ecf20Sopenharmony_ci			continue
32658c2ecf20Sopenharmony_ci		# trace event processing
32668c2ecf20Sopenharmony_ci		if(t.fevent):
32678c2ecf20Sopenharmony_ci			if(t.type == 'suspend_resume'):
32688c2ecf20Sopenharmony_ci				# suspend_resume trace events have two types, begin and end
32698c2ecf20Sopenharmony_ci				if(re.match('(?P<name>.*) begin$', t.name)):
32708c2ecf20Sopenharmony_ci					isbegin = True
32718c2ecf20Sopenharmony_ci				elif(re.match('(?P<name>.*) end$', t.name)):
32728c2ecf20Sopenharmony_ci					isbegin = False
32738c2ecf20Sopenharmony_ci				else:
32748c2ecf20Sopenharmony_ci					continue
32758c2ecf20Sopenharmony_ci				if '[' in t.name:
32768c2ecf20Sopenharmony_ci					m = re.match('(?P<name>.*)\[.*', t.name)
32778c2ecf20Sopenharmony_ci				else:
32788c2ecf20Sopenharmony_ci					m = re.match('(?P<name>.*) .*', t.name)
32798c2ecf20Sopenharmony_ci				name = m.group('name')
32808c2ecf20Sopenharmony_ci				# ignore these events
32818c2ecf20Sopenharmony_ci				if(name.split('[')[0] in tracewatch):
32828c2ecf20Sopenharmony_ci					continue
32838c2ecf20Sopenharmony_ci				# -- phase changes --
32848c2ecf20Sopenharmony_ci				# start of kernel suspend
32858c2ecf20Sopenharmony_ci				if(re.match('suspend_enter\[.*', t.name)):
32868c2ecf20Sopenharmony_ci					if(isbegin and data.tKernSus == 0):
32878c2ecf20Sopenharmony_ci						data.tKernSus = t.time
32888c2ecf20Sopenharmony_ci					continue
32898c2ecf20Sopenharmony_ci				# suspend_prepare start
32908c2ecf20Sopenharmony_ci				elif(re.match('dpm_prepare\[.*', t.name)):
32918c2ecf20Sopenharmony_ci					if isbegin and data.first_suspend_prepare:
32928c2ecf20Sopenharmony_ci						data.first_suspend_prepare = False
32938c2ecf20Sopenharmony_ci						if data.tKernSus == 0:
32948c2ecf20Sopenharmony_ci							data.tKernSus = t.time
32958c2ecf20Sopenharmony_ci						continue
32968c2ecf20Sopenharmony_ci					phase = data.setPhase('suspend_prepare', t.time, isbegin)
32978c2ecf20Sopenharmony_ci					continue
32988c2ecf20Sopenharmony_ci				# suspend start
32998c2ecf20Sopenharmony_ci				elif(re.match('dpm_suspend\[.*', t.name)):
33008c2ecf20Sopenharmony_ci					phase = data.setPhase('suspend', t.time, isbegin)
33018c2ecf20Sopenharmony_ci					continue
33028c2ecf20Sopenharmony_ci				# suspend_late start
33038c2ecf20Sopenharmony_ci				elif(re.match('dpm_suspend_late\[.*', t.name)):
33048c2ecf20Sopenharmony_ci					phase = data.setPhase('suspend_late', t.time, isbegin)
33058c2ecf20Sopenharmony_ci					continue
33068c2ecf20Sopenharmony_ci				# suspend_noirq start
33078c2ecf20Sopenharmony_ci				elif(re.match('dpm_suspend_noirq\[.*', t.name)):
33088c2ecf20Sopenharmony_ci					phase = data.setPhase('suspend_noirq', t.time, isbegin)
33098c2ecf20Sopenharmony_ci					continue
33108c2ecf20Sopenharmony_ci				# suspend_machine/resume_machine
33118c2ecf20Sopenharmony_ci				elif(re.match(tp.machinesuspend, t.name)):
33128c2ecf20Sopenharmony_ci					lp = data.lastPhase()
33138c2ecf20Sopenharmony_ci					if(isbegin):
33148c2ecf20Sopenharmony_ci						hwsus = True
33158c2ecf20Sopenharmony_ci						if lp.startswith('resume_machine'):
33168c2ecf20Sopenharmony_ci							# trim out s2idle loops, track time trying to freeze
33178c2ecf20Sopenharmony_ci							llp = data.lastPhase(2)
33188c2ecf20Sopenharmony_ci							if llp.startswith('suspend_machine'):
33198c2ecf20Sopenharmony_ci								if 'trying' not in data.dmesg[llp]:
33208c2ecf20Sopenharmony_ci									data.dmesg[llp]['trying'] = 0
33218c2ecf20Sopenharmony_ci								data.dmesg[llp]['trying'] += \
33228c2ecf20Sopenharmony_ci									t.time - data.dmesg[lp]['start']
33238c2ecf20Sopenharmony_ci							data.currphase = ''
33248c2ecf20Sopenharmony_ci							del data.dmesg[lp]
33258c2ecf20Sopenharmony_ci							continue
33268c2ecf20Sopenharmony_ci						phase = data.setPhase('suspend_machine', data.dmesg[lp]['end'], True)
33278c2ecf20Sopenharmony_ci						data.setPhase(phase, t.time, False)
33288c2ecf20Sopenharmony_ci						if data.tSuspended == 0:
33298c2ecf20Sopenharmony_ci							data.tSuspended = t.time
33308c2ecf20Sopenharmony_ci					else:
33318c2ecf20Sopenharmony_ci						if lp.startswith('resume_machine'):
33328c2ecf20Sopenharmony_ci							data.dmesg[lp]['end'] = t.time
33338c2ecf20Sopenharmony_ci							continue
33348c2ecf20Sopenharmony_ci						phase = data.setPhase('resume_machine', t.time, True)
33358c2ecf20Sopenharmony_ci						if(sysvals.suspendmode in ['mem', 'disk']):
33368c2ecf20Sopenharmony_ci							susp = phase.replace('resume', 'suspend')
33378c2ecf20Sopenharmony_ci							if susp in data.dmesg:
33388c2ecf20Sopenharmony_ci								data.dmesg[susp]['end'] = t.time
33398c2ecf20Sopenharmony_ci							data.tSuspended = t.time
33408c2ecf20Sopenharmony_ci						data.tResumed = t.time
33418c2ecf20Sopenharmony_ci					continue
33428c2ecf20Sopenharmony_ci				# resume_noirq start
33438c2ecf20Sopenharmony_ci				elif(re.match('dpm_resume_noirq\[.*', t.name)):
33448c2ecf20Sopenharmony_ci					phase = data.setPhase('resume_noirq', t.time, isbegin)
33458c2ecf20Sopenharmony_ci					continue
33468c2ecf20Sopenharmony_ci				# resume_early start
33478c2ecf20Sopenharmony_ci				elif(re.match('dpm_resume_early\[.*', t.name)):
33488c2ecf20Sopenharmony_ci					phase = data.setPhase('resume_early', t.time, isbegin)
33498c2ecf20Sopenharmony_ci					continue
33508c2ecf20Sopenharmony_ci				# resume start
33518c2ecf20Sopenharmony_ci				elif(re.match('dpm_resume\[.*', t.name)):
33528c2ecf20Sopenharmony_ci					phase = data.setPhase('resume', t.time, isbegin)
33538c2ecf20Sopenharmony_ci					continue
33548c2ecf20Sopenharmony_ci				# resume complete start
33558c2ecf20Sopenharmony_ci				elif(re.match('dpm_complete\[.*', t.name)):
33568c2ecf20Sopenharmony_ci					phase = data.setPhase('resume_complete', t.time, isbegin)
33578c2ecf20Sopenharmony_ci					continue
33588c2ecf20Sopenharmony_ci				# skip trace events inside devices calls
33598c2ecf20Sopenharmony_ci				if(not data.isTraceEventOutsideDeviceCalls(pid, t.time)):
33608c2ecf20Sopenharmony_ci					continue
33618c2ecf20Sopenharmony_ci				# global events (outside device calls) are graphed
33628c2ecf20Sopenharmony_ci				if(name not in testrun.ttemp):
33638c2ecf20Sopenharmony_ci					testrun.ttemp[name] = []
33648c2ecf20Sopenharmony_ci				# special handling for s2idle_enter
33658c2ecf20Sopenharmony_ci				if name == 'machine_suspend':
33668c2ecf20Sopenharmony_ci					if hwsus:
33678c2ecf20Sopenharmony_ci						s2idle_enter = hwsus = False
33688c2ecf20Sopenharmony_ci					elif s2idle_enter and not isbegin:
33698c2ecf20Sopenharmony_ci						if(len(testrun.ttemp[name]) > 0):
33708c2ecf20Sopenharmony_ci							testrun.ttemp[name][-1]['end'] = t.time
33718c2ecf20Sopenharmony_ci							testrun.ttemp[name][-1]['loop'] += 1
33728c2ecf20Sopenharmony_ci					elif not s2idle_enter and isbegin:
33738c2ecf20Sopenharmony_ci						s2idle_enter = True
33748c2ecf20Sopenharmony_ci						testrun.ttemp[name].append({'begin': t.time,
33758c2ecf20Sopenharmony_ci							'end': t.time, 'pid': pid, 'loop': 0})
33768c2ecf20Sopenharmony_ci					continue
33778c2ecf20Sopenharmony_ci				if(isbegin):
33788c2ecf20Sopenharmony_ci					# create a new list entry
33798c2ecf20Sopenharmony_ci					testrun.ttemp[name].append(\
33808c2ecf20Sopenharmony_ci						{'begin': t.time, 'end': t.time, 'pid': pid})
33818c2ecf20Sopenharmony_ci				else:
33828c2ecf20Sopenharmony_ci					if(len(testrun.ttemp[name]) > 0):
33838c2ecf20Sopenharmony_ci						# if an entry exists, assume this is its end
33848c2ecf20Sopenharmony_ci						testrun.ttemp[name][-1]['end'] = t.time
33858c2ecf20Sopenharmony_ci			# device callback start
33868c2ecf20Sopenharmony_ci			elif(t.type == 'device_pm_callback_start'):
33878c2ecf20Sopenharmony_ci				if phase not in data.dmesg:
33888c2ecf20Sopenharmony_ci					continue
33898c2ecf20Sopenharmony_ci				m = re.match('(?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*',\
33908c2ecf20Sopenharmony_ci					t.name);
33918c2ecf20Sopenharmony_ci				if(not m):
33928c2ecf20Sopenharmony_ci					continue
33938c2ecf20Sopenharmony_ci				drv = m.group('drv')
33948c2ecf20Sopenharmony_ci				n = m.group('d')
33958c2ecf20Sopenharmony_ci				p = m.group('p')
33968c2ecf20Sopenharmony_ci				if(n and p):
33978c2ecf20Sopenharmony_ci					data.newAction(phase, n, pid, p, t.time, -1, drv)
33988c2ecf20Sopenharmony_ci					if pid not in data.devpids:
33998c2ecf20Sopenharmony_ci						data.devpids.append(pid)
34008c2ecf20Sopenharmony_ci			# device callback finish
34018c2ecf20Sopenharmony_ci			elif(t.type == 'device_pm_callback_end'):
34028c2ecf20Sopenharmony_ci				if phase not in data.dmesg:
34038c2ecf20Sopenharmony_ci					continue
34048c2ecf20Sopenharmony_ci				m = re.match('(?P<drv>.*) (?P<d>.*), err.*', t.name);
34058c2ecf20Sopenharmony_ci				if(not m):
34068c2ecf20Sopenharmony_ci					continue
34078c2ecf20Sopenharmony_ci				n = m.group('d')
34088c2ecf20Sopenharmony_ci				dev = data.findDevice(phase, n)
34098c2ecf20Sopenharmony_ci				if dev:
34108c2ecf20Sopenharmony_ci					dev['length'] = t.time - dev['start']
34118c2ecf20Sopenharmony_ci					dev['end'] = t.time
34128c2ecf20Sopenharmony_ci		# kprobe event processing
34138c2ecf20Sopenharmony_ci		elif(t.fkprobe):
34148c2ecf20Sopenharmony_ci			kprobename = t.type
34158c2ecf20Sopenharmony_ci			kprobedata = t.name
34168c2ecf20Sopenharmony_ci			key = (kprobename, pid)
34178c2ecf20Sopenharmony_ci			# displayname is generated from kprobe data
34188c2ecf20Sopenharmony_ci			displayname = ''
34198c2ecf20Sopenharmony_ci			if(t.fcall):
34208c2ecf20Sopenharmony_ci				displayname = sysvals.kprobeDisplayName(kprobename, kprobedata)
34218c2ecf20Sopenharmony_ci				if not displayname:
34228c2ecf20Sopenharmony_ci					continue
34238c2ecf20Sopenharmony_ci				if(key not in tp.ktemp):
34248c2ecf20Sopenharmony_ci					tp.ktemp[key] = []
34258c2ecf20Sopenharmony_ci				tp.ktemp[key].append({
34268c2ecf20Sopenharmony_ci					'pid': pid,
34278c2ecf20Sopenharmony_ci					'begin': t.time,
34288c2ecf20Sopenharmony_ci					'end': -1,
34298c2ecf20Sopenharmony_ci					'name': displayname,
34308c2ecf20Sopenharmony_ci					'cdata': kprobedata,
34318c2ecf20Sopenharmony_ci					'proc': m_proc,
34328c2ecf20Sopenharmony_ci				})
34338c2ecf20Sopenharmony_ci				# start of kernel resume
34348c2ecf20Sopenharmony_ci				if(data.tKernSus == 0 and phase == 'suspend_prepare' \
34358c2ecf20Sopenharmony_ci					and kprobename in ksuscalls):
34368c2ecf20Sopenharmony_ci					data.tKernSus = t.time
34378c2ecf20Sopenharmony_ci			elif(t.freturn):
34388c2ecf20Sopenharmony_ci				if(key not in tp.ktemp) or len(tp.ktemp[key]) < 1:
34398c2ecf20Sopenharmony_ci					continue
34408c2ecf20Sopenharmony_ci				e = next((x for x in reversed(tp.ktemp[key]) if x['end'] < 0), 0)
34418c2ecf20Sopenharmony_ci				if not e:
34428c2ecf20Sopenharmony_ci					continue
34438c2ecf20Sopenharmony_ci				e['end'] = t.time
34448c2ecf20Sopenharmony_ci				e['rdata'] = kprobedata
34458c2ecf20Sopenharmony_ci				# end of kernel resume
34468c2ecf20Sopenharmony_ci				if(phase != 'suspend_prepare' and kprobename in krescalls):
34478c2ecf20Sopenharmony_ci					if phase in data.dmesg:
34488c2ecf20Sopenharmony_ci						data.dmesg[phase]['end'] = t.time
34498c2ecf20Sopenharmony_ci					data.tKernRes = t.time
34508c2ecf20Sopenharmony_ci
34518c2ecf20Sopenharmony_ci		# callgraph processing
34528c2ecf20Sopenharmony_ci		elif sysvals.usecallgraph:
34538c2ecf20Sopenharmony_ci			# create a callgraph object for the data
34548c2ecf20Sopenharmony_ci			key = (m_proc, pid)
34558c2ecf20Sopenharmony_ci			if(key not in testrun.ftemp):
34568c2ecf20Sopenharmony_ci				testrun.ftemp[key] = []
34578c2ecf20Sopenharmony_ci				testrun.ftemp[key].append(FTraceCallGraph(pid, sysvals))
34588c2ecf20Sopenharmony_ci			# when the call is finished, see which device matches it
34598c2ecf20Sopenharmony_ci			cg = testrun.ftemp[key][-1]
34608c2ecf20Sopenharmony_ci			res = cg.addLine(t)
34618c2ecf20Sopenharmony_ci			if(res != 0):
34628c2ecf20Sopenharmony_ci				testrun.ftemp[key].append(FTraceCallGraph(pid, sysvals))
34638c2ecf20Sopenharmony_ci			if(res == -1):
34648c2ecf20Sopenharmony_ci				testrun.ftemp[key][-1].addLine(t)
34658c2ecf20Sopenharmony_ci	tf.close()
34668c2ecf20Sopenharmony_ci	if len(testdata) < 1:
34678c2ecf20Sopenharmony_ci		sysvals.vprint('WARNING: ftrace start marker is missing')
34688c2ecf20Sopenharmony_ci	if data and not data.devicegroups:
34698c2ecf20Sopenharmony_ci		sysvals.vprint('WARNING: ftrace end marker is missing')
34708c2ecf20Sopenharmony_ci		data.handleEndMarker(t.time, t.name)
34718c2ecf20Sopenharmony_ci
34728c2ecf20Sopenharmony_ci	if sysvals.suspendmode == 'command':
34738c2ecf20Sopenharmony_ci		for test in testruns:
34748c2ecf20Sopenharmony_ci			for p in test.data.sortedPhases():
34758c2ecf20Sopenharmony_ci				if p == 'suspend_prepare':
34768c2ecf20Sopenharmony_ci					test.data.dmesg[p]['start'] = test.data.start
34778c2ecf20Sopenharmony_ci					test.data.dmesg[p]['end'] = test.data.end
34788c2ecf20Sopenharmony_ci				else:
34798c2ecf20Sopenharmony_ci					test.data.dmesg[p]['start'] = test.data.end
34808c2ecf20Sopenharmony_ci					test.data.dmesg[p]['end'] = test.data.end
34818c2ecf20Sopenharmony_ci			test.data.tSuspended = test.data.end
34828c2ecf20Sopenharmony_ci			test.data.tResumed = test.data.end
34838c2ecf20Sopenharmony_ci			test.data.fwValid = False
34848c2ecf20Sopenharmony_ci
34858c2ecf20Sopenharmony_ci	# dev source and procmon events can be unreadable with mixed phase height
34868c2ecf20Sopenharmony_ci	if sysvals.usedevsrc or sysvals.useprocmon:
34878c2ecf20Sopenharmony_ci		sysvals.mixedphaseheight = False
34888c2ecf20Sopenharmony_ci
34898c2ecf20Sopenharmony_ci	# expand phase boundaries so there are no gaps
34908c2ecf20Sopenharmony_ci	for data in testdata:
34918c2ecf20Sopenharmony_ci		lp = data.sortedPhases()[0]
34928c2ecf20Sopenharmony_ci		for p in data.sortedPhases():
34938c2ecf20Sopenharmony_ci			if(p != lp and not ('machine' in p and 'machine' in lp)):
34948c2ecf20Sopenharmony_ci				data.dmesg[lp]['end'] = data.dmesg[p]['start']
34958c2ecf20Sopenharmony_ci			lp = p
34968c2ecf20Sopenharmony_ci
34978c2ecf20Sopenharmony_ci	for i in range(len(testruns)):
34988c2ecf20Sopenharmony_ci		test = testruns[i]
34998c2ecf20Sopenharmony_ci		data = test.data
35008c2ecf20Sopenharmony_ci		# find the total time range for this test (begin, end)
35018c2ecf20Sopenharmony_ci		tlb, tle = data.start, data.end
35028c2ecf20Sopenharmony_ci		if i < len(testruns) - 1:
35038c2ecf20Sopenharmony_ci			tle = testruns[i+1].data.start
35048c2ecf20Sopenharmony_ci		# add the process usage data to the timeline
35058c2ecf20Sopenharmony_ci		if sysvals.useprocmon:
35068c2ecf20Sopenharmony_ci			data.createProcessUsageEvents()
35078c2ecf20Sopenharmony_ci		# add the traceevent data to the device hierarchy
35088c2ecf20Sopenharmony_ci		if(sysvals.usetraceevents):
35098c2ecf20Sopenharmony_ci			# add actual trace funcs
35108c2ecf20Sopenharmony_ci			for name in sorted(test.ttemp):
35118c2ecf20Sopenharmony_ci				for event in test.ttemp[name]:
35128c2ecf20Sopenharmony_ci					if event['end'] - event['begin'] <= 0:
35138c2ecf20Sopenharmony_ci						continue
35148c2ecf20Sopenharmony_ci					title = name
35158c2ecf20Sopenharmony_ci					if name == 'machine_suspend' and 'loop' in event:
35168c2ecf20Sopenharmony_ci						title = 's2idle_enter_%dx' % event['loop']
35178c2ecf20Sopenharmony_ci					data.newActionGlobal(title, event['begin'], event['end'], event['pid'])
35188c2ecf20Sopenharmony_ci			# add the kprobe based virtual tracefuncs as actual devices
35198c2ecf20Sopenharmony_ci			for key in sorted(tp.ktemp):
35208c2ecf20Sopenharmony_ci				name, pid = key
35218c2ecf20Sopenharmony_ci				if name not in sysvals.tracefuncs:
35228c2ecf20Sopenharmony_ci					continue
35238c2ecf20Sopenharmony_ci				if pid not in data.devpids:
35248c2ecf20Sopenharmony_ci					data.devpids.append(pid)
35258c2ecf20Sopenharmony_ci				for e in tp.ktemp[key]:
35268c2ecf20Sopenharmony_ci					kb, ke = e['begin'], e['end']
35278c2ecf20Sopenharmony_ci					if ke - kb < 0.000001 or tlb > kb or tle <= kb:
35288c2ecf20Sopenharmony_ci						continue
35298c2ecf20Sopenharmony_ci					color = sysvals.kprobeColor(name)
35308c2ecf20Sopenharmony_ci					data.newActionGlobal(e['name'], kb, ke, pid, color)
35318c2ecf20Sopenharmony_ci			# add config base kprobes and dev kprobes
35328c2ecf20Sopenharmony_ci			if sysvals.usedevsrc:
35338c2ecf20Sopenharmony_ci				for key in sorted(tp.ktemp):
35348c2ecf20Sopenharmony_ci					name, pid = key
35358c2ecf20Sopenharmony_ci					if name in sysvals.tracefuncs or name not in sysvals.dev_tracefuncs:
35368c2ecf20Sopenharmony_ci						continue
35378c2ecf20Sopenharmony_ci					for e in tp.ktemp[key]:
35388c2ecf20Sopenharmony_ci						kb, ke = e['begin'], e['end']
35398c2ecf20Sopenharmony_ci						if ke - kb < 0.000001 or tlb > kb or tle <= kb:
35408c2ecf20Sopenharmony_ci							continue
35418c2ecf20Sopenharmony_ci						data.addDeviceFunctionCall(e['name'], name, e['proc'], pid, kb,
35428c2ecf20Sopenharmony_ci							ke, e['cdata'], e['rdata'])
35438c2ecf20Sopenharmony_ci		if sysvals.usecallgraph:
35448c2ecf20Sopenharmony_ci			# add the callgraph data to the device hierarchy
35458c2ecf20Sopenharmony_ci			sortlist = dict()
35468c2ecf20Sopenharmony_ci			for key in sorted(test.ftemp):
35478c2ecf20Sopenharmony_ci				proc, pid = key
35488c2ecf20Sopenharmony_ci				for cg in test.ftemp[key]:
35498c2ecf20Sopenharmony_ci					if len(cg.list) < 1 or cg.invalid or (cg.end - cg.start == 0):
35508c2ecf20Sopenharmony_ci						continue
35518c2ecf20Sopenharmony_ci					if(not cg.postProcess()):
35528c2ecf20Sopenharmony_ci						id = 'task %s' % (pid)
35538c2ecf20Sopenharmony_ci						sysvals.vprint('Sanity check failed for '+\
35548c2ecf20Sopenharmony_ci							id+', ignoring this callback')
35558c2ecf20Sopenharmony_ci						continue
35568c2ecf20Sopenharmony_ci					# match cg data to devices
35578c2ecf20Sopenharmony_ci					devname = ''
35588c2ecf20Sopenharmony_ci					if sysvals.suspendmode != 'command':
35598c2ecf20Sopenharmony_ci						devname = cg.deviceMatch(pid, data)
35608c2ecf20Sopenharmony_ci					if not devname:
35618c2ecf20Sopenharmony_ci						sortkey = '%f%f%d' % (cg.start, cg.end, pid)
35628c2ecf20Sopenharmony_ci						sortlist[sortkey] = cg
35638c2ecf20Sopenharmony_ci					elif len(cg.list) > 1000000 and cg.name != sysvals.ftopfunc:
35648c2ecf20Sopenharmony_ci						sysvals.vprint('WARNING: the callgraph for %s is massive (%d lines)' %\
35658c2ecf20Sopenharmony_ci							(devname, len(cg.list)))
35668c2ecf20Sopenharmony_ci			# create blocks for orphan cg data
35678c2ecf20Sopenharmony_ci			for sortkey in sorted(sortlist):
35688c2ecf20Sopenharmony_ci				cg = sortlist[sortkey]
35698c2ecf20Sopenharmony_ci				name = cg.name
35708c2ecf20Sopenharmony_ci				if sysvals.isCallgraphFunc(name):
35718c2ecf20Sopenharmony_ci					sysvals.vprint('Callgraph found for task %d: %.3fms, %s' % (cg.pid, (cg.end - cg.start)*1000, name))
35728c2ecf20Sopenharmony_ci					cg.newActionFromFunction(data)
35738c2ecf20Sopenharmony_ci	if sysvals.suspendmode == 'command':
35748c2ecf20Sopenharmony_ci		return (testdata, '')
35758c2ecf20Sopenharmony_ci
35768c2ecf20Sopenharmony_ci	# fill in any missing phases
35778c2ecf20Sopenharmony_ci	error = []
35788c2ecf20Sopenharmony_ci	for data in testdata:
35798c2ecf20Sopenharmony_ci		tn = '' if len(testdata) == 1 else ('%d' % (data.testnumber + 1))
35808c2ecf20Sopenharmony_ci		terr = ''
35818c2ecf20Sopenharmony_ci		phasedef = data.phasedef
35828c2ecf20Sopenharmony_ci		lp = 'suspend_prepare'
35838c2ecf20Sopenharmony_ci		for p in sorted(phasedef, key=lambda k:phasedef[k]['order']):
35848c2ecf20Sopenharmony_ci			if p not in data.dmesg:
35858c2ecf20Sopenharmony_ci				if not terr:
35868c2ecf20Sopenharmony_ci					ph = p if 'machine' in p else lp
35878c2ecf20Sopenharmony_ci					terr = '%s%s failed in %s phase' % (sysvals.suspendmode, tn, ph)
35888c2ecf20Sopenharmony_ci					pprint('TEST%s FAILED: %s' % (tn, terr))
35898c2ecf20Sopenharmony_ci					error.append(terr)
35908c2ecf20Sopenharmony_ci					if data.tSuspended == 0:
35918c2ecf20Sopenharmony_ci						data.tSuspended = data.dmesg[lp]['end']
35928c2ecf20Sopenharmony_ci					if data.tResumed == 0:
35938c2ecf20Sopenharmony_ci						data.tResumed = data.dmesg[lp]['end']
35948c2ecf20Sopenharmony_ci					data.fwValid = False
35958c2ecf20Sopenharmony_ci				sysvals.vprint('WARNING: phase "%s" is missing!' % p)
35968c2ecf20Sopenharmony_ci			lp = p
35978c2ecf20Sopenharmony_ci		if not terr and 'dev' in data.wifi and data.wifi['stat'] == 'timeout':
35988c2ecf20Sopenharmony_ci			terr = '%s%s failed in wifi_resume <i>(%s %.0fs timeout)</i>' % \
35998c2ecf20Sopenharmony_ci				(sysvals.suspendmode, tn, data.wifi['dev'], data.wifi['time'])
36008c2ecf20Sopenharmony_ci			error.append(terr)
36018c2ecf20Sopenharmony_ci		if not terr and data.enterfail:
36028c2ecf20Sopenharmony_ci			pprint('test%s FAILED: enter %s failed with %s' % (tn, sysvals.suspendmode, data.enterfail))
36038c2ecf20Sopenharmony_ci			terr = 'test%s failed to enter %s mode' % (tn, sysvals.suspendmode)
36048c2ecf20Sopenharmony_ci			error.append(terr)
36058c2ecf20Sopenharmony_ci		if data.tSuspended == 0:
36068c2ecf20Sopenharmony_ci			data.tSuspended = data.tKernRes
36078c2ecf20Sopenharmony_ci		if data.tResumed == 0:
36088c2ecf20Sopenharmony_ci			data.tResumed = data.tSuspended
36098c2ecf20Sopenharmony_ci
36108c2ecf20Sopenharmony_ci		if(len(sysvals.devicefilter) > 0):
36118c2ecf20Sopenharmony_ci			data.deviceFilter(sysvals.devicefilter)
36128c2ecf20Sopenharmony_ci		data.fixupInitcallsThatDidntReturn()
36138c2ecf20Sopenharmony_ci		if sysvals.usedevsrc:
36148c2ecf20Sopenharmony_ci			data.optimizeDevSrc()
36158c2ecf20Sopenharmony_ci
36168c2ecf20Sopenharmony_ci	# x2: merge any overlapping devices between test runs
36178c2ecf20Sopenharmony_ci	if sysvals.usedevsrc and len(testdata) > 1:
36188c2ecf20Sopenharmony_ci		tc = len(testdata)
36198c2ecf20Sopenharmony_ci		for i in range(tc - 1):
36208c2ecf20Sopenharmony_ci			devlist = testdata[i].overflowDevices()
36218c2ecf20Sopenharmony_ci			for j in range(i + 1, tc):
36228c2ecf20Sopenharmony_ci				testdata[j].mergeOverlapDevices(devlist)
36238c2ecf20Sopenharmony_ci		testdata[0].stitchTouchingThreads(testdata[1:])
36248c2ecf20Sopenharmony_ci	return (testdata, ', '.join(error))
36258c2ecf20Sopenharmony_ci
36268c2ecf20Sopenharmony_ci# Function: loadKernelLog
36278c2ecf20Sopenharmony_ci# Description:
36288c2ecf20Sopenharmony_ci#	 [deprecated for kernel 3.15.0 or newer]
36298c2ecf20Sopenharmony_ci#	 load the dmesg file into memory and fix up any ordering issues
36308c2ecf20Sopenharmony_ci#	 The dmesg filename is taken from sysvals
36318c2ecf20Sopenharmony_ci# Output:
36328c2ecf20Sopenharmony_ci#	 An array of empty Data objects with only their dmesgtext attributes set
36338c2ecf20Sopenharmony_cidef loadKernelLog():
36348c2ecf20Sopenharmony_ci	sysvals.vprint('Analyzing the dmesg data (%s)...' % \
36358c2ecf20Sopenharmony_ci		os.path.basename(sysvals.dmesgfile))
36368c2ecf20Sopenharmony_ci	if(os.path.exists(sysvals.dmesgfile) == False):
36378c2ecf20Sopenharmony_ci		doError('%s does not exist' % sysvals.dmesgfile)
36388c2ecf20Sopenharmony_ci
36398c2ecf20Sopenharmony_ci	# there can be multiple test runs in a single file
36408c2ecf20Sopenharmony_ci	tp = TestProps()
36418c2ecf20Sopenharmony_ci	tp.stamp = datetime.now().strftime('# suspend-%m%d%y-%H%M%S localhost mem unknown')
36428c2ecf20Sopenharmony_ci	testruns = []
36438c2ecf20Sopenharmony_ci	data = 0
36448c2ecf20Sopenharmony_ci	lf = sysvals.openlog(sysvals.dmesgfile, 'r')
36458c2ecf20Sopenharmony_ci	for line in lf:
36468c2ecf20Sopenharmony_ci		line = line.replace('\r\n', '')
36478c2ecf20Sopenharmony_ci		idx = line.find('[')
36488c2ecf20Sopenharmony_ci		if idx > 1:
36498c2ecf20Sopenharmony_ci			line = line[idx:]
36508c2ecf20Sopenharmony_ci		if tp.stampInfo(line, sysvals):
36518c2ecf20Sopenharmony_ci			continue
36528c2ecf20Sopenharmony_ci		m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
36538c2ecf20Sopenharmony_ci		if(not m):
36548c2ecf20Sopenharmony_ci			continue
36558c2ecf20Sopenharmony_ci		msg = m.group("msg")
36568c2ecf20Sopenharmony_ci		if(re.match('PM: Syncing filesystems.*', msg)):
36578c2ecf20Sopenharmony_ci			if(data):
36588c2ecf20Sopenharmony_ci				testruns.append(data)
36598c2ecf20Sopenharmony_ci			data = Data(len(testruns))
36608c2ecf20Sopenharmony_ci			tp.parseStamp(data, sysvals)
36618c2ecf20Sopenharmony_ci		if(not data):
36628c2ecf20Sopenharmony_ci			continue
36638c2ecf20Sopenharmony_ci		m = re.match('.* *(?P<k>[0-9]\.[0-9]{2}\.[0-9]-.*) .*', msg)
36648c2ecf20Sopenharmony_ci		if(m):
36658c2ecf20Sopenharmony_ci			sysvals.stamp['kernel'] = m.group('k')
36668c2ecf20Sopenharmony_ci		m = re.match('PM: Preparing system for (?P<m>.*) sleep', msg)
36678c2ecf20Sopenharmony_ci		if(m):
36688c2ecf20Sopenharmony_ci			sysvals.stamp['mode'] = sysvals.suspendmode = m.group('m')
36698c2ecf20Sopenharmony_ci		data.dmesgtext.append(line)
36708c2ecf20Sopenharmony_ci	lf.close()
36718c2ecf20Sopenharmony_ci
36728c2ecf20Sopenharmony_ci	if data:
36738c2ecf20Sopenharmony_ci		testruns.append(data)
36748c2ecf20Sopenharmony_ci	if len(testruns) < 1:
36758c2ecf20Sopenharmony_ci		doError('dmesg log has no suspend/resume data: %s' \
36768c2ecf20Sopenharmony_ci			% sysvals.dmesgfile)
36778c2ecf20Sopenharmony_ci
36788c2ecf20Sopenharmony_ci	# fix lines with same timestamp/function with the call and return swapped
36798c2ecf20Sopenharmony_ci	for data in testruns:
36808c2ecf20Sopenharmony_ci		last = ''
36818c2ecf20Sopenharmony_ci		for line in data.dmesgtext:
36828c2ecf20Sopenharmony_ci			mc = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) calling  '+\
36838c2ecf20Sopenharmony_ci				'(?P<f>.*)\+ @ .*, parent: .*', line)
36848c2ecf20Sopenharmony_ci			mr = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) call '+\
36858c2ecf20Sopenharmony_ci				'(?P<f>.*)\+ returned .* after (?P<dt>.*) usecs', last)
36868c2ecf20Sopenharmony_ci			if(mc and mr and (mc.group('t') == mr.group('t')) and
36878c2ecf20Sopenharmony_ci				(mc.group('f') == mr.group('f'))):
36888c2ecf20Sopenharmony_ci				i = data.dmesgtext.index(last)
36898c2ecf20Sopenharmony_ci				j = data.dmesgtext.index(line)
36908c2ecf20Sopenharmony_ci				data.dmesgtext[i] = line
36918c2ecf20Sopenharmony_ci				data.dmesgtext[j] = last
36928c2ecf20Sopenharmony_ci			last = line
36938c2ecf20Sopenharmony_ci	return testruns
36948c2ecf20Sopenharmony_ci
36958c2ecf20Sopenharmony_ci# Function: parseKernelLog
36968c2ecf20Sopenharmony_ci# Description:
36978c2ecf20Sopenharmony_ci#	 [deprecated for kernel 3.15.0 or newer]
36988c2ecf20Sopenharmony_ci#	 Analyse a dmesg log output file generated from this app during
36998c2ecf20Sopenharmony_ci#	 the execution phase. Create a set of device structures in memory
37008c2ecf20Sopenharmony_ci#	 for subsequent formatting in the html output file
37018c2ecf20Sopenharmony_ci#	 This call is only for legacy support on kernels where the ftrace
37028c2ecf20Sopenharmony_ci#	 data lacks the suspend_resume or device_pm_callbacks trace events.
37038c2ecf20Sopenharmony_ci# Arguments:
37048c2ecf20Sopenharmony_ci#	 data: an empty Data object (with dmesgtext) obtained from loadKernelLog
37058c2ecf20Sopenharmony_ci# Output:
37068c2ecf20Sopenharmony_ci#	 The filled Data object
37078c2ecf20Sopenharmony_cidef parseKernelLog(data):
37088c2ecf20Sopenharmony_ci	phase = 'suspend_runtime'
37098c2ecf20Sopenharmony_ci
37108c2ecf20Sopenharmony_ci	if(data.fwValid):
37118c2ecf20Sopenharmony_ci		sysvals.vprint('Firmware Suspend = %u ns, Firmware Resume = %u ns' % \
37128c2ecf20Sopenharmony_ci			(data.fwSuspend, data.fwResume))
37138c2ecf20Sopenharmony_ci
37148c2ecf20Sopenharmony_ci	# dmesg phase match table
37158c2ecf20Sopenharmony_ci	dm = {
37168c2ecf20Sopenharmony_ci		'suspend_prepare': ['PM: Syncing filesystems.*'],
37178c2ecf20Sopenharmony_ci		        'suspend': ['PM: Entering [a-z]* sleep.*', 'Suspending console.*'],
37188c2ecf20Sopenharmony_ci		   'suspend_late': ['PM: suspend of devices complete after.*'],
37198c2ecf20Sopenharmony_ci		  'suspend_noirq': ['PM: late suspend of devices complete after.*'],
37208c2ecf20Sopenharmony_ci		'suspend_machine': ['PM: noirq suspend of devices complete after.*'],
37218c2ecf20Sopenharmony_ci		 'resume_machine': ['ACPI: Low-level resume complete.*'],
37228c2ecf20Sopenharmony_ci		   'resume_noirq': ['ACPI: Waking up from system sleep state.*'],
37238c2ecf20Sopenharmony_ci		   'resume_early': ['PM: noirq resume of devices complete after.*'],
37248c2ecf20Sopenharmony_ci		         'resume': ['PM: early resume of devices complete after.*'],
37258c2ecf20Sopenharmony_ci		'resume_complete': ['PM: resume of devices complete after.*'],
37268c2ecf20Sopenharmony_ci		    'post_resume': ['.*Restarting tasks \.\.\..*'],
37278c2ecf20Sopenharmony_ci	}
37288c2ecf20Sopenharmony_ci	if(sysvals.suspendmode == 'standby'):
37298c2ecf20Sopenharmony_ci		dm['resume_machine'] = ['PM: Restoring platform NVS memory']
37308c2ecf20Sopenharmony_ci	elif(sysvals.suspendmode == 'disk'):
37318c2ecf20Sopenharmony_ci		dm['suspend_late'] = ['PM: freeze of devices complete after.*']
37328c2ecf20Sopenharmony_ci		dm['suspend_noirq'] = ['PM: late freeze of devices complete after.*']
37338c2ecf20Sopenharmony_ci		dm['suspend_machine'] = ['PM: noirq freeze of devices complete after.*']
37348c2ecf20Sopenharmony_ci		dm['resume_machine'] = ['PM: Restoring platform NVS memory']
37358c2ecf20Sopenharmony_ci		dm['resume_early'] = ['PM: noirq restore of devices complete after.*']
37368c2ecf20Sopenharmony_ci		dm['resume'] = ['PM: early restore of devices complete after.*']
37378c2ecf20Sopenharmony_ci		dm['resume_complete'] = ['PM: restore of devices complete after.*']
37388c2ecf20Sopenharmony_ci	elif(sysvals.suspendmode == 'freeze'):
37398c2ecf20Sopenharmony_ci		dm['resume_machine'] = ['ACPI: resume from mwait']
37408c2ecf20Sopenharmony_ci
37418c2ecf20Sopenharmony_ci	# action table (expected events that occur and show up in dmesg)
37428c2ecf20Sopenharmony_ci	at = {
37438c2ecf20Sopenharmony_ci		'sync_filesystems': {
37448c2ecf20Sopenharmony_ci			'smsg': 'PM: Syncing filesystems.*',
37458c2ecf20Sopenharmony_ci			'emsg': 'PM: Preparing system for mem sleep.*' },
37468c2ecf20Sopenharmony_ci		'freeze_user_processes': {
37478c2ecf20Sopenharmony_ci			'smsg': 'Freezing user space processes .*',
37488c2ecf20Sopenharmony_ci			'emsg': 'Freezing remaining freezable tasks.*' },
37498c2ecf20Sopenharmony_ci		'freeze_tasks': {
37508c2ecf20Sopenharmony_ci			'smsg': 'Freezing remaining freezable tasks.*',
37518c2ecf20Sopenharmony_ci			'emsg': 'PM: Entering (?P<mode>[a-z,A-Z]*) sleep.*' },
37528c2ecf20Sopenharmony_ci		'ACPI prepare': {
37538c2ecf20Sopenharmony_ci			'smsg': 'ACPI: Preparing to enter system sleep state.*',
37548c2ecf20Sopenharmony_ci			'emsg': 'PM: Saving platform NVS memory.*' },
37558c2ecf20Sopenharmony_ci		'PM vns': {
37568c2ecf20Sopenharmony_ci			'smsg': 'PM: Saving platform NVS memory.*',
37578c2ecf20Sopenharmony_ci			'emsg': 'Disabling non-boot CPUs .*' },
37588c2ecf20Sopenharmony_ci	}
37598c2ecf20Sopenharmony_ci
37608c2ecf20Sopenharmony_ci	t0 = -1.0
37618c2ecf20Sopenharmony_ci	cpu_start = -1.0
37628c2ecf20Sopenharmony_ci	prevktime = -1.0
37638c2ecf20Sopenharmony_ci	actions = dict()
37648c2ecf20Sopenharmony_ci	for line in data.dmesgtext:
37658c2ecf20Sopenharmony_ci		# parse each dmesg line into the time and message
37668c2ecf20Sopenharmony_ci		m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
37678c2ecf20Sopenharmony_ci		if(m):
37688c2ecf20Sopenharmony_ci			val = m.group('ktime')
37698c2ecf20Sopenharmony_ci			try:
37708c2ecf20Sopenharmony_ci				ktime = float(val)
37718c2ecf20Sopenharmony_ci			except:
37728c2ecf20Sopenharmony_ci				continue
37738c2ecf20Sopenharmony_ci			msg = m.group('msg')
37748c2ecf20Sopenharmony_ci			# initialize data start to first line time
37758c2ecf20Sopenharmony_ci			if t0 < 0:
37768c2ecf20Sopenharmony_ci				data.setStart(ktime)
37778c2ecf20Sopenharmony_ci				t0 = ktime
37788c2ecf20Sopenharmony_ci		else:
37798c2ecf20Sopenharmony_ci			continue
37808c2ecf20Sopenharmony_ci
37818c2ecf20Sopenharmony_ci		# check for a phase change line
37828c2ecf20Sopenharmony_ci		phasechange = False
37838c2ecf20Sopenharmony_ci		for p in dm:
37848c2ecf20Sopenharmony_ci			for s in dm[p]:
37858c2ecf20Sopenharmony_ci				if(re.match(s, msg)):
37868c2ecf20Sopenharmony_ci					phasechange, phase = True, p
37878c2ecf20Sopenharmony_ci					break
37888c2ecf20Sopenharmony_ci
37898c2ecf20Sopenharmony_ci		# hack for determining resume_machine end for freeze
37908c2ecf20Sopenharmony_ci		if(not sysvals.usetraceevents and sysvals.suspendmode == 'freeze' \
37918c2ecf20Sopenharmony_ci			and phase == 'resume_machine' and \
37928c2ecf20Sopenharmony_ci			re.match('calling  (?P<f>.*)\+ @ .*, parent: .*', msg)):
37938c2ecf20Sopenharmony_ci			data.setPhase(phase, ktime, False)
37948c2ecf20Sopenharmony_ci			phase = 'resume_noirq'
37958c2ecf20Sopenharmony_ci			data.setPhase(phase, ktime, True)
37968c2ecf20Sopenharmony_ci
37978c2ecf20Sopenharmony_ci		if phasechange:
37988c2ecf20Sopenharmony_ci			if phase == 'suspend_prepare':
37998c2ecf20Sopenharmony_ci				data.setPhase(phase, ktime, True)
38008c2ecf20Sopenharmony_ci				data.setStart(ktime)
38018c2ecf20Sopenharmony_ci				data.tKernSus = ktime
38028c2ecf20Sopenharmony_ci			elif phase == 'suspend':
38038c2ecf20Sopenharmony_ci				lp = data.lastPhase()
38048c2ecf20Sopenharmony_ci				if lp:
38058c2ecf20Sopenharmony_ci					data.setPhase(lp, ktime, False)
38068c2ecf20Sopenharmony_ci				data.setPhase(phase, ktime, True)
38078c2ecf20Sopenharmony_ci			elif phase == 'suspend_late':
38088c2ecf20Sopenharmony_ci				lp = data.lastPhase()
38098c2ecf20Sopenharmony_ci				if lp:
38108c2ecf20Sopenharmony_ci					data.setPhase(lp, ktime, False)
38118c2ecf20Sopenharmony_ci				data.setPhase(phase, ktime, True)
38128c2ecf20Sopenharmony_ci			elif phase == 'suspend_noirq':
38138c2ecf20Sopenharmony_ci				lp = data.lastPhase()
38148c2ecf20Sopenharmony_ci				if lp:
38158c2ecf20Sopenharmony_ci					data.setPhase(lp, ktime, False)
38168c2ecf20Sopenharmony_ci				data.setPhase(phase, ktime, True)
38178c2ecf20Sopenharmony_ci			elif phase == 'suspend_machine':
38188c2ecf20Sopenharmony_ci				lp = data.lastPhase()
38198c2ecf20Sopenharmony_ci				if lp:
38208c2ecf20Sopenharmony_ci					data.setPhase(lp, ktime, False)
38218c2ecf20Sopenharmony_ci				data.setPhase(phase, ktime, True)
38228c2ecf20Sopenharmony_ci			elif phase == 'resume_machine':
38238c2ecf20Sopenharmony_ci				lp = data.lastPhase()
38248c2ecf20Sopenharmony_ci				if(sysvals.suspendmode in ['freeze', 'standby']):
38258c2ecf20Sopenharmony_ci					data.tSuspended = prevktime
38268c2ecf20Sopenharmony_ci					if lp:
38278c2ecf20Sopenharmony_ci						data.setPhase(lp, prevktime, False)
38288c2ecf20Sopenharmony_ci				else:
38298c2ecf20Sopenharmony_ci					data.tSuspended = ktime
38308c2ecf20Sopenharmony_ci					if lp:
38318c2ecf20Sopenharmony_ci						data.setPhase(lp, prevktime, False)
38328c2ecf20Sopenharmony_ci				data.tResumed = ktime
38338c2ecf20Sopenharmony_ci				data.setPhase(phase, ktime, True)
38348c2ecf20Sopenharmony_ci			elif phase == 'resume_noirq':
38358c2ecf20Sopenharmony_ci				lp = data.lastPhase()
38368c2ecf20Sopenharmony_ci				if lp:
38378c2ecf20Sopenharmony_ci					data.setPhase(lp, ktime, False)
38388c2ecf20Sopenharmony_ci				data.setPhase(phase, ktime, True)
38398c2ecf20Sopenharmony_ci			elif phase == 'resume_early':
38408c2ecf20Sopenharmony_ci				lp = data.lastPhase()
38418c2ecf20Sopenharmony_ci				if lp:
38428c2ecf20Sopenharmony_ci					data.setPhase(lp, ktime, False)
38438c2ecf20Sopenharmony_ci				data.setPhase(phase, ktime, True)
38448c2ecf20Sopenharmony_ci			elif phase == 'resume':
38458c2ecf20Sopenharmony_ci				lp = data.lastPhase()
38468c2ecf20Sopenharmony_ci				if lp:
38478c2ecf20Sopenharmony_ci					data.setPhase(lp, ktime, False)
38488c2ecf20Sopenharmony_ci				data.setPhase(phase, ktime, True)
38498c2ecf20Sopenharmony_ci			elif phase == 'resume_complete':
38508c2ecf20Sopenharmony_ci				lp = data.lastPhase()
38518c2ecf20Sopenharmony_ci				if lp:
38528c2ecf20Sopenharmony_ci					data.setPhase(lp, ktime, False)
38538c2ecf20Sopenharmony_ci				data.setPhase(phase, ktime, True)
38548c2ecf20Sopenharmony_ci			elif phase == 'post_resume':
38558c2ecf20Sopenharmony_ci				lp = data.lastPhase()
38568c2ecf20Sopenharmony_ci				if lp:
38578c2ecf20Sopenharmony_ci					data.setPhase(lp, ktime, False)
38588c2ecf20Sopenharmony_ci				data.setEnd(ktime)
38598c2ecf20Sopenharmony_ci				data.tKernRes = ktime
38608c2ecf20Sopenharmony_ci				break
38618c2ecf20Sopenharmony_ci
38628c2ecf20Sopenharmony_ci		# -- device callbacks --
38638c2ecf20Sopenharmony_ci		if(phase in data.sortedPhases()):
38648c2ecf20Sopenharmony_ci			# device init call
38658c2ecf20Sopenharmony_ci			if(re.match('calling  (?P<f>.*)\+ @ .*, parent: .*', msg)):
38668c2ecf20Sopenharmony_ci				sm = re.match('calling  (?P<f>.*)\+ @ '+\
38678c2ecf20Sopenharmony_ci					'(?P<n>.*), parent: (?P<p>.*)', msg);
38688c2ecf20Sopenharmony_ci				f = sm.group('f')
38698c2ecf20Sopenharmony_ci				n = sm.group('n')
38708c2ecf20Sopenharmony_ci				p = sm.group('p')
38718c2ecf20Sopenharmony_ci				if(f and n and p):
38728c2ecf20Sopenharmony_ci					data.newAction(phase, f, int(n), p, ktime, -1, '')
38738c2ecf20Sopenharmony_ci			# device init return
38748c2ecf20Sopenharmony_ci			elif(re.match('call (?P<f>.*)\+ returned .* after '+\
38758c2ecf20Sopenharmony_ci				'(?P<t>.*) usecs', msg)):
38768c2ecf20Sopenharmony_ci				sm = re.match('call (?P<f>.*)\+ returned .* after '+\
38778c2ecf20Sopenharmony_ci					'(?P<t>.*) usecs(?P<a>.*)', msg);
38788c2ecf20Sopenharmony_ci				f = sm.group('f')
38798c2ecf20Sopenharmony_ci				t = sm.group('t')
38808c2ecf20Sopenharmony_ci				list = data.dmesg[phase]['list']
38818c2ecf20Sopenharmony_ci				if(f in list):
38828c2ecf20Sopenharmony_ci					dev = list[f]
38838c2ecf20Sopenharmony_ci					dev['length'] = int(t)
38848c2ecf20Sopenharmony_ci					dev['end'] = ktime
38858c2ecf20Sopenharmony_ci
38868c2ecf20Sopenharmony_ci		# if trace events are not available, these are better than nothing
38878c2ecf20Sopenharmony_ci		if(not sysvals.usetraceevents):
38888c2ecf20Sopenharmony_ci			# look for known actions
38898c2ecf20Sopenharmony_ci			for a in sorted(at):
38908c2ecf20Sopenharmony_ci				if(re.match(at[a]['smsg'], msg)):
38918c2ecf20Sopenharmony_ci					if(a not in actions):
38928c2ecf20Sopenharmony_ci						actions[a] = []
38938c2ecf20Sopenharmony_ci					actions[a].append({'begin': ktime, 'end': ktime})
38948c2ecf20Sopenharmony_ci				if(re.match(at[a]['emsg'], msg)):
38958c2ecf20Sopenharmony_ci					if(a in actions):
38968c2ecf20Sopenharmony_ci						actions[a][-1]['end'] = ktime
38978c2ecf20Sopenharmony_ci			# now look for CPU on/off events
38988c2ecf20Sopenharmony_ci			if(re.match('Disabling non-boot CPUs .*', msg)):
38998c2ecf20Sopenharmony_ci				# start of first cpu suspend
39008c2ecf20Sopenharmony_ci				cpu_start = ktime
39018c2ecf20Sopenharmony_ci			elif(re.match('Enabling non-boot CPUs .*', msg)):
39028c2ecf20Sopenharmony_ci				# start of first cpu resume
39038c2ecf20Sopenharmony_ci				cpu_start = ktime
39048c2ecf20Sopenharmony_ci			elif(re.match('smpboot: CPU (?P<cpu>[0-9]*) is now offline', msg)):
39058c2ecf20Sopenharmony_ci				# end of a cpu suspend, start of the next
39068c2ecf20Sopenharmony_ci				m = re.match('smpboot: CPU (?P<cpu>[0-9]*) is now offline', msg)
39078c2ecf20Sopenharmony_ci				cpu = 'CPU'+m.group('cpu')
39088c2ecf20Sopenharmony_ci				if(cpu not in actions):
39098c2ecf20Sopenharmony_ci					actions[cpu] = []
39108c2ecf20Sopenharmony_ci				actions[cpu].append({'begin': cpu_start, 'end': ktime})
39118c2ecf20Sopenharmony_ci				cpu_start = ktime
39128c2ecf20Sopenharmony_ci			elif(re.match('CPU(?P<cpu>[0-9]*) is up', msg)):
39138c2ecf20Sopenharmony_ci				# end of a cpu resume, start of the next
39148c2ecf20Sopenharmony_ci				m = re.match('CPU(?P<cpu>[0-9]*) is up', msg)
39158c2ecf20Sopenharmony_ci				cpu = 'CPU'+m.group('cpu')
39168c2ecf20Sopenharmony_ci				if(cpu not in actions):
39178c2ecf20Sopenharmony_ci					actions[cpu] = []
39188c2ecf20Sopenharmony_ci				actions[cpu].append({'begin': cpu_start, 'end': ktime})
39198c2ecf20Sopenharmony_ci				cpu_start = ktime
39208c2ecf20Sopenharmony_ci		prevktime = ktime
39218c2ecf20Sopenharmony_ci	data.initDevicegroups()
39228c2ecf20Sopenharmony_ci
39238c2ecf20Sopenharmony_ci	# fill in any missing phases
39248c2ecf20Sopenharmony_ci	phasedef = data.phasedef
39258c2ecf20Sopenharmony_ci	terr, lp = '', 'suspend_prepare'
39268c2ecf20Sopenharmony_ci	for p in sorted(phasedef, key=lambda k:phasedef[k]['order']):
39278c2ecf20Sopenharmony_ci		if p not in data.dmesg:
39288c2ecf20Sopenharmony_ci			if not terr:
39298c2ecf20Sopenharmony_ci				pprint('TEST FAILED: %s failed in %s phase' % (sysvals.suspendmode, lp))
39308c2ecf20Sopenharmony_ci				terr = '%s failed in %s phase' % (sysvals.suspendmode, lp)
39318c2ecf20Sopenharmony_ci				if data.tSuspended == 0:
39328c2ecf20Sopenharmony_ci					data.tSuspended = data.dmesg[lp]['end']
39338c2ecf20Sopenharmony_ci				if data.tResumed == 0:
39348c2ecf20Sopenharmony_ci					data.tResumed = data.dmesg[lp]['end']
39358c2ecf20Sopenharmony_ci			sysvals.vprint('WARNING: phase "%s" is missing!' % p)
39368c2ecf20Sopenharmony_ci		lp = p
39378c2ecf20Sopenharmony_ci	lp = data.sortedPhases()[0]
39388c2ecf20Sopenharmony_ci	for p in data.sortedPhases():
39398c2ecf20Sopenharmony_ci		if(p != lp and not ('machine' in p and 'machine' in lp)):
39408c2ecf20Sopenharmony_ci			data.dmesg[lp]['end'] = data.dmesg[p]['start']
39418c2ecf20Sopenharmony_ci		lp = p
39428c2ecf20Sopenharmony_ci	if data.tSuspended == 0:
39438c2ecf20Sopenharmony_ci		data.tSuspended = data.tKernRes
39448c2ecf20Sopenharmony_ci	if data.tResumed == 0:
39458c2ecf20Sopenharmony_ci		data.tResumed = data.tSuspended
39468c2ecf20Sopenharmony_ci
39478c2ecf20Sopenharmony_ci	# fill in any actions we've found
39488c2ecf20Sopenharmony_ci	for name in sorted(actions):
39498c2ecf20Sopenharmony_ci		for event in actions[name]:
39508c2ecf20Sopenharmony_ci			data.newActionGlobal(name, event['begin'], event['end'])
39518c2ecf20Sopenharmony_ci
39528c2ecf20Sopenharmony_ci	if(len(sysvals.devicefilter) > 0):
39538c2ecf20Sopenharmony_ci		data.deviceFilter(sysvals.devicefilter)
39548c2ecf20Sopenharmony_ci	data.fixupInitcallsThatDidntReturn()
39558c2ecf20Sopenharmony_ci	return True
39568c2ecf20Sopenharmony_ci
39578c2ecf20Sopenharmony_cidef callgraphHTML(sv, hf, num, cg, title, color, devid):
39588c2ecf20Sopenharmony_ci	html_func_top = '<article id="{0}" class="atop" style="background:{1}">\n<input type="checkbox" class="pf" id="f{2}" checked/><label for="f{2}">{3} {4}</label>\n'
39598c2ecf20Sopenharmony_ci	html_func_start = '<article>\n<input type="checkbox" class="pf" id="f{0}" checked/><label for="f{0}">{1} {2}</label>\n'
39608c2ecf20Sopenharmony_ci	html_func_end = '</article>\n'
39618c2ecf20Sopenharmony_ci	html_func_leaf = '<article>{0} {1}</article>\n'
39628c2ecf20Sopenharmony_ci
39638c2ecf20Sopenharmony_ci	cgid = devid
39648c2ecf20Sopenharmony_ci	if cg.id:
39658c2ecf20Sopenharmony_ci		cgid += cg.id
39668c2ecf20Sopenharmony_ci	cglen = (cg.end - cg.start) * 1000
39678c2ecf20Sopenharmony_ci	if cglen < sv.mincglen:
39688c2ecf20Sopenharmony_ci		return num
39698c2ecf20Sopenharmony_ci
39708c2ecf20Sopenharmony_ci	fmt = '<r>(%.3f ms @ '+sv.timeformat+' to '+sv.timeformat+')</r>'
39718c2ecf20Sopenharmony_ci	flen = fmt % (cglen, cg.start, cg.end)
39728c2ecf20Sopenharmony_ci	hf.write(html_func_top.format(cgid, color, num, title, flen))
39738c2ecf20Sopenharmony_ci	num += 1
39748c2ecf20Sopenharmony_ci	for line in cg.list:
39758c2ecf20Sopenharmony_ci		if(line.length < 0.000000001):
39768c2ecf20Sopenharmony_ci			flen = ''
39778c2ecf20Sopenharmony_ci		else:
39788c2ecf20Sopenharmony_ci			fmt = '<n>(%.3f ms @ '+sv.timeformat+')</n>'
39798c2ecf20Sopenharmony_ci			flen = fmt % (line.length*1000, line.time)
39808c2ecf20Sopenharmony_ci		if line.isLeaf():
39818c2ecf20Sopenharmony_ci			hf.write(html_func_leaf.format(line.name, flen))
39828c2ecf20Sopenharmony_ci		elif line.freturn:
39838c2ecf20Sopenharmony_ci			hf.write(html_func_end)
39848c2ecf20Sopenharmony_ci		else:
39858c2ecf20Sopenharmony_ci			hf.write(html_func_start.format(num, line.name, flen))
39868c2ecf20Sopenharmony_ci			num += 1
39878c2ecf20Sopenharmony_ci	hf.write(html_func_end)
39888c2ecf20Sopenharmony_ci	return num
39898c2ecf20Sopenharmony_ci
39908c2ecf20Sopenharmony_cidef addCallgraphs(sv, hf, data):
39918c2ecf20Sopenharmony_ci	hf.write('<section id="callgraphs" class="callgraph">\n')
39928c2ecf20Sopenharmony_ci	# write out the ftrace data converted to html
39938c2ecf20Sopenharmony_ci	num = 0
39948c2ecf20Sopenharmony_ci	for p in data.sortedPhases():
39958c2ecf20Sopenharmony_ci		if sv.cgphase and p != sv.cgphase:
39968c2ecf20Sopenharmony_ci			continue
39978c2ecf20Sopenharmony_ci		list = data.dmesg[p]['list']
39988c2ecf20Sopenharmony_ci		for d in data.sortedDevices(p):
39998c2ecf20Sopenharmony_ci			if len(sv.cgfilter) > 0 and d not in sv.cgfilter:
40008c2ecf20Sopenharmony_ci				continue
40018c2ecf20Sopenharmony_ci			dev = list[d]
40028c2ecf20Sopenharmony_ci			color = 'white'
40038c2ecf20Sopenharmony_ci			if 'color' in data.dmesg[p]:
40048c2ecf20Sopenharmony_ci				color = data.dmesg[p]['color']
40058c2ecf20Sopenharmony_ci			if 'color' in dev:
40068c2ecf20Sopenharmony_ci				color = dev['color']
40078c2ecf20Sopenharmony_ci			name = d if '[' not in d else d.split('[')[0]
40088c2ecf20Sopenharmony_ci			if(d in sv.devprops):
40098c2ecf20Sopenharmony_ci				name = sv.devprops[d].altName(d)
40108c2ecf20Sopenharmony_ci			if 'drv' in dev and dev['drv']:
40118c2ecf20Sopenharmony_ci				name += ' {%s}' % dev['drv']
40128c2ecf20Sopenharmony_ci			if sv.suspendmode in suspendmodename:
40138c2ecf20Sopenharmony_ci				name += ' '+p
40148c2ecf20Sopenharmony_ci			if('ftrace' in dev):
40158c2ecf20Sopenharmony_ci				cg = dev['ftrace']
40168c2ecf20Sopenharmony_ci				if cg.name == sv.ftopfunc:
40178c2ecf20Sopenharmony_ci					name = 'top level suspend/resume call'
40188c2ecf20Sopenharmony_ci				num = callgraphHTML(sv, hf, num, cg,
40198c2ecf20Sopenharmony_ci					name, color, dev['id'])
40208c2ecf20Sopenharmony_ci			if('ftraces' in dev):
40218c2ecf20Sopenharmony_ci				for cg in dev['ftraces']:
40228c2ecf20Sopenharmony_ci					num = callgraphHTML(sv, hf, num, cg,
40238c2ecf20Sopenharmony_ci						name+' &rarr; '+cg.name, color, dev['id'])
40248c2ecf20Sopenharmony_ci	hf.write('\n\n    </section>\n')
40258c2ecf20Sopenharmony_ci
40268c2ecf20Sopenharmony_cidef summaryCSS(title, center=True):
40278c2ecf20Sopenharmony_ci	tdcenter = 'text-align:center;' if center else ''
40288c2ecf20Sopenharmony_ci	out = '<!DOCTYPE html>\n<html>\n<head>\n\
40298c2ecf20Sopenharmony_ci	<meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\
40308c2ecf20Sopenharmony_ci	<title>'+title+'</title>\n\
40318c2ecf20Sopenharmony_ci	<style type=\'text/css\'>\n\
40328c2ecf20Sopenharmony_ci		.stamp {width: 100%;text-align:center;background:#888;line-height:30px;color:white;font: 25px Arial;}\n\
40338c2ecf20Sopenharmony_ci		table {width:100%;border-collapse: collapse;border:1px solid;}\n\
40348c2ecf20Sopenharmony_ci		th {border: 1px solid black;background:#222;color:white;}\n\
40358c2ecf20Sopenharmony_ci		td {font: 14px "Times New Roman";'+tdcenter+'}\n\
40368c2ecf20Sopenharmony_ci		tr.head td {border: 1px solid black;background:#aaa;}\n\
40378c2ecf20Sopenharmony_ci		tr.alt {background-color:#ddd;}\n\
40388c2ecf20Sopenharmony_ci		tr.notice {color:red;}\n\
40398c2ecf20Sopenharmony_ci		.minval {background-color:#BBFFBB;}\n\
40408c2ecf20Sopenharmony_ci		.medval {background-color:#BBBBFF;}\n\
40418c2ecf20Sopenharmony_ci		.maxval {background-color:#FFBBBB;}\n\
40428c2ecf20Sopenharmony_ci		.head a {color:#000;text-decoration: none;}\n\
40438c2ecf20Sopenharmony_ci	</style>\n</head>\n<body>\n'
40448c2ecf20Sopenharmony_ci	return out
40458c2ecf20Sopenharmony_ci
40468c2ecf20Sopenharmony_ci# Function: createHTMLSummarySimple
40478c2ecf20Sopenharmony_ci# Description:
40488c2ecf20Sopenharmony_ci#	 Create summary html file for a series of tests
40498c2ecf20Sopenharmony_ci# Arguments:
40508c2ecf20Sopenharmony_ci#	 testruns: array of Data objects from parseTraceLog
40518c2ecf20Sopenharmony_cidef createHTMLSummarySimple(testruns, htmlfile, title):
40528c2ecf20Sopenharmony_ci	# write the html header first (html head, css code, up to body start)
40538c2ecf20Sopenharmony_ci	html = summaryCSS('Summary - SleepGraph')
40548c2ecf20Sopenharmony_ci
40558c2ecf20Sopenharmony_ci	# extract the test data into list
40568c2ecf20Sopenharmony_ci	list = dict()
40578c2ecf20Sopenharmony_ci	tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()]
40588c2ecf20Sopenharmony_ci	iMin, iMed, iMax = [0, 0], [0, 0], [0, 0]
40598c2ecf20Sopenharmony_ci	num = 0
40608c2ecf20Sopenharmony_ci	useturbo = usewifi = False
40618c2ecf20Sopenharmony_ci	lastmode = ''
40628c2ecf20Sopenharmony_ci	cnt = dict()
40638c2ecf20Sopenharmony_ci	for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'], v['time'])):
40648c2ecf20Sopenharmony_ci		mode = data['mode']
40658c2ecf20Sopenharmony_ci		if mode not in list:
40668c2ecf20Sopenharmony_ci			list[mode] = {'data': [], 'avg': [0,0], 'min': [0,0], 'max': [0,0], 'med': [0,0]}
40678c2ecf20Sopenharmony_ci		if lastmode and lastmode != mode and num > 0:
40688c2ecf20Sopenharmony_ci			for i in range(2):
40698c2ecf20Sopenharmony_ci				s = sorted(tMed[i])
40708c2ecf20Sopenharmony_ci				list[lastmode]['med'][i] = s[int(len(s)//2)]
40718c2ecf20Sopenharmony_ci				iMed[i] = tMed[i][list[lastmode]['med'][i]]
40728c2ecf20Sopenharmony_ci			list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num]
40738c2ecf20Sopenharmony_ci			list[lastmode]['min'] = tMin
40748c2ecf20Sopenharmony_ci			list[lastmode]['max'] = tMax
40758c2ecf20Sopenharmony_ci			list[lastmode]['idx'] = (iMin, iMed, iMax)
40768c2ecf20Sopenharmony_ci			tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()]
40778c2ecf20Sopenharmony_ci			iMin, iMed, iMax = [0, 0], [0, 0], [0, 0]
40788c2ecf20Sopenharmony_ci			num = 0
40798c2ecf20Sopenharmony_ci		pkgpc10 = syslpi = wifi = ''
40808c2ecf20Sopenharmony_ci		if 'pkgpc10' in data and 'syslpi' in data:
40818c2ecf20Sopenharmony_ci			pkgpc10, syslpi, useturbo = data['pkgpc10'], data['syslpi'], True
40828c2ecf20Sopenharmony_ci		if 'wifi' in data:
40838c2ecf20Sopenharmony_ci			wifi, usewifi = data['wifi'], True
40848c2ecf20Sopenharmony_ci		res = data['result']
40858c2ecf20Sopenharmony_ci		tVal = [float(data['suspend']), float(data['resume'])]
40868c2ecf20Sopenharmony_ci		list[mode]['data'].append([data['host'], data['kernel'],
40878c2ecf20Sopenharmony_ci			data['time'], tVal[0], tVal[1], data['url'], res,
40888c2ecf20Sopenharmony_ci			data['issues'], data['sus_worst'], data['sus_worsttime'],
40898c2ecf20Sopenharmony_ci			data['res_worst'], data['res_worsttime'], pkgpc10, syslpi, wifi])
40908c2ecf20Sopenharmony_ci		idx = len(list[mode]['data']) - 1
40918c2ecf20Sopenharmony_ci		if res.startswith('fail in'):
40928c2ecf20Sopenharmony_ci			res = 'fail'
40938c2ecf20Sopenharmony_ci		if res not in cnt:
40948c2ecf20Sopenharmony_ci			cnt[res] = 1
40958c2ecf20Sopenharmony_ci		else:
40968c2ecf20Sopenharmony_ci			cnt[res] += 1
40978c2ecf20Sopenharmony_ci		if res == 'pass':
40988c2ecf20Sopenharmony_ci			for i in range(2):
40998c2ecf20Sopenharmony_ci				tMed[i][tVal[i]] = idx
41008c2ecf20Sopenharmony_ci				tAvg[i] += tVal[i]
41018c2ecf20Sopenharmony_ci				if tMin[i] == 0 or tVal[i] < tMin[i]:
41028c2ecf20Sopenharmony_ci					iMin[i] = idx
41038c2ecf20Sopenharmony_ci					tMin[i] = tVal[i]
41048c2ecf20Sopenharmony_ci				if tMax[i] == 0 or tVal[i] > tMax[i]:
41058c2ecf20Sopenharmony_ci					iMax[i] = idx
41068c2ecf20Sopenharmony_ci					tMax[i] = tVal[i]
41078c2ecf20Sopenharmony_ci			num += 1
41088c2ecf20Sopenharmony_ci		lastmode = mode
41098c2ecf20Sopenharmony_ci	if lastmode and num > 0:
41108c2ecf20Sopenharmony_ci		for i in range(2):
41118c2ecf20Sopenharmony_ci			s = sorted(tMed[i])
41128c2ecf20Sopenharmony_ci			list[lastmode]['med'][i] = s[int(len(s)//2)]
41138c2ecf20Sopenharmony_ci			iMed[i] = tMed[i][list[lastmode]['med'][i]]
41148c2ecf20Sopenharmony_ci		list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num]
41158c2ecf20Sopenharmony_ci		list[lastmode]['min'] = tMin
41168c2ecf20Sopenharmony_ci		list[lastmode]['max'] = tMax
41178c2ecf20Sopenharmony_ci		list[lastmode]['idx'] = (iMin, iMed, iMax)
41188c2ecf20Sopenharmony_ci
41198c2ecf20Sopenharmony_ci	# group test header
41208c2ecf20Sopenharmony_ci	desc = []
41218c2ecf20Sopenharmony_ci	for ilk in sorted(cnt, reverse=True):
41228c2ecf20Sopenharmony_ci		if cnt[ilk] > 0:
41238c2ecf20Sopenharmony_ci			desc.append('%d %s' % (cnt[ilk], ilk))
41248c2ecf20Sopenharmony_ci	html += '<div class="stamp">%s (%d tests: %s)</div>\n' % (title, len(testruns), ', '.join(desc))
41258c2ecf20Sopenharmony_ci	th = '\t<th>{0}</th>\n'
41268c2ecf20Sopenharmony_ci	td = '\t<td>{0}</td>\n'
41278c2ecf20Sopenharmony_ci	tdh = '\t<td{1}>{0}</td>\n'
41288c2ecf20Sopenharmony_ci	tdlink = '\t<td><a href="{0}">html</a></td>\n'
41298c2ecf20Sopenharmony_ci	cols = 12
41308c2ecf20Sopenharmony_ci	if useturbo:
41318c2ecf20Sopenharmony_ci		cols += 2
41328c2ecf20Sopenharmony_ci	if usewifi:
41338c2ecf20Sopenharmony_ci		cols += 1
41348c2ecf20Sopenharmony_ci	colspan = '%d' % cols
41358c2ecf20Sopenharmony_ci
41368c2ecf20Sopenharmony_ci	# table header
41378c2ecf20Sopenharmony_ci	html += '<table>\n<tr>\n' + th.format('#') +\
41388c2ecf20Sopenharmony_ci		th.format('Mode') + th.format('Host') + th.format('Kernel') +\
41398c2ecf20Sopenharmony_ci		th.format('Test Time') + th.format('Result') + th.format('Issues') +\
41408c2ecf20Sopenharmony_ci		th.format('Suspend') + th.format('Resume') +\
41418c2ecf20Sopenharmony_ci		th.format('Worst Suspend Device') + th.format('SD Time') +\
41428c2ecf20Sopenharmony_ci		th.format('Worst Resume Device') + th.format('RD Time')
41438c2ecf20Sopenharmony_ci	if useturbo:
41448c2ecf20Sopenharmony_ci		html += th.format('PkgPC10') + th.format('SysLPI')
41458c2ecf20Sopenharmony_ci	if usewifi:
41468c2ecf20Sopenharmony_ci		html += th.format('Wifi')
41478c2ecf20Sopenharmony_ci	html += th.format('Detail')+'</tr>\n'
41488c2ecf20Sopenharmony_ci	# export list into html
41498c2ecf20Sopenharmony_ci	head = '<tr class="head"><td>{0}</td><td>{1}</td>'+\
41508c2ecf20Sopenharmony_ci		'<td colspan='+colspan+' class="sus">Suspend Avg={2} '+\
41518c2ecf20Sopenharmony_ci		'<span class=minval><a href="#s{10}min">Min={3}</a></span> '+\
41528c2ecf20Sopenharmony_ci		'<span class=medval><a href="#s{10}med">Med={4}</a></span> '+\
41538c2ecf20Sopenharmony_ci		'<span class=maxval><a href="#s{10}max">Max={5}</a></span> '+\
41548c2ecf20Sopenharmony_ci		'Resume Avg={6} '+\
41558c2ecf20Sopenharmony_ci		'<span class=minval><a href="#r{10}min">Min={7}</a></span> '+\
41568c2ecf20Sopenharmony_ci		'<span class=medval><a href="#r{10}med">Med={8}</a></span> '+\
41578c2ecf20Sopenharmony_ci		'<span class=maxval><a href="#r{10}max">Max={9}</a></span></td>'+\
41588c2ecf20Sopenharmony_ci		'</tr>\n'
41598c2ecf20Sopenharmony_ci	headnone = '<tr class="head"><td>{0}</td><td>{1}</td><td colspan='+\
41608c2ecf20Sopenharmony_ci		colspan+'></td></tr>\n'
41618c2ecf20Sopenharmony_ci	for mode in sorted(list):
41628c2ecf20Sopenharmony_ci		# header line for each suspend mode
41638c2ecf20Sopenharmony_ci		num = 0
41648c2ecf20Sopenharmony_ci		tAvg, tMin, tMax, tMed = list[mode]['avg'], list[mode]['min'],\
41658c2ecf20Sopenharmony_ci			list[mode]['max'], list[mode]['med']
41668c2ecf20Sopenharmony_ci		count = len(list[mode]['data'])
41678c2ecf20Sopenharmony_ci		if 'idx' in list[mode]:
41688c2ecf20Sopenharmony_ci			iMin, iMed, iMax = list[mode]['idx']
41698c2ecf20Sopenharmony_ci			html += head.format('%d' % count, mode.upper(),
41708c2ecf20Sopenharmony_ci				'%.3f' % tAvg[0], '%.3f' % tMin[0], '%.3f' % tMed[0], '%.3f' % tMax[0],
41718c2ecf20Sopenharmony_ci				'%.3f' % tAvg[1], '%.3f' % tMin[1], '%.3f' % tMed[1], '%.3f' % tMax[1],
41728c2ecf20Sopenharmony_ci				mode.lower()
41738c2ecf20Sopenharmony_ci			)
41748c2ecf20Sopenharmony_ci		else:
41758c2ecf20Sopenharmony_ci			iMin = iMed = iMax = [-1, -1, -1]
41768c2ecf20Sopenharmony_ci			html += headnone.format('%d' % count, mode.upper())
41778c2ecf20Sopenharmony_ci		for d in list[mode]['data']:
41788c2ecf20Sopenharmony_ci			# row classes - alternate row color
41798c2ecf20Sopenharmony_ci			rcls = ['alt'] if num % 2 == 1 else []
41808c2ecf20Sopenharmony_ci			if d[6] != 'pass':
41818c2ecf20Sopenharmony_ci				rcls.append('notice')
41828c2ecf20Sopenharmony_ci			html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n'
41838c2ecf20Sopenharmony_ci			# figure out if the line has sus or res highlighted
41848c2ecf20Sopenharmony_ci			idx = list[mode]['data'].index(d)
41858c2ecf20Sopenharmony_ci			tHigh = ['', '']
41868c2ecf20Sopenharmony_ci			for i in range(2):
41878c2ecf20Sopenharmony_ci				tag = 's%s' % mode if i == 0 else 'r%s' % mode
41888c2ecf20Sopenharmony_ci				if idx == iMin[i]:
41898c2ecf20Sopenharmony_ci					tHigh[i] = ' id="%smin" class=minval title="Minimum"' % tag
41908c2ecf20Sopenharmony_ci				elif idx == iMax[i]:
41918c2ecf20Sopenharmony_ci					tHigh[i] = ' id="%smax" class=maxval title="Maximum"' % tag
41928c2ecf20Sopenharmony_ci				elif idx == iMed[i]:
41938c2ecf20Sopenharmony_ci					tHigh[i] = ' id="%smed" class=medval title="Median"' % tag
41948c2ecf20Sopenharmony_ci			html += td.format("%d" % (list[mode]['data'].index(d) + 1)) # row
41958c2ecf20Sopenharmony_ci			html += td.format(mode)										# mode
41968c2ecf20Sopenharmony_ci			html += td.format(d[0])										# host
41978c2ecf20Sopenharmony_ci			html += td.format(d[1])										# kernel
41988c2ecf20Sopenharmony_ci			html += td.format(d[2])										# time
41998c2ecf20Sopenharmony_ci			html += td.format(d[6])										# result
42008c2ecf20Sopenharmony_ci			html += td.format(d[7])										# issues
42018c2ecf20Sopenharmony_ci			html += tdh.format('%.3f ms' % d[3], tHigh[0]) if d[3] else td.format('')	# suspend
42028c2ecf20Sopenharmony_ci			html += tdh.format('%.3f ms' % d[4], tHigh[1]) if d[4] else td.format('')	# resume
42038c2ecf20Sopenharmony_ci			html += td.format(d[8])										# sus_worst
42048c2ecf20Sopenharmony_ci			html += td.format('%.3f ms' % d[9])	if d[9] else td.format('')		# sus_worst time
42058c2ecf20Sopenharmony_ci			html += td.format(d[10])									# res_worst
42068c2ecf20Sopenharmony_ci			html += td.format('%.3f ms' % d[11]) if d[11] else td.format('')	# res_worst time
42078c2ecf20Sopenharmony_ci			if useturbo:
42088c2ecf20Sopenharmony_ci				html += td.format(d[12])								# pkg_pc10
42098c2ecf20Sopenharmony_ci				html += td.format(d[13])								# syslpi
42108c2ecf20Sopenharmony_ci			if usewifi:
42118c2ecf20Sopenharmony_ci				html += td.format(d[14])								# wifi
42128c2ecf20Sopenharmony_ci			html += tdlink.format(d[5]) if d[5] else td.format('')		# url
42138c2ecf20Sopenharmony_ci			html += '</tr>\n'
42148c2ecf20Sopenharmony_ci			num += 1
42158c2ecf20Sopenharmony_ci
42168c2ecf20Sopenharmony_ci	# flush the data to file
42178c2ecf20Sopenharmony_ci	hf = open(htmlfile, 'w')
42188c2ecf20Sopenharmony_ci	hf.write(html+'</table>\n</body>\n</html>\n')
42198c2ecf20Sopenharmony_ci	hf.close()
42208c2ecf20Sopenharmony_ci
42218c2ecf20Sopenharmony_cidef createHTMLDeviceSummary(testruns, htmlfile, title):
42228c2ecf20Sopenharmony_ci	html = summaryCSS('Device Summary - SleepGraph', False)
42238c2ecf20Sopenharmony_ci
42248c2ecf20Sopenharmony_ci	# create global device list from all tests
42258c2ecf20Sopenharmony_ci	devall = dict()
42268c2ecf20Sopenharmony_ci	for data in testruns:
42278c2ecf20Sopenharmony_ci		host, url, devlist = data['host'], data['url'], data['devlist']
42288c2ecf20Sopenharmony_ci		for type in devlist:
42298c2ecf20Sopenharmony_ci			if type not in devall:
42308c2ecf20Sopenharmony_ci				devall[type] = dict()
42318c2ecf20Sopenharmony_ci			mdevlist, devlist = devall[type], data['devlist'][type]
42328c2ecf20Sopenharmony_ci			for name in devlist:
42338c2ecf20Sopenharmony_ci				length = devlist[name]
42348c2ecf20Sopenharmony_ci				if name not in mdevlist:
42358c2ecf20Sopenharmony_ci					mdevlist[name] = {'name': name, 'host': host,
42368c2ecf20Sopenharmony_ci						'worst': length, 'total': length, 'count': 1,
42378c2ecf20Sopenharmony_ci						'url': url}
42388c2ecf20Sopenharmony_ci				else:
42398c2ecf20Sopenharmony_ci					if length > mdevlist[name]['worst']:
42408c2ecf20Sopenharmony_ci						mdevlist[name]['worst'] = length
42418c2ecf20Sopenharmony_ci						mdevlist[name]['url'] = url
42428c2ecf20Sopenharmony_ci						mdevlist[name]['host'] = host
42438c2ecf20Sopenharmony_ci					mdevlist[name]['total'] += length
42448c2ecf20Sopenharmony_ci					mdevlist[name]['count'] += 1
42458c2ecf20Sopenharmony_ci
42468c2ecf20Sopenharmony_ci	# generate the html
42478c2ecf20Sopenharmony_ci	th = '\t<th>{0}</th>\n'
42488c2ecf20Sopenharmony_ci	td = '\t<td align=center>{0}</td>\n'
42498c2ecf20Sopenharmony_ci	tdr = '\t<td align=right>{0}</td>\n'
42508c2ecf20Sopenharmony_ci	tdlink = '\t<td align=center><a href="{0}">html</a></td>\n'
42518c2ecf20Sopenharmony_ci	limit = 1
42528c2ecf20Sopenharmony_ci	for type in sorted(devall, reverse=True):
42538c2ecf20Sopenharmony_ci		num = 0
42548c2ecf20Sopenharmony_ci		devlist = devall[type]
42558c2ecf20Sopenharmony_ci		# table header
42568c2ecf20Sopenharmony_ci		html += '<div class="stamp">%s (%s devices > %d ms)</div><table>\n' % \
42578c2ecf20Sopenharmony_ci			(title, type.upper(), limit)
42588c2ecf20Sopenharmony_ci		html += '<tr>\n' + '<th align=right>Device Name</th>' +\
42598c2ecf20Sopenharmony_ci			th.format('Average Time') + th.format('Count') +\
42608c2ecf20Sopenharmony_ci			th.format('Worst Time') + th.format('Host (worst time)') +\
42618c2ecf20Sopenharmony_ci			th.format('Link (worst time)') + '</tr>\n'
42628c2ecf20Sopenharmony_ci		for name in sorted(devlist, key=lambda k:(devlist[k]['worst'], \
42638c2ecf20Sopenharmony_ci			devlist[k]['total'], devlist[k]['name']), reverse=True):
42648c2ecf20Sopenharmony_ci			data = devall[type][name]
42658c2ecf20Sopenharmony_ci			data['average'] = data['total'] / data['count']
42668c2ecf20Sopenharmony_ci			if data['average'] < limit:
42678c2ecf20Sopenharmony_ci				continue
42688c2ecf20Sopenharmony_ci			# row classes - alternate row color
42698c2ecf20Sopenharmony_ci			rcls = ['alt'] if num % 2 == 1 else []
42708c2ecf20Sopenharmony_ci			html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n'
42718c2ecf20Sopenharmony_ci			html += tdr.format(data['name'])				# name
42728c2ecf20Sopenharmony_ci			html += td.format('%.3f ms' % data['average'])	# average
42738c2ecf20Sopenharmony_ci			html += td.format(data['count'])				# count
42748c2ecf20Sopenharmony_ci			html += td.format('%.3f ms' % data['worst'])	# worst
42758c2ecf20Sopenharmony_ci			html += td.format(data['host'])					# host
42768c2ecf20Sopenharmony_ci			html += tdlink.format(data['url'])				# url
42778c2ecf20Sopenharmony_ci			html += '</tr>\n'
42788c2ecf20Sopenharmony_ci			num += 1
42798c2ecf20Sopenharmony_ci		html += '</table>\n'
42808c2ecf20Sopenharmony_ci
42818c2ecf20Sopenharmony_ci	# flush the data to file
42828c2ecf20Sopenharmony_ci	hf = open(htmlfile, 'w')
42838c2ecf20Sopenharmony_ci	hf.write(html+'</body>\n</html>\n')
42848c2ecf20Sopenharmony_ci	hf.close()
42858c2ecf20Sopenharmony_ci	return devall
42868c2ecf20Sopenharmony_ci
42878c2ecf20Sopenharmony_cidef createHTMLIssuesSummary(testruns, issues, htmlfile, title, extra=''):
42888c2ecf20Sopenharmony_ci	multihost = len([e for e in issues if len(e['urls']) > 1]) > 0
42898c2ecf20Sopenharmony_ci	html = summaryCSS('Issues Summary - SleepGraph', False)
42908c2ecf20Sopenharmony_ci	total = len(testruns)
42918c2ecf20Sopenharmony_ci
42928c2ecf20Sopenharmony_ci	# generate the html
42938c2ecf20Sopenharmony_ci	th = '\t<th>{0}</th>\n'
42948c2ecf20Sopenharmony_ci	td = '\t<td align={0}>{1}</td>\n'
42958c2ecf20Sopenharmony_ci	tdlink = '<a href="{1}">{0}</a>'
42968c2ecf20Sopenharmony_ci	subtitle = '%d issues' % len(issues) if len(issues) > 0 else 'no issues'
42978c2ecf20Sopenharmony_ci	html += '<div class="stamp">%s (%s)</div><table>\n' % (title, subtitle)
42988c2ecf20Sopenharmony_ci	html += '<tr>\n' + th.format('Issue') + th.format('Count')
42998c2ecf20Sopenharmony_ci	if multihost:
43008c2ecf20Sopenharmony_ci		html += th.format('Hosts')
43018c2ecf20Sopenharmony_ci	html += th.format('Tests') + th.format('Fail Rate') +\
43028c2ecf20Sopenharmony_ci		th.format('First Instance') + '</tr>\n'
43038c2ecf20Sopenharmony_ci
43048c2ecf20Sopenharmony_ci	num = 0
43058c2ecf20Sopenharmony_ci	for e in sorted(issues, key=lambda v:v['count'], reverse=True):
43068c2ecf20Sopenharmony_ci		testtotal = 0
43078c2ecf20Sopenharmony_ci		links = []
43088c2ecf20Sopenharmony_ci		for host in sorted(e['urls']):
43098c2ecf20Sopenharmony_ci			links.append(tdlink.format(host, e['urls'][host][0]))
43108c2ecf20Sopenharmony_ci			testtotal += len(e['urls'][host])
43118c2ecf20Sopenharmony_ci		rate = '%d/%d (%.2f%%)' % (testtotal, total, 100*float(testtotal)/float(total))
43128c2ecf20Sopenharmony_ci		# row classes - alternate row color
43138c2ecf20Sopenharmony_ci		rcls = ['alt'] if num % 2 == 1 else []
43148c2ecf20Sopenharmony_ci		html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n'
43158c2ecf20Sopenharmony_ci		html += td.format('left', e['line'])		# issue
43168c2ecf20Sopenharmony_ci		html += td.format('center', e['count'])		# count
43178c2ecf20Sopenharmony_ci		if multihost:
43188c2ecf20Sopenharmony_ci			html += td.format('center', len(e['urls']))	# hosts
43198c2ecf20Sopenharmony_ci		html += td.format('center', testtotal)		# test count
43208c2ecf20Sopenharmony_ci		html += td.format('center', rate)			# test rate
43218c2ecf20Sopenharmony_ci		html += td.format('center nowrap', '<br>'.join(links))	# links
43228c2ecf20Sopenharmony_ci		html += '</tr>\n'
43238c2ecf20Sopenharmony_ci		num += 1
43248c2ecf20Sopenharmony_ci
43258c2ecf20Sopenharmony_ci	# flush the data to file
43268c2ecf20Sopenharmony_ci	hf = open(htmlfile, 'w')
43278c2ecf20Sopenharmony_ci	hf.write(html+'</table>\n'+extra+'</body>\n</html>\n')
43288c2ecf20Sopenharmony_ci	hf.close()
43298c2ecf20Sopenharmony_ci	return issues
43308c2ecf20Sopenharmony_ci
43318c2ecf20Sopenharmony_cidef ordinal(value):
43328c2ecf20Sopenharmony_ci	suffix = 'th'
43338c2ecf20Sopenharmony_ci	if value < 10 or value > 19:
43348c2ecf20Sopenharmony_ci		if value % 10 == 1:
43358c2ecf20Sopenharmony_ci			suffix = 'st'
43368c2ecf20Sopenharmony_ci		elif value % 10 == 2:
43378c2ecf20Sopenharmony_ci			suffix = 'nd'
43388c2ecf20Sopenharmony_ci		elif value % 10 == 3:
43398c2ecf20Sopenharmony_ci			suffix = 'rd'
43408c2ecf20Sopenharmony_ci	return '%d%s' % (value, suffix)
43418c2ecf20Sopenharmony_ci
43428c2ecf20Sopenharmony_ci# Function: createHTML
43438c2ecf20Sopenharmony_ci# Description:
43448c2ecf20Sopenharmony_ci#	 Create the output html file from the resident test data
43458c2ecf20Sopenharmony_ci# Arguments:
43468c2ecf20Sopenharmony_ci#	 testruns: array of Data objects from parseKernelLog or parseTraceLog
43478c2ecf20Sopenharmony_ci# Output:
43488c2ecf20Sopenharmony_ci#	 True if the html file was created, false if it failed
43498c2ecf20Sopenharmony_cidef createHTML(testruns, testfail):
43508c2ecf20Sopenharmony_ci	if len(testruns) < 1:
43518c2ecf20Sopenharmony_ci		pprint('ERROR: Not enough test data to build a timeline')
43528c2ecf20Sopenharmony_ci		return
43538c2ecf20Sopenharmony_ci
43548c2ecf20Sopenharmony_ci	kerror = False
43558c2ecf20Sopenharmony_ci	for data in testruns:
43568c2ecf20Sopenharmony_ci		if data.kerror:
43578c2ecf20Sopenharmony_ci			kerror = True
43588c2ecf20Sopenharmony_ci		if(sysvals.suspendmode in ['freeze', 'standby']):
43598c2ecf20Sopenharmony_ci			data.trimFreezeTime(testruns[-1].tSuspended)
43608c2ecf20Sopenharmony_ci		else:
43618c2ecf20Sopenharmony_ci			data.getMemTime()
43628c2ecf20Sopenharmony_ci
43638c2ecf20Sopenharmony_ci	# html function templates
43648c2ecf20Sopenharmony_ci	html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">{2}&rarr;</div>\n'
43658c2ecf20Sopenharmony_ci	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'
43668c2ecf20Sopenharmony_ci	html_cpuexec = '<div class="jiffie" style="left:{0}%;top:{1}px;height:{2}px;width:{3}%;background:{4};"></div>\n'
43678c2ecf20Sopenharmony_ci	html_timetotal = '<table class="time1">\n<tr>'\
43688c2ecf20Sopenharmony_ci		'<td class="green" title="{3}">{2} Suspend Time: <b>{0} ms</b></td>'\
43698c2ecf20Sopenharmony_ci		'<td class="yellow" title="{4}">{2} Resume Time: <b>{1} ms</b></td>'\
43708c2ecf20Sopenharmony_ci		'</tr>\n</table>\n'
43718c2ecf20Sopenharmony_ci	html_timetotal2 = '<table class="time1">\n<tr>'\
43728c2ecf20Sopenharmony_ci		'<td class="green" title="{4}">{3} Suspend Time: <b>{0} ms</b></td>'\
43738c2ecf20Sopenharmony_ci		'<td class="gray" title="time spent in low-power mode with clock running">'+sysvals.suspendmode+' time: <b>{1} ms</b></td>'\
43748c2ecf20Sopenharmony_ci		'<td class="yellow" title="{5}">{3} Resume Time: <b>{2} ms</b></td>'\
43758c2ecf20Sopenharmony_ci		'</tr>\n</table>\n'
43768c2ecf20Sopenharmony_ci	html_timetotal3 = '<table class="time1">\n<tr>'\
43778c2ecf20Sopenharmony_ci		'<td class="green">Execution Time: <b>{0} ms</b></td>'\
43788c2ecf20Sopenharmony_ci		'<td class="yellow">Command: <b>{1}</b></td>'\
43798c2ecf20Sopenharmony_ci		'</tr>\n</table>\n'
43808c2ecf20Sopenharmony_ci	html_fail = '<table class="testfail"><tr><td>{0}</td></tr></table>\n'
43818c2ecf20Sopenharmony_ci	html_kdesc = '<td class="{3}" title="time spent in kernel execution">{0}Kernel {2}: {1} ms</td>'
43828c2ecf20Sopenharmony_ci	html_fwdesc = '<td class="{3}" title="time spent in firmware">{0}Firmware {2}: {1} ms</td>'
43838c2ecf20Sopenharmony_ci	html_wifdesc = '<td class="yellow" title="time for wifi to reconnect after resume complete ({2})">{0}Wifi Resume: {1}</td>'
43848c2ecf20Sopenharmony_ci
43858c2ecf20Sopenharmony_ci	# html format variables
43868c2ecf20Sopenharmony_ci	scaleH = 20
43878c2ecf20Sopenharmony_ci	if kerror:
43888c2ecf20Sopenharmony_ci		scaleH = 40
43898c2ecf20Sopenharmony_ci
43908c2ecf20Sopenharmony_ci	# device timeline
43918c2ecf20Sopenharmony_ci	devtl = Timeline(30, scaleH)
43928c2ecf20Sopenharmony_ci
43938c2ecf20Sopenharmony_ci	# write the test title and general info header
43948c2ecf20Sopenharmony_ci	devtl.createHeader(sysvals, testruns[0].stamp)
43958c2ecf20Sopenharmony_ci
43968c2ecf20Sopenharmony_ci	# Generate the header for this timeline
43978c2ecf20Sopenharmony_ci	for data in testruns:
43988c2ecf20Sopenharmony_ci		tTotal = data.end - data.start
43998c2ecf20Sopenharmony_ci		if(tTotal == 0):
44008c2ecf20Sopenharmony_ci			doError('No timeline data')
44018c2ecf20Sopenharmony_ci		if sysvals.suspendmode == 'command':
44028c2ecf20Sopenharmony_ci			run_time = '%.0f' % (tTotal * 1000)
44038c2ecf20Sopenharmony_ci			if sysvals.testcommand:
44048c2ecf20Sopenharmony_ci				testdesc = sysvals.testcommand
44058c2ecf20Sopenharmony_ci			else:
44068c2ecf20Sopenharmony_ci				testdesc = 'unknown'
44078c2ecf20Sopenharmony_ci			if(len(testruns) > 1):
44088c2ecf20Sopenharmony_ci				testdesc = ordinal(data.testnumber+1)+' '+testdesc
44098c2ecf20Sopenharmony_ci			thtml = html_timetotal3.format(run_time, testdesc)
44108c2ecf20Sopenharmony_ci			devtl.html += thtml
44118c2ecf20Sopenharmony_ci			continue
44128c2ecf20Sopenharmony_ci		# typical full suspend/resume header
44138c2ecf20Sopenharmony_ci		stot, rtot = sktime, rktime = data.getTimeValues()
44148c2ecf20Sopenharmony_ci		ssrc, rsrc, testdesc, testdesc2 = ['kernel'], ['kernel'], 'Kernel', ''
44158c2ecf20Sopenharmony_ci		if data.fwValid:
44168c2ecf20Sopenharmony_ci			stot += (data.fwSuspend/1000000.0)
44178c2ecf20Sopenharmony_ci			rtot += (data.fwResume/1000000.0)
44188c2ecf20Sopenharmony_ci			ssrc.append('firmware')
44198c2ecf20Sopenharmony_ci			rsrc.append('firmware')
44208c2ecf20Sopenharmony_ci			testdesc = 'Total'
44218c2ecf20Sopenharmony_ci		if 'time' in data.wifi and data.wifi['stat'] != 'timeout':
44228c2ecf20Sopenharmony_ci			rtot += data.end - data.tKernRes + (data.wifi['time'] * 1000.0)
44238c2ecf20Sopenharmony_ci			rsrc.append('wifi')
44248c2ecf20Sopenharmony_ci			testdesc = 'Total'
44258c2ecf20Sopenharmony_ci		suspend_time, resume_time = '%.3f' % stot, '%.3f' % rtot
44268c2ecf20Sopenharmony_ci		stitle = 'time from kernel suspend start to %s mode [%s time]' % \
44278c2ecf20Sopenharmony_ci			(sysvals.suspendmode, ' & '.join(ssrc))
44288c2ecf20Sopenharmony_ci		rtitle = 'time from %s mode to kernel resume complete [%s time]' % \
44298c2ecf20Sopenharmony_ci			(sysvals.suspendmode, ' & '.join(rsrc))
44308c2ecf20Sopenharmony_ci		if(len(testruns) > 1):
44318c2ecf20Sopenharmony_ci			testdesc = testdesc2 = ordinal(data.testnumber+1)
44328c2ecf20Sopenharmony_ci			testdesc2 += ' '
44338c2ecf20Sopenharmony_ci		if(len(data.tLow) == 0):
44348c2ecf20Sopenharmony_ci			thtml = html_timetotal.format(suspend_time, \
44358c2ecf20Sopenharmony_ci				resume_time, testdesc, stitle, rtitle)
44368c2ecf20Sopenharmony_ci		else:
44378c2ecf20Sopenharmony_ci			low_time = '+'.join(data.tLow)
44388c2ecf20Sopenharmony_ci			thtml = html_timetotal2.format(suspend_time, low_time, \
44398c2ecf20Sopenharmony_ci				resume_time, testdesc, stitle, rtitle)
44408c2ecf20Sopenharmony_ci		devtl.html += thtml
44418c2ecf20Sopenharmony_ci		if not data.fwValid and 'dev' not in data.wifi:
44428c2ecf20Sopenharmony_ci			continue
44438c2ecf20Sopenharmony_ci		# extra detail when the times come from multiple sources
44448c2ecf20Sopenharmony_ci		thtml = '<table class="time2">\n<tr>'
44458c2ecf20Sopenharmony_ci		thtml += html_kdesc.format(testdesc2, '%.3f'%sktime, 'Suspend', 'green')
44468c2ecf20Sopenharmony_ci		if data.fwValid:
44478c2ecf20Sopenharmony_ci			sftime = '%.3f'%(data.fwSuspend / 1000000.0)
44488c2ecf20Sopenharmony_ci			rftime = '%.3f'%(data.fwResume / 1000000.0)
44498c2ecf20Sopenharmony_ci			thtml += html_fwdesc.format(testdesc2, sftime, 'Suspend', 'green')
44508c2ecf20Sopenharmony_ci			thtml += html_fwdesc.format(testdesc2, rftime, 'Resume', 'yellow')
44518c2ecf20Sopenharmony_ci		thtml += html_kdesc.format(testdesc2, '%.3f'%rktime, 'Resume', 'yellow')
44528c2ecf20Sopenharmony_ci		if 'time' in data.wifi:
44538c2ecf20Sopenharmony_ci			if data.wifi['stat'] != 'timeout':
44548c2ecf20Sopenharmony_ci				wtime = '%.0f ms'%(data.end - data.tKernRes + (data.wifi['time'] * 1000.0))
44558c2ecf20Sopenharmony_ci			else:
44568c2ecf20Sopenharmony_ci				wtime = 'TIMEOUT'
44578c2ecf20Sopenharmony_ci			thtml += html_wifdesc.format(testdesc2, wtime, data.wifi['dev'])
44588c2ecf20Sopenharmony_ci		thtml += '</tr>\n</table>\n'
44598c2ecf20Sopenharmony_ci		devtl.html += thtml
44608c2ecf20Sopenharmony_ci	if testfail:
44618c2ecf20Sopenharmony_ci		devtl.html += html_fail.format(testfail)
44628c2ecf20Sopenharmony_ci
44638c2ecf20Sopenharmony_ci	# time scale for potentially multiple datasets
44648c2ecf20Sopenharmony_ci	t0 = testruns[0].start
44658c2ecf20Sopenharmony_ci	tMax = testruns[-1].end
44668c2ecf20Sopenharmony_ci	tTotal = tMax - t0
44678c2ecf20Sopenharmony_ci
44688c2ecf20Sopenharmony_ci	# determine the maximum number of rows we need to draw
44698c2ecf20Sopenharmony_ci	fulllist = []
44708c2ecf20Sopenharmony_ci	threadlist = []
44718c2ecf20Sopenharmony_ci	pscnt = 0
44728c2ecf20Sopenharmony_ci	devcnt = 0
44738c2ecf20Sopenharmony_ci	for data in testruns:
44748c2ecf20Sopenharmony_ci		data.selectTimelineDevices('%f', tTotal, sysvals.mindevlen)
44758c2ecf20Sopenharmony_ci		for group in data.devicegroups:
44768c2ecf20Sopenharmony_ci			devlist = []
44778c2ecf20Sopenharmony_ci			for phase in group:
44788c2ecf20Sopenharmony_ci				for devname in sorted(data.tdevlist[phase]):
44798c2ecf20Sopenharmony_ci					d = DevItem(data.testnumber, phase, data.dmesg[phase]['list'][devname])
44808c2ecf20Sopenharmony_ci					devlist.append(d)
44818c2ecf20Sopenharmony_ci					if d.isa('kth'):
44828c2ecf20Sopenharmony_ci						threadlist.append(d)
44838c2ecf20Sopenharmony_ci					else:
44848c2ecf20Sopenharmony_ci						if d.isa('ps'):
44858c2ecf20Sopenharmony_ci							pscnt += 1
44868c2ecf20Sopenharmony_ci						else:
44878c2ecf20Sopenharmony_ci							devcnt += 1
44888c2ecf20Sopenharmony_ci						fulllist.append(d)
44898c2ecf20Sopenharmony_ci			if sysvals.mixedphaseheight:
44908c2ecf20Sopenharmony_ci				devtl.getPhaseRows(devlist)
44918c2ecf20Sopenharmony_ci	if not sysvals.mixedphaseheight:
44928c2ecf20Sopenharmony_ci		if len(threadlist) > 0 and len(fulllist) > 0:
44938c2ecf20Sopenharmony_ci			if pscnt > 0 and devcnt > 0:
44948c2ecf20Sopenharmony_ci				msg = 'user processes & device pm callbacks'
44958c2ecf20Sopenharmony_ci			elif pscnt > 0:
44968c2ecf20Sopenharmony_ci				msg = 'user processes'
44978c2ecf20Sopenharmony_ci			else:
44988c2ecf20Sopenharmony_ci				msg = 'device pm callbacks'
44998c2ecf20Sopenharmony_ci			d = testruns[0].addHorizontalDivider(msg, testruns[-1].end)
45008c2ecf20Sopenharmony_ci			fulllist.insert(0, d)
45018c2ecf20Sopenharmony_ci		devtl.getPhaseRows(fulllist)
45028c2ecf20Sopenharmony_ci		if len(threadlist) > 0:
45038c2ecf20Sopenharmony_ci			d = testruns[0].addHorizontalDivider('asynchronous kernel threads', testruns[-1].end)
45048c2ecf20Sopenharmony_ci			threadlist.insert(0, d)
45058c2ecf20Sopenharmony_ci			devtl.getPhaseRows(threadlist, devtl.rows)
45068c2ecf20Sopenharmony_ci	devtl.calcTotalRows()
45078c2ecf20Sopenharmony_ci
45088c2ecf20Sopenharmony_ci	# draw the full timeline
45098c2ecf20Sopenharmony_ci	devtl.createZoomBox(sysvals.suspendmode, len(testruns))
45108c2ecf20Sopenharmony_ci	for data in testruns:
45118c2ecf20Sopenharmony_ci		# draw each test run and block chronologically
45128c2ecf20Sopenharmony_ci		phases = {'suspend':[],'resume':[]}
45138c2ecf20Sopenharmony_ci		for phase in data.sortedPhases():
45148c2ecf20Sopenharmony_ci			if data.dmesg[phase]['start'] >= data.tSuspended:
45158c2ecf20Sopenharmony_ci				phases['resume'].append(phase)
45168c2ecf20Sopenharmony_ci			else:
45178c2ecf20Sopenharmony_ci				phases['suspend'].append(phase)
45188c2ecf20Sopenharmony_ci		# now draw the actual timeline blocks
45198c2ecf20Sopenharmony_ci		for dir in phases:
45208c2ecf20Sopenharmony_ci			# draw suspend and resume blocks separately
45218c2ecf20Sopenharmony_ci			bname = '%s%d' % (dir[0], data.testnumber)
45228c2ecf20Sopenharmony_ci			if dir == 'suspend':
45238c2ecf20Sopenharmony_ci				m0 = data.start
45248c2ecf20Sopenharmony_ci				mMax = data.tSuspended
45258c2ecf20Sopenharmony_ci				left = '%f' % (((m0-t0)*100.0)/tTotal)
45268c2ecf20Sopenharmony_ci			else:
45278c2ecf20Sopenharmony_ci				m0 = data.tSuspended
45288c2ecf20Sopenharmony_ci				mMax = data.end
45298c2ecf20Sopenharmony_ci				# in an x2 run, remove any gap between blocks
45308c2ecf20Sopenharmony_ci				if len(testruns) > 1 and data.testnumber == 0:
45318c2ecf20Sopenharmony_ci					mMax = testruns[1].start
45328c2ecf20Sopenharmony_ci				left = '%f' % ((((m0-t0)*100.0)+sysvals.srgap/2)/tTotal)
45338c2ecf20Sopenharmony_ci			mTotal = mMax - m0
45348c2ecf20Sopenharmony_ci			# if a timeline block is 0 length, skip altogether
45358c2ecf20Sopenharmony_ci			if mTotal == 0:
45368c2ecf20Sopenharmony_ci				continue
45378c2ecf20Sopenharmony_ci			width = '%f' % (((mTotal*100.0)-sysvals.srgap/2)/tTotal)
45388c2ecf20Sopenharmony_ci			devtl.html += devtl.html_tblock.format(bname, left, width, devtl.scaleH)
45398c2ecf20Sopenharmony_ci			for b in phases[dir]:
45408c2ecf20Sopenharmony_ci				# draw the phase color background
45418c2ecf20Sopenharmony_ci				phase = data.dmesg[b]
45428c2ecf20Sopenharmony_ci				length = phase['end']-phase['start']
45438c2ecf20Sopenharmony_ci				left = '%f' % (((phase['start']-m0)*100.0)/mTotal)
45448c2ecf20Sopenharmony_ci				width = '%f' % ((length*100.0)/mTotal)
45458c2ecf20Sopenharmony_ci				devtl.html += devtl.html_phase.format(left, width, \
45468c2ecf20Sopenharmony_ci					'%.3f'%devtl.scaleH, '%.3f'%devtl.bodyH, \
45478c2ecf20Sopenharmony_ci					data.dmesg[b]['color'], '')
45488c2ecf20Sopenharmony_ci			for e in data.errorinfo[dir]:
45498c2ecf20Sopenharmony_ci				# draw red lines for any kernel errors found
45508c2ecf20Sopenharmony_ci				type, t, idx1, idx2 = e
45518c2ecf20Sopenharmony_ci				id = '%d_%d' % (idx1, idx2)
45528c2ecf20Sopenharmony_ci				right = '%f' % (((mMax-t)*100.0)/mTotal)
45538c2ecf20Sopenharmony_ci				devtl.html += html_error.format(right, id, type)
45548c2ecf20Sopenharmony_ci			for b in phases[dir]:
45558c2ecf20Sopenharmony_ci				# draw the devices for this phase
45568c2ecf20Sopenharmony_ci				phaselist = data.dmesg[b]['list']
45578c2ecf20Sopenharmony_ci				for d in sorted(data.tdevlist[b]):
45588c2ecf20Sopenharmony_ci					dname = d if '[' not in d else d.split('[')[0]
45598c2ecf20Sopenharmony_ci					name, dev = dname, phaselist[d]
45608c2ecf20Sopenharmony_ci					drv = xtraclass = xtrainfo = xtrastyle = ''
45618c2ecf20Sopenharmony_ci					if 'htmlclass' in dev:
45628c2ecf20Sopenharmony_ci						xtraclass = dev['htmlclass']
45638c2ecf20Sopenharmony_ci					if 'color' in dev:
45648c2ecf20Sopenharmony_ci						xtrastyle = 'background:%s;' % dev['color']
45658c2ecf20Sopenharmony_ci					if(d in sysvals.devprops):
45668c2ecf20Sopenharmony_ci						name = sysvals.devprops[d].altName(d)
45678c2ecf20Sopenharmony_ci						xtraclass = sysvals.devprops[d].xtraClass()
45688c2ecf20Sopenharmony_ci						xtrainfo = sysvals.devprops[d].xtraInfo()
45698c2ecf20Sopenharmony_ci					elif xtraclass == ' kth':
45708c2ecf20Sopenharmony_ci						xtrainfo = ' kernel_thread'
45718c2ecf20Sopenharmony_ci					if('drv' in dev and dev['drv']):
45728c2ecf20Sopenharmony_ci						drv = ' {%s}' % dev['drv']
45738c2ecf20Sopenharmony_ci					rowheight = devtl.phaseRowHeight(data.testnumber, b, dev['row'])
45748c2ecf20Sopenharmony_ci					rowtop = devtl.phaseRowTop(data.testnumber, b, dev['row'])
45758c2ecf20Sopenharmony_ci					top = '%.3f' % (rowtop + devtl.scaleH)
45768c2ecf20Sopenharmony_ci					left = '%f' % (((dev['start']-m0)*100)/mTotal)
45778c2ecf20Sopenharmony_ci					width = '%f' % (((dev['end']-dev['start'])*100)/mTotal)
45788c2ecf20Sopenharmony_ci					length = ' (%0.3f ms) ' % ((dev['end']-dev['start'])*1000)
45798c2ecf20Sopenharmony_ci					title = name+drv+xtrainfo+length
45808c2ecf20Sopenharmony_ci					if sysvals.suspendmode == 'command':
45818c2ecf20Sopenharmony_ci						title += sysvals.testcommand
45828c2ecf20Sopenharmony_ci					elif xtraclass == ' ps':
45838c2ecf20Sopenharmony_ci						if 'suspend' in b:
45848c2ecf20Sopenharmony_ci							title += 'pre_suspend_process'
45858c2ecf20Sopenharmony_ci						else:
45868c2ecf20Sopenharmony_ci							title += 'post_resume_process'
45878c2ecf20Sopenharmony_ci					else:
45888c2ecf20Sopenharmony_ci						title += b
45898c2ecf20Sopenharmony_ci					devtl.html += devtl.html_device.format(dev['id'], \
45908c2ecf20Sopenharmony_ci						title, left, top, '%.3f'%rowheight, width, \
45918c2ecf20Sopenharmony_ci						dname+drv, xtraclass, xtrastyle)
45928c2ecf20Sopenharmony_ci					if('cpuexec' in dev):
45938c2ecf20Sopenharmony_ci						for t in sorted(dev['cpuexec']):
45948c2ecf20Sopenharmony_ci							start, end = t
45958c2ecf20Sopenharmony_ci							j = float(dev['cpuexec'][t]) / 5
45968c2ecf20Sopenharmony_ci							if j > 1.0:
45978c2ecf20Sopenharmony_ci								j = 1.0
45988c2ecf20Sopenharmony_ci							height = '%.3f' % (rowheight/3)
45998c2ecf20Sopenharmony_ci							top = '%.3f' % (rowtop + devtl.scaleH + 2*rowheight/3)
46008c2ecf20Sopenharmony_ci							left = '%f' % (((start-m0)*100)/mTotal)
46018c2ecf20Sopenharmony_ci							width = '%f' % ((end-start)*100/mTotal)
46028c2ecf20Sopenharmony_ci							color = 'rgba(255, 0, 0, %f)' % j
46038c2ecf20Sopenharmony_ci							devtl.html += \
46048c2ecf20Sopenharmony_ci								html_cpuexec.format(left, top, height, width, color)
46058c2ecf20Sopenharmony_ci					if('src' not in dev):
46068c2ecf20Sopenharmony_ci						continue
46078c2ecf20Sopenharmony_ci					# draw any trace events for this device
46088c2ecf20Sopenharmony_ci					for e in dev['src']:
46098c2ecf20Sopenharmony_ci						if e.length == 0:
46108c2ecf20Sopenharmony_ci							continue
46118c2ecf20Sopenharmony_ci						height = '%.3f' % devtl.rowH
46128c2ecf20Sopenharmony_ci						top = '%.3f' % (rowtop + devtl.scaleH + (e.row*devtl.rowH))
46138c2ecf20Sopenharmony_ci						left = '%f' % (((e.time-m0)*100)/mTotal)
46148c2ecf20Sopenharmony_ci						width = '%f' % (e.length*100/mTotal)
46158c2ecf20Sopenharmony_ci						xtrastyle = ''
46168c2ecf20Sopenharmony_ci						if e.color:
46178c2ecf20Sopenharmony_ci							xtrastyle = 'background:%s;' % e.color
46188c2ecf20Sopenharmony_ci						devtl.html += \
46198c2ecf20Sopenharmony_ci							html_traceevent.format(e.title(), \
46208c2ecf20Sopenharmony_ci								left, top, height, width, e.text(), '', xtrastyle)
46218c2ecf20Sopenharmony_ci			# draw the time scale, try to make the number of labels readable
46228c2ecf20Sopenharmony_ci			devtl.createTimeScale(m0, mMax, tTotal, dir)
46238c2ecf20Sopenharmony_ci			devtl.html += '</div>\n'
46248c2ecf20Sopenharmony_ci
46258c2ecf20Sopenharmony_ci	# timeline is finished
46268c2ecf20Sopenharmony_ci	devtl.html += '</div>\n</div>\n'
46278c2ecf20Sopenharmony_ci
46288c2ecf20Sopenharmony_ci	# draw a legend which describes the phases by color
46298c2ecf20Sopenharmony_ci	if sysvals.suspendmode != 'command':
46308c2ecf20Sopenharmony_ci		phasedef = testruns[-1].phasedef
46318c2ecf20Sopenharmony_ci		devtl.html += '<div class="legend">\n'
46328c2ecf20Sopenharmony_ci		pdelta = 100.0/len(phasedef.keys())
46338c2ecf20Sopenharmony_ci		pmargin = pdelta / 4.0
46348c2ecf20Sopenharmony_ci		for phase in sorted(phasedef, key=lambda k:phasedef[k]['order']):
46358c2ecf20Sopenharmony_ci			id, p = '', phasedef[phase]
46368c2ecf20Sopenharmony_ci			for word in phase.split('_'):
46378c2ecf20Sopenharmony_ci				id += word[0]
46388c2ecf20Sopenharmony_ci			order = '%.2f' % ((p['order'] * pdelta) + pmargin)
46398c2ecf20Sopenharmony_ci			name = phase.replace('_', ' &nbsp;')
46408c2ecf20Sopenharmony_ci			devtl.html += devtl.html_legend.format(order, p['color'], name, id)
46418c2ecf20Sopenharmony_ci		devtl.html += '</div>\n'
46428c2ecf20Sopenharmony_ci
46438c2ecf20Sopenharmony_ci	hf = open(sysvals.htmlfile, 'w')
46448c2ecf20Sopenharmony_ci	addCSS(hf, sysvals, len(testruns), kerror)
46458c2ecf20Sopenharmony_ci
46468c2ecf20Sopenharmony_ci	# write the device timeline
46478c2ecf20Sopenharmony_ci	hf.write(devtl.html)
46488c2ecf20Sopenharmony_ci	hf.write('<div id="devicedetailtitle"></div>\n')
46498c2ecf20Sopenharmony_ci	hf.write('<div id="devicedetail" style="display:none;">\n')
46508c2ecf20Sopenharmony_ci	# draw the colored boxes for the device detail section
46518c2ecf20Sopenharmony_ci	for data in testruns:
46528c2ecf20Sopenharmony_ci		hf.write('<div id="devicedetail%d">\n' % data.testnumber)
46538c2ecf20Sopenharmony_ci		pscolor = 'linear-gradient(to top left, #ccc, #eee)'
46548c2ecf20Sopenharmony_ci		hf.write(devtl.html_phaselet.format('pre_suspend_process', \
46558c2ecf20Sopenharmony_ci			'0', '0', pscolor))
46568c2ecf20Sopenharmony_ci		for b in data.sortedPhases():
46578c2ecf20Sopenharmony_ci			phase = data.dmesg[b]
46588c2ecf20Sopenharmony_ci			length = phase['end']-phase['start']
46598c2ecf20Sopenharmony_ci			left = '%.3f' % (((phase['start']-t0)*100.0)/tTotal)
46608c2ecf20Sopenharmony_ci			width = '%.3f' % ((length*100.0)/tTotal)
46618c2ecf20Sopenharmony_ci			hf.write(devtl.html_phaselet.format(b, left, width, \
46628c2ecf20Sopenharmony_ci				data.dmesg[b]['color']))
46638c2ecf20Sopenharmony_ci		hf.write(devtl.html_phaselet.format('post_resume_process', \
46648c2ecf20Sopenharmony_ci			'0', '0', pscolor))
46658c2ecf20Sopenharmony_ci		if sysvals.suspendmode == 'command':
46668c2ecf20Sopenharmony_ci			hf.write(devtl.html_phaselet.format('cmdexec', '0', '0', pscolor))
46678c2ecf20Sopenharmony_ci		hf.write('</div>\n')
46688c2ecf20Sopenharmony_ci	hf.write('</div>\n')
46698c2ecf20Sopenharmony_ci
46708c2ecf20Sopenharmony_ci	# write the ftrace data (callgraph)
46718c2ecf20Sopenharmony_ci	if sysvals.cgtest >= 0 and len(testruns) > sysvals.cgtest:
46728c2ecf20Sopenharmony_ci		data = testruns[sysvals.cgtest]
46738c2ecf20Sopenharmony_ci	else:
46748c2ecf20Sopenharmony_ci		data = testruns[-1]
46758c2ecf20Sopenharmony_ci	if sysvals.usecallgraph:
46768c2ecf20Sopenharmony_ci		addCallgraphs(sysvals, hf, data)
46778c2ecf20Sopenharmony_ci
46788c2ecf20Sopenharmony_ci	# add the test log as a hidden div
46798c2ecf20Sopenharmony_ci	if sysvals.testlog and sysvals.logmsg:
46808c2ecf20Sopenharmony_ci		hf.write('<div id="testlog" style="display:none;">\n'+sysvals.logmsg+'</div>\n')
46818c2ecf20Sopenharmony_ci	# add the dmesg log as a hidden div
46828c2ecf20Sopenharmony_ci	if sysvals.dmesglog and sysvals.dmesgfile:
46838c2ecf20Sopenharmony_ci		hf.write('<div id="dmesglog" style="display:none;">\n')
46848c2ecf20Sopenharmony_ci		lf = sysvals.openlog(sysvals.dmesgfile, 'r')
46858c2ecf20Sopenharmony_ci		for line in lf:
46868c2ecf20Sopenharmony_ci			line = line.replace('<', '&lt').replace('>', '&gt')
46878c2ecf20Sopenharmony_ci			hf.write(line)
46888c2ecf20Sopenharmony_ci		lf.close()
46898c2ecf20Sopenharmony_ci		hf.write('</div>\n')
46908c2ecf20Sopenharmony_ci	# add the ftrace log as a hidden div
46918c2ecf20Sopenharmony_ci	if sysvals.ftracelog and sysvals.ftracefile:
46928c2ecf20Sopenharmony_ci		hf.write('<div id="ftracelog" style="display:none;">\n')
46938c2ecf20Sopenharmony_ci		lf = sysvals.openlog(sysvals.ftracefile, 'r')
46948c2ecf20Sopenharmony_ci		for line in lf:
46958c2ecf20Sopenharmony_ci			hf.write(line)
46968c2ecf20Sopenharmony_ci		lf.close()
46978c2ecf20Sopenharmony_ci		hf.write('</div>\n')
46988c2ecf20Sopenharmony_ci
46998c2ecf20Sopenharmony_ci	# write the footer and close
47008c2ecf20Sopenharmony_ci	addScriptCode(hf, testruns)
47018c2ecf20Sopenharmony_ci	hf.write('</body>\n</html>\n')
47028c2ecf20Sopenharmony_ci	hf.close()
47038c2ecf20Sopenharmony_ci	return True
47048c2ecf20Sopenharmony_ci
47058c2ecf20Sopenharmony_cidef addCSS(hf, sv, testcount=1, kerror=False, extra=''):
47068c2ecf20Sopenharmony_ci	kernel = sv.stamp['kernel']
47078c2ecf20Sopenharmony_ci	host = sv.hostname[0].upper()+sv.hostname[1:]
47088c2ecf20Sopenharmony_ci	mode = sv.suspendmode
47098c2ecf20Sopenharmony_ci	if sv.suspendmode in suspendmodename:
47108c2ecf20Sopenharmony_ci		mode = suspendmodename[sv.suspendmode]
47118c2ecf20Sopenharmony_ci	title = host+' '+mode+' '+kernel
47128c2ecf20Sopenharmony_ci
47138c2ecf20Sopenharmony_ci	# various format changes by flags
47148c2ecf20Sopenharmony_ci	cgchk = 'checked'
47158c2ecf20Sopenharmony_ci	cgnchk = 'not(:checked)'
47168c2ecf20Sopenharmony_ci	if sv.cgexp:
47178c2ecf20Sopenharmony_ci		cgchk = 'not(:checked)'
47188c2ecf20Sopenharmony_ci		cgnchk = 'checked'
47198c2ecf20Sopenharmony_ci
47208c2ecf20Sopenharmony_ci	hoverZ = 'z-index:8;'
47218c2ecf20Sopenharmony_ci	if sv.usedevsrc:
47228c2ecf20Sopenharmony_ci		hoverZ = ''
47238c2ecf20Sopenharmony_ci
47248c2ecf20Sopenharmony_ci	devlistpos = 'absolute'
47258c2ecf20Sopenharmony_ci	if testcount > 1:
47268c2ecf20Sopenharmony_ci		devlistpos = 'relative'
47278c2ecf20Sopenharmony_ci
47288c2ecf20Sopenharmony_ci	scaleTH = 20
47298c2ecf20Sopenharmony_ci	if kerror:
47308c2ecf20Sopenharmony_ci		scaleTH = 60
47318c2ecf20Sopenharmony_ci
47328c2ecf20Sopenharmony_ci	# write the html header first (html head, css code, up to body start)
47338c2ecf20Sopenharmony_ci	html_header = '<!DOCTYPE html>\n<html>\n<head>\n\
47348c2ecf20Sopenharmony_ci	<meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\
47358c2ecf20Sopenharmony_ci	<title>'+title+'</title>\n\
47368c2ecf20Sopenharmony_ci	<style type=\'text/css\'>\n\
47378c2ecf20Sopenharmony_ci		body {overflow-y:scroll;}\n\
47388c2ecf20Sopenharmony_ci		.stamp {width:100%;text-align:center;background:gray;line-height:30px;color:white;font:25px Arial;}\n\
47398c2ecf20Sopenharmony_ci		.stamp.sysinfo {font:10px Arial;}\n\
47408c2ecf20Sopenharmony_ci		.callgraph {margin-top:30px;box-shadow:5px 5px 20px black;}\n\
47418c2ecf20Sopenharmony_ci		.callgraph article * {padding-left:28px;}\n\
47428c2ecf20Sopenharmony_ci		h1 {color:black;font:bold 30px Times;}\n\
47438c2ecf20Sopenharmony_ci		t0 {color:black;font:bold 30px Times;}\n\
47448c2ecf20Sopenharmony_ci		t1 {color:black;font:30px Times;}\n\
47458c2ecf20Sopenharmony_ci		t2 {color:black;font:25px Times;}\n\
47468c2ecf20Sopenharmony_ci		t3 {color:black;font:20px Times;white-space:nowrap;}\n\
47478c2ecf20Sopenharmony_ci		t4 {color:black;font:bold 30px Times;line-height:60px;white-space:nowrap;}\n\
47488c2ecf20Sopenharmony_ci		cS {font:bold 13px Times;}\n\
47498c2ecf20Sopenharmony_ci		table {width:100%;}\n\
47508c2ecf20Sopenharmony_ci		.gray {background:rgba(80,80,80,0.1);}\n\
47518c2ecf20Sopenharmony_ci		.green {background:rgba(204,255,204,0.4);}\n\
47528c2ecf20Sopenharmony_ci		.purple {background:rgba(128,0,128,0.2);}\n\
47538c2ecf20Sopenharmony_ci		.yellow {background:rgba(255,255,204,0.4);}\n\
47548c2ecf20Sopenharmony_ci		.blue {background:rgba(169,208,245,0.4);}\n\
47558c2ecf20Sopenharmony_ci		.time1 {font:22px Arial;border:1px solid;}\n\
47568c2ecf20Sopenharmony_ci		.time2 {font:15px Arial;border-bottom:1px solid;border-left:1px solid;border-right:1px solid;}\n\
47578c2ecf20Sopenharmony_ci		.testfail {font:bold 22px Arial;color:red;border:1px dashed;}\n\
47588c2ecf20Sopenharmony_ci		td {text-align:center;}\n\
47598c2ecf20Sopenharmony_ci		r {color:#500000;font:15px Tahoma;}\n\
47608c2ecf20Sopenharmony_ci		n {color:#505050;font:15px Tahoma;}\n\
47618c2ecf20Sopenharmony_ci		.tdhl {color:red;}\n\
47628c2ecf20Sopenharmony_ci		.hide {display:none;}\n\
47638c2ecf20Sopenharmony_ci		.pf {display:none;}\n\
47648c2ecf20Sopenharmony_ci		.pf:'+cgchk+' + label {background:url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/><rect x="8" y="4" width="2" height="10" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\
47658c2ecf20Sopenharmony_ci		.pf:'+cgnchk+' ~ label {background:url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\
47668c2ecf20Sopenharmony_ci		.pf:'+cgchk+' ~ *:not(:nth-child(2)) {display:none;}\n\
47678c2ecf20Sopenharmony_ci		.zoombox {position:relative;width:100%;overflow-x:scroll;-webkit-user-select:none;-moz-user-select:none;user-select:none;}\n\
47688c2ecf20Sopenharmony_ci		.timeline {position:relative;font-size:14px;cursor:pointer;width:100%; overflow:hidden;background:linear-gradient(#cccccc, white);}\n\
47698c2ecf20Sopenharmony_ci		.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\
47708c2ecf20Sopenharmony_ci		.thread.ps {border-radius:3px;background:linear-gradient(to top, #ccc, #eee);}\n\
47718c2ecf20Sopenharmony_ci		.thread:hover {background:white;border:1px solid red;'+hoverZ+'}\n\
47728c2ecf20Sopenharmony_ci		.thread.sec,.thread.sec:hover {background:black;border:0;color:white;line-height:15px;font-size:10px;}\n\
47738c2ecf20Sopenharmony_ci		.hover {background:white;border:1px solid red;'+hoverZ+'}\n\
47748c2ecf20Sopenharmony_ci		.hover.sync {background:white;}\n\
47758c2ecf20Sopenharmony_ci		.hover.bg,.hover.kth,.hover.sync,.hover.ps {background:white;}\n\
47768c2ecf20Sopenharmony_ci		.jiffie {position:absolute;pointer-events: none;z-index:8;}\n\
47778c2ecf20Sopenharmony_ci		.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\
47788c2ecf20Sopenharmony_ci		.traceevent:hover {color:white;font-weight:bold;border:1px solid white;}\n\
47798c2ecf20Sopenharmony_ci		.phase {position:absolute;overflow:hidden;border:0px;text-align:center;}\n\
47808c2ecf20Sopenharmony_ci		.phaselet {float:left;overflow:hidden;border:0px;text-align:center;min-height:100px;font-size:24px;}\n\
47818c2ecf20Sopenharmony_ci		.t {position:absolute;line-height:'+('%d'%scaleTH)+'px;pointer-events:none;top:0;height:100%;border-right:1px solid black;z-index:6;}\n\
47828c2ecf20Sopenharmony_ci		.err {position:absolute;top:0%;height:100%;border-right:3px solid red;color:red;font:bold 14px Times;line-height:18px;}\n\
47838c2ecf20Sopenharmony_ci		.legend {position:relative; width:100%; height:40px; text-align:center;margin-bottom:20px}\n\
47848c2ecf20Sopenharmony_ci		.legend .square {position:absolute;cursor:pointer;top:10px; width:0px;height:20px;border:1px solid;padding-left:20px;}\n\
47858c2ecf20Sopenharmony_ci		button {height:40px;width:200px;margin-bottom:20px;margin-top:20px;font-size:24px;}\n\
47868c2ecf20Sopenharmony_ci		.btnfmt {position:relative;float:right;height:25px;width:auto;margin-top:3px;margin-bottom:0;font-size:10px;text-align:center;}\n\
47878c2ecf20Sopenharmony_ci		.devlist {position:'+devlistpos+';width:190px;}\n\
47888c2ecf20Sopenharmony_ci		a:link {color:white;text-decoration:none;}\n\
47898c2ecf20Sopenharmony_ci		a:visited {color:white;}\n\
47908c2ecf20Sopenharmony_ci		a:hover {color:white;}\n\
47918c2ecf20Sopenharmony_ci		a:active {color:white;}\n\
47928c2ecf20Sopenharmony_ci		.version {position:relative;float:left;color:white;font-size:10px;line-height:30px;margin-left:10px;}\n\
47938c2ecf20Sopenharmony_ci		#devicedetail {min-height:100px;box-shadow:5px 5px 20px black;}\n\
47948c2ecf20Sopenharmony_ci		.tblock {position:absolute;height:100%;background:#ddd;}\n\
47958c2ecf20Sopenharmony_ci		.tback {position:absolute;width:100%;background:linear-gradient(#ccc, #ddd);}\n\
47968c2ecf20Sopenharmony_ci		.bg {z-index:1;}\n\
47978c2ecf20Sopenharmony_ci'+extra+'\
47988c2ecf20Sopenharmony_ci	</style>\n</head>\n<body>\n'
47998c2ecf20Sopenharmony_ci	hf.write(html_header)
48008c2ecf20Sopenharmony_ci
48018c2ecf20Sopenharmony_ci# Function: addScriptCode
48028c2ecf20Sopenharmony_ci# Description:
48038c2ecf20Sopenharmony_ci#	 Adds the javascript code to the output html
48048c2ecf20Sopenharmony_ci# Arguments:
48058c2ecf20Sopenharmony_ci#	 hf: the open html file pointer
48068c2ecf20Sopenharmony_ci#	 testruns: array of Data objects from parseKernelLog or parseTraceLog
48078c2ecf20Sopenharmony_cidef addScriptCode(hf, testruns):
48088c2ecf20Sopenharmony_ci	t0 = testruns[0].start * 1000
48098c2ecf20Sopenharmony_ci	tMax = testruns[-1].end * 1000
48108c2ecf20Sopenharmony_ci	# create an array in javascript memory with the device details
48118c2ecf20Sopenharmony_ci	detail = '	var devtable = [];\n'
48128c2ecf20Sopenharmony_ci	for data in testruns:
48138c2ecf20Sopenharmony_ci		topo = data.deviceTopology()
48148c2ecf20Sopenharmony_ci		detail += '	devtable[%d] = "%s";\n' % (data.testnumber, topo)
48158c2ecf20Sopenharmony_ci	detail += '	var bounds = [%f,%f];\n' % (t0, tMax)
48168c2ecf20Sopenharmony_ci	# add the code which will manipulate the data in the browser
48178c2ecf20Sopenharmony_ci	script_code = \
48188c2ecf20Sopenharmony_ci	'<script type="text/javascript">\n'+detail+\
48198c2ecf20Sopenharmony_ci	'	var resolution = -1;\n'\
48208c2ecf20Sopenharmony_ci	'	var dragval = [0, 0];\n'\
48218c2ecf20Sopenharmony_ci	'	function redrawTimescale(t0, tMax, tS) {\n'\
48228c2ecf20Sopenharmony_ci	'		var rline = \'<div class="t" style="left:0;border-left:1px solid black;border-right:0;">\';\n'\
48238c2ecf20Sopenharmony_ci	'		var tTotal = tMax - t0;\n'\
48248c2ecf20Sopenharmony_ci	'		var list = document.getElementsByClassName("tblock");\n'\
48258c2ecf20Sopenharmony_ci	'		for (var i = 0; i < list.length; i++) {\n'\
48268c2ecf20Sopenharmony_ci	'			var timescale = list[i].getElementsByClassName("timescale")[0];\n'\
48278c2ecf20Sopenharmony_ci	'			var m0 = t0 + (tTotal*parseFloat(list[i].style.left)/100);\n'\
48288c2ecf20Sopenharmony_ci	'			var mTotal = tTotal*parseFloat(list[i].style.width)/100;\n'\
48298c2ecf20Sopenharmony_ci	'			var mMax = m0 + mTotal;\n'\
48308c2ecf20Sopenharmony_ci	'			var html = "";\n'\
48318c2ecf20Sopenharmony_ci	'			var divTotal = Math.floor(mTotal/tS) + 1;\n'\
48328c2ecf20Sopenharmony_ci	'			if(divTotal > 1000) continue;\n'\
48338c2ecf20Sopenharmony_ci	'			var divEdge = (mTotal - tS*(divTotal-1))*100/mTotal;\n'\
48348c2ecf20Sopenharmony_ci	'			var pos = 0.0, val = 0.0;\n'\
48358c2ecf20Sopenharmony_ci	'			for (var j = 0; j < divTotal; j++) {\n'\
48368c2ecf20Sopenharmony_ci	'				var htmlline = "";\n'\
48378c2ecf20Sopenharmony_ci	'				var mode = list[i].id[5];\n'\
48388c2ecf20Sopenharmony_ci	'				if(mode == "s") {\n'\
48398c2ecf20Sopenharmony_ci	'					pos = 100 - (((j)*tS*100)/mTotal) - divEdge;\n'\
48408c2ecf20Sopenharmony_ci	'					val = (j-divTotal+1)*tS;\n'\
48418c2ecf20Sopenharmony_ci	'					if(j == divTotal - 1)\n'\
48428c2ecf20Sopenharmony_ci	'						htmlline = \'<div class="t" style="right:\'+pos+\'%"><cS>S&rarr;</cS></div>\';\n'\
48438c2ecf20Sopenharmony_ci	'					else\n'\
48448c2ecf20Sopenharmony_ci	'						htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\
48458c2ecf20Sopenharmony_ci	'				} else {\n'\
48468c2ecf20Sopenharmony_ci	'					pos = 100 - (((j)*tS*100)/mTotal);\n'\
48478c2ecf20Sopenharmony_ci	'					val = (j)*tS;\n'\
48488c2ecf20Sopenharmony_ci	'					htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\
48498c2ecf20Sopenharmony_ci	'					if(j == 0)\n'\
48508c2ecf20Sopenharmony_ci	'						if(mode == "r")\n'\
48518c2ecf20Sopenharmony_ci	'							htmlline = rline+"<cS>&larr;R</cS></div>";\n'\
48528c2ecf20Sopenharmony_ci	'						else\n'\
48538c2ecf20Sopenharmony_ci	'							htmlline = rline+"<cS>0ms</div>";\n'\
48548c2ecf20Sopenharmony_ci	'				}\n'\
48558c2ecf20Sopenharmony_ci	'				html += htmlline;\n'\
48568c2ecf20Sopenharmony_ci	'			}\n'\
48578c2ecf20Sopenharmony_ci	'			timescale.innerHTML = html;\n'\
48588c2ecf20Sopenharmony_ci	'		}\n'\
48598c2ecf20Sopenharmony_ci	'	}\n'\
48608c2ecf20Sopenharmony_ci	'	function zoomTimeline() {\n'\
48618c2ecf20Sopenharmony_ci	'		var dmesg = document.getElementById("dmesg");\n'\
48628c2ecf20Sopenharmony_ci	'		var zoombox = document.getElementById("dmesgzoombox");\n'\
48638c2ecf20Sopenharmony_ci	'		var left = zoombox.scrollLeft;\n'\
48648c2ecf20Sopenharmony_ci	'		var val = parseFloat(dmesg.style.width);\n'\
48658c2ecf20Sopenharmony_ci	'		var newval = 100;\n'\
48668c2ecf20Sopenharmony_ci	'		var sh = window.outerWidth / 2;\n'\
48678c2ecf20Sopenharmony_ci	'		if(this.id == "zoomin") {\n'\
48688c2ecf20Sopenharmony_ci	'			newval = val * 1.2;\n'\
48698c2ecf20Sopenharmony_ci	'			if(newval > 910034) newval = 910034;\n'\
48708c2ecf20Sopenharmony_ci	'			dmesg.style.width = newval+"%";\n'\
48718c2ecf20Sopenharmony_ci	'			zoombox.scrollLeft = ((left + sh) * newval / val) - sh;\n'\
48728c2ecf20Sopenharmony_ci	'		} else if (this.id == "zoomout") {\n'\
48738c2ecf20Sopenharmony_ci	'			newval = val / 1.2;\n'\
48748c2ecf20Sopenharmony_ci	'			if(newval < 100) newval = 100;\n'\
48758c2ecf20Sopenharmony_ci	'			dmesg.style.width = newval+"%";\n'\
48768c2ecf20Sopenharmony_ci	'			zoombox.scrollLeft = ((left + sh) * newval / val) - sh;\n'\
48778c2ecf20Sopenharmony_ci	'		} else {\n'\
48788c2ecf20Sopenharmony_ci	'			zoombox.scrollLeft = 0;\n'\
48798c2ecf20Sopenharmony_ci	'			dmesg.style.width = "100%";\n'\
48808c2ecf20Sopenharmony_ci	'		}\n'\
48818c2ecf20Sopenharmony_ci	'		var tS = [10000, 5000, 2000, 1000, 500, 200, 100, 50, 20, 10, 5, 2, 1];\n'\
48828c2ecf20Sopenharmony_ci	'		var t0 = bounds[0];\n'\
48838c2ecf20Sopenharmony_ci	'		var tMax = bounds[1];\n'\
48848c2ecf20Sopenharmony_ci	'		var tTotal = tMax - t0;\n'\
48858c2ecf20Sopenharmony_ci	'		var wTotal = tTotal * 100.0 / newval;\n'\
48868c2ecf20Sopenharmony_ci	'		var idx = 7*window.innerWidth/1100;\n'\
48878c2ecf20Sopenharmony_ci	'		for(var i = 0; (i < tS.length)&&((wTotal / tS[i]) < idx); i++);\n'\
48888c2ecf20Sopenharmony_ci	'		if(i >= tS.length) i = tS.length - 1;\n'\
48898c2ecf20Sopenharmony_ci	'		if(tS[i] == resolution) return;\n'\
48908c2ecf20Sopenharmony_ci	'		resolution = tS[i];\n'\
48918c2ecf20Sopenharmony_ci	'		redrawTimescale(t0, tMax, tS[i]);\n'\
48928c2ecf20Sopenharmony_ci	'	}\n'\
48938c2ecf20Sopenharmony_ci	'	function deviceName(title) {\n'\
48948c2ecf20Sopenharmony_ci	'		var name = title.slice(0, title.indexOf(" ("));\n'\
48958c2ecf20Sopenharmony_ci	'		return name;\n'\
48968c2ecf20Sopenharmony_ci	'	}\n'\
48978c2ecf20Sopenharmony_ci	'	function deviceHover() {\n'\
48988c2ecf20Sopenharmony_ci	'		var name = deviceName(this.title);\n'\
48998c2ecf20Sopenharmony_ci	'		var dmesg = document.getElementById("dmesg");\n'\
49008c2ecf20Sopenharmony_ci	'		var dev = dmesg.getElementsByClassName("thread");\n'\
49018c2ecf20Sopenharmony_ci	'		var cpu = -1;\n'\
49028c2ecf20Sopenharmony_ci	'		if(name.match("CPU_ON\[[0-9]*\]"))\n'\
49038c2ecf20Sopenharmony_ci	'			cpu = parseInt(name.slice(7));\n'\
49048c2ecf20Sopenharmony_ci	'		else if(name.match("CPU_OFF\[[0-9]*\]"))\n'\
49058c2ecf20Sopenharmony_ci	'			cpu = parseInt(name.slice(8));\n'\
49068c2ecf20Sopenharmony_ci	'		for (var i = 0; i < dev.length; i++) {\n'\
49078c2ecf20Sopenharmony_ci	'			dname = deviceName(dev[i].title);\n'\
49088c2ecf20Sopenharmony_ci	'			var cname = dev[i].className.slice(dev[i].className.indexOf("thread"));\n'\
49098c2ecf20Sopenharmony_ci	'			if((cpu >= 0 && dname.match("CPU_O[NF]*\\\[*"+cpu+"\\\]")) ||\n'\
49108c2ecf20Sopenharmony_ci	'				(name == dname))\n'\
49118c2ecf20Sopenharmony_ci	'			{\n'\
49128c2ecf20Sopenharmony_ci	'				dev[i].className = "hover "+cname;\n'\
49138c2ecf20Sopenharmony_ci	'			} else {\n'\
49148c2ecf20Sopenharmony_ci	'				dev[i].className = cname;\n'\
49158c2ecf20Sopenharmony_ci	'			}\n'\
49168c2ecf20Sopenharmony_ci	'		}\n'\
49178c2ecf20Sopenharmony_ci	'	}\n'\
49188c2ecf20Sopenharmony_ci	'	function deviceUnhover() {\n'\
49198c2ecf20Sopenharmony_ci	'		var dmesg = document.getElementById("dmesg");\n'\
49208c2ecf20Sopenharmony_ci	'		var dev = dmesg.getElementsByClassName("thread");\n'\
49218c2ecf20Sopenharmony_ci	'		for (var i = 0; i < dev.length; i++) {\n'\
49228c2ecf20Sopenharmony_ci	'			dev[i].className = dev[i].className.slice(dev[i].className.indexOf("thread"));\n'\
49238c2ecf20Sopenharmony_ci	'		}\n'\
49248c2ecf20Sopenharmony_ci	'	}\n'\
49258c2ecf20Sopenharmony_ci	'	function deviceTitle(title, total, cpu) {\n'\
49268c2ecf20Sopenharmony_ci	'		var prefix = "Total";\n'\
49278c2ecf20Sopenharmony_ci	'		if(total.length > 3) {\n'\
49288c2ecf20Sopenharmony_ci	'			prefix = "Average";\n'\
49298c2ecf20Sopenharmony_ci	'			total[1] = (total[1]+total[3])/2;\n'\
49308c2ecf20Sopenharmony_ci	'			total[2] = (total[2]+total[4])/2;\n'\
49318c2ecf20Sopenharmony_ci	'		}\n'\
49328c2ecf20Sopenharmony_ci	'		var devtitle = document.getElementById("devicedetailtitle");\n'\
49338c2ecf20Sopenharmony_ci	'		var name = deviceName(title);\n'\
49348c2ecf20Sopenharmony_ci	'		if(cpu >= 0) name = "CPU"+cpu;\n'\
49358c2ecf20Sopenharmony_ci	'		var driver = "";\n'\
49368c2ecf20Sopenharmony_ci	'		var tS = "<t2>(</t2>";\n'\
49378c2ecf20Sopenharmony_ci	'		var tR = "<t2>)</t2>";\n'\
49388c2ecf20Sopenharmony_ci	'		if(total[1] > 0)\n'\
49398c2ecf20Sopenharmony_ci	'			tS = "<t2>("+prefix+" Suspend:</t2><t0> "+total[1].toFixed(3)+" ms</t0> ";\n'\
49408c2ecf20Sopenharmony_ci	'		if(total[2] > 0)\n'\
49418c2ecf20Sopenharmony_ci	'			tR = " <t2>"+prefix+" Resume:</t2><t0> "+total[2].toFixed(3)+" ms<t2>)</t2></t0>";\n'\
49428c2ecf20Sopenharmony_ci	'		var s = title.indexOf("{");\n'\
49438c2ecf20Sopenharmony_ci	'		var e = title.indexOf("}");\n'\
49448c2ecf20Sopenharmony_ci	'		if((s >= 0) && (e >= 0))\n'\
49458c2ecf20Sopenharmony_ci	'			driver = title.slice(s+1, e) + " <t1>@</t1> ";\n'\
49468c2ecf20Sopenharmony_ci	'		if(total[1] > 0 && total[2] > 0)\n'\
49478c2ecf20Sopenharmony_ci	'			devtitle.innerHTML = "<t0>"+driver+name+"</t0> "+tS+tR;\n'\
49488c2ecf20Sopenharmony_ci	'		else\n'\
49498c2ecf20Sopenharmony_ci	'			devtitle.innerHTML = "<t0>"+title+"</t0>";\n'\
49508c2ecf20Sopenharmony_ci	'		return name;\n'\
49518c2ecf20Sopenharmony_ci	'	}\n'\
49528c2ecf20Sopenharmony_ci	'	function deviceDetail() {\n'\
49538c2ecf20Sopenharmony_ci	'		var devinfo = document.getElementById("devicedetail");\n'\
49548c2ecf20Sopenharmony_ci	'		devinfo.style.display = "block";\n'\
49558c2ecf20Sopenharmony_ci	'		var name = deviceName(this.title);\n'\
49568c2ecf20Sopenharmony_ci	'		var cpu = -1;\n'\
49578c2ecf20Sopenharmony_ci	'		if(name.match("CPU_ON\[[0-9]*\]"))\n'\
49588c2ecf20Sopenharmony_ci	'			cpu = parseInt(name.slice(7));\n'\
49598c2ecf20Sopenharmony_ci	'		else if(name.match("CPU_OFF\[[0-9]*\]"))\n'\
49608c2ecf20Sopenharmony_ci	'			cpu = parseInt(name.slice(8));\n'\
49618c2ecf20Sopenharmony_ci	'		var dmesg = document.getElementById("dmesg");\n'\
49628c2ecf20Sopenharmony_ci	'		var dev = dmesg.getElementsByClassName("thread");\n'\
49638c2ecf20Sopenharmony_ci	'		var idlist = [];\n'\
49648c2ecf20Sopenharmony_ci	'		var pdata = [[]];\n'\
49658c2ecf20Sopenharmony_ci	'		if(document.getElementById("devicedetail1"))\n'\
49668c2ecf20Sopenharmony_ci	'			pdata = [[], []];\n'\
49678c2ecf20Sopenharmony_ci	'		var pd = pdata[0];\n'\
49688c2ecf20Sopenharmony_ci	'		var total = [0.0, 0.0, 0.0];\n'\
49698c2ecf20Sopenharmony_ci	'		for (var i = 0; i < dev.length; i++) {\n'\
49708c2ecf20Sopenharmony_ci	'			dname = deviceName(dev[i].title);\n'\
49718c2ecf20Sopenharmony_ci	'			if((cpu >= 0 && dname.match("CPU_O[NF]*\\\[*"+cpu+"\\\]")) ||\n'\
49728c2ecf20Sopenharmony_ci	'				(name == dname))\n'\
49738c2ecf20Sopenharmony_ci	'			{\n'\
49748c2ecf20Sopenharmony_ci	'				idlist[idlist.length] = dev[i].id;\n'\
49758c2ecf20Sopenharmony_ci	'				var tidx = 1;\n'\
49768c2ecf20Sopenharmony_ci	'				if(dev[i].id[0] == "a") {\n'\
49778c2ecf20Sopenharmony_ci	'					pd = pdata[0];\n'\
49788c2ecf20Sopenharmony_ci	'				} else {\n'\
49798c2ecf20Sopenharmony_ci	'					if(pdata.length == 1) pdata[1] = [];\n'\
49808c2ecf20Sopenharmony_ci	'					if(total.length == 3) total[3]=total[4]=0.0;\n'\
49818c2ecf20Sopenharmony_ci	'					pd = pdata[1];\n'\
49828c2ecf20Sopenharmony_ci	'					tidx = 3;\n'\
49838c2ecf20Sopenharmony_ci	'				}\n'\
49848c2ecf20Sopenharmony_ci	'				var info = dev[i].title.split(" ");\n'\
49858c2ecf20Sopenharmony_ci	'				var pname = info[info.length-1];\n'\
49868c2ecf20Sopenharmony_ci	'				pd[pname] = parseFloat(info[info.length-3].slice(1));\n'\
49878c2ecf20Sopenharmony_ci	'				total[0] += pd[pname];\n'\
49888c2ecf20Sopenharmony_ci	'				if(pname.indexOf("suspend") >= 0)\n'\
49898c2ecf20Sopenharmony_ci	'					total[tidx] += pd[pname];\n'\
49908c2ecf20Sopenharmony_ci	'				else\n'\
49918c2ecf20Sopenharmony_ci	'					total[tidx+1] += pd[pname];\n'\
49928c2ecf20Sopenharmony_ci	'			}\n'\
49938c2ecf20Sopenharmony_ci	'		}\n'\
49948c2ecf20Sopenharmony_ci	'		var devname = deviceTitle(this.title, total, cpu);\n'\
49958c2ecf20Sopenharmony_ci	'		var left = 0.0;\n'\
49968c2ecf20Sopenharmony_ci	'		for (var t = 0; t < pdata.length; t++) {\n'\
49978c2ecf20Sopenharmony_ci	'			pd = pdata[t];\n'\
49988c2ecf20Sopenharmony_ci	'			devinfo = document.getElementById("devicedetail"+t);\n'\
49998c2ecf20Sopenharmony_ci	'			var phases = devinfo.getElementsByClassName("phaselet");\n'\
50008c2ecf20Sopenharmony_ci	'			for (var i = 0; i < phases.length; i++) {\n'\
50018c2ecf20Sopenharmony_ci	'				if(phases[i].id in pd) {\n'\
50028c2ecf20Sopenharmony_ci	'					var w = 100.0*pd[phases[i].id]/total[0];\n'\
50038c2ecf20Sopenharmony_ci	'					var fs = 32;\n'\
50048c2ecf20Sopenharmony_ci	'					if(w < 8) fs = 4*w | 0;\n'\
50058c2ecf20Sopenharmony_ci	'					var fs2 = fs*3/4;\n'\
50068c2ecf20Sopenharmony_ci	'					phases[i].style.width = w+"%";\n'\
50078c2ecf20Sopenharmony_ci	'					phases[i].style.left = left+"%";\n'\
50088c2ecf20Sopenharmony_ci	'					phases[i].title = phases[i].id+" "+pd[phases[i].id]+" ms";\n'\
50098c2ecf20Sopenharmony_ci	'					left += w;\n'\
50108c2ecf20Sopenharmony_ci	'					var time = "<t4 style=\\"font-size:"+fs+"px\\">"+pd[phases[i].id]+" ms<br></t4>";\n'\
50118c2ecf20Sopenharmony_ci	'					var pname = "<t3 style=\\"font-size:"+fs2+"px\\">"+phases[i].id.replace(new RegExp("_", "g"), " ")+"</t3>";\n'\
50128c2ecf20Sopenharmony_ci	'					phases[i].innerHTML = time+pname;\n'\
50138c2ecf20Sopenharmony_ci	'				} else {\n'\
50148c2ecf20Sopenharmony_ci	'					phases[i].style.width = "0%";\n'\
50158c2ecf20Sopenharmony_ci	'					phases[i].style.left = left+"%";\n'\
50168c2ecf20Sopenharmony_ci	'				}\n'\
50178c2ecf20Sopenharmony_ci	'			}\n'\
50188c2ecf20Sopenharmony_ci	'		}\n'\
50198c2ecf20Sopenharmony_ci	'		if(typeof devstats !== \'undefined\')\n'\
50208c2ecf20Sopenharmony_ci	'			callDetail(this.id, this.title);\n'\
50218c2ecf20Sopenharmony_ci	'		var cglist = document.getElementById("callgraphs");\n'\
50228c2ecf20Sopenharmony_ci	'		if(!cglist) return;\n'\
50238c2ecf20Sopenharmony_ci	'		var cg = cglist.getElementsByClassName("atop");\n'\
50248c2ecf20Sopenharmony_ci	'		if(cg.length < 10) return;\n'\
50258c2ecf20Sopenharmony_ci	'		for (var i = 0; i < cg.length; i++) {\n'\
50268c2ecf20Sopenharmony_ci	'			cgid = cg[i].id.split("x")[0]\n'\
50278c2ecf20Sopenharmony_ci	'			if(idlist.indexOf(cgid) >= 0) {\n'\
50288c2ecf20Sopenharmony_ci	'				cg[i].style.display = "block";\n'\
50298c2ecf20Sopenharmony_ci	'			} else {\n'\
50308c2ecf20Sopenharmony_ci	'				cg[i].style.display = "none";\n'\
50318c2ecf20Sopenharmony_ci	'			}\n'\
50328c2ecf20Sopenharmony_ci	'		}\n'\
50338c2ecf20Sopenharmony_ci	'	}\n'\
50348c2ecf20Sopenharmony_ci	'	function callDetail(devid, devtitle) {\n'\
50358c2ecf20Sopenharmony_ci	'		if(!(devid in devstats) || devstats[devid].length < 1)\n'\
50368c2ecf20Sopenharmony_ci	'			return;\n'\
50378c2ecf20Sopenharmony_ci	'		var list = devstats[devid];\n'\
50388c2ecf20Sopenharmony_ci	'		var tmp = devtitle.split(" ");\n'\
50398c2ecf20Sopenharmony_ci	'		var name = tmp[0], phase = tmp[tmp.length-1];\n'\
50408c2ecf20Sopenharmony_ci	'		var dd = document.getElementById(phase);\n'\
50418c2ecf20Sopenharmony_ci	'		var total = parseFloat(tmp[1].slice(1));\n'\
50428c2ecf20Sopenharmony_ci	'		var mlist = [];\n'\
50438c2ecf20Sopenharmony_ci	'		var maxlen = 0;\n'\
50448c2ecf20Sopenharmony_ci	'		var info = []\n'\
50458c2ecf20Sopenharmony_ci	'		for(var i in list) {\n'\
50468c2ecf20Sopenharmony_ci	'			if(list[i][0] == "@") {\n'\
50478c2ecf20Sopenharmony_ci	'				info = list[i].split("|");\n'\
50488c2ecf20Sopenharmony_ci	'				continue;\n'\
50498c2ecf20Sopenharmony_ci	'			}\n'\
50508c2ecf20Sopenharmony_ci	'			var tmp = list[i].split("|");\n'\
50518c2ecf20Sopenharmony_ci	'			var t = parseFloat(tmp[0]), f = tmp[1], c = parseInt(tmp[2]);\n'\
50528c2ecf20Sopenharmony_ci	'			var p = (t*100.0/total).toFixed(2);\n'\
50538c2ecf20Sopenharmony_ci	'			mlist[mlist.length] = [f, c, t.toFixed(2), p+"%"];\n'\
50548c2ecf20Sopenharmony_ci	'			if(f.length > maxlen)\n'\
50558c2ecf20Sopenharmony_ci	'				maxlen = f.length;\n'\
50568c2ecf20Sopenharmony_ci	'		}\n'\
50578c2ecf20Sopenharmony_ci	'		var pad = 5;\n'\
50588c2ecf20Sopenharmony_ci	'		if(mlist.length == 0) pad = 30;\n'\
50598c2ecf20Sopenharmony_ci	'		var html = \'<div style="padding-top:\'+pad+\'px"><t3> <b>\'+name+\':</b>\';\n'\
50608c2ecf20Sopenharmony_ci	'		if(info.length > 2)\n'\
50618c2ecf20Sopenharmony_ci	'			html += " start=<b>"+info[1]+"</b>, end=<b>"+info[2]+"</b>";\n'\
50628c2ecf20Sopenharmony_ci	'		if(info.length > 3)\n'\
50638c2ecf20Sopenharmony_ci	'			html += ", length<i>(w/o overhead)</i>=<b>"+info[3]+" ms</b>";\n'\
50648c2ecf20Sopenharmony_ci	'		if(info.length > 4)\n'\
50658c2ecf20Sopenharmony_ci	'			html += ", return=<b>"+info[4]+"</b>";\n'\
50668c2ecf20Sopenharmony_ci	'		html += "</t3></div>";\n'\
50678c2ecf20Sopenharmony_ci	'		if(mlist.length > 0) {\n'\
50688c2ecf20Sopenharmony_ci	'			html += \'<table class=fstat style="padding-top:\'+(maxlen*5)+\'px;"><tr><th>Function</th>\';\n'\
50698c2ecf20Sopenharmony_ci	'			for(var i in mlist)\n'\
50708c2ecf20Sopenharmony_ci	'				html += "<td class=vt>"+mlist[i][0]+"</td>";\n'\
50718c2ecf20Sopenharmony_ci	'			html += "</tr><tr><th>Calls</th>";\n'\
50728c2ecf20Sopenharmony_ci	'			for(var i in mlist)\n'\
50738c2ecf20Sopenharmony_ci	'				html += "<td>"+mlist[i][1]+"</td>";\n'\
50748c2ecf20Sopenharmony_ci	'			html += "</tr><tr><th>Time(ms)</th>";\n'\
50758c2ecf20Sopenharmony_ci	'			for(var i in mlist)\n'\
50768c2ecf20Sopenharmony_ci	'				html += "<td>"+mlist[i][2]+"</td>";\n'\
50778c2ecf20Sopenharmony_ci	'			html += "</tr><tr><th>Percent</th>";\n'\
50788c2ecf20Sopenharmony_ci	'			for(var i in mlist)\n'\
50798c2ecf20Sopenharmony_ci	'				html += "<td>"+mlist[i][3]+"</td>";\n'\
50808c2ecf20Sopenharmony_ci	'			html += "</tr></table>";\n'\
50818c2ecf20Sopenharmony_ci	'		}\n'\
50828c2ecf20Sopenharmony_ci	'		dd.innerHTML = html;\n'\
50838c2ecf20Sopenharmony_ci	'		var height = (maxlen*5)+100;\n'\
50848c2ecf20Sopenharmony_ci	'		dd.style.height = height+"px";\n'\
50858c2ecf20Sopenharmony_ci	'		document.getElementById("devicedetail").style.height = height+"px";\n'\
50868c2ecf20Sopenharmony_ci	'	}\n'\
50878c2ecf20Sopenharmony_ci	'	function callSelect() {\n'\
50888c2ecf20Sopenharmony_ci	'		var cglist = document.getElementById("callgraphs");\n'\
50898c2ecf20Sopenharmony_ci	'		if(!cglist) return;\n'\
50908c2ecf20Sopenharmony_ci	'		var cg = cglist.getElementsByClassName("atop");\n'\
50918c2ecf20Sopenharmony_ci	'		for (var i = 0; i < cg.length; i++) {\n'\
50928c2ecf20Sopenharmony_ci	'			if(this.id == cg[i].id) {\n'\
50938c2ecf20Sopenharmony_ci	'				cg[i].style.display = "block";\n'\
50948c2ecf20Sopenharmony_ci	'			} else {\n'\
50958c2ecf20Sopenharmony_ci	'				cg[i].style.display = "none";\n'\
50968c2ecf20Sopenharmony_ci	'			}\n'\
50978c2ecf20Sopenharmony_ci	'		}\n'\
50988c2ecf20Sopenharmony_ci	'	}\n'\
50998c2ecf20Sopenharmony_ci	'	function devListWindow(e) {\n'\
51008c2ecf20Sopenharmony_ci	'		var win = window.open();\n'\
51018c2ecf20Sopenharmony_ci	'		var html = "<title>"+e.target.innerHTML+"</title>"+\n'\
51028c2ecf20Sopenharmony_ci	'			"<style type=\\"text/css\\">"+\n'\
51038c2ecf20Sopenharmony_ci	'			"   ul {list-style-type:circle;padding-left:10px;margin-left:10px;}"+\n'\
51048c2ecf20Sopenharmony_ci	'			"</style>"\n'\
51058c2ecf20Sopenharmony_ci	'		var dt = devtable[0];\n'\
51068c2ecf20Sopenharmony_ci	'		if(e.target.id != "devlist1")\n'\
51078c2ecf20Sopenharmony_ci	'			dt = devtable[1];\n'\
51088c2ecf20Sopenharmony_ci	'		win.document.write(html+dt);\n'\
51098c2ecf20Sopenharmony_ci	'	}\n'\
51108c2ecf20Sopenharmony_ci	'	function errWindow() {\n'\
51118c2ecf20Sopenharmony_ci	'		var range = this.id.split("_");\n'\
51128c2ecf20Sopenharmony_ci	'		var idx1 = parseInt(range[0]);\n'\
51138c2ecf20Sopenharmony_ci	'		var idx2 = parseInt(range[1]);\n'\
51148c2ecf20Sopenharmony_ci	'		var win = window.open();\n'\
51158c2ecf20Sopenharmony_ci	'		var log = document.getElementById("dmesglog");\n'\
51168c2ecf20Sopenharmony_ci	'		var title = "<title>dmesg log</title>";\n'\
51178c2ecf20Sopenharmony_ci	'		var text = log.innerHTML.split("\\n");\n'\
51188c2ecf20Sopenharmony_ci	'		var html = "";\n'\
51198c2ecf20Sopenharmony_ci	'		for(var i = 0; i < text.length; i++) {\n'\
51208c2ecf20Sopenharmony_ci	'			if(i == idx1) {\n'\
51218c2ecf20Sopenharmony_ci	'				html += "<e id=target>"+text[i]+"</e>\\n";\n'\
51228c2ecf20Sopenharmony_ci	'			} else if(i > idx1 && i <= idx2) {\n'\
51238c2ecf20Sopenharmony_ci	'				html += "<e>"+text[i]+"</e>\\n";\n'\
51248c2ecf20Sopenharmony_ci	'			} else {\n'\
51258c2ecf20Sopenharmony_ci	'				html += text[i]+"\\n";\n'\
51268c2ecf20Sopenharmony_ci	'			}\n'\
51278c2ecf20Sopenharmony_ci	'		}\n'\
51288c2ecf20Sopenharmony_ci	'		win.document.write("<style>e{color:red}</style>"+title+"<pre>"+html+"</pre>");\n'\
51298c2ecf20Sopenharmony_ci	'		win.location.hash = "#target";\n'\
51308c2ecf20Sopenharmony_ci	'		win.document.close();\n'\
51318c2ecf20Sopenharmony_ci	'	}\n'\
51328c2ecf20Sopenharmony_ci	'	function logWindow(e) {\n'\
51338c2ecf20Sopenharmony_ci	'		var name = e.target.id.slice(4);\n'\
51348c2ecf20Sopenharmony_ci	'		var win = window.open();\n'\
51358c2ecf20Sopenharmony_ci	'		var log = document.getElementById(name+"log");\n'\
51368c2ecf20Sopenharmony_ci	'		var title = "<title>"+document.title.split(" ")[0]+" "+name+" log</title>";\n'\
51378c2ecf20Sopenharmony_ci	'		win.document.write(title+"<pre>"+log.innerHTML+"</pre>");\n'\
51388c2ecf20Sopenharmony_ci	'		win.document.close();\n'\
51398c2ecf20Sopenharmony_ci	'	}\n'\
51408c2ecf20Sopenharmony_ci	'	function onMouseDown(e) {\n'\
51418c2ecf20Sopenharmony_ci	'		dragval[0] = e.clientX;\n'\
51428c2ecf20Sopenharmony_ci	'		dragval[1] = document.getElementById("dmesgzoombox").scrollLeft;\n'\
51438c2ecf20Sopenharmony_ci	'		document.onmousemove = onMouseMove;\n'\
51448c2ecf20Sopenharmony_ci	'	}\n'\
51458c2ecf20Sopenharmony_ci	'	function onMouseMove(e) {\n'\
51468c2ecf20Sopenharmony_ci	'		var zoombox = document.getElementById("dmesgzoombox");\n'\
51478c2ecf20Sopenharmony_ci	'		zoombox.scrollLeft = dragval[1] + dragval[0] - e.clientX;\n'\
51488c2ecf20Sopenharmony_ci	'	}\n'\
51498c2ecf20Sopenharmony_ci	'	function onMouseUp(e) {\n'\
51508c2ecf20Sopenharmony_ci	'		document.onmousemove = null;\n'\
51518c2ecf20Sopenharmony_ci	'	}\n'\
51528c2ecf20Sopenharmony_ci	'	function onKeyPress(e) {\n'\
51538c2ecf20Sopenharmony_ci	'		var c = e.charCode;\n'\
51548c2ecf20Sopenharmony_ci	'		if(c != 42 && c != 43 && c != 45) return;\n'\
51558c2ecf20Sopenharmony_ci	'		var click = document.createEvent("Events");\n'\
51568c2ecf20Sopenharmony_ci	'		click.initEvent("click", true, false);\n'\
51578c2ecf20Sopenharmony_ci	'		if(c == 43)  \n'\
51588c2ecf20Sopenharmony_ci	'			document.getElementById("zoomin").dispatchEvent(click);\n'\
51598c2ecf20Sopenharmony_ci	'		else if(c == 45)\n'\
51608c2ecf20Sopenharmony_ci	'			document.getElementById("zoomout").dispatchEvent(click);\n'\
51618c2ecf20Sopenharmony_ci	'		else if(c == 42)\n'\
51628c2ecf20Sopenharmony_ci	'			document.getElementById("zoomdef").dispatchEvent(click);\n'\
51638c2ecf20Sopenharmony_ci	'	}\n'\
51648c2ecf20Sopenharmony_ci	'	window.addEventListener("resize", function () {zoomTimeline();});\n'\
51658c2ecf20Sopenharmony_ci	'	window.addEventListener("load", function () {\n'\
51668c2ecf20Sopenharmony_ci	'		var dmesg = document.getElementById("dmesg");\n'\
51678c2ecf20Sopenharmony_ci	'		dmesg.style.width = "100%"\n'\
51688c2ecf20Sopenharmony_ci	'		dmesg.onmousedown = onMouseDown;\n'\
51698c2ecf20Sopenharmony_ci	'		document.onmouseup = onMouseUp;\n'\
51708c2ecf20Sopenharmony_ci	'		document.onkeypress = onKeyPress;\n'\
51718c2ecf20Sopenharmony_ci	'		document.getElementById("zoomin").onclick = zoomTimeline;\n'\
51728c2ecf20Sopenharmony_ci	'		document.getElementById("zoomout").onclick = zoomTimeline;\n'\
51738c2ecf20Sopenharmony_ci	'		document.getElementById("zoomdef").onclick = zoomTimeline;\n'\
51748c2ecf20Sopenharmony_ci	'		var list = document.getElementsByClassName("err");\n'\
51758c2ecf20Sopenharmony_ci	'		for (var i = 0; i < list.length; i++)\n'\
51768c2ecf20Sopenharmony_ci	'			list[i].onclick = errWindow;\n'\
51778c2ecf20Sopenharmony_ci	'		var list = document.getElementsByClassName("logbtn");\n'\
51788c2ecf20Sopenharmony_ci	'		for (var i = 0; i < list.length; i++)\n'\
51798c2ecf20Sopenharmony_ci	'			list[i].onclick = logWindow;\n'\
51808c2ecf20Sopenharmony_ci	'		list = document.getElementsByClassName("devlist");\n'\
51818c2ecf20Sopenharmony_ci	'		for (var i = 0; i < list.length; i++)\n'\
51828c2ecf20Sopenharmony_ci	'			list[i].onclick = devListWindow;\n'\
51838c2ecf20Sopenharmony_ci	'		var dev = dmesg.getElementsByClassName("thread");\n'\
51848c2ecf20Sopenharmony_ci	'		for (var i = 0; i < dev.length; i++) {\n'\
51858c2ecf20Sopenharmony_ci	'			dev[i].onclick = deviceDetail;\n'\
51868c2ecf20Sopenharmony_ci	'			dev[i].onmouseover = deviceHover;\n'\
51878c2ecf20Sopenharmony_ci	'			dev[i].onmouseout = deviceUnhover;\n'\
51888c2ecf20Sopenharmony_ci	'		}\n'\
51898c2ecf20Sopenharmony_ci	'		var dev = dmesg.getElementsByClassName("srccall");\n'\
51908c2ecf20Sopenharmony_ci	'		for (var i = 0; i < dev.length; i++)\n'\
51918c2ecf20Sopenharmony_ci	'			dev[i].onclick = callSelect;\n'\
51928c2ecf20Sopenharmony_ci	'		zoomTimeline();\n'\
51938c2ecf20Sopenharmony_ci	'	});\n'\
51948c2ecf20Sopenharmony_ci	'</script>\n'
51958c2ecf20Sopenharmony_ci	hf.write(script_code);
51968c2ecf20Sopenharmony_ci
51978c2ecf20Sopenharmony_cidef setRuntimeSuspend(before=True):
51988c2ecf20Sopenharmony_ci	global sysvals
51998c2ecf20Sopenharmony_ci	sv = sysvals
52008c2ecf20Sopenharmony_ci	if sv.rs == 0:
52018c2ecf20Sopenharmony_ci		return
52028c2ecf20Sopenharmony_ci	if before:
52038c2ecf20Sopenharmony_ci		# runtime suspend disable or enable
52048c2ecf20Sopenharmony_ci		if sv.rs > 0:
52058c2ecf20Sopenharmony_ci			sv.rstgt, sv.rsval, sv.rsdir = 'on', 'auto', 'enabled'
52068c2ecf20Sopenharmony_ci		else:
52078c2ecf20Sopenharmony_ci			sv.rstgt, sv.rsval, sv.rsdir = 'auto', 'on', 'disabled'
52088c2ecf20Sopenharmony_ci		pprint('CONFIGURING RUNTIME SUSPEND...')
52098c2ecf20Sopenharmony_ci		sv.rslist = deviceInfo(sv.rstgt)
52108c2ecf20Sopenharmony_ci		for i in sv.rslist:
52118c2ecf20Sopenharmony_ci			sv.setVal(sv.rsval, i)
52128c2ecf20Sopenharmony_ci		pprint('runtime suspend %s on all devices (%d changed)' % (sv.rsdir, len(sv.rslist)))
52138c2ecf20Sopenharmony_ci		pprint('waiting 5 seconds...')
52148c2ecf20Sopenharmony_ci		time.sleep(5)
52158c2ecf20Sopenharmony_ci	else:
52168c2ecf20Sopenharmony_ci		# runtime suspend re-enable or re-disable
52178c2ecf20Sopenharmony_ci		for i in sv.rslist:
52188c2ecf20Sopenharmony_ci			sv.setVal(sv.rstgt, i)
52198c2ecf20Sopenharmony_ci		pprint('runtime suspend settings restored on %d devices' % len(sv.rslist))
52208c2ecf20Sopenharmony_ci
52218c2ecf20Sopenharmony_ci# Function: executeSuspend
52228c2ecf20Sopenharmony_ci# Description:
52238c2ecf20Sopenharmony_ci#	 Execute system suspend through the sysfs interface, then copy the output
52248c2ecf20Sopenharmony_ci#	 dmesg and ftrace files to the test output directory.
52258c2ecf20Sopenharmony_cidef executeSuspend(quiet=False):
52268c2ecf20Sopenharmony_ci	pm = ProcessMonitor()
52278c2ecf20Sopenharmony_ci	tp = sysvals.tpath
52288c2ecf20Sopenharmony_ci	if sysvals.wifi:
52298c2ecf20Sopenharmony_ci		wifi = sysvals.checkWifi()
52308c2ecf20Sopenharmony_ci	testdata = []
52318c2ecf20Sopenharmony_ci	# run these commands to prepare the system for suspend
52328c2ecf20Sopenharmony_ci	if sysvals.display:
52338c2ecf20Sopenharmony_ci		if not quiet:
52348c2ecf20Sopenharmony_ci			pprint('SET DISPLAY TO %s' % sysvals.display.upper())
52358c2ecf20Sopenharmony_ci		displayControl(sysvals.display)
52368c2ecf20Sopenharmony_ci		time.sleep(1)
52378c2ecf20Sopenharmony_ci	if sysvals.sync:
52388c2ecf20Sopenharmony_ci		if not quiet:
52398c2ecf20Sopenharmony_ci			pprint('SYNCING FILESYSTEMS')
52408c2ecf20Sopenharmony_ci		call('sync', shell=True)
52418c2ecf20Sopenharmony_ci	# mark the start point in the kernel ring buffer just as we start
52428c2ecf20Sopenharmony_ci	sysvals.initdmesg()
52438c2ecf20Sopenharmony_ci	# start ftrace
52448c2ecf20Sopenharmony_ci	if(sysvals.usecallgraph or sysvals.usetraceevents):
52458c2ecf20Sopenharmony_ci		if not quiet:
52468c2ecf20Sopenharmony_ci			pprint('START TRACING')
52478c2ecf20Sopenharmony_ci		sysvals.fsetVal('1', 'tracing_on')
52488c2ecf20Sopenharmony_ci		if sysvals.useprocmon:
52498c2ecf20Sopenharmony_ci			pm.start()
52508c2ecf20Sopenharmony_ci	sysvals.cmdinfo(True)
52518c2ecf20Sopenharmony_ci	# execute however many s/r runs requested
52528c2ecf20Sopenharmony_ci	for count in range(1,sysvals.execcount+1):
52538c2ecf20Sopenharmony_ci		# x2delay in between test runs
52548c2ecf20Sopenharmony_ci		if(count > 1 and sysvals.x2delay > 0):
52558c2ecf20Sopenharmony_ci			sysvals.fsetVal('WAIT %d' % sysvals.x2delay, 'trace_marker')
52568c2ecf20Sopenharmony_ci			time.sleep(sysvals.x2delay/1000.0)
52578c2ecf20Sopenharmony_ci			sysvals.fsetVal('WAIT END', 'trace_marker')
52588c2ecf20Sopenharmony_ci		# start message
52598c2ecf20Sopenharmony_ci		if sysvals.testcommand != '':
52608c2ecf20Sopenharmony_ci			pprint('COMMAND START')
52618c2ecf20Sopenharmony_ci		else:
52628c2ecf20Sopenharmony_ci			if(sysvals.rtcwake):
52638c2ecf20Sopenharmony_ci				pprint('SUSPEND START')
52648c2ecf20Sopenharmony_ci			else:
52658c2ecf20Sopenharmony_ci				pprint('SUSPEND START (press a key to resume)')
52668c2ecf20Sopenharmony_ci		# set rtcwake
52678c2ecf20Sopenharmony_ci		if(sysvals.rtcwake):
52688c2ecf20Sopenharmony_ci			if not quiet:
52698c2ecf20Sopenharmony_ci				pprint('will issue an rtcwake in %d seconds' % sysvals.rtcwaketime)
52708c2ecf20Sopenharmony_ci			sysvals.rtcWakeAlarmOn()
52718c2ecf20Sopenharmony_ci		# start of suspend trace marker
52728c2ecf20Sopenharmony_ci		if(sysvals.usecallgraph or sysvals.usetraceevents):
52738c2ecf20Sopenharmony_ci			sysvals.fsetVal(datetime.now().strftime(sysvals.tmstart), 'trace_marker')
52748c2ecf20Sopenharmony_ci		# predelay delay
52758c2ecf20Sopenharmony_ci		if(count == 1 and sysvals.predelay > 0):
52768c2ecf20Sopenharmony_ci			sysvals.fsetVal('WAIT %d' % sysvals.predelay, 'trace_marker')
52778c2ecf20Sopenharmony_ci			time.sleep(sysvals.predelay/1000.0)
52788c2ecf20Sopenharmony_ci			sysvals.fsetVal('WAIT END', 'trace_marker')
52798c2ecf20Sopenharmony_ci		# initiate suspend or command
52808c2ecf20Sopenharmony_ci		tdata = {'error': ''}
52818c2ecf20Sopenharmony_ci		if sysvals.testcommand != '':
52828c2ecf20Sopenharmony_ci			res = call(sysvals.testcommand+' 2>&1', shell=True);
52838c2ecf20Sopenharmony_ci			if res != 0:
52848c2ecf20Sopenharmony_ci				tdata['error'] = 'cmd returned %d' % res
52858c2ecf20Sopenharmony_ci		else:
52868c2ecf20Sopenharmony_ci			mode = sysvals.suspendmode
52878c2ecf20Sopenharmony_ci			if sysvals.memmode and os.path.exists(sysvals.mempowerfile):
52888c2ecf20Sopenharmony_ci				mode = 'mem'
52898c2ecf20Sopenharmony_ci				pf = open(sysvals.mempowerfile, 'w')
52908c2ecf20Sopenharmony_ci				pf.write(sysvals.memmode)
52918c2ecf20Sopenharmony_ci				pf.close()
52928c2ecf20Sopenharmony_ci			if sysvals.diskmode and os.path.exists(sysvals.diskpowerfile):
52938c2ecf20Sopenharmony_ci				mode = 'disk'
52948c2ecf20Sopenharmony_ci				pf = open(sysvals.diskpowerfile, 'w')
52958c2ecf20Sopenharmony_ci				pf.write(sysvals.diskmode)
52968c2ecf20Sopenharmony_ci				pf.close()
52978c2ecf20Sopenharmony_ci			if mode == 'freeze' and sysvals.haveTurbostat():
52988c2ecf20Sopenharmony_ci				# execution will pause here
52998c2ecf20Sopenharmony_ci				turbo = sysvals.turbostat()
53008c2ecf20Sopenharmony_ci				if turbo:
53018c2ecf20Sopenharmony_ci					tdata['turbo'] = turbo
53028c2ecf20Sopenharmony_ci			else:
53038c2ecf20Sopenharmony_ci				pf = open(sysvals.powerfile, 'w')
53048c2ecf20Sopenharmony_ci				pf.write(mode)
53058c2ecf20Sopenharmony_ci				# execution will pause here
53068c2ecf20Sopenharmony_ci				try:
53078c2ecf20Sopenharmony_ci					pf.close()
53088c2ecf20Sopenharmony_ci				except Exception as e:
53098c2ecf20Sopenharmony_ci					tdata['error'] = str(e)
53108c2ecf20Sopenharmony_ci		if(sysvals.rtcwake):
53118c2ecf20Sopenharmony_ci			sysvals.rtcWakeAlarmOff()
53128c2ecf20Sopenharmony_ci		# postdelay delay
53138c2ecf20Sopenharmony_ci		if(count == sysvals.execcount and sysvals.postdelay > 0):
53148c2ecf20Sopenharmony_ci			sysvals.fsetVal('WAIT %d' % sysvals.postdelay, 'trace_marker')
53158c2ecf20Sopenharmony_ci			time.sleep(sysvals.postdelay/1000.0)
53168c2ecf20Sopenharmony_ci			sysvals.fsetVal('WAIT END', 'trace_marker')
53178c2ecf20Sopenharmony_ci		# return from suspend
53188c2ecf20Sopenharmony_ci		pprint('RESUME COMPLETE')
53198c2ecf20Sopenharmony_ci		if(sysvals.usecallgraph or sysvals.usetraceevents):
53208c2ecf20Sopenharmony_ci			sysvals.fsetVal(datetime.now().strftime(sysvals.tmend), 'trace_marker')
53218c2ecf20Sopenharmony_ci		if sysvals.wifi and wifi:
53228c2ecf20Sopenharmony_ci			tdata['wifi'] = sysvals.pollWifi(wifi)
53238c2ecf20Sopenharmony_ci		if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'):
53248c2ecf20Sopenharmony_ci			tdata['fw'] = getFPDT(False)
53258c2ecf20Sopenharmony_ci		testdata.append(tdata)
53268c2ecf20Sopenharmony_ci	cmdafter = sysvals.cmdinfo(False)
53278c2ecf20Sopenharmony_ci	# stop ftrace
53288c2ecf20Sopenharmony_ci	if(sysvals.usecallgraph or sysvals.usetraceevents):
53298c2ecf20Sopenharmony_ci		if sysvals.useprocmon:
53308c2ecf20Sopenharmony_ci			pm.stop()
53318c2ecf20Sopenharmony_ci		sysvals.fsetVal('0', 'tracing_on')
53328c2ecf20Sopenharmony_ci	# grab a copy of the dmesg output
53338c2ecf20Sopenharmony_ci	if not quiet:
53348c2ecf20Sopenharmony_ci		pprint('CAPTURING DMESG')
53358c2ecf20Sopenharmony_ci	sysvals.getdmesg(testdata)
53368c2ecf20Sopenharmony_ci	# grab a copy of the ftrace output
53378c2ecf20Sopenharmony_ci	if(sysvals.usecallgraph or sysvals.usetraceevents):
53388c2ecf20Sopenharmony_ci		if not quiet:
53398c2ecf20Sopenharmony_ci			pprint('CAPTURING TRACE')
53408c2ecf20Sopenharmony_ci		op = sysvals.writeDatafileHeader(sysvals.ftracefile, testdata)
53418c2ecf20Sopenharmony_ci		fp = open(tp+'trace', 'r')
53428c2ecf20Sopenharmony_ci		for line in fp:
53438c2ecf20Sopenharmony_ci			op.write(line)
53448c2ecf20Sopenharmony_ci		op.close()
53458c2ecf20Sopenharmony_ci		sysvals.fsetVal('', 'trace')
53468c2ecf20Sopenharmony_ci		sysvals.platforminfo(cmdafter)
53478c2ecf20Sopenharmony_ci
53488c2ecf20Sopenharmony_cidef readFile(file):
53498c2ecf20Sopenharmony_ci	if os.path.islink(file):
53508c2ecf20Sopenharmony_ci		return os.readlink(file).split('/')[-1]
53518c2ecf20Sopenharmony_ci	else:
53528c2ecf20Sopenharmony_ci		return sysvals.getVal(file).strip()
53538c2ecf20Sopenharmony_ci
53548c2ecf20Sopenharmony_ci# Function: ms2nice
53558c2ecf20Sopenharmony_ci# Description:
53568c2ecf20Sopenharmony_ci#	 Print out a very concise time string in minutes and seconds
53578c2ecf20Sopenharmony_ci# Output:
53588c2ecf20Sopenharmony_ci#	 The time string, e.g. "1901m16s"
53598c2ecf20Sopenharmony_cidef ms2nice(val):
53608c2ecf20Sopenharmony_ci	val = int(val)
53618c2ecf20Sopenharmony_ci	h = val // 3600000
53628c2ecf20Sopenharmony_ci	m = (val // 60000) % 60
53638c2ecf20Sopenharmony_ci	s = (val // 1000) % 60
53648c2ecf20Sopenharmony_ci	if h > 0:
53658c2ecf20Sopenharmony_ci		return '%d:%02d:%02d' % (h, m, s)
53668c2ecf20Sopenharmony_ci	if m > 0:
53678c2ecf20Sopenharmony_ci		return '%02d:%02d' % (m, s)
53688c2ecf20Sopenharmony_ci	return '%ds' % s
53698c2ecf20Sopenharmony_ci
53708c2ecf20Sopenharmony_cidef yesno(val):
53718c2ecf20Sopenharmony_ci	list = {'enabled':'A', 'disabled':'S', 'auto':'E', 'on':'D',
53728c2ecf20Sopenharmony_ci		'active':'A', 'suspended':'S', 'suspending':'S'}
53738c2ecf20Sopenharmony_ci	if val not in list:
53748c2ecf20Sopenharmony_ci		return ' '
53758c2ecf20Sopenharmony_ci	return list[val]
53768c2ecf20Sopenharmony_ci
53778c2ecf20Sopenharmony_ci# Function: deviceInfo
53788c2ecf20Sopenharmony_ci# Description:
53798c2ecf20Sopenharmony_ci#	 Detect all the USB hosts and devices currently connected and add
53808c2ecf20Sopenharmony_ci#	 a list of USB device names to sysvals for better timeline readability
53818c2ecf20Sopenharmony_cidef deviceInfo(output=''):
53828c2ecf20Sopenharmony_ci	if not output:
53838c2ecf20Sopenharmony_ci		pprint('LEGEND\n'\
53848c2ecf20Sopenharmony_ci		'---------------------------------------------------------------------------------------------\n'\
53858c2ecf20Sopenharmony_ci		'  A = async/sync PM queue (A/S)               C = runtime active children\n'\
53868c2ecf20Sopenharmony_ci		'  R = runtime suspend enabled/disabled (E/D)  rACTIVE = runtime active (min/sec)\n'\
53878c2ecf20Sopenharmony_ci		'  S = runtime status active/suspended (A/S)   rSUSPEND = runtime suspend (min/sec)\n'\
53888c2ecf20Sopenharmony_ci		'  U = runtime usage count\n'\
53898c2ecf20Sopenharmony_ci		'---------------------------------------------------------------------------------------------\n'\
53908c2ecf20Sopenharmony_ci		'DEVICE                     NAME                       A R S U C    rACTIVE   rSUSPEND\n'\
53918c2ecf20Sopenharmony_ci		'---------------------------------------------------------------------------------------------')
53928c2ecf20Sopenharmony_ci
53938c2ecf20Sopenharmony_ci	res = []
53948c2ecf20Sopenharmony_ci	tgtval = 'runtime_status'
53958c2ecf20Sopenharmony_ci	lines = dict()
53968c2ecf20Sopenharmony_ci	for dirname, dirnames, filenames in os.walk('/sys/devices'):
53978c2ecf20Sopenharmony_ci		if(not re.match('.*/power', dirname) or
53988c2ecf20Sopenharmony_ci			'control' not in filenames or
53998c2ecf20Sopenharmony_ci			tgtval not in filenames):
54008c2ecf20Sopenharmony_ci			continue
54018c2ecf20Sopenharmony_ci		name = ''
54028c2ecf20Sopenharmony_ci		dirname = dirname[:-6]
54038c2ecf20Sopenharmony_ci		device = dirname.split('/')[-1]
54048c2ecf20Sopenharmony_ci		power = dict()
54058c2ecf20Sopenharmony_ci		power[tgtval] = readFile('%s/power/%s' % (dirname, tgtval))
54068c2ecf20Sopenharmony_ci		# only list devices which support runtime suspend
54078c2ecf20Sopenharmony_ci		if power[tgtval] not in ['active', 'suspended', 'suspending']:
54088c2ecf20Sopenharmony_ci			continue
54098c2ecf20Sopenharmony_ci		for i in ['product', 'driver', 'subsystem']:
54108c2ecf20Sopenharmony_ci			file = '%s/%s' % (dirname, i)
54118c2ecf20Sopenharmony_ci			if os.path.exists(file):
54128c2ecf20Sopenharmony_ci				name = readFile(file)
54138c2ecf20Sopenharmony_ci				break
54148c2ecf20Sopenharmony_ci		for i in ['async', 'control', 'runtime_status', 'runtime_usage',
54158c2ecf20Sopenharmony_ci			'runtime_active_kids', 'runtime_active_time',
54168c2ecf20Sopenharmony_ci			'runtime_suspended_time']:
54178c2ecf20Sopenharmony_ci			if i in filenames:
54188c2ecf20Sopenharmony_ci				power[i] = readFile('%s/power/%s' % (dirname, i))
54198c2ecf20Sopenharmony_ci		if output:
54208c2ecf20Sopenharmony_ci			if power['control'] == output:
54218c2ecf20Sopenharmony_ci				res.append('%s/power/control' % dirname)
54228c2ecf20Sopenharmony_ci			continue
54238c2ecf20Sopenharmony_ci		lines[dirname] = '%-26s %-26s %1s %1s %1s %1s %1s %10s %10s' % \
54248c2ecf20Sopenharmony_ci			(device[:26], name[:26],
54258c2ecf20Sopenharmony_ci			yesno(power['async']), \
54268c2ecf20Sopenharmony_ci			yesno(power['control']), \
54278c2ecf20Sopenharmony_ci			yesno(power['runtime_status']), \
54288c2ecf20Sopenharmony_ci			power['runtime_usage'], \
54298c2ecf20Sopenharmony_ci			power['runtime_active_kids'], \
54308c2ecf20Sopenharmony_ci			ms2nice(power['runtime_active_time']), \
54318c2ecf20Sopenharmony_ci			ms2nice(power['runtime_suspended_time']))
54328c2ecf20Sopenharmony_ci	for i in sorted(lines):
54338c2ecf20Sopenharmony_ci		print(lines[i])
54348c2ecf20Sopenharmony_ci	return res
54358c2ecf20Sopenharmony_ci
54368c2ecf20Sopenharmony_ci# Function: getModes
54378c2ecf20Sopenharmony_ci# Description:
54388c2ecf20Sopenharmony_ci#	 Determine the supported power modes on this system
54398c2ecf20Sopenharmony_ci# Output:
54408c2ecf20Sopenharmony_ci#	 A string list of the available modes
54418c2ecf20Sopenharmony_cidef getModes():
54428c2ecf20Sopenharmony_ci	modes = []
54438c2ecf20Sopenharmony_ci	if(os.path.exists(sysvals.powerfile)):
54448c2ecf20Sopenharmony_ci		fp = open(sysvals.powerfile, 'r')
54458c2ecf20Sopenharmony_ci		modes = fp.read().split()
54468c2ecf20Sopenharmony_ci		fp.close()
54478c2ecf20Sopenharmony_ci	if(os.path.exists(sysvals.mempowerfile)):
54488c2ecf20Sopenharmony_ci		deep = False
54498c2ecf20Sopenharmony_ci		fp = open(sysvals.mempowerfile, 'r')
54508c2ecf20Sopenharmony_ci		for m in fp.read().split():
54518c2ecf20Sopenharmony_ci			memmode = m.strip('[]')
54528c2ecf20Sopenharmony_ci			if memmode == 'deep':
54538c2ecf20Sopenharmony_ci				deep = True
54548c2ecf20Sopenharmony_ci			else:
54558c2ecf20Sopenharmony_ci				modes.append('mem-%s' % memmode)
54568c2ecf20Sopenharmony_ci		fp.close()
54578c2ecf20Sopenharmony_ci		if 'mem' in modes and not deep:
54588c2ecf20Sopenharmony_ci			modes.remove('mem')
54598c2ecf20Sopenharmony_ci	if('disk' in modes and os.path.exists(sysvals.diskpowerfile)):
54608c2ecf20Sopenharmony_ci		fp = open(sysvals.diskpowerfile, 'r')
54618c2ecf20Sopenharmony_ci		for m in fp.read().split():
54628c2ecf20Sopenharmony_ci			modes.append('disk-%s' % m.strip('[]'))
54638c2ecf20Sopenharmony_ci		fp.close()
54648c2ecf20Sopenharmony_ci	return modes
54658c2ecf20Sopenharmony_ci
54668c2ecf20Sopenharmony_ci# Function: dmidecode
54678c2ecf20Sopenharmony_ci# Description:
54688c2ecf20Sopenharmony_ci#	 Read the bios tables and pull out system info
54698c2ecf20Sopenharmony_ci# Arguments:
54708c2ecf20Sopenharmony_ci#	 mempath: /dev/mem or custom mem path
54718c2ecf20Sopenharmony_ci#	 fatal: True to exit on error, False to return empty dict
54728c2ecf20Sopenharmony_ci# Output:
54738c2ecf20Sopenharmony_ci#	 A dict object with all available key/values
54748c2ecf20Sopenharmony_cidef dmidecode(mempath, fatal=False):
54758c2ecf20Sopenharmony_ci	out = dict()
54768c2ecf20Sopenharmony_ci
54778c2ecf20Sopenharmony_ci	# the list of values to retrieve, with hardcoded (type, idx)
54788c2ecf20Sopenharmony_ci	info = {
54798c2ecf20Sopenharmony_ci		'bios-vendor': (0, 4),
54808c2ecf20Sopenharmony_ci		'bios-version': (0, 5),
54818c2ecf20Sopenharmony_ci		'bios-release-date': (0, 8),
54828c2ecf20Sopenharmony_ci		'system-manufacturer': (1, 4),
54838c2ecf20Sopenharmony_ci		'system-product-name': (1, 5),
54848c2ecf20Sopenharmony_ci		'system-version': (1, 6),
54858c2ecf20Sopenharmony_ci		'system-serial-number': (1, 7),
54868c2ecf20Sopenharmony_ci		'baseboard-manufacturer': (2, 4),
54878c2ecf20Sopenharmony_ci		'baseboard-product-name': (2, 5),
54888c2ecf20Sopenharmony_ci		'baseboard-version': (2, 6),
54898c2ecf20Sopenharmony_ci		'baseboard-serial-number': (2, 7),
54908c2ecf20Sopenharmony_ci		'chassis-manufacturer': (3, 4),
54918c2ecf20Sopenharmony_ci		'chassis-type': (3, 5),
54928c2ecf20Sopenharmony_ci		'chassis-version': (3, 6),
54938c2ecf20Sopenharmony_ci		'chassis-serial-number': (3, 7),
54948c2ecf20Sopenharmony_ci		'processor-manufacturer': (4, 7),
54958c2ecf20Sopenharmony_ci		'processor-version': (4, 16),
54968c2ecf20Sopenharmony_ci	}
54978c2ecf20Sopenharmony_ci	if(not os.path.exists(mempath)):
54988c2ecf20Sopenharmony_ci		if(fatal):
54998c2ecf20Sopenharmony_ci			doError('file does not exist: %s' % mempath)
55008c2ecf20Sopenharmony_ci		return out
55018c2ecf20Sopenharmony_ci	if(not os.access(mempath, os.R_OK)):
55028c2ecf20Sopenharmony_ci		if(fatal):
55038c2ecf20Sopenharmony_ci			doError('file is not readable: %s' % mempath)
55048c2ecf20Sopenharmony_ci		return out
55058c2ecf20Sopenharmony_ci
55068c2ecf20Sopenharmony_ci	# by default use legacy scan, but try to use EFI first
55078c2ecf20Sopenharmony_ci	memaddr = 0xf0000
55088c2ecf20Sopenharmony_ci	memsize = 0x10000
55098c2ecf20Sopenharmony_ci	for ep in ['/sys/firmware/efi/systab', '/proc/efi/systab']:
55108c2ecf20Sopenharmony_ci		if not os.path.exists(ep) or not os.access(ep, os.R_OK):
55118c2ecf20Sopenharmony_ci			continue
55128c2ecf20Sopenharmony_ci		fp = open(ep, 'r')
55138c2ecf20Sopenharmony_ci		buf = fp.read()
55148c2ecf20Sopenharmony_ci		fp.close()
55158c2ecf20Sopenharmony_ci		i = buf.find('SMBIOS=')
55168c2ecf20Sopenharmony_ci		if i >= 0:
55178c2ecf20Sopenharmony_ci			try:
55188c2ecf20Sopenharmony_ci				memaddr = int(buf[i+7:], 16)
55198c2ecf20Sopenharmony_ci				memsize = 0x20
55208c2ecf20Sopenharmony_ci			except:
55218c2ecf20Sopenharmony_ci				continue
55228c2ecf20Sopenharmony_ci
55238c2ecf20Sopenharmony_ci	# read in the memory for scanning
55248c2ecf20Sopenharmony_ci	try:
55258c2ecf20Sopenharmony_ci		fp = open(mempath, 'rb')
55268c2ecf20Sopenharmony_ci		fp.seek(memaddr)
55278c2ecf20Sopenharmony_ci		buf = fp.read(memsize)
55288c2ecf20Sopenharmony_ci	except:
55298c2ecf20Sopenharmony_ci		if(fatal):
55308c2ecf20Sopenharmony_ci			doError('DMI table is unreachable, sorry')
55318c2ecf20Sopenharmony_ci		else:
55328c2ecf20Sopenharmony_ci			pprint('WARNING: /dev/mem is not readable, ignoring DMI data')
55338c2ecf20Sopenharmony_ci			return out
55348c2ecf20Sopenharmony_ci	fp.close()
55358c2ecf20Sopenharmony_ci
55368c2ecf20Sopenharmony_ci	# search for either an SM table or DMI table
55378c2ecf20Sopenharmony_ci	i = base = length = num = 0
55388c2ecf20Sopenharmony_ci	while(i < memsize):
55398c2ecf20Sopenharmony_ci		if buf[i:i+4] == b'_SM_' and i < memsize - 16:
55408c2ecf20Sopenharmony_ci			length = struct.unpack('H', buf[i+22:i+24])[0]
55418c2ecf20Sopenharmony_ci			base, num = struct.unpack('IH', buf[i+24:i+30])
55428c2ecf20Sopenharmony_ci			break
55438c2ecf20Sopenharmony_ci		elif buf[i:i+5] == b'_DMI_':
55448c2ecf20Sopenharmony_ci			length = struct.unpack('H', buf[i+6:i+8])[0]
55458c2ecf20Sopenharmony_ci			base, num = struct.unpack('IH', buf[i+8:i+14])
55468c2ecf20Sopenharmony_ci			break
55478c2ecf20Sopenharmony_ci		i += 16
55488c2ecf20Sopenharmony_ci	if base == 0 and length == 0 and num == 0:
55498c2ecf20Sopenharmony_ci		if(fatal):
55508c2ecf20Sopenharmony_ci			doError('Neither SMBIOS nor DMI were found')
55518c2ecf20Sopenharmony_ci		else:
55528c2ecf20Sopenharmony_ci			return out
55538c2ecf20Sopenharmony_ci
55548c2ecf20Sopenharmony_ci	# read in the SM or DMI table
55558c2ecf20Sopenharmony_ci	try:
55568c2ecf20Sopenharmony_ci		fp = open(mempath, 'rb')
55578c2ecf20Sopenharmony_ci		fp.seek(base)
55588c2ecf20Sopenharmony_ci		buf = fp.read(length)
55598c2ecf20Sopenharmony_ci	except:
55608c2ecf20Sopenharmony_ci		if(fatal):
55618c2ecf20Sopenharmony_ci			doError('DMI table is unreachable, sorry')
55628c2ecf20Sopenharmony_ci		else:
55638c2ecf20Sopenharmony_ci			pprint('WARNING: /dev/mem is not readable, ignoring DMI data')
55648c2ecf20Sopenharmony_ci			return out
55658c2ecf20Sopenharmony_ci	fp.close()
55668c2ecf20Sopenharmony_ci
55678c2ecf20Sopenharmony_ci	# scan the table for the values we want
55688c2ecf20Sopenharmony_ci	count = i = 0
55698c2ecf20Sopenharmony_ci	while(count < num and i <= len(buf) - 4):
55708c2ecf20Sopenharmony_ci		type, size, handle = struct.unpack('BBH', buf[i:i+4])
55718c2ecf20Sopenharmony_ci		n = i + size
55728c2ecf20Sopenharmony_ci		while n < len(buf) - 1:
55738c2ecf20Sopenharmony_ci			if 0 == struct.unpack('H', buf[n:n+2])[0]:
55748c2ecf20Sopenharmony_ci				break
55758c2ecf20Sopenharmony_ci			n += 1
55768c2ecf20Sopenharmony_ci		data = buf[i+size:n+2].split(b'\0')
55778c2ecf20Sopenharmony_ci		for name in info:
55788c2ecf20Sopenharmony_ci			itype, idxadr = info[name]
55798c2ecf20Sopenharmony_ci			if itype == type:
55808c2ecf20Sopenharmony_ci				idx = struct.unpack('B', buf[i+idxadr:i+idxadr+1])[0]
55818c2ecf20Sopenharmony_ci				if idx > 0 and idx < len(data) - 1:
55828c2ecf20Sopenharmony_ci					s = data[idx-1].decode('utf-8')
55838c2ecf20Sopenharmony_ci					if s.strip() and s.strip().lower() != 'to be filled by o.e.m.':
55848c2ecf20Sopenharmony_ci						out[name] = s
55858c2ecf20Sopenharmony_ci		i = n + 2
55868c2ecf20Sopenharmony_ci		count += 1
55878c2ecf20Sopenharmony_ci	return out
55888c2ecf20Sopenharmony_ci
55898c2ecf20Sopenharmony_cidef displayControl(cmd):
55908c2ecf20Sopenharmony_ci	xset, ret = 'timeout 10 xset -d :0.0 {0}', 0
55918c2ecf20Sopenharmony_ci	if sysvals.sudouser:
55928c2ecf20Sopenharmony_ci		xset = 'sudo -u %s %s' % (sysvals.sudouser, xset)
55938c2ecf20Sopenharmony_ci	if cmd == 'init':
55948c2ecf20Sopenharmony_ci		ret = call(xset.format('dpms 0 0 0'), shell=True)
55958c2ecf20Sopenharmony_ci		if not ret:
55968c2ecf20Sopenharmony_ci			ret = call(xset.format('s off'), shell=True)
55978c2ecf20Sopenharmony_ci	elif cmd == 'reset':
55988c2ecf20Sopenharmony_ci		ret = call(xset.format('s reset'), shell=True)
55998c2ecf20Sopenharmony_ci	elif cmd in ['on', 'off', 'standby', 'suspend']:
56008c2ecf20Sopenharmony_ci		b4 = displayControl('stat')
56018c2ecf20Sopenharmony_ci		ret = call(xset.format('dpms force %s' % cmd), shell=True)
56028c2ecf20Sopenharmony_ci		if not ret:
56038c2ecf20Sopenharmony_ci			curr = displayControl('stat')
56048c2ecf20Sopenharmony_ci			sysvals.vprint('Display Switched: %s -> %s' % (b4, curr))
56058c2ecf20Sopenharmony_ci			if curr != cmd:
56068c2ecf20Sopenharmony_ci				sysvals.vprint('WARNING: Display failed to change to %s' % cmd)
56078c2ecf20Sopenharmony_ci		if ret:
56088c2ecf20Sopenharmony_ci			sysvals.vprint('WARNING: Display failed to change to %s with xset' % cmd)
56098c2ecf20Sopenharmony_ci			return ret
56108c2ecf20Sopenharmony_ci	elif cmd == 'stat':
56118c2ecf20Sopenharmony_ci		fp = Popen(xset.format('q').split(' '), stdout=PIPE).stdout
56128c2ecf20Sopenharmony_ci		ret = 'unknown'
56138c2ecf20Sopenharmony_ci		for line in fp:
56148c2ecf20Sopenharmony_ci			m = re.match('[\s]*Monitor is (?P<m>.*)', ascii(line))
56158c2ecf20Sopenharmony_ci			if(m and len(m.group('m')) >= 2):
56168c2ecf20Sopenharmony_ci				out = m.group('m').lower()
56178c2ecf20Sopenharmony_ci				ret = out[3:] if out[0:2] == 'in' else out
56188c2ecf20Sopenharmony_ci				break
56198c2ecf20Sopenharmony_ci		fp.close()
56208c2ecf20Sopenharmony_ci	return ret
56218c2ecf20Sopenharmony_ci
56228c2ecf20Sopenharmony_ci# Function: getFPDT
56238c2ecf20Sopenharmony_ci# Description:
56248c2ecf20Sopenharmony_ci#	 Read the acpi bios tables and pull out FPDT, the firmware data
56258c2ecf20Sopenharmony_ci# Arguments:
56268c2ecf20Sopenharmony_ci#	 output: True to output the info to stdout, False otherwise
56278c2ecf20Sopenharmony_cidef getFPDT(output):
56288c2ecf20Sopenharmony_ci	rectype = {}
56298c2ecf20Sopenharmony_ci	rectype[0] = 'Firmware Basic Boot Performance Record'
56308c2ecf20Sopenharmony_ci	rectype[1] = 'S3 Performance Table Record'
56318c2ecf20Sopenharmony_ci	prectype = {}
56328c2ecf20Sopenharmony_ci	prectype[0] = 'Basic S3 Resume Performance Record'
56338c2ecf20Sopenharmony_ci	prectype[1] = 'Basic S3 Suspend Performance Record'
56348c2ecf20Sopenharmony_ci
56358c2ecf20Sopenharmony_ci	sysvals.rootCheck(True)
56368c2ecf20Sopenharmony_ci	if(not os.path.exists(sysvals.fpdtpath)):
56378c2ecf20Sopenharmony_ci		if(output):
56388c2ecf20Sopenharmony_ci			doError('file does not exist: %s' % sysvals.fpdtpath)
56398c2ecf20Sopenharmony_ci		return False
56408c2ecf20Sopenharmony_ci	if(not os.access(sysvals.fpdtpath, os.R_OK)):
56418c2ecf20Sopenharmony_ci		if(output):
56428c2ecf20Sopenharmony_ci			doError('file is not readable: %s' % sysvals.fpdtpath)
56438c2ecf20Sopenharmony_ci		return False
56448c2ecf20Sopenharmony_ci	if(not os.path.exists(sysvals.mempath)):
56458c2ecf20Sopenharmony_ci		if(output):
56468c2ecf20Sopenharmony_ci			doError('file does not exist: %s' % sysvals.mempath)
56478c2ecf20Sopenharmony_ci		return False
56488c2ecf20Sopenharmony_ci	if(not os.access(sysvals.mempath, os.R_OK)):
56498c2ecf20Sopenharmony_ci		if(output):
56508c2ecf20Sopenharmony_ci			doError('file is not readable: %s' % sysvals.mempath)
56518c2ecf20Sopenharmony_ci		return False
56528c2ecf20Sopenharmony_ci
56538c2ecf20Sopenharmony_ci	fp = open(sysvals.fpdtpath, 'rb')
56548c2ecf20Sopenharmony_ci	buf = fp.read()
56558c2ecf20Sopenharmony_ci	fp.close()
56568c2ecf20Sopenharmony_ci
56578c2ecf20Sopenharmony_ci	if(len(buf) < 36):
56588c2ecf20Sopenharmony_ci		if(output):
56598c2ecf20Sopenharmony_ci			doError('Invalid FPDT table data, should '+\
56608c2ecf20Sopenharmony_ci				'be at least 36 bytes')
56618c2ecf20Sopenharmony_ci		return False
56628c2ecf20Sopenharmony_ci
56638c2ecf20Sopenharmony_ci	table = struct.unpack('4sIBB6s8sI4sI', buf[0:36])
56648c2ecf20Sopenharmony_ci	if(output):
56658c2ecf20Sopenharmony_ci		pprint('\n'\
56668c2ecf20Sopenharmony_ci		'Firmware Performance Data Table (%s)\n'\
56678c2ecf20Sopenharmony_ci		'                  Signature : %s\n'\
56688c2ecf20Sopenharmony_ci		'               Table Length : %u\n'\
56698c2ecf20Sopenharmony_ci		'                   Revision : %u\n'\
56708c2ecf20Sopenharmony_ci		'                   Checksum : 0x%x\n'\
56718c2ecf20Sopenharmony_ci		'                     OEM ID : %s\n'\
56728c2ecf20Sopenharmony_ci		'               OEM Table ID : %s\n'\
56738c2ecf20Sopenharmony_ci		'               OEM Revision : %u\n'\
56748c2ecf20Sopenharmony_ci		'                 Creator ID : %s\n'\
56758c2ecf20Sopenharmony_ci		'           Creator Revision : 0x%x\n'\
56768c2ecf20Sopenharmony_ci		'' % (ascii(table[0]), ascii(table[0]), table[1], table[2],
56778c2ecf20Sopenharmony_ci			table[3], ascii(table[4]), ascii(table[5]), table[6],
56788c2ecf20Sopenharmony_ci			ascii(table[7]), table[8]))
56798c2ecf20Sopenharmony_ci
56808c2ecf20Sopenharmony_ci	if(table[0] != b'FPDT'):
56818c2ecf20Sopenharmony_ci		if(output):
56828c2ecf20Sopenharmony_ci			doError('Invalid FPDT table')
56838c2ecf20Sopenharmony_ci		return False
56848c2ecf20Sopenharmony_ci	if(len(buf) <= 36):
56858c2ecf20Sopenharmony_ci		return False
56868c2ecf20Sopenharmony_ci	i = 0
56878c2ecf20Sopenharmony_ci	fwData = [0, 0]
56888c2ecf20Sopenharmony_ci	records = buf[36:]
56898c2ecf20Sopenharmony_ci	try:
56908c2ecf20Sopenharmony_ci		fp = open(sysvals.mempath, 'rb')
56918c2ecf20Sopenharmony_ci	except:
56928c2ecf20Sopenharmony_ci		pprint('WARNING: /dev/mem is not readable, ignoring the FPDT data')
56938c2ecf20Sopenharmony_ci		return False
56948c2ecf20Sopenharmony_ci	while(i < len(records)):
56958c2ecf20Sopenharmony_ci		header = struct.unpack('HBB', records[i:i+4])
56968c2ecf20Sopenharmony_ci		if(header[0] not in rectype):
56978c2ecf20Sopenharmony_ci			i += header[1]
56988c2ecf20Sopenharmony_ci			continue
56998c2ecf20Sopenharmony_ci		if(header[1] != 16):
57008c2ecf20Sopenharmony_ci			i += header[1]
57018c2ecf20Sopenharmony_ci			continue
57028c2ecf20Sopenharmony_ci		addr = struct.unpack('Q', records[i+8:i+16])[0]
57038c2ecf20Sopenharmony_ci		try:
57048c2ecf20Sopenharmony_ci			fp.seek(addr)
57058c2ecf20Sopenharmony_ci			first = fp.read(8)
57068c2ecf20Sopenharmony_ci		except:
57078c2ecf20Sopenharmony_ci			if(output):
57088c2ecf20Sopenharmony_ci				pprint('Bad address 0x%x in %s' % (addr, sysvals.mempath))
57098c2ecf20Sopenharmony_ci			return [0, 0]
57108c2ecf20Sopenharmony_ci		rechead = struct.unpack('4sI', first)
57118c2ecf20Sopenharmony_ci		recdata = fp.read(rechead[1]-8)
57128c2ecf20Sopenharmony_ci		if(rechead[0] == b'FBPT'):
57138c2ecf20Sopenharmony_ci			record = struct.unpack('HBBIQQQQQ', recdata[:48])
57148c2ecf20Sopenharmony_ci			if(output):
57158c2ecf20Sopenharmony_ci				pprint('%s (%s)\n'\
57168c2ecf20Sopenharmony_ci				'                  Reset END : %u ns\n'\
57178c2ecf20Sopenharmony_ci				'  OS Loader LoadImage Start : %u ns\n'\
57188c2ecf20Sopenharmony_ci				' OS Loader StartImage Start : %u ns\n'\
57198c2ecf20Sopenharmony_ci				'     ExitBootServices Entry : %u ns\n'\
57208c2ecf20Sopenharmony_ci				'      ExitBootServices Exit : %u ns'\
57218c2ecf20Sopenharmony_ci				'' % (rectype[header[0]], ascii(rechead[0]), record[4], record[5],
57228c2ecf20Sopenharmony_ci					record[6], record[7], record[8]))
57238c2ecf20Sopenharmony_ci		elif(rechead[0] == b'S3PT'):
57248c2ecf20Sopenharmony_ci			if(output):
57258c2ecf20Sopenharmony_ci				pprint('%s (%s)' % (rectype[header[0]], ascii(rechead[0])))
57268c2ecf20Sopenharmony_ci			j = 0
57278c2ecf20Sopenharmony_ci			while(j < len(recdata)):
57288c2ecf20Sopenharmony_ci				prechead = struct.unpack('HBB', recdata[j:j+4])
57298c2ecf20Sopenharmony_ci				if(prechead[0] not in prectype):
57308c2ecf20Sopenharmony_ci					continue
57318c2ecf20Sopenharmony_ci				if(prechead[0] == 0):
57328c2ecf20Sopenharmony_ci					record = struct.unpack('IIQQ', recdata[j:j+prechead[1]])
57338c2ecf20Sopenharmony_ci					fwData[1] = record[2]
57348c2ecf20Sopenharmony_ci					if(output):
57358c2ecf20Sopenharmony_ci						pprint('    %s\n'\
57368c2ecf20Sopenharmony_ci						'               Resume Count : %u\n'\
57378c2ecf20Sopenharmony_ci						'                 FullResume : %u ns\n'\
57388c2ecf20Sopenharmony_ci						'              AverageResume : %u ns'\
57398c2ecf20Sopenharmony_ci						'' % (prectype[prechead[0]], record[1],
57408c2ecf20Sopenharmony_ci								record[2], record[3]))
57418c2ecf20Sopenharmony_ci				elif(prechead[0] == 1):
57428c2ecf20Sopenharmony_ci					record = struct.unpack('QQ', recdata[j+4:j+prechead[1]])
57438c2ecf20Sopenharmony_ci					fwData[0] = record[1] - record[0]
57448c2ecf20Sopenharmony_ci					if(output):
57458c2ecf20Sopenharmony_ci						pprint('    %s\n'\
57468c2ecf20Sopenharmony_ci						'               SuspendStart : %u ns\n'\
57478c2ecf20Sopenharmony_ci						'                 SuspendEnd : %u ns\n'\
57488c2ecf20Sopenharmony_ci						'                SuspendTime : %u ns'\
57498c2ecf20Sopenharmony_ci						'' % (prectype[prechead[0]], record[0],
57508c2ecf20Sopenharmony_ci								record[1], fwData[0]))
57518c2ecf20Sopenharmony_ci
57528c2ecf20Sopenharmony_ci				j += prechead[1]
57538c2ecf20Sopenharmony_ci		if(output):
57548c2ecf20Sopenharmony_ci			pprint('')
57558c2ecf20Sopenharmony_ci		i += header[1]
57568c2ecf20Sopenharmony_ci	fp.close()
57578c2ecf20Sopenharmony_ci	return fwData
57588c2ecf20Sopenharmony_ci
57598c2ecf20Sopenharmony_ci# Function: statusCheck
57608c2ecf20Sopenharmony_ci# Description:
57618c2ecf20Sopenharmony_ci#	 Verify that the requested command and options will work, and
57628c2ecf20Sopenharmony_ci#	 print the results to the terminal
57638c2ecf20Sopenharmony_ci# Output:
57648c2ecf20Sopenharmony_ci#	 True if the test will work, False if not
57658c2ecf20Sopenharmony_cidef statusCheck(probecheck=False):
57668c2ecf20Sopenharmony_ci	status = ''
57678c2ecf20Sopenharmony_ci
57688c2ecf20Sopenharmony_ci	pprint('Checking this system (%s)...' % platform.node())
57698c2ecf20Sopenharmony_ci
57708c2ecf20Sopenharmony_ci	# check we have root access
57718c2ecf20Sopenharmony_ci	res = sysvals.colorText('NO (No features of this tool will work!)')
57728c2ecf20Sopenharmony_ci	if(sysvals.rootCheck(False)):
57738c2ecf20Sopenharmony_ci		res = 'YES'
57748c2ecf20Sopenharmony_ci	pprint('    have root access: %s' % res)
57758c2ecf20Sopenharmony_ci	if(res != 'YES'):
57768c2ecf20Sopenharmony_ci		pprint('    Try running this script with sudo')
57778c2ecf20Sopenharmony_ci		return 'missing root access'
57788c2ecf20Sopenharmony_ci
57798c2ecf20Sopenharmony_ci	# check sysfs is mounted
57808c2ecf20Sopenharmony_ci	res = sysvals.colorText('NO (No features of this tool will work!)')
57818c2ecf20Sopenharmony_ci	if(os.path.exists(sysvals.powerfile)):
57828c2ecf20Sopenharmony_ci		res = 'YES'
57838c2ecf20Sopenharmony_ci	pprint('    is sysfs mounted: %s' % res)
57848c2ecf20Sopenharmony_ci	if(res != 'YES'):
57858c2ecf20Sopenharmony_ci		return 'sysfs is missing'
57868c2ecf20Sopenharmony_ci
57878c2ecf20Sopenharmony_ci	# check target mode is a valid mode
57888c2ecf20Sopenharmony_ci	if sysvals.suspendmode != 'command':
57898c2ecf20Sopenharmony_ci		res = sysvals.colorText('NO')
57908c2ecf20Sopenharmony_ci		modes = getModes()
57918c2ecf20Sopenharmony_ci		if(sysvals.suspendmode in modes):
57928c2ecf20Sopenharmony_ci			res = 'YES'
57938c2ecf20Sopenharmony_ci		else:
57948c2ecf20Sopenharmony_ci			status = '%s mode is not supported' % sysvals.suspendmode
57958c2ecf20Sopenharmony_ci		pprint('    is "%s" a valid power mode: %s' % (sysvals.suspendmode, res))
57968c2ecf20Sopenharmony_ci		if(res == 'NO'):
57978c2ecf20Sopenharmony_ci			pprint('      valid power modes are: %s' % modes)
57988c2ecf20Sopenharmony_ci			pprint('      please choose one with -m')
57998c2ecf20Sopenharmony_ci
58008c2ecf20Sopenharmony_ci	# check if ftrace is available
58018c2ecf20Sopenharmony_ci	res = sysvals.colorText('NO')
58028c2ecf20Sopenharmony_ci	ftgood = sysvals.verifyFtrace()
58038c2ecf20Sopenharmony_ci	if(ftgood):
58048c2ecf20Sopenharmony_ci		res = 'YES'
58058c2ecf20Sopenharmony_ci	elif(sysvals.usecallgraph):
58068c2ecf20Sopenharmony_ci		status = 'ftrace is not properly supported'
58078c2ecf20Sopenharmony_ci	pprint('    is ftrace supported: %s' % res)
58088c2ecf20Sopenharmony_ci
58098c2ecf20Sopenharmony_ci	# check if kprobes are available
58108c2ecf20Sopenharmony_ci	if sysvals.usekprobes:
58118c2ecf20Sopenharmony_ci		res = sysvals.colorText('NO')
58128c2ecf20Sopenharmony_ci		sysvals.usekprobes = sysvals.verifyKprobes()
58138c2ecf20Sopenharmony_ci		if(sysvals.usekprobes):
58148c2ecf20Sopenharmony_ci			res = 'YES'
58158c2ecf20Sopenharmony_ci		else:
58168c2ecf20Sopenharmony_ci			sysvals.usedevsrc = False
58178c2ecf20Sopenharmony_ci		pprint('    are kprobes supported: %s' % res)
58188c2ecf20Sopenharmony_ci
58198c2ecf20Sopenharmony_ci	# what data source are we using
58208c2ecf20Sopenharmony_ci	res = 'DMESG'
58218c2ecf20Sopenharmony_ci	if(ftgood):
58228c2ecf20Sopenharmony_ci		sysvals.usetraceevents = True
58238c2ecf20Sopenharmony_ci		for e in sysvals.traceevents:
58248c2ecf20Sopenharmony_ci			if not os.path.exists(sysvals.epath+e):
58258c2ecf20Sopenharmony_ci				sysvals.usetraceevents = False
58268c2ecf20Sopenharmony_ci		if(sysvals.usetraceevents):
58278c2ecf20Sopenharmony_ci			res = 'FTRACE (all trace events found)'
58288c2ecf20Sopenharmony_ci	pprint('    timeline data source: %s' % res)
58298c2ecf20Sopenharmony_ci
58308c2ecf20Sopenharmony_ci	# check if rtcwake
58318c2ecf20Sopenharmony_ci	res = sysvals.colorText('NO')
58328c2ecf20Sopenharmony_ci	if(sysvals.rtcpath != ''):
58338c2ecf20Sopenharmony_ci		res = 'YES'
58348c2ecf20Sopenharmony_ci	elif(sysvals.rtcwake):
58358c2ecf20Sopenharmony_ci		status = 'rtcwake is not properly supported'
58368c2ecf20Sopenharmony_ci	pprint('    is rtcwake supported: %s' % res)
58378c2ecf20Sopenharmony_ci
58388c2ecf20Sopenharmony_ci	# check info commands
58398c2ecf20Sopenharmony_ci	pprint('    optional commands this tool may use for info:')
58408c2ecf20Sopenharmony_ci	no = sysvals.colorText('MISSING')
58418c2ecf20Sopenharmony_ci	yes = sysvals.colorText('FOUND', 32)
58428c2ecf20Sopenharmony_ci	for c in ['turbostat', 'mcelog', 'lspci', 'lsusb']:
58438c2ecf20Sopenharmony_ci		if c == 'turbostat':
58448c2ecf20Sopenharmony_ci			res = yes if sysvals.haveTurbostat() else no
58458c2ecf20Sopenharmony_ci		else:
58468c2ecf20Sopenharmony_ci			res = yes if sysvals.getExec(c) else no
58478c2ecf20Sopenharmony_ci		pprint('        %s: %s' % (c, res))
58488c2ecf20Sopenharmony_ci
58498c2ecf20Sopenharmony_ci	if not probecheck:
58508c2ecf20Sopenharmony_ci		return status
58518c2ecf20Sopenharmony_ci
58528c2ecf20Sopenharmony_ci	# verify kprobes
58538c2ecf20Sopenharmony_ci	if sysvals.usekprobes:
58548c2ecf20Sopenharmony_ci		for name in sysvals.tracefuncs:
58558c2ecf20Sopenharmony_ci			sysvals.defaultKprobe(name, sysvals.tracefuncs[name])
58568c2ecf20Sopenharmony_ci		if sysvals.usedevsrc:
58578c2ecf20Sopenharmony_ci			for name in sysvals.dev_tracefuncs:
58588c2ecf20Sopenharmony_ci				sysvals.defaultKprobe(name, sysvals.dev_tracefuncs[name])
58598c2ecf20Sopenharmony_ci		sysvals.addKprobes(True)
58608c2ecf20Sopenharmony_ci
58618c2ecf20Sopenharmony_ci	return status
58628c2ecf20Sopenharmony_ci
58638c2ecf20Sopenharmony_ci# Function: doError
58648c2ecf20Sopenharmony_ci# Description:
58658c2ecf20Sopenharmony_ci#	 generic error function for catastrphic failures
58668c2ecf20Sopenharmony_ci# Arguments:
58678c2ecf20Sopenharmony_ci#	 msg: the error message to print
58688c2ecf20Sopenharmony_ci#	 help: True if printHelp should be called after, False otherwise
58698c2ecf20Sopenharmony_cidef doError(msg, help=False):
58708c2ecf20Sopenharmony_ci	if(help == True):
58718c2ecf20Sopenharmony_ci		printHelp()
58728c2ecf20Sopenharmony_ci	pprint('ERROR: %s\n' % msg)
58738c2ecf20Sopenharmony_ci	sysvals.outputResult({'error':msg})
58748c2ecf20Sopenharmony_ci	sys.exit(1)
58758c2ecf20Sopenharmony_ci
58768c2ecf20Sopenharmony_ci# Function: getArgInt
58778c2ecf20Sopenharmony_ci# Description:
58788c2ecf20Sopenharmony_ci#	 pull out an integer argument from the command line with checks
58798c2ecf20Sopenharmony_cidef getArgInt(name, args, min, max, main=True):
58808c2ecf20Sopenharmony_ci	if main:
58818c2ecf20Sopenharmony_ci		try:
58828c2ecf20Sopenharmony_ci			arg = next(args)
58838c2ecf20Sopenharmony_ci		except:
58848c2ecf20Sopenharmony_ci			doError(name+': no argument supplied', True)
58858c2ecf20Sopenharmony_ci	else:
58868c2ecf20Sopenharmony_ci		arg = args
58878c2ecf20Sopenharmony_ci	try:
58888c2ecf20Sopenharmony_ci		val = int(arg)
58898c2ecf20Sopenharmony_ci	except:
58908c2ecf20Sopenharmony_ci		doError(name+': non-integer value given', True)
58918c2ecf20Sopenharmony_ci	if(val < min or val > max):
58928c2ecf20Sopenharmony_ci		doError(name+': value should be between %d and %d' % (min, max), True)
58938c2ecf20Sopenharmony_ci	return val
58948c2ecf20Sopenharmony_ci
58958c2ecf20Sopenharmony_ci# Function: getArgFloat
58968c2ecf20Sopenharmony_ci# Description:
58978c2ecf20Sopenharmony_ci#	 pull out a float argument from the command line with checks
58988c2ecf20Sopenharmony_cidef getArgFloat(name, args, min, max, main=True):
58998c2ecf20Sopenharmony_ci	if main:
59008c2ecf20Sopenharmony_ci		try:
59018c2ecf20Sopenharmony_ci			arg = next(args)
59028c2ecf20Sopenharmony_ci		except:
59038c2ecf20Sopenharmony_ci			doError(name+': no argument supplied', True)
59048c2ecf20Sopenharmony_ci	else:
59058c2ecf20Sopenharmony_ci		arg = args
59068c2ecf20Sopenharmony_ci	try:
59078c2ecf20Sopenharmony_ci		val = float(arg)
59088c2ecf20Sopenharmony_ci	except:
59098c2ecf20Sopenharmony_ci		doError(name+': non-numerical value given', True)
59108c2ecf20Sopenharmony_ci	if(val < min or val > max):
59118c2ecf20Sopenharmony_ci		doError(name+': value should be between %f and %f' % (min, max), True)
59128c2ecf20Sopenharmony_ci	return val
59138c2ecf20Sopenharmony_ci
59148c2ecf20Sopenharmony_cidef processData(live=False, quiet=False):
59158c2ecf20Sopenharmony_ci	if not quiet:
59168c2ecf20Sopenharmony_ci		pprint('PROCESSING: %s' % sysvals.htmlfile)
59178c2ecf20Sopenharmony_ci	sysvals.vprint('usetraceevents=%s, usetracemarkers=%s, usekprobes=%s' % \
59188c2ecf20Sopenharmony_ci		(sysvals.usetraceevents, sysvals.usetracemarkers, sysvals.usekprobes))
59198c2ecf20Sopenharmony_ci	error = ''
59208c2ecf20Sopenharmony_ci	if(sysvals.usetraceevents):
59218c2ecf20Sopenharmony_ci		testruns, error = parseTraceLog(live)
59228c2ecf20Sopenharmony_ci		if sysvals.dmesgfile:
59238c2ecf20Sopenharmony_ci			for data in testruns:
59248c2ecf20Sopenharmony_ci				data.extractErrorInfo()
59258c2ecf20Sopenharmony_ci	else:
59268c2ecf20Sopenharmony_ci		testruns = loadKernelLog()
59278c2ecf20Sopenharmony_ci		for data in testruns:
59288c2ecf20Sopenharmony_ci			parseKernelLog(data)
59298c2ecf20Sopenharmony_ci		if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)):
59308c2ecf20Sopenharmony_ci			appendIncompleteTraceLog(testruns)
59318c2ecf20Sopenharmony_ci	if not sysvals.stamp:
59328c2ecf20Sopenharmony_ci		pprint('ERROR: data does not include the expected stamp')
59338c2ecf20Sopenharmony_ci		return (testruns, {'error': 'timeline generation failed'})
59348c2ecf20Sopenharmony_ci	shown = ['bios', 'biosdate', 'cpu', 'host', 'kernel', 'man', 'memfr',
59358c2ecf20Sopenharmony_ci			'memsz', 'mode', 'numcpu', 'plat', 'time', 'wifi']
59368c2ecf20Sopenharmony_ci	sysvals.vprint('System Info:')
59378c2ecf20Sopenharmony_ci	for key in sorted(sysvals.stamp):
59388c2ecf20Sopenharmony_ci		if key in shown:
59398c2ecf20Sopenharmony_ci			sysvals.vprint('    %-8s : %s' % (key.upper(), sysvals.stamp[key]))
59408c2ecf20Sopenharmony_ci	sysvals.vprint('Command:\n    %s' % sysvals.cmdline)
59418c2ecf20Sopenharmony_ci	for data in testruns:
59428c2ecf20Sopenharmony_ci		if data.turbostat:
59438c2ecf20Sopenharmony_ci			idx, s = 0, 'Turbostat:\n    '
59448c2ecf20Sopenharmony_ci			for val in data.turbostat.split('|'):
59458c2ecf20Sopenharmony_ci				idx += len(val) + 1
59468c2ecf20Sopenharmony_ci				if idx >= 80:
59478c2ecf20Sopenharmony_ci					idx = 0
59488c2ecf20Sopenharmony_ci					s += '\n    '
59498c2ecf20Sopenharmony_ci				s += val + ' '
59508c2ecf20Sopenharmony_ci			sysvals.vprint(s)
59518c2ecf20Sopenharmony_ci		data.printDetails()
59528c2ecf20Sopenharmony_ci	if len(sysvals.platinfo) > 0:
59538c2ecf20Sopenharmony_ci		sysvals.vprint('\nPlatform Info:')
59548c2ecf20Sopenharmony_ci		for info in sysvals.platinfo:
59558c2ecf20Sopenharmony_ci			sysvals.vprint('[%s - %s]' % (info[0], info[1]))
59568c2ecf20Sopenharmony_ci			sysvals.vprint(info[2])
59578c2ecf20Sopenharmony_ci		sysvals.vprint('')
59588c2ecf20Sopenharmony_ci	if sysvals.cgdump:
59598c2ecf20Sopenharmony_ci		for data in testruns:
59608c2ecf20Sopenharmony_ci			data.debugPrint()
59618c2ecf20Sopenharmony_ci		sys.exit(0)
59628c2ecf20Sopenharmony_ci	if len(testruns) < 1:
59638c2ecf20Sopenharmony_ci		pprint('ERROR: Not enough test data to build a timeline')
59648c2ecf20Sopenharmony_ci		return (testruns, {'error': 'timeline generation failed'})
59658c2ecf20Sopenharmony_ci	sysvals.vprint('Creating the html timeline (%s)...' % sysvals.htmlfile)
59668c2ecf20Sopenharmony_ci	createHTML(testruns, error)
59678c2ecf20Sopenharmony_ci	if not quiet:
59688c2ecf20Sopenharmony_ci		pprint('DONE:       %s' % sysvals.htmlfile)
59698c2ecf20Sopenharmony_ci	data = testruns[0]
59708c2ecf20Sopenharmony_ci	stamp = data.stamp
59718c2ecf20Sopenharmony_ci	stamp['suspend'], stamp['resume'] = data.getTimeValues()
59728c2ecf20Sopenharmony_ci	if data.fwValid:
59738c2ecf20Sopenharmony_ci		stamp['fwsuspend'], stamp['fwresume'] = data.fwSuspend, data.fwResume
59748c2ecf20Sopenharmony_ci	if error:
59758c2ecf20Sopenharmony_ci		stamp['error'] = error
59768c2ecf20Sopenharmony_ci	return (testruns, stamp)
59778c2ecf20Sopenharmony_ci
59788c2ecf20Sopenharmony_ci# Function: rerunTest
59798c2ecf20Sopenharmony_ci# Description:
59808c2ecf20Sopenharmony_ci#	 generate an output from an existing set of ftrace/dmesg logs
59818c2ecf20Sopenharmony_cidef rerunTest(htmlfile=''):
59828c2ecf20Sopenharmony_ci	if sysvals.ftracefile:
59838c2ecf20Sopenharmony_ci		doesTraceLogHaveTraceEvents()
59848c2ecf20Sopenharmony_ci	if not sysvals.dmesgfile and not sysvals.usetraceevents:
59858c2ecf20Sopenharmony_ci		doError('recreating this html output requires a dmesg file')
59868c2ecf20Sopenharmony_ci	if htmlfile:
59878c2ecf20Sopenharmony_ci		sysvals.htmlfile = htmlfile
59888c2ecf20Sopenharmony_ci	else:
59898c2ecf20Sopenharmony_ci		sysvals.setOutputFile()
59908c2ecf20Sopenharmony_ci	if os.path.exists(sysvals.htmlfile):
59918c2ecf20Sopenharmony_ci		if not os.path.isfile(sysvals.htmlfile):
59928c2ecf20Sopenharmony_ci			doError('a directory already exists with this name: %s' % sysvals.htmlfile)
59938c2ecf20Sopenharmony_ci		elif not os.access(sysvals.htmlfile, os.W_OK):
59948c2ecf20Sopenharmony_ci			doError('missing permission to write to %s' % sysvals.htmlfile)
59958c2ecf20Sopenharmony_ci	testruns, stamp = processData()
59968c2ecf20Sopenharmony_ci	sysvals.resetlog()
59978c2ecf20Sopenharmony_ci	return stamp
59988c2ecf20Sopenharmony_ci
59998c2ecf20Sopenharmony_ci# Function: runTest
60008c2ecf20Sopenharmony_ci# Description:
60018c2ecf20Sopenharmony_ci#	 execute a suspend/resume, gather the logs, and generate the output
60028c2ecf20Sopenharmony_cidef runTest(n=0, quiet=False):
60038c2ecf20Sopenharmony_ci	# prepare for the test
60048c2ecf20Sopenharmony_ci	sysvals.initFtrace(quiet)
60058c2ecf20Sopenharmony_ci	sysvals.initTestOutput('suspend')
60068c2ecf20Sopenharmony_ci
60078c2ecf20Sopenharmony_ci	# execute the test
60088c2ecf20Sopenharmony_ci	executeSuspend(quiet)
60098c2ecf20Sopenharmony_ci	sysvals.cleanupFtrace()
60108c2ecf20Sopenharmony_ci	if sysvals.skiphtml:
60118c2ecf20Sopenharmony_ci		sysvals.outputResult({}, n)
60128c2ecf20Sopenharmony_ci		sysvals.sudoUserchown(sysvals.testdir)
60138c2ecf20Sopenharmony_ci		return
60148c2ecf20Sopenharmony_ci	testruns, stamp = processData(True, quiet)
60158c2ecf20Sopenharmony_ci	for data in testruns:
60168c2ecf20Sopenharmony_ci		del data
60178c2ecf20Sopenharmony_ci	sysvals.sudoUserchown(sysvals.testdir)
60188c2ecf20Sopenharmony_ci	sysvals.outputResult(stamp, n)
60198c2ecf20Sopenharmony_ci	if 'error' in stamp:
60208c2ecf20Sopenharmony_ci		return 2
60218c2ecf20Sopenharmony_ci	return 0
60228c2ecf20Sopenharmony_ci
60238c2ecf20Sopenharmony_cidef find_in_html(html, start, end, firstonly=True):
60248c2ecf20Sopenharmony_ci	cnt, out, list = len(html), [], []
60258c2ecf20Sopenharmony_ci	if firstonly:
60268c2ecf20Sopenharmony_ci		m = re.search(start, html)
60278c2ecf20Sopenharmony_ci		if m:
60288c2ecf20Sopenharmony_ci			list.append(m)
60298c2ecf20Sopenharmony_ci	else:
60308c2ecf20Sopenharmony_ci		list = re.finditer(start, html)
60318c2ecf20Sopenharmony_ci	for match in list:
60328c2ecf20Sopenharmony_ci		s = match.end()
60338c2ecf20Sopenharmony_ci		e = cnt if (len(out) < 1 or s + 10000 > cnt) else s + 10000
60348c2ecf20Sopenharmony_ci		m = re.search(end, html[s:e])
60358c2ecf20Sopenharmony_ci		if not m:
60368c2ecf20Sopenharmony_ci			break
60378c2ecf20Sopenharmony_ci		e = s + m.start()
60388c2ecf20Sopenharmony_ci		str = html[s:e]
60398c2ecf20Sopenharmony_ci		if end == 'ms':
60408c2ecf20Sopenharmony_ci			num = re.search(r'[-+]?\d*\.\d+|\d+', str)
60418c2ecf20Sopenharmony_ci			str = num.group() if num else 'NaN'
60428c2ecf20Sopenharmony_ci		if firstonly:
60438c2ecf20Sopenharmony_ci			return str
60448c2ecf20Sopenharmony_ci		out.append(str)
60458c2ecf20Sopenharmony_ci	if firstonly:
60468c2ecf20Sopenharmony_ci		return ''
60478c2ecf20Sopenharmony_ci	return out
60488c2ecf20Sopenharmony_ci
60498c2ecf20Sopenharmony_cidef data_from_html(file, outpath, issues, fulldetail=False):
60508c2ecf20Sopenharmony_ci	html = open(file, 'r').read()
60518c2ecf20Sopenharmony_ci	sysvals.htmlfile = os.path.relpath(file, outpath)
60528c2ecf20Sopenharmony_ci	# extract general info
60538c2ecf20Sopenharmony_ci	suspend = find_in_html(html, 'Kernel Suspend', 'ms')
60548c2ecf20Sopenharmony_ci	resume = find_in_html(html, 'Kernel Resume', 'ms')
60558c2ecf20Sopenharmony_ci	sysinfo = find_in_html(html, '<div class="stamp sysinfo">', '</div>')
60568c2ecf20Sopenharmony_ci	line = find_in_html(html, '<div class="stamp">', '</div>')
60578c2ecf20Sopenharmony_ci	stmp = line.split()
60588c2ecf20Sopenharmony_ci	if not suspend or not resume or len(stmp) != 8:
60598c2ecf20Sopenharmony_ci		return False
60608c2ecf20Sopenharmony_ci	try:
60618c2ecf20Sopenharmony_ci		dt = datetime.strptime(' '.join(stmp[3:]), '%B %d %Y, %I:%M:%S %p')
60628c2ecf20Sopenharmony_ci	except:
60638c2ecf20Sopenharmony_ci		return False
60648c2ecf20Sopenharmony_ci	sysvals.hostname = stmp[0]
60658c2ecf20Sopenharmony_ci	tstr = dt.strftime('%Y/%m/%d %H:%M:%S')
60668c2ecf20Sopenharmony_ci	error = find_in_html(html, '<table class="testfail"><tr><td>', '</td>')
60678c2ecf20Sopenharmony_ci	if error:
60688c2ecf20Sopenharmony_ci		m = re.match('[a-z0-9]* failed in (?P<p>\S*).*', error)
60698c2ecf20Sopenharmony_ci		if m:
60708c2ecf20Sopenharmony_ci			result = 'fail in %s' % m.group('p')
60718c2ecf20Sopenharmony_ci		else:
60728c2ecf20Sopenharmony_ci			result = 'fail'
60738c2ecf20Sopenharmony_ci	else:
60748c2ecf20Sopenharmony_ci		result = 'pass'
60758c2ecf20Sopenharmony_ci	# extract error info
60768c2ecf20Sopenharmony_ci	tp, ilist = False, []
60778c2ecf20Sopenharmony_ci	extra = dict()
60788c2ecf20Sopenharmony_ci	log = find_in_html(html, '<div id="dmesglog" style="display:none;">',
60798c2ecf20Sopenharmony_ci		'</div>').strip()
60808c2ecf20Sopenharmony_ci	if log:
60818c2ecf20Sopenharmony_ci		d = Data(0)
60828c2ecf20Sopenharmony_ci		d.end = 999999999
60838c2ecf20Sopenharmony_ci		d.dmesgtext = log.split('\n')
60848c2ecf20Sopenharmony_ci		tp = d.extractErrorInfo()
60858c2ecf20Sopenharmony_ci		for msg in tp.msglist:
60868c2ecf20Sopenharmony_ci			sysvals.errorSummary(issues, msg)
60878c2ecf20Sopenharmony_ci		if stmp[2] == 'freeze':
60888c2ecf20Sopenharmony_ci			extra = d.turbostatInfo()
60898c2ecf20Sopenharmony_ci		elist = dict()
60908c2ecf20Sopenharmony_ci		for dir in d.errorinfo:
60918c2ecf20Sopenharmony_ci			for err in d.errorinfo[dir]:
60928c2ecf20Sopenharmony_ci				if err[0] not in elist:
60938c2ecf20Sopenharmony_ci					elist[err[0]] = 0
60948c2ecf20Sopenharmony_ci				elist[err[0]] += 1
60958c2ecf20Sopenharmony_ci		for i in elist:
60968c2ecf20Sopenharmony_ci			ilist.append('%sx%d' % (i, elist[i]) if elist[i] > 1 else i)
60978c2ecf20Sopenharmony_ci	wifi = find_in_html(html, 'Wifi Resume: ', '</td>')
60988c2ecf20Sopenharmony_ci	if wifi:
60998c2ecf20Sopenharmony_ci		extra['wifi'] = wifi
61008c2ecf20Sopenharmony_ci	low = find_in_html(html, 'freeze time: <b>', ' ms</b>')
61018c2ecf20Sopenharmony_ci	if low and 'waking' in low:
61028c2ecf20Sopenharmony_ci		issue = 'FREEZEWAKE'
61038c2ecf20Sopenharmony_ci		match = [i for i in issues if i['match'] == issue]
61048c2ecf20Sopenharmony_ci		if len(match) > 0:
61058c2ecf20Sopenharmony_ci			match[0]['count'] += 1
61068c2ecf20Sopenharmony_ci			if sysvals.hostname not in match[0]['urls']:
61078c2ecf20Sopenharmony_ci				match[0]['urls'][sysvals.hostname] = [sysvals.htmlfile]
61088c2ecf20Sopenharmony_ci			elif sysvals.htmlfile not in match[0]['urls'][sysvals.hostname]:
61098c2ecf20Sopenharmony_ci				match[0]['urls'][sysvals.hostname].append(sysvals.htmlfile)
61108c2ecf20Sopenharmony_ci		else:
61118c2ecf20Sopenharmony_ci			issues.append({
61128c2ecf20Sopenharmony_ci				'match': issue, 'count': 1, 'line': issue,
61138c2ecf20Sopenharmony_ci				'urls': {sysvals.hostname: [sysvals.htmlfile]},
61148c2ecf20Sopenharmony_ci			})
61158c2ecf20Sopenharmony_ci		ilist.append(issue)
61168c2ecf20Sopenharmony_ci	# extract device info
61178c2ecf20Sopenharmony_ci	devices = dict()
61188c2ecf20Sopenharmony_ci	for line in html.split('\n'):
61198c2ecf20Sopenharmony_ci		m = re.match(' *<div id=\"[a,0-9]*\" *title=\"(?P<title>.*)\" class=\"thread.*', line)
61208c2ecf20Sopenharmony_ci		if not m or 'thread kth' in line or 'thread sec' in line:
61218c2ecf20Sopenharmony_ci			continue
61228c2ecf20Sopenharmony_ci		m = re.match('(?P<n>.*) \((?P<t>[0-9,\.]*) ms\) (?P<p>.*)', m.group('title'))
61238c2ecf20Sopenharmony_ci		if not m:
61248c2ecf20Sopenharmony_ci			continue
61258c2ecf20Sopenharmony_ci		name, time, phase = m.group('n'), m.group('t'), m.group('p')
61268c2ecf20Sopenharmony_ci		if ' async' in name or ' sync' in name:
61278c2ecf20Sopenharmony_ci			name = ' '.join(name.split(' ')[:-1])
61288c2ecf20Sopenharmony_ci		if phase.startswith('suspend'):
61298c2ecf20Sopenharmony_ci			d = 'suspend'
61308c2ecf20Sopenharmony_ci		elif phase.startswith('resume'):
61318c2ecf20Sopenharmony_ci			d = 'resume'
61328c2ecf20Sopenharmony_ci		else:
61338c2ecf20Sopenharmony_ci			continue
61348c2ecf20Sopenharmony_ci		if d not in devices:
61358c2ecf20Sopenharmony_ci			devices[d] = dict()
61368c2ecf20Sopenharmony_ci		if name not in devices[d]:
61378c2ecf20Sopenharmony_ci			devices[d][name] = 0.0
61388c2ecf20Sopenharmony_ci		devices[d][name] += float(time)
61398c2ecf20Sopenharmony_ci	# create worst device info
61408c2ecf20Sopenharmony_ci	worst = dict()
61418c2ecf20Sopenharmony_ci	for d in ['suspend', 'resume']:
61428c2ecf20Sopenharmony_ci		worst[d] = {'name':'', 'time': 0.0}
61438c2ecf20Sopenharmony_ci		dev = devices[d] if d in devices else 0
61448c2ecf20Sopenharmony_ci		if dev and len(dev.keys()) > 0:
61458c2ecf20Sopenharmony_ci			n = sorted(dev, key=lambda k:(dev[k], k), reverse=True)[0]
61468c2ecf20Sopenharmony_ci			worst[d]['name'], worst[d]['time'] = n, dev[n]
61478c2ecf20Sopenharmony_ci	data = {
61488c2ecf20Sopenharmony_ci		'mode': stmp[2],
61498c2ecf20Sopenharmony_ci		'host': stmp[0],
61508c2ecf20Sopenharmony_ci		'kernel': stmp[1],
61518c2ecf20Sopenharmony_ci		'sysinfo': sysinfo,
61528c2ecf20Sopenharmony_ci		'time': tstr,
61538c2ecf20Sopenharmony_ci		'result': result,
61548c2ecf20Sopenharmony_ci		'issues': ' '.join(ilist),
61558c2ecf20Sopenharmony_ci		'suspend': suspend,
61568c2ecf20Sopenharmony_ci		'resume': resume,
61578c2ecf20Sopenharmony_ci		'devlist': devices,
61588c2ecf20Sopenharmony_ci		'sus_worst': worst['suspend']['name'],
61598c2ecf20Sopenharmony_ci		'sus_worsttime': worst['suspend']['time'],
61608c2ecf20Sopenharmony_ci		'res_worst': worst['resume']['name'],
61618c2ecf20Sopenharmony_ci		'res_worsttime': worst['resume']['time'],
61628c2ecf20Sopenharmony_ci		'url': sysvals.htmlfile,
61638c2ecf20Sopenharmony_ci	}
61648c2ecf20Sopenharmony_ci	for key in extra:
61658c2ecf20Sopenharmony_ci		data[key] = extra[key]
61668c2ecf20Sopenharmony_ci	if fulldetail:
61678c2ecf20Sopenharmony_ci		data['funclist'] = find_in_html(html, '<div title="', '" class="traceevent"', False)
61688c2ecf20Sopenharmony_ci	if tp:
61698c2ecf20Sopenharmony_ci		for arg in ['-multi ', '-info ']:
61708c2ecf20Sopenharmony_ci			if arg in tp.cmdline:
61718c2ecf20Sopenharmony_ci				data['target'] = tp.cmdline[tp.cmdline.find(arg):].split()[1]
61728c2ecf20Sopenharmony_ci				break
61738c2ecf20Sopenharmony_ci	return data
61748c2ecf20Sopenharmony_ci
61758c2ecf20Sopenharmony_cidef genHtml(subdir, force=False):
61768c2ecf20Sopenharmony_ci	for dirname, dirnames, filenames in os.walk(subdir):
61778c2ecf20Sopenharmony_ci		sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = ''
61788c2ecf20Sopenharmony_ci		for filename in filenames:
61798c2ecf20Sopenharmony_ci			file = os.path.join(dirname, filename)
61808c2ecf20Sopenharmony_ci			if sysvals.usable(file):
61818c2ecf20Sopenharmony_ci				if(re.match('.*_dmesg.txt', filename)):
61828c2ecf20Sopenharmony_ci					sysvals.dmesgfile = file
61838c2ecf20Sopenharmony_ci				elif(re.match('.*_ftrace.txt', filename)):
61848c2ecf20Sopenharmony_ci					sysvals.ftracefile = file
61858c2ecf20Sopenharmony_ci		sysvals.setOutputFile()
61868c2ecf20Sopenharmony_ci		if (sysvals.dmesgfile or sysvals.ftracefile) and sysvals.htmlfile and \
61878c2ecf20Sopenharmony_ci			(force or not sysvals.usable(sysvals.htmlfile)):
61888c2ecf20Sopenharmony_ci			pprint('FTRACE: %s' % sysvals.ftracefile)
61898c2ecf20Sopenharmony_ci			if sysvals.dmesgfile:
61908c2ecf20Sopenharmony_ci				pprint('DMESG : %s' % sysvals.dmesgfile)
61918c2ecf20Sopenharmony_ci			rerunTest()
61928c2ecf20Sopenharmony_ci
61938c2ecf20Sopenharmony_ci# Function: runSummary
61948c2ecf20Sopenharmony_ci# Description:
61958c2ecf20Sopenharmony_ci#	 create a summary of tests in a sub-directory
61968c2ecf20Sopenharmony_cidef runSummary(subdir, local=True, genhtml=False):
61978c2ecf20Sopenharmony_ci	inpath = os.path.abspath(subdir)
61988c2ecf20Sopenharmony_ci	outpath = os.path.abspath('.') if local else inpath
61998c2ecf20Sopenharmony_ci	pprint('Generating a summary of folder:\n   %s' % inpath)
62008c2ecf20Sopenharmony_ci	if genhtml:
62018c2ecf20Sopenharmony_ci		genHtml(subdir)
62028c2ecf20Sopenharmony_ci	target, issues, testruns = '', [], []
62038c2ecf20Sopenharmony_ci	desc = {'host':[],'mode':[],'kernel':[]}
62048c2ecf20Sopenharmony_ci	for dirname, dirnames, filenames in os.walk(subdir):
62058c2ecf20Sopenharmony_ci		for filename in filenames:
62068c2ecf20Sopenharmony_ci			if(not re.match('.*.html', filename)):
62078c2ecf20Sopenharmony_ci				continue
62088c2ecf20Sopenharmony_ci			data = data_from_html(os.path.join(dirname, filename), outpath, issues)
62098c2ecf20Sopenharmony_ci			if(not data):
62108c2ecf20Sopenharmony_ci				continue
62118c2ecf20Sopenharmony_ci			if 'target' in data:
62128c2ecf20Sopenharmony_ci				target = data['target']
62138c2ecf20Sopenharmony_ci			testruns.append(data)
62148c2ecf20Sopenharmony_ci			for key in desc:
62158c2ecf20Sopenharmony_ci				if data[key] not in desc[key]:
62168c2ecf20Sopenharmony_ci					desc[key].append(data[key])
62178c2ecf20Sopenharmony_ci	pprint('Summary files:')
62188c2ecf20Sopenharmony_ci	if len(desc['host']) == len(desc['mode']) == len(desc['kernel']) == 1:
62198c2ecf20Sopenharmony_ci		title = '%s %s %s' % (desc['host'][0], desc['kernel'][0], desc['mode'][0])
62208c2ecf20Sopenharmony_ci		if target:
62218c2ecf20Sopenharmony_ci			title += ' %s' % target
62228c2ecf20Sopenharmony_ci	else:
62238c2ecf20Sopenharmony_ci		title = inpath
62248c2ecf20Sopenharmony_ci	createHTMLSummarySimple(testruns, os.path.join(outpath, 'summary.html'), title)
62258c2ecf20Sopenharmony_ci	pprint('   summary.html         - tabular list of test data found')
62268c2ecf20Sopenharmony_ci	createHTMLDeviceSummary(testruns, os.path.join(outpath, 'summary-devices.html'), title)
62278c2ecf20Sopenharmony_ci	pprint('   summary-devices.html - kernel device list sorted by total execution time')
62288c2ecf20Sopenharmony_ci	createHTMLIssuesSummary(testruns, issues, os.path.join(outpath, 'summary-issues.html'), title)
62298c2ecf20Sopenharmony_ci	pprint('   summary-issues.html  - kernel issues found sorted by frequency')
62308c2ecf20Sopenharmony_ci
62318c2ecf20Sopenharmony_ci# Function: checkArgBool
62328c2ecf20Sopenharmony_ci# Description:
62338c2ecf20Sopenharmony_ci#	 check if a boolean string value is true or false
62348c2ecf20Sopenharmony_cidef checkArgBool(name, value):
62358c2ecf20Sopenharmony_ci	if value in switchvalues:
62368c2ecf20Sopenharmony_ci		if value in switchoff:
62378c2ecf20Sopenharmony_ci			return False
62388c2ecf20Sopenharmony_ci		return True
62398c2ecf20Sopenharmony_ci	doError('invalid boolean --> (%s: %s), use "true/false" or "1/0"' % (name, value), True)
62408c2ecf20Sopenharmony_ci	return False
62418c2ecf20Sopenharmony_ci
62428c2ecf20Sopenharmony_ci# Function: configFromFile
62438c2ecf20Sopenharmony_ci# Description:
62448c2ecf20Sopenharmony_ci#	 Configure the script via the info in a config file
62458c2ecf20Sopenharmony_cidef configFromFile(file):
62468c2ecf20Sopenharmony_ci	Config = configparser.ConfigParser()
62478c2ecf20Sopenharmony_ci
62488c2ecf20Sopenharmony_ci	Config.read(file)
62498c2ecf20Sopenharmony_ci	sections = Config.sections()
62508c2ecf20Sopenharmony_ci	overridekprobes = False
62518c2ecf20Sopenharmony_ci	overridedevkprobes = False
62528c2ecf20Sopenharmony_ci	if 'Settings' in sections:
62538c2ecf20Sopenharmony_ci		for opt in Config.options('Settings'):
62548c2ecf20Sopenharmony_ci			value = Config.get('Settings', opt).lower()
62558c2ecf20Sopenharmony_ci			option = opt.lower()
62568c2ecf20Sopenharmony_ci			if(option == 'verbose'):
62578c2ecf20Sopenharmony_ci				sysvals.verbose = checkArgBool(option, value)
62588c2ecf20Sopenharmony_ci			elif(option == 'addlogs'):
62598c2ecf20Sopenharmony_ci				sysvals.dmesglog = sysvals.ftracelog = checkArgBool(option, value)
62608c2ecf20Sopenharmony_ci			elif(option == 'dev'):
62618c2ecf20Sopenharmony_ci				sysvals.usedevsrc = checkArgBool(option, value)
62628c2ecf20Sopenharmony_ci			elif(option == 'proc'):
62638c2ecf20Sopenharmony_ci				sysvals.useprocmon = checkArgBool(option, value)
62648c2ecf20Sopenharmony_ci			elif(option == 'x2'):
62658c2ecf20Sopenharmony_ci				if checkArgBool(option, value):
62668c2ecf20Sopenharmony_ci					sysvals.execcount = 2
62678c2ecf20Sopenharmony_ci			elif(option == 'callgraph'):
62688c2ecf20Sopenharmony_ci				sysvals.usecallgraph = checkArgBool(option, value)
62698c2ecf20Sopenharmony_ci			elif(option == 'override-timeline-functions'):
62708c2ecf20Sopenharmony_ci				overridekprobes = checkArgBool(option, value)
62718c2ecf20Sopenharmony_ci			elif(option == 'override-dev-timeline-functions'):
62728c2ecf20Sopenharmony_ci				overridedevkprobes = checkArgBool(option, value)
62738c2ecf20Sopenharmony_ci			elif(option == 'skiphtml'):
62748c2ecf20Sopenharmony_ci				sysvals.skiphtml = checkArgBool(option, value)
62758c2ecf20Sopenharmony_ci			elif(option == 'sync'):
62768c2ecf20Sopenharmony_ci				sysvals.sync = checkArgBool(option, value)
62778c2ecf20Sopenharmony_ci			elif(option == 'rs' or option == 'runtimesuspend'):
62788c2ecf20Sopenharmony_ci				if value in switchvalues:
62798c2ecf20Sopenharmony_ci					if value in switchoff:
62808c2ecf20Sopenharmony_ci						sysvals.rs = -1
62818c2ecf20Sopenharmony_ci					else:
62828c2ecf20Sopenharmony_ci						sysvals.rs = 1
62838c2ecf20Sopenharmony_ci				else:
62848c2ecf20Sopenharmony_ci					doError('invalid value --> (%s: %s), use "enable/disable"' % (option, value), True)
62858c2ecf20Sopenharmony_ci			elif(option == 'display'):
62868c2ecf20Sopenharmony_ci				disopt = ['on', 'off', 'standby', 'suspend']
62878c2ecf20Sopenharmony_ci				if value not in disopt:
62888c2ecf20Sopenharmony_ci					doError('invalid value --> (%s: %s), use %s' % (option, value, disopt), True)
62898c2ecf20Sopenharmony_ci				sysvals.display = value
62908c2ecf20Sopenharmony_ci			elif(option == 'gzip'):
62918c2ecf20Sopenharmony_ci				sysvals.gzip = checkArgBool(option, value)
62928c2ecf20Sopenharmony_ci			elif(option == 'cgfilter'):
62938c2ecf20Sopenharmony_ci				sysvals.setCallgraphFilter(value)
62948c2ecf20Sopenharmony_ci			elif(option == 'cgskip'):
62958c2ecf20Sopenharmony_ci				if value in switchoff:
62968c2ecf20Sopenharmony_ci					sysvals.cgskip = ''
62978c2ecf20Sopenharmony_ci				else:
62988c2ecf20Sopenharmony_ci					sysvals.cgskip = sysvals.configFile(val)
62998c2ecf20Sopenharmony_ci					if(not sysvals.cgskip):
63008c2ecf20Sopenharmony_ci						doError('%s does not exist' % sysvals.cgskip)
63018c2ecf20Sopenharmony_ci			elif(option == 'cgtest'):
63028c2ecf20Sopenharmony_ci				sysvals.cgtest = getArgInt('cgtest', value, 0, 1, False)
63038c2ecf20Sopenharmony_ci			elif(option == 'cgphase'):
63048c2ecf20Sopenharmony_ci				d = Data(0)
63058c2ecf20Sopenharmony_ci				if value not in d.phasedef:
63068c2ecf20Sopenharmony_ci					doError('invalid phase --> (%s: %s), valid phases are %s'\
63078c2ecf20Sopenharmony_ci						% (option, value, d.phasedef.keys()), True)
63088c2ecf20Sopenharmony_ci				sysvals.cgphase = value
63098c2ecf20Sopenharmony_ci			elif(option == 'fadd'):
63108c2ecf20Sopenharmony_ci				file = sysvals.configFile(value)
63118c2ecf20Sopenharmony_ci				if(not file):
63128c2ecf20Sopenharmony_ci					doError('%s does not exist' % value)
63138c2ecf20Sopenharmony_ci				sysvals.addFtraceFilterFunctions(file)
63148c2ecf20Sopenharmony_ci			elif(option == 'result'):
63158c2ecf20Sopenharmony_ci				sysvals.result = value
63168c2ecf20Sopenharmony_ci			elif(option == 'multi'):
63178c2ecf20Sopenharmony_ci				nums = value.split()
63188c2ecf20Sopenharmony_ci				if len(nums) != 2:
63198c2ecf20Sopenharmony_ci					doError('multi requires 2 integers (exec_count and delay)', True)
63208c2ecf20Sopenharmony_ci				sysvals.multiinit(nums[0], nums[1])
63218c2ecf20Sopenharmony_ci			elif(option == 'devicefilter'):
63228c2ecf20Sopenharmony_ci				sysvals.setDeviceFilter(value)
63238c2ecf20Sopenharmony_ci			elif(option == 'expandcg'):
63248c2ecf20Sopenharmony_ci				sysvals.cgexp = checkArgBool(option, value)
63258c2ecf20Sopenharmony_ci			elif(option == 'srgap'):
63268c2ecf20Sopenharmony_ci				if checkArgBool(option, value):
63278c2ecf20Sopenharmony_ci					sysvals.srgap = 5
63288c2ecf20Sopenharmony_ci			elif(option == 'mode'):
63298c2ecf20Sopenharmony_ci				sysvals.suspendmode = value
63308c2ecf20Sopenharmony_ci			elif(option == 'command' or option == 'cmd'):
63318c2ecf20Sopenharmony_ci				sysvals.testcommand = value
63328c2ecf20Sopenharmony_ci			elif(option == 'x2delay'):
63338c2ecf20Sopenharmony_ci				sysvals.x2delay = getArgInt('x2delay', value, 0, 60000, False)
63348c2ecf20Sopenharmony_ci			elif(option == 'predelay'):
63358c2ecf20Sopenharmony_ci				sysvals.predelay = getArgInt('predelay', value, 0, 60000, False)
63368c2ecf20Sopenharmony_ci			elif(option == 'postdelay'):
63378c2ecf20Sopenharmony_ci				sysvals.postdelay = getArgInt('postdelay', value, 0, 60000, False)
63388c2ecf20Sopenharmony_ci			elif(option == 'maxdepth'):
63398c2ecf20Sopenharmony_ci				sysvals.max_graph_depth = getArgInt('maxdepth', value, 0, 1000, False)
63408c2ecf20Sopenharmony_ci			elif(option == 'rtcwake'):
63418c2ecf20Sopenharmony_ci				if value in switchoff:
63428c2ecf20Sopenharmony_ci					sysvals.rtcwake = False
63438c2ecf20Sopenharmony_ci				else:
63448c2ecf20Sopenharmony_ci					sysvals.rtcwake = True
63458c2ecf20Sopenharmony_ci					sysvals.rtcwaketime = getArgInt('rtcwake', value, 0, 3600, False)
63468c2ecf20Sopenharmony_ci			elif(option == 'timeprec'):
63478c2ecf20Sopenharmony_ci				sysvals.setPrecision(getArgInt('timeprec', value, 0, 6, False))
63488c2ecf20Sopenharmony_ci			elif(option == 'mindev'):
63498c2ecf20Sopenharmony_ci				sysvals.mindevlen = getArgFloat('mindev', value, 0.0, 10000.0, False)
63508c2ecf20Sopenharmony_ci			elif(option == 'callloop-maxgap'):
63518c2ecf20Sopenharmony_ci				sysvals.callloopmaxgap = getArgFloat('callloop-maxgap', value, 0.0, 1.0, False)
63528c2ecf20Sopenharmony_ci			elif(option == 'callloop-maxlen'):
63538c2ecf20Sopenharmony_ci				sysvals.callloopmaxgap = getArgFloat('callloop-maxlen', value, 0.0, 1.0, False)
63548c2ecf20Sopenharmony_ci			elif(option == 'mincg'):
63558c2ecf20Sopenharmony_ci				sysvals.mincglen = getArgFloat('mincg', value, 0.0, 10000.0, False)
63568c2ecf20Sopenharmony_ci			elif(option == 'bufsize'):
63578c2ecf20Sopenharmony_ci				sysvals.bufsize = getArgInt('bufsize', value, 1, 1024*1024*8, False)
63588c2ecf20Sopenharmony_ci			elif(option == 'output-dir'):
63598c2ecf20Sopenharmony_ci				sysvals.outdir = sysvals.setOutputFolder(value)
63608c2ecf20Sopenharmony_ci
63618c2ecf20Sopenharmony_ci	if sysvals.suspendmode == 'command' and not sysvals.testcommand:
63628c2ecf20Sopenharmony_ci		doError('No command supplied for mode "command"')
63638c2ecf20Sopenharmony_ci
63648c2ecf20Sopenharmony_ci	# compatibility errors
63658c2ecf20Sopenharmony_ci	if sysvals.usedevsrc and sysvals.usecallgraph:
63668c2ecf20Sopenharmony_ci		doError('-dev is not compatible with -f')
63678c2ecf20Sopenharmony_ci	if sysvals.usecallgraph and sysvals.useprocmon:
63688c2ecf20Sopenharmony_ci		doError('-proc is not compatible with -f')
63698c2ecf20Sopenharmony_ci
63708c2ecf20Sopenharmony_ci	if overridekprobes:
63718c2ecf20Sopenharmony_ci		sysvals.tracefuncs = dict()
63728c2ecf20Sopenharmony_ci	if overridedevkprobes:
63738c2ecf20Sopenharmony_ci		sysvals.dev_tracefuncs = dict()
63748c2ecf20Sopenharmony_ci
63758c2ecf20Sopenharmony_ci	kprobes = dict()
63768c2ecf20Sopenharmony_ci	kprobesec = 'dev_timeline_functions_'+platform.machine()
63778c2ecf20Sopenharmony_ci	if kprobesec in sections:
63788c2ecf20Sopenharmony_ci		for name in Config.options(kprobesec):
63798c2ecf20Sopenharmony_ci			text = Config.get(kprobesec, name)
63808c2ecf20Sopenharmony_ci			kprobes[name] = (text, True)
63818c2ecf20Sopenharmony_ci	kprobesec = 'timeline_functions_'+platform.machine()
63828c2ecf20Sopenharmony_ci	if kprobesec in sections:
63838c2ecf20Sopenharmony_ci		for name in Config.options(kprobesec):
63848c2ecf20Sopenharmony_ci			if name in kprobes:
63858c2ecf20Sopenharmony_ci				doError('Duplicate timeline function found "%s"' % (name))
63868c2ecf20Sopenharmony_ci			text = Config.get(kprobesec, name)
63878c2ecf20Sopenharmony_ci			kprobes[name] = (text, False)
63888c2ecf20Sopenharmony_ci
63898c2ecf20Sopenharmony_ci	for name in kprobes:
63908c2ecf20Sopenharmony_ci		function = name
63918c2ecf20Sopenharmony_ci		format = name
63928c2ecf20Sopenharmony_ci		color = ''
63938c2ecf20Sopenharmony_ci		args = dict()
63948c2ecf20Sopenharmony_ci		text, dev = kprobes[name]
63958c2ecf20Sopenharmony_ci		data = text.split()
63968c2ecf20Sopenharmony_ci		i = 0
63978c2ecf20Sopenharmony_ci		for val in data:
63988c2ecf20Sopenharmony_ci			# bracketted strings are special formatting, read them separately
63998c2ecf20Sopenharmony_ci			if val[0] == '[' and val[-1] == ']':
64008c2ecf20Sopenharmony_ci				for prop in val[1:-1].split(','):
64018c2ecf20Sopenharmony_ci					p = prop.split('=')
64028c2ecf20Sopenharmony_ci					if p[0] == 'color':
64038c2ecf20Sopenharmony_ci						try:
64048c2ecf20Sopenharmony_ci							color = int(p[1], 16)
64058c2ecf20Sopenharmony_ci							color = '#'+p[1]
64068c2ecf20Sopenharmony_ci						except:
64078c2ecf20Sopenharmony_ci							color = p[1]
64088c2ecf20Sopenharmony_ci				continue
64098c2ecf20Sopenharmony_ci			# first real arg should be the format string
64108c2ecf20Sopenharmony_ci			if i == 0:
64118c2ecf20Sopenharmony_ci				format = val
64128c2ecf20Sopenharmony_ci			# all other args are actual function args
64138c2ecf20Sopenharmony_ci			else:
64148c2ecf20Sopenharmony_ci				d = val.split('=')
64158c2ecf20Sopenharmony_ci				args[d[0]] = d[1]
64168c2ecf20Sopenharmony_ci			i += 1
64178c2ecf20Sopenharmony_ci		if not function or not format:
64188c2ecf20Sopenharmony_ci			doError('Invalid kprobe: %s' % name)
64198c2ecf20Sopenharmony_ci		for arg in re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', format):
64208c2ecf20Sopenharmony_ci			if arg not in args:
64218c2ecf20Sopenharmony_ci				doError('Kprobe "%s" is missing argument "%s"' % (name, arg))
64228c2ecf20Sopenharmony_ci		if (dev and name in sysvals.dev_tracefuncs) or (not dev and name in sysvals.tracefuncs):
64238c2ecf20Sopenharmony_ci			doError('Duplicate timeline function found "%s"' % (name))
64248c2ecf20Sopenharmony_ci
64258c2ecf20Sopenharmony_ci		kp = {
64268c2ecf20Sopenharmony_ci			'name': name,
64278c2ecf20Sopenharmony_ci			'func': function,
64288c2ecf20Sopenharmony_ci			'format': format,
64298c2ecf20Sopenharmony_ci			sysvals.archargs: args
64308c2ecf20Sopenharmony_ci		}
64318c2ecf20Sopenharmony_ci		if color:
64328c2ecf20Sopenharmony_ci			kp['color'] = color
64338c2ecf20Sopenharmony_ci		if dev:
64348c2ecf20Sopenharmony_ci			sysvals.dev_tracefuncs[name] = kp
64358c2ecf20Sopenharmony_ci		else:
64368c2ecf20Sopenharmony_ci			sysvals.tracefuncs[name] = kp
64378c2ecf20Sopenharmony_ci
64388c2ecf20Sopenharmony_ci# Function: printHelp
64398c2ecf20Sopenharmony_ci# Description:
64408c2ecf20Sopenharmony_ci#	 print out the help text
64418c2ecf20Sopenharmony_cidef printHelp():
64428c2ecf20Sopenharmony_ci	pprint('\n%s v%s\n'\
64438c2ecf20Sopenharmony_ci	'Usage: sudo sleepgraph <options> <commands>\n'\
64448c2ecf20Sopenharmony_ci	'\n'\
64458c2ecf20Sopenharmony_ci	'Description:\n'\
64468c2ecf20Sopenharmony_ci	'  This tool is designed to assist kernel and OS developers in optimizing\n'\
64478c2ecf20Sopenharmony_ci	'  their linux stack\'s suspend/resume time. Using a kernel image built\n'\
64488c2ecf20Sopenharmony_ci	'  with a few extra options enabled, the tool will execute a suspend and\n'\
64498c2ecf20Sopenharmony_ci	'  capture dmesg and ftrace data until resume is complete. This data is\n'\
64508c2ecf20Sopenharmony_ci	'  transformed into a device timeline and an optional callgraph to give\n'\
64518c2ecf20Sopenharmony_ci	'  a detailed view of which devices/subsystems are taking the most\n'\
64528c2ecf20Sopenharmony_ci	'  time in suspend/resume.\n'\
64538c2ecf20Sopenharmony_ci	'\n'\
64548c2ecf20Sopenharmony_ci	'  If no specific command is given, the default behavior is to initiate\n'\
64558c2ecf20Sopenharmony_ci	'  a suspend/resume and capture the dmesg/ftrace output as an html timeline.\n'\
64568c2ecf20Sopenharmony_ci	'\n'\
64578c2ecf20Sopenharmony_ci	'  Generates output files in subdirectory: suspend-yymmdd-HHMMSS\n'\
64588c2ecf20Sopenharmony_ci	'   HTML output:                    <hostname>_<mode>.html\n'\
64598c2ecf20Sopenharmony_ci	'   raw dmesg output:               <hostname>_<mode>_dmesg.txt\n'\
64608c2ecf20Sopenharmony_ci	'   raw ftrace output:              <hostname>_<mode>_ftrace.txt\n'\
64618c2ecf20Sopenharmony_ci	'\n'\
64628c2ecf20Sopenharmony_ci	'Options:\n'\
64638c2ecf20Sopenharmony_ci	'   -h           Print this help text\n'\
64648c2ecf20Sopenharmony_ci	'   -v           Print the current tool version\n'\
64658c2ecf20Sopenharmony_ci	'   -config fn   Pull arguments and config options from file fn\n'\
64668c2ecf20Sopenharmony_ci	'   -verbose     Print extra information during execution and analysis\n'\
64678c2ecf20Sopenharmony_ci	'   -m mode      Mode to initiate for suspend (default: %s)\n'\
64688c2ecf20Sopenharmony_ci	'   -o name      Overrides the output subdirectory name when running a new test\n'\
64698c2ecf20Sopenharmony_ci	'                default: suspend-{date}-{time}\n'\
64708c2ecf20Sopenharmony_ci	'   -rtcwake t   Wakeup t seconds after suspend, set t to "off" to disable (default: 15)\n'\
64718c2ecf20Sopenharmony_ci	'   -addlogs     Add the dmesg and ftrace logs to the html output\n'\
64728c2ecf20Sopenharmony_ci	'   -noturbostat Dont use turbostat in freeze mode (default: disabled)\n'\
64738c2ecf20Sopenharmony_ci	'   -srgap       Add a visible gap in the timeline between sus/res (default: disabled)\n'\
64748c2ecf20Sopenharmony_ci	'   -skiphtml    Run the test and capture the trace logs, but skip the timeline (default: disabled)\n'\
64758c2ecf20Sopenharmony_ci	'   -result fn   Export a results table to a text file for parsing.\n'\
64768c2ecf20Sopenharmony_ci	'   -wifi        If a wifi connection is available, check that it reconnects after resume.\n'\
64778c2ecf20Sopenharmony_ci	'  [testprep]\n'\
64788c2ecf20Sopenharmony_ci	'   -sync        Sync the filesystems before starting the test\n'\
64798c2ecf20Sopenharmony_ci	'   -rs on/off   Enable/disable runtime suspend for all devices, restore all after test\n'\
64808c2ecf20Sopenharmony_ci	'   -display m   Change the display mode to m for the test (on/off/standby/suspend)\n'\
64818c2ecf20Sopenharmony_ci	'  [advanced]\n'\
64828c2ecf20Sopenharmony_ci	'   -gzip        Gzip the trace and dmesg logs to save space\n'\
64838c2ecf20Sopenharmony_ci	'   -cmd {s}     Run the timeline over a custom command, e.g. "sync -d"\n'\
64848c2ecf20Sopenharmony_ci	'   -proc        Add usermode process info into the timeline (default: disabled)\n'\
64858c2ecf20Sopenharmony_ci	'   -dev         Add kernel function calls and threads to the timeline (default: disabled)\n'\
64868c2ecf20Sopenharmony_ci	'   -x2          Run two suspend/resumes back to back (default: disabled)\n'\
64878c2ecf20Sopenharmony_ci	'   -x2delay t   Include t ms delay between multiple test runs (default: 0 ms)\n'\
64888c2ecf20Sopenharmony_ci	'   -predelay t  Include t ms delay before 1st suspend (default: 0 ms)\n'\
64898c2ecf20Sopenharmony_ci	'   -postdelay t Include t ms delay after last resume (default: 0 ms)\n'\
64908c2ecf20Sopenharmony_ci	'   -mindev ms   Discard all device blocks shorter than ms milliseconds (e.g. 0.001 for us)\n'\
64918c2ecf20Sopenharmony_ci	'   -multi n d   Execute <n> consecutive tests at <d> seconds intervals. If <n> is followed\n'\
64928c2ecf20Sopenharmony_ci	'                by a "d", "h", or "m" execute for <n> days, hours, or mins instead.\n'\
64938c2ecf20Sopenharmony_ci	'                The outputs will be created in a new subdirectory with a summary page.\n'\
64948c2ecf20Sopenharmony_ci	'   -maxfail n   Abort a -multi run after n consecutive fails (default is 0 = never abort)\n'\
64958c2ecf20Sopenharmony_ci	'  [debug]\n'\
64968c2ecf20Sopenharmony_ci	'   -f           Use ftrace to create device callgraphs (default: disabled)\n'\
64978c2ecf20Sopenharmony_ci	'   -ftop        Use ftrace on the top level call: "%s" (default: disabled)\n'\
64988c2ecf20Sopenharmony_ci	'   -maxdepth N  limit the callgraph data to N call levels (default: 0=all)\n'\
64998c2ecf20Sopenharmony_ci	'   -expandcg    pre-expand the callgraph data in the html output (default: disabled)\n'\
65008c2ecf20Sopenharmony_ci	'   -fadd file   Add functions to be graphed in the timeline from a list in a text file\n'\
65018c2ecf20Sopenharmony_ci	'   -filter "d1,d2,..." Filter out all but this comma-delimited list of device names\n'\
65028c2ecf20Sopenharmony_ci	'   -mincg  ms   Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)\n'\
65038c2ecf20Sopenharmony_ci	'   -cgphase P   Only show callgraph data for phase P (e.g. suspend_late)\n'\
65048c2ecf20Sopenharmony_ci	'   -cgtest N    Only show callgraph data for test N (e.g. 0 or 1 in an x2 run)\n'\
65058c2ecf20Sopenharmony_ci	'   -timeprec N  Number of significant digits in timestamps (0:S, [3:ms], 6:us)\n'\
65068c2ecf20Sopenharmony_ci	'   -cgfilter S  Filter the callgraph output in the timeline\n'\
65078c2ecf20Sopenharmony_ci	'   -cgskip file Callgraph functions to skip, off to disable (default: cgskip.txt)\n'\
65088c2ecf20Sopenharmony_ci	'   -bufsize N   Set trace buffer size to N kilo-bytes (default: all of free memory)\n'\
65098c2ecf20Sopenharmony_ci	'   -devdump     Print out all the raw device data for each phase\n'\
65108c2ecf20Sopenharmony_ci	'   -cgdump      Print out all the raw callgraph data\n'\
65118c2ecf20Sopenharmony_ci	'\n'\
65128c2ecf20Sopenharmony_ci	'Other commands:\n'\
65138c2ecf20Sopenharmony_ci	'   -modes       List available suspend modes\n'\
65148c2ecf20Sopenharmony_ci	'   -status      Test to see if the system is enabled to run this tool\n'\
65158c2ecf20Sopenharmony_ci	'   -fpdt        Print out the contents of the ACPI Firmware Performance Data Table\n'\
65168c2ecf20Sopenharmony_ci	'   -wificheck   Print out wifi connection info\n'\
65178c2ecf20Sopenharmony_ci	'   -x<mode>     Test xset by toggling the given mode (on/off/standby/suspend)\n'\
65188c2ecf20Sopenharmony_ci	'   -sysinfo     Print out system info extracted from BIOS\n'\
65198c2ecf20Sopenharmony_ci	'   -devinfo     Print out the pm settings of all devices which support runtime suspend\n'\
65208c2ecf20Sopenharmony_ci	'   -cmdinfo     Print out all the platform info collected before and after suspend/resume\n'\
65218c2ecf20Sopenharmony_ci	'   -flist       Print the list of functions currently being captured in ftrace\n'\
65228c2ecf20Sopenharmony_ci	'   -flistall    Print all functions capable of being captured in ftrace\n'\
65238c2ecf20Sopenharmony_ci	'   -summary dir Create a summary of tests in this dir [-genhtml builds missing html]\n'\
65248c2ecf20Sopenharmony_ci	'  [redo]\n'\
65258c2ecf20Sopenharmony_ci	'   -ftrace ftracefile  Create HTML output using ftrace input (used with -dmesg)\n'\
65268c2ecf20Sopenharmony_ci	'   -dmesg dmesgfile    Create HTML output using dmesg (used with -ftrace)\n'\
65278c2ecf20Sopenharmony_ci	'' % (sysvals.title, sysvals.version, sysvals.suspendmode, sysvals.ftopfunc))
65288c2ecf20Sopenharmony_ci	return True
65298c2ecf20Sopenharmony_ci
65308c2ecf20Sopenharmony_ci# ----------------- MAIN --------------------
65318c2ecf20Sopenharmony_ci# exec start (skipped if script is loaded as library)
65328c2ecf20Sopenharmony_ciif __name__ == '__main__':
65338c2ecf20Sopenharmony_ci	genhtml = False
65348c2ecf20Sopenharmony_ci	cmd = ''
65358c2ecf20Sopenharmony_ci	simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall',
65368c2ecf20Sopenharmony_ci		'-devinfo', '-status', '-xon', '-xoff', '-xstandby', '-xsuspend',
65378c2ecf20Sopenharmony_ci		'-xinit', '-xreset', '-xstat', '-wificheck', '-cmdinfo']
65388c2ecf20Sopenharmony_ci	if '-f' in sys.argv:
65398c2ecf20Sopenharmony_ci		sysvals.cgskip = sysvals.configFile('cgskip.txt')
65408c2ecf20Sopenharmony_ci	# loop through the command line arguments
65418c2ecf20Sopenharmony_ci	args = iter(sys.argv[1:])
65428c2ecf20Sopenharmony_ci	for arg in args:
65438c2ecf20Sopenharmony_ci		if(arg == '-m'):
65448c2ecf20Sopenharmony_ci			try:
65458c2ecf20Sopenharmony_ci				val = next(args)
65468c2ecf20Sopenharmony_ci			except:
65478c2ecf20Sopenharmony_ci				doError('No mode supplied', True)
65488c2ecf20Sopenharmony_ci			if val == 'command' and not sysvals.testcommand:
65498c2ecf20Sopenharmony_ci				doError('No command supplied for mode "command"', True)
65508c2ecf20Sopenharmony_ci			sysvals.suspendmode = val
65518c2ecf20Sopenharmony_ci		elif(arg in simplecmds):
65528c2ecf20Sopenharmony_ci			cmd = arg[1:]
65538c2ecf20Sopenharmony_ci		elif(arg == '-h'):
65548c2ecf20Sopenharmony_ci			printHelp()
65558c2ecf20Sopenharmony_ci			sys.exit(0)
65568c2ecf20Sopenharmony_ci		elif(arg == '-v'):
65578c2ecf20Sopenharmony_ci			pprint("Version %s" % sysvals.version)
65588c2ecf20Sopenharmony_ci			sys.exit(0)
65598c2ecf20Sopenharmony_ci		elif(arg == '-x2'):
65608c2ecf20Sopenharmony_ci			sysvals.execcount = 2
65618c2ecf20Sopenharmony_ci		elif(arg == '-x2delay'):
65628c2ecf20Sopenharmony_ci			sysvals.x2delay = getArgInt('-x2delay', args, 0, 60000)
65638c2ecf20Sopenharmony_ci		elif(arg == '-predelay'):
65648c2ecf20Sopenharmony_ci			sysvals.predelay = getArgInt('-predelay', args, 0, 60000)
65658c2ecf20Sopenharmony_ci		elif(arg == '-postdelay'):
65668c2ecf20Sopenharmony_ci			sysvals.postdelay = getArgInt('-postdelay', args, 0, 60000)
65678c2ecf20Sopenharmony_ci		elif(arg == '-f'):
65688c2ecf20Sopenharmony_ci			sysvals.usecallgraph = True
65698c2ecf20Sopenharmony_ci		elif(arg == '-ftop'):
65708c2ecf20Sopenharmony_ci			sysvals.usecallgraph = True
65718c2ecf20Sopenharmony_ci			sysvals.ftop = True
65728c2ecf20Sopenharmony_ci			sysvals.usekprobes = False
65738c2ecf20Sopenharmony_ci		elif(arg == '-skiphtml'):
65748c2ecf20Sopenharmony_ci			sysvals.skiphtml = True
65758c2ecf20Sopenharmony_ci		elif(arg == '-cgdump'):
65768c2ecf20Sopenharmony_ci			sysvals.cgdump = True
65778c2ecf20Sopenharmony_ci		elif(arg == '-devdump'):
65788c2ecf20Sopenharmony_ci			sysvals.devdump = True
65798c2ecf20Sopenharmony_ci		elif(arg == '-genhtml'):
65808c2ecf20Sopenharmony_ci			genhtml = True
65818c2ecf20Sopenharmony_ci		elif(arg == '-addlogs'):
65828c2ecf20Sopenharmony_ci			sysvals.dmesglog = sysvals.ftracelog = True
65838c2ecf20Sopenharmony_ci		elif(arg == '-nologs'):
65848c2ecf20Sopenharmony_ci			sysvals.dmesglog = sysvals.ftracelog = False
65858c2ecf20Sopenharmony_ci		elif(arg == '-addlogdmesg'):
65868c2ecf20Sopenharmony_ci			sysvals.dmesglog = True
65878c2ecf20Sopenharmony_ci		elif(arg == '-addlogftrace'):
65888c2ecf20Sopenharmony_ci			sysvals.ftracelog = True
65898c2ecf20Sopenharmony_ci		elif(arg == '-noturbostat'):
65908c2ecf20Sopenharmony_ci			sysvals.tstat = False
65918c2ecf20Sopenharmony_ci		elif(arg == '-verbose'):
65928c2ecf20Sopenharmony_ci			sysvals.verbose = True
65938c2ecf20Sopenharmony_ci		elif(arg == '-proc'):
65948c2ecf20Sopenharmony_ci			sysvals.useprocmon = True
65958c2ecf20Sopenharmony_ci		elif(arg == '-dev'):
65968c2ecf20Sopenharmony_ci			sysvals.usedevsrc = True
65978c2ecf20Sopenharmony_ci		elif(arg == '-sync'):
65988c2ecf20Sopenharmony_ci			sysvals.sync = True
65998c2ecf20Sopenharmony_ci		elif(arg == '-wifi'):
66008c2ecf20Sopenharmony_ci			sysvals.wifi = True
66018c2ecf20Sopenharmony_ci		elif(arg == '-gzip'):
66028c2ecf20Sopenharmony_ci			sysvals.gzip = True
66038c2ecf20Sopenharmony_ci		elif(arg == '-info'):
66048c2ecf20Sopenharmony_ci			try:
66058c2ecf20Sopenharmony_ci				val = next(args)
66068c2ecf20Sopenharmony_ci			except:
66078c2ecf20Sopenharmony_ci				doError('-info requires one string argument', True)
66088c2ecf20Sopenharmony_ci		elif(arg == '-rs'):
66098c2ecf20Sopenharmony_ci			try:
66108c2ecf20Sopenharmony_ci				val = next(args)
66118c2ecf20Sopenharmony_ci			except:
66128c2ecf20Sopenharmony_ci				doError('-rs requires "enable" or "disable"', True)
66138c2ecf20Sopenharmony_ci			if val.lower() in switchvalues:
66148c2ecf20Sopenharmony_ci				if val.lower() in switchoff:
66158c2ecf20Sopenharmony_ci					sysvals.rs = -1
66168c2ecf20Sopenharmony_ci				else:
66178c2ecf20Sopenharmony_ci					sysvals.rs = 1
66188c2ecf20Sopenharmony_ci			else:
66198c2ecf20Sopenharmony_ci				doError('invalid option: %s, use "enable/disable" or "on/off"' % val, True)
66208c2ecf20Sopenharmony_ci		elif(arg == '-display'):
66218c2ecf20Sopenharmony_ci			try:
66228c2ecf20Sopenharmony_ci				val = next(args)
66238c2ecf20Sopenharmony_ci			except:
66248c2ecf20Sopenharmony_ci				doError('-display requires an mode value', True)
66258c2ecf20Sopenharmony_ci			disopt = ['on', 'off', 'standby', 'suspend']
66268c2ecf20Sopenharmony_ci			if val.lower() not in disopt:
66278c2ecf20Sopenharmony_ci				doError('valid display mode values are %s' % disopt, True)
66288c2ecf20Sopenharmony_ci			sysvals.display = val.lower()
66298c2ecf20Sopenharmony_ci		elif(arg == '-maxdepth'):
66308c2ecf20Sopenharmony_ci			sysvals.max_graph_depth = getArgInt('-maxdepth', args, 0, 1000)
66318c2ecf20Sopenharmony_ci		elif(arg == '-rtcwake'):
66328c2ecf20Sopenharmony_ci			try:
66338c2ecf20Sopenharmony_ci				val = next(args)
66348c2ecf20Sopenharmony_ci			except:
66358c2ecf20Sopenharmony_ci				doError('No rtcwake time supplied', True)
66368c2ecf20Sopenharmony_ci			if val.lower() in switchoff:
66378c2ecf20Sopenharmony_ci				sysvals.rtcwake = False
66388c2ecf20Sopenharmony_ci			else:
66398c2ecf20Sopenharmony_ci				sysvals.rtcwake = True
66408c2ecf20Sopenharmony_ci				sysvals.rtcwaketime = getArgInt('-rtcwake', val, 0, 3600, False)
66418c2ecf20Sopenharmony_ci		elif(arg == '-timeprec'):
66428c2ecf20Sopenharmony_ci			sysvals.setPrecision(getArgInt('-timeprec', args, 0, 6))
66438c2ecf20Sopenharmony_ci		elif(arg == '-mindev'):
66448c2ecf20Sopenharmony_ci			sysvals.mindevlen = getArgFloat('-mindev', args, 0.0, 10000.0)
66458c2ecf20Sopenharmony_ci		elif(arg == '-mincg'):
66468c2ecf20Sopenharmony_ci			sysvals.mincglen = getArgFloat('-mincg', args, 0.0, 10000.0)
66478c2ecf20Sopenharmony_ci		elif(arg == '-bufsize'):
66488c2ecf20Sopenharmony_ci			sysvals.bufsize = getArgInt('-bufsize', args, 1, 1024*1024*8)
66498c2ecf20Sopenharmony_ci		elif(arg == '-cgtest'):
66508c2ecf20Sopenharmony_ci			sysvals.cgtest = getArgInt('-cgtest', args, 0, 1)
66518c2ecf20Sopenharmony_ci		elif(arg == '-cgphase'):
66528c2ecf20Sopenharmony_ci			try:
66538c2ecf20Sopenharmony_ci				val = next(args)
66548c2ecf20Sopenharmony_ci			except:
66558c2ecf20Sopenharmony_ci				doError('No phase name supplied', True)
66568c2ecf20Sopenharmony_ci			d = Data(0)
66578c2ecf20Sopenharmony_ci			if val not in d.phasedef:
66588c2ecf20Sopenharmony_ci				doError('invalid phase --> (%s: %s), valid phases are %s'\
66598c2ecf20Sopenharmony_ci					% (arg, val, d.phasedef.keys()), True)
66608c2ecf20Sopenharmony_ci			sysvals.cgphase = val
66618c2ecf20Sopenharmony_ci		elif(arg == '-cgfilter'):
66628c2ecf20Sopenharmony_ci			try:
66638c2ecf20Sopenharmony_ci				val = next(args)
66648c2ecf20Sopenharmony_ci			except:
66658c2ecf20Sopenharmony_ci				doError('No callgraph functions supplied', True)
66668c2ecf20Sopenharmony_ci			sysvals.setCallgraphFilter(val)
66678c2ecf20Sopenharmony_ci		elif(arg == '-skipkprobe'):
66688c2ecf20Sopenharmony_ci			try:
66698c2ecf20Sopenharmony_ci				val = next(args)
66708c2ecf20Sopenharmony_ci			except:
66718c2ecf20Sopenharmony_ci				doError('No kprobe functions supplied', True)
66728c2ecf20Sopenharmony_ci			sysvals.skipKprobes(val)
66738c2ecf20Sopenharmony_ci		elif(arg == '-cgskip'):
66748c2ecf20Sopenharmony_ci			try:
66758c2ecf20Sopenharmony_ci				val = next(args)
66768c2ecf20Sopenharmony_ci			except:
66778c2ecf20Sopenharmony_ci				doError('No file supplied', True)
66788c2ecf20Sopenharmony_ci			if val.lower() in switchoff:
66798c2ecf20Sopenharmony_ci				sysvals.cgskip = ''
66808c2ecf20Sopenharmony_ci			else:
66818c2ecf20Sopenharmony_ci				sysvals.cgskip = sysvals.configFile(val)
66828c2ecf20Sopenharmony_ci				if(not sysvals.cgskip):
66838c2ecf20Sopenharmony_ci					doError('%s does not exist' % sysvals.cgskip)
66848c2ecf20Sopenharmony_ci		elif(arg == '-callloop-maxgap'):
66858c2ecf20Sopenharmony_ci			sysvals.callloopmaxgap = getArgFloat('-callloop-maxgap', args, 0.0, 1.0)
66868c2ecf20Sopenharmony_ci		elif(arg == '-callloop-maxlen'):
66878c2ecf20Sopenharmony_ci			sysvals.callloopmaxlen = getArgFloat('-callloop-maxlen', args, 0.0, 1.0)
66888c2ecf20Sopenharmony_ci		elif(arg == '-cmd'):
66898c2ecf20Sopenharmony_ci			try:
66908c2ecf20Sopenharmony_ci				val = next(args)
66918c2ecf20Sopenharmony_ci			except:
66928c2ecf20Sopenharmony_ci				doError('No command string supplied', True)
66938c2ecf20Sopenharmony_ci			sysvals.testcommand = val
66948c2ecf20Sopenharmony_ci			sysvals.suspendmode = 'command'
66958c2ecf20Sopenharmony_ci		elif(arg == '-expandcg'):
66968c2ecf20Sopenharmony_ci			sysvals.cgexp = True
66978c2ecf20Sopenharmony_ci		elif(arg == '-srgap'):
66988c2ecf20Sopenharmony_ci			sysvals.srgap = 5
66998c2ecf20Sopenharmony_ci		elif(arg == '-maxfail'):
67008c2ecf20Sopenharmony_ci			sysvals.maxfail = getArgInt('-maxfail', args, 0, 1000000)
67018c2ecf20Sopenharmony_ci		elif(arg == '-multi'):
67028c2ecf20Sopenharmony_ci			try:
67038c2ecf20Sopenharmony_ci				c, d = next(args), next(args)
67048c2ecf20Sopenharmony_ci			except:
67058c2ecf20Sopenharmony_ci				doError('-multi requires two values', True)
67068c2ecf20Sopenharmony_ci			sysvals.multiinit(c, d)
67078c2ecf20Sopenharmony_ci		elif(arg == '-o'):
67088c2ecf20Sopenharmony_ci			try:
67098c2ecf20Sopenharmony_ci				val = next(args)
67108c2ecf20Sopenharmony_ci			except:
67118c2ecf20Sopenharmony_ci				doError('No subdirectory name supplied', True)
67128c2ecf20Sopenharmony_ci			sysvals.outdir = sysvals.setOutputFolder(val)
67138c2ecf20Sopenharmony_ci		elif(arg == '-config'):
67148c2ecf20Sopenharmony_ci			try:
67158c2ecf20Sopenharmony_ci				val = next(args)
67168c2ecf20Sopenharmony_ci			except:
67178c2ecf20Sopenharmony_ci				doError('No text file supplied', True)
67188c2ecf20Sopenharmony_ci			file = sysvals.configFile(val)
67198c2ecf20Sopenharmony_ci			if(not file):
67208c2ecf20Sopenharmony_ci				doError('%s does not exist' % val)
67218c2ecf20Sopenharmony_ci			configFromFile(file)
67228c2ecf20Sopenharmony_ci		elif(arg == '-fadd'):
67238c2ecf20Sopenharmony_ci			try:
67248c2ecf20Sopenharmony_ci				val = next(args)
67258c2ecf20Sopenharmony_ci			except:
67268c2ecf20Sopenharmony_ci				doError('No text file supplied', True)
67278c2ecf20Sopenharmony_ci			file = sysvals.configFile(val)
67288c2ecf20Sopenharmony_ci			if(not file):
67298c2ecf20Sopenharmony_ci				doError('%s does not exist' % val)
67308c2ecf20Sopenharmony_ci			sysvals.addFtraceFilterFunctions(file)
67318c2ecf20Sopenharmony_ci		elif(arg == '-dmesg'):
67328c2ecf20Sopenharmony_ci			try:
67338c2ecf20Sopenharmony_ci				val = next(args)
67348c2ecf20Sopenharmony_ci			except:
67358c2ecf20Sopenharmony_ci				doError('No dmesg file supplied', True)
67368c2ecf20Sopenharmony_ci			sysvals.notestrun = True
67378c2ecf20Sopenharmony_ci			sysvals.dmesgfile = val
67388c2ecf20Sopenharmony_ci			if(os.path.exists(sysvals.dmesgfile) == False):
67398c2ecf20Sopenharmony_ci				doError('%s does not exist' % sysvals.dmesgfile)
67408c2ecf20Sopenharmony_ci		elif(arg == '-ftrace'):
67418c2ecf20Sopenharmony_ci			try:
67428c2ecf20Sopenharmony_ci				val = next(args)
67438c2ecf20Sopenharmony_ci			except:
67448c2ecf20Sopenharmony_ci				doError('No ftrace file supplied', True)
67458c2ecf20Sopenharmony_ci			sysvals.notestrun = True
67468c2ecf20Sopenharmony_ci			sysvals.ftracefile = val
67478c2ecf20Sopenharmony_ci			if(os.path.exists(sysvals.ftracefile) == False):
67488c2ecf20Sopenharmony_ci				doError('%s does not exist' % sysvals.ftracefile)
67498c2ecf20Sopenharmony_ci		elif(arg == '-summary'):
67508c2ecf20Sopenharmony_ci			try:
67518c2ecf20Sopenharmony_ci				val = next(args)
67528c2ecf20Sopenharmony_ci			except:
67538c2ecf20Sopenharmony_ci				doError('No directory supplied', True)
67548c2ecf20Sopenharmony_ci			cmd = 'summary'
67558c2ecf20Sopenharmony_ci			sysvals.outdir = val
67568c2ecf20Sopenharmony_ci			sysvals.notestrun = True
67578c2ecf20Sopenharmony_ci			if(os.path.isdir(val) == False):
67588c2ecf20Sopenharmony_ci				doError('%s is not accesible' % val)
67598c2ecf20Sopenharmony_ci		elif(arg == '-filter'):
67608c2ecf20Sopenharmony_ci			try:
67618c2ecf20Sopenharmony_ci				val = next(args)
67628c2ecf20Sopenharmony_ci			except:
67638c2ecf20Sopenharmony_ci				doError('No devnames supplied', True)
67648c2ecf20Sopenharmony_ci			sysvals.setDeviceFilter(val)
67658c2ecf20Sopenharmony_ci		elif(arg == '-result'):
67668c2ecf20Sopenharmony_ci			try:
67678c2ecf20Sopenharmony_ci				val = next(args)
67688c2ecf20Sopenharmony_ci			except:
67698c2ecf20Sopenharmony_ci				doError('No result file supplied', True)
67708c2ecf20Sopenharmony_ci			sysvals.result = val
67718c2ecf20Sopenharmony_ci			sysvals.signalHandlerInit()
67728c2ecf20Sopenharmony_ci		else:
67738c2ecf20Sopenharmony_ci			doError('Invalid argument: '+arg, True)
67748c2ecf20Sopenharmony_ci
67758c2ecf20Sopenharmony_ci	# compatibility errors
67768c2ecf20Sopenharmony_ci	if(sysvals.usecallgraph and sysvals.usedevsrc):
67778c2ecf20Sopenharmony_ci		doError('-dev is not compatible with -f')
67788c2ecf20Sopenharmony_ci	if(sysvals.usecallgraph and sysvals.useprocmon):
67798c2ecf20Sopenharmony_ci		doError('-proc is not compatible with -f')
67808c2ecf20Sopenharmony_ci
67818c2ecf20Sopenharmony_ci	if sysvals.usecallgraph and sysvals.cgskip:
67828c2ecf20Sopenharmony_ci		sysvals.vprint('Using cgskip file: %s' % sysvals.cgskip)
67838c2ecf20Sopenharmony_ci		sysvals.setCallgraphBlacklist(sysvals.cgskip)
67848c2ecf20Sopenharmony_ci
67858c2ecf20Sopenharmony_ci	# callgraph size cannot exceed device size
67868c2ecf20Sopenharmony_ci	if sysvals.mincglen < sysvals.mindevlen:
67878c2ecf20Sopenharmony_ci		sysvals.mincglen = sysvals.mindevlen
67888c2ecf20Sopenharmony_ci
67898c2ecf20Sopenharmony_ci	# remove existing buffers before calculating memory
67908c2ecf20Sopenharmony_ci	if(sysvals.usecallgraph or sysvals.usedevsrc):
67918c2ecf20Sopenharmony_ci		sysvals.fsetVal('16', 'buffer_size_kb')
67928c2ecf20Sopenharmony_ci	sysvals.cpuInfo()
67938c2ecf20Sopenharmony_ci
67948c2ecf20Sopenharmony_ci	# just run a utility command and exit
67958c2ecf20Sopenharmony_ci	if(cmd != ''):
67968c2ecf20Sopenharmony_ci		ret = 0
67978c2ecf20Sopenharmony_ci		if(cmd == 'status'):
67988c2ecf20Sopenharmony_ci			if not statusCheck(True):
67998c2ecf20Sopenharmony_ci				ret = 1
68008c2ecf20Sopenharmony_ci		elif(cmd == 'fpdt'):
68018c2ecf20Sopenharmony_ci			if not getFPDT(True):
68028c2ecf20Sopenharmony_ci				ret = 1
68038c2ecf20Sopenharmony_ci		elif(cmd == 'sysinfo'):
68048c2ecf20Sopenharmony_ci			sysvals.printSystemInfo(True)
68058c2ecf20Sopenharmony_ci		elif(cmd == 'devinfo'):
68068c2ecf20Sopenharmony_ci			deviceInfo()
68078c2ecf20Sopenharmony_ci		elif(cmd == 'modes'):
68088c2ecf20Sopenharmony_ci			pprint(getModes())
68098c2ecf20Sopenharmony_ci		elif(cmd == 'flist'):
68108c2ecf20Sopenharmony_ci			sysvals.getFtraceFilterFunctions(True)
68118c2ecf20Sopenharmony_ci		elif(cmd == 'flistall'):
68128c2ecf20Sopenharmony_ci			sysvals.getFtraceFilterFunctions(False)
68138c2ecf20Sopenharmony_ci		elif(cmd == 'summary'):
68148c2ecf20Sopenharmony_ci			runSummary(sysvals.outdir, True, genhtml)
68158c2ecf20Sopenharmony_ci		elif(cmd in ['xon', 'xoff', 'xstandby', 'xsuspend', 'xinit', 'xreset']):
68168c2ecf20Sopenharmony_ci			sysvals.verbose = True
68178c2ecf20Sopenharmony_ci			ret = displayControl(cmd[1:])
68188c2ecf20Sopenharmony_ci		elif(cmd == 'xstat'):
68198c2ecf20Sopenharmony_ci			pprint('Display Status: %s' % displayControl('stat').upper())
68208c2ecf20Sopenharmony_ci		elif(cmd == 'wificheck'):
68218c2ecf20Sopenharmony_ci			dev = sysvals.checkWifi()
68228c2ecf20Sopenharmony_ci			if dev:
68238c2ecf20Sopenharmony_ci				print('%s is connected' % sysvals.wifiDetails(dev))
68248c2ecf20Sopenharmony_ci			else:
68258c2ecf20Sopenharmony_ci				print('No wifi connection found')
68268c2ecf20Sopenharmony_ci		elif(cmd == 'cmdinfo'):
68278c2ecf20Sopenharmony_ci			for out in sysvals.cmdinfo(False, True):
68288c2ecf20Sopenharmony_ci				print('[%s - %s]\n%s\n' % out)
68298c2ecf20Sopenharmony_ci		sys.exit(ret)
68308c2ecf20Sopenharmony_ci
68318c2ecf20Sopenharmony_ci	# if instructed, re-analyze existing data files
68328c2ecf20Sopenharmony_ci	if(sysvals.notestrun):
68338c2ecf20Sopenharmony_ci		stamp = rerunTest(sysvals.outdir)
68348c2ecf20Sopenharmony_ci		sysvals.outputResult(stamp)
68358c2ecf20Sopenharmony_ci		sys.exit(0)
68368c2ecf20Sopenharmony_ci
68378c2ecf20Sopenharmony_ci	# verify that we can run a test
68388c2ecf20Sopenharmony_ci	error = statusCheck()
68398c2ecf20Sopenharmony_ci	if(error):
68408c2ecf20Sopenharmony_ci		doError(error)
68418c2ecf20Sopenharmony_ci
68428c2ecf20Sopenharmony_ci	# extract mem/disk extra modes and convert
68438c2ecf20Sopenharmony_ci	mode = sysvals.suspendmode
68448c2ecf20Sopenharmony_ci	if mode.startswith('mem'):
68458c2ecf20Sopenharmony_ci		memmode = mode.split('-', 1)[-1] if '-' in mode else 'deep'
68468c2ecf20Sopenharmony_ci		if memmode == 'shallow':
68478c2ecf20Sopenharmony_ci			mode = 'standby'
68488c2ecf20Sopenharmony_ci		elif memmode ==  's2idle':
68498c2ecf20Sopenharmony_ci			mode = 'freeze'
68508c2ecf20Sopenharmony_ci		else:
68518c2ecf20Sopenharmony_ci			mode = 'mem'
68528c2ecf20Sopenharmony_ci		sysvals.memmode = memmode
68538c2ecf20Sopenharmony_ci		sysvals.suspendmode = mode
68548c2ecf20Sopenharmony_ci	if mode.startswith('disk-'):
68558c2ecf20Sopenharmony_ci		sysvals.diskmode = mode.split('-', 1)[-1]
68568c2ecf20Sopenharmony_ci		sysvals.suspendmode = 'disk'
68578c2ecf20Sopenharmony_ci
68588c2ecf20Sopenharmony_ci	sysvals.systemInfo(dmidecode(sysvals.mempath))
68598c2ecf20Sopenharmony_ci
68608c2ecf20Sopenharmony_ci	setRuntimeSuspend(True)
68618c2ecf20Sopenharmony_ci	if sysvals.display:
68628c2ecf20Sopenharmony_ci		displayControl('init')
68638c2ecf20Sopenharmony_ci	failcnt, ret = 0, 0
68648c2ecf20Sopenharmony_ci	if sysvals.multitest['run']:
68658c2ecf20Sopenharmony_ci		# run multiple tests in a separate subdirectory
68668c2ecf20Sopenharmony_ci		if not sysvals.outdir:
68678c2ecf20Sopenharmony_ci			if 'time' in sysvals.multitest:
68688c2ecf20Sopenharmony_ci				s = '-%dm' % sysvals.multitest['time']
68698c2ecf20Sopenharmony_ci			else:
68708c2ecf20Sopenharmony_ci				s = '-x%d' % sysvals.multitest['count']
68718c2ecf20Sopenharmony_ci			sysvals.outdir = datetime.now().strftime('suspend-%y%m%d-%H%M%S'+s)
68728c2ecf20Sopenharmony_ci		if not os.path.isdir(sysvals.outdir):
68738c2ecf20Sopenharmony_ci			os.makedirs(sysvals.outdir)
68748c2ecf20Sopenharmony_ci		sysvals.sudoUserchown(sysvals.outdir)
68758c2ecf20Sopenharmony_ci		finish = datetime.now()
68768c2ecf20Sopenharmony_ci		if 'time' in sysvals.multitest:
68778c2ecf20Sopenharmony_ci			finish += timedelta(minutes=sysvals.multitest['time'])
68788c2ecf20Sopenharmony_ci		for i in range(sysvals.multitest['count']):
68798c2ecf20Sopenharmony_ci			sysvals.multistat(True, i, finish)
68808c2ecf20Sopenharmony_ci			if i != 0 and sysvals.multitest['delay'] > 0:
68818c2ecf20Sopenharmony_ci				pprint('Waiting %d seconds...' % (sysvals.multitest['delay']))
68828c2ecf20Sopenharmony_ci				time.sleep(sysvals.multitest['delay'])
68838c2ecf20Sopenharmony_ci			fmt = 'suspend-%y%m%d-%H%M%S'
68848c2ecf20Sopenharmony_ci			sysvals.testdir = os.path.join(sysvals.outdir, datetime.now().strftime(fmt))
68858c2ecf20Sopenharmony_ci			ret = runTest(i+1, True)
68868c2ecf20Sopenharmony_ci			failcnt = 0 if not ret else failcnt + 1
68878c2ecf20Sopenharmony_ci			if sysvals.maxfail > 0 and failcnt >= sysvals.maxfail:
68888c2ecf20Sopenharmony_ci				pprint('Maximum fail count of %d reached, aborting multitest' % (sysvals.maxfail))
68898c2ecf20Sopenharmony_ci				break
68908c2ecf20Sopenharmony_ci			time.sleep(5)
68918c2ecf20Sopenharmony_ci			sysvals.resetlog()
68928c2ecf20Sopenharmony_ci			sysvals.multistat(False, i, finish)
68938c2ecf20Sopenharmony_ci			if 'time' in sysvals.multitest and datetime.now() >= finish:
68948c2ecf20Sopenharmony_ci				break
68958c2ecf20Sopenharmony_ci		if not sysvals.skiphtml:
68968c2ecf20Sopenharmony_ci			runSummary(sysvals.outdir, False, False)
68978c2ecf20Sopenharmony_ci		sysvals.sudoUserchown(sysvals.outdir)
68988c2ecf20Sopenharmony_ci	else:
68998c2ecf20Sopenharmony_ci		if sysvals.outdir:
69008c2ecf20Sopenharmony_ci			sysvals.testdir = sysvals.outdir
69018c2ecf20Sopenharmony_ci		# run the test in the current directory
69028c2ecf20Sopenharmony_ci		ret = runTest()
69038c2ecf20Sopenharmony_ci	if sysvals.display:
69048c2ecf20Sopenharmony_ci		displayControl('reset')
69058c2ecf20Sopenharmony_ci	setRuntimeSuspend(False)
69068c2ecf20Sopenharmony_ci	sys.exit(ret)
6907