162306a36Sopenharmony_ci#!/usr/bin/env python3
262306a36Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0-only
362306a36Sopenharmony_ci#
462306a36Sopenharmony_ci# Tool for analyzing suspend/resume timing
562306a36Sopenharmony_ci# Copyright (c) 2013, Intel Corporation.
662306a36Sopenharmony_ci#
762306a36Sopenharmony_ci# This program is free software; you can redistribute it and/or modify it
862306a36Sopenharmony_ci# under the terms and conditions of the GNU General Public License,
962306a36Sopenharmony_ci# version 2, as published by the Free Software Foundation.
1062306a36Sopenharmony_ci#
1162306a36Sopenharmony_ci# This program is distributed in the hope it will be useful, but WITHOUT
1262306a36Sopenharmony_ci# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1362306a36Sopenharmony_ci# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
1462306a36Sopenharmony_ci# more details.
1562306a36Sopenharmony_ci#
1662306a36Sopenharmony_ci# Authors:
1762306a36Sopenharmony_ci#	 Todd Brandt <todd.e.brandt@linux.intel.com>
1862306a36Sopenharmony_ci#
1962306a36Sopenharmony_ci# Links:
2062306a36Sopenharmony_ci#	 Home Page
2162306a36Sopenharmony_ci#	   https://01.org/pm-graph
2262306a36Sopenharmony_ci#	 Source repo
2362306a36Sopenharmony_ci#	   git@github.com:intel/pm-graph
2462306a36Sopenharmony_ci#
2562306a36Sopenharmony_ci# Description:
2662306a36Sopenharmony_ci#	 This tool is designed to assist kernel and OS developers in optimizing
2762306a36Sopenharmony_ci#	 their linux stack's suspend/resume time. Using a kernel image built
2862306a36Sopenharmony_ci#	 with a few extra options enabled, the tool will execute a suspend and
2962306a36Sopenharmony_ci#	 will capture dmesg and ftrace data until resume is complete. This data
3062306a36Sopenharmony_ci#	 is transformed into a device timeline and a callgraph to give a quick
3162306a36Sopenharmony_ci#	 and detailed view of which devices and callbacks are taking the most
3262306a36Sopenharmony_ci#	 time in suspend/resume. The output is a single html file which can be
3362306a36Sopenharmony_ci#	 viewed in firefox or chrome.
3462306a36Sopenharmony_ci#
3562306a36Sopenharmony_ci#	 The following kernel build options are required:
3662306a36Sopenharmony_ci#		 CONFIG_DEVMEM=y
3762306a36Sopenharmony_ci#		 CONFIG_PM_DEBUG=y
3862306a36Sopenharmony_ci#		 CONFIG_PM_SLEEP_DEBUG=y
3962306a36Sopenharmony_ci#		 CONFIG_FTRACE=y
4062306a36Sopenharmony_ci#		 CONFIG_FUNCTION_TRACER=y
4162306a36Sopenharmony_ci#		 CONFIG_FUNCTION_GRAPH_TRACER=y
4262306a36Sopenharmony_ci#		 CONFIG_KPROBES=y
4362306a36Sopenharmony_ci#		 CONFIG_KPROBES_ON_FTRACE=y
4462306a36Sopenharmony_ci#
4562306a36Sopenharmony_ci#	 For kernel versions older than 3.15:
4662306a36Sopenharmony_ci#	 The following additional kernel parameters are required:
4762306a36Sopenharmony_ci#		 (e.g. in file /etc/default/grub)
4862306a36Sopenharmony_ci#		 GRUB_CMDLINE_LINUX_DEFAULT="... initcall_debug log_buf_len=16M ..."
4962306a36Sopenharmony_ci#
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci# ----------------- LIBRARIES --------------------
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ciimport sys
5462306a36Sopenharmony_ciimport time
5562306a36Sopenharmony_ciimport os
5662306a36Sopenharmony_ciimport string
5762306a36Sopenharmony_ciimport re
5862306a36Sopenharmony_ciimport platform
5962306a36Sopenharmony_ciimport signal
6062306a36Sopenharmony_ciimport codecs
6162306a36Sopenharmony_cifrom datetime import datetime, timedelta
6262306a36Sopenharmony_ciimport struct
6362306a36Sopenharmony_ciimport configparser
6462306a36Sopenharmony_ciimport gzip
6562306a36Sopenharmony_cifrom threading import Thread
6662306a36Sopenharmony_cifrom subprocess import call, Popen, PIPE
6762306a36Sopenharmony_ciimport base64
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cidebugtiming = False
7062306a36Sopenharmony_cimystarttime = time.time()
7162306a36Sopenharmony_cidef pprint(msg):
7262306a36Sopenharmony_ci	if debugtiming:
7362306a36Sopenharmony_ci		print('[%09.3f] %s' % (time.time()-mystarttime, msg))
7462306a36Sopenharmony_ci	else:
7562306a36Sopenharmony_ci		print(msg)
7662306a36Sopenharmony_ci	sys.stdout.flush()
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cidef ascii(text):
7962306a36Sopenharmony_ci	return text.decode('ascii', 'ignore')
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci# ----------------- CLASSES --------------------
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci# Class: SystemValues
8462306a36Sopenharmony_ci# Description:
8562306a36Sopenharmony_ci#	 A global, single-instance container used to
8662306a36Sopenharmony_ci#	 store system values and test parameters
8762306a36Sopenharmony_ciclass SystemValues:
8862306a36Sopenharmony_ci	title = 'SleepGraph'
8962306a36Sopenharmony_ci	version = '5.11'
9062306a36Sopenharmony_ci	ansi = False
9162306a36Sopenharmony_ci	rs = 0
9262306a36Sopenharmony_ci	display = ''
9362306a36Sopenharmony_ci	gzip = False
9462306a36Sopenharmony_ci	sync = False
9562306a36Sopenharmony_ci	wifi = False
9662306a36Sopenharmony_ci	netfix = False
9762306a36Sopenharmony_ci	verbose = False
9862306a36Sopenharmony_ci	testlog = True
9962306a36Sopenharmony_ci	dmesglog = True
10062306a36Sopenharmony_ci	ftracelog = False
10162306a36Sopenharmony_ci	acpidebug = True
10262306a36Sopenharmony_ci	tstat = True
10362306a36Sopenharmony_ci	wifitrace = False
10462306a36Sopenharmony_ci	mindevlen = 0.0001
10562306a36Sopenharmony_ci	mincglen = 0.0
10662306a36Sopenharmony_ci	cgphase = ''
10762306a36Sopenharmony_ci	cgtest = -1
10862306a36Sopenharmony_ci	cgskip = ''
10962306a36Sopenharmony_ci	maxfail = 0
11062306a36Sopenharmony_ci	multitest = {'run': False, 'count': 1000000, 'delay': 0}
11162306a36Sopenharmony_ci	max_graph_depth = 0
11262306a36Sopenharmony_ci	callloopmaxgap = 0.0001
11362306a36Sopenharmony_ci	callloopmaxlen = 0.005
11462306a36Sopenharmony_ci	bufsize = 0
11562306a36Sopenharmony_ci	cpucount = 0
11662306a36Sopenharmony_ci	memtotal = 204800
11762306a36Sopenharmony_ci	memfree = 204800
11862306a36Sopenharmony_ci	osversion = ''
11962306a36Sopenharmony_ci	srgap = 0
12062306a36Sopenharmony_ci	cgexp = False
12162306a36Sopenharmony_ci	testdir = ''
12262306a36Sopenharmony_ci	outdir = ''
12362306a36Sopenharmony_ci	tpath = '/sys/kernel/tracing/'
12462306a36Sopenharmony_ci	fpdtpath = '/sys/firmware/acpi/tables/FPDT'
12562306a36Sopenharmony_ci	epath = '/sys/kernel/tracing/events/power/'
12662306a36Sopenharmony_ci	pmdpath = '/sys/power/pm_debug_messages'
12762306a36Sopenharmony_ci	s0ixpath = '/sys/module/intel_pmc_core/parameters/warn_on_s0ix_failures'
12862306a36Sopenharmony_ci	s0ixres = '/sys/devices/system/cpu/cpuidle/low_power_idle_system_residency_us'
12962306a36Sopenharmony_ci	acpipath='/sys/module/acpi/parameters/debug_level'
13062306a36Sopenharmony_ci	traceevents = [
13162306a36Sopenharmony_ci		'suspend_resume',
13262306a36Sopenharmony_ci		'wakeup_source_activate',
13362306a36Sopenharmony_ci		'wakeup_source_deactivate',
13462306a36Sopenharmony_ci		'device_pm_callback_end',
13562306a36Sopenharmony_ci		'device_pm_callback_start'
13662306a36Sopenharmony_ci	]
13762306a36Sopenharmony_ci	logmsg = ''
13862306a36Sopenharmony_ci	testcommand = ''
13962306a36Sopenharmony_ci	mempath = '/dev/mem'
14062306a36Sopenharmony_ci	powerfile = '/sys/power/state'
14162306a36Sopenharmony_ci	mempowerfile = '/sys/power/mem_sleep'
14262306a36Sopenharmony_ci	diskpowerfile = '/sys/power/disk'
14362306a36Sopenharmony_ci	suspendmode = 'mem'
14462306a36Sopenharmony_ci	memmode = ''
14562306a36Sopenharmony_ci	diskmode = ''
14662306a36Sopenharmony_ci	hostname = 'localhost'
14762306a36Sopenharmony_ci	prefix = 'test'
14862306a36Sopenharmony_ci	teststamp = ''
14962306a36Sopenharmony_ci	sysstamp = ''
15062306a36Sopenharmony_ci	dmesgstart = 0.0
15162306a36Sopenharmony_ci	dmesgfile = ''
15262306a36Sopenharmony_ci	ftracefile = ''
15362306a36Sopenharmony_ci	htmlfile = 'output.html'
15462306a36Sopenharmony_ci	result = ''
15562306a36Sopenharmony_ci	rtcwake = True
15662306a36Sopenharmony_ci	rtcwaketime = 15
15762306a36Sopenharmony_ci	rtcpath = ''
15862306a36Sopenharmony_ci	devicefilter = []
15962306a36Sopenharmony_ci	cgfilter = []
16062306a36Sopenharmony_ci	stamp = 0
16162306a36Sopenharmony_ci	execcount = 1
16262306a36Sopenharmony_ci	x2delay = 0
16362306a36Sopenharmony_ci	skiphtml = False
16462306a36Sopenharmony_ci	usecallgraph = False
16562306a36Sopenharmony_ci	ftopfunc = 'pm_suspend'
16662306a36Sopenharmony_ci	ftop = False
16762306a36Sopenharmony_ci	usetraceevents = False
16862306a36Sopenharmony_ci	usetracemarkers = True
16962306a36Sopenharmony_ci	useftrace = True
17062306a36Sopenharmony_ci	usekprobes = True
17162306a36Sopenharmony_ci	usedevsrc = False
17262306a36Sopenharmony_ci	useprocmon = False
17362306a36Sopenharmony_ci	notestrun = False
17462306a36Sopenharmony_ci	cgdump = False
17562306a36Sopenharmony_ci	devdump = False
17662306a36Sopenharmony_ci	mixedphaseheight = True
17762306a36Sopenharmony_ci	devprops = dict()
17862306a36Sopenharmony_ci	cfgdef = dict()
17962306a36Sopenharmony_ci	platinfo = []
18062306a36Sopenharmony_ci	predelay = 0
18162306a36Sopenharmony_ci	postdelay = 0
18262306a36Sopenharmony_ci	tmstart = 'SUSPEND START %Y%m%d-%H:%M:%S.%f'
18362306a36Sopenharmony_ci	tmend = 'RESUME COMPLETE %Y%m%d-%H:%M:%S.%f'
18462306a36Sopenharmony_ci	tracefuncs = {
18562306a36Sopenharmony_ci		'async_synchronize_full': {},
18662306a36Sopenharmony_ci		'sys_sync': {},
18762306a36Sopenharmony_ci		'ksys_sync': {},
18862306a36Sopenharmony_ci		'__pm_notifier_call_chain': {},
18962306a36Sopenharmony_ci		'pm_prepare_console': {},
19062306a36Sopenharmony_ci		'pm_notifier_call_chain': {},
19162306a36Sopenharmony_ci		'freeze_processes': {},
19262306a36Sopenharmony_ci		'freeze_kernel_threads': {},
19362306a36Sopenharmony_ci		'pm_restrict_gfp_mask': {},
19462306a36Sopenharmony_ci		'acpi_suspend_begin': {},
19562306a36Sopenharmony_ci		'acpi_hibernation_begin': {},
19662306a36Sopenharmony_ci		'acpi_hibernation_enter': {},
19762306a36Sopenharmony_ci		'acpi_hibernation_leave': {},
19862306a36Sopenharmony_ci		'acpi_pm_freeze': {},
19962306a36Sopenharmony_ci		'acpi_pm_thaw': {},
20062306a36Sopenharmony_ci		'acpi_s2idle_end': {},
20162306a36Sopenharmony_ci		'acpi_s2idle_sync': {},
20262306a36Sopenharmony_ci		'acpi_s2idle_begin': {},
20362306a36Sopenharmony_ci		'acpi_s2idle_prepare': {},
20462306a36Sopenharmony_ci		'acpi_s2idle_prepare_late': {},
20562306a36Sopenharmony_ci		'acpi_s2idle_wake': {},
20662306a36Sopenharmony_ci		'acpi_s2idle_wakeup': {},
20762306a36Sopenharmony_ci		'acpi_s2idle_restore': {},
20862306a36Sopenharmony_ci		'acpi_s2idle_restore_early': {},
20962306a36Sopenharmony_ci		'hibernate_preallocate_memory': {},
21062306a36Sopenharmony_ci		'create_basic_memory_bitmaps': {},
21162306a36Sopenharmony_ci		'swsusp_write': {},
21262306a36Sopenharmony_ci		'suspend_console': {},
21362306a36Sopenharmony_ci		'acpi_pm_prepare': {},
21462306a36Sopenharmony_ci		'syscore_suspend': {},
21562306a36Sopenharmony_ci		'arch_enable_nonboot_cpus_end': {},
21662306a36Sopenharmony_ci		'syscore_resume': {},
21762306a36Sopenharmony_ci		'acpi_pm_finish': {},
21862306a36Sopenharmony_ci		'resume_console': {},
21962306a36Sopenharmony_ci		'acpi_pm_end': {},
22062306a36Sopenharmony_ci		'pm_restore_gfp_mask': {},
22162306a36Sopenharmony_ci		'thaw_processes': {},
22262306a36Sopenharmony_ci		'pm_restore_console': {},
22362306a36Sopenharmony_ci		'CPU_OFF': {
22462306a36Sopenharmony_ci			'func':'_cpu_down',
22562306a36Sopenharmony_ci			'args_x86_64': {'cpu':'%di:s32'},
22662306a36Sopenharmony_ci			'format': 'CPU_OFF[{cpu}]'
22762306a36Sopenharmony_ci		},
22862306a36Sopenharmony_ci		'CPU_ON': {
22962306a36Sopenharmony_ci			'func':'_cpu_up',
23062306a36Sopenharmony_ci			'args_x86_64': {'cpu':'%di:s32'},
23162306a36Sopenharmony_ci			'format': 'CPU_ON[{cpu}]'
23262306a36Sopenharmony_ci		},
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci	dev_tracefuncs = {
23562306a36Sopenharmony_ci		# general wait/delay/sleep
23662306a36Sopenharmony_ci		'msleep': { 'args_x86_64': {'time':'%di:s32'}, 'ub': 1 },
23762306a36Sopenharmony_ci		'schedule_timeout': { 'args_x86_64': {'timeout':'%di:s32'}, 'ub': 1 },
23862306a36Sopenharmony_ci		'udelay': { 'func':'__const_udelay', 'args_x86_64': {'loops':'%di:s32'}, 'ub': 1 },
23962306a36Sopenharmony_ci		'usleep_range': { 'args_x86_64': {'min':'%di:s32', 'max':'%si:s32'}, 'ub': 1 },
24062306a36Sopenharmony_ci		'mutex_lock_slowpath': { 'func':'__mutex_lock_slowpath', 'ub': 1 },
24162306a36Sopenharmony_ci		'acpi_os_stall': {'ub': 1},
24262306a36Sopenharmony_ci		'rt_mutex_slowlock': {'ub': 1},
24362306a36Sopenharmony_ci		# ACPI
24462306a36Sopenharmony_ci		'acpi_resume_power_resources': {},
24562306a36Sopenharmony_ci		'acpi_ps_execute_method': { 'args_x86_64': {
24662306a36Sopenharmony_ci			'fullpath':'+0(+40(%di)):string',
24762306a36Sopenharmony_ci		}},
24862306a36Sopenharmony_ci		# mei_me
24962306a36Sopenharmony_ci		'mei_reset': {},
25062306a36Sopenharmony_ci		# filesystem
25162306a36Sopenharmony_ci		'ext4_sync_fs': {},
25262306a36Sopenharmony_ci		# 80211
25362306a36Sopenharmony_ci		'ath10k_bmi_read_memory': { 'args_x86_64': {'length':'%cx:s32'} },
25462306a36Sopenharmony_ci		'ath10k_bmi_write_memory': { 'args_x86_64': {'length':'%cx:s32'} },
25562306a36Sopenharmony_ci		'ath10k_bmi_fast_download': { 'args_x86_64': {'length':'%cx:s32'} },
25662306a36Sopenharmony_ci		'iwlagn_mac_start': {},
25762306a36Sopenharmony_ci		'iwlagn_alloc_bcast_station': {},
25862306a36Sopenharmony_ci		'iwl_trans_pcie_start_hw': {},
25962306a36Sopenharmony_ci		'iwl_trans_pcie_start_fw': {},
26062306a36Sopenharmony_ci		'iwl_run_init_ucode': {},
26162306a36Sopenharmony_ci		'iwl_load_ucode_wait_alive': {},
26262306a36Sopenharmony_ci		'iwl_alive_start': {},
26362306a36Sopenharmony_ci		'iwlagn_mac_stop': {},
26462306a36Sopenharmony_ci		'iwlagn_mac_suspend': {},
26562306a36Sopenharmony_ci		'iwlagn_mac_resume': {},
26662306a36Sopenharmony_ci		'iwlagn_mac_add_interface': {},
26762306a36Sopenharmony_ci		'iwlagn_mac_remove_interface': {},
26862306a36Sopenharmony_ci		'iwlagn_mac_change_interface': {},
26962306a36Sopenharmony_ci		'iwlagn_mac_config': {},
27062306a36Sopenharmony_ci		'iwlagn_configure_filter': {},
27162306a36Sopenharmony_ci		'iwlagn_mac_hw_scan': {},
27262306a36Sopenharmony_ci		'iwlagn_bss_info_changed': {},
27362306a36Sopenharmony_ci		'iwlagn_mac_channel_switch': {},
27462306a36Sopenharmony_ci		'iwlagn_mac_flush': {},
27562306a36Sopenharmony_ci		# ATA
27662306a36Sopenharmony_ci		'ata_eh_recover': { 'args_x86_64': {'port':'+36(%di):s32'} },
27762306a36Sopenharmony_ci		# i915
27862306a36Sopenharmony_ci		'i915_gem_resume': {},
27962306a36Sopenharmony_ci		'i915_restore_state': {},
28062306a36Sopenharmony_ci		'intel_opregion_setup': {},
28162306a36Sopenharmony_ci		'g4x_pre_enable_dp': {},
28262306a36Sopenharmony_ci		'vlv_pre_enable_dp': {},
28362306a36Sopenharmony_ci		'chv_pre_enable_dp': {},
28462306a36Sopenharmony_ci		'g4x_enable_dp': {},
28562306a36Sopenharmony_ci		'vlv_enable_dp': {},
28662306a36Sopenharmony_ci		'intel_hpd_init': {},
28762306a36Sopenharmony_ci		'intel_opregion_register': {},
28862306a36Sopenharmony_ci		'intel_dp_detect': {},
28962306a36Sopenharmony_ci		'intel_hdmi_detect': {},
29062306a36Sopenharmony_ci		'intel_opregion_init': {},
29162306a36Sopenharmony_ci		'intel_fbdev_set_suspend': {},
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci	infocmds = [
29462306a36Sopenharmony_ci		[0, 'sysinfo', 'uname', '-a'],
29562306a36Sopenharmony_ci		[0, 'cpuinfo', 'head', '-7', '/proc/cpuinfo'],
29662306a36Sopenharmony_ci		[0, 'kparams', 'cat', '/proc/cmdline'],
29762306a36Sopenharmony_ci		[0, 'mcelog', 'mcelog'],
29862306a36Sopenharmony_ci		[0, 'pcidevices', 'lspci', '-tv'],
29962306a36Sopenharmony_ci		[0, 'usbdevices', 'lsusb', '-tv'],
30062306a36Sopenharmony_ci		[0, 'acpidevices', 'sh', '-c', 'ls -l /sys/bus/acpi/devices/*/physical_node'],
30162306a36Sopenharmony_ci		[0, 's0ix_require', 'cat', '/sys/kernel/debug/pmc_core/substate_requirements'],
30262306a36Sopenharmony_ci		[0, 's0ix_debug', 'cat', '/sys/kernel/debug/pmc_core/slp_s0_debug_status'],
30362306a36Sopenharmony_ci		[0, 'ethtool', 'ethtool', '{ethdev}'],
30462306a36Sopenharmony_ci		[1, 's0ix_residency', 'cat', '/sys/kernel/debug/pmc_core/slp_s0_residency_usec'],
30562306a36Sopenharmony_ci		[1, 'interrupts', 'cat', '/proc/interrupts'],
30662306a36Sopenharmony_ci		[1, 'wakeups', 'cat', '/sys/kernel/debug/wakeup_sources'],
30762306a36Sopenharmony_ci		[2, 'gpecounts', 'sh', '-c', 'grep -v invalid /sys/firmware/acpi/interrupts/*'],
30862306a36Sopenharmony_ci		[2, 'suspendstats', 'sh', '-c', 'grep -v invalid /sys/power/suspend_stats/*'],
30962306a36Sopenharmony_ci		[2, 'cpuidle', 'sh', '-c', 'grep -v invalid /sys/devices/system/cpu/cpu*/cpuidle/state*/s2idle/*'],
31062306a36Sopenharmony_ci		[2, 'battery', 'sh', '-c', 'grep -v invalid /sys/class/power_supply/*/*'],
31162306a36Sopenharmony_ci		[2, 'thermal', 'sh', '-c', 'grep . /sys/class/thermal/thermal_zone*/temp'],
31262306a36Sopenharmony_ci	]
31362306a36Sopenharmony_ci	cgblacklist = []
31462306a36Sopenharmony_ci	kprobes = dict()
31562306a36Sopenharmony_ci	timeformat = '%.3f'
31662306a36Sopenharmony_ci	cmdline = '%s %s' % \
31762306a36Sopenharmony_ci			(os.path.basename(sys.argv[0]), ' '.join(sys.argv[1:]))
31862306a36Sopenharmony_ci	sudouser = ''
31962306a36Sopenharmony_ci	def __init__(self):
32062306a36Sopenharmony_ci		self.archargs = 'args_'+platform.machine()
32162306a36Sopenharmony_ci		self.hostname = platform.node()
32262306a36Sopenharmony_ci		if(self.hostname == ''):
32362306a36Sopenharmony_ci			self.hostname = 'localhost'
32462306a36Sopenharmony_ci		rtc = "rtc0"
32562306a36Sopenharmony_ci		if os.path.exists('/dev/rtc'):
32662306a36Sopenharmony_ci			rtc = os.readlink('/dev/rtc')
32762306a36Sopenharmony_ci		rtc = '/sys/class/rtc/'+rtc
32862306a36Sopenharmony_ci		if os.path.exists(rtc) and os.path.exists(rtc+'/date') and \
32962306a36Sopenharmony_ci			os.path.exists(rtc+'/time') and os.path.exists(rtc+'/wakealarm'):
33062306a36Sopenharmony_ci			self.rtcpath = rtc
33162306a36Sopenharmony_ci		if (hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()):
33262306a36Sopenharmony_ci			self.ansi = True
33362306a36Sopenharmony_ci		self.testdir = datetime.now().strftime('suspend-%y%m%d-%H%M%S')
33462306a36Sopenharmony_ci		if os.getuid() == 0 and 'SUDO_USER' in os.environ and \
33562306a36Sopenharmony_ci			os.environ['SUDO_USER']:
33662306a36Sopenharmony_ci			self.sudouser = os.environ['SUDO_USER']
33762306a36Sopenharmony_ci	def resetlog(self):
33862306a36Sopenharmony_ci		self.logmsg = ''
33962306a36Sopenharmony_ci		self.platinfo = []
34062306a36Sopenharmony_ci	def vprint(self, msg):
34162306a36Sopenharmony_ci		self.logmsg += msg+'\n'
34262306a36Sopenharmony_ci		if self.verbose or msg.startswith('WARNING:'):
34362306a36Sopenharmony_ci			pprint(msg)
34462306a36Sopenharmony_ci	def signalHandler(self, signum, frame):
34562306a36Sopenharmony_ci		if not self.result:
34662306a36Sopenharmony_ci			return
34762306a36Sopenharmony_ci		signame = self.signames[signum] if signum in self.signames else 'UNKNOWN'
34862306a36Sopenharmony_ci		msg = 'Signal %s caused a tool exit, line %d' % (signame, frame.f_lineno)
34962306a36Sopenharmony_ci		self.outputResult({'error':msg})
35062306a36Sopenharmony_ci		sys.exit(3)
35162306a36Sopenharmony_ci	def signalHandlerInit(self):
35262306a36Sopenharmony_ci		capture = ['BUS', 'SYS', 'XCPU', 'XFSZ', 'PWR', 'HUP', 'INT', 'QUIT',
35362306a36Sopenharmony_ci			'ILL', 'ABRT', 'FPE', 'SEGV', 'TERM']
35462306a36Sopenharmony_ci		self.signames = dict()
35562306a36Sopenharmony_ci		for i in capture:
35662306a36Sopenharmony_ci			s = 'SIG'+i
35762306a36Sopenharmony_ci			try:
35862306a36Sopenharmony_ci				signum = getattr(signal, s)
35962306a36Sopenharmony_ci				signal.signal(signum, self.signalHandler)
36062306a36Sopenharmony_ci			except:
36162306a36Sopenharmony_ci				continue
36262306a36Sopenharmony_ci			self.signames[signum] = s
36362306a36Sopenharmony_ci	def rootCheck(self, fatal=True):
36462306a36Sopenharmony_ci		if(os.access(self.powerfile, os.W_OK)):
36562306a36Sopenharmony_ci			return True
36662306a36Sopenharmony_ci		if fatal:
36762306a36Sopenharmony_ci			msg = 'This command requires sysfs mount and root access'
36862306a36Sopenharmony_ci			pprint('ERROR: %s\n' % msg)
36962306a36Sopenharmony_ci			self.outputResult({'error':msg})
37062306a36Sopenharmony_ci			sys.exit(1)
37162306a36Sopenharmony_ci		return False
37262306a36Sopenharmony_ci	def rootUser(self, fatal=False):
37362306a36Sopenharmony_ci		if 'USER' in os.environ and os.environ['USER'] == 'root':
37462306a36Sopenharmony_ci			return True
37562306a36Sopenharmony_ci		if fatal:
37662306a36Sopenharmony_ci			msg = 'This command must be run as root'
37762306a36Sopenharmony_ci			pprint('ERROR: %s\n' % msg)
37862306a36Sopenharmony_ci			self.outputResult({'error':msg})
37962306a36Sopenharmony_ci			sys.exit(1)
38062306a36Sopenharmony_ci		return False
38162306a36Sopenharmony_ci	def usable(self, file, ishtml=False):
38262306a36Sopenharmony_ci		if not os.path.exists(file) or os.path.getsize(file) < 1:
38362306a36Sopenharmony_ci			return False
38462306a36Sopenharmony_ci		if ishtml:
38562306a36Sopenharmony_ci			try:
38662306a36Sopenharmony_ci				fp = open(file, 'r')
38762306a36Sopenharmony_ci				res = fp.read(1000)
38862306a36Sopenharmony_ci				fp.close()
38962306a36Sopenharmony_ci			except:
39062306a36Sopenharmony_ci				return False
39162306a36Sopenharmony_ci			if '<html>' not in res:
39262306a36Sopenharmony_ci				return False
39362306a36Sopenharmony_ci		return True
39462306a36Sopenharmony_ci	def getExec(self, cmd):
39562306a36Sopenharmony_ci		try:
39662306a36Sopenharmony_ci			fp = Popen(['which', cmd], stdout=PIPE, stderr=PIPE).stdout
39762306a36Sopenharmony_ci			out = ascii(fp.read()).strip()
39862306a36Sopenharmony_ci			fp.close()
39962306a36Sopenharmony_ci		except:
40062306a36Sopenharmony_ci			out = ''
40162306a36Sopenharmony_ci		if out:
40262306a36Sopenharmony_ci			return out
40362306a36Sopenharmony_ci		for path in ['/sbin', '/bin', '/usr/sbin', '/usr/bin',
40462306a36Sopenharmony_ci			'/usr/local/sbin', '/usr/local/bin']:
40562306a36Sopenharmony_ci			cmdfull = os.path.join(path, cmd)
40662306a36Sopenharmony_ci			if os.path.exists(cmdfull):
40762306a36Sopenharmony_ci				return cmdfull
40862306a36Sopenharmony_ci		return out
40962306a36Sopenharmony_ci	def setPrecision(self, num):
41062306a36Sopenharmony_ci		if num < 0 or num > 6:
41162306a36Sopenharmony_ci			return
41262306a36Sopenharmony_ci		self.timeformat = '%.{0}f'.format(num)
41362306a36Sopenharmony_ci	def setOutputFolder(self, value):
41462306a36Sopenharmony_ci		args = dict()
41562306a36Sopenharmony_ci		n = datetime.now()
41662306a36Sopenharmony_ci		args['date'] = n.strftime('%y%m%d')
41762306a36Sopenharmony_ci		args['time'] = n.strftime('%H%M%S')
41862306a36Sopenharmony_ci		args['hostname'] = args['host'] = self.hostname
41962306a36Sopenharmony_ci		args['mode'] = self.suspendmode
42062306a36Sopenharmony_ci		return value.format(**args)
42162306a36Sopenharmony_ci	def setOutputFile(self):
42262306a36Sopenharmony_ci		if self.dmesgfile != '':
42362306a36Sopenharmony_ci			m = re.match('(?P<name>.*)_dmesg\.txt.*', self.dmesgfile)
42462306a36Sopenharmony_ci			if(m):
42562306a36Sopenharmony_ci				self.htmlfile = m.group('name')+'.html'
42662306a36Sopenharmony_ci		if self.ftracefile != '':
42762306a36Sopenharmony_ci			m = re.match('(?P<name>.*)_ftrace\.txt.*', self.ftracefile)
42862306a36Sopenharmony_ci			if(m):
42962306a36Sopenharmony_ci				self.htmlfile = m.group('name')+'.html'
43062306a36Sopenharmony_ci	def systemInfo(self, info):
43162306a36Sopenharmony_ci		p = m = ''
43262306a36Sopenharmony_ci		if 'baseboard-manufacturer' in info:
43362306a36Sopenharmony_ci			m = info['baseboard-manufacturer']
43462306a36Sopenharmony_ci		elif 'system-manufacturer' in info:
43562306a36Sopenharmony_ci			m = info['system-manufacturer']
43662306a36Sopenharmony_ci		if 'system-product-name' in info:
43762306a36Sopenharmony_ci			p = info['system-product-name']
43862306a36Sopenharmony_ci		elif 'baseboard-product-name' in info:
43962306a36Sopenharmony_ci			p = info['baseboard-product-name']
44062306a36Sopenharmony_ci		if m[:5].lower() == 'intel' and 'baseboard-product-name' in info:
44162306a36Sopenharmony_ci			p = info['baseboard-product-name']
44262306a36Sopenharmony_ci		c = info['processor-version'] if 'processor-version' in info else ''
44362306a36Sopenharmony_ci		b = info['bios-version'] if 'bios-version' in info else ''
44462306a36Sopenharmony_ci		r = info['bios-release-date'] if 'bios-release-date' in info else ''
44562306a36Sopenharmony_ci		self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | biosdate:%s | numcpu:%d | memsz:%d | memfr:%d' % \
44662306a36Sopenharmony_ci			(m, p, c, b, r, self.cpucount, self.memtotal, self.memfree)
44762306a36Sopenharmony_ci		if self.osversion:
44862306a36Sopenharmony_ci			self.sysstamp += ' | os:%s' % self.osversion
44962306a36Sopenharmony_ci	def printSystemInfo(self, fatal=False):
45062306a36Sopenharmony_ci		self.rootCheck(True)
45162306a36Sopenharmony_ci		out = dmidecode(self.mempath, fatal)
45262306a36Sopenharmony_ci		if len(out) < 1:
45362306a36Sopenharmony_ci			return
45462306a36Sopenharmony_ci		fmt = '%-24s: %s'
45562306a36Sopenharmony_ci		if self.osversion:
45662306a36Sopenharmony_ci			print(fmt % ('os-version', self.osversion))
45762306a36Sopenharmony_ci		for name in sorted(out):
45862306a36Sopenharmony_ci			print(fmt % (name, out[name]))
45962306a36Sopenharmony_ci		print(fmt % ('cpucount', ('%d' % self.cpucount)))
46062306a36Sopenharmony_ci		print(fmt % ('memtotal', ('%d kB' % self.memtotal)))
46162306a36Sopenharmony_ci		print(fmt % ('memfree', ('%d kB' % self.memfree)))
46262306a36Sopenharmony_ci	def cpuInfo(self):
46362306a36Sopenharmony_ci		self.cpucount = 0
46462306a36Sopenharmony_ci		if os.path.exists('/proc/cpuinfo'):
46562306a36Sopenharmony_ci			with open('/proc/cpuinfo', 'r') as fp:
46662306a36Sopenharmony_ci				for line in fp:
46762306a36Sopenharmony_ci					if re.match('^processor[ \t]*:[ \t]*[0-9]*', line):
46862306a36Sopenharmony_ci						self.cpucount += 1
46962306a36Sopenharmony_ci		if os.path.exists('/proc/meminfo'):
47062306a36Sopenharmony_ci			with open('/proc/meminfo', 'r') as fp:
47162306a36Sopenharmony_ci				for line in fp:
47262306a36Sopenharmony_ci					m = re.match('^MemTotal:[ \t]*(?P<sz>[0-9]*) *kB', line)
47362306a36Sopenharmony_ci					if m:
47462306a36Sopenharmony_ci						self.memtotal = int(m.group('sz'))
47562306a36Sopenharmony_ci					m = re.match('^MemFree:[ \t]*(?P<sz>[0-9]*) *kB', line)
47662306a36Sopenharmony_ci					if m:
47762306a36Sopenharmony_ci						self.memfree = int(m.group('sz'))
47862306a36Sopenharmony_ci		if os.path.exists('/etc/os-release'):
47962306a36Sopenharmony_ci			with open('/etc/os-release', 'r') as fp:
48062306a36Sopenharmony_ci				for line in fp:
48162306a36Sopenharmony_ci					if line.startswith('PRETTY_NAME='):
48262306a36Sopenharmony_ci						self.osversion = line[12:].strip().replace('"', '')
48362306a36Sopenharmony_ci	def initTestOutput(self, name):
48462306a36Sopenharmony_ci		self.prefix = self.hostname
48562306a36Sopenharmony_ci		v = open('/proc/version', 'r').read().strip()
48662306a36Sopenharmony_ci		kver = v.split()[2]
48762306a36Sopenharmony_ci		fmt = name+'-%m%d%y-%H%M%S'
48862306a36Sopenharmony_ci		testtime = datetime.now().strftime(fmt)
48962306a36Sopenharmony_ci		self.teststamp = \
49062306a36Sopenharmony_ci			'# '+testtime+' '+self.prefix+' '+self.suspendmode+' '+kver
49162306a36Sopenharmony_ci		ext = ''
49262306a36Sopenharmony_ci		if self.gzip:
49362306a36Sopenharmony_ci			ext = '.gz'
49462306a36Sopenharmony_ci		self.dmesgfile = \
49562306a36Sopenharmony_ci			self.testdir+'/'+self.prefix+'_'+self.suspendmode+'_dmesg.txt'+ext
49662306a36Sopenharmony_ci		self.ftracefile = \
49762306a36Sopenharmony_ci			self.testdir+'/'+self.prefix+'_'+self.suspendmode+'_ftrace.txt'+ext
49862306a36Sopenharmony_ci		self.htmlfile = \
49962306a36Sopenharmony_ci			self.testdir+'/'+self.prefix+'_'+self.suspendmode+'.html'
50062306a36Sopenharmony_ci		if not os.path.isdir(self.testdir):
50162306a36Sopenharmony_ci			os.makedirs(self.testdir)
50262306a36Sopenharmony_ci		self.sudoUserchown(self.testdir)
50362306a36Sopenharmony_ci	def getValueList(self, value):
50462306a36Sopenharmony_ci		out = []
50562306a36Sopenharmony_ci		for i in value.split(','):
50662306a36Sopenharmony_ci			if i.strip():
50762306a36Sopenharmony_ci				out.append(i.strip())
50862306a36Sopenharmony_ci		return out
50962306a36Sopenharmony_ci	def setDeviceFilter(self, value):
51062306a36Sopenharmony_ci		self.devicefilter = self.getValueList(value)
51162306a36Sopenharmony_ci	def setCallgraphFilter(self, value):
51262306a36Sopenharmony_ci		self.cgfilter = self.getValueList(value)
51362306a36Sopenharmony_ci	def skipKprobes(self, value):
51462306a36Sopenharmony_ci		for k in self.getValueList(value):
51562306a36Sopenharmony_ci			if k in self.tracefuncs:
51662306a36Sopenharmony_ci				del self.tracefuncs[k]
51762306a36Sopenharmony_ci			if k in self.dev_tracefuncs:
51862306a36Sopenharmony_ci				del self.dev_tracefuncs[k]
51962306a36Sopenharmony_ci	def setCallgraphBlacklist(self, file):
52062306a36Sopenharmony_ci		self.cgblacklist = self.listFromFile(file)
52162306a36Sopenharmony_ci	def rtcWakeAlarmOn(self):
52262306a36Sopenharmony_ci		call('echo 0 > '+self.rtcpath+'/wakealarm', shell=True)
52362306a36Sopenharmony_ci		nowtime = open(self.rtcpath+'/since_epoch', 'r').read().strip()
52462306a36Sopenharmony_ci		if nowtime:
52562306a36Sopenharmony_ci			nowtime = int(nowtime)
52662306a36Sopenharmony_ci		else:
52762306a36Sopenharmony_ci			# if hardware time fails, use the software time
52862306a36Sopenharmony_ci			nowtime = int(datetime.now().strftime('%s'))
52962306a36Sopenharmony_ci		alarm = nowtime + self.rtcwaketime
53062306a36Sopenharmony_ci		call('echo %d > %s/wakealarm' % (alarm, self.rtcpath), shell=True)
53162306a36Sopenharmony_ci	def rtcWakeAlarmOff(self):
53262306a36Sopenharmony_ci		call('echo 0 > %s/wakealarm' % self.rtcpath, shell=True)
53362306a36Sopenharmony_ci	def initdmesg(self):
53462306a36Sopenharmony_ci		# get the latest time stamp from the dmesg log
53562306a36Sopenharmony_ci		lines = Popen('dmesg', stdout=PIPE).stdout.readlines()
53662306a36Sopenharmony_ci		ktime = '0'
53762306a36Sopenharmony_ci		for line in reversed(lines):
53862306a36Sopenharmony_ci			line = ascii(line).replace('\r\n', '')
53962306a36Sopenharmony_ci			idx = line.find('[')
54062306a36Sopenharmony_ci			if idx > 1:
54162306a36Sopenharmony_ci				line = line[idx:]
54262306a36Sopenharmony_ci			m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
54362306a36Sopenharmony_ci			if(m):
54462306a36Sopenharmony_ci				ktime = m.group('ktime')
54562306a36Sopenharmony_ci				break
54662306a36Sopenharmony_ci		self.dmesgstart = float(ktime)
54762306a36Sopenharmony_ci	def getdmesg(self, testdata):
54862306a36Sopenharmony_ci		op = self.writeDatafileHeader(self.dmesgfile, testdata)
54962306a36Sopenharmony_ci		# store all new dmesg lines since initdmesg was called
55062306a36Sopenharmony_ci		fp = Popen('dmesg', stdout=PIPE).stdout
55162306a36Sopenharmony_ci		for line in fp:
55262306a36Sopenharmony_ci			line = ascii(line).replace('\r\n', '')
55362306a36Sopenharmony_ci			idx = line.find('[')
55462306a36Sopenharmony_ci			if idx > 1:
55562306a36Sopenharmony_ci				line = line[idx:]
55662306a36Sopenharmony_ci			m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
55762306a36Sopenharmony_ci			if(not m):
55862306a36Sopenharmony_ci				continue
55962306a36Sopenharmony_ci			ktime = float(m.group('ktime'))
56062306a36Sopenharmony_ci			if ktime > self.dmesgstart:
56162306a36Sopenharmony_ci				op.write(line)
56262306a36Sopenharmony_ci		fp.close()
56362306a36Sopenharmony_ci		op.close()
56462306a36Sopenharmony_ci	def listFromFile(self, file):
56562306a36Sopenharmony_ci		list = []
56662306a36Sopenharmony_ci		fp = open(file)
56762306a36Sopenharmony_ci		for i in fp.read().split('\n'):
56862306a36Sopenharmony_ci			i = i.strip()
56962306a36Sopenharmony_ci			if i and i[0] != '#':
57062306a36Sopenharmony_ci				list.append(i)
57162306a36Sopenharmony_ci		fp.close()
57262306a36Sopenharmony_ci		return list
57362306a36Sopenharmony_ci	def addFtraceFilterFunctions(self, file):
57462306a36Sopenharmony_ci		for i in self.listFromFile(file):
57562306a36Sopenharmony_ci			if len(i) < 2:
57662306a36Sopenharmony_ci				continue
57762306a36Sopenharmony_ci			self.tracefuncs[i] = dict()
57862306a36Sopenharmony_ci	def getFtraceFilterFunctions(self, current):
57962306a36Sopenharmony_ci		self.rootCheck(True)
58062306a36Sopenharmony_ci		if not current:
58162306a36Sopenharmony_ci			call('cat '+self.tpath+'available_filter_functions', shell=True)
58262306a36Sopenharmony_ci			return
58362306a36Sopenharmony_ci		master = self.listFromFile(self.tpath+'available_filter_functions')
58462306a36Sopenharmony_ci		for i in sorted(self.tracefuncs):
58562306a36Sopenharmony_ci			if 'func' in self.tracefuncs[i]:
58662306a36Sopenharmony_ci				i = self.tracefuncs[i]['func']
58762306a36Sopenharmony_ci			if i in master:
58862306a36Sopenharmony_ci				print(i)
58962306a36Sopenharmony_ci			else:
59062306a36Sopenharmony_ci				print(self.colorText(i))
59162306a36Sopenharmony_ci	def setFtraceFilterFunctions(self, list):
59262306a36Sopenharmony_ci		master = self.listFromFile(self.tpath+'available_filter_functions')
59362306a36Sopenharmony_ci		flist = ''
59462306a36Sopenharmony_ci		for i in list:
59562306a36Sopenharmony_ci			if i not in master:
59662306a36Sopenharmony_ci				continue
59762306a36Sopenharmony_ci			if ' [' in i:
59862306a36Sopenharmony_ci				flist += i.split(' ')[0]+'\n'
59962306a36Sopenharmony_ci			else:
60062306a36Sopenharmony_ci				flist += i+'\n'
60162306a36Sopenharmony_ci		fp = open(self.tpath+'set_graph_function', 'w')
60262306a36Sopenharmony_ci		fp.write(flist)
60362306a36Sopenharmony_ci		fp.close()
60462306a36Sopenharmony_ci	def basicKprobe(self, name):
60562306a36Sopenharmony_ci		self.kprobes[name] = {'name': name,'func': name,'args': dict(),'format': name}
60662306a36Sopenharmony_ci	def defaultKprobe(self, name, kdata):
60762306a36Sopenharmony_ci		k = kdata
60862306a36Sopenharmony_ci		for field in ['name', 'format', 'func']:
60962306a36Sopenharmony_ci			if field not in k:
61062306a36Sopenharmony_ci				k[field] = name
61162306a36Sopenharmony_ci		if self.archargs in k:
61262306a36Sopenharmony_ci			k['args'] = k[self.archargs]
61362306a36Sopenharmony_ci		else:
61462306a36Sopenharmony_ci			k['args'] = dict()
61562306a36Sopenharmony_ci			k['format'] = name
61662306a36Sopenharmony_ci		self.kprobes[name] = k
61762306a36Sopenharmony_ci	def kprobeColor(self, name):
61862306a36Sopenharmony_ci		if name not in self.kprobes or 'color' not in self.kprobes[name]:
61962306a36Sopenharmony_ci			return ''
62062306a36Sopenharmony_ci		return self.kprobes[name]['color']
62162306a36Sopenharmony_ci	def kprobeDisplayName(self, name, dataraw):
62262306a36Sopenharmony_ci		if name not in self.kprobes:
62362306a36Sopenharmony_ci			self.basicKprobe(name)
62462306a36Sopenharmony_ci		data = ''
62562306a36Sopenharmony_ci		quote=0
62662306a36Sopenharmony_ci		# first remvoe any spaces inside quotes, and the quotes
62762306a36Sopenharmony_ci		for c in dataraw:
62862306a36Sopenharmony_ci			if c == '"':
62962306a36Sopenharmony_ci				quote = (quote + 1) % 2
63062306a36Sopenharmony_ci			if quote and c == ' ':
63162306a36Sopenharmony_ci				data += '_'
63262306a36Sopenharmony_ci			elif c != '"':
63362306a36Sopenharmony_ci				data += c
63462306a36Sopenharmony_ci		fmt, args = self.kprobes[name]['format'], self.kprobes[name]['args']
63562306a36Sopenharmony_ci		arglist = dict()
63662306a36Sopenharmony_ci		# now process the args
63762306a36Sopenharmony_ci		for arg in sorted(args):
63862306a36Sopenharmony_ci			arglist[arg] = ''
63962306a36Sopenharmony_ci			m = re.match('.* '+arg+'=(?P<arg>.*) ', data);
64062306a36Sopenharmony_ci			if m:
64162306a36Sopenharmony_ci				arglist[arg] = m.group('arg')
64262306a36Sopenharmony_ci			else:
64362306a36Sopenharmony_ci				m = re.match('.* '+arg+'=(?P<arg>.*)', data);
64462306a36Sopenharmony_ci				if m:
64562306a36Sopenharmony_ci					arglist[arg] = m.group('arg')
64662306a36Sopenharmony_ci		out = fmt.format(**arglist)
64762306a36Sopenharmony_ci		out = out.replace(' ', '_').replace('"', '')
64862306a36Sopenharmony_ci		return out
64962306a36Sopenharmony_ci	def kprobeText(self, kname, kprobe):
65062306a36Sopenharmony_ci		name = fmt = func = kname
65162306a36Sopenharmony_ci		args = dict()
65262306a36Sopenharmony_ci		if 'name' in kprobe:
65362306a36Sopenharmony_ci			name = kprobe['name']
65462306a36Sopenharmony_ci		if 'format' in kprobe:
65562306a36Sopenharmony_ci			fmt = kprobe['format']
65662306a36Sopenharmony_ci		if 'func' in kprobe:
65762306a36Sopenharmony_ci			func = kprobe['func']
65862306a36Sopenharmony_ci		if self.archargs in kprobe:
65962306a36Sopenharmony_ci			args = kprobe[self.archargs]
66062306a36Sopenharmony_ci		if 'args' in kprobe:
66162306a36Sopenharmony_ci			args = kprobe['args']
66262306a36Sopenharmony_ci		if re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', func):
66362306a36Sopenharmony_ci			doError('Kprobe "%s" has format info in the function name "%s"' % (name, func))
66462306a36Sopenharmony_ci		for arg in re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', fmt):
66562306a36Sopenharmony_ci			if arg not in args:
66662306a36Sopenharmony_ci				doError('Kprobe "%s" is missing argument "%s"' % (name, arg))
66762306a36Sopenharmony_ci		val = 'p:%s_cal %s' % (name, func)
66862306a36Sopenharmony_ci		for i in sorted(args):
66962306a36Sopenharmony_ci			val += ' %s=%s' % (i, args[i])
67062306a36Sopenharmony_ci		val += '\nr:%s_ret %s $retval\n' % (name, func)
67162306a36Sopenharmony_ci		return val
67262306a36Sopenharmony_ci	def addKprobes(self, output=False):
67362306a36Sopenharmony_ci		if len(self.kprobes) < 1:
67462306a36Sopenharmony_ci			return
67562306a36Sopenharmony_ci		if output:
67662306a36Sopenharmony_ci			pprint('    kprobe functions in this kernel:')
67762306a36Sopenharmony_ci		# first test each kprobe
67862306a36Sopenharmony_ci		rejects = []
67962306a36Sopenharmony_ci		# sort kprobes: trace, ub-dev, custom, dev
68062306a36Sopenharmony_ci		kpl = [[], [], [], []]
68162306a36Sopenharmony_ci		linesout = len(self.kprobes)
68262306a36Sopenharmony_ci		for name in sorted(self.kprobes):
68362306a36Sopenharmony_ci			res = self.colorText('YES', 32)
68462306a36Sopenharmony_ci			if not self.testKprobe(name, self.kprobes[name]):
68562306a36Sopenharmony_ci				res = self.colorText('NO')
68662306a36Sopenharmony_ci				rejects.append(name)
68762306a36Sopenharmony_ci			else:
68862306a36Sopenharmony_ci				if name in self.tracefuncs:
68962306a36Sopenharmony_ci					kpl[0].append(name)
69062306a36Sopenharmony_ci				elif name in self.dev_tracefuncs:
69162306a36Sopenharmony_ci					if 'ub' in self.dev_tracefuncs[name]:
69262306a36Sopenharmony_ci						kpl[1].append(name)
69362306a36Sopenharmony_ci					else:
69462306a36Sopenharmony_ci						kpl[3].append(name)
69562306a36Sopenharmony_ci				else:
69662306a36Sopenharmony_ci					kpl[2].append(name)
69762306a36Sopenharmony_ci			if output:
69862306a36Sopenharmony_ci				pprint('         %s: %s' % (name, res))
69962306a36Sopenharmony_ci		kplist = kpl[0] + kpl[1] + kpl[2] + kpl[3]
70062306a36Sopenharmony_ci		# remove all failed ones from the list
70162306a36Sopenharmony_ci		for name in rejects:
70262306a36Sopenharmony_ci			self.kprobes.pop(name)
70362306a36Sopenharmony_ci		# set the kprobes all at once
70462306a36Sopenharmony_ci		self.fsetVal('', 'kprobe_events')
70562306a36Sopenharmony_ci		kprobeevents = ''
70662306a36Sopenharmony_ci		for kp in kplist:
70762306a36Sopenharmony_ci			kprobeevents += self.kprobeText(kp, self.kprobes[kp])
70862306a36Sopenharmony_ci		self.fsetVal(kprobeevents, 'kprobe_events')
70962306a36Sopenharmony_ci		if output:
71062306a36Sopenharmony_ci			check = self.fgetVal('kprobe_events')
71162306a36Sopenharmony_ci			linesack = (len(check.split('\n')) - 1) // 2
71262306a36Sopenharmony_ci			pprint('    kprobe functions enabled: %d/%d' % (linesack, linesout))
71362306a36Sopenharmony_ci		self.fsetVal('1', 'events/kprobes/enable')
71462306a36Sopenharmony_ci	def testKprobe(self, kname, kprobe):
71562306a36Sopenharmony_ci		self.fsetVal('0', 'events/kprobes/enable')
71662306a36Sopenharmony_ci		kprobeevents = self.kprobeText(kname, kprobe)
71762306a36Sopenharmony_ci		if not kprobeevents:
71862306a36Sopenharmony_ci			return False
71962306a36Sopenharmony_ci		try:
72062306a36Sopenharmony_ci			self.fsetVal(kprobeevents, 'kprobe_events')
72162306a36Sopenharmony_ci			check = self.fgetVal('kprobe_events')
72262306a36Sopenharmony_ci		except:
72362306a36Sopenharmony_ci			return False
72462306a36Sopenharmony_ci		linesout = len(kprobeevents.split('\n'))
72562306a36Sopenharmony_ci		linesack = len(check.split('\n'))
72662306a36Sopenharmony_ci		if linesack < linesout:
72762306a36Sopenharmony_ci			return False
72862306a36Sopenharmony_ci		return True
72962306a36Sopenharmony_ci	def setVal(self, val, file):
73062306a36Sopenharmony_ci		if not os.path.exists(file):
73162306a36Sopenharmony_ci			return False
73262306a36Sopenharmony_ci		try:
73362306a36Sopenharmony_ci			fp = open(file, 'wb', 0)
73462306a36Sopenharmony_ci			fp.write(val.encode())
73562306a36Sopenharmony_ci			fp.flush()
73662306a36Sopenharmony_ci			fp.close()
73762306a36Sopenharmony_ci		except:
73862306a36Sopenharmony_ci			return False
73962306a36Sopenharmony_ci		return True
74062306a36Sopenharmony_ci	def fsetVal(self, val, path):
74162306a36Sopenharmony_ci		if not self.useftrace:
74262306a36Sopenharmony_ci			return False
74362306a36Sopenharmony_ci		return self.setVal(val, self.tpath+path)
74462306a36Sopenharmony_ci	def getVal(self, file):
74562306a36Sopenharmony_ci		res = ''
74662306a36Sopenharmony_ci		if not os.path.exists(file):
74762306a36Sopenharmony_ci			return res
74862306a36Sopenharmony_ci		try:
74962306a36Sopenharmony_ci			fp = open(file, 'r')
75062306a36Sopenharmony_ci			res = fp.read()
75162306a36Sopenharmony_ci			fp.close()
75262306a36Sopenharmony_ci		except:
75362306a36Sopenharmony_ci			pass
75462306a36Sopenharmony_ci		return res
75562306a36Sopenharmony_ci	def fgetVal(self, path):
75662306a36Sopenharmony_ci		if not self.useftrace:
75762306a36Sopenharmony_ci			return ''
75862306a36Sopenharmony_ci		return self.getVal(self.tpath+path)
75962306a36Sopenharmony_ci	def cleanupFtrace(self):
76062306a36Sopenharmony_ci		if self.useftrace:
76162306a36Sopenharmony_ci			self.fsetVal('0', 'events/kprobes/enable')
76262306a36Sopenharmony_ci			self.fsetVal('', 'kprobe_events')
76362306a36Sopenharmony_ci			self.fsetVal('1024', 'buffer_size_kb')
76462306a36Sopenharmony_ci	def setupAllKprobes(self):
76562306a36Sopenharmony_ci		for name in self.tracefuncs:
76662306a36Sopenharmony_ci			self.defaultKprobe(name, self.tracefuncs[name])
76762306a36Sopenharmony_ci		for name in self.dev_tracefuncs:
76862306a36Sopenharmony_ci			self.defaultKprobe(name, self.dev_tracefuncs[name])
76962306a36Sopenharmony_ci	def isCallgraphFunc(self, name):
77062306a36Sopenharmony_ci		if len(self.tracefuncs) < 1 and self.suspendmode == 'command':
77162306a36Sopenharmony_ci			return True
77262306a36Sopenharmony_ci		for i in self.tracefuncs:
77362306a36Sopenharmony_ci			if 'func' in self.tracefuncs[i]:
77462306a36Sopenharmony_ci				f = self.tracefuncs[i]['func']
77562306a36Sopenharmony_ci			else:
77662306a36Sopenharmony_ci				f = i
77762306a36Sopenharmony_ci			if name == f:
77862306a36Sopenharmony_ci				return True
77962306a36Sopenharmony_ci		return False
78062306a36Sopenharmony_ci	def initFtrace(self, quiet=False):
78162306a36Sopenharmony_ci		if not self.useftrace:
78262306a36Sopenharmony_ci			return
78362306a36Sopenharmony_ci		if not quiet:
78462306a36Sopenharmony_ci			sysvals.printSystemInfo(False)
78562306a36Sopenharmony_ci			pprint('INITIALIZING FTRACE')
78662306a36Sopenharmony_ci		# turn trace off
78762306a36Sopenharmony_ci		self.fsetVal('0', 'tracing_on')
78862306a36Sopenharmony_ci		self.cleanupFtrace()
78962306a36Sopenharmony_ci		# set the trace clock to global
79062306a36Sopenharmony_ci		self.fsetVal('global', 'trace_clock')
79162306a36Sopenharmony_ci		self.fsetVal('nop', 'current_tracer')
79262306a36Sopenharmony_ci		# set trace buffer to an appropriate value
79362306a36Sopenharmony_ci		cpus = max(1, self.cpucount)
79462306a36Sopenharmony_ci		if self.bufsize > 0:
79562306a36Sopenharmony_ci			tgtsize = self.bufsize
79662306a36Sopenharmony_ci		elif self.usecallgraph or self.usedevsrc:
79762306a36Sopenharmony_ci			bmax = (1*1024*1024) if self.suspendmode in ['disk', 'command'] \
79862306a36Sopenharmony_ci				else (3*1024*1024)
79962306a36Sopenharmony_ci			tgtsize = min(self.memfree, bmax)
80062306a36Sopenharmony_ci		else:
80162306a36Sopenharmony_ci			tgtsize = 65536
80262306a36Sopenharmony_ci		while not self.fsetVal('%d' % (tgtsize // cpus), 'buffer_size_kb'):
80362306a36Sopenharmony_ci			# if the size failed to set, lower it and keep trying
80462306a36Sopenharmony_ci			tgtsize -= 65536
80562306a36Sopenharmony_ci			if tgtsize < 65536:
80662306a36Sopenharmony_ci				tgtsize = int(self.fgetVal('buffer_size_kb')) * cpus
80762306a36Sopenharmony_ci				break
80862306a36Sopenharmony_ci		self.vprint('Setting trace buffers to %d kB (%d kB per cpu)' % (tgtsize, tgtsize/cpus))
80962306a36Sopenharmony_ci		# initialize the callgraph trace
81062306a36Sopenharmony_ci		if(self.usecallgraph):
81162306a36Sopenharmony_ci			# set trace type
81262306a36Sopenharmony_ci			self.fsetVal('function_graph', 'current_tracer')
81362306a36Sopenharmony_ci			self.fsetVal('', 'set_ftrace_filter')
81462306a36Sopenharmony_ci			# temporary hack to fix https://bugzilla.kernel.org/show_bug.cgi?id=212761
81562306a36Sopenharmony_ci			fp = open(self.tpath+'set_ftrace_notrace', 'w')
81662306a36Sopenharmony_ci			fp.write('native_queued_spin_lock_slowpath\ndev_driver_string')
81762306a36Sopenharmony_ci			fp.close()
81862306a36Sopenharmony_ci			# set trace format options
81962306a36Sopenharmony_ci			self.fsetVal('print-parent', 'trace_options')
82062306a36Sopenharmony_ci			self.fsetVal('funcgraph-abstime', 'trace_options')
82162306a36Sopenharmony_ci			self.fsetVal('funcgraph-cpu', 'trace_options')
82262306a36Sopenharmony_ci			self.fsetVal('funcgraph-duration', 'trace_options')
82362306a36Sopenharmony_ci			self.fsetVal('funcgraph-proc', 'trace_options')
82462306a36Sopenharmony_ci			self.fsetVal('funcgraph-tail', 'trace_options')
82562306a36Sopenharmony_ci			self.fsetVal('nofuncgraph-overhead', 'trace_options')
82662306a36Sopenharmony_ci			self.fsetVal('context-info', 'trace_options')
82762306a36Sopenharmony_ci			self.fsetVal('graph-time', 'trace_options')
82862306a36Sopenharmony_ci			self.fsetVal('%d' % self.max_graph_depth, 'max_graph_depth')
82962306a36Sopenharmony_ci			cf = ['dpm_run_callback']
83062306a36Sopenharmony_ci			if(self.usetraceevents):
83162306a36Sopenharmony_ci				cf += ['dpm_prepare', 'dpm_complete']
83262306a36Sopenharmony_ci			for fn in self.tracefuncs:
83362306a36Sopenharmony_ci				if 'func' in self.tracefuncs[fn]:
83462306a36Sopenharmony_ci					cf.append(self.tracefuncs[fn]['func'])
83562306a36Sopenharmony_ci				else:
83662306a36Sopenharmony_ci					cf.append(fn)
83762306a36Sopenharmony_ci			if self.ftop:
83862306a36Sopenharmony_ci				self.setFtraceFilterFunctions([self.ftopfunc])
83962306a36Sopenharmony_ci			else:
84062306a36Sopenharmony_ci				self.setFtraceFilterFunctions(cf)
84162306a36Sopenharmony_ci		# initialize the kprobe trace
84262306a36Sopenharmony_ci		elif self.usekprobes:
84362306a36Sopenharmony_ci			for name in self.tracefuncs:
84462306a36Sopenharmony_ci				self.defaultKprobe(name, self.tracefuncs[name])
84562306a36Sopenharmony_ci			if self.usedevsrc:
84662306a36Sopenharmony_ci				for name in self.dev_tracefuncs:
84762306a36Sopenharmony_ci					self.defaultKprobe(name, self.dev_tracefuncs[name])
84862306a36Sopenharmony_ci			if not quiet:
84962306a36Sopenharmony_ci				pprint('INITIALIZING KPROBES')
85062306a36Sopenharmony_ci			self.addKprobes(self.verbose)
85162306a36Sopenharmony_ci		if(self.usetraceevents):
85262306a36Sopenharmony_ci			# turn trace events on
85362306a36Sopenharmony_ci			events = iter(self.traceevents)
85462306a36Sopenharmony_ci			for e in events:
85562306a36Sopenharmony_ci				self.fsetVal('1', 'events/power/'+e+'/enable')
85662306a36Sopenharmony_ci		# clear the trace buffer
85762306a36Sopenharmony_ci		self.fsetVal('', 'trace')
85862306a36Sopenharmony_ci	def verifyFtrace(self):
85962306a36Sopenharmony_ci		# files needed for any trace data
86062306a36Sopenharmony_ci		files = ['buffer_size_kb', 'current_tracer', 'trace', 'trace_clock',
86162306a36Sopenharmony_ci				 'trace_marker', 'trace_options', 'tracing_on']
86262306a36Sopenharmony_ci		# files needed for callgraph trace data
86362306a36Sopenharmony_ci		tp = self.tpath
86462306a36Sopenharmony_ci		if(self.usecallgraph):
86562306a36Sopenharmony_ci			files += [
86662306a36Sopenharmony_ci				'available_filter_functions',
86762306a36Sopenharmony_ci				'set_ftrace_filter',
86862306a36Sopenharmony_ci				'set_graph_function'
86962306a36Sopenharmony_ci			]
87062306a36Sopenharmony_ci		for f in files:
87162306a36Sopenharmony_ci			if(os.path.exists(tp+f) == False):
87262306a36Sopenharmony_ci				return False
87362306a36Sopenharmony_ci		return True
87462306a36Sopenharmony_ci	def verifyKprobes(self):
87562306a36Sopenharmony_ci		# files needed for kprobes to work
87662306a36Sopenharmony_ci		files = ['kprobe_events', 'events']
87762306a36Sopenharmony_ci		tp = self.tpath
87862306a36Sopenharmony_ci		for f in files:
87962306a36Sopenharmony_ci			if(os.path.exists(tp+f) == False):
88062306a36Sopenharmony_ci				return False
88162306a36Sopenharmony_ci		return True
88262306a36Sopenharmony_ci	def colorText(self, str, color=31):
88362306a36Sopenharmony_ci		if not self.ansi:
88462306a36Sopenharmony_ci			return str
88562306a36Sopenharmony_ci		return '\x1B[%d;40m%s\x1B[m' % (color, str)
88662306a36Sopenharmony_ci	def writeDatafileHeader(self, filename, testdata):
88762306a36Sopenharmony_ci		fp = self.openlog(filename, 'w')
88862306a36Sopenharmony_ci		fp.write('%s\n%s\n# command | %s\n' % (self.teststamp, self.sysstamp, self.cmdline))
88962306a36Sopenharmony_ci		for test in testdata:
89062306a36Sopenharmony_ci			if 'fw' in test:
89162306a36Sopenharmony_ci				fw = test['fw']
89262306a36Sopenharmony_ci				if(fw):
89362306a36Sopenharmony_ci					fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1]))
89462306a36Sopenharmony_ci			if 'turbo' in test:
89562306a36Sopenharmony_ci				fp.write('# turbostat %s\n' % test['turbo'])
89662306a36Sopenharmony_ci			if 'wifi' in test:
89762306a36Sopenharmony_ci				fp.write('# wifi %s\n' % test['wifi'])
89862306a36Sopenharmony_ci			if 'netfix' in test:
89962306a36Sopenharmony_ci				fp.write('# netfix %s\n' % test['netfix'])
90062306a36Sopenharmony_ci			if test['error'] or len(testdata) > 1:
90162306a36Sopenharmony_ci				fp.write('# enter_sleep_error %s\n' % test['error'])
90262306a36Sopenharmony_ci		return fp
90362306a36Sopenharmony_ci	def sudoUserchown(self, dir):
90462306a36Sopenharmony_ci		if os.path.exists(dir) and self.sudouser:
90562306a36Sopenharmony_ci			cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1'
90662306a36Sopenharmony_ci			call(cmd.format(self.sudouser, dir), shell=True)
90762306a36Sopenharmony_ci	def outputResult(self, testdata, num=0):
90862306a36Sopenharmony_ci		if not self.result:
90962306a36Sopenharmony_ci			return
91062306a36Sopenharmony_ci		n = ''
91162306a36Sopenharmony_ci		if num > 0:
91262306a36Sopenharmony_ci			n = '%d' % num
91362306a36Sopenharmony_ci		fp = open(self.result, 'a')
91462306a36Sopenharmony_ci		if 'error' in testdata:
91562306a36Sopenharmony_ci			fp.write('result%s: fail\n' % n)
91662306a36Sopenharmony_ci			fp.write('error%s: %s\n' % (n, testdata['error']))
91762306a36Sopenharmony_ci		else:
91862306a36Sopenharmony_ci			fp.write('result%s: pass\n' % n)
91962306a36Sopenharmony_ci		if 'mode' in testdata:
92062306a36Sopenharmony_ci			fp.write('mode%s: %s\n' % (n, testdata['mode']))
92162306a36Sopenharmony_ci		for v in ['suspend', 'resume', 'boot', 'lastinit']:
92262306a36Sopenharmony_ci			if v in testdata:
92362306a36Sopenharmony_ci				fp.write('%s%s: %.3f\n' % (v, n, testdata[v]))
92462306a36Sopenharmony_ci		for v in ['fwsuspend', 'fwresume']:
92562306a36Sopenharmony_ci			if v in testdata:
92662306a36Sopenharmony_ci				fp.write('%s%s: %.3f\n' % (v, n, testdata[v] / 1000000.0))
92762306a36Sopenharmony_ci		if 'bugurl' in testdata:
92862306a36Sopenharmony_ci			fp.write('url%s: %s\n' % (n, testdata['bugurl']))
92962306a36Sopenharmony_ci		fp.close()
93062306a36Sopenharmony_ci		self.sudoUserchown(self.result)
93162306a36Sopenharmony_ci	def configFile(self, file):
93262306a36Sopenharmony_ci		dir = os.path.dirname(os.path.realpath(__file__))
93362306a36Sopenharmony_ci		if os.path.exists(file):
93462306a36Sopenharmony_ci			return file
93562306a36Sopenharmony_ci		elif os.path.exists(dir+'/'+file):
93662306a36Sopenharmony_ci			return dir+'/'+file
93762306a36Sopenharmony_ci		elif os.path.exists(dir+'/config/'+file):
93862306a36Sopenharmony_ci			return dir+'/config/'+file
93962306a36Sopenharmony_ci		return ''
94062306a36Sopenharmony_ci	def openlog(self, filename, mode):
94162306a36Sopenharmony_ci		isgz = self.gzip
94262306a36Sopenharmony_ci		if mode == 'r':
94362306a36Sopenharmony_ci			try:
94462306a36Sopenharmony_ci				with gzip.open(filename, mode+'t') as fp:
94562306a36Sopenharmony_ci					test = fp.read(64)
94662306a36Sopenharmony_ci				isgz = True
94762306a36Sopenharmony_ci			except:
94862306a36Sopenharmony_ci				isgz = False
94962306a36Sopenharmony_ci		if isgz:
95062306a36Sopenharmony_ci			return gzip.open(filename, mode+'t')
95162306a36Sopenharmony_ci		return open(filename, mode)
95262306a36Sopenharmony_ci	def putlog(self, filename, text):
95362306a36Sopenharmony_ci		with self.openlog(filename, 'a') as fp:
95462306a36Sopenharmony_ci			fp.write(text)
95562306a36Sopenharmony_ci			fp.close()
95662306a36Sopenharmony_ci	def dlog(self, text):
95762306a36Sopenharmony_ci		if not self.dmesgfile:
95862306a36Sopenharmony_ci			return
95962306a36Sopenharmony_ci		self.putlog(self.dmesgfile, '# %s\n' % text)
96062306a36Sopenharmony_ci	def flog(self, text):
96162306a36Sopenharmony_ci		self.putlog(self.ftracefile, text)
96262306a36Sopenharmony_ci	def b64unzip(self, data):
96362306a36Sopenharmony_ci		try:
96462306a36Sopenharmony_ci			out = codecs.decode(base64.b64decode(data), 'zlib').decode()
96562306a36Sopenharmony_ci		except:
96662306a36Sopenharmony_ci			out = data
96762306a36Sopenharmony_ci		return out
96862306a36Sopenharmony_ci	def b64zip(self, data):
96962306a36Sopenharmony_ci		out = base64.b64encode(codecs.encode(data.encode(), 'zlib')).decode()
97062306a36Sopenharmony_ci		return out
97162306a36Sopenharmony_ci	def platforminfo(self, cmdafter):
97262306a36Sopenharmony_ci		# add platform info on to a completed ftrace file
97362306a36Sopenharmony_ci		if not os.path.exists(self.ftracefile):
97462306a36Sopenharmony_ci			return False
97562306a36Sopenharmony_ci		footer = '#\n'
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci		# add test command string line if need be
97862306a36Sopenharmony_ci		if self.suspendmode == 'command' and self.testcommand:
97962306a36Sopenharmony_ci			footer += '# platform-testcmd: %s\n' % (self.testcommand)
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci		# get a list of target devices from the ftrace file
98262306a36Sopenharmony_ci		props = dict()
98362306a36Sopenharmony_ci		tp = TestProps()
98462306a36Sopenharmony_ci		tf = self.openlog(self.ftracefile, 'r')
98562306a36Sopenharmony_ci		for line in tf:
98662306a36Sopenharmony_ci			if tp.stampInfo(line, self):
98762306a36Sopenharmony_ci				continue
98862306a36Sopenharmony_ci			# parse only valid lines, if this is not one move on
98962306a36Sopenharmony_ci			m = re.match(tp.ftrace_line_fmt, line)
99062306a36Sopenharmony_ci			if(not m or 'device_pm_callback_start' not in line):
99162306a36Sopenharmony_ci				continue
99262306a36Sopenharmony_ci			m = re.match('.*: (?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*', m.group('msg'));
99362306a36Sopenharmony_ci			if(not m):
99462306a36Sopenharmony_ci				continue
99562306a36Sopenharmony_ci			dev = m.group('d')
99662306a36Sopenharmony_ci			if dev not in props:
99762306a36Sopenharmony_ci				props[dev] = DevProps()
99862306a36Sopenharmony_ci		tf.close()
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci		# now get the syspath for each target device
100162306a36Sopenharmony_ci		for dirname, dirnames, filenames in os.walk('/sys/devices'):
100262306a36Sopenharmony_ci			if(re.match('.*/power', dirname) and 'async' in filenames):
100362306a36Sopenharmony_ci				dev = dirname.split('/')[-2]
100462306a36Sopenharmony_ci				if dev in props and (not props[dev].syspath or len(dirname) < len(props[dev].syspath)):
100562306a36Sopenharmony_ci					props[dev].syspath = dirname[:-6]
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci		# now fill in the properties for our target devices
100862306a36Sopenharmony_ci		for dev in sorted(props):
100962306a36Sopenharmony_ci			dirname = props[dev].syspath
101062306a36Sopenharmony_ci			if not dirname or not os.path.exists(dirname):
101162306a36Sopenharmony_ci				continue
101262306a36Sopenharmony_ci			props[dev].isasync = False
101362306a36Sopenharmony_ci			if os.path.exists(dirname+'/power/async'):
101462306a36Sopenharmony_ci				fp = open(dirname+'/power/async')
101562306a36Sopenharmony_ci				if 'enabled' in fp.read():
101662306a36Sopenharmony_ci					props[dev].isasync = True
101762306a36Sopenharmony_ci				fp.close()
101862306a36Sopenharmony_ci			fields = os.listdir(dirname)
101962306a36Sopenharmony_ci			for file in ['product', 'name', 'model', 'description', 'id', 'idVendor']:
102062306a36Sopenharmony_ci				if file not in fields:
102162306a36Sopenharmony_ci					continue
102262306a36Sopenharmony_ci				try:
102362306a36Sopenharmony_ci					with open(os.path.join(dirname, file), 'rb') as fp:
102462306a36Sopenharmony_ci						props[dev].altname = ascii(fp.read())
102562306a36Sopenharmony_ci				except:
102662306a36Sopenharmony_ci					continue
102762306a36Sopenharmony_ci				if file == 'idVendor':
102862306a36Sopenharmony_ci					idv, idp = props[dev].altname.strip(), ''
102962306a36Sopenharmony_ci					try:
103062306a36Sopenharmony_ci						with open(os.path.join(dirname, 'idProduct'), 'rb') as fp:
103162306a36Sopenharmony_ci							idp = ascii(fp.read()).strip()
103262306a36Sopenharmony_ci					except:
103362306a36Sopenharmony_ci						props[dev].altname = ''
103462306a36Sopenharmony_ci						break
103562306a36Sopenharmony_ci					props[dev].altname = '%s:%s' % (idv, idp)
103662306a36Sopenharmony_ci				break
103762306a36Sopenharmony_ci			if props[dev].altname:
103862306a36Sopenharmony_ci				out = props[dev].altname.strip().replace('\n', ' ')\
103962306a36Sopenharmony_ci					.replace(',', ' ').replace(';', ' ')
104062306a36Sopenharmony_ci				props[dev].altname = out
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci		# add a devinfo line to the bottom of ftrace
104362306a36Sopenharmony_ci		out = ''
104462306a36Sopenharmony_ci		for dev in sorted(props):
104562306a36Sopenharmony_ci			out += props[dev].out(dev)
104662306a36Sopenharmony_ci		footer += '# platform-devinfo: %s\n' % self.b64zip(out)
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci		# add a line for each of these commands with their outputs
104962306a36Sopenharmony_ci		for name, cmdline, info in cmdafter:
105062306a36Sopenharmony_ci			footer += '# platform-%s: %s | %s\n' % (name, cmdline, self.b64zip(info))
105162306a36Sopenharmony_ci		self.flog(footer)
105262306a36Sopenharmony_ci		return True
105362306a36Sopenharmony_ci	def commonPrefix(self, list):
105462306a36Sopenharmony_ci		if len(list) < 2:
105562306a36Sopenharmony_ci			return ''
105662306a36Sopenharmony_ci		prefix = list[0]
105762306a36Sopenharmony_ci		for s in list[1:]:
105862306a36Sopenharmony_ci			while s[:len(prefix)] != prefix and prefix:
105962306a36Sopenharmony_ci				prefix = prefix[:len(prefix)-1]
106062306a36Sopenharmony_ci			if not prefix:
106162306a36Sopenharmony_ci				break
106262306a36Sopenharmony_ci		if '/' in prefix and prefix[-1] != '/':
106362306a36Sopenharmony_ci			prefix = prefix[0:prefix.rfind('/')+1]
106462306a36Sopenharmony_ci		return prefix
106562306a36Sopenharmony_ci	def dictify(self, text, format):
106662306a36Sopenharmony_ci		out = dict()
106762306a36Sopenharmony_ci		header = True if format == 1 else False
106862306a36Sopenharmony_ci		delim = ' ' if format == 1 else ':'
106962306a36Sopenharmony_ci		for line in text.split('\n'):
107062306a36Sopenharmony_ci			if header:
107162306a36Sopenharmony_ci				header, out['@'] = False, line
107262306a36Sopenharmony_ci				continue
107362306a36Sopenharmony_ci			line = line.strip()
107462306a36Sopenharmony_ci			if delim in line:
107562306a36Sopenharmony_ci				data = line.split(delim, 1)
107662306a36Sopenharmony_ci				num = re.search(r'[\d]+', data[1])
107762306a36Sopenharmony_ci				if format == 2 and num:
107862306a36Sopenharmony_ci					out[data[0].strip()] = num.group()
107962306a36Sopenharmony_ci				else:
108062306a36Sopenharmony_ci					out[data[0].strip()] = data[1]
108162306a36Sopenharmony_ci		return out
108262306a36Sopenharmony_ci	def cmdinfovar(self, arg):
108362306a36Sopenharmony_ci		if arg == 'ethdev':
108462306a36Sopenharmony_ci			try:
108562306a36Sopenharmony_ci				cmd = [self.getExec('ip'), '-4', '-o', '-br', 'addr']
108662306a36Sopenharmony_ci				fp = Popen(cmd, stdout=PIPE, stderr=PIPE).stdout
108762306a36Sopenharmony_ci				info = ascii(fp.read()).strip()
108862306a36Sopenharmony_ci				fp.close()
108962306a36Sopenharmony_ci			except:
109062306a36Sopenharmony_ci				return 'iptoolcrash'
109162306a36Sopenharmony_ci			for line in info.split('\n'):
109262306a36Sopenharmony_ci				if line[0] == 'e' and 'UP' in line:
109362306a36Sopenharmony_ci					return line.split()[0]
109462306a36Sopenharmony_ci			return 'nodevicefound'
109562306a36Sopenharmony_ci		return 'unknown'
109662306a36Sopenharmony_ci	def cmdinfo(self, begin, debug=False):
109762306a36Sopenharmony_ci		out = []
109862306a36Sopenharmony_ci		if begin:
109962306a36Sopenharmony_ci			self.cmd1 = dict()
110062306a36Sopenharmony_ci		for cargs in self.infocmds:
110162306a36Sopenharmony_ci			delta, name, args = cargs[0], cargs[1], cargs[2:]
110262306a36Sopenharmony_ci			for i in range(len(args)):
110362306a36Sopenharmony_ci				if args[i][0] == '{' and args[i][-1] == '}':
110462306a36Sopenharmony_ci					args[i] = self.cmdinfovar(args[i][1:-1])
110562306a36Sopenharmony_ci			cmdline, cmdpath = ' '.join(args[0:]), self.getExec(args[0])
110662306a36Sopenharmony_ci			if not cmdpath or (begin and not delta):
110762306a36Sopenharmony_ci				continue
110862306a36Sopenharmony_ci			self.dlog('[%s]' % cmdline)
110962306a36Sopenharmony_ci			try:
111062306a36Sopenharmony_ci				fp = Popen([cmdpath]+args[1:], stdout=PIPE, stderr=PIPE).stdout
111162306a36Sopenharmony_ci				info = ascii(fp.read()).strip()
111262306a36Sopenharmony_ci				fp.close()
111362306a36Sopenharmony_ci			except:
111462306a36Sopenharmony_ci				continue
111562306a36Sopenharmony_ci			if not debug and begin:
111662306a36Sopenharmony_ci				self.cmd1[name] = self.dictify(info, delta)
111762306a36Sopenharmony_ci			elif not debug and delta and name in self.cmd1:
111862306a36Sopenharmony_ci				before, after = self.cmd1[name], self.dictify(info, delta)
111962306a36Sopenharmony_ci				dinfo = ('\t%s\n' % before['@']) if '@' in before and len(before) > 1 else ''
112062306a36Sopenharmony_ci				prefix = self.commonPrefix(list(before.keys()))
112162306a36Sopenharmony_ci				for key in sorted(before):
112262306a36Sopenharmony_ci					if key in after and before[key] != after[key]:
112362306a36Sopenharmony_ci						title = key.replace(prefix, '')
112462306a36Sopenharmony_ci						if delta == 2:
112562306a36Sopenharmony_ci							dinfo += '\t%s : %s -> %s\n' % \
112662306a36Sopenharmony_ci								(title, before[key].strip(), after[key].strip())
112762306a36Sopenharmony_ci						else:
112862306a36Sopenharmony_ci							dinfo += '%10s (start) : %s\n%10s (after) : %s\n' % \
112962306a36Sopenharmony_ci								(title, before[key], title, after[key])
113062306a36Sopenharmony_ci				dinfo = '\tnothing changed' if not dinfo else dinfo.rstrip()
113162306a36Sopenharmony_ci				out.append((name, cmdline, dinfo))
113262306a36Sopenharmony_ci			else:
113362306a36Sopenharmony_ci				out.append((name, cmdline, '\tnothing' if not info else info))
113462306a36Sopenharmony_ci		return out
113562306a36Sopenharmony_ci	def testVal(self, file, fmt='basic', value=''):
113662306a36Sopenharmony_ci		if file == 'restoreall':
113762306a36Sopenharmony_ci			for f in self.cfgdef:
113862306a36Sopenharmony_ci				if os.path.exists(f):
113962306a36Sopenharmony_ci					fp = open(f, 'w')
114062306a36Sopenharmony_ci					fp.write(self.cfgdef[f])
114162306a36Sopenharmony_ci					fp.close()
114262306a36Sopenharmony_ci			self.cfgdef = dict()
114362306a36Sopenharmony_ci		elif value and os.path.exists(file):
114462306a36Sopenharmony_ci			fp = open(file, 'r+')
114562306a36Sopenharmony_ci			if fmt == 'radio':
114662306a36Sopenharmony_ci				m = re.match('.*\[(?P<v>.*)\].*', fp.read())
114762306a36Sopenharmony_ci				if m:
114862306a36Sopenharmony_ci					self.cfgdef[file] = m.group('v')
114962306a36Sopenharmony_ci			elif fmt == 'acpi':
115062306a36Sopenharmony_ci				line = fp.read().strip().split('\n')[-1]
115162306a36Sopenharmony_ci				m = re.match('.* (?P<v>[0-9A-Fx]*) .*', line)
115262306a36Sopenharmony_ci				if m:
115362306a36Sopenharmony_ci					self.cfgdef[file] = m.group('v')
115462306a36Sopenharmony_ci			else:
115562306a36Sopenharmony_ci				self.cfgdef[file] = fp.read().strip()
115662306a36Sopenharmony_ci			fp.write(value)
115762306a36Sopenharmony_ci			fp.close()
115862306a36Sopenharmony_ci	def s0ixSupport(self):
115962306a36Sopenharmony_ci		if not os.path.exists(self.s0ixres) or not os.path.exists(self.mempowerfile):
116062306a36Sopenharmony_ci			return False
116162306a36Sopenharmony_ci		fp = open(sysvals.mempowerfile, 'r')
116262306a36Sopenharmony_ci		data = fp.read().strip()
116362306a36Sopenharmony_ci		fp.close()
116462306a36Sopenharmony_ci		if '[s2idle]' in data:
116562306a36Sopenharmony_ci			return True
116662306a36Sopenharmony_ci		return False
116762306a36Sopenharmony_ci	def haveTurbostat(self):
116862306a36Sopenharmony_ci		if not self.tstat:
116962306a36Sopenharmony_ci			return False
117062306a36Sopenharmony_ci		cmd = self.getExec('turbostat')
117162306a36Sopenharmony_ci		if not cmd:
117262306a36Sopenharmony_ci			return False
117362306a36Sopenharmony_ci		fp = Popen([cmd, '-v'], stdout=PIPE, stderr=PIPE).stderr
117462306a36Sopenharmony_ci		out = ascii(fp.read()).strip()
117562306a36Sopenharmony_ci		fp.close()
117662306a36Sopenharmony_ci		if re.match('turbostat version .*', out):
117762306a36Sopenharmony_ci			self.vprint(out)
117862306a36Sopenharmony_ci			return True
117962306a36Sopenharmony_ci		return False
118062306a36Sopenharmony_ci	def turbostat(self, s0ixready):
118162306a36Sopenharmony_ci		cmd = self.getExec('turbostat')
118262306a36Sopenharmony_ci		rawout = keyline = valline = ''
118362306a36Sopenharmony_ci		fullcmd = '%s -q -S echo freeze > %s' % (cmd, self.powerfile)
118462306a36Sopenharmony_ci		fp = Popen(['sh', '-c', fullcmd], stdout=PIPE, stderr=PIPE).stderr
118562306a36Sopenharmony_ci		for line in fp:
118662306a36Sopenharmony_ci			line = ascii(line)
118762306a36Sopenharmony_ci			rawout += line
118862306a36Sopenharmony_ci			if keyline and valline:
118962306a36Sopenharmony_ci				continue
119062306a36Sopenharmony_ci			if re.match('(?i)Avg_MHz.*', line):
119162306a36Sopenharmony_ci				keyline = line.strip().split()
119262306a36Sopenharmony_ci			elif keyline:
119362306a36Sopenharmony_ci				valline = line.strip().split()
119462306a36Sopenharmony_ci		fp.close()
119562306a36Sopenharmony_ci		if not keyline or not valline or len(keyline) != len(valline):
119662306a36Sopenharmony_ci			errmsg = 'unrecognized turbostat output:\n'+rawout.strip()
119762306a36Sopenharmony_ci			self.vprint(errmsg)
119862306a36Sopenharmony_ci			if not self.verbose:
119962306a36Sopenharmony_ci				pprint(errmsg)
120062306a36Sopenharmony_ci			return ''
120162306a36Sopenharmony_ci		if self.verbose:
120262306a36Sopenharmony_ci			pprint(rawout.strip())
120362306a36Sopenharmony_ci		out = []
120462306a36Sopenharmony_ci		for key in keyline:
120562306a36Sopenharmony_ci			idx = keyline.index(key)
120662306a36Sopenharmony_ci			val = valline[idx]
120762306a36Sopenharmony_ci			if key == 'SYS%LPI' and not s0ixready and re.match('^[0\.]*$', val):
120862306a36Sopenharmony_ci				continue
120962306a36Sopenharmony_ci			out.append('%s=%s' % (key, val))
121062306a36Sopenharmony_ci		return '|'.join(out)
121162306a36Sopenharmony_ci	def netfixon(self, net='both'):
121262306a36Sopenharmony_ci		cmd = self.getExec('netfix')
121362306a36Sopenharmony_ci		if not cmd:
121462306a36Sopenharmony_ci			return ''
121562306a36Sopenharmony_ci		fp = Popen([cmd, '-s', net, 'on'], stdout=PIPE, stderr=PIPE).stdout
121662306a36Sopenharmony_ci		out = ascii(fp.read()).strip()
121762306a36Sopenharmony_ci		fp.close()
121862306a36Sopenharmony_ci		return out
121962306a36Sopenharmony_ci	def wifiDetails(self, dev):
122062306a36Sopenharmony_ci		try:
122162306a36Sopenharmony_ci			info = open('/sys/class/net/%s/device/uevent' % dev, 'r').read().strip()
122262306a36Sopenharmony_ci		except:
122362306a36Sopenharmony_ci			return dev
122462306a36Sopenharmony_ci		vals = [dev]
122562306a36Sopenharmony_ci		for prop in info.split('\n'):
122662306a36Sopenharmony_ci			if prop.startswith('DRIVER=') or prop.startswith('PCI_ID='):
122762306a36Sopenharmony_ci				vals.append(prop.split('=')[-1])
122862306a36Sopenharmony_ci		return ':'.join(vals)
122962306a36Sopenharmony_ci	def checkWifi(self, dev=''):
123062306a36Sopenharmony_ci		try:
123162306a36Sopenharmony_ci			w = open('/proc/net/wireless', 'r').read().strip()
123262306a36Sopenharmony_ci		except:
123362306a36Sopenharmony_ci			return ''
123462306a36Sopenharmony_ci		for line in reversed(w.split('\n')):
123562306a36Sopenharmony_ci			m = re.match(' *(?P<dev>.*): (?P<stat>[0-9a-f]*) .*', line)
123662306a36Sopenharmony_ci			if not m or (dev and dev != m.group('dev')):
123762306a36Sopenharmony_ci				continue
123862306a36Sopenharmony_ci			return m.group('dev')
123962306a36Sopenharmony_ci		return ''
124062306a36Sopenharmony_ci	def pollWifi(self, dev, timeout=10):
124162306a36Sopenharmony_ci		start = time.time()
124262306a36Sopenharmony_ci		while (time.time() - start) < timeout:
124362306a36Sopenharmony_ci			w = self.checkWifi(dev)
124462306a36Sopenharmony_ci			if w:
124562306a36Sopenharmony_ci				return '%s reconnected %.2f' % \
124662306a36Sopenharmony_ci					(self.wifiDetails(dev), max(0, time.time() - start))
124762306a36Sopenharmony_ci			time.sleep(0.01)
124862306a36Sopenharmony_ci		return '%s timeout %d' % (self.wifiDetails(dev), timeout)
124962306a36Sopenharmony_ci	def errorSummary(self, errinfo, msg):
125062306a36Sopenharmony_ci		found = False
125162306a36Sopenharmony_ci		for entry in errinfo:
125262306a36Sopenharmony_ci			if re.match(entry['match'], msg):
125362306a36Sopenharmony_ci				entry['count'] += 1
125462306a36Sopenharmony_ci				if self.hostname not in entry['urls']:
125562306a36Sopenharmony_ci					entry['urls'][self.hostname] = [self.htmlfile]
125662306a36Sopenharmony_ci				elif self.htmlfile not in entry['urls'][self.hostname]:
125762306a36Sopenharmony_ci					entry['urls'][self.hostname].append(self.htmlfile)
125862306a36Sopenharmony_ci				found = True
125962306a36Sopenharmony_ci				break
126062306a36Sopenharmony_ci		if found:
126162306a36Sopenharmony_ci			return
126262306a36Sopenharmony_ci		arr = msg.split()
126362306a36Sopenharmony_ci		for j in range(len(arr)):
126462306a36Sopenharmony_ci			if re.match('^[0-9,\-\.]*$', arr[j]):
126562306a36Sopenharmony_ci				arr[j] = '[0-9,\-\.]*'
126662306a36Sopenharmony_ci			else:
126762306a36Sopenharmony_ci				arr[j] = arr[j]\
126862306a36Sopenharmony_ci					.replace('\\', '\\\\').replace(']', '\]').replace('[', '\[')\
126962306a36Sopenharmony_ci					.replace('.', '\.').replace('+', '\+').replace('*', '\*')\
127062306a36Sopenharmony_ci					.replace('(', '\(').replace(')', '\)').replace('}', '\}')\
127162306a36Sopenharmony_ci					.replace('{', '\{')
127262306a36Sopenharmony_ci		mstr = ' *'.join(arr)
127362306a36Sopenharmony_ci		entry = {
127462306a36Sopenharmony_ci			'line': msg,
127562306a36Sopenharmony_ci			'match': mstr,
127662306a36Sopenharmony_ci			'count': 1,
127762306a36Sopenharmony_ci			'urls': {self.hostname: [self.htmlfile]}
127862306a36Sopenharmony_ci		}
127962306a36Sopenharmony_ci		errinfo.append(entry)
128062306a36Sopenharmony_ci	def multistat(self, start, idx, finish):
128162306a36Sopenharmony_ci		if 'time' in self.multitest:
128262306a36Sopenharmony_ci			id = '%d Duration=%dmin' % (idx+1, self.multitest['time'])
128362306a36Sopenharmony_ci		else:
128462306a36Sopenharmony_ci			id = '%d/%d' % (idx+1, self.multitest['count'])
128562306a36Sopenharmony_ci		t = time.time()
128662306a36Sopenharmony_ci		if 'start' not in self.multitest:
128762306a36Sopenharmony_ci			self.multitest['start'] = self.multitest['last'] = t
128862306a36Sopenharmony_ci			self.multitest['total'] = 0.0
128962306a36Sopenharmony_ci			pprint('TEST (%s) START' % id)
129062306a36Sopenharmony_ci			return
129162306a36Sopenharmony_ci		dt = t - self.multitest['last']
129262306a36Sopenharmony_ci		if not start:
129362306a36Sopenharmony_ci			if idx == 0 and self.multitest['delay'] > 0:
129462306a36Sopenharmony_ci				self.multitest['total'] += self.multitest['delay']
129562306a36Sopenharmony_ci			pprint('TEST (%s) COMPLETE -- Duration %.1fs' % (id, dt))
129662306a36Sopenharmony_ci			return
129762306a36Sopenharmony_ci		self.multitest['total'] += dt
129862306a36Sopenharmony_ci		self.multitest['last'] = t
129962306a36Sopenharmony_ci		avg = self.multitest['total'] / idx
130062306a36Sopenharmony_ci		if 'time' in self.multitest:
130162306a36Sopenharmony_ci			left = finish - datetime.now()
130262306a36Sopenharmony_ci			left -= timedelta(microseconds=left.microseconds)
130362306a36Sopenharmony_ci		else:
130462306a36Sopenharmony_ci			left = timedelta(seconds=((self.multitest['count'] - idx) * int(avg)))
130562306a36Sopenharmony_ci		pprint('TEST (%s) START - Avg Duration %.1fs, Time left %s' % \
130662306a36Sopenharmony_ci			(id, avg, str(left)))
130762306a36Sopenharmony_ci	def multiinit(self, c, d):
130862306a36Sopenharmony_ci		sz, unit = 'count', 'm'
130962306a36Sopenharmony_ci		if c.endswith('d') or c.endswith('h') or c.endswith('m'):
131062306a36Sopenharmony_ci			sz, unit, c = 'time', c[-1], c[:-1]
131162306a36Sopenharmony_ci		self.multitest['run'] = True
131262306a36Sopenharmony_ci		self.multitest[sz] = getArgInt('multi: n d (exec count)', c, 1, 1000000, False)
131362306a36Sopenharmony_ci		self.multitest['delay'] = getArgInt('multi: n d (delay between tests)', d, 0, 3600, False)
131462306a36Sopenharmony_ci		if unit == 'd':
131562306a36Sopenharmony_ci			self.multitest[sz] *= 1440
131662306a36Sopenharmony_ci		elif unit == 'h':
131762306a36Sopenharmony_ci			self.multitest[sz] *= 60
131862306a36Sopenharmony_ci	def displayControl(self, cmd):
131962306a36Sopenharmony_ci		xset, ret = 'timeout 10 xset -d :0.0 {0}', 0
132062306a36Sopenharmony_ci		if self.sudouser:
132162306a36Sopenharmony_ci			xset = 'sudo -u %s %s' % (self.sudouser, xset)
132262306a36Sopenharmony_ci		if cmd == 'init':
132362306a36Sopenharmony_ci			ret = call(xset.format('dpms 0 0 0'), shell=True)
132462306a36Sopenharmony_ci			if not ret:
132562306a36Sopenharmony_ci				ret = call(xset.format('s off'), shell=True)
132662306a36Sopenharmony_ci		elif cmd == 'reset':
132762306a36Sopenharmony_ci			ret = call(xset.format('s reset'), shell=True)
132862306a36Sopenharmony_ci		elif cmd in ['on', 'off', 'standby', 'suspend']:
132962306a36Sopenharmony_ci			b4 = self.displayControl('stat')
133062306a36Sopenharmony_ci			ret = call(xset.format('dpms force %s' % cmd), shell=True)
133162306a36Sopenharmony_ci			if not ret:
133262306a36Sopenharmony_ci				curr = self.displayControl('stat')
133362306a36Sopenharmony_ci				self.vprint('Display Switched: %s -> %s' % (b4, curr))
133462306a36Sopenharmony_ci				if curr != cmd:
133562306a36Sopenharmony_ci					self.vprint('WARNING: Display failed to change to %s' % cmd)
133662306a36Sopenharmony_ci			if ret:
133762306a36Sopenharmony_ci				self.vprint('WARNING: Display failed to change to %s with xset' % cmd)
133862306a36Sopenharmony_ci				return ret
133962306a36Sopenharmony_ci		elif cmd == 'stat':
134062306a36Sopenharmony_ci			fp = Popen(xset.format('q').split(' '), stdout=PIPE).stdout
134162306a36Sopenharmony_ci			ret = 'unknown'
134262306a36Sopenharmony_ci			for line in fp:
134362306a36Sopenharmony_ci				m = re.match('[\s]*Monitor is (?P<m>.*)', ascii(line))
134462306a36Sopenharmony_ci				if(m and len(m.group('m')) >= 2):
134562306a36Sopenharmony_ci					out = m.group('m').lower()
134662306a36Sopenharmony_ci					ret = out[3:] if out[0:2] == 'in' else out
134762306a36Sopenharmony_ci					break
134862306a36Sopenharmony_ci			fp.close()
134962306a36Sopenharmony_ci		return ret
135062306a36Sopenharmony_ci	def setRuntimeSuspend(self, before=True):
135162306a36Sopenharmony_ci		if before:
135262306a36Sopenharmony_ci			# runtime suspend disable or enable
135362306a36Sopenharmony_ci			if self.rs > 0:
135462306a36Sopenharmony_ci				self.rstgt, self.rsval, self.rsdir = 'on', 'auto', 'enabled'
135562306a36Sopenharmony_ci			else:
135662306a36Sopenharmony_ci				self.rstgt, self.rsval, self.rsdir = 'auto', 'on', 'disabled'
135762306a36Sopenharmony_ci			pprint('CONFIGURING RUNTIME SUSPEND...')
135862306a36Sopenharmony_ci			self.rslist = deviceInfo(self.rstgt)
135962306a36Sopenharmony_ci			for i in self.rslist:
136062306a36Sopenharmony_ci				self.setVal(self.rsval, i)
136162306a36Sopenharmony_ci			pprint('runtime suspend %s on all devices (%d changed)' % (self.rsdir, len(self.rslist)))
136262306a36Sopenharmony_ci			pprint('waiting 5 seconds...')
136362306a36Sopenharmony_ci			time.sleep(5)
136462306a36Sopenharmony_ci		else:
136562306a36Sopenharmony_ci			# runtime suspend re-enable or re-disable
136662306a36Sopenharmony_ci			for i in self.rslist:
136762306a36Sopenharmony_ci				self.setVal(self.rstgt, i)
136862306a36Sopenharmony_ci			pprint('runtime suspend settings restored on %d devices' % len(self.rslist))
136962306a36Sopenharmony_ci	def start(self, pm):
137062306a36Sopenharmony_ci		if self.useftrace:
137162306a36Sopenharmony_ci			self.dlog('start ftrace tracing')
137262306a36Sopenharmony_ci			self.fsetVal('1', 'tracing_on')
137362306a36Sopenharmony_ci			if self.useprocmon:
137462306a36Sopenharmony_ci				self.dlog('start the process monitor')
137562306a36Sopenharmony_ci				pm.start()
137662306a36Sopenharmony_ci	def stop(self, pm):
137762306a36Sopenharmony_ci		if self.useftrace:
137862306a36Sopenharmony_ci			if self.useprocmon:
137962306a36Sopenharmony_ci				self.dlog('stop the process monitor')
138062306a36Sopenharmony_ci				pm.stop()
138162306a36Sopenharmony_ci			self.dlog('stop ftrace tracing')
138262306a36Sopenharmony_ci			self.fsetVal('0', 'tracing_on')
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_cisysvals = SystemValues()
138562306a36Sopenharmony_ciswitchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0']
138662306a36Sopenharmony_ciswitchoff = ['disable', 'off', 'false', '0']
138762306a36Sopenharmony_cisuspendmodename = {
138862306a36Sopenharmony_ci	'standby': 'standby (S1)',
138962306a36Sopenharmony_ci	'freeze': 'freeze (S2idle)',
139062306a36Sopenharmony_ci	'mem': 'suspend (S3)',
139162306a36Sopenharmony_ci	'disk': 'hibernate (S4)'
139262306a36Sopenharmony_ci}
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci# Class: DevProps
139562306a36Sopenharmony_ci# Description:
139662306a36Sopenharmony_ci#	 Simple class which holds property values collected
139762306a36Sopenharmony_ci#	 for all the devices used in the timeline.
139862306a36Sopenharmony_ciclass DevProps:
139962306a36Sopenharmony_ci	def __init__(self):
140062306a36Sopenharmony_ci		self.syspath = ''
140162306a36Sopenharmony_ci		self.altname = ''
140262306a36Sopenharmony_ci		self.isasync = True
140362306a36Sopenharmony_ci		self.xtraclass = ''
140462306a36Sopenharmony_ci		self.xtrainfo = ''
140562306a36Sopenharmony_ci	def out(self, dev):
140662306a36Sopenharmony_ci		return '%s,%s,%d;' % (dev, self.altname, self.isasync)
140762306a36Sopenharmony_ci	def debug(self, dev):
140862306a36Sopenharmony_ci		pprint('%s:\n\taltname = %s\n\t  async = %s' % (dev, self.altname, self.isasync))
140962306a36Sopenharmony_ci	def altName(self, dev):
141062306a36Sopenharmony_ci		if not self.altname or self.altname == dev:
141162306a36Sopenharmony_ci			return dev
141262306a36Sopenharmony_ci		return '%s [%s]' % (self.altname, dev)
141362306a36Sopenharmony_ci	def xtraClass(self):
141462306a36Sopenharmony_ci		if self.xtraclass:
141562306a36Sopenharmony_ci			return ' '+self.xtraclass
141662306a36Sopenharmony_ci		if not self.isasync:
141762306a36Sopenharmony_ci			return ' sync'
141862306a36Sopenharmony_ci		return ''
141962306a36Sopenharmony_ci	def xtraInfo(self):
142062306a36Sopenharmony_ci		if self.xtraclass:
142162306a36Sopenharmony_ci			return ' '+self.xtraclass
142262306a36Sopenharmony_ci		if self.isasync:
142362306a36Sopenharmony_ci			return ' (async)'
142462306a36Sopenharmony_ci		return ' (sync)'
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci# Class: DeviceNode
142762306a36Sopenharmony_ci# Description:
142862306a36Sopenharmony_ci#	 A container used to create a device hierachy, with a single root node
142962306a36Sopenharmony_ci#	 and a tree of child nodes. Used by Data.deviceTopology()
143062306a36Sopenharmony_ciclass DeviceNode:
143162306a36Sopenharmony_ci	def __init__(self, nodename, nodedepth):
143262306a36Sopenharmony_ci		self.name = nodename
143362306a36Sopenharmony_ci		self.children = []
143462306a36Sopenharmony_ci		self.depth = nodedepth
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci# Class: Data
143762306a36Sopenharmony_ci# Description:
143862306a36Sopenharmony_ci#	 The primary container for suspend/resume test data. There is one for
143962306a36Sopenharmony_ci#	 each test run. The data is organized into a cronological hierarchy:
144062306a36Sopenharmony_ci#	 Data.dmesg {
144162306a36Sopenharmony_ci#		phases {
144262306a36Sopenharmony_ci#			10 sequential, non-overlapping phases of S/R
144362306a36Sopenharmony_ci#			contents: times for phase start/end, order/color data for html
144462306a36Sopenharmony_ci#			devlist {
144562306a36Sopenharmony_ci#				device callback or action list for this phase
144662306a36Sopenharmony_ci#				device {
144762306a36Sopenharmony_ci#					a single device callback or generic action
144862306a36Sopenharmony_ci#					contents: start/stop times, pid/cpu/driver info
144962306a36Sopenharmony_ci#						parents/children, html id for timeline/callgraph
145062306a36Sopenharmony_ci#						optionally includes an ftrace callgraph
145162306a36Sopenharmony_ci#						optionally includes dev/ps data
145262306a36Sopenharmony_ci#				}
145362306a36Sopenharmony_ci#			}
145462306a36Sopenharmony_ci#		}
145562306a36Sopenharmony_ci#	}
145662306a36Sopenharmony_ci#
145762306a36Sopenharmony_ciclass Data:
145862306a36Sopenharmony_ci	phasedef = {
145962306a36Sopenharmony_ci		'suspend_prepare': {'order': 0, 'color': '#CCFFCC'},
146062306a36Sopenharmony_ci		        'suspend': {'order': 1, 'color': '#88FF88'},
146162306a36Sopenharmony_ci		   'suspend_late': {'order': 2, 'color': '#00AA00'},
146262306a36Sopenharmony_ci		  'suspend_noirq': {'order': 3, 'color': '#008888'},
146362306a36Sopenharmony_ci		'suspend_machine': {'order': 4, 'color': '#0000FF'},
146462306a36Sopenharmony_ci		 'resume_machine': {'order': 5, 'color': '#FF0000'},
146562306a36Sopenharmony_ci		   'resume_noirq': {'order': 6, 'color': '#FF9900'},
146662306a36Sopenharmony_ci		   'resume_early': {'order': 7, 'color': '#FFCC00'},
146762306a36Sopenharmony_ci		         'resume': {'order': 8, 'color': '#FFFF88'},
146862306a36Sopenharmony_ci		'resume_complete': {'order': 9, 'color': '#FFFFCC'},
146962306a36Sopenharmony_ci	}
147062306a36Sopenharmony_ci	errlist = {
147162306a36Sopenharmony_ci		'HWERROR' : r'.*\[ *Hardware Error *\].*',
147262306a36Sopenharmony_ci		'FWBUG'   : r'.*\[ *Firmware Bug *\].*',
147362306a36Sopenharmony_ci		'TASKFAIL': r'.*Freezing .*after *.*',
147462306a36Sopenharmony_ci		'BUG'     : r'(?i).*\bBUG\b.*',
147562306a36Sopenharmony_ci		'ERROR'   : r'(?i).*\bERROR\b.*',
147662306a36Sopenharmony_ci		'WARNING' : r'(?i).*\bWARNING\b.*',
147762306a36Sopenharmony_ci		'FAULT'   : r'(?i).*\bFAULT\b.*',
147862306a36Sopenharmony_ci		'FAIL'    : r'(?i).*\bFAILED\b.*',
147962306a36Sopenharmony_ci		'INVALID' : r'(?i).*\bINVALID\b.*',
148062306a36Sopenharmony_ci		'CRASH'   : r'(?i).*\bCRASHED\b.*',
148162306a36Sopenharmony_ci		'TIMEOUT' : r'(?i).*\bTIMEOUT\b.*',
148262306a36Sopenharmony_ci		'ABORT'   : r'(?i).*\bABORT\b.*',
148362306a36Sopenharmony_ci		'IRQ'     : r'.*\bgenirq: .*',
148462306a36Sopenharmony_ci		'ACPI'    : r'.*\bACPI *(?P<b>[A-Za-z]*) *Error[: ].*',
148562306a36Sopenharmony_ci		'DISKFULL': r'.*\bNo space left on device.*',
148662306a36Sopenharmony_ci		'USBERR'  : r'.*usb .*device .*, error [0-9-]*',
148762306a36Sopenharmony_ci		'ATAERR'  : r' *ata[0-9\.]*: .*failed.*',
148862306a36Sopenharmony_ci		'MEIERR'  : r' *mei.*: .*failed.*',
148962306a36Sopenharmony_ci		'TPMERR'  : r'(?i) *tpm *tpm[0-9]*: .*error.*',
149062306a36Sopenharmony_ci	}
149162306a36Sopenharmony_ci	def __init__(self, num):
149262306a36Sopenharmony_ci		idchar = 'abcdefghij'
149362306a36Sopenharmony_ci		self.start = 0.0 # test start
149462306a36Sopenharmony_ci		self.end = 0.0   # test end
149562306a36Sopenharmony_ci		self.hwstart = 0 # rtc test start
149662306a36Sopenharmony_ci		self.hwend = 0   # rtc test end
149762306a36Sopenharmony_ci		self.tSuspended = 0.0 # low-level suspend start
149862306a36Sopenharmony_ci		self.tResumed = 0.0   # low-level resume start
149962306a36Sopenharmony_ci		self.tKernSus = 0.0   # kernel level suspend start
150062306a36Sopenharmony_ci		self.tKernRes = 0.0   # kernel level resume end
150162306a36Sopenharmony_ci		self.fwValid = False  # is firmware data available
150262306a36Sopenharmony_ci		self.fwSuspend = 0    # time spent in firmware suspend
150362306a36Sopenharmony_ci		self.fwResume = 0     # time spent in firmware resume
150462306a36Sopenharmony_ci		self.html_device_id = 0
150562306a36Sopenharmony_ci		self.stamp = 0
150662306a36Sopenharmony_ci		self.outfile = ''
150762306a36Sopenharmony_ci		self.kerror = False
150862306a36Sopenharmony_ci		self.wifi = dict()
150962306a36Sopenharmony_ci		self.turbostat = 0
151062306a36Sopenharmony_ci		self.enterfail = ''
151162306a36Sopenharmony_ci		self.currphase = ''
151262306a36Sopenharmony_ci		self.pstl = dict()    # process timeline
151362306a36Sopenharmony_ci		self.testnumber = num
151462306a36Sopenharmony_ci		self.idstr = idchar[num]
151562306a36Sopenharmony_ci		self.dmesgtext = []   # dmesg text file in memory
151662306a36Sopenharmony_ci		self.dmesg = dict()   # root data structure
151762306a36Sopenharmony_ci		self.errorinfo = {'suspend':[],'resume':[]}
151862306a36Sopenharmony_ci		self.tLow = []        # time spent in low-level suspends (standby/freeze)
151962306a36Sopenharmony_ci		self.devpids = []
152062306a36Sopenharmony_ci		self.devicegroups = 0
152162306a36Sopenharmony_ci	def sortedPhases(self):
152262306a36Sopenharmony_ci		return sorted(self.dmesg, key=lambda k:self.dmesg[k]['order'])
152362306a36Sopenharmony_ci	def initDevicegroups(self):
152462306a36Sopenharmony_ci		# called when phases are all finished being added
152562306a36Sopenharmony_ci		for phase in sorted(self.dmesg.keys()):
152662306a36Sopenharmony_ci			if '*' in phase:
152762306a36Sopenharmony_ci				p = phase.split('*')
152862306a36Sopenharmony_ci				pnew = '%s%d' % (p[0], len(p))
152962306a36Sopenharmony_ci				self.dmesg[pnew] = self.dmesg.pop(phase)
153062306a36Sopenharmony_ci		self.devicegroups = []
153162306a36Sopenharmony_ci		for phase in self.sortedPhases():
153262306a36Sopenharmony_ci			self.devicegroups.append([phase])
153362306a36Sopenharmony_ci	def nextPhase(self, phase, offset):
153462306a36Sopenharmony_ci		order = self.dmesg[phase]['order'] + offset
153562306a36Sopenharmony_ci		for p in self.dmesg:
153662306a36Sopenharmony_ci			if self.dmesg[p]['order'] == order:
153762306a36Sopenharmony_ci				return p
153862306a36Sopenharmony_ci		return ''
153962306a36Sopenharmony_ci	def lastPhase(self, depth=1):
154062306a36Sopenharmony_ci		plist = self.sortedPhases()
154162306a36Sopenharmony_ci		if len(plist) < depth:
154262306a36Sopenharmony_ci			return ''
154362306a36Sopenharmony_ci		return plist[-1*depth]
154462306a36Sopenharmony_ci	def turbostatInfo(self):
154562306a36Sopenharmony_ci		tp = TestProps()
154662306a36Sopenharmony_ci		out = {'syslpi':'N/A','pkgpc10':'N/A'}
154762306a36Sopenharmony_ci		for line in self.dmesgtext:
154862306a36Sopenharmony_ci			m = re.match(tp.tstatfmt, line)
154962306a36Sopenharmony_ci			if not m:
155062306a36Sopenharmony_ci				continue
155162306a36Sopenharmony_ci			for i in m.group('t').split('|'):
155262306a36Sopenharmony_ci				if 'SYS%LPI' in i:
155362306a36Sopenharmony_ci					out['syslpi'] = i.split('=')[-1]+'%'
155462306a36Sopenharmony_ci				elif 'pc10' in i:
155562306a36Sopenharmony_ci					out['pkgpc10'] = i.split('=')[-1]+'%'
155662306a36Sopenharmony_ci			break
155762306a36Sopenharmony_ci		return out
155862306a36Sopenharmony_ci	def extractErrorInfo(self):
155962306a36Sopenharmony_ci		lf = self.dmesgtext
156062306a36Sopenharmony_ci		if len(self.dmesgtext) < 1 and sysvals.dmesgfile:
156162306a36Sopenharmony_ci			lf = sysvals.openlog(sysvals.dmesgfile, 'r')
156262306a36Sopenharmony_ci		i = 0
156362306a36Sopenharmony_ci		tp = TestProps()
156462306a36Sopenharmony_ci		list = []
156562306a36Sopenharmony_ci		for line in lf:
156662306a36Sopenharmony_ci			i += 1
156762306a36Sopenharmony_ci			if tp.stampInfo(line, sysvals):
156862306a36Sopenharmony_ci				continue
156962306a36Sopenharmony_ci			m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
157062306a36Sopenharmony_ci			if not m:
157162306a36Sopenharmony_ci				continue
157262306a36Sopenharmony_ci			t = float(m.group('ktime'))
157362306a36Sopenharmony_ci			if t < self.start or t > self.end:
157462306a36Sopenharmony_ci				continue
157562306a36Sopenharmony_ci			dir = 'suspend' if t < self.tSuspended else 'resume'
157662306a36Sopenharmony_ci			msg = m.group('msg')
157762306a36Sopenharmony_ci			if re.match('capability: warning: .*', msg):
157862306a36Sopenharmony_ci				continue
157962306a36Sopenharmony_ci			for err in self.errlist:
158062306a36Sopenharmony_ci				if re.match(self.errlist[err], msg):
158162306a36Sopenharmony_ci					list.append((msg, err, dir, t, i, i))
158262306a36Sopenharmony_ci					self.kerror = True
158362306a36Sopenharmony_ci					break
158462306a36Sopenharmony_ci		tp.msglist = []
158562306a36Sopenharmony_ci		for msg, type, dir, t, idx1, idx2 in list:
158662306a36Sopenharmony_ci			tp.msglist.append(msg)
158762306a36Sopenharmony_ci			self.errorinfo[dir].append((type, t, idx1, idx2))
158862306a36Sopenharmony_ci		if self.kerror:
158962306a36Sopenharmony_ci			sysvals.dmesglog = True
159062306a36Sopenharmony_ci		if len(self.dmesgtext) < 1 and sysvals.dmesgfile:
159162306a36Sopenharmony_ci			lf.close()
159262306a36Sopenharmony_ci		return tp
159362306a36Sopenharmony_ci	def setStart(self, time, msg=''):
159462306a36Sopenharmony_ci		self.start = time
159562306a36Sopenharmony_ci		if msg:
159662306a36Sopenharmony_ci			try:
159762306a36Sopenharmony_ci				self.hwstart = datetime.strptime(msg, sysvals.tmstart)
159862306a36Sopenharmony_ci			except:
159962306a36Sopenharmony_ci				self.hwstart = 0
160062306a36Sopenharmony_ci	def setEnd(self, time, msg=''):
160162306a36Sopenharmony_ci		self.end = time
160262306a36Sopenharmony_ci		if msg:
160362306a36Sopenharmony_ci			try:
160462306a36Sopenharmony_ci				self.hwend = datetime.strptime(msg, sysvals.tmend)
160562306a36Sopenharmony_ci			except:
160662306a36Sopenharmony_ci				self.hwend = 0
160762306a36Sopenharmony_ci	def isTraceEventOutsideDeviceCalls(self, pid, time):
160862306a36Sopenharmony_ci		for phase in self.sortedPhases():
160962306a36Sopenharmony_ci			list = self.dmesg[phase]['list']
161062306a36Sopenharmony_ci			for dev in list:
161162306a36Sopenharmony_ci				d = list[dev]
161262306a36Sopenharmony_ci				if(d['pid'] == pid and time >= d['start'] and
161362306a36Sopenharmony_ci					time < d['end']):
161462306a36Sopenharmony_ci					return False
161562306a36Sopenharmony_ci		return True
161662306a36Sopenharmony_ci	def sourcePhase(self, start):
161762306a36Sopenharmony_ci		for phase in self.sortedPhases():
161862306a36Sopenharmony_ci			if 'machine' in phase:
161962306a36Sopenharmony_ci				continue
162062306a36Sopenharmony_ci			pend = self.dmesg[phase]['end']
162162306a36Sopenharmony_ci			if start <= pend:
162262306a36Sopenharmony_ci				return phase
162362306a36Sopenharmony_ci		return 'resume_complete' if 'resume_complete' in self.dmesg else ''
162462306a36Sopenharmony_ci	def sourceDevice(self, phaselist, start, end, pid, type):
162562306a36Sopenharmony_ci		tgtdev = ''
162662306a36Sopenharmony_ci		for phase in phaselist:
162762306a36Sopenharmony_ci			list = self.dmesg[phase]['list']
162862306a36Sopenharmony_ci			for devname in list:
162962306a36Sopenharmony_ci				dev = list[devname]
163062306a36Sopenharmony_ci				# pid must match
163162306a36Sopenharmony_ci				if dev['pid'] != pid:
163262306a36Sopenharmony_ci					continue
163362306a36Sopenharmony_ci				devS = dev['start']
163462306a36Sopenharmony_ci				devE = dev['end']
163562306a36Sopenharmony_ci				if type == 'device':
163662306a36Sopenharmony_ci					# device target event is entirely inside the source boundary
163762306a36Sopenharmony_ci					if(start < devS or start >= devE or end <= devS or end > devE):
163862306a36Sopenharmony_ci						continue
163962306a36Sopenharmony_ci				elif type == 'thread':
164062306a36Sopenharmony_ci					# thread target event will expand the source boundary
164162306a36Sopenharmony_ci					if start < devS:
164262306a36Sopenharmony_ci						dev['start'] = start
164362306a36Sopenharmony_ci					if end > devE:
164462306a36Sopenharmony_ci						dev['end'] = end
164562306a36Sopenharmony_ci				tgtdev = dev
164662306a36Sopenharmony_ci				break
164762306a36Sopenharmony_ci		return tgtdev
164862306a36Sopenharmony_ci	def addDeviceFunctionCall(self, displayname, kprobename, proc, pid, start, end, cdata, rdata):
164962306a36Sopenharmony_ci		# try to place the call in a device
165062306a36Sopenharmony_ci		phases = self.sortedPhases()
165162306a36Sopenharmony_ci		tgtdev = self.sourceDevice(phases, start, end, pid, 'device')
165262306a36Sopenharmony_ci		# calls with device pids that occur outside device bounds are dropped
165362306a36Sopenharmony_ci		# TODO: include these somehow
165462306a36Sopenharmony_ci		if not tgtdev and pid in self.devpids:
165562306a36Sopenharmony_ci			return False
165662306a36Sopenharmony_ci		# try to place the call in a thread
165762306a36Sopenharmony_ci		if not tgtdev:
165862306a36Sopenharmony_ci			tgtdev = self.sourceDevice(phases, start, end, pid, 'thread')
165962306a36Sopenharmony_ci		# create new thread blocks, expand as new calls are found
166062306a36Sopenharmony_ci		if not tgtdev:
166162306a36Sopenharmony_ci			if proc == '<...>':
166262306a36Sopenharmony_ci				threadname = 'kthread-%d' % (pid)
166362306a36Sopenharmony_ci			else:
166462306a36Sopenharmony_ci				threadname = '%s-%d' % (proc, pid)
166562306a36Sopenharmony_ci			tgtphase = self.sourcePhase(start)
166662306a36Sopenharmony_ci			if not tgtphase:
166762306a36Sopenharmony_ci				return False
166862306a36Sopenharmony_ci			self.newAction(tgtphase, threadname, pid, '', start, end, '', ' kth', '')
166962306a36Sopenharmony_ci			return self.addDeviceFunctionCall(displayname, kprobename, proc, pid, start, end, cdata, rdata)
167062306a36Sopenharmony_ci		# this should not happen
167162306a36Sopenharmony_ci		if not tgtdev:
167262306a36Sopenharmony_ci			sysvals.vprint('[%f - %f] %s-%d %s %s %s' % \
167362306a36Sopenharmony_ci				(start, end, proc, pid, kprobename, cdata, rdata))
167462306a36Sopenharmony_ci			return False
167562306a36Sopenharmony_ci		# place the call data inside the src element of the tgtdev
167662306a36Sopenharmony_ci		if('src' not in tgtdev):
167762306a36Sopenharmony_ci			tgtdev['src'] = []
167862306a36Sopenharmony_ci		dtf = sysvals.dev_tracefuncs
167962306a36Sopenharmony_ci		ubiquitous = False
168062306a36Sopenharmony_ci		if kprobename in dtf and 'ub' in dtf[kprobename]:
168162306a36Sopenharmony_ci			ubiquitous = True
168262306a36Sopenharmony_ci		mc = re.match('\(.*\) *(?P<args>.*)', cdata)
168362306a36Sopenharmony_ci		mr = re.match('\((?P<caller>\S*).* arg1=(?P<ret>.*)', rdata)
168462306a36Sopenharmony_ci		if mc and mr:
168562306a36Sopenharmony_ci			c = mr.group('caller').split('+')[0]
168662306a36Sopenharmony_ci			a = mc.group('args').strip()
168762306a36Sopenharmony_ci			r = mr.group('ret')
168862306a36Sopenharmony_ci			if len(r) > 6:
168962306a36Sopenharmony_ci				r = ''
169062306a36Sopenharmony_ci			else:
169162306a36Sopenharmony_ci				r = 'ret=%s ' % r
169262306a36Sopenharmony_ci			if ubiquitous and c in dtf and 'ub' in dtf[c]:
169362306a36Sopenharmony_ci				return False
169462306a36Sopenharmony_ci		else:
169562306a36Sopenharmony_ci			return False
169662306a36Sopenharmony_ci		color = sysvals.kprobeColor(kprobename)
169762306a36Sopenharmony_ci		e = DevFunction(displayname, a, c, r, start, end, ubiquitous, proc, pid, color)
169862306a36Sopenharmony_ci		tgtdev['src'].append(e)
169962306a36Sopenharmony_ci		return True
170062306a36Sopenharmony_ci	def overflowDevices(self):
170162306a36Sopenharmony_ci		# get a list of devices that extend beyond the end of this test run
170262306a36Sopenharmony_ci		devlist = []
170362306a36Sopenharmony_ci		for phase in self.sortedPhases():
170462306a36Sopenharmony_ci			list = self.dmesg[phase]['list']
170562306a36Sopenharmony_ci			for devname in list:
170662306a36Sopenharmony_ci				dev = list[devname]
170762306a36Sopenharmony_ci				if dev['end'] > self.end:
170862306a36Sopenharmony_ci					devlist.append(dev)
170962306a36Sopenharmony_ci		return devlist
171062306a36Sopenharmony_ci	def mergeOverlapDevices(self, devlist):
171162306a36Sopenharmony_ci		# merge any devices that overlap devlist
171262306a36Sopenharmony_ci		for dev in devlist:
171362306a36Sopenharmony_ci			devname = dev['name']
171462306a36Sopenharmony_ci			for phase in self.sortedPhases():
171562306a36Sopenharmony_ci				list = self.dmesg[phase]['list']
171662306a36Sopenharmony_ci				if devname not in list:
171762306a36Sopenharmony_ci					continue
171862306a36Sopenharmony_ci				tdev = list[devname]
171962306a36Sopenharmony_ci				o = min(dev['end'], tdev['end']) - max(dev['start'], tdev['start'])
172062306a36Sopenharmony_ci				if o <= 0:
172162306a36Sopenharmony_ci					continue
172262306a36Sopenharmony_ci				dev['end'] = tdev['end']
172362306a36Sopenharmony_ci				if 'src' not in dev or 'src' not in tdev:
172462306a36Sopenharmony_ci					continue
172562306a36Sopenharmony_ci				dev['src'] += tdev['src']
172662306a36Sopenharmony_ci				del list[devname]
172762306a36Sopenharmony_ci	def usurpTouchingThread(self, name, dev):
172862306a36Sopenharmony_ci		# the caller test has priority of this thread, give it to him
172962306a36Sopenharmony_ci		for phase in self.sortedPhases():
173062306a36Sopenharmony_ci			list = self.dmesg[phase]['list']
173162306a36Sopenharmony_ci			if name in list:
173262306a36Sopenharmony_ci				tdev = list[name]
173362306a36Sopenharmony_ci				if tdev['start'] - dev['end'] < 0.1:
173462306a36Sopenharmony_ci					dev['end'] = tdev['end']
173562306a36Sopenharmony_ci					if 'src' not in dev:
173662306a36Sopenharmony_ci						dev['src'] = []
173762306a36Sopenharmony_ci					if 'src' in tdev:
173862306a36Sopenharmony_ci						dev['src'] += tdev['src']
173962306a36Sopenharmony_ci					del list[name]
174062306a36Sopenharmony_ci				break
174162306a36Sopenharmony_ci	def stitchTouchingThreads(self, testlist):
174262306a36Sopenharmony_ci		# merge any threads between tests that touch
174362306a36Sopenharmony_ci		for phase in self.sortedPhases():
174462306a36Sopenharmony_ci			list = self.dmesg[phase]['list']
174562306a36Sopenharmony_ci			for devname in list:
174662306a36Sopenharmony_ci				dev = list[devname]
174762306a36Sopenharmony_ci				if 'htmlclass' not in dev or 'kth' not in dev['htmlclass']:
174862306a36Sopenharmony_ci					continue
174962306a36Sopenharmony_ci				for data in testlist:
175062306a36Sopenharmony_ci					data.usurpTouchingThread(devname, dev)
175162306a36Sopenharmony_ci	def optimizeDevSrc(self):
175262306a36Sopenharmony_ci		# merge any src call loops to reduce timeline size
175362306a36Sopenharmony_ci		for phase in self.sortedPhases():
175462306a36Sopenharmony_ci			list = self.dmesg[phase]['list']
175562306a36Sopenharmony_ci			for dev in list:
175662306a36Sopenharmony_ci				if 'src' not in list[dev]:
175762306a36Sopenharmony_ci					continue
175862306a36Sopenharmony_ci				src = list[dev]['src']
175962306a36Sopenharmony_ci				p = 0
176062306a36Sopenharmony_ci				for e in sorted(src, key=lambda event: event.time):
176162306a36Sopenharmony_ci					if not p or not e.repeat(p):
176262306a36Sopenharmony_ci						p = e
176362306a36Sopenharmony_ci						continue
176462306a36Sopenharmony_ci					# e is another iteration of p, move it into p
176562306a36Sopenharmony_ci					p.end = e.end
176662306a36Sopenharmony_ci					p.length = p.end - p.time
176762306a36Sopenharmony_ci					p.count += 1
176862306a36Sopenharmony_ci					src.remove(e)
176962306a36Sopenharmony_ci	def trimTimeVal(self, t, t0, dT, left):
177062306a36Sopenharmony_ci		if left:
177162306a36Sopenharmony_ci			if(t > t0):
177262306a36Sopenharmony_ci				if(t - dT < t0):
177362306a36Sopenharmony_ci					return t0
177462306a36Sopenharmony_ci				return t - dT
177562306a36Sopenharmony_ci			else:
177662306a36Sopenharmony_ci				return t
177762306a36Sopenharmony_ci		else:
177862306a36Sopenharmony_ci			if(t < t0 + dT):
177962306a36Sopenharmony_ci				if(t > t0):
178062306a36Sopenharmony_ci					return t0 + dT
178162306a36Sopenharmony_ci				return t + dT
178262306a36Sopenharmony_ci			else:
178362306a36Sopenharmony_ci				return t
178462306a36Sopenharmony_ci	def trimTime(self, t0, dT, left):
178562306a36Sopenharmony_ci		self.tSuspended = self.trimTimeVal(self.tSuspended, t0, dT, left)
178662306a36Sopenharmony_ci		self.tResumed = self.trimTimeVal(self.tResumed, t0, dT, left)
178762306a36Sopenharmony_ci		self.start = self.trimTimeVal(self.start, t0, dT, left)
178862306a36Sopenharmony_ci		self.tKernSus = self.trimTimeVal(self.tKernSus, t0, dT, left)
178962306a36Sopenharmony_ci		self.tKernRes = self.trimTimeVal(self.tKernRes, t0, dT, left)
179062306a36Sopenharmony_ci		self.end = self.trimTimeVal(self.end, t0, dT, left)
179162306a36Sopenharmony_ci		for phase in self.sortedPhases():
179262306a36Sopenharmony_ci			p = self.dmesg[phase]
179362306a36Sopenharmony_ci			p['start'] = self.trimTimeVal(p['start'], t0, dT, left)
179462306a36Sopenharmony_ci			p['end'] = self.trimTimeVal(p['end'], t0, dT, left)
179562306a36Sopenharmony_ci			list = p['list']
179662306a36Sopenharmony_ci			for name in list:
179762306a36Sopenharmony_ci				d = list[name]
179862306a36Sopenharmony_ci				d['start'] = self.trimTimeVal(d['start'], t0, dT, left)
179962306a36Sopenharmony_ci				d['end'] = self.trimTimeVal(d['end'], t0, dT, left)
180062306a36Sopenharmony_ci				d['length'] = d['end'] - d['start']
180162306a36Sopenharmony_ci				if('ftrace' in d):
180262306a36Sopenharmony_ci					cg = d['ftrace']
180362306a36Sopenharmony_ci					cg.start = self.trimTimeVal(cg.start, t0, dT, left)
180462306a36Sopenharmony_ci					cg.end = self.trimTimeVal(cg.end, t0, dT, left)
180562306a36Sopenharmony_ci					for line in cg.list:
180662306a36Sopenharmony_ci						line.time = self.trimTimeVal(line.time, t0, dT, left)
180762306a36Sopenharmony_ci				if('src' in d):
180862306a36Sopenharmony_ci					for e in d['src']:
180962306a36Sopenharmony_ci						e.time = self.trimTimeVal(e.time, t0, dT, left)
181062306a36Sopenharmony_ci						e.end = self.trimTimeVal(e.end, t0, dT, left)
181162306a36Sopenharmony_ci						e.length = e.end - e.time
181262306a36Sopenharmony_ci				if('cpuexec' in d):
181362306a36Sopenharmony_ci					cpuexec = dict()
181462306a36Sopenharmony_ci					for e in d['cpuexec']:
181562306a36Sopenharmony_ci						c0, cN = e
181662306a36Sopenharmony_ci						c0 = self.trimTimeVal(c0, t0, dT, left)
181762306a36Sopenharmony_ci						cN = self.trimTimeVal(cN, t0, dT, left)
181862306a36Sopenharmony_ci						cpuexec[(c0, cN)] = d['cpuexec'][e]
181962306a36Sopenharmony_ci					d['cpuexec'] = cpuexec
182062306a36Sopenharmony_ci		for dir in ['suspend', 'resume']:
182162306a36Sopenharmony_ci			list = []
182262306a36Sopenharmony_ci			for e in self.errorinfo[dir]:
182362306a36Sopenharmony_ci				type, tm, idx1, idx2 = e
182462306a36Sopenharmony_ci				tm = self.trimTimeVal(tm, t0, dT, left)
182562306a36Sopenharmony_ci				list.append((type, tm, idx1, idx2))
182662306a36Sopenharmony_ci			self.errorinfo[dir] = list
182762306a36Sopenharmony_ci	def trimFreezeTime(self, tZero):
182862306a36Sopenharmony_ci		# trim out any standby or freeze clock time
182962306a36Sopenharmony_ci		lp = ''
183062306a36Sopenharmony_ci		for phase in self.sortedPhases():
183162306a36Sopenharmony_ci			if 'resume_machine' in phase and 'suspend_machine' in lp:
183262306a36Sopenharmony_ci				tS, tR = self.dmesg[lp]['end'], self.dmesg[phase]['start']
183362306a36Sopenharmony_ci				tL = tR - tS
183462306a36Sopenharmony_ci				if tL <= 0:
183562306a36Sopenharmony_ci					continue
183662306a36Sopenharmony_ci				left = True if tR > tZero else False
183762306a36Sopenharmony_ci				self.trimTime(tS, tL, left)
183862306a36Sopenharmony_ci				if 'waking' in self.dmesg[lp]:
183962306a36Sopenharmony_ci					tCnt = self.dmesg[lp]['waking'][0]
184062306a36Sopenharmony_ci					if self.dmesg[lp]['waking'][1] >= 0.001:
184162306a36Sopenharmony_ci						tTry = '%.0f' % (round(self.dmesg[lp]['waking'][1] * 1000))
184262306a36Sopenharmony_ci					else:
184362306a36Sopenharmony_ci						tTry = '%.3f' % (self.dmesg[lp]['waking'][1] * 1000)
184462306a36Sopenharmony_ci					text = '%.0f (%s ms waking %d times)' % (tL * 1000, tTry, tCnt)
184562306a36Sopenharmony_ci				else:
184662306a36Sopenharmony_ci					text = '%.0f' % (tL * 1000)
184762306a36Sopenharmony_ci				self.tLow.append(text)
184862306a36Sopenharmony_ci			lp = phase
184962306a36Sopenharmony_ci	def getMemTime(self):
185062306a36Sopenharmony_ci		if not self.hwstart or not self.hwend:
185162306a36Sopenharmony_ci			return
185262306a36Sopenharmony_ci		stime = (self.tSuspended - self.start) * 1000000
185362306a36Sopenharmony_ci		rtime = (self.end - self.tResumed) * 1000000
185462306a36Sopenharmony_ci		hws = self.hwstart + timedelta(microseconds=stime)
185562306a36Sopenharmony_ci		hwr = self.hwend - timedelta(microseconds=rtime)
185662306a36Sopenharmony_ci		self.tLow.append('%.0f'%((hwr - hws).total_seconds() * 1000))
185762306a36Sopenharmony_ci	def getTimeValues(self):
185862306a36Sopenharmony_ci		s = (self.tSuspended - self.tKernSus) * 1000
185962306a36Sopenharmony_ci		r = (self.tKernRes - self.tResumed) * 1000
186062306a36Sopenharmony_ci		return (max(s, 0), max(r, 0))
186162306a36Sopenharmony_ci	def setPhase(self, phase, ktime, isbegin, order=-1):
186262306a36Sopenharmony_ci		if(isbegin):
186362306a36Sopenharmony_ci			# phase start over current phase
186462306a36Sopenharmony_ci			if self.currphase:
186562306a36Sopenharmony_ci				if 'resume_machine' not in self.currphase:
186662306a36Sopenharmony_ci					sysvals.vprint('WARNING: phase %s failed to end' % self.currphase)
186762306a36Sopenharmony_ci				self.dmesg[self.currphase]['end'] = ktime
186862306a36Sopenharmony_ci			phases = self.dmesg.keys()
186962306a36Sopenharmony_ci			color = self.phasedef[phase]['color']
187062306a36Sopenharmony_ci			count = len(phases) if order < 0 else order
187162306a36Sopenharmony_ci			# create unique name for every new phase
187262306a36Sopenharmony_ci			while phase in phases:
187362306a36Sopenharmony_ci				phase += '*'
187462306a36Sopenharmony_ci			self.dmesg[phase] = {'list': dict(), 'start': -1.0, 'end': -1.0,
187562306a36Sopenharmony_ci				'row': 0, 'color': color, 'order': count}
187662306a36Sopenharmony_ci			self.dmesg[phase]['start'] = ktime
187762306a36Sopenharmony_ci			self.currphase = phase
187862306a36Sopenharmony_ci		else:
187962306a36Sopenharmony_ci			# phase end without a start
188062306a36Sopenharmony_ci			if phase not in self.currphase:
188162306a36Sopenharmony_ci				if self.currphase:
188262306a36Sopenharmony_ci					sysvals.vprint('WARNING: %s ended instead of %s, ftrace corruption?' % (phase, self.currphase))
188362306a36Sopenharmony_ci				else:
188462306a36Sopenharmony_ci					sysvals.vprint('WARNING: %s ended without a start, ftrace corruption?' % phase)
188562306a36Sopenharmony_ci					return phase
188662306a36Sopenharmony_ci			phase = self.currphase
188762306a36Sopenharmony_ci			self.dmesg[phase]['end'] = ktime
188862306a36Sopenharmony_ci			self.currphase = ''
188962306a36Sopenharmony_ci		return phase
189062306a36Sopenharmony_ci	def sortedDevices(self, phase):
189162306a36Sopenharmony_ci		list = self.dmesg[phase]['list']
189262306a36Sopenharmony_ci		return sorted(list, key=lambda k:list[k]['start'])
189362306a36Sopenharmony_ci	def fixupInitcalls(self, phase):
189462306a36Sopenharmony_ci		# if any calls never returned, clip them at system resume end
189562306a36Sopenharmony_ci		phaselist = self.dmesg[phase]['list']
189662306a36Sopenharmony_ci		for devname in phaselist:
189762306a36Sopenharmony_ci			dev = phaselist[devname]
189862306a36Sopenharmony_ci			if(dev['end'] < 0):
189962306a36Sopenharmony_ci				for p in self.sortedPhases():
190062306a36Sopenharmony_ci					if self.dmesg[p]['end'] > dev['start']:
190162306a36Sopenharmony_ci						dev['end'] = self.dmesg[p]['end']
190262306a36Sopenharmony_ci						break
190362306a36Sopenharmony_ci				sysvals.vprint('%s (%s): callback didnt return' % (devname, phase))
190462306a36Sopenharmony_ci	def deviceFilter(self, devicefilter):
190562306a36Sopenharmony_ci		for phase in self.sortedPhases():
190662306a36Sopenharmony_ci			list = self.dmesg[phase]['list']
190762306a36Sopenharmony_ci			rmlist = []
190862306a36Sopenharmony_ci			for name in list:
190962306a36Sopenharmony_ci				keep = False
191062306a36Sopenharmony_ci				for filter in devicefilter:
191162306a36Sopenharmony_ci					if filter in name or \
191262306a36Sopenharmony_ci						('drv' in list[name] and filter in list[name]['drv']):
191362306a36Sopenharmony_ci						keep = True
191462306a36Sopenharmony_ci				if not keep:
191562306a36Sopenharmony_ci					rmlist.append(name)
191662306a36Sopenharmony_ci			for name in rmlist:
191762306a36Sopenharmony_ci				del list[name]
191862306a36Sopenharmony_ci	def fixupInitcallsThatDidntReturn(self):
191962306a36Sopenharmony_ci		# if any calls never returned, clip them at system resume end
192062306a36Sopenharmony_ci		for phase in self.sortedPhases():
192162306a36Sopenharmony_ci			self.fixupInitcalls(phase)
192262306a36Sopenharmony_ci	def phaseOverlap(self, phases):
192362306a36Sopenharmony_ci		rmgroups = []
192462306a36Sopenharmony_ci		newgroup = []
192562306a36Sopenharmony_ci		for group in self.devicegroups:
192662306a36Sopenharmony_ci			for phase in phases:
192762306a36Sopenharmony_ci				if phase not in group:
192862306a36Sopenharmony_ci					continue
192962306a36Sopenharmony_ci				for p in group:
193062306a36Sopenharmony_ci					if p not in newgroup:
193162306a36Sopenharmony_ci						newgroup.append(p)
193262306a36Sopenharmony_ci				if group not in rmgroups:
193362306a36Sopenharmony_ci					rmgroups.append(group)
193462306a36Sopenharmony_ci		for group in rmgroups:
193562306a36Sopenharmony_ci			self.devicegroups.remove(group)
193662306a36Sopenharmony_ci		self.devicegroups.append(newgroup)
193762306a36Sopenharmony_ci	def newActionGlobal(self, name, start, end, pid=-1, color=''):
193862306a36Sopenharmony_ci		# which phase is this device callback or action in
193962306a36Sopenharmony_ci		phases = self.sortedPhases()
194062306a36Sopenharmony_ci		targetphase = 'none'
194162306a36Sopenharmony_ci		htmlclass = ''
194262306a36Sopenharmony_ci		overlap = 0.0
194362306a36Sopenharmony_ci		myphases = []
194462306a36Sopenharmony_ci		for phase in phases:
194562306a36Sopenharmony_ci			pstart = self.dmesg[phase]['start']
194662306a36Sopenharmony_ci			pend = self.dmesg[phase]['end']
194762306a36Sopenharmony_ci			# see if the action overlaps this phase
194862306a36Sopenharmony_ci			o = max(0, min(end, pend) - max(start, pstart))
194962306a36Sopenharmony_ci			if o > 0:
195062306a36Sopenharmony_ci				myphases.append(phase)
195162306a36Sopenharmony_ci			# set the target phase to the one that overlaps most
195262306a36Sopenharmony_ci			if o > overlap:
195362306a36Sopenharmony_ci				if overlap > 0 and phase == 'post_resume':
195462306a36Sopenharmony_ci					continue
195562306a36Sopenharmony_ci				targetphase = phase
195662306a36Sopenharmony_ci				overlap = o
195762306a36Sopenharmony_ci		# if no target phase was found, pin it to the edge
195862306a36Sopenharmony_ci		if targetphase == 'none':
195962306a36Sopenharmony_ci			p0start = self.dmesg[phases[0]]['start']
196062306a36Sopenharmony_ci			if start <= p0start:
196162306a36Sopenharmony_ci				targetphase = phases[0]
196262306a36Sopenharmony_ci			else:
196362306a36Sopenharmony_ci				targetphase = phases[-1]
196462306a36Sopenharmony_ci		if pid == -2:
196562306a36Sopenharmony_ci			htmlclass = ' bg'
196662306a36Sopenharmony_ci		elif pid == -3:
196762306a36Sopenharmony_ci			htmlclass = ' ps'
196862306a36Sopenharmony_ci		if len(myphases) > 1:
196962306a36Sopenharmony_ci			htmlclass = ' bg'
197062306a36Sopenharmony_ci			self.phaseOverlap(myphases)
197162306a36Sopenharmony_ci		if targetphase in phases:
197262306a36Sopenharmony_ci			newname = self.newAction(targetphase, name, pid, '', start, end, '', htmlclass, color)
197362306a36Sopenharmony_ci			return (targetphase, newname)
197462306a36Sopenharmony_ci		return False
197562306a36Sopenharmony_ci	def newAction(self, phase, name, pid, parent, start, end, drv, htmlclass='', color=''):
197662306a36Sopenharmony_ci		# new device callback for a specific phase
197762306a36Sopenharmony_ci		self.html_device_id += 1
197862306a36Sopenharmony_ci		devid = '%s%d' % (self.idstr, self.html_device_id)
197962306a36Sopenharmony_ci		list = self.dmesg[phase]['list']
198062306a36Sopenharmony_ci		length = -1.0
198162306a36Sopenharmony_ci		if(start >= 0 and end >= 0):
198262306a36Sopenharmony_ci			length = end - start
198362306a36Sopenharmony_ci		if pid == -2 or name not in sysvals.tracefuncs.keys():
198462306a36Sopenharmony_ci			i = 2
198562306a36Sopenharmony_ci			origname = name
198662306a36Sopenharmony_ci			while(name in list):
198762306a36Sopenharmony_ci				name = '%s[%d]' % (origname, i)
198862306a36Sopenharmony_ci				i += 1
198962306a36Sopenharmony_ci		list[name] = {'name': name, 'start': start, 'end': end, 'pid': pid,
199062306a36Sopenharmony_ci			'par': parent, 'length': length, 'row': 0, 'id': devid, 'drv': drv }
199162306a36Sopenharmony_ci		if htmlclass:
199262306a36Sopenharmony_ci			list[name]['htmlclass'] = htmlclass
199362306a36Sopenharmony_ci		if color:
199462306a36Sopenharmony_ci			list[name]['color'] = color
199562306a36Sopenharmony_ci		return name
199662306a36Sopenharmony_ci	def findDevice(self, phase, name):
199762306a36Sopenharmony_ci		list = self.dmesg[phase]['list']
199862306a36Sopenharmony_ci		mydev = ''
199962306a36Sopenharmony_ci		for devname in sorted(list):
200062306a36Sopenharmony_ci			if name == devname or re.match('^%s\[(?P<num>[0-9]*)\]$' % name, devname):
200162306a36Sopenharmony_ci				mydev = devname
200262306a36Sopenharmony_ci		if mydev:
200362306a36Sopenharmony_ci			return list[mydev]
200462306a36Sopenharmony_ci		return False
200562306a36Sopenharmony_ci	def deviceChildren(self, devname, phase):
200662306a36Sopenharmony_ci		devlist = []
200762306a36Sopenharmony_ci		list = self.dmesg[phase]['list']
200862306a36Sopenharmony_ci		for child in list:
200962306a36Sopenharmony_ci			if(list[child]['par'] == devname):
201062306a36Sopenharmony_ci				devlist.append(child)
201162306a36Sopenharmony_ci		return devlist
201262306a36Sopenharmony_ci	def maxDeviceNameSize(self, phase):
201362306a36Sopenharmony_ci		size = 0
201462306a36Sopenharmony_ci		for name in self.dmesg[phase]['list']:
201562306a36Sopenharmony_ci			if len(name) > size:
201662306a36Sopenharmony_ci				size = len(name)
201762306a36Sopenharmony_ci		return size
201862306a36Sopenharmony_ci	def printDetails(self):
201962306a36Sopenharmony_ci		sysvals.vprint('Timeline Details:')
202062306a36Sopenharmony_ci		sysvals.vprint('          test start: %f' % self.start)
202162306a36Sopenharmony_ci		sysvals.vprint('kernel suspend start: %f' % self.tKernSus)
202262306a36Sopenharmony_ci		tS = tR = False
202362306a36Sopenharmony_ci		for phase in self.sortedPhases():
202462306a36Sopenharmony_ci			devlist = self.dmesg[phase]['list']
202562306a36Sopenharmony_ci			dc, ps, pe = len(devlist), self.dmesg[phase]['start'], self.dmesg[phase]['end']
202662306a36Sopenharmony_ci			if not tS and ps >= self.tSuspended:
202762306a36Sopenharmony_ci				sysvals.vprint('   machine suspended: %f' % self.tSuspended)
202862306a36Sopenharmony_ci				tS = True
202962306a36Sopenharmony_ci			if not tR and ps >= self.tResumed:
203062306a36Sopenharmony_ci				sysvals.vprint('     machine resumed: %f' % self.tResumed)
203162306a36Sopenharmony_ci				tR = True
203262306a36Sopenharmony_ci			sysvals.vprint('%20s: %f - %f (%d devices)' % (phase, ps, pe, dc))
203362306a36Sopenharmony_ci			if sysvals.devdump:
203462306a36Sopenharmony_ci				sysvals.vprint(''.join('-' for i in range(80)))
203562306a36Sopenharmony_ci				maxname = '%d' % self.maxDeviceNameSize(phase)
203662306a36Sopenharmony_ci				fmt = '%3d) %'+maxname+'s - %f - %f'
203762306a36Sopenharmony_ci				c = 1
203862306a36Sopenharmony_ci				for name in sorted(devlist):
203962306a36Sopenharmony_ci					s = devlist[name]['start']
204062306a36Sopenharmony_ci					e = devlist[name]['end']
204162306a36Sopenharmony_ci					sysvals.vprint(fmt % (c, name, s, e))
204262306a36Sopenharmony_ci					c += 1
204362306a36Sopenharmony_ci				sysvals.vprint(''.join('-' for i in range(80)))
204462306a36Sopenharmony_ci		sysvals.vprint('   kernel resume end: %f' % self.tKernRes)
204562306a36Sopenharmony_ci		sysvals.vprint('            test end: %f' % self.end)
204662306a36Sopenharmony_ci	def deviceChildrenAllPhases(self, devname):
204762306a36Sopenharmony_ci		devlist = []
204862306a36Sopenharmony_ci		for phase in self.sortedPhases():
204962306a36Sopenharmony_ci			list = self.deviceChildren(devname, phase)
205062306a36Sopenharmony_ci			for dev in sorted(list):
205162306a36Sopenharmony_ci				if dev not in devlist:
205262306a36Sopenharmony_ci					devlist.append(dev)
205362306a36Sopenharmony_ci		return devlist
205462306a36Sopenharmony_ci	def masterTopology(self, name, list, depth):
205562306a36Sopenharmony_ci		node = DeviceNode(name, depth)
205662306a36Sopenharmony_ci		for cname in list:
205762306a36Sopenharmony_ci			# avoid recursions
205862306a36Sopenharmony_ci			if name == cname:
205962306a36Sopenharmony_ci				continue
206062306a36Sopenharmony_ci			clist = self.deviceChildrenAllPhases(cname)
206162306a36Sopenharmony_ci			cnode = self.masterTopology(cname, clist, depth+1)
206262306a36Sopenharmony_ci			node.children.append(cnode)
206362306a36Sopenharmony_ci		return node
206462306a36Sopenharmony_ci	def printTopology(self, node):
206562306a36Sopenharmony_ci		html = ''
206662306a36Sopenharmony_ci		if node.name:
206762306a36Sopenharmony_ci			info = ''
206862306a36Sopenharmony_ci			drv = ''
206962306a36Sopenharmony_ci			for phase in self.sortedPhases():
207062306a36Sopenharmony_ci				list = self.dmesg[phase]['list']
207162306a36Sopenharmony_ci				if node.name in list:
207262306a36Sopenharmony_ci					s = list[node.name]['start']
207362306a36Sopenharmony_ci					e = list[node.name]['end']
207462306a36Sopenharmony_ci					if list[node.name]['drv']:
207562306a36Sopenharmony_ci						drv = ' {'+list[node.name]['drv']+'}'
207662306a36Sopenharmony_ci					info += ('<li>%s: %.3fms</li>' % (phase, (e-s)*1000))
207762306a36Sopenharmony_ci			html += '<li><b>'+node.name+drv+'</b>'
207862306a36Sopenharmony_ci			if info:
207962306a36Sopenharmony_ci				html += '<ul>'+info+'</ul>'
208062306a36Sopenharmony_ci			html += '</li>'
208162306a36Sopenharmony_ci		if len(node.children) > 0:
208262306a36Sopenharmony_ci			html += '<ul>'
208362306a36Sopenharmony_ci			for cnode in node.children:
208462306a36Sopenharmony_ci				html += self.printTopology(cnode)
208562306a36Sopenharmony_ci			html += '</ul>'
208662306a36Sopenharmony_ci		return html
208762306a36Sopenharmony_ci	def rootDeviceList(self):
208862306a36Sopenharmony_ci		# list of devices graphed
208962306a36Sopenharmony_ci		real = []
209062306a36Sopenharmony_ci		for phase in self.sortedPhases():
209162306a36Sopenharmony_ci			list = self.dmesg[phase]['list']
209262306a36Sopenharmony_ci			for dev in sorted(list):
209362306a36Sopenharmony_ci				if list[dev]['pid'] >= 0 and dev not in real:
209462306a36Sopenharmony_ci					real.append(dev)
209562306a36Sopenharmony_ci		# list of top-most root devices
209662306a36Sopenharmony_ci		rootlist = []
209762306a36Sopenharmony_ci		for phase in self.sortedPhases():
209862306a36Sopenharmony_ci			list = self.dmesg[phase]['list']
209962306a36Sopenharmony_ci			for dev in sorted(list):
210062306a36Sopenharmony_ci				pdev = list[dev]['par']
210162306a36Sopenharmony_ci				pid = list[dev]['pid']
210262306a36Sopenharmony_ci				if(pid < 0 or re.match('[0-9]*-[0-9]*\.[0-9]*[\.0-9]*\:[\.0-9]*$', pdev)):
210362306a36Sopenharmony_ci					continue
210462306a36Sopenharmony_ci				if pdev and pdev not in real and pdev not in rootlist:
210562306a36Sopenharmony_ci					rootlist.append(pdev)
210662306a36Sopenharmony_ci		return rootlist
210762306a36Sopenharmony_ci	def deviceTopology(self):
210862306a36Sopenharmony_ci		rootlist = self.rootDeviceList()
210962306a36Sopenharmony_ci		master = self.masterTopology('', rootlist, 0)
211062306a36Sopenharmony_ci		return self.printTopology(master)
211162306a36Sopenharmony_ci	def selectTimelineDevices(self, widfmt, tTotal, mindevlen):
211262306a36Sopenharmony_ci		# only select devices that will actually show up in html
211362306a36Sopenharmony_ci		self.tdevlist = dict()
211462306a36Sopenharmony_ci		for phase in self.dmesg:
211562306a36Sopenharmony_ci			devlist = []
211662306a36Sopenharmony_ci			list = self.dmesg[phase]['list']
211762306a36Sopenharmony_ci			for dev in list:
211862306a36Sopenharmony_ci				length = (list[dev]['end'] - list[dev]['start']) * 1000
211962306a36Sopenharmony_ci				width = widfmt % (((list[dev]['end']-list[dev]['start'])*100)/tTotal)
212062306a36Sopenharmony_ci				if length >= mindevlen:
212162306a36Sopenharmony_ci					devlist.append(dev)
212262306a36Sopenharmony_ci			self.tdevlist[phase] = devlist
212362306a36Sopenharmony_ci	def addHorizontalDivider(self, devname, devend):
212462306a36Sopenharmony_ci		phase = 'suspend_prepare'
212562306a36Sopenharmony_ci		self.newAction(phase, devname, -2, '', \
212662306a36Sopenharmony_ci			self.start, devend, '', ' sec', '')
212762306a36Sopenharmony_ci		if phase not in self.tdevlist:
212862306a36Sopenharmony_ci			self.tdevlist[phase] = []
212962306a36Sopenharmony_ci		self.tdevlist[phase].append(devname)
213062306a36Sopenharmony_ci		d = DevItem(0, phase, self.dmesg[phase]['list'][devname])
213162306a36Sopenharmony_ci		return d
213262306a36Sopenharmony_ci	def addProcessUsageEvent(self, name, times):
213362306a36Sopenharmony_ci		# get the start and end times for this process
213462306a36Sopenharmony_ci		cpuexec = dict()
213562306a36Sopenharmony_ci		tlast = start = end = -1
213662306a36Sopenharmony_ci		for t in sorted(times):
213762306a36Sopenharmony_ci			if tlast < 0:
213862306a36Sopenharmony_ci				tlast = t
213962306a36Sopenharmony_ci				continue
214062306a36Sopenharmony_ci			if name in self.pstl[t] and self.pstl[t][name] > 0:
214162306a36Sopenharmony_ci				if start < 0:
214262306a36Sopenharmony_ci					start = tlast
214362306a36Sopenharmony_ci				end, key = t, (tlast, t)
214462306a36Sopenharmony_ci				maxj = (t - tlast) * 1024.0
214562306a36Sopenharmony_ci				cpuexec[key] = min(1.0, float(self.pstl[t][name]) / maxj)
214662306a36Sopenharmony_ci			tlast = t
214762306a36Sopenharmony_ci		if start < 0 or end < 0:
214862306a36Sopenharmony_ci			return
214962306a36Sopenharmony_ci		# add a new action for this process and get the object
215062306a36Sopenharmony_ci		out = self.newActionGlobal(name, start, end, -3)
215162306a36Sopenharmony_ci		if out:
215262306a36Sopenharmony_ci			phase, devname = out
215362306a36Sopenharmony_ci			dev = self.dmesg[phase]['list'][devname]
215462306a36Sopenharmony_ci			dev['cpuexec'] = cpuexec
215562306a36Sopenharmony_ci	def createProcessUsageEvents(self):
215662306a36Sopenharmony_ci		# get an array of process names and times
215762306a36Sopenharmony_ci		proclist = {'sus': dict(), 'res': dict()}
215862306a36Sopenharmony_ci		tdata = {'sus': [], 'res': []}
215962306a36Sopenharmony_ci		for t in sorted(self.pstl):
216062306a36Sopenharmony_ci			dir = 'sus' if t < self.tSuspended else 'res'
216162306a36Sopenharmony_ci			for ps in sorted(self.pstl[t]):
216262306a36Sopenharmony_ci				if ps not in proclist[dir]:
216362306a36Sopenharmony_ci					proclist[dir][ps] = 0
216462306a36Sopenharmony_ci			tdata[dir].append(t)
216562306a36Sopenharmony_ci		# process the events for suspend and resume
216662306a36Sopenharmony_ci		if len(proclist['sus']) > 0 or len(proclist['res']) > 0:
216762306a36Sopenharmony_ci			sysvals.vprint('Process Execution:')
216862306a36Sopenharmony_ci		for dir in ['sus', 'res']:
216962306a36Sopenharmony_ci			for ps in sorted(proclist[dir]):
217062306a36Sopenharmony_ci				self.addProcessUsageEvent(ps, tdata[dir])
217162306a36Sopenharmony_ci	def handleEndMarker(self, time, msg=''):
217262306a36Sopenharmony_ci		dm = self.dmesg
217362306a36Sopenharmony_ci		self.setEnd(time, msg)
217462306a36Sopenharmony_ci		self.initDevicegroups()
217562306a36Sopenharmony_ci		# give suspend_prepare an end if needed
217662306a36Sopenharmony_ci		if 'suspend_prepare' in dm and dm['suspend_prepare']['end'] < 0:
217762306a36Sopenharmony_ci			dm['suspend_prepare']['end'] = time
217862306a36Sopenharmony_ci		# assume resume machine ends at next phase start
217962306a36Sopenharmony_ci		if 'resume_machine' in dm and dm['resume_machine']['end'] < 0:
218062306a36Sopenharmony_ci			np = self.nextPhase('resume_machine', 1)
218162306a36Sopenharmony_ci			if np:
218262306a36Sopenharmony_ci				dm['resume_machine']['end'] = dm[np]['start']
218362306a36Sopenharmony_ci		# if kernel resume end not found, assume its the end marker
218462306a36Sopenharmony_ci		if self.tKernRes == 0.0:
218562306a36Sopenharmony_ci			self.tKernRes = time
218662306a36Sopenharmony_ci		# if kernel suspend start not found, assume its the end marker
218762306a36Sopenharmony_ci		if self.tKernSus == 0.0:
218862306a36Sopenharmony_ci			self.tKernSus = time
218962306a36Sopenharmony_ci		# set resume complete to end at end marker
219062306a36Sopenharmony_ci		if 'resume_complete' in dm:
219162306a36Sopenharmony_ci			dm['resume_complete']['end'] = time
219262306a36Sopenharmony_ci	def initcall_debug_call(self, line, quick=False):
219362306a36Sopenharmony_ci		m = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) .* (?P<f>.*)\: '+\
219462306a36Sopenharmony_ci			'PM: *calling .* @ (?P<n>.*), parent: (?P<p>.*)', line)
219562306a36Sopenharmony_ci		if not m:
219662306a36Sopenharmony_ci			m = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) .* (?P<f>.*)\: '+\
219762306a36Sopenharmony_ci				'calling .* @ (?P<n>.*), parent: (?P<p>.*)', line)
219862306a36Sopenharmony_ci		if not m:
219962306a36Sopenharmony_ci			m = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) calling  '+\
220062306a36Sopenharmony_ci				'(?P<f>.*)\+ @ (?P<n>.*), parent: (?P<p>.*)', line)
220162306a36Sopenharmony_ci		if m:
220262306a36Sopenharmony_ci			return True if quick else m.group('t', 'f', 'n', 'p')
220362306a36Sopenharmony_ci		return False if quick else ('', '', '', '')
220462306a36Sopenharmony_ci	def initcall_debug_return(self, line, quick=False):
220562306a36Sopenharmony_ci		m = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) .* (?P<f>.*)\: PM: '+\
220662306a36Sopenharmony_ci			'.* returned (?P<r>[0-9]*) after (?P<dt>[0-9]*) usecs', line)
220762306a36Sopenharmony_ci		if not m:
220862306a36Sopenharmony_ci			m = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) .* (?P<f>.*)\: '+\
220962306a36Sopenharmony_ci				'.* returned (?P<r>[0-9]*) after (?P<dt>[0-9]*) usecs', line)
221062306a36Sopenharmony_ci		if not m:
221162306a36Sopenharmony_ci			m = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) call '+\
221262306a36Sopenharmony_ci				'(?P<f>.*)\+ returned .* after (?P<dt>.*) usecs', line)
221362306a36Sopenharmony_ci		if m:
221462306a36Sopenharmony_ci			return True if quick else m.group('t', 'f', 'dt')
221562306a36Sopenharmony_ci		return False if quick else ('', '', '')
221662306a36Sopenharmony_ci	def debugPrint(self):
221762306a36Sopenharmony_ci		for p in self.sortedPhases():
221862306a36Sopenharmony_ci			list = self.dmesg[p]['list']
221962306a36Sopenharmony_ci			for devname in sorted(list):
222062306a36Sopenharmony_ci				dev = list[devname]
222162306a36Sopenharmony_ci				if 'ftrace' in dev:
222262306a36Sopenharmony_ci					dev['ftrace'].debugPrint(' [%s]' % devname)
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci# Class: DevFunction
222562306a36Sopenharmony_ci# Description:
222662306a36Sopenharmony_ci#	 A container for kprobe function data we want in the dev timeline
222762306a36Sopenharmony_ciclass DevFunction:
222862306a36Sopenharmony_ci	def __init__(self, name, args, caller, ret, start, end, u, proc, pid, color):
222962306a36Sopenharmony_ci		self.row = 0
223062306a36Sopenharmony_ci		self.count = 1
223162306a36Sopenharmony_ci		self.name = name
223262306a36Sopenharmony_ci		self.args = args
223362306a36Sopenharmony_ci		self.caller = caller
223462306a36Sopenharmony_ci		self.ret = ret
223562306a36Sopenharmony_ci		self.time = start
223662306a36Sopenharmony_ci		self.length = end - start
223762306a36Sopenharmony_ci		self.end = end
223862306a36Sopenharmony_ci		self.ubiquitous = u
223962306a36Sopenharmony_ci		self.proc = proc
224062306a36Sopenharmony_ci		self.pid = pid
224162306a36Sopenharmony_ci		self.color = color
224262306a36Sopenharmony_ci	def title(self):
224362306a36Sopenharmony_ci		cnt = ''
224462306a36Sopenharmony_ci		if self.count > 1:
224562306a36Sopenharmony_ci			cnt = '(x%d)' % self.count
224662306a36Sopenharmony_ci		l = '%0.3fms' % (self.length * 1000)
224762306a36Sopenharmony_ci		if self.ubiquitous:
224862306a36Sopenharmony_ci			title = '%s(%s)%s <- %s, %s(%s)' % \
224962306a36Sopenharmony_ci				(self.name, self.args, cnt, self.caller, self.ret, l)
225062306a36Sopenharmony_ci		else:
225162306a36Sopenharmony_ci			title = '%s(%s) %s%s(%s)' % (self.name, self.args, self.ret, cnt, l)
225262306a36Sopenharmony_ci		return title.replace('"', '')
225362306a36Sopenharmony_ci	def text(self):
225462306a36Sopenharmony_ci		if self.count > 1:
225562306a36Sopenharmony_ci			text = '%s(x%d)' % (self.name, self.count)
225662306a36Sopenharmony_ci		else:
225762306a36Sopenharmony_ci			text = self.name
225862306a36Sopenharmony_ci		return text
225962306a36Sopenharmony_ci	def repeat(self, tgt):
226062306a36Sopenharmony_ci		# is the tgt call just a repeat of this call (e.g. are we in a loop)
226162306a36Sopenharmony_ci		dt = self.time - tgt.end
226262306a36Sopenharmony_ci		# only combine calls if -all- attributes are identical
226362306a36Sopenharmony_ci		if tgt.caller == self.caller and \
226462306a36Sopenharmony_ci			tgt.name == self.name and tgt.args == self.args and \
226562306a36Sopenharmony_ci			tgt.proc == self.proc and tgt.pid == self.pid and \
226662306a36Sopenharmony_ci			tgt.ret == self.ret and dt >= 0 and \
226762306a36Sopenharmony_ci			dt <= sysvals.callloopmaxgap and \
226862306a36Sopenharmony_ci			self.length < sysvals.callloopmaxlen:
226962306a36Sopenharmony_ci			return True
227062306a36Sopenharmony_ci		return False
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci# Class: FTraceLine
227362306a36Sopenharmony_ci# Description:
227462306a36Sopenharmony_ci#	 A container for a single line of ftrace data. There are six basic types:
227562306a36Sopenharmony_ci#		 callgraph line:
227662306a36Sopenharmony_ci#			  call: "  dpm_run_callback() {"
227762306a36Sopenharmony_ci#			return: "  }"
227862306a36Sopenharmony_ci#			  leaf: " dpm_run_callback();"
227962306a36Sopenharmony_ci#		 trace event:
228062306a36Sopenharmony_ci#			 tracing_mark_write: SUSPEND START or RESUME COMPLETE
228162306a36Sopenharmony_ci#			 suspend_resume: phase or custom exec block data
228262306a36Sopenharmony_ci#			 device_pm_callback: device callback info
228362306a36Sopenharmony_ciclass FTraceLine:
228462306a36Sopenharmony_ci	def __init__(self, t, m='', d=''):
228562306a36Sopenharmony_ci		self.length = 0.0
228662306a36Sopenharmony_ci		self.fcall = False
228762306a36Sopenharmony_ci		self.freturn = False
228862306a36Sopenharmony_ci		self.fevent = False
228962306a36Sopenharmony_ci		self.fkprobe = False
229062306a36Sopenharmony_ci		self.depth = 0
229162306a36Sopenharmony_ci		self.name = ''
229262306a36Sopenharmony_ci		self.type = ''
229362306a36Sopenharmony_ci		self.time = float(t)
229462306a36Sopenharmony_ci		if not m and not d:
229562306a36Sopenharmony_ci			return
229662306a36Sopenharmony_ci		# is this a trace event
229762306a36Sopenharmony_ci		if(d == 'traceevent' or re.match('^ *\/\* *(?P<msg>.*) \*\/ *$', m)):
229862306a36Sopenharmony_ci			if(d == 'traceevent'):
229962306a36Sopenharmony_ci				# nop format trace event
230062306a36Sopenharmony_ci				msg = m
230162306a36Sopenharmony_ci			else:
230262306a36Sopenharmony_ci				# function_graph format trace event
230362306a36Sopenharmony_ci				em = re.match('^ *\/\* *(?P<msg>.*) \*\/ *$', m)
230462306a36Sopenharmony_ci				msg = em.group('msg')
230562306a36Sopenharmony_ci
230662306a36Sopenharmony_ci			emm = re.match('^(?P<call>.*?): (?P<msg>.*)', msg)
230762306a36Sopenharmony_ci			if(emm):
230862306a36Sopenharmony_ci				self.name = emm.group('msg')
230962306a36Sopenharmony_ci				self.type = emm.group('call')
231062306a36Sopenharmony_ci			else:
231162306a36Sopenharmony_ci				self.name = msg
231262306a36Sopenharmony_ci			km = re.match('^(?P<n>.*)_cal$', self.type)
231362306a36Sopenharmony_ci			if km:
231462306a36Sopenharmony_ci				self.fcall = True
231562306a36Sopenharmony_ci				self.fkprobe = True
231662306a36Sopenharmony_ci				self.type = km.group('n')
231762306a36Sopenharmony_ci				return
231862306a36Sopenharmony_ci			km = re.match('^(?P<n>.*)_ret$', self.type)
231962306a36Sopenharmony_ci			if km:
232062306a36Sopenharmony_ci				self.freturn = True
232162306a36Sopenharmony_ci				self.fkprobe = True
232262306a36Sopenharmony_ci				self.type = km.group('n')
232362306a36Sopenharmony_ci				return
232462306a36Sopenharmony_ci			self.fevent = True
232562306a36Sopenharmony_ci			return
232662306a36Sopenharmony_ci		# convert the duration to seconds
232762306a36Sopenharmony_ci		if(d):
232862306a36Sopenharmony_ci			self.length = float(d)/1000000
232962306a36Sopenharmony_ci		# the indentation determines the depth
233062306a36Sopenharmony_ci		match = re.match('^(?P<d> *)(?P<o>.*)$', m)
233162306a36Sopenharmony_ci		if(not match):
233262306a36Sopenharmony_ci			return
233362306a36Sopenharmony_ci		self.depth = self.getDepth(match.group('d'))
233462306a36Sopenharmony_ci		m = match.group('o')
233562306a36Sopenharmony_ci		# function return
233662306a36Sopenharmony_ci		if(m[0] == '}'):
233762306a36Sopenharmony_ci			self.freturn = True
233862306a36Sopenharmony_ci			if(len(m) > 1):
233962306a36Sopenharmony_ci				# includes comment with function name
234062306a36Sopenharmony_ci				match = re.match('^} *\/\* *(?P<n>.*) *\*\/$', m)
234162306a36Sopenharmony_ci				if(match):
234262306a36Sopenharmony_ci					self.name = match.group('n').strip()
234362306a36Sopenharmony_ci		# function call
234462306a36Sopenharmony_ci		else:
234562306a36Sopenharmony_ci			self.fcall = True
234662306a36Sopenharmony_ci			# function call with children
234762306a36Sopenharmony_ci			if(m[-1] == '{'):
234862306a36Sopenharmony_ci				match = re.match('^(?P<n>.*) *\(.*', m)
234962306a36Sopenharmony_ci				if(match):
235062306a36Sopenharmony_ci					self.name = match.group('n').strip()
235162306a36Sopenharmony_ci			# function call with no children (leaf)
235262306a36Sopenharmony_ci			elif(m[-1] == ';'):
235362306a36Sopenharmony_ci				self.freturn = True
235462306a36Sopenharmony_ci				match = re.match('^(?P<n>.*) *\(.*', m)
235562306a36Sopenharmony_ci				if(match):
235662306a36Sopenharmony_ci					self.name = match.group('n').strip()
235762306a36Sopenharmony_ci			# something else (possibly a trace marker)
235862306a36Sopenharmony_ci			else:
235962306a36Sopenharmony_ci				self.name = m
236062306a36Sopenharmony_ci	def isCall(self):
236162306a36Sopenharmony_ci		return self.fcall and not self.freturn
236262306a36Sopenharmony_ci	def isReturn(self):
236362306a36Sopenharmony_ci		return self.freturn and not self.fcall
236462306a36Sopenharmony_ci	def isLeaf(self):
236562306a36Sopenharmony_ci		return self.fcall and self.freturn
236662306a36Sopenharmony_ci	def getDepth(self, str):
236762306a36Sopenharmony_ci		return len(str)/2
236862306a36Sopenharmony_ci	def debugPrint(self, info=''):
236962306a36Sopenharmony_ci		if self.isLeaf():
237062306a36Sopenharmony_ci			pprint(' -- %12.6f (depth=%02d): %s(); (%.3f us) %s' % (self.time, \
237162306a36Sopenharmony_ci				self.depth, self.name, self.length*1000000, info))
237262306a36Sopenharmony_ci		elif self.freturn:
237362306a36Sopenharmony_ci			pprint(' -- %12.6f (depth=%02d): %s} (%.3f us) %s' % (self.time, \
237462306a36Sopenharmony_ci				self.depth, self.name, self.length*1000000, info))
237562306a36Sopenharmony_ci		else:
237662306a36Sopenharmony_ci			pprint(' -- %12.6f (depth=%02d): %s() { (%.3f us) %s' % (self.time, \
237762306a36Sopenharmony_ci				self.depth, self.name, self.length*1000000, info))
237862306a36Sopenharmony_ci	def startMarker(self):
237962306a36Sopenharmony_ci		# Is this the starting line of a suspend?
238062306a36Sopenharmony_ci		if not self.fevent:
238162306a36Sopenharmony_ci			return False
238262306a36Sopenharmony_ci		if sysvals.usetracemarkers:
238362306a36Sopenharmony_ci			if(self.name.startswith('SUSPEND START')):
238462306a36Sopenharmony_ci				return True
238562306a36Sopenharmony_ci			return False
238662306a36Sopenharmony_ci		else:
238762306a36Sopenharmony_ci			if(self.type == 'suspend_resume' and
238862306a36Sopenharmony_ci				re.match('suspend_enter\[.*\] begin', self.name)):
238962306a36Sopenharmony_ci				return True
239062306a36Sopenharmony_ci			return False
239162306a36Sopenharmony_ci	def endMarker(self):
239262306a36Sopenharmony_ci		# Is this the ending line of a resume?
239362306a36Sopenharmony_ci		if not self.fevent:
239462306a36Sopenharmony_ci			return False
239562306a36Sopenharmony_ci		if sysvals.usetracemarkers:
239662306a36Sopenharmony_ci			if(self.name.startswith('RESUME COMPLETE')):
239762306a36Sopenharmony_ci				return True
239862306a36Sopenharmony_ci			return False
239962306a36Sopenharmony_ci		else:
240062306a36Sopenharmony_ci			if(self.type == 'suspend_resume' and
240162306a36Sopenharmony_ci				re.match('thaw_processes\[.*\] end', self.name)):
240262306a36Sopenharmony_ci				return True
240362306a36Sopenharmony_ci			return False
240462306a36Sopenharmony_ci
240562306a36Sopenharmony_ci# Class: FTraceCallGraph
240662306a36Sopenharmony_ci# Description:
240762306a36Sopenharmony_ci#	 A container for the ftrace callgraph of a single recursive function.
240862306a36Sopenharmony_ci#	 This can be a dpm_run_callback, dpm_prepare, or dpm_complete callgraph
240962306a36Sopenharmony_ci#	 Each instance is tied to a single device in a single phase, and is
241062306a36Sopenharmony_ci#	 comprised of an ordered list of FTraceLine objects
241162306a36Sopenharmony_ciclass FTraceCallGraph:
241262306a36Sopenharmony_ci	vfname = 'missing_function_name'
241362306a36Sopenharmony_ci	def __init__(self, pid, sv):
241462306a36Sopenharmony_ci		self.id = ''
241562306a36Sopenharmony_ci		self.invalid = False
241662306a36Sopenharmony_ci		self.name = ''
241762306a36Sopenharmony_ci		self.partial = False
241862306a36Sopenharmony_ci		self.ignore = False
241962306a36Sopenharmony_ci		self.start = -1.0
242062306a36Sopenharmony_ci		self.end = -1.0
242162306a36Sopenharmony_ci		self.list = []
242262306a36Sopenharmony_ci		self.depth = 0
242362306a36Sopenharmony_ci		self.pid = pid
242462306a36Sopenharmony_ci		self.sv = sv
242562306a36Sopenharmony_ci	def addLine(self, line):
242662306a36Sopenharmony_ci		# if this is already invalid, just leave
242762306a36Sopenharmony_ci		if(self.invalid):
242862306a36Sopenharmony_ci			if(line.depth == 0 and line.freturn):
242962306a36Sopenharmony_ci				return 1
243062306a36Sopenharmony_ci			return 0
243162306a36Sopenharmony_ci		# invalidate on bad depth
243262306a36Sopenharmony_ci		if(self.depth < 0):
243362306a36Sopenharmony_ci			self.invalidate(line)
243462306a36Sopenharmony_ci			return 0
243562306a36Sopenharmony_ci		# ignore data til we return to the current depth
243662306a36Sopenharmony_ci		if self.ignore:
243762306a36Sopenharmony_ci			if line.depth > self.depth:
243862306a36Sopenharmony_ci				return 0
243962306a36Sopenharmony_ci			else:
244062306a36Sopenharmony_ci				self.list[-1].freturn = True
244162306a36Sopenharmony_ci				self.list[-1].length = line.time - self.list[-1].time
244262306a36Sopenharmony_ci				self.ignore = False
244362306a36Sopenharmony_ci				# if this is a return at self.depth, no more work is needed
244462306a36Sopenharmony_ci				if line.depth == self.depth and line.isReturn():
244562306a36Sopenharmony_ci					if line.depth == 0:
244662306a36Sopenharmony_ci						self.end = line.time
244762306a36Sopenharmony_ci						return 1
244862306a36Sopenharmony_ci					return 0
244962306a36Sopenharmony_ci		# compare current depth with this lines pre-call depth
245062306a36Sopenharmony_ci		prelinedep = line.depth
245162306a36Sopenharmony_ci		if line.isReturn():
245262306a36Sopenharmony_ci			prelinedep += 1
245362306a36Sopenharmony_ci		last = 0
245462306a36Sopenharmony_ci		lasttime = line.time
245562306a36Sopenharmony_ci		if len(self.list) > 0:
245662306a36Sopenharmony_ci			last = self.list[-1]
245762306a36Sopenharmony_ci			lasttime = last.time
245862306a36Sopenharmony_ci			if last.isLeaf():
245962306a36Sopenharmony_ci				lasttime += last.length
246062306a36Sopenharmony_ci		# handle low misalignments by inserting returns
246162306a36Sopenharmony_ci		mismatch = prelinedep - self.depth
246262306a36Sopenharmony_ci		warning = self.sv.verbose and abs(mismatch) > 1
246362306a36Sopenharmony_ci		info = []
246462306a36Sopenharmony_ci		if mismatch < 0:
246562306a36Sopenharmony_ci			idx = 0
246662306a36Sopenharmony_ci			# add return calls to get the depth down
246762306a36Sopenharmony_ci			while prelinedep < self.depth:
246862306a36Sopenharmony_ci				self.depth -= 1
246962306a36Sopenharmony_ci				if idx == 0 and last and last.isCall():
247062306a36Sopenharmony_ci					# special case, turn last call into a leaf
247162306a36Sopenharmony_ci					last.depth = self.depth
247262306a36Sopenharmony_ci					last.freturn = True
247362306a36Sopenharmony_ci					last.length = line.time - last.time
247462306a36Sopenharmony_ci					if warning:
247562306a36Sopenharmony_ci						info.append(('[make leaf]', last))
247662306a36Sopenharmony_ci				else:
247762306a36Sopenharmony_ci					vline = FTraceLine(lasttime)
247862306a36Sopenharmony_ci					vline.depth = self.depth
247962306a36Sopenharmony_ci					vline.name = self.vfname
248062306a36Sopenharmony_ci					vline.freturn = True
248162306a36Sopenharmony_ci					self.list.append(vline)
248262306a36Sopenharmony_ci					if warning:
248362306a36Sopenharmony_ci						if idx == 0:
248462306a36Sopenharmony_ci							info.append(('', last))
248562306a36Sopenharmony_ci						info.append(('[add return]', vline))
248662306a36Sopenharmony_ci				idx += 1
248762306a36Sopenharmony_ci			if warning:
248862306a36Sopenharmony_ci				info.append(('', line))
248962306a36Sopenharmony_ci		# handle high misalignments by inserting calls
249062306a36Sopenharmony_ci		elif mismatch > 0:
249162306a36Sopenharmony_ci			idx = 0
249262306a36Sopenharmony_ci			if warning:
249362306a36Sopenharmony_ci				info.append(('', last))
249462306a36Sopenharmony_ci			# add calls to get the depth up
249562306a36Sopenharmony_ci			while prelinedep > self.depth:
249662306a36Sopenharmony_ci				if idx == 0 and line.isReturn():
249762306a36Sopenharmony_ci					# special case, turn this return into a leaf
249862306a36Sopenharmony_ci					line.fcall = True
249962306a36Sopenharmony_ci					prelinedep -= 1
250062306a36Sopenharmony_ci					if warning:
250162306a36Sopenharmony_ci						info.append(('[make leaf]', line))
250262306a36Sopenharmony_ci				else:
250362306a36Sopenharmony_ci					vline = FTraceLine(lasttime)
250462306a36Sopenharmony_ci					vline.depth = self.depth
250562306a36Sopenharmony_ci					vline.name = self.vfname
250662306a36Sopenharmony_ci					vline.fcall = True
250762306a36Sopenharmony_ci					self.list.append(vline)
250862306a36Sopenharmony_ci					self.depth += 1
250962306a36Sopenharmony_ci					if not last:
251062306a36Sopenharmony_ci						self.start = vline.time
251162306a36Sopenharmony_ci					if warning:
251262306a36Sopenharmony_ci						info.append(('[add call]', vline))
251362306a36Sopenharmony_ci				idx += 1
251462306a36Sopenharmony_ci			if warning and ('[make leaf]', line) not in info:
251562306a36Sopenharmony_ci				info.append(('', line))
251662306a36Sopenharmony_ci		if warning:
251762306a36Sopenharmony_ci			pprint('WARNING: ftrace data missing, corrections made:')
251862306a36Sopenharmony_ci			for i in info:
251962306a36Sopenharmony_ci				t, obj = i
252062306a36Sopenharmony_ci				if obj:
252162306a36Sopenharmony_ci					obj.debugPrint(t)
252262306a36Sopenharmony_ci		# process the call and set the new depth
252362306a36Sopenharmony_ci		skipadd = False
252462306a36Sopenharmony_ci		md = self.sv.max_graph_depth
252562306a36Sopenharmony_ci		if line.isCall():
252662306a36Sopenharmony_ci			# ignore blacklisted/overdepth funcs
252762306a36Sopenharmony_ci			if (md and self.depth >= md - 1) or (line.name in self.sv.cgblacklist):
252862306a36Sopenharmony_ci				self.ignore = True
252962306a36Sopenharmony_ci			else:
253062306a36Sopenharmony_ci				self.depth += 1
253162306a36Sopenharmony_ci		elif line.isReturn():
253262306a36Sopenharmony_ci			self.depth -= 1
253362306a36Sopenharmony_ci			# remove blacklisted/overdepth/empty funcs that slipped through
253462306a36Sopenharmony_ci			if (last and last.isCall() and last.depth == line.depth) or \
253562306a36Sopenharmony_ci				(md and last and last.depth >= md) or \
253662306a36Sopenharmony_ci				(line.name in self.sv.cgblacklist):
253762306a36Sopenharmony_ci				while len(self.list) > 0 and self.list[-1].depth > line.depth:
253862306a36Sopenharmony_ci					self.list.pop(-1)
253962306a36Sopenharmony_ci				if len(self.list) == 0:
254062306a36Sopenharmony_ci					self.invalid = True
254162306a36Sopenharmony_ci					return 1
254262306a36Sopenharmony_ci				self.list[-1].freturn = True
254362306a36Sopenharmony_ci				self.list[-1].length = line.time - self.list[-1].time
254462306a36Sopenharmony_ci				self.list[-1].name = line.name
254562306a36Sopenharmony_ci				skipadd = True
254662306a36Sopenharmony_ci		if len(self.list) < 1:
254762306a36Sopenharmony_ci			self.start = line.time
254862306a36Sopenharmony_ci		# check for a mismatch that returned all the way to callgraph end
254962306a36Sopenharmony_ci		res = 1
255062306a36Sopenharmony_ci		if mismatch < 0 and self.list[-1].depth == 0 and self.list[-1].freturn:
255162306a36Sopenharmony_ci			line = self.list[-1]
255262306a36Sopenharmony_ci			skipadd = True
255362306a36Sopenharmony_ci			res = -1
255462306a36Sopenharmony_ci		if not skipadd:
255562306a36Sopenharmony_ci			self.list.append(line)
255662306a36Sopenharmony_ci		if(line.depth == 0 and line.freturn):
255762306a36Sopenharmony_ci			if(self.start < 0):
255862306a36Sopenharmony_ci				self.start = line.time
255962306a36Sopenharmony_ci			self.end = line.time
256062306a36Sopenharmony_ci			if line.fcall:
256162306a36Sopenharmony_ci				self.end += line.length
256262306a36Sopenharmony_ci			if self.list[0].name == self.vfname:
256362306a36Sopenharmony_ci				self.invalid = True
256462306a36Sopenharmony_ci			if res == -1:
256562306a36Sopenharmony_ci				self.partial = True
256662306a36Sopenharmony_ci			return res
256762306a36Sopenharmony_ci		return 0
256862306a36Sopenharmony_ci	def invalidate(self, line):
256962306a36Sopenharmony_ci		if(len(self.list) > 0):
257062306a36Sopenharmony_ci			first = self.list[0]
257162306a36Sopenharmony_ci			self.list = []
257262306a36Sopenharmony_ci			self.list.append(first)
257362306a36Sopenharmony_ci		self.invalid = True
257462306a36Sopenharmony_ci		id = 'task %s' % (self.pid)
257562306a36Sopenharmony_ci		window = '(%f - %f)' % (self.start, line.time)
257662306a36Sopenharmony_ci		if(self.depth < 0):
257762306a36Sopenharmony_ci			pprint('Data misalignment for '+id+\
257862306a36Sopenharmony_ci				' (buffer overflow), ignoring this callback')
257962306a36Sopenharmony_ci		else:
258062306a36Sopenharmony_ci			pprint('Too much data for '+id+\
258162306a36Sopenharmony_ci				' '+window+', ignoring this callback')
258262306a36Sopenharmony_ci	def slice(self, dev):
258362306a36Sopenharmony_ci		minicg = FTraceCallGraph(dev['pid'], self.sv)
258462306a36Sopenharmony_ci		minicg.name = self.name
258562306a36Sopenharmony_ci		mydepth = -1
258662306a36Sopenharmony_ci		good = False
258762306a36Sopenharmony_ci		for l in self.list:
258862306a36Sopenharmony_ci			if(l.time < dev['start'] or l.time > dev['end']):
258962306a36Sopenharmony_ci				continue
259062306a36Sopenharmony_ci			if mydepth < 0:
259162306a36Sopenharmony_ci				if l.name == 'mutex_lock' and l.freturn:
259262306a36Sopenharmony_ci					mydepth = l.depth
259362306a36Sopenharmony_ci				continue
259462306a36Sopenharmony_ci			elif l.depth == mydepth and l.name == 'mutex_unlock' and l.fcall:
259562306a36Sopenharmony_ci				good = True
259662306a36Sopenharmony_ci				break
259762306a36Sopenharmony_ci			l.depth -= mydepth
259862306a36Sopenharmony_ci			minicg.addLine(l)
259962306a36Sopenharmony_ci		if not good or len(minicg.list) < 1:
260062306a36Sopenharmony_ci			return 0
260162306a36Sopenharmony_ci		return minicg
260262306a36Sopenharmony_ci	def repair(self, enddepth):
260362306a36Sopenharmony_ci		# bring the depth back to 0 with additional returns
260462306a36Sopenharmony_ci		fixed = False
260562306a36Sopenharmony_ci		last = self.list[-1]
260662306a36Sopenharmony_ci		for i in reversed(range(enddepth)):
260762306a36Sopenharmony_ci			t = FTraceLine(last.time)
260862306a36Sopenharmony_ci			t.depth = i
260962306a36Sopenharmony_ci			t.freturn = True
261062306a36Sopenharmony_ci			fixed = self.addLine(t)
261162306a36Sopenharmony_ci			if fixed != 0:
261262306a36Sopenharmony_ci				self.end = last.time
261362306a36Sopenharmony_ci				return True
261462306a36Sopenharmony_ci		return False
261562306a36Sopenharmony_ci	def postProcess(self):
261662306a36Sopenharmony_ci		if len(self.list) > 0:
261762306a36Sopenharmony_ci			self.name = self.list[0].name
261862306a36Sopenharmony_ci		stack = dict()
261962306a36Sopenharmony_ci		cnt = 0
262062306a36Sopenharmony_ci		last = 0
262162306a36Sopenharmony_ci		for l in self.list:
262262306a36Sopenharmony_ci			# ftrace bug: reported duration is not reliable
262362306a36Sopenharmony_ci			# check each leaf and clip it at max possible length
262462306a36Sopenharmony_ci			if last and last.isLeaf():
262562306a36Sopenharmony_ci				if last.length > l.time - last.time:
262662306a36Sopenharmony_ci					last.length = l.time - last.time
262762306a36Sopenharmony_ci			if l.isCall():
262862306a36Sopenharmony_ci				stack[l.depth] = l
262962306a36Sopenharmony_ci				cnt += 1
263062306a36Sopenharmony_ci			elif l.isReturn():
263162306a36Sopenharmony_ci				if(l.depth not in stack):
263262306a36Sopenharmony_ci					if self.sv.verbose:
263362306a36Sopenharmony_ci						pprint('Post Process Error: Depth missing')
263462306a36Sopenharmony_ci						l.debugPrint()
263562306a36Sopenharmony_ci					return False
263662306a36Sopenharmony_ci				# calculate call length from call/return lines
263762306a36Sopenharmony_ci				cl = stack[l.depth]
263862306a36Sopenharmony_ci				cl.length = l.time - cl.time
263962306a36Sopenharmony_ci				if cl.name == self.vfname:
264062306a36Sopenharmony_ci					cl.name = l.name
264162306a36Sopenharmony_ci				stack.pop(l.depth)
264262306a36Sopenharmony_ci				l.length = 0
264362306a36Sopenharmony_ci				cnt -= 1
264462306a36Sopenharmony_ci			last = l
264562306a36Sopenharmony_ci		if(cnt == 0):
264662306a36Sopenharmony_ci			# trace caught the whole call tree
264762306a36Sopenharmony_ci			return True
264862306a36Sopenharmony_ci		elif(cnt < 0):
264962306a36Sopenharmony_ci			if self.sv.verbose:
265062306a36Sopenharmony_ci				pprint('Post Process Error: Depth is less than 0')
265162306a36Sopenharmony_ci			return False
265262306a36Sopenharmony_ci		# trace ended before call tree finished
265362306a36Sopenharmony_ci		return self.repair(cnt)
265462306a36Sopenharmony_ci	def deviceMatch(self, pid, data):
265562306a36Sopenharmony_ci		found = ''
265662306a36Sopenharmony_ci		# add the callgraph data to the device hierarchy
265762306a36Sopenharmony_ci		borderphase = {
265862306a36Sopenharmony_ci			'dpm_prepare': 'suspend_prepare',
265962306a36Sopenharmony_ci			'dpm_complete': 'resume_complete'
266062306a36Sopenharmony_ci		}
266162306a36Sopenharmony_ci		if(self.name in borderphase):
266262306a36Sopenharmony_ci			p = borderphase[self.name]
266362306a36Sopenharmony_ci			list = data.dmesg[p]['list']
266462306a36Sopenharmony_ci			for devname in list:
266562306a36Sopenharmony_ci				dev = list[devname]
266662306a36Sopenharmony_ci				if(pid == dev['pid'] and
266762306a36Sopenharmony_ci					self.start <= dev['start'] and
266862306a36Sopenharmony_ci					self.end >= dev['end']):
266962306a36Sopenharmony_ci					cg = self.slice(dev)
267062306a36Sopenharmony_ci					if cg:
267162306a36Sopenharmony_ci						dev['ftrace'] = cg
267262306a36Sopenharmony_ci					found = devname
267362306a36Sopenharmony_ci			return found
267462306a36Sopenharmony_ci		for p in data.sortedPhases():
267562306a36Sopenharmony_ci			if(data.dmesg[p]['start'] <= self.start and
267662306a36Sopenharmony_ci				self.start <= data.dmesg[p]['end']):
267762306a36Sopenharmony_ci				list = data.dmesg[p]['list']
267862306a36Sopenharmony_ci				for devname in sorted(list, key=lambda k:list[k]['start']):
267962306a36Sopenharmony_ci					dev = list[devname]
268062306a36Sopenharmony_ci					if(pid == dev['pid'] and
268162306a36Sopenharmony_ci						self.start <= dev['start'] and
268262306a36Sopenharmony_ci						self.end >= dev['end']):
268362306a36Sopenharmony_ci						dev['ftrace'] = self
268462306a36Sopenharmony_ci						found = devname
268562306a36Sopenharmony_ci						break
268662306a36Sopenharmony_ci				break
268762306a36Sopenharmony_ci		return found
268862306a36Sopenharmony_ci	def newActionFromFunction(self, data):
268962306a36Sopenharmony_ci		name = self.name
269062306a36Sopenharmony_ci		if name in ['dpm_run_callback', 'dpm_prepare', 'dpm_complete']:
269162306a36Sopenharmony_ci			return
269262306a36Sopenharmony_ci		fs = self.start
269362306a36Sopenharmony_ci		fe = self.end
269462306a36Sopenharmony_ci		if fs < data.start or fe > data.end:
269562306a36Sopenharmony_ci			return
269662306a36Sopenharmony_ci		phase = ''
269762306a36Sopenharmony_ci		for p in data.sortedPhases():
269862306a36Sopenharmony_ci			if(data.dmesg[p]['start'] <= self.start and
269962306a36Sopenharmony_ci				self.start < data.dmesg[p]['end']):
270062306a36Sopenharmony_ci				phase = p
270162306a36Sopenharmony_ci				break
270262306a36Sopenharmony_ci		if not phase:
270362306a36Sopenharmony_ci			return
270462306a36Sopenharmony_ci		out = data.newActionGlobal(name, fs, fe, -2)
270562306a36Sopenharmony_ci		if out:
270662306a36Sopenharmony_ci			phase, myname = out
270762306a36Sopenharmony_ci			data.dmesg[phase]['list'][myname]['ftrace'] = self
270862306a36Sopenharmony_ci	def debugPrint(self, info=''):
270962306a36Sopenharmony_ci		pprint('%s pid=%d [%f - %f] %.3f us' % \
271062306a36Sopenharmony_ci			(self.name, self.pid, self.start, self.end,
271162306a36Sopenharmony_ci			(self.end - self.start)*1000000))
271262306a36Sopenharmony_ci		for l in self.list:
271362306a36Sopenharmony_ci			if l.isLeaf():
271462306a36Sopenharmony_ci				pprint('%f (%02d): %s(); (%.3f us)%s' % (l.time, \
271562306a36Sopenharmony_ci					l.depth, l.name, l.length*1000000, info))
271662306a36Sopenharmony_ci			elif l.freturn:
271762306a36Sopenharmony_ci				pprint('%f (%02d): %s} (%.3f us)%s' % (l.time, \
271862306a36Sopenharmony_ci					l.depth, l.name, l.length*1000000, info))
271962306a36Sopenharmony_ci			else:
272062306a36Sopenharmony_ci				pprint('%f (%02d): %s() { (%.3f us)%s' % (l.time, \
272162306a36Sopenharmony_ci					l.depth, l.name, l.length*1000000, info))
272262306a36Sopenharmony_ci		pprint(' ')
272362306a36Sopenharmony_ci
272462306a36Sopenharmony_ciclass DevItem:
272562306a36Sopenharmony_ci	def __init__(self, test, phase, dev):
272662306a36Sopenharmony_ci		self.test = test
272762306a36Sopenharmony_ci		self.phase = phase
272862306a36Sopenharmony_ci		self.dev = dev
272962306a36Sopenharmony_ci	def isa(self, cls):
273062306a36Sopenharmony_ci		if 'htmlclass' in self.dev and cls in self.dev['htmlclass']:
273162306a36Sopenharmony_ci			return True
273262306a36Sopenharmony_ci		return False
273362306a36Sopenharmony_ci
273462306a36Sopenharmony_ci# Class: Timeline
273562306a36Sopenharmony_ci# Description:
273662306a36Sopenharmony_ci#	 A container for a device timeline which calculates
273762306a36Sopenharmony_ci#	 all the html properties to display it correctly
273862306a36Sopenharmony_ciclass Timeline:
273962306a36Sopenharmony_ci	html_tblock = '<div id="block{0}" class="tblock" style="left:{1}%;width:{2}%;"><div class="tback" style="height:{3}px"></div>\n'
274062306a36Sopenharmony_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'
274162306a36Sopenharmony_ci	html_phase = '<div class="phase" style="left:{0}%;width:{1}%;top:{2}px;height:{3}px;background:{4}">{5}</div>\n'
274262306a36Sopenharmony_ci	html_phaselet = '<div id="{0}" class="phaselet" style="left:{1}%;width:{2}%;background:{3}"></div>\n'
274362306a36Sopenharmony_ci	html_legend = '<div id="p{3}" class="square" style="left:{0}%;background:{1}">&nbsp;{2}</div>\n'
274462306a36Sopenharmony_ci	def __init__(self, rowheight, scaleheight):
274562306a36Sopenharmony_ci		self.html = ''
274662306a36Sopenharmony_ci		self.height = 0  # total timeline height
274762306a36Sopenharmony_ci		self.scaleH = scaleheight # timescale (top) row height
274862306a36Sopenharmony_ci		self.rowH = rowheight     # device row height
274962306a36Sopenharmony_ci		self.bodyH = 0   # body height
275062306a36Sopenharmony_ci		self.rows = 0    # total timeline rows
275162306a36Sopenharmony_ci		self.rowlines = dict()
275262306a36Sopenharmony_ci		self.rowheight = dict()
275362306a36Sopenharmony_ci	def createHeader(self, sv, stamp):
275462306a36Sopenharmony_ci		if(not stamp['time']):
275562306a36Sopenharmony_ci			return
275662306a36Sopenharmony_ci		self.html += '<div class="version"><a href="https://01.org/pm-graph">%s v%s</a></div>' \
275762306a36Sopenharmony_ci			% (sv.title, sv.version)
275862306a36Sopenharmony_ci		if sv.logmsg and sv.testlog:
275962306a36Sopenharmony_ci			self.html += '<button id="showtest" class="logbtn btnfmt">log</button>'
276062306a36Sopenharmony_ci		if sv.dmesglog:
276162306a36Sopenharmony_ci			self.html += '<button id="showdmesg" class="logbtn btnfmt">dmesg</button>'
276262306a36Sopenharmony_ci		if sv.ftracelog:
276362306a36Sopenharmony_ci			self.html += '<button id="showftrace" class="logbtn btnfmt">ftrace</button>'
276462306a36Sopenharmony_ci		headline_stamp = '<div class="stamp">{0} {1} {2} {3}</div>\n'
276562306a36Sopenharmony_ci		self.html += headline_stamp.format(stamp['host'], stamp['kernel'],
276662306a36Sopenharmony_ci			stamp['mode'], stamp['time'])
276762306a36Sopenharmony_ci		if 'man' in stamp and 'plat' in stamp and 'cpu' in stamp and \
276862306a36Sopenharmony_ci			stamp['man'] and stamp['plat'] and stamp['cpu']:
276962306a36Sopenharmony_ci			headline_sysinfo = '<div class="stamp sysinfo">{0} {1} <i>with</i> {2}</div>\n'
277062306a36Sopenharmony_ci			self.html += headline_sysinfo.format(stamp['man'], stamp['plat'], stamp['cpu'])
277162306a36Sopenharmony_ci
277262306a36Sopenharmony_ci	# Function: getDeviceRows
277362306a36Sopenharmony_ci	# Description:
277462306a36Sopenharmony_ci	#    determine how may rows the device funcs will take
277562306a36Sopenharmony_ci	# Arguments:
277662306a36Sopenharmony_ci	#	 rawlist: the list of devices/actions for a single phase
277762306a36Sopenharmony_ci	# Output:
277862306a36Sopenharmony_ci	#	 The total number of rows needed to display this phase of the timeline
277962306a36Sopenharmony_ci	def getDeviceRows(self, rawlist):
278062306a36Sopenharmony_ci		# clear all rows and set them to undefined
278162306a36Sopenharmony_ci		sortdict = dict()
278262306a36Sopenharmony_ci		for item in rawlist:
278362306a36Sopenharmony_ci			item.row = -1
278462306a36Sopenharmony_ci			sortdict[item] = item.length
278562306a36Sopenharmony_ci		sortlist = sorted(sortdict, key=sortdict.get, reverse=True)
278662306a36Sopenharmony_ci		remaining = len(sortlist)
278762306a36Sopenharmony_ci		rowdata = dict()
278862306a36Sopenharmony_ci		row = 1
278962306a36Sopenharmony_ci		# try to pack each row with as many ranges as possible
279062306a36Sopenharmony_ci		while(remaining > 0):
279162306a36Sopenharmony_ci			if(row not in rowdata):
279262306a36Sopenharmony_ci				rowdata[row] = []
279362306a36Sopenharmony_ci			for i in sortlist:
279462306a36Sopenharmony_ci				if(i.row >= 0):
279562306a36Sopenharmony_ci					continue
279662306a36Sopenharmony_ci				s = i.time
279762306a36Sopenharmony_ci				e = i.time + i.length
279862306a36Sopenharmony_ci				valid = True
279962306a36Sopenharmony_ci				for ritem in rowdata[row]:
280062306a36Sopenharmony_ci					rs = ritem.time
280162306a36Sopenharmony_ci					re = ritem.time + ritem.length
280262306a36Sopenharmony_ci					if(not (((s <= rs) and (e <= rs)) or
280362306a36Sopenharmony_ci						((s >= re) and (e >= re)))):
280462306a36Sopenharmony_ci						valid = False
280562306a36Sopenharmony_ci						break
280662306a36Sopenharmony_ci				if(valid):
280762306a36Sopenharmony_ci					rowdata[row].append(i)
280862306a36Sopenharmony_ci					i.row = row
280962306a36Sopenharmony_ci					remaining -= 1
281062306a36Sopenharmony_ci			row += 1
281162306a36Sopenharmony_ci		return row
281262306a36Sopenharmony_ci	# Function: getPhaseRows
281362306a36Sopenharmony_ci	# Description:
281462306a36Sopenharmony_ci	#	 Organize the timeline entries into the smallest
281562306a36Sopenharmony_ci	#	 number of rows possible, with no entry overlapping
281662306a36Sopenharmony_ci	# Arguments:
281762306a36Sopenharmony_ci	#	 devlist: the list of devices/actions in a group of contiguous phases
281862306a36Sopenharmony_ci	# Output:
281962306a36Sopenharmony_ci	#	 The total number of rows needed to display this phase of the timeline
282062306a36Sopenharmony_ci	def getPhaseRows(self, devlist, row=0, sortby='length'):
282162306a36Sopenharmony_ci		# clear all rows and set them to undefined
282262306a36Sopenharmony_ci		remaining = len(devlist)
282362306a36Sopenharmony_ci		rowdata = dict()
282462306a36Sopenharmony_ci		sortdict = dict()
282562306a36Sopenharmony_ci		myphases = []
282662306a36Sopenharmony_ci		# initialize all device rows to -1 and calculate devrows
282762306a36Sopenharmony_ci		for item in devlist:
282862306a36Sopenharmony_ci			dev = item.dev
282962306a36Sopenharmony_ci			tp = (item.test, item.phase)
283062306a36Sopenharmony_ci			if tp not in myphases:
283162306a36Sopenharmony_ci				myphases.append(tp)
283262306a36Sopenharmony_ci			dev['row'] = -1
283362306a36Sopenharmony_ci			if sortby == 'start':
283462306a36Sopenharmony_ci				# sort by start 1st, then length 2nd
283562306a36Sopenharmony_ci				sortdict[item] = (-1*float(dev['start']), float(dev['end']) - float(dev['start']))
283662306a36Sopenharmony_ci			else:
283762306a36Sopenharmony_ci				# sort by length 1st, then name 2nd
283862306a36Sopenharmony_ci				sortdict[item] = (float(dev['end']) - float(dev['start']), item.dev['name'])
283962306a36Sopenharmony_ci			if 'src' in dev:
284062306a36Sopenharmony_ci				dev['devrows'] = self.getDeviceRows(dev['src'])
284162306a36Sopenharmony_ci		# sort the devlist by length so that large items graph on top
284262306a36Sopenharmony_ci		sortlist = sorted(sortdict, key=sortdict.get, reverse=True)
284362306a36Sopenharmony_ci		orderedlist = []
284462306a36Sopenharmony_ci		for item in sortlist:
284562306a36Sopenharmony_ci			if item.dev['pid'] == -2:
284662306a36Sopenharmony_ci				orderedlist.append(item)
284762306a36Sopenharmony_ci		for item in sortlist:
284862306a36Sopenharmony_ci			if item not in orderedlist:
284962306a36Sopenharmony_ci				orderedlist.append(item)
285062306a36Sopenharmony_ci		# try to pack each row with as many devices as possible
285162306a36Sopenharmony_ci		while(remaining > 0):
285262306a36Sopenharmony_ci			rowheight = 1
285362306a36Sopenharmony_ci			if(row not in rowdata):
285462306a36Sopenharmony_ci				rowdata[row] = []
285562306a36Sopenharmony_ci			for item in orderedlist:
285662306a36Sopenharmony_ci				dev = item.dev
285762306a36Sopenharmony_ci				if(dev['row'] < 0):
285862306a36Sopenharmony_ci					s = dev['start']
285962306a36Sopenharmony_ci					e = dev['end']
286062306a36Sopenharmony_ci					valid = True
286162306a36Sopenharmony_ci					for ritem in rowdata[row]:
286262306a36Sopenharmony_ci						rs = ritem.dev['start']
286362306a36Sopenharmony_ci						re = ritem.dev['end']
286462306a36Sopenharmony_ci						if(not (((s <= rs) and (e <= rs)) or
286562306a36Sopenharmony_ci							((s >= re) and (e >= re)))):
286662306a36Sopenharmony_ci							valid = False
286762306a36Sopenharmony_ci							break
286862306a36Sopenharmony_ci					if(valid):
286962306a36Sopenharmony_ci						rowdata[row].append(item)
287062306a36Sopenharmony_ci						dev['row'] = row
287162306a36Sopenharmony_ci						remaining -= 1
287262306a36Sopenharmony_ci						if 'devrows' in dev and dev['devrows'] > rowheight:
287362306a36Sopenharmony_ci							rowheight = dev['devrows']
287462306a36Sopenharmony_ci			for t, p in myphases:
287562306a36Sopenharmony_ci				if t not in self.rowlines or t not in self.rowheight:
287662306a36Sopenharmony_ci					self.rowlines[t] = dict()
287762306a36Sopenharmony_ci					self.rowheight[t] = dict()
287862306a36Sopenharmony_ci				if p not in self.rowlines[t] or p not in self.rowheight[t]:
287962306a36Sopenharmony_ci					self.rowlines[t][p] = dict()
288062306a36Sopenharmony_ci					self.rowheight[t][p] = dict()
288162306a36Sopenharmony_ci				rh = self.rowH
288262306a36Sopenharmony_ci				# section headers should use a different row height
288362306a36Sopenharmony_ci				if len(rowdata[row]) == 1 and \
288462306a36Sopenharmony_ci					'htmlclass' in rowdata[row][0].dev and \
288562306a36Sopenharmony_ci					'sec' in rowdata[row][0].dev['htmlclass']:
288662306a36Sopenharmony_ci					rh = 15
288762306a36Sopenharmony_ci				self.rowlines[t][p][row] = rowheight
288862306a36Sopenharmony_ci				self.rowheight[t][p][row] = rowheight * rh
288962306a36Sopenharmony_ci			row += 1
289062306a36Sopenharmony_ci		if(row > self.rows):
289162306a36Sopenharmony_ci			self.rows = int(row)
289262306a36Sopenharmony_ci		return row
289362306a36Sopenharmony_ci	def phaseRowHeight(self, test, phase, row):
289462306a36Sopenharmony_ci		return self.rowheight[test][phase][row]
289562306a36Sopenharmony_ci	def phaseRowTop(self, test, phase, row):
289662306a36Sopenharmony_ci		top = 0
289762306a36Sopenharmony_ci		for i in sorted(self.rowheight[test][phase]):
289862306a36Sopenharmony_ci			if i >= row:
289962306a36Sopenharmony_ci				break
290062306a36Sopenharmony_ci			top += self.rowheight[test][phase][i]
290162306a36Sopenharmony_ci		return top
290262306a36Sopenharmony_ci	def calcTotalRows(self):
290362306a36Sopenharmony_ci		# Calculate the heights and offsets for the header and rows
290462306a36Sopenharmony_ci		maxrows = 0
290562306a36Sopenharmony_ci		standardphases = []
290662306a36Sopenharmony_ci		for t in self.rowlines:
290762306a36Sopenharmony_ci			for p in self.rowlines[t]:
290862306a36Sopenharmony_ci				total = 0
290962306a36Sopenharmony_ci				for i in sorted(self.rowlines[t][p]):
291062306a36Sopenharmony_ci					total += self.rowlines[t][p][i]
291162306a36Sopenharmony_ci				if total > maxrows:
291262306a36Sopenharmony_ci					maxrows = total
291362306a36Sopenharmony_ci				if total == len(self.rowlines[t][p]):
291462306a36Sopenharmony_ci					standardphases.append((t, p))
291562306a36Sopenharmony_ci		self.height = self.scaleH + (maxrows*self.rowH)
291662306a36Sopenharmony_ci		self.bodyH = self.height - self.scaleH
291762306a36Sopenharmony_ci		# if there is 1 line per row, draw them the standard way
291862306a36Sopenharmony_ci		for t, p in standardphases:
291962306a36Sopenharmony_ci			for i in sorted(self.rowheight[t][p]):
292062306a36Sopenharmony_ci				self.rowheight[t][p][i] = float(self.bodyH)/len(self.rowlines[t][p])
292162306a36Sopenharmony_ci	def createZoomBox(self, mode='command', testcount=1):
292262306a36Sopenharmony_ci		# Create bounding box, add buttons
292362306a36Sopenharmony_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'
292462306a36Sopenharmony_ci		html_timeline = '<div id="dmesgzoombox" class="zoombox">\n<div id="{0}" class="timeline" style="height:{1}px">\n'
292562306a36Sopenharmony_ci		html_devlist1 = '<button id="devlist1" class="devlist" style="float:left;">Device Detail{0}</button>'
292662306a36Sopenharmony_ci		html_devlist2 = '<button id="devlist2" class="devlist" style="float:right;">Device Detail2</button>\n'
292762306a36Sopenharmony_ci		if mode != 'command':
292862306a36Sopenharmony_ci			if testcount > 1:
292962306a36Sopenharmony_ci				self.html += html_devlist2
293062306a36Sopenharmony_ci				self.html += html_devlist1.format('1')
293162306a36Sopenharmony_ci			else:
293262306a36Sopenharmony_ci				self.html += html_devlist1.format('')
293362306a36Sopenharmony_ci		self.html += html_zoombox
293462306a36Sopenharmony_ci		self.html += html_timeline.format('dmesg', self.height)
293562306a36Sopenharmony_ci	# Function: createTimeScale
293662306a36Sopenharmony_ci	# Description:
293762306a36Sopenharmony_ci	#	 Create the timescale for a timeline block
293862306a36Sopenharmony_ci	# Arguments:
293962306a36Sopenharmony_ci	#	 m0: start time (mode begin)
294062306a36Sopenharmony_ci	#	 mMax: end time (mode end)
294162306a36Sopenharmony_ci	#	 tTotal: total timeline time
294262306a36Sopenharmony_ci	#	 mode: suspend or resume
294362306a36Sopenharmony_ci	# Output:
294462306a36Sopenharmony_ci	#	 The html code needed to display the time scale
294562306a36Sopenharmony_ci	def createTimeScale(self, m0, mMax, tTotal, mode):
294662306a36Sopenharmony_ci		timescale = '<div class="t" style="right:{0}%">{1}</div>\n'
294762306a36Sopenharmony_ci		rline = '<div class="t" style="left:0;border-left:1px solid black;border-right:0;">{0}</div>\n'
294862306a36Sopenharmony_ci		output = '<div class="timescale">\n'
294962306a36Sopenharmony_ci		# set scale for timeline
295062306a36Sopenharmony_ci		mTotal = mMax - m0
295162306a36Sopenharmony_ci		tS = 0.1
295262306a36Sopenharmony_ci		if(tTotal <= 0):
295362306a36Sopenharmony_ci			return output+'</div>\n'
295462306a36Sopenharmony_ci		if(tTotal > 4):
295562306a36Sopenharmony_ci			tS = 1
295662306a36Sopenharmony_ci		divTotal = int(mTotal/tS) + 1
295762306a36Sopenharmony_ci		divEdge = (mTotal - tS*(divTotal-1))*100/mTotal
295862306a36Sopenharmony_ci		for i in range(divTotal):
295962306a36Sopenharmony_ci			htmlline = ''
296062306a36Sopenharmony_ci			if(mode == 'suspend'):
296162306a36Sopenharmony_ci				pos = '%0.3f' % (100 - ((float(i)*tS*100)/mTotal) - divEdge)
296262306a36Sopenharmony_ci				val = '%0.fms' % (float(i-divTotal+1)*tS*1000)
296362306a36Sopenharmony_ci				if(i == divTotal - 1):
296462306a36Sopenharmony_ci					val = mode
296562306a36Sopenharmony_ci				htmlline = timescale.format(pos, val)
296662306a36Sopenharmony_ci			else:
296762306a36Sopenharmony_ci				pos = '%0.3f' % (100 - ((float(i)*tS*100)/mTotal))
296862306a36Sopenharmony_ci				val = '%0.fms' % (float(i)*tS*1000)
296962306a36Sopenharmony_ci				htmlline = timescale.format(pos, val)
297062306a36Sopenharmony_ci				if(i == 0):
297162306a36Sopenharmony_ci					htmlline = rline.format(mode)
297262306a36Sopenharmony_ci			output += htmlline
297362306a36Sopenharmony_ci		self.html += output+'</div>\n'
297462306a36Sopenharmony_ci
297562306a36Sopenharmony_ci# Class: TestProps
297662306a36Sopenharmony_ci# Description:
297762306a36Sopenharmony_ci#	 A list of values describing the properties of these test runs
297862306a36Sopenharmony_ciclass TestProps:
297962306a36Sopenharmony_ci	stampfmt = '# [a-z]*-(?P<m>[0-9]{2})(?P<d>[0-9]{2})(?P<y>[0-9]{2})-'+\
298062306a36Sopenharmony_ci				'(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\
298162306a36Sopenharmony_ci				' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$'
298262306a36Sopenharmony_ci	wififmt    = '^# wifi *(?P<d>\S*) *(?P<s>\S*) *(?P<t>[0-9\.]+).*'
298362306a36Sopenharmony_ci	tstatfmt   = '^# turbostat (?P<t>\S*)'
298462306a36Sopenharmony_ci	testerrfmt = '^# enter_sleep_error (?P<e>.*)'
298562306a36Sopenharmony_ci	sysinfofmt = '^# sysinfo .*'
298662306a36Sopenharmony_ci	cmdlinefmt = '^# command \| (?P<cmd>.*)'
298762306a36Sopenharmony_ci	kparamsfmt = '^# kparams \| (?P<kp>.*)'
298862306a36Sopenharmony_ci	devpropfmt = '# Device Properties: .*'
298962306a36Sopenharmony_ci	pinfofmt   = '# platform-(?P<val>[a-z,A-Z,0-9,_]*): (?P<info>.*)'
299062306a36Sopenharmony_ci	tracertypefmt = '# tracer: (?P<t>.*)'
299162306a36Sopenharmony_ci	firmwarefmt = '# fwsuspend (?P<s>[0-9]*) fwresume (?P<r>[0-9]*)$'
299262306a36Sopenharmony_ci	procexecfmt = 'ps - (?P<ps>.*)$'
299362306a36Sopenharmony_ci	procmultifmt = '@(?P<n>[0-9]*)\|(?P<ps>.*)$'
299462306a36Sopenharmony_ci	ftrace_line_fmt_fg = \
299562306a36Sopenharmony_ci		'^ *(?P<time>[0-9\.]*) *\| *(?P<cpu>[0-9]*)\)'+\
299662306a36Sopenharmony_ci		' *(?P<proc>.*)-(?P<pid>[0-9]*) *\|'+\
299762306a36Sopenharmony_ci		'[ +!#\*@$]*(?P<dur>[0-9\.]*) .*\|  (?P<msg>.*)'
299862306a36Sopenharmony_ci	ftrace_line_fmt_nop = \
299962306a36Sopenharmony_ci		' *(?P<proc>.*)-(?P<pid>[0-9]*) *\[(?P<cpu>[0-9]*)\] *'+\
300062306a36Sopenharmony_ci		'(?P<flags>\S*) *(?P<time>[0-9\.]*): *'+\
300162306a36Sopenharmony_ci		'(?P<msg>.*)'
300262306a36Sopenharmony_ci	machinesuspend = 'machine_suspend\[.*'
300362306a36Sopenharmony_ci	multiproclist = dict()
300462306a36Sopenharmony_ci	multiproctime = 0.0
300562306a36Sopenharmony_ci	multiproccnt = 0
300662306a36Sopenharmony_ci	def __init__(self):
300762306a36Sopenharmony_ci		self.stamp = ''
300862306a36Sopenharmony_ci		self.sysinfo = ''
300962306a36Sopenharmony_ci		self.cmdline = ''
301062306a36Sopenharmony_ci		self.testerror = []
301162306a36Sopenharmony_ci		self.turbostat = []
301262306a36Sopenharmony_ci		self.wifi = []
301362306a36Sopenharmony_ci		self.fwdata = []
301462306a36Sopenharmony_ci		self.ftrace_line_fmt = self.ftrace_line_fmt_nop
301562306a36Sopenharmony_ci		self.cgformat = False
301662306a36Sopenharmony_ci		self.data = 0
301762306a36Sopenharmony_ci		self.ktemp = dict()
301862306a36Sopenharmony_ci	def setTracerType(self, tracer):
301962306a36Sopenharmony_ci		if(tracer == 'function_graph'):
302062306a36Sopenharmony_ci			self.cgformat = True
302162306a36Sopenharmony_ci			self.ftrace_line_fmt = self.ftrace_line_fmt_fg
302262306a36Sopenharmony_ci		elif(tracer == 'nop'):
302362306a36Sopenharmony_ci			self.ftrace_line_fmt = self.ftrace_line_fmt_nop
302462306a36Sopenharmony_ci		else:
302562306a36Sopenharmony_ci			doError('Invalid tracer format: [%s]' % tracer)
302662306a36Sopenharmony_ci	def stampInfo(self, line, sv):
302762306a36Sopenharmony_ci		if re.match(self.stampfmt, line):
302862306a36Sopenharmony_ci			self.stamp = line
302962306a36Sopenharmony_ci			return True
303062306a36Sopenharmony_ci		elif re.match(self.sysinfofmt, line):
303162306a36Sopenharmony_ci			self.sysinfo = line
303262306a36Sopenharmony_ci			return True
303362306a36Sopenharmony_ci		elif re.match(self.tstatfmt, line):
303462306a36Sopenharmony_ci			self.turbostat.append(line)
303562306a36Sopenharmony_ci			return True
303662306a36Sopenharmony_ci		elif re.match(self.wififmt, line):
303762306a36Sopenharmony_ci			self.wifi.append(line)
303862306a36Sopenharmony_ci			return True
303962306a36Sopenharmony_ci		elif re.match(self.testerrfmt, line):
304062306a36Sopenharmony_ci			self.testerror.append(line)
304162306a36Sopenharmony_ci			return True
304262306a36Sopenharmony_ci		elif re.match(self.firmwarefmt, line):
304362306a36Sopenharmony_ci			self.fwdata.append(line)
304462306a36Sopenharmony_ci			return True
304562306a36Sopenharmony_ci		elif(re.match(self.devpropfmt, line)):
304662306a36Sopenharmony_ci			self.parseDevprops(line, sv)
304762306a36Sopenharmony_ci			return True
304862306a36Sopenharmony_ci		elif(re.match(self.pinfofmt, line)):
304962306a36Sopenharmony_ci			self.parsePlatformInfo(line, sv)
305062306a36Sopenharmony_ci			return True
305162306a36Sopenharmony_ci		m = re.match(self.cmdlinefmt, line)
305262306a36Sopenharmony_ci		if m:
305362306a36Sopenharmony_ci			self.cmdline = m.group('cmd')
305462306a36Sopenharmony_ci			return True
305562306a36Sopenharmony_ci		m = re.match(self.tracertypefmt, line)
305662306a36Sopenharmony_ci		if(m):
305762306a36Sopenharmony_ci			self.setTracerType(m.group('t'))
305862306a36Sopenharmony_ci			return True
305962306a36Sopenharmony_ci		return False
306062306a36Sopenharmony_ci	def parseStamp(self, data, sv):
306162306a36Sopenharmony_ci		# global test data
306262306a36Sopenharmony_ci		m = re.match(self.stampfmt, self.stamp)
306362306a36Sopenharmony_ci		if not self.stamp or not m:
306462306a36Sopenharmony_ci			doError('data does not include the expected stamp')
306562306a36Sopenharmony_ci		data.stamp = {'time': '', 'host': '', 'mode': ''}
306662306a36Sopenharmony_ci		dt = datetime(int(m.group('y'))+2000, int(m.group('m')),
306762306a36Sopenharmony_ci			int(m.group('d')), int(m.group('H')), int(m.group('M')),
306862306a36Sopenharmony_ci			int(m.group('S')))
306962306a36Sopenharmony_ci		data.stamp['time'] = dt.strftime('%B %d %Y, %I:%M:%S %p')
307062306a36Sopenharmony_ci		data.stamp['host'] = m.group('host')
307162306a36Sopenharmony_ci		data.stamp['mode'] = m.group('mode')
307262306a36Sopenharmony_ci		data.stamp['kernel'] = m.group('kernel')
307362306a36Sopenharmony_ci		if re.match(self.sysinfofmt, self.sysinfo):
307462306a36Sopenharmony_ci			for f in self.sysinfo.split('|'):
307562306a36Sopenharmony_ci				if '#' in f:
307662306a36Sopenharmony_ci					continue
307762306a36Sopenharmony_ci				tmp = f.strip().split(':', 1)
307862306a36Sopenharmony_ci				key = tmp[0]
307962306a36Sopenharmony_ci				val = tmp[1]
308062306a36Sopenharmony_ci				data.stamp[key] = val
308162306a36Sopenharmony_ci		sv.hostname = data.stamp['host']
308262306a36Sopenharmony_ci		sv.suspendmode = data.stamp['mode']
308362306a36Sopenharmony_ci		if sv.suspendmode == 'freeze':
308462306a36Sopenharmony_ci			self.machinesuspend = 'timekeeping_freeze\[.*'
308562306a36Sopenharmony_ci		else:
308662306a36Sopenharmony_ci			self.machinesuspend = 'machine_suspend\[.*'
308762306a36Sopenharmony_ci		if sv.suspendmode == 'command' and sv.ftracefile != '':
308862306a36Sopenharmony_ci			modes = ['on', 'freeze', 'standby', 'mem', 'disk']
308962306a36Sopenharmony_ci			fp = sv.openlog(sv.ftracefile, 'r')
309062306a36Sopenharmony_ci			for line in fp:
309162306a36Sopenharmony_ci				m = re.match('.* machine_suspend\[(?P<mode>.*)\]', line)
309262306a36Sopenharmony_ci				if m and m.group('mode') in ['1', '2', '3', '4']:
309362306a36Sopenharmony_ci					sv.suspendmode = modes[int(m.group('mode'))]
309462306a36Sopenharmony_ci					data.stamp['mode'] = sv.suspendmode
309562306a36Sopenharmony_ci					break
309662306a36Sopenharmony_ci			fp.close()
309762306a36Sopenharmony_ci		sv.cmdline = self.cmdline
309862306a36Sopenharmony_ci		if not sv.stamp:
309962306a36Sopenharmony_ci			sv.stamp = data.stamp
310062306a36Sopenharmony_ci		# firmware data
310162306a36Sopenharmony_ci		if sv.suspendmode == 'mem' and len(self.fwdata) > data.testnumber:
310262306a36Sopenharmony_ci			m = re.match(self.firmwarefmt, self.fwdata[data.testnumber])
310362306a36Sopenharmony_ci			if m:
310462306a36Sopenharmony_ci				data.fwSuspend, data.fwResume = int(m.group('s')), int(m.group('r'))
310562306a36Sopenharmony_ci				if(data.fwSuspend > 0 or data.fwResume > 0):
310662306a36Sopenharmony_ci					data.fwValid = True
310762306a36Sopenharmony_ci		# turbostat data
310862306a36Sopenharmony_ci		if len(self.turbostat) > data.testnumber:
310962306a36Sopenharmony_ci			m = re.match(self.tstatfmt, self.turbostat[data.testnumber])
311062306a36Sopenharmony_ci			if m:
311162306a36Sopenharmony_ci				data.turbostat = m.group('t')
311262306a36Sopenharmony_ci		# wifi data
311362306a36Sopenharmony_ci		if len(self.wifi) > data.testnumber:
311462306a36Sopenharmony_ci			m = re.match(self.wififmt, self.wifi[data.testnumber])
311562306a36Sopenharmony_ci			if m:
311662306a36Sopenharmony_ci				data.wifi = {'dev': m.group('d'), 'stat': m.group('s'),
311762306a36Sopenharmony_ci					'time': float(m.group('t'))}
311862306a36Sopenharmony_ci				data.stamp['wifi'] = m.group('d')
311962306a36Sopenharmony_ci		# sleep mode enter errors
312062306a36Sopenharmony_ci		if len(self.testerror) > data.testnumber:
312162306a36Sopenharmony_ci			m = re.match(self.testerrfmt, self.testerror[data.testnumber])
312262306a36Sopenharmony_ci			if m:
312362306a36Sopenharmony_ci				data.enterfail = m.group('e')
312462306a36Sopenharmony_ci	def devprops(self, data):
312562306a36Sopenharmony_ci		props = dict()
312662306a36Sopenharmony_ci		devlist = data.split(';')
312762306a36Sopenharmony_ci		for dev in devlist:
312862306a36Sopenharmony_ci			f = dev.split(',')
312962306a36Sopenharmony_ci			if len(f) < 3:
313062306a36Sopenharmony_ci				continue
313162306a36Sopenharmony_ci			dev = f[0]
313262306a36Sopenharmony_ci			props[dev] = DevProps()
313362306a36Sopenharmony_ci			props[dev].altname = f[1]
313462306a36Sopenharmony_ci			if int(f[2]):
313562306a36Sopenharmony_ci				props[dev].isasync = True
313662306a36Sopenharmony_ci			else:
313762306a36Sopenharmony_ci				props[dev].isasync = False
313862306a36Sopenharmony_ci		return props
313962306a36Sopenharmony_ci	def parseDevprops(self, line, sv):
314062306a36Sopenharmony_ci		idx = line.index(': ') + 2
314162306a36Sopenharmony_ci		if idx >= len(line):
314262306a36Sopenharmony_ci			return
314362306a36Sopenharmony_ci		props = self.devprops(line[idx:])
314462306a36Sopenharmony_ci		if sv.suspendmode == 'command' and 'testcommandstring' in props:
314562306a36Sopenharmony_ci			sv.testcommand = props['testcommandstring'].altname
314662306a36Sopenharmony_ci		sv.devprops = props
314762306a36Sopenharmony_ci	def parsePlatformInfo(self, line, sv):
314862306a36Sopenharmony_ci		m = re.match(self.pinfofmt, line)
314962306a36Sopenharmony_ci		if not m:
315062306a36Sopenharmony_ci			return
315162306a36Sopenharmony_ci		name, info = m.group('val'), m.group('info')
315262306a36Sopenharmony_ci		if name == 'devinfo':
315362306a36Sopenharmony_ci			sv.devprops = self.devprops(sv.b64unzip(info))
315462306a36Sopenharmony_ci			return
315562306a36Sopenharmony_ci		elif name == 'testcmd':
315662306a36Sopenharmony_ci			sv.testcommand = info
315762306a36Sopenharmony_ci			return
315862306a36Sopenharmony_ci		field = info.split('|')
315962306a36Sopenharmony_ci		if len(field) < 2:
316062306a36Sopenharmony_ci			return
316162306a36Sopenharmony_ci		cmdline = field[0].strip()
316262306a36Sopenharmony_ci		output = sv.b64unzip(field[1].strip())
316362306a36Sopenharmony_ci		sv.platinfo.append([name, cmdline, output])
316462306a36Sopenharmony_ci
316562306a36Sopenharmony_ci# Class: TestRun
316662306a36Sopenharmony_ci# Description:
316762306a36Sopenharmony_ci#	 A container for a suspend/resume test run. This is necessary as
316862306a36Sopenharmony_ci#	 there could be more than one, and they need to be separate.
316962306a36Sopenharmony_ciclass TestRun:
317062306a36Sopenharmony_ci	def __init__(self, dataobj):
317162306a36Sopenharmony_ci		self.data = dataobj
317262306a36Sopenharmony_ci		self.ftemp = dict()
317362306a36Sopenharmony_ci		self.ttemp = dict()
317462306a36Sopenharmony_ci
317562306a36Sopenharmony_ciclass ProcessMonitor:
317662306a36Sopenharmony_ci	maxchars = 512
317762306a36Sopenharmony_ci	def __init__(self):
317862306a36Sopenharmony_ci		self.proclist = dict()
317962306a36Sopenharmony_ci		self.running = False
318062306a36Sopenharmony_ci	def procstat(self):
318162306a36Sopenharmony_ci		c = ['cat /proc/[1-9]*/stat 2>/dev/null']
318262306a36Sopenharmony_ci		process = Popen(c, shell=True, stdout=PIPE)
318362306a36Sopenharmony_ci		running = dict()
318462306a36Sopenharmony_ci		for line in process.stdout:
318562306a36Sopenharmony_ci			data = ascii(line).split()
318662306a36Sopenharmony_ci			pid = data[0]
318762306a36Sopenharmony_ci			name = re.sub('[()]', '', data[1])
318862306a36Sopenharmony_ci			user = int(data[13])
318962306a36Sopenharmony_ci			kern = int(data[14])
319062306a36Sopenharmony_ci			kjiff = ujiff = 0
319162306a36Sopenharmony_ci			if pid not in self.proclist:
319262306a36Sopenharmony_ci				self.proclist[pid] = {'name' : name, 'user' : user, 'kern' : kern}
319362306a36Sopenharmony_ci			else:
319462306a36Sopenharmony_ci				val = self.proclist[pid]
319562306a36Sopenharmony_ci				ujiff = user - val['user']
319662306a36Sopenharmony_ci				kjiff = kern - val['kern']
319762306a36Sopenharmony_ci				val['user'] = user
319862306a36Sopenharmony_ci				val['kern'] = kern
319962306a36Sopenharmony_ci			if ujiff > 0 or kjiff > 0:
320062306a36Sopenharmony_ci				running[pid] = ujiff + kjiff
320162306a36Sopenharmony_ci		process.wait()
320262306a36Sopenharmony_ci		out = ['']
320362306a36Sopenharmony_ci		for pid in running:
320462306a36Sopenharmony_ci			jiffies = running[pid]
320562306a36Sopenharmony_ci			val = self.proclist[pid]
320662306a36Sopenharmony_ci			if len(out[-1]) > self.maxchars:
320762306a36Sopenharmony_ci				out.append('')
320862306a36Sopenharmony_ci			elif len(out[-1]) > 0:
320962306a36Sopenharmony_ci				out[-1] += ','
321062306a36Sopenharmony_ci			out[-1] += '%s-%s %d' % (val['name'], pid, jiffies)
321162306a36Sopenharmony_ci		if len(out) > 1:
321262306a36Sopenharmony_ci			for line in out:
321362306a36Sopenharmony_ci				sysvals.fsetVal('ps - @%d|%s' % (len(out), line), 'trace_marker')
321462306a36Sopenharmony_ci		else:
321562306a36Sopenharmony_ci			sysvals.fsetVal('ps - %s' % out[0], 'trace_marker')
321662306a36Sopenharmony_ci	def processMonitor(self, tid):
321762306a36Sopenharmony_ci		while self.running:
321862306a36Sopenharmony_ci			self.procstat()
321962306a36Sopenharmony_ci	def start(self):
322062306a36Sopenharmony_ci		self.thread = Thread(target=self.processMonitor, args=(0,))
322162306a36Sopenharmony_ci		self.running = True
322262306a36Sopenharmony_ci		self.thread.start()
322362306a36Sopenharmony_ci	def stop(self):
322462306a36Sopenharmony_ci		self.running = False
322562306a36Sopenharmony_ci
322662306a36Sopenharmony_ci# ----------------- FUNCTIONS --------------------
322762306a36Sopenharmony_ci
322862306a36Sopenharmony_ci# Function: doesTraceLogHaveTraceEvents
322962306a36Sopenharmony_ci# Description:
323062306a36Sopenharmony_ci#	 Quickly determine if the ftrace log has all of the trace events,
323162306a36Sopenharmony_ci#	 markers, and/or kprobes required for primary parsing.
323262306a36Sopenharmony_cidef doesTraceLogHaveTraceEvents():
323362306a36Sopenharmony_ci	kpcheck = ['_cal: (', '_ret: (']
323462306a36Sopenharmony_ci	techeck = ['suspend_resume', 'device_pm_callback', 'tracing_mark_write']
323562306a36Sopenharmony_ci	tmcheck = ['SUSPEND START', 'RESUME COMPLETE']
323662306a36Sopenharmony_ci	sysvals.usekprobes = False
323762306a36Sopenharmony_ci	fp = sysvals.openlog(sysvals.ftracefile, 'r')
323862306a36Sopenharmony_ci	for line in fp:
323962306a36Sopenharmony_ci		# check for kprobes
324062306a36Sopenharmony_ci		if not sysvals.usekprobes:
324162306a36Sopenharmony_ci			for i in kpcheck:
324262306a36Sopenharmony_ci				if i in line:
324362306a36Sopenharmony_ci					sysvals.usekprobes = True
324462306a36Sopenharmony_ci		# check for all necessary trace events
324562306a36Sopenharmony_ci		check = techeck[:]
324662306a36Sopenharmony_ci		for i in techeck:
324762306a36Sopenharmony_ci			if i in line:
324862306a36Sopenharmony_ci				check.remove(i)
324962306a36Sopenharmony_ci		techeck = check
325062306a36Sopenharmony_ci		# check for all necessary trace markers
325162306a36Sopenharmony_ci		check = tmcheck[:]
325262306a36Sopenharmony_ci		for i in tmcheck:
325362306a36Sopenharmony_ci			if i in line:
325462306a36Sopenharmony_ci				check.remove(i)
325562306a36Sopenharmony_ci		tmcheck = check
325662306a36Sopenharmony_ci	fp.close()
325762306a36Sopenharmony_ci	sysvals.usetraceevents = True if len(techeck) < 3 else False
325862306a36Sopenharmony_ci	sysvals.usetracemarkers = True if len(tmcheck) == 0 else False
325962306a36Sopenharmony_ci
326062306a36Sopenharmony_ci# Function: appendIncompleteTraceLog
326162306a36Sopenharmony_ci# Description:
326262306a36Sopenharmony_ci#	 Adds callgraph data which lacks trace event data. This is only
326362306a36Sopenharmony_ci#	 for timelines generated from 3.15 or older
326462306a36Sopenharmony_ci# Arguments:
326562306a36Sopenharmony_ci#	 testruns: the array of Data objects obtained from parseKernelLog
326662306a36Sopenharmony_cidef appendIncompleteTraceLog(testruns):
326762306a36Sopenharmony_ci	# create TestRun vessels for ftrace parsing
326862306a36Sopenharmony_ci	testcnt = len(testruns)
326962306a36Sopenharmony_ci	testidx = 0
327062306a36Sopenharmony_ci	testrun = []
327162306a36Sopenharmony_ci	for data in testruns:
327262306a36Sopenharmony_ci		testrun.append(TestRun(data))
327362306a36Sopenharmony_ci
327462306a36Sopenharmony_ci	# extract the callgraph and traceevent data
327562306a36Sopenharmony_ci	sysvals.vprint('Analyzing the ftrace data (%s)...' % \
327662306a36Sopenharmony_ci		os.path.basename(sysvals.ftracefile))
327762306a36Sopenharmony_ci	tp = TestProps()
327862306a36Sopenharmony_ci	tf = sysvals.openlog(sysvals.ftracefile, 'r')
327962306a36Sopenharmony_ci	data = 0
328062306a36Sopenharmony_ci	for line in tf:
328162306a36Sopenharmony_ci		# remove any latent carriage returns
328262306a36Sopenharmony_ci		line = line.replace('\r\n', '')
328362306a36Sopenharmony_ci		if tp.stampInfo(line, sysvals):
328462306a36Sopenharmony_ci			continue
328562306a36Sopenharmony_ci		# parse only valid lines, if this is not one move on
328662306a36Sopenharmony_ci		m = re.match(tp.ftrace_line_fmt, line)
328762306a36Sopenharmony_ci		if(not m):
328862306a36Sopenharmony_ci			continue
328962306a36Sopenharmony_ci		# gather the basic message data from the line
329062306a36Sopenharmony_ci		m_time = m.group('time')
329162306a36Sopenharmony_ci		m_pid = m.group('pid')
329262306a36Sopenharmony_ci		m_msg = m.group('msg')
329362306a36Sopenharmony_ci		if(tp.cgformat):
329462306a36Sopenharmony_ci			m_param3 = m.group('dur')
329562306a36Sopenharmony_ci		else:
329662306a36Sopenharmony_ci			m_param3 = 'traceevent'
329762306a36Sopenharmony_ci		if(m_time and m_pid and m_msg):
329862306a36Sopenharmony_ci			t = FTraceLine(m_time, m_msg, m_param3)
329962306a36Sopenharmony_ci			pid = int(m_pid)
330062306a36Sopenharmony_ci		else:
330162306a36Sopenharmony_ci			continue
330262306a36Sopenharmony_ci		# the line should be a call, return, or event
330362306a36Sopenharmony_ci		if(not t.fcall and not t.freturn and not t.fevent):
330462306a36Sopenharmony_ci			continue
330562306a36Sopenharmony_ci		# look for the suspend start marker
330662306a36Sopenharmony_ci		if(t.startMarker()):
330762306a36Sopenharmony_ci			data = testrun[testidx].data
330862306a36Sopenharmony_ci			tp.parseStamp(data, sysvals)
330962306a36Sopenharmony_ci			data.setStart(t.time, t.name)
331062306a36Sopenharmony_ci			continue
331162306a36Sopenharmony_ci		if(not data):
331262306a36Sopenharmony_ci			continue
331362306a36Sopenharmony_ci		# find the end of resume
331462306a36Sopenharmony_ci		if(t.endMarker()):
331562306a36Sopenharmony_ci			data.setEnd(t.time, t.name)
331662306a36Sopenharmony_ci			testidx += 1
331762306a36Sopenharmony_ci			if(testidx >= testcnt):
331862306a36Sopenharmony_ci				break
331962306a36Sopenharmony_ci			continue
332062306a36Sopenharmony_ci		# trace event processing
332162306a36Sopenharmony_ci		if(t.fevent):
332262306a36Sopenharmony_ci			continue
332362306a36Sopenharmony_ci		# call/return processing
332462306a36Sopenharmony_ci		elif sysvals.usecallgraph:
332562306a36Sopenharmony_ci			# create a callgraph object for the data
332662306a36Sopenharmony_ci			if(pid not in testrun[testidx].ftemp):
332762306a36Sopenharmony_ci				testrun[testidx].ftemp[pid] = []
332862306a36Sopenharmony_ci				testrun[testidx].ftemp[pid].append(FTraceCallGraph(pid, sysvals))
332962306a36Sopenharmony_ci			# when the call is finished, see which device matches it
333062306a36Sopenharmony_ci			cg = testrun[testidx].ftemp[pid][-1]
333162306a36Sopenharmony_ci			res = cg.addLine(t)
333262306a36Sopenharmony_ci			if(res != 0):
333362306a36Sopenharmony_ci				testrun[testidx].ftemp[pid].append(FTraceCallGraph(pid, sysvals))
333462306a36Sopenharmony_ci			if(res == -1):
333562306a36Sopenharmony_ci				testrun[testidx].ftemp[pid][-1].addLine(t)
333662306a36Sopenharmony_ci	tf.close()
333762306a36Sopenharmony_ci
333862306a36Sopenharmony_ci	for test in testrun:
333962306a36Sopenharmony_ci		# add the callgraph data to the device hierarchy
334062306a36Sopenharmony_ci		for pid in test.ftemp:
334162306a36Sopenharmony_ci			for cg in test.ftemp[pid]:
334262306a36Sopenharmony_ci				if len(cg.list) < 1 or cg.invalid or (cg.end - cg.start == 0):
334362306a36Sopenharmony_ci					continue
334462306a36Sopenharmony_ci				if(not cg.postProcess()):
334562306a36Sopenharmony_ci					id = 'task %s cpu %s' % (pid, m.group('cpu'))
334662306a36Sopenharmony_ci					sysvals.vprint('Sanity check failed for '+\
334762306a36Sopenharmony_ci						id+', ignoring this callback')
334862306a36Sopenharmony_ci					continue
334962306a36Sopenharmony_ci				callstart = cg.start
335062306a36Sopenharmony_ci				callend = cg.end
335162306a36Sopenharmony_ci				for p in test.data.sortedPhases():
335262306a36Sopenharmony_ci					if(test.data.dmesg[p]['start'] <= callstart and
335362306a36Sopenharmony_ci						callstart <= test.data.dmesg[p]['end']):
335462306a36Sopenharmony_ci						list = test.data.dmesg[p]['list']
335562306a36Sopenharmony_ci						for devname in list:
335662306a36Sopenharmony_ci							dev = list[devname]
335762306a36Sopenharmony_ci							if(pid == dev['pid'] and
335862306a36Sopenharmony_ci								callstart <= dev['start'] and
335962306a36Sopenharmony_ci								callend >= dev['end']):
336062306a36Sopenharmony_ci								dev['ftrace'] = cg
336162306a36Sopenharmony_ci						break
336262306a36Sopenharmony_ci
336362306a36Sopenharmony_ci# Function: loadTraceLog
336462306a36Sopenharmony_ci# Description:
336562306a36Sopenharmony_ci#	 load the ftrace file into memory and fix up any ordering issues
336662306a36Sopenharmony_ci# Output:
336762306a36Sopenharmony_ci#	 TestProps instance and an array of lines in proper order
336862306a36Sopenharmony_cidef loadTraceLog():
336962306a36Sopenharmony_ci	tp, data, lines, trace = TestProps(), dict(), [], []
337062306a36Sopenharmony_ci	tf = sysvals.openlog(sysvals.ftracefile, 'r')
337162306a36Sopenharmony_ci	for line in tf:
337262306a36Sopenharmony_ci		# remove any latent carriage returns
337362306a36Sopenharmony_ci		line = line.replace('\r\n', '')
337462306a36Sopenharmony_ci		if tp.stampInfo(line, sysvals):
337562306a36Sopenharmony_ci			continue
337662306a36Sopenharmony_ci		# ignore all other commented lines
337762306a36Sopenharmony_ci		if line[0] == '#':
337862306a36Sopenharmony_ci			continue
337962306a36Sopenharmony_ci		# ftrace line: parse only valid lines
338062306a36Sopenharmony_ci		m = re.match(tp.ftrace_line_fmt, line)
338162306a36Sopenharmony_ci		if(not m):
338262306a36Sopenharmony_ci			continue
338362306a36Sopenharmony_ci		dur = m.group('dur') if tp.cgformat else 'traceevent'
338462306a36Sopenharmony_ci		info = (m.group('time'), m.group('proc'), m.group('pid'),
338562306a36Sopenharmony_ci			m.group('msg'), dur)
338662306a36Sopenharmony_ci		# group the data by timestamp
338762306a36Sopenharmony_ci		t = float(info[0])
338862306a36Sopenharmony_ci		if t in data:
338962306a36Sopenharmony_ci			data[t].append(info)
339062306a36Sopenharmony_ci		else:
339162306a36Sopenharmony_ci			data[t] = [info]
339262306a36Sopenharmony_ci		# we only care about trace event ordering
339362306a36Sopenharmony_ci		if (info[3].startswith('suspend_resume:') or \
339462306a36Sopenharmony_ci			info[3].startswith('tracing_mark_write:')) and t not in trace:
339562306a36Sopenharmony_ci				trace.append(t)
339662306a36Sopenharmony_ci	tf.close()
339762306a36Sopenharmony_ci	for t in sorted(data):
339862306a36Sopenharmony_ci		first, last, blk = [], [], data[t]
339962306a36Sopenharmony_ci		if len(blk) > 1 and t in trace:
340062306a36Sopenharmony_ci			# move certain lines to the start or end of a timestamp block
340162306a36Sopenharmony_ci			for i in range(len(blk)):
340262306a36Sopenharmony_ci				if 'SUSPEND START' in blk[i][3]:
340362306a36Sopenharmony_ci					first.append(i)
340462306a36Sopenharmony_ci				elif re.match('.* timekeeping_freeze.*begin', blk[i][3]):
340562306a36Sopenharmony_ci					last.append(i)
340662306a36Sopenharmony_ci				elif re.match('.* timekeeping_freeze.*end', blk[i][3]):
340762306a36Sopenharmony_ci					first.append(i)
340862306a36Sopenharmony_ci				elif 'RESUME COMPLETE' in blk[i][3]:
340962306a36Sopenharmony_ci					last.append(i)
341062306a36Sopenharmony_ci			if len(first) == 1 and len(last) == 0:
341162306a36Sopenharmony_ci				blk.insert(0, blk.pop(first[0]))
341262306a36Sopenharmony_ci			elif len(last) == 1 and len(first) == 0:
341362306a36Sopenharmony_ci				blk.append(blk.pop(last[0]))
341462306a36Sopenharmony_ci		for info in blk:
341562306a36Sopenharmony_ci			lines.append(info)
341662306a36Sopenharmony_ci	return (tp, lines)
341762306a36Sopenharmony_ci
341862306a36Sopenharmony_ci# Function: parseTraceLog
341962306a36Sopenharmony_ci# Description:
342062306a36Sopenharmony_ci#	 Analyze an ftrace log output file generated from this app during
342162306a36Sopenharmony_ci#	 the execution phase. Used when the ftrace log is the primary data source
342262306a36Sopenharmony_ci#	 and includes the suspend_resume and device_pm_callback trace events
342362306a36Sopenharmony_ci#	 The ftrace filename is taken from sysvals
342462306a36Sopenharmony_ci# Output:
342562306a36Sopenharmony_ci#	 An array of Data objects
342662306a36Sopenharmony_cidef parseTraceLog(live=False):
342762306a36Sopenharmony_ci	sysvals.vprint('Analyzing the ftrace data (%s)...' % \
342862306a36Sopenharmony_ci		os.path.basename(sysvals.ftracefile))
342962306a36Sopenharmony_ci	if(os.path.exists(sysvals.ftracefile) == False):
343062306a36Sopenharmony_ci		doError('%s does not exist' % sysvals.ftracefile)
343162306a36Sopenharmony_ci	if not live:
343262306a36Sopenharmony_ci		sysvals.setupAllKprobes()
343362306a36Sopenharmony_ci	ksuscalls = ['ksys_sync', 'pm_prepare_console']
343462306a36Sopenharmony_ci	krescalls = ['pm_restore_console']
343562306a36Sopenharmony_ci	tracewatch = ['irq_wakeup']
343662306a36Sopenharmony_ci	if sysvals.usekprobes:
343762306a36Sopenharmony_ci		tracewatch += ['sync_filesystems', 'freeze_processes', 'syscore_suspend',
343862306a36Sopenharmony_ci			'syscore_resume', 'resume_console', 'thaw_processes', 'CPU_ON',
343962306a36Sopenharmony_ci			'CPU_OFF', 'acpi_suspend']
344062306a36Sopenharmony_ci
344162306a36Sopenharmony_ci	# extract the callgraph and traceevent data
344262306a36Sopenharmony_ci	s2idle_enter = hwsus = False
344362306a36Sopenharmony_ci	testruns, testdata = [], []
344462306a36Sopenharmony_ci	testrun, data, limbo = 0, 0, True
344562306a36Sopenharmony_ci	phase = 'suspend_prepare'
344662306a36Sopenharmony_ci	tp, tf = loadTraceLog()
344762306a36Sopenharmony_ci	for m_time, m_proc, m_pid, m_msg, m_param3 in tf:
344862306a36Sopenharmony_ci		# gather the basic message data from the line
344962306a36Sopenharmony_ci		if(m_time and m_pid and m_msg):
345062306a36Sopenharmony_ci			t = FTraceLine(m_time, m_msg, m_param3)
345162306a36Sopenharmony_ci			pid = int(m_pid)
345262306a36Sopenharmony_ci		else:
345362306a36Sopenharmony_ci			continue
345462306a36Sopenharmony_ci		# the line should be a call, return, or event
345562306a36Sopenharmony_ci		if(not t.fcall and not t.freturn and not t.fevent):
345662306a36Sopenharmony_ci			continue
345762306a36Sopenharmony_ci		# find the start of suspend
345862306a36Sopenharmony_ci		if(t.startMarker()):
345962306a36Sopenharmony_ci			data, limbo = Data(len(testdata)), False
346062306a36Sopenharmony_ci			testdata.append(data)
346162306a36Sopenharmony_ci			testrun = TestRun(data)
346262306a36Sopenharmony_ci			testruns.append(testrun)
346362306a36Sopenharmony_ci			tp.parseStamp(data, sysvals)
346462306a36Sopenharmony_ci			data.setStart(t.time, t.name)
346562306a36Sopenharmony_ci			data.first_suspend_prepare = True
346662306a36Sopenharmony_ci			phase = data.setPhase('suspend_prepare', t.time, True)
346762306a36Sopenharmony_ci			continue
346862306a36Sopenharmony_ci		if(not data or limbo):
346962306a36Sopenharmony_ci			continue
347062306a36Sopenharmony_ci		# process cpu exec line
347162306a36Sopenharmony_ci		if t.type == 'tracing_mark_write':
347262306a36Sopenharmony_ci			if t.name == 'CMD COMPLETE' and data.tKernRes == 0:
347362306a36Sopenharmony_ci				data.tKernRes = t.time
347462306a36Sopenharmony_ci			m = re.match(tp.procexecfmt, t.name)
347562306a36Sopenharmony_ci			if(m):
347662306a36Sopenharmony_ci				parts, msg = 1, m.group('ps')
347762306a36Sopenharmony_ci				m = re.match(tp.procmultifmt, msg)
347862306a36Sopenharmony_ci				if(m):
347962306a36Sopenharmony_ci					parts, msg = int(m.group('n')), m.group('ps')
348062306a36Sopenharmony_ci					if tp.multiproccnt == 0:
348162306a36Sopenharmony_ci						tp.multiproctime = t.time
348262306a36Sopenharmony_ci						tp.multiproclist = dict()
348362306a36Sopenharmony_ci					proclist = tp.multiproclist
348462306a36Sopenharmony_ci					tp.multiproccnt += 1
348562306a36Sopenharmony_ci				else:
348662306a36Sopenharmony_ci					proclist = dict()
348762306a36Sopenharmony_ci					tp.multiproccnt = 0
348862306a36Sopenharmony_ci				for ps in msg.split(','):
348962306a36Sopenharmony_ci					val = ps.split()
349062306a36Sopenharmony_ci					if not val or len(val) != 2:
349162306a36Sopenharmony_ci						continue
349262306a36Sopenharmony_ci					name = val[0].replace('--', '-')
349362306a36Sopenharmony_ci					proclist[name] = int(val[1])
349462306a36Sopenharmony_ci				if parts == 1:
349562306a36Sopenharmony_ci					data.pstl[t.time] = proclist
349662306a36Sopenharmony_ci				elif parts == tp.multiproccnt:
349762306a36Sopenharmony_ci					data.pstl[tp.multiproctime] = proclist
349862306a36Sopenharmony_ci					tp.multiproccnt = 0
349962306a36Sopenharmony_ci				continue
350062306a36Sopenharmony_ci		# find the end of resume
350162306a36Sopenharmony_ci		if(t.endMarker()):
350262306a36Sopenharmony_ci			if data.tKernRes == 0:
350362306a36Sopenharmony_ci				data.tKernRes = t.time
350462306a36Sopenharmony_ci			data.handleEndMarker(t.time, t.name)
350562306a36Sopenharmony_ci			if(not sysvals.usetracemarkers):
350662306a36Sopenharmony_ci				# no trace markers? then quit and be sure to finish recording
350762306a36Sopenharmony_ci				# the event we used to trigger resume end
350862306a36Sopenharmony_ci				if('thaw_processes' in testrun.ttemp and len(testrun.ttemp['thaw_processes']) > 0):
350962306a36Sopenharmony_ci					# if an entry exists, assume this is its end
351062306a36Sopenharmony_ci					testrun.ttemp['thaw_processes'][-1]['end'] = t.time
351162306a36Sopenharmony_ci			limbo = True
351262306a36Sopenharmony_ci			continue
351362306a36Sopenharmony_ci		# trace event processing
351462306a36Sopenharmony_ci		if(t.fevent):
351562306a36Sopenharmony_ci			if(t.type == 'suspend_resume'):
351662306a36Sopenharmony_ci				# suspend_resume trace events have two types, begin and end
351762306a36Sopenharmony_ci				if(re.match('(?P<name>.*) begin$', t.name)):
351862306a36Sopenharmony_ci					isbegin = True
351962306a36Sopenharmony_ci				elif(re.match('(?P<name>.*) end$', t.name)):
352062306a36Sopenharmony_ci					isbegin = False
352162306a36Sopenharmony_ci				else:
352262306a36Sopenharmony_ci					continue
352362306a36Sopenharmony_ci				if '[' in t.name:
352462306a36Sopenharmony_ci					m = re.match('(?P<name>.*)\[.*', t.name)
352562306a36Sopenharmony_ci				else:
352662306a36Sopenharmony_ci					m = re.match('(?P<name>.*) .*', t.name)
352762306a36Sopenharmony_ci				name = m.group('name')
352862306a36Sopenharmony_ci				# ignore these events
352962306a36Sopenharmony_ci				if(name.split('[')[0] in tracewatch):
353062306a36Sopenharmony_ci					continue
353162306a36Sopenharmony_ci				# -- phase changes --
353262306a36Sopenharmony_ci				# start of kernel suspend
353362306a36Sopenharmony_ci				if(re.match('suspend_enter\[.*', t.name)):
353462306a36Sopenharmony_ci					if(isbegin and data.tKernSus == 0):
353562306a36Sopenharmony_ci						data.tKernSus = t.time
353662306a36Sopenharmony_ci					continue
353762306a36Sopenharmony_ci				# suspend_prepare start
353862306a36Sopenharmony_ci				elif(re.match('dpm_prepare\[.*', t.name)):
353962306a36Sopenharmony_ci					if isbegin and data.first_suspend_prepare:
354062306a36Sopenharmony_ci						data.first_suspend_prepare = False
354162306a36Sopenharmony_ci						if data.tKernSus == 0:
354262306a36Sopenharmony_ci							data.tKernSus = t.time
354362306a36Sopenharmony_ci						continue
354462306a36Sopenharmony_ci					phase = data.setPhase('suspend_prepare', t.time, isbegin)
354562306a36Sopenharmony_ci					continue
354662306a36Sopenharmony_ci				# suspend start
354762306a36Sopenharmony_ci				elif(re.match('dpm_suspend\[.*', t.name)):
354862306a36Sopenharmony_ci					phase = data.setPhase('suspend', t.time, isbegin)
354962306a36Sopenharmony_ci					continue
355062306a36Sopenharmony_ci				# suspend_late start
355162306a36Sopenharmony_ci				elif(re.match('dpm_suspend_late\[.*', t.name)):
355262306a36Sopenharmony_ci					phase = data.setPhase('suspend_late', t.time, isbegin)
355362306a36Sopenharmony_ci					continue
355462306a36Sopenharmony_ci				# suspend_noirq start
355562306a36Sopenharmony_ci				elif(re.match('dpm_suspend_noirq\[.*', t.name)):
355662306a36Sopenharmony_ci					phase = data.setPhase('suspend_noirq', t.time, isbegin)
355762306a36Sopenharmony_ci					continue
355862306a36Sopenharmony_ci				# suspend_machine/resume_machine
355962306a36Sopenharmony_ci				elif(re.match(tp.machinesuspend, t.name)):
356062306a36Sopenharmony_ci					lp = data.lastPhase()
356162306a36Sopenharmony_ci					if(isbegin):
356262306a36Sopenharmony_ci						hwsus = True
356362306a36Sopenharmony_ci						if lp.startswith('resume_machine'):
356462306a36Sopenharmony_ci							# trim out s2idle loops, track time trying to freeze
356562306a36Sopenharmony_ci							llp = data.lastPhase(2)
356662306a36Sopenharmony_ci							if llp.startswith('suspend_machine'):
356762306a36Sopenharmony_ci								if 'waking' not in data.dmesg[llp]:
356862306a36Sopenharmony_ci									data.dmesg[llp]['waking'] = [0, 0.0]
356962306a36Sopenharmony_ci								data.dmesg[llp]['waking'][0] += 1
357062306a36Sopenharmony_ci								data.dmesg[llp]['waking'][1] += \
357162306a36Sopenharmony_ci									t.time - data.dmesg[lp]['start']
357262306a36Sopenharmony_ci							data.currphase = ''
357362306a36Sopenharmony_ci							del data.dmesg[lp]
357462306a36Sopenharmony_ci							continue
357562306a36Sopenharmony_ci						phase = data.setPhase('suspend_machine', data.dmesg[lp]['end'], True)
357662306a36Sopenharmony_ci						data.setPhase(phase, t.time, False)
357762306a36Sopenharmony_ci						if data.tSuspended == 0:
357862306a36Sopenharmony_ci							data.tSuspended = t.time
357962306a36Sopenharmony_ci					else:
358062306a36Sopenharmony_ci						if lp.startswith('resume_machine'):
358162306a36Sopenharmony_ci							data.dmesg[lp]['end'] = t.time
358262306a36Sopenharmony_ci							continue
358362306a36Sopenharmony_ci						phase = data.setPhase('resume_machine', t.time, True)
358462306a36Sopenharmony_ci						if(sysvals.suspendmode in ['mem', 'disk']):
358562306a36Sopenharmony_ci							susp = phase.replace('resume', 'suspend')
358662306a36Sopenharmony_ci							if susp in data.dmesg:
358762306a36Sopenharmony_ci								data.dmesg[susp]['end'] = t.time
358862306a36Sopenharmony_ci							data.tSuspended = t.time
358962306a36Sopenharmony_ci						data.tResumed = t.time
359062306a36Sopenharmony_ci					continue
359162306a36Sopenharmony_ci				# resume_noirq start
359262306a36Sopenharmony_ci				elif(re.match('dpm_resume_noirq\[.*', t.name)):
359362306a36Sopenharmony_ci					phase = data.setPhase('resume_noirq', t.time, isbegin)
359462306a36Sopenharmony_ci					continue
359562306a36Sopenharmony_ci				# resume_early start
359662306a36Sopenharmony_ci				elif(re.match('dpm_resume_early\[.*', t.name)):
359762306a36Sopenharmony_ci					phase = data.setPhase('resume_early', t.time, isbegin)
359862306a36Sopenharmony_ci					continue
359962306a36Sopenharmony_ci				# resume start
360062306a36Sopenharmony_ci				elif(re.match('dpm_resume\[.*', t.name)):
360162306a36Sopenharmony_ci					phase = data.setPhase('resume', t.time, isbegin)
360262306a36Sopenharmony_ci					continue
360362306a36Sopenharmony_ci				# resume complete start
360462306a36Sopenharmony_ci				elif(re.match('dpm_complete\[.*', t.name)):
360562306a36Sopenharmony_ci					phase = data.setPhase('resume_complete', t.time, isbegin)
360662306a36Sopenharmony_ci					continue
360762306a36Sopenharmony_ci				# skip trace events inside devices calls
360862306a36Sopenharmony_ci				if(not data.isTraceEventOutsideDeviceCalls(pid, t.time)):
360962306a36Sopenharmony_ci					continue
361062306a36Sopenharmony_ci				# global events (outside device calls) are graphed
361162306a36Sopenharmony_ci				if(name not in testrun.ttemp):
361262306a36Sopenharmony_ci					testrun.ttemp[name] = []
361362306a36Sopenharmony_ci				# special handling for s2idle_enter
361462306a36Sopenharmony_ci				if name == 'machine_suspend':
361562306a36Sopenharmony_ci					if hwsus:
361662306a36Sopenharmony_ci						s2idle_enter = hwsus = False
361762306a36Sopenharmony_ci					elif s2idle_enter and not isbegin:
361862306a36Sopenharmony_ci						if(len(testrun.ttemp[name]) > 0):
361962306a36Sopenharmony_ci							testrun.ttemp[name][-1]['end'] = t.time
362062306a36Sopenharmony_ci							testrun.ttemp[name][-1]['loop'] += 1
362162306a36Sopenharmony_ci					elif not s2idle_enter and isbegin:
362262306a36Sopenharmony_ci						s2idle_enter = True
362362306a36Sopenharmony_ci						testrun.ttemp[name].append({'begin': t.time,
362462306a36Sopenharmony_ci							'end': t.time, 'pid': pid, 'loop': 0})
362562306a36Sopenharmony_ci					continue
362662306a36Sopenharmony_ci				if(isbegin):
362762306a36Sopenharmony_ci					# create a new list entry
362862306a36Sopenharmony_ci					testrun.ttemp[name].append(\
362962306a36Sopenharmony_ci						{'begin': t.time, 'end': t.time, 'pid': pid})
363062306a36Sopenharmony_ci				else:
363162306a36Sopenharmony_ci					if(len(testrun.ttemp[name]) > 0):
363262306a36Sopenharmony_ci						# if an entry exists, assume this is its end
363362306a36Sopenharmony_ci						testrun.ttemp[name][-1]['end'] = t.time
363462306a36Sopenharmony_ci			# device callback start
363562306a36Sopenharmony_ci			elif(t.type == 'device_pm_callback_start'):
363662306a36Sopenharmony_ci				if phase not in data.dmesg:
363762306a36Sopenharmony_ci					continue
363862306a36Sopenharmony_ci				m = re.match('(?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*',\
363962306a36Sopenharmony_ci					t.name);
364062306a36Sopenharmony_ci				if(not m):
364162306a36Sopenharmony_ci					continue
364262306a36Sopenharmony_ci				drv = m.group('drv')
364362306a36Sopenharmony_ci				n = m.group('d')
364462306a36Sopenharmony_ci				p = m.group('p')
364562306a36Sopenharmony_ci				if(n and p):
364662306a36Sopenharmony_ci					data.newAction(phase, n, pid, p, t.time, -1, drv)
364762306a36Sopenharmony_ci					if pid not in data.devpids:
364862306a36Sopenharmony_ci						data.devpids.append(pid)
364962306a36Sopenharmony_ci			# device callback finish
365062306a36Sopenharmony_ci			elif(t.type == 'device_pm_callback_end'):
365162306a36Sopenharmony_ci				if phase not in data.dmesg:
365262306a36Sopenharmony_ci					continue
365362306a36Sopenharmony_ci				m = re.match('(?P<drv>.*) (?P<d>.*), err.*', t.name);
365462306a36Sopenharmony_ci				if(not m):
365562306a36Sopenharmony_ci					continue
365662306a36Sopenharmony_ci				n = m.group('d')
365762306a36Sopenharmony_ci				dev = data.findDevice(phase, n)
365862306a36Sopenharmony_ci				if dev:
365962306a36Sopenharmony_ci					dev['length'] = t.time - dev['start']
366062306a36Sopenharmony_ci					dev['end'] = t.time
366162306a36Sopenharmony_ci		# kprobe event processing
366262306a36Sopenharmony_ci		elif(t.fkprobe):
366362306a36Sopenharmony_ci			kprobename = t.type
366462306a36Sopenharmony_ci			kprobedata = t.name
366562306a36Sopenharmony_ci			key = (kprobename, pid)
366662306a36Sopenharmony_ci			# displayname is generated from kprobe data
366762306a36Sopenharmony_ci			displayname = ''
366862306a36Sopenharmony_ci			if(t.fcall):
366962306a36Sopenharmony_ci				displayname = sysvals.kprobeDisplayName(kprobename, kprobedata)
367062306a36Sopenharmony_ci				if not displayname:
367162306a36Sopenharmony_ci					continue
367262306a36Sopenharmony_ci				if(key not in tp.ktemp):
367362306a36Sopenharmony_ci					tp.ktemp[key] = []
367462306a36Sopenharmony_ci				tp.ktemp[key].append({
367562306a36Sopenharmony_ci					'pid': pid,
367662306a36Sopenharmony_ci					'begin': t.time,
367762306a36Sopenharmony_ci					'end': -1,
367862306a36Sopenharmony_ci					'name': displayname,
367962306a36Sopenharmony_ci					'cdata': kprobedata,
368062306a36Sopenharmony_ci					'proc': m_proc,
368162306a36Sopenharmony_ci				})
368262306a36Sopenharmony_ci				# start of kernel resume
368362306a36Sopenharmony_ci				if(data.tKernSus == 0 and phase == 'suspend_prepare' \
368462306a36Sopenharmony_ci					and kprobename in ksuscalls):
368562306a36Sopenharmony_ci					data.tKernSus = t.time
368662306a36Sopenharmony_ci			elif(t.freturn):
368762306a36Sopenharmony_ci				if(key not in tp.ktemp) or len(tp.ktemp[key]) < 1:
368862306a36Sopenharmony_ci					continue
368962306a36Sopenharmony_ci				e = next((x for x in reversed(tp.ktemp[key]) if x['end'] < 0), 0)
369062306a36Sopenharmony_ci				if not e:
369162306a36Sopenharmony_ci					continue
369262306a36Sopenharmony_ci				if (t.time - e['begin']) * 1000 < sysvals.mindevlen:
369362306a36Sopenharmony_ci					tp.ktemp[key].pop()
369462306a36Sopenharmony_ci					continue
369562306a36Sopenharmony_ci				e['end'] = t.time
369662306a36Sopenharmony_ci				e['rdata'] = kprobedata
369762306a36Sopenharmony_ci				# end of kernel resume
369862306a36Sopenharmony_ci				if(phase != 'suspend_prepare' and kprobename in krescalls):
369962306a36Sopenharmony_ci					if phase in data.dmesg:
370062306a36Sopenharmony_ci						data.dmesg[phase]['end'] = t.time
370162306a36Sopenharmony_ci					data.tKernRes = t.time
370262306a36Sopenharmony_ci
370362306a36Sopenharmony_ci		# callgraph processing
370462306a36Sopenharmony_ci		elif sysvals.usecallgraph:
370562306a36Sopenharmony_ci			# create a callgraph object for the data
370662306a36Sopenharmony_ci			key = (m_proc, pid)
370762306a36Sopenharmony_ci			if(key not in testrun.ftemp):
370862306a36Sopenharmony_ci				testrun.ftemp[key] = []
370962306a36Sopenharmony_ci				testrun.ftemp[key].append(FTraceCallGraph(pid, sysvals))
371062306a36Sopenharmony_ci			# when the call is finished, see which device matches it
371162306a36Sopenharmony_ci			cg = testrun.ftemp[key][-1]
371262306a36Sopenharmony_ci			res = cg.addLine(t)
371362306a36Sopenharmony_ci			if(res != 0):
371462306a36Sopenharmony_ci				testrun.ftemp[key].append(FTraceCallGraph(pid, sysvals))
371562306a36Sopenharmony_ci			if(res == -1):
371662306a36Sopenharmony_ci				testrun.ftemp[key][-1].addLine(t)
371762306a36Sopenharmony_ci	if len(testdata) < 1:
371862306a36Sopenharmony_ci		sysvals.vprint('WARNING: ftrace start marker is missing')
371962306a36Sopenharmony_ci	if data and not data.devicegroups:
372062306a36Sopenharmony_ci		sysvals.vprint('WARNING: ftrace end marker is missing')
372162306a36Sopenharmony_ci		data.handleEndMarker(t.time, t.name)
372262306a36Sopenharmony_ci
372362306a36Sopenharmony_ci	if sysvals.suspendmode == 'command':
372462306a36Sopenharmony_ci		for test in testruns:
372562306a36Sopenharmony_ci			for p in test.data.sortedPhases():
372662306a36Sopenharmony_ci				if p == 'suspend_prepare':
372762306a36Sopenharmony_ci					test.data.dmesg[p]['start'] = test.data.start
372862306a36Sopenharmony_ci					test.data.dmesg[p]['end'] = test.data.end
372962306a36Sopenharmony_ci				else:
373062306a36Sopenharmony_ci					test.data.dmesg[p]['start'] = test.data.end
373162306a36Sopenharmony_ci					test.data.dmesg[p]['end'] = test.data.end
373262306a36Sopenharmony_ci			test.data.tSuspended = test.data.end
373362306a36Sopenharmony_ci			test.data.tResumed = test.data.end
373462306a36Sopenharmony_ci			test.data.fwValid = False
373562306a36Sopenharmony_ci
373662306a36Sopenharmony_ci	# dev source and procmon events can be unreadable with mixed phase height
373762306a36Sopenharmony_ci	if sysvals.usedevsrc or sysvals.useprocmon:
373862306a36Sopenharmony_ci		sysvals.mixedphaseheight = False
373962306a36Sopenharmony_ci
374062306a36Sopenharmony_ci	# expand phase boundaries so there are no gaps
374162306a36Sopenharmony_ci	for data in testdata:
374262306a36Sopenharmony_ci		lp = data.sortedPhases()[0]
374362306a36Sopenharmony_ci		for p in data.sortedPhases():
374462306a36Sopenharmony_ci			if(p != lp and not ('machine' in p and 'machine' in lp)):
374562306a36Sopenharmony_ci				data.dmesg[lp]['end'] = data.dmesg[p]['start']
374662306a36Sopenharmony_ci			lp = p
374762306a36Sopenharmony_ci
374862306a36Sopenharmony_ci	for i in range(len(testruns)):
374962306a36Sopenharmony_ci		test = testruns[i]
375062306a36Sopenharmony_ci		data = test.data
375162306a36Sopenharmony_ci		# find the total time range for this test (begin, end)
375262306a36Sopenharmony_ci		tlb, tle = data.start, data.end
375362306a36Sopenharmony_ci		if i < len(testruns) - 1:
375462306a36Sopenharmony_ci			tle = testruns[i+1].data.start
375562306a36Sopenharmony_ci		# add the process usage data to the timeline
375662306a36Sopenharmony_ci		if sysvals.useprocmon:
375762306a36Sopenharmony_ci			data.createProcessUsageEvents()
375862306a36Sopenharmony_ci		# add the traceevent data to the device hierarchy
375962306a36Sopenharmony_ci		if(sysvals.usetraceevents):
376062306a36Sopenharmony_ci			# add actual trace funcs
376162306a36Sopenharmony_ci			for name in sorted(test.ttemp):
376262306a36Sopenharmony_ci				for event in test.ttemp[name]:
376362306a36Sopenharmony_ci					if event['end'] - event['begin'] <= 0:
376462306a36Sopenharmony_ci						continue
376562306a36Sopenharmony_ci					title = name
376662306a36Sopenharmony_ci					if name == 'machine_suspend' and 'loop' in event:
376762306a36Sopenharmony_ci						title = 's2idle_enter_%dx' % event['loop']
376862306a36Sopenharmony_ci					data.newActionGlobal(title, event['begin'], event['end'], event['pid'])
376962306a36Sopenharmony_ci			# add the kprobe based virtual tracefuncs as actual devices
377062306a36Sopenharmony_ci			for key in sorted(tp.ktemp):
377162306a36Sopenharmony_ci				name, pid = key
377262306a36Sopenharmony_ci				if name not in sysvals.tracefuncs:
377362306a36Sopenharmony_ci					continue
377462306a36Sopenharmony_ci				if pid not in data.devpids:
377562306a36Sopenharmony_ci					data.devpids.append(pid)
377662306a36Sopenharmony_ci				for e in tp.ktemp[key]:
377762306a36Sopenharmony_ci					kb, ke = e['begin'], e['end']
377862306a36Sopenharmony_ci					if ke - kb < 0.000001 or tlb > kb or tle <= kb:
377962306a36Sopenharmony_ci						continue
378062306a36Sopenharmony_ci					color = sysvals.kprobeColor(name)
378162306a36Sopenharmony_ci					data.newActionGlobal(e['name'], kb, ke, pid, color)
378262306a36Sopenharmony_ci			# add config base kprobes and dev kprobes
378362306a36Sopenharmony_ci			if sysvals.usedevsrc:
378462306a36Sopenharmony_ci				for key in sorted(tp.ktemp):
378562306a36Sopenharmony_ci					name, pid = key
378662306a36Sopenharmony_ci					if name in sysvals.tracefuncs or name not in sysvals.dev_tracefuncs:
378762306a36Sopenharmony_ci						continue
378862306a36Sopenharmony_ci					for e in tp.ktemp[key]:
378962306a36Sopenharmony_ci						kb, ke = e['begin'], e['end']
379062306a36Sopenharmony_ci						if ke - kb < 0.000001 or tlb > kb or tle <= kb:
379162306a36Sopenharmony_ci							continue
379262306a36Sopenharmony_ci						data.addDeviceFunctionCall(e['name'], name, e['proc'], pid, kb,
379362306a36Sopenharmony_ci							ke, e['cdata'], e['rdata'])
379462306a36Sopenharmony_ci		if sysvals.usecallgraph:
379562306a36Sopenharmony_ci			# add the callgraph data to the device hierarchy
379662306a36Sopenharmony_ci			sortlist = dict()
379762306a36Sopenharmony_ci			for key in sorted(test.ftemp):
379862306a36Sopenharmony_ci				proc, pid = key
379962306a36Sopenharmony_ci				for cg in test.ftemp[key]:
380062306a36Sopenharmony_ci					if len(cg.list) < 1 or cg.invalid or (cg.end - cg.start == 0):
380162306a36Sopenharmony_ci						continue
380262306a36Sopenharmony_ci					if(not cg.postProcess()):
380362306a36Sopenharmony_ci						id = 'task %s' % (pid)
380462306a36Sopenharmony_ci						sysvals.vprint('Sanity check failed for '+\
380562306a36Sopenharmony_ci							id+', ignoring this callback')
380662306a36Sopenharmony_ci						continue
380762306a36Sopenharmony_ci					# match cg data to devices
380862306a36Sopenharmony_ci					devname = ''
380962306a36Sopenharmony_ci					if sysvals.suspendmode != 'command':
381062306a36Sopenharmony_ci						devname = cg.deviceMatch(pid, data)
381162306a36Sopenharmony_ci					if not devname:
381262306a36Sopenharmony_ci						sortkey = '%f%f%d' % (cg.start, cg.end, pid)
381362306a36Sopenharmony_ci						sortlist[sortkey] = cg
381462306a36Sopenharmony_ci					elif len(cg.list) > 1000000 and cg.name != sysvals.ftopfunc:
381562306a36Sopenharmony_ci						sysvals.vprint('WARNING: the callgraph for %s is massive (%d lines)' %\
381662306a36Sopenharmony_ci							(devname, len(cg.list)))
381762306a36Sopenharmony_ci			# create blocks for orphan cg data
381862306a36Sopenharmony_ci			for sortkey in sorted(sortlist):
381962306a36Sopenharmony_ci				cg = sortlist[sortkey]
382062306a36Sopenharmony_ci				name = cg.name
382162306a36Sopenharmony_ci				if sysvals.isCallgraphFunc(name):
382262306a36Sopenharmony_ci					sysvals.vprint('Callgraph found for task %d: %.3fms, %s' % (cg.pid, (cg.end - cg.start)*1000, name))
382362306a36Sopenharmony_ci					cg.newActionFromFunction(data)
382462306a36Sopenharmony_ci	if sysvals.suspendmode == 'command':
382562306a36Sopenharmony_ci		return (testdata, '')
382662306a36Sopenharmony_ci
382762306a36Sopenharmony_ci	# fill in any missing phases
382862306a36Sopenharmony_ci	error = []
382962306a36Sopenharmony_ci	for data in testdata:
383062306a36Sopenharmony_ci		tn = '' if len(testdata) == 1 else ('%d' % (data.testnumber + 1))
383162306a36Sopenharmony_ci		terr = ''
383262306a36Sopenharmony_ci		phasedef = data.phasedef
383362306a36Sopenharmony_ci		lp = 'suspend_prepare'
383462306a36Sopenharmony_ci		for p in sorted(phasedef, key=lambda k:phasedef[k]['order']):
383562306a36Sopenharmony_ci			if p not in data.dmesg:
383662306a36Sopenharmony_ci				if not terr:
383762306a36Sopenharmony_ci					ph = p if 'machine' in p else lp
383862306a36Sopenharmony_ci					if p == 'suspend_machine':
383962306a36Sopenharmony_ci						sm = sysvals.suspendmode
384062306a36Sopenharmony_ci						if sm in suspendmodename:
384162306a36Sopenharmony_ci							sm = suspendmodename[sm]
384262306a36Sopenharmony_ci						terr = 'test%s did not enter %s power mode' % (tn, sm)
384362306a36Sopenharmony_ci					else:
384462306a36Sopenharmony_ci						terr = '%s%s failed in %s phase' % (sysvals.suspendmode, tn, ph)
384562306a36Sopenharmony_ci					pprint('TEST%s FAILED: %s' % (tn, terr))
384662306a36Sopenharmony_ci					error.append(terr)
384762306a36Sopenharmony_ci					if data.tSuspended == 0:
384862306a36Sopenharmony_ci						data.tSuspended = data.dmesg[lp]['end']
384962306a36Sopenharmony_ci					if data.tResumed == 0:
385062306a36Sopenharmony_ci						data.tResumed = data.dmesg[lp]['end']
385162306a36Sopenharmony_ci					data.fwValid = False
385262306a36Sopenharmony_ci				sysvals.vprint('WARNING: phase "%s" is missing!' % p)
385362306a36Sopenharmony_ci			lp = p
385462306a36Sopenharmony_ci		if not terr and 'dev' in data.wifi and data.wifi['stat'] == 'timeout':
385562306a36Sopenharmony_ci			terr = '%s%s failed in wifi_resume <i>(%s %.0fs timeout)</i>' % \
385662306a36Sopenharmony_ci				(sysvals.suspendmode, tn, data.wifi['dev'], data.wifi['time'])
385762306a36Sopenharmony_ci			error.append(terr)
385862306a36Sopenharmony_ci		if not terr and data.enterfail:
385962306a36Sopenharmony_ci			pprint('test%s FAILED: enter %s failed with %s' % (tn, sysvals.suspendmode, data.enterfail))
386062306a36Sopenharmony_ci			terr = 'test%s failed to enter %s mode' % (tn, sysvals.suspendmode)
386162306a36Sopenharmony_ci			error.append(terr)
386262306a36Sopenharmony_ci		if data.tSuspended == 0:
386362306a36Sopenharmony_ci			data.tSuspended = data.tKernRes
386462306a36Sopenharmony_ci		if data.tResumed == 0:
386562306a36Sopenharmony_ci			data.tResumed = data.tSuspended
386662306a36Sopenharmony_ci
386762306a36Sopenharmony_ci		if(len(sysvals.devicefilter) > 0):
386862306a36Sopenharmony_ci			data.deviceFilter(sysvals.devicefilter)
386962306a36Sopenharmony_ci		data.fixupInitcallsThatDidntReturn()
387062306a36Sopenharmony_ci		if sysvals.usedevsrc:
387162306a36Sopenharmony_ci			data.optimizeDevSrc()
387262306a36Sopenharmony_ci
387362306a36Sopenharmony_ci	# x2: merge any overlapping devices between test runs
387462306a36Sopenharmony_ci	if sysvals.usedevsrc and len(testdata) > 1:
387562306a36Sopenharmony_ci		tc = len(testdata)
387662306a36Sopenharmony_ci		for i in range(tc - 1):
387762306a36Sopenharmony_ci			devlist = testdata[i].overflowDevices()
387862306a36Sopenharmony_ci			for j in range(i + 1, tc):
387962306a36Sopenharmony_ci				testdata[j].mergeOverlapDevices(devlist)
388062306a36Sopenharmony_ci		testdata[0].stitchTouchingThreads(testdata[1:])
388162306a36Sopenharmony_ci	return (testdata, ', '.join(error))
388262306a36Sopenharmony_ci
388362306a36Sopenharmony_ci# Function: loadKernelLog
388462306a36Sopenharmony_ci# Description:
388562306a36Sopenharmony_ci#	 load the dmesg file into memory and fix up any ordering issues
388662306a36Sopenharmony_ci# Output:
388762306a36Sopenharmony_ci#	 An array of empty Data objects with only their dmesgtext attributes set
388862306a36Sopenharmony_cidef loadKernelLog():
388962306a36Sopenharmony_ci	sysvals.vprint('Analyzing the dmesg data (%s)...' % \
389062306a36Sopenharmony_ci		os.path.basename(sysvals.dmesgfile))
389162306a36Sopenharmony_ci	if(os.path.exists(sysvals.dmesgfile) == False):
389262306a36Sopenharmony_ci		doError('%s does not exist' % sysvals.dmesgfile)
389362306a36Sopenharmony_ci
389462306a36Sopenharmony_ci	# there can be multiple test runs in a single file
389562306a36Sopenharmony_ci	tp = TestProps()
389662306a36Sopenharmony_ci	tp.stamp = datetime.now().strftime('# suspend-%m%d%y-%H%M%S localhost mem unknown')
389762306a36Sopenharmony_ci	testruns = []
389862306a36Sopenharmony_ci	data = 0
389962306a36Sopenharmony_ci	lf = sysvals.openlog(sysvals.dmesgfile, 'r')
390062306a36Sopenharmony_ci	for line in lf:
390162306a36Sopenharmony_ci		line = line.replace('\r\n', '')
390262306a36Sopenharmony_ci		idx = line.find('[')
390362306a36Sopenharmony_ci		if idx > 1:
390462306a36Sopenharmony_ci			line = line[idx:]
390562306a36Sopenharmony_ci		if tp.stampInfo(line, sysvals):
390662306a36Sopenharmony_ci			continue
390762306a36Sopenharmony_ci		m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
390862306a36Sopenharmony_ci		if(not m):
390962306a36Sopenharmony_ci			continue
391062306a36Sopenharmony_ci		msg = m.group("msg")
391162306a36Sopenharmony_ci		if re.match('PM: Syncing filesystems.*', msg) or \
391262306a36Sopenharmony_ci			re.match('PM: suspend entry.*', msg):
391362306a36Sopenharmony_ci			if(data):
391462306a36Sopenharmony_ci				testruns.append(data)
391562306a36Sopenharmony_ci			data = Data(len(testruns))
391662306a36Sopenharmony_ci			tp.parseStamp(data, sysvals)
391762306a36Sopenharmony_ci		if(not data):
391862306a36Sopenharmony_ci			continue
391962306a36Sopenharmony_ci		m = re.match('.* *(?P<k>[0-9]\.[0-9]{2}\.[0-9]-.*) .*', msg)
392062306a36Sopenharmony_ci		if(m):
392162306a36Sopenharmony_ci			sysvals.stamp['kernel'] = m.group('k')
392262306a36Sopenharmony_ci		m = re.match('PM: Preparing system for (?P<m>.*) sleep', msg)
392362306a36Sopenharmony_ci		if not m:
392462306a36Sopenharmony_ci			m = re.match('PM: Preparing system for sleep \((?P<m>.*)\)', msg)
392562306a36Sopenharmony_ci		if m:
392662306a36Sopenharmony_ci			sysvals.stamp['mode'] = sysvals.suspendmode = m.group('m')
392762306a36Sopenharmony_ci		data.dmesgtext.append(line)
392862306a36Sopenharmony_ci	lf.close()
392962306a36Sopenharmony_ci
393062306a36Sopenharmony_ci	if sysvals.suspendmode == 's2idle':
393162306a36Sopenharmony_ci		sysvals.suspendmode = 'freeze'
393262306a36Sopenharmony_ci	elif sysvals.suspendmode == 'deep':
393362306a36Sopenharmony_ci		sysvals.suspendmode = 'mem'
393462306a36Sopenharmony_ci	if data:
393562306a36Sopenharmony_ci		testruns.append(data)
393662306a36Sopenharmony_ci	if len(testruns) < 1:
393762306a36Sopenharmony_ci		doError('dmesg log has no suspend/resume data: %s' \
393862306a36Sopenharmony_ci			% sysvals.dmesgfile)
393962306a36Sopenharmony_ci
394062306a36Sopenharmony_ci	# fix lines with same timestamp/function with the call and return swapped
394162306a36Sopenharmony_ci	for data in testruns:
394262306a36Sopenharmony_ci		last = ''
394362306a36Sopenharmony_ci		for line in data.dmesgtext:
394462306a36Sopenharmony_ci			ct, cf, n, p = data.initcall_debug_call(line)
394562306a36Sopenharmony_ci			rt, rf, l = data.initcall_debug_return(last)
394662306a36Sopenharmony_ci			if ct and rt and ct == rt and cf == rf:
394762306a36Sopenharmony_ci				i = data.dmesgtext.index(last)
394862306a36Sopenharmony_ci				j = data.dmesgtext.index(line)
394962306a36Sopenharmony_ci				data.dmesgtext[i] = line
395062306a36Sopenharmony_ci				data.dmesgtext[j] = last
395162306a36Sopenharmony_ci			last = line
395262306a36Sopenharmony_ci	return testruns
395362306a36Sopenharmony_ci
395462306a36Sopenharmony_ci# Function: parseKernelLog
395562306a36Sopenharmony_ci# Description:
395662306a36Sopenharmony_ci#	 Analyse a dmesg log output file generated from this app during
395762306a36Sopenharmony_ci#	 the execution phase. Create a set of device structures in memory
395862306a36Sopenharmony_ci#	 for subsequent formatting in the html output file
395962306a36Sopenharmony_ci#	 This call is only for legacy support on kernels where the ftrace
396062306a36Sopenharmony_ci#	 data lacks the suspend_resume or device_pm_callbacks trace events.
396162306a36Sopenharmony_ci# Arguments:
396262306a36Sopenharmony_ci#	 data: an empty Data object (with dmesgtext) obtained from loadKernelLog
396362306a36Sopenharmony_ci# Output:
396462306a36Sopenharmony_ci#	 The filled Data object
396562306a36Sopenharmony_cidef parseKernelLog(data):
396662306a36Sopenharmony_ci	phase = 'suspend_runtime'
396762306a36Sopenharmony_ci
396862306a36Sopenharmony_ci	if(data.fwValid):
396962306a36Sopenharmony_ci		sysvals.vprint('Firmware Suspend = %u ns, Firmware Resume = %u ns' % \
397062306a36Sopenharmony_ci			(data.fwSuspend, data.fwResume))
397162306a36Sopenharmony_ci
397262306a36Sopenharmony_ci	# dmesg phase match table
397362306a36Sopenharmony_ci	dm = {
397462306a36Sopenharmony_ci		'suspend_prepare': ['PM: Syncing filesystems.*', 'PM: suspend entry.*'],
397562306a36Sopenharmony_ci		        'suspend': ['PM: Entering [a-z]* sleep.*', 'Suspending console.*',
397662306a36Sopenharmony_ci		                    'PM: Suspending system .*'],
397762306a36Sopenharmony_ci		   'suspend_late': ['PM: suspend of devices complete after.*',
397862306a36Sopenharmony_ci							'PM: freeze of devices complete after.*'],
397962306a36Sopenharmony_ci		  'suspend_noirq': ['PM: late suspend of devices complete after.*',
398062306a36Sopenharmony_ci							'PM: late freeze of devices complete after.*'],
398162306a36Sopenharmony_ci		'suspend_machine': ['PM: suspend-to-idle',
398262306a36Sopenharmony_ci							'PM: noirq suspend of devices complete after.*',
398362306a36Sopenharmony_ci							'PM: noirq freeze of devices complete after.*'],
398462306a36Sopenharmony_ci		 'resume_machine': ['[PM: ]*Timekeeping suspended for.*',
398562306a36Sopenharmony_ci							'ACPI: Low-level resume complete.*',
398662306a36Sopenharmony_ci							'ACPI: resume from mwait',
398762306a36Sopenharmony_ci							'Suspended for [0-9\.]* seconds'],
398862306a36Sopenharmony_ci		   'resume_noirq': ['PM: resume from suspend-to-idle',
398962306a36Sopenharmony_ci							'ACPI: Waking up from system sleep state.*'],
399062306a36Sopenharmony_ci		   'resume_early': ['PM: noirq resume of devices complete after.*',
399162306a36Sopenharmony_ci							'PM: noirq restore of devices complete after.*'],
399262306a36Sopenharmony_ci		         'resume': ['PM: early resume of devices complete after.*',
399362306a36Sopenharmony_ci							'PM: early restore of devices complete after.*'],
399462306a36Sopenharmony_ci		'resume_complete': ['PM: resume of devices complete after.*',
399562306a36Sopenharmony_ci							'PM: restore of devices complete after.*'],
399662306a36Sopenharmony_ci		    'post_resume': ['.*Restarting tasks \.\.\..*'],
399762306a36Sopenharmony_ci	}
399862306a36Sopenharmony_ci
399962306a36Sopenharmony_ci	# action table (expected events that occur and show up in dmesg)
400062306a36Sopenharmony_ci	at = {
400162306a36Sopenharmony_ci		'sync_filesystems': {
400262306a36Sopenharmony_ci			'smsg': '.*[Ff]+ilesystems.*',
400362306a36Sopenharmony_ci			'emsg': 'PM: Preparing system for[a-z]* sleep.*' },
400462306a36Sopenharmony_ci		'freeze_user_processes': {
400562306a36Sopenharmony_ci			'smsg': 'Freezing user space processes.*',
400662306a36Sopenharmony_ci			'emsg': 'Freezing remaining freezable tasks.*' },
400762306a36Sopenharmony_ci		'freeze_tasks': {
400862306a36Sopenharmony_ci			'smsg': 'Freezing remaining freezable tasks.*',
400962306a36Sopenharmony_ci			'emsg': 'PM: Suspending system.*' },
401062306a36Sopenharmony_ci		'ACPI prepare': {
401162306a36Sopenharmony_ci			'smsg': 'ACPI: Preparing to enter system sleep state.*',
401262306a36Sopenharmony_ci			'emsg': 'PM: Saving platform NVS memory.*' },
401362306a36Sopenharmony_ci		'PM vns': {
401462306a36Sopenharmony_ci			'smsg': 'PM: Saving platform NVS memory.*',
401562306a36Sopenharmony_ci			'emsg': 'Disabling non-boot CPUs .*' },
401662306a36Sopenharmony_ci	}
401762306a36Sopenharmony_ci
401862306a36Sopenharmony_ci	t0 = -1.0
401962306a36Sopenharmony_ci	cpu_start = -1.0
402062306a36Sopenharmony_ci	prevktime = -1.0
402162306a36Sopenharmony_ci	actions = dict()
402262306a36Sopenharmony_ci	for line in data.dmesgtext:
402362306a36Sopenharmony_ci		# parse each dmesg line into the time and message
402462306a36Sopenharmony_ci		m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
402562306a36Sopenharmony_ci		if(m):
402662306a36Sopenharmony_ci			val = m.group('ktime')
402762306a36Sopenharmony_ci			try:
402862306a36Sopenharmony_ci				ktime = float(val)
402962306a36Sopenharmony_ci			except:
403062306a36Sopenharmony_ci				continue
403162306a36Sopenharmony_ci			msg = m.group('msg')
403262306a36Sopenharmony_ci			# initialize data start to first line time
403362306a36Sopenharmony_ci			if t0 < 0:
403462306a36Sopenharmony_ci				data.setStart(ktime)
403562306a36Sopenharmony_ci				t0 = ktime
403662306a36Sopenharmony_ci		else:
403762306a36Sopenharmony_ci			continue
403862306a36Sopenharmony_ci
403962306a36Sopenharmony_ci		# check for a phase change line
404062306a36Sopenharmony_ci		phasechange = False
404162306a36Sopenharmony_ci		for p in dm:
404262306a36Sopenharmony_ci			for s in dm[p]:
404362306a36Sopenharmony_ci				if(re.match(s, msg)):
404462306a36Sopenharmony_ci					phasechange, phase = True, p
404562306a36Sopenharmony_ci					dm[p] = [s]
404662306a36Sopenharmony_ci					break
404762306a36Sopenharmony_ci
404862306a36Sopenharmony_ci		# hack for determining resume_machine end for freeze
404962306a36Sopenharmony_ci		if(not sysvals.usetraceevents and sysvals.suspendmode == 'freeze' \
405062306a36Sopenharmony_ci			and phase == 'resume_machine' and \
405162306a36Sopenharmony_ci			data.initcall_debug_call(line, True)):
405262306a36Sopenharmony_ci			data.setPhase(phase, ktime, False)
405362306a36Sopenharmony_ci			phase = 'resume_noirq'
405462306a36Sopenharmony_ci			data.setPhase(phase, ktime, True)
405562306a36Sopenharmony_ci
405662306a36Sopenharmony_ci		if phasechange:
405762306a36Sopenharmony_ci			if phase == 'suspend_prepare':
405862306a36Sopenharmony_ci				data.setPhase(phase, ktime, True)
405962306a36Sopenharmony_ci				data.setStart(ktime)
406062306a36Sopenharmony_ci				data.tKernSus = ktime
406162306a36Sopenharmony_ci			elif phase == 'suspend':
406262306a36Sopenharmony_ci				lp = data.lastPhase()
406362306a36Sopenharmony_ci				if lp:
406462306a36Sopenharmony_ci					data.setPhase(lp, ktime, False)
406562306a36Sopenharmony_ci				data.setPhase(phase, ktime, True)
406662306a36Sopenharmony_ci			elif phase == 'suspend_late':
406762306a36Sopenharmony_ci				lp = data.lastPhase()
406862306a36Sopenharmony_ci				if lp:
406962306a36Sopenharmony_ci					data.setPhase(lp, ktime, False)
407062306a36Sopenharmony_ci				data.setPhase(phase, ktime, True)
407162306a36Sopenharmony_ci			elif phase == 'suspend_noirq':
407262306a36Sopenharmony_ci				lp = data.lastPhase()
407362306a36Sopenharmony_ci				if lp:
407462306a36Sopenharmony_ci					data.setPhase(lp, ktime, False)
407562306a36Sopenharmony_ci				data.setPhase(phase, ktime, True)
407662306a36Sopenharmony_ci			elif phase == 'suspend_machine':
407762306a36Sopenharmony_ci				lp = data.lastPhase()
407862306a36Sopenharmony_ci				if lp:
407962306a36Sopenharmony_ci					data.setPhase(lp, ktime, False)
408062306a36Sopenharmony_ci				data.setPhase(phase, ktime, True)
408162306a36Sopenharmony_ci			elif phase == 'resume_machine':
408262306a36Sopenharmony_ci				lp = data.lastPhase()
408362306a36Sopenharmony_ci				if(sysvals.suspendmode in ['freeze', 'standby']):
408462306a36Sopenharmony_ci					data.tSuspended = prevktime
408562306a36Sopenharmony_ci					if lp:
408662306a36Sopenharmony_ci						data.setPhase(lp, prevktime, False)
408762306a36Sopenharmony_ci				else:
408862306a36Sopenharmony_ci					data.tSuspended = ktime
408962306a36Sopenharmony_ci					if lp:
409062306a36Sopenharmony_ci						data.setPhase(lp, prevktime, False)
409162306a36Sopenharmony_ci				data.tResumed = ktime
409262306a36Sopenharmony_ci				data.setPhase(phase, ktime, True)
409362306a36Sopenharmony_ci			elif phase == 'resume_noirq':
409462306a36Sopenharmony_ci				lp = data.lastPhase()
409562306a36Sopenharmony_ci				if lp:
409662306a36Sopenharmony_ci					data.setPhase(lp, ktime, False)
409762306a36Sopenharmony_ci				data.setPhase(phase, ktime, True)
409862306a36Sopenharmony_ci			elif phase == 'resume_early':
409962306a36Sopenharmony_ci				lp = data.lastPhase()
410062306a36Sopenharmony_ci				if lp:
410162306a36Sopenharmony_ci					data.setPhase(lp, ktime, False)
410262306a36Sopenharmony_ci				data.setPhase(phase, ktime, True)
410362306a36Sopenharmony_ci			elif phase == 'resume':
410462306a36Sopenharmony_ci				lp = data.lastPhase()
410562306a36Sopenharmony_ci				if lp:
410662306a36Sopenharmony_ci					data.setPhase(lp, ktime, False)
410762306a36Sopenharmony_ci				data.setPhase(phase, ktime, True)
410862306a36Sopenharmony_ci			elif phase == 'resume_complete':
410962306a36Sopenharmony_ci				lp = data.lastPhase()
411062306a36Sopenharmony_ci				if lp:
411162306a36Sopenharmony_ci					data.setPhase(lp, ktime, False)
411262306a36Sopenharmony_ci				data.setPhase(phase, ktime, True)
411362306a36Sopenharmony_ci			elif phase == 'post_resume':
411462306a36Sopenharmony_ci				lp = data.lastPhase()
411562306a36Sopenharmony_ci				if lp:
411662306a36Sopenharmony_ci					data.setPhase(lp, ktime, False)
411762306a36Sopenharmony_ci				data.setEnd(ktime)
411862306a36Sopenharmony_ci				data.tKernRes = ktime
411962306a36Sopenharmony_ci				break
412062306a36Sopenharmony_ci
412162306a36Sopenharmony_ci		# -- device callbacks --
412262306a36Sopenharmony_ci		if(phase in data.sortedPhases()):
412362306a36Sopenharmony_ci			# device init call
412462306a36Sopenharmony_ci			t, f, n, p = data.initcall_debug_call(line)
412562306a36Sopenharmony_ci			if t and f and n and p:
412662306a36Sopenharmony_ci				data.newAction(phase, f, int(n), p, ktime, -1, '')
412762306a36Sopenharmony_ci			else:
412862306a36Sopenharmony_ci				# device init return
412962306a36Sopenharmony_ci				t, f, l = data.initcall_debug_return(line)
413062306a36Sopenharmony_ci				if t and f and l:
413162306a36Sopenharmony_ci					list = data.dmesg[phase]['list']
413262306a36Sopenharmony_ci					if(f in list):
413362306a36Sopenharmony_ci						dev = list[f]
413462306a36Sopenharmony_ci						dev['length'] = int(l)
413562306a36Sopenharmony_ci						dev['end'] = ktime
413662306a36Sopenharmony_ci
413762306a36Sopenharmony_ci		# if trace events are not available, these are better than nothing
413862306a36Sopenharmony_ci		if(not sysvals.usetraceevents):
413962306a36Sopenharmony_ci			# look for known actions
414062306a36Sopenharmony_ci			for a in sorted(at):
414162306a36Sopenharmony_ci				if(re.match(at[a]['smsg'], msg)):
414262306a36Sopenharmony_ci					if(a not in actions):
414362306a36Sopenharmony_ci						actions[a] = [{'begin': ktime, 'end': ktime}]
414462306a36Sopenharmony_ci				if(re.match(at[a]['emsg'], msg)):
414562306a36Sopenharmony_ci					if(a in actions and actions[a][-1]['begin'] == actions[a][-1]['end']):
414662306a36Sopenharmony_ci						actions[a][-1]['end'] = ktime
414762306a36Sopenharmony_ci			# now look for CPU on/off events
414862306a36Sopenharmony_ci			if(re.match('Disabling non-boot CPUs .*', msg)):
414962306a36Sopenharmony_ci				# start of first cpu suspend
415062306a36Sopenharmony_ci				cpu_start = ktime
415162306a36Sopenharmony_ci			elif(re.match('Enabling non-boot CPUs .*', msg)):
415262306a36Sopenharmony_ci				# start of first cpu resume
415362306a36Sopenharmony_ci				cpu_start = ktime
415462306a36Sopenharmony_ci			elif(re.match('smpboot: CPU (?P<cpu>[0-9]*) is now offline', msg) \
415562306a36Sopenharmony_ci				or re.match('psci: CPU(?P<cpu>[0-9]*) killed.*', msg)):
415662306a36Sopenharmony_ci				# end of a cpu suspend, start of the next
415762306a36Sopenharmony_ci				m = re.match('smpboot: CPU (?P<cpu>[0-9]*) is now offline', msg)
415862306a36Sopenharmony_ci				if(not m):
415962306a36Sopenharmony_ci					m = re.match('psci: CPU(?P<cpu>[0-9]*) killed.*', msg)
416062306a36Sopenharmony_ci				cpu = 'CPU'+m.group('cpu')
416162306a36Sopenharmony_ci				if(cpu not in actions):
416262306a36Sopenharmony_ci					actions[cpu] = []
416362306a36Sopenharmony_ci				actions[cpu].append({'begin': cpu_start, 'end': ktime})
416462306a36Sopenharmony_ci				cpu_start = ktime
416562306a36Sopenharmony_ci			elif(re.match('CPU(?P<cpu>[0-9]*) is up', msg)):
416662306a36Sopenharmony_ci				# end of a cpu resume, start of the next
416762306a36Sopenharmony_ci				m = re.match('CPU(?P<cpu>[0-9]*) is up', msg)
416862306a36Sopenharmony_ci				cpu = 'CPU'+m.group('cpu')
416962306a36Sopenharmony_ci				if(cpu not in actions):
417062306a36Sopenharmony_ci					actions[cpu] = []
417162306a36Sopenharmony_ci				actions[cpu].append({'begin': cpu_start, 'end': ktime})
417262306a36Sopenharmony_ci				cpu_start = ktime
417362306a36Sopenharmony_ci		prevktime = ktime
417462306a36Sopenharmony_ci	data.initDevicegroups()
417562306a36Sopenharmony_ci
417662306a36Sopenharmony_ci	# fill in any missing phases
417762306a36Sopenharmony_ci	phasedef = data.phasedef
417862306a36Sopenharmony_ci	terr, lp = '', 'suspend_prepare'
417962306a36Sopenharmony_ci	if lp not in data.dmesg:
418062306a36Sopenharmony_ci		doError('dmesg log format has changed, could not find start of suspend')
418162306a36Sopenharmony_ci	for p in sorted(phasedef, key=lambda k:phasedef[k]['order']):
418262306a36Sopenharmony_ci		if p not in data.dmesg:
418362306a36Sopenharmony_ci			if not terr:
418462306a36Sopenharmony_ci				pprint('TEST FAILED: %s failed in %s phase' % (sysvals.suspendmode, lp))
418562306a36Sopenharmony_ci				terr = '%s failed in %s phase' % (sysvals.suspendmode, lp)
418662306a36Sopenharmony_ci				if data.tSuspended == 0:
418762306a36Sopenharmony_ci					data.tSuspended = data.dmesg[lp]['end']
418862306a36Sopenharmony_ci				if data.tResumed == 0:
418962306a36Sopenharmony_ci					data.tResumed = data.dmesg[lp]['end']
419062306a36Sopenharmony_ci			sysvals.vprint('WARNING: phase "%s" is missing!' % p)
419162306a36Sopenharmony_ci		lp = p
419262306a36Sopenharmony_ci	lp = data.sortedPhases()[0]
419362306a36Sopenharmony_ci	for p in data.sortedPhases():
419462306a36Sopenharmony_ci		if(p != lp and not ('machine' in p and 'machine' in lp)):
419562306a36Sopenharmony_ci			data.dmesg[lp]['end'] = data.dmesg[p]['start']
419662306a36Sopenharmony_ci		lp = p
419762306a36Sopenharmony_ci	if data.tSuspended == 0:
419862306a36Sopenharmony_ci		data.tSuspended = data.tKernRes
419962306a36Sopenharmony_ci	if data.tResumed == 0:
420062306a36Sopenharmony_ci		data.tResumed = data.tSuspended
420162306a36Sopenharmony_ci
420262306a36Sopenharmony_ci	# fill in any actions we've found
420362306a36Sopenharmony_ci	for name in sorted(actions):
420462306a36Sopenharmony_ci		for event in actions[name]:
420562306a36Sopenharmony_ci			data.newActionGlobal(name, event['begin'], event['end'])
420662306a36Sopenharmony_ci
420762306a36Sopenharmony_ci	if(len(sysvals.devicefilter) > 0):
420862306a36Sopenharmony_ci		data.deviceFilter(sysvals.devicefilter)
420962306a36Sopenharmony_ci	data.fixupInitcallsThatDidntReturn()
421062306a36Sopenharmony_ci	return True
421162306a36Sopenharmony_ci
421262306a36Sopenharmony_cidef callgraphHTML(sv, hf, num, cg, title, color, devid):
421362306a36Sopenharmony_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'
421462306a36Sopenharmony_ci	html_func_start = '<article>\n<input type="checkbox" class="pf" id="f{0}" checked/><label for="f{0}">{1} {2}</label>\n'
421562306a36Sopenharmony_ci	html_func_end = '</article>\n'
421662306a36Sopenharmony_ci	html_func_leaf = '<article>{0} {1}</article>\n'
421762306a36Sopenharmony_ci
421862306a36Sopenharmony_ci	cgid = devid
421962306a36Sopenharmony_ci	if cg.id:
422062306a36Sopenharmony_ci		cgid += cg.id
422162306a36Sopenharmony_ci	cglen = (cg.end - cg.start) * 1000
422262306a36Sopenharmony_ci	if cglen < sv.mincglen:
422362306a36Sopenharmony_ci		return num
422462306a36Sopenharmony_ci
422562306a36Sopenharmony_ci	fmt = '<r>(%.3f ms @ '+sv.timeformat+' to '+sv.timeformat+')</r>'
422662306a36Sopenharmony_ci	flen = fmt % (cglen, cg.start, cg.end)
422762306a36Sopenharmony_ci	hf.write(html_func_top.format(cgid, color, num, title, flen))
422862306a36Sopenharmony_ci	num += 1
422962306a36Sopenharmony_ci	for line in cg.list:
423062306a36Sopenharmony_ci		if(line.length < 0.000000001):
423162306a36Sopenharmony_ci			flen = ''
423262306a36Sopenharmony_ci		else:
423362306a36Sopenharmony_ci			fmt = '<n>(%.3f ms @ '+sv.timeformat+')</n>'
423462306a36Sopenharmony_ci			flen = fmt % (line.length*1000, line.time)
423562306a36Sopenharmony_ci		if line.isLeaf():
423662306a36Sopenharmony_ci			if line.length * 1000 < sv.mincglen:
423762306a36Sopenharmony_ci				continue
423862306a36Sopenharmony_ci			hf.write(html_func_leaf.format(line.name, flen))
423962306a36Sopenharmony_ci		elif line.freturn:
424062306a36Sopenharmony_ci			hf.write(html_func_end)
424162306a36Sopenharmony_ci		else:
424262306a36Sopenharmony_ci			hf.write(html_func_start.format(num, line.name, flen))
424362306a36Sopenharmony_ci			num += 1
424462306a36Sopenharmony_ci	hf.write(html_func_end)
424562306a36Sopenharmony_ci	return num
424662306a36Sopenharmony_ci
424762306a36Sopenharmony_cidef addCallgraphs(sv, hf, data):
424862306a36Sopenharmony_ci	hf.write('<section id="callgraphs" class="callgraph">\n')
424962306a36Sopenharmony_ci	# write out the ftrace data converted to html
425062306a36Sopenharmony_ci	num = 0
425162306a36Sopenharmony_ci	for p in data.sortedPhases():
425262306a36Sopenharmony_ci		if sv.cgphase and p != sv.cgphase:
425362306a36Sopenharmony_ci			continue
425462306a36Sopenharmony_ci		list = data.dmesg[p]['list']
425562306a36Sopenharmony_ci		for d in data.sortedDevices(p):
425662306a36Sopenharmony_ci			if len(sv.cgfilter) > 0 and d not in sv.cgfilter:
425762306a36Sopenharmony_ci				continue
425862306a36Sopenharmony_ci			dev = list[d]
425962306a36Sopenharmony_ci			color = 'white'
426062306a36Sopenharmony_ci			if 'color' in data.dmesg[p]:
426162306a36Sopenharmony_ci				color = data.dmesg[p]['color']
426262306a36Sopenharmony_ci			if 'color' in dev:
426362306a36Sopenharmony_ci				color = dev['color']
426462306a36Sopenharmony_ci			name = d if '[' not in d else d.split('[')[0]
426562306a36Sopenharmony_ci			if(d in sv.devprops):
426662306a36Sopenharmony_ci				name = sv.devprops[d].altName(d)
426762306a36Sopenharmony_ci			if 'drv' in dev and dev['drv']:
426862306a36Sopenharmony_ci				name += ' {%s}' % dev['drv']
426962306a36Sopenharmony_ci			if sv.suspendmode in suspendmodename:
427062306a36Sopenharmony_ci				name += ' '+p
427162306a36Sopenharmony_ci			if('ftrace' in dev):
427262306a36Sopenharmony_ci				cg = dev['ftrace']
427362306a36Sopenharmony_ci				if cg.name == sv.ftopfunc:
427462306a36Sopenharmony_ci					name = 'top level suspend/resume call'
427562306a36Sopenharmony_ci				num = callgraphHTML(sv, hf, num, cg,
427662306a36Sopenharmony_ci					name, color, dev['id'])
427762306a36Sopenharmony_ci			if('ftraces' in dev):
427862306a36Sopenharmony_ci				for cg in dev['ftraces']:
427962306a36Sopenharmony_ci					num = callgraphHTML(sv, hf, num, cg,
428062306a36Sopenharmony_ci						name+' &rarr; '+cg.name, color, dev['id'])
428162306a36Sopenharmony_ci	hf.write('\n\n    </section>\n')
428262306a36Sopenharmony_ci
428362306a36Sopenharmony_cidef summaryCSS(title, center=True):
428462306a36Sopenharmony_ci	tdcenter = 'text-align:center;' if center else ''
428562306a36Sopenharmony_ci	out = '<!DOCTYPE html>\n<html>\n<head>\n\
428662306a36Sopenharmony_ci	<meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\
428762306a36Sopenharmony_ci	<title>'+title+'</title>\n\
428862306a36Sopenharmony_ci	<style type=\'text/css\'>\n\
428962306a36Sopenharmony_ci		.stamp {width: 100%;text-align:center;background:#888;line-height:30px;color:white;font: 25px Arial;}\n\
429062306a36Sopenharmony_ci		table {width:100%;border-collapse: collapse;border:1px solid;}\n\
429162306a36Sopenharmony_ci		th {border: 1px solid black;background:#222;color:white;}\n\
429262306a36Sopenharmony_ci		td {font: 14px "Times New Roman";'+tdcenter+'}\n\
429362306a36Sopenharmony_ci		tr.head td {border: 1px solid black;background:#aaa;}\n\
429462306a36Sopenharmony_ci		tr.alt {background-color:#ddd;}\n\
429562306a36Sopenharmony_ci		tr.notice {color:red;}\n\
429662306a36Sopenharmony_ci		.minval {background-color:#BBFFBB;}\n\
429762306a36Sopenharmony_ci		.medval {background-color:#BBBBFF;}\n\
429862306a36Sopenharmony_ci		.maxval {background-color:#FFBBBB;}\n\
429962306a36Sopenharmony_ci		.head a {color:#000;text-decoration: none;}\n\
430062306a36Sopenharmony_ci	</style>\n</head>\n<body>\n'
430162306a36Sopenharmony_ci	return out
430262306a36Sopenharmony_ci
430362306a36Sopenharmony_ci# Function: createHTMLSummarySimple
430462306a36Sopenharmony_ci# Description:
430562306a36Sopenharmony_ci#	 Create summary html file for a series of tests
430662306a36Sopenharmony_ci# Arguments:
430762306a36Sopenharmony_ci#	 testruns: array of Data objects from parseTraceLog
430862306a36Sopenharmony_cidef createHTMLSummarySimple(testruns, htmlfile, title):
430962306a36Sopenharmony_ci	# write the html header first (html head, css code, up to body start)
431062306a36Sopenharmony_ci	html = summaryCSS('Summary - SleepGraph')
431162306a36Sopenharmony_ci
431262306a36Sopenharmony_ci	# extract the test data into list
431362306a36Sopenharmony_ci	list = dict()
431462306a36Sopenharmony_ci	tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()]
431562306a36Sopenharmony_ci	iMin, iMed, iMax = [0, 0], [0, 0], [0, 0]
431662306a36Sopenharmony_ci	num = 0
431762306a36Sopenharmony_ci	useturbo = usewifi = False
431862306a36Sopenharmony_ci	lastmode = ''
431962306a36Sopenharmony_ci	cnt = dict()
432062306a36Sopenharmony_ci	for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'], v['time'])):
432162306a36Sopenharmony_ci		mode = data['mode']
432262306a36Sopenharmony_ci		if mode not in list:
432362306a36Sopenharmony_ci			list[mode] = {'data': [], 'avg': [0,0], 'min': [0,0], 'max': [0,0], 'med': [0,0]}
432462306a36Sopenharmony_ci		if lastmode and lastmode != mode and num > 0:
432562306a36Sopenharmony_ci			for i in range(2):
432662306a36Sopenharmony_ci				s = sorted(tMed[i])
432762306a36Sopenharmony_ci				list[lastmode]['med'][i] = s[int(len(s)//2)]
432862306a36Sopenharmony_ci				iMed[i] = tMed[i][list[lastmode]['med'][i]]
432962306a36Sopenharmony_ci			list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num]
433062306a36Sopenharmony_ci			list[lastmode]['min'] = tMin
433162306a36Sopenharmony_ci			list[lastmode]['max'] = tMax
433262306a36Sopenharmony_ci			list[lastmode]['idx'] = (iMin, iMed, iMax)
433362306a36Sopenharmony_ci			tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()]
433462306a36Sopenharmony_ci			iMin, iMed, iMax = [0, 0], [0, 0], [0, 0]
433562306a36Sopenharmony_ci			num = 0
433662306a36Sopenharmony_ci		pkgpc10 = syslpi = wifi = ''
433762306a36Sopenharmony_ci		if 'pkgpc10' in data and 'syslpi' in data:
433862306a36Sopenharmony_ci			pkgpc10, syslpi, useturbo = data['pkgpc10'], data['syslpi'], True
433962306a36Sopenharmony_ci		if 'wifi' in data:
434062306a36Sopenharmony_ci			wifi, usewifi = data['wifi'], True
434162306a36Sopenharmony_ci		res = data['result']
434262306a36Sopenharmony_ci		tVal = [float(data['suspend']), float(data['resume'])]
434362306a36Sopenharmony_ci		list[mode]['data'].append([data['host'], data['kernel'],
434462306a36Sopenharmony_ci			data['time'], tVal[0], tVal[1], data['url'], res,
434562306a36Sopenharmony_ci			data['issues'], data['sus_worst'], data['sus_worsttime'],
434662306a36Sopenharmony_ci			data['res_worst'], data['res_worsttime'], pkgpc10, syslpi, wifi])
434762306a36Sopenharmony_ci		idx = len(list[mode]['data']) - 1
434862306a36Sopenharmony_ci		if res.startswith('fail in'):
434962306a36Sopenharmony_ci			res = 'fail'
435062306a36Sopenharmony_ci		if res not in cnt:
435162306a36Sopenharmony_ci			cnt[res] = 1
435262306a36Sopenharmony_ci		else:
435362306a36Sopenharmony_ci			cnt[res] += 1
435462306a36Sopenharmony_ci		if res == 'pass':
435562306a36Sopenharmony_ci			for i in range(2):
435662306a36Sopenharmony_ci				tMed[i][tVal[i]] = idx
435762306a36Sopenharmony_ci				tAvg[i] += tVal[i]
435862306a36Sopenharmony_ci				if tMin[i] == 0 or tVal[i] < tMin[i]:
435962306a36Sopenharmony_ci					iMin[i] = idx
436062306a36Sopenharmony_ci					tMin[i] = tVal[i]
436162306a36Sopenharmony_ci				if tMax[i] == 0 or tVal[i] > tMax[i]:
436262306a36Sopenharmony_ci					iMax[i] = idx
436362306a36Sopenharmony_ci					tMax[i] = tVal[i]
436462306a36Sopenharmony_ci			num += 1
436562306a36Sopenharmony_ci		lastmode = mode
436662306a36Sopenharmony_ci	if lastmode and num > 0:
436762306a36Sopenharmony_ci		for i in range(2):
436862306a36Sopenharmony_ci			s = sorted(tMed[i])
436962306a36Sopenharmony_ci			list[lastmode]['med'][i] = s[int(len(s)//2)]
437062306a36Sopenharmony_ci			iMed[i] = tMed[i][list[lastmode]['med'][i]]
437162306a36Sopenharmony_ci		list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num]
437262306a36Sopenharmony_ci		list[lastmode]['min'] = tMin
437362306a36Sopenharmony_ci		list[lastmode]['max'] = tMax
437462306a36Sopenharmony_ci		list[lastmode]['idx'] = (iMin, iMed, iMax)
437562306a36Sopenharmony_ci
437662306a36Sopenharmony_ci	# group test header
437762306a36Sopenharmony_ci	desc = []
437862306a36Sopenharmony_ci	for ilk in sorted(cnt, reverse=True):
437962306a36Sopenharmony_ci		if cnt[ilk] > 0:
438062306a36Sopenharmony_ci			desc.append('%d %s' % (cnt[ilk], ilk))
438162306a36Sopenharmony_ci	html += '<div class="stamp">%s (%d tests: %s)</div>\n' % (title, len(testruns), ', '.join(desc))
438262306a36Sopenharmony_ci	th = '\t<th>{0}</th>\n'
438362306a36Sopenharmony_ci	td = '\t<td>{0}</td>\n'
438462306a36Sopenharmony_ci	tdh = '\t<td{1}>{0}</td>\n'
438562306a36Sopenharmony_ci	tdlink = '\t<td><a href="{0}">html</a></td>\n'
438662306a36Sopenharmony_ci	cols = 12
438762306a36Sopenharmony_ci	if useturbo:
438862306a36Sopenharmony_ci		cols += 2
438962306a36Sopenharmony_ci	if usewifi:
439062306a36Sopenharmony_ci		cols += 1
439162306a36Sopenharmony_ci	colspan = '%d' % cols
439262306a36Sopenharmony_ci
439362306a36Sopenharmony_ci	# table header
439462306a36Sopenharmony_ci	html += '<table>\n<tr>\n' + th.format('#') +\
439562306a36Sopenharmony_ci		th.format('Mode') + th.format('Host') + th.format('Kernel') +\
439662306a36Sopenharmony_ci		th.format('Test Time') + th.format('Result') + th.format('Issues') +\
439762306a36Sopenharmony_ci		th.format('Suspend') + th.format('Resume') +\
439862306a36Sopenharmony_ci		th.format('Worst Suspend Device') + th.format('SD Time') +\
439962306a36Sopenharmony_ci		th.format('Worst Resume Device') + th.format('RD Time')
440062306a36Sopenharmony_ci	if useturbo:
440162306a36Sopenharmony_ci		html += th.format('PkgPC10') + th.format('SysLPI')
440262306a36Sopenharmony_ci	if usewifi:
440362306a36Sopenharmony_ci		html += th.format('Wifi')
440462306a36Sopenharmony_ci	html += th.format('Detail')+'</tr>\n'
440562306a36Sopenharmony_ci	# export list into html
440662306a36Sopenharmony_ci	head = '<tr class="head"><td>{0}</td><td>{1}</td>'+\
440762306a36Sopenharmony_ci		'<td colspan='+colspan+' class="sus">Suspend Avg={2} '+\
440862306a36Sopenharmony_ci		'<span class=minval><a href="#s{10}min">Min={3}</a></span> '+\
440962306a36Sopenharmony_ci		'<span class=medval><a href="#s{10}med">Med={4}</a></span> '+\
441062306a36Sopenharmony_ci		'<span class=maxval><a href="#s{10}max">Max={5}</a></span> '+\
441162306a36Sopenharmony_ci		'Resume Avg={6} '+\
441262306a36Sopenharmony_ci		'<span class=minval><a href="#r{10}min">Min={7}</a></span> '+\
441362306a36Sopenharmony_ci		'<span class=medval><a href="#r{10}med">Med={8}</a></span> '+\
441462306a36Sopenharmony_ci		'<span class=maxval><a href="#r{10}max">Max={9}</a></span></td>'+\
441562306a36Sopenharmony_ci		'</tr>\n'
441662306a36Sopenharmony_ci	headnone = '<tr class="head"><td>{0}</td><td>{1}</td><td colspan='+\
441762306a36Sopenharmony_ci		colspan+'></td></tr>\n'
441862306a36Sopenharmony_ci	for mode in sorted(list):
441962306a36Sopenharmony_ci		# header line for each suspend mode
442062306a36Sopenharmony_ci		num = 0
442162306a36Sopenharmony_ci		tAvg, tMin, tMax, tMed = list[mode]['avg'], list[mode]['min'],\
442262306a36Sopenharmony_ci			list[mode]['max'], list[mode]['med']
442362306a36Sopenharmony_ci		count = len(list[mode]['data'])
442462306a36Sopenharmony_ci		if 'idx' in list[mode]:
442562306a36Sopenharmony_ci			iMin, iMed, iMax = list[mode]['idx']
442662306a36Sopenharmony_ci			html += head.format('%d' % count, mode.upper(),
442762306a36Sopenharmony_ci				'%.3f' % tAvg[0], '%.3f' % tMin[0], '%.3f' % tMed[0], '%.3f' % tMax[0],
442862306a36Sopenharmony_ci				'%.3f' % tAvg[1], '%.3f' % tMin[1], '%.3f' % tMed[1], '%.3f' % tMax[1],
442962306a36Sopenharmony_ci				mode.lower()
443062306a36Sopenharmony_ci			)
443162306a36Sopenharmony_ci		else:
443262306a36Sopenharmony_ci			iMin = iMed = iMax = [-1, -1, -1]
443362306a36Sopenharmony_ci			html += headnone.format('%d' % count, mode.upper())
443462306a36Sopenharmony_ci		for d in list[mode]['data']:
443562306a36Sopenharmony_ci			# row classes - alternate row color
443662306a36Sopenharmony_ci			rcls = ['alt'] if num % 2 == 1 else []
443762306a36Sopenharmony_ci			if d[6] != 'pass':
443862306a36Sopenharmony_ci				rcls.append('notice')
443962306a36Sopenharmony_ci			html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n'
444062306a36Sopenharmony_ci			# figure out if the line has sus or res highlighted
444162306a36Sopenharmony_ci			idx = list[mode]['data'].index(d)
444262306a36Sopenharmony_ci			tHigh = ['', '']
444362306a36Sopenharmony_ci			for i in range(2):
444462306a36Sopenharmony_ci				tag = 's%s' % mode if i == 0 else 'r%s' % mode
444562306a36Sopenharmony_ci				if idx == iMin[i]:
444662306a36Sopenharmony_ci					tHigh[i] = ' id="%smin" class=minval title="Minimum"' % tag
444762306a36Sopenharmony_ci				elif idx == iMax[i]:
444862306a36Sopenharmony_ci					tHigh[i] = ' id="%smax" class=maxval title="Maximum"' % tag
444962306a36Sopenharmony_ci				elif idx == iMed[i]:
445062306a36Sopenharmony_ci					tHigh[i] = ' id="%smed" class=medval title="Median"' % tag
445162306a36Sopenharmony_ci			html += td.format("%d" % (list[mode]['data'].index(d) + 1)) # row
445262306a36Sopenharmony_ci			html += td.format(mode)										# mode
445362306a36Sopenharmony_ci			html += td.format(d[0])										# host
445462306a36Sopenharmony_ci			html += td.format(d[1])										# kernel
445562306a36Sopenharmony_ci			html += td.format(d[2])										# time
445662306a36Sopenharmony_ci			html += td.format(d[6])										# result
445762306a36Sopenharmony_ci			html += td.format(d[7])										# issues
445862306a36Sopenharmony_ci			html += tdh.format('%.3f ms' % d[3], tHigh[0]) if d[3] else td.format('')	# suspend
445962306a36Sopenharmony_ci			html += tdh.format('%.3f ms' % d[4], tHigh[1]) if d[4] else td.format('')	# resume
446062306a36Sopenharmony_ci			html += td.format(d[8])										# sus_worst
446162306a36Sopenharmony_ci			html += td.format('%.3f ms' % d[9])	if d[9] else td.format('')		# sus_worst time
446262306a36Sopenharmony_ci			html += td.format(d[10])									# res_worst
446362306a36Sopenharmony_ci			html += td.format('%.3f ms' % d[11]) if d[11] else td.format('')	# res_worst time
446462306a36Sopenharmony_ci			if useturbo:
446562306a36Sopenharmony_ci				html += td.format(d[12])								# pkg_pc10
446662306a36Sopenharmony_ci				html += td.format(d[13])								# syslpi
446762306a36Sopenharmony_ci			if usewifi:
446862306a36Sopenharmony_ci				html += td.format(d[14])								# wifi
446962306a36Sopenharmony_ci			html += tdlink.format(d[5]) if d[5] else td.format('')		# url
447062306a36Sopenharmony_ci			html += '</tr>\n'
447162306a36Sopenharmony_ci			num += 1
447262306a36Sopenharmony_ci
447362306a36Sopenharmony_ci	# flush the data to file
447462306a36Sopenharmony_ci	hf = open(htmlfile, 'w')
447562306a36Sopenharmony_ci	hf.write(html+'</table>\n</body>\n</html>\n')
447662306a36Sopenharmony_ci	hf.close()
447762306a36Sopenharmony_ci
447862306a36Sopenharmony_cidef createHTMLDeviceSummary(testruns, htmlfile, title):
447962306a36Sopenharmony_ci	html = summaryCSS('Device Summary - SleepGraph', False)
448062306a36Sopenharmony_ci
448162306a36Sopenharmony_ci	# create global device list from all tests
448262306a36Sopenharmony_ci	devall = dict()
448362306a36Sopenharmony_ci	for data in testruns:
448462306a36Sopenharmony_ci		host, url, devlist = data['host'], data['url'], data['devlist']
448562306a36Sopenharmony_ci		for type in devlist:
448662306a36Sopenharmony_ci			if type not in devall:
448762306a36Sopenharmony_ci				devall[type] = dict()
448862306a36Sopenharmony_ci			mdevlist, devlist = devall[type], data['devlist'][type]
448962306a36Sopenharmony_ci			for name in devlist:
449062306a36Sopenharmony_ci				length = devlist[name]
449162306a36Sopenharmony_ci				if name not in mdevlist:
449262306a36Sopenharmony_ci					mdevlist[name] = {'name': name, 'host': host,
449362306a36Sopenharmony_ci						'worst': length, 'total': length, 'count': 1,
449462306a36Sopenharmony_ci						'url': url}
449562306a36Sopenharmony_ci				else:
449662306a36Sopenharmony_ci					if length > mdevlist[name]['worst']:
449762306a36Sopenharmony_ci						mdevlist[name]['worst'] = length
449862306a36Sopenharmony_ci						mdevlist[name]['url'] = url
449962306a36Sopenharmony_ci						mdevlist[name]['host'] = host
450062306a36Sopenharmony_ci					mdevlist[name]['total'] += length
450162306a36Sopenharmony_ci					mdevlist[name]['count'] += 1
450262306a36Sopenharmony_ci
450362306a36Sopenharmony_ci	# generate the html
450462306a36Sopenharmony_ci	th = '\t<th>{0}</th>\n'
450562306a36Sopenharmony_ci	td = '\t<td align=center>{0}</td>\n'
450662306a36Sopenharmony_ci	tdr = '\t<td align=right>{0}</td>\n'
450762306a36Sopenharmony_ci	tdlink = '\t<td align=center><a href="{0}">html</a></td>\n'
450862306a36Sopenharmony_ci	limit = 1
450962306a36Sopenharmony_ci	for type in sorted(devall, reverse=True):
451062306a36Sopenharmony_ci		num = 0
451162306a36Sopenharmony_ci		devlist = devall[type]
451262306a36Sopenharmony_ci		# table header
451362306a36Sopenharmony_ci		html += '<div class="stamp">%s (%s devices > %d ms)</div><table>\n' % \
451462306a36Sopenharmony_ci			(title, type.upper(), limit)
451562306a36Sopenharmony_ci		html += '<tr>\n' + '<th align=right>Device Name</th>' +\
451662306a36Sopenharmony_ci			th.format('Average Time') + th.format('Count') +\
451762306a36Sopenharmony_ci			th.format('Worst Time') + th.format('Host (worst time)') +\
451862306a36Sopenharmony_ci			th.format('Link (worst time)') + '</tr>\n'
451962306a36Sopenharmony_ci		for name in sorted(devlist, key=lambda k:(devlist[k]['worst'], \
452062306a36Sopenharmony_ci			devlist[k]['total'], devlist[k]['name']), reverse=True):
452162306a36Sopenharmony_ci			data = devall[type][name]
452262306a36Sopenharmony_ci			data['average'] = data['total'] / data['count']
452362306a36Sopenharmony_ci			if data['average'] < limit:
452462306a36Sopenharmony_ci				continue
452562306a36Sopenharmony_ci			# row classes - alternate row color
452662306a36Sopenharmony_ci			rcls = ['alt'] if num % 2 == 1 else []
452762306a36Sopenharmony_ci			html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n'
452862306a36Sopenharmony_ci			html += tdr.format(data['name'])				# name
452962306a36Sopenharmony_ci			html += td.format('%.3f ms' % data['average'])	# average
453062306a36Sopenharmony_ci			html += td.format(data['count'])				# count
453162306a36Sopenharmony_ci			html += td.format('%.3f ms' % data['worst'])	# worst
453262306a36Sopenharmony_ci			html += td.format(data['host'])					# host
453362306a36Sopenharmony_ci			html += tdlink.format(data['url'])				# url
453462306a36Sopenharmony_ci			html += '</tr>\n'
453562306a36Sopenharmony_ci			num += 1
453662306a36Sopenharmony_ci		html += '</table>\n'
453762306a36Sopenharmony_ci
453862306a36Sopenharmony_ci	# flush the data to file
453962306a36Sopenharmony_ci	hf = open(htmlfile, 'w')
454062306a36Sopenharmony_ci	hf.write(html+'</body>\n</html>\n')
454162306a36Sopenharmony_ci	hf.close()
454262306a36Sopenharmony_ci	return devall
454362306a36Sopenharmony_ci
454462306a36Sopenharmony_cidef createHTMLIssuesSummary(testruns, issues, htmlfile, title, extra=''):
454562306a36Sopenharmony_ci	multihost = len([e for e in issues if len(e['urls']) > 1]) > 0
454662306a36Sopenharmony_ci	html = summaryCSS('Issues Summary - SleepGraph', False)
454762306a36Sopenharmony_ci	total = len(testruns)
454862306a36Sopenharmony_ci
454962306a36Sopenharmony_ci	# generate the html
455062306a36Sopenharmony_ci	th = '\t<th>{0}</th>\n'
455162306a36Sopenharmony_ci	td = '\t<td align={0}>{1}</td>\n'
455262306a36Sopenharmony_ci	tdlink = '<a href="{1}">{0}</a>'
455362306a36Sopenharmony_ci	subtitle = '%d issues' % len(issues) if len(issues) > 0 else 'no issues'
455462306a36Sopenharmony_ci	html += '<div class="stamp">%s (%s)</div><table>\n' % (title, subtitle)
455562306a36Sopenharmony_ci	html += '<tr>\n' + th.format('Issue') + th.format('Count')
455662306a36Sopenharmony_ci	if multihost:
455762306a36Sopenharmony_ci		html += th.format('Hosts')
455862306a36Sopenharmony_ci	html += th.format('Tests') + th.format('Fail Rate') +\
455962306a36Sopenharmony_ci		th.format('First Instance') + '</tr>\n'
456062306a36Sopenharmony_ci
456162306a36Sopenharmony_ci	num = 0
456262306a36Sopenharmony_ci	for e in sorted(issues, key=lambda v:v['count'], reverse=True):
456362306a36Sopenharmony_ci		testtotal = 0
456462306a36Sopenharmony_ci		links = []
456562306a36Sopenharmony_ci		for host in sorted(e['urls']):
456662306a36Sopenharmony_ci			links.append(tdlink.format(host, e['urls'][host][0]))
456762306a36Sopenharmony_ci			testtotal += len(e['urls'][host])
456862306a36Sopenharmony_ci		rate = '%d/%d (%.2f%%)' % (testtotal, total, 100*float(testtotal)/float(total))
456962306a36Sopenharmony_ci		# row classes - alternate row color
457062306a36Sopenharmony_ci		rcls = ['alt'] if num % 2 == 1 else []
457162306a36Sopenharmony_ci		html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n'
457262306a36Sopenharmony_ci		html += td.format('left', e['line'])		# issue
457362306a36Sopenharmony_ci		html += td.format('center', e['count'])		# count
457462306a36Sopenharmony_ci		if multihost:
457562306a36Sopenharmony_ci			html += td.format('center', len(e['urls']))	# hosts
457662306a36Sopenharmony_ci		html += td.format('center', testtotal)		# test count
457762306a36Sopenharmony_ci		html += td.format('center', rate)			# test rate
457862306a36Sopenharmony_ci		html += td.format('center nowrap', '<br>'.join(links))	# links
457962306a36Sopenharmony_ci		html += '</tr>\n'
458062306a36Sopenharmony_ci		num += 1
458162306a36Sopenharmony_ci
458262306a36Sopenharmony_ci	# flush the data to file
458362306a36Sopenharmony_ci	hf = open(htmlfile, 'w')
458462306a36Sopenharmony_ci	hf.write(html+'</table>\n'+extra+'</body>\n</html>\n')
458562306a36Sopenharmony_ci	hf.close()
458662306a36Sopenharmony_ci	return issues
458762306a36Sopenharmony_ci
458862306a36Sopenharmony_cidef ordinal(value):
458962306a36Sopenharmony_ci	suffix = 'th'
459062306a36Sopenharmony_ci	if value < 10 or value > 19:
459162306a36Sopenharmony_ci		if value % 10 == 1:
459262306a36Sopenharmony_ci			suffix = 'st'
459362306a36Sopenharmony_ci		elif value % 10 == 2:
459462306a36Sopenharmony_ci			suffix = 'nd'
459562306a36Sopenharmony_ci		elif value % 10 == 3:
459662306a36Sopenharmony_ci			suffix = 'rd'
459762306a36Sopenharmony_ci	return '%d%s' % (value, suffix)
459862306a36Sopenharmony_ci
459962306a36Sopenharmony_ci# Function: createHTML
460062306a36Sopenharmony_ci# Description:
460162306a36Sopenharmony_ci#	 Create the output html file from the resident test data
460262306a36Sopenharmony_ci# Arguments:
460362306a36Sopenharmony_ci#	 testruns: array of Data objects from parseKernelLog or parseTraceLog
460462306a36Sopenharmony_ci# Output:
460562306a36Sopenharmony_ci#	 True if the html file was created, false if it failed
460662306a36Sopenharmony_cidef createHTML(testruns, testfail):
460762306a36Sopenharmony_ci	if len(testruns) < 1:
460862306a36Sopenharmony_ci		pprint('ERROR: Not enough test data to build a timeline')
460962306a36Sopenharmony_ci		return
461062306a36Sopenharmony_ci
461162306a36Sopenharmony_ci	kerror = False
461262306a36Sopenharmony_ci	for data in testruns:
461362306a36Sopenharmony_ci		if data.kerror:
461462306a36Sopenharmony_ci			kerror = True
461562306a36Sopenharmony_ci		if(sysvals.suspendmode in ['freeze', 'standby']):
461662306a36Sopenharmony_ci			data.trimFreezeTime(testruns[-1].tSuspended)
461762306a36Sopenharmony_ci		else:
461862306a36Sopenharmony_ci			data.getMemTime()
461962306a36Sopenharmony_ci
462062306a36Sopenharmony_ci	# html function templates
462162306a36Sopenharmony_ci	html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">{2}&rarr;</div>\n'
462262306a36Sopenharmony_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'
462362306a36Sopenharmony_ci	html_cpuexec = '<div class="jiffie" style="left:{0}%;top:{1}px;height:{2}px;width:{3}%;background:{4};"></div>\n'
462462306a36Sopenharmony_ci	html_timetotal = '<table class="time1">\n<tr>'\
462562306a36Sopenharmony_ci		'<td class="green" title="{3}">{2} Suspend Time: <b>{0} ms</b></td>'\
462662306a36Sopenharmony_ci		'<td class="yellow" title="{4}">{2} Resume Time: <b>{1} ms</b></td>'\
462762306a36Sopenharmony_ci		'</tr>\n</table>\n'
462862306a36Sopenharmony_ci	html_timetotal2 = '<table class="time1">\n<tr>'\
462962306a36Sopenharmony_ci		'<td class="green" title="{4}">{3} Suspend Time: <b>{0} ms</b></td>'\
463062306a36Sopenharmony_ci		'<td class="gray" title="time spent in low-power mode with clock running">'+sysvals.suspendmode+' time: <b>{1} ms</b></td>'\
463162306a36Sopenharmony_ci		'<td class="yellow" title="{5}">{3} Resume Time: <b>{2} ms</b></td>'\
463262306a36Sopenharmony_ci		'</tr>\n</table>\n'
463362306a36Sopenharmony_ci	html_timetotal3 = '<table class="time1">\n<tr>'\
463462306a36Sopenharmony_ci		'<td class="green">Execution Time: <b>{0} ms</b></td>'\
463562306a36Sopenharmony_ci		'<td class="yellow">Command: <b>{1}</b></td>'\
463662306a36Sopenharmony_ci		'</tr>\n</table>\n'
463762306a36Sopenharmony_ci	html_fail = '<table class="testfail"><tr><td>{0}</td></tr></table>\n'
463862306a36Sopenharmony_ci	html_kdesc = '<td class="{3}" title="time spent in kernel execution">{0}Kernel {2}: {1} ms</td>'
463962306a36Sopenharmony_ci	html_fwdesc = '<td class="{3}" title="time spent in firmware">{0}Firmware {2}: {1} ms</td>'
464062306a36Sopenharmony_ci	html_wifdesc = '<td class="yellow" title="time for wifi to reconnect after resume complete ({2})">{0}Wifi Resume: {1}</td>'
464162306a36Sopenharmony_ci
464262306a36Sopenharmony_ci	# html format variables
464362306a36Sopenharmony_ci	scaleH = 20
464462306a36Sopenharmony_ci	if kerror:
464562306a36Sopenharmony_ci		scaleH = 40
464662306a36Sopenharmony_ci
464762306a36Sopenharmony_ci	# device timeline
464862306a36Sopenharmony_ci	devtl = Timeline(30, scaleH)
464962306a36Sopenharmony_ci
465062306a36Sopenharmony_ci	# write the test title and general info header
465162306a36Sopenharmony_ci	devtl.createHeader(sysvals, testruns[0].stamp)
465262306a36Sopenharmony_ci
465362306a36Sopenharmony_ci	# Generate the header for this timeline
465462306a36Sopenharmony_ci	for data in testruns:
465562306a36Sopenharmony_ci		tTotal = data.end - data.start
465662306a36Sopenharmony_ci		if(tTotal == 0):
465762306a36Sopenharmony_ci			doError('No timeline data')
465862306a36Sopenharmony_ci		if sysvals.suspendmode == 'command':
465962306a36Sopenharmony_ci			run_time = '%.0f' % (tTotal * 1000)
466062306a36Sopenharmony_ci			if sysvals.testcommand:
466162306a36Sopenharmony_ci				testdesc = sysvals.testcommand
466262306a36Sopenharmony_ci			else:
466362306a36Sopenharmony_ci				testdesc = 'unknown'
466462306a36Sopenharmony_ci			if(len(testruns) > 1):
466562306a36Sopenharmony_ci				testdesc = ordinal(data.testnumber+1)+' '+testdesc
466662306a36Sopenharmony_ci			thtml = html_timetotal3.format(run_time, testdesc)
466762306a36Sopenharmony_ci			devtl.html += thtml
466862306a36Sopenharmony_ci			continue
466962306a36Sopenharmony_ci		# typical full suspend/resume header
467062306a36Sopenharmony_ci		stot, rtot = sktime, rktime = data.getTimeValues()
467162306a36Sopenharmony_ci		ssrc, rsrc, testdesc, testdesc2 = ['kernel'], ['kernel'], 'Kernel', ''
467262306a36Sopenharmony_ci		if data.fwValid:
467362306a36Sopenharmony_ci			stot += (data.fwSuspend/1000000.0)
467462306a36Sopenharmony_ci			rtot += (data.fwResume/1000000.0)
467562306a36Sopenharmony_ci			ssrc.append('firmware')
467662306a36Sopenharmony_ci			rsrc.append('firmware')
467762306a36Sopenharmony_ci			testdesc = 'Total'
467862306a36Sopenharmony_ci		if 'time' in data.wifi and data.wifi['stat'] != 'timeout':
467962306a36Sopenharmony_ci			rtot += data.end - data.tKernRes + (data.wifi['time'] * 1000.0)
468062306a36Sopenharmony_ci			rsrc.append('wifi')
468162306a36Sopenharmony_ci			testdesc = 'Total'
468262306a36Sopenharmony_ci		suspend_time, resume_time = '%.3f' % stot, '%.3f' % rtot
468362306a36Sopenharmony_ci		stitle = 'time from kernel suspend start to %s mode [%s time]' % \
468462306a36Sopenharmony_ci			(sysvals.suspendmode, ' & '.join(ssrc))
468562306a36Sopenharmony_ci		rtitle = 'time from %s mode to kernel resume complete [%s time]' % \
468662306a36Sopenharmony_ci			(sysvals.suspendmode, ' & '.join(rsrc))
468762306a36Sopenharmony_ci		if(len(testruns) > 1):
468862306a36Sopenharmony_ci			testdesc = testdesc2 = ordinal(data.testnumber+1)
468962306a36Sopenharmony_ci			testdesc2 += ' '
469062306a36Sopenharmony_ci		if(len(data.tLow) == 0):
469162306a36Sopenharmony_ci			thtml = html_timetotal.format(suspend_time, \
469262306a36Sopenharmony_ci				resume_time, testdesc, stitle, rtitle)
469362306a36Sopenharmony_ci		else:
469462306a36Sopenharmony_ci			low_time = '+'.join(data.tLow)
469562306a36Sopenharmony_ci			thtml = html_timetotal2.format(suspend_time, low_time, \
469662306a36Sopenharmony_ci				resume_time, testdesc, stitle, rtitle)
469762306a36Sopenharmony_ci		devtl.html += thtml
469862306a36Sopenharmony_ci		if not data.fwValid and 'dev' not in data.wifi:
469962306a36Sopenharmony_ci			continue
470062306a36Sopenharmony_ci		# extra detail when the times come from multiple sources
470162306a36Sopenharmony_ci		thtml = '<table class="time2">\n<tr>'
470262306a36Sopenharmony_ci		thtml += html_kdesc.format(testdesc2, '%.3f'%sktime, 'Suspend', 'green')
470362306a36Sopenharmony_ci		if data.fwValid:
470462306a36Sopenharmony_ci			sftime = '%.3f'%(data.fwSuspend / 1000000.0)
470562306a36Sopenharmony_ci			rftime = '%.3f'%(data.fwResume / 1000000.0)
470662306a36Sopenharmony_ci			thtml += html_fwdesc.format(testdesc2, sftime, 'Suspend', 'green')
470762306a36Sopenharmony_ci			thtml += html_fwdesc.format(testdesc2, rftime, 'Resume', 'yellow')
470862306a36Sopenharmony_ci		thtml += html_kdesc.format(testdesc2, '%.3f'%rktime, 'Resume', 'yellow')
470962306a36Sopenharmony_ci		if 'time' in data.wifi:
471062306a36Sopenharmony_ci			if data.wifi['stat'] != 'timeout':
471162306a36Sopenharmony_ci				wtime = '%.0f ms'%(data.end - data.tKernRes + (data.wifi['time'] * 1000.0))
471262306a36Sopenharmony_ci			else:
471362306a36Sopenharmony_ci				wtime = 'TIMEOUT'
471462306a36Sopenharmony_ci			thtml += html_wifdesc.format(testdesc2, wtime, data.wifi['dev'])
471562306a36Sopenharmony_ci		thtml += '</tr>\n</table>\n'
471662306a36Sopenharmony_ci		devtl.html += thtml
471762306a36Sopenharmony_ci	if testfail:
471862306a36Sopenharmony_ci		devtl.html += html_fail.format(testfail)
471962306a36Sopenharmony_ci
472062306a36Sopenharmony_ci	# time scale for potentially multiple datasets
472162306a36Sopenharmony_ci	t0 = testruns[0].start
472262306a36Sopenharmony_ci	tMax = testruns[-1].end
472362306a36Sopenharmony_ci	tTotal = tMax - t0
472462306a36Sopenharmony_ci
472562306a36Sopenharmony_ci	# determine the maximum number of rows we need to draw
472662306a36Sopenharmony_ci	fulllist = []
472762306a36Sopenharmony_ci	threadlist = []
472862306a36Sopenharmony_ci	pscnt = 0
472962306a36Sopenharmony_ci	devcnt = 0
473062306a36Sopenharmony_ci	for data in testruns:
473162306a36Sopenharmony_ci		data.selectTimelineDevices('%f', tTotal, sysvals.mindevlen)
473262306a36Sopenharmony_ci		for group in data.devicegroups:
473362306a36Sopenharmony_ci			devlist = []
473462306a36Sopenharmony_ci			for phase in group:
473562306a36Sopenharmony_ci				for devname in sorted(data.tdevlist[phase]):
473662306a36Sopenharmony_ci					d = DevItem(data.testnumber, phase, data.dmesg[phase]['list'][devname])
473762306a36Sopenharmony_ci					devlist.append(d)
473862306a36Sopenharmony_ci					if d.isa('kth'):
473962306a36Sopenharmony_ci						threadlist.append(d)
474062306a36Sopenharmony_ci					else:
474162306a36Sopenharmony_ci						if d.isa('ps'):
474262306a36Sopenharmony_ci							pscnt += 1
474362306a36Sopenharmony_ci						else:
474462306a36Sopenharmony_ci							devcnt += 1
474562306a36Sopenharmony_ci						fulllist.append(d)
474662306a36Sopenharmony_ci			if sysvals.mixedphaseheight:
474762306a36Sopenharmony_ci				devtl.getPhaseRows(devlist)
474862306a36Sopenharmony_ci	if not sysvals.mixedphaseheight:
474962306a36Sopenharmony_ci		if len(threadlist) > 0 and len(fulllist) > 0:
475062306a36Sopenharmony_ci			if pscnt > 0 and devcnt > 0:
475162306a36Sopenharmony_ci				msg = 'user processes & device pm callbacks'
475262306a36Sopenharmony_ci			elif pscnt > 0:
475362306a36Sopenharmony_ci				msg = 'user processes'
475462306a36Sopenharmony_ci			else:
475562306a36Sopenharmony_ci				msg = 'device pm callbacks'
475662306a36Sopenharmony_ci			d = testruns[0].addHorizontalDivider(msg, testruns[-1].end)
475762306a36Sopenharmony_ci			fulllist.insert(0, d)
475862306a36Sopenharmony_ci		devtl.getPhaseRows(fulllist)
475962306a36Sopenharmony_ci		if len(threadlist) > 0:
476062306a36Sopenharmony_ci			d = testruns[0].addHorizontalDivider('asynchronous kernel threads', testruns[-1].end)
476162306a36Sopenharmony_ci			threadlist.insert(0, d)
476262306a36Sopenharmony_ci			devtl.getPhaseRows(threadlist, devtl.rows)
476362306a36Sopenharmony_ci	devtl.calcTotalRows()
476462306a36Sopenharmony_ci
476562306a36Sopenharmony_ci	# draw the full timeline
476662306a36Sopenharmony_ci	devtl.createZoomBox(sysvals.suspendmode, len(testruns))
476762306a36Sopenharmony_ci	for data in testruns:
476862306a36Sopenharmony_ci		# draw each test run and block chronologically
476962306a36Sopenharmony_ci		phases = {'suspend':[],'resume':[]}
477062306a36Sopenharmony_ci		for phase in data.sortedPhases():
477162306a36Sopenharmony_ci			if data.dmesg[phase]['start'] >= data.tSuspended:
477262306a36Sopenharmony_ci				phases['resume'].append(phase)
477362306a36Sopenharmony_ci			else:
477462306a36Sopenharmony_ci				phases['suspend'].append(phase)
477562306a36Sopenharmony_ci		# now draw the actual timeline blocks
477662306a36Sopenharmony_ci		for dir in phases:
477762306a36Sopenharmony_ci			# draw suspend and resume blocks separately
477862306a36Sopenharmony_ci			bname = '%s%d' % (dir[0], data.testnumber)
477962306a36Sopenharmony_ci			if dir == 'suspend':
478062306a36Sopenharmony_ci				m0 = data.start
478162306a36Sopenharmony_ci				mMax = data.tSuspended
478262306a36Sopenharmony_ci				left = '%f' % (((m0-t0)*100.0)/tTotal)
478362306a36Sopenharmony_ci			else:
478462306a36Sopenharmony_ci				m0 = data.tSuspended
478562306a36Sopenharmony_ci				mMax = data.end
478662306a36Sopenharmony_ci				# in an x2 run, remove any gap between blocks
478762306a36Sopenharmony_ci				if len(testruns) > 1 and data.testnumber == 0:
478862306a36Sopenharmony_ci					mMax = testruns[1].start
478962306a36Sopenharmony_ci				left = '%f' % ((((m0-t0)*100.0)+sysvals.srgap/2)/tTotal)
479062306a36Sopenharmony_ci			mTotal = mMax - m0
479162306a36Sopenharmony_ci			# if a timeline block is 0 length, skip altogether
479262306a36Sopenharmony_ci			if mTotal == 0:
479362306a36Sopenharmony_ci				continue
479462306a36Sopenharmony_ci			width = '%f' % (((mTotal*100.0)-sysvals.srgap/2)/tTotal)
479562306a36Sopenharmony_ci			devtl.html += devtl.html_tblock.format(bname, left, width, devtl.scaleH)
479662306a36Sopenharmony_ci			for b in phases[dir]:
479762306a36Sopenharmony_ci				# draw the phase color background
479862306a36Sopenharmony_ci				phase = data.dmesg[b]
479962306a36Sopenharmony_ci				length = phase['end']-phase['start']
480062306a36Sopenharmony_ci				left = '%f' % (((phase['start']-m0)*100.0)/mTotal)
480162306a36Sopenharmony_ci				width = '%f' % ((length*100.0)/mTotal)
480262306a36Sopenharmony_ci				devtl.html += devtl.html_phase.format(left, width, \
480362306a36Sopenharmony_ci					'%.3f'%devtl.scaleH, '%.3f'%devtl.bodyH, \
480462306a36Sopenharmony_ci					data.dmesg[b]['color'], '')
480562306a36Sopenharmony_ci			for e in data.errorinfo[dir]:
480662306a36Sopenharmony_ci				# draw red lines for any kernel errors found
480762306a36Sopenharmony_ci				type, t, idx1, idx2 = e
480862306a36Sopenharmony_ci				id = '%d_%d' % (idx1, idx2)
480962306a36Sopenharmony_ci				right = '%f' % (((mMax-t)*100.0)/mTotal)
481062306a36Sopenharmony_ci				devtl.html += html_error.format(right, id, type)
481162306a36Sopenharmony_ci			for b in phases[dir]:
481262306a36Sopenharmony_ci				# draw the devices for this phase
481362306a36Sopenharmony_ci				phaselist = data.dmesg[b]['list']
481462306a36Sopenharmony_ci				for d in sorted(data.tdevlist[b]):
481562306a36Sopenharmony_ci					dname = d if ('[' not in d or 'CPU' in d) else d.split('[')[0]
481662306a36Sopenharmony_ci					name, dev = dname, phaselist[d]
481762306a36Sopenharmony_ci					drv = xtraclass = xtrainfo = xtrastyle = ''
481862306a36Sopenharmony_ci					if 'htmlclass' in dev:
481962306a36Sopenharmony_ci						xtraclass = dev['htmlclass']
482062306a36Sopenharmony_ci					if 'color' in dev:
482162306a36Sopenharmony_ci						xtrastyle = 'background:%s;' % dev['color']
482262306a36Sopenharmony_ci					if(d in sysvals.devprops):
482362306a36Sopenharmony_ci						name = sysvals.devprops[d].altName(d)
482462306a36Sopenharmony_ci						xtraclass = sysvals.devprops[d].xtraClass()
482562306a36Sopenharmony_ci						xtrainfo = sysvals.devprops[d].xtraInfo()
482662306a36Sopenharmony_ci					elif xtraclass == ' kth':
482762306a36Sopenharmony_ci						xtrainfo = ' kernel_thread'
482862306a36Sopenharmony_ci					if('drv' in dev and dev['drv']):
482962306a36Sopenharmony_ci						drv = ' {%s}' % dev['drv']
483062306a36Sopenharmony_ci					rowheight = devtl.phaseRowHeight(data.testnumber, b, dev['row'])
483162306a36Sopenharmony_ci					rowtop = devtl.phaseRowTop(data.testnumber, b, dev['row'])
483262306a36Sopenharmony_ci					top = '%.3f' % (rowtop + devtl.scaleH)
483362306a36Sopenharmony_ci					left = '%f' % (((dev['start']-m0)*100)/mTotal)
483462306a36Sopenharmony_ci					width = '%f' % (((dev['end']-dev['start'])*100)/mTotal)
483562306a36Sopenharmony_ci					length = ' (%0.3f ms) ' % ((dev['end']-dev['start'])*1000)
483662306a36Sopenharmony_ci					title = name+drv+xtrainfo+length
483762306a36Sopenharmony_ci					if sysvals.suspendmode == 'command':
483862306a36Sopenharmony_ci						title += sysvals.testcommand
483962306a36Sopenharmony_ci					elif xtraclass == ' ps':
484062306a36Sopenharmony_ci						if 'suspend' in b:
484162306a36Sopenharmony_ci							title += 'pre_suspend_process'
484262306a36Sopenharmony_ci						else:
484362306a36Sopenharmony_ci							title += 'post_resume_process'
484462306a36Sopenharmony_ci					else:
484562306a36Sopenharmony_ci						title += b
484662306a36Sopenharmony_ci					devtl.html += devtl.html_device.format(dev['id'], \
484762306a36Sopenharmony_ci						title, left, top, '%.3f'%rowheight, width, \
484862306a36Sopenharmony_ci						dname+drv, xtraclass, xtrastyle)
484962306a36Sopenharmony_ci					if('cpuexec' in dev):
485062306a36Sopenharmony_ci						for t in sorted(dev['cpuexec']):
485162306a36Sopenharmony_ci							start, end = t
485262306a36Sopenharmony_ci							height = '%.3f' % (rowheight/3)
485362306a36Sopenharmony_ci							top = '%.3f' % (rowtop + devtl.scaleH + 2*rowheight/3)
485462306a36Sopenharmony_ci							left = '%f' % (((start-m0)*100)/mTotal)
485562306a36Sopenharmony_ci							width = '%f' % ((end-start)*100/mTotal)
485662306a36Sopenharmony_ci							color = 'rgba(255, 0, 0, %f)' % dev['cpuexec'][t]
485762306a36Sopenharmony_ci							devtl.html += \
485862306a36Sopenharmony_ci								html_cpuexec.format(left, top, height, width, color)
485962306a36Sopenharmony_ci					if('src' not in dev):
486062306a36Sopenharmony_ci						continue
486162306a36Sopenharmony_ci					# draw any trace events for this device
486262306a36Sopenharmony_ci					for e in dev['src']:
486362306a36Sopenharmony_ci						if e.length == 0:
486462306a36Sopenharmony_ci							continue
486562306a36Sopenharmony_ci						height = '%.3f' % devtl.rowH
486662306a36Sopenharmony_ci						top = '%.3f' % (rowtop + devtl.scaleH + (e.row*devtl.rowH))
486762306a36Sopenharmony_ci						left = '%f' % (((e.time-m0)*100)/mTotal)
486862306a36Sopenharmony_ci						width = '%f' % (e.length*100/mTotal)
486962306a36Sopenharmony_ci						xtrastyle = ''
487062306a36Sopenharmony_ci						if e.color:
487162306a36Sopenharmony_ci							xtrastyle = 'background:%s;' % e.color
487262306a36Sopenharmony_ci						devtl.html += \
487362306a36Sopenharmony_ci							html_traceevent.format(e.title(), \
487462306a36Sopenharmony_ci								left, top, height, width, e.text(), '', xtrastyle)
487562306a36Sopenharmony_ci			# draw the time scale, try to make the number of labels readable
487662306a36Sopenharmony_ci			devtl.createTimeScale(m0, mMax, tTotal, dir)
487762306a36Sopenharmony_ci			devtl.html += '</div>\n'
487862306a36Sopenharmony_ci
487962306a36Sopenharmony_ci	# timeline is finished
488062306a36Sopenharmony_ci	devtl.html += '</div>\n</div>\n'
488162306a36Sopenharmony_ci
488262306a36Sopenharmony_ci	# draw a legend which describes the phases by color
488362306a36Sopenharmony_ci	if sysvals.suspendmode != 'command':
488462306a36Sopenharmony_ci		phasedef = testruns[-1].phasedef
488562306a36Sopenharmony_ci		devtl.html += '<div class="legend">\n'
488662306a36Sopenharmony_ci		pdelta = 100.0/len(phasedef.keys())
488762306a36Sopenharmony_ci		pmargin = pdelta / 4.0
488862306a36Sopenharmony_ci		for phase in sorted(phasedef, key=lambda k:phasedef[k]['order']):
488962306a36Sopenharmony_ci			id, p = '', phasedef[phase]
489062306a36Sopenharmony_ci			for word in phase.split('_'):
489162306a36Sopenharmony_ci				id += word[0]
489262306a36Sopenharmony_ci			order = '%.2f' % ((p['order'] * pdelta) + pmargin)
489362306a36Sopenharmony_ci			name = phase.replace('_', ' &nbsp;')
489462306a36Sopenharmony_ci			devtl.html += devtl.html_legend.format(order, p['color'], name, id)
489562306a36Sopenharmony_ci		devtl.html += '</div>\n'
489662306a36Sopenharmony_ci
489762306a36Sopenharmony_ci	hf = open(sysvals.htmlfile, 'w')
489862306a36Sopenharmony_ci	addCSS(hf, sysvals, len(testruns), kerror)
489962306a36Sopenharmony_ci
490062306a36Sopenharmony_ci	# write the device timeline
490162306a36Sopenharmony_ci	hf.write(devtl.html)
490262306a36Sopenharmony_ci	hf.write('<div id="devicedetailtitle"></div>\n')
490362306a36Sopenharmony_ci	hf.write('<div id="devicedetail" style="display:none;">\n')
490462306a36Sopenharmony_ci	# draw the colored boxes for the device detail section
490562306a36Sopenharmony_ci	for data in testruns:
490662306a36Sopenharmony_ci		hf.write('<div id="devicedetail%d">\n' % data.testnumber)
490762306a36Sopenharmony_ci		pscolor = 'linear-gradient(to top left, #ccc, #eee)'
490862306a36Sopenharmony_ci		hf.write(devtl.html_phaselet.format('pre_suspend_process', \
490962306a36Sopenharmony_ci			'0', '0', pscolor))
491062306a36Sopenharmony_ci		for b in data.sortedPhases():
491162306a36Sopenharmony_ci			phase = data.dmesg[b]
491262306a36Sopenharmony_ci			length = phase['end']-phase['start']
491362306a36Sopenharmony_ci			left = '%.3f' % (((phase['start']-t0)*100.0)/tTotal)
491462306a36Sopenharmony_ci			width = '%.3f' % ((length*100.0)/tTotal)
491562306a36Sopenharmony_ci			hf.write(devtl.html_phaselet.format(b, left, width, \
491662306a36Sopenharmony_ci				data.dmesg[b]['color']))
491762306a36Sopenharmony_ci		hf.write(devtl.html_phaselet.format('post_resume_process', \
491862306a36Sopenharmony_ci			'0', '0', pscolor))
491962306a36Sopenharmony_ci		if sysvals.suspendmode == 'command':
492062306a36Sopenharmony_ci			hf.write(devtl.html_phaselet.format('cmdexec', '0', '0', pscolor))
492162306a36Sopenharmony_ci		hf.write('</div>\n')
492262306a36Sopenharmony_ci	hf.write('</div>\n')
492362306a36Sopenharmony_ci
492462306a36Sopenharmony_ci	# write the ftrace data (callgraph)
492562306a36Sopenharmony_ci	if sysvals.cgtest >= 0 and len(testruns) > sysvals.cgtest:
492662306a36Sopenharmony_ci		data = testruns[sysvals.cgtest]
492762306a36Sopenharmony_ci	else:
492862306a36Sopenharmony_ci		data = testruns[-1]
492962306a36Sopenharmony_ci	if sysvals.usecallgraph:
493062306a36Sopenharmony_ci		addCallgraphs(sysvals, hf, data)
493162306a36Sopenharmony_ci
493262306a36Sopenharmony_ci	# add the test log as a hidden div
493362306a36Sopenharmony_ci	if sysvals.testlog and sysvals.logmsg:
493462306a36Sopenharmony_ci		hf.write('<div id="testlog" style="display:none;">\n'+sysvals.logmsg+'</div>\n')
493562306a36Sopenharmony_ci	# add the dmesg log as a hidden div
493662306a36Sopenharmony_ci	if sysvals.dmesglog and sysvals.dmesgfile:
493762306a36Sopenharmony_ci		hf.write('<div id="dmesglog" style="display:none;">\n')
493862306a36Sopenharmony_ci		lf = sysvals.openlog(sysvals.dmesgfile, 'r')
493962306a36Sopenharmony_ci		for line in lf:
494062306a36Sopenharmony_ci			line = line.replace('<', '&lt').replace('>', '&gt')
494162306a36Sopenharmony_ci			hf.write(line)
494262306a36Sopenharmony_ci		lf.close()
494362306a36Sopenharmony_ci		hf.write('</div>\n')
494462306a36Sopenharmony_ci	# add the ftrace log as a hidden div
494562306a36Sopenharmony_ci	if sysvals.ftracelog and sysvals.ftracefile:
494662306a36Sopenharmony_ci		hf.write('<div id="ftracelog" style="display:none;">\n')
494762306a36Sopenharmony_ci		lf = sysvals.openlog(sysvals.ftracefile, 'r')
494862306a36Sopenharmony_ci		for line in lf:
494962306a36Sopenharmony_ci			hf.write(line)
495062306a36Sopenharmony_ci		lf.close()
495162306a36Sopenharmony_ci		hf.write('</div>\n')
495262306a36Sopenharmony_ci
495362306a36Sopenharmony_ci	# write the footer and close
495462306a36Sopenharmony_ci	addScriptCode(hf, testruns)
495562306a36Sopenharmony_ci	hf.write('</body>\n</html>\n')
495662306a36Sopenharmony_ci	hf.close()
495762306a36Sopenharmony_ci	return True
495862306a36Sopenharmony_ci
495962306a36Sopenharmony_cidef addCSS(hf, sv, testcount=1, kerror=False, extra=''):
496062306a36Sopenharmony_ci	kernel = sv.stamp['kernel']
496162306a36Sopenharmony_ci	host = sv.hostname[0].upper()+sv.hostname[1:]
496262306a36Sopenharmony_ci	mode = sv.suspendmode
496362306a36Sopenharmony_ci	if sv.suspendmode in suspendmodename:
496462306a36Sopenharmony_ci		mode = suspendmodename[sv.suspendmode]
496562306a36Sopenharmony_ci	title = host+' '+mode+' '+kernel
496662306a36Sopenharmony_ci
496762306a36Sopenharmony_ci	# various format changes by flags
496862306a36Sopenharmony_ci	cgchk = 'checked'
496962306a36Sopenharmony_ci	cgnchk = 'not(:checked)'
497062306a36Sopenharmony_ci	if sv.cgexp:
497162306a36Sopenharmony_ci		cgchk = 'not(:checked)'
497262306a36Sopenharmony_ci		cgnchk = 'checked'
497362306a36Sopenharmony_ci
497462306a36Sopenharmony_ci	hoverZ = 'z-index:8;'
497562306a36Sopenharmony_ci	if sv.usedevsrc:
497662306a36Sopenharmony_ci		hoverZ = ''
497762306a36Sopenharmony_ci
497862306a36Sopenharmony_ci	devlistpos = 'absolute'
497962306a36Sopenharmony_ci	if testcount > 1:
498062306a36Sopenharmony_ci		devlistpos = 'relative'
498162306a36Sopenharmony_ci
498262306a36Sopenharmony_ci	scaleTH = 20
498362306a36Sopenharmony_ci	if kerror:
498462306a36Sopenharmony_ci		scaleTH = 60
498562306a36Sopenharmony_ci
498662306a36Sopenharmony_ci	# write the html header first (html head, css code, up to body start)
498762306a36Sopenharmony_ci	html_header = '<!DOCTYPE html>\n<html>\n<head>\n\
498862306a36Sopenharmony_ci	<meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\
498962306a36Sopenharmony_ci	<title>'+title+'</title>\n\
499062306a36Sopenharmony_ci	<style type=\'text/css\'>\n\
499162306a36Sopenharmony_ci		body {overflow-y:scroll;}\n\
499262306a36Sopenharmony_ci		.stamp {width:100%;text-align:center;background:gray;line-height:30px;color:white;font:25px Arial;}\n\
499362306a36Sopenharmony_ci		.stamp.sysinfo {font:10px Arial;}\n\
499462306a36Sopenharmony_ci		.callgraph {margin-top:30px;box-shadow:5px 5px 20px black;}\n\
499562306a36Sopenharmony_ci		.callgraph article * {padding-left:28px;}\n\
499662306a36Sopenharmony_ci		h1 {color:black;font:bold 30px Times;}\n\
499762306a36Sopenharmony_ci		t0 {color:black;font:bold 30px Times;}\n\
499862306a36Sopenharmony_ci		t1 {color:black;font:30px Times;}\n\
499962306a36Sopenharmony_ci		t2 {color:black;font:25px Times;}\n\
500062306a36Sopenharmony_ci		t3 {color:black;font:20px Times;white-space:nowrap;}\n\
500162306a36Sopenharmony_ci		t4 {color:black;font:bold 30px Times;line-height:60px;white-space:nowrap;}\n\
500262306a36Sopenharmony_ci		cS {font:bold 13px Times;}\n\
500362306a36Sopenharmony_ci		table {width:100%;}\n\
500462306a36Sopenharmony_ci		.gray {background:rgba(80,80,80,0.1);}\n\
500562306a36Sopenharmony_ci		.green {background:rgba(204,255,204,0.4);}\n\
500662306a36Sopenharmony_ci		.purple {background:rgba(128,0,128,0.2);}\n\
500762306a36Sopenharmony_ci		.yellow {background:rgba(255,255,204,0.4);}\n\
500862306a36Sopenharmony_ci		.blue {background:rgba(169,208,245,0.4);}\n\
500962306a36Sopenharmony_ci		.time1 {font:22px Arial;border:1px solid;}\n\
501062306a36Sopenharmony_ci		.time2 {font:15px Arial;border-bottom:1px solid;border-left:1px solid;border-right:1px solid;}\n\
501162306a36Sopenharmony_ci		.testfail {font:bold 22px Arial;color:red;border:1px dashed;}\n\
501262306a36Sopenharmony_ci		td {text-align:center;}\n\
501362306a36Sopenharmony_ci		r {color:#500000;font:15px Tahoma;}\n\
501462306a36Sopenharmony_ci		n {color:#505050;font:15px Tahoma;}\n\
501562306a36Sopenharmony_ci		.tdhl {color:red;}\n\
501662306a36Sopenharmony_ci		.hide {display:none;}\n\
501762306a36Sopenharmony_ci		.pf {display:none;}\n\
501862306a36Sopenharmony_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\
501962306a36Sopenharmony_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\
502062306a36Sopenharmony_ci		.pf:'+cgchk+' ~ *:not(:nth-child(2)) {display:none;}\n\
502162306a36Sopenharmony_ci		.zoombox {position:relative;width:100%;overflow-x:scroll;-webkit-user-select:none;-moz-user-select:none;user-select:none;}\n\
502262306a36Sopenharmony_ci		.timeline {position:relative;font-size:14px;cursor:pointer;width:100%; overflow:hidden;background:linear-gradient(#cccccc, white);}\n\
502362306a36Sopenharmony_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\
502462306a36Sopenharmony_ci		.thread.ps {border-radius:3px;background:linear-gradient(to top, #ccc, #eee);}\n\
502562306a36Sopenharmony_ci		.thread:hover {background:white;border:1px solid red;'+hoverZ+'}\n\
502662306a36Sopenharmony_ci		.thread.sec,.thread.sec:hover {background:black;border:0;color:white;line-height:15px;font-size:10px;}\n\
502762306a36Sopenharmony_ci		.hover {background:white;border:1px solid red;'+hoverZ+'}\n\
502862306a36Sopenharmony_ci		.hover.sync {background:white;}\n\
502962306a36Sopenharmony_ci		.hover.bg,.hover.kth,.hover.sync,.hover.ps {background:white;}\n\
503062306a36Sopenharmony_ci		.jiffie {position:absolute;pointer-events: none;z-index:8;}\n\
503162306a36Sopenharmony_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\
503262306a36Sopenharmony_ci		.traceevent:hover {color:white;font-weight:bold;border:1px solid white;}\n\
503362306a36Sopenharmony_ci		.phase {position:absolute;overflow:hidden;border:0px;text-align:center;}\n\
503462306a36Sopenharmony_ci		.phaselet {float:left;overflow:hidden;border:0px;text-align:center;min-height:100px;font-size:24px;}\n\
503562306a36Sopenharmony_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\
503662306a36Sopenharmony_ci		.err {position:absolute;top:0%;height:100%;border-right:3px solid red;color:red;font:bold 14px Times;line-height:18px;}\n\
503762306a36Sopenharmony_ci		.legend {position:relative; width:100%; height:40px; text-align:center;margin-bottom:20px}\n\
503862306a36Sopenharmony_ci		.legend .square {position:absolute;cursor:pointer;top:10px; width:0px;height:20px;border:1px solid;padding-left:20px;}\n\
503962306a36Sopenharmony_ci		button {height:40px;width:200px;margin-bottom:20px;margin-top:20px;font-size:24px;}\n\
504062306a36Sopenharmony_ci		.btnfmt {position:relative;float:right;height:25px;width:auto;margin-top:3px;margin-bottom:0;font-size:10px;text-align:center;}\n\
504162306a36Sopenharmony_ci		.devlist {position:'+devlistpos+';width:190px;}\n\
504262306a36Sopenharmony_ci		a:link {color:white;text-decoration:none;}\n\
504362306a36Sopenharmony_ci		a:visited {color:white;}\n\
504462306a36Sopenharmony_ci		a:hover {color:white;}\n\
504562306a36Sopenharmony_ci		a:active {color:white;}\n\
504662306a36Sopenharmony_ci		.version {position:relative;float:left;color:white;font-size:10px;line-height:30px;margin-left:10px;}\n\
504762306a36Sopenharmony_ci		#devicedetail {min-height:100px;box-shadow:5px 5px 20px black;}\n\
504862306a36Sopenharmony_ci		.tblock {position:absolute;height:100%;background:#ddd;}\n\
504962306a36Sopenharmony_ci		.tback {position:absolute;width:100%;background:linear-gradient(#ccc, #ddd);}\n\
505062306a36Sopenharmony_ci		.bg {z-index:1;}\n\
505162306a36Sopenharmony_ci'+extra+'\
505262306a36Sopenharmony_ci	</style>\n</head>\n<body>\n'
505362306a36Sopenharmony_ci	hf.write(html_header)
505462306a36Sopenharmony_ci
505562306a36Sopenharmony_ci# Function: addScriptCode
505662306a36Sopenharmony_ci# Description:
505762306a36Sopenharmony_ci#	 Adds the javascript code to the output html
505862306a36Sopenharmony_ci# Arguments:
505962306a36Sopenharmony_ci#	 hf: the open html file pointer
506062306a36Sopenharmony_ci#	 testruns: array of Data objects from parseKernelLog or parseTraceLog
506162306a36Sopenharmony_cidef addScriptCode(hf, testruns):
506262306a36Sopenharmony_ci	t0 = testruns[0].start * 1000
506362306a36Sopenharmony_ci	tMax = testruns[-1].end * 1000
506462306a36Sopenharmony_ci	# create an array in javascript memory with the device details
506562306a36Sopenharmony_ci	detail = '	var devtable = [];\n'
506662306a36Sopenharmony_ci	for data in testruns:
506762306a36Sopenharmony_ci		topo = data.deviceTopology()
506862306a36Sopenharmony_ci		detail += '	devtable[%d] = "%s";\n' % (data.testnumber, topo)
506962306a36Sopenharmony_ci	detail += '	var bounds = [%f,%f];\n' % (t0, tMax)
507062306a36Sopenharmony_ci	# add the code which will manipulate the data in the browser
507162306a36Sopenharmony_ci	script_code = \
507262306a36Sopenharmony_ci	'<script type="text/javascript">\n'+detail+\
507362306a36Sopenharmony_ci	'	var resolution = -1;\n'\
507462306a36Sopenharmony_ci	'	var dragval = [0, 0];\n'\
507562306a36Sopenharmony_ci	'	function redrawTimescale(t0, tMax, tS) {\n'\
507662306a36Sopenharmony_ci	'		var rline = \'<div class="t" style="left:0;border-left:1px solid black;border-right:0;">\';\n'\
507762306a36Sopenharmony_ci	'		var tTotal = tMax - t0;\n'\
507862306a36Sopenharmony_ci	'		var list = document.getElementsByClassName("tblock");\n'\
507962306a36Sopenharmony_ci	'		for (var i = 0; i < list.length; i++) {\n'\
508062306a36Sopenharmony_ci	'			var timescale = list[i].getElementsByClassName("timescale")[0];\n'\
508162306a36Sopenharmony_ci	'			var m0 = t0 + (tTotal*parseFloat(list[i].style.left)/100);\n'\
508262306a36Sopenharmony_ci	'			var mTotal = tTotal*parseFloat(list[i].style.width)/100;\n'\
508362306a36Sopenharmony_ci	'			var mMax = m0 + mTotal;\n'\
508462306a36Sopenharmony_ci	'			var html = "";\n'\
508562306a36Sopenharmony_ci	'			var divTotal = Math.floor(mTotal/tS) + 1;\n'\
508662306a36Sopenharmony_ci	'			if(divTotal > 1000) continue;\n'\
508762306a36Sopenharmony_ci	'			var divEdge = (mTotal - tS*(divTotal-1))*100/mTotal;\n'\
508862306a36Sopenharmony_ci	'			var pos = 0.0, val = 0.0;\n'\
508962306a36Sopenharmony_ci	'			for (var j = 0; j < divTotal; j++) {\n'\
509062306a36Sopenharmony_ci	'				var htmlline = "";\n'\
509162306a36Sopenharmony_ci	'				var mode = list[i].id[5];\n'\
509262306a36Sopenharmony_ci	'				if(mode == "s") {\n'\
509362306a36Sopenharmony_ci	'					pos = 100 - (((j)*tS*100)/mTotal) - divEdge;\n'\
509462306a36Sopenharmony_ci	'					val = (j-divTotal+1)*tS;\n'\
509562306a36Sopenharmony_ci	'					if(j == divTotal - 1)\n'\
509662306a36Sopenharmony_ci	'						htmlline = \'<div class="t" style="right:\'+pos+\'%"><cS>S&rarr;</cS></div>\';\n'\
509762306a36Sopenharmony_ci	'					else\n'\
509862306a36Sopenharmony_ci	'						htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\
509962306a36Sopenharmony_ci	'				} else {\n'\
510062306a36Sopenharmony_ci	'					pos = 100 - (((j)*tS*100)/mTotal);\n'\
510162306a36Sopenharmony_ci	'					val = (j)*tS;\n'\
510262306a36Sopenharmony_ci	'					htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\
510362306a36Sopenharmony_ci	'					if(j == 0)\n'\
510462306a36Sopenharmony_ci	'						if(mode == "r")\n'\
510562306a36Sopenharmony_ci	'							htmlline = rline+"<cS>&larr;R</cS></div>";\n'\
510662306a36Sopenharmony_ci	'						else\n'\
510762306a36Sopenharmony_ci	'							htmlline = rline+"<cS>0ms</div>";\n'\
510862306a36Sopenharmony_ci	'				}\n'\
510962306a36Sopenharmony_ci	'				html += htmlline;\n'\
511062306a36Sopenharmony_ci	'			}\n'\
511162306a36Sopenharmony_ci	'			timescale.innerHTML = html;\n'\
511262306a36Sopenharmony_ci	'		}\n'\
511362306a36Sopenharmony_ci	'	}\n'\
511462306a36Sopenharmony_ci	'	function zoomTimeline() {\n'\
511562306a36Sopenharmony_ci	'		var dmesg = document.getElementById("dmesg");\n'\
511662306a36Sopenharmony_ci	'		var zoombox = document.getElementById("dmesgzoombox");\n'\
511762306a36Sopenharmony_ci	'		var left = zoombox.scrollLeft;\n'\
511862306a36Sopenharmony_ci	'		var val = parseFloat(dmesg.style.width);\n'\
511962306a36Sopenharmony_ci	'		var newval = 100;\n'\
512062306a36Sopenharmony_ci	'		var sh = window.outerWidth / 2;\n'\
512162306a36Sopenharmony_ci	'		if(this.id == "zoomin") {\n'\
512262306a36Sopenharmony_ci	'			newval = val * 1.2;\n'\
512362306a36Sopenharmony_ci	'			if(newval > 910034) newval = 910034;\n'\
512462306a36Sopenharmony_ci	'			dmesg.style.width = newval+"%";\n'\
512562306a36Sopenharmony_ci	'			zoombox.scrollLeft = ((left + sh) * newval / val) - sh;\n'\
512662306a36Sopenharmony_ci	'		} else if (this.id == "zoomout") {\n'\
512762306a36Sopenharmony_ci	'			newval = val / 1.2;\n'\
512862306a36Sopenharmony_ci	'			if(newval < 100) newval = 100;\n'\
512962306a36Sopenharmony_ci	'			dmesg.style.width = newval+"%";\n'\
513062306a36Sopenharmony_ci	'			zoombox.scrollLeft = ((left + sh) * newval / val) - sh;\n'\
513162306a36Sopenharmony_ci	'		} else {\n'\
513262306a36Sopenharmony_ci	'			zoombox.scrollLeft = 0;\n'\
513362306a36Sopenharmony_ci	'			dmesg.style.width = "100%";\n'\
513462306a36Sopenharmony_ci	'		}\n'\
513562306a36Sopenharmony_ci	'		var tS = [10000, 5000, 2000, 1000, 500, 200, 100, 50, 20, 10, 5, 2, 1];\n'\
513662306a36Sopenharmony_ci	'		var t0 = bounds[0];\n'\
513762306a36Sopenharmony_ci	'		var tMax = bounds[1];\n'\
513862306a36Sopenharmony_ci	'		var tTotal = tMax - t0;\n'\
513962306a36Sopenharmony_ci	'		var wTotal = tTotal * 100.0 / newval;\n'\
514062306a36Sopenharmony_ci	'		var idx = 7*window.innerWidth/1100;\n'\
514162306a36Sopenharmony_ci	'		for(var i = 0; (i < tS.length)&&((wTotal / tS[i]) < idx); i++);\n'\
514262306a36Sopenharmony_ci	'		if(i >= tS.length) i = tS.length - 1;\n'\
514362306a36Sopenharmony_ci	'		if(tS[i] == resolution) return;\n'\
514462306a36Sopenharmony_ci	'		resolution = tS[i];\n'\
514562306a36Sopenharmony_ci	'		redrawTimescale(t0, tMax, tS[i]);\n'\
514662306a36Sopenharmony_ci	'	}\n'\
514762306a36Sopenharmony_ci	'	function deviceName(title) {\n'\
514862306a36Sopenharmony_ci	'		var name = title.slice(0, title.indexOf(" ("));\n'\
514962306a36Sopenharmony_ci	'		return name;\n'\
515062306a36Sopenharmony_ci	'	}\n'\
515162306a36Sopenharmony_ci	'	function deviceHover() {\n'\
515262306a36Sopenharmony_ci	'		var name = deviceName(this.title);\n'\
515362306a36Sopenharmony_ci	'		var dmesg = document.getElementById("dmesg");\n'\
515462306a36Sopenharmony_ci	'		var dev = dmesg.getElementsByClassName("thread");\n'\
515562306a36Sopenharmony_ci	'		var cpu = -1;\n'\
515662306a36Sopenharmony_ci	'		if(name.match("CPU_ON\[[0-9]*\]"))\n'\
515762306a36Sopenharmony_ci	'			cpu = parseInt(name.slice(7));\n'\
515862306a36Sopenharmony_ci	'		else if(name.match("CPU_OFF\[[0-9]*\]"))\n'\
515962306a36Sopenharmony_ci	'			cpu = parseInt(name.slice(8));\n'\
516062306a36Sopenharmony_ci	'		for (var i = 0; i < dev.length; i++) {\n'\
516162306a36Sopenharmony_ci	'			dname = deviceName(dev[i].title);\n'\
516262306a36Sopenharmony_ci	'			var cname = dev[i].className.slice(dev[i].className.indexOf("thread"));\n'\
516362306a36Sopenharmony_ci	'			if((cpu >= 0 && dname.match("CPU_O[NF]*\\\[*"+cpu+"\\\]")) ||\n'\
516462306a36Sopenharmony_ci	'				(name == dname))\n'\
516562306a36Sopenharmony_ci	'			{\n'\
516662306a36Sopenharmony_ci	'				dev[i].className = "hover "+cname;\n'\
516762306a36Sopenharmony_ci	'			} else {\n'\
516862306a36Sopenharmony_ci	'				dev[i].className = cname;\n'\
516962306a36Sopenharmony_ci	'			}\n'\
517062306a36Sopenharmony_ci	'		}\n'\
517162306a36Sopenharmony_ci	'	}\n'\
517262306a36Sopenharmony_ci	'	function deviceUnhover() {\n'\
517362306a36Sopenharmony_ci	'		var dmesg = document.getElementById("dmesg");\n'\
517462306a36Sopenharmony_ci	'		var dev = dmesg.getElementsByClassName("thread");\n'\
517562306a36Sopenharmony_ci	'		for (var i = 0; i < dev.length; i++) {\n'\
517662306a36Sopenharmony_ci	'			dev[i].className = dev[i].className.slice(dev[i].className.indexOf("thread"));\n'\
517762306a36Sopenharmony_ci	'		}\n'\
517862306a36Sopenharmony_ci	'	}\n'\
517962306a36Sopenharmony_ci	'	function deviceTitle(title, total, cpu) {\n'\
518062306a36Sopenharmony_ci	'		var prefix = "Total";\n'\
518162306a36Sopenharmony_ci	'		if(total.length > 3) {\n'\
518262306a36Sopenharmony_ci	'			prefix = "Average";\n'\
518362306a36Sopenharmony_ci	'			total[1] = (total[1]+total[3])/2;\n'\
518462306a36Sopenharmony_ci	'			total[2] = (total[2]+total[4])/2;\n'\
518562306a36Sopenharmony_ci	'		}\n'\
518662306a36Sopenharmony_ci	'		var devtitle = document.getElementById("devicedetailtitle");\n'\
518762306a36Sopenharmony_ci	'		var name = deviceName(title);\n'\
518862306a36Sopenharmony_ci	'		if(cpu >= 0) name = "CPU"+cpu;\n'\
518962306a36Sopenharmony_ci	'		var driver = "";\n'\
519062306a36Sopenharmony_ci	'		var tS = "<t2>(</t2>";\n'\
519162306a36Sopenharmony_ci	'		var tR = "<t2>)</t2>";\n'\
519262306a36Sopenharmony_ci	'		if(total[1] > 0)\n'\
519362306a36Sopenharmony_ci	'			tS = "<t2>("+prefix+" Suspend:</t2><t0> "+total[1].toFixed(3)+" ms</t0> ";\n'\
519462306a36Sopenharmony_ci	'		if(total[2] > 0)\n'\
519562306a36Sopenharmony_ci	'			tR = " <t2>"+prefix+" Resume:</t2><t0> "+total[2].toFixed(3)+" ms<t2>)</t2></t0>";\n'\
519662306a36Sopenharmony_ci	'		var s = title.indexOf("{");\n'\
519762306a36Sopenharmony_ci	'		var e = title.indexOf("}");\n'\
519862306a36Sopenharmony_ci	'		if((s >= 0) && (e >= 0))\n'\
519962306a36Sopenharmony_ci	'			driver = title.slice(s+1, e) + " <t1>@</t1> ";\n'\
520062306a36Sopenharmony_ci	'		if(total[1] > 0 && total[2] > 0)\n'\
520162306a36Sopenharmony_ci	'			devtitle.innerHTML = "<t0>"+driver+name+"</t0> "+tS+tR;\n'\
520262306a36Sopenharmony_ci	'		else\n'\
520362306a36Sopenharmony_ci	'			devtitle.innerHTML = "<t0>"+title+"</t0>";\n'\
520462306a36Sopenharmony_ci	'		return name;\n'\
520562306a36Sopenharmony_ci	'	}\n'\
520662306a36Sopenharmony_ci	'	function deviceDetail() {\n'\
520762306a36Sopenharmony_ci	'		var devinfo = document.getElementById("devicedetail");\n'\
520862306a36Sopenharmony_ci	'		devinfo.style.display = "block";\n'\
520962306a36Sopenharmony_ci	'		var name = deviceName(this.title);\n'\
521062306a36Sopenharmony_ci	'		var cpu = -1;\n'\
521162306a36Sopenharmony_ci	'		if(name.match("CPU_ON\[[0-9]*\]"))\n'\
521262306a36Sopenharmony_ci	'			cpu = parseInt(name.slice(7));\n'\
521362306a36Sopenharmony_ci	'		else if(name.match("CPU_OFF\[[0-9]*\]"))\n'\
521462306a36Sopenharmony_ci	'			cpu = parseInt(name.slice(8));\n'\
521562306a36Sopenharmony_ci	'		var dmesg = document.getElementById("dmesg");\n'\
521662306a36Sopenharmony_ci	'		var dev = dmesg.getElementsByClassName("thread");\n'\
521762306a36Sopenharmony_ci	'		var idlist = [];\n'\
521862306a36Sopenharmony_ci	'		var pdata = [[]];\n'\
521962306a36Sopenharmony_ci	'		if(document.getElementById("devicedetail1"))\n'\
522062306a36Sopenharmony_ci	'			pdata = [[], []];\n'\
522162306a36Sopenharmony_ci	'		var pd = pdata[0];\n'\
522262306a36Sopenharmony_ci	'		var total = [0.0, 0.0, 0.0];\n'\
522362306a36Sopenharmony_ci	'		for (var i = 0; i < dev.length; i++) {\n'\
522462306a36Sopenharmony_ci	'			dname = deviceName(dev[i].title);\n'\
522562306a36Sopenharmony_ci	'			if((cpu >= 0 && dname.match("CPU_O[NF]*\\\[*"+cpu+"\\\]")) ||\n'\
522662306a36Sopenharmony_ci	'				(name == dname))\n'\
522762306a36Sopenharmony_ci	'			{\n'\
522862306a36Sopenharmony_ci	'				idlist[idlist.length] = dev[i].id;\n'\
522962306a36Sopenharmony_ci	'				var tidx = 1;\n'\
523062306a36Sopenharmony_ci	'				if(dev[i].id[0] == "a") {\n'\
523162306a36Sopenharmony_ci	'					pd = pdata[0];\n'\
523262306a36Sopenharmony_ci	'				} else {\n'\
523362306a36Sopenharmony_ci	'					if(pdata.length == 1) pdata[1] = [];\n'\
523462306a36Sopenharmony_ci	'					if(total.length == 3) total[3]=total[4]=0.0;\n'\
523562306a36Sopenharmony_ci	'					pd = pdata[1];\n'\
523662306a36Sopenharmony_ci	'					tidx = 3;\n'\
523762306a36Sopenharmony_ci	'				}\n'\
523862306a36Sopenharmony_ci	'				var info = dev[i].title.split(" ");\n'\
523962306a36Sopenharmony_ci	'				var pname = info[info.length-1];\n'\
524062306a36Sopenharmony_ci	'				pd[pname] = parseFloat(info[info.length-3].slice(1));\n'\
524162306a36Sopenharmony_ci	'				total[0] += pd[pname];\n'\
524262306a36Sopenharmony_ci	'				if(pname.indexOf("suspend") >= 0)\n'\
524362306a36Sopenharmony_ci	'					total[tidx] += pd[pname];\n'\
524462306a36Sopenharmony_ci	'				else\n'\
524562306a36Sopenharmony_ci	'					total[tidx+1] += pd[pname];\n'\
524662306a36Sopenharmony_ci	'			}\n'\
524762306a36Sopenharmony_ci	'		}\n'\
524862306a36Sopenharmony_ci	'		var devname = deviceTitle(this.title, total, cpu);\n'\
524962306a36Sopenharmony_ci	'		var left = 0.0;\n'\
525062306a36Sopenharmony_ci	'		for (var t = 0; t < pdata.length; t++) {\n'\
525162306a36Sopenharmony_ci	'			pd = pdata[t];\n'\
525262306a36Sopenharmony_ci	'			devinfo = document.getElementById("devicedetail"+t);\n'\
525362306a36Sopenharmony_ci	'			var phases = devinfo.getElementsByClassName("phaselet");\n'\
525462306a36Sopenharmony_ci	'			for (var i = 0; i < phases.length; i++) {\n'\
525562306a36Sopenharmony_ci	'				if(phases[i].id in pd) {\n'\
525662306a36Sopenharmony_ci	'					var w = 100.0*pd[phases[i].id]/total[0];\n'\
525762306a36Sopenharmony_ci	'					var fs = 32;\n'\
525862306a36Sopenharmony_ci	'					if(w < 8) fs = 4*w | 0;\n'\
525962306a36Sopenharmony_ci	'					var fs2 = fs*3/4;\n'\
526062306a36Sopenharmony_ci	'					phases[i].style.width = w+"%";\n'\
526162306a36Sopenharmony_ci	'					phases[i].style.left = left+"%";\n'\
526262306a36Sopenharmony_ci	'					phases[i].title = phases[i].id+" "+pd[phases[i].id]+" ms";\n'\
526362306a36Sopenharmony_ci	'					left += w;\n'\
526462306a36Sopenharmony_ci	'					var time = "<t4 style=\\"font-size:"+fs+"px\\">"+pd[phases[i].id]+" ms<br></t4>";\n'\
526562306a36Sopenharmony_ci	'					var pname = "<t3 style=\\"font-size:"+fs2+"px\\">"+phases[i].id.replace(new RegExp("_", "g"), " ")+"</t3>";\n'\
526662306a36Sopenharmony_ci	'					phases[i].innerHTML = time+pname;\n'\
526762306a36Sopenharmony_ci	'				} else {\n'\
526862306a36Sopenharmony_ci	'					phases[i].style.width = "0%";\n'\
526962306a36Sopenharmony_ci	'					phases[i].style.left = left+"%";\n'\
527062306a36Sopenharmony_ci	'				}\n'\
527162306a36Sopenharmony_ci	'			}\n'\
527262306a36Sopenharmony_ci	'		}\n'\
527362306a36Sopenharmony_ci	'		if(typeof devstats !== \'undefined\')\n'\
527462306a36Sopenharmony_ci	'			callDetail(this.id, this.title);\n'\
527562306a36Sopenharmony_ci	'		var cglist = document.getElementById("callgraphs");\n'\
527662306a36Sopenharmony_ci	'		if(!cglist) return;\n'\
527762306a36Sopenharmony_ci	'		var cg = cglist.getElementsByClassName("atop");\n'\
527862306a36Sopenharmony_ci	'		if(cg.length < 10) return;\n'\
527962306a36Sopenharmony_ci	'		for (var i = 0; i < cg.length; i++) {\n'\
528062306a36Sopenharmony_ci	'			cgid = cg[i].id.split("x")[0]\n'\
528162306a36Sopenharmony_ci	'			if(idlist.indexOf(cgid) >= 0) {\n'\
528262306a36Sopenharmony_ci	'				cg[i].style.display = "block";\n'\
528362306a36Sopenharmony_ci	'			} else {\n'\
528462306a36Sopenharmony_ci	'				cg[i].style.display = "none";\n'\
528562306a36Sopenharmony_ci	'			}\n'\
528662306a36Sopenharmony_ci	'		}\n'\
528762306a36Sopenharmony_ci	'	}\n'\
528862306a36Sopenharmony_ci	'	function callDetail(devid, devtitle) {\n'\
528962306a36Sopenharmony_ci	'		if(!(devid in devstats) || devstats[devid].length < 1)\n'\
529062306a36Sopenharmony_ci	'			return;\n'\
529162306a36Sopenharmony_ci	'		var list = devstats[devid];\n'\
529262306a36Sopenharmony_ci	'		var tmp = devtitle.split(" ");\n'\
529362306a36Sopenharmony_ci	'		var name = tmp[0], phase = tmp[tmp.length-1];\n'\
529462306a36Sopenharmony_ci	'		var dd = document.getElementById(phase);\n'\
529562306a36Sopenharmony_ci	'		var total = parseFloat(tmp[1].slice(1));\n'\
529662306a36Sopenharmony_ci	'		var mlist = [];\n'\
529762306a36Sopenharmony_ci	'		var maxlen = 0;\n'\
529862306a36Sopenharmony_ci	'		var info = []\n'\
529962306a36Sopenharmony_ci	'		for(var i in list) {\n'\
530062306a36Sopenharmony_ci	'			if(list[i][0] == "@") {\n'\
530162306a36Sopenharmony_ci	'				info = list[i].split("|");\n'\
530262306a36Sopenharmony_ci	'				continue;\n'\
530362306a36Sopenharmony_ci	'			}\n'\
530462306a36Sopenharmony_ci	'			var tmp = list[i].split("|");\n'\
530562306a36Sopenharmony_ci	'			var t = parseFloat(tmp[0]), f = tmp[1], c = parseInt(tmp[2]);\n'\
530662306a36Sopenharmony_ci	'			var p = (t*100.0/total).toFixed(2);\n'\
530762306a36Sopenharmony_ci	'			mlist[mlist.length] = [f, c, t.toFixed(2), p+"%"];\n'\
530862306a36Sopenharmony_ci	'			if(f.length > maxlen)\n'\
530962306a36Sopenharmony_ci	'				maxlen = f.length;\n'\
531062306a36Sopenharmony_ci	'		}\n'\
531162306a36Sopenharmony_ci	'		var pad = 5;\n'\
531262306a36Sopenharmony_ci	'		if(mlist.length == 0) pad = 30;\n'\
531362306a36Sopenharmony_ci	'		var html = \'<div style="padding-top:\'+pad+\'px"><t3> <b>\'+name+\':</b>\';\n'\
531462306a36Sopenharmony_ci	'		if(info.length > 2)\n'\
531562306a36Sopenharmony_ci	'			html += " start=<b>"+info[1]+"</b>, end=<b>"+info[2]+"</b>";\n'\
531662306a36Sopenharmony_ci	'		if(info.length > 3)\n'\
531762306a36Sopenharmony_ci	'			html += ", length<i>(w/o overhead)</i>=<b>"+info[3]+" ms</b>";\n'\
531862306a36Sopenharmony_ci	'		if(info.length > 4)\n'\
531962306a36Sopenharmony_ci	'			html += ", return=<b>"+info[4]+"</b>";\n'\
532062306a36Sopenharmony_ci	'		html += "</t3></div>";\n'\
532162306a36Sopenharmony_ci	'		if(mlist.length > 0) {\n'\
532262306a36Sopenharmony_ci	'			html += \'<table class=fstat style="padding-top:\'+(maxlen*5)+\'px;"><tr><th>Function</th>\';\n'\
532362306a36Sopenharmony_ci	'			for(var i in mlist)\n'\
532462306a36Sopenharmony_ci	'				html += "<td class=vt>"+mlist[i][0]+"</td>";\n'\
532562306a36Sopenharmony_ci	'			html += "</tr><tr><th>Calls</th>";\n'\
532662306a36Sopenharmony_ci	'			for(var i in mlist)\n'\
532762306a36Sopenharmony_ci	'				html += "<td>"+mlist[i][1]+"</td>";\n'\
532862306a36Sopenharmony_ci	'			html += "</tr><tr><th>Time(ms)</th>";\n'\
532962306a36Sopenharmony_ci	'			for(var i in mlist)\n'\
533062306a36Sopenharmony_ci	'				html += "<td>"+mlist[i][2]+"</td>";\n'\
533162306a36Sopenharmony_ci	'			html += "</tr><tr><th>Percent</th>";\n'\
533262306a36Sopenharmony_ci	'			for(var i in mlist)\n'\
533362306a36Sopenharmony_ci	'				html += "<td>"+mlist[i][3]+"</td>";\n'\
533462306a36Sopenharmony_ci	'			html += "</tr></table>";\n'\
533562306a36Sopenharmony_ci	'		}\n'\
533662306a36Sopenharmony_ci	'		dd.innerHTML = html;\n'\
533762306a36Sopenharmony_ci	'		var height = (maxlen*5)+100;\n'\
533862306a36Sopenharmony_ci	'		dd.style.height = height+"px";\n'\
533962306a36Sopenharmony_ci	'		document.getElementById("devicedetail").style.height = height+"px";\n'\
534062306a36Sopenharmony_ci	'	}\n'\
534162306a36Sopenharmony_ci	'	function callSelect() {\n'\
534262306a36Sopenharmony_ci	'		var cglist = document.getElementById("callgraphs");\n'\
534362306a36Sopenharmony_ci	'		if(!cglist) return;\n'\
534462306a36Sopenharmony_ci	'		var cg = cglist.getElementsByClassName("atop");\n'\
534562306a36Sopenharmony_ci	'		for (var i = 0; i < cg.length; i++) {\n'\
534662306a36Sopenharmony_ci	'			if(this.id == cg[i].id) {\n'\
534762306a36Sopenharmony_ci	'				cg[i].style.display = "block";\n'\
534862306a36Sopenharmony_ci	'			} else {\n'\
534962306a36Sopenharmony_ci	'				cg[i].style.display = "none";\n'\
535062306a36Sopenharmony_ci	'			}\n'\
535162306a36Sopenharmony_ci	'		}\n'\
535262306a36Sopenharmony_ci	'	}\n'\
535362306a36Sopenharmony_ci	'	function devListWindow(e) {\n'\
535462306a36Sopenharmony_ci	'		var win = window.open();\n'\
535562306a36Sopenharmony_ci	'		var html = "<title>"+e.target.innerHTML+"</title>"+\n'\
535662306a36Sopenharmony_ci	'			"<style type=\\"text/css\\">"+\n'\
535762306a36Sopenharmony_ci	'			"   ul {list-style-type:circle;padding-left:10px;margin-left:10px;}"+\n'\
535862306a36Sopenharmony_ci	'			"</style>"\n'\
535962306a36Sopenharmony_ci	'		var dt = devtable[0];\n'\
536062306a36Sopenharmony_ci	'		if(e.target.id != "devlist1")\n'\
536162306a36Sopenharmony_ci	'			dt = devtable[1];\n'\
536262306a36Sopenharmony_ci	'		win.document.write(html+dt);\n'\
536362306a36Sopenharmony_ci	'	}\n'\
536462306a36Sopenharmony_ci	'	function errWindow() {\n'\
536562306a36Sopenharmony_ci	'		var range = this.id.split("_");\n'\
536662306a36Sopenharmony_ci	'		var idx1 = parseInt(range[0]);\n'\
536762306a36Sopenharmony_ci	'		var idx2 = parseInt(range[1]);\n'\
536862306a36Sopenharmony_ci	'		var win = window.open();\n'\
536962306a36Sopenharmony_ci	'		var log = document.getElementById("dmesglog");\n'\
537062306a36Sopenharmony_ci	'		var title = "<title>dmesg log</title>";\n'\
537162306a36Sopenharmony_ci	'		var text = log.innerHTML.split("\\n");\n'\
537262306a36Sopenharmony_ci	'		var html = "";\n'\
537362306a36Sopenharmony_ci	'		for(var i = 0; i < text.length; i++) {\n'\
537462306a36Sopenharmony_ci	'			if(i == idx1) {\n'\
537562306a36Sopenharmony_ci	'				html += "<e id=target>"+text[i]+"</e>\\n";\n'\
537662306a36Sopenharmony_ci	'			} else if(i > idx1 && i <= idx2) {\n'\
537762306a36Sopenharmony_ci	'				html += "<e>"+text[i]+"</e>\\n";\n'\
537862306a36Sopenharmony_ci	'			} else {\n'\
537962306a36Sopenharmony_ci	'				html += text[i]+"\\n";\n'\
538062306a36Sopenharmony_ci	'			}\n'\
538162306a36Sopenharmony_ci	'		}\n'\
538262306a36Sopenharmony_ci	'		win.document.write("<style>e{color:red}</style>"+title+"<pre>"+html+"</pre>");\n'\
538362306a36Sopenharmony_ci	'		win.location.hash = "#target";\n'\
538462306a36Sopenharmony_ci	'		win.document.close();\n'\
538562306a36Sopenharmony_ci	'	}\n'\
538662306a36Sopenharmony_ci	'	function logWindow(e) {\n'\
538762306a36Sopenharmony_ci	'		var name = e.target.id.slice(4);\n'\
538862306a36Sopenharmony_ci	'		var win = window.open();\n'\
538962306a36Sopenharmony_ci	'		var log = document.getElementById(name+"log");\n'\
539062306a36Sopenharmony_ci	'		var title = "<title>"+document.title.split(" ")[0]+" "+name+" log</title>";\n'\
539162306a36Sopenharmony_ci	'		win.document.write(title+"<pre>"+log.innerHTML+"</pre>");\n'\
539262306a36Sopenharmony_ci	'		win.document.close();\n'\
539362306a36Sopenharmony_ci	'	}\n'\
539462306a36Sopenharmony_ci	'	function onMouseDown(e) {\n'\
539562306a36Sopenharmony_ci	'		dragval[0] = e.clientX;\n'\
539662306a36Sopenharmony_ci	'		dragval[1] = document.getElementById("dmesgzoombox").scrollLeft;\n'\
539762306a36Sopenharmony_ci	'		document.onmousemove = onMouseMove;\n'\
539862306a36Sopenharmony_ci	'	}\n'\
539962306a36Sopenharmony_ci	'	function onMouseMove(e) {\n'\
540062306a36Sopenharmony_ci	'		var zoombox = document.getElementById("dmesgzoombox");\n'\
540162306a36Sopenharmony_ci	'		zoombox.scrollLeft = dragval[1] + dragval[0] - e.clientX;\n'\
540262306a36Sopenharmony_ci	'	}\n'\
540362306a36Sopenharmony_ci	'	function onMouseUp(e) {\n'\
540462306a36Sopenharmony_ci	'		document.onmousemove = null;\n'\
540562306a36Sopenharmony_ci	'	}\n'\
540662306a36Sopenharmony_ci	'	function onKeyPress(e) {\n'\
540762306a36Sopenharmony_ci	'		var c = e.charCode;\n'\
540862306a36Sopenharmony_ci	'		if(c != 42 && c != 43 && c != 45) return;\n'\
540962306a36Sopenharmony_ci	'		var click = document.createEvent("Events");\n'\
541062306a36Sopenharmony_ci	'		click.initEvent("click", true, false);\n'\
541162306a36Sopenharmony_ci	'		if(c == 43)  \n'\
541262306a36Sopenharmony_ci	'			document.getElementById("zoomin").dispatchEvent(click);\n'\
541362306a36Sopenharmony_ci	'		else if(c == 45)\n'\
541462306a36Sopenharmony_ci	'			document.getElementById("zoomout").dispatchEvent(click);\n'\
541562306a36Sopenharmony_ci	'		else if(c == 42)\n'\
541662306a36Sopenharmony_ci	'			document.getElementById("zoomdef").dispatchEvent(click);\n'\
541762306a36Sopenharmony_ci	'	}\n'\
541862306a36Sopenharmony_ci	'	window.addEventListener("resize", function () {zoomTimeline();});\n'\
541962306a36Sopenharmony_ci	'	window.addEventListener("load", function () {\n'\
542062306a36Sopenharmony_ci	'		var dmesg = document.getElementById("dmesg");\n'\
542162306a36Sopenharmony_ci	'		dmesg.style.width = "100%"\n'\
542262306a36Sopenharmony_ci	'		dmesg.onmousedown = onMouseDown;\n'\
542362306a36Sopenharmony_ci	'		document.onmouseup = onMouseUp;\n'\
542462306a36Sopenharmony_ci	'		document.onkeypress = onKeyPress;\n'\
542562306a36Sopenharmony_ci	'		document.getElementById("zoomin").onclick = zoomTimeline;\n'\
542662306a36Sopenharmony_ci	'		document.getElementById("zoomout").onclick = zoomTimeline;\n'\
542762306a36Sopenharmony_ci	'		document.getElementById("zoomdef").onclick = zoomTimeline;\n'\
542862306a36Sopenharmony_ci	'		var list = document.getElementsByClassName("err");\n'\
542962306a36Sopenharmony_ci	'		for (var i = 0; i < list.length; i++)\n'\
543062306a36Sopenharmony_ci	'			list[i].onclick = errWindow;\n'\
543162306a36Sopenharmony_ci	'		var list = document.getElementsByClassName("logbtn");\n'\
543262306a36Sopenharmony_ci	'		for (var i = 0; i < list.length; i++)\n'\
543362306a36Sopenharmony_ci	'			list[i].onclick = logWindow;\n'\
543462306a36Sopenharmony_ci	'		list = document.getElementsByClassName("devlist");\n'\
543562306a36Sopenharmony_ci	'		for (var i = 0; i < list.length; i++)\n'\
543662306a36Sopenharmony_ci	'			list[i].onclick = devListWindow;\n'\
543762306a36Sopenharmony_ci	'		var dev = dmesg.getElementsByClassName("thread");\n'\
543862306a36Sopenharmony_ci	'		for (var i = 0; i < dev.length; i++) {\n'\
543962306a36Sopenharmony_ci	'			dev[i].onclick = deviceDetail;\n'\
544062306a36Sopenharmony_ci	'			dev[i].onmouseover = deviceHover;\n'\
544162306a36Sopenharmony_ci	'			dev[i].onmouseout = deviceUnhover;\n'\
544262306a36Sopenharmony_ci	'		}\n'\
544362306a36Sopenharmony_ci	'		var dev = dmesg.getElementsByClassName("srccall");\n'\
544462306a36Sopenharmony_ci	'		for (var i = 0; i < dev.length; i++)\n'\
544562306a36Sopenharmony_ci	'			dev[i].onclick = callSelect;\n'\
544662306a36Sopenharmony_ci	'		zoomTimeline();\n'\
544762306a36Sopenharmony_ci	'	});\n'\
544862306a36Sopenharmony_ci	'</script>\n'
544962306a36Sopenharmony_ci	hf.write(script_code);
545062306a36Sopenharmony_ci
545162306a36Sopenharmony_ci# Function: executeSuspend
545262306a36Sopenharmony_ci# Description:
545362306a36Sopenharmony_ci#	 Execute system suspend through the sysfs interface, then copy the output
545462306a36Sopenharmony_ci#	 dmesg and ftrace files to the test output directory.
545562306a36Sopenharmony_cidef executeSuspend(quiet=False):
545662306a36Sopenharmony_ci	sv, tp, pm = sysvals, sysvals.tpath, ProcessMonitor()
545762306a36Sopenharmony_ci	if sv.wifi:
545862306a36Sopenharmony_ci		wifi = sv.checkWifi()
545962306a36Sopenharmony_ci		sv.dlog('wifi check, connected device is "%s"' % wifi)
546062306a36Sopenharmony_ci	testdata = []
546162306a36Sopenharmony_ci	# run these commands to prepare the system for suspend
546262306a36Sopenharmony_ci	if sv.display:
546362306a36Sopenharmony_ci		if not quiet:
546462306a36Sopenharmony_ci			pprint('SET DISPLAY TO %s' % sv.display.upper())
546562306a36Sopenharmony_ci		ret = sv.displayControl(sv.display)
546662306a36Sopenharmony_ci		sv.dlog('xset display %s, ret = %d' % (sv.display, ret))
546762306a36Sopenharmony_ci		time.sleep(1)
546862306a36Sopenharmony_ci	if sv.sync:
546962306a36Sopenharmony_ci		if not quiet:
547062306a36Sopenharmony_ci			pprint('SYNCING FILESYSTEMS')
547162306a36Sopenharmony_ci		sv.dlog('syncing filesystems')
547262306a36Sopenharmony_ci		call('sync', shell=True)
547362306a36Sopenharmony_ci	sv.dlog('read dmesg')
547462306a36Sopenharmony_ci	sv.initdmesg()
547562306a36Sopenharmony_ci	sv.dlog('cmdinfo before')
547662306a36Sopenharmony_ci	sv.cmdinfo(True)
547762306a36Sopenharmony_ci	sv.start(pm)
547862306a36Sopenharmony_ci	# execute however many s/r runs requested
547962306a36Sopenharmony_ci	for count in range(1,sv.execcount+1):
548062306a36Sopenharmony_ci		# x2delay in between test runs
548162306a36Sopenharmony_ci		if(count > 1 and sv.x2delay > 0):
548262306a36Sopenharmony_ci			sv.fsetVal('WAIT %d' % sv.x2delay, 'trace_marker')
548362306a36Sopenharmony_ci			time.sleep(sv.x2delay/1000.0)
548462306a36Sopenharmony_ci			sv.fsetVal('WAIT END', 'trace_marker')
548562306a36Sopenharmony_ci		# start message
548662306a36Sopenharmony_ci		if sv.testcommand != '':
548762306a36Sopenharmony_ci			pprint('COMMAND START')
548862306a36Sopenharmony_ci		else:
548962306a36Sopenharmony_ci			if(sv.rtcwake):
549062306a36Sopenharmony_ci				pprint('SUSPEND START')
549162306a36Sopenharmony_ci			else:
549262306a36Sopenharmony_ci				pprint('SUSPEND START (press a key to resume)')
549362306a36Sopenharmony_ci		# set rtcwake
549462306a36Sopenharmony_ci		if(sv.rtcwake):
549562306a36Sopenharmony_ci			if not quiet:
549662306a36Sopenharmony_ci				pprint('will issue an rtcwake in %d seconds' % sv.rtcwaketime)
549762306a36Sopenharmony_ci			sv.dlog('enable RTC wake alarm')
549862306a36Sopenharmony_ci			sv.rtcWakeAlarmOn()
549962306a36Sopenharmony_ci		# start of suspend trace marker
550062306a36Sopenharmony_ci		sv.fsetVal(datetime.now().strftime(sv.tmstart), 'trace_marker')
550162306a36Sopenharmony_ci		# predelay delay
550262306a36Sopenharmony_ci		if(count == 1 and sv.predelay > 0):
550362306a36Sopenharmony_ci			sv.fsetVal('WAIT %d' % sv.predelay, 'trace_marker')
550462306a36Sopenharmony_ci			time.sleep(sv.predelay/1000.0)
550562306a36Sopenharmony_ci			sv.fsetVal('WAIT END', 'trace_marker')
550662306a36Sopenharmony_ci		# initiate suspend or command
550762306a36Sopenharmony_ci		sv.dlog('system executing a suspend')
550862306a36Sopenharmony_ci		tdata = {'error': ''}
550962306a36Sopenharmony_ci		if sv.testcommand != '':
551062306a36Sopenharmony_ci			res = call(sv.testcommand+' 2>&1', shell=True);
551162306a36Sopenharmony_ci			if res != 0:
551262306a36Sopenharmony_ci				tdata['error'] = 'cmd returned %d' % res
551362306a36Sopenharmony_ci		else:
551462306a36Sopenharmony_ci			s0ixready = sv.s0ixSupport()
551562306a36Sopenharmony_ci			mode = sv.suspendmode
551662306a36Sopenharmony_ci			if sv.memmode and os.path.exists(sv.mempowerfile):
551762306a36Sopenharmony_ci				mode = 'mem'
551862306a36Sopenharmony_ci				sv.testVal(sv.mempowerfile, 'radio', sv.memmode)
551962306a36Sopenharmony_ci			if sv.diskmode and os.path.exists(sv.diskpowerfile):
552062306a36Sopenharmony_ci				mode = 'disk'
552162306a36Sopenharmony_ci				sv.testVal(sv.diskpowerfile, 'radio', sv.diskmode)
552262306a36Sopenharmony_ci			if sv.acpidebug:
552362306a36Sopenharmony_ci				sv.testVal(sv.acpipath, 'acpi', '0xe')
552462306a36Sopenharmony_ci			if ((mode == 'freeze') or (sv.memmode == 's2idle')) \
552562306a36Sopenharmony_ci				and sv.haveTurbostat():
552662306a36Sopenharmony_ci				# execution will pause here
552762306a36Sopenharmony_ci				turbo = sv.turbostat(s0ixready)
552862306a36Sopenharmony_ci				if turbo:
552962306a36Sopenharmony_ci					tdata['turbo'] = turbo
553062306a36Sopenharmony_ci			else:
553162306a36Sopenharmony_ci				pf = open(sv.powerfile, 'w')
553262306a36Sopenharmony_ci				pf.write(mode)
553362306a36Sopenharmony_ci				# execution will pause here
553462306a36Sopenharmony_ci				try:
553562306a36Sopenharmony_ci					pf.close()
553662306a36Sopenharmony_ci				except Exception as e:
553762306a36Sopenharmony_ci					tdata['error'] = str(e)
553862306a36Sopenharmony_ci		sv.fsetVal('CMD COMPLETE', 'trace_marker')
553962306a36Sopenharmony_ci		sv.dlog('system returned')
554062306a36Sopenharmony_ci		# reset everything
554162306a36Sopenharmony_ci		sv.testVal('restoreall')
554262306a36Sopenharmony_ci		if(sv.rtcwake):
554362306a36Sopenharmony_ci			sv.dlog('disable RTC wake alarm')
554462306a36Sopenharmony_ci			sv.rtcWakeAlarmOff()
554562306a36Sopenharmony_ci		# postdelay delay
554662306a36Sopenharmony_ci		if(count == sv.execcount and sv.postdelay > 0):
554762306a36Sopenharmony_ci			sv.fsetVal('WAIT %d' % sv.postdelay, 'trace_marker')
554862306a36Sopenharmony_ci			time.sleep(sv.postdelay/1000.0)
554962306a36Sopenharmony_ci			sv.fsetVal('WAIT END', 'trace_marker')
555062306a36Sopenharmony_ci		# return from suspend
555162306a36Sopenharmony_ci		pprint('RESUME COMPLETE')
555262306a36Sopenharmony_ci		if(count < sv.execcount):
555362306a36Sopenharmony_ci			sv.fsetVal(datetime.now().strftime(sv.tmend), 'trace_marker')
555462306a36Sopenharmony_ci		elif(not sv.wifitrace):
555562306a36Sopenharmony_ci			sv.fsetVal(datetime.now().strftime(sv.tmend), 'trace_marker')
555662306a36Sopenharmony_ci			sv.stop(pm)
555762306a36Sopenharmony_ci		if sv.wifi and wifi:
555862306a36Sopenharmony_ci			tdata['wifi'] = sv.pollWifi(wifi)
555962306a36Sopenharmony_ci			sv.dlog('wifi check, %s' % tdata['wifi'])
556062306a36Sopenharmony_ci		if(count == sv.execcount and sv.wifitrace):
556162306a36Sopenharmony_ci			sv.fsetVal(datetime.now().strftime(sv.tmend), 'trace_marker')
556262306a36Sopenharmony_ci			sv.stop(pm)
556362306a36Sopenharmony_ci		if sv.netfix:
556462306a36Sopenharmony_ci			tdata['netfix'] = sv.netfixon()
556562306a36Sopenharmony_ci			sv.dlog('netfix, %s' % tdata['netfix'])
556662306a36Sopenharmony_ci		if(sv.suspendmode == 'mem' or sv.suspendmode == 'command'):
556762306a36Sopenharmony_ci			sv.dlog('read the ACPI FPDT')
556862306a36Sopenharmony_ci			tdata['fw'] = getFPDT(False)
556962306a36Sopenharmony_ci		testdata.append(tdata)
557062306a36Sopenharmony_ci	sv.dlog('cmdinfo after')
557162306a36Sopenharmony_ci	cmdafter = sv.cmdinfo(False)
557262306a36Sopenharmony_ci	# grab a copy of the dmesg output
557362306a36Sopenharmony_ci	if not quiet:
557462306a36Sopenharmony_ci		pprint('CAPTURING DMESG')
557562306a36Sopenharmony_ci	sv.getdmesg(testdata)
557662306a36Sopenharmony_ci	# grab a copy of the ftrace output
557762306a36Sopenharmony_ci	if sv.useftrace:
557862306a36Sopenharmony_ci		if not quiet:
557962306a36Sopenharmony_ci			pprint('CAPTURING TRACE')
558062306a36Sopenharmony_ci		op = sv.writeDatafileHeader(sv.ftracefile, testdata)
558162306a36Sopenharmony_ci		fp = open(tp+'trace', 'rb')
558262306a36Sopenharmony_ci		op.write(ascii(fp.read()))
558362306a36Sopenharmony_ci		op.close()
558462306a36Sopenharmony_ci		sv.fsetVal('', 'trace')
558562306a36Sopenharmony_ci		sv.platforminfo(cmdafter)
558662306a36Sopenharmony_ci
558762306a36Sopenharmony_cidef readFile(file):
558862306a36Sopenharmony_ci	if os.path.islink(file):
558962306a36Sopenharmony_ci		return os.readlink(file).split('/')[-1]
559062306a36Sopenharmony_ci	else:
559162306a36Sopenharmony_ci		return sysvals.getVal(file).strip()
559262306a36Sopenharmony_ci
559362306a36Sopenharmony_ci# Function: ms2nice
559462306a36Sopenharmony_ci# Description:
559562306a36Sopenharmony_ci#	 Print out a very concise time string in minutes and seconds
559662306a36Sopenharmony_ci# Output:
559762306a36Sopenharmony_ci#	 The time string, e.g. "1901m16s"
559862306a36Sopenharmony_cidef ms2nice(val):
559962306a36Sopenharmony_ci	val = int(val)
560062306a36Sopenharmony_ci	h = val // 3600000
560162306a36Sopenharmony_ci	m = (val // 60000) % 60
560262306a36Sopenharmony_ci	s = (val // 1000) % 60
560362306a36Sopenharmony_ci	if h > 0:
560462306a36Sopenharmony_ci		return '%d:%02d:%02d' % (h, m, s)
560562306a36Sopenharmony_ci	if m > 0:
560662306a36Sopenharmony_ci		return '%02d:%02d' % (m, s)
560762306a36Sopenharmony_ci	return '%ds' % s
560862306a36Sopenharmony_ci
560962306a36Sopenharmony_cidef yesno(val):
561062306a36Sopenharmony_ci	list = {'enabled':'A', 'disabled':'S', 'auto':'E', 'on':'D',
561162306a36Sopenharmony_ci		'active':'A', 'suspended':'S', 'suspending':'S'}
561262306a36Sopenharmony_ci	if val not in list:
561362306a36Sopenharmony_ci		return ' '
561462306a36Sopenharmony_ci	return list[val]
561562306a36Sopenharmony_ci
561662306a36Sopenharmony_ci# Function: deviceInfo
561762306a36Sopenharmony_ci# Description:
561862306a36Sopenharmony_ci#	 Detect all the USB hosts and devices currently connected and add
561962306a36Sopenharmony_ci#	 a list of USB device names to sysvals for better timeline readability
562062306a36Sopenharmony_cidef deviceInfo(output=''):
562162306a36Sopenharmony_ci	if not output:
562262306a36Sopenharmony_ci		pprint('LEGEND\n'\
562362306a36Sopenharmony_ci		'---------------------------------------------------------------------------------------------\n'\
562462306a36Sopenharmony_ci		'  A = async/sync PM queue (A/S)               C = runtime active children\n'\
562562306a36Sopenharmony_ci		'  R = runtime suspend enabled/disabled (E/D)  rACTIVE = runtime active (min/sec)\n'\
562662306a36Sopenharmony_ci		'  S = runtime status active/suspended (A/S)   rSUSPEND = runtime suspend (min/sec)\n'\
562762306a36Sopenharmony_ci		'  U = runtime usage count\n'\
562862306a36Sopenharmony_ci		'---------------------------------------------------------------------------------------------\n'\
562962306a36Sopenharmony_ci		'DEVICE                     NAME                       A R S U C    rACTIVE   rSUSPEND\n'\
563062306a36Sopenharmony_ci		'---------------------------------------------------------------------------------------------')
563162306a36Sopenharmony_ci
563262306a36Sopenharmony_ci	res = []
563362306a36Sopenharmony_ci	tgtval = 'runtime_status'
563462306a36Sopenharmony_ci	lines = dict()
563562306a36Sopenharmony_ci	for dirname, dirnames, filenames in os.walk('/sys/devices'):
563662306a36Sopenharmony_ci		if(not re.match('.*/power', dirname) or
563762306a36Sopenharmony_ci			'control' not in filenames or
563862306a36Sopenharmony_ci			tgtval not in filenames):
563962306a36Sopenharmony_ci			continue
564062306a36Sopenharmony_ci		name = ''
564162306a36Sopenharmony_ci		dirname = dirname[:-6]
564262306a36Sopenharmony_ci		device = dirname.split('/')[-1]
564362306a36Sopenharmony_ci		power = dict()
564462306a36Sopenharmony_ci		power[tgtval] = readFile('%s/power/%s' % (dirname, tgtval))
564562306a36Sopenharmony_ci		# only list devices which support runtime suspend
564662306a36Sopenharmony_ci		if power[tgtval] not in ['active', 'suspended', 'suspending']:
564762306a36Sopenharmony_ci			continue
564862306a36Sopenharmony_ci		for i in ['product', 'driver', 'subsystem']:
564962306a36Sopenharmony_ci			file = '%s/%s' % (dirname, i)
565062306a36Sopenharmony_ci			if os.path.exists(file):
565162306a36Sopenharmony_ci				name = readFile(file)
565262306a36Sopenharmony_ci				break
565362306a36Sopenharmony_ci		for i in ['async', 'control', 'runtime_status', 'runtime_usage',
565462306a36Sopenharmony_ci			'runtime_active_kids', 'runtime_active_time',
565562306a36Sopenharmony_ci			'runtime_suspended_time']:
565662306a36Sopenharmony_ci			if i in filenames:
565762306a36Sopenharmony_ci				power[i] = readFile('%s/power/%s' % (dirname, i))
565862306a36Sopenharmony_ci		if output:
565962306a36Sopenharmony_ci			if power['control'] == output:
566062306a36Sopenharmony_ci				res.append('%s/power/control' % dirname)
566162306a36Sopenharmony_ci			continue
566262306a36Sopenharmony_ci		lines[dirname] = '%-26s %-26s %1s %1s %1s %1s %1s %10s %10s' % \
566362306a36Sopenharmony_ci			(device[:26], name[:26],
566462306a36Sopenharmony_ci			yesno(power['async']), \
566562306a36Sopenharmony_ci			yesno(power['control']), \
566662306a36Sopenharmony_ci			yesno(power['runtime_status']), \
566762306a36Sopenharmony_ci			power['runtime_usage'], \
566862306a36Sopenharmony_ci			power['runtime_active_kids'], \
566962306a36Sopenharmony_ci			ms2nice(power['runtime_active_time']), \
567062306a36Sopenharmony_ci			ms2nice(power['runtime_suspended_time']))
567162306a36Sopenharmony_ci	for i in sorted(lines):
567262306a36Sopenharmony_ci		print(lines[i])
567362306a36Sopenharmony_ci	return res
567462306a36Sopenharmony_ci
567562306a36Sopenharmony_ci# Function: getModes
567662306a36Sopenharmony_ci# Description:
567762306a36Sopenharmony_ci#	 Determine the supported power modes on this system
567862306a36Sopenharmony_ci# Output:
567962306a36Sopenharmony_ci#	 A string list of the available modes
568062306a36Sopenharmony_cidef getModes():
568162306a36Sopenharmony_ci	modes = []
568262306a36Sopenharmony_ci	if(os.path.exists(sysvals.powerfile)):
568362306a36Sopenharmony_ci		fp = open(sysvals.powerfile, 'r')
568462306a36Sopenharmony_ci		modes = fp.read().split()
568562306a36Sopenharmony_ci		fp.close()
568662306a36Sopenharmony_ci	if(os.path.exists(sysvals.mempowerfile)):
568762306a36Sopenharmony_ci		deep = False
568862306a36Sopenharmony_ci		fp = open(sysvals.mempowerfile, 'r')
568962306a36Sopenharmony_ci		for m in fp.read().split():
569062306a36Sopenharmony_ci			memmode = m.strip('[]')
569162306a36Sopenharmony_ci			if memmode == 'deep':
569262306a36Sopenharmony_ci				deep = True
569362306a36Sopenharmony_ci			else:
569462306a36Sopenharmony_ci				modes.append('mem-%s' % memmode)
569562306a36Sopenharmony_ci		fp.close()
569662306a36Sopenharmony_ci		if 'mem' in modes and not deep:
569762306a36Sopenharmony_ci			modes.remove('mem')
569862306a36Sopenharmony_ci	if('disk' in modes and os.path.exists(sysvals.diskpowerfile)):
569962306a36Sopenharmony_ci		fp = open(sysvals.diskpowerfile, 'r')
570062306a36Sopenharmony_ci		for m in fp.read().split():
570162306a36Sopenharmony_ci			modes.append('disk-%s' % m.strip('[]'))
570262306a36Sopenharmony_ci		fp.close()
570362306a36Sopenharmony_ci	return modes
570462306a36Sopenharmony_ci
570562306a36Sopenharmony_ci# Function: dmidecode
570662306a36Sopenharmony_ci# Description:
570762306a36Sopenharmony_ci#	 Read the bios tables and pull out system info
570862306a36Sopenharmony_ci# Arguments:
570962306a36Sopenharmony_ci#	 mempath: /dev/mem or custom mem path
571062306a36Sopenharmony_ci#	 fatal: True to exit on error, False to return empty dict
571162306a36Sopenharmony_ci# Output:
571262306a36Sopenharmony_ci#	 A dict object with all available key/values
571362306a36Sopenharmony_cidef dmidecode(mempath, fatal=False):
571462306a36Sopenharmony_ci	out = dict()
571562306a36Sopenharmony_ci
571662306a36Sopenharmony_ci	# the list of values to retrieve, with hardcoded (type, idx)
571762306a36Sopenharmony_ci	info = {
571862306a36Sopenharmony_ci		'bios-vendor': (0, 4),
571962306a36Sopenharmony_ci		'bios-version': (0, 5),
572062306a36Sopenharmony_ci		'bios-release-date': (0, 8),
572162306a36Sopenharmony_ci		'system-manufacturer': (1, 4),
572262306a36Sopenharmony_ci		'system-product-name': (1, 5),
572362306a36Sopenharmony_ci		'system-version': (1, 6),
572462306a36Sopenharmony_ci		'system-serial-number': (1, 7),
572562306a36Sopenharmony_ci		'baseboard-manufacturer': (2, 4),
572662306a36Sopenharmony_ci		'baseboard-product-name': (2, 5),
572762306a36Sopenharmony_ci		'baseboard-version': (2, 6),
572862306a36Sopenharmony_ci		'baseboard-serial-number': (2, 7),
572962306a36Sopenharmony_ci		'chassis-manufacturer': (3, 4),
573062306a36Sopenharmony_ci		'chassis-type': (3, 5),
573162306a36Sopenharmony_ci		'chassis-version': (3, 6),
573262306a36Sopenharmony_ci		'chassis-serial-number': (3, 7),
573362306a36Sopenharmony_ci		'processor-manufacturer': (4, 7),
573462306a36Sopenharmony_ci		'processor-version': (4, 16),
573562306a36Sopenharmony_ci	}
573662306a36Sopenharmony_ci	if(not os.path.exists(mempath)):
573762306a36Sopenharmony_ci		if(fatal):
573862306a36Sopenharmony_ci			doError('file does not exist: %s' % mempath)
573962306a36Sopenharmony_ci		return out
574062306a36Sopenharmony_ci	if(not os.access(mempath, os.R_OK)):
574162306a36Sopenharmony_ci		if(fatal):
574262306a36Sopenharmony_ci			doError('file is not readable: %s' % mempath)
574362306a36Sopenharmony_ci		return out
574462306a36Sopenharmony_ci
574562306a36Sopenharmony_ci	# by default use legacy scan, but try to use EFI first
574662306a36Sopenharmony_ci	memaddr = 0xf0000
574762306a36Sopenharmony_ci	memsize = 0x10000
574862306a36Sopenharmony_ci	for ep in ['/sys/firmware/efi/systab', '/proc/efi/systab']:
574962306a36Sopenharmony_ci		if not os.path.exists(ep) or not os.access(ep, os.R_OK):
575062306a36Sopenharmony_ci			continue
575162306a36Sopenharmony_ci		fp = open(ep, 'r')
575262306a36Sopenharmony_ci		buf = fp.read()
575362306a36Sopenharmony_ci		fp.close()
575462306a36Sopenharmony_ci		i = buf.find('SMBIOS=')
575562306a36Sopenharmony_ci		if i >= 0:
575662306a36Sopenharmony_ci			try:
575762306a36Sopenharmony_ci				memaddr = int(buf[i+7:], 16)
575862306a36Sopenharmony_ci				memsize = 0x20
575962306a36Sopenharmony_ci			except:
576062306a36Sopenharmony_ci				continue
576162306a36Sopenharmony_ci
576262306a36Sopenharmony_ci	# read in the memory for scanning
576362306a36Sopenharmony_ci	try:
576462306a36Sopenharmony_ci		fp = open(mempath, 'rb')
576562306a36Sopenharmony_ci		fp.seek(memaddr)
576662306a36Sopenharmony_ci		buf = fp.read(memsize)
576762306a36Sopenharmony_ci	except:
576862306a36Sopenharmony_ci		if(fatal):
576962306a36Sopenharmony_ci			doError('DMI table is unreachable, sorry')
577062306a36Sopenharmony_ci		else:
577162306a36Sopenharmony_ci			pprint('WARNING: /dev/mem is not readable, ignoring DMI data')
577262306a36Sopenharmony_ci			return out
577362306a36Sopenharmony_ci	fp.close()
577462306a36Sopenharmony_ci
577562306a36Sopenharmony_ci	# search for either an SM table or DMI table
577662306a36Sopenharmony_ci	i = base = length = num = 0
577762306a36Sopenharmony_ci	while(i < memsize):
577862306a36Sopenharmony_ci		if buf[i:i+4] == b'_SM_' and i < memsize - 16:
577962306a36Sopenharmony_ci			length = struct.unpack('H', buf[i+22:i+24])[0]
578062306a36Sopenharmony_ci			base, num = struct.unpack('IH', buf[i+24:i+30])
578162306a36Sopenharmony_ci			break
578262306a36Sopenharmony_ci		elif buf[i:i+5] == b'_DMI_':
578362306a36Sopenharmony_ci			length = struct.unpack('H', buf[i+6:i+8])[0]
578462306a36Sopenharmony_ci			base, num = struct.unpack('IH', buf[i+8:i+14])
578562306a36Sopenharmony_ci			break
578662306a36Sopenharmony_ci		i += 16
578762306a36Sopenharmony_ci	if base == 0 and length == 0 and num == 0:
578862306a36Sopenharmony_ci		if(fatal):
578962306a36Sopenharmony_ci			doError('Neither SMBIOS nor DMI were found')
579062306a36Sopenharmony_ci		else:
579162306a36Sopenharmony_ci			return out
579262306a36Sopenharmony_ci
579362306a36Sopenharmony_ci	# read in the SM or DMI table
579462306a36Sopenharmony_ci	try:
579562306a36Sopenharmony_ci		fp = open(mempath, 'rb')
579662306a36Sopenharmony_ci		fp.seek(base)
579762306a36Sopenharmony_ci		buf = fp.read(length)
579862306a36Sopenharmony_ci	except:
579962306a36Sopenharmony_ci		if(fatal):
580062306a36Sopenharmony_ci			doError('DMI table is unreachable, sorry')
580162306a36Sopenharmony_ci		else:
580262306a36Sopenharmony_ci			pprint('WARNING: /dev/mem is not readable, ignoring DMI data')
580362306a36Sopenharmony_ci			return out
580462306a36Sopenharmony_ci	fp.close()
580562306a36Sopenharmony_ci
580662306a36Sopenharmony_ci	# scan the table for the values we want
580762306a36Sopenharmony_ci	count = i = 0
580862306a36Sopenharmony_ci	while(count < num and i <= len(buf) - 4):
580962306a36Sopenharmony_ci		type, size, handle = struct.unpack('BBH', buf[i:i+4])
581062306a36Sopenharmony_ci		n = i + size
581162306a36Sopenharmony_ci		while n < len(buf) - 1:
581262306a36Sopenharmony_ci			if 0 == struct.unpack('H', buf[n:n+2])[0]:
581362306a36Sopenharmony_ci				break
581462306a36Sopenharmony_ci			n += 1
581562306a36Sopenharmony_ci		data = buf[i+size:n+2].split(b'\0')
581662306a36Sopenharmony_ci		for name in info:
581762306a36Sopenharmony_ci			itype, idxadr = info[name]
581862306a36Sopenharmony_ci			if itype == type:
581962306a36Sopenharmony_ci				idx = struct.unpack('B', buf[i+idxadr:i+idxadr+1])[0]
582062306a36Sopenharmony_ci				if idx > 0 and idx < len(data) - 1:
582162306a36Sopenharmony_ci					s = data[idx-1].decode('utf-8')
582262306a36Sopenharmony_ci					if s.strip() and s.strip().lower() != 'to be filled by o.e.m.':
582362306a36Sopenharmony_ci						out[name] = s
582462306a36Sopenharmony_ci		i = n + 2
582562306a36Sopenharmony_ci		count += 1
582662306a36Sopenharmony_ci	return out
582762306a36Sopenharmony_ci
582862306a36Sopenharmony_ci# Function: getFPDT
582962306a36Sopenharmony_ci# Description:
583062306a36Sopenharmony_ci#	 Read the acpi bios tables and pull out FPDT, the firmware data
583162306a36Sopenharmony_ci# Arguments:
583262306a36Sopenharmony_ci#	 output: True to output the info to stdout, False otherwise
583362306a36Sopenharmony_cidef getFPDT(output):
583462306a36Sopenharmony_ci	rectype = {}
583562306a36Sopenharmony_ci	rectype[0] = 'Firmware Basic Boot Performance Record'
583662306a36Sopenharmony_ci	rectype[1] = 'S3 Performance Table Record'
583762306a36Sopenharmony_ci	prectype = {}
583862306a36Sopenharmony_ci	prectype[0] = 'Basic S3 Resume Performance Record'
583962306a36Sopenharmony_ci	prectype[1] = 'Basic S3 Suspend Performance Record'
584062306a36Sopenharmony_ci
584162306a36Sopenharmony_ci	sysvals.rootCheck(True)
584262306a36Sopenharmony_ci	if(not os.path.exists(sysvals.fpdtpath)):
584362306a36Sopenharmony_ci		if(output):
584462306a36Sopenharmony_ci			doError('file does not exist: %s' % sysvals.fpdtpath)
584562306a36Sopenharmony_ci		return False
584662306a36Sopenharmony_ci	if(not os.access(sysvals.fpdtpath, os.R_OK)):
584762306a36Sopenharmony_ci		if(output):
584862306a36Sopenharmony_ci			doError('file is not readable: %s' % sysvals.fpdtpath)
584962306a36Sopenharmony_ci		return False
585062306a36Sopenharmony_ci	if(not os.path.exists(sysvals.mempath)):
585162306a36Sopenharmony_ci		if(output):
585262306a36Sopenharmony_ci			doError('file does not exist: %s' % sysvals.mempath)
585362306a36Sopenharmony_ci		return False
585462306a36Sopenharmony_ci	if(not os.access(sysvals.mempath, os.R_OK)):
585562306a36Sopenharmony_ci		if(output):
585662306a36Sopenharmony_ci			doError('file is not readable: %s' % sysvals.mempath)
585762306a36Sopenharmony_ci		return False
585862306a36Sopenharmony_ci
585962306a36Sopenharmony_ci	fp = open(sysvals.fpdtpath, 'rb')
586062306a36Sopenharmony_ci	buf = fp.read()
586162306a36Sopenharmony_ci	fp.close()
586262306a36Sopenharmony_ci
586362306a36Sopenharmony_ci	if(len(buf) < 36):
586462306a36Sopenharmony_ci		if(output):
586562306a36Sopenharmony_ci			doError('Invalid FPDT table data, should '+\
586662306a36Sopenharmony_ci				'be at least 36 bytes')
586762306a36Sopenharmony_ci		return False
586862306a36Sopenharmony_ci
586962306a36Sopenharmony_ci	table = struct.unpack('4sIBB6s8sI4sI', buf[0:36])
587062306a36Sopenharmony_ci	if(output):
587162306a36Sopenharmony_ci		pprint('\n'\
587262306a36Sopenharmony_ci		'Firmware Performance Data Table (%s)\n'\
587362306a36Sopenharmony_ci		'                  Signature : %s\n'\
587462306a36Sopenharmony_ci		'               Table Length : %u\n'\
587562306a36Sopenharmony_ci		'                   Revision : %u\n'\
587662306a36Sopenharmony_ci		'                   Checksum : 0x%x\n'\
587762306a36Sopenharmony_ci		'                     OEM ID : %s\n'\
587862306a36Sopenharmony_ci		'               OEM Table ID : %s\n'\
587962306a36Sopenharmony_ci		'               OEM Revision : %u\n'\
588062306a36Sopenharmony_ci		'                 Creator ID : %s\n'\
588162306a36Sopenharmony_ci		'           Creator Revision : 0x%x\n'\
588262306a36Sopenharmony_ci		'' % (ascii(table[0]), ascii(table[0]), table[1], table[2],
588362306a36Sopenharmony_ci			table[3], ascii(table[4]), ascii(table[5]), table[6],
588462306a36Sopenharmony_ci			ascii(table[7]), table[8]))
588562306a36Sopenharmony_ci
588662306a36Sopenharmony_ci	if(table[0] != b'FPDT'):
588762306a36Sopenharmony_ci		if(output):
588862306a36Sopenharmony_ci			doError('Invalid FPDT table')
588962306a36Sopenharmony_ci		return False
589062306a36Sopenharmony_ci	if(len(buf) <= 36):
589162306a36Sopenharmony_ci		return False
589262306a36Sopenharmony_ci	i = 0
589362306a36Sopenharmony_ci	fwData = [0, 0]
589462306a36Sopenharmony_ci	records = buf[36:]
589562306a36Sopenharmony_ci	try:
589662306a36Sopenharmony_ci		fp = open(sysvals.mempath, 'rb')
589762306a36Sopenharmony_ci	except:
589862306a36Sopenharmony_ci		pprint('WARNING: /dev/mem is not readable, ignoring the FPDT data')
589962306a36Sopenharmony_ci		return False
590062306a36Sopenharmony_ci	while(i < len(records)):
590162306a36Sopenharmony_ci		header = struct.unpack('HBB', records[i:i+4])
590262306a36Sopenharmony_ci		if(header[0] not in rectype):
590362306a36Sopenharmony_ci			i += header[1]
590462306a36Sopenharmony_ci			continue
590562306a36Sopenharmony_ci		if(header[1] != 16):
590662306a36Sopenharmony_ci			i += header[1]
590762306a36Sopenharmony_ci			continue
590862306a36Sopenharmony_ci		addr = struct.unpack('Q', records[i+8:i+16])[0]
590962306a36Sopenharmony_ci		try:
591062306a36Sopenharmony_ci			fp.seek(addr)
591162306a36Sopenharmony_ci			first = fp.read(8)
591262306a36Sopenharmony_ci		except:
591362306a36Sopenharmony_ci			if(output):
591462306a36Sopenharmony_ci				pprint('Bad address 0x%x in %s' % (addr, sysvals.mempath))
591562306a36Sopenharmony_ci			return [0, 0]
591662306a36Sopenharmony_ci		rechead = struct.unpack('4sI', first)
591762306a36Sopenharmony_ci		recdata = fp.read(rechead[1]-8)
591862306a36Sopenharmony_ci		if(rechead[0] == b'FBPT'):
591962306a36Sopenharmony_ci			record = struct.unpack('HBBIQQQQQ', recdata[:48])
592062306a36Sopenharmony_ci			if(output):
592162306a36Sopenharmony_ci				pprint('%s (%s)\n'\
592262306a36Sopenharmony_ci				'                  Reset END : %u ns\n'\
592362306a36Sopenharmony_ci				'  OS Loader LoadImage Start : %u ns\n'\
592462306a36Sopenharmony_ci				' OS Loader StartImage Start : %u ns\n'\
592562306a36Sopenharmony_ci				'     ExitBootServices Entry : %u ns\n'\
592662306a36Sopenharmony_ci				'      ExitBootServices Exit : %u ns'\
592762306a36Sopenharmony_ci				'' % (rectype[header[0]], ascii(rechead[0]), record[4], record[5],
592862306a36Sopenharmony_ci					record[6], record[7], record[8]))
592962306a36Sopenharmony_ci		elif(rechead[0] == b'S3PT'):
593062306a36Sopenharmony_ci			if(output):
593162306a36Sopenharmony_ci				pprint('%s (%s)' % (rectype[header[0]], ascii(rechead[0])))
593262306a36Sopenharmony_ci			j = 0
593362306a36Sopenharmony_ci			while(j < len(recdata)):
593462306a36Sopenharmony_ci				prechead = struct.unpack('HBB', recdata[j:j+4])
593562306a36Sopenharmony_ci				if(prechead[0] not in prectype):
593662306a36Sopenharmony_ci					continue
593762306a36Sopenharmony_ci				if(prechead[0] == 0):
593862306a36Sopenharmony_ci					record = struct.unpack('IIQQ', recdata[j:j+prechead[1]])
593962306a36Sopenharmony_ci					fwData[1] = record[2]
594062306a36Sopenharmony_ci					if(output):
594162306a36Sopenharmony_ci						pprint('    %s\n'\
594262306a36Sopenharmony_ci						'               Resume Count : %u\n'\
594362306a36Sopenharmony_ci						'                 FullResume : %u ns\n'\
594462306a36Sopenharmony_ci						'              AverageResume : %u ns'\
594562306a36Sopenharmony_ci						'' % (prectype[prechead[0]], record[1],
594662306a36Sopenharmony_ci								record[2], record[3]))
594762306a36Sopenharmony_ci				elif(prechead[0] == 1):
594862306a36Sopenharmony_ci					record = struct.unpack('QQ', recdata[j+4:j+prechead[1]])
594962306a36Sopenharmony_ci					fwData[0] = record[1] - record[0]
595062306a36Sopenharmony_ci					if(output):
595162306a36Sopenharmony_ci						pprint('    %s\n'\
595262306a36Sopenharmony_ci						'               SuspendStart : %u ns\n'\
595362306a36Sopenharmony_ci						'                 SuspendEnd : %u ns\n'\
595462306a36Sopenharmony_ci						'                SuspendTime : %u ns'\
595562306a36Sopenharmony_ci						'' % (prectype[prechead[0]], record[0],
595662306a36Sopenharmony_ci								record[1], fwData[0]))
595762306a36Sopenharmony_ci
595862306a36Sopenharmony_ci				j += prechead[1]
595962306a36Sopenharmony_ci		if(output):
596062306a36Sopenharmony_ci			pprint('')
596162306a36Sopenharmony_ci		i += header[1]
596262306a36Sopenharmony_ci	fp.close()
596362306a36Sopenharmony_ci	return fwData
596462306a36Sopenharmony_ci
596562306a36Sopenharmony_ci# Function: statusCheck
596662306a36Sopenharmony_ci# Description:
596762306a36Sopenharmony_ci#	 Verify that the requested command and options will work, and
596862306a36Sopenharmony_ci#	 print the results to the terminal
596962306a36Sopenharmony_ci# Output:
597062306a36Sopenharmony_ci#	 True if the test will work, False if not
597162306a36Sopenharmony_cidef statusCheck(probecheck=False):
597262306a36Sopenharmony_ci	status = ''
597362306a36Sopenharmony_ci
597462306a36Sopenharmony_ci	pprint('Checking this system (%s)...' % platform.node())
597562306a36Sopenharmony_ci
597662306a36Sopenharmony_ci	# check we have root access
597762306a36Sopenharmony_ci	res = sysvals.colorText('NO (No features of this tool will work!)')
597862306a36Sopenharmony_ci	if(sysvals.rootCheck(False)):
597962306a36Sopenharmony_ci		res = 'YES'
598062306a36Sopenharmony_ci	pprint('    have root access: %s' % res)
598162306a36Sopenharmony_ci	if(res != 'YES'):
598262306a36Sopenharmony_ci		pprint('    Try running this script with sudo')
598362306a36Sopenharmony_ci		return 'missing root access'
598462306a36Sopenharmony_ci
598562306a36Sopenharmony_ci	# check sysfs is mounted
598662306a36Sopenharmony_ci	res = sysvals.colorText('NO (No features of this tool will work!)')
598762306a36Sopenharmony_ci	if(os.path.exists(sysvals.powerfile)):
598862306a36Sopenharmony_ci		res = 'YES'
598962306a36Sopenharmony_ci	pprint('    is sysfs mounted: %s' % res)
599062306a36Sopenharmony_ci	if(res != 'YES'):
599162306a36Sopenharmony_ci		return 'sysfs is missing'
599262306a36Sopenharmony_ci
599362306a36Sopenharmony_ci	# check target mode is a valid mode
599462306a36Sopenharmony_ci	if sysvals.suspendmode != 'command':
599562306a36Sopenharmony_ci		res = sysvals.colorText('NO')
599662306a36Sopenharmony_ci		modes = getModes()
599762306a36Sopenharmony_ci		if(sysvals.suspendmode in modes):
599862306a36Sopenharmony_ci			res = 'YES'
599962306a36Sopenharmony_ci		else:
600062306a36Sopenharmony_ci			status = '%s mode is not supported' % sysvals.suspendmode
600162306a36Sopenharmony_ci		pprint('    is "%s" a valid power mode: %s' % (sysvals.suspendmode, res))
600262306a36Sopenharmony_ci		if(res == 'NO'):
600362306a36Sopenharmony_ci			pprint('      valid power modes are: %s' % modes)
600462306a36Sopenharmony_ci			pprint('      please choose one with -m')
600562306a36Sopenharmony_ci
600662306a36Sopenharmony_ci	# check if ftrace is available
600762306a36Sopenharmony_ci	if sysvals.useftrace:
600862306a36Sopenharmony_ci		res = sysvals.colorText('NO')
600962306a36Sopenharmony_ci		sysvals.useftrace = sysvals.verifyFtrace()
601062306a36Sopenharmony_ci		efmt = '"{0}" uses ftrace, and it is not properly supported'
601162306a36Sopenharmony_ci		if sysvals.useftrace:
601262306a36Sopenharmony_ci			res = 'YES'
601362306a36Sopenharmony_ci		elif sysvals.usecallgraph:
601462306a36Sopenharmony_ci			status = efmt.format('-f')
601562306a36Sopenharmony_ci		elif sysvals.usedevsrc:
601662306a36Sopenharmony_ci			status = efmt.format('-dev')
601762306a36Sopenharmony_ci		elif sysvals.useprocmon:
601862306a36Sopenharmony_ci			status = efmt.format('-proc')
601962306a36Sopenharmony_ci		pprint('    is ftrace supported: %s' % res)
602062306a36Sopenharmony_ci
602162306a36Sopenharmony_ci	# check if kprobes are available
602262306a36Sopenharmony_ci	if sysvals.usekprobes:
602362306a36Sopenharmony_ci		res = sysvals.colorText('NO')
602462306a36Sopenharmony_ci		sysvals.usekprobes = sysvals.verifyKprobes()
602562306a36Sopenharmony_ci		if(sysvals.usekprobes):
602662306a36Sopenharmony_ci			res = 'YES'
602762306a36Sopenharmony_ci		else:
602862306a36Sopenharmony_ci			sysvals.usedevsrc = False
602962306a36Sopenharmony_ci		pprint('    are kprobes supported: %s' % res)
603062306a36Sopenharmony_ci
603162306a36Sopenharmony_ci	# what data source are we using
603262306a36Sopenharmony_ci	res = 'DMESG (very limited, ftrace is preferred)'
603362306a36Sopenharmony_ci	if sysvals.useftrace:
603462306a36Sopenharmony_ci		sysvals.usetraceevents = True
603562306a36Sopenharmony_ci		for e in sysvals.traceevents:
603662306a36Sopenharmony_ci			if not os.path.exists(sysvals.epath+e):
603762306a36Sopenharmony_ci				sysvals.usetraceevents = False
603862306a36Sopenharmony_ci		if(sysvals.usetraceevents):
603962306a36Sopenharmony_ci			res = 'FTRACE (all trace events found)'
604062306a36Sopenharmony_ci	pprint('    timeline data source: %s' % res)
604162306a36Sopenharmony_ci
604262306a36Sopenharmony_ci	# check if rtcwake
604362306a36Sopenharmony_ci	res = sysvals.colorText('NO')
604462306a36Sopenharmony_ci	if(sysvals.rtcpath != ''):
604562306a36Sopenharmony_ci		res = 'YES'
604662306a36Sopenharmony_ci	elif(sysvals.rtcwake):
604762306a36Sopenharmony_ci		status = 'rtcwake is not properly supported'
604862306a36Sopenharmony_ci	pprint('    is rtcwake supported: %s' % res)
604962306a36Sopenharmony_ci
605062306a36Sopenharmony_ci	# check info commands
605162306a36Sopenharmony_ci	pprint('    optional commands this tool may use for info:')
605262306a36Sopenharmony_ci	no = sysvals.colorText('MISSING')
605362306a36Sopenharmony_ci	yes = sysvals.colorText('FOUND', 32)
605462306a36Sopenharmony_ci	for c in ['turbostat', 'mcelog', 'lspci', 'lsusb', 'netfix']:
605562306a36Sopenharmony_ci		if c == 'turbostat':
605662306a36Sopenharmony_ci			res = yes if sysvals.haveTurbostat() else no
605762306a36Sopenharmony_ci		else:
605862306a36Sopenharmony_ci			res = yes if sysvals.getExec(c) else no
605962306a36Sopenharmony_ci		pprint('        %s: %s' % (c, res))
606062306a36Sopenharmony_ci
606162306a36Sopenharmony_ci	if not probecheck:
606262306a36Sopenharmony_ci		return status
606362306a36Sopenharmony_ci
606462306a36Sopenharmony_ci	# verify kprobes
606562306a36Sopenharmony_ci	if sysvals.usekprobes:
606662306a36Sopenharmony_ci		for name in sysvals.tracefuncs:
606762306a36Sopenharmony_ci			sysvals.defaultKprobe(name, sysvals.tracefuncs[name])
606862306a36Sopenharmony_ci		if sysvals.usedevsrc:
606962306a36Sopenharmony_ci			for name in sysvals.dev_tracefuncs:
607062306a36Sopenharmony_ci				sysvals.defaultKprobe(name, sysvals.dev_tracefuncs[name])
607162306a36Sopenharmony_ci		sysvals.addKprobes(True)
607262306a36Sopenharmony_ci
607362306a36Sopenharmony_ci	return status
607462306a36Sopenharmony_ci
607562306a36Sopenharmony_ci# Function: doError
607662306a36Sopenharmony_ci# Description:
607762306a36Sopenharmony_ci#	 generic error function for catastrphic failures
607862306a36Sopenharmony_ci# Arguments:
607962306a36Sopenharmony_ci#	 msg: the error message to print
608062306a36Sopenharmony_ci#	 help: True if printHelp should be called after, False otherwise
608162306a36Sopenharmony_cidef doError(msg, help=False):
608262306a36Sopenharmony_ci	if(help == True):
608362306a36Sopenharmony_ci		printHelp()
608462306a36Sopenharmony_ci	pprint('ERROR: %s\n' % msg)
608562306a36Sopenharmony_ci	sysvals.outputResult({'error':msg})
608662306a36Sopenharmony_ci	sys.exit(1)
608762306a36Sopenharmony_ci
608862306a36Sopenharmony_ci# Function: getArgInt
608962306a36Sopenharmony_ci# Description:
609062306a36Sopenharmony_ci#	 pull out an integer argument from the command line with checks
609162306a36Sopenharmony_cidef getArgInt(name, args, min, max, main=True):
609262306a36Sopenharmony_ci	if main:
609362306a36Sopenharmony_ci		try:
609462306a36Sopenharmony_ci			arg = next(args)
609562306a36Sopenharmony_ci		except:
609662306a36Sopenharmony_ci			doError(name+': no argument supplied', True)
609762306a36Sopenharmony_ci	else:
609862306a36Sopenharmony_ci		arg = args
609962306a36Sopenharmony_ci	try:
610062306a36Sopenharmony_ci		val = int(arg)
610162306a36Sopenharmony_ci	except:
610262306a36Sopenharmony_ci		doError(name+': non-integer value given', True)
610362306a36Sopenharmony_ci	if(val < min or val > max):
610462306a36Sopenharmony_ci		doError(name+': value should be between %d and %d' % (min, max), True)
610562306a36Sopenharmony_ci	return val
610662306a36Sopenharmony_ci
610762306a36Sopenharmony_ci# Function: getArgFloat
610862306a36Sopenharmony_ci# Description:
610962306a36Sopenharmony_ci#	 pull out a float argument from the command line with checks
611062306a36Sopenharmony_cidef getArgFloat(name, args, min, max, main=True):
611162306a36Sopenharmony_ci	if main:
611262306a36Sopenharmony_ci		try:
611362306a36Sopenharmony_ci			arg = next(args)
611462306a36Sopenharmony_ci		except:
611562306a36Sopenharmony_ci			doError(name+': no argument supplied', True)
611662306a36Sopenharmony_ci	else:
611762306a36Sopenharmony_ci		arg = args
611862306a36Sopenharmony_ci	try:
611962306a36Sopenharmony_ci		val = float(arg)
612062306a36Sopenharmony_ci	except:
612162306a36Sopenharmony_ci		doError(name+': non-numerical value given', True)
612262306a36Sopenharmony_ci	if(val < min or val > max):
612362306a36Sopenharmony_ci		doError(name+': value should be between %f and %f' % (min, max), True)
612462306a36Sopenharmony_ci	return val
612562306a36Sopenharmony_ci
612662306a36Sopenharmony_cidef processData(live=False, quiet=False):
612762306a36Sopenharmony_ci	if not quiet:
612862306a36Sopenharmony_ci		pprint('PROCESSING: %s' % sysvals.htmlfile)
612962306a36Sopenharmony_ci	sysvals.vprint('usetraceevents=%s, usetracemarkers=%s, usekprobes=%s' % \
613062306a36Sopenharmony_ci		(sysvals.usetraceevents, sysvals.usetracemarkers, sysvals.usekprobes))
613162306a36Sopenharmony_ci	error = ''
613262306a36Sopenharmony_ci	if(sysvals.usetraceevents):
613362306a36Sopenharmony_ci		testruns, error = parseTraceLog(live)
613462306a36Sopenharmony_ci		if sysvals.dmesgfile:
613562306a36Sopenharmony_ci			for data in testruns:
613662306a36Sopenharmony_ci				data.extractErrorInfo()
613762306a36Sopenharmony_ci	else:
613862306a36Sopenharmony_ci		testruns = loadKernelLog()
613962306a36Sopenharmony_ci		for data in testruns:
614062306a36Sopenharmony_ci			parseKernelLog(data)
614162306a36Sopenharmony_ci		if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)):
614262306a36Sopenharmony_ci			appendIncompleteTraceLog(testruns)
614362306a36Sopenharmony_ci	if not sysvals.stamp:
614462306a36Sopenharmony_ci		pprint('ERROR: data does not include the expected stamp')
614562306a36Sopenharmony_ci		return (testruns, {'error': 'timeline generation failed'})
614662306a36Sopenharmony_ci	shown = ['os', 'bios', 'biosdate', 'cpu', 'host', 'kernel', 'man', 'memfr',
614762306a36Sopenharmony_ci			'memsz', 'mode', 'numcpu', 'plat', 'time', 'wifi']
614862306a36Sopenharmony_ci	sysvals.vprint('System Info:')
614962306a36Sopenharmony_ci	for key in sorted(sysvals.stamp):
615062306a36Sopenharmony_ci		if key in shown:
615162306a36Sopenharmony_ci			sysvals.vprint('    %-8s : %s' % (key.upper(), sysvals.stamp[key]))
615262306a36Sopenharmony_ci	sysvals.vprint('Command:\n    %s' % sysvals.cmdline)
615362306a36Sopenharmony_ci	for data in testruns:
615462306a36Sopenharmony_ci		if data.turbostat:
615562306a36Sopenharmony_ci			idx, s = 0, 'Turbostat:\n    '
615662306a36Sopenharmony_ci			for val in data.turbostat.split('|'):
615762306a36Sopenharmony_ci				idx += len(val) + 1
615862306a36Sopenharmony_ci				if idx >= 80:
615962306a36Sopenharmony_ci					idx = 0
616062306a36Sopenharmony_ci					s += '\n    '
616162306a36Sopenharmony_ci				s += val + ' '
616262306a36Sopenharmony_ci			sysvals.vprint(s)
616362306a36Sopenharmony_ci		data.printDetails()
616462306a36Sopenharmony_ci	if len(sysvals.platinfo) > 0:
616562306a36Sopenharmony_ci		sysvals.vprint('\nPlatform Info:')
616662306a36Sopenharmony_ci		for info in sysvals.platinfo:
616762306a36Sopenharmony_ci			sysvals.vprint('[%s - %s]' % (info[0], info[1]))
616862306a36Sopenharmony_ci			sysvals.vprint(info[2])
616962306a36Sopenharmony_ci		sysvals.vprint('')
617062306a36Sopenharmony_ci	if sysvals.cgdump:
617162306a36Sopenharmony_ci		for data in testruns:
617262306a36Sopenharmony_ci			data.debugPrint()
617362306a36Sopenharmony_ci		sys.exit(0)
617462306a36Sopenharmony_ci	if len(testruns) < 1:
617562306a36Sopenharmony_ci		pprint('ERROR: Not enough test data to build a timeline')
617662306a36Sopenharmony_ci		return (testruns, {'error': 'timeline generation failed'})
617762306a36Sopenharmony_ci	sysvals.vprint('Creating the html timeline (%s)...' % sysvals.htmlfile)
617862306a36Sopenharmony_ci	createHTML(testruns, error)
617962306a36Sopenharmony_ci	if not quiet:
618062306a36Sopenharmony_ci		pprint('DONE:       %s' % sysvals.htmlfile)
618162306a36Sopenharmony_ci	data = testruns[0]
618262306a36Sopenharmony_ci	stamp = data.stamp
618362306a36Sopenharmony_ci	stamp['suspend'], stamp['resume'] = data.getTimeValues()
618462306a36Sopenharmony_ci	if data.fwValid:
618562306a36Sopenharmony_ci		stamp['fwsuspend'], stamp['fwresume'] = data.fwSuspend, data.fwResume
618662306a36Sopenharmony_ci	if error:
618762306a36Sopenharmony_ci		stamp['error'] = error
618862306a36Sopenharmony_ci	return (testruns, stamp)
618962306a36Sopenharmony_ci
619062306a36Sopenharmony_ci# Function: rerunTest
619162306a36Sopenharmony_ci# Description:
619262306a36Sopenharmony_ci#	 generate an output from an existing set of ftrace/dmesg logs
619362306a36Sopenharmony_cidef rerunTest(htmlfile=''):
619462306a36Sopenharmony_ci	if sysvals.ftracefile:
619562306a36Sopenharmony_ci		doesTraceLogHaveTraceEvents()
619662306a36Sopenharmony_ci	if not sysvals.dmesgfile and not sysvals.usetraceevents:
619762306a36Sopenharmony_ci		doError('recreating this html output requires a dmesg file')
619862306a36Sopenharmony_ci	if htmlfile:
619962306a36Sopenharmony_ci		sysvals.htmlfile = htmlfile
620062306a36Sopenharmony_ci	else:
620162306a36Sopenharmony_ci		sysvals.setOutputFile()
620262306a36Sopenharmony_ci	if os.path.exists(sysvals.htmlfile):
620362306a36Sopenharmony_ci		if not os.path.isfile(sysvals.htmlfile):
620462306a36Sopenharmony_ci			doError('a directory already exists with this name: %s' % sysvals.htmlfile)
620562306a36Sopenharmony_ci		elif not os.access(sysvals.htmlfile, os.W_OK):
620662306a36Sopenharmony_ci			doError('missing permission to write to %s' % sysvals.htmlfile)
620762306a36Sopenharmony_ci	testruns, stamp = processData()
620862306a36Sopenharmony_ci	sysvals.resetlog()
620962306a36Sopenharmony_ci	return stamp
621062306a36Sopenharmony_ci
621162306a36Sopenharmony_ci# Function: runTest
621262306a36Sopenharmony_ci# Description:
621362306a36Sopenharmony_ci#	 execute a suspend/resume, gather the logs, and generate the output
621462306a36Sopenharmony_cidef runTest(n=0, quiet=False):
621562306a36Sopenharmony_ci	# prepare for the test
621662306a36Sopenharmony_ci	sysvals.initTestOutput('suspend')
621762306a36Sopenharmony_ci	op = sysvals.writeDatafileHeader(sysvals.dmesgfile, [])
621862306a36Sopenharmony_ci	op.write('# EXECUTION TRACE START\n')
621962306a36Sopenharmony_ci	op.close()
622062306a36Sopenharmony_ci	if n <= 1:
622162306a36Sopenharmony_ci		if sysvals.rs != 0:
622262306a36Sopenharmony_ci			sysvals.dlog('%sabling runtime suspend' % ('en' if sysvals.rs > 0 else 'dis'))
622362306a36Sopenharmony_ci			sysvals.setRuntimeSuspend(True)
622462306a36Sopenharmony_ci		if sysvals.display:
622562306a36Sopenharmony_ci			ret = sysvals.displayControl('init')
622662306a36Sopenharmony_ci			sysvals.dlog('xset display init, ret = %d' % ret)
622762306a36Sopenharmony_ci	sysvals.testVal(sysvals.pmdpath, 'basic', '1')
622862306a36Sopenharmony_ci	sysvals.testVal(sysvals.s0ixpath, 'basic', 'Y')
622962306a36Sopenharmony_ci	sysvals.dlog('initialize ftrace')
623062306a36Sopenharmony_ci	sysvals.initFtrace(quiet)
623162306a36Sopenharmony_ci
623262306a36Sopenharmony_ci	# execute the test
623362306a36Sopenharmony_ci	executeSuspend(quiet)
623462306a36Sopenharmony_ci	sysvals.cleanupFtrace()
623562306a36Sopenharmony_ci	if sysvals.skiphtml:
623662306a36Sopenharmony_ci		sysvals.outputResult({}, n)
623762306a36Sopenharmony_ci		sysvals.sudoUserchown(sysvals.testdir)
623862306a36Sopenharmony_ci		return
623962306a36Sopenharmony_ci	testruns, stamp = processData(True, quiet)
624062306a36Sopenharmony_ci	for data in testruns:
624162306a36Sopenharmony_ci		del data
624262306a36Sopenharmony_ci	sysvals.sudoUserchown(sysvals.testdir)
624362306a36Sopenharmony_ci	sysvals.outputResult(stamp, n)
624462306a36Sopenharmony_ci	if 'error' in stamp:
624562306a36Sopenharmony_ci		return 2
624662306a36Sopenharmony_ci	return 0
624762306a36Sopenharmony_ci
624862306a36Sopenharmony_cidef find_in_html(html, start, end, firstonly=True):
624962306a36Sopenharmony_ci	cnt, out, list = len(html), [], []
625062306a36Sopenharmony_ci	if firstonly:
625162306a36Sopenharmony_ci		m = re.search(start, html)
625262306a36Sopenharmony_ci		if m:
625362306a36Sopenharmony_ci			list.append(m)
625462306a36Sopenharmony_ci	else:
625562306a36Sopenharmony_ci		list = re.finditer(start, html)
625662306a36Sopenharmony_ci	for match in list:
625762306a36Sopenharmony_ci		s = match.end()
625862306a36Sopenharmony_ci		e = cnt if (len(out) < 1 or s + 10000 > cnt) else s + 10000
625962306a36Sopenharmony_ci		m = re.search(end, html[s:e])
626062306a36Sopenharmony_ci		if not m:
626162306a36Sopenharmony_ci			break
626262306a36Sopenharmony_ci		e = s + m.start()
626362306a36Sopenharmony_ci		str = html[s:e]
626462306a36Sopenharmony_ci		if end == 'ms':
626562306a36Sopenharmony_ci			num = re.search(r'[-+]?\d*\.\d+|\d+', str)
626662306a36Sopenharmony_ci			str = num.group() if num else 'NaN'
626762306a36Sopenharmony_ci		if firstonly:
626862306a36Sopenharmony_ci			return str
626962306a36Sopenharmony_ci		out.append(str)
627062306a36Sopenharmony_ci	if firstonly:
627162306a36Sopenharmony_ci		return ''
627262306a36Sopenharmony_ci	return out
627362306a36Sopenharmony_ci
627462306a36Sopenharmony_cidef data_from_html(file, outpath, issues, fulldetail=False):
627562306a36Sopenharmony_ci	html = open(file, 'r').read()
627662306a36Sopenharmony_ci	sysvals.htmlfile = os.path.relpath(file, outpath)
627762306a36Sopenharmony_ci	# extract general info
627862306a36Sopenharmony_ci	suspend = find_in_html(html, 'Kernel Suspend', 'ms')
627962306a36Sopenharmony_ci	resume = find_in_html(html, 'Kernel Resume', 'ms')
628062306a36Sopenharmony_ci	sysinfo = find_in_html(html, '<div class="stamp sysinfo">', '</div>')
628162306a36Sopenharmony_ci	line = find_in_html(html, '<div class="stamp">', '</div>')
628262306a36Sopenharmony_ci	stmp = line.split()
628362306a36Sopenharmony_ci	if not suspend or not resume or len(stmp) != 8:
628462306a36Sopenharmony_ci		return False
628562306a36Sopenharmony_ci	try:
628662306a36Sopenharmony_ci		dt = datetime.strptime(' '.join(stmp[3:]), '%B %d %Y, %I:%M:%S %p')
628762306a36Sopenharmony_ci	except:
628862306a36Sopenharmony_ci		return False
628962306a36Sopenharmony_ci	sysvals.hostname = stmp[0]
629062306a36Sopenharmony_ci	tstr = dt.strftime('%Y/%m/%d %H:%M:%S')
629162306a36Sopenharmony_ci	error = find_in_html(html, '<table class="testfail"><tr><td>', '</td>')
629262306a36Sopenharmony_ci	if error:
629362306a36Sopenharmony_ci		m = re.match('[a-z0-9]* failed in (?P<p>\S*).*', error)
629462306a36Sopenharmony_ci		if m:
629562306a36Sopenharmony_ci			result = 'fail in %s' % m.group('p')
629662306a36Sopenharmony_ci		else:
629762306a36Sopenharmony_ci			result = 'fail'
629862306a36Sopenharmony_ci	else:
629962306a36Sopenharmony_ci		result = 'pass'
630062306a36Sopenharmony_ci	# extract error info
630162306a36Sopenharmony_ci	tp, ilist = False, []
630262306a36Sopenharmony_ci	extra = dict()
630362306a36Sopenharmony_ci	log = find_in_html(html, '<div id="dmesglog" style="display:none;">',
630462306a36Sopenharmony_ci		'</div>').strip()
630562306a36Sopenharmony_ci	if log:
630662306a36Sopenharmony_ci		d = Data(0)
630762306a36Sopenharmony_ci		d.end = 999999999
630862306a36Sopenharmony_ci		d.dmesgtext = log.split('\n')
630962306a36Sopenharmony_ci		tp = d.extractErrorInfo()
631062306a36Sopenharmony_ci		for msg in tp.msglist:
631162306a36Sopenharmony_ci			sysvals.errorSummary(issues, msg)
631262306a36Sopenharmony_ci		if stmp[2] == 'freeze':
631362306a36Sopenharmony_ci			extra = d.turbostatInfo()
631462306a36Sopenharmony_ci		elist = dict()
631562306a36Sopenharmony_ci		for dir in d.errorinfo:
631662306a36Sopenharmony_ci			for err in d.errorinfo[dir]:
631762306a36Sopenharmony_ci				if err[0] not in elist:
631862306a36Sopenharmony_ci					elist[err[0]] = 0
631962306a36Sopenharmony_ci				elist[err[0]] += 1
632062306a36Sopenharmony_ci		for i in elist:
632162306a36Sopenharmony_ci			ilist.append('%sx%d' % (i, elist[i]) if elist[i] > 1 else i)
632262306a36Sopenharmony_ci		line = find_in_html(log, '# wifi ', '\n')
632362306a36Sopenharmony_ci		if line:
632462306a36Sopenharmony_ci			extra['wifi'] = line
632562306a36Sopenharmony_ci		line = find_in_html(log, '# netfix ', '\n')
632662306a36Sopenharmony_ci		if line:
632762306a36Sopenharmony_ci			extra['netfix'] = line
632862306a36Sopenharmony_ci	low = find_in_html(html, 'freeze time: <b>', ' ms</b>')
632962306a36Sopenharmony_ci	for lowstr in ['waking', '+']:
633062306a36Sopenharmony_ci		if not low:
633162306a36Sopenharmony_ci			break
633262306a36Sopenharmony_ci		if lowstr not in low:
633362306a36Sopenharmony_ci			continue
633462306a36Sopenharmony_ci		if lowstr == '+':
633562306a36Sopenharmony_ci			issue = 'S2LOOPx%d' % len(low.split('+'))
633662306a36Sopenharmony_ci		else:
633762306a36Sopenharmony_ci			m = re.match('.*waking *(?P<n>[0-9]*) *times.*', low)
633862306a36Sopenharmony_ci			issue = 'S2WAKEx%s' % m.group('n') if m else 'S2WAKExNaN'
633962306a36Sopenharmony_ci		match = [i for i in issues if i['match'] == issue]
634062306a36Sopenharmony_ci		if len(match) > 0:
634162306a36Sopenharmony_ci			match[0]['count'] += 1
634262306a36Sopenharmony_ci			if sysvals.hostname not in match[0]['urls']:
634362306a36Sopenharmony_ci				match[0]['urls'][sysvals.hostname] = [sysvals.htmlfile]
634462306a36Sopenharmony_ci			elif sysvals.htmlfile not in match[0]['urls'][sysvals.hostname]:
634562306a36Sopenharmony_ci				match[0]['urls'][sysvals.hostname].append(sysvals.htmlfile)
634662306a36Sopenharmony_ci		else:
634762306a36Sopenharmony_ci			issues.append({
634862306a36Sopenharmony_ci				'match': issue, 'count': 1, 'line': issue,
634962306a36Sopenharmony_ci				'urls': {sysvals.hostname: [sysvals.htmlfile]},
635062306a36Sopenharmony_ci			})
635162306a36Sopenharmony_ci		ilist.append(issue)
635262306a36Sopenharmony_ci	# extract device info
635362306a36Sopenharmony_ci	devices = dict()
635462306a36Sopenharmony_ci	for line in html.split('\n'):
635562306a36Sopenharmony_ci		m = re.match(' *<div id=\"[a,0-9]*\" *title=\"(?P<title>.*)\" class=\"thread.*', line)
635662306a36Sopenharmony_ci		if not m or 'thread kth' in line or 'thread sec' in line:
635762306a36Sopenharmony_ci			continue
635862306a36Sopenharmony_ci		m = re.match('(?P<n>.*) \((?P<t>[0-9,\.]*) ms\) (?P<p>.*)', m.group('title'))
635962306a36Sopenharmony_ci		if not m:
636062306a36Sopenharmony_ci			continue
636162306a36Sopenharmony_ci		name, time, phase = m.group('n'), m.group('t'), m.group('p')
636262306a36Sopenharmony_ci		if name == 'async_synchronize_full':
636362306a36Sopenharmony_ci			continue
636462306a36Sopenharmony_ci		if ' async' in name or ' sync' in name:
636562306a36Sopenharmony_ci			name = ' '.join(name.split(' ')[:-1])
636662306a36Sopenharmony_ci		if phase.startswith('suspend'):
636762306a36Sopenharmony_ci			d = 'suspend'
636862306a36Sopenharmony_ci		elif phase.startswith('resume'):
636962306a36Sopenharmony_ci			d = 'resume'
637062306a36Sopenharmony_ci		else:
637162306a36Sopenharmony_ci			continue
637262306a36Sopenharmony_ci		if d not in devices:
637362306a36Sopenharmony_ci			devices[d] = dict()
637462306a36Sopenharmony_ci		if name not in devices[d]:
637562306a36Sopenharmony_ci			devices[d][name] = 0.0
637662306a36Sopenharmony_ci		devices[d][name] += float(time)
637762306a36Sopenharmony_ci	# create worst device info
637862306a36Sopenharmony_ci	worst = dict()
637962306a36Sopenharmony_ci	for d in ['suspend', 'resume']:
638062306a36Sopenharmony_ci		worst[d] = {'name':'', 'time': 0.0}
638162306a36Sopenharmony_ci		dev = devices[d] if d in devices else 0
638262306a36Sopenharmony_ci		if dev and len(dev.keys()) > 0:
638362306a36Sopenharmony_ci			n = sorted(dev, key=lambda k:(dev[k], k), reverse=True)[0]
638462306a36Sopenharmony_ci			worst[d]['name'], worst[d]['time'] = n, dev[n]
638562306a36Sopenharmony_ci	data = {
638662306a36Sopenharmony_ci		'mode': stmp[2],
638762306a36Sopenharmony_ci		'host': stmp[0],
638862306a36Sopenharmony_ci		'kernel': stmp[1],
638962306a36Sopenharmony_ci		'sysinfo': sysinfo,
639062306a36Sopenharmony_ci		'time': tstr,
639162306a36Sopenharmony_ci		'result': result,
639262306a36Sopenharmony_ci		'issues': ' '.join(ilist),
639362306a36Sopenharmony_ci		'suspend': suspend,
639462306a36Sopenharmony_ci		'resume': resume,
639562306a36Sopenharmony_ci		'devlist': devices,
639662306a36Sopenharmony_ci		'sus_worst': worst['suspend']['name'],
639762306a36Sopenharmony_ci		'sus_worsttime': worst['suspend']['time'],
639862306a36Sopenharmony_ci		'res_worst': worst['resume']['name'],
639962306a36Sopenharmony_ci		'res_worsttime': worst['resume']['time'],
640062306a36Sopenharmony_ci		'url': sysvals.htmlfile,
640162306a36Sopenharmony_ci	}
640262306a36Sopenharmony_ci	for key in extra:
640362306a36Sopenharmony_ci		data[key] = extra[key]
640462306a36Sopenharmony_ci	if fulldetail:
640562306a36Sopenharmony_ci		data['funclist'] = find_in_html(html, '<div title="', '" class="traceevent"', False)
640662306a36Sopenharmony_ci	if tp:
640762306a36Sopenharmony_ci		for arg in ['-multi ', '-info ']:
640862306a36Sopenharmony_ci			if arg in tp.cmdline:
640962306a36Sopenharmony_ci				data['target'] = tp.cmdline[tp.cmdline.find(arg):].split()[1]
641062306a36Sopenharmony_ci				break
641162306a36Sopenharmony_ci	return data
641262306a36Sopenharmony_ci
641362306a36Sopenharmony_cidef genHtml(subdir, force=False):
641462306a36Sopenharmony_ci	for dirname, dirnames, filenames in os.walk(subdir):
641562306a36Sopenharmony_ci		sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = ''
641662306a36Sopenharmony_ci		for filename in filenames:
641762306a36Sopenharmony_ci			file = os.path.join(dirname, filename)
641862306a36Sopenharmony_ci			if sysvals.usable(file):
641962306a36Sopenharmony_ci				if(re.match('.*_dmesg.txt', filename)):
642062306a36Sopenharmony_ci					sysvals.dmesgfile = file
642162306a36Sopenharmony_ci				elif(re.match('.*_ftrace.txt', filename)):
642262306a36Sopenharmony_ci					sysvals.ftracefile = file
642362306a36Sopenharmony_ci		sysvals.setOutputFile()
642462306a36Sopenharmony_ci		if (sysvals.dmesgfile or sysvals.ftracefile) and sysvals.htmlfile and \
642562306a36Sopenharmony_ci			(force or not sysvals.usable(sysvals.htmlfile, True)):
642662306a36Sopenharmony_ci			pprint('FTRACE: %s' % sysvals.ftracefile)
642762306a36Sopenharmony_ci			if sysvals.dmesgfile:
642862306a36Sopenharmony_ci				pprint('DMESG : %s' % sysvals.dmesgfile)
642962306a36Sopenharmony_ci			rerunTest()
643062306a36Sopenharmony_ci
643162306a36Sopenharmony_ci# Function: runSummary
643262306a36Sopenharmony_ci# Description:
643362306a36Sopenharmony_ci#	 create a summary of tests in a sub-directory
643462306a36Sopenharmony_cidef runSummary(subdir, local=True, genhtml=False):
643562306a36Sopenharmony_ci	inpath = os.path.abspath(subdir)
643662306a36Sopenharmony_ci	outpath = os.path.abspath('.') if local else inpath
643762306a36Sopenharmony_ci	pprint('Generating a summary of folder:\n   %s' % inpath)
643862306a36Sopenharmony_ci	if genhtml:
643962306a36Sopenharmony_ci		genHtml(subdir)
644062306a36Sopenharmony_ci	target, issues, testruns = '', [], []
644162306a36Sopenharmony_ci	desc = {'host':[],'mode':[],'kernel':[]}
644262306a36Sopenharmony_ci	for dirname, dirnames, filenames in os.walk(subdir):
644362306a36Sopenharmony_ci		for filename in filenames:
644462306a36Sopenharmony_ci			if(not re.match('.*.html', filename)):
644562306a36Sopenharmony_ci				continue
644662306a36Sopenharmony_ci			data = data_from_html(os.path.join(dirname, filename), outpath, issues)
644762306a36Sopenharmony_ci			if(not data):
644862306a36Sopenharmony_ci				continue
644962306a36Sopenharmony_ci			if 'target' in data:
645062306a36Sopenharmony_ci				target = data['target']
645162306a36Sopenharmony_ci			testruns.append(data)
645262306a36Sopenharmony_ci			for key in desc:
645362306a36Sopenharmony_ci				if data[key] not in desc[key]:
645462306a36Sopenharmony_ci					desc[key].append(data[key])
645562306a36Sopenharmony_ci	pprint('Summary files:')
645662306a36Sopenharmony_ci	if len(desc['host']) == len(desc['mode']) == len(desc['kernel']) == 1:
645762306a36Sopenharmony_ci		title = '%s %s %s' % (desc['host'][0], desc['kernel'][0], desc['mode'][0])
645862306a36Sopenharmony_ci		if target:
645962306a36Sopenharmony_ci			title += ' %s' % target
646062306a36Sopenharmony_ci	else:
646162306a36Sopenharmony_ci		title = inpath
646262306a36Sopenharmony_ci	createHTMLSummarySimple(testruns, os.path.join(outpath, 'summary.html'), title)
646362306a36Sopenharmony_ci	pprint('   summary.html         - tabular list of test data found')
646462306a36Sopenharmony_ci	createHTMLDeviceSummary(testruns, os.path.join(outpath, 'summary-devices.html'), title)
646562306a36Sopenharmony_ci	pprint('   summary-devices.html - kernel device list sorted by total execution time')
646662306a36Sopenharmony_ci	createHTMLIssuesSummary(testruns, issues, os.path.join(outpath, 'summary-issues.html'), title)
646762306a36Sopenharmony_ci	pprint('   summary-issues.html  - kernel issues found sorted by frequency')
646862306a36Sopenharmony_ci
646962306a36Sopenharmony_ci# Function: checkArgBool
647062306a36Sopenharmony_ci# Description:
647162306a36Sopenharmony_ci#	 check if a boolean string value is true or false
647262306a36Sopenharmony_cidef checkArgBool(name, value):
647362306a36Sopenharmony_ci	if value in switchvalues:
647462306a36Sopenharmony_ci		if value in switchoff:
647562306a36Sopenharmony_ci			return False
647662306a36Sopenharmony_ci		return True
647762306a36Sopenharmony_ci	doError('invalid boolean --> (%s: %s), use "true/false" or "1/0"' % (name, value), True)
647862306a36Sopenharmony_ci	return False
647962306a36Sopenharmony_ci
648062306a36Sopenharmony_ci# Function: configFromFile
648162306a36Sopenharmony_ci# Description:
648262306a36Sopenharmony_ci#	 Configure the script via the info in a config file
648362306a36Sopenharmony_cidef configFromFile(file):
648462306a36Sopenharmony_ci	Config = configparser.ConfigParser()
648562306a36Sopenharmony_ci
648662306a36Sopenharmony_ci	Config.read(file)
648762306a36Sopenharmony_ci	sections = Config.sections()
648862306a36Sopenharmony_ci	overridekprobes = False
648962306a36Sopenharmony_ci	overridedevkprobes = False
649062306a36Sopenharmony_ci	if 'Settings' in sections:
649162306a36Sopenharmony_ci		for opt in Config.options('Settings'):
649262306a36Sopenharmony_ci			value = Config.get('Settings', opt).lower()
649362306a36Sopenharmony_ci			option = opt.lower()
649462306a36Sopenharmony_ci			if(option == 'verbose'):
649562306a36Sopenharmony_ci				sysvals.verbose = checkArgBool(option, value)
649662306a36Sopenharmony_ci			elif(option == 'addlogs'):
649762306a36Sopenharmony_ci				sysvals.dmesglog = sysvals.ftracelog = checkArgBool(option, value)
649862306a36Sopenharmony_ci			elif(option == 'dev'):
649962306a36Sopenharmony_ci				sysvals.usedevsrc = checkArgBool(option, value)
650062306a36Sopenharmony_ci			elif(option == 'proc'):
650162306a36Sopenharmony_ci				sysvals.useprocmon = checkArgBool(option, value)
650262306a36Sopenharmony_ci			elif(option == 'x2'):
650362306a36Sopenharmony_ci				if checkArgBool(option, value):
650462306a36Sopenharmony_ci					sysvals.execcount = 2
650562306a36Sopenharmony_ci			elif(option == 'callgraph'):
650662306a36Sopenharmony_ci				sysvals.usecallgraph = checkArgBool(option, value)
650762306a36Sopenharmony_ci			elif(option == 'override-timeline-functions'):
650862306a36Sopenharmony_ci				overridekprobes = checkArgBool(option, value)
650962306a36Sopenharmony_ci			elif(option == 'override-dev-timeline-functions'):
651062306a36Sopenharmony_ci				overridedevkprobes = checkArgBool(option, value)
651162306a36Sopenharmony_ci			elif(option == 'skiphtml'):
651262306a36Sopenharmony_ci				sysvals.skiphtml = checkArgBool(option, value)
651362306a36Sopenharmony_ci			elif(option == 'sync'):
651462306a36Sopenharmony_ci				sysvals.sync = checkArgBool(option, value)
651562306a36Sopenharmony_ci			elif(option == 'rs' or option == 'runtimesuspend'):
651662306a36Sopenharmony_ci				if value in switchvalues:
651762306a36Sopenharmony_ci					if value in switchoff:
651862306a36Sopenharmony_ci						sysvals.rs = -1
651962306a36Sopenharmony_ci					else:
652062306a36Sopenharmony_ci						sysvals.rs = 1
652162306a36Sopenharmony_ci				else:
652262306a36Sopenharmony_ci					doError('invalid value --> (%s: %s), use "enable/disable"' % (option, value), True)
652362306a36Sopenharmony_ci			elif(option == 'display'):
652462306a36Sopenharmony_ci				disopt = ['on', 'off', 'standby', 'suspend']
652562306a36Sopenharmony_ci				if value not in disopt:
652662306a36Sopenharmony_ci					doError('invalid value --> (%s: %s), use %s' % (option, value, disopt), True)
652762306a36Sopenharmony_ci				sysvals.display = value
652862306a36Sopenharmony_ci			elif(option == 'gzip'):
652962306a36Sopenharmony_ci				sysvals.gzip = checkArgBool(option, value)
653062306a36Sopenharmony_ci			elif(option == 'cgfilter'):
653162306a36Sopenharmony_ci				sysvals.setCallgraphFilter(value)
653262306a36Sopenharmony_ci			elif(option == 'cgskip'):
653362306a36Sopenharmony_ci				if value in switchoff:
653462306a36Sopenharmony_ci					sysvals.cgskip = ''
653562306a36Sopenharmony_ci				else:
653662306a36Sopenharmony_ci					sysvals.cgskip = sysvals.configFile(val)
653762306a36Sopenharmony_ci					if(not sysvals.cgskip):
653862306a36Sopenharmony_ci						doError('%s does not exist' % sysvals.cgskip)
653962306a36Sopenharmony_ci			elif(option == 'cgtest'):
654062306a36Sopenharmony_ci				sysvals.cgtest = getArgInt('cgtest', value, 0, 1, False)
654162306a36Sopenharmony_ci			elif(option == 'cgphase'):
654262306a36Sopenharmony_ci				d = Data(0)
654362306a36Sopenharmony_ci				if value not in d.phasedef:
654462306a36Sopenharmony_ci					doError('invalid phase --> (%s: %s), valid phases are %s'\
654562306a36Sopenharmony_ci						% (option, value, d.phasedef.keys()), True)
654662306a36Sopenharmony_ci				sysvals.cgphase = value
654762306a36Sopenharmony_ci			elif(option == 'fadd'):
654862306a36Sopenharmony_ci				file = sysvals.configFile(value)
654962306a36Sopenharmony_ci				if(not file):
655062306a36Sopenharmony_ci					doError('%s does not exist' % value)
655162306a36Sopenharmony_ci				sysvals.addFtraceFilterFunctions(file)
655262306a36Sopenharmony_ci			elif(option == 'result'):
655362306a36Sopenharmony_ci				sysvals.result = value
655462306a36Sopenharmony_ci			elif(option == 'multi'):
655562306a36Sopenharmony_ci				nums = value.split()
655662306a36Sopenharmony_ci				if len(nums) != 2:
655762306a36Sopenharmony_ci					doError('multi requires 2 integers (exec_count and delay)', True)
655862306a36Sopenharmony_ci				sysvals.multiinit(nums[0], nums[1])
655962306a36Sopenharmony_ci			elif(option == 'devicefilter'):
656062306a36Sopenharmony_ci				sysvals.setDeviceFilter(value)
656162306a36Sopenharmony_ci			elif(option == 'expandcg'):
656262306a36Sopenharmony_ci				sysvals.cgexp = checkArgBool(option, value)
656362306a36Sopenharmony_ci			elif(option == 'srgap'):
656462306a36Sopenharmony_ci				if checkArgBool(option, value):
656562306a36Sopenharmony_ci					sysvals.srgap = 5
656662306a36Sopenharmony_ci			elif(option == 'mode'):
656762306a36Sopenharmony_ci				sysvals.suspendmode = value
656862306a36Sopenharmony_ci			elif(option == 'command' or option == 'cmd'):
656962306a36Sopenharmony_ci				sysvals.testcommand = value
657062306a36Sopenharmony_ci			elif(option == 'x2delay'):
657162306a36Sopenharmony_ci				sysvals.x2delay = getArgInt('x2delay', value, 0, 60000, False)
657262306a36Sopenharmony_ci			elif(option == 'predelay'):
657362306a36Sopenharmony_ci				sysvals.predelay = getArgInt('predelay', value, 0, 60000, False)
657462306a36Sopenharmony_ci			elif(option == 'postdelay'):
657562306a36Sopenharmony_ci				sysvals.postdelay = getArgInt('postdelay', value, 0, 60000, False)
657662306a36Sopenharmony_ci			elif(option == 'maxdepth'):
657762306a36Sopenharmony_ci				sysvals.max_graph_depth = getArgInt('maxdepth', value, 0, 1000, False)
657862306a36Sopenharmony_ci			elif(option == 'rtcwake'):
657962306a36Sopenharmony_ci				if value in switchoff:
658062306a36Sopenharmony_ci					sysvals.rtcwake = False
658162306a36Sopenharmony_ci				else:
658262306a36Sopenharmony_ci					sysvals.rtcwake = True
658362306a36Sopenharmony_ci					sysvals.rtcwaketime = getArgInt('rtcwake', value, 0, 3600, False)
658462306a36Sopenharmony_ci			elif(option == 'timeprec'):
658562306a36Sopenharmony_ci				sysvals.setPrecision(getArgInt('timeprec', value, 0, 6, False))
658662306a36Sopenharmony_ci			elif(option == 'mindev'):
658762306a36Sopenharmony_ci				sysvals.mindevlen = getArgFloat('mindev', value, 0.0, 10000.0, False)
658862306a36Sopenharmony_ci			elif(option == 'callloop-maxgap'):
658962306a36Sopenharmony_ci				sysvals.callloopmaxgap = getArgFloat('callloop-maxgap', value, 0.0, 1.0, False)
659062306a36Sopenharmony_ci			elif(option == 'callloop-maxlen'):
659162306a36Sopenharmony_ci				sysvals.callloopmaxgap = getArgFloat('callloop-maxlen', value, 0.0, 1.0, False)
659262306a36Sopenharmony_ci			elif(option == 'mincg'):
659362306a36Sopenharmony_ci				sysvals.mincglen = getArgFloat('mincg', value, 0.0, 10000.0, False)
659462306a36Sopenharmony_ci			elif(option == 'bufsize'):
659562306a36Sopenharmony_ci				sysvals.bufsize = getArgInt('bufsize', value, 1, 1024*1024*8, False)
659662306a36Sopenharmony_ci			elif(option == 'output-dir'):
659762306a36Sopenharmony_ci				sysvals.outdir = sysvals.setOutputFolder(value)
659862306a36Sopenharmony_ci
659962306a36Sopenharmony_ci	if sysvals.suspendmode == 'command' and not sysvals.testcommand:
660062306a36Sopenharmony_ci		doError('No command supplied for mode "command"')
660162306a36Sopenharmony_ci
660262306a36Sopenharmony_ci	# compatibility errors
660362306a36Sopenharmony_ci	if sysvals.usedevsrc and sysvals.usecallgraph:
660462306a36Sopenharmony_ci		doError('-dev is not compatible with -f')
660562306a36Sopenharmony_ci	if sysvals.usecallgraph and sysvals.useprocmon:
660662306a36Sopenharmony_ci		doError('-proc is not compatible with -f')
660762306a36Sopenharmony_ci
660862306a36Sopenharmony_ci	if overridekprobes:
660962306a36Sopenharmony_ci		sysvals.tracefuncs = dict()
661062306a36Sopenharmony_ci	if overridedevkprobes:
661162306a36Sopenharmony_ci		sysvals.dev_tracefuncs = dict()
661262306a36Sopenharmony_ci
661362306a36Sopenharmony_ci	kprobes = dict()
661462306a36Sopenharmony_ci	kprobesec = 'dev_timeline_functions_'+platform.machine()
661562306a36Sopenharmony_ci	if kprobesec in sections:
661662306a36Sopenharmony_ci		for name in Config.options(kprobesec):
661762306a36Sopenharmony_ci			text = Config.get(kprobesec, name)
661862306a36Sopenharmony_ci			kprobes[name] = (text, True)
661962306a36Sopenharmony_ci	kprobesec = 'timeline_functions_'+platform.machine()
662062306a36Sopenharmony_ci	if kprobesec in sections:
662162306a36Sopenharmony_ci		for name in Config.options(kprobesec):
662262306a36Sopenharmony_ci			if name in kprobes:
662362306a36Sopenharmony_ci				doError('Duplicate timeline function found "%s"' % (name))
662462306a36Sopenharmony_ci			text = Config.get(kprobesec, name)
662562306a36Sopenharmony_ci			kprobes[name] = (text, False)
662662306a36Sopenharmony_ci
662762306a36Sopenharmony_ci	for name in kprobes:
662862306a36Sopenharmony_ci		function = name
662962306a36Sopenharmony_ci		format = name
663062306a36Sopenharmony_ci		color = ''
663162306a36Sopenharmony_ci		args = dict()
663262306a36Sopenharmony_ci		text, dev = kprobes[name]
663362306a36Sopenharmony_ci		data = text.split()
663462306a36Sopenharmony_ci		i = 0
663562306a36Sopenharmony_ci		for val in data:
663662306a36Sopenharmony_ci			# bracketted strings are special formatting, read them separately
663762306a36Sopenharmony_ci			if val[0] == '[' and val[-1] == ']':
663862306a36Sopenharmony_ci				for prop in val[1:-1].split(','):
663962306a36Sopenharmony_ci					p = prop.split('=')
664062306a36Sopenharmony_ci					if p[0] == 'color':
664162306a36Sopenharmony_ci						try:
664262306a36Sopenharmony_ci							color = int(p[1], 16)
664362306a36Sopenharmony_ci							color = '#'+p[1]
664462306a36Sopenharmony_ci						except:
664562306a36Sopenharmony_ci							color = p[1]
664662306a36Sopenharmony_ci				continue
664762306a36Sopenharmony_ci			# first real arg should be the format string
664862306a36Sopenharmony_ci			if i == 0:
664962306a36Sopenharmony_ci				format = val
665062306a36Sopenharmony_ci			# all other args are actual function args
665162306a36Sopenharmony_ci			else:
665262306a36Sopenharmony_ci				d = val.split('=')
665362306a36Sopenharmony_ci				args[d[0]] = d[1]
665462306a36Sopenharmony_ci			i += 1
665562306a36Sopenharmony_ci		if not function or not format:
665662306a36Sopenharmony_ci			doError('Invalid kprobe: %s' % name)
665762306a36Sopenharmony_ci		for arg in re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', format):
665862306a36Sopenharmony_ci			if arg not in args:
665962306a36Sopenharmony_ci				doError('Kprobe "%s" is missing argument "%s"' % (name, arg))
666062306a36Sopenharmony_ci		if (dev and name in sysvals.dev_tracefuncs) or (not dev and name in sysvals.tracefuncs):
666162306a36Sopenharmony_ci			doError('Duplicate timeline function found "%s"' % (name))
666262306a36Sopenharmony_ci
666362306a36Sopenharmony_ci		kp = {
666462306a36Sopenharmony_ci			'name': name,
666562306a36Sopenharmony_ci			'func': function,
666662306a36Sopenharmony_ci			'format': format,
666762306a36Sopenharmony_ci			sysvals.archargs: args
666862306a36Sopenharmony_ci		}
666962306a36Sopenharmony_ci		if color:
667062306a36Sopenharmony_ci			kp['color'] = color
667162306a36Sopenharmony_ci		if dev:
667262306a36Sopenharmony_ci			sysvals.dev_tracefuncs[name] = kp
667362306a36Sopenharmony_ci		else:
667462306a36Sopenharmony_ci			sysvals.tracefuncs[name] = kp
667562306a36Sopenharmony_ci
667662306a36Sopenharmony_ci# Function: printHelp
667762306a36Sopenharmony_ci# Description:
667862306a36Sopenharmony_ci#	 print out the help text
667962306a36Sopenharmony_cidef printHelp():
668062306a36Sopenharmony_ci	pprint('\n%s v%s\n'\
668162306a36Sopenharmony_ci	'Usage: sudo sleepgraph <options> <commands>\n'\
668262306a36Sopenharmony_ci	'\n'\
668362306a36Sopenharmony_ci	'Description:\n'\
668462306a36Sopenharmony_ci	'  This tool is designed to assist kernel and OS developers in optimizing\n'\
668562306a36Sopenharmony_ci	'  their linux stack\'s suspend/resume time. Using a kernel image built\n'\
668662306a36Sopenharmony_ci	'  with a few extra options enabled, the tool will execute a suspend and\n'\
668762306a36Sopenharmony_ci	'  capture dmesg and ftrace data until resume is complete. This data is\n'\
668862306a36Sopenharmony_ci	'  transformed into a device timeline and an optional callgraph to give\n'\
668962306a36Sopenharmony_ci	'  a detailed view of which devices/subsystems are taking the most\n'\
669062306a36Sopenharmony_ci	'  time in suspend/resume.\n'\
669162306a36Sopenharmony_ci	'\n'\
669262306a36Sopenharmony_ci	'  If no specific command is given, the default behavior is to initiate\n'\
669362306a36Sopenharmony_ci	'  a suspend/resume and capture the dmesg/ftrace output as an html timeline.\n'\
669462306a36Sopenharmony_ci	'\n'\
669562306a36Sopenharmony_ci	'  Generates output files in subdirectory: suspend-yymmdd-HHMMSS\n'\
669662306a36Sopenharmony_ci	'   HTML output:                    <hostname>_<mode>.html\n'\
669762306a36Sopenharmony_ci	'   raw dmesg output:               <hostname>_<mode>_dmesg.txt\n'\
669862306a36Sopenharmony_ci	'   raw ftrace output:              <hostname>_<mode>_ftrace.txt\n'\
669962306a36Sopenharmony_ci	'\n'\
670062306a36Sopenharmony_ci	'Options:\n'\
670162306a36Sopenharmony_ci	'   -h           Print this help text\n'\
670262306a36Sopenharmony_ci	'   -v           Print the current tool version\n'\
670362306a36Sopenharmony_ci	'   -config fn   Pull arguments and config options from file fn\n'\
670462306a36Sopenharmony_ci	'   -verbose     Print extra information during execution and analysis\n'\
670562306a36Sopenharmony_ci	'   -m mode      Mode to initiate for suspend (default: %s)\n'\
670662306a36Sopenharmony_ci	'   -o name      Overrides the output subdirectory name when running a new test\n'\
670762306a36Sopenharmony_ci	'                default: suspend-{date}-{time}\n'\
670862306a36Sopenharmony_ci	'   -rtcwake t   Wakeup t seconds after suspend, set t to "off" to disable (default: 15)\n'\
670962306a36Sopenharmony_ci	'   -addlogs     Add the dmesg and ftrace logs to the html output\n'\
671062306a36Sopenharmony_ci	'   -noturbostat Dont use turbostat in freeze mode (default: disabled)\n'\
671162306a36Sopenharmony_ci	'   -srgap       Add a visible gap in the timeline between sus/res (default: disabled)\n'\
671262306a36Sopenharmony_ci	'   -skiphtml    Run the test and capture the trace logs, but skip the timeline (default: disabled)\n'\
671362306a36Sopenharmony_ci	'   -result fn   Export a results table to a text file for parsing.\n'\
671462306a36Sopenharmony_ci	'   -wifi        If a wifi connection is available, check that it reconnects after resume.\n'\
671562306a36Sopenharmony_ci	'   -wifitrace   Trace kernel execution through wifi reconnect.\n'\
671662306a36Sopenharmony_ci	'   -netfix      Use netfix to reset the network in the event it fails to resume.\n'\
671762306a36Sopenharmony_ci	'  [testprep]\n'\
671862306a36Sopenharmony_ci	'   -sync        Sync the filesystems before starting the test\n'\
671962306a36Sopenharmony_ci	'   -rs on/off   Enable/disable runtime suspend for all devices, restore all after test\n'\
672062306a36Sopenharmony_ci	'   -display m   Change the display mode to m for the test (on/off/standby/suspend)\n'\
672162306a36Sopenharmony_ci	'  [advanced]\n'\
672262306a36Sopenharmony_ci	'   -gzip        Gzip the trace and dmesg logs to save space\n'\
672362306a36Sopenharmony_ci	'   -cmd {s}     Run the timeline over a custom command, e.g. "sync -d"\n'\
672462306a36Sopenharmony_ci	'   -proc        Add usermode process info into the timeline (default: disabled)\n'\
672562306a36Sopenharmony_ci	'   -dev         Add kernel function calls and threads to the timeline (default: disabled)\n'\
672662306a36Sopenharmony_ci	'   -x2          Run two suspend/resumes back to back (default: disabled)\n'\
672762306a36Sopenharmony_ci	'   -x2delay t   Include t ms delay between multiple test runs (default: 0 ms)\n'\
672862306a36Sopenharmony_ci	'   -predelay t  Include t ms delay before 1st suspend (default: 0 ms)\n'\
672962306a36Sopenharmony_ci	'   -postdelay t Include t ms delay after last resume (default: 0 ms)\n'\
673062306a36Sopenharmony_ci	'   -mindev ms   Discard all device blocks shorter than ms milliseconds (e.g. 0.001 for us)\n'\
673162306a36Sopenharmony_ci	'   -multi n d   Execute <n> consecutive tests at <d> seconds intervals. If <n> is followed\n'\
673262306a36Sopenharmony_ci	'                by a "d", "h", or "m" execute for <n> days, hours, or mins instead.\n'\
673362306a36Sopenharmony_ci	'                The outputs will be created in a new subdirectory with a summary page.\n'\
673462306a36Sopenharmony_ci	'   -maxfail n   Abort a -multi run after n consecutive fails (default is 0 = never abort)\n'\
673562306a36Sopenharmony_ci	'  [debug]\n'\
673662306a36Sopenharmony_ci	'   -f           Use ftrace to create device callgraphs (default: disabled)\n'\
673762306a36Sopenharmony_ci	'   -ftop        Use ftrace on the top level call: "%s" (default: disabled)\n'\
673862306a36Sopenharmony_ci	'   -maxdepth N  limit the callgraph data to N call levels (default: 0=all)\n'\
673962306a36Sopenharmony_ci	'   -expandcg    pre-expand the callgraph data in the html output (default: disabled)\n'\
674062306a36Sopenharmony_ci	'   -fadd file   Add functions to be graphed in the timeline from a list in a text file\n'\
674162306a36Sopenharmony_ci	'   -filter "d1,d2,..." Filter out all but this comma-delimited list of device names\n'\
674262306a36Sopenharmony_ci	'   -mincg  ms   Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)\n'\
674362306a36Sopenharmony_ci	'   -cgphase P   Only show callgraph data for phase P (e.g. suspend_late)\n'\
674462306a36Sopenharmony_ci	'   -cgtest N    Only show callgraph data for test N (e.g. 0 or 1 in an x2 run)\n'\
674562306a36Sopenharmony_ci	'   -timeprec N  Number of significant digits in timestamps (0:S, [3:ms], 6:us)\n'\
674662306a36Sopenharmony_ci	'   -cgfilter S  Filter the callgraph output in the timeline\n'\
674762306a36Sopenharmony_ci	'   -cgskip file Callgraph functions to skip, off to disable (default: cgskip.txt)\n'\
674862306a36Sopenharmony_ci	'   -bufsize N   Set trace buffer size to N kilo-bytes (default: all of free memory)\n'\
674962306a36Sopenharmony_ci	'   -devdump     Print out all the raw device data for each phase\n'\
675062306a36Sopenharmony_ci	'   -cgdump      Print out all the raw callgraph data\n'\
675162306a36Sopenharmony_ci	'\n'\
675262306a36Sopenharmony_ci	'Other commands:\n'\
675362306a36Sopenharmony_ci	'   -modes       List available suspend modes\n'\
675462306a36Sopenharmony_ci	'   -status      Test to see if the system is enabled to run this tool\n'\
675562306a36Sopenharmony_ci	'   -fpdt        Print out the contents of the ACPI Firmware Performance Data Table\n'\
675662306a36Sopenharmony_ci	'   -wificheck   Print out wifi connection info\n'\
675762306a36Sopenharmony_ci	'   -x<mode>     Test xset by toggling the given mode (on/off/standby/suspend)\n'\
675862306a36Sopenharmony_ci	'   -sysinfo     Print out system info extracted from BIOS\n'\
675962306a36Sopenharmony_ci	'   -devinfo     Print out the pm settings of all devices which support runtime suspend\n'\
676062306a36Sopenharmony_ci	'   -cmdinfo     Print out all the platform info collected before and after suspend/resume\n'\
676162306a36Sopenharmony_ci	'   -flist       Print the list of functions currently being captured in ftrace\n'\
676262306a36Sopenharmony_ci	'   -flistall    Print all functions capable of being captured in ftrace\n'\
676362306a36Sopenharmony_ci	'   -summary dir Create a summary of tests in this dir [-genhtml builds missing html]\n'\
676462306a36Sopenharmony_ci	'  [redo]\n'\
676562306a36Sopenharmony_ci	'   -ftrace ftracefile  Create HTML output using ftrace input (used with -dmesg)\n'\
676662306a36Sopenharmony_ci	'   -dmesg dmesgfile    Create HTML output using dmesg (used with -ftrace)\n'\
676762306a36Sopenharmony_ci	'' % (sysvals.title, sysvals.version, sysvals.suspendmode, sysvals.ftopfunc))
676862306a36Sopenharmony_ci	return True
676962306a36Sopenharmony_ci
677062306a36Sopenharmony_ci# ----------------- MAIN --------------------
677162306a36Sopenharmony_ci# exec start (skipped if script is loaded as library)
677262306a36Sopenharmony_ciif __name__ == '__main__':
677362306a36Sopenharmony_ci	genhtml = False
677462306a36Sopenharmony_ci	cmd = ''
677562306a36Sopenharmony_ci	simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall',
677662306a36Sopenharmony_ci		'-devinfo', '-status', '-xon', '-xoff', '-xstandby', '-xsuspend',
677762306a36Sopenharmony_ci		'-xinit', '-xreset', '-xstat', '-wificheck', '-cmdinfo']
677862306a36Sopenharmony_ci	if '-f' in sys.argv:
677962306a36Sopenharmony_ci		sysvals.cgskip = sysvals.configFile('cgskip.txt')
678062306a36Sopenharmony_ci	# loop through the command line arguments
678162306a36Sopenharmony_ci	args = iter(sys.argv[1:])
678262306a36Sopenharmony_ci	for arg in args:
678362306a36Sopenharmony_ci		if(arg == '-m'):
678462306a36Sopenharmony_ci			try:
678562306a36Sopenharmony_ci				val = next(args)
678662306a36Sopenharmony_ci			except:
678762306a36Sopenharmony_ci				doError('No mode supplied', True)
678862306a36Sopenharmony_ci			if val == 'command' and not sysvals.testcommand:
678962306a36Sopenharmony_ci				doError('No command supplied for mode "command"', True)
679062306a36Sopenharmony_ci			sysvals.suspendmode = val
679162306a36Sopenharmony_ci		elif(arg in simplecmds):
679262306a36Sopenharmony_ci			cmd = arg[1:]
679362306a36Sopenharmony_ci		elif(arg == '-h'):
679462306a36Sopenharmony_ci			printHelp()
679562306a36Sopenharmony_ci			sys.exit(0)
679662306a36Sopenharmony_ci		elif(arg == '-v'):
679762306a36Sopenharmony_ci			pprint("Version %s" % sysvals.version)
679862306a36Sopenharmony_ci			sys.exit(0)
679962306a36Sopenharmony_ci		elif(arg == '-debugtiming'):
680062306a36Sopenharmony_ci			debugtiming = True
680162306a36Sopenharmony_ci		elif(arg == '-x2'):
680262306a36Sopenharmony_ci			sysvals.execcount = 2
680362306a36Sopenharmony_ci		elif(arg == '-x2delay'):
680462306a36Sopenharmony_ci			sysvals.x2delay = getArgInt('-x2delay', args, 0, 60000)
680562306a36Sopenharmony_ci		elif(arg == '-predelay'):
680662306a36Sopenharmony_ci			sysvals.predelay = getArgInt('-predelay', args, 0, 60000)
680762306a36Sopenharmony_ci		elif(arg == '-postdelay'):
680862306a36Sopenharmony_ci			sysvals.postdelay = getArgInt('-postdelay', args, 0, 60000)
680962306a36Sopenharmony_ci		elif(arg == '-f'):
681062306a36Sopenharmony_ci			sysvals.usecallgraph = True
681162306a36Sopenharmony_ci		elif(arg == '-ftop'):
681262306a36Sopenharmony_ci			sysvals.usecallgraph = True
681362306a36Sopenharmony_ci			sysvals.ftop = True
681462306a36Sopenharmony_ci			sysvals.usekprobes = False
681562306a36Sopenharmony_ci		elif(arg == '-skiphtml'):
681662306a36Sopenharmony_ci			sysvals.skiphtml = True
681762306a36Sopenharmony_ci		elif(arg == '-cgdump'):
681862306a36Sopenharmony_ci			sysvals.cgdump = True
681962306a36Sopenharmony_ci		elif(arg == '-devdump'):
682062306a36Sopenharmony_ci			sysvals.devdump = True
682162306a36Sopenharmony_ci		elif(arg == '-genhtml'):
682262306a36Sopenharmony_ci			genhtml = True
682362306a36Sopenharmony_ci		elif(arg == '-addlogs'):
682462306a36Sopenharmony_ci			sysvals.dmesglog = sysvals.ftracelog = True
682562306a36Sopenharmony_ci		elif(arg == '-nologs'):
682662306a36Sopenharmony_ci			sysvals.dmesglog = sysvals.ftracelog = False
682762306a36Sopenharmony_ci		elif(arg == '-addlogdmesg'):
682862306a36Sopenharmony_ci			sysvals.dmesglog = True
682962306a36Sopenharmony_ci		elif(arg == '-addlogftrace'):
683062306a36Sopenharmony_ci			sysvals.ftracelog = True
683162306a36Sopenharmony_ci		elif(arg == '-noturbostat'):
683262306a36Sopenharmony_ci			sysvals.tstat = False
683362306a36Sopenharmony_ci		elif(arg == '-verbose'):
683462306a36Sopenharmony_ci			sysvals.verbose = True
683562306a36Sopenharmony_ci		elif(arg == '-proc'):
683662306a36Sopenharmony_ci			sysvals.useprocmon = True
683762306a36Sopenharmony_ci		elif(arg == '-dev'):
683862306a36Sopenharmony_ci			sysvals.usedevsrc = True
683962306a36Sopenharmony_ci		elif(arg == '-sync'):
684062306a36Sopenharmony_ci			sysvals.sync = True
684162306a36Sopenharmony_ci		elif(arg == '-wifi'):
684262306a36Sopenharmony_ci			sysvals.wifi = True
684362306a36Sopenharmony_ci		elif(arg == '-wifitrace'):
684462306a36Sopenharmony_ci			sysvals.wifitrace = True
684562306a36Sopenharmony_ci		elif(arg == '-netfix'):
684662306a36Sopenharmony_ci			sysvals.netfix = True
684762306a36Sopenharmony_ci		elif(arg == '-gzip'):
684862306a36Sopenharmony_ci			sysvals.gzip = True
684962306a36Sopenharmony_ci		elif(arg == '-info'):
685062306a36Sopenharmony_ci			try:
685162306a36Sopenharmony_ci				val = next(args)
685262306a36Sopenharmony_ci			except:
685362306a36Sopenharmony_ci				doError('-info requires one string argument', True)
685462306a36Sopenharmony_ci		elif(arg == '-desc'):
685562306a36Sopenharmony_ci			try:
685662306a36Sopenharmony_ci				val = next(args)
685762306a36Sopenharmony_ci			except:
685862306a36Sopenharmony_ci				doError('-desc requires one string argument', True)
685962306a36Sopenharmony_ci		elif(arg == '-rs'):
686062306a36Sopenharmony_ci			try:
686162306a36Sopenharmony_ci				val = next(args)
686262306a36Sopenharmony_ci			except:
686362306a36Sopenharmony_ci				doError('-rs requires "enable" or "disable"', True)
686462306a36Sopenharmony_ci			if val.lower() in switchvalues:
686562306a36Sopenharmony_ci				if val.lower() in switchoff:
686662306a36Sopenharmony_ci					sysvals.rs = -1
686762306a36Sopenharmony_ci				else:
686862306a36Sopenharmony_ci					sysvals.rs = 1
686962306a36Sopenharmony_ci			else:
687062306a36Sopenharmony_ci				doError('invalid option: %s, use "enable/disable" or "on/off"' % val, True)
687162306a36Sopenharmony_ci		elif(arg == '-display'):
687262306a36Sopenharmony_ci			try:
687362306a36Sopenharmony_ci				val = next(args)
687462306a36Sopenharmony_ci			except:
687562306a36Sopenharmony_ci				doError('-display requires an mode value', True)
687662306a36Sopenharmony_ci			disopt = ['on', 'off', 'standby', 'suspend']
687762306a36Sopenharmony_ci			if val.lower() not in disopt:
687862306a36Sopenharmony_ci				doError('valid display mode values are %s' % disopt, True)
687962306a36Sopenharmony_ci			sysvals.display = val.lower()
688062306a36Sopenharmony_ci		elif(arg == '-maxdepth'):
688162306a36Sopenharmony_ci			sysvals.max_graph_depth = getArgInt('-maxdepth', args, 0, 1000)
688262306a36Sopenharmony_ci		elif(arg == '-rtcwake'):
688362306a36Sopenharmony_ci			try:
688462306a36Sopenharmony_ci				val = next(args)
688562306a36Sopenharmony_ci			except:
688662306a36Sopenharmony_ci				doError('No rtcwake time supplied', True)
688762306a36Sopenharmony_ci			if val.lower() in switchoff:
688862306a36Sopenharmony_ci				sysvals.rtcwake = False
688962306a36Sopenharmony_ci			else:
689062306a36Sopenharmony_ci				sysvals.rtcwake = True
689162306a36Sopenharmony_ci				sysvals.rtcwaketime = getArgInt('-rtcwake', val, 0, 3600, False)
689262306a36Sopenharmony_ci		elif(arg == '-timeprec'):
689362306a36Sopenharmony_ci			sysvals.setPrecision(getArgInt('-timeprec', args, 0, 6))
689462306a36Sopenharmony_ci		elif(arg == '-mindev'):
689562306a36Sopenharmony_ci			sysvals.mindevlen = getArgFloat('-mindev', args, 0.0, 10000.0)
689662306a36Sopenharmony_ci		elif(arg == '-mincg'):
689762306a36Sopenharmony_ci			sysvals.mincglen = getArgFloat('-mincg', args, 0.0, 10000.0)
689862306a36Sopenharmony_ci		elif(arg == '-bufsize'):
689962306a36Sopenharmony_ci			sysvals.bufsize = getArgInt('-bufsize', args, 1, 1024*1024*8)
690062306a36Sopenharmony_ci		elif(arg == '-cgtest'):
690162306a36Sopenharmony_ci			sysvals.cgtest = getArgInt('-cgtest', args, 0, 1)
690262306a36Sopenharmony_ci		elif(arg == '-cgphase'):
690362306a36Sopenharmony_ci			try:
690462306a36Sopenharmony_ci				val = next(args)
690562306a36Sopenharmony_ci			except:
690662306a36Sopenharmony_ci				doError('No phase name supplied', True)
690762306a36Sopenharmony_ci			d = Data(0)
690862306a36Sopenharmony_ci			if val not in d.phasedef:
690962306a36Sopenharmony_ci				doError('invalid phase --> (%s: %s), valid phases are %s'\
691062306a36Sopenharmony_ci					% (arg, val, d.phasedef.keys()), True)
691162306a36Sopenharmony_ci			sysvals.cgphase = val
691262306a36Sopenharmony_ci		elif(arg == '-cgfilter'):
691362306a36Sopenharmony_ci			try:
691462306a36Sopenharmony_ci				val = next(args)
691562306a36Sopenharmony_ci			except:
691662306a36Sopenharmony_ci				doError('No callgraph functions supplied', True)
691762306a36Sopenharmony_ci			sysvals.setCallgraphFilter(val)
691862306a36Sopenharmony_ci		elif(arg == '-skipkprobe'):
691962306a36Sopenharmony_ci			try:
692062306a36Sopenharmony_ci				val = next(args)
692162306a36Sopenharmony_ci			except:
692262306a36Sopenharmony_ci				doError('No kprobe functions supplied', True)
692362306a36Sopenharmony_ci			sysvals.skipKprobes(val)
692462306a36Sopenharmony_ci		elif(arg == '-cgskip'):
692562306a36Sopenharmony_ci			try:
692662306a36Sopenharmony_ci				val = next(args)
692762306a36Sopenharmony_ci			except:
692862306a36Sopenharmony_ci				doError('No file supplied', True)
692962306a36Sopenharmony_ci			if val.lower() in switchoff:
693062306a36Sopenharmony_ci				sysvals.cgskip = ''
693162306a36Sopenharmony_ci			else:
693262306a36Sopenharmony_ci				sysvals.cgskip = sysvals.configFile(val)
693362306a36Sopenharmony_ci				if(not sysvals.cgskip):
693462306a36Sopenharmony_ci					doError('%s does not exist' % sysvals.cgskip)
693562306a36Sopenharmony_ci		elif(arg == '-callloop-maxgap'):
693662306a36Sopenharmony_ci			sysvals.callloopmaxgap = getArgFloat('-callloop-maxgap', args, 0.0, 1.0)
693762306a36Sopenharmony_ci		elif(arg == '-callloop-maxlen'):
693862306a36Sopenharmony_ci			sysvals.callloopmaxlen = getArgFloat('-callloop-maxlen', args, 0.0, 1.0)
693962306a36Sopenharmony_ci		elif(arg == '-cmd'):
694062306a36Sopenharmony_ci			try:
694162306a36Sopenharmony_ci				val = next(args)
694262306a36Sopenharmony_ci			except:
694362306a36Sopenharmony_ci				doError('No command string supplied', True)
694462306a36Sopenharmony_ci			sysvals.testcommand = val
694562306a36Sopenharmony_ci			sysvals.suspendmode = 'command'
694662306a36Sopenharmony_ci		elif(arg == '-expandcg'):
694762306a36Sopenharmony_ci			sysvals.cgexp = True
694862306a36Sopenharmony_ci		elif(arg == '-srgap'):
694962306a36Sopenharmony_ci			sysvals.srgap = 5
695062306a36Sopenharmony_ci		elif(arg == '-maxfail'):
695162306a36Sopenharmony_ci			sysvals.maxfail = getArgInt('-maxfail', args, 0, 1000000)
695262306a36Sopenharmony_ci		elif(arg == '-multi'):
695362306a36Sopenharmony_ci			try:
695462306a36Sopenharmony_ci				c, d = next(args), next(args)
695562306a36Sopenharmony_ci			except:
695662306a36Sopenharmony_ci				doError('-multi requires two values', True)
695762306a36Sopenharmony_ci			sysvals.multiinit(c, d)
695862306a36Sopenharmony_ci		elif(arg == '-o'):
695962306a36Sopenharmony_ci			try:
696062306a36Sopenharmony_ci				val = next(args)
696162306a36Sopenharmony_ci			except:
696262306a36Sopenharmony_ci				doError('No subdirectory name supplied', True)
696362306a36Sopenharmony_ci			sysvals.outdir = sysvals.setOutputFolder(val)
696462306a36Sopenharmony_ci		elif(arg == '-config'):
696562306a36Sopenharmony_ci			try:
696662306a36Sopenharmony_ci				val = next(args)
696762306a36Sopenharmony_ci			except:
696862306a36Sopenharmony_ci				doError('No text file supplied', True)
696962306a36Sopenharmony_ci			file = sysvals.configFile(val)
697062306a36Sopenharmony_ci			if(not file):
697162306a36Sopenharmony_ci				doError('%s does not exist' % val)
697262306a36Sopenharmony_ci			configFromFile(file)
697362306a36Sopenharmony_ci		elif(arg == '-fadd'):
697462306a36Sopenharmony_ci			try:
697562306a36Sopenharmony_ci				val = next(args)
697662306a36Sopenharmony_ci			except:
697762306a36Sopenharmony_ci				doError('No text file supplied', True)
697862306a36Sopenharmony_ci			file = sysvals.configFile(val)
697962306a36Sopenharmony_ci			if(not file):
698062306a36Sopenharmony_ci				doError('%s does not exist' % val)
698162306a36Sopenharmony_ci			sysvals.addFtraceFilterFunctions(file)
698262306a36Sopenharmony_ci		elif(arg == '-dmesg'):
698362306a36Sopenharmony_ci			try:
698462306a36Sopenharmony_ci				val = next(args)
698562306a36Sopenharmony_ci			except:
698662306a36Sopenharmony_ci				doError('No dmesg file supplied', True)
698762306a36Sopenharmony_ci			sysvals.notestrun = True
698862306a36Sopenharmony_ci			sysvals.dmesgfile = val
698962306a36Sopenharmony_ci			if(os.path.exists(sysvals.dmesgfile) == False):
699062306a36Sopenharmony_ci				doError('%s does not exist' % sysvals.dmesgfile)
699162306a36Sopenharmony_ci		elif(arg == '-ftrace'):
699262306a36Sopenharmony_ci			try:
699362306a36Sopenharmony_ci				val = next(args)
699462306a36Sopenharmony_ci			except:
699562306a36Sopenharmony_ci				doError('No ftrace file supplied', True)
699662306a36Sopenharmony_ci			sysvals.notestrun = True
699762306a36Sopenharmony_ci			sysvals.ftracefile = val
699862306a36Sopenharmony_ci			if(os.path.exists(sysvals.ftracefile) == False):
699962306a36Sopenharmony_ci				doError('%s does not exist' % sysvals.ftracefile)
700062306a36Sopenharmony_ci		elif(arg == '-summary'):
700162306a36Sopenharmony_ci			try:
700262306a36Sopenharmony_ci				val = next(args)
700362306a36Sopenharmony_ci			except:
700462306a36Sopenharmony_ci				doError('No directory supplied', True)
700562306a36Sopenharmony_ci			cmd = 'summary'
700662306a36Sopenharmony_ci			sysvals.outdir = val
700762306a36Sopenharmony_ci			sysvals.notestrun = True
700862306a36Sopenharmony_ci			if(os.path.isdir(val) == False):
700962306a36Sopenharmony_ci				doError('%s is not accesible' % val)
701062306a36Sopenharmony_ci		elif(arg == '-filter'):
701162306a36Sopenharmony_ci			try:
701262306a36Sopenharmony_ci				val = next(args)
701362306a36Sopenharmony_ci			except:
701462306a36Sopenharmony_ci				doError('No devnames supplied', True)
701562306a36Sopenharmony_ci			sysvals.setDeviceFilter(val)
701662306a36Sopenharmony_ci		elif(arg == '-result'):
701762306a36Sopenharmony_ci			try:
701862306a36Sopenharmony_ci				val = next(args)
701962306a36Sopenharmony_ci			except:
702062306a36Sopenharmony_ci				doError('No result file supplied', True)
702162306a36Sopenharmony_ci			sysvals.result = val
702262306a36Sopenharmony_ci			sysvals.signalHandlerInit()
702362306a36Sopenharmony_ci		else:
702462306a36Sopenharmony_ci			doError('Invalid argument: '+arg, True)
702562306a36Sopenharmony_ci
702662306a36Sopenharmony_ci	# compatibility errors
702762306a36Sopenharmony_ci	if(sysvals.usecallgraph and sysvals.usedevsrc):
702862306a36Sopenharmony_ci		doError('-dev is not compatible with -f')
702962306a36Sopenharmony_ci	if(sysvals.usecallgraph and sysvals.useprocmon):
703062306a36Sopenharmony_ci		doError('-proc is not compatible with -f')
703162306a36Sopenharmony_ci
703262306a36Sopenharmony_ci	if sysvals.usecallgraph and sysvals.cgskip:
703362306a36Sopenharmony_ci		sysvals.vprint('Using cgskip file: %s' % sysvals.cgskip)
703462306a36Sopenharmony_ci		sysvals.setCallgraphBlacklist(sysvals.cgskip)
703562306a36Sopenharmony_ci
703662306a36Sopenharmony_ci	# callgraph size cannot exceed device size
703762306a36Sopenharmony_ci	if sysvals.mincglen < sysvals.mindevlen:
703862306a36Sopenharmony_ci		sysvals.mincglen = sysvals.mindevlen
703962306a36Sopenharmony_ci
704062306a36Sopenharmony_ci	# remove existing buffers before calculating memory
704162306a36Sopenharmony_ci	if(sysvals.usecallgraph or sysvals.usedevsrc):
704262306a36Sopenharmony_ci		sysvals.fsetVal('16', 'buffer_size_kb')
704362306a36Sopenharmony_ci	sysvals.cpuInfo()
704462306a36Sopenharmony_ci
704562306a36Sopenharmony_ci	# just run a utility command and exit
704662306a36Sopenharmony_ci	if(cmd != ''):
704762306a36Sopenharmony_ci		ret = 0
704862306a36Sopenharmony_ci		if(cmd == 'status'):
704962306a36Sopenharmony_ci			if not statusCheck(True):
705062306a36Sopenharmony_ci				ret = 1
705162306a36Sopenharmony_ci		elif(cmd == 'fpdt'):
705262306a36Sopenharmony_ci			if not getFPDT(True):
705362306a36Sopenharmony_ci				ret = 1
705462306a36Sopenharmony_ci		elif(cmd == 'sysinfo'):
705562306a36Sopenharmony_ci			sysvals.printSystemInfo(True)
705662306a36Sopenharmony_ci		elif(cmd == 'devinfo'):
705762306a36Sopenharmony_ci			deviceInfo()
705862306a36Sopenharmony_ci		elif(cmd == 'modes'):
705962306a36Sopenharmony_ci			pprint(getModes())
706062306a36Sopenharmony_ci		elif(cmd == 'flist'):
706162306a36Sopenharmony_ci			sysvals.getFtraceFilterFunctions(True)
706262306a36Sopenharmony_ci		elif(cmd == 'flistall'):
706362306a36Sopenharmony_ci			sysvals.getFtraceFilterFunctions(False)
706462306a36Sopenharmony_ci		elif(cmd == 'summary'):
706562306a36Sopenharmony_ci			runSummary(sysvals.outdir, True, genhtml)
706662306a36Sopenharmony_ci		elif(cmd in ['xon', 'xoff', 'xstandby', 'xsuspend', 'xinit', 'xreset']):
706762306a36Sopenharmony_ci			sysvals.verbose = True
706862306a36Sopenharmony_ci			ret = sysvals.displayControl(cmd[1:])
706962306a36Sopenharmony_ci		elif(cmd == 'xstat'):
707062306a36Sopenharmony_ci			pprint('Display Status: %s' % sysvals.displayControl('stat').upper())
707162306a36Sopenharmony_ci		elif(cmd == 'wificheck'):
707262306a36Sopenharmony_ci			dev = sysvals.checkWifi()
707362306a36Sopenharmony_ci			if dev:
707462306a36Sopenharmony_ci				print('%s is connected' % sysvals.wifiDetails(dev))
707562306a36Sopenharmony_ci			else:
707662306a36Sopenharmony_ci				print('No wifi connection found')
707762306a36Sopenharmony_ci		elif(cmd == 'cmdinfo'):
707862306a36Sopenharmony_ci			for out in sysvals.cmdinfo(False, True):
707962306a36Sopenharmony_ci				print('[%s - %s]\n%s\n' % out)
708062306a36Sopenharmony_ci		sys.exit(ret)
708162306a36Sopenharmony_ci
708262306a36Sopenharmony_ci	# if instructed, re-analyze existing data files
708362306a36Sopenharmony_ci	if(sysvals.notestrun):
708462306a36Sopenharmony_ci		stamp = rerunTest(sysvals.outdir)
708562306a36Sopenharmony_ci		sysvals.outputResult(stamp)
708662306a36Sopenharmony_ci		sys.exit(0)
708762306a36Sopenharmony_ci
708862306a36Sopenharmony_ci	# verify that we can run a test
708962306a36Sopenharmony_ci	error = statusCheck()
709062306a36Sopenharmony_ci	if(error):
709162306a36Sopenharmony_ci		doError(error)
709262306a36Sopenharmony_ci
709362306a36Sopenharmony_ci	# extract mem/disk extra modes and convert
709462306a36Sopenharmony_ci	mode = sysvals.suspendmode
709562306a36Sopenharmony_ci	if mode.startswith('mem'):
709662306a36Sopenharmony_ci		memmode = mode.split('-', 1)[-1] if '-' in mode else 'deep'
709762306a36Sopenharmony_ci		if memmode == 'shallow':
709862306a36Sopenharmony_ci			mode = 'standby'
709962306a36Sopenharmony_ci		elif memmode ==  's2idle':
710062306a36Sopenharmony_ci			mode = 'freeze'
710162306a36Sopenharmony_ci		else:
710262306a36Sopenharmony_ci			mode = 'mem'
710362306a36Sopenharmony_ci		sysvals.memmode = memmode
710462306a36Sopenharmony_ci		sysvals.suspendmode = mode
710562306a36Sopenharmony_ci	if mode.startswith('disk-'):
710662306a36Sopenharmony_ci		sysvals.diskmode = mode.split('-', 1)[-1]
710762306a36Sopenharmony_ci		sysvals.suspendmode = 'disk'
710862306a36Sopenharmony_ci	sysvals.systemInfo(dmidecode(sysvals.mempath))
710962306a36Sopenharmony_ci
711062306a36Sopenharmony_ci	failcnt, ret = 0, 0
711162306a36Sopenharmony_ci	if sysvals.multitest['run']:
711262306a36Sopenharmony_ci		# run multiple tests in a separate subdirectory
711362306a36Sopenharmony_ci		if not sysvals.outdir:
711462306a36Sopenharmony_ci			if 'time' in sysvals.multitest:
711562306a36Sopenharmony_ci				s = '-%dm' % sysvals.multitest['time']
711662306a36Sopenharmony_ci			else:
711762306a36Sopenharmony_ci				s = '-x%d' % sysvals.multitest['count']
711862306a36Sopenharmony_ci			sysvals.outdir = datetime.now().strftime('suspend-%y%m%d-%H%M%S'+s)
711962306a36Sopenharmony_ci		if not os.path.isdir(sysvals.outdir):
712062306a36Sopenharmony_ci			os.makedirs(sysvals.outdir)
712162306a36Sopenharmony_ci		sysvals.sudoUserchown(sysvals.outdir)
712262306a36Sopenharmony_ci		finish = datetime.now()
712362306a36Sopenharmony_ci		if 'time' in sysvals.multitest:
712462306a36Sopenharmony_ci			finish += timedelta(minutes=sysvals.multitest['time'])
712562306a36Sopenharmony_ci		for i in range(sysvals.multitest['count']):
712662306a36Sopenharmony_ci			sysvals.multistat(True, i, finish)
712762306a36Sopenharmony_ci			if i != 0 and sysvals.multitest['delay'] > 0:
712862306a36Sopenharmony_ci				pprint('Waiting %d seconds...' % (sysvals.multitest['delay']))
712962306a36Sopenharmony_ci				time.sleep(sysvals.multitest['delay'])
713062306a36Sopenharmony_ci			fmt = 'suspend-%y%m%d-%H%M%S'
713162306a36Sopenharmony_ci			sysvals.testdir = os.path.join(sysvals.outdir, datetime.now().strftime(fmt))
713262306a36Sopenharmony_ci			ret = runTest(i+1, not sysvals.verbose)
713362306a36Sopenharmony_ci			failcnt = 0 if not ret else failcnt + 1
713462306a36Sopenharmony_ci			if sysvals.maxfail > 0 and failcnt >= sysvals.maxfail:
713562306a36Sopenharmony_ci				pprint('Maximum fail count of %d reached, aborting multitest' % (sysvals.maxfail))
713662306a36Sopenharmony_ci				break
713762306a36Sopenharmony_ci			sysvals.resetlog()
713862306a36Sopenharmony_ci			sysvals.multistat(False, i, finish)
713962306a36Sopenharmony_ci			if 'time' in sysvals.multitest and datetime.now() >= finish:
714062306a36Sopenharmony_ci				break
714162306a36Sopenharmony_ci		if not sysvals.skiphtml:
714262306a36Sopenharmony_ci			runSummary(sysvals.outdir, False, False)
714362306a36Sopenharmony_ci		sysvals.sudoUserchown(sysvals.outdir)
714462306a36Sopenharmony_ci	else:
714562306a36Sopenharmony_ci		if sysvals.outdir:
714662306a36Sopenharmony_ci			sysvals.testdir = sysvals.outdir
714762306a36Sopenharmony_ci		# run the test in the current directory
714862306a36Sopenharmony_ci		ret = runTest()
714962306a36Sopenharmony_ci
715062306a36Sopenharmony_ci	# reset to default values after testing
715162306a36Sopenharmony_ci	if sysvals.display:
715262306a36Sopenharmony_ci		sysvals.displayControl('reset')
715362306a36Sopenharmony_ci	if sysvals.rs != 0:
715462306a36Sopenharmony_ci		sysvals.setRuntimeSuspend(False)
715562306a36Sopenharmony_ci	sys.exit(ret)
7156