xref: /OK3568_Linux_fs/kernel/tools/power/pm-graph/sleepgraph.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun#!/usr/bin/env python3
2*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only
3*4882a593Smuzhiyun#
4*4882a593Smuzhiyun# Tool for analyzing suspend/resume timing
5*4882a593Smuzhiyun# Copyright (c) 2013, Intel Corporation.
6*4882a593Smuzhiyun#
7*4882a593Smuzhiyun# This program is free software; you can redistribute it and/or modify it
8*4882a593Smuzhiyun# under the terms and conditions of the GNU General Public License,
9*4882a593Smuzhiyun# version 2, as published by the Free Software Foundation.
10*4882a593Smuzhiyun#
11*4882a593Smuzhiyun# This program is distributed in the hope it will be useful, but WITHOUT
12*4882a593Smuzhiyun# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13*4882a593Smuzhiyun# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
14*4882a593Smuzhiyun# more details.
15*4882a593Smuzhiyun#
16*4882a593Smuzhiyun# Authors:
17*4882a593Smuzhiyun#	 Todd Brandt <todd.e.brandt@linux.intel.com>
18*4882a593Smuzhiyun#
19*4882a593Smuzhiyun# Links:
20*4882a593Smuzhiyun#	 Home Page
21*4882a593Smuzhiyun#	   https://01.org/pm-graph
22*4882a593Smuzhiyun#	 Source repo
23*4882a593Smuzhiyun#	   git@github.com:intel/pm-graph
24*4882a593Smuzhiyun#
25*4882a593Smuzhiyun# Description:
26*4882a593Smuzhiyun#	 This tool is designed to assist kernel and OS developers in optimizing
27*4882a593Smuzhiyun#	 their linux stack's suspend/resume time. Using a kernel image built
28*4882a593Smuzhiyun#	 with a few extra options enabled, the tool will execute a suspend and
29*4882a593Smuzhiyun#	 will capture dmesg and ftrace data until resume is complete. This data
30*4882a593Smuzhiyun#	 is transformed into a device timeline and a callgraph to give a quick
31*4882a593Smuzhiyun#	 and detailed view of which devices and callbacks are taking the most
32*4882a593Smuzhiyun#	 time in suspend/resume. The output is a single html file which can be
33*4882a593Smuzhiyun#	 viewed in firefox or chrome.
34*4882a593Smuzhiyun#
35*4882a593Smuzhiyun#	 The following kernel build options are required:
36*4882a593Smuzhiyun#		 CONFIG_DEVMEM=y
37*4882a593Smuzhiyun#		 CONFIG_PM_DEBUG=y
38*4882a593Smuzhiyun#		 CONFIG_PM_SLEEP_DEBUG=y
39*4882a593Smuzhiyun#		 CONFIG_FTRACE=y
40*4882a593Smuzhiyun#		 CONFIG_FUNCTION_TRACER=y
41*4882a593Smuzhiyun#		 CONFIG_FUNCTION_GRAPH_TRACER=y
42*4882a593Smuzhiyun#		 CONFIG_KPROBES=y
43*4882a593Smuzhiyun#		 CONFIG_KPROBES_ON_FTRACE=y
44*4882a593Smuzhiyun#
45*4882a593Smuzhiyun#	 For kernel versions older than 3.15:
46*4882a593Smuzhiyun#	 The following additional kernel parameters are required:
47*4882a593Smuzhiyun#		 (e.g. in file /etc/default/grub)
48*4882a593Smuzhiyun#		 GRUB_CMDLINE_LINUX_DEFAULT="... initcall_debug log_buf_len=16M ..."
49*4882a593Smuzhiyun#
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun# ----------------- LIBRARIES --------------------
52*4882a593Smuzhiyun
53*4882a593Smuzhiyunimport sys
54*4882a593Smuzhiyunimport time
55*4882a593Smuzhiyunimport os
56*4882a593Smuzhiyunimport string
57*4882a593Smuzhiyunimport re
58*4882a593Smuzhiyunimport platform
59*4882a593Smuzhiyunimport signal
60*4882a593Smuzhiyunimport codecs
61*4882a593Smuzhiyunfrom datetime import datetime, timedelta
62*4882a593Smuzhiyunimport struct
63*4882a593Smuzhiyunimport configparser
64*4882a593Smuzhiyunimport gzip
65*4882a593Smuzhiyunfrom threading import Thread
66*4882a593Smuzhiyunfrom subprocess import call, Popen, PIPE
67*4882a593Smuzhiyunimport base64
68*4882a593Smuzhiyun
69*4882a593Smuzhiyundef pprint(msg):
70*4882a593Smuzhiyun	print(msg)
71*4882a593Smuzhiyun	sys.stdout.flush()
72*4882a593Smuzhiyun
73*4882a593Smuzhiyundef ascii(text):
74*4882a593Smuzhiyun	return text.decode('ascii', 'ignore')
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun# ----------------- CLASSES --------------------
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun# Class: SystemValues
79*4882a593Smuzhiyun# Description:
80*4882a593Smuzhiyun#	 A global, single-instance container used to
81*4882a593Smuzhiyun#	 store system values and test parameters
82*4882a593Smuzhiyunclass SystemValues:
83*4882a593Smuzhiyun	title = 'SleepGraph'
84*4882a593Smuzhiyun	version = '5.7'
85*4882a593Smuzhiyun	ansi = False
86*4882a593Smuzhiyun	rs = 0
87*4882a593Smuzhiyun	display = ''
88*4882a593Smuzhiyun	gzip = False
89*4882a593Smuzhiyun	sync = False
90*4882a593Smuzhiyun	wifi = False
91*4882a593Smuzhiyun	verbose = False
92*4882a593Smuzhiyun	testlog = True
93*4882a593Smuzhiyun	dmesglog = True
94*4882a593Smuzhiyun	ftracelog = False
95*4882a593Smuzhiyun	tstat = True
96*4882a593Smuzhiyun	mindevlen = 0.0
97*4882a593Smuzhiyun	mincglen = 0.0
98*4882a593Smuzhiyun	cgphase = ''
99*4882a593Smuzhiyun	cgtest = -1
100*4882a593Smuzhiyun	cgskip = ''
101*4882a593Smuzhiyun	maxfail = 0
102*4882a593Smuzhiyun	multitest = {'run': False, 'count': 1000000, 'delay': 0}
103*4882a593Smuzhiyun	max_graph_depth = 0
104*4882a593Smuzhiyun	callloopmaxgap = 0.0001
105*4882a593Smuzhiyun	callloopmaxlen = 0.005
106*4882a593Smuzhiyun	bufsize = 0
107*4882a593Smuzhiyun	cpucount = 0
108*4882a593Smuzhiyun	memtotal = 204800
109*4882a593Smuzhiyun	memfree = 204800
110*4882a593Smuzhiyun	srgap = 0
111*4882a593Smuzhiyun	cgexp = False
112*4882a593Smuzhiyun	testdir = ''
113*4882a593Smuzhiyun	outdir = ''
114*4882a593Smuzhiyun	tpath = '/sys/kernel/debug/tracing/'
115*4882a593Smuzhiyun	fpdtpath = '/sys/firmware/acpi/tables/FPDT'
116*4882a593Smuzhiyun	epath = '/sys/kernel/debug/tracing/events/power/'
117*4882a593Smuzhiyun	pmdpath = '/sys/power/pm_debug_messages'
118*4882a593Smuzhiyun	traceevents = [
119*4882a593Smuzhiyun		'suspend_resume',
120*4882a593Smuzhiyun		'wakeup_source_activate',
121*4882a593Smuzhiyun		'wakeup_source_deactivate',
122*4882a593Smuzhiyun		'device_pm_callback_end',
123*4882a593Smuzhiyun		'device_pm_callback_start'
124*4882a593Smuzhiyun	]
125*4882a593Smuzhiyun	logmsg = ''
126*4882a593Smuzhiyun	testcommand = ''
127*4882a593Smuzhiyun	mempath = '/dev/mem'
128*4882a593Smuzhiyun	powerfile = '/sys/power/state'
129*4882a593Smuzhiyun	mempowerfile = '/sys/power/mem_sleep'
130*4882a593Smuzhiyun	diskpowerfile = '/sys/power/disk'
131*4882a593Smuzhiyun	suspendmode = 'mem'
132*4882a593Smuzhiyun	memmode = ''
133*4882a593Smuzhiyun	diskmode = ''
134*4882a593Smuzhiyun	hostname = 'localhost'
135*4882a593Smuzhiyun	prefix = 'test'
136*4882a593Smuzhiyun	teststamp = ''
137*4882a593Smuzhiyun	sysstamp = ''
138*4882a593Smuzhiyun	dmesgstart = 0.0
139*4882a593Smuzhiyun	dmesgfile = ''
140*4882a593Smuzhiyun	ftracefile = ''
141*4882a593Smuzhiyun	htmlfile = 'output.html'
142*4882a593Smuzhiyun	result = ''
143*4882a593Smuzhiyun	rtcwake = True
144*4882a593Smuzhiyun	rtcwaketime = 15
145*4882a593Smuzhiyun	rtcpath = ''
146*4882a593Smuzhiyun	devicefilter = []
147*4882a593Smuzhiyun	cgfilter = []
148*4882a593Smuzhiyun	stamp = 0
149*4882a593Smuzhiyun	execcount = 1
150*4882a593Smuzhiyun	x2delay = 0
151*4882a593Smuzhiyun	skiphtml = False
152*4882a593Smuzhiyun	usecallgraph = False
153*4882a593Smuzhiyun	ftopfunc = 'pm_suspend'
154*4882a593Smuzhiyun	ftop = False
155*4882a593Smuzhiyun	usetraceevents = False
156*4882a593Smuzhiyun	usetracemarkers = True
157*4882a593Smuzhiyun	usekprobes = True
158*4882a593Smuzhiyun	usedevsrc = False
159*4882a593Smuzhiyun	useprocmon = False
160*4882a593Smuzhiyun	notestrun = False
161*4882a593Smuzhiyun	cgdump = False
162*4882a593Smuzhiyun	devdump = False
163*4882a593Smuzhiyun	mixedphaseheight = True
164*4882a593Smuzhiyun	devprops = dict()
165*4882a593Smuzhiyun	platinfo = []
166*4882a593Smuzhiyun	predelay = 0
167*4882a593Smuzhiyun	postdelay = 0
168*4882a593Smuzhiyun	pmdebug = ''
169*4882a593Smuzhiyun	tmstart = 'SUSPEND START %Y%m%d-%H:%M:%S.%f'
170*4882a593Smuzhiyun	tmend = 'RESUME COMPLETE %Y%m%d-%H:%M:%S.%f'
171*4882a593Smuzhiyun	tracefuncs = {
172*4882a593Smuzhiyun		'sys_sync': {},
173*4882a593Smuzhiyun		'ksys_sync': {},
174*4882a593Smuzhiyun		'pm_notifier_call_chain_robust': {},
175*4882a593Smuzhiyun		'pm_prepare_console': {},
176*4882a593Smuzhiyun		'pm_notifier_call_chain': {},
177*4882a593Smuzhiyun		'freeze_processes': {},
178*4882a593Smuzhiyun		'freeze_kernel_threads': {},
179*4882a593Smuzhiyun		'pm_restrict_gfp_mask': {},
180*4882a593Smuzhiyun		'acpi_suspend_begin': {},
181*4882a593Smuzhiyun		'acpi_hibernation_begin': {},
182*4882a593Smuzhiyun		'acpi_hibernation_enter': {},
183*4882a593Smuzhiyun		'acpi_hibernation_leave': {},
184*4882a593Smuzhiyun		'acpi_pm_freeze': {},
185*4882a593Smuzhiyun		'acpi_pm_thaw': {},
186*4882a593Smuzhiyun		'acpi_s2idle_end': {},
187*4882a593Smuzhiyun		'acpi_s2idle_sync': {},
188*4882a593Smuzhiyun		'acpi_s2idle_begin': {},
189*4882a593Smuzhiyun		'acpi_s2idle_prepare': {},
190*4882a593Smuzhiyun		'acpi_s2idle_prepare_late': {},
191*4882a593Smuzhiyun		'acpi_s2idle_wake': {},
192*4882a593Smuzhiyun		'acpi_s2idle_wakeup': {},
193*4882a593Smuzhiyun		'acpi_s2idle_restore': {},
194*4882a593Smuzhiyun		'acpi_s2idle_restore_early': {},
195*4882a593Smuzhiyun		'hibernate_preallocate_memory': {},
196*4882a593Smuzhiyun		'create_basic_memory_bitmaps': {},
197*4882a593Smuzhiyun		'swsusp_write': {},
198*4882a593Smuzhiyun		'suspend_console': {},
199*4882a593Smuzhiyun		'acpi_pm_prepare': {},
200*4882a593Smuzhiyun		'syscore_suspend': {},
201*4882a593Smuzhiyun		'arch_enable_nonboot_cpus_end': {},
202*4882a593Smuzhiyun		'syscore_resume': {},
203*4882a593Smuzhiyun		'acpi_pm_finish': {},
204*4882a593Smuzhiyun		'resume_console': {},
205*4882a593Smuzhiyun		'acpi_pm_end': {},
206*4882a593Smuzhiyun		'pm_restore_gfp_mask': {},
207*4882a593Smuzhiyun		'thaw_processes': {},
208*4882a593Smuzhiyun		'pm_restore_console': {},
209*4882a593Smuzhiyun		'CPU_OFF': {
210*4882a593Smuzhiyun			'func':'_cpu_down',
211*4882a593Smuzhiyun			'args_x86_64': {'cpu':'%di:s32'},
212*4882a593Smuzhiyun			'format': 'CPU_OFF[{cpu}]'
213*4882a593Smuzhiyun		},
214*4882a593Smuzhiyun		'CPU_ON': {
215*4882a593Smuzhiyun			'func':'_cpu_up',
216*4882a593Smuzhiyun			'args_x86_64': {'cpu':'%di:s32'},
217*4882a593Smuzhiyun			'format': 'CPU_ON[{cpu}]'
218*4882a593Smuzhiyun		},
219*4882a593Smuzhiyun	}
220*4882a593Smuzhiyun	dev_tracefuncs = {
221*4882a593Smuzhiyun		# general wait/delay/sleep
222*4882a593Smuzhiyun		'msleep': { 'args_x86_64': {'time':'%di:s32'}, 'ub': 1 },
223*4882a593Smuzhiyun		'schedule_timeout': { 'args_x86_64': {'timeout':'%di:s32'}, 'ub': 1 },
224*4882a593Smuzhiyun		'udelay': { 'func':'__const_udelay', 'args_x86_64': {'loops':'%di:s32'}, 'ub': 1 },
225*4882a593Smuzhiyun		'usleep_range': { 'args_x86_64': {'min':'%di:s32', 'max':'%si:s32'}, 'ub': 1 },
226*4882a593Smuzhiyun		'mutex_lock_slowpath': { 'func':'__mutex_lock_slowpath', 'ub': 1 },
227*4882a593Smuzhiyun		'acpi_os_stall': {'ub': 1},
228*4882a593Smuzhiyun		'rt_mutex_slowlock': {'ub': 1},
229*4882a593Smuzhiyun		# ACPI
230*4882a593Smuzhiyun		'acpi_resume_power_resources': {},
231*4882a593Smuzhiyun		'acpi_ps_execute_method': { 'args_x86_64': {
232*4882a593Smuzhiyun			'fullpath':'+0(+40(%di)):string',
233*4882a593Smuzhiyun		}},
234*4882a593Smuzhiyun		# mei_me
235*4882a593Smuzhiyun		'mei_reset': {},
236*4882a593Smuzhiyun		# filesystem
237*4882a593Smuzhiyun		'ext4_sync_fs': {},
238*4882a593Smuzhiyun		# 80211
239*4882a593Smuzhiyun		'ath10k_bmi_read_memory': { 'args_x86_64': {'length':'%cx:s32'} },
240*4882a593Smuzhiyun		'ath10k_bmi_write_memory': { 'args_x86_64': {'length':'%cx:s32'} },
241*4882a593Smuzhiyun		'ath10k_bmi_fast_download': { 'args_x86_64': {'length':'%cx:s32'} },
242*4882a593Smuzhiyun		'iwlagn_mac_start': {},
243*4882a593Smuzhiyun		'iwlagn_alloc_bcast_station': {},
244*4882a593Smuzhiyun		'iwl_trans_pcie_start_hw': {},
245*4882a593Smuzhiyun		'iwl_trans_pcie_start_fw': {},
246*4882a593Smuzhiyun		'iwl_run_init_ucode': {},
247*4882a593Smuzhiyun		'iwl_load_ucode_wait_alive': {},
248*4882a593Smuzhiyun		'iwl_alive_start': {},
249*4882a593Smuzhiyun		'iwlagn_mac_stop': {},
250*4882a593Smuzhiyun		'iwlagn_mac_suspend': {},
251*4882a593Smuzhiyun		'iwlagn_mac_resume': {},
252*4882a593Smuzhiyun		'iwlagn_mac_add_interface': {},
253*4882a593Smuzhiyun		'iwlagn_mac_remove_interface': {},
254*4882a593Smuzhiyun		'iwlagn_mac_change_interface': {},
255*4882a593Smuzhiyun		'iwlagn_mac_config': {},
256*4882a593Smuzhiyun		'iwlagn_configure_filter': {},
257*4882a593Smuzhiyun		'iwlagn_mac_hw_scan': {},
258*4882a593Smuzhiyun		'iwlagn_bss_info_changed': {},
259*4882a593Smuzhiyun		'iwlagn_mac_channel_switch': {},
260*4882a593Smuzhiyun		'iwlagn_mac_flush': {},
261*4882a593Smuzhiyun		# ATA
262*4882a593Smuzhiyun		'ata_eh_recover': { 'args_x86_64': {'port':'+36(%di):s32'} },
263*4882a593Smuzhiyun		# i915
264*4882a593Smuzhiyun		'i915_gem_resume': {},
265*4882a593Smuzhiyun		'i915_restore_state': {},
266*4882a593Smuzhiyun		'intel_opregion_setup': {},
267*4882a593Smuzhiyun		'g4x_pre_enable_dp': {},
268*4882a593Smuzhiyun		'vlv_pre_enable_dp': {},
269*4882a593Smuzhiyun		'chv_pre_enable_dp': {},
270*4882a593Smuzhiyun		'g4x_enable_dp': {},
271*4882a593Smuzhiyun		'vlv_enable_dp': {},
272*4882a593Smuzhiyun		'intel_hpd_init': {},
273*4882a593Smuzhiyun		'intel_opregion_register': {},
274*4882a593Smuzhiyun		'intel_dp_detect': {},
275*4882a593Smuzhiyun		'intel_hdmi_detect': {},
276*4882a593Smuzhiyun		'intel_opregion_init': {},
277*4882a593Smuzhiyun		'intel_fbdev_set_suspend': {},
278*4882a593Smuzhiyun	}
279*4882a593Smuzhiyun	infocmds = [
280*4882a593Smuzhiyun		[0, 'kparams', 'cat', '/proc/cmdline'],
281*4882a593Smuzhiyun		[0, 'mcelog', 'mcelog'],
282*4882a593Smuzhiyun		[0, 'pcidevices', 'lspci', '-tv'],
283*4882a593Smuzhiyun		[0, 'usbdevices', 'lsusb', '-t'],
284*4882a593Smuzhiyun		[1, 'interrupts', 'cat', '/proc/interrupts'],
285*4882a593Smuzhiyun		[1, 'wakeups', 'cat', '/sys/kernel/debug/wakeup_sources'],
286*4882a593Smuzhiyun		[2, 'gpecounts', 'sh', '-c', 'grep -v invalid /sys/firmware/acpi/interrupts/*'],
287*4882a593Smuzhiyun		[2, 'suspendstats', 'sh', '-c', 'grep -v invalid /sys/power/suspend_stats/*'],
288*4882a593Smuzhiyun		[2, 'cpuidle', 'sh', '-c', 'grep -v invalid /sys/devices/system/cpu/cpu*/cpuidle/state*/s2idle/*'],
289*4882a593Smuzhiyun		[2, 'battery', 'sh', '-c', 'grep -v invalid /sys/class/power_supply/*/*'],
290*4882a593Smuzhiyun	]
291*4882a593Smuzhiyun	cgblacklist = []
292*4882a593Smuzhiyun	kprobes = dict()
293*4882a593Smuzhiyun	timeformat = '%.3f'
294*4882a593Smuzhiyun	cmdline = '%s %s' % \
295*4882a593Smuzhiyun			(os.path.basename(sys.argv[0]), ' '.join(sys.argv[1:]))
296*4882a593Smuzhiyun	sudouser = ''
297*4882a593Smuzhiyun	def __init__(self):
298*4882a593Smuzhiyun		self.archargs = 'args_'+platform.machine()
299*4882a593Smuzhiyun		self.hostname = platform.node()
300*4882a593Smuzhiyun		if(self.hostname == ''):
301*4882a593Smuzhiyun			self.hostname = 'localhost'
302*4882a593Smuzhiyun		rtc = "rtc0"
303*4882a593Smuzhiyun		if os.path.exists('/dev/rtc'):
304*4882a593Smuzhiyun			rtc = os.readlink('/dev/rtc')
305*4882a593Smuzhiyun		rtc = '/sys/class/rtc/'+rtc
306*4882a593Smuzhiyun		if os.path.exists(rtc) and os.path.exists(rtc+'/date') and \
307*4882a593Smuzhiyun			os.path.exists(rtc+'/time') and os.path.exists(rtc+'/wakealarm'):
308*4882a593Smuzhiyun			self.rtcpath = rtc
309*4882a593Smuzhiyun		if (hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()):
310*4882a593Smuzhiyun			self.ansi = True
311*4882a593Smuzhiyun		self.testdir = datetime.now().strftime('suspend-%y%m%d-%H%M%S')
312*4882a593Smuzhiyun		if os.getuid() == 0 and 'SUDO_USER' in os.environ and \
313*4882a593Smuzhiyun			os.environ['SUDO_USER']:
314*4882a593Smuzhiyun			self.sudouser = os.environ['SUDO_USER']
315*4882a593Smuzhiyun	def resetlog(self):
316*4882a593Smuzhiyun		self.logmsg = ''
317*4882a593Smuzhiyun		self.platinfo = []
318*4882a593Smuzhiyun	def vprint(self, msg):
319*4882a593Smuzhiyun		self.logmsg += msg+'\n'
320*4882a593Smuzhiyun		if self.verbose or msg.startswith('WARNING:'):
321*4882a593Smuzhiyun			pprint(msg)
322*4882a593Smuzhiyun	def signalHandler(self, signum, frame):
323*4882a593Smuzhiyun		if not self.result:
324*4882a593Smuzhiyun			return
325*4882a593Smuzhiyun		signame = self.signames[signum] if signum in self.signames else 'UNKNOWN'
326*4882a593Smuzhiyun		msg = 'Signal %s caused a tool exit, line %d' % (signame, frame.f_lineno)
327*4882a593Smuzhiyun		self.outputResult({'error':msg})
328*4882a593Smuzhiyun		sys.exit(3)
329*4882a593Smuzhiyun	def signalHandlerInit(self):
330*4882a593Smuzhiyun		capture = ['BUS', 'SYS', 'XCPU', 'XFSZ', 'PWR', 'HUP', 'INT', 'QUIT',
331*4882a593Smuzhiyun			'ILL', 'ABRT', 'FPE', 'SEGV', 'TERM']
332*4882a593Smuzhiyun		self.signames = dict()
333*4882a593Smuzhiyun		for i in capture:
334*4882a593Smuzhiyun			s = 'SIG'+i
335*4882a593Smuzhiyun			try:
336*4882a593Smuzhiyun				signum = getattr(signal, s)
337*4882a593Smuzhiyun				signal.signal(signum, self.signalHandler)
338*4882a593Smuzhiyun			except:
339*4882a593Smuzhiyun				continue
340*4882a593Smuzhiyun			self.signames[signum] = s
341*4882a593Smuzhiyun	def rootCheck(self, fatal=True):
342*4882a593Smuzhiyun		if(os.access(self.powerfile, os.W_OK)):
343*4882a593Smuzhiyun			return True
344*4882a593Smuzhiyun		if fatal:
345*4882a593Smuzhiyun			msg = 'This command requires sysfs mount and root access'
346*4882a593Smuzhiyun			pprint('ERROR: %s\n' % msg)
347*4882a593Smuzhiyun			self.outputResult({'error':msg})
348*4882a593Smuzhiyun			sys.exit(1)
349*4882a593Smuzhiyun		return False
350*4882a593Smuzhiyun	def rootUser(self, fatal=False):
351*4882a593Smuzhiyun		if 'USER' in os.environ and os.environ['USER'] == 'root':
352*4882a593Smuzhiyun			return True
353*4882a593Smuzhiyun		if fatal:
354*4882a593Smuzhiyun			msg = 'This command must be run as root'
355*4882a593Smuzhiyun			pprint('ERROR: %s\n' % msg)
356*4882a593Smuzhiyun			self.outputResult({'error':msg})
357*4882a593Smuzhiyun			sys.exit(1)
358*4882a593Smuzhiyun		return False
359*4882a593Smuzhiyun	def usable(self, file):
360*4882a593Smuzhiyun		return (os.path.exists(file) and os.path.getsize(file) > 0)
361*4882a593Smuzhiyun	def getExec(self, cmd):
362*4882a593Smuzhiyun		try:
363*4882a593Smuzhiyun			fp = Popen(['which', cmd], stdout=PIPE, stderr=PIPE).stdout
364*4882a593Smuzhiyun			out = ascii(fp.read()).strip()
365*4882a593Smuzhiyun			fp.close()
366*4882a593Smuzhiyun		except:
367*4882a593Smuzhiyun			out = ''
368*4882a593Smuzhiyun		if out:
369*4882a593Smuzhiyun			return out
370*4882a593Smuzhiyun		for path in ['/sbin', '/bin', '/usr/sbin', '/usr/bin',
371*4882a593Smuzhiyun			'/usr/local/sbin', '/usr/local/bin']:
372*4882a593Smuzhiyun			cmdfull = os.path.join(path, cmd)
373*4882a593Smuzhiyun			if os.path.exists(cmdfull):
374*4882a593Smuzhiyun				return cmdfull
375*4882a593Smuzhiyun		return out
376*4882a593Smuzhiyun	def setPrecision(self, num):
377*4882a593Smuzhiyun		if num < 0 or num > 6:
378*4882a593Smuzhiyun			return
379*4882a593Smuzhiyun		self.timeformat = '%.{0}f'.format(num)
380*4882a593Smuzhiyun	def setOutputFolder(self, value):
381*4882a593Smuzhiyun		args = dict()
382*4882a593Smuzhiyun		n = datetime.now()
383*4882a593Smuzhiyun		args['date'] = n.strftime('%y%m%d')
384*4882a593Smuzhiyun		args['time'] = n.strftime('%H%M%S')
385*4882a593Smuzhiyun		args['hostname'] = args['host'] = self.hostname
386*4882a593Smuzhiyun		args['mode'] = self.suspendmode
387*4882a593Smuzhiyun		return value.format(**args)
388*4882a593Smuzhiyun	def setOutputFile(self):
389*4882a593Smuzhiyun		if self.dmesgfile != '':
390*4882a593Smuzhiyun			m = re.match('(?P<name>.*)_dmesg\.txt.*', self.dmesgfile)
391*4882a593Smuzhiyun			if(m):
392*4882a593Smuzhiyun				self.htmlfile = m.group('name')+'.html'
393*4882a593Smuzhiyun		if self.ftracefile != '':
394*4882a593Smuzhiyun			m = re.match('(?P<name>.*)_ftrace\.txt.*', self.ftracefile)
395*4882a593Smuzhiyun			if(m):
396*4882a593Smuzhiyun				self.htmlfile = m.group('name')+'.html'
397*4882a593Smuzhiyun	def systemInfo(self, info):
398*4882a593Smuzhiyun		p = m = ''
399*4882a593Smuzhiyun		if 'baseboard-manufacturer' in info:
400*4882a593Smuzhiyun			m = info['baseboard-manufacturer']
401*4882a593Smuzhiyun		elif 'system-manufacturer' in info:
402*4882a593Smuzhiyun			m = info['system-manufacturer']
403*4882a593Smuzhiyun		if 'system-product-name' in info:
404*4882a593Smuzhiyun			p = info['system-product-name']
405*4882a593Smuzhiyun		elif 'baseboard-product-name' in info:
406*4882a593Smuzhiyun			p = info['baseboard-product-name']
407*4882a593Smuzhiyun		if m[:5].lower() == 'intel' and 'baseboard-product-name' in info:
408*4882a593Smuzhiyun			p = info['baseboard-product-name']
409*4882a593Smuzhiyun		c = info['processor-version'] if 'processor-version' in info else ''
410*4882a593Smuzhiyun		b = info['bios-version'] if 'bios-version' in info else ''
411*4882a593Smuzhiyun		r = info['bios-release-date'] if 'bios-release-date' in info else ''
412*4882a593Smuzhiyun		self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | biosdate:%s | numcpu:%d | memsz:%d | memfr:%d' % \
413*4882a593Smuzhiyun			(m, p, c, b, r, self.cpucount, self.memtotal, self.memfree)
414*4882a593Smuzhiyun	def printSystemInfo(self, fatal=False):
415*4882a593Smuzhiyun		self.rootCheck(True)
416*4882a593Smuzhiyun		out = dmidecode(self.mempath, fatal)
417*4882a593Smuzhiyun		if len(out) < 1:
418*4882a593Smuzhiyun			return
419*4882a593Smuzhiyun		fmt = '%-24s: %s'
420*4882a593Smuzhiyun		for name in sorted(out):
421*4882a593Smuzhiyun			print(fmt % (name, out[name]))
422*4882a593Smuzhiyun		print(fmt % ('cpucount', ('%d' % self.cpucount)))
423*4882a593Smuzhiyun		print(fmt % ('memtotal', ('%d kB' % self.memtotal)))
424*4882a593Smuzhiyun		print(fmt % ('memfree', ('%d kB' % self.memfree)))
425*4882a593Smuzhiyun	def cpuInfo(self):
426*4882a593Smuzhiyun		self.cpucount = 0
427*4882a593Smuzhiyun		fp = open('/proc/cpuinfo', 'r')
428*4882a593Smuzhiyun		for line in fp:
429*4882a593Smuzhiyun			if re.match('^processor[ \t]*:[ \t]*[0-9]*', line):
430*4882a593Smuzhiyun				self.cpucount += 1
431*4882a593Smuzhiyun		fp.close()
432*4882a593Smuzhiyun		fp = open('/proc/meminfo', 'r')
433*4882a593Smuzhiyun		for line in fp:
434*4882a593Smuzhiyun			m = re.match('^MemTotal:[ \t]*(?P<sz>[0-9]*) *kB', line)
435*4882a593Smuzhiyun			if m:
436*4882a593Smuzhiyun				self.memtotal = int(m.group('sz'))
437*4882a593Smuzhiyun			m = re.match('^MemFree:[ \t]*(?P<sz>[0-9]*) *kB', line)
438*4882a593Smuzhiyun			if m:
439*4882a593Smuzhiyun				self.memfree = int(m.group('sz'))
440*4882a593Smuzhiyun		fp.close()
441*4882a593Smuzhiyun	def initTestOutput(self, name):
442*4882a593Smuzhiyun		self.prefix = self.hostname
443*4882a593Smuzhiyun		v = open('/proc/version', 'r').read().strip()
444*4882a593Smuzhiyun		kver = v.split()[2]
445*4882a593Smuzhiyun		fmt = name+'-%m%d%y-%H%M%S'
446*4882a593Smuzhiyun		testtime = datetime.now().strftime(fmt)
447*4882a593Smuzhiyun		self.teststamp = \
448*4882a593Smuzhiyun			'# '+testtime+' '+self.prefix+' '+self.suspendmode+' '+kver
449*4882a593Smuzhiyun		ext = ''
450*4882a593Smuzhiyun		if self.gzip:
451*4882a593Smuzhiyun			ext = '.gz'
452*4882a593Smuzhiyun		self.dmesgfile = \
453*4882a593Smuzhiyun			self.testdir+'/'+self.prefix+'_'+self.suspendmode+'_dmesg.txt'+ext
454*4882a593Smuzhiyun		self.ftracefile = \
455*4882a593Smuzhiyun			self.testdir+'/'+self.prefix+'_'+self.suspendmode+'_ftrace.txt'+ext
456*4882a593Smuzhiyun		self.htmlfile = \
457*4882a593Smuzhiyun			self.testdir+'/'+self.prefix+'_'+self.suspendmode+'.html'
458*4882a593Smuzhiyun		if not os.path.isdir(self.testdir):
459*4882a593Smuzhiyun			os.makedirs(self.testdir)
460*4882a593Smuzhiyun		self.sudoUserchown(self.testdir)
461*4882a593Smuzhiyun	def getValueList(self, value):
462*4882a593Smuzhiyun		out = []
463*4882a593Smuzhiyun		for i in value.split(','):
464*4882a593Smuzhiyun			if i.strip():
465*4882a593Smuzhiyun				out.append(i.strip())
466*4882a593Smuzhiyun		return out
467*4882a593Smuzhiyun	def setDeviceFilter(self, value):
468*4882a593Smuzhiyun		self.devicefilter = self.getValueList(value)
469*4882a593Smuzhiyun	def setCallgraphFilter(self, value):
470*4882a593Smuzhiyun		self.cgfilter = self.getValueList(value)
471*4882a593Smuzhiyun	def skipKprobes(self, value):
472*4882a593Smuzhiyun		for k in self.getValueList(value):
473*4882a593Smuzhiyun			if k in self.tracefuncs:
474*4882a593Smuzhiyun				del self.tracefuncs[k]
475*4882a593Smuzhiyun			if k in self.dev_tracefuncs:
476*4882a593Smuzhiyun				del self.dev_tracefuncs[k]
477*4882a593Smuzhiyun	def setCallgraphBlacklist(self, file):
478*4882a593Smuzhiyun		self.cgblacklist = self.listFromFile(file)
479*4882a593Smuzhiyun	def rtcWakeAlarmOn(self):
480*4882a593Smuzhiyun		call('echo 0 > '+self.rtcpath+'/wakealarm', shell=True)
481*4882a593Smuzhiyun		nowtime = open(self.rtcpath+'/since_epoch', 'r').read().strip()
482*4882a593Smuzhiyun		if nowtime:
483*4882a593Smuzhiyun			nowtime = int(nowtime)
484*4882a593Smuzhiyun		else:
485*4882a593Smuzhiyun			# if hardware time fails, use the software time
486*4882a593Smuzhiyun			nowtime = int(datetime.now().strftime('%s'))
487*4882a593Smuzhiyun		alarm = nowtime + self.rtcwaketime
488*4882a593Smuzhiyun		call('echo %d > %s/wakealarm' % (alarm, self.rtcpath), shell=True)
489*4882a593Smuzhiyun	def rtcWakeAlarmOff(self):
490*4882a593Smuzhiyun		call('echo 0 > %s/wakealarm' % self.rtcpath, shell=True)
491*4882a593Smuzhiyun	def initdmesg(self):
492*4882a593Smuzhiyun		# get the latest time stamp from the dmesg log
493*4882a593Smuzhiyun		fp = Popen('dmesg', stdout=PIPE).stdout
494*4882a593Smuzhiyun		ktime = '0'
495*4882a593Smuzhiyun		for line in fp:
496*4882a593Smuzhiyun			line = ascii(line).replace('\r\n', '')
497*4882a593Smuzhiyun			idx = line.find('[')
498*4882a593Smuzhiyun			if idx > 1:
499*4882a593Smuzhiyun				line = line[idx:]
500*4882a593Smuzhiyun			m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
501*4882a593Smuzhiyun			if(m):
502*4882a593Smuzhiyun				ktime = m.group('ktime')
503*4882a593Smuzhiyun		fp.close()
504*4882a593Smuzhiyun		self.dmesgstart = float(ktime)
505*4882a593Smuzhiyun	def getdmesg(self, testdata):
506*4882a593Smuzhiyun		op = self.writeDatafileHeader(self.dmesgfile, testdata)
507*4882a593Smuzhiyun		# store all new dmesg lines since initdmesg was called
508*4882a593Smuzhiyun		fp = Popen('dmesg', stdout=PIPE).stdout
509*4882a593Smuzhiyun		for line in fp:
510*4882a593Smuzhiyun			line = ascii(line).replace('\r\n', '')
511*4882a593Smuzhiyun			idx = line.find('[')
512*4882a593Smuzhiyun			if idx > 1:
513*4882a593Smuzhiyun				line = line[idx:]
514*4882a593Smuzhiyun			m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
515*4882a593Smuzhiyun			if(not m):
516*4882a593Smuzhiyun				continue
517*4882a593Smuzhiyun			ktime = float(m.group('ktime'))
518*4882a593Smuzhiyun			if ktime > self.dmesgstart:
519*4882a593Smuzhiyun				op.write(line)
520*4882a593Smuzhiyun		fp.close()
521*4882a593Smuzhiyun		op.close()
522*4882a593Smuzhiyun	def listFromFile(self, file):
523*4882a593Smuzhiyun		list = []
524*4882a593Smuzhiyun		fp = open(file)
525*4882a593Smuzhiyun		for i in fp.read().split('\n'):
526*4882a593Smuzhiyun			i = i.strip()
527*4882a593Smuzhiyun			if i and i[0] != '#':
528*4882a593Smuzhiyun				list.append(i)
529*4882a593Smuzhiyun		fp.close()
530*4882a593Smuzhiyun		return list
531*4882a593Smuzhiyun	def addFtraceFilterFunctions(self, file):
532*4882a593Smuzhiyun		for i in self.listFromFile(file):
533*4882a593Smuzhiyun			if len(i) < 2:
534*4882a593Smuzhiyun				continue
535*4882a593Smuzhiyun			self.tracefuncs[i] = dict()
536*4882a593Smuzhiyun	def getFtraceFilterFunctions(self, current):
537*4882a593Smuzhiyun		self.rootCheck(True)
538*4882a593Smuzhiyun		if not current:
539*4882a593Smuzhiyun			call('cat '+self.tpath+'available_filter_functions', shell=True)
540*4882a593Smuzhiyun			return
541*4882a593Smuzhiyun		master = self.listFromFile(self.tpath+'available_filter_functions')
542*4882a593Smuzhiyun		for i in sorted(self.tracefuncs):
543*4882a593Smuzhiyun			if 'func' in self.tracefuncs[i]:
544*4882a593Smuzhiyun				i = self.tracefuncs[i]['func']
545*4882a593Smuzhiyun			if i in master:
546*4882a593Smuzhiyun				print(i)
547*4882a593Smuzhiyun			else:
548*4882a593Smuzhiyun				print(self.colorText(i))
549*4882a593Smuzhiyun	def setFtraceFilterFunctions(self, list):
550*4882a593Smuzhiyun		master = self.listFromFile(self.tpath+'available_filter_functions')
551*4882a593Smuzhiyun		flist = ''
552*4882a593Smuzhiyun		for i in list:
553*4882a593Smuzhiyun			if i not in master:
554*4882a593Smuzhiyun				continue
555*4882a593Smuzhiyun			if ' [' in i:
556*4882a593Smuzhiyun				flist += i.split(' ')[0]+'\n'
557*4882a593Smuzhiyun			else:
558*4882a593Smuzhiyun				flist += i+'\n'
559*4882a593Smuzhiyun		fp = open(self.tpath+'set_graph_function', 'w')
560*4882a593Smuzhiyun		fp.write(flist)
561*4882a593Smuzhiyun		fp.close()
562*4882a593Smuzhiyun	def basicKprobe(self, name):
563*4882a593Smuzhiyun		self.kprobes[name] = {'name': name,'func': name,'args': dict(),'format': name}
564*4882a593Smuzhiyun	def defaultKprobe(self, name, kdata):
565*4882a593Smuzhiyun		k = kdata
566*4882a593Smuzhiyun		for field in ['name', 'format', 'func']:
567*4882a593Smuzhiyun			if field not in k:
568*4882a593Smuzhiyun				k[field] = name
569*4882a593Smuzhiyun		if self.archargs in k:
570*4882a593Smuzhiyun			k['args'] = k[self.archargs]
571*4882a593Smuzhiyun		else:
572*4882a593Smuzhiyun			k['args'] = dict()
573*4882a593Smuzhiyun			k['format'] = name
574*4882a593Smuzhiyun		self.kprobes[name] = k
575*4882a593Smuzhiyun	def kprobeColor(self, name):
576*4882a593Smuzhiyun		if name not in self.kprobes or 'color' not in self.kprobes[name]:
577*4882a593Smuzhiyun			return ''
578*4882a593Smuzhiyun		return self.kprobes[name]['color']
579*4882a593Smuzhiyun	def kprobeDisplayName(self, name, dataraw):
580*4882a593Smuzhiyun		if name not in self.kprobes:
581*4882a593Smuzhiyun			self.basicKprobe(name)
582*4882a593Smuzhiyun		data = ''
583*4882a593Smuzhiyun		quote=0
584*4882a593Smuzhiyun		# first remvoe any spaces inside quotes, and the quotes
585*4882a593Smuzhiyun		for c in dataraw:
586*4882a593Smuzhiyun			if c == '"':
587*4882a593Smuzhiyun				quote = (quote + 1) % 2
588*4882a593Smuzhiyun			if quote and c == ' ':
589*4882a593Smuzhiyun				data += '_'
590*4882a593Smuzhiyun			elif c != '"':
591*4882a593Smuzhiyun				data += c
592*4882a593Smuzhiyun		fmt, args = self.kprobes[name]['format'], self.kprobes[name]['args']
593*4882a593Smuzhiyun		arglist = dict()
594*4882a593Smuzhiyun		# now process the args
595*4882a593Smuzhiyun		for arg in sorted(args):
596*4882a593Smuzhiyun			arglist[arg] = ''
597*4882a593Smuzhiyun			m = re.match('.* '+arg+'=(?P<arg>.*) ', data);
598*4882a593Smuzhiyun			if m:
599*4882a593Smuzhiyun				arglist[arg] = m.group('arg')
600*4882a593Smuzhiyun			else:
601*4882a593Smuzhiyun				m = re.match('.* '+arg+'=(?P<arg>.*)', data);
602*4882a593Smuzhiyun				if m:
603*4882a593Smuzhiyun					arglist[arg] = m.group('arg')
604*4882a593Smuzhiyun		out = fmt.format(**arglist)
605*4882a593Smuzhiyun		out = out.replace(' ', '_').replace('"', '')
606*4882a593Smuzhiyun		return out
607*4882a593Smuzhiyun	def kprobeText(self, kname, kprobe):
608*4882a593Smuzhiyun		name = fmt = func = kname
609*4882a593Smuzhiyun		args = dict()
610*4882a593Smuzhiyun		if 'name' in kprobe:
611*4882a593Smuzhiyun			name = kprobe['name']
612*4882a593Smuzhiyun		if 'format' in kprobe:
613*4882a593Smuzhiyun			fmt = kprobe['format']
614*4882a593Smuzhiyun		if 'func' in kprobe:
615*4882a593Smuzhiyun			func = kprobe['func']
616*4882a593Smuzhiyun		if self.archargs in kprobe:
617*4882a593Smuzhiyun			args = kprobe[self.archargs]
618*4882a593Smuzhiyun		if 'args' in kprobe:
619*4882a593Smuzhiyun			args = kprobe['args']
620*4882a593Smuzhiyun		if re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', func):
621*4882a593Smuzhiyun			doError('Kprobe "%s" has format info in the function name "%s"' % (name, func))
622*4882a593Smuzhiyun		for arg in re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', fmt):
623*4882a593Smuzhiyun			if arg not in args:
624*4882a593Smuzhiyun				doError('Kprobe "%s" is missing argument "%s"' % (name, arg))
625*4882a593Smuzhiyun		val = 'p:%s_cal %s' % (name, func)
626*4882a593Smuzhiyun		for i in sorted(args):
627*4882a593Smuzhiyun			val += ' %s=%s' % (i, args[i])
628*4882a593Smuzhiyun		val += '\nr:%s_ret %s $retval\n' % (name, func)
629*4882a593Smuzhiyun		return val
630*4882a593Smuzhiyun	def addKprobes(self, output=False):
631*4882a593Smuzhiyun		if len(self.kprobes) < 1:
632*4882a593Smuzhiyun			return
633*4882a593Smuzhiyun		if output:
634*4882a593Smuzhiyun			pprint('    kprobe functions in this kernel:')
635*4882a593Smuzhiyun		# first test each kprobe
636*4882a593Smuzhiyun		rejects = []
637*4882a593Smuzhiyun		# sort kprobes: trace, ub-dev, custom, dev
638*4882a593Smuzhiyun		kpl = [[], [], [], []]
639*4882a593Smuzhiyun		linesout = len(self.kprobes)
640*4882a593Smuzhiyun		for name in sorted(self.kprobes):
641*4882a593Smuzhiyun			res = self.colorText('YES', 32)
642*4882a593Smuzhiyun			if not self.testKprobe(name, self.kprobes[name]):
643*4882a593Smuzhiyun				res = self.colorText('NO')
644*4882a593Smuzhiyun				rejects.append(name)
645*4882a593Smuzhiyun			else:
646*4882a593Smuzhiyun				if name in self.tracefuncs:
647*4882a593Smuzhiyun					kpl[0].append(name)
648*4882a593Smuzhiyun				elif name in self.dev_tracefuncs:
649*4882a593Smuzhiyun					if 'ub' in self.dev_tracefuncs[name]:
650*4882a593Smuzhiyun						kpl[1].append(name)
651*4882a593Smuzhiyun					else:
652*4882a593Smuzhiyun						kpl[3].append(name)
653*4882a593Smuzhiyun				else:
654*4882a593Smuzhiyun					kpl[2].append(name)
655*4882a593Smuzhiyun			if output:
656*4882a593Smuzhiyun				pprint('         %s: %s' % (name, res))
657*4882a593Smuzhiyun		kplist = kpl[0] + kpl[1] + kpl[2] + kpl[3]
658*4882a593Smuzhiyun		# remove all failed ones from the list
659*4882a593Smuzhiyun		for name in rejects:
660*4882a593Smuzhiyun			self.kprobes.pop(name)
661*4882a593Smuzhiyun		# set the kprobes all at once
662*4882a593Smuzhiyun		self.fsetVal('', 'kprobe_events')
663*4882a593Smuzhiyun		kprobeevents = ''
664*4882a593Smuzhiyun		for kp in kplist:
665*4882a593Smuzhiyun			kprobeevents += self.kprobeText(kp, self.kprobes[kp])
666*4882a593Smuzhiyun		self.fsetVal(kprobeevents, 'kprobe_events')
667*4882a593Smuzhiyun		if output:
668*4882a593Smuzhiyun			check = self.fgetVal('kprobe_events')
669*4882a593Smuzhiyun			linesack = (len(check.split('\n')) - 1) // 2
670*4882a593Smuzhiyun			pprint('    kprobe functions enabled: %d/%d' % (linesack, linesout))
671*4882a593Smuzhiyun		self.fsetVal('1', 'events/kprobes/enable')
672*4882a593Smuzhiyun	def testKprobe(self, kname, kprobe):
673*4882a593Smuzhiyun		self.fsetVal('0', 'events/kprobes/enable')
674*4882a593Smuzhiyun		kprobeevents = self.kprobeText(kname, kprobe)
675*4882a593Smuzhiyun		if not kprobeevents:
676*4882a593Smuzhiyun			return False
677*4882a593Smuzhiyun		try:
678*4882a593Smuzhiyun			self.fsetVal(kprobeevents, 'kprobe_events')
679*4882a593Smuzhiyun			check = self.fgetVal('kprobe_events')
680*4882a593Smuzhiyun		except:
681*4882a593Smuzhiyun			return False
682*4882a593Smuzhiyun		linesout = len(kprobeevents.split('\n'))
683*4882a593Smuzhiyun		linesack = len(check.split('\n'))
684*4882a593Smuzhiyun		if linesack < linesout:
685*4882a593Smuzhiyun			return False
686*4882a593Smuzhiyun		return True
687*4882a593Smuzhiyun	def setVal(self, val, file):
688*4882a593Smuzhiyun		if not os.path.exists(file):
689*4882a593Smuzhiyun			return False
690*4882a593Smuzhiyun		try:
691*4882a593Smuzhiyun			fp = open(file, 'wb', 0)
692*4882a593Smuzhiyun			fp.write(val.encode())
693*4882a593Smuzhiyun			fp.flush()
694*4882a593Smuzhiyun			fp.close()
695*4882a593Smuzhiyun		except:
696*4882a593Smuzhiyun			return False
697*4882a593Smuzhiyun		return True
698*4882a593Smuzhiyun	def fsetVal(self, val, path):
699*4882a593Smuzhiyun		return self.setVal(val, self.tpath+path)
700*4882a593Smuzhiyun	def getVal(self, file):
701*4882a593Smuzhiyun		res = ''
702*4882a593Smuzhiyun		if not os.path.exists(file):
703*4882a593Smuzhiyun			return res
704*4882a593Smuzhiyun		try:
705*4882a593Smuzhiyun			fp = open(file, 'r')
706*4882a593Smuzhiyun			res = fp.read()
707*4882a593Smuzhiyun			fp.close()
708*4882a593Smuzhiyun		except:
709*4882a593Smuzhiyun			pass
710*4882a593Smuzhiyun		return res
711*4882a593Smuzhiyun	def fgetVal(self, path):
712*4882a593Smuzhiyun		return self.getVal(self.tpath+path)
713*4882a593Smuzhiyun	def cleanupFtrace(self):
714*4882a593Smuzhiyun		if(self.usecallgraph or self.usetraceevents or self.usedevsrc):
715*4882a593Smuzhiyun			self.fsetVal('0', 'events/kprobes/enable')
716*4882a593Smuzhiyun			self.fsetVal('', 'kprobe_events')
717*4882a593Smuzhiyun			self.fsetVal('1024', 'buffer_size_kb')
718*4882a593Smuzhiyun		if self.pmdebug:
719*4882a593Smuzhiyun			self.setVal(self.pmdebug, self.pmdpath)
720*4882a593Smuzhiyun	def setupAllKprobes(self):
721*4882a593Smuzhiyun		for name in self.tracefuncs:
722*4882a593Smuzhiyun			self.defaultKprobe(name, self.tracefuncs[name])
723*4882a593Smuzhiyun		for name in self.dev_tracefuncs:
724*4882a593Smuzhiyun			self.defaultKprobe(name, self.dev_tracefuncs[name])
725*4882a593Smuzhiyun	def isCallgraphFunc(self, name):
726*4882a593Smuzhiyun		if len(self.tracefuncs) < 1 and self.suspendmode == 'command':
727*4882a593Smuzhiyun			return True
728*4882a593Smuzhiyun		for i in self.tracefuncs:
729*4882a593Smuzhiyun			if 'func' in self.tracefuncs[i]:
730*4882a593Smuzhiyun				f = self.tracefuncs[i]['func']
731*4882a593Smuzhiyun			else:
732*4882a593Smuzhiyun				f = i
733*4882a593Smuzhiyun			if name == f:
734*4882a593Smuzhiyun				return True
735*4882a593Smuzhiyun		return False
736*4882a593Smuzhiyun	def initFtrace(self, quiet=False):
737*4882a593Smuzhiyun		if not quiet:
738*4882a593Smuzhiyun			sysvals.printSystemInfo(False)
739*4882a593Smuzhiyun			pprint('INITIALIZING FTRACE...')
740*4882a593Smuzhiyun		# turn trace off
741*4882a593Smuzhiyun		self.fsetVal('0', 'tracing_on')
742*4882a593Smuzhiyun		self.cleanupFtrace()
743*4882a593Smuzhiyun		# pm debug messages
744*4882a593Smuzhiyun		pv = self.getVal(self.pmdpath)
745*4882a593Smuzhiyun		if pv != '1':
746*4882a593Smuzhiyun			self.setVal('1', self.pmdpath)
747*4882a593Smuzhiyun			self.pmdebug = pv
748*4882a593Smuzhiyun		# set the trace clock to global
749*4882a593Smuzhiyun		self.fsetVal('global', 'trace_clock')
750*4882a593Smuzhiyun		self.fsetVal('nop', 'current_tracer')
751*4882a593Smuzhiyun		# set trace buffer to an appropriate value
752*4882a593Smuzhiyun		cpus = max(1, self.cpucount)
753*4882a593Smuzhiyun		if self.bufsize > 0:
754*4882a593Smuzhiyun			tgtsize = self.bufsize
755*4882a593Smuzhiyun		elif self.usecallgraph or self.usedevsrc:
756*4882a593Smuzhiyun			bmax = (1*1024*1024) if self.suspendmode in ['disk', 'command'] \
757*4882a593Smuzhiyun				else (3*1024*1024)
758*4882a593Smuzhiyun			tgtsize = min(self.memfree, bmax)
759*4882a593Smuzhiyun		else:
760*4882a593Smuzhiyun			tgtsize = 65536
761*4882a593Smuzhiyun		while not self.fsetVal('%d' % (tgtsize // cpus), 'buffer_size_kb'):
762*4882a593Smuzhiyun			# if the size failed to set, lower it and keep trying
763*4882a593Smuzhiyun			tgtsize -= 65536
764*4882a593Smuzhiyun			if tgtsize < 65536:
765*4882a593Smuzhiyun				tgtsize = int(self.fgetVal('buffer_size_kb')) * cpus
766*4882a593Smuzhiyun				break
767*4882a593Smuzhiyun		self.vprint('Setting trace buffers to %d kB (%d kB per cpu)' % (tgtsize, tgtsize/cpus))
768*4882a593Smuzhiyun		# initialize the callgraph trace
769*4882a593Smuzhiyun		if(self.usecallgraph):
770*4882a593Smuzhiyun			# set trace type
771*4882a593Smuzhiyun			self.fsetVal('function_graph', 'current_tracer')
772*4882a593Smuzhiyun			self.fsetVal('', 'set_ftrace_filter')
773*4882a593Smuzhiyun			# set trace format options
774*4882a593Smuzhiyun			self.fsetVal('print-parent', 'trace_options')
775*4882a593Smuzhiyun			self.fsetVal('funcgraph-abstime', 'trace_options')
776*4882a593Smuzhiyun			self.fsetVal('funcgraph-cpu', 'trace_options')
777*4882a593Smuzhiyun			self.fsetVal('funcgraph-duration', 'trace_options')
778*4882a593Smuzhiyun			self.fsetVal('funcgraph-proc', 'trace_options')
779*4882a593Smuzhiyun			self.fsetVal('funcgraph-tail', 'trace_options')
780*4882a593Smuzhiyun			self.fsetVal('nofuncgraph-overhead', 'trace_options')
781*4882a593Smuzhiyun			self.fsetVal('context-info', 'trace_options')
782*4882a593Smuzhiyun			self.fsetVal('graph-time', 'trace_options')
783*4882a593Smuzhiyun			self.fsetVal('%d' % self.max_graph_depth, 'max_graph_depth')
784*4882a593Smuzhiyun			cf = ['dpm_run_callback']
785*4882a593Smuzhiyun			if(self.usetraceevents):
786*4882a593Smuzhiyun				cf += ['dpm_prepare', 'dpm_complete']
787*4882a593Smuzhiyun			for fn in self.tracefuncs:
788*4882a593Smuzhiyun				if 'func' in self.tracefuncs[fn]:
789*4882a593Smuzhiyun					cf.append(self.tracefuncs[fn]['func'])
790*4882a593Smuzhiyun				else:
791*4882a593Smuzhiyun					cf.append(fn)
792*4882a593Smuzhiyun			if self.ftop:
793*4882a593Smuzhiyun				self.setFtraceFilterFunctions([self.ftopfunc])
794*4882a593Smuzhiyun			else:
795*4882a593Smuzhiyun				self.setFtraceFilterFunctions(cf)
796*4882a593Smuzhiyun		# initialize the kprobe trace
797*4882a593Smuzhiyun		elif self.usekprobes:
798*4882a593Smuzhiyun			for name in self.tracefuncs:
799*4882a593Smuzhiyun				self.defaultKprobe(name, self.tracefuncs[name])
800*4882a593Smuzhiyun			if self.usedevsrc:
801*4882a593Smuzhiyun				for name in self.dev_tracefuncs:
802*4882a593Smuzhiyun					self.defaultKprobe(name, self.dev_tracefuncs[name])
803*4882a593Smuzhiyun			if not quiet:
804*4882a593Smuzhiyun				pprint('INITIALIZING KPROBES...')
805*4882a593Smuzhiyun			self.addKprobes(self.verbose)
806*4882a593Smuzhiyun		if(self.usetraceevents):
807*4882a593Smuzhiyun			# turn trace events on
808*4882a593Smuzhiyun			events = iter(self.traceevents)
809*4882a593Smuzhiyun			for e in events:
810*4882a593Smuzhiyun				self.fsetVal('1', 'events/power/'+e+'/enable')
811*4882a593Smuzhiyun		# clear the trace buffer
812*4882a593Smuzhiyun		self.fsetVal('', 'trace')
813*4882a593Smuzhiyun	def verifyFtrace(self):
814*4882a593Smuzhiyun		# files needed for any trace data
815*4882a593Smuzhiyun		files = ['buffer_size_kb', 'current_tracer', 'trace', 'trace_clock',
816*4882a593Smuzhiyun				 'trace_marker', 'trace_options', 'tracing_on']
817*4882a593Smuzhiyun		# files needed for callgraph trace data
818*4882a593Smuzhiyun		tp = self.tpath
819*4882a593Smuzhiyun		if(self.usecallgraph):
820*4882a593Smuzhiyun			files += [
821*4882a593Smuzhiyun				'available_filter_functions',
822*4882a593Smuzhiyun				'set_ftrace_filter',
823*4882a593Smuzhiyun				'set_graph_function'
824*4882a593Smuzhiyun			]
825*4882a593Smuzhiyun		for f in files:
826*4882a593Smuzhiyun			if(os.path.exists(tp+f) == False):
827*4882a593Smuzhiyun				return False
828*4882a593Smuzhiyun		return True
829*4882a593Smuzhiyun	def verifyKprobes(self):
830*4882a593Smuzhiyun		# files needed for kprobes to work
831*4882a593Smuzhiyun		files = ['kprobe_events', 'events']
832*4882a593Smuzhiyun		tp = self.tpath
833*4882a593Smuzhiyun		for f in files:
834*4882a593Smuzhiyun			if(os.path.exists(tp+f) == False):
835*4882a593Smuzhiyun				return False
836*4882a593Smuzhiyun		return True
837*4882a593Smuzhiyun	def colorText(self, str, color=31):
838*4882a593Smuzhiyun		if not self.ansi:
839*4882a593Smuzhiyun			return str
840*4882a593Smuzhiyun		return '\x1B[%d;40m%s\x1B[m' % (color, str)
841*4882a593Smuzhiyun	def writeDatafileHeader(self, filename, testdata):
842*4882a593Smuzhiyun		fp = self.openlog(filename, 'w')
843*4882a593Smuzhiyun		fp.write('%s\n%s\n# command | %s\n' % (self.teststamp, self.sysstamp, self.cmdline))
844*4882a593Smuzhiyun		for test in testdata:
845*4882a593Smuzhiyun			if 'fw' in test:
846*4882a593Smuzhiyun				fw = test['fw']
847*4882a593Smuzhiyun				if(fw):
848*4882a593Smuzhiyun					fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1]))
849*4882a593Smuzhiyun			if 'turbo' in test:
850*4882a593Smuzhiyun				fp.write('# turbostat %s\n' % test['turbo'])
851*4882a593Smuzhiyun			if 'wifi' in test:
852*4882a593Smuzhiyun				fp.write('# wifi %s\n' % test['wifi'])
853*4882a593Smuzhiyun			if test['error'] or len(testdata) > 1:
854*4882a593Smuzhiyun				fp.write('# enter_sleep_error %s\n' % test['error'])
855*4882a593Smuzhiyun		return fp
856*4882a593Smuzhiyun	def sudoUserchown(self, dir):
857*4882a593Smuzhiyun		if os.path.exists(dir) and self.sudouser:
858*4882a593Smuzhiyun			cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1'
859*4882a593Smuzhiyun			call(cmd.format(self.sudouser, dir), shell=True)
860*4882a593Smuzhiyun	def outputResult(self, testdata, num=0):
861*4882a593Smuzhiyun		if not self.result:
862*4882a593Smuzhiyun			return
863*4882a593Smuzhiyun		n = ''
864*4882a593Smuzhiyun		if num > 0:
865*4882a593Smuzhiyun			n = '%d' % num
866*4882a593Smuzhiyun		fp = open(self.result, 'a')
867*4882a593Smuzhiyun		if 'error' in testdata:
868*4882a593Smuzhiyun			fp.write('result%s: fail\n' % n)
869*4882a593Smuzhiyun			fp.write('error%s: %s\n' % (n, testdata['error']))
870*4882a593Smuzhiyun		else:
871*4882a593Smuzhiyun			fp.write('result%s: pass\n' % n)
872*4882a593Smuzhiyun		for v in ['suspend', 'resume', 'boot', 'lastinit']:
873*4882a593Smuzhiyun			if v in testdata:
874*4882a593Smuzhiyun				fp.write('%s%s: %.3f\n' % (v, n, testdata[v]))
875*4882a593Smuzhiyun		for v in ['fwsuspend', 'fwresume']:
876*4882a593Smuzhiyun			if v in testdata:
877*4882a593Smuzhiyun				fp.write('%s%s: %.3f\n' % (v, n, testdata[v] / 1000000.0))
878*4882a593Smuzhiyun		if 'bugurl' in testdata:
879*4882a593Smuzhiyun			fp.write('url%s: %s\n' % (n, testdata['bugurl']))
880*4882a593Smuzhiyun		fp.close()
881*4882a593Smuzhiyun		self.sudoUserchown(self.result)
882*4882a593Smuzhiyun	def configFile(self, file):
883*4882a593Smuzhiyun		dir = os.path.dirname(os.path.realpath(__file__))
884*4882a593Smuzhiyun		if os.path.exists(file):
885*4882a593Smuzhiyun			return file
886*4882a593Smuzhiyun		elif os.path.exists(dir+'/'+file):
887*4882a593Smuzhiyun			return dir+'/'+file
888*4882a593Smuzhiyun		elif os.path.exists(dir+'/config/'+file):
889*4882a593Smuzhiyun			return dir+'/config/'+file
890*4882a593Smuzhiyun		return ''
891*4882a593Smuzhiyun	def openlog(self, filename, mode):
892*4882a593Smuzhiyun		isgz = self.gzip
893*4882a593Smuzhiyun		if mode == 'r':
894*4882a593Smuzhiyun			try:
895*4882a593Smuzhiyun				with gzip.open(filename, mode+'t') as fp:
896*4882a593Smuzhiyun					test = fp.read(64)
897*4882a593Smuzhiyun				isgz = True
898*4882a593Smuzhiyun			except:
899*4882a593Smuzhiyun				isgz = False
900*4882a593Smuzhiyun		if isgz:
901*4882a593Smuzhiyun			return gzip.open(filename, mode+'t')
902*4882a593Smuzhiyun		return open(filename, mode)
903*4882a593Smuzhiyun	def b64unzip(self, data):
904*4882a593Smuzhiyun		try:
905*4882a593Smuzhiyun			out = codecs.decode(base64.b64decode(data), 'zlib').decode()
906*4882a593Smuzhiyun		except:
907*4882a593Smuzhiyun			out = data
908*4882a593Smuzhiyun		return out
909*4882a593Smuzhiyun	def b64zip(self, data):
910*4882a593Smuzhiyun		out = base64.b64encode(codecs.encode(data.encode(), 'zlib')).decode()
911*4882a593Smuzhiyun		return out
912*4882a593Smuzhiyun	def platforminfo(self, cmdafter):
913*4882a593Smuzhiyun		# add platform info on to a completed ftrace file
914*4882a593Smuzhiyun		if not os.path.exists(self.ftracefile):
915*4882a593Smuzhiyun			return False
916*4882a593Smuzhiyun		footer = '#\n'
917*4882a593Smuzhiyun
918*4882a593Smuzhiyun		# add test command string line if need be
919*4882a593Smuzhiyun		if self.suspendmode == 'command' and self.testcommand:
920*4882a593Smuzhiyun			footer += '# platform-testcmd: %s\n' % (self.testcommand)
921*4882a593Smuzhiyun
922*4882a593Smuzhiyun		# get a list of target devices from the ftrace file
923*4882a593Smuzhiyun		props = dict()
924*4882a593Smuzhiyun		tp = TestProps()
925*4882a593Smuzhiyun		tf = self.openlog(self.ftracefile, 'r')
926*4882a593Smuzhiyun		for line in tf:
927*4882a593Smuzhiyun			if tp.stampInfo(line, self):
928*4882a593Smuzhiyun				continue
929*4882a593Smuzhiyun			# parse only valid lines, if this is not one move on
930*4882a593Smuzhiyun			m = re.match(tp.ftrace_line_fmt, line)
931*4882a593Smuzhiyun			if(not m or 'device_pm_callback_start' not in line):
932*4882a593Smuzhiyun				continue
933*4882a593Smuzhiyun			m = re.match('.*: (?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*', m.group('msg'));
934*4882a593Smuzhiyun			if(not m):
935*4882a593Smuzhiyun				continue
936*4882a593Smuzhiyun			dev = m.group('d')
937*4882a593Smuzhiyun			if dev not in props:
938*4882a593Smuzhiyun				props[dev] = DevProps()
939*4882a593Smuzhiyun		tf.close()
940*4882a593Smuzhiyun
941*4882a593Smuzhiyun		# now get the syspath for each target device
942*4882a593Smuzhiyun		for dirname, dirnames, filenames in os.walk('/sys/devices'):
943*4882a593Smuzhiyun			if(re.match('.*/power', dirname) and 'async' in filenames):
944*4882a593Smuzhiyun				dev = dirname.split('/')[-2]
945*4882a593Smuzhiyun				if dev in props and (not props[dev].syspath or len(dirname) < len(props[dev].syspath)):
946*4882a593Smuzhiyun					props[dev].syspath = dirname[:-6]
947*4882a593Smuzhiyun
948*4882a593Smuzhiyun		# now fill in the properties for our target devices
949*4882a593Smuzhiyun		for dev in sorted(props):
950*4882a593Smuzhiyun			dirname = props[dev].syspath
951*4882a593Smuzhiyun			if not dirname or not os.path.exists(dirname):
952*4882a593Smuzhiyun				continue
953*4882a593Smuzhiyun			with open(dirname+'/power/async') as fp:
954*4882a593Smuzhiyun				text = fp.read()
955*4882a593Smuzhiyun				props[dev].isasync = False
956*4882a593Smuzhiyun				if 'enabled' in text:
957*4882a593Smuzhiyun					props[dev].isasync = True
958*4882a593Smuzhiyun			fields = os.listdir(dirname)
959*4882a593Smuzhiyun			if 'product' in fields:
960*4882a593Smuzhiyun				with open(dirname+'/product', 'rb') as fp:
961*4882a593Smuzhiyun					props[dev].altname = ascii(fp.read())
962*4882a593Smuzhiyun			elif 'name' in fields:
963*4882a593Smuzhiyun				with open(dirname+'/name', 'rb') as fp:
964*4882a593Smuzhiyun					props[dev].altname = ascii(fp.read())
965*4882a593Smuzhiyun			elif 'model' in fields:
966*4882a593Smuzhiyun				with open(dirname+'/model', 'rb') as fp:
967*4882a593Smuzhiyun					props[dev].altname = ascii(fp.read())
968*4882a593Smuzhiyun			elif 'description' in fields:
969*4882a593Smuzhiyun				with open(dirname+'/description', 'rb') as fp:
970*4882a593Smuzhiyun					props[dev].altname = ascii(fp.read())
971*4882a593Smuzhiyun			elif 'id' in fields:
972*4882a593Smuzhiyun				with open(dirname+'/id', 'rb') as fp:
973*4882a593Smuzhiyun					props[dev].altname = ascii(fp.read())
974*4882a593Smuzhiyun			elif 'idVendor' in fields and 'idProduct' in fields:
975*4882a593Smuzhiyun				idv, idp = '', ''
976*4882a593Smuzhiyun				with open(dirname+'/idVendor', 'rb') as fp:
977*4882a593Smuzhiyun					idv = ascii(fp.read()).strip()
978*4882a593Smuzhiyun				with open(dirname+'/idProduct', 'rb') as fp:
979*4882a593Smuzhiyun					idp = ascii(fp.read()).strip()
980*4882a593Smuzhiyun				props[dev].altname = '%s:%s' % (idv, idp)
981*4882a593Smuzhiyun			if props[dev].altname:
982*4882a593Smuzhiyun				out = props[dev].altname.strip().replace('\n', ' ')\
983*4882a593Smuzhiyun					.replace(',', ' ').replace(';', ' ')
984*4882a593Smuzhiyun				props[dev].altname = out
985*4882a593Smuzhiyun
986*4882a593Smuzhiyun		# add a devinfo line to the bottom of ftrace
987*4882a593Smuzhiyun		out = ''
988*4882a593Smuzhiyun		for dev in sorted(props):
989*4882a593Smuzhiyun			out += props[dev].out(dev)
990*4882a593Smuzhiyun		footer += '# platform-devinfo: %s\n' % self.b64zip(out)
991*4882a593Smuzhiyun
992*4882a593Smuzhiyun		# add a line for each of these commands with their outputs
993*4882a593Smuzhiyun		for name, cmdline, info in cmdafter:
994*4882a593Smuzhiyun			footer += '# platform-%s: %s | %s\n' % (name, cmdline, self.b64zip(info))
995*4882a593Smuzhiyun
996*4882a593Smuzhiyun		with self.openlog(self.ftracefile, 'a') as fp:
997*4882a593Smuzhiyun			fp.write(footer)
998*4882a593Smuzhiyun		return True
999*4882a593Smuzhiyun	def commonPrefix(self, list):
1000*4882a593Smuzhiyun		if len(list) < 2:
1001*4882a593Smuzhiyun			return ''
1002*4882a593Smuzhiyun		prefix = list[0]
1003*4882a593Smuzhiyun		for s in list[1:]:
1004*4882a593Smuzhiyun			while s[:len(prefix)] != prefix and prefix:
1005*4882a593Smuzhiyun				prefix = prefix[:len(prefix)-1]
1006*4882a593Smuzhiyun			if not prefix:
1007*4882a593Smuzhiyun				break
1008*4882a593Smuzhiyun		if '/' in prefix and prefix[-1] != '/':
1009*4882a593Smuzhiyun			prefix = prefix[0:prefix.rfind('/')+1]
1010*4882a593Smuzhiyun		return prefix
1011*4882a593Smuzhiyun	def dictify(self, text, format):
1012*4882a593Smuzhiyun		out = dict()
1013*4882a593Smuzhiyun		header = True if format == 1 else False
1014*4882a593Smuzhiyun		delim = ' ' if format == 1 else ':'
1015*4882a593Smuzhiyun		for line in text.split('\n'):
1016*4882a593Smuzhiyun			if header:
1017*4882a593Smuzhiyun				header, out['@'] = False, line
1018*4882a593Smuzhiyun				continue
1019*4882a593Smuzhiyun			line = line.strip()
1020*4882a593Smuzhiyun			if delim in line:
1021*4882a593Smuzhiyun				data = line.split(delim, 1)
1022*4882a593Smuzhiyun				num = re.search(r'[\d]+', data[1])
1023*4882a593Smuzhiyun				if format == 2 and num:
1024*4882a593Smuzhiyun					out[data[0].strip()] = num.group()
1025*4882a593Smuzhiyun				else:
1026*4882a593Smuzhiyun					out[data[0].strip()] = data[1]
1027*4882a593Smuzhiyun		return out
1028*4882a593Smuzhiyun	def cmdinfo(self, begin, debug=False):
1029*4882a593Smuzhiyun		out = []
1030*4882a593Smuzhiyun		if begin:
1031*4882a593Smuzhiyun			self.cmd1 = dict()
1032*4882a593Smuzhiyun		for cargs in self.infocmds:
1033*4882a593Smuzhiyun			delta, name = cargs[0], cargs[1]
1034*4882a593Smuzhiyun			cmdline, cmdpath = ' '.join(cargs[2:]), self.getExec(cargs[2])
1035*4882a593Smuzhiyun			if not cmdpath or (begin and not delta):
1036*4882a593Smuzhiyun				continue
1037*4882a593Smuzhiyun			try:
1038*4882a593Smuzhiyun				fp = Popen([cmdpath]+cargs[3:], stdout=PIPE, stderr=PIPE).stdout
1039*4882a593Smuzhiyun				info = ascii(fp.read()).strip()
1040*4882a593Smuzhiyun				fp.close()
1041*4882a593Smuzhiyun			except:
1042*4882a593Smuzhiyun				continue
1043*4882a593Smuzhiyun			if not debug and begin:
1044*4882a593Smuzhiyun				self.cmd1[name] = self.dictify(info, delta)
1045*4882a593Smuzhiyun			elif not debug and delta and name in self.cmd1:
1046*4882a593Smuzhiyun				before, after = self.cmd1[name], self.dictify(info, delta)
1047*4882a593Smuzhiyun				dinfo = ('\t%s\n' % before['@']) if '@' in before else ''
1048*4882a593Smuzhiyun				prefix = self.commonPrefix(list(before.keys()))
1049*4882a593Smuzhiyun				for key in sorted(before):
1050*4882a593Smuzhiyun					if key in after and before[key] != after[key]:
1051*4882a593Smuzhiyun						title = key.replace(prefix, '')
1052*4882a593Smuzhiyun						if delta == 2:
1053*4882a593Smuzhiyun							dinfo += '\t%s : %s -> %s\n' % \
1054*4882a593Smuzhiyun								(title, before[key].strip(), after[key].strip())
1055*4882a593Smuzhiyun						else:
1056*4882a593Smuzhiyun							dinfo += '%10s (start) : %s\n%10s (after) : %s\n' % \
1057*4882a593Smuzhiyun								(title, before[key], title, after[key])
1058*4882a593Smuzhiyun				dinfo = '\tnothing changed' if not dinfo else dinfo.rstrip()
1059*4882a593Smuzhiyun				out.append((name, cmdline, dinfo))
1060*4882a593Smuzhiyun			else:
1061*4882a593Smuzhiyun				out.append((name, cmdline, '\tnothing' if not info else info))
1062*4882a593Smuzhiyun		return out
1063*4882a593Smuzhiyun	def haveTurbostat(self):
1064*4882a593Smuzhiyun		if not self.tstat:
1065*4882a593Smuzhiyun			return False
1066*4882a593Smuzhiyun		cmd = self.getExec('turbostat')
1067*4882a593Smuzhiyun		if not cmd:
1068*4882a593Smuzhiyun			return False
1069*4882a593Smuzhiyun		fp = Popen([cmd, '-v'], stdout=PIPE, stderr=PIPE).stderr
1070*4882a593Smuzhiyun		out = ascii(fp.read()).strip()
1071*4882a593Smuzhiyun		fp.close()
1072*4882a593Smuzhiyun		if re.match('turbostat version .*', out):
1073*4882a593Smuzhiyun			self.vprint(out)
1074*4882a593Smuzhiyun			return True
1075*4882a593Smuzhiyun		return False
1076*4882a593Smuzhiyun	def turbostat(self):
1077*4882a593Smuzhiyun		cmd = self.getExec('turbostat')
1078*4882a593Smuzhiyun		rawout = keyline = valline = ''
1079*4882a593Smuzhiyun		fullcmd = '%s -q -S echo freeze > %s' % (cmd, self.powerfile)
1080*4882a593Smuzhiyun		fp = Popen(['sh', '-c', fullcmd], stdout=PIPE, stderr=PIPE).stderr
1081*4882a593Smuzhiyun		for line in fp:
1082*4882a593Smuzhiyun			line = ascii(line)
1083*4882a593Smuzhiyun			rawout += line
1084*4882a593Smuzhiyun			if keyline and valline:
1085*4882a593Smuzhiyun				continue
1086*4882a593Smuzhiyun			if re.match('(?i)Avg_MHz.*', line):
1087*4882a593Smuzhiyun				keyline = line.strip().split()
1088*4882a593Smuzhiyun			elif keyline:
1089*4882a593Smuzhiyun				valline = line.strip().split()
1090*4882a593Smuzhiyun		fp.close()
1091*4882a593Smuzhiyun		if not keyline or not valline or len(keyline) != len(valline):
1092*4882a593Smuzhiyun			errmsg = 'unrecognized turbostat output:\n'+rawout.strip()
1093*4882a593Smuzhiyun			self.vprint(errmsg)
1094*4882a593Smuzhiyun			if not self.verbose:
1095*4882a593Smuzhiyun				pprint(errmsg)
1096*4882a593Smuzhiyun			return ''
1097*4882a593Smuzhiyun		if self.verbose:
1098*4882a593Smuzhiyun			pprint(rawout.strip())
1099*4882a593Smuzhiyun		out = []
1100*4882a593Smuzhiyun		for key in keyline:
1101*4882a593Smuzhiyun			idx = keyline.index(key)
1102*4882a593Smuzhiyun			val = valline[idx]
1103*4882a593Smuzhiyun			out.append('%s=%s' % (key, val))
1104*4882a593Smuzhiyun		return '|'.join(out)
1105*4882a593Smuzhiyun	def wifiDetails(self, dev):
1106*4882a593Smuzhiyun		try:
1107*4882a593Smuzhiyun			info = open('/sys/class/net/%s/device/uevent' % dev, 'r').read().strip()
1108*4882a593Smuzhiyun		except:
1109*4882a593Smuzhiyun			return dev
1110*4882a593Smuzhiyun		vals = [dev]
1111*4882a593Smuzhiyun		for prop in info.split('\n'):
1112*4882a593Smuzhiyun			if prop.startswith('DRIVER=') or prop.startswith('PCI_ID='):
1113*4882a593Smuzhiyun				vals.append(prop.split('=')[-1])
1114*4882a593Smuzhiyun		return ':'.join(vals)
1115*4882a593Smuzhiyun	def checkWifi(self, dev=''):
1116*4882a593Smuzhiyun		try:
1117*4882a593Smuzhiyun			w = open('/proc/net/wireless', 'r').read().strip()
1118*4882a593Smuzhiyun		except:
1119*4882a593Smuzhiyun			return ''
1120*4882a593Smuzhiyun		for line in reversed(w.split('\n')):
1121*4882a593Smuzhiyun			m = re.match(' *(?P<dev>.*): (?P<stat>[0-9a-f]*) .*', w.split('\n')[-1])
1122*4882a593Smuzhiyun			if not m or (dev and dev != m.group('dev')):
1123*4882a593Smuzhiyun				continue
1124*4882a593Smuzhiyun			return m.group('dev')
1125*4882a593Smuzhiyun		return ''
1126*4882a593Smuzhiyun	def pollWifi(self, dev, timeout=60):
1127*4882a593Smuzhiyun		start = time.time()
1128*4882a593Smuzhiyun		while (time.time() - start) < timeout:
1129*4882a593Smuzhiyun			w = self.checkWifi(dev)
1130*4882a593Smuzhiyun			if w:
1131*4882a593Smuzhiyun				return '%s reconnected %.2f' % \
1132*4882a593Smuzhiyun					(self.wifiDetails(dev), max(0, time.time() - start))
1133*4882a593Smuzhiyun			time.sleep(0.01)
1134*4882a593Smuzhiyun		return '%s timeout %d' % (self.wifiDetails(dev), timeout)
1135*4882a593Smuzhiyun	def errorSummary(self, errinfo, msg):
1136*4882a593Smuzhiyun		found = False
1137*4882a593Smuzhiyun		for entry in errinfo:
1138*4882a593Smuzhiyun			if re.match(entry['match'], msg):
1139*4882a593Smuzhiyun				entry['count'] += 1
1140*4882a593Smuzhiyun				if self.hostname not in entry['urls']:
1141*4882a593Smuzhiyun					entry['urls'][self.hostname] = [self.htmlfile]
1142*4882a593Smuzhiyun				elif self.htmlfile not in entry['urls'][self.hostname]:
1143*4882a593Smuzhiyun					entry['urls'][self.hostname].append(self.htmlfile)
1144*4882a593Smuzhiyun				found = True
1145*4882a593Smuzhiyun				break
1146*4882a593Smuzhiyun		if found:
1147*4882a593Smuzhiyun			return
1148*4882a593Smuzhiyun		arr = msg.split()
1149*4882a593Smuzhiyun		for j in range(len(arr)):
1150*4882a593Smuzhiyun			if re.match('^[0-9,\-\.]*$', arr[j]):
1151*4882a593Smuzhiyun				arr[j] = '[0-9,\-\.]*'
1152*4882a593Smuzhiyun			else:
1153*4882a593Smuzhiyun				arr[j] = arr[j]\
1154*4882a593Smuzhiyun					.replace('\\', '\\\\').replace(']', '\]').replace('[', '\[')\
1155*4882a593Smuzhiyun					.replace('.', '\.').replace('+', '\+').replace('*', '\*')\
1156*4882a593Smuzhiyun					.replace('(', '\(').replace(')', '\)').replace('}', '\}')\
1157*4882a593Smuzhiyun					.replace('{', '\{')
1158*4882a593Smuzhiyun		mstr = ' *'.join(arr)
1159*4882a593Smuzhiyun		entry = {
1160*4882a593Smuzhiyun			'line': msg,
1161*4882a593Smuzhiyun			'match': mstr,
1162*4882a593Smuzhiyun			'count': 1,
1163*4882a593Smuzhiyun			'urls': {self.hostname: [self.htmlfile]}
1164*4882a593Smuzhiyun		}
1165*4882a593Smuzhiyun		errinfo.append(entry)
1166*4882a593Smuzhiyun	def multistat(self, start, idx, finish):
1167*4882a593Smuzhiyun		if 'time' in self.multitest:
1168*4882a593Smuzhiyun			id = '%d Duration=%dmin' % (idx+1, self.multitest['time'])
1169*4882a593Smuzhiyun		else:
1170*4882a593Smuzhiyun			id = '%d/%d' % (idx+1, self.multitest['count'])
1171*4882a593Smuzhiyun		t = time.time()
1172*4882a593Smuzhiyun		if 'start' not in self.multitest:
1173*4882a593Smuzhiyun			self.multitest['start'] = self.multitest['last'] = t
1174*4882a593Smuzhiyun			self.multitest['total'] = 0.0
1175*4882a593Smuzhiyun			pprint('TEST (%s) START' % id)
1176*4882a593Smuzhiyun			return
1177*4882a593Smuzhiyun		dt = t - self.multitest['last']
1178*4882a593Smuzhiyun		if not start:
1179*4882a593Smuzhiyun			if idx == 0 and self.multitest['delay'] > 0:
1180*4882a593Smuzhiyun				self.multitest['total'] += self.multitest['delay']
1181*4882a593Smuzhiyun			pprint('TEST (%s) COMPLETE -- Duration %.1fs' % (id, dt))
1182*4882a593Smuzhiyun			return
1183*4882a593Smuzhiyun		self.multitest['total'] += dt
1184*4882a593Smuzhiyun		self.multitest['last'] = t
1185*4882a593Smuzhiyun		avg = self.multitest['total'] / idx
1186*4882a593Smuzhiyun		if 'time' in self.multitest:
1187*4882a593Smuzhiyun			left = finish - datetime.now()
1188*4882a593Smuzhiyun			left -= timedelta(microseconds=left.microseconds)
1189*4882a593Smuzhiyun		else:
1190*4882a593Smuzhiyun			left = timedelta(seconds=((self.multitest['count'] - idx) * int(avg)))
1191*4882a593Smuzhiyun		pprint('TEST (%s) START - Avg Duration %.1fs, Time left %s' % \
1192*4882a593Smuzhiyun			(id, avg, str(left)))
1193*4882a593Smuzhiyun	def multiinit(self, c, d):
1194*4882a593Smuzhiyun		sz, unit = 'count', 'm'
1195*4882a593Smuzhiyun		if c.endswith('d') or c.endswith('h') or c.endswith('m'):
1196*4882a593Smuzhiyun			sz, unit, c = 'time', c[-1], c[:-1]
1197*4882a593Smuzhiyun		self.multitest['run'] = True
1198*4882a593Smuzhiyun		self.multitest[sz] = getArgInt('multi: n d (exec count)', c, 1, 1000000, False)
1199*4882a593Smuzhiyun		self.multitest['delay'] = getArgInt('multi: n d (delay between tests)', d, 0, 3600, False)
1200*4882a593Smuzhiyun		if unit == 'd':
1201*4882a593Smuzhiyun			self.multitest[sz] *= 1440
1202*4882a593Smuzhiyun		elif unit == 'h':
1203*4882a593Smuzhiyun			self.multitest[sz] *= 60
1204*4882a593Smuzhiyun
1205*4882a593Smuzhiyunsysvals = SystemValues()
1206*4882a593Smuzhiyunswitchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0']
1207*4882a593Smuzhiyunswitchoff = ['disable', 'off', 'false', '0']
1208*4882a593Smuzhiyunsuspendmodename = {
1209*4882a593Smuzhiyun	'freeze': 'Freeze (S0)',
1210*4882a593Smuzhiyun	'standby': 'Standby (S1)',
1211*4882a593Smuzhiyun	'mem': 'Suspend (S3)',
1212*4882a593Smuzhiyun	'disk': 'Hibernate (S4)'
1213*4882a593Smuzhiyun}
1214*4882a593Smuzhiyun
1215*4882a593Smuzhiyun# Class: DevProps
1216*4882a593Smuzhiyun# Description:
1217*4882a593Smuzhiyun#	 Simple class which holds property values collected
1218*4882a593Smuzhiyun#	 for all the devices used in the timeline.
1219*4882a593Smuzhiyunclass DevProps:
1220*4882a593Smuzhiyun	def __init__(self):
1221*4882a593Smuzhiyun		self.syspath = ''
1222*4882a593Smuzhiyun		self.altname = ''
1223*4882a593Smuzhiyun		self.isasync = True
1224*4882a593Smuzhiyun		self.xtraclass = ''
1225*4882a593Smuzhiyun		self.xtrainfo = ''
1226*4882a593Smuzhiyun	def out(self, dev):
1227*4882a593Smuzhiyun		return '%s,%s,%d;' % (dev, self.altname, self.isasync)
1228*4882a593Smuzhiyun	def debug(self, dev):
1229*4882a593Smuzhiyun		pprint('%s:\n\taltname = %s\n\t  async = %s' % (dev, self.altname, self.isasync))
1230*4882a593Smuzhiyun	def altName(self, dev):
1231*4882a593Smuzhiyun		if not self.altname or self.altname == dev:
1232*4882a593Smuzhiyun			return dev
1233*4882a593Smuzhiyun		return '%s [%s]' % (self.altname, dev)
1234*4882a593Smuzhiyun	def xtraClass(self):
1235*4882a593Smuzhiyun		if self.xtraclass:
1236*4882a593Smuzhiyun			return ' '+self.xtraclass
1237*4882a593Smuzhiyun		if not self.isasync:
1238*4882a593Smuzhiyun			return ' sync'
1239*4882a593Smuzhiyun		return ''
1240*4882a593Smuzhiyun	def xtraInfo(self):
1241*4882a593Smuzhiyun		if self.xtraclass:
1242*4882a593Smuzhiyun			return ' '+self.xtraclass
1243*4882a593Smuzhiyun		if self.isasync:
1244*4882a593Smuzhiyun			return ' (async)'
1245*4882a593Smuzhiyun		return ' (sync)'
1246*4882a593Smuzhiyun
1247*4882a593Smuzhiyun# Class: DeviceNode
1248*4882a593Smuzhiyun# Description:
1249*4882a593Smuzhiyun#	 A container used to create a device hierachy, with a single root node
1250*4882a593Smuzhiyun#	 and a tree of child nodes. Used by Data.deviceTopology()
1251*4882a593Smuzhiyunclass DeviceNode:
1252*4882a593Smuzhiyun	def __init__(self, nodename, nodedepth):
1253*4882a593Smuzhiyun		self.name = nodename
1254*4882a593Smuzhiyun		self.children = []
1255*4882a593Smuzhiyun		self.depth = nodedepth
1256*4882a593Smuzhiyun
1257*4882a593Smuzhiyun# Class: Data
1258*4882a593Smuzhiyun# Description:
1259*4882a593Smuzhiyun#	 The primary container for suspend/resume test data. There is one for
1260*4882a593Smuzhiyun#	 each test run. The data is organized into a cronological hierarchy:
1261*4882a593Smuzhiyun#	 Data.dmesg {
1262*4882a593Smuzhiyun#		phases {
1263*4882a593Smuzhiyun#			10 sequential, non-overlapping phases of S/R
1264*4882a593Smuzhiyun#			contents: times for phase start/end, order/color data for html
1265*4882a593Smuzhiyun#			devlist {
1266*4882a593Smuzhiyun#				device callback or action list for this phase
1267*4882a593Smuzhiyun#				device {
1268*4882a593Smuzhiyun#					a single device callback or generic action
1269*4882a593Smuzhiyun#					contents: start/stop times, pid/cpu/driver info
1270*4882a593Smuzhiyun#						parents/children, html id for timeline/callgraph
1271*4882a593Smuzhiyun#						optionally includes an ftrace callgraph
1272*4882a593Smuzhiyun#						optionally includes dev/ps data
1273*4882a593Smuzhiyun#				}
1274*4882a593Smuzhiyun#			}
1275*4882a593Smuzhiyun#		}
1276*4882a593Smuzhiyun#	}
1277*4882a593Smuzhiyun#
1278*4882a593Smuzhiyunclass Data:
1279*4882a593Smuzhiyun	phasedef = {
1280*4882a593Smuzhiyun		'suspend_prepare': {'order': 0, 'color': '#CCFFCC'},
1281*4882a593Smuzhiyun		        'suspend': {'order': 1, 'color': '#88FF88'},
1282*4882a593Smuzhiyun		   'suspend_late': {'order': 2, 'color': '#00AA00'},
1283*4882a593Smuzhiyun		  'suspend_noirq': {'order': 3, 'color': '#008888'},
1284*4882a593Smuzhiyun		'suspend_machine': {'order': 4, 'color': '#0000FF'},
1285*4882a593Smuzhiyun		 'resume_machine': {'order': 5, 'color': '#FF0000'},
1286*4882a593Smuzhiyun		   'resume_noirq': {'order': 6, 'color': '#FF9900'},
1287*4882a593Smuzhiyun		   'resume_early': {'order': 7, 'color': '#FFCC00'},
1288*4882a593Smuzhiyun		         'resume': {'order': 8, 'color': '#FFFF88'},
1289*4882a593Smuzhiyun		'resume_complete': {'order': 9, 'color': '#FFFFCC'},
1290*4882a593Smuzhiyun	}
1291*4882a593Smuzhiyun	errlist = {
1292*4882a593Smuzhiyun		'HWERROR' : r'.*\[ *Hardware Error *\].*',
1293*4882a593Smuzhiyun		'FWBUG'   : r'.*\[ *Firmware Bug *\].*',
1294*4882a593Smuzhiyun		'BUG'     : r'(?i).*\bBUG\b.*',
1295*4882a593Smuzhiyun		'ERROR'   : r'(?i).*\bERROR\b.*',
1296*4882a593Smuzhiyun		'WARNING' : r'(?i).*\bWARNING\b.*',
1297*4882a593Smuzhiyun		'FAULT'   : r'(?i).*\bFAULT\b.*',
1298*4882a593Smuzhiyun		'FAIL'    : r'(?i).*\bFAILED\b.*',
1299*4882a593Smuzhiyun		'INVALID' : r'(?i).*\bINVALID\b.*',
1300*4882a593Smuzhiyun		'CRASH'   : r'(?i).*\bCRASHED\b.*',
1301*4882a593Smuzhiyun		'TIMEOUT' : r'(?i).*\bTIMEOUT\b.*',
1302*4882a593Smuzhiyun		'IRQ'     : r'.*\bgenirq: .*',
1303*4882a593Smuzhiyun		'TASKFAIL': r'.*Freezing of tasks *.*',
1304*4882a593Smuzhiyun		'ACPI'    : r'.*\bACPI *(?P<b>[A-Za-z]*) *Error[: ].*',
1305*4882a593Smuzhiyun		'DISKFULL': r'.*\bNo space left on device.*',
1306*4882a593Smuzhiyun		'USBERR'  : r'.*usb .*device .*, error [0-9-]*',
1307*4882a593Smuzhiyun		'ATAERR'  : r' *ata[0-9\.]*: .*failed.*',
1308*4882a593Smuzhiyun		'MEIERR'  : r' *mei.*: .*failed.*',
1309*4882a593Smuzhiyun		'TPMERR'  : r'(?i) *tpm *tpm[0-9]*: .*error.*',
1310*4882a593Smuzhiyun	}
1311*4882a593Smuzhiyun	def __init__(self, num):
1312*4882a593Smuzhiyun		idchar = 'abcdefghij'
1313*4882a593Smuzhiyun		self.start = 0.0 # test start
1314*4882a593Smuzhiyun		self.end = 0.0   # test end
1315*4882a593Smuzhiyun		self.hwstart = 0 # rtc test start
1316*4882a593Smuzhiyun		self.hwend = 0   # rtc test end
1317*4882a593Smuzhiyun		self.tSuspended = 0.0 # low-level suspend start
1318*4882a593Smuzhiyun		self.tResumed = 0.0   # low-level resume start
1319*4882a593Smuzhiyun		self.tKernSus = 0.0   # kernel level suspend start
1320*4882a593Smuzhiyun		self.tKernRes = 0.0   # kernel level resume end
1321*4882a593Smuzhiyun		self.fwValid = False  # is firmware data available
1322*4882a593Smuzhiyun		self.fwSuspend = 0    # time spent in firmware suspend
1323*4882a593Smuzhiyun		self.fwResume = 0     # time spent in firmware resume
1324*4882a593Smuzhiyun		self.html_device_id = 0
1325*4882a593Smuzhiyun		self.stamp = 0
1326*4882a593Smuzhiyun		self.outfile = ''
1327*4882a593Smuzhiyun		self.kerror = False
1328*4882a593Smuzhiyun		self.wifi = dict()
1329*4882a593Smuzhiyun		self.turbostat = 0
1330*4882a593Smuzhiyun		self.enterfail = ''
1331*4882a593Smuzhiyun		self.currphase = ''
1332*4882a593Smuzhiyun		self.pstl = dict()    # process timeline
1333*4882a593Smuzhiyun		self.testnumber = num
1334*4882a593Smuzhiyun		self.idstr = idchar[num]
1335*4882a593Smuzhiyun		self.dmesgtext = []   # dmesg text file in memory
1336*4882a593Smuzhiyun		self.dmesg = dict()   # root data structure
1337*4882a593Smuzhiyun		self.errorinfo = {'suspend':[],'resume':[]}
1338*4882a593Smuzhiyun		self.tLow = []        # time spent in low-level suspends (standby/freeze)
1339*4882a593Smuzhiyun		self.devpids = []
1340*4882a593Smuzhiyun		self.devicegroups = 0
1341*4882a593Smuzhiyun	def sortedPhases(self):
1342*4882a593Smuzhiyun		return sorted(self.dmesg, key=lambda k:self.dmesg[k]['order'])
1343*4882a593Smuzhiyun	def initDevicegroups(self):
1344*4882a593Smuzhiyun		# called when phases are all finished being added
1345*4882a593Smuzhiyun		for phase in sorted(self.dmesg.keys()):
1346*4882a593Smuzhiyun			if '*' in phase:
1347*4882a593Smuzhiyun				p = phase.split('*')
1348*4882a593Smuzhiyun				pnew = '%s%d' % (p[0], len(p))
1349*4882a593Smuzhiyun				self.dmesg[pnew] = self.dmesg.pop(phase)
1350*4882a593Smuzhiyun		self.devicegroups = []
1351*4882a593Smuzhiyun		for phase in self.sortedPhases():
1352*4882a593Smuzhiyun			self.devicegroups.append([phase])
1353*4882a593Smuzhiyun	def nextPhase(self, phase, offset):
1354*4882a593Smuzhiyun		order = self.dmesg[phase]['order'] + offset
1355*4882a593Smuzhiyun		for p in self.dmesg:
1356*4882a593Smuzhiyun			if self.dmesg[p]['order'] == order:
1357*4882a593Smuzhiyun				return p
1358*4882a593Smuzhiyun		return ''
1359*4882a593Smuzhiyun	def lastPhase(self, depth=1):
1360*4882a593Smuzhiyun		plist = self.sortedPhases()
1361*4882a593Smuzhiyun		if len(plist) < depth:
1362*4882a593Smuzhiyun			return ''
1363*4882a593Smuzhiyun		return plist[-1*depth]
1364*4882a593Smuzhiyun	def turbostatInfo(self):
1365*4882a593Smuzhiyun		tp = TestProps()
1366*4882a593Smuzhiyun		out = {'syslpi':'N/A','pkgpc10':'N/A'}
1367*4882a593Smuzhiyun		for line in self.dmesgtext:
1368*4882a593Smuzhiyun			m = re.match(tp.tstatfmt, line)
1369*4882a593Smuzhiyun			if not m:
1370*4882a593Smuzhiyun				continue
1371*4882a593Smuzhiyun			for i in m.group('t').split('|'):
1372*4882a593Smuzhiyun				if 'SYS%LPI' in i:
1373*4882a593Smuzhiyun					out['syslpi'] = i.split('=')[-1]+'%'
1374*4882a593Smuzhiyun				elif 'pc10' in i:
1375*4882a593Smuzhiyun					out['pkgpc10'] = i.split('=')[-1]+'%'
1376*4882a593Smuzhiyun			break
1377*4882a593Smuzhiyun		return out
1378*4882a593Smuzhiyun	def extractErrorInfo(self):
1379*4882a593Smuzhiyun		lf = self.dmesgtext
1380*4882a593Smuzhiyun		if len(self.dmesgtext) < 1 and sysvals.dmesgfile:
1381*4882a593Smuzhiyun			lf = sysvals.openlog(sysvals.dmesgfile, 'r')
1382*4882a593Smuzhiyun		i = 0
1383*4882a593Smuzhiyun		tp = TestProps()
1384*4882a593Smuzhiyun		list = []
1385*4882a593Smuzhiyun		for line in lf:
1386*4882a593Smuzhiyun			i += 1
1387*4882a593Smuzhiyun			if tp.stampInfo(line, sysvals):
1388*4882a593Smuzhiyun				continue
1389*4882a593Smuzhiyun			m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
1390*4882a593Smuzhiyun			if not m:
1391*4882a593Smuzhiyun				continue
1392*4882a593Smuzhiyun			t = float(m.group('ktime'))
1393*4882a593Smuzhiyun			if t < self.start or t > self.end:
1394*4882a593Smuzhiyun				continue
1395*4882a593Smuzhiyun			dir = 'suspend' if t < self.tSuspended else 'resume'
1396*4882a593Smuzhiyun			msg = m.group('msg')
1397*4882a593Smuzhiyun			if re.match('capability: warning: .*', msg):
1398*4882a593Smuzhiyun				continue
1399*4882a593Smuzhiyun			for err in self.errlist:
1400*4882a593Smuzhiyun				if re.match(self.errlist[err], msg):
1401*4882a593Smuzhiyun					list.append((msg, err, dir, t, i, i))
1402*4882a593Smuzhiyun					self.kerror = True
1403*4882a593Smuzhiyun					break
1404*4882a593Smuzhiyun		tp.msglist = []
1405*4882a593Smuzhiyun		for msg, type, dir, t, idx1, idx2 in list:
1406*4882a593Smuzhiyun			tp.msglist.append(msg)
1407*4882a593Smuzhiyun			self.errorinfo[dir].append((type, t, idx1, idx2))
1408*4882a593Smuzhiyun		if self.kerror:
1409*4882a593Smuzhiyun			sysvals.dmesglog = True
1410*4882a593Smuzhiyun		if len(self.dmesgtext) < 1 and sysvals.dmesgfile:
1411*4882a593Smuzhiyun			lf.close()
1412*4882a593Smuzhiyun		return tp
1413*4882a593Smuzhiyun	def setStart(self, time, msg=''):
1414*4882a593Smuzhiyun		self.start = time
1415*4882a593Smuzhiyun		if msg:
1416*4882a593Smuzhiyun			try:
1417*4882a593Smuzhiyun				self.hwstart = datetime.strptime(msg, sysvals.tmstart)
1418*4882a593Smuzhiyun			except:
1419*4882a593Smuzhiyun				self.hwstart = 0
1420*4882a593Smuzhiyun	def setEnd(self, time, msg=''):
1421*4882a593Smuzhiyun		self.end = time
1422*4882a593Smuzhiyun		if msg:
1423*4882a593Smuzhiyun			try:
1424*4882a593Smuzhiyun				self.hwend = datetime.strptime(msg, sysvals.tmend)
1425*4882a593Smuzhiyun			except:
1426*4882a593Smuzhiyun				self.hwend = 0
1427*4882a593Smuzhiyun	def isTraceEventOutsideDeviceCalls(self, pid, time):
1428*4882a593Smuzhiyun		for phase in self.sortedPhases():
1429*4882a593Smuzhiyun			list = self.dmesg[phase]['list']
1430*4882a593Smuzhiyun			for dev in list:
1431*4882a593Smuzhiyun				d = list[dev]
1432*4882a593Smuzhiyun				if(d['pid'] == pid and time >= d['start'] and
1433*4882a593Smuzhiyun					time < d['end']):
1434*4882a593Smuzhiyun					return False
1435*4882a593Smuzhiyun		return True
1436*4882a593Smuzhiyun	def sourcePhase(self, start):
1437*4882a593Smuzhiyun		for phase in self.sortedPhases():
1438*4882a593Smuzhiyun			if 'machine' in phase:
1439*4882a593Smuzhiyun				continue
1440*4882a593Smuzhiyun			pend = self.dmesg[phase]['end']
1441*4882a593Smuzhiyun			if start <= pend:
1442*4882a593Smuzhiyun				return phase
1443*4882a593Smuzhiyun		return 'resume_complete'
1444*4882a593Smuzhiyun	def sourceDevice(self, phaselist, start, end, pid, type):
1445*4882a593Smuzhiyun		tgtdev = ''
1446*4882a593Smuzhiyun		for phase in phaselist:
1447*4882a593Smuzhiyun			list = self.dmesg[phase]['list']
1448*4882a593Smuzhiyun			for devname in list:
1449*4882a593Smuzhiyun				dev = list[devname]
1450*4882a593Smuzhiyun				# pid must match
1451*4882a593Smuzhiyun				if dev['pid'] != pid:
1452*4882a593Smuzhiyun					continue
1453*4882a593Smuzhiyun				devS = dev['start']
1454*4882a593Smuzhiyun				devE = dev['end']
1455*4882a593Smuzhiyun				if type == 'device':
1456*4882a593Smuzhiyun					# device target event is entirely inside the source boundary
1457*4882a593Smuzhiyun					if(start < devS or start >= devE or end <= devS or end > devE):
1458*4882a593Smuzhiyun						continue
1459*4882a593Smuzhiyun				elif type == 'thread':
1460*4882a593Smuzhiyun					# thread target event will expand the source boundary
1461*4882a593Smuzhiyun					if start < devS:
1462*4882a593Smuzhiyun						dev['start'] = start
1463*4882a593Smuzhiyun					if end > devE:
1464*4882a593Smuzhiyun						dev['end'] = end
1465*4882a593Smuzhiyun				tgtdev = dev
1466*4882a593Smuzhiyun				break
1467*4882a593Smuzhiyun		return tgtdev
1468*4882a593Smuzhiyun	def addDeviceFunctionCall(self, displayname, kprobename, proc, pid, start, end, cdata, rdata):
1469*4882a593Smuzhiyun		# try to place the call in a device
1470*4882a593Smuzhiyun		phases = self.sortedPhases()
1471*4882a593Smuzhiyun		tgtdev = self.sourceDevice(phases, start, end, pid, 'device')
1472*4882a593Smuzhiyun		# calls with device pids that occur outside device bounds are dropped
1473*4882a593Smuzhiyun		# TODO: include these somehow
1474*4882a593Smuzhiyun		if not tgtdev and pid in self.devpids:
1475*4882a593Smuzhiyun			return False
1476*4882a593Smuzhiyun		# try to place the call in a thread
1477*4882a593Smuzhiyun		if not tgtdev:
1478*4882a593Smuzhiyun			tgtdev = self.sourceDevice(phases, start, end, pid, 'thread')
1479*4882a593Smuzhiyun		# create new thread blocks, expand as new calls are found
1480*4882a593Smuzhiyun		if not tgtdev:
1481*4882a593Smuzhiyun			if proc == '<...>':
1482*4882a593Smuzhiyun				threadname = 'kthread-%d' % (pid)
1483*4882a593Smuzhiyun			else:
1484*4882a593Smuzhiyun				threadname = '%s-%d' % (proc, pid)
1485*4882a593Smuzhiyun			tgtphase = self.sourcePhase(start)
1486*4882a593Smuzhiyun			self.newAction(tgtphase, threadname, pid, '', start, end, '', ' kth', '')
1487*4882a593Smuzhiyun			return self.addDeviceFunctionCall(displayname, kprobename, proc, pid, start, end, cdata, rdata)
1488*4882a593Smuzhiyun		# this should not happen
1489*4882a593Smuzhiyun		if not tgtdev:
1490*4882a593Smuzhiyun			sysvals.vprint('[%f - %f] %s-%d %s %s %s' % \
1491*4882a593Smuzhiyun				(start, end, proc, pid, kprobename, cdata, rdata))
1492*4882a593Smuzhiyun			return False
1493*4882a593Smuzhiyun		# place the call data inside the src element of the tgtdev
1494*4882a593Smuzhiyun		if('src' not in tgtdev):
1495*4882a593Smuzhiyun			tgtdev['src'] = []
1496*4882a593Smuzhiyun		dtf = sysvals.dev_tracefuncs
1497*4882a593Smuzhiyun		ubiquitous = False
1498*4882a593Smuzhiyun		if kprobename in dtf and 'ub' in dtf[kprobename]:
1499*4882a593Smuzhiyun			ubiquitous = True
1500*4882a593Smuzhiyun		title = cdata+' '+rdata
1501*4882a593Smuzhiyun		mstr = '\(.*\) *(?P<args>.*) *\((?P<caller>.*)\+.* arg1=(?P<ret>.*)'
1502*4882a593Smuzhiyun		m = re.match(mstr, title)
1503*4882a593Smuzhiyun		if m:
1504*4882a593Smuzhiyun			c = m.group('caller')
1505*4882a593Smuzhiyun			a = m.group('args').strip()
1506*4882a593Smuzhiyun			r = m.group('ret')
1507*4882a593Smuzhiyun			if len(r) > 6:
1508*4882a593Smuzhiyun				r = ''
1509*4882a593Smuzhiyun			else:
1510*4882a593Smuzhiyun				r = 'ret=%s ' % r
1511*4882a593Smuzhiyun			if ubiquitous and c in dtf and 'ub' in dtf[c]:
1512*4882a593Smuzhiyun				return False
1513*4882a593Smuzhiyun		color = sysvals.kprobeColor(kprobename)
1514*4882a593Smuzhiyun		e = DevFunction(displayname, a, c, r, start, end, ubiquitous, proc, pid, color)
1515*4882a593Smuzhiyun		tgtdev['src'].append(e)
1516*4882a593Smuzhiyun		return True
1517*4882a593Smuzhiyun	def overflowDevices(self):
1518*4882a593Smuzhiyun		# get a list of devices that extend beyond the end of this test run
1519*4882a593Smuzhiyun		devlist = []
1520*4882a593Smuzhiyun		for phase in self.sortedPhases():
1521*4882a593Smuzhiyun			list = self.dmesg[phase]['list']
1522*4882a593Smuzhiyun			for devname in list:
1523*4882a593Smuzhiyun				dev = list[devname]
1524*4882a593Smuzhiyun				if dev['end'] > self.end:
1525*4882a593Smuzhiyun					devlist.append(dev)
1526*4882a593Smuzhiyun		return devlist
1527*4882a593Smuzhiyun	def mergeOverlapDevices(self, devlist):
1528*4882a593Smuzhiyun		# merge any devices that overlap devlist
1529*4882a593Smuzhiyun		for dev in devlist:
1530*4882a593Smuzhiyun			devname = dev['name']
1531*4882a593Smuzhiyun			for phase in self.sortedPhases():
1532*4882a593Smuzhiyun				list = self.dmesg[phase]['list']
1533*4882a593Smuzhiyun				if devname not in list:
1534*4882a593Smuzhiyun					continue
1535*4882a593Smuzhiyun				tdev = list[devname]
1536*4882a593Smuzhiyun				o = min(dev['end'], tdev['end']) - max(dev['start'], tdev['start'])
1537*4882a593Smuzhiyun				if o <= 0:
1538*4882a593Smuzhiyun					continue
1539*4882a593Smuzhiyun				dev['end'] = tdev['end']
1540*4882a593Smuzhiyun				if 'src' not in dev or 'src' not in tdev:
1541*4882a593Smuzhiyun					continue
1542*4882a593Smuzhiyun				dev['src'] += tdev['src']
1543*4882a593Smuzhiyun				del list[devname]
1544*4882a593Smuzhiyun	def usurpTouchingThread(self, name, dev):
1545*4882a593Smuzhiyun		# the caller test has priority of this thread, give it to him
1546*4882a593Smuzhiyun		for phase in self.sortedPhases():
1547*4882a593Smuzhiyun			list = self.dmesg[phase]['list']
1548*4882a593Smuzhiyun			if name in list:
1549*4882a593Smuzhiyun				tdev = list[name]
1550*4882a593Smuzhiyun				if tdev['start'] - dev['end'] < 0.1:
1551*4882a593Smuzhiyun					dev['end'] = tdev['end']
1552*4882a593Smuzhiyun					if 'src' not in dev:
1553*4882a593Smuzhiyun						dev['src'] = []
1554*4882a593Smuzhiyun					if 'src' in tdev:
1555*4882a593Smuzhiyun						dev['src'] += tdev['src']
1556*4882a593Smuzhiyun					del list[name]
1557*4882a593Smuzhiyun				break
1558*4882a593Smuzhiyun	def stitchTouchingThreads(self, testlist):
1559*4882a593Smuzhiyun		# merge any threads between tests that touch
1560*4882a593Smuzhiyun		for phase in self.sortedPhases():
1561*4882a593Smuzhiyun			list = self.dmesg[phase]['list']
1562*4882a593Smuzhiyun			for devname in list:
1563*4882a593Smuzhiyun				dev = list[devname]
1564*4882a593Smuzhiyun				if 'htmlclass' not in dev or 'kth' not in dev['htmlclass']:
1565*4882a593Smuzhiyun					continue
1566*4882a593Smuzhiyun				for data in testlist:
1567*4882a593Smuzhiyun					data.usurpTouchingThread(devname, dev)
1568*4882a593Smuzhiyun	def optimizeDevSrc(self):
1569*4882a593Smuzhiyun		# merge any src call loops to reduce timeline size
1570*4882a593Smuzhiyun		for phase in self.sortedPhases():
1571*4882a593Smuzhiyun			list = self.dmesg[phase]['list']
1572*4882a593Smuzhiyun			for dev in list:
1573*4882a593Smuzhiyun				if 'src' not in list[dev]:
1574*4882a593Smuzhiyun					continue
1575*4882a593Smuzhiyun				src = list[dev]['src']
1576*4882a593Smuzhiyun				p = 0
1577*4882a593Smuzhiyun				for e in sorted(src, key=lambda event: event.time):
1578*4882a593Smuzhiyun					if not p or not e.repeat(p):
1579*4882a593Smuzhiyun						p = e
1580*4882a593Smuzhiyun						continue
1581*4882a593Smuzhiyun					# e is another iteration of p, move it into p
1582*4882a593Smuzhiyun					p.end = e.end
1583*4882a593Smuzhiyun					p.length = p.end - p.time
1584*4882a593Smuzhiyun					p.count += 1
1585*4882a593Smuzhiyun					src.remove(e)
1586*4882a593Smuzhiyun	def trimTimeVal(self, t, t0, dT, left):
1587*4882a593Smuzhiyun		if left:
1588*4882a593Smuzhiyun			if(t > t0):
1589*4882a593Smuzhiyun				if(t - dT < t0):
1590*4882a593Smuzhiyun					return t0
1591*4882a593Smuzhiyun				return t - dT
1592*4882a593Smuzhiyun			else:
1593*4882a593Smuzhiyun				return t
1594*4882a593Smuzhiyun		else:
1595*4882a593Smuzhiyun			if(t < t0 + dT):
1596*4882a593Smuzhiyun				if(t > t0):
1597*4882a593Smuzhiyun					return t0 + dT
1598*4882a593Smuzhiyun				return t + dT
1599*4882a593Smuzhiyun			else:
1600*4882a593Smuzhiyun				return t
1601*4882a593Smuzhiyun	def trimTime(self, t0, dT, left):
1602*4882a593Smuzhiyun		self.tSuspended = self.trimTimeVal(self.tSuspended, t0, dT, left)
1603*4882a593Smuzhiyun		self.tResumed = self.trimTimeVal(self.tResumed, t0, dT, left)
1604*4882a593Smuzhiyun		self.start = self.trimTimeVal(self.start, t0, dT, left)
1605*4882a593Smuzhiyun		self.tKernSus = self.trimTimeVal(self.tKernSus, t0, dT, left)
1606*4882a593Smuzhiyun		self.tKernRes = self.trimTimeVal(self.tKernRes, t0, dT, left)
1607*4882a593Smuzhiyun		self.end = self.trimTimeVal(self.end, t0, dT, left)
1608*4882a593Smuzhiyun		for phase in self.sortedPhases():
1609*4882a593Smuzhiyun			p = self.dmesg[phase]
1610*4882a593Smuzhiyun			p['start'] = self.trimTimeVal(p['start'], t0, dT, left)
1611*4882a593Smuzhiyun			p['end'] = self.trimTimeVal(p['end'], t0, dT, left)
1612*4882a593Smuzhiyun			list = p['list']
1613*4882a593Smuzhiyun			for name in list:
1614*4882a593Smuzhiyun				d = list[name]
1615*4882a593Smuzhiyun				d['start'] = self.trimTimeVal(d['start'], t0, dT, left)
1616*4882a593Smuzhiyun				d['end'] = self.trimTimeVal(d['end'], t0, dT, left)
1617*4882a593Smuzhiyun				d['length'] = d['end'] - d['start']
1618*4882a593Smuzhiyun				if('ftrace' in d):
1619*4882a593Smuzhiyun					cg = d['ftrace']
1620*4882a593Smuzhiyun					cg.start = self.trimTimeVal(cg.start, t0, dT, left)
1621*4882a593Smuzhiyun					cg.end = self.trimTimeVal(cg.end, t0, dT, left)
1622*4882a593Smuzhiyun					for line in cg.list:
1623*4882a593Smuzhiyun						line.time = self.trimTimeVal(line.time, t0, dT, left)
1624*4882a593Smuzhiyun				if('src' in d):
1625*4882a593Smuzhiyun					for e in d['src']:
1626*4882a593Smuzhiyun						e.time = self.trimTimeVal(e.time, t0, dT, left)
1627*4882a593Smuzhiyun						e.end = self.trimTimeVal(e.end, t0, dT, left)
1628*4882a593Smuzhiyun						e.length = e.end - e.time
1629*4882a593Smuzhiyun		for dir in ['suspend', 'resume']:
1630*4882a593Smuzhiyun			list = []
1631*4882a593Smuzhiyun			for e in self.errorinfo[dir]:
1632*4882a593Smuzhiyun				type, tm, idx1, idx2 = e
1633*4882a593Smuzhiyun				tm = self.trimTimeVal(tm, t0, dT, left)
1634*4882a593Smuzhiyun				list.append((type, tm, idx1, idx2))
1635*4882a593Smuzhiyun			self.errorinfo[dir] = list
1636*4882a593Smuzhiyun	def trimFreezeTime(self, tZero):
1637*4882a593Smuzhiyun		# trim out any standby or freeze clock time
1638*4882a593Smuzhiyun		lp = ''
1639*4882a593Smuzhiyun		for phase in self.sortedPhases():
1640*4882a593Smuzhiyun			if 'resume_machine' in phase and 'suspend_machine' in lp:
1641*4882a593Smuzhiyun				tS, tR = self.dmesg[lp]['end'], self.dmesg[phase]['start']
1642*4882a593Smuzhiyun				tL = tR - tS
1643*4882a593Smuzhiyun				if tL > 0:
1644*4882a593Smuzhiyun					left = True if tR > tZero else False
1645*4882a593Smuzhiyun					self.trimTime(tS, tL, left)
1646*4882a593Smuzhiyun					if 'trying' in self.dmesg[lp] and self.dmesg[lp]['trying'] >= 0.001:
1647*4882a593Smuzhiyun						tTry = round(self.dmesg[lp]['trying'] * 1000)
1648*4882a593Smuzhiyun						text = '%.0f (-%.0f waking)' % (tL * 1000, tTry)
1649*4882a593Smuzhiyun					else:
1650*4882a593Smuzhiyun						text = '%.0f' % (tL * 1000)
1651*4882a593Smuzhiyun					self.tLow.append(text)
1652*4882a593Smuzhiyun			lp = phase
1653*4882a593Smuzhiyun	def getMemTime(self):
1654*4882a593Smuzhiyun		if not self.hwstart or not self.hwend:
1655*4882a593Smuzhiyun			return
1656*4882a593Smuzhiyun		stime = (self.tSuspended - self.start) * 1000000
1657*4882a593Smuzhiyun		rtime = (self.end - self.tResumed) * 1000000
1658*4882a593Smuzhiyun		hws = self.hwstart + timedelta(microseconds=stime)
1659*4882a593Smuzhiyun		hwr = self.hwend - timedelta(microseconds=rtime)
1660*4882a593Smuzhiyun		self.tLow.append('%.0f'%((hwr - hws).total_seconds() * 1000))
1661*4882a593Smuzhiyun	def getTimeValues(self):
1662*4882a593Smuzhiyun		sktime = (self.tSuspended - self.tKernSus) * 1000
1663*4882a593Smuzhiyun		rktime = (self.tKernRes - self.tResumed) * 1000
1664*4882a593Smuzhiyun		return (sktime, rktime)
1665*4882a593Smuzhiyun	def setPhase(self, phase, ktime, isbegin, order=-1):
1666*4882a593Smuzhiyun		if(isbegin):
1667*4882a593Smuzhiyun			# phase start over current phase
1668*4882a593Smuzhiyun			if self.currphase:
1669*4882a593Smuzhiyun				if 'resume_machine' not in self.currphase:
1670*4882a593Smuzhiyun					sysvals.vprint('WARNING: phase %s failed to end' % self.currphase)
1671*4882a593Smuzhiyun				self.dmesg[self.currphase]['end'] = ktime
1672*4882a593Smuzhiyun			phases = self.dmesg.keys()
1673*4882a593Smuzhiyun			color = self.phasedef[phase]['color']
1674*4882a593Smuzhiyun			count = len(phases) if order < 0 else order
1675*4882a593Smuzhiyun			# create unique name for every new phase
1676*4882a593Smuzhiyun			while phase in phases:
1677*4882a593Smuzhiyun				phase += '*'
1678*4882a593Smuzhiyun			self.dmesg[phase] = {'list': dict(), 'start': -1.0, 'end': -1.0,
1679*4882a593Smuzhiyun				'row': 0, 'color': color, 'order': count}
1680*4882a593Smuzhiyun			self.dmesg[phase]['start'] = ktime
1681*4882a593Smuzhiyun			self.currphase = phase
1682*4882a593Smuzhiyun		else:
1683*4882a593Smuzhiyun			# phase end without a start
1684*4882a593Smuzhiyun			if phase not in self.currphase:
1685*4882a593Smuzhiyun				if self.currphase:
1686*4882a593Smuzhiyun					sysvals.vprint('WARNING: %s ended instead of %s, ftrace corruption?' % (phase, self.currphase))
1687*4882a593Smuzhiyun				else:
1688*4882a593Smuzhiyun					sysvals.vprint('WARNING: %s ended without a start, ftrace corruption?' % phase)
1689*4882a593Smuzhiyun					return phase
1690*4882a593Smuzhiyun			phase = self.currphase
1691*4882a593Smuzhiyun			self.dmesg[phase]['end'] = ktime
1692*4882a593Smuzhiyun			self.currphase = ''
1693*4882a593Smuzhiyun		return phase
1694*4882a593Smuzhiyun	def sortedDevices(self, phase):
1695*4882a593Smuzhiyun		list = self.dmesg[phase]['list']
1696*4882a593Smuzhiyun		return sorted(list, key=lambda k:list[k]['start'])
1697*4882a593Smuzhiyun	def fixupInitcalls(self, phase):
1698*4882a593Smuzhiyun		# if any calls never returned, clip them at system resume end
1699*4882a593Smuzhiyun		phaselist = self.dmesg[phase]['list']
1700*4882a593Smuzhiyun		for devname in phaselist:
1701*4882a593Smuzhiyun			dev = phaselist[devname]
1702*4882a593Smuzhiyun			if(dev['end'] < 0):
1703*4882a593Smuzhiyun				for p in self.sortedPhases():
1704*4882a593Smuzhiyun					if self.dmesg[p]['end'] > dev['start']:
1705*4882a593Smuzhiyun						dev['end'] = self.dmesg[p]['end']
1706*4882a593Smuzhiyun						break
1707*4882a593Smuzhiyun				sysvals.vprint('%s (%s): callback didnt return' % (devname, phase))
1708*4882a593Smuzhiyun	def deviceFilter(self, devicefilter):
1709*4882a593Smuzhiyun		for phase in self.sortedPhases():
1710*4882a593Smuzhiyun			list = self.dmesg[phase]['list']
1711*4882a593Smuzhiyun			rmlist = []
1712*4882a593Smuzhiyun			for name in list:
1713*4882a593Smuzhiyun				keep = False
1714*4882a593Smuzhiyun				for filter in devicefilter:
1715*4882a593Smuzhiyun					if filter in name or \
1716*4882a593Smuzhiyun						('drv' in list[name] and filter in list[name]['drv']):
1717*4882a593Smuzhiyun						keep = True
1718*4882a593Smuzhiyun				if not keep:
1719*4882a593Smuzhiyun					rmlist.append(name)
1720*4882a593Smuzhiyun			for name in rmlist:
1721*4882a593Smuzhiyun				del list[name]
1722*4882a593Smuzhiyun	def fixupInitcallsThatDidntReturn(self):
1723*4882a593Smuzhiyun		# if any calls never returned, clip them at system resume end
1724*4882a593Smuzhiyun		for phase in self.sortedPhases():
1725*4882a593Smuzhiyun			self.fixupInitcalls(phase)
1726*4882a593Smuzhiyun	def phaseOverlap(self, phases):
1727*4882a593Smuzhiyun		rmgroups = []
1728*4882a593Smuzhiyun		newgroup = []
1729*4882a593Smuzhiyun		for group in self.devicegroups:
1730*4882a593Smuzhiyun			for phase in phases:
1731*4882a593Smuzhiyun				if phase not in group:
1732*4882a593Smuzhiyun					continue
1733*4882a593Smuzhiyun				for p in group:
1734*4882a593Smuzhiyun					if p not in newgroup:
1735*4882a593Smuzhiyun						newgroup.append(p)
1736*4882a593Smuzhiyun				if group not in rmgroups:
1737*4882a593Smuzhiyun					rmgroups.append(group)
1738*4882a593Smuzhiyun		for group in rmgroups:
1739*4882a593Smuzhiyun			self.devicegroups.remove(group)
1740*4882a593Smuzhiyun		self.devicegroups.append(newgroup)
1741*4882a593Smuzhiyun	def newActionGlobal(self, name, start, end, pid=-1, color=''):
1742*4882a593Smuzhiyun		# which phase is this device callback or action in
1743*4882a593Smuzhiyun		phases = self.sortedPhases()
1744*4882a593Smuzhiyun		targetphase = 'none'
1745*4882a593Smuzhiyun		htmlclass = ''
1746*4882a593Smuzhiyun		overlap = 0.0
1747*4882a593Smuzhiyun		myphases = []
1748*4882a593Smuzhiyun		for phase in phases:
1749*4882a593Smuzhiyun			pstart = self.dmesg[phase]['start']
1750*4882a593Smuzhiyun			pend = self.dmesg[phase]['end']
1751*4882a593Smuzhiyun			# see if the action overlaps this phase
1752*4882a593Smuzhiyun			o = max(0, min(end, pend) - max(start, pstart))
1753*4882a593Smuzhiyun			if o > 0:
1754*4882a593Smuzhiyun				myphases.append(phase)
1755*4882a593Smuzhiyun			# set the target phase to the one that overlaps most
1756*4882a593Smuzhiyun			if o > overlap:
1757*4882a593Smuzhiyun				if overlap > 0 and phase == 'post_resume':
1758*4882a593Smuzhiyun					continue
1759*4882a593Smuzhiyun				targetphase = phase
1760*4882a593Smuzhiyun				overlap = o
1761*4882a593Smuzhiyun		# if no target phase was found, pin it to the edge
1762*4882a593Smuzhiyun		if targetphase == 'none':
1763*4882a593Smuzhiyun			p0start = self.dmesg[phases[0]]['start']
1764*4882a593Smuzhiyun			if start <= p0start:
1765*4882a593Smuzhiyun				targetphase = phases[0]
1766*4882a593Smuzhiyun			else:
1767*4882a593Smuzhiyun				targetphase = phases[-1]
1768*4882a593Smuzhiyun		if pid == -2:
1769*4882a593Smuzhiyun			htmlclass = ' bg'
1770*4882a593Smuzhiyun		elif pid == -3:
1771*4882a593Smuzhiyun			htmlclass = ' ps'
1772*4882a593Smuzhiyun		if len(myphases) > 1:
1773*4882a593Smuzhiyun			htmlclass = ' bg'
1774*4882a593Smuzhiyun			self.phaseOverlap(myphases)
1775*4882a593Smuzhiyun		if targetphase in phases:
1776*4882a593Smuzhiyun			newname = self.newAction(targetphase, name, pid, '', start, end, '', htmlclass, color)
1777*4882a593Smuzhiyun			return (targetphase, newname)
1778*4882a593Smuzhiyun		return False
1779*4882a593Smuzhiyun	def newAction(self, phase, name, pid, parent, start, end, drv, htmlclass='', color=''):
1780*4882a593Smuzhiyun		# new device callback for a specific phase
1781*4882a593Smuzhiyun		self.html_device_id += 1
1782*4882a593Smuzhiyun		devid = '%s%d' % (self.idstr, self.html_device_id)
1783*4882a593Smuzhiyun		list = self.dmesg[phase]['list']
1784*4882a593Smuzhiyun		length = -1.0
1785*4882a593Smuzhiyun		if(start >= 0 and end >= 0):
1786*4882a593Smuzhiyun			length = end - start
1787*4882a593Smuzhiyun		if pid == -2 or name not in sysvals.tracefuncs.keys():
1788*4882a593Smuzhiyun			i = 2
1789*4882a593Smuzhiyun			origname = name
1790*4882a593Smuzhiyun			while(name in list):
1791*4882a593Smuzhiyun				name = '%s[%d]' % (origname, i)
1792*4882a593Smuzhiyun				i += 1
1793*4882a593Smuzhiyun		list[name] = {'name': name, 'start': start, 'end': end, 'pid': pid,
1794*4882a593Smuzhiyun			'par': parent, 'length': length, 'row': 0, 'id': devid, 'drv': drv }
1795*4882a593Smuzhiyun		if htmlclass:
1796*4882a593Smuzhiyun			list[name]['htmlclass'] = htmlclass
1797*4882a593Smuzhiyun		if color:
1798*4882a593Smuzhiyun			list[name]['color'] = color
1799*4882a593Smuzhiyun		return name
1800*4882a593Smuzhiyun	def findDevice(self, phase, name):
1801*4882a593Smuzhiyun		list = self.dmesg[phase]['list']
1802*4882a593Smuzhiyun		mydev = ''
1803*4882a593Smuzhiyun		for devname in sorted(list):
1804*4882a593Smuzhiyun			if name == devname or re.match('^%s\[(?P<num>[0-9]*)\]$' % name, devname):
1805*4882a593Smuzhiyun				mydev = devname
1806*4882a593Smuzhiyun		if mydev:
1807*4882a593Smuzhiyun			return list[mydev]
1808*4882a593Smuzhiyun		return False
1809*4882a593Smuzhiyun	def deviceChildren(self, devname, phase):
1810*4882a593Smuzhiyun		devlist = []
1811*4882a593Smuzhiyun		list = self.dmesg[phase]['list']
1812*4882a593Smuzhiyun		for child in list:
1813*4882a593Smuzhiyun			if(list[child]['par'] == devname):
1814*4882a593Smuzhiyun				devlist.append(child)
1815*4882a593Smuzhiyun		return devlist
1816*4882a593Smuzhiyun	def maxDeviceNameSize(self, phase):
1817*4882a593Smuzhiyun		size = 0
1818*4882a593Smuzhiyun		for name in self.dmesg[phase]['list']:
1819*4882a593Smuzhiyun			if len(name) > size:
1820*4882a593Smuzhiyun				size = len(name)
1821*4882a593Smuzhiyun		return size
1822*4882a593Smuzhiyun	def printDetails(self):
1823*4882a593Smuzhiyun		sysvals.vprint('Timeline Details:')
1824*4882a593Smuzhiyun		sysvals.vprint('          test start: %f' % self.start)
1825*4882a593Smuzhiyun		sysvals.vprint('kernel suspend start: %f' % self.tKernSus)
1826*4882a593Smuzhiyun		tS = tR = False
1827*4882a593Smuzhiyun		for phase in self.sortedPhases():
1828*4882a593Smuzhiyun			devlist = self.dmesg[phase]['list']
1829*4882a593Smuzhiyun			dc, ps, pe = len(devlist), self.dmesg[phase]['start'], self.dmesg[phase]['end']
1830*4882a593Smuzhiyun			if not tS and ps >= self.tSuspended:
1831*4882a593Smuzhiyun				sysvals.vprint('   machine suspended: %f' % self.tSuspended)
1832*4882a593Smuzhiyun				tS = True
1833*4882a593Smuzhiyun			if not tR and ps >= self.tResumed:
1834*4882a593Smuzhiyun				sysvals.vprint('     machine resumed: %f' % self.tResumed)
1835*4882a593Smuzhiyun				tR = True
1836*4882a593Smuzhiyun			sysvals.vprint('%20s: %f - %f (%d devices)' % (phase, ps, pe, dc))
1837*4882a593Smuzhiyun			if sysvals.devdump:
1838*4882a593Smuzhiyun				sysvals.vprint(''.join('-' for i in range(80)))
1839*4882a593Smuzhiyun				maxname = '%d' % self.maxDeviceNameSize(phase)
1840*4882a593Smuzhiyun				fmt = '%3d) %'+maxname+'s - %f - %f'
1841*4882a593Smuzhiyun				c = 1
1842*4882a593Smuzhiyun				for name in sorted(devlist):
1843*4882a593Smuzhiyun					s = devlist[name]['start']
1844*4882a593Smuzhiyun					e = devlist[name]['end']
1845*4882a593Smuzhiyun					sysvals.vprint(fmt % (c, name, s, e))
1846*4882a593Smuzhiyun					c += 1
1847*4882a593Smuzhiyun				sysvals.vprint(''.join('-' for i in range(80)))
1848*4882a593Smuzhiyun		sysvals.vprint('   kernel resume end: %f' % self.tKernRes)
1849*4882a593Smuzhiyun		sysvals.vprint('            test end: %f' % self.end)
1850*4882a593Smuzhiyun	def deviceChildrenAllPhases(self, devname):
1851*4882a593Smuzhiyun		devlist = []
1852*4882a593Smuzhiyun		for phase in self.sortedPhases():
1853*4882a593Smuzhiyun			list = self.deviceChildren(devname, phase)
1854*4882a593Smuzhiyun			for dev in sorted(list):
1855*4882a593Smuzhiyun				if dev not in devlist:
1856*4882a593Smuzhiyun					devlist.append(dev)
1857*4882a593Smuzhiyun		return devlist
1858*4882a593Smuzhiyun	def masterTopology(self, name, list, depth):
1859*4882a593Smuzhiyun		node = DeviceNode(name, depth)
1860*4882a593Smuzhiyun		for cname in list:
1861*4882a593Smuzhiyun			# avoid recursions
1862*4882a593Smuzhiyun			if name == cname:
1863*4882a593Smuzhiyun				continue
1864*4882a593Smuzhiyun			clist = self.deviceChildrenAllPhases(cname)
1865*4882a593Smuzhiyun			cnode = self.masterTopology(cname, clist, depth+1)
1866*4882a593Smuzhiyun			node.children.append(cnode)
1867*4882a593Smuzhiyun		return node
1868*4882a593Smuzhiyun	def printTopology(self, node):
1869*4882a593Smuzhiyun		html = ''
1870*4882a593Smuzhiyun		if node.name:
1871*4882a593Smuzhiyun			info = ''
1872*4882a593Smuzhiyun			drv = ''
1873*4882a593Smuzhiyun			for phase in self.sortedPhases():
1874*4882a593Smuzhiyun				list = self.dmesg[phase]['list']
1875*4882a593Smuzhiyun				if node.name in list:
1876*4882a593Smuzhiyun					s = list[node.name]['start']
1877*4882a593Smuzhiyun					e = list[node.name]['end']
1878*4882a593Smuzhiyun					if list[node.name]['drv']:
1879*4882a593Smuzhiyun						drv = ' {'+list[node.name]['drv']+'}'
1880*4882a593Smuzhiyun					info += ('<li>%s: %.3fms</li>' % (phase, (e-s)*1000))
1881*4882a593Smuzhiyun			html += '<li><b>'+node.name+drv+'</b>'
1882*4882a593Smuzhiyun			if info:
1883*4882a593Smuzhiyun				html += '<ul>'+info+'</ul>'
1884*4882a593Smuzhiyun			html += '</li>'
1885*4882a593Smuzhiyun		if len(node.children) > 0:
1886*4882a593Smuzhiyun			html += '<ul>'
1887*4882a593Smuzhiyun			for cnode in node.children:
1888*4882a593Smuzhiyun				html += self.printTopology(cnode)
1889*4882a593Smuzhiyun			html += '</ul>'
1890*4882a593Smuzhiyun		return html
1891*4882a593Smuzhiyun	def rootDeviceList(self):
1892*4882a593Smuzhiyun		# list of devices graphed
1893*4882a593Smuzhiyun		real = []
1894*4882a593Smuzhiyun		for phase in self.sortedPhases():
1895*4882a593Smuzhiyun			list = self.dmesg[phase]['list']
1896*4882a593Smuzhiyun			for dev in sorted(list):
1897*4882a593Smuzhiyun				if list[dev]['pid'] >= 0 and dev not in real:
1898*4882a593Smuzhiyun					real.append(dev)
1899*4882a593Smuzhiyun		# list of top-most root devices
1900*4882a593Smuzhiyun		rootlist = []
1901*4882a593Smuzhiyun		for phase in self.sortedPhases():
1902*4882a593Smuzhiyun			list = self.dmesg[phase]['list']
1903*4882a593Smuzhiyun			for dev in sorted(list):
1904*4882a593Smuzhiyun				pdev = list[dev]['par']
1905*4882a593Smuzhiyun				pid = list[dev]['pid']
1906*4882a593Smuzhiyun				if(pid < 0 or re.match('[0-9]*-[0-9]*\.[0-9]*[\.0-9]*\:[\.0-9]*$', pdev)):
1907*4882a593Smuzhiyun					continue
1908*4882a593Smuzhiyun				if pdev and pdev not in real and pdev not in rootlist:
1909*4882a593Smuzhiyun					rootlist.append(pdev)
1910*4882a593Smuzhiyun		return rootlist
1911*4882a593Smuzhiyun	def deviceTopology(self):
1912*4882a593Smuzhiyun		rootlist = self.rootDeviceList()
1913*4882a593Smuzhiyun		master = self.masterTopology('', rootlist, 0)
1914*4882a593Smuzhiyun		return self.printTopology(master)
1915*4882a593Smuzhiyun	def selectTimelineDevices(self, widfmt, tTotal, mindevlen):
1916*4882a593Smuzhiyun		# only select devices that will actually show up in html
1917*4882a593Smuzhiyun		self.tdevlist = dict()
1918*4882a593Smuzhiyun		for phase in self.dmesg:
1919*4882a593Smuzhiyun			devlist = []
1920*4882a593Smuzhiyun			list = self.dmesg[phase]['list']
1921*4882a593Smuzhiyun			for dev in list:
1922*4882a593Smuzhiyun				length = (list[dev]['end'] - list[dev]['start']) * 1000
1923*4882a593Smuzhiyun				width = widfmt % (((list[dev]['end']-list[dev]['start'])*100)/tTotal)
1924*4882a593Smuzhiyun				if width != '0.000000' and length >= mindevlen:
1925*4882a593Smuzhiyun					devlist.append(dev)
1926*4882a593Smuzhiyun			self.tdevlist[phase] = devlist
1927*4882a593Smuzhiyun	def addHorizontalDivider(self, devname, devend):
1928*4882a593Smuzhiyun		phase = 'suspend_prepare'
1929*4882a593Smuzhiyun		self.newAction(phase, devname, -2, '', \
1930*4882a593Smuzhiyun			self.start, devend, '', ' sec', '')
1931*4882a593Smuzhiyun		if phase not in self.tdevlist:
1932*4882a593Smuzhiyun			self.tdevlist[phase] = []
1933*4882a593Smuzhiyun		self.tdevlist[phase].append(devname)
1934*4882a593Smuzhiyun		d = DevItem(0, phase, self.dmesg[phase]['list'][devname])
1935*4882a593Smuzhiyun		return d
1936*4882a593Smuzhiyun	def addProcessUsageEvent(self, name, times):
1937*4882a593Smuzhiyun		# get the start and end times for this process
1938*4882a593Smuzhiyun		maxC = 0
1939*4882a593Smuzhiyun		tlast = 0
1940*4882a593Smuzhiyun		start = -1
1941*4882a593Smuzhiyun		end = -1
1942*4882a593Smuzhiyun		for t in sorted(times):
1943*4882a593Smuzhiyun			if tlast == 0:
1944*4882a593Smuzhiyun				tlast = t
1945*4882a593Smuzhiyun				continue
1946*4882a593Smuzhiyun			if name in self.pstl[t]:
1947*4882a593Smuzhiyun				if start == -1 or tlast < start:
1948*4882a593Smuzhiyun					start = tlast
1949*4882a593Smuzhiyun				if end == -1 or t > end:
1950*4882a593Smuzhiyun					end = t
1951*4882a593Smuzhiyun			tlast = t
1952*4882a593Smuzhiyun		if start == -1 or end == -1:
1953*4882a593Smuzhiyun			return 0
1954*4882a593Smuzhiyun		# add a new action for this process and get the object
1955*4882a593Smuzhiyun		out = self.newActionGlobal(name, start, end, -3)
1956*4882a593Smuzhiyun		if not out:
1957*4882a593Smuzhiyun			return 0
1958*4882a593Smuzhiyun		phase, devname = out
1959*4882a593Smuzhiyun		dev = self.dmesg[phase]['list'][devname]
1960*4882a593Smuzhiyun		# get the cpu exec data
1961*4882a593Smuzhiyun		tlast = 0
1962*4882a593Smuzhiyun		clast = 0
1963*4882a593Smuzhiyun		cpuexec = dict()
1964*4882a593Smuzhiyun		for t in sorted(times):
1965*4882a593Smuzhiyun			if tlast == 0 or t <= start or t > end:
1966*4882a593Smuzhiyun				tlast = t
1967*4882a593Smuzhiyun				continue
1968*4882a593Smuzhiyun			list = self.pstl[t]
1969*4882a593Smuzhiyun			c = 0
1970*4882a593Smuzhiyun			if name in list:
1971*4882a593Smuzhiyun				c = list[name]
1972*4882a593Smuzhiyun			if c > maxC:
1973*4882a593Smuzhiyun				maxC = c
1974*4882a593Smuzhiyun			if c != clast:
1975*4882a593Smuzhiyun				key = (tlast, t)
1976*4882a593Smuzhiyun				cpuexec[key] = c
1977*4882a593Smuzhiyun				tlast = t
1978*4882a593Smuzhiyun				clast = c
1979*4882a593Smuzhiyun		dev['cpuexec'] = cpuexec
1980*4882a593Smuzhiyun		return maxC
1981*4882a593Smuzhiyun	def createProcessUsageEvents(self):
1982*4882a593Smuzhiyun		# get an array of process names
1983*4882a593Smuzhiyun		proclist = []
1984*4882a593Smuzhiyun		for t in sorted(self.pstl):
1985*4882a593Smuzhiyun			pslist = self.pstl[t]
1986*4882a593Smuzhiyun			for ps in sorted(pslist):
1987*4882a593Smuzhiyun				if ps not in proclist:
1988*4882a593Smuzhiyun					proclist.append(ps)
1989*4882a593Smuzhiyun		# get a list of data points for suspend and resume
1990*4882a593Smuzhiyun		tsus = []
1991*4882a593Smuzhiyun		tres = []
1992*4882a593Smuzhiyun		for t in sorted(self.pstl):
1993*4882a593Smuzhiyun			if t < self.tSuspended:
1994*4882a593Smuzhiyun				tsus.append(t)
1995*4882a593Smuzhiyun			else:
1996*4882a593Smuzhiyun				tres.append(t)
1997*4882a593Smuzhiyun		# process the events for suspend and resume
1998*4882a593Smuzhiyun		if len(proclist) > 0:
1999*4882a593Smuzhiyun			sysvals.vprint('Process Execution:')
2000*4882a593Smuzhiyun		for ps in proclist:
2001*4882a593Smuzhiyun			c = self.addProcessUsageEvent(ps, tsus)
2002*4882a593Smuzhiyun			if c > 0:
2003*4882a593Smuzhiyun				sysvals.vprint('%25s (sus): %d' % (ps, c))
2004*4882a593Smuzhiyun			c = self.addProcessUsageEvent(ps, tres)
2005*4882a593Smuzhiyun			if c > 0:
2006*4882a593Smuzhiyun				sysvals.vprint('%25s (res): %d' % (ps, c))
2007*4882a593Smuzhiyun	def handleEndMarker(self, time, msg=''):
2008*4882a593Smuzhiyun		dm = self.dmesg
2009*4882a593Smuzhiyun		self.setEnd(time, msg)
2010*4882a593Smuzhiyun		self.initDevicegroups()
2011*4882a593Smuzhiyun		# give suspend_prepare an end if needed
2012*4882a593Smuzhiyun		if 'suspend_prepare' in dm and dm['suspend_prepare']['end'] < 0:
2013*4882a593Smuzhiyun			dm['suspend_prepare']['end'] = time
2014*4882a593Smuzhiyun		# assume resume machine ends at next phase start
2015*4882a593Smuzhiyun		if 'resume_machine' in dm and dm['resume_machine']['end'] < 0:
2016*4882a593Smuzhiyun			np = self.nextPhase('resume_machine', 1)
2017*4882a593Smuzhiyun			if np:
2018*4882a593Smuzhiyun				dm['resume_machine']['end'] = dm[np]['start']
2019*4882a593Smuzhiyun		# if kernel resume end not found, assume its the end marker
2020*4882a593Smuzhiyun		if self.tKernRes == 0.0:
2021*4882a593Smuzhiyun			self.tKernRes = time
2022*4882a593Smuzhiyun		# if kernel suspend start not found, assume its the end marker
2023*4882a593Smuzhiyun		if self.tKernSus == 0.0:
2024*4882a593Smuzhiyun			self.tKernSus = time
2025*4882a593Smuzhiyun		# set resume complete to end at end marker
2026*4882a593Smuzhiyun		if 'resume_complete' in dm:
2027*4882a593Smuzhiyun			dm['resume_complete']['end'] = time
2028*4882a593Smuzhiyun	def debugPrint(self):
2029*4882a593Smuzhiyun		for p in self.sortedPhases():
2030*4882a593Smuzhiyun			list = self.dmesg[p]['list']
2031*4882a593Smuzhiyun			for devname in sorted(list):
2032*4882a593Smuzhiyun				dev = list[devname]
2033*4882a593Smuzhiyun				if 'ftrace' in dev:
2034*4882a593Smuzhiyun					dev['ftrace'].debugPrint(' [%s]' % devname)
2035*4882a593Smuzhiyun
2036*4882a593Smuzhiyun# Class: DevFunction
2037*4882a593Smuzhiyun# Description:
2038*4882a593Smuzhiyun#	 A container for kprobe function data we want in the dev timeline
2039*4882a593Smuzhiyunclass DevFunction:
2040*4882a593Smuzhiyun	def __init__(self, name, args, caller, ret, start, end, u, proc, pid, color):
2041*4882a593Smuzhiyun		self.row = 0
2042*4882a593Smuzhiyun		self.count = 1
2043*4882a593Smuzhiyun		self.name = name
2044*4882a593Smuzhiyun		self.args = args
2045*4882a593Smuzhiyun		self.caller = caller
2046*4882a593Smuzhiyun		self.ret = ret
2047*4882a593Smuzhiyun		self.time = start
2048*4882a593Smuzhiyun		self.length = end - start
2049*4882a593Smuzhiyun		self.end = end
2050*4882a593Smuzhiyun		self.ubiquitous = u
2051*4882a593Smuzhiyun		self.proc = proc
2052*4882a593Smuzhiyun		self.pid = pid
2053*4882a593Smuzhiyun		self.color = color
2054*4882a593Smuzhiyun	def title(self):
2055*4882a593Smuzhiyun		cnt = ''
2056*4882a593Smuzhiyun		if self.count > 1:
2057*4882a593Smuzhiyun			cnt = '(x%d)' % self.count
2058*4882a593Smuzhiyun		l = '%0.3fms' % (self.length * 1000)
2059*4882a593Smuzhiyun		if self.ubiquitous:
2060*4882a593Smuzhiyun			title = '%s(%s)%s <- %s, %s(%s)' % \
2061*4882a593Smuzhiyun				(self.name, self.args, cnt, self.caller, self.ret, l)
2062*4882a593Smuzhiyun		else:
2063*4882a593Smuzhiyun			title = '%s(%s) %s%s(%s)' % (self.name, self.args, self.ret, cnt, l)
2064*4882a593Smuzhiyun		return title.replace('"', '')
2065*4882a593Smuzhiyun	def text(self):
2066*4882a593Smuzhiyun		if self.count > 1:
2067*4882a593Smuzhiyun			text = '%s(x%d)' % (self.name, self.count)
2068*4882a593Smuzhiyun		else:
2069*4882a593Smuzhiyun			text = self.name
2070*4882a593Smuzhiyun		return text
2071*4882a593Smuzhiyun	def repeat(self, tgt):
2072*4882a593Smuzhiyun		# is the tgt call just a repeat of this call (e.g. are we in a loop)
2073*4882a593Smuzhiyun		dt = self.time - tgt.end
2074*4882a593Smuzhiyun		# only combine calls if -all- attributes are identical
2075*4882a593Smuzhiyun		if tgt.caller == self.caller and \
2076*4882a593Smuzhiyun			tgt.name == self.name and tgt.args == self.args and \
2077*4882a593Smuzhiyun			tgt.proc == self.proc and tgt.pid == self.pid and \
2078*4882a593Smuzhiyun			tgt.ret == self.ret and dt >= 0 and \
2079*4882a593Smuzhiyun			dt <= sysvals.callloopmaxgap and \
2080*4882a593Smuzhiyun			self.length < sysvals.callloopmaxlen:
2081*4882a593Smuzhiyun			return True
2082*4882a593Smuzhiyun		return False
2083*4882a593Smuzhiyun
2084*4882a593Smuzhiyun# Class: FTraceLine
2085*4882a593Smuzhiyun# Description:
2086*4882a593Smuzhiyun#	 A container for a single line of ftrace data. There are six basic types:
2087*4882a593Smuzhiyun#		 callgraph line:
2088*4882a593Smuzhiyun#			  call: "  dpm_run_callback() {"
2089*4882a593Smuzhiyun#			return: "  }"
2090*4882a593Smuzhiyun#			  leaf: " dpm_run_callback();"
2091*4882a593Smuzhiyun#		 trace event:
2092*4882a593Smuzhiyun#			 tracing_mark_write: SUSPEND START or RESUME COMPLETE
2093*4882a593Smuzhiyun#			 suspend_resume: phase or custom exec block data
2094*4882a593Smuzhiyun#			 device_pm_callback: device callback info
2095*4882a593Smuzhiyunclass FTraceLine:
2096*4882a593Smuzhiyun	def __init__(self, t, m='', d=''):
2097*4882a593Smuzhiyun		self.length = 0.0
2098*4882a593Smuzhiyun		self.fcall = False
2099*4882a593Smuzhiyun		self.freturn = False
2100*4882a593Smuzhiyun		self.fevent = False
2101*4882a593Smuzhiyun		self.fkprobe = False
2102*4882a593Smuzhiyun		self.depth = 0
2103*4882a593Smuzhiyun		self.name = ''
2104*4882a593Smuzhiyun		self.type = ''
2105*4882a593Smuzhiyun		self.time = float(t)
2106*4882a593Smuzhiyun		if not m and not d:
2107*4882a593Smuzhiyun			return
2108*4882a593Smuzhiyun		# is this a trace event
2109*4882a593Smuzhiyun		if(d == 'traceevent' or re.match('^ *\/\* *(?P<msg>.*) \*\/ *$', m)):
2110*4882a593Smuzhiyun			if(d == 'traceevent'):
2111*4882a593Smuzhiyun				# nop format trace event
2112*4882a593Smuzhiyun				msg = m
2113*4882a593Smuzhiyun			else:
2114*4882a593Smuzhiyun				# function_graph format trace event
2115*4882a593Smuzhiyun				em = re.match('^ *\/\* *(?P<msg>.*) \*\/ *$', m)
2116*4882a593Smuzhiyun				msg = em.group('msg')
2117*4882a593Smuzhiyun
2118*4882a593Smuzhiyun			emm = re.match('^(?P<call>.*?): (?P<msg>.*)', msg)
2119*4882a593Smuzhiyun			if(emm):
2120*4882a593Smuzhiyun				self.name = emm.group('msg')
2121*4882a593Smuzhiyun				self.type = emm.group('call')
2122*4882a593Smuzhiyun			else:
2123*4882a593Smuzhiyun				self.name = msg
2124*4882a593Smuzhiyun			km = re.match('^(?P<n>.*)_cal$', self.type)
2125*4882a593Smuzhiyun			if km:
2126*4882a593Smuzhiyun				self.fcall = True
2127*4882a593Smuzhiyun				self.fkprobe = True
2128*4882a593Smuzhiyun				self.type = km.group('n')
2129*4882a593Smuzhiyun				return
2130*4882a593Smuzhiyun			km = re.match('^(?P<n>.*)_ret$', self.type)
2131*4882a593Smuzhiyun			if km:
2132*4882a593Smuzhiyun				self.freturn = True
2133*4882a593Smuzhiyun				self.fkprobe = True
2134*4882a593Smuzhiyun				self.type = km.group('n')
2135*4882a593Smuzhiyun				return
2136*4882a593Smuzhiyun			self.fevent = True
2137*4882a593Smuzhiyun			return
2138*4882a593Smuzhiyun		# convert the duration to seconds
2139*4882a593Smuzhiyun		if(d):
2140*4882a593Smuzhiyun			self.length = float(d)/1000000
2141*4882a593Smuzhiyun		# the indentation determines the depth
2142*4882a593Smuzhiyun		match = re.match('^(?P<d> *)(?P<o>.*)$', m)
2143*4882a593Smuzhiyun		if(not match):
2144*4882a593Smuzhiyun			return
2145*4882a593Smuzhiyun		self.depth = self.getDepth(match.group('d'))
2146*4882a593Smuzhiyun		m = match.group('o')
2147*4882a593Smuzhiyun		# function return
2148*4882a593Smuzhiyun		if(m[0] == '}'):
2149*4882a593Smuzhiyun			self.freturn = True
2150*4882a593Smuzhiyun			if(len(m) > 1):
2151*4882a593Smuzhiyun				# includes comment with function name
2152*4882a593Smuzhiyun				match = re.match('^} *\/\* *(?P<n>.*) *\*\/$', m)
2153*4882a593Smuzhiyun				if(match):
2154*4882a593Smuzhiyun					self.name = match.group('n').strip()
2155*4882a593Smuzhiyun		# function call
2156*4882a593Smuzhiyun		else:
2157*4882a593Smuzhiyun			self.fcall = True
2158*4882a593Smuzhiyun			# function call with children
2159*4882a593Smuzhiyun			if(m[-1] == '{'):
2160*4882a593Smuzhiyun				match = re.match('^(?P<n>.*) *\(.*', m)
2161*4882a593Smuzhiyun				if(match):
2162*4882a593Smuzhiyun					self.name = match.group('n').strip()
2163*4882a593Smuzhiyun			# function call with no children (leaf)
2164*4882a593Smuzhiyun			elif(m[-1] == ';'):
2165*4882a593Smuzhiyun				self.freturn = True
2166*4882a593Smuzhiyun				match = re.match('^(?P<n>.*) *\(.*', m)
2167*4882a593Smuzhiyun				if(match):
2168*4882a593Smuzhiyun					self.name = match.group('n').strip()
2169*4882a593Smuzhiyun			# something else (possibly a trace marker)
2170*4882a593Smuzhiyun			else:
2171*4882a593Smuzhiyun				self.name = m
2172*4882a593Smuzhiyun	def isCall(self):
2173*4882a593Smuzhiyun		return self.fcall and not self.freturn
2174*4882a593Smuzhiyun	def isReturn(self):
2175*4882a593Smuzhiyun		return self.freturn and not self.fcall
2176*4882a593Smuzhiyun	def isLeaf(self):
2177*4882a593Smuzhiyun		return self.fcall and self.freturn
2178*4882a593Smuzhiyun	def getDepth(self, str):
2179*4882a593Smuzhiyun		return len(str)/2
2180*4882a593Smuzhiyun	def debugPrint(self, info=''):
2181*4882a593Smuzhiyun		if self.isLeaf():
2182*4882a593Smuzhiyun			pprint(' -- %12.6f (depth=%02d): %s(); (%.3f us) %s' % (self.time, \
2183*4882a593Smuzhiyun				self.depth, self.name, self.length*1000000, info))
2184*4882a593Smuzhiyun		elif self.freturn:
2185*4882a593Smuzhiyun			pprint(' -- %12.6f (depth=%02d): %s} (%.3f us) %s' % (self.time, \
2186*4882a593Smuzhiyun				self.depth, self.name, self.length*1000000, info))
2187*4882a593Smuzhiyun		else:
2188*4882a593Smuzhiyun			pprint(' -- %12.6f (depth=%02d): %s() { (%.3f us) %s' % (self.time, \
2189*4882a593Smuzhiyun				self.depth, self.name, self.length*1000000, info))
2190*4882a593Smuzhiyun	def startMarker(self):
2191*4882a593Smuzhiyun		# Is this the starting line of a suspend?
2192*4882a593Smuzhiyun		if not self.fevent:
2193*4882a593Smuzhiyun			return False
2194*4882a593Smuzhiyun		if sysvals.usetracemarkers:
2195*4882a593Smuzhiyun			if(self.name.startswith('SUSPEND START')):
2196*4882a593Smuzhiyun				return True
2197*4882a593Smuzhiyun			return False
2198*4882a593Smuzhiyun		else:
2199*4882a593Smuzhiyun			if(self.type == 'suspend_resume' and
2200*4882a593Smuzhiyun				re.match('suspend_enter\[.*\] begin', self.name)):
2201*4882a593Smuzhiyun				return True
2202*4882a593Smuzhiyun			return False
2203*4882a593Smuzhiyun	def endMarker(self):
2204*4882a593Smuzhiyun		# Is this the ending line of a resume?
2205*4882a593Smuzhiyun		if not self.fevent:
2206*4882a593Smuzhiyun			return False
2207*4882a593Smuzhiyun		if sysvals.usetracemarkers:
2208*4882a593Smuzhiyun			if(self.name.startswith('RESUME COMPLETE')):
2209*4882a593Smuzhiyun				return True
2210*4882a593Smuzhiyun			return False
2211*4882a593Smuzhiyun		else:
2212*4882a593Smuzhiyun			if(self.type == 'suspend_resume' and
2213*4882a593Smuzhiyun				re.match('thaw_processes\[.*\] end', self.name)):
2214*4882a593Smuzhiyun				return True
2215*4882a593Smuzhiyun			return False
2216*4882a593Smuzhiyun
2217*4882a593Smuzhiyun# Class: FTraceCallGraph
2218*4882a593Smuzhiyun# Description:
2219*4882a593Smuzhiyun#	 A container for the ftrace callgraph of a single recursive function.
2220*4882a593Smuzhiyun#	 This can be a dpm_run_callback, dpm_prepare, or dpm_complete callgraph
2221*4882a593Smuzhiyun#	 Each instance is tied to a single device in a single phase, and is
2222*4882a593Smuzhiyun#	 comprised of an ordered list of FTraceLine objects
2223*4882a593Smuzhiyunclass FTraceCallGraph:
2224*4882a593Smuzhiyun	vfname = 'missing_function_name'
2225*4882a593Smuzhiyun	def __init__(self, pid, sv):
2226*4882a593Smuzhiyun		self.id = ''
2227*4882a593Smuzhiyun		self.invalid = False
2228*4882a593Smuzhiyun		self.name = ''
2229*4882a593Smuzhiyun		self.partial = False
2230*4882a593Smuzhiyun		self.ignore = False
2231*4882a593Smuzhiyun		self.start = -1.0
2232*4882a593Smuzhiyun		self.end = -1.0
2233*4882a593Smuzhiyun		self.list = []
2234*4882a593Smuzhiyun		self.depth = 0
2235*4882a593Smuzhiyun		self.pid = pid
2236*4882a593Smuzhiyun		self.sv = sv
2237*4882a593Smuzhiyun	def addLine(self, line):
2238*4882a593Smuzhiyun		# if this is already invalid, just leave
2239*4882a593Smuzhiyun		if(self.invalid):
2240*4882a593Smuzhiyun			if(line.depth == 0 and line.freturn):
2241*4882a593Smuzhiyun				return 1
2242*4882a593Smuzhiyun			return 0
2243*4882a593Smuzhiyun		# invalidate on bad depth
2244*4882a593Smuzhiyun		if(self.depth < 0):
2245*4882a593Smuzhiyun			self.invalidate(line)
2246*4882a593Smuzhiyun			return 0
2247*4882a593Smuzhiyun		# ignore data til we return to the current depth
2248*4882a593Smuzhiyun		if self.ignore:
2249*4882a593Smuzhiyun			if line.depth > self.depth:
2250*4882a593Smuzhiyun				return 0
2251*4882a593Smuzhiyun			else:
2252*4882a593Smuzhiyun				self.list[-1].freturn = True
2253*4882a593Smuzhiyun				self.list[-1].length = line.time - self.list[-1].time
2254*4882a593Smuzhiyun				self.ignore = False
2255*4882a593Smuzhiyun				# if this is a return at self.depth, no more work is needed
2256*4882a593Smuzhiyun				if line.depth == self.depth and line.isReturn():
2257*4882a593Smuzhiyun					if line.depth == 0:
2258*4882a593Smuzhiyun						self.end = line.time
2259*4882a593Smuzhiyun						return 1
2260*4882a593Smuzhiyun					return 0
2261*4882a593Smuzhiyun		# compare current depth with this lines pre-call depth
2262*4882a593Smuzhiyun		prelinedep = line.depth
2263*4882a593Smuzhiyun		if line.isReturn():
2264*4882a593Smuzhiyun			prelinedep += 1
2265*4882a593Smuzhiyun		last = 0
2266*4882a593Smuzhiyun		lasttime = line.time
2267*4882a593Smuzhiyun		if len(self.list) > 0:
2268*4882a593Smuzhiyun			last = self.list[-1]
2269*4882a593Smuzhiyun			lasttime = last.time
2270*4882a593Smuzhiyun			if last.isLeaf():
2271*4882a593Smuzhiyun				lasttime += last.length
2272*4882a593Smuzhiyun		# handle low misalignments by inserting returns
2273*4882a593Smuzhiyun		mismatch = prelinedep - self.depth
2274*4882a593Smuzhiyun		warning = self.sv.verbose and abs(mismatch) > 1
2275*4882a593Smuzhiyun		info = []
2276*4882a593Smuzhiyun		if mismatch < 0:
2277*4882a593Smuzhiyun			idx = 0
2278*4882a593Smuzhiyun			# add return calls to get the depth down
2279*4882a593Smuzhiyun			while prelinedep < self.depth:
2280*4882a593Smuzhiyun				self.depth -= 1
2281*4882a593Smuzhiyun				if idx == 0 and last and last.isCall():
2282*4882a593Smuzhiyun					# special case, turn last call into a leaf
2283*4882a593Smuzhiyun					last.depth = self.depth
2284*4882a593Smuzhiyun					last.freturn = True
2285*4882a593Smuzhiyun					last.length = line.time - last.time
2286*4882a593Smuzhiyun					if warning:
2287*4882a593Smuzhiyun						info.append(('[make leaf]', last))
2288*4882a593Smuzhiyun				else:
2289*4882a593Smuzhiyun					vline = FTraceLine(lasttime)
2290*4882a593Smuzhiyun					vline.depth = self.depth
2291*4882a593Smuzhiyun					vline.name = self.vfname
2292*4882a593Smuzhiyun					vline.freturn = True
2293*4882a593Smuzhiyun					self.list.append(vline)
2294*4882a593Smuzhiyun					if warning:
2295*4882a593Smuzhiyun						if idx == 0:
2296*4882a593Smuzhiyun							info.append(('', last))
2297*4882a593Smuzhiyun						info.append(('[add return]', vline))
2298*4882a593Smuzhiyun				idx += 1
2299*4882a593Smuzhiyun			if warning:
2300*4882a593Smuzhiyun				info.append(('', line))
2301*4882a593Smuzhiyun		# handle high misalignments by inserting calls
2302*4882a593Smuzhiyun		elif mismatch > 0:
2303*4882a593Smuzhiyun			idx = 0
2304*4882a593Smuzhiyun			if warning:
2305*4882a593Smuzhiyun				info.append(('', last))
2306*4882a593Smuzhiyun			# add calls to get the depth up
2307*4882a593Smuzhiyun			while prelinedep > self.depth:
2308*4882a593Smuzhiyun				if idx == 0 and line.isReturn():
2309*4882a593Smuzhiyun					# special case, turn this return into a leaf
2310*4882a593Smuzhiyun					line.fcall = True
2311*4882a593Smuzhiyun					prelinedep -= 1
2312*4882a593Smuzhiyun					if warning:
2313*4882a593Smuzhiyun						info.append(('[make leaf]', line))
2314*4882a593Smuzhiyun				else:
2315*4882a593Smuzhiyun					vline = FTraceLine(lasttime)
2316*4882a593Smuzhiyun					vline.depth = self.depth
2317*4882a593Smuzhiyun					vline.name = self.vfname
2318*4882a593Smuzhiyun					vline.fcall = True
2319*4882a593Smuzhiyun					self.list.append(vline)
2320*4882a593Smuzhiyun					self.depth += 1
2321*4882a593Smuzhiyun					if not last:
2322*4882a593Smuzhiyun						self.start = vline.time
2323*4882a593Smuzhiyun					if warning:
2324*4882a593Smuzhiyun						info.append(('[add call]', vline))
2325*4882a593Smuzhiyun				idx += 1
2326*4882a593Smuzhiyun			if warning and ('[make leaf]', line) not in info:
2327*4882a593Smuzhiyun				info.append(('', line))
2328*4882a593Smuzhiyun		if warning:
2329*4882a593Smuzhiyun			pprint('WARNING: ftrace data missing, corrections made:')
2330*4882a593Smuzhiyun			for i in info:
2331*4882a593Smuzhiyun				t, obj = i
2332*4882a593Smuzhiyun				if obj:
2333*4882a593Smuzhiyun					obj.debugPrint(t)
2334*4882a593Smuzhiyun		# process the call and set the new depth
2335*4882a593Smuzhiyun		skipadd = False
2336*4882a593Smuzhiyun		md = self.sv.max_graph_depth
2337*4882a593Smuzhiyun		if line.isCall():
2338*4882a593Smuzhiyun			# ignore blacklisted/overdepth funcs
2339*4882a593Smuzhiyun			if (md and self.depth >= md - 1) or (line.name in self.sv.cgblacklist):
2340*4882a593Smuzhiyun				self.ignore = True
2341*4882a593Smuzhiyun			else:
2342*4882a593Smuzhiyun				self.depth += 1
2343*4882a593Smuzhiyun		elif line.isReturn():
2344*4882a593Smuzhiyun			self.depth -= 1
2345*4882a593Smuzhiyun			# remove blacklisted/overdepth/empty funcs that slipped through
2346*4882a593Smuzhiyun			if (last and last.isCall() and last.depth == line.depth) or \
2347*4882a593Smuzhiyun				(md and last and last.depth >= md) or \
2348*4882a593Smuzhiyun				(line.name in self.sv.cgblacklist):
2349*4882a593Smuzhiyun				while len(self.list) > 0 and self.list[-1].depth > line.depth:
2350*4882a593Smuzhiyun					self.list.pop(-1)
2351*4882a593Smuzhiyun				if len(self.list) == 0:
2352*4882a593Smuzhiyun					self.invalid = True
2353*4882a593Smuzhiyun					return 1
2354*4882a593Smuzhiyun				self.list[-1].freturn = True
2355*4882a593Smuzhiyun				self.list[-1].length = line.time - self.list[-1].time
2356*4882a593Smuzhiyun				self.list[-1].name = line.name
2357*4882a593Smuzhiyun				skipadd = True
2358*4882a593Smuzhiyun		if len(self.list) < 1:
2359*4882a593Smuzhiyun			self.start = line.time
2360*4882a593Smuzhiyun		# check for a mismatch that returned all the way to callgraph end
2361*4882a593Smuzhiyun		res = 1
2362*4882a593Smuzhiyun		if mismatch < 0 and self.list[-1].depth == 0 and self.list[-1].freturn:
2363*4882a593Smuzhiyun			line = self.list[-1]
2364*4882a593Smuzhiyun			skipadd = True
2365*4882a593Smuzhiyun			res = -1
2366*4882a593Smuzhiyun		if not skipadd:
2367*4882a593Smuzhiyun			self.list.append(line)
2368*4882a593Smuzhiyun		if(line.depth == 0 and line.freturn):
2369*4882a593Smuzhiyun			if(self.start < 0):
2370*4882a593Smuzhiyun				self.start = line.time
2371*4882a593Smuzhiyun			self.end = line.time
2372*4882a593Smuzhiyun			if line.fcall:
2373*4882a593Smuzhiyun				self.end += line.length
2374*4882a593Smuzhiyun			if self.list[0].name == self.vfname:
2375*4882a593Smuzhiyun				self.invalid = True
2376*4882a593Smuzhiyun			if res == -1:
2377*4882a593Smuzhiyun				self.partial = True
2378*4882a593Smuzhiyun			return res
2379*4882a593Smuzhiyun		return 0
2380*4882a593Smuzhiyun	def invalidate(self, line):
2381*4882a593Smuzhiyun		if(len(self.list) > 0):
2382*4882a593Smuzhiyun			first = self.list[0]
2383*4882a593Smuzhiyun			self.list = []
2384*4882a593Smuzhiyun			self.list.append(first)
2385*4882a593Smuzhiyun		self.invalid = True
2386*4882a593Smuzhiyun		id = 'task %s' % (self.pid)
2387*4882a593Smuzhiyun		window = '(%f - %f)' % (self.start, line.time)
2388*4882a593Smuzhiyun		if(self.depth < 0):
2389*4882a593Smuzhiyun			pprint('Data misalignment for '+id+\
2390*4882a593Smuzhiyun				' (buffer overflow), ignoring this callback')
2391*4882a593Smuzhiyun		else:
2392*4882a593Smuzhiyun			pprint('Too much data for '+id+\
2393*4882a593Smuzhiyun				' '+window+', ignoring this callback')
2394*4882a593Smuzhiyun	def slice(self, dev):
2395*4882a593Smuzhiyun		minicg = FTraceCallGraph(dev['pid'], self.sv)
2396*4882a593Smuzhiyun		minicg.name = self.name
2397*4882a593Smuzhiyun		mydepth = -1
2398*4882a593Smuzhiyun		good = False
2399*4882a593Smuzhiyun		for l in self.list:
2400*4882a593Smuzhiyun			if(l.time < dev['start'] or l.time > dev['end']):
2401*4882a593Smuzhiyun				continue
2402*4882a593Smuzhiyun			if mydepth < 0:
2403*4882a593Smuzhiyun				if l.name == 'mutex_lock' and l.freturn:
2404*4882a593Smuzhiyun					mydepth = l.depth
2405*4882a593Smuzhiyun				continue
2406*4882a593Smuzhiyun			elif l.depth == mydepth and l.name == 'mutex_unlock' and l.fcall:
2407*4882a593Smuzhiyun				good = True
2408*4882a593Smuzhiyun				break
2409*4882a593Smuzhiyun			l.depth -= mydepth
2410*4882a593Smuzhiyun			minicg.addLine(l)
2411*4882a593Smuzhiyun		if not good or len(minicg.list) < 1:
2412*4882a593Smuzhiyun			return 0
2413*4882a593Smuzhiyun		return minicg
2414*4882a593Smuzhiyun	def repair(self, enddepth):
2415*4882a593Smuzhiyun		# bring the depth back to 0 with additional returns
2416*4882a593Smuzhiyun		fixed = False
2417*4882a593Smuzhiyun		last = self.list[-1]
2418*4882a593Smuzhiyun		for i in reversed(range(enddepth)):
2419*4882a593Smuzhiyun			t = FTraceLine(last.time)
2420*4882a593Smuzhiyun			t.depth = i
2421*4882a593Smuzhiyun			t.freturn = True
2422*4882a593Smuzhiyun			fixed = self.addLine(t)
2423*4882a593Smuzhiyun			if fixed != 0:
2424*4882a593Smuzhiyun				self.end = last.time
2425*4882a593Smuzhiyun				return True
2426*4882a593Smuzhiyun		return False
2427*4882a593Smuzhiyun	def postProcess(self):
2428*4882a593Smuzhiyun		if len(self.list) > 0:
2429*4882a593Smuzhiyun			self.name = self.list[0].name
2430*4882a593Smuzhiyun		stack = dict()
2431*4882a593Smuzhiyun		cnt = 0
2432*4882a593Smuzhiyun		last = 0
2433*4882a593Smuzhiyun		for l in self.list:
2434*4882a593Smuzhiyun			# ftrace bug: reported duration is not reliable
2435*4882a593Smuzhiyun			# check each leaf and clip it at max possible length
2436*4882a593Smuzhiyun			if last and last.isLeaf():
2437*4882a593Smuzhiyun				if last.length > l.time - last.time:
2438*4882a593Smuzhiyun					last.length = l.time - last.time
2439*4882a593Smuzhiyun			if l.isCall():
2440*4882a593Smuzhiyun				stack[l.depth] = l
2441*4882a593Smuzhiyun				cnt += 1
2442*4882a593Smuzhiyun			elif l.isReturn():
2443*4882a593Smuzhiyun				if(l.depth not in stack):
2444*4882a593Smuzhiyun					if self.sv.verbose:
2445*4882a593Smuzhiyun						pprint('Post Process Error: Depth missing')
2446*4882a593Smuzhiyun						l.debugPrint()
2447*4882a593Smuzhiyun					return False
2448*4882a593Smuzhiyun				# calculate call length from call/return lines
2449*4882a593Smuzhiyun				cl = stack[l.depth]
2450*4882a593Smuzhiyun				cl.length = l.time - cl.time
2451*4882a593Smuzhiyun				if cl.name == self.vfname:
2452*4882a593Smuzhiyun					cl.name = l.name
2453*4882a593Smuzhiyun				stack.pop(l.depth)
2454*4882a593Smuzhiyun				l.length = 0
2455*4882a593Smuzhiyun				cnt -= 1
2456*4882a593Smuzhiyun			last = l
2457*4882a593Smuzhiyun		if(cnt == 0):
2458*4882a593Smuzhiyun			# trace caught the whole call tree
2459*4882a593Smuzhiyun			return True
2460*4882a593Smuzhiyun		elif(cnt < 0):
2461*4882a593Smuzhiyun			if self.sv.verbose:
2462*4882a593Smuzhiyun				pprint('Post Process Error: Depth is less than 0')
2463*4882a593Smuzhiyun			return False
2464*4882a593Smuzhiyun		# trace ended before call tree finished
2465*4882a593Smuzhiyun		return self.repair(cnt)
2466*4882a593Smuzhiyun	def deviceMatch(self, pid, data):
2467*4882a593Smuzhiyun		found = ''
2468*4882a593Smuzhiyun		# add the callgraph data to the device hierarchy
2469*4882a593Smuzhiyun		borderphase = {
2470*4882a593Smuzhiyun			'dpm_prepare': 'suspend_prepare',
2471*4882a593Smuzhiyun			'dpm_complete': 'resume_complete'
2472*4882a593Smuzhiyun		}
2473*4882a593Smuzhiyun		if(self.name in borderphase):
2474*4882a593Smuzhiyun			p = borderphase[self.name]
2475*4882a593Smuzhiyun			list = data.dmesg[p]['list']
2476*4882a593Smuzhiyun			for devname in list:
2477*4882a593Smuzhiyun				dev = list[devname]
2478*4882a593Smuzhiyun				if(pid == dev['pid'] and
2479*4882a593Smuzhiyun					self.start <= dev['start'] and
2480*4882a593Smuzhiyun					self.end >= dev['end']):
2481*4882a593Smuzhiyun					cg = self.slice(dev)
2482*4882a593Smuzhiyun					if cg:
2483*4882a593Smuzhiyun						dev['ftrace'] = cg
2484*4882a593Smuzhiyun					found = devname
2485*4882a593Smuzhiyun			return found
2486*4882a593Smuzhiyun		for p in data.sortedPhases():
2487*4882a593Smuzhiyun			if(data.dmesg[p]['start'] <= self.start and
2488*4882a593Smuzhiyun				self.start <= data.dmesg[p]['end']):
2489*4882a593Smuzhiyun				list = data.dmesg[p]['list']
2490*4882a593Smuzhiyun				for devname in sorted(list, key=lambda k:list[k]['start']):
2491*4882a593Smuzhiyun					dev = list[devname]
2492*4882a593Smuzhiyun					if(pid == dev['pid'] and
2493*4882a593Smuzhiyun						self.start <= dev['start'] and
2494*4882a593Smuzhiyun						self.end >= dev['end']):
2495*4882a593Smuzhiyun						dev['ftrace'] = self
2496*4882a593Smuzhiyun						found = devname
2497*4882a593Smuzhiyun						break
2498*4882a593Smuzhiyun				break
2499*4882a593Smuzhiyun		return found
2500*4882a593Smuzhiyun	def newActionFromFunction(self, data):
2501*4882a593Smuzhiyun		name = self.name
2502*4882a593Smuzhiyun		if name in ['dpm_run_callback', 'dpm_prepare', 'dpm_complete']:
2503*4882a593Smuzhiyun			return
2504*4882a593Smuzhiyun		fs = self.start
2505*4882a593Smuzhiyun		fe = self.end
2506*4882a593Smuzhiyun		if fs < data.start or fe > data.end:
2507*4882a593Smuzhiyun			return
2508*4882a593Smuzhiyun		phase = ''
2509*4882a593Smuzhiyun		for p in data.sortedPhases():
2510*4882a593Smuzhiyun			if(data.dmesg[p]['start'] <= self.start and
2511*4882a593Smuzhiyun				self.start < data.dmesg[p]['end']):
2512*4882a593Smuzhiyun				phase = p
2513*4882a593Smuzhiyun				break
2514*4882a593Smuzhiyun		if not phase:
2515*4882a593Smuzhiyun			return
2516*4882a593Smuzhiyun		out = data.newActionGlobal(name, fs, fe, -2)
2517*4882a593Smuzhiyun		if out:
2518*4882a593Smuzhiyun			phase, myname = out
2519*4882a593Smuzhiyun			data.dmesg[phase]['list'][myname]['ftrace'] = self
2520*4882a593Smuzhiyun	def debugPrint(self, info=''):
2521*4882a593Smuzhiyun		pprint('%s pid=%d [%f - %f] %.3f us' % \
2522*4882a593Smuzhiyun			(self.name, self.pid, self.start, self.end,
2523*4882a593Smuzhiyun			(self.end - self.start)*1000000))
2524*4882a593Smuzhiyun		for l in self.list:
2525*4882a593Smuzhiyun			if l.isLeaf():
2526*4882a593Smuzhiyun				pprint('%f (%02d): %s(); (%.3f us)%s' % (l.time, \
2527*4882a593Smuzhiyun					l.depth, l.name, l.length*1000000, info))
2528*4882a593Smuzhiyun			elif l.freturn:
2529*4882a593Smuzhiyun				pprint('%f (%02d): %s} (%.3f us)%s' % (l.time, \
2530*4882a593Smuzhiyun					l.depth, l.name, l.length*1000000, info))
2531*4882a593Smuzhiyun			else:
2532*4882a593Smuzhiyun				pprint('%f (%02d): %s() { (%.3f us)%s' % (l.time, \
2533*4882a593Smuzhiyun					l.depth, l.name, l.length*1000000, info))
2534*4882a593Smuzhiyun		pprint(' ')
2535*4882a593Smuzhiyun
2536*4882a593Smuzhiyunclass DevItem:
2537*4882a593Smuzhiyun	def __init__(self, test, phase, dev):
2538*4882a593Smuzhiyun		self.test = test
2539*4882a593Smuzhiyun		self.phase = phase
2540*4882a593Smuzhiyun		self.dev = dev
2541*4882a593Smuzhiyun	def isa(self, cls):
2542*4882a593Smuzhiyun		if 'htmlclass' in self.dev and cls in self.dev['htmlclass']:
2543*4882a593Smuzhiyun			return True
2544*4882a593Smuzhiyun		return False
2545*4882a593Smuzhiyun
2546*4882a593Smuzhiyun# Class: Timeline
2547*4882a593Smuzhiyun# Description:
2548*4882a593Smuzhiyun#	 A container for a device timeline which calculates
2549*4882a593Smuzhiyun#	 all the html properties to display it correctly
2550*4882a593Smuzhiyunclass Timeline:
2551*4882a593Smuzhiyun	html_tblock = '<div id="block{0}" class="tblock" style="left:{1}%;width:{2}%;"><div class="tback" style="height:{3}px"></div>\n'
2552*4882a593Smuzhiyun	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'
2553*4882a593Smuzhiyun	html_phase = '<div class="phase" style="left:{0}%;width:{1}%;top:{2}px;height:{3}px;background:{4}">{5}</div>\n'
2554*4882a593Smuzhiyun	html_phaselet = '<div id="{0}" class="phaselet" style="left:{1}%;width:{2}%;background:{3}"></div>\n'
2555*4882a593Smuzhiyun	html_legend = '<div id="p{3}" class="square" style="left:{0}%;background:{1}">&nbsp;{2}</div>\n'
2556*4882a593Smuzhiyun	def __init__(self, rowheight, scaleheight):
2557*4882a593Smuzhiyun		self.html = ''
2558*4882a593Smuzhiyun		self.height = 0  # total timeline height
2559*4882a593Smuzhiyun		self.scaleH = scaleheight # timescale (top) row height
2560*4882a593Smuzhiyun		self.rowH = rowheight     # device row height
2561*4882a593Smuzhiyun		self.bodyH = 0   # body height
2562*4882a593Smuzhiyun		self.rows = 0    # total timeline rows
2563*4882a593Smuzhiyun		self.rowlines = dict()
2564*4882a593Smuzhiyun		self.rowheight = dict()
2565*4882a593Smuzhiyun	def createHeader(self, sv, stamp):
2566*4882a593Smuzhiyun		if(not stamp['time']):
2567*4882a593Smuzhiyun			return
2568*4882a593Smuzhiyun		self.html += '<div class="version"><a href="https://01.org/pm-graph">%s v%s</a></div>' \
2569*4882a593Smuzhiyun			% (sv.title, sv.version)
2570*4882a593Smuzhiyun		if sv.logmsg and sv.testlog:
2571*4882a593Smuzhiyun			self.html += '<button id="showtest" class="logbtn btnfmt">log</button>'
2572*4882a593Smuzhiyun		if sv.dmesglog:
2573*4882a593Smuzhiyun			self.html += '<button id="showdmesg" class="logbtn btnfmt">dmesg</button>'
2574*4882a593Smuzhiyun		if sv.ftracelog:
2575*4882a593Smuzhiyun			self.html += '<button id="showftrace" class="logbtn btnfmt">ftrace</button>'
2576*4882a593Smuzhiyun		headline_stamp = '<div class="stamp">{0} {1} {2} {3}</div>\n'
2577*4882a593Smuzhiyun		self.html += headline_stamp.format(stamp['host'], stamp['kernel'],
2578*4882a593Smuzhiyun			stamp['mode'], stamp['time'])
2579*4882a593Smuzhiyun		if 'man' in stamp and 'plat' in stamp and 'cpu' in stamp and \
2580*4882a593Smuzhiyun			stamp['man'] and stamp['plat'] and stamp['cpu']:
2581*4882a593Smuzhiyun			headline_sysinfo = '<div class="stamp sysinfo">{0} {1} <i>with</i> {2}</div>\n'
2582*4882a593Smuzhiyun			self.html += headline_sysinfo.format(stamp['man'], stamp['plat'], stamp['cpu'])
2583*4882a593Smuzhiyun
2584*4882a593Smuzhiyun	# Function: getDeviceRows
2585*4882a593Smuzhiyun	# Description:
2586*4882a593Smuzhiyun	#    determine how may rows the device funcs will take
2587*4882a593Smuzhiyun	# Arguments:
2588*4882a593Smuzhiyun	#	 rawlist: the list of devices/actions for a single phase
2589*4882a593Smuzhiyun	# Output:
2590*4882a593Smuzhiyun	#	 The total number of rows needed to display this phase of the timeline
2591*4882a593Smuzhiyun	def getDeviceRows(self, rawlist):
2592*4882a593Smuzhiyun		# clear all rows and set them to undefined
2593*4882a593Smuzhiyun		sortdict = dict()
2594*4882a593Smuzhiyun		for item in rawlist:
2595*4882a593Smuzhiyun			item.row = -1
2596*4882a593Smuzhiyun			sortdict[item] = item.length
2597*4882a593Smuzhiyun		sortlist = sorted(sortdict, key=sortdict.get, reverse=True)
2598*4882a593Smuzhiyun		remaining = len(sortlist)
2599*4882a593Smuzhiyun		rowdata = dict()
2600*4882a593Smuzhiyun		row = 1
2601*4882a593Smuzhiyun		# try to pack each row with as many ranges as possible
2602*4882a593Smuzhiyun		while(remaining > 0):
2603*4882a593Smuzhiyun			if(row not in rowdata):
2604*4882a593Smuzhiyun				rowdata[row] = []
2605*4882a593Smuzhiyun			for i in sortlist:
2606*4882a593Smuzhiyun				if(i.row >= 0):
2607*4882a593Smuzhiyun					continue
2608*4882a593Smuzhiyun				s = i.time
2609*4882a593Smuzhiyun				e = i.time + i.length
2610*4882a593Smuzhiyun				valid = True
2611*4882a593Smuzhiyun				for ritem in rowdata[row]:
2612*4882a593Smuzhiyun					rs = ritem.time
2613*4882a593Smuzhiyun					re = ritem.time + ritem.length
2614*4882a593Smuzhiyun					if(not (((s <= rs) and (e <= rs)) or
2615*4882a593Smuzhiyun						((s >= re) and (e >= re)))):
2616*4882a593Smuzhiyun						valid = False
2617*4882a593Smuzhiyun						break
2618*4882a593Smuzhiyun				if(valid):
2619*4882a593Smuzhiyun					rowdata[row].append(i)
2620*4882a593Smuzhiyun					i.row = row
2621*4882a593Smuzhiyun					remaining -= 1
2622*4882a593Smuzhiyun			row += 1
2623*4882a593Smuzhiyun		return row
2624*4882a593Smuzhiyun	# Function: getPhaseRows
2625*4882a593Smuzhiyun	# Description:
2626*4882a593Smuzhiyun	#	 Organize the timeline entries into the smallest
2627*4882a593Smuzhiyun	#	 number of rows possible, with no entry overlapping
2628*4882a593Smuzhiyun	# Arguments:
2629*4882a593Smuzhiyun	#	 devlist: the list of devices/actions in a group of contiguous phases
2630*4882a593Smuzhiyun	# Output:
2631*4882a593Smuzhiyun	#	 The total number of rows needed to display this phase of the timeline
2632*4882a593Smuzhiyun	def getPhaseRows(self, devlist, row=0, sortby='length'):
2633*4882a593Smuzhiyun		# clear all rows and set them to undefined
2634*4882a593Smuzhiyun		remaining = len(devlist)
2635*4882a593Smuzhiyun		rowdata = dict()
2636*4882a593Smuzhiyun		sortdict = dict()
2637*4882a593Smuzhiyun		myphases = []
2638*4882a593Smuzhiyun		# initialize all device rows to -1 and calculate devrows
2639*4882a593Smuzhiyun		for item in devlist:
2640*4882a593Smuzhiyun			dev = item.dev
2641*4882a593Smuzhiyun			tp = (item.test, item.phase)
2642*4882a593Smuzhiyun			if tp not in myphases:
2643*4882a593Smuzhiyun				myphases.append(tp)
2644*4882a593Smuzhiyun			dev['row'] = -1
2645*4882a593Smuzhiyun			if sortby == 'start':
2646*4882a593Smuzhiyun				# sort by start 1st, then length 2nd
2647*4882a593Smuzhiyun				sortdict[item] = (-1*float(dev['start']), float(dev['end']) - float(dev['start']))
2648*4882a593Smuzhiyun			else:
2649*4882a593Smuzhiyun				# sort by length 1st, then name 2nd
2650*4882a593Smuzhiyun				sortdict[item] = (float(dev['end']) - float(dev['start']), item.dev['name'])
2651*4882a593Smuzhiyun			if 'src' in dev:
2652*4882a593Smuzhiyun				dev['devrows'] = self.getDeviceRows(dev['src'])
2653*4882a593Smuzhiyun		# sort the devlist by length so that large items graph on top
2654*4882a593Smuzhiyun		sortlist = sorted(sortdict, key=sortdict.get, reverse=True)
2655*4882a593Smuzhiyun		orderedlist = []
2656*4882a593Smuzhiyun		for item in sortlist:
2657*4882a593Smuzhiyun			if item.dev['pid'] == -2:
2658*4882a593Smuzhiyun				orderedlist.append(item)
2659*4882a593Smuzhiyun		for item in sortlist:
2660*4882a593Smuzhiyun			if item not in orderedlist:
2661*4882a593Smuzhiyun				orderedlist.append(item)
2662*4882a593Smuzhiyun		# try to pack each row with as many devices as possible
2663*4882a593Smuzhiyun		while(remaining > 0):
2664*4882a593Smuzhiyun			rowheight = 1
2665*4882a593Smuzhiyun			if(row not in rowdata):
2666*4882a593Smuzhiyun				rowdata[row] = []
2667*4882a593Smuzhiyun			for item in orderedlist:
2668*4882a593Smuzhiyun				dev = item.dev
2669*4882a593Smuzhiyun				if(dev['row'] < 0):
2670*4882a593Smuzhiyun					s = dev['start']
2671*4882a593Smuzhiyun					e = dev['end']
2672*4882a593Smuzhiyun					valid = True
2673*4882a593Smuzhiyun					for ritem in rowdata[row]:
2674*4882a593Smuzhiyun						rs = ritem.dev['start']
2675*4882a593Smuzhiyun						re = ritem.dev['end']
2676*4882a593Smuzhiyun						if(not (((s <= rs) and (e <= rs)) or
2677*4882a593Smuzhiyun							((s >= re) and (e >= re)))):
2678*4882a593Smuzhiyun							valid = False
2679*4882a593Smuzhiyun							break
2680*4882a593Smuzhiyun					if(valid):
2681*4882a593Smuzhiyun						rowdata[row].append(item)
2682*4882a593Smuzhiyun						dev['row'] = row
2683*4882a593Smuzhiyun						remaining -= 1
2684*4882a593Smuzhiyun						if 'devrows' in dev and dev['devrows'] > rowheight:
2685*4882a593Smuzhiyun							rowheight = dev['devrows']
2686*4882a593Smuzhiyun			for t, p in myphases:
2687*4882a593Smuzhiyun				if t not in self.rowlines or t not in self.rowheight:
2688*4882a593Smuzhiyun					self.rowlines[t] = dict()
2689*4882a593Smuzhiyun					self.rowheight[t] = dict()
2690*4882a593Smuzhiyun				if p not in self.rowlines[t] or p not in self.rowheight[t]:
2691*4882a593Smuzhiyun					self.rowlines[t][p] = dict()
2692*4882a593Smuzhiyun					self.rowheight[t][p] = dict()
2693*4882a593Smuzhiyun				rh = self.rowH
2694*4882a593Smuzhiyun				# section headers should use a different row height
2695*4882a593Smuzhiyun				if len(rowdata[row]) == 1 and \
2696*4882a593Smuzhiyun					'htmlclass' in rowdata[row][0].dev and \
2697*4882a593Smuzhiyun					'sec' in rowdata[row][0].dev['htmlclass']:
2698*4882a593Smuzhiyun					rh = 15
2699*4882a593Smuzhiyun				self.rowlines[t][p][row] = rowheight
2700*4882a593Smuzhiyun				self.rowheight[t][p][row] = rowheight * rh
2701*4882a593Smuzhiyun			row += 1
2702*4882a593Smuzhiyun		if(row > self.rows):
2703*4882a593Smuzhiyun			self.rows = int(row)
2704*4882a593Smuzhiyun		return row
2705*4882a593Smuzhiyun	def phaseRowHeight(self, test, phase, row):
2706*4882a593Smuzhiyun		return self.rowheight[test][phase][row]
2707*4882a593Smuzhiyun	def phaseRowTop(self, test, phase, row):
2708*4882a593Smuzhiyun		top = 0
2709*4882a593Smuzhiyun		for i in sorted(self.rowheight[test][phase]):
2710*4882a593Smuzhiyun			if i >= row:
2711*4882a593Smuzhiyun				break
2712*4882a593Smuzhiyun			top += self.rowheight[test][phase][i]
2713*4882a593Smuzhiyun		return top
2714*4882a593Smuzhiyun	def calcTotalRows(self):
2715*4882a593Smuzhiyun		# Calculate the heights and offsets for the header and rows
2716*4882a593Smuzhiyun		maxrows = 0
2717*4882a593Smuzhiyun		standardphases = []
2718*4882a593Smuzhiyun		for t in self.rowlines:
2719*4882a593Smuzhiyun			for p in self.rowlines[t]:
2720*4882a593Smuzhiyun				total = 0
2721*4882a593Smuzhiyun				for i in sorted(self.rowlines[t][p]):
2722*4882a593Smuzhiyun					total += self.rowlines[t][p][i]
2723*4882a593Smuzhiyun				if total > maxrows:
2724*4882a593Smuzhiyun					maxrows = total
2725*4882a593Smuzhiyun				if total == len(self.rowlines[t][p]):
2726*4882a593Smuzhiyun					standardphases.append((t, p))
2727*4882a593Smuzhiyun		self.height = self.scaleH + (maxrows*self.rowH)
2728*4882a593Smuzhiyun		self.bodyH = self.height - self.scaleH
2729*4882a593Smuzhiyun		# if there is 1 line per row, draw them the standard way
2730*4882a593Smuzhiyun		for t, p in standardphases:
2731*4882a593Smuzhiyun			for i in sorted(self.rowheight[t][p]):
2732*4882a593Smuzhiyun				self.rowheight[t][p][i] = float(self.bodyH)/len(self.rowlines[t][p])
2733*4882a593Smuzhiyun	def createZoomBox(self, mode='command', testcount=1):
2734*4882a593Smuzhiyun		# Create bounding box, add buttons
2735*4882a593Smuzhiyun		html_zoombox = '<center><button id="zoomin">ZOOM IN +</button><button id="zoomout">ZOOM OUT -</button><button id="zoomdef">ZOOM 1:1</button></center>\n'
2736*4882a593Smuzhiyun		html_timeline = '<div id="dmesgzoombox" class="zoombox">\n<div id="{0}" class="timeline" style="height:{1}px">\n'
2737*4882a593Smuzhiyun		html_devlist1 = '<button id="devlist1" class="devlist" style="float:left;">Device Detail{0}</button>'
2738*4882a593Smuzhiyun		html_devlist2 = '<button id="devlist2" class="devlist" style="float:right;">Device Detail2</button>\n'
2739*4882a593Smuzhiyun		if mode != 'command':
2740*4882a593Smuzhiyun			if testcount > 1:
2741*4882a593Smuzhiyun				self.html += html_devlist2
2742*4882a593Smuzhiyun				self.html += html_devlist1.format('1')
2743*4882a593Smuzhiyun			else:
2744*4882a593Smuzhiyun				self.html += html_devlist1.format('')
2745*4882a593Smuzhiyun		self.html += html_zoombox
2746*4882a593Smuzhiyun		self.html += html_timeline.format('dmesg', self.height)
2747*4882a593Smuzhiyun	# Function: createTimeScale
2748*4882a593Smuzhiyun	# Description:
2749*4882a593Smuzhiyun	#	 Create the timescale for a timeline block
2750*4882a593Smuzhiyun	# Arguments:
2751*4882a593Smuzhiyun	#	 m0: start time (mode begin)
2752*4882a593Smuzhiyun	#	 mMax: end time (mode end)
2753*4882a593Smuzhiyun	#	 tTotal: total timeline time
2754*4882a593Smuzhiyun	#	 mode: suspend or resume
2755*4882a593Smuzhiyun	# Output:
2756*4882a593Smuzhiyun	#	 The html code needed to display the time scale
2757*4882a593Smuzhiyun	def createTimeScale(self, m0, mMax, tTotal, mode):
2758*4882a593Smuzhiyun		timescale = '<div class="t" style="right:{0}%">{1}</div>\n'
2759*4882a593Smuzhiyun		rline = '<div class="t" style="left:0;border-left:1px solid black;border-right:0;">{0}</div>\n'
2760*4882a593Smuzhiyun		output = '<div class="timescale">\n'
2761*4882a593Smuzhiyun		# set scale for timeline
2762*4882a593Smuzhiyun		mTotal = mMax - m0
2763*4882a593Smuzhiyun		tS = 0.1
2764*4882a593Smuzhiyun		if(tTotal <= 0):
2765*4882a593Smuzhiyun			return output+'</div>\n'
2766*4882a593Smuzhiyun		if(tTotal > 4):
2767*4882a593Smuzhiyun			tS = 1
2768*4882a593Smuzhiyun		divTotal = int(mTotal/tS) + 1
2769*4882a593Smuzhiyun		divEdge = (mTotal - tS*(divTotal-1))*100/mTotal
2770*4882a593Smuzhiyun		for i in range(divTotal):
2771*4882a593Smuzhiyun			htmlline = ''
2772*4882a593Smuzhiyun			if(mode == 'suspend'):
2773*4882a593Smuzhiyun				pos = '%0.3f' % (100 - ((float(i)*tS*100)/mTotal) - divEdge)
2774*4882a593Smuzhiyun				val = '%0.fms' % (float(i-divTotal+1)*tS*1000)
2775*4882a593Smuzhiyun				if(i == divTotal - 1):
2776*4882a593Smuzhiyun					val = mode
2777*4882a593Smuzhiyun				htmlline = timescale.format(pos, val)
2778*4882a593Smuzhiyun			else:
2779*4882a593Smuzhiyun				pos = '%0.3f' % (100 - ((float(i)*tS*100)/mTotal))
2780*4882a593Smuzhiyun				val = '%0.fms' % (float(i)*tS*1000)
2781*4882a593Smuzhiyun				htmlline = timescale.format(pos, val)
2782*4882a593Smuzhiyun				if(i == 0):
2783*4882a593Smuzhiyun					htmlline = rline.format(mode)
2784*4882a593Smuzhiyun			output += htmlline
2785*4882a593Smuzhiyun		self.html += output+'</div>\n'
2786*4882a593Smuzhiyun
2787*4882a593Smuzhiyun# Class: TestProps
2788*4882a593Smuzhiyun# Description:
2789*4882a593Smuzhiyun#	 A list of values describing the properties of these test runs
2790*4882a593Smuzhiyunclass TestProps:
2791*4882a593Smuzhiyun	stampfmt = '# [a-z]*-(?P<m>[0-9]{2})(?P<d>[0-9]{2})(?P<y>[0-9]{2})-'+\
2792*4882a593Smuzhiyun				'(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\
2793*4882a593Smuzhiyun				' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$'
2794*4882a593Smuzhiyun	wififmt    = '^# wifi *(?P<d>\S*) *(?P<s>\S*) *(?P<t>[0-9\.]+).*'
2795*4882a593Smuzhiyun	tstatfmt   = '^# turbostat (?P<t>\S*)'
2796*4882a593Smuzhiyun	testerrfmt = '^# enter_sleep_error (?P<e>.*)'
2797*4882a593Smuzhiyun	sysinfofmt = '^# sysinfo .*'
2798*4882a593Smuzhiyun	cmdlinefmt = '^# command \| (?P<cmd>.*)'
2799*4882a593Smuzhiyun	kparamsfmt = '^# kparams \| (?P<kp>.*)'
2800*4882a593Smuzhiyun	devpropfmt = '# Device Properties: .*'
2801*4882a593Smuzhiyun	pinfofmt   = '# platform-(?P<val>[a-z,A-Z,0-9]*): (?P<info>.*)'
2802*4882a593Smuzhiyun	tracertypefmt = '# tracer: (?P<t>.*)'
2803*4882a593Smuzhiyun	firmwarefmt = '# fwsuspend (?P<s>[0-9]*) fwresume (?P<r>[0-9]*)$'
2804*4882a593Smuzhiyun	procexecfmt = 'ps - (?P<ps>.*)$'
2805*4882a593Smuzhiyun	ftrace_line_fmt_fg = \
2806*4882a593Smuzhiyun		'^ *(?P<time>[0-9\.]*) *\| *(?P<cpu>[0-9]*)\)'+\
2807*4882a593Smuzhiyun		' *(?P<proc>.*)-(?P<pid>[0-9]*) *\|'+\
2808*4882a593Smuzhiyun		'[ +!#\*@$]*(?P<dur>[0-9\.]*) .*\|  (?P<msg>.*)'
2809*4882a593Smuzhiyun	ftrace_line_fmt_nop = \
2810*4882a593Smuzhiyun		' *(?P<proc>.*)-(?P<pid>[0-9]*) *\[(?P<cpu>[0-9]*)\] *'+\
2811*4882a593Smuzhiyun		'(?P<flags>\S*) *(?P<time>[0-9\.]*): *'+\
2812*4882a593Smuzhiyun		'(?P<msg>.*)'
2813*4882a593Smuzhiyun	machinesuspend = 'machine_suspend\[.*'
2814*4882a593Smuzhiyun	def __init__(self):
2815*4882a593Smuzhiyun		self.stamp = ''
2816*4882a593Smuzhiyun		self.sysinfo = ''
2817*4882a593Smuzhiyun		self.cmdline = ''
2818*4882a593Smuzhiyun		self.testerror = []
2819*4882a593Smuzhiyun		self.turbostat = []
2820*4882a593Smuzhiyun		self.wifi = []
2821*4882a593Smuzhiyun		self.fwdata = []
2822*4882a593Smuzhiyun		self.ftrace_line_fmt = self.ftrace_line_fmt_nop
2823*4882a593Smuzhiyun		self.cgformat = False
2824*4882a593Smuzhiyun		self.data = 0
2825*4882a593Smuzhiyun		self.ktemp = dict()
2826*4882a593Smuzhiyun	def setTracerType(self, tracer):
2827*4882a593Smuzhiyun		if(tracer == 'function_graph'):
2828*4882a593Smuzhiyun			self.cgformat = True
2829*4882a593Smuzhiyun			self.ftrace_line_fmt = self.ftrace_line_fmt_fg
2830*4882a593Smuzhiyun		elif(tracer == 'nop'):
2831*4882a593Smuzhiyun			self.ftrace_line_fmt = self.ftrace_line_fmt_nop
2832*4882a593Smuzhiyun		else:
2833*4882a593Smuzhiyun			doError('Invalid tracer format: [%s]' % tracer)
2834*4882a593Smuzhiyun	def stampInfo(self, line, sv):
2835*4882a593Smuzhiyun		if re.match(self.stampfmt, line):
2836*4882a593Smuzhiyun			self.stamp = line
2837*4882a593Smuzhiyun			return True
2838*4882a593Smuzhiyun		elif re.match(self.sysinfofmt, line):
2839*4882a593Smuzhiyun			self.sysinfo = line
2840*4882a593Smuzhiyun			return True
2841*4882a593Smuzhiyun		elif re.match(self.tstatfmt, line):
2842*4882a593Smuzhiyun			self.turbostat.append(line)
2843*4882a593Smuzhiyun			return True
2844*4882a593Smuzhiyun		elif re.match(self.wififmt, line):
2845*4882a593Smuzhiyun			self.wifi.append(line)
2846*4882a593Smuzhiyun			return True
2847*4882a593Smuzhiyun		elif re.match(self.testerrfmt, line):
2848*4882a593Smuzhiyun			self.testerror.append(line)
2849*4882a593Smuzhiyun			return True
2850*4882a593Smuzhiyun		elif re.match(self.firmwarefmt, line):
2851*4882a593Smuzhiyun			self.fwdata.append(line)
2852*4882a593Smuzhiyun			return True
2853*4882a593Smuzhiyun		elif(re.match(self.devpropfmt, line)):
2854*4882a593Smuzhiyun			self.parseDevprops(line, sv)
2855*4882a593Smuzhiyun			return True
2856*4882a593Smuzhiyun		elif(re.match(self.pinfofmt, line)):
2857*4882a593Smuzhiyun			self.parsePlatformInfo(line, sv)
2858*4882a593Smuzhiyun			return True
2859*4882a593Smuzhiyun		m = re.match(self.cmdlinefmt, line)
2860*4882a593Smuzhiyun		if m:
2861*4882a593Smuzhiyun			self.cmdline = m.group('cmd')
2862*4882a593Smuzhiyun			return True
2863*4882a593Smuzhiyun		m = re.match(self.tracertypefmt, line)
2864*4882a593Smuzhiyun		if(m):
2865*4882a593Smuzhiyun			self.setTracerType(m.group('t'))
2866*4882a593Smuzhiyun			return True
2867*4882a593Smuzhiyun		return False
2868*4882a593Smuzhiyun	def parseStamp(self, data, sv):
2869*4882a593Smuzhiyun		# global test data
2870*4882a593Smuzhiyun		m = re.match(self.stampfmt, self.stamp)
2871*4882a593Smuzhiyun		if not self.stamp or not m:
2872*4882a593Smuzhiyun			doError('data does not include the expected stamp')
2873*4882a593Smuzhiyun		data.stamp = {'time': '', 'host': '', 'mode': ''}
2874*4882a593Smuzhiyun		dt = datetime(int(m.group('y'))+2000, int(m.group('m')),
2875*4882a593Smuzhiyun			int(m.group('d')), int(m.group('H')), int(m.group('M')),
2876*4882a593Smuzhiyun			int(m.group('S')))
2877*4882a593Smuzhiyun		data.stamp['time'] = dt.strftime('%B %d %Y, %I:%M:%S %p')
2878*4882a593Smuzhiyun		data.stamp['host'] = m.group('host')
2879*4882a593Smuzhiyun		data.stamp['mode'] = m.group('mode')
2880*4882a593Smuzhiyun		data.stamp['kernel'] = m.group('kernel')
2881*4882a593Smuzhiyun		if re.match(self.sysinfofmt, self.sysinfo):
2882*4882a593Smuzhiyun			for f in self.sysinfo.split('|'):
2883*4882a593Smuzhiyun				if '#' in f:
2884*4882a593Smuzhiyun					continue
2885*4882a593Smuzhiyun				tmp = f.strip().split(':', 1)
2886*4882a593Smuzhiyun				key = tmp[0]
2887*4882a593Smuzhiyun				val = tmp[1]
2888*4882a593Smuzhiyun				data.stamp[key] = val
2889*4882a593Smuzhiyun		sv.hostname = data.stamp['host']
2890*4882a593Smuzhiyun		sv.suspendmode = data.stamp['mode']
2891*4882a593Smuzhiyun		if sv.suspendmode == 'freeze':
2892*4882a593Smuzhiyun			self.machinesuspend = 'timekeeping_freeze\[.*'
2893*4882a593Smuzhiyun		else:
2894*4882a593Smuzhiyun			self.machinesuspend = 'machine_suspend\[.*'
2895*4882a593Smuzhiyun		if sv.suspendmode == 'command' and sv.ftracefile != '':
2896*4882a593Smuzhiyun			modes = ['on', 'freeze', 'standby', 'mem', 'disk']
2897*4882a593Smuzhiyun			fp = sv.openlog(sv.ftracefile, 'r')
2898*4882a593Smuzhiyun			for line in fp:
2899*4882a593Smuzhiyun				m = re.match('.* machine_suspend\[(?P<mode>.*)\]', line)
2900*4882a593Smuzhiyun				if m and m.group('mode') in ['1', '2', '3', '4']:
2901*4882a593Smuzhiyun					sv.suspendmode = modes[int(m.group('mode'))]
2902*4882a593Smuzhiyun					data.stamp['mode'] = sv.suspendmode
2903*4882a593Smuzhiyun					break
2904*4882a593Smuzhiyun			fp.close()
2905*4882a593Smuzhiyun		sv.cmdline = self.cmdline
2906*4882a593Smuzhiyun		if not sv.stamp:
2907*4882a593Smuzhiyun			sv.stamp = data.stamp
2908*4882a593Smuzhiyun		# firmware data
2909*4882a593Smuzhiyun		if sv.suspendmode == 'mem' and len(self.fwdata) > data.testnumber:
2910*4882a593Smuzhiyun			m = re.match(self.firmwarefmt, self.fwdata[data.testnumber])
2911*4882a593Smuzhiyun			if m:
2912*4882a593Smuzhiyun				data.fwSuspend, data.fwResume = int(m.group('s')), int(m.group('r'))
2913*4882a593Smuzhiyun				if(data.fwSuspend > 0 or data.fwResume > 0):
2914*4882a593Smuzhiyun					data.fwValid = True
2915*4882a593Smuzhiyun		# turbostat data
2916*4882a593Smuzhiyun		if len(self.turbostat) > data.testnumber:
2917*4882a593Smuzhiyun			m = re.match(self.tstatfmt, self.turbostat[data.testnumber])
2918*4882a593Smuzhiyun			if m:
2919*4882a593Smuzhiyun				data.turbostat = m.group('t')
2920*4882a593Smuzhiyun		# wifi data
2921*4882a593Smuzhiyun		if len(self.wifi) > data.testnumber:
2922*4882a593Smuzhiyun			m = re.match(self.wififmt, self.wifi[data.testnumber])
2923*4882a593Smuzhiyun			if m:
2924*4882a593Smuzhiyun				data.wifi = {'dev': m.group('d'), 'stat': m.group('s'),
2925*4882a593Smuzhiyun					'time': float(m.group('t'))}
2926*4882a593Smuzhiyun				data.stamp['wifi'] = m.group('d')
2927*4882a593Smuzhiyun		# sleep mode enter errors
2928*4882a593Smuzhiyun		if len(self.testerror) > data.testnumber:
2929*4882a593Smuzhiyun			m = re.match(self.testerrfmt, self.testerror[data.testnumber])
2930*4882a593Smuzhiyun			if m:
2931*4882a593Smuzhiyun				data.enterfail = m.group('e')
2932*4882a593Smuzhiyun	def devprops(self, data):
2933*4882a593Smuzhiyun		props = dict()
2934*4882a593Smuzhiyun		devlist = data.split(';')
2935*4882a593Smuzhiyun		for dev in devlist:
2936*4882a593Smuzhiyun			f = dev.split(',')
2937*4882a593Smuzhiyun			if len(f) < 3:
2938*4882a593Smuzhiyun				continue
2939*4882a593Smuzhiyun			dev = f[0]
2940*4882a593Smuzhiyun			props[dev] = DevProps()
2941*4882a593Smuzhiyun			props[dev].altname = f[1]
2942*4882a593Smuzhiyun			if int(f[2]):
2943*4882a593Smuzhiyun				props[dev].isasync = True
2944*4882a593Smuzhiyun			else:
2945*4882a593Smuzhiyun				props[dev].isasync = False
2946*4882a593Smuzhiyun		return props
2947*4882a593Smuzhiyun	def parseDevprops(self, line, sv):
2948*4882a593Smuzhiyun		idx = line.index(': ') + 2
2949*4882a593Smuzhiyun		if idx >= len(line):
2950*4882a593Smuzhiyun			return
2951*4882a593Smuzhiyun		props = self.devprops(line[idx:])
2952*4882a593Smuzhiyun		if sv.suspendmode == 'command' and 'testcommandstring' in props:
2953*4882a593Smuzhiyun			sv.testcommand = props['testcommandstring'].altname
2954*4882a593Smuzhiyun		sv.devprops = props
2955*4882a593Smuzhiyun	def parsePlatformInfo(self, line, sv):
2956*4882a593Smuzhiyun		m = re.match(self.pinfofmt, line)
2957*4882a593Smuzhiyun		if not m:
2958*4882a593Smuzhiyun			return
2959*4882a593Smuzhiyun		name, info = m.group('val'), m.group('info')
2960*4882a593Smuzhiyun		if name == 'devinfo':
2961*4882a593Smuzhiyun			sv.devprops = self.devprops(sv.b64unzip(info))
2962*4882a593Smuzhiyun			return
2963*4882a593Smuzhiyun		elif name == 'testcmd':
2964*4882a593Smuzhiyun			sv.testcommand = info
2965*4882a593Smuzhiyun			return
2966*4882a593Smuzhiyun		field = info.split('|')
2967*4882a593Smuzhiyun		if len(field) < 2:
2968*4882a593Smuzhiyun			return
2969*4882a593Smuzhiyun		cmdline = field[0].strip()
2970*4882a593Smuzhiyun		output = sv.b64unzip(field[1].strip())
2971*4882a593Smuzhiyun		sv.platinfo.append([name, cmdline, output])
2972*4882a593Smuzhiyun
2973*4882a593Smuzhiyun# Class: TestRun
2974*4882a593Smuzhiyun# Description:
2975*4882a593Smuzhiyun#	 A container for a suspend/resume test run. This is necessary as
2976*4882a593Smuzhiyun#	 there could be more than one, and they need to be separate.
2977*4882a593Smuzhiyunclass TestRun:
2978*4882a593Smuzhiyun	def __init__(self, dataobj):
2979*4882a593Smuzhiyun		self.data = dataobj
2980*4882a593Smuzhiyun		self.ftemp = dict()
2981*4882a593Smuzhiyun		self.ttemp = dict()
2982*4882a593Smuzhiyun
2983*4882a593Smuzhiyunclass ProcessMonitor:
2984*4882a593Smuzhiyun	def __init__(self):
2985*4882a593Smuzhiyun		self.proclist = dict()
2986*4882a593Smuzhiyun		self.running = False
2987*4882a593Smuzhiyun	def procstat(self):
2988*4882a593Smuzhiyun		c = ['cat /proc/[1-9]*/stat 2>/dev/null']
2989*4882a593Smuzhiyun		process = Popen(c, shell=True, stdout=PIPE)
2990*4882a593Smuzhiyun		running = dict()
2991*4882a593Smuzhiyun		for line in process.stdout:
2992*4882a593Smuzhiyun			data = ascii(line).split()
2993*4882a593Smuzhiyun			pid = data[0]
2994*4882a593Smuzhiyun			name = re.sub('[()]', '', data[1])
2995*4882a593Smuzhiyun			user = int(data[13])
2996*4882a593Smuzhiyun			kern = int(data[14])
2997*4882a593Smuzhiyun			kjiff = ujiff = 0
2998*4882a593Smuzhiyun			if pid not in self.proclist:
2999*4882a593Smuzhiyun				self.proclist[pid] = {'name' : name, 'user' : user, 'kern' : kern}
3000*4882a593Smuzhiyun			else:
3001*4882a593Smuzhiyun				val = self.proclist[pid]
3002*4882a593Smuzhiyun				ujiff = user - val['user']
3003*4882a593Smuzhiyun				kjiff = kern - val['kern']
3004*4882a593Smuzhiyun				val['user'] = user
3005*4882a593Smuzhiyun				val['kern'] = kern
3006*4882a593Smuzhiyun			if ujiff > 0 or kjiff > 0:
3007*4882a593Smuzhiyun				running[pid] = ujiff + kjiff
3008*4882a593Smuzhiyun		process.wait()
3009*4882a593Smuzhiyun		out = ''
3010*4882a593Smuzhiyun		for pid in running:
3011*4882a593Smuzhiyun			jiffies = running[pid]
3012*4882a593Smuzhiyun			val = self.proclist[pid]
3013*4882a593Smuzhiyun			if out:
3014*4882a593Smuzhiyun				out += ','
3015*4882a593Smuzhiyun			out += '%s-%s %d' % (val['name'], pid, jiffies)
3016*4882a593Smuzhiyun		return 'ps - '+out
3017*4882a593Smuzhiyun	def processMonitor(self, tid):
3018*4882a593Smuzhiyun		while self.running:
3019*4882a593Smuzhiyun			out = self.procstat()
3020*4882a593Smuzhiyun			if out:
3021*4882a593Smuzhiyun				sysvals.fsetVal(out, 'trace_marker')
3022*4882a593Smuzhiyun	def start(self):
3023*4882a593Smuzhiyun		self.thread = Thread(target=self.processMonitor, args=(0,))
3024*4882a593Smuzhiyun		self.running = True
3025*4882a593Smuzhiyun		self.thread.start()
3026*4882a593Smuzhiyun	def stop(self):
3027*4882a593Smuzhiyun		self.running = False
3028*4882a593Smuzhiyun
3029*4882a593Smuzhiyun# ----------------- FUNCTIONS --------------------
3030*4882a593Smuzhiyun
3031*4882a593Smuzhiyun# Function: doesTraceLogHaveTraceEvents
3032*4882a593Smuzhiyun# Description:
3033*4882a593Smuzhiyun#	 Quickly determine if the ftrace log has all of the trace events,
3034*4882a593Smuzhiyun#	 markers, and/or kprobes required for primary parsing.
3035*4882a593Smuzhiyundef doesTraceLogHaveTraceEvents():
3036*4882a593Smuzhiyun	kpcheck = ['_cal: (', '_ret: (']
3037*4882a593Smuzhiyun	techeck = ['suspend_resume', 'device_pm_callback']
3038*4882a593Smuzhiyun	tmcheck = ['SUSPEND START', 'RESUME COMPLETE']
3039*4882a593Smuzhiyun	sysvals.usekprobes = False
3040*4882a593Smuzhiyun	fp = sysvals.openlog(sysvals.ftracefile, 'r')
3041*4882a593Smuzhiyun	for line in fp:
3042*4882a593Smuzhiyun		# check for kprobes
3043*4882a593Smuzhiyun		if not sysvals.usekprobes:
3044*4882a593Smuzhiyun			for i in kpcheck:
3045*4882a593Smuzhiyun				if i in line:
3046*4882a593Smuzhiyun					sysvals.usekprobes = True
3047*4882a593Smuzhiyun		# check for all necessary trace events
3048*4882a593Smuzhiyun		check = techeck[:]
3049*4882a593Smuzhiyun		for i in techeck:
3050*4882a593Smuzhiyun			if i in line:
3051*4882a593Smuzhiyun				check.remove(i)
3052*4882a593Smuzhiyun		techeck = check
3053*4882a593Smuzhiyun		# check for all necessary trace markers
3054*4882a593Smuzhiyun		check = tmcheck[:]
3055*4882a593Smuzhiyun		for i in tmcheck:
3056*4882a593Smuzhiyun			if i in line:
3057*4882a593Smuzhiyun				check.remove(i)
3058*4882a593Smuzhiyun		tmcheck = check
3059*4882a593Smuzhiyun	fp.close()
3060*4882a593Smuzhiyun	sysvals.usetraceevents = True if len(techeck) < 2 else False
3061*4882a593Smuzhiyun	sysvals.usetracemarkers = True if len(tmcheck) == 0 else False
3062*4882a593Smuzhiyun
3063*4882a593Smuzhiyun# Function: appendIncompleteTraceLog
3064*4882a593Smuzhiyun# Description:
3065*4882a593Smuzhiyun#	 [deprecated for kernel 3.15 or newer]
3066*4882a593Smuzhiyun#	 Adds callgraph data which lacks trace event data. This is only
3067*4882a593Smuzhiyun#	 for timelines generated from 3.15 or older
3068*4882a593Smuzhiyun# Arguments:
3069*4882a593Smuzhiyun#	 testruns: the array of Data objects obtained from parseKernelLog
3070*4882a593Smuzhiyundef appendIncompleteTraceLog(testruns):
3071*4882a593Smuzhiyun	# create TestRun vessels for ftrace parsing
3072*4882a593Smuzhiyun	testcnt = len(testruns)
3073*4882a593Smuzhiyun	testidx = 0
3074*4882a593Smuzhiyun	testrun = []
3075*4882a593Smuzhiyun	for data in testruns:
3076*4882a593Smuzhiyun		testrun.append(TestRun(data))
3077*4882a593Smuzhiyun
3078*4882a593Smuzhiyun	# extract the callgraph and traceevent data
3079*4882a593Smuzhiyun	sysvals.vprint('Analyzing the ftrace data (%s)...' % \
3080*4882a593Smuzhiyun		os.path.basename(sysvals.ftracefile))
3081*4882a593Smuzhiyun	tp = TestProps()
3082*4882a593Smuzhiyun	tf = sysvals.openlog(sysvals.ftracefile, 'r')
3083*4882a593Smuzhiyun	data = 0
3084*4882a593Smuzhiyun	for line in tf:
3085*4882a593Smuzhiyun		# remove any latent carriage returns
3086*4882a593Smuzhiyun		line = line.replace('\r\n', '')
3087*4882a593Smuzhiyun		if tp.stampInfo(line, sysvals):
3088*4882a593Smuzhiyun			continue
3089*4882a593Smuzhiyun		# parse only valid lines, if this is not one move on
3090*4882a593Smuzhiyun		m = re.match(tp.ftrace_line_fmt, line)
3091*4882a593Smuzhiyun		if(not m):
3092*4882a593Smuzhiyun			continue
3093*4882a593Smuzhiyun		# gather the basic message data from the line
3094*4882a593Smuzhiyun		m_time = m.group('time')
3095*4882a593Smuzhiyun		m_pid = m.group('pid')
3096*4882a593Smuzhiyun		m_msg = m.group('msg')
3097*4882a593Smuzhiyun		if(tp.cgformat):
3098*4882a593Smuzhiyun			m_param3 = m.group('dur')
3099*4882a593Smuzhiyun		else:
3100*4882a593Smuzhiyun			m_param3 = 'traceevent'
3101*4882a593Smuzhiyun		if(m_time and m_pid and m_msg):
3102*4882a593Smuzhiyun			t = FTraceLine(m_time, m_msg, m_param3)
3103*4882a593Smuzhiyun			pid = int(m_pid)
3104*4882a593Smuzhiyun		else:
3105*4882a593Smuzhiyun			continue
3106*4882a593Smuzhiyun		# the line should be a call, return, or event
3107*4882a593Smuzhiyun		if(not t.fcall and not t.freturn and not t.fevent):
3108*4882a593Smuzhiyun			continue
3109*4882a593Smuzhiyun		# look for the suspend start marker
3110*4882a593Smuzhiyun		if(t.startMarker()):
3111*4882a593Smuzhiyun			data = testrun[testidx].data
3112*4882a593Smuzhiyun			tp.parseStamp(data, sysvals)
3113*4882a593Smuzhiyun			data.setStart(t.time, t.name)
3114*4882a593Smuzhiyun			continue
3115*4882a593Smuzhiyun		if(not data):
3116*4882a593Smuzhiyun			continue
3117*4882a593Smuzhiyun		# find the end of resume
3118*4882a593Smuzhiyun		if(t.endMarker()):
3119*4882a593Smuzhiyun			data.setEnd(t.time, t.name)
3120*4882a593Smuzhiyun			testidx += 1
3121*4882a593Smuzhiyun			if(testidx >= testcnt):
3122*4882a593Smuzhiyun				break
3123*4882a593Smuzhiyun			continue
3124*4882a593Smuzhiyun		# trace event processing
3125*4882a593Smuzhiyun		if(t.fevent):
3126*4882a593Smuzhiyun			continue
3127*4882a593Smuzhiyun		# call/return processing
3128*4882a593Smuzhiyun		elif sysvals.usecallgraph:
3129*4882a593Smuzhiyun			# create a callgraph object for the data
3130*4882a593Smuzhiyun			if(pid not in testrun[testidx].ftemp):
3131*4882a593Smuzhiyun				testrun[testidx].ftemp[pid] = []
3132*4882a593Smuzhiyun				testrun[testidx].ftemp[pid].append(FTraceCallGraph(pid, sysvals))
3133*4882a593Smuzhiyun			# when the call is finished, see which device matches it
3134*4882a593Smuzhiyun			cg = testrun[testidx].ftemp[pid][-1]
3135*4882a593Smuzhiyun			res = cg.addLine(t)
3136*4882a593Smuzhiyun			if(res != 0):
3137*4882a593Smuzhiyun				testrun[testidx].ftemp[pid].append(FTraceCallGraph(pid, sysvals))
3138*4882a593Smuzhiyun			if(res == -1):
3139*4882a593Smuzhiyun				testrun[testidx].ftemp[pid][-1].addLine(t)
3140*4882a593Smuzhiyun	tf.close()
3141*4882a593Smuzhiyun
3142*4882a593Smuzhiyun	for test in testrun:
3143*4882a593Smuzhiyun		# add the callgraph data to the device hierarchy
3144*4882a593Smuzhiyun		for pid in test.ftemp:
3145*4882a593Smuzhiyun			for cg in test.ftemp[pid]:
3146*4882a593Smuzhiyun				if len(cg.list) < 1 or cg.invalid or (cg.end - cg.start == 0):
3147*4882a593Smuzhiyun					continue
3148*4882a593Smuzhiyun				if(not cg.postProcess()):
3149*4882a593Smuzhiyun					id = 'task %s cpu %s' % (pid, m.group('cpu'))
3150*4882a593Smuzhiyun					sysvals.vprint('Sanity check failed for '+\
3151*4882a593Smuzhiyun						id+', ignoring this callback')
3152*4882a593Smuzhiyun					continue
3153*4882a593Smuzhiyun				callstart = cg.start
3154*4882a593Smuzhiyun				callend = cg.end
3155*4882a593Smuzhiyun				for p in test.data.sortedPhases():
3156*4882a593Smuzhiyun					if(test.data.dmesg[p]['start'] <= callstart and
3157*4882a593Smuzhiyun						callstart <= test.data.dmesg[p]['end']):
3158*4882a593Smuzhiyun						list = test.data.dmesg[p]['list']
3159*4882a593Smuzhiyun						for devname in list:
3160*4882a593Smuzhiyun							dev = list[devname]
3161*4882a593Smuzhiyun							if(pid == dev['pid'] and
3162*4882a593Smuzhiyun								callstart <= dev['start'] and
3163*4882a593Smuzhiyun								callend >= dev['end']):
3164*4882a593Smuzhiyun								dev['ftrace'] = cg
3165*4882a593Smuzhiyun						break
3166*4882a593Smuzhiyun
3167*4882a593Smuzhiyun# Function: parseTraceLog
3168*4882a593Smuzhiyun# Description:
3169*4882a593Smuzhiyun#	 Analyze an ftrace log output file generated from this app during
3170*4882a593Smuzhiyun#	 the execution phase. Used when the ftrace log is the primary data source
3171*4882a593Smuzhiyun#	 and includes the suspend_resume and device_pm_callback trace events
3172*4882a593Smuzhiyun#	 The ftrace filename is taken from sysvals
3173*4882a593Smuzhiyun# Output:
3174*4882a593Smuzhiyun#	 An array of Data objects
3175*4882a593Smuzhiyundef parseTraceLog(live=False):
3176*4882a593Smuzhiyun	sysvals.vprint('Analyzing the ftrace data (%s)...' % \
3177*4882a593Smuzhiyun		os.path.basename(sysvals.ftracefile))
3178*4882a593Smuzhiyun	if(os.path.exists(sysvals.ftracefile) == False):
3179*4882a593Smuzhiyun		doError('%s does not exist' % sysvals.ftracefile)
3180*4882a593Smuzhiyun	if not live:
3181*4882a593Smuzhiyun		sysvals.setupAllKprobes()
3182*4882a593Smuzhiyun	ksuscalls = ['ksys_sync', 'pm_prepare_console']
3183*4882a593Smuzhiyun	krescalls = ['pm_restore_console']
3184*4882a593Smuzhiyun	tracewatch = ['irq_wakeup']
3185*4882a593Smuzhiyun	if sysvals.usekprobes:
3186*4882a593Smuzhiyun		tracewatch += ['sync_filesystems', 'freeze_processes', 'syscore_suspend',
3187*4882a593Smuzhiyun			'syscore_resume', 'resume_console', 'thaw_processes', 'CPU_ON',
3188*4882a593Smuzhiyun			'CPU_OFF', 'acpi_suspend']
3189*4882a593Smuzhiyun
3190*4882a593Smuzhiyun	# extract the callgraph and traceevent data
3191*4882a593Smuzhiyun	s2idle_enter = hwsus = False
3192*4882a593Smuzhiyun	tp = TestProps()
3193*4882a593Smuzhiyun	testruns, testdata = [], []
3194*4882a593Smuzhiyun	testrun, data, limbo = 0, 0, True
3195*4882a593Smuzhiyun	tf = sysvals.openlog(sysvals.ftracefile, 'r')
3196*4882a593Smuzhiyun	phase = 'suspend_prepare'
3197*4882a593Smuzhiyun	for line in tf:
3198*4882a593Smuzhiyun		# remove any latent carriage returns
3199*4882a593Smuzhiyun		line = line.replace('\r\n', '')
3200*4882a593Smuzhiyun		if tp.stampInfo(line, sysvals):
3201*4882a593Smuzhiyun			continue
3202*4882a593Smuzhiyun		# ignore all other commented lines
3203*4882a593Smuzhiyun		if line[0] == '#':
3204*4882a593Smuzhiyun			continue
3205*4882a593Smuzhiyun		# ftrace line: parse only valid lines
3206*4882a593Smuzhiyun		m = re.match(tp.ftrace_line_fmt, line)
3207*4882a593Smuzhiyun		if(not m):
3208*4882a593Smuzhiyun			continue
3209*4882a593Smuzhiyun		# gather the basic message data from the line
3210*4882a593Smuzhiyun		m_time = m.group('time')
3211*4882a593Smuzhiyun		m_proc = m.group('proc')
3212*4882a593Smuzhiyun		m_pid = m.group('pid')
3213*4882a593Smuzhiyun		m_msg = m.group('msg')
3214*4882a593Smuzhiyun		if(tp.cgformat):
3215*4882a593Smuzhiyun			m_param3 = m.group('dur')
3216*4882a593Smuzhiyun		else:
3217*4882a593Smuzhiyun			m_param3 = 'traceevent'
3218*4882a593Smuzhiyun		if(m_time and m_pid and m_msg):
3219*4882a593Smuzhiyun			t = FTraceLine(m_time, m_msg, m_param3)
3220*4882a593Smuzhiyun			pid = int(m_pid)
3221*4882a593Smuzhiyun		else:
3222*4882a593Smuzhiyun			continue
3223*4882a593Smuzhiyun		# the line should be a call, return, or event
3224*4882a593Smuzhiyun		if(not t.fcall and not t.freturn and not t.fevent):
3225*4882a593Smuzhiyun			continue
3226*4882a593Smuzhiyun		# find the start of suspend
3227*4882a593Smuzhiyun		if(t.startMarker()):
3228*4882a593Smuzhiyun			data, limbo = Data(len(testdata)), False
3229*4882a593Smuzhiyun			testdata.append(data)
3230*4882a593Smuzhiyun			testrun = TestRun(data)
3231*4882a593Smuzhiyun			testruns.append(testrun)
3232*4882a593Smuzhiyun			tp.parseStamp(data, sysvals)
3233*4882a593Smuzhiyun			data.setStart(t.time, t.name)
3234*4882a593Smuzhiyun			data.first_suspend_prepare = True
3235*4882a593Smuzhiyun			phase = data.setPhase('suspend_prepare', t.time, True)
3236*4882a593Smuzhiyun			continue
3237*4882a593Smuzhiyun		if(not data or limbo):
3238*4882a593Smuzhiyun			continue
3239*4882a593Smuzhiyun		# process cpu exec line
3240*4882a593Smuzhiyun		if t.type == 'tracing_mark_write':
3241*4882a593Smuzhiyun			m = re.match(tp.procexecfmt, t.name)
3242*4882a593Smuzhiyun			if(m):
3243*4882a593Smuzhiyun				proclist = dict()
3244*4882a593Smuzhiyun				for ps in m.group('ps').split(','):
3245*4882a593Smuzhiyun					val = ps.split()
3246*4882a593Smuzhiyun					if not val:
3247*4882a593Smuzhiyun						continue
3248*4882a593Smuzhiyun					name = val[0].replace('--', '-')
3249*4882a593Smuzhiyun					proclist[name] = int(val[1])
3250*4882a593Smuzhiyun				data.pstl[t.time] = proclist
3251*4882a593Smuzhiyun				continue
3252*4882a593Smuzhiyun		# find the end of resume
3253*4882a593Smuzhiyun		if(t.endMarker()):
3254*4882a593Smuzhiyun			if data.tKernRes == 0:
3255*4882a593Smuzhiyun				data.tKernRes = t.time
3256*4882a593Smuzhiyun			data.handleEndMarker(t.time, t.name)
3257*4882a593Smuzhiyun			if(not sysvals.usetracemarkers):
3258*4882a593Smuzhiyun				# no trace markers? then quit and be sure to finish recording
3259*4882a593Smuzhiyun				# the event we used to trigger resume end
3260*4882a593Smuzhiyun				if('thaw_processes' in testrun.ttemp and len(testrun.ttemp['thaw_processes']) > 0):
3261*4882a593Smuzhiyun					# if an entry exists, assume this is its end
3262*4882a593Smuzhiyun					testrun.ttemp['thaw_processes'][-1]['end'] = t.time
3263*4882a593Smuzhiyun			limbo = True
3264*4882a593Smuzhiyun			continue
3265*4882a593Smuzhiyun		# trace event processing
3266*4882a593Smuzhiyun		if(t.fevent):
3267*4882a593Smuzhiyun			if(t.type == 'suspend_resume'):
3268*4882a593Smuzhiyun				# suspend_resume trace events have two types, begin and end
3269*4882a593Smuzhiyun				if(re.match('(?P<name>.*) begin$', t.name)):
3270*4882a593Smuzhiyun					isbegin = True
3271*4882a593Smuzhiyun				elif(re.match('(?P<name>.*) end$', t.name)):
3272*4882a593Smuzhiyun					isbegin = False
3273*4882a593Smuzhiyun				else:
3274*4882a593Smuzhiyun					continue
3275*4882a593Smuzhiyun				if '[' in t.name:
3276*4882a593Smuzhiyun					m = re.match('(?P<name>.*)\[.*', t.name)
3277*4882a593Smuzhiyun				else:
3278*4882a593Smuzhiyun					m = re.match('(?P<name>.*) .*', t.name)
3279*4882a593Smuzhiyun				name = m.group('name')
3280*4882a593Smuzhiyun				# ignore these events
3281*4882a593Smuzhiyun				if(name.split('[')[0] in tracewatch):
3282*4882a593Smuzhiyun					continue
3283*4882a593Smuzhiyun				# -- phase changes --
3284*4882a593Smuzhiyun				# start of kernel suspend
3285*4882a593Smuzhiyun				if(re.match('suspend_enter\[.*', t.name)):
3286*4882a593Smuzhiyun					if(isbegin and data.tKernSus == 0):
3287*4882a593Smuzhiyun						data.tKernSus = t.time
3288*4882a593Smuzhiyun					continue
3289*4882a593Smuzhiyun				# suspend_prepare start
3290*4882a593Smuzhiyun				elif(re.match('dpm_prepare\[.*', t.name)):
3291*4882a593Smuzhiyun					if isbegin and data.first_suspend_prepare:
3292*4882a593Smuzhiyun						data.first_suspend_prepare = False
3293*4882a593Smuzhiyun						if data.tKernSus == 0:
3294*4882a593Smuzhiyun							data.tKernSus = t.time
3295*4882a593Smuzhiyun						continue
3296*4882a593Smuzhiyun					phase = data.setPhase('suspend_prepare', t.time, isbegin)
3297*4882a593Smuzhiyun					continue
3298*4882a593Smuzhiyun				# suspend start
3299*4882a593Smuzhiyun				elif(re.match('dpm_suspend\[.*', t.name)):
3300*4882a593Smuzhiyun					phase = data.setPhase('suspend', t.time, isbegin)
3301*4882a593Smuzhiyun					continue
3302*4882a593Smuzhiyun				# suspend_late start
3303*4882a593Smuzhiyun				elif(re.match('dpm_suspend_late\[.*', t.name)):
3304*4882a593Smuzhiyun					phase = data.setPhase('suspend_late', t.time, isbegin)
3305*4882a593Smuzhiyun					continue
3306*4882a593Smuzhiyun				# suspend_noirq start
3307*4882a593Smuzhiyun				elif(re.match('dpm_suspend_noirq\[.*', t.name)):
3308*4882a593Smuzhiyun					phase = data.setPhase('suspend_noirq', t.time, isbegin)
3309*4882a593Smuzhiyun					continue
3310*4882a593Smuzhiyun				# suspend_machine/resume_machine
3311*4882a593Smuzhiyun				elif(re.match(tp.machinesuspend, t.name)):
3312*4882a593Smuzhiyun					lp = data.lastPhase()
3313*4882a593Smuzhiyun					if(isbegin):
3314*4882a593Smuzhiyun						hwsus = True
3315*4882a593Smuzhiyun						if lp.startswith('resume_machine'):
3316*4882a593Smuzhiyun							# trim out s2idle loops, track time trying to freeze
3317*4882a593Smuzhiyun							llp = data.lastPhase(2)
3318*4882a593Smuzhiyun							if llp.startswith('suspend_machine'):
3319*4882a593Smuzhiyun								if 'trying' not in data.dmesg[llp]:
3320*4882a593Smuzhiyun									data.dmesg[llp]['trying'] = 0
3321*4882a593Smuzhiyun								data.dmesg[llp]['trying'] += \
3322*4882a593Smuzhiyun									t.time - data.dmesg[lp]['start']
3323*4882a593Smuzhiyun							data.currphase = ''
3324*4882a593Smuzhiyun							del data.dmesg[lp]
3325*4882a593Smuzhiyun							continue
3326*4882a593Smuzhiyun						phase = data.setPhase('suspend_machine', data.dmesg[lp]['end'], True)
3327*4882a593Smuzhiyun						data.setPhase(phase, t.time, False)
3328*4882a593Smuzhiyun						if data.tSuspended == 0:
3329*4882a593Smuzhiyun							data.tSuspended = t.time
3330*4882a593Smuzhiyun					else:
3331*4882a593Smuzhiyun						if lp.startswith('resume_machine'):
3332*4882a593Smuzhiyun							data.dmesg[lp]['end'] = t.time
3333*4882a593Smuzhiyun							continue
3334*4882a593Smuzhiyun						phase = data.setPhase('resume_machine', t.time, True)
3335*4882a593Smuzhiyun						if(sysvals.suspendmode in ['mem', 'disk']):
3336*4882a593Smuzhiyun							susp = phase.replace('resume', 'suspend')
3337*4882a593Smuzhiyun							if susp in data.dmesg:
3338*4882a593Smuzhiyun								data.dmesg[susp]['end'] = t.time
3339*4882a593Smuzhiyun							data.tSuspended = t.time
3340*4882a593Smuzhiyun						data.tResumed = t.time
3341*4882a593Smuzhiyun					continue
3342*4882a593Smuzhiyun				# resume_noirq start
3343*4882a593Smuzhiyun				elif(re.match('dpm_resume_noirq\[.*', t.name)):
3344*4882a593Smuzhiyun					phase = data.setPhase('resume_noirq', t.time, isbegin)
3345*4882a593Smuzhiyun					continue
3346*4882a593Smuzhiyun				# resume_early start
3347*4882a593Smuzhiyun				elif(re.match('dpm_resume_early\[.*', t.name)):
3348*4882a593Smuzhiyun					phase = data.setPhase('resume_early', t.time, isbegin)
3349*4882a593Smuzhiyun					continue
3350*4882a593Smuzhiyun				# resume start
3351*4882a593Smuzhiyun				elif(re.match('dpm_resume\[.*', t.name)):
3352*4882a593Smuzhiyun					phase = data.setPhase('resume', t.time, isbegin)
3353*4882a593Smuzhiyun					continue
3354*4882a593Smuzhiyun				# resume complete start
3355*4882a593Smuzhiyun				elif(re.match('dpm_complete\[.*', t.name)):
3356*4882a593Smuzhiyun					phase = data.setPhase('resume_complete', t.time, isbegin)
3357*4882a593Smuzhiyun					continue
3358*4882a593Smuzhiyun				# skip trace events inside devices calls
3359*4882a593Smuzhiyun				if(not data.isTraceEventOutsideDeviceCalls(pid, t.time)):
3360*4882a593Smuzhiyun					continue
3361*4882a593Smuzhiyun				# global events (outside device calls) are graphed
3362*4882a593Smuzhiyun				if(name not in testrun.ttemp):
3363*4882a593Smuzhiyun					testrun.ttemp[name] = []
3364*4882a593Smuzhiyun				# special handling for s2idle_enter
3365*4882a593Smuzhiyun				if name == 'machine_suspend':
3366*4882a593Smuzhiyun					if hwsus:
3367*4882a593Smuzhiyun						s2idle_enter = hwsus = False
3368*4882a593Smuzhiyun					elif s2idle_enter and not isbegin:
3369*4882a593Smuzhiyun						if(len(testrun.ttemp[name]) > 0):
3370*4882a593Smuzhiyun							testrun.ttemp[name][-1]['end'] = t.time
3371*4882a593Smuzhiyun							testrun.ttemp[name][-1]['loop'] += 1
3372*4882a593Smuzhiyun					elif not s2idle_enter and isbegin:
3373*4882a593Smuzhiyun						s2idle_enter = True
3374*4882a593Smuzhiyun						testrun.ttemp[name].append({'begin': t.time,
3375*4882a593Smuzhiyun							'end': t.time, 'pid': pid, 'loop': 0})
3376*4882a593Smuzhiyun					continue
3377*4882a593Smuzhiyun				if(isbegin):
3378*4882a593Smuzhiyun					# create a new list entry
3379*4882a593Smuzhiyun					testrun.ttemp[name].append(\
3380*4882a593Smuzhiyun						{'begin': t.time, 'end': t.time, 'pid': pid})
3381*4882a593Smuzhiyun				else:
3382*4882a593Smuzhiyun					if(len(testrun.ttemp[name]) > 0):
3383*4882a593Smuzhiyun						# if an entry exists, assume this is its end
3384*4882a593Smuzhiyun						testrun.ttemp[name][-1]['end'] = t.time
3385*4882a593Smuzhiyun			# device callback start
3386*4882a593Smuzhiyun			elif(t.type == 'device_pm_callback_start'):
3387*4882a593Smuzhiyun				if phase not in data.dmesg:
3388*4882a593Smuzhiyun					continue
3389*4882a593Smuzhiyun				m = re.match('(?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*',\
3390*4882a593Smuzhiyun					t.name);
3391*4882a593Smuzhiyun				if(not m):
3392*4882a593Smuzhiyun					continue
3393*4882a593Smuzhiyun				drv = m.group('drv')
3394*4882a593Smuzhiyun				n = m.group('d')
3395*4882a593Smuzhiyun				p = m.group('p')
3396*4882a593Smuzhiyun				if(n and p):
3397*4882a593Smuzhiyun					data.newAction(phase, n, pid, p, t.time, -1, drv)
3398*4882a593Smuzhiyun					if pid not in data.devpids:
3399*4882a593Smuzhiyun						data.devpids.append(pid)
3400*4882a593Smuzhiyun			# device callback finish
3401*4882a593Smuzhiyun			elif(t.type == 'device_pm_callback_end'):
3402*4882a593Smuzhiyun				if phase not in data.dmesg:
3403*4882a593Smuzhiyun					continue
3404*4882a593Smuzhiyun				m = re.match('(?P<drv>.*) (?P<d>.*), err.*', t.name);
3405*4882a593Smuzhiyun				if(not m):
3406*4882a593Smuzhiyun					continue
3407*4882a593Smuzhiyun				n = m.group('d')
3408*4882a593Smuzhiyun				dev = data.findDevice(phase, n)
3409*4882a593Smuzhiyun				if dev:
3410*4882a593Smuzhiyun					dev['length'] = t.time - dev['start']
3411*4882a593Smuzhiyun					dev['end'] = t.time
3412*4882a593Smuzhiyun		# kprobe event processing
3413*4882a593Smuzhiyun		elif(t.fkprobe):
3414*4882a593Smuzhiyun			kprobename = t.type
3415*4882a593Smuzhiyun			kprobedata = t.name
3416*4882a593Smuzhiyun			key = (kprobename, pid)
3417*4882a593Smuzhiyun			# displayname is generated from kprobe data
3418*4882a593Smuzhiyun			displayname = ''
3419*4882a593Smuzhiyun			if(t.fcall):
3420*4882a593Smuzhiyun				displayname = sysvals.kprobeDisplayName(kprobename, kprobedata)
3421*4882a593Smuzhiyun				if not displayname:
3422*4882a593Smuzhiyun					continue
3423*4882a593Smuzhiyun				if(key not in tp.ktemp):
3424*4882a593Smuzhiyun					tp.ktemp[key] = []
3425*4882a593Smuzhiyun				tp.ktemp[key].append({
3426*4882a593Smuzhiyun					'pid': pid,
3427*4882a593Smuzhiyun					'begin': t.time,
3428*4882a593Smuzhiyun					'end': -1,
3429*4882a593Smuzhiyun					'name': displayname,
3430*4882a593Smuzhiyun					'cdata': kprobedata,
3431*4882a593Smuzhiyun					'proc': m_proc,
3432*4882a593Smuzhiyun				})
3433*4882a593Smuzhiyun				# start of kernel resume
3434*4882a593Smuzhiyun				if(data.tKernSus == 0 and phase == 'suspend_prepare' \
3435*4882a593Smuzhiyun					and kprobename in ksuscalls):
3436*4882a593Smuzhiyun					data.tKernSus = t.time
3437*4882a593Smuzhiyun			elif(t.freturn):
3438*4882a593Smuzhiyun				if(key not in tp.ktemp) or len(tp.ktemp[key]) < 1:
3439*4882a593Smuzhiyun					continue
3440*4882a593Smuzhiyun				e = next((x for x in reversed(tp.ktemp[key]) if x['end'] < 0), 0)
3441*4882a593Smuzhiyun				if not e:
3442*4882a593Smuzhiyun					continue
3443*4882a593Smuzhiyun				e['end'] = t.time
3444*4882a593Smuzhiyun				e['rdata'] = kprobedata
3445*4882a593Smuzhiyun				# end of kernel resume
3446*4882a593Smuzhiyun				if(phase != 'suspend_prepare' and kprobename in krescalls):
3447*4882a593Smuzhiyun					if phase in data.dmesg:
3448*4882a593Smuzhiyun						data.dmesg[phase]['end'] = t.time
3449*4882a593Smuzhiyun					data.tKernRes = t.time
3450*4882a593Smuzhiyun
3451*4882a593Smuzhiyun		# callgraph processing
3452*4882a593Smuzhiyun		elif sysvals.usecallgraph:
3453*4882a593Smuzhiyun			# create a callgraph object for the data
3454*4882a593Smuzhiyun			key = (m_proc, pid)
3455*4882a593Smuzhiyun			if(key not in testrun.ftemp):
3456*4882a593Smuzhiyun				testrun.ftemp[key] = []
3457*4882a593Smuzhiyun				testrun.ftemp[key].append(FTraceCallGraph(pid, sysvals))
3458*4882a593Smuzhiyun			# when the call is finished, see which device matches it
3459*4882a593Smuzhiyun			cg = testrun.ftemp[key][-1]
3460*4882a593Smuzhiyun			res = cg.addLine(t)
3461*4882a593Smuzhiyun			if(res != 0):
3462*4882a593Smuzhiyun				testrun.ftemp[key].append(FTraceCallGraph(pid, sysvals))
3463*4882a593Smuzhiyun			if(res == -1):
3464*4882a593Smuzhiyun				testrun.ftemp[key][-1].addLine(t)
3465*4882a593Smuzhiyun	tf.close()
3466*4882a593Smuzhiyun	if len(testdata) < 1:
3467*4882a593Smuzhiyun		sysvals.vprint('WARNING: ftrace start marker is missing')
3468*4882a593Smuzhiyun	if data and not data.devicegroups:
3469*4882a593Smuzhiyun		sysvals.vprint('WARNING: ftrace end marker is missing')
3470*4882a593Smuzhiyun		data.handleEndMarker(t.time, t.name)
3471*4882a593Smuzhiyun
3472*4882a593Smuzhiyun	if sysvals.suspendmode == 'command':
3473*4882a593Smuzhiyun		for test in testruns:
3474*4882a593Smuzhiyun			for p in test.data.sortedPhases():
3475*4882a593Smuzhiyun				if p == 'suspend_prepare':
3476*4882a593Smuzhiyun					test.data.dmesg[p]['start'] = test.data.start
3477*4882a593Smuzhiyun					test.data.dmesg[p]['end'] = test.data.end
3478*4882a593Smuzhiyun				else:
3479*4882a593Smuzhiyun					test.data.dmesg[p]['start'] = test.data.end
3480*4882a593Smuzhiyun					test.data.dmesg[p]['end'] = test.data.end
3481*4882a593Smuzhiyun			test.data.tSuspended = test.data.end
3482*4882a593Smuzhiyun			test.data.tResumed = test.data.end
3483*4882a593Smuzhiyun			test.data.fwValid = False
3484*4882a593Smuzhiyun
3485*4882a593Smuzhiyun	# dev source and procmon events can be unreadable with mixed phase height
3486*4882a593Smuzhiyun	if sysvals.usedevsrc or sysvals.useprocmon:
3487*4882a593Smuzhiyun		sysvals.mixedphaseheight = False
3488*4882a593Smuzhiyun
3489*4882a593Smuzhiyun	# expand phase boundaries so there are no gaps
3490*4882a593Smuzhiyun	for data in testdata:
3491*4882a593Smuzhiyun		lp = data.sortedPhases()[0]
3492*4882a593Smuzhiyun		for p in data.sortedPhases():
3493*4882a593Smuzhiyun			if(p != lp and not ('machine' in p and 'machine' in lp)):
3494*4882a593Smuzhiyun				data.dmesg[lp]['end'] = data.dmesg[p]['start']
3495*4882a593Smuzhiyun			lp = p
3496*4882a593Smuzhiyun
3497*4882a593Smuzhiyun	for i in range(len(testruns)):
3498*4882a593Smuzhiyun		test = testruns[i]
3499*4882a593Smuzhiyun		data = test.data
3500*4882a593Smuzhiyun		# find the total time range for this test (begin, end)
3501*4882a593Smuzhiyun		tlb, tle = data.start, data.end
3502*4882a593Smuzhiyun		if i < len(testruns) - 1:
3503*4882a593Smuzhiyun			tle = testruns[i+1].data.start
3504*4882a593Smuzhiyun		# add the process usage data to the timeline
3505*4882a593Smuzhiyun		if sysvals.useprocmon:
3506*4882a593Smuzhiyun			data.createProcessUsageEvents()
3507*4882a593Smuzhiyun		# add the traceevent data to the device hierarchy
3508*4882a593Smuzhiyun		if(sysvals.usetraceevents):
3509*4882a593Smuzhiyun			# add actual trace funcs
3510*4882a593Smuzhiyun			for name in sorted(test.ttemp):
3511*4882a593Smuzhiyun				for event in test.ttemp[name]:
3512*4882a593Smuzhiyun					if event['end'] - event['begin'] <= 0:
3513*4882a593Smuzhiyun						continue
3514*4882a593Smuzhiyun					title = name
3515*4882a593Smuzhiyun					if name == 'machine_suspend' and 'loop' in event:
3516*4882a593Smuzhiyun						title = 's2idle_enter_%dx' % event['loop']
3517*4882a593Smuzhiyun					data.newActionGlobal(title, event['begin'], event['end'], event['pid'])
3518*4882a593Smuzhiyun			# add the kprobe based virtual tracefuncs as actual devices
3519*4882a593Smuzhiyun			for key in sorted(tp.ktemp):
3520*4882a593Smuzhiyun				name, pid = key
3521*4882a593Smuzhiyun				if name not in sysvals.tracefuncs:
3522*4882a593Smuzhiyun					continue
3523*4882a593Smuzhiyun				if pid not in data.devpids:
3524*4882a593Smuzhiyun					data.devpids.append(pid)
3525*4882a593Smuzhiyun				for e in tp.ktemp[key]:
3526*4882a593Smuzhiyun					kb, ke = e['begin'], e['end']
3527*4882a593Smuzhiyun					if ke - kb < 0.000001 or tlb > kb or tle <= kb:
3528*4882a593Smuzhiyun						continue
3529*4882a593Smuzhiyun					color = sysvals.kprobeColor(name)
3530*4882a593Smuzhiyun					data.newActionGlobal(e['name'], kb, ke, pid, color)
3531*4882a593Smuzhiyun			# add config base kprobes and dev kprobes
3532*4882a593Smuzhiyun			if sysvals.usedevsrc:
3533*4882a593Smuzhiyun				for key in sorted(tp.ktemp):
3534*4882a593Smuzhiyun					name, pid = key
3535*4882a593Smuzhiyun					if name in sysvals.tracefuncs or name not in sysvals.dev_tracefuncs:
3536*4882a593Smuzhiyun						continue
3537*4882a593Smuzhiyun					for e in tp.ktemp[key]:
3538*4882a593Smuzhiyun						kb, ke = e['begin'], e['end']
3539*4882a593Smuzhiyun						if ke - kb < 0.000001 or tlb > kb or tle <= kb:
3540*4882a593Smuzhiyun							continue
3541*4882a593Smuzhiyun						data.addDeviceFunctionCall(e['name'], name, e['proc'], pid, kb,
3542*4882a593Smuzhiyun							ke, e['cdata'], e['rdata'])
3543*4882a593Smuzhiyun		if sysvals.usecallgraph:
3544*4882a593Smuzhiyun			# add the callgraph data to the device hierarchy
3545*4882a593Smuzhiyun			sortlist = dict()
3546*4882a593Smuzhiyun			for key in sorted(test.ftemp):
3547*4882a593Smuzhiyun				proc, pid = key
3548*4882a593Smuzhiyun				for cg in test.ftemp[key]:
3549*4882a593Smuzhiyun					if len(cg.list) < 1 or cg.invalid or (cg.end - cg.start == 0):
3550*4882a593Smuzhiyun						continue
3551*4882a593Smuzhiyun					if(not cg.postProcess()):
3552*4882a593Smuzhiyun						id = 'task %s' % (pid)
3553*4882a593Smuzhiyun						sysvals.vprint('Sanity check failed for '+\
3554*4882a593Smuzhiyun							id+', ignoring this callback')
3555*4882a593Smuzhiyun						continue
3556*4882a593Smuzhiyun					# match cg data to devices
3557*4882a593Smuzhiyun					devname = ''
3558*4882a593Smuzhiyun					if sysvals.suspendmode != 'command':
3559*4882a593Smuzhiyun						devname = cg.deviceMatch(pid, data)
3560*4882a593Smuzhiyun					if not devname:
3561*4882a593Smuzhiyun						sortkey = '%f%f%d' % (cg.start, cg.end, pid)
3562*4882a593Smuzhiyun						sortlist[sortkey] = cg
3563*4882a593Smuzhiyun					elif len(cg.list) > 1000000 and cg.name != sysvals.ftopfunc:
3564*4882a593Smuzhiyun						sysvals.vprint('WARNING: the callgraph for %s is massive (%d lines)' %\
3565*4882a593Smuzhiyun							(devname, len(cg.list)))
3566*4882a593Smuzhiyun			# create blocks for orphan cg data
3567*4882a593Smuzhiyun			for sortkey in sorted(sortlist):
3568*4882a593Smuzhiyun				cg = sortlist[sortkey]
3569*4882a593Smuzhiyun				name = cg.name
3570*4882a593Smuzhiyun				if sysvals.isCallgraphFunc(name):
3571*4882a593Smuzhiyun					sysvals.vprint('Callgraph found for task %d: %.3fms, %s' % (cg.pid, (cg.end - cg.start)*1000, name))
3572*4882a593Smuzhiyun					cg.newActionFromFunction(data)
3573*4882a593Smuzhiyun	if sysvals.suspendmode == 'command':
3574*4882a593Smuzhiyun		return (testdata, '')
3575*4882a593Smuzhiyun
3576*4882a593Smuzhiyun	# fill in any missing phases
3577*4882a593Smuzhiyun	error = []
3578*4882a593Smuzhiyun	for data in testdata:
3579*4882a593Smuzhiyun		tn = '' if len(testdata) == 1 else ('%d' % (data.testnumber + 1))
3580*4882a593Smuzhiyun		terr = ''
3581*4882a593Smuzhiyun		phasedef = data.phasedef
3582*4882a593Smuzhiyun		lp = 'suspend_prepare'
3583*4882a593Smuzhiyun		for p in sorted(phasedef, key=lambda k:phasedef[k]['order']):
3584*4882a593Smuzhiyun			if p not in data.dmesg:
3585*4882a593Smuzhiyun				if not terr:
3586*4882a593Smuzhiyun					ph = p if 'machine' in p else lp
3587*4882a593Smuzhiyun					terr = '%s%s failed in %s phase' % (sysvals.suspendmode, tn, ph)
3588*4882a593Smuzhiyun					pprint('TEST%s FAILED: %s' % (tn, terr))
3589*4882a593Smuzhiyun					error.append(terr)
3590*4882a593Smuzhiyun					if data.tSuspended == 0:
3591*4882a593Smuzhiyun						data.tSuspended = data.dmesg[lp]['end']
3592*4882a593Smuzhiyun					if data.tResumed == 0:
3593*4882a593Smuzhiyun						data.tResumed = data.dmesg[lp]['end']
3594*4882a593Smuzhiyun					data.fwValid = False
3595*4882a593Smuzhiyun				sysvals.vprint('WARNING: phase "%s" is missing!' % p)
3596*4882a593Smuzhiyun			lp = p
3597*4882a593Smuzhiyun		if not terr and 'dev' in data.wifi and data.wifi['stat'] == 'timeout':
3598*4882a593Smuzhiyun			terr = '%s%s failed in wifi_resume <i>(%s %.0fs timeout)</i>' % \
3599*4882a593Smuzhiyun				(sysvals.suspendmode, tn, data.wifi['dev'], data.wifi['time'])
3600*4882a593Smuzhiyun			error.append(terr)
3601*4882a593Smuzhiyun		if not terr and data.enterfail:
3602*4882a593Smuzhiyun			pprint('test%s FAILED: enter %s failed with %s' % (tn, sysvals.suspendmode, data.enterfail))
3603*4882a593Smuzhiyun			terr = 'test%s failed to enter %s mode' % (tn, sysvals.suspendmode)
3604*4882a593Smuzhiyun			error.append(terr)
3605*4882a593Smuzhiyun		if data.tSuspended == 0:
3606*4882a593Smuzhiyun			data.tSuspended = data.tKernRes
3607*4882a593Smuzhiyun		if data.tResumed == 0:
3608*4882a593Smuzhiyun			data.tResumed = data.tSuspended
3609*4882a593Smuzhiyun
3610*4882a593Smuzhiyun		if(len(sysvals.devicefilter) > 0):
3611*4882a593Smuzhiyun			data.deviceFilter(sysvals.devicefilter)
3612*4882a593Smuzhiyun		data.fixupInitcallsThatDidntReturn()
3613*4882a593Smuzhiyun		if sysvals.usedevsrc:
3614*4882a593Smuzhiyun			data.optimizeDevSrc()
3615*4882a593Smuzhiyun
3616*4882a593Smuzhiyun	# x2: merge any overlapping devices between test runs
3617*4882a593Smuzhiyun	if sysvals.usedevsrc and len(testdata) > 1:
3618*4882a593Smuzhiyun		tc = len(testdata)
3619*4882a593Smuzhiyun		for i in range(tc - 1):
3620*4882a593Smuzhiyun			devlist = testdata[i].overflowDevices()
3621*4882a593Smuzhiyun			for j in range(i + 1, tc):
3622*4882a593Smuzhiyun				testdata[j].mergeOverlapDevices(devlist)
3623*4882a593Smuzhiyun		testdata[0].stitchTouchingThreads(testdata[1:])
3624*4882a593Smuzhiyun	return (testdata, ', '.join(error))
3625*4882a593Smuzhiyun
3626*4882a593Smuzhiyun# Function: loadKernelLog
3627*4882a593Smuzhiyun# Description:
3628*4882a593Smuzhiyun#	 [deprecated for kernel 3.15.0 or newer]
3629*4882a593Smuzhiyun#	 load the dmesg file into memory and fix up any ordering issues
3630*4882a593Smuzhiyun#	 The dmesg filename is taken from sysvals
3631*4882a593Smuzhiyun# Output:
3632*4882a593Smuzhiyun#	 An array of empty Data objects with only their dmesgtext attributes set
3633*4882a593Smuzhiyundef loadKernelLog():
3634*4882a593Smuzhiyun	sysvals.vprint('Analyzing the dmesg data (%s)...' % \
3635*4882a593Smuzhiyun		os.path.basename(sysvals.dmesgfile))
3636*4882a593Smuzhiyun	if(os.path.exists(sysvals.dmesgfile) == False):
3637*4882a593Smuzhiyun		doError('%s does not exist' % sysvals.dmesgfile)
3638*4882a593Smuzhiyun
3639*4882a593Smuzhiyun	# there can be multiple test runs in a single file
3640*4882a593Smuzhiyun	tp = TestProps()
3641*4882a593Smuzhiyun	tp.stamp = datetime.now().strftime('# suspend-%m%d%y-%H%M%S localhost mem unknown')
3642*4882a593Smuzhiyun	testruns = []
3643*4882a593Smuzhiyun	data = 0
3644*4882a593Smuzhiyun	lf = sysvals.openlog(sysvals.dmesgfile, 'r')
3645*4882a593Smuzhiyun	for line in lf:
3646*4882a593Smuzhiyun		line = line.replace('\r\n', '')
3647*4882a593Smuzhiyun		idx = line.find('[')
3648*4882a593Smuzhiyun		if idx > 1:
3649*4882a593Smuzhiyun			line = line[idx:]
3650*4882a593Smuzhiyun		if tp.stampInfo(line, sysvals):
3651*4882a593Smuzhiyun			continue
3652*4882a593Smuzhiyun		m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
3653*4882a593Smuzhiyun		if(not m):
3654*4882a593Smuzhiyun			continue
3655*4882a593Smuzhiyun		msg = m.group("msg")
3656*4882a593Smuzhiyun		if(re.match('PM: Syncing filesystems.*', msg)):
3657*4882a593Smuzhiyun			if(data):
3658*4882a593Smuzhiyun				testruns.append(data)
3659*4882a593Smuzhiyun			data = Data(len(testruns))
3660*4882a593Smuzhiyun			tp.parseStamp(data, sysvals)
3661*4882a593Smuzhiyun		if(not data):
3662*4882a593Smuzhiyun			continue
3663*4882a593Smuzhiyun		m = re.match('.* *(?P<k>[0-9]\.[0-9]{2}\.[0-9]-.*) .*', msg)
3664*4882a593Smuzhiyun		if(m):
3665*4882a593Smuzhiyun			sysvals.stamp['kernel'] = m.group('k')
3666*4882a593Smuzhiyun		m = re.match('PM: Preparing system for (?P<m>.*) sleep', msg)
3667*4882a593Smuzhiyun		if(m):
3668*4882a593Smuzhiyun			sysvals.stamp['mode'] = sysvals.suspendmode = m.group('m')
3669*4882a593Smuzhiyun		data.dmesgtext.append(line)
3670*4882a593Smuzhiyun	lf.close()
3671*4882a593Smuzhiyun
3672*4882a593Smuzhiyun	if data:
3673*4882a593Smuzhiyun		testruns.append(data)
3674*4882a593Smuzhiyun	if len(testruns) < 1:
3675*4882a593Smuzhiyun		doError('dmesg log has no suspend/resume data: %s' \
3676*4882a593Smuzhiyun			% sysvals.dmesgfile)
3677*4882a593Smuzhiyun
3678*4882a593Smuzhiyun	# fix lines with same timestamp/function with the call and return swapped
3679*4882a593Smuzhiyun	for data in testruns:
3680*4882a593Smuzhiyun		last = ''
3681*4882a593Smuzhiyun		for line in data.dmesgtext:
3682*4882a593Smuzhiyun			mc = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) calling  '+\
3683*4882a593Smuzhiyun				'(?P<f>.*)\+ @ .*, parent: .*', line)
3684*4882a593Smuzhiyun			mr = re.match('.*(\[ *)(?P<t>[0-9\.]*)(\]) call '+\
3685*4882a593Smuzhiyun				'(?P<f>.*)\+ returned .* after (?P<dt>.*) usecs', last)
3686*4882a593Smuzhiyun			if(mc and mr and (mc.group('t') == mr.group('t')) and
3687*4882a593Smuzhiyun				(mc.group('f') == mr.group('f'))):
3688*4882a593Smuzhiyun				i = data.dmesgtext.index(last)
3689*4882a593Smuzhiyun				j = data.dmesgtext.index(line)
3690*4882a593Smuzhiyun				data.dmesgtext[i] = line
3691*4882a593Smuzhiyun				data.dmesgtext[j] = last
3692*4882a593Smuzhiyun			last = line
3693*4882a593Smuzhiyun	return testruns
3694*4882a593Smuzhiyun
3695*4882a593Smuzhiyun# Function: parseKernelLog
3696*4882a593Smuzhiyun# Description:
3697*4882a593Smuzhiyun#	 [deprecated for kernel 3.15.0 or newer]
3698*4882a593Smuzhiyun#	 Analyse a dmesg log output file generated from this app during
3699*4882a593Smuzhiyun#	 the execution phase. Create a set of device structures in memory
3700*4882a593Smuzhiyun#	 for subsequent formatting in the html output file
3701*4882a593Smuzhiyun#	 This call is only for legacy support on kernels where the ftrace
3702*4882a593Smuzhiyun#	 data lacks the suspend_resume or device_pm_callbacks trace events.
3703*4882a593Smuzhiyun# Arguments:
3704*4882a593Smuzhiyun#	 data: an empty Data object (with dmesgtext) obtained from loadKernelLog
3705*4882a593Smuzhiyun# Output:
3706*4882a593Smuzhiyun#	 The filled Data object
3707*4882a593Smuzhiyundef parseKernelLog(data):
3708*4882a593Smuzhiyun	phase = 'suspend_runtime'
3709*4882a593Smuzhiyun
3710*4882a593Smuzhiyun	if(data.fwValid):
3711*4882a593Smuzhiyun		sysvals.vprint('Firmware Suspend = %u ns, Firmware Resume = %u ns' % \
3712*4882a593Smuzhiyun			(data.fwSuspend, data.fwResume))
3713*4882a593Smuzhiyun
3714*4882a593Smuzhiyun	# dmesg phase match table
3715*4882a593Smuzhiyun	dm = {
3716*4882a593Smuzhiyun		'suspend_prepare': ['PM: Syncing filesystems.*'],
3717*4882a593Smuzhiyun		        'suspend': ['PM: Entering [a-z]* sleep.*', 'Suspending console.*'],
3718*4882a593Smuzhiyun		   'suspend_late': ['PM: suspend of devices complete after.*'],
3719*4882a593Smuzhiyun		  'suspend_noirq': ['PM: late suspend of devices complete after.*'],
3720*4882a593Smuzhiyun		'suspend_machine': ['PM: noirq suspend of devices complete after.*'],
3721*4882a593Smuzhiyun		 'resume_machine': ['ACPI: Low-level resume complete.*'],
3722*4882a593Smuzhiyun		   'resume_noirq': ['ACPI: Waking up from system sleep state.*'],
3723*4882a593Smuzhiyun		   'resume_early': ['PM: noirq resume of devices complete after.*'],
3724*4882a593Smuzhiyun		         'resume': ['PM: early resume of devices complete after.*'],
3725*4882a593Smuzhiyun		'resume_complete': ['PM: resume of devices complete after.*'],
3726*4882a593Smuzhiyun		    'post_resume': ['.*Restarting tasks \.\.\..*'],
3727*4882a593Smuzhiyun	}
3728*4882a593Smuzhiyun	if(sysvals.suspendmode == 'standby'):
3729*4882a593Smuzhiyun		dm['resume_machine'] = ['PM: Restoring platform NVS memory']
3730*4882a593Smuzhiyun	elif(sysvals.suspendmode == 'disk'):
3731*4882a593Smuzhiyun		dm['suspend_late'] = ['PM: freeze of devices complete after.*']
3732*4882a593Smuzhiyun		dm['suspend_noirq'] = ['PM: late freeze of devices complete after.*']
3733*4882a593Smuzhiyun		dm['suspend_machine'] = ['PM: noirq freeze of devices complete after.*']
3734*4882a593Smuzhiyun		dm['resume_machine'] = ['PM: Restoring platform NVS memory']
3735*4882a593Smuzhiyun		dm['resume_early'] = ['PM: noirq restore of devices complete after.*']
3736*4882a593Smuzhiyun		dm['resume'] = ['PM: early restore of devices complete after.*']
3737*4882a593Smuzhiyun		dm['resume_complete'] = ['PM: restore of devices complete after.*']
3738*4882a593Smuzhiyun	elif(sysvals.suspendmode == 'freeze'):
3739*4882a593Smuzhiyun		dm['resume_machine'] = ['ACPI: resume from mwait']
3740*4882a593Smuzhiyun
3741*4882a593Smuzhiyun	# action table (expected events that occur and show up in dmesg)
3742*4882a593Smuzhiyun	at = {
3743*4882a593Smuzhiyun		'sync_filesystems': {
3744*4882a593Smuzhiyun			'smsg': 'PM: Syncing filesystems.*',
3745*4882a593Smuzhiyun			'emsg': 'PM: Preparing system for mem sleep.*' },
3746*4882a593Smuzhiyun		'freeze_user_processes': {
3747*4882a593Smuzhiyun			'smsg': 'Freezing user space processes .*',
3748*4882a593Smuzhiyun			'emsg': 'Freezing remaining freezable tasks.*' },
3749*4882a593Smuzhiyun		'freeze_tasks': {
3750*4882a593Smuzhiyun			'smsg': 'Freezing remaining freezable tasks.*',
3751*4882a593Smuzhiyun			'emsg': 'PM: Entering (?P<mode>[a-z,A-Z]*) sleep.*' },
3752*4882a593Smuzhiyun		'ACPI prepare': {
3753*4882a593Smuzhiyun			'smsg': 'ACPI: Preparing to enter system sleep state.*',
3754*4882a593Smuzhiyun			'emsg': 'PM: Saving platform NVS memory.*' },
3755*4882a593Smuzhiyun		'PM vns': {
3756*4882a593Smuzhiyun			'smsg': 'PM: Saving platform NVS memory.*',
3757*4882a593Smuzhiyun			'emsg': 'Disabling non-boot CPUs .*' },
3758*4882a593Smuzhiyun	}
3759*4882a593Smuzhiyun
3760*4882a593Smuzhiyun	t0 = -1.0
3761*4882a593Smuzhiyun	cpu_start = -1.0
3762*4882a593Smuzhiyun	prevktime = -1.0
3763*4882a593Smuzhiyun	actions = dict()
3764*4882a593Smuzhiyun	for line in data.dmesgtext:
3765*4882a593Smuzhiyun		# parse each dmesg line into the time and message
3766*4882a593Smuzhiyun		m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
3767*4882a593Smuzhiyun		if(m):
3768*4882a593Smuzhiyun			val = m.group('ktime')
3769*4882a593Smuzhiyun			try:
3770*4882a593Smuzhiyun				ktime = float(val)
3771*4882a593Smuzhiyun			except:
3772*4882a593Smuzhiyun				continue
3773*4882a593Smuzhiyun			msg = m.group('msg')
3774*4882a593Smuzhiyun			# initialize data start to first line time
3775*4882a593Smuzhiyun			if t0 < 0:
3776*4882a593Smuzhiyun				data.setStart(ktime)
3777*4882a593Smuzhiyun				t0 = ktime
3778*4882a593Smuzhiyun		else:
3779*4882a593Smuzhiyun			continue
3780*4882a593Smuzhiyun
3781*4882a593Smuzhiyun		# check for a phase change line
3782*4882a593Smuzhiyun		phasechange = False
3783*4882a593Smuzhiyun		for p in dm:
3784*4882a593Smuzhiyun			for s in dm[p]:
3785*4882a593Smuzhiyun				if(re.match(s, msg)):
3786*4882a593Smuzhiyun					phasechange, phase = True, p
3787*4882a593Smuzhiyun					break
3788*4882a593Smuzhiyun
3789*4882a593Smuzhiyun		# hack for determining resume_machine end for freeze
3790*4882a593Smuzhiyun		if(not sysvals.usetraceevents and sysvals.suspendmode == 'freeze' \
3791*4882a593Smuzhiyun			and phase == 'resume_machine' and \
3792*4882a593Smuzhiyun			re.match('calling  (?P<f>.*)\+ @ .*, parent: .*', msg)):
3793*4882a593Smuzhiyun			data.setPhase(phase, ktime, False)
3794*4882a593Smuzhiyun			phase = 'resume_noirq'
3795*4882a593Smuzhiyun			data.setPhase(phase, ktime, True)
3796*4882a593Smuzhiyun
3797*4882a593Smuzhiyun		if phasechange:
3798*4882a593Smuzhiyun			if phase == 'suspend_prepare':
3799*4882a593Smuzhiyun				data.setPhase(phase, ktime, True)
3800*4882a593Smuzhiyun				data.setStart(ktime)
3801*4882a593Smuzhiyun				data.tKernSus = ktime
3802*4882a593Smuzhiyun			elif phase == 'suspend':
3803*4882a593Smuzhiyun				lp = data.lastPhase()
3804*4882a593Smuzhiyun				if lp:
3805*4882a593Smuzhiyun					data.setPhase(lp, ktime, False)
3806*4882a593Smuzhiyun				data.setPhase(phase, ktime, True)
3807*4882a593Smuzhiyun			elif phase == 'suspend_late':
3808*4882a593Smuzhiyun				lp = data.lastPhase()
3809*4882a593Smuzhiyun				if lp:
3810*4882a593Smuzhiyun					data.setPhase(lp, ktime, False)
3811*4882a593Smuzhiyun				data.setPhase(phase, ktime, True)
3812*4882a593Smuzhiyun			elif phase == 'suspend_noirq':
3813*4882a593Smuzhiyun				lp = data.lastPhase()
3814*4882a593Smuzhiyun				if lp:
3815*4882a593Smuzhiyun					data.setPhase(lp, ktime, False)
3816*4882a593Smuzhiyun				data.setPhase(phase, ktime, True)
3817*4882a593Smuzhiyun			elif phase == 'suspend_machine':
3818*4882a593Smuzhiyun				lp = data.lastPhase()
3819*4882a593Smuzhiyun				if lp:
3820*4882a593Smuzhiyun					data.setPhase(lp, ktime, False)
3821*4882a593Smuzhiyun				data.setPhase(phase, ktime, True)
3822*4882a593Smuzhiyun			elif phase == 'resume_machine':
3823*4882a593Smuzhiyun				lp = data.lastPhase()
3824*4882a593Smuzhiyun				if(sysvals.suspendmode in ['freeze', 'standby']):
3825*4882a593Smuzhiyun					data.tSuspended = prevktime
3826*4882a593Smuzhiyun					if lp:
3827*4882a593Smuzhiyun						data.setPhase(lp, prevktime, False)
3828*4882a593Smuzhiyun				else:
3829*4882a593Smuzhiyun					data.tSuspended = ktime
3830*4882a593Smuzhiyun					if lp:
3831*4882a593Smuzhiyun						data.setPhase(lp, prevktime, False)
3832*4882a593Smuzhiyun				data.tResumed = ktime
3833*4882a593Smuzhiyun				data.setPhase(phase, ktime, True)
3834*4882a593Smuzhiyun			elif phase == 'resume_noirq':
3835*4882a593Smuzhiyun				lp = data.lastPhase()
3836*4882a593Smuzhiyun				if lp:
3837*4882a593Smuzhiyun					data.setPhase(lp, ktime, False)
3838*4882a593Smuzhiyun				data.setPhase(phase, ktime, True)
3839*4882a593Smuzhiyun			elif phase == 'resume_early':
3840*4882a593Smuzhiyun				lp = data.lastPhase()
3841*4882a593Smuzhiyun				if lp:
3842*4882a593Smuzhiyun					data.setPhase(lp, ktime, False)
3843*4882a593Smuzhiyun				data.setPhase(phase, ktime, True)
3844*4882a593Smuzhiyun			elif phase == 'resume':
3845*4882a593Smuzhiyun				lp = data.lastPhase()
3846*4882a593Smuzhiyun				if lp:
3847*4882a593Smuzhiyun					data.setPhase(lp, ktime, False)
3848*4882a593Smuzhiyun				data.setPhase(phase, ktime, True)
3849*4882a593Smuzhiyun			elif phase == 'resume_complete':
3850*4882a593Smuzhiyun				lp = data.lastPhase()
3851*4882a593Smuzhiyun				if lp:
3852*4882a593Smuzhiyun					data.setPhase(lp, ktime, False)
3853*4882a593Smuzhiyun				data.setPhase(phase, ktime, True)
3854*4882a593Smuzhiyun			elif phase == 'post_resume':
3855*4882a593Smuzhiyun				lp = data.lastPhase()
3856*4882a593Smuzhiyun				if lp:
3857*4882a593Smuzhiyun					data.setPhase(lp, ktime, False)
3858*4882a593Smuzhiyun				data.setEnd(ktime)
3859*4882a593Smuzhiyun				data.tKernRes = ktime
3860*4882a593Smuzhiyun				break
3861*4882a593Smuzhiyun
3862*4882a593Smuzhiyun		# -- device callbacks --
3863*4882a593Smuzhiyun		if(phase in data.sortedPhases()):
3864*4882a593Smuzhiyun			# device init call
3865*4882a593Smuzhiyun			if(re.match('calling  (?P<f>.*)\+ @ .*, parent: .*', msg)):
3866*4882a593Smuzhiyun				sm = re.match('calling  (?P<f>.*)\+ @ '+\
3867*4882a593Smuzhiyun					'(?P<n>.*), parent: (?P<p>.*)', msg);
3868*4882a593Smuzhiyun				f = sm.group('f')
3869*4882a593Smuzhiyun				n = sm.group('n')
3870*4882a593Smuzhiyun				p = sm.group('p')
3871*4882a593Smuzhiyun				if(f and n and p):
3872*4882a593Smuzhiyun					data.newAction(phase, f, int(n), p, ktime, -1, '')
3873*4882a593Smuzhiyun			# device init return
3874*4882a593Smuzhiyun			elif(re.match('call (?P<f>.*)\+ returned .* after '+\
3875*4882a593Smuzhiyun				'(?P<t>.*) usecs', msg)):
3876*4882a593Smuzhiyun				sm = re.match('call (?P<f>.*)\+ returned .* after '+\
3877*4882a593Smuzhiyun					'(?P<t>.*) usecs(?P<a>.*)', msg);
3878*4882a593Smuzhiyun				f = sm.group('f')
3879*4882a593Smuzhiyun				t = sm.group('t')
3880*4882a593Smuzhiyun				list = data.dmesg[phase]['list']
3881*4882a593Smuzhiyun				if(f in list):
3882*4882a593Smuzhiyun					dev = list[f]
3883*4882a593Smuzhiyun					dev['length'] = int(t)
3884*4882a593Smuzhiyun					dev['end'] = ktime
3885*4882a593Smuzhiyun
3886*4882a593Smuzhiyun		# if trace events are not available, these are better than nothing
3887*4882a593Smuzhiyun		if(not sysvals.usetraceevents):
3888*4882a593Smuzhiyun			# look for known actions
3889*4882a593Smuzhiyun			for a in sorted(at):
3890*4882a593Smuzhiyun				if(re.match(at[a]['smsg'], msg)):
3891*4882a593Smuzhiyun					if(a not in actions):
3892*4882a593Smuzhiyun						actions[a] = []
3893*4882a593Smuzhiyun					actions[a].append({'begin': ktime, 'end': ktime})
3894*4882a593Smuzhiyun				if(re.match(at[a]['emsg'], msg)):
3895*4882a593Smuzhiyun					if(a in actions):
3896*4882a593Smuzhiyun						actions[a][-1]['end'] = ktime
3897*4882a593Smuzhiyun			# now look for CPU on/off events
3898*4882a593Smuzhiyun			if(re.match('Disabling non-boot CPUs .*', msg)):
3899*4882a593Smuzhiyun				# start of first cpu suspend
3900*4882a593Smuzhiyun				cpu_start = ktime
3901*4882a593Smuzhiyun			elif(re.match('Enabling non-boot CPUs .*', msg)):
3902*4882a593Smuzhiyun				# start of first cpu resume
3903*4882a593Smuzhiyun				cpu_start = ktime
3904*4882a593Smuzhiyun			elif(re.match('smpboot: CPU (?P<cpu>[0-9]*) is now offline', msg)):
3905*4882a593Smuzhiyun				# end of a cpu suspend, start of the next
3906*4882a593Smuzhiyun				m = re.match('smpboot: CPU (?P<cpu>[0-9]*) is now offline', msg)
3907*4882a593Smuzhiyun				cpu = 'CPU'+m.group('cpu')
3908*4882a593Smuzhiyun				if(cpu not in actions):
3909*4882a593Smuzhiyun					actions[cpu] = []
3910*4882a593Smuzhiyun				actions[cpu].append({'begin': cpu_start, 'end': ktime})
3911*4882a593Smuzhiyun				cpu_start = ktime
3912*4882a593Smuzhiyun			elif(re.match('CPU(?P<cpu>[0-9]*) is up', msg)):
3913*4882a593Smuzhiyun				# end of a cpu resume, start of the next
3914*4882a593Smuzhiyun				m = re.match('CPU(?P<cpu>[0-9]*) is up', msg)
3915*4882a593Smuzhiyun				cpu = 'CPU'+m.group('cpu')
3916*4882a593Smuzhiyun				if(cpu not in actions):
3917*4882a593Smuzhiyun					actions[cpu] = []
3918*4882a593Smuzhiyun				actions[cpu].append({'begin': cpu_start, 'end': ktime})
3919*4882a593Smuzhiyun				cpu_start = ktime
3920*4882a593Smuzhiyun		prevktime = ktime
3921*4882a593Smuzhiyun	data.initDevicegroups()
3922*4882a593Smuzhiyun
3923*4882a593Smuzhiyun	# fill in any missing phases
3924*4882a593Smuzhiyun	phasedef = data.phasedef
3925*4882a593Smuzhiyun	terr, lp = '', 'suspend_prepare'
3926*4882a593Smuzhiyun	for p in sorted(phasedef, key=lambda k:phasedef[k]['order']):
3927*4882a593Smuzhiyun		if p not in data.dmesg:
3928*4882a593Smuzhiyun			if not terr:
3929*4882a593Smuzhiyun				pprint('TEST FAILED: %s failed in %s phase' % (sysvals.suspendmode, lp))
3930*4882a593Smuzhiyun				terr = '%s failed in %s phase' % (sysvals.suspendmode, lp)
3931*4882a593Smuzhiyun				if data.tSuspended == 0:
3932*4882a593Smuzhiyun					data.tSuspended = data.dmesg[lp]['end']
3933*4882a593Smuzhiyun				if data.tResumed == 0:
3934*4882a593Smuzhiyun					data.tResumed = data.dmesg[lp]['end']
3935*4882a593Smuzhiyun			sysvals.vprint('WARNING: phase "%s" is missing!' % p)
3936*4882a593Smuzhiyun		lp = p
3937*4882a593Smuzhiyun	lp = data.sortedPhases()[0]
3938*4882a593Smuzhiyun	for p in data.sortedPhases():
3939*4882a593Smuzhiyun		if(p != lp and not ('machine' in p and 'machine' in lp)):
3940*4882a593Smuzhiyun			data.dmesg[lp]['end'] = data.dmesg[p]['start']
3941*4882a593Smuzhiyun		lp = p
3942*4882a593Smuzhiyun	if data.tSuspended == 0:
3943*4882a593Smuzhiyun		data.tSuspended = data.tKernRes
3944*4882a593Smuzhiyun	if data.tResumed == 0:
3945*4882a593Smuzhiyun		data.tResumed = data.tSuspended
3946*4882a593Smuzhiyun
3947*4882a593Smuzhiyun	# fill in any actions we've found
3948*4882a593Smuzhiyun	for name in sorted(actions):
3949*4882a593Smuzhiyun		for event in actions[name]:
3950*4882a593Smuzhiyun			data.newActionGlobal(name, event['begin'], event['end'])
3951*4882a593Smuzhiyun
3952*4882a593Smuzhiyun	if(len(sysvals.devicefilter) > 0):
3953*4882a593Smuzhiyun		data.deviceFilter(sysvals.devicefilter)
3954*4882a593Smuzhiyun	data.fixupInitcallsThatDidntReturn()
3955*4882a593Smuzhiyun	return True
3956*4882a593Smuzhiyun
3957*4882a593Smuzhiyundef callgraphHTML(sv, hf, num, cg, title, color, devid):
3958*4882a593Smuzhiyun	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'
3959*4882a593Smuzhiyun	html_func_start = '<article>\n<input type="checkbox" class="pf" id="f{0}" checked/><label for="f{0}">{1} {2}</label>\n'
3960*4882a593Smuzhiyun	html_func_end = '</article>\n'
3961*4882a593Smuzhiyun	html_func_leaf = '<article>{0} {1}</article>\n'
3962*4882a593Smuzhiyun
3963*4882a593Smuzhiyun	cgid = devid
3964*4882a593Smuzhiyun	if cg.id:
3965*4882a593Smuzhiyun		cgid += cg.id
3966*4882a593Smuzhiyun	cglen = (cg.end - cg.start) * 1000
3967*4882a593Smuzhiyun	if cglen < sv.mincglen:
3968*4882a593Smuzhiyun		return num
3969*4882a593Smuzhiyun
3970*4882a593Smuzhiyun	fmt = '<r>(%.3f ms @ '+sv.timeformat+' to '+sv.timeformat+')</r>'
3971*4882a593Smuzhiyun	flen = fmt % (cglen, cg.start, cg.end)
3972*4882a593Smuzhiyun	hf.write(html_func_top.format(cgid, color, num, title, flen))
3973*4882a593Smuzhiyun	num += 1
3974*4882a593Smuzhiyun	for line in cg.list:
3975*4882a593Smuzhiyun		if(line.length < 0.000000001):
3976*4882a593Smuzhiyun			flen = ''
3977*4882a593Smuzhiyun		else:
3978*4882a593Smuzhiyun			fmt = '<n>(%.3f ms @ '+sv.timeformat+')</n>'
3979*4882a593Smuzhiyun			flen = fmt % (line.length*1000, line.time)
3980*4882a593Smuzhiyun		if line.isLeaf():
3981*4882a593Smuzhiyun			hf.write(html_func_leaf.format(line.name, flen))
3982*4882a593Smuzhiyun		elif line.freturn:
3983*4882a593Smuzhiyun			hf.write(html_func_end)
3984*4882a593Smuzhiyun		else:
3985*4882a593Smuzhiyun			hf.write(html_func_start.format(num, line.name, flen))
3986*4882a593Smuzhiyun			num += 1
3987*4882a593Smuzhiyun	hf.write(html_func_end)
3988*4882a593Smuzhiyun	return num
3989*4882a593Smuzhiyun
3990*4882a593Smuzhiyundef addCallgraphs(sv, hf, data):
3991*4882a593Smuzhiyun	hf.write('<section id="callgraphs" class="callgraph">\n')
3992*4882a593Smuzhiyun	# write out the ftrace data converted to html
3993*4882a593Smuzhiyun	num = 0
3994*4882a593Smuzhiyun	for p in data.sortedPhases():
3995*4882a593Smuzhiyun		if sv.cgphase and p != sv.cgphase:
3996*4882a593Smuzhiyun			continue
3997*4882a593Smuzhiyun		list = data.dmesg[p]['list']
3998*4882a593Smuzhiyun		for d in data.sortedDevices(p):
3999*4882a593Smuzhiyun			if len(sv.cgfilter) > 0 and d not in sv.cgfilter:
4000*4882a593Smuzhiyun				continue
4001*4882a593Smuzhiyun			dev = list[d]
4002*4882a593Smuzhiyun			color = 'white'
4003*4882a593Smuzhiyun			if 'color' in data.dmesg[p]:
4004*4882a593Smuzhiyun				color = data.dmesg[p]['color']
4005*4882a593Smuzhiyun			if 'color' in dev:
4006*4882a593Smuzhiyun				color = dev['color']
4007*4882a593Smuzhiyun			name = d if '[' not in d else d.split('[')[0]
4008*4882a593Smuzhiyun			if(d in sv.devprops):
4009*4882a593Smuzhiyun				name = sv.devprops[d].altName(d)
4010*4882a593Smuzhiyun			if 'drv' in dev and dev['drv']:
4011*4882a593Smuzhiyun				name += ' {%s}' % dev['drv']
4012*4882a593Smuzhiyun			if sv.suspendmode in suspendmodename:
4013*4882a593Smuzhiyun				name += ' '+p
4014*4882a593Smuzhiyun			if('ftrace' in dev):
4015*4882a593Smuzhiyun				cg = dev['ftrace']
4016*4882a593Smuzhiyun				if cg.name == sv.ftopfunc:
4017*4882a593Smuzhiyun					name = 'top level suspend/resume call'
4018*4882a593Smuzhiyun				num = callgraphHTML(sv, hf, num, cg,
4019*4882a593Smuzhiyun					name, color, dev['id'])
4020*4882a593Smuzhiyun			if('ftraces' in dev):
4021*4882a593Smuzhiyun				for cg in dev['ftraces']:
4022*4882a593Smuzhiyun					num = callgraphHTML(sv, hf, num, cg,
4023*4882a593Smuzhiyun						name+' &rarr; '+cg.name, color, dev['id'])
4024*4882a593Smuzhiyun	hf.write('\n\n    </section>\n')
4025*4882a593Smuzhiyun
4026*4882a593Smuzhiyundef summaryCSS(title, center=True):
4027*4882a593Smuzhiyun	tdcenter = 'text-align:center;' if center else ''
4028*4882a593Smuzhiyun	out = '<!DOCTYPE html>\n<html>\n<head>\n\
4029*4882a593Smuzhiyun	<meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\
4030*4882a593Smuzhiyun	<title>'+title+'</title>\n\
4031*4882a593Smuzhiyun	<style type=\'text/css\'>\n\
4032*4882a593Smuzhiyun		.stamp {width: 100%;text-align:center;background:#888;line-height:30px;color:white;font: 25px Arial;}\n\
4033*4882a593Smuzhiyun		table {width:100%;border-collapse: collapse;border:1px solid;}\n\
4034*4882a593Smuzhiyun		th {border: 1px solid black;background:#222;color:white;}\n\
4035*4882a593Smuzhiyun		td {font: 14px "Times New Roman";'+tdcenter+'}\n\
4036*4882a593Smuzhiyun		tr.head td {border: 1px solid black;background:#aaa;}\n\
4037*4882a593Smuzhiyun		tr.alt {background-color:#ddd;}\n\
4038*4882a593Smuzhiyun		tr.notice {color:red;}\n\
4039*4882a593Smuzhiyun		.minval {background-color:#BBFFBB;}\n\
4040*4882a593Smuzhiyun		.medval {background-color:#BBBBFF;}\n\
4041*4882a593Smuzhiyun		.maxval {background-color:#FFBBBB;}\n\
4042*4882a593Smuzhiyun		.head a {color:#000;text-decoration: none;}\n\
4043*4882a593Smuzhiyun	</style>\n</head>\n<body>\n'
4044*4882a593Smuzhiyun	return out
4045*4882a593Smuzhiyun
4046*4882a593Smuzhiyun# Function: createHTMLSummarySimple
4047*4882a593Smuzhiyun# Description:
4048*4882a593Smuzhiyun#	 Create summary html file for a series of tests
4049*4882a593Smuzhiyun# Arguments:
4050*4882a593Smuzhiyun#	 testruns: array of Data objects from parseTraceLog
4051*4882a593Smuzhiyundef createHTMLSummarySimple(testruns, htmlfile, title):
4052*4882a593Smuzhiyun	# write the html header first (html head, css code, up to body start)
4053*4882a593Smuzhiyun	html = summaryCSS('Summary - SleepGraph')
4054*4882a593Smuzhiyun
4055*4882a593Smuzhiyun	# extract the test data into list
4056*4882a593Smuzhiyun	list = dict()
4057*4882a593Smuzhiyun	tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()]
4058*4882a593Smuzhiyun	iMin, iMed, iMax = [0, 0], [0, 0], [0, 0]
4059*4882a593Smuzhiyun	num = 0
4060*4882a593Smuzhiyun	useturbo = usewifi = False
4061*4882a593Smuzhiyun	lastmode = ''
4062*4882a593Smuzhiyun	cnt = dict()
4063*4882a593Smuzhiyun	for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'], v['time'])):
4064*4882a593Smuzhiyun		mode = data['mode']
4065*4882a593Smuzhiyun		if mode not in list:
4066*4882a593Smuzhiyun			list[mode] = {'data': [], 'avg': [0,0], 'min': [0,0], 'max': [0,0], 'med': [0,0]}
4067*4882a593Smuzhiyun		if lastmode and lastmode != mode and num > 0:
4068*4882a593Smuzhiyun			for i in range(2):
4069*4882a593Smuzhiyun				s = sorted(tMed[i])
4070*4882a593Smuzhiyun				list[lastmode]['med'][i] = s[int(len(s)//2)]
4071*4882a593Smuzhiyun				iMed[i] = tMed[i][list[lastmode]['med'][i]]
4072*4882a593Smuzhiyun			list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num]
4073*4882a593Smuzhiyun			list[lastmode]['min'] = tMin
4074*4882a593Smuzhiyun			list[lastmode]['max'] = tMax
4075*4882a593Smuzhiyun			list[lastmode]['idx'] = (iMin, iMed, iMax)
4076*4882a593Smuzhiyun			tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()]
4077*4882a593Smuzhiyun			iMin, iMed, iMax = [0, 0], [0, 0], [0, 0]
4078*4882a593Smuzhiyun			num = 0
4079*4882a593Smuzhiyun		pkgpc10 = syslpi = wifi = ''
4080*4882a593Smuzhiyun		if 'pkgpc10' in data and 'syslpi' in data:
4081*4882a593Smuzhiyun			pkgpc10, syslpi, useturbo = data['pkgpc10'], data['syslpi'], True
4082*4882a593Smuzhiyun		if 'wifi' in data:
4083*4882a593Smuzhiyun			wifi, usewifi = data['wifi'], True
4084*4882a593Smuzhiyun		res = data['result']
4085*4882a593Smuzhiyun		tVal = [float(data['suspend']), float(data['resume'])]
4086*4882a593Smuzhiyun		list[mode]['data'].append([data['host'], data['kernel'],
4087*4882a593Smuzhiyun			data['time'], tVal[0], tVal[1], data['url'], res,
4088*4882a593Smuzhiyun			data['issues'], data['sus_worst'], data['sus_worsttime'],
4089*4882a593Smuzhiyun			data['res_worst'], data['res_worsttime'], pkgpc10, syslpi, wifi])
4090*4882a593Smuzhiyun		idx = len(list[mode]['data']) - 1
4091*4882a593Smuzhiyun		if res.startswith('fail in'):
4092*4882a593Smuzhiyun			res = 'fail'
4093*4882a593Smuzhiyun		if res not in cnt:
4094*4882a593Smuzhiyun			cnt[res] = 1
4095*4882a593Smuzhiyun		else:
4096*4882a593Smuzhiyun			cnt[res] += 1
4097*4882a593Smuzhiyun		if res == 'pass':
4098*4882a593Smuzhiyun			for i in range(2):
4099*4882a593Smuzhiyun				tMed[i][tVal[i]] = idx
4100*4882a593Smuzhiyun				tAvg[i] += tVal[i]
4101*4882a593Smuzhiyun				if tMin[i] == 0 or tVal[i] < tMin[i]:
4102*4882a593Smuzhiyun					iMin[i] = idx
4103*4882a593Smuzhiyun					tMin[i] = tVal[i]
4104*4882a593Smuzhiyun				if tMax[i] == 0 or tVal[i] > tMax[i]:
4105*4882a593Smuzhiyun					iMax[i] = idx
4106*4882a593Smuzhiyun					tMax[i] = tVal[i]
4107*4882a593Smuzhiyun			num += 1
4108*4882a593Smuzhiyun		lastmode = mode
4109*4882a593Smuzhiyun	if lastmode and num > 0:
4110*4882a593Smuzhiyun		for i in range(2):
4111*4882a593Smuzhiyun			s = sorted(tMed[i])
4112*4882a593Smuzhiyun			list[lastmode]['med'][i] = s[int(len(s)//2)]
4113*4882a593Smuzhiyun			iMed[i] = tMed[i][list[lastmode]['med'][i]]
4114*4882a593Smuzhiyun		list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num]
4115*4882a593Smuzhiyun		list[lastmode]['min'] = tMin
4116*4882a593Smuzhiyun		list[lastmode]['max'] = tMax
4117*4882a593Smuzhiyun		list[lastmode]['idx'] = (iMin, iMed, iMax)
4118*4882a593Smuzhiyun
4119*4882a593Smuzhiyun	# group test header
4120*4882a593Smuzhiyun	desc = []
4121*4882a593Smuzhiyun	for ilk in sorted(cnt, reverse=True):
4122*4882a593Smuzhiyun		if cnt[ilk] > 0:
4123*4882a593Smuzhiyun			desc.append('%d %s' % (cnt[ilk], ilk))
4124*4882a593Smuzhiyun	html += '<div class="stamp">%s (%d tests: %s)</div>\n' % (title, len(testruns), ', '.join(desc))
4125*4882a593Smuzhiyun	th = '\t<th>{0}</th>\n'
4126*4882a593Smuzhiyun	td = '\t<td>{0}</td>\n'
4127*4882a593Smuzhiyun	tdh = '\t<td{1}>{0}</td>\n'
4128*4882a593Smuzhiyun	tdlink = '\t<td><a href="{0}">html</a></td>\n'
4129*4882a593Smuzhiyun	cols = 12
4130*4882a593Smuzhiyun	if useturbo:
4131*4882a593Smuzhiyun		cols += 2
4132*4882a593Smuzhiyun	if usewifi:
4133*4882a593Smuzhiyun		cols += 1
4134*4882a593Smuzhiyun	colspan = '%d' % cols
4135*4882a593Smuzhiyun
4136*4882a593Smuzhiyun	# table header
4137*4882a593Smuzhiyun	html += '<table>\n<tr>\n' + th.format('#') +\
4138*4882a593Smuzhiyun		th.format('Mode') + th.format('Host') + th.format('Kernel') +\
4139*4882a593Smuzhiyun		th.format('Test Time') + th.format('Result') + th.format('Issues') +\
4140*4882a593Smuzhiyun		th.format('Suspend') + th.format('Resume') +\
4141*4882a593Smuzhiyun		th.format('Worst Suspend Device') + th.format('SD Time') +\
4142*4882a593Smuzhiyun		th.format('Worst Resume Device') + th.format('RD Time')
4143*4882a593Smuzhiyun	if useturbo:
4144*4882a593Smuzhiyun		html += th.format('PkgPC10') + th.format('SysLPI')
4145*4882a593Smuzhiyun	if usewifi:
4146*4882a593Smuzhiyun		html += th.format('Wifi')
4147*4882a593Smuzhiyun	html += th.format('Detail')+'</tr>\n'
4148*4882a593Smuzhiyun	# export list into html
4149*4882a593Smuzhiyun	head = '<tr class="head"><td>{0}</td><td>{1}</td>'+\
4150*4882a593Smuzhiyun		'<td colspan='+colspan+' class="sus">Suspend Avg={2} '+\
4151*4882a593Smuzhiyun		'<span class=minval><a href="#s{10}min">Min={3}</a></span> '+\
4152*4882a593Smuzhiyun		'<span class=medval><a href="#s{10}med">Med={4}</a></span> '+\
4153*4882a593Smuzhiyun		'<span class=maxval><a href="#s{10}max">Max={5}</a></span> '+\
4154*4882a593Smuzhiyun		'Resume Avg={6} '+\
4155*4882a593Smuzhiyun		'<span class=minval><a href="#r{10}min">Min={7}</a></span> '+\
4156*4882a593Smuzhiyun		'<span class=medval><a href="#r{10}med">Med={8}</a></span> '+\
4157*4882a593Smuzhiyun		'<span class=maxval><a href="#r{10}max">Max={9}</a></span></td>'+\
4158*4882a593Smuzhiyun		'</tr>\n'
4159*4882a593Smuzhiyun	headnone = '<tr class="head"><td>{0}</td><td>{1}</td><td colspan='+\
4160*4882a593Smuzhiyun		colspan+'></td></tr>\n'
4161*4882a593Smuzhiyun	for mode in sorted(list):
4162*4882a593Smuzhiyun		# header line for each suspend mode
4163*4882a593Smuzhiyun		num = 0
4164*4882a593Smuzhiyun		tAvg, tMin, tMax, tMed = list[mode]['avg'], list[mode]['min'],\
4165*4882a593Smuzhiyun			list[mode]['max'], list[mode]['med']
4166*4882a593Smuzhiyun		count = len(list[mode]['data'])
4167*4882a593Smuzhiyun		if 'idx' in list[mode]:
4168*4882a593Smuzhiyun			iMin, iMed, iMax = list[mode]['idx']
4169*4882a593Smuzhiyun			html += head.format('%d' % count, mode.upper(),
4170*4882a593Smuzhiyun				'%.3f' % tAvg[0], '%.3f' % tMin[0], '%.3f' % tMed[0], '%.3f' % tMax[0],
4171*4882a593Smuzhiyun				'%.3f' % tAvg[1], '%.3f' % tMin[1], '%.3f' % tMed[1], '%.3f' % tMax[1],
4172*4882a593Smuzhiyun				mode.lower()
4173*4882a593Smuzhiyun			)
4174*4882a593Smuzhiyun		else:
4175*4882a593Smuzhiyun			iMin = iMed = iMax = [-1, -1, -1]
4176*4882a593Smuzhiyun			html += headnone.format('%d' % count, mode.upper())
4177*4882a593Smuzhiyun		for d in list[mode]['data']:
4178*4882a593Smuzhiyun			# row classes - alternate row color
4179*4882a593Smuzhiyun			rcls = ['alt'] if num % 2 == 1 else []
4180*4882a593Smuzhiyun			if d[6] != 'pass':
4181*4882a593Smuzhiyun				rcls.append('notice')
4182*4882a593Smuzhiyun			html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n'
4183*4882a593Smuzhiyun			# figure out if the line has sus or res highlighted
4184*4882a593Smuzhiyun			idx = list[mode]['data'].index(d)
4185*4882a593Smuzhiyun			tHigh = ['', '']
4186*4882a593Smuzhiyun			for i in range(2):
4187*4882a593Smuzhiyun				tag = 's%s' % mode if i == 0 else 'r%s' % mode
4188*4882a593Smuzhiyun				if idx == iMin[i]:
4189*4882a593Smuzhiyun					tHigh[i] = ' id="%smin" class=minval title="Minimum"' % tag
4190*4882a593Smuzhiyun				elif idx == iMax[i]:
4191*4882a593Smuzhiyun					tHigh[i] = ' id="%smax" class=maxval title="Maximum"' % tag
4192*4882a593Smuzhiyun				elif idx == iMed[i]:
4193*4882a593Smuzhiyun					tHigh[i] = ' id="%smed" class=medval title="Median"' % tag
4194*4882a593Smuzhiyun			html += td.format("%d" % (list[mode]['data'].index(d) + 1)) # row
4195*4882a593Smuzhiyun			html += td.format(mode)										# mode
4196*4882a593Smuzhiyun			html += td.format(d[0])										# host
4197*4882a593Smuzhiyun			html += td.format(d[1])										# kernel
4198*4882a593Smuzhiyun			html += td.format(d[2])										# time
4199*4882a593Smuzhiyun			html += td.format(d[6])										# result
4200*4882a593Smuzhiyun			html += td.format(d[7])										# issues
4201*4882a593Smuzhiyun			html += tdh.format('%.3f ms' % d[3], tHigh[0]) if d[3] else td.format('')	# suspend
4202*4882a593Smuzhiyun			html += tdh.format('%.3f ms' % d[4], tHigh[1]) if d[4] else td.format('')	# resume
4203*4882a593Smuzhiyun			html += td.format(d[8])										# sus_worst
4204*4882a593Smuzhiyun			html += td.format('%.3f ms' % d[9])	if d[9] else td.format('')		# sus_worst time
4205*4882a593Smuzhiyun			html += td.format(d[10])									# res_worst
4206*4882a593Smuzhiyun			html += td.format('%.3f ms' % d[11]) if d[11] else td.format('')	# res_worst time
4207*4882a593Smuzhiyun			if useturbo:
4208*4882a593Smuzhiyun				html += td.format(d[12])								# pkg_pc10
4209*4882a593Smuzhiyun				html += td.format(d[13])								# syslpi
4210*4882a593Smuzhiyun			if usewifi:
4211*4882a593Smuzhiyun				html += td.format(d[14])								# wifi
4212*4882a593Smuzhiyun			html += tdlink.format(d[5]) if d[5] else td.format('')		# url
4213*4882a593Smuzhiyun			html += '</tr>\n'
4214*4882a593Smuzhiyun			num += 1
4215*4882a593Smuzhiyun
4216*4882a593Smuzhiyun	# flush the data to file
4217*4882a593Smuzhiyun	hf = open(htmlfile, 'w')
4218*4882a593Smuzhiyun	hf.write(html+'</table>\n</body>\n</html>\n')
4219*4882a593Smuzhiyun	hf.close()
4220*4882a593Smuzhiyun
4221*4882a593Smuzhiyundef createHTMLDeviceSummary(testruns, htmlfile, title):
4222*4882a593Smuzhiyun	html = summaryCSS('Device Summary - SleepGraph', False)
4223*4882a593Smuzhiyun
4224*4882a593Smuzhiyun	# create global device list from all tests
4225*4882a593Smuzhiyun	devall = dict()
4226*4882a593Smuzhiyun	for data in testruns:
4227*4882a593Smuzhiyun		host, url, devlist = data['host'], data['url'], data['devlist']
4228*4882a593Smuzhiyun		for type in devlist:
4229*4882a593Smuzhiyun			if type not in devall:
4230*4882a593Smuzhiyun				devall[type] = dict()
4231*4882a593Smuzhiyun			mdevlist, devlist = devall[type], data['devlist'][type]
4232*4882a593Smuzhiyun			for name in devlist:
4233*4882a593Smuzhiyun				length = devlist[name]
4234*4882a593Smuzhiyun				if name not in mdevlist:
4235*4882a593Smuzhiyun					mdevlist[name] = {'name': name, 'host': host,
4236*4882a593Smuzhiyun						'worst': length, 'total': length, 'count': 1,
4237*4882a593Smuzhiyun						'url': url}
4238*4882a593Smuzhiyun				else:
4239*4882a593Smuzhiyun					if length > mdevlist[name]['worst']:
4240*4882a593Smuzhiyun						mdevlist[name]['worst'] = length
4241*4882a593Smuzhiyun						mdevlist[name]['url'] = url
4242*4882a593Smuzhiyun						mdevlist[name]['host'] = host
4243*4882a593Smuzhiyun					mdevlist[name]['total'] += length
4244*4882a593Smuzhiyun					mdevlist[name]['count'] += 1
4245*4882a593Smuzhiyun
4246*4882a593Smuzhiyun	# generate the html
4247*4882a593Smuzhiyun	th = '\t<th>{0}</th>\n'
4248*4882a593Smuzhiyun	td = '\t<td align=center>{0}</td>\n'
4249*4882a593Smuzhiyun	tdr = '\t<td align=right>{0}</td>\n'
4250*4882a593Smuzhiyun	tdlink = '\t<td align=center><a href="{0}">html</a></td>\n'
4251*4882a593Smuzhiyun	limit = 1
4252*4882a593Smuzhiyun	for type in sorted(devall, reverse=True):
4253*4882a593Smuzhiyun		num = 0
4254*4882a593Smuzhiyun		devlist = devall[type]
4255*4882a593Smuzhiyun		# table header
4256*4882a593Smuzhiyun		html += '<div class="stamp">%s (%s devices > %d ms)</div><table>\n' % \
4257*4882a593Smuzhiyun			(title, type.upper(), limit)
4258*4882a593Smuzhiyun		html += '<tr>\n' + '<th align=right>Device Name</th>' +\
4259*4882a593Smuzhiyun			th.format('Average Time') + th.format('Count') +\
4260*4882a593Smuzhiyun			th.format('Worst Time') + th.format('Host (worst time)') +\
4261*4882a593Smuzhiyun			th.format('Link (worst time)') + '</tr>\n'
4262*4882a593Smuzhiyun		for name in sorted(devlist, key=lambda k:(devlist[k]['worst'], \
4263*4882a593Smuzhiyun			devlist[k]['total'], devlist[k]['name']), reverse=True):
4264*4882a593Smuzhiyun			data = devall[type][name]
4265*4882a593Smuzhiyun			data['average'] = data['total'] / data['count']
4266*4882a593Smuzhiyun			if data['average'] < limit:
4267*4882a593Smuzhiyun				continue
4268*4882a593Smuzhiyun			# row classes - alternate row color
4269*4882a593Smuzhiyun			rcls = ['alt'] if num % 2 == 1 else []
4270*4882a593Smuzhiyun			html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n'
4271*4882a593Smuzhiyun			html += tdr.format(data['name'])				# name
4272*4882a593Smuzhiyun			html += td.format('%.3f ms' % data['average'])	# average
4273*4882a593Smuzhiyun			html += td.format(data['count'])				# count
4274*4882a593Smuzhiyun			html += td.format('%.3f ms' % data['worst'])	# worst
4275*4882a593Smuzhiyun			html += td.format(data['host'])					# host
4276*4882a593Smuzhiyun			html += tdlink.format(data['url'])				# url
4277*4882a593Smuzhiyun			html += '</tr>\n'
4278*4882a593Smuzhiyun			num += 1
4279*4882a593Smuzhiyun		html += '</table>\n'
4280*4882a593Smuzhiyun
4281*4882a593Smuzhiyun	# flush the data to file
4282*4882a593Smuzhiyun	hf = open(htmlfile, 'w')
4283*4882a593Smuzhiyun	hf.write(html+'</body>\n</html>\n')
4284*4882a593Smuzhiyun	hf.close()
4285*4882a593Smuzhiyun	return devall
4286*4882a593Smuzhiyun
4287*4882a593Smuzhiyundef createHTMLIssuesSummary(testruns, issues, htmlfile, title, extra=''):
4288*4882a593Smuzhiyun	multihost = len([e for e in issues if len(e['urls']) > 1]) > 0
4289*4882a593Smuzhiyun	html = summaryCSS('Issues Summary - SleepGraph', False)
4290*4882a593Smuzhiyun	total = len(testruns)
4291*4882a593Smuzhiyun
4292*4882a593Smuzhiyun	# generate the html
4293*4882a593Smuzhiyun	th = '\t<th>{0}</th>\n'
4294*4882a593Smuzhiyun	td = '\t<td align={0}>{1}</td>\n'
4295*4882a593Smuzhiyun	tdlink = '<a href="{1}">{0}</a>'
4296*4882a593Smuzhiyun	subtitle = '%d issues' % len(issues) if len(issues) > 0 else 'no issues'
4297*4882a593Smuzhiyun	html += '<div class="stamp">%s (%s)</div><table>\n' % (title, subtitle)
4298*4882a593Smuzhiyun	html += '<tr>\n' + th.format('Issue') + th.format('Count')
4299*4882a593Smuzhiyun	if multihost:
4300*4882a593Smuzhiyun		html += th.format('Hosts')
4301*4882a593Smuzhiyun	html += th.format('Tests') + th.format('Fail Rate') +\
4302*4882a593Smuzhiyun		th.format('First Instance') + '</tr>\n'
4303*4882a593Smuzhiyun
4304*4882a593Smuzhiyun	num = 0
4305*4882a593Smuzhiyun	for e in sorted(issues, key=lambda v:v['count'], reverse=True):
4306*4882a593Smuzhiyun		testtotal = 0
4307*4882a593Smuzhiyun		links = []
4308*4882a593Smuzhiyun		for host in sorted(e['urls']):
4309*4882a593Smuzhiyun			links.append(tdlink.format(host, e['urls'][host][0]))
4310*4882a593Smuzhiyun			testtotal += len(e['urls'][host])
4311*4882a593Smuzhiyun		rate = '%d/%d (%.2f%%)' % (testtotal, total, 100*float(testtotal)/float(total))
4312*4882a593Smuzhiyun		# row classes - alternate row color
4313*4882a593Smuzhiyun		rcls = ['alt'] if num % 2 == 1 else []
4314*4882a593Smuzhiyun		html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n'
4315*4882a593Smuzhiyun		html += td.format('left', e['line'])		# issue
4316*4882a593Smuzhiyun		html += td.format('center', e['count'])		# count
4317*4882a593Smuzhiyun		if multihost:
4318*4882a593Smuzhiyun			html += td.format('center', len(e['urls']))	# hosts
4319*4882a593Smuzhiyun		html += td.format('center', testtotal)		# test count
4320*4882a593Smuzhiyun		html += td.format('center', rate)			# test rate
4321*4882a593Smuzhiyun		html += td.format('center nowrap', '<br>'.join(links))	# links
4322*4882a593Smuzhiyun		html += '</tr>\n'
4323*4882a593Smuzhiyun		num += 1
4324*4882a593Smuzhiyun
4325*4882a593Smuzhiyun	# flush the data to file
4326*4882a593Smuzhiyun	hf = open(htmlfile, 'w')
4327*4882a593Smuzhiyun	hf.write(html+'</table>\n'+extra+'</body>\n</html>\n')
4328*4882a593Smuzhiyun	hf.close()
4329*4882a593Smuzhiyun	return issues
4330*4882a593Smuzhiyun
4331*4882a593Smuzhiyundef ordinal(value):
4332*4882a593Smuzhiyun	suffix = 'th'
4333*4882a593Smuzhiyun	if value < 10 or value > 19:
4334*4882a593Smuzhiyun		if value % 10 == 1:
4335*4882a593Smuzhiyun			suffix = 'st'
4336*4882a593Smuzhiyun		elif value % 10 == 2:
4337*4882a593Smuzhiyun			suffix = 'nd'
4338*4882a593Smuzhiyun		elif value % 10 == 3:
4339*4882a593Smuzhiyun			suffix = 'rd'
4340*4882a593Smuzhiyun	return '%d%s' % (value, suffix)
4341*4882a593Smuzhiyun
4342*4882a593Smuzhiyun# Function: createHTML
4343*4882a593Smuzhiyun# Description:
4344*4882a593Smuzhiyun#	 Create the output html file from the resident test data
4345*4882a593Smuzhiyun# Arguments:
4346*4882a593Smuzhiyun#	 testruns: array of Data objects from parseKernelLog or parseTraceLog
4347*4882a593Smuzhiyun# Output:
4348*4882a593Smuzhiyun#	 True if the html file was created, false if it failed
4349*4882a593Smuzhiyundef createHTML(testruns, testfail):
4350*4882a593Smuzhiyun	if len(testruns) < 1:
4351*4882a593Smuzhiyun		pprint('ERROR: Not enough test data to build a timeline')
4352*4882a593Smuzhiyun		return
4353*4882a593Smuzhiyun
4354*4882a593Smuzhiyun	kerror = False
4355*4882a593Smuzhiyun	for data in testruns:
4356*4882a593Smuzhiyun		if data.kerror:
4357*4882a593Smuzhiyun			kerror = True
4358*4882a593Smuzhiyun		if(sysvals.suspendmode in ['freeze', 'standby']):
4359*4882a593Smuzhiyun			data.trimFreezeTime(testruns[-1].tSuspended)
4360*4882a593Smuzhiyun		else:
4361*4882a593Smuzhiyun			data.getMemTime()
4362*4882a593Smuzhiyun
4363*4882a593Smuzhiyun	# html function templates
4364*4882a593Smuzhiyun	html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">{2}&rarr;</div>\n'
4365*4882a593Smuzhiyun	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'
4366*4882a593Smuzhiyun	html_cpuexec = '<div class="jiffie" style="left:{0}%;top:{1}px;height:{2}px;width:{3}%;background:{4};"></div>\n'
4367*4882a593Smuzhiyun	html_timetotal = '<table class="time1">\n<tr>'\
4368*4882a593Smuzhiyun		'<td class="green" title="{3}">{2} Suspend Time: <b>{0} ms</b></td>'\
4369*4882a593Smuzhiyun		'<td class="yellow" title="{4}">{2} Resume Time: <b>{1} ms</b></td>'\
4370*4882a593Smuzhiyun		'</tr>\n</table>\n'
4371*4882a593Smuzhiyun	html_timetotal2 = '<table class="time1">\n<tr>'\
4372*4882a593Smuzhiyun		'<td class="green" title="{4}">{3} Suspend Time: <b>{0} ms</b></td>'\
4373*4882a593Smuzhiyun		'<td class="gray" title="time spent in low-power mode with clock running">'+sysvals.suspendmode+' time: <b>{1} ms</b></td>'\
4374*4882a593Smuzhiyun		'<td class="yellow" title="{5}">{3} Resume Time: <b>{2} ms</b></td>'\
4375*4882a593Smuzhiyun		'</tr>\n</table>\n'
4376*4882a593Smuzhiyun	html_timetotal3 = '<table class="time1">\n<tr>'\
4377*4882a593Smuzhiyun		'<td class="green">Execution Time: <b>{0} ms</b></td>'\
4378*4882a593Smuzhiyun		'<td class="yellow">Command: <b>{1}</b></td>'\
4379*4882a593Smuzhiyun		'</tr>\n</table>\n'
4380*4882a593Smuzhiyun	html_fail = '<table class="testfail"><tr><td>{0}</td></tr></table>\n'
4381*4882a593Smuzhiyun	html_kdesc = '<td class="{3}" title="time spent in kernel execution">{0}Kernel {2}: {1} ms</td>'
4382*4882a593Smuzhiyun	html_fwdesc = '<td class="{3}" title="time spent in firmware">{0}Firmware {2}: {1} ms</td>'
4383*4882a593Smuzhiyun	html_wifdesc = '<td class="yellow" title="time for wifi to reconnect after resume complete ({2})">{0}Wifi Resume: {1}</td>'
4384*4882a593Smuzhiyun
4385*4882a593Smuzhiyun	# html format variables
4386*4882a593Smuzhiyun	scaleH = 20
4387*4882a593Smuzhiyun	if kerror:
4388*4882a593Smuzhiyun		scaleH = 40
4389*4882a593Smuzhiyun
4390*4882a593Smuzhiyun	# device timeline
4391*4882a593Smuzhiyun	devtl = Timeline(30, scaleH)
4392*4882a593Smuzhiyun
4393*4882a593Smuzhiyun	# write the test title and general info header
4394*4882a593Smuzhiyun	devtl.createHeader(sysvals, testruns[0].stamp)
4395*4882a593Smuzhiyun
4396*4882a593Smuzhiyun	# Generate the header for this timeline
4397*4882a593Smuzhiyun	for data in testruns:
4398*4882a593Smuzhiyun		tTotal = data.end - data.start
4399*4882a593Smuzhiyun		if(tTotal == 0):
4400*4882a593Smuzhiyun			doError('No timeline data')
4401*4882a593Smuzhiyun		if sysvals.suspendmode == 'command':
4402*4882a593Smuzhiyun			run_time = '%.0f' % (tTotal * 1000)
4403*4882a593Smuzhiyun			if sysvals.testcommand:
4404*4882a593Smuzhiyun				testdesc = sysvals.testcommand
4405*4882a593Smuzhiyun			else:
4406*4882a593Smuzhiyun				testdesc = 'unknown'
4407*4882a593Smuzhiyun			if(len(testruns) > 1):
4408*4882a593Smuzhiyun				testdesc = ordinal(data.testnumber+1)+' '+testdesc
4409*4882a593Smuzhiyun			thtml = html_timetotal3.format(run_time, testdesc)
4410*4882a593Smuzhiyun			devtl.html += thtml
4411*4882a593Smuzhiyun			continue
4412*4882a593Smuzhiyun		# typical full suspend/resume header
4413*4882a593Smuzhiyun		stot, rtot = sktime, rktime = data.getTimeValues()
4414*4882a593Smuzhiyun		ssrc, rsrc, testdesc, testdesc2 = ['kernel'], ['kernel'], 'Kernel', ''
4415*4882a593Smuzhiyun		if data.fwValid:
4416*4882a593Smuzhiyun			stot += (data.fwSuspend/1000000.0)
4417*4882a593Smuzhiyun			rtot += (data.fwResume/1000000.0)
4418*4882a593Smuzhiyun			ssrc.append('firmware')
4419*4882a593Smuzhiyun			rsrc.append('firmware')
4420*4882a593Smuzhiyun			testdesc = 'Total'
4421*4882a593Smuzhiyun		if 'time' in data.wifi and data.wifi['stat'] != 'timeout':
4422*4882a593Smuzhiyun			rtot += data.end - data.tKernRes + (data.wifi['time'] * 1000.0)
4423*4882a593Smuzhiyun			rsrc.append('wifi')
4424*4882a593Smuzhiyun			testdesc = 'Total'
4425*4882a593Smuzhiyun		suspend_time, resume_time = '%.3f' % stot, '%.3f' % rtot
4426*4882a593Smuzhiyun		stitle = 'time from kernel suspend start to %s mode [%s time]' % \
4427*4882a593Smuzhiyun			(sysvals.suspendmode, ' & '.join(ssrc))
4428*4882a593Smuzhiyun		rtitle = 'time from %s mode to kernel resume complete [%s time]' % \
4429*4882a593Smuzhiyun			(sysvals.suspendmode, ' & '.join(rsrc))
4430*4882a593Smuzhiyun		if(len(testruns) > 1):
4431*4882a593Smuzhiyun			testdesc = testdesc2 = ordinal(data.testnumber+1)
4432*4882a593Smuzhiyun			testdesc2 += ' '
4433*4882a593Smuzhiyun		if(len(data.tLow) == 0):
4434*4882a593Smuzhiyun			thtml = html_timetotal.format(suspend_time, \
4435*4882a593Smuzhiyun				resume_time, testdesc, stitle, rtitle)
4436*4882a593Smuzhiyun		else:
4437*4882a593Smuzhiyun			low_time = '+'.join(data.tLow)
4438*4882a593Smuzhiyun			thtml = html_timetotal2.format(suspend_time, low_time, \
4439*4882a593Smuzhiyun				resume_time, testdesc, stitle, rtitle)
4440*4882a593Smuzhiyun		devtl.html += thtml
4441*4882a593Smuzhiyun		if not data.fwValid and 'dev' not in data.wifi:
4442*4882a593Smuzhiyun			continue
4443*4882a593Smuzhiyun		# extra detail when the times come from multiple sources
4444*4882a593Smuzhiyun		thtml = '<table class="time2">\n<tr>'
4445*4882a593Smuzhiyun		thtml += html_kdesc.format(testdesc2, '%.3f'%sktime, 'Suspend', 'green')
4446*4882a593Smuzhiyun		if data.fwValid:
4447*4882a593Smuzhiyun			sftime = '%.3f'%(data.fwSuspend / 1000000.0)
4448*4882a593Smuzhiyun			rftime = '%.3f'%(data.fwResume / 1000000.0)
4449*4882a593Smuzhiyun			thtml += html_fwdesc.format(testdesc2, sftime, 'Suspend', 'green')
4450*4882a593Smuzhiyun			thtml += html_fwdesc.format(testdesc2, rftime, 'Resume', 'yellow')
4451*4882a593Smuzhiyun		thtml += html_kdesc.format(testdesc2, '%.3f'%rktime, 'Resume', 'yellow')
4452*4882a593Smuzhiyun		if 'time' in data.wifi:
4453*4882a593Smuzhiyun			if data.wifi['stat'] != 'timeout':
4454*4882a593Smuzhiyun				wtime = '%.0f ms'%(data.end - data.tKernRes + (data.wifi['time'] * 1000.0))
4455*4882a593Smuzhiyun			else:
4456*4882a593Smuzhiyun				wtime = 'TIMEOUT'
4457*4882a593Smuzhiyun			thtml += html_wifdesc.format(testdesc2, wtime, data.wifi['dev'])
4458*4882a593Smuzhiyun		thtml += '</tr>\n</table>\n'
4459*4882a593Smuzhiyun		devtl.html += thtml
4460*4882a593Smuzhiyun	if testfail:
4461*4882a593Smuzhiyun		devtl.html += html_fail.format(testfail)
4462*4882a593Smuzhiyun
4463*4882a593Smuzhiyun	# time scale for potentially multiple datasets
4464*4882a593Smuzhiyun	t0 = testruns[0].start
4465*4882a593Smuzhiyun	tMax = testruns[-1].end
4466*4882a593Smuzhiyun	tTotal = tMax - t0
4467*4882a593Smuzhiyun
4468*4882a593Smuzhiyun	# determine the maximum number of rows we need to draw
4469*4882a593Smuzhiyun	fulllist = []
4470*4882a593Smuzhiyun	threadlist = []
4471*4882a593Smuzhiyun	pscnt = 0
4472*4882a593Smuzhiyun	devcnt = 0
4473*4882a593Smuzhiyun	for data in testruns:
4474*4882a593Smuzhiyun		data.selectTimelineDevices('%f', tTotal, sysvals.mindevlen)
4475*4882a593Smuzhiyun		for group in data.devicegroups:
4476*4882a593Smuzhiyun			devlist = []
4477*4882a593Smuzhiyun			for phase in group:
4478*4882a593Smuzhiyun				for devname in sorted(data.tdevlist[phase]):
4479*4882a593Smuzhiyun					d = DevItem(data.testnumber, phase, data.dmesg[phase]['list'][devname])
4480*4882a593Smuzhiyun					devlist.append(d)
4481*4882a593Smuzhiyun					if d.isa('kth'):
4482*4882a593Smuzhiyun						threadlist.append(d)
4483*4882a593Smuzhiyun					else:
4484*4882a593Smuzhiyun						if d.isa('ps'):
4485*4882a593Smuzhiyun							pscnt += 1
4486*4882a593Smuzhiyun						else:
4487*4882a593Smuzhiyun							devcnt += 1
4488*4882a593Smuzhiyun						fulllist.append(d)
4489*4882a593Smuzhiyun			if sysvals.mixedphaseheight:
4490*4882a593Smuzhiyun				devtl.getPhaseRows(devlist)
4491*4882a593Smuzhiyun	if not sysvals.mixedphaseheight:
4492*4882a593Smuzhiyun		if len(threadlist) > 0 and len(fulllist) > 0:
4493*4882a593Smuzhiyun			if pscnt > 0 and devcnt > 0:
4494*4882a593Smuzhiyun				msg = 'user processes & device pm callbacks'
4495*4882a593Smuzhiyun			elif pscnt > 0:
4496*4882a593Smuzhiyun				msg = 'user processes'
4497*4882a593Smuzhiyun			else:
4498*4882a593Smuzhiyun				msg = 'device pm callbacks'
4499*4882a593Smuzhiyun			d = testruns[0].addHorizontalDivider(msg, testruns[-1].end)
4500*4882a593Smuzhiyun			fulllist.insert(0, d)
4501*4882a593Smuzhiyun		devtl.getPhaseRows(fulllist)
4502*4882a593Smuzhiyun		if len(threadlist) > 0:
4503*4882a593Smuzhiyun			d = testruns[0].addHorizontalDivider('asynchronous kernel threads', testruns[-1].end)
4504*4882a593Smuzhiyun			threadlist.insert(0, d)
4505*4882a593Smuzhiyun			devtl.getPhaseRows(threadlist, devtl.rows)
4506*4882a593Smuzhiyun	devtl.calcTotalRows()
4507*4882a593Smuzhiyun
4508*4882a593Smuzhiyun	# draw the full timeline
4509*4882a593Smuzhiyun	devtl.createZoomBox(sysvals.suspendmode, len(testruns))
4510*4882a593Smuzhiyun	for data in testruns:
4511*4882a593Smuzhiyun		# draw each test run and block chronologically
4512*4882a593Smuzhiyun		phases = {'suspend':[],'resume':[]}
4513*4882a593Smuzhiyun		for phase in data.sortedPhases():
4514*4882a593Smuzhiyun			if data.dmesg[phase]['start'] >= data.tSuspended:
4515*4882a593Smuzhiyun				phases['resume'].append(phase)
4516*4882a593Smuzhiyun			else:
4517*4882a593Smuzhiyun				phases['suspend'].append(phase)
4518*4882a593Smuzhiyun		# now draw the actual timeline blocks
4519*4882a593Smuzhiyun		for dir in phases:
4520*4882a593Smuzhiyun			# draw suspend and resume blocks separately
4521*4882a593Smuzhiyun			bname = '%s%d' % (dir[0], data.testnumber)
4522*4882a593Smuzhiyun			if dir == 'suspend':
4523*4882a593Smuzhiyun				m0 = data.start
4524*4882a593Smuzhiyun				mMax = data.tSuspended
4525*4882a593Smuzhiyun				left = '%f' % (((m0-t0)*100.0)/tTotal)
4526*4882a593Smuzhiyun			else:
4527*4882a593Smuzhiyun				m0 = data.tSuspended
4528*4882a593Smuzhiyun				mMax = data.end
4529*4882a593Smuzhiyun				# in an x2 run, remove any gap between blocks
4530*4882a593Smuzhiyun				if len(testruns) > 1 and data.testnumber == 0:
4531*4882a593Smuzhiyun					mMax = testruns[1].start
4532*4882a593Smuzhiyun				left = '%f' % ((((m0-t0)*100.0)+sysvals.srgap/2)/tTotal)
4533*4882a593Smuzhiyun			mTotal = mMax - m0
4534*4882a593Smuzhiyun			# if a timeline block is 0 length, skip altogether
4535*4882a593Smuzhiyun			if mTotal == 0:
4536*4882a593Smuzhiyun				continue
4537*4882a593Smuzhiyun			width = '%f' % (((mTotal*100.0)-sysvals.srgap/2)/tTotal)
4538*4882a593Smuzhiyun			devtl.html += devtl.html_tblock.format(bname, left, width, devtl.scaleH)
4539*4882a593Smuzhiyun			for b in phases[dir]:
4540*4882a593Smuzhiyun				# draw the phase color background
4541*4882a593Smuzhiyun				phase = data.dmesg[b]
4542*4882a593Smuzhiyun				length = phase['end']-phase['start']
4543*4882a593Smuzhiyun				left = '%f' % (((phase['start']-m0)*100.0)/mTotal)
4544*4882a593Smuzhiyun				width = '%f' % ((length*100.0)/mTotal)
4545*4882a593Smuzhiyun				devtl.html += devtl.html_phase.format(left, width, \
4546*4882a593Smuzhiyun					'%.3f'%devtl.scaleH, '%.3f'%devtl.bodyH, \
4547*4882a593Smuzhiyun					data.dmesg[b]['color'], '')
4548*4882a593Smuzhiyun			for e in data.errorinfo[dir]:
4549*4882a593Smuzhiyun				# draw red lines for any kernel errors found
4550*4882a593Smuzhiyun				type, t, idx1, idx2 = e
4551*4882a593Smuzhiyun				id = '%d_%d' % (idx1, idx2)
4552*4882a593Smuzhiyun				right = '%f' % (((mMax-t)*100.0)/mTotal)
4553*4882a593Smuzhiyun				devtl.html += html_error.format(right, id, type)
4554*4882a593Smuzhiyun			for b in phases[dir]:
4555*4882a593Smuzhiyun				# draw the devices for this phase
4556*4882a593Smuzhiyun				phaselist = data.dmesg[b]['list']
4557*4882a593Smuzhiyun				for d in sorted(data.tdevlist[b]):
4558*4882a593Smuzhiyun					dname = d if '[' not in d else d.split('[')[0]
4559*4882a593Smuzhiyun					name, dev = dname, phaselist[d]
4560*4882a593Smuzhiyun					drv = xtraclass = xtrainfo = xtrastyle = ''
4561*4882a593Smuzhiyun					if 'htmlclass' in dev:
4562*4882a593Smuzhiyun						xtraclass = dev['htmlclass']
4563*4882a593Smuzhiyun					if 'color' in dev:
4564*4882a593Smuzhiyun						xtrastyle = 'background:%s;' % dev['color']
4565*4882a593Smuzhiyun					if(d in sysvals.devprops):
4566*4882a593Smuzhiyun						name = sysvals.devprops[d].altName(d)
4567*4882a593Smuzhiyun						xtraclass = sysvals.devprops[d].xtraClass()
4568*4882a593Smuzhiyun						xtrainfo = sysvals.devprops[d].xtraInfo()
4569*4882a593Smuzhiyun					elif xtraclass == ' kth':
4570*4882a593Smuzhiyun						xtrainfo = ' kernel_thread'
4571*4882a593Smuzhiyun					if('drv' in dev and dev['drv']):
4572*4882a593Smuzhiyun						drv = ' {%s}' % dev['drv']
4573*4882a593Smuzhiyun					rowheight = devtl.phaseRowHeight(data.testnumber, b, dev['row'])
4574*4882a593Smuzhiyun					rowtop = devtl.phaseRowTop(data.testnumber, b, dev['row'])
4575*4882a593Smuzhiyun					top = '%.3f' % (rowtop + devtl.scaleH)
4576*4882a593Smuzhiyun					left = '%f' % (((dev['start']-m0)*100)/mTotal)
4577*4882a593Smuzhiyun					width = '%f' % (((dev['end']-dev['start'])*100)/mTotal)
4578*4882a593Smuzhiyun					length = ' (%0.3f ms) ' % ((dev['end']-dev['start'])*1000)
4579*4882a593Smuzhiyun					title = name+drv+xtrainfo+length
4580*4882a593Smuzhiyun					if sysvals.suspendmode == 'command':
4581*4882a593Smuzhiyun						title += sysvals.testcommand
4582*4882a593Smuzhiyun					elif xtraclass == ' ps':
4583*4882a593Smuzhiyun						if 'suspend' in b:
4584*4882a593Smuzhiyun							title += 'pre_suspend_process'
4585*4882a593Smuzhiyun						else:
4586*4882a593Smuzhiyun							title += 'post_resume_process'
4587*4882a593Smuzhiyun					else:
4588*4882a593Smuzhiyun						title += b
4589*4882a593Smuzhiyun					devtl.html += devtl.html_device.format(dev['id'], \
4590*4882a593Smuzhiyun						title, left, top, '%.3f'%rowheight, width, \
4591*4882a593Smuzhiyun						dname+drv, xtraclass, xtrastyle)
4592*4882a593Smuzhiyun					if('cpuexec' in dev):
4593*4882a593Smuzhiyun						for t in sorted(dev['cpuexec']):
4594*4882a593Smuzhiyun							start, end = t
4595*4882a593Smuzhiyun							j = float(dev['cpuexec'][t]) / 5
4596*4882a593Smuzhiyun							if j > 1.0:
4597*4882a593Smuzhiyun								j = 1.0
4598*4882a593Smuzhiyun							height = '%.3f' % (rowheight/3)
4599*4882a593Smuzhiyun							top = '%.3f' % (rowtop + devtl.scaleH + 2*rowheight/3)
4600*4882a593Smuzhiyun							left = '%f' % (((start-m0)*100)/mTotal)
4601*4882a593Smuzhiyun							width = '%f' % ((end-start)*100/mTotal)
4602*4882a593Smuzhiyun							color = 'rgba(255, 0, 0, %f)' % j
4603*4882a593Smuzhiyun							devtl.html += \
4604*4882a593Smuzhiyun								html_cpuexec.format(left, top, height, width, color)
4605*4882a593Smuzhiyun					if('src' not in dev):
4606*4882a593Smuzhiyun						continue
4607*4882a593Smuzhiyun					# draw any trace events for this device
4608*4882a593Smuzhiyun					for e in dev['src']:
4609*4882a593Smuzhiyun						if e.length == 0:
4610*4882a593Smuzhiyun							continue
4611*4882a593Smuzhiyun						height = '%.3f' % devtl.rowH
4612*4882a593Smuzhiyun						top = '%.3f' % (rowtop + devtl.scaleH + (e.row*devtl.rowH))
4613*4882a593Smuzhiyun						left = '%f' % (((e.time-m0)*100)/mTotal)
4614*4882a593Smuzhiyun						width = '%f' % (e.length*100/mTotal)
4615*4882a593Smuzhiyun						xtrastyle = ''
4616*4882a593Smuzhiyun						if e.color:
4617*4882a593Smuzhiyun							xtrastyle = 'background:%s;' % e.color
4618*4882a593Smuzhiyun						devtl.html += \
4619*4882a593Smuzhiyun							html_traceevent.format(e.title(), \
4620*4882a593Smuzhiyun								left, top, height, width, e.text(), '', xtrastyle)
4621*4882a593Smuzhiyun			# draw the time scale, try to make the number of labels readable
4622*4882a593Smuzhiyun			devtl.createTimeScale(m0, mMax, tTotal, dir)
4623*4882a593Smuzhiyun			devtl.html += '</div>\n'
4624*4882a593Smuzhiyun
4625*4882a593Smuzhiyun	# timeline is finished
4626*4882a593Smuzhiyun	devtl.html += '</div>\n</div>\n'
4627*4882a593Smuzhiyun
4628*4882a593Smuzhiyun	# draw a legend which describes the phases by color
4629*4882a593Smuzhiyun	if sysvals.suspendmode != 'command':
4630*4882a593Smuzhiyun		phasedef = testruns[-1].phasedef
4631*4882a593Smuzhiyun		devtl.html += '<div class="legend">\n'
4632*4882a593Smuzhiyun		pdelta = 100.0/len(phasedef.keys())
4633*4882a593Smuzhiyun		pmargin = pdelta / 4.0
4634*4882a593Smuzhiyun		for phase in sorted(phasedef, key=lambda k:phasedef[k]['order']):
4635*4882a593Smuzhiyun			id, p = '', phasedef[phase]
4636*4882a593Smuzhiyun			for word in phase.split('_'):
4637*4882a593Smuzhiyun				id += word[0]
4638*4882a593Smuzhiyun			order = '%.2f' % ((p['order'] * pdelta) + pmargin)
4639*4882a593Smuzhiyun			name = phase.replace('_', ' &nbsp;')
4640*4882a593Smuzhiyun			devtl.html += devtl.html_legend.format(order, p['color'], name, id)
4641*4882a593Smuzhiyun		devtl.html += '</div>\n'
4642*4882a593Smuzhiyun
4643*4882a593Smuzhiyun	hf = open(sysvals.htmlfile, 'w')
4644*4882a593Smuzhiyun	addCSS(hf, sysvals, len(testruns), kerror)
4645*4882a593Smuzhiyun
4646*4882a593Smuzhiyun	# write the device timeline
4647*4882a593Smuzhiyun	hf.write(devtl.html)
4648*4882a593Smuzhiyun	hf.write('<div id="devicedetailtitle"></div>\n')
4649*4882a593Smuzhiyun	hf.write('<div id="devicedetail" style="display:none;">\n')
4650*4882a593Smuzhiyun	# draw the colored boxes for the device detail section
4651*4882a593Smuzhiyun	for data in testruns:
4652*4882a593Smuzhiyun		hf.write('<div id="devicedetail%d">\n' % data.testnumber)
4653*4882a593Smuzhiyun		pscolor = 'linear-gradient(to top left, #ccc, #eee)'
4654*4882a593Smuzhiyun		hf.write(devtl.html_phaselet.format('pre_suspend_process', \
4655*4882a593Smuzhiyun			'0', '0', pscolor))
4656*4882a593Smuzhiyun		for b in data.sortedPhases():
4657*4882a593Smuzhiyun			phase = data.dmesg[b]
4658*4882a593Smuzhiyun			length = phase['end']-phase['start']
4659*4882a593Smuzhiyun			left = '%.3f' % (((phase['start']-t0)*100.0)/tTotal)
4660*4882a593Smuzhiyun			width = '%.3f' % ((length*100.0)/tTotal)
4661*4882a593Smuzhiyun			hf.write(devtl.html_phaselet.format(b, left, width, \
4662*4882a593Smuzhiyun				data.dmesg[b]['color']))
4663*4882a593Smuzhiyun		hf.write(devtl.html_phaselet.format('post_resume_process', \
4664*4882a593Smuzhiyun			'0', '0', pscolor))
4665*4882a593Smuzhiyun		if sysvals.suspendmode == 'command':
4666*4882a593Smuzhiyun			hf.write(devtl.html_phaselet.format('cmdexec', '0', '0', pscolor))
4667*4882a593Smuzhiyun		hf.write('</div>\n')
4668*4882a593Smuzhiyun	hf.write('</div>\n')
4669*4882a593Smuzhiyun
4670*4882a593Smuzhiyun	# write the ftrace data (callgraph)
4671*4882a593Smuzhiyun	if sysvals.cgtest >= 0 and len(testruns) > sysvals.cgtest:
4672*4882a593Smuzhiyun		data = testruns[sysvals.cgtest]
4673*4882a593Smuzhiyun	else:
4674*4882a593Smuzhiyun		data = testruns[-1]
4675*4882a593Smuzhiyun	if sysvals.usecallgraph:
4676*4882a593Smuzhiyun		addCallgraphs(sysvals, hf, data)
4677*4882a593Smuzhiyun
4678*4882a593Smuzhiyun	# add the test log as a hidden div
4679*4882a593Smuzhiyun	if sysvals.testlog and sysvals.logmsg:
4680*4882a593Smuzhiyun		hf.write('<div id="testlog" style="display:none;">\n'+sysvals.logmsg+'</div>\n')
4681*4882a593Smuzhiyun	# add the dmesg log as a hidden div
4682*4882a593Smuzhiyun	if sysvals.dmesglog and sysvals.dmesgfile:
4683*4882a593Smuzhiyun		hf.write('<div id="dmesglog" style="display:none;">\n')
4684*4882a593Smuzhiyun		lf = sysvals.openlog(sysvals.dmesgfile, 'r')
4685*4882a593Smuzhiyun		for line in lf:
4686*4882a593Smuzhiyun			line = line.replace('<', '&lt').replace('>', '&gt')
4687*4882a593Smuzhiyun			hf.write(line)
4688*4882a593Smuzhiyun		lf.close()
4689*4882a593Smuzhiyun		hf.write('</div>\n')
4690*4882a593Smuzhiyun	# add the ftrace log as a hidden div
4691*4882a593Smuzhiyun	if sysvals.ftracelog and sysvals.ftracefile:
4692*4882a593Smuzhiyun		hf.write('<div id="ftracelog" style="display:none;">\n')
4693*4882a593Smuzhiyun		lf = sysvals.openlog(sysvals.ftracefile, 'r')
4694*4882a593Smuzhiyun		for line in lf:
4695*4882a593Smuzhiyun			hf.write(line)
4696*4882a593Smuzhiyun		lf.close()
4697*4882a593Smuzhiyun		hf.write('</div>\n')
4698*4882a593Smuzhiyun
4699*4882a593Smuzhiyun	# write the footer and close
4700*4882a593Smuzhiyun	addScriptCode(hf, testruns)
4701*4882a593Smuzhiyun	hf.write('</body>\n</html>\n')
4702*4882a593Smuzhiyun	hf.close()
4703*4882a593Smuzhiyun	return True
4704*4882a593Smuzhiyun
4705*4882a593Smuzhiyundef addCSS(hf, sv, testcount=1, kerror=False, extra=''):
4706*4882a593Smuzhiyun	kernel = sv.stamp['kernel']
4707*4882a593Smuzhiyun	host = sv.hostname[0].upper()+sv.hostname[1:]
4708*4882a593Smuzhiyun	mode = sv.suspendmode
4709*4882a593Smuzhiyun	if sv.suspendmode in suspendmodename:
4710*4882a593Smuzhiyun		mode = suspendmodename[sv.suspendmode]
4711*4882a593Smuzhiyun	title = host+' '+mode+' '+kernel
4712*4882a593Smuzhiyun
4713*4882a593Smuzhiyun	# various format changes by flags
4714*4882a593Smuzhiyun	cgchk = 'checked'
4715*4882a593Smuzhiyun	cgnchk = 'not(:checked)'
4716*4882a593Smuzhiyun	if sv.cgexp:
4717*4882a593Smuzhiyun		cgchk = 'not(:checked)'
4718*4882a593Smuzhiyun		cgnchk = 'checked'
4719*4882a593Smuzhiyun
4720*4882a593Smuzhiyun	hoverZ = 'z-index:8;'
4721*4882a593Smuzhiyun	if sv.usedevsrc:
4722*4882a593Smuzhiyun		hoverZ = ''
4723*4882a593Smuzhiyun
4724*4882a593Smuzhiyun	devlistpos = 'absolute'
4725*4882a593Smuzhiyun	if testcount > 1:
4726*4882a593Smuzhiyun		devlistpos = 'relative'
4727*4882a593Smuzhiyun
4728*4882a593Smuzhiyun	scaleTH = 20
4729*4882a593Smuzhiyun	if kerror:
4730*4882a593Smuzhiyun		scaleTH = 60
4731*4882a593Smuzhiyun
4732*4882a593Smuzhiyun	# write the html header first (html head, css code, up to body start)
4733*4882a593Smuzhiyun	html_header = '<!DOCTYPE html>\n<html>\n<head>\n\
4734*4882a593Smuzhiyun	<meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\
4735*4882a593Smuzhiyun	<title>'+title+'</title>\n\
4736*4882a593Smuzhiyun	<style type=\'text/css\'>\n\
4737*4882a593Smuzhiyun		body {overflow-y:scroll;}\n\
4738*4882a593Smuzhiyun		.stamp {width:100%;text-align:center;background:gray;line-height:30px;color:white;font:25px Arial;}\n\
4739*4882a593Smuzhiyun		.stamp.sysinfo {font:10px Arial;}\n\
4740*4882a593Smuzhiyun		.callgraph {margin-top:30px;box-shadow:5px 5px 20px black;}\n\
4741*4882a593Smuzhiyun		.callgraph article * {padding-left:28px;}\n\
4742*4882a593Smuzhiyun		h1 {color:black;font:bold 30px Times;}\n\
4743*4882a593Smuzhiyun		t0 {color:black;font:bold 30px Times;}\n\
4744*4882a593Smuzhiyun		t1 {color:black;font:30px Times;}\n\
4745*4882a593Smuzhiyun		t2 {color:black;font:25px Times;}\n\
4746*4882a593Smuzhiyun		t3 {color:black;font:20px Times;white-space:nowrap;}\n\
4747*4882a593Smuzhiyun		t4 {color:black;font:bold 30px Times;line-height:60px;white-space:nowrap;}\n\
4748*4882a593Smuzhiyun		cS {font:bold 13px Times;}\n\
4749*4882a593Smuzhiyun		table {width:100%;}\n\
4750*4882a593Smuzhiyun		.gray {background:rgba(80,80,80,0.1);}\n\
4751*4882a593Smuzhiyun		.green {background:rgba(204,255,204,0.4);}\n\
4752*4882a593Smuzhiyun		.purple {background:rgba(128,0,128,0.2);}\n\
4753*4882a593Smuzhiyun		.yellow {background:rgba(255,255,204,0.4);}\n\
4754*4882a593Smuzhiyun		.blue {background:rgba(169,208,245,0.4);}\n\
4755*4882a593Smuzhiyun		.time1 {font:22px Arial;border:1px solid;}\n\
4756*4882a593Smuzhiyun		.time2 {font:15px Arial;border-bottom:1px solid;border-left:1px solid;border-right:1px solid;}\n\
4757*4882a593Smuzhiyun		.testfail {font:bold 22px Arial;color:red;border:1px dashed;}\n\
4758*4882a593Smuzhiyun		td {text-align:center;}\n\
4759*4882a593Smuzhiyun		r {color:#500000;font:15px Tahoma;}\n\
4760*4882a593Smuzhiyun		n {color:#505050;font:15px Tahoma;}\n\
4761*4882a593Smuzhiyun		.tdhl {color:red;}\n\
4762*4882a593Smuzhiyun		.hide {display:none;}\n\
4763*4882a593Smuzhiyun		.pf {display:none;}\n\
4764*4882a593Smuzhiyun		.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\
4765*4882a593Smuzhiyun		.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\
4766*4882a593Smuzhiyun		.pf:'+cgchk+' ~ *:not(:nth-child(2)) {display:none;}\n\
4767*4882a593Smuzhiyun		.zoombox {position:relative;width:100%;overflow-x:scroll;-webkit-user-select:none;-moz-user-select:none;user-select:none;}\n\
4768*4882a593Smuzhiyun		.timeline {position:relative;font-size:14px;cursor:pointer;width:100%; overflow:hidden;background:linear-gradient(#cccccc, white);}\n\
4769*4882a593Smuzhiyun		.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\
4770*4882a593Smuzhiyun		.thread.ps {border-radius:3px;background:linear-gradient(to top, #ccc, #eee);}\n\
4771*4882a593Smuzhiyun		.thread:hover {background:white;border:1px solid red;'+hoverZ+'}\n\
4772*4882a593Smuzhiyun		.thread.sec,.thread.sec:hover {background:black;border:0;color:white;line-height:15px;font-size:10px;}\n\
4773*4882a593Smuzhiyun		.hover {background:white;border:1px solid red;'+hoverZ+'}\n\
4774*4882a593Smuzhiyun		.hover.sync {background:white;}\n\
4775*4882a593Smuzhiyun		.hover.bg,.hover.kth,.hover.sync,.hover.ps {background:white;}\n\
4776*4882a593Smuzhiyun		.jiffie {position:absolute;pointer-events: none;z-index:8;}\n\
4777*4882a593Smuzhiyun		.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\
4778*4882a593Smuzhiyun		.traceevent:hover {color:white;font-weight:bold;border:1px solid white;}\n\
4779*4882a593Smuzhiyun		.phase {position:absolute;overflow:hidden;border:0px;text-align:center;}\n\
4780*4882a593Smuzhiyun		.phaselet {float:left;overflow:hidden;border:0px;text-align:center;min-height:100px;font-size:24px;}\n\
4781*4882a593Smuzhiyun		.t {position:absolute;line-height:'+('%d'%scaleTH)+'px;pointer-events:none;top:0;height:100%;border-right:1px solid black;z-index:6;}\n\
4782*4882a593Smuzhiyun		.err {position:absolute;top:0%;height:100%;border-right:3px solid red;color:red;font:bold 14px Times;line-height:18px;}\n\
4783*4882a593Smuzhiyun		.legend {position:relative; width:100%; height:40px; text-align:center;margin-bottom:20px}\n\
4784*4882a593Smuzhiyun		.legend .square {position:absolute;cursor:pointer;top:10px; width:0px;height:20px;border:1px solid;padding-left:20px;}\n\
4785*4882a593Smuzhiyun		button {height:40px;width:200px;margin-bottom:20px;margin-top:20px;font-size:24px;}\n\
4786*4882a593Smuzhiyun		.btnfmt {position:relative;float:right;height:25px;width:auto;margin-top:3px;margin-bottom:0;font-size:10px;text-align:center;}\n\
4787*4882a593Smuzhiyun		.devlist {position:'+devlistpos+';width:190px;}\n\
4788*4882a593Smuzhiyun		a:link {color:white;text-decoration:none;}\n\
4789*4882a593Smuzhiyun		a:visited {color:white;}\n\
4790*4882a593Smuzhiyun		a:hover {color:white;}\n\
4791*4882a593Smuzhiyun		a:active {color:white;}\n\
4792*4882a593Smuzhiyun		.version {position:relative;float:left;color:white;font-size:10px;line-height:30px;margin-left:10px;}\n\
4793*4882a593Smuzhiyun		#devicedetail {min-height:100px;box-shadow:5px 5px 20px black;}\n\
4794*4882a593Smuzhiyun		.tblock {position:absolute;height:100%;background:#ddd;}\n\
4795*4882a593Smuzhiyun		.tback {position:absolute;width:100%;background:linear-gradient(#ccc, #ddd);}\n\
4796*4882a593Smuzhiyun		.bg {z-index:1;}\n\
4797*4882a593Smuzhiyun'+extra+'\
4798*4882a593Smuzhiyun	</style>\n</head>\n<body>\n'
4799*4882a593Smuzhiyun	hf.write(html_header)
4800*4882a593Smuzhiyun
4801*4882a593Smuzhiyun# Function: addScriptCode
4802*4882a593Smuzhiyun# Description:
4803*4882a593Smuzhiyun#	 Adds the javascript code to the output html
4804*4882a593Smuzhiyun# Arguments:
4805*4882a593Smuzhiyun#	 hf: the open html file pointer
4806*4882a593Smuzhiyun#	 testruns: array of Data objects from parseKernelLog or parseTraceLog
4807*4882a593Smuzhiyundef addScriptCode(hf, testruns):
4808*4882a593Smuzhiyun	t0 = testruns[0].start * 1000
4809*4882a593Smuzhiyun	tMax = testruns[-1].end * 1000
4810*4882a593Smuzhiyun	# create an array in javascript memory with the device details
4811*4882a593Smuzhiyun	detail = '	var devtable = [];\n'
4812*4882a593Smuzhiyun	for data in testruns:
4813*4882a593Smuzhiyun		topo = data.deviceTopology()
4814*4882a593Smuzhiyun		detail += '	devtable[%d] = "%s";\n' % (data.testnumber, topo)
4815*4882a593Smuzhiyun	detail += '	var bounds = [%f,%f];\n' % (t0, tMax)
4816*4882a593Smuzhiyun	# add the code which will manipulate the data in the browser
4817*4882a593Smuzhiyun	script_code = \
4818*4882a593Smuzhiyun	'<script type="text/javascript">\n'+detail+\
4819*4882a593Smuzhiyun	'	var resolution = -1;\n'\
4820*4882a593Smuzhiyun	'	var dragval = [0, 0];\n'\
4821*4882a593Smuzhiyun	'	function redrawTimescale(t0, tMax, tS) {\n'\
4822*4882a593Smuzhiyun	'		var rline = \'<div class="t" style="left:0;border-left:1px solid black;border-right:0;">\';\n'\
4823*4882a593Smuzhiyun	'		var tTotal = tMax - t0;\n'\
4824*4882a593Smuzhiyun	'		var list = document.getElementsByClassName("tblock");\n'\
4825*4882a593Smuzhiyun	'		for (var i = 0; i < list.length; i++) {\n'\
4826*4882a593Smuzhiyun	'			var timescale = list[i].getElementsByClassName("timescale")[0];\n'\
4827*4882a593Smuzhiyun	'			var m0 = t0 + (tTotal*parseFloat(list[i].style.left)/100);\n'\
4828*4882a593Smuzhiyun	'			var mTotal = tTotal*parseFloat(list[i].style.width)/100;\n'\
4829*4882a593Smuzhiyun	'			var mMax = m0 + mTotal;\n'\
4830*4882a593Smuzhiyun	'			var html = "";\n'\
4831*4882a593Smuzhiyun	'			var divTotal = Math.floor(mTotal/tS) + 1;\n'\
4832*4882a593Smuzhiyun	'			if(divTotal > 1000) continue;\n'\
4833*4882a593Smuzhiyun	'			var divEdge = (mTotal - tS*(divTotal-1))*100/mTotal;\n'\
4834*4882a593Smuzhiyun	'			var pos = 0.0, val = 0.0;\n'\
4835*4882a593Smuzhiyun	'			for (var j = 0; j < divTotal; j++) {\n'\
4836*4882a593Smuzhiyun	'				var htmlline = "";\n'\
4837*4882a593Smuzhiyun	'				var mode = list[i].id[5];\n'\
4838*4882a593Smuzhiyun	'				if(mode == "s") {\n'\
4839*4882a593Smuzhiyun	'					pos = 100 - (((j)*tS*100)/mTotal) - divEdge;\n'\
4840*4882a593Smuzhiyun	'					val = (j-divTotal+1)*tS;\n'\
4841*4882a593Smuzhiyun	'					if(j == divTotal - 1)\n'\
4842*4882a593Smuzhiyun	'						htmlline = \'<div class="t" style="right:\'+pos+\'%"><cS>S&rarr;</cS></div>\';\n'\
4843*4882a593Smuzhiyun	'					else\n'\
4844*4882a593Smuzhiyun	'						htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\
4845*4882a593Smuzhiyun	'				} else {\n'\
4846*4882a593Smuzhiyun	'					pos = 100 - (((j)*tS*100)/mTotal);\n'\
4847*4882a593Smuzhiyun	'					val = (j)*tS;\n'\
4848*4882a593Smuzhiyun	'					htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\
4849*4882a593Smuzhiyun	'					if(j == 0)\n'\
4850*4882a593Smuzhiyun	'						if(mode == "r")\n'\
4851*4882a593Smuzhiyun	'							htmlline = rline+"<cS>&larr;R</cS></div>";\n'\
4852*4882a593Smuzhiyun	'						else\n'\
4853*4882a593Smuzhiyun	'							htmlline = rline+"<cS>0ms</div>";\n'\
4854*4882a593Smuzhiyun	'				}\n'\
4855*4882a593Smuzhiyun	'				html += htmlline;\n'\
4856*4882a593Smuzhiyun	'			}\n'\
4857*4882a593Smuzhiyun	'			timescale.innerHTML = html;\n'\
4858*4882a593Smuzhiyun	'		}\n'\
4859*4882a593Smuzhiyun	'	}\n'\
4860*4882a593Smuzhiyun	'	function zoomTimeline() {\n'\
4861*4882a593Smuzhiyun	'		var dmesg = document.getElementById("dmesg");\n'\
4862*4882a593Smuzhiyun	'		var zoombox = document.getElementById("dmesgzoombox");\n'\
4863*4882a593Smuzhiyun	'		var left = zoombox.scrollLeft;\n'\
4864*4882a593Smuzhiyun	'		var val = parseFloat(dmesg.style.width);\n'\
4865*4882a593Smuzhiyun	'		var newval = 100;\n'\
4866*4882a593Smuzhiyun	'		var sh = window.outerWidth / 2;\n'\
4867*4882a593Smuzhiyun	'		if(this.id == "zoomin") {\n'\
4868*4882a593Smuzhiyun	'			newval = val * 1.2;\n'\
4869*4882a593Smuzhiyun	'			if(newval > 910034) newval = 910034;\n'\
4870*4882a593Smuzhiyun	'			dmesg.style.width = newval+"%";\n'\
4871*4882a593Smuzhiyun	'			zoombox.scrollLeft = ((left + sh) * newval / val) - sh;\n'\
4872*4882a593Smuzhiyun	'		} else if (this.id == "zoomout") {\n'\
4873*4882a593Smuzhiyun	'			newval = val / 1.2;\n'\
4874*4882a593Smuzhiyun	'			if(newval < 100) newval = 100;\n'\
4875*4882a593Smuzhiyun	'			dmesg.style.width = newval+"%";\n'\
4876*4882a593Smuzhiyun	'			zoombox.scrollLeft = ((left + sh) * newval / val) - sh;\n'\
4877*4882a593Smuzhiyun	'		} else {\n'\
4878*4882a593Smuzhiyun	'			zoombox.scrollLeft = 0;\n'\
4879*4882a593Smuzhiyun	'			dmesg.style.width = "100%";\n'\
4880*4882a593Smuzhiyun	'		}\n'\
4881*4882a593Smuzhiyun	'		var tS = [10000, 5000, 2000, 1000, 500, 200, 100, 50, 20, 10, 5, 2, 1];\n'\
4882*4882a593Smuzhiyun	'		var t0 = bounds[0];\n'\
4883*4882a593Smuzhiyun	'		var tMax = bounds[1];\n'\
4884*4882a593Smuzhiyun	'		var tTotal = tMax - t0;\n'\
4885*4882a593Smuzhiyun	'		var wTotal = tTotal * 100.0 / newval;\n'\
4886*4882a593Smuzhiyun	'		var idx = 7*window.innerWidth/1100;\n'\
4887*4882a593Smuzhiyun	'		for(var i = 0; (i < tS.length)&&((wTotal / tS[i]) < idx); i++);\n'\
4888*4882a593Smuzhiyun	'		if(i >= tS.length) i = tS.length - 1;\n'\
4889*4882a593Smuzhiyun	'		if(tS[i] == resolution) return;\n'\
4890*4882a593Smuzhiyun	'		resolution = tS[i];\n'\
4891*4882a593Smuzhiyun	'		redrawTimescale(t0, tMax, tS[i]);\n'\
4892*4882a593Smuzhiyun	'	}\n'\
4893*4882a593Smuzhiyun	'	function deviceName(title) {\n'\
4894*4882a593Smuzhiyun	'		var name = title.slice(0, title.indexOf(" ("));\n'\
4895*4882a593Smuzhiyun	'		return name;\n'\
4896*4882a593Smuzhiyun	'	}\n'\
4897*4882a593Smuzhiyun	'	function deviceHover() {\n'\
4898*4882a593Smuzhiyun	'		var name = deviceName(this.title);\n'\
4899*4882a593Smuzhiyun	'		var dmesg = document.getElementById("dmesg");\n'\
4900*4882a593Smuzhiyun	'		var dev = dmesg.getElementsByClassName("thread");\n'\
4901*4882a593Smuzhiyun	'		var cpu = -1;\n'\
4902*4882a593Smuzhiyun	'		if(name.match("CPU_ON\[[0-9]*\]"))\n'\
4903*4882a593Smuzhiyun	'			cpu = parseInt(name.slice(7));\n'\
4904*4882a593Smuzhiyun	'		else if(name.match("CPU_OFF\[[0-9]*\]"))\n'\
4905*4882a593Smuzhiyun	'			cpu = parseInt(name.slice(8));\n'\
4906*4882a593Smuzhiyun	'		for (var i = 0; i < dev.length; i++) {\n'\
4907*4882a593Smuzhiyun	'			dname = deviceName(dev[i].title);\n'\
4908*4882a593Smuzhiyun	'			var cname = dev[i].className.slice(dev[i].className.indexOf("thread"));\n'\
4909*4882a593Smuzhiyun	'			if((cpu >= 0 && dname.match("CPU_O[NF]*\\\[*"+cpu+"\\\]")) ||\n'\
4910*4882a593Smuzhiyun	'				(name == dname))\n'\
4911*4882a593Smuzhiyun	'			{\n'\
4912*4882a593Smuzhiyun	'				dev[i].className = "hover "+cname;\n'\
4913*4882a593Smuzhiyun	'			} else {\n'\
4914*4882a593Smuzhiyun	'				dev[i].className = cname;\n'\
4915*4882a593Smuzhiyun	'			}\n'\
4916*4882a593Smuzhiyun	'		}\n'\
4917*4882a593Smuzhiyun	'	}\n'\
4918*4882a593Smuzhiyun	'	function deviceUnhover() {\n'\
4919*4882a593Smuzhiyun	'		var dmesg = document.getElementById("dmesg");\n'\
4920*4882a593Smuzhiyun	'		var dev = dmesg.getElementsByClassName("thread");\n'\
4921*4882a593Smuzhiyun	'		for (var i = 0; i < dev.length; i++) {\n'\
4922*4882a593Smuzhiyun	'			dev[i].className = dev[i].className.slice(dev[i].className.indexOf("thread"));\n'\
4923*4882a593Smuzhiyun	'		}\n'\
4924*4882a593Smuzhiyun	'	}\n'\
4925*4882a593Smuzhiyun	'	function deviceTitle(title, total, cpu) {\n'\
4926*4882a593Smuzhiyun	'		var prefix = "Total";\n'\
4927*4882a593Smuzhiyun	'		if(total.length > 3) {\n'\
4928*4882a593Smuzhiyun	'			prefix = "Average";\n'\
4929*4882a593Smuzhiyun	'			total[1] = (total[1]+total[3])/2;\n'\
4930*4882a593Smuzhiyun	'			total[2] = (total[2]+total[4])/2;\n'\
4931*4882a593Smuzhiyun	'		}\n'\
4932*4882a593Smuzhiyun	'		var devtitle = document.getElementById("devicedetailtitle");\n'\
4933*4882a593Smuzhiyun	'		var name = deviceName(title);\n'\
4934*4882a593Smuzhiyun	'		if(cpu >= 0) name = "CPU"+cpu;\n'\
4935*4882a593Smuzhiyun	'		var driver = "";\n'\
4936*4882a593Smuzhiyun	'		var tS = "<t2>(</t2>";\n'\
4937*4882a593Smuzhiyun	'		var tR = "<t2>)</t2>";\n'\
4938*4882a593Smuzhiyun	'		if(total[1] > 0)\n'\
4939*4882a593Smuzhiyun	'			tS = "<t2>("+prefix+" Suspend:</t2><t0> "+total[1].toFixed(3)+" ms</t0> ";\n'\
4940*4882a593Smuzhiyun	'		if(total[2] > 0)\n'\
4941*4882a593Smuzhiyun	'			tR = " <t2>"+prefix+" Resume:</t2><t0> "+total[2].toFixed(3)+" ms<t2>)</t2></t0>";\n'\
4942*4882a593Smuzhiyun	'		var s = title.indexOf("{");\n'\
4943*4882a593Smuzhiyun	'		var e = title.indexOf("}");\n'\
4944*4882a593Smuzhiyun	'		if((s >= 0) && (e >= 0))\n'\
4945*4882a593Smuzhiyun	'			driver = title.slice(s+1, e) + " <t1>@</t1> ";\n'\
4946*4882a593Smuzhiyun	'		if(total[1] > 0 && total[2] > 0)\n'\
4947*4882a593Smuzhiyun	'			devtitle.innerHTML = "<t0>"+driver+name+"</t0> "+tS+tR;\n'\
4948*4882a593Smuzhiyun	'		else\n'\
4949*4882a593Smuzhiyun	'			devtitle.innerHTML = "<t0>"+title+"</t0>";\n'\
4950*4882a593Smuzhiyun	'		return name;\n'\
4951*4882a593Smuzhiyun	'	}\n'\
4952*4882a593Smuzhiyun	'	function deviceDetail() {\n'\
4953*4882a593Smuzhiyun	'		var devinfo = document.getElementById("devicedetail");\n'\
4954*4882a593Smuzhiyun	'		devinfo.style.display = "block";\n'\
4955*4882a593Smuzhiyun	'		var name = deviceName(this.title);\n'\
4956*4882a593Smuzhiyun	'		var cpu = -1;\n'\
4957*4882a593Smuzhiyun	'		if(name.match("CPU_ON\[[0-9]*\]"))\n'\
4958*4882a593Smuzhiyun	'			cpu = parseInt(name.slice(7));\n'\
4959*4882a593Smuzhiyun	'		else if(name.match("CPU_OFF\[[0-9]*\]"))\n'\
4960*4882a593Smuzhiyun	'			cpu = parseInt(name.slice(8));\n'\
4961*4882a593Smuzhiyun	'		var dmesg = document.getElementById("dmesg");\n'\
4962*4882a593Smuzhiyun	'		var dev = dmesg.getElementsByClassName("thread");\n'\
4963*4882a593Smuzhiyun	'		var idlist = [];\n'\
4964*4882a593Smuzhiyun	'		var pdata = [[]];\n'\
4965*4882a593Smuzhiyun	'		if(document.getElementById("devicedetail1"))\n'\
4966*4882a593Smuzhiyun	'			pdata = [[], []];\n'\
4967*4882a593Smuzhiyun	'		var pd = pdata[0];\n'\
4968*4882a593Smuzhiyun	'		var total = [0.0, 0.0, 0.0];\n'\
4969*4882a593Smuzhiyun	'		for (var i = 0; i < dev.length; i++) {\n'\
4970*4882a593Smuzhiyun	'			dname = deviceName(dev[i].title);\n'\
4971*4882a593Smuzhiyun	'			if((cpu >= 0 && dname.match("CPU_O[NF]*\\\[*"+cpu+"\\\]")) ||\n'\
4972*4882a593Smuzhiyun	'				(name == dname))\n'\
4973*4882a593Smuzhiyun	'			{\n'\
4974*4882a593Smuzhiyun	'				idlist[idlist.length] = dev[i].id;\n'\
4975*4882a593Smuzhiyun	'				var tidx = 1;\n'\
4976*4882a593Smuzhiyun	'				if(dev[i].id[0] == "a") {\n'\
4977*4882a593Smuzhiyun	'					pd = pdata[0];\n'\
4978*4882a593Smuzhiyun	'				} else {\n'\
4979*4882a593Smuzhiyun	'					if(pdata.length == 1) pdata[1] = [];\n'\
4980*4882a593Smuzhiyun	'					if(total.length == 3) total[3]=total[4]=0.0;\n'\
4981*4882a593Smuzhiyun	'					pd = pdata[1];\n'\
4982*4882a593Smuzhiyun	'					tidx = 3;\n'\
4983*4882a593Smuzhiyun	'				}\n'\
4984*4882a593Smuzhiyun	'				var info = dev[i].title.split(" ");\n'\
4985*4882a593Smuzhiyun	'				var pname = info[info.length-1];\n'\
4986*4882a593Smuzhiyun	'				pd[pname] = parseFloat(info[info.length-3].slice(1));\n'\
4987*4882a593Smuzhiyun	'				total[0] += pd[pname];\n'\
4988*4882a593Smuzhiyun	'				if(pname.indexOf("suspend") >= 0)\n'\
4989*4882a593Smuzhiyun	'					total[tidx] += pd[pname];\n'\
4990*4882a593Smuzhiyun	'				else\n'\
4991*4882a593Smuzhiyun	'					total[tidx+1] += pd[pname];\n'\
4992*4882a593Smuzhiyun	'			}\n'\
4993*4882a593Smuzhiyun	'		}\n'\
4994*4882a593Smuzhiyun	'		var devname = deviceTitle(this.title, total, cpu);\n'\
4995*4882a593Smuzhiyun	'		var left = 0.0;\n'\
4996*4882a593Smuzhiyun	'		for (var t = 0; t < pdata.length; t++) {\n'\
4997*4882a593Smuzhiyun	'			pd = pdata[t];\n'\
4998*4882a593Smuzhiyun	'			devinfo = document.getElementById("devicedetail"+t);\n'\
4999*4882a593Smuzhiyun	'			var phases = devinfo.getElementsByClassName("phaselet");\n'\
5000*4882a593Smuzhiyun	'			for (var i = 0; i < phases.length; i++) {\n'\
5001*4882a593Smuzhiyun	'				if(phases[i].id in pd) {\n'\
5002*4882a593Smuzhiyun	'					var w = 100.0*pd[phases[i].id]/total[0];\n'\
5003*4882a593Smuzhiyun	'					var fs = 32;\n'\
5004*4882a593Smuzhiyun	'					if(w < 8) fs = 4*w | 0;\n'\
5005*4882a593Smuzhiyun	'					var fs2 = fs*3/4;\n'\
5006*4882a593Smuzhiyun	'					phases[i].style.width = w+"%";\n'\
5007*4882a593Smuzhiyun	'					phases[i].style.left = left+"%";\n'\
5008*4882a593Smuzhiyun	'					phases[i].title = phases[i].id+" "+pd[phases[i].id]+" ms";\n'\
5009*4882a593Smuzhiyun	'					left += w;\n'\
5010*4882a593Smuzhiyun	'					var time = "<t4 style=\\"font-size:"+fs+"px\\">"+pd[phases[i].id]+" ms<br></t4>";\n'\
5011*4882a593Smuzhiyun	'					var pname = "<t3 style=\\"font-size:"+fs2+"px\\">"+phases[i].id.replace(new RegExp("_", "g"), " ")+"</t3>";\n'\
5012*4882a593Smuzhiyun	'					phases[i].innerHTML = time+pname;\n'\
5013*4882a593Smuzhiyun	'				} else {\n'\
5014*4882a593Smuzhiyun	'					phases[i].style.width = "0%";\n'\
5015*4882a593Smuzhiyun	'					phases[i].style.left = left+"%";\n'\
5016*4882a593Smuzhiyun	'				}\n'\
5017*4882a593Smuzhiyun	'			}\n'\
5018*4882a593Smuzhiyun	'		}\n'\
5019*4882a593Smuzhiyun	'		if(typeof devstats !== \'undefined\')\n'\
5020*4882a593Smuzhiyun	'			callDetail(this.id, this.title);\n'\
5021*4882a593Smuzhiyun	'		var cglist = document.getElementById("callgraphs");\n'\
5022*4882a593Smuzhiyun	'		if(!cglist) return;\n'\
5023*4882a593Smuzhiyun	'		var cg = cglist.getElementsByClassName("atop");\n'\
5024*4882a593Smuzhiyun	'		if(cg.length < 10) return;\n'\
5025*4882a593Smuzhiyun	'		for (var i = 0; i < cg.length; i++) {\n'\
5026*4882a593Smuzhiyun	'			cgid = cg[i].id.split("x")[0]\n'\
5027*4882a593Smuzhiyun	'			if(idlist.indexOf(cgid) >= 0) {\n'\
5028*4882a593Smuzhiyun	'				cg[i].style.display = "block";\n'\
5029*4882a593Smuzhiyun	'			} else {\n'\
5030*4882a593Smuzhiyun	'				cg[i].style.display = "none";\n'\
5031*4882a593Smuzhiyun	'			}\n'\
5032*4882a593Smuzhiyun	'		}\n'\
5033*4882a593Smuzhiyun	'	}\n'\
5034*4882a593Smuzhiyun	'	function callDetail(devid, devtitle) {\n'\
5035*4882a593Smuzhiyun	'		if(!(devid in devstats) || devstats[devid].length < 1)\n'\
5036*4882a593Smuzhiyun	'			return;\n'\
5037*4882a593Smuzhiyun	'		var list = devstats[devid];\n'\
5038*4882a593Smuzhiyun	'		var tmp = devtitle.split(" ");\n'\
5039*4882a593Smuzhiyun	'		var name = tmp[0], phase = tmp[tmp.length-1];\n'\
5040*4882a593Smuzhiyun	'		var dd = document.getElementById(phase);\n'\
5041*4882a593Smuzhiyun	'		var total = parseFloat(tmp[1].slice(1));\n'\
5042*4882a593Smuzhiyun	'		var mlist = [];\n'\
5043*4882a593Smuzhiyun	'		var maxlen = 0;\n'\
5044*4882a593Smuzhiyun	'		var info = []\n'\
5045*4882a593Smuzhiyun	'		for(var i in list) {\n'\
5046*4882a593Smuzhiyun	'			if(list[i][0] == "@") {\n'\
5047*4882a593Smuzhiyun	'				info = list[i].split("|");\n'\
5048*4882a593Smuzhiyun	'				continue;\n'\
5049*4882a593Smuzhiyun	'			}\n'\
5050*4882a593Smuzhiyun	'			var tmp = list[i].split("|");\n'\
5051*4882a593Smuzhiyun	'			var t = parseFloat(tmp[0]), f = tmp[1], c = parseInt(tmp[2]);\n'\
5052*4882a593Smuzhiyun	'			var p = (t*100.0/total).toFixed(2);\n'\
5053*4882a593Smuzhiyun	'			mlist[mlist.length] = [f, c, t.toFixed(2), p+"%"];\n'\
5054*4882a593Smuzhiyun	'			if(f.length > maxlen)\n'\
5055*4882a593Smuzhiyun	'				maxlen = f.length;\n'\
5056*4882a593Smuzhiyun	'		}\n'\
5057*4882a593Smuzhiyun	'		var pad = 5;\n'\
5058*4882a593Smuzhiyun	'		if(mlist.length == 0) pad = 30;\n'\
5059*4882a593Smuzhiyun	'		var html = \'<div style="padding-top:\'+pad+\'px"><t3> <b>\'+name+\':</b>\';\n'\
5060*4882a593Smuzhiyun	'		if(info.length > 2)\n'\
5061*4882a593Smuzhiyun	'			html += " start=<b>"+info[1]+"</b>, end=<b>"+info[2]+"</b>";\n'\
5062*4882a593Smuzhiyun	'		if(info.length > 3)\n'\
5063*4882a593Smuzhiyun	'			html += ", length<i>(w/o overhead)</i>=<b>"+info[3]+" ms</b>";\n'\
5064*4882a593Smuzhiyun	'		if(info.length > 4)\n'\
5065*4882a593Smuzhiyun	'			html += ", return=<b>"+info[4]+"</b>";\n'\
5066*4882a593Smuzhiyun	'		html += "</t3></div>";\n'\
5067*4882a593Smuzhiyun	'		if(mlist.length > 0) {\n'\
5068*4882a593Smuzhiyun	'			html += \'<table class=fstat style="padding-top:\'+(maxlen*5)+\'px;"><tr><th>Function</th>\';\n'\
5069*4882a593Smuzhiyun	'			for(var i in mlist)\n'\
5070*4882a593Smuzhiyun	'				html += "<td class=vt>"+mlist[i][0]+"</td>";\n'\
5071*4882a593Smuzhiyun	'			html += "</tr><tr><th>Calls</th>";\n'\
5072*4882a593Smuzhiyun	'			for(var i in mlist)\n'\
5073*4882a593Smuzhiyun	'				html += "<td>"+mlist[i][1]+"</td>";\n'\
5074*4882a593Smuzhiyun	'			html += "</tr><tr><th>Time(ms)</th>";\n'\
5075*4882a593Smuzhiyun	'			for(var i in mlist)\n'\
5076*4882a593Smuzhiyun	'				html += "<td>"+mlist[i][2]+"</td>";\n'\
5077*4882a593Smuzhiyun	'			html += "</tr><tr><th>Percent</th>";\n'\
5078*4882a593Smuzhiyun	'			for(var i in mlist)\n'\
5079*4882a593Smuzhiyun	'				html += "<td>"+mlist[i][3]+"</td>";\n'\
5080*4882a593Smuzhiyun	'			html += "</tr></table>";\n'\
5081*4882a593Smuzhiyun	'		}\n'\
5082*4882a593Smuzhiyun	'		dd.innerHTML = html;\n'\
5083*4882a593Smuzhiyun	'		var height = (maxlen*5)+100;\n'\
5084*4882a593Smuzhiyun	'		dd.style.height = height+"px";\n'\
5085*4882a593Smuzhiyun	'		document.getElementById("devicedetail").style.height = height+"px";\n'\
5086*4882a593Smuzhiyun	'	}\n'\
5087*4882a593Smuzhiyun	'	function callSelect() {\n'\
5088*4882a593Smuzhiyun	'		var cglist = document.getElementById("callgraphs");\n'\
5089*4882a593Smuzhiyun	'		if(!cglist) return;\n'\
5090*4882a593Smuzhiyun	'		var cg = cglist.getElementsByClassName("atop");\n'\
5091*4882a593Smuzhiyun	'		for (var i = 0; i < cg.length; i++) {\n'\
5092*4882a593Smuzhiyun	'			if(this.id == cg[i].id) {\n'\
5093*4882a593Smuzhiyun	'				cg[i].style.display = "block";\n'\
5094*4882a593Smuzhiyun	'			} else {\n'\
5095*4882a593Smuzhiyun	'				cg[i].style.display = "none";\n'\
5096*4882a593Smuzhiyun	'			}\n'\
5097*4882a593Smuzhiyun	'		}\n'\
5098*4882a593Smuzhiyun	'	}\n'\
5099*4882a593Smuzhiyun	'	function devListWindow(e) {\n'\
5100*4882a593Smuzhiyun	'		var win = window.open();\n'\
5101*4882a593Smuzhiyun	'		var html = "<title>"+e.target.innerHTML+"</title>"+\n'\
5102*4882a593Smuzhiyun	'			"<style type=\\"text/css\\">"+\n'\
5103*4882a593Smuzhiyun	'			"   ul {list-style-type:circle;padding-left:10px;margin-left:10px;}"+\n'\
5104*4882a593Smuzhiyun	'			"</style>"\n'\
5105*4882a593Smuzhiyun	'		var dt = devtable[0];\n'\
5106*4882a593Smuzhiyun	'		if(e.target.id != "devlist1")\n'\
5107*4882a593Smuzhiyun	'			dt = devtable[1];\n'\
5108*4882a593Smuzhiyun	'		win.document.write(html+dt);\n'\
5109*4882a593Smuzhiyun	'	}\n'\
5110*4882a593Smuzhiyun	'	function errWindow() {\n'\
5111*4882a593Smuzhiyun	'		var range = this.id.split("_");\n'\
5112*4882a593Smuzhiyun	'		var idx1 = parseInt(range[0]);\n'\
5113*4882a593Smuzhiyun	'		var idx2 = parseInt(range[1]);\n'\
5114*4882a593Smuzhiyun	'		var win = window.open();\n'\
5115*4882a593Smuzhiyun	'		var log = document.getElementById("dmesglog");\n'\
5116*4882a593Smuzhiyun	'		var title = "<title>dmesg log</title>";\n'\
5117*4882a593Smuzhiyun	'		var text = log.innerHTML.split("\\n");\n'\
5118*4882a593Smuzhiyun	'		var html = "";\n'\
5119*4882a593Smuzhiyun	'		for(var i = 0; i < text.length; i++) {\n'\
5120*4882a593Smuzhiyun	'			if(i == idx1) {\n'\
5121*4882a593Smuzhiyun	'				html += "<e id=target>"+text[i]+"</e>\\n";\n'\
5122*4882a593Smuzhiyun	'			} else if(i > idx1 && i <= idx2) {\n'\
5123*4882a593Smuzhiyun	'				html += "<e>"+text[i]+"</e>\\n";\n'\
5124*4882a593Smuzhiyun	'			} else {\n'\
5125*4882a593Smuzhiyun	'				html += text[i]+"\\n";\n'\
5126*4882a593Smuzhiyun	'			}\n'\
5127*4882a593Smuzhiyun	'		}\n'\
5128*4882a593Smuzhiyun	'		win.document.write("<style>e{color:red}</style>"+title+"<pre>"+html+"</pre>");\n'\
5129*4882a593Smuzhiyun	'		win.location.hash = "#target";\n'\
5130*4882a593Smuzhiyun	'		win.document.close();\n'\
5131*4882a593Smuzhiyun	'	}\n'\
5132*4882a593Smuzhiyun	'	function logWindow(e) {\n'\
5133*4882a593Smuzhiyun	'		var name = e.target.id.slice(4);\n'\
5134*4882a593Smuzhiyun	'		var win = window.open();\n'\
5135*4882a593Smuzhiyun	'		var log = document.getElementById(name+"log");\n'\
5136*4882a593Smuzhiyun	'		var title = "<title>"+document.title.split(" ")[0]+" "+name+" log</title>";\n'\
5137*4882a593Smuzhiyun	'		win.document.write(title+"<pre>"+log.innerHTML+"</pre>");\n'\
5138*4882a593Smuzhiyun	'		win.document.close();\n'\
5139*4882a593Smuzhiyun	'	}\n'\
5140*4882a593Smuzhiyun	'	function onMouseDown(e) {\n'\
5141*4882a593Smuzhiyun	'		dragval[0] = e.clientX;\n'\
5142*4882a593Smuzhiyun	'		dragval[1] = document.getElementById("dmesgzoombox").scrollLeft;\n'\
5143*4882a593Smuzhiyun	'		document.onmousemove = onMouseMove;\n'\
5144*4882a593Smuzhiyun	'	}\n'\
5145*4882a593Smuzhiyun	'	function onMouseMove(e) {\n'\
5146*4882a593Smuzhiyun	'		var zoombox = document.getElementById("dmesgzoombox");\n'\
5147*4882a593Smuzhiyun	'		zoombox.scrollLeft = dragval[1] + dragval[0] - e.clientX;\n'\
5148*4882a593Smuzhiyun	'	}\n'\
5149*4882a593Smuzhiyun	'	function onMouseUp(e) {\n'\
5150*4882a593Smuzhiyun	'		document.onmousemove = null;\n'\
5151*4882a593Smuzhiyun	'	}\n'\
5152*4882a593Smuzhiyun	'	function onKeyPress(e) {\n'\
5153*4882a593Smuzhiyun	'		var c = e.charCode;\n'\
5154*4882a593Smuzhiyun	'		if(c != 42 && c != 43 && c != 45) return;\n'\
5155*4882a593Smuzhiyun	'		var click = document.createEvent("Events");\n'\
5156*4882a593Smuzhiyun	'		click.initEvent("click", true, false);\n'\
5157*4882a593Smuzhiyun	'		if(c == 43)  \n'\
5158*4882a593Smuzhiyun	'			document.getElementById("zoomin").dispatchEvent(click);\n'\
5159*4882a593Smuzhiyun	'		else if(c == 45)\n'\
5160*4882a593Smuzhiyun	'			document.getElementById("zoomout").dispatchEvent(click);\n'\
5161*4882a593Smuzhiyun	'		else if(c == 42)\n'\
5162*4882a593Smuzhiyun	'			document.getElementById("zoomdef").dispatchEvent(click);\n'\
5163*4882a593Smuzhiyun	'	}\n'\
5164*4882a593Smuzhiyun	'	window.addEventListener("resize", function () {zoomTimeline();});\n'\
5165*4882a593Smuzhiyun	'	window.addEventListener("load", function () {\n'\
5166*4882a593Smuzhiyun	'		var dmesg = document.getElementById("dmesg");\n'\
5167*4882a593Smuzhiyun	'		dmesg.style.width = "100%"\n'\
5168*4882a593Smuzhiyun	'		dmesg.onmousedown = onMouseDown;\n'\
5169*4882a593Smuzhiyun	'		document.onmouseup = onMouseUp;\n'\
5170*4882a593Smuzhiyun	'		document.onkeypress = onKeyPress;\n'\
5171*4882a593Smuzhiyun	'		document.getElementById("zoomin").onclick = zoomTimeline;\n'\
5172*4882a593Smuzhiyun	'		document.getElementById("zoomout").onclick = zoomTimeline;\n'\
5173*4882a593Smuzhiyun	'		document.getElementById("zoomdef").onclick = zoomTimeline;\n'\
5174*4882a593Smuzhiyun	'		var list = document.getElementsByClassName("err");\n'\
5175*4882a593Smuzhiyun	'		for (var i = 0; i < list.length; i++)\n'\
5176*4882a593Smuzhiyun	'			list[i].onclick = errWindow;\n'\
5177*4882a593Smuzhiyun	'		var list = document.getElementsByClassName("logbtn");\n'\
5178*4882a593Smuzhiyun	'		for (var i = 0; i < list.length; i++)\n'\
5179*4882a593Smuzhiyun	'			list[i].onclick = logWindow;\n'\
5180*4882a593Smuzhiyun	'		list = document.getElementsByClassName("devlist");\n'\
5181*4882a593Smuzhiyun	'		for (var i = 0; i < list.length; i++)\n'\
5182*4882a593Smuzhiyun	'			list[i].onclick = devListWindow;\n'\
5183*4882a593Smuzhiyun	'		var dev = dmesg.getElementsByClassName("thread");\n'\
5184*4882a593Smuzhiyun	'		for (var i = 0; i < dev.length; i++) {\n'\
5185*4882a593Smuzhiyun	'			dev[i].onclick = deviceDetail;\n'\
5186*4882a593Smuzhiyun	'			dev[i].onmouseover = deviceHover;\n'\
5187*4882a593Smuzhiyun	'			dev[i].onmouseout = deviceUnhover;\n'\
5188*4882a593Smuzhiyun	'		}\n'\
5189*4882a593Smuzhiyun	'		var dev = dmesg.getElementsByClassName("srccall");\n'\
5190*4882a593Smuzhiyun	'		for (var i = 0; i < dev.length; i++)\n'\
5191*4882a593Smuzhiyun	'			dev[i].onclick = callSelect;\n'\
5192*4882a593Smuzhiyun	'		zoomTimeline();\n'\
5193*4882a593Smuzhiyun	'	});\n'\
5194*4882a593Smuzhiyun	'</script>\n'
5195*4882a593Smuzhiyun	hf.write(script_code);
5196*4882a593Smuzhiyun
5197*4882a593Smuzhiyundef setRuntimeSuspend(before=True):
5198*4882a593Smuzhiyun	global sysvals
5199*4882a593Smuzhiyun	sv = sysvals
5200*4882a593Smuzhiyun	if sv.rs == 0:
5201*4882a593Smuzhiyun		return
5202*4882a593Smuzhiyun	if before:
5203*4882a593Smuzhiyun		# runtime suspend disable or enable
5204*4882a593Smuzhiyun		if sv.rs > 0:
5205*4882a593Smuzhiyun			sv.rstgt, sv.rsval, sv.rsdir = 'on', 'auto', 'enabled'
5206*4882a593Smuzhiyun		else:
5207*4882a593Smuzhiyun			sv.rstgt, sv.rsval, sv.rsdir = 'auto', 'on', 'disabled'
5208*4882a593Smuzhiyun		pprint('CONFIGURING RUNTIME SUSPEND...')
5209*4882a593Smuzhiyun		sv.rslist = deviceInfo(sv.rstgt)
5210*4882a593Smuzhiyun		for i in sv.rslist:
5211*4882a593Smuzhiyun			sv.setVal(sv.rsval, i)
5212*4882a593Smuzhiyun		pprint('runtime suspend %s on all devices (%d changed)' % (sv.rsdir, len(sv.rslist)))
5213*4882a593Smuzhiyun		pprint('waiting 5 seconds...')
5214*4882a593Smuzhiyun		time.sleep(5)
5215*4882a593Smuzhiyun	else:
5216*4882a593Smuzhiyun		# runtime suspend re-enable or re-disable
5217*4882a593Smuzhiyun		for i in sv.rslist:
5218*4882a593Smuzhiyun			sv.setVal(sv.rstgt, i)
5219*4882a593Smuzhiyun		pprint('runtime suspend settings restored on %d devices' % len(sv.rslist))
5220*4882a593Smuzhiyun
5221*4882a593Smuzhiyun# Function: executeSuspend
5222*4882a593Smuzhiyun# Description:
5223*4882a593Smuzhiyun#	 Execute system suspend through the sysfs interface, then copy the output
5224*4882a593Smuzhiyun#	 dmesg and ftrace files to the test output directory.
5225*4882a593Smuzhiyundef executeSuspend(quiet=False):
5226*4882a593Smuzhiyun	pm = ProcessMonitor()
5227*4882a593Smuzhiyun	tp = sysvals.tpath
5228*4882a593Smuzhiyun	if sysvals.wifi:
5229*4882a593Smuzhiyun		wifi = sysvals.checkWifi()
5230*4882a593Smuzhiyun	testdata = []
5231*4882a593Smuzhiyun	# run these commands to prepare the system for suspend
5232*4882a593Smuzhiyun	if sysvals.display:
5233*4882a593Smuzhiyun		if not quiet:
5234*4882a593Smuzhiyun			pprint('SET DISPLAY TO %s' % sysvals.display.upper())
5235*4882a593Smuzhiyun		displayControl(sysvals.display)
5236*4882a593Smuzhiyun		time.sleep(1)
5237*4882a593Smuzhiyun	if sysvals.sync:
5238*4882a593Smuzhiyun		if not quiet:
5239*4882a593Smuzhiyun			pprint('SYNCING FILESYSTEMS')
5240*4882a593Smuzhiyun		call('sync', shell=True)
5241*4882a593Smuzhiyun	# mark the start point in the kernel ring buffer just as we start
5242*4882a593Smuzhiyun	sysvals.initdmesg()
5243*4882a593Smuzhiyun	# start ftrace
5244*4882a593Smuzhiyun	if(sysvals.usecallgraph or sysvals.usetraceevents):
5245*4882a593Smuzhiyun		if not quiet:
5246*4882a593Smuzhiyun			pprint('START TRACING')
5247*4882a593Smuzhiyun		sysvals.fsetVal('1', 'tracing_on')
5248*4882a593Smuzhiyun		if sysvals.useprocmon:
5249*4882a593Smuzhiyun			pm.start()
5250*4882a593Smuzhiyun	sysvals.cmdinfo(True)
5251*4882a593Smuzhiyun	# execute however many s/r runs requested
5252*4882a593Smuzhiyun	for count in range(1,sysvals.execcount+1):
5253*4882a593Smuzhiyun		# x2delay in between test runs
5254*4882a593Smuzhiyun		if(count > 1 and sysvals.x2delay > 0):
5255*4882a593Smuzhiyun			sysvals.fsetVal('WAIT %d' % sysvals.x2delay, 'trace_marker')
5256*4882a593Smuzhiyun			time.sleep(sysvals.x2delay/1000.0)
5257*4882a593Smuzhiyun			sysvals.fsetVal('WAIT END', 'trace_marker')
5258*4882a593Smuzhiyun		# start message
5259*4882a593Smuzhiyun		if sysvals.testcommand != '':
5260*4882a593Smuzhiyun			pprint('COMMAND START')
5261*4882a593Smuzhiyun		else:
5262*4882a593Smuzhiyun			if(sysvals.rtcwake):
5263*4882a593Smuzhiyun				pprint('SUSPEND START')
5264*4882a593Smuzhiyun			else:
5265*4882a593Smuzhiyun				pprint('SUSPEND START (press a key to resume)')
5266*4882a593Smuzhiyun		# set rtcwake
5267*4882a593Smuzhiyun		if(sysvals.rtcwake):
5268*4882a593Smuzhiyun			if not quiet:
5269*4882a593Smuzhiyun				pprint('will issue an rtcwake in %d seconds' % sysvals.rtcwaketime)
5270*4882a593Smuzhiyun			sysvals.rtcWakeAlarmOn()
5271*4882a593Smuzhiyun		# start of suspend trace marker
5272*4882a593Smuzhiyun		if(sysvals.usecallgraph or sysvals.usetraceevents):
5273*4882a593Smuzhiyun			sysvals.fsetVal(datetime.now().strftime(sysvals.tmstart), 'trace_marker')
5274*4882a593Smuzhiyun		# predelay delay
5275*4882a593Smuzhiyun		if(count == 1 and sysvals.predelay > 0):
5276*4882a593Smuzhiyun			sysvals.fsetVal('WAIT %d' % sysvals.predelay, 'trace_marker')
5277*4882a593Smuzhiyun			time.sleep(sysvals.predelay/1000.0)
5278*4882a593Smuzhiyun			sysvals.fsetVal('WAIT END', 'trace_marker')
5279*4882a593Smuzhiyun		# initiate suspend or command
5280*4882a593Smuzhiyun		tdata = {'error': ''}
5281*4882a593Smuzhiyun		if sysvals.testcommand != '':
5282*4882a593Smuzhiyun			res = call(sysvals.testcommand+' 2>&1', shell=True);
5283*4882a593Smuzhiyun			if res != 0:
5284*4882a593Smuzhiyun				tdata['error'] = 'cmd returned %d' % res
5285*4882a593Smuzhiyun		else:
5286*4882a593Smuzhiyun			mode = sysvals.suspendmode
5287*4882a593Smuzhiyun			if sysvals.memmode and os.path.exists(sysvals.mempowerfile):
5288*4882a593Smuzhiyun				mode = 'mem'
5289*4882a593Smuzhiyun				pf = open(sysvals.mempowerfile, 'w')
5290*4882a593Smuzhiyun				pf.write(sysvals.memmode)
5291*4882a593Smuzhiyun				pf.close()
5292*4882a593Smuzhiyun			if sysvals.diskmode and os.path.exists(sysvals.diskpowerfile):
5293*4882a593Smuzhiyun				mode = 'disk'
5294*4882a593Smuzhiyun				pf = open(sysvals.diskpowerfile, 'w')
5295*4882a593Smuzhiyun				pf.write(sysvals.diskmode)
5296*4882a593Smuzhiyun				pf.close()
5297*4882a593Smuzhiyun			if mode == 'freeze' and sysvals.haveTurbostat():
5298*4882a593Smuzhiyun				# execution will pause here
5299*4882a593Smuzhiyun				turbo = sysvals.turbostat()
5300*4882a593Smuzhiyun				if turbo:
5301*4882a593Smuzhiyun					tdata['turbo'] = turbo
5302*4882a593Smuzhiyun			else:
5303*4882a593Smuzhiyun				pf = open(sysvals.powerfile, 'w')
5304*4882a593Smuzhiyun				pf.write(mode)
5305*4882a593Smuzhiyun				# execution will pause here
5306*4882a593Smuzhiyun				try:
5307*4882a593Smuzhiyun					pf.close()
5308*4882a593Smuzhiyun				except Exception as e:
5309*4882a593Smuzhiyun					tdata['error'] = str(e)
5310*4882a593Smuzhiyun		if(sysvals.rtcwake):
5311*4882a593Smuzhiyun			sysvals.rtcWakeAlarmOff()
5312*4882a593Smuzhiyun		# postdelay delay
5313*4882a593Smuzhiyun		if(count == sysvals.execcount and sysvals.postdelay > 0):
5314*4882a593Smuzhiyun			sysvals.fsetVal('WAIT %d' % sysvals.postdelay, 'trace_marker')
5315*4882a593Smuzhiyun			time.sleep(sysvals.postdelay/1000.0)
5316*4882a593Smuzhiyun			sysvals.fsetVal('WAIT END', 'trace_marker')
5317*4882a593Smuzhiyun		# return from suspend
5318*4882a593Smuzhiyun		pprint('RESUME COMPLETE')
5319*4882a593Smuzhiyun		if(sysvals.usecallgraph or sysvals.usetraceevents):
5320*4882a593Smuzhiyun			sysvals.fsetVal(datetime.now().strftime(sysvals.tmend), 'trace_marker')
5321*4882a593Smuzhiyun		if sysvals.wifi and wifi:
5322*4882a593Smuzhiyun			tdata['wifi'] = sysvals.pollWifi(wifi)
5323*4882a593Smuzhiyun		if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'):
5324*4882a593Smuzhiyun			tdata['fw'] = getFPDT(False)
5325*4882a593Smuzhiyun		testdata.append(tdata)
5326*4882a593Smuzhiyun	cmdafter = sysvals.cmdinfo(False)
5327*4882a593Smuzhiyun	# stop ftrace
5328*4882a593Smuzhiyun	if(sysvals.usecallgraph or sysvals.usetraceevents):
5329*4882a593Smuzhiyun		if sysvals.useprocmon:
5330*4882a593Smuzhiyun			pm.stop()
5331*4882a593Smuzhiyun		sysvals.fsetVal('0', 'tracing_on')
5332*4882a593Smuzhiyun	# grab a copy of the dmesg output
5333*4882a593Smuzhiyun	if not quiet:
5334*4882a593Smuzhiyun		pprint('CAPTURING DMESG')
5335*4882a593Smuzhiyun	sysvals.getdmesg(testdata)
5336*4882a593Smuzhiyun	# grab a copy of the ftrace output
5337*4882a593Smuzhiyun	if(sysvals.usecallgraph or sysvals.usetraceevents):
5338*4882a593Smuzhiyun		if not quiet:
5339*4882a593Smuzhiyun			pprint('CAPTURING TRACE')
5340*4882a593Smuzhiyun		op = sysvals.writeDatafileHeader(sysvals.ftracefile, testdata)
5341*4882a593Smuzhiyun		fp = open(tp+'trace', 'r')
5342*4882a593Smuzhiyun		for line in fp:
5343*4882a593Smuzhiyun			op.write(line)
5344*4882a593Smuzhiyun		op.close()
5345*4882a593Smuzhiyun		sysvals.fsetVal('', 'trace')
5346*4882a593Smuzhiyun		sysvals.platforminfo(cmdafter)
5347*4882a593Smuzhiyun
5348*4882a593Smuzhiyundef readFile(file):
5349*4882a593Smuzhiyun	if os.path.islink(file):
5350*4882a593Smuzhiyun		return os.readlink(file).split('/')[-1]
5351*4882a593Smuzhiyun	else:
5352*4882a593Smuzhiyun		return sysvals.getVal(file).strip()
5353*4882a593Smuzhiyun
5354*4882a593Smuzhiyun# Function: ms2nice
5355*4882a593Smuzhiyun# Description:
5356*4882a593Smuzhiyun#	 Print out a very concise time string in minutes and seconds
5357*4882a593Smuzhiyun# Output:
5358*4882a593Smuzhiyun#	 The time string, e.g. "1901m16s"
5359*4882a593Smuzhiyundef ms2nice(val):
5360*4882a593Smuzhiyun	val = int(val)
5361*4882a593Smuzhiyun	h = val // 3600000
5362*4882a593Smuzhiyun	m = (val // 60000) % 60
5363*4882a593Smuzhiyun	s = (val // 1000) % 60
5364*4882a593Smuzhiyun	if h > 0:
5365*4882a593Smuzhiyun		return '%d:%02d:%02d' % (h, m, s)
5366*4882a593Smuzhiyun	if m > 0:
5367*4882a593Smuzhiyun		return '%02d:%02d' % (m, s)
5368*4882a593Smuzhiyun	return '%ds' % s
5369*4882a593Smuzhiyun
5370*4882a593Smuzhiyundef yesno(val):
5371*4882a593Smuzhiyun	list = {'enabled':'A', 'disabled':'S', 'auto':'E', 'on':'D',
5372*4882a593Smuzhiyun		'active':'A', 'suspended':'S', 'suspending':'S'}
5373*4882a593Smuzhiyun	if val not in list:
5374*4882a593Smuzhiyun		return ' '
5375*4882a593Smuzhiyun	return list[val]
5376*4882a593Smuzhiyun
5377*4882a593Smuzhiyun# Function: deviceInfo
5378*4882a593Smuzhiyun# Description:
5379*4882a593Smuzhiyun#	 Detect all the USB hosts and devices currently connected and add
5380*4882a593Smuzhiyun#	 a list of USB device names to sysvals for better timeline readability
5381*4882a593Smuzhiyundef deviceInfo(output=''):
5382*4882a593Smuzhiyun	if not output:
5383*4882a593Smuzhiyun		pprint('LEGEND\n'\
5384*4882a593Smuzhiyun		'---------------------------------------------------------------------------------------------\n'\
5385*4882a593Smuzhiyun		'  A = async/sync PM queue (A/S)               C = runtime active children\n'\
5386*4882a593Smuzhiyun		'  R = runtime suspend enabled/disabled (E/D)  rACTIVE = runtime active (min/sec)\n'\
5387*4882a593Smuzhiyun		'  S = runtime status active/suspended (A/S)   rSUSPEND = runtime suspend (min/sec)\n'\
5388*4882a593Smuzhiyun		'  U = runtime usage count\n'\
5389*4882a593Smuzhiyun		'---------------------------------------------------------------------------------------------\n'\
5390*4882a593Smuzhiyun		'DEVICE                     NAME                       A R S U C    rACTIVE   rSUSPEND\n'\
5391*4882a593Smuzhiyun		'---------------------------------------------------------------------------------------------')
5392*4882a593Smuzhiyun
5393*4882a593Smuzhiyun	res = []
5394*4882a593Smuzhiyun	tgtval = 'runtime_status'
5395*4882a593Smuzhiyun	lines = dict()
5396*4882a593Smuzhiyun	for dirname, dirnames, filenames in os.walk('/sys/devices'):
5397*4882a593Smuzhiyun		if(not re.match('.*/power', dirname) or
5398*4882a593Smuzhiyun			'control' not in filenames or
5399*4882a593Smuzhiyun			tgtval not in filenames):
5400*4882a593Smuzhiyun			continue
5401*4882a593Smuzhiyun		name = ''
5402*4882a593Smuzhiyun		dirname = dirname[:-6]
5403*4882a593Smuzhiyun		device = dirname.split('/')[-1]
5404*4882a593Smuzhiyun		power = dict()
5405*4882a593Smuzhiyun		power[tgtval] = readFile('%s/power/%s' % (dirname, tgtval))
5406*4882a593Smuzhiyun		# only list devices which support runtime suspend
5407*4882a593Smuzhiyun		if power[tgtval] not in ['active', 'suspended', 'suspending']:
5408*4882a593Smuzhiyun			continue
5409*4882a593Smuzhiyun		for i in ['product', 'driver', 'subsystem']:
5410*4882a593Smuzhiyun			file = '%s/%s' % (dirname, i)
5411*4882a593Smuzhiyun			if os.path.exists(file):
5412*4882a593Smuzhiyun				name = readFile(file)
5413*4882a593Smuzhiyun				break
5414*4882a593Smuzhiyun		for i in ['async', 'control', 'runtime_status', 'runtime_usage',
5415*4882a593Smuzhiyun			'runtime_active_kids', 'runtime_active_time',
5416*4882a593Smuzhiyun			'runtime_suspended_time']:
5417*4882a593Smuzhiyun			if i in filenames:
5418*4882a593Smuzhiyun				power[i] = readFile('%s/power/%s' % (dirname, i))
5419*4882a593Smuzhiyun		if output:
5420*4882a593Smuzhiyun			if power['control'] == output:
5421*4882a593Smuzhiyun				res.append('%s/power/control' % dirname)
5422*4882a593Smuzhiyun			continue
5423*4882a593Smuzhiyun		lines[dirname] = '%-26s %-26s %1s %1s %1s %1s %1s %10s %10s' % \
5424*4882a593Smuzhiyun			(device[:26], name[:26],
5425*4882a593Smuzhiyun			yesno(power['async']), \
5426*4882a593Smuzhiyun			yesno(power['control']), \
5427*4882a593Smuzhiyun			yesno(power['runtime_status']), \
5428*4882a593Smuzhiyun			power['runtime_usage'], \
5429*4882a593Smuzhiyun			power['runtime_active_kids'], \
5430*4882a593Smuzhiyun			ms2nice(power['runtime_active_time']), \
5431*4882a593Smuzhiyun			ms2nice(power['runtime_suspended_time']))
5432*4882a593Smuzhiyun	for i in sorted(lines):
5433*4882a593Smuzhiyun		print(lines[i])
5434*4882a593Smuzhiyun	return res
5435*4882a593Smuzhiyun
5436*4882a593Smuzhiyun# Function: getModes
5437*4882a593Smuzhiyun# Description:
5438*4882a593Smuzhiyun#	 Determine the supported power modes on this system
5439*4882a593Smuzhiyun# Output:
5440*4882a593Smuzhiyun#	 A string list of the available modes
5441*4882a593Smuzhiyundef getModes():
5442*4882a593Smuzhiyun	modes = []
5443*4882a593Smuzhiyun	if(os.path.exists(sysvals.powerfile)):
5444*4882a593Smuzhiyun		fp = open(sysvals.powerfile, 'r')
5445*4882a593Smuzhiyun		modes = fp.read().split()
5446*4882a593Smuzhiyun		fp.close()
5447*4882a593Smuzhiyun	if(os.path.exists(sysvals.mempowerfile)):
5448*4882a593Smuzhiyun		deep = False
5449*4882a593Smuzhiyun		fp = open(sysvals.mempowerfile, 'r')
5450*4882a593Smuzhiyun		for m in fp.read().split():
5451*4882a593Smuzhiyun			memmode = m.strip('[]')
5452*4882a593Smuzhiyun			if memmode == 'deep':
5453*4882a593Smuzhiyun				deep = True
5454*4882a593Smuzhiyun			else:
5455*4882a593Smuzhiyun				modes.append('mem-%s' % memmode)
5456*4882a593Smuzhiyun		fp.close()
5457*4882a593Smuzhiyun		if 'mem' in modes and not deep:
5458*4882a593Smuzhiyun			modes.remove('mem')
5459*4882a593Smuzhiyun	if('disk' in modes and os.path.exists(sysvals.diskpowerfile)):
5460*4882a593Smuzhiyun		fp = open(sysvals.diskpowerfile, 'r')
5461*4882a593Smuzhiyun		for m in fp.read().split():
5462*4882a593Smuzhiyun			modes.append('disk-%s' % m.strip('[]'))
5463*4882a593Smuzhiyun		fp.close()
5464*4882a593Smuzhiyun	return modes
5465*4882a593Smuzhiyun
5466*4882a593Smuzhiyun# Function: dmidecode
5467*4882a593Smuzhiyun# Description:
5468*4882a593Smuzhiyun#	 Read the bios tables and pull out system info
5469*4882a593Smuzhiyun# Arguments:
5470*4882a593Smuzhiyun#	 mempath: /dev/mem or custom mem path
5471*4882a593Smuzhiyun#	 fatal: True to exit on error, False to return empty dict
5472*4882a593Smuzhiyun# Output:
5473*4882a593Smuzhiyun#	 A dict object with all available key/values
5474*4882a593Smuzhiyundef dmidecode(mempath, fatal=False):
5475*4882a593Smuzhiyun	out = dict()
5476*4882a593Smuzhiyun
5477*4882a593Smuzhiyun	# the list of values to retrieve, with hardcoded (type, idx)
5478*4882a593Smuzhiyun	info = {
5479*4882a593Smuzhiyun		'bios-vendor': (0, 4),
5480*4882a593Smuzhiyun		'bios-version': (0, 5),
5481*4882a593Smuzhiyun		'bios-release-date': (0, 8),
5482*4882a593Smuzhiyun		'system-manufacturer': (1, 4),
5483*4882a593Smuzhiyun		'system-product-name': (1, 5),
5484*4882a593Smuzhiyun		'system-version': (1, 6),
5485*4882a593Smuzhiyun		'system-serial-number': (1, 7),
5486*4882a593Smuzhiyun		'baseboard-manufacturer': (2, 4),
5487*4882a593Smuzhiyun		'baseboard-product-name': (2, 5),
5488*4882a593Smuzhiyun		'baseboard-version': (2, 6),
5489*4882a593Smuzhiyun		'baseboard-serial-number': (2, 7),
5490*4882a593Smuzhiyun		'chassis-manufacturer': (3, 4),
5491*4882a593Smuzhiyun		'chassis-type': (3, 5),
5492*4882a593Smuzhiyun		'chassis-version': (3, 6),
5493*4882a593Smuzhiyun		'chassis-serial-number': (3, 7),
5494*4882a593Smuzhiyun		'processor-manufacturer': (4, 7),
5495*4882a593Smuzhiyun		'processor-version': (4, 16),
5496*4882a593Smuzhiyun	}
5497*4882a593Smuzhiyun	if(not os.path.exists(mempath)):
5498*4882a593Smuzhiyun		if(fatal):
5499*4882a593Smuzhiyun			doError('file does not exist: %s' % mempath)
5500*4882a593Smuzhiyun		return out
5501*4882a593Smuzhiyun	if(not os.access(mempath, os.R_OK)):
5502*4882a593Smuzhiyun		if(fatal):
5503*4882a593Smuzhiyun			doError('file is not readable: %s' % mempath)
5504*4882a593Smuzhiyun		return out
5505*4882a593Smuzhiyun
5506*4882a593Smuzhiyun	# by default use legacy scan, but try to use EFI first
5507*4882a593Smuzhiyun	memaddr = 0xf0000
5508*4882a593Smuzhiyun	memsize = 0x10000
5509*4882a593Smuzhiyun	for ep in ['/sys/firmware/efi/systab', '/proc/efi/systab']:
5510*4882a593Smuzhiyun		if not os.path.exists(ep) or not os.access(ep, os.R_OK):
5511*4882a593Smuzhiyun			continue
5512*4882a593Smuzhiyun		fp = open(ep, 'r')
5513*4882a593Smuzhiyun		buf = fp.read()
5514*4882a593Smuzhiyun		fp.close()
5515*4882a593Smuzhiyun		i = buf.find('SMBIOS=')
5516*4882a593Smuzhiyun		if i >= 0:
5517*4882a593Smuzhiyun			try:
5518*4882a593Smuzhiyun				memaddr = int(buf[i+7:], 16)
5519*4882a593Smuzhiyun				memsize = 0x20
5520*4882a593Smuzhiyun			except:
5521*4882a593Smuzhiyun				continue
5522*4882a593Smuzhiyun
5523*4882a593Smuzhiyun	# read in the memory for scanning
5524*4882a593Smuzhiyun	try:
5525*4882a593Smuzhiyun		fp = open(mempath, 'rb')
5526*4882a593Smuzhiyun		fp.seek(memaddr)
5527*4882a593Smuzhiyun		buf = fp.read(memsize)
5528*4882a593Smuzhiyun	except:
5529*4882a593Smuzhiyun		if(fatal):
5530*4882a593Smuzhiyun			doError('DMI table is unreachable, sorry')
5531*4882a593Smuzhiyun		else:
5532*4882a593Smuzhiyun			pprint('WARNING: /dev/mem is not readable, ignoring DMI data')
5533*4882a593Smuzhiyun			return out
5534*4882a593Smuzhiyun	fp.close()
5535*4882a593Smuzhiyun
5536*4882a593Smuzhiyun	# search for either an SM table or DMI table
5537*4882a593Smuzhiyun	i = base = length = num = 0
5538*4882a593Smuzhiyun	while(i < memsize):
5539*4882a593Smuzhiyun		if buf[i:i+4] == b'_SM_' and i < memsize - 16:
5540*4882a593Smuzhiyun			length = struct.unpack('H', buf[i+22:i+24])[0]
5541*4882a593Smuzhiyun			base, num = struct.unpack('IH', buf[i+24:i+30])
5542*4882a593Smuzhiyun			break
5543*4882a593Smuzhiyun		elif buf[i:i+5] == b'_DMI_':
5544*4882a593Smuzhiyun			length = struct.unpack('H', buf[i+6:i+8])[0]
5545*4882a593Smuzhiyun			base, num = struct.unpack('IH', buf[i+8:i+14])
5546*4882a593Smuzhiyun			break
5547*4882a593Smuzhiyun		i += 16
5548*4882a593Smuzhiyun	if base == 0 and length == 0 and num == 0:
5549*4882a593Smuzhiyun		if(fatal):
5550*4882a593Smuzhiyun			doError('Neither SMBIOS nor DMI were found')
5551*4882a593Smuzhiyun		else:
5552*4882a593Smuzhiyun			return out
5553*4882a593Smuzhiyun
5554*4882a593Smuzhiyun	# read in the SM or DMI table
5555*4882a593Smuzhiyun	try:
5556*4882a593Smuzhiyun		fp = open(mempath, 'rb')
5557*4882a593Smuzhiyun		fp.seek(base)
5558*4882a593Smuzhiyun		buf = fp.read(length)
5559*4882a593Smuzhiyun	except:
5560*4882a593Smuzhiyun		if(fatal):
5561*4882a593Smuzhiyun			doError('DMI table is unreachable, sorry')
5562*4882a593Smuzhiyun		else:
5563*4882a593Smuzhiyun			pprint('WARNING: /dev/mem is not readable, ignoring DMI data')
5564*4882a593Smuzhiyun			return out
5565*4882a593Smuzhiyun	fp.close()
5566*4882a593Smuzhiyun
5567*4882a593Smuzhiyun	# scan the table for the values we want
5568*4882a593Smuzhiyun	count = i = 0
5569*4882a593Smuzhiyun	while(count < num and i <= len(buf) - 4):
5570*4882a593Smuzhiyun		type, size, handle = struct.unpack('BBH', buf[i:i+4])
5571*4882a593Smuzhiyun		n = i + size
5572*4882a593Smuzhiyun		while n < len(buf) - 1:
5573*4882a593Smuzhiyun			if 0 == struct.unpack('H', buf[n:n+2])[0]:
5574*4882a593Smuzhiyun				break
5575*4882a593Smuzhiyun			n += 1
5576*4882a593Smuzhiyun		data = buf[i+size:n+2].split(b'\0')
5577*4882a593Smuzhiyun		for name in info:
5578*4882a593Smuzhiyun			itype, idxadr = info[name]
5579*4882a593Smuzhiyun			if itype == type:
5580*4882a593Smuzhiyun				idx = struct.unpack('B', buf[i+idxadr:i+idxadr+1])[0]
5581*4882a593Smuzhiyun				if idx > 0 and idx < len(data) - 1:
5582*4882a593Smuzhiyun					s = data[idx-1].decode('utf-8')
5583*4882a593Smuzhiyun					if s.strip() and s.strip().lower() != 'to be filled by o.e.m.':
5584*4882a593Smuzhiyun						out[name] = s
5585*4882a593Smuzhiyun		i = n + 2
5586*4882a593Smuzhiyun		count += 1
5587*4882a593Smuzhiyun	return out
5588*4882a593Smuzhiyun
5589*4882a593Smuzhiyundef displayControl(cmd):
5590*4882a593Smuzhiyun	xset, ret = 'timeout 10 xset -d :0.0 {0}', 0
5591*4882a593Smuzhiyun	if sysvals.sudouser:
5592*4882a593Smuzhiyun		xset = 'sudo -u %s %s' % (sysvals.sudouser, xset)
5593*4882a593Smuzhiyun	if cmd == 'init':
5594*4882a593Smuzhiyun		ret = call(xset.format('dpms 0 0 0'), shell=True)
5595*4882a593Smuzhiyun		if not ret:
5596*4882a593Smuzhiyun			ret = call(xset.format('s off'), shell=True)
5597*4882a593Smuzhiyun	elif cmd == 'reset':
5598*4882a593Smuzhiyun		ret = call(xset.format('s reset'), shell=True)
5599*4882a593Smuzhiyun	elif cmd in ['on', 'off', 'standby', 'suspend']:
5600*4882a593Smuzhiyun		b4 = displayControl('stat')
5601*4882a593Smuzhiyun		ret = call(xset.format('dpms force %s' % cmd), shell=True)
5602*4882a593Smuzhiyun		if not ret:
5603*4882a593Smuzhiyun			curr = displayControl('stat')
5604*4882a593Smuzhiyun			sysvals.vprint('Display Switched: %s -> %s' % (b4, curr))
5605*4882a593Smuzhiyun			if curr != cmd:
5606*4882a593Smuzhiyun				sysvals.vprint('WARNING: Display failed to change to %s' % cmd)
5607*4882a593Smuzhiyun		if ret:
5608*4882a593Smuzhiyun			sysvals.vprint('WARNING: Display failed to change to %s with xset' % cmd)
5609*4882a593Smuzhiyun			return ret
5610*4882a593Smuzhiyun	elif cmd == 'stat':
5611*4882a593Smuzhiyun		fp = Popen(xset.format('q').split(' '), stdout=PIPE).stdout
5612*4882a593Smuzhiyun		ret = 'unknown'
5613*4882a593Smuzhiyun		for line in fp:
5614*4882a593Smuzhiyun			m = re.match('[\s]*Monitor is (?P<m>.*)', ascii(line))
5615*4882a593Smuzhiyun			if(m and len(m.group('m')) >= 2):
5616*4882a593Smuzhiyun				out = m.group('m').lower()
5617*4882a593Smuzhiyun				ret = out[3:] if out[0:2] == 'in' else out
5618*4882a593Smuzhiyun				break
5619*4882a593Smuzhiyun		fp.close()
5620*4882a593Smuzhiyun	return ret
5621*4882a593Smuzhiyun
5622*4882a593Smuzhiyun# Function: getFPDT
5623*4882a593Smuzhiyun# Description:
5624*4882a593Smuzhiyun#	 Read the acpi bios tables and pull out FPDT, the firmware data
5625*4882a593Smuzhiyun# Arguments:
5626*4882a593Smuzhiyun#	 output: True to output the info to stdout, False otherwise
5627*4882a593Smuzhiyundef getFPDT(output):
5628*4882a593Smuzhiyun	rectype = {}
5629*4882a593Smuzhiyun	rectype[0] = 'Firmware Basic Boot Performance Record'
5630*4882a593Smuzhiyun	rectype[1] = 'S3 Performance Table Record'
5631*4882a593Smuzhiyun	prectype = {}
5632*4882a593Smuzhiyun	prectype[0] = 'Basic S3 Resume Performance Record'
5633*4882a593Smuzhiyun	prectype[1] = 'Basic S3 Suspend Performance Record'
5634*4882a593Smuzhiyun
5635*4882a593Smuzhiyun	sysvals.rootCheck(True)
5636*4882a593Smuzhiyun	if(not os.path.exists(sysvals.fpdtpath)):
5637*4882a593Smuzhiyun		if(output):
5638*4882a593Smuzhiyun			doError('file does not exist: %s' % sysvals.fpdtpath)
5639*4882a593Smuzhiyun		return False
5640*4882a593Smuzhiyun	if(not os.access(sysvals.fpdtpath, os.R_OK)):
5641*4882a593Smuzhiyun		if(output):
5642*4882a593Smuzhiyun			doError('file is not readable: %s' % sysvals.fpdtpath)
5643*4882a593Smuzhiyun		return False
5644*4882a593Smuzhiyun	if(not os.path.exists(sysvals.mempath)):
5645*4882a593Smuzhiyun		if(output):
5646*4882a593Smuzhiyun			doError('file does not exist: %s' % sysvals.mempath)
5647*4882a593Smuzhiyun		return False
5648*4882a593Smuzhiyun	if(not os.access(sysvals.mempath, os.R_OK)):
5649*4882a593Smuzhiyun		if(output):
5650*4882a593Smuzhiyun			doError('file is not readable: %s' % sysvals.mempath)
5651*4882a593Smuzhiyun		return False
5652*4882a593Smuzhiyun
5653*4882a593Smuzhiyun	fp = open(sysvals.fpdtpath, 'rb')
5654*4882a593Smuzhiyun	buf = fp.read()
5655*4882a593Smuzhiyun	fp.close()
5656*4882a593Smuzhiyun
5657*4882a593Smuzhiyun	if(len(buf) < 36):
5658*4882a593Smuzhiyun		if(output):
5659*4882a593Smuzhiyun			doError('Invalid FPDT table data, should '+\
5660*4882a593Smuzhiyun				'be at least 36 bytes')
5661*4882a593Smuzhiyun		return False
5662*4882a593Smuzhiyun
5663*4882a593Smuzhiyun	table = struct.unpack('4sIBB6s8sI4sI', buf[0:36])
5664*4882a593Smuzhiyun	if(output):
5665*4882a593Smuzhiyun		pprint('\n'\
5666*4882a593Smuzhiyun		'Firmware Performance Data Table (%s)\n'\
5667*4882a593Smuzhiyun		'                  Signature : %s\n'\
5668*4882a593Smuzhiyun		'               Table Length : %u\n'\
5669*4882a593Smuzhiyun		'                   Revision : %u\n'\
5670*4882a593Smuzhiyun		'                   Checksum : 0x%x\n'\
5671*4882a593Smuzhiyun		'                     OEM ID : %s\n'\
5672*4882a593Smuzhiyun		'               OEM Table ID : %s\n'\
5673*4882a593Smuzhiyun		'               OEM Revision : %u\n'\
5674*4882a593Smuzhiyun		'                 Creator ID : %s\n'\
5675*4882a593Smuzhiyun		'           Creator Revision : 0x%x\n'\
5676*4882a593Smuzhiyun		'' % (ascii(table[0]), ascii(table[0]), table[1], table[2],
5677*4882a593Smuzhiyun			table[3], ascii(table[4]), ascii(table[5]), table[6],
5678*4882a593Smuzhiyun			ascii(table[7]), table[8]))
5679*4882a593Smuzhiyun
5680*4882a593Smuzhiyun	if(table[0] != b'FPDT'):
5681*4882a593Smuzhiyun		if(output):
5682*4882a593Smuzhiyun			doError('Invalid FPDT table')
5683*4882a593Smuzhiyun		return False
5684*4882a593Smuzhiyun	if(len(buf) <= 36):
5685*4882a593Smuzhiyun		return False
5686*4882a593Smuzhiyun	i = 0
5687*4882a593Smuzhiyun	fwData = [0, 0]
5688*4882a593Smuzhiyun	records = buf[36:]
5689*4882a593Smuzhiyun	try:
5690*4882a593Smuzhiyun		fp = open(sysvals.mempath, 'rb')
5691*4882a593Smuzhiyun	except:
5692*4882a593Smuzhiyun		pprint('WARNING: /dev/mem is not readable, ignoring the FPDT data')
5693*4882a593Smuzhiyun		return False
5694*4882a593Smuzhiyun	while(i < len(records)):
5695*4882a593Smuzhiyun		header = struct.unpack('HBB', records[i:i+4])
5696*4882a593Smuzhiyun		if(header[0] not in rectype):
5697*4882a593Smuzhiyun			i += header[1]
5698*4882a593Smuzhiyun			continue
5699*4882a593Smuzhiyun		if(header[1] != 16):
5700*4882a593Smuzhiyun			i += header[1]
5701*4882a593Smuzhiyun			continue
5702*4882a593Smuzhiyun		addr = struct.unpack('Q', records[i+8:i+16])[0]
5703*4882a593Smuzhiyun		try:
5704*4882a593Smuzhiyun			fp.seek(addr)
5705*4882a593Smuzhiyun			first = fp.read(8)
5706*4882a593Smuzhiyun		except:
5707*4882a593Smuzhiyun			if(output):
5708*4882a593Smuzhiyun				pprint('Bad address 0x%x in %s' % (addr, sysvals.mempath))
5709*4882a593Smuzhiyun			return [0, 0]
5710*4882a593Smuzhiyun		rechead = struct.unpack('4sI', first)
5711*4882a593Smuzhiyun		recdata = fp.read(rechead[1]-8)
5712*4882a593Smuzhiyun		if(rechead[0] == b'FBPT'):
5713*4882a593Smuzhiyun			record = struct.unpack('HBBIQQQQQ', recdata[:48])
5714*4882a593Smuzhiyun			if(output):
5715*4882a593Smuzhiyun				pprint('%s (%s)\n'\
5716*4882a593Smuzhiyun				'                  Reset END : %u ns\n'\
5717*4882a593Smuzhiyun				'  OS Loader LoadImage Start : %u ns\n'\
5718*4882a593Smuzhiyun				' OS Loader StartImage Start : %u ns\n'\
5719*4882a593Smuzhiyun				'     ExitBootServices Entry : %u ns\n'\
5720*4882a593Smuzhiyun				'      ExitBootServices Exit : %u ns'\
5721*4882a593Smuzhiyun				'' % (rectype[header[0]], ascii(rechead[0]), record[4], record[5],
5722*4882a593Smuzhiyun					record[6], record[7], record[8]))
5723*4882a593Smuzhiyun		elif(rechead[0] == b'S3PT'):
5724*4882a593Smuzhiyun			if(output):
5725*4882a593Smuzhiyun				pprint('%s (%s)' % (rectype[header[0]], ascii(rechead[0])))
5726*4882a593Smuzhiyun			j = 0
5727*4882a593Smuzhiyun			while(j < len(recdata)):
5728*4882a593Smuzhiyun				prechead = struct.unpack('HBB', recdata[j:j+4])
5729*4882a593Smuzhiyun				if(prechead[0] not in prectype):
5730*4882a593Smuzhiyun					continue
5731*4882a593Smuzhiyun				if(prechead[0] == 0):
5732*4882a593Smuzhiyun					record = struct.unpack('IIQQ', recdata[j:j+prechead[1]])
5733*4882a593Smuzhiyun					fwData[1] = record[2]
5734*4882a593Smuzhiyun					if(output):
5735*4882a593Smuzhiyun						pprint('    %s\n'\
5736*4882a593Smuzhiyun						'               Resume Count : %u\n'\
5737*4882a593Smuzhiyun						'                 FullResume : %u ns\n'\
5738*4882a593Smuzhiyun						'              AverageResume : %u ns'\
5739*4882a593Smuzhiyun						'' % (prectype[prechead[0]], record[1],
5740*4882a593Smuzhiyun								record[2], record[3]))
5741*4882a593Smuzhiyun				elif(prechead[0] == 1):
5742*4882a593Smuzhiyun					record = struct.unpack('QQ', recdata[j+4:j+prechead[1]])
5743*4882a593Smuzhiyun					fwData[0] = record[1] - record[0]
5744*4882a593Smuzhiyun					if(output):
5745*4882a593Smuzhiyun						pprint('    %s\n'\
5746*4882a593Smuzhiyun						'               SuspendStart : %u ns\n'\
5747*4882a593Smuzhiyun						'                 SuspendEnd : %u ns\n'\
5748*4882a593Smuzhiyun						'                SuspendTime : %u ns'\
5749*4882a593Smuzhiyun						'' % (prectype[prechead[0]], record[0],
5750*4882a593Smuzhiyun								record[1], fwData[0]))
5751*4882a593Smuzhiyun
5752*4882a593Smuzhiyun				j += prechead[1]
5753*4882a593Smuzhiyun		if(output):
5754*4882a593Smuzhiyun			pprint('')
5755*4882a593Smuzhiyun		i += header[1]
5756*4882a593Smuzhiyun	fp.close()
5757*4882a593Smuzhiyun	return fwData
5758*4882a593Smuzhiyun
5759*4882a593Smuzhiyun# Function: statusCheck
5760*4882a593Smuzhiyun# Description:
5761*4882a593Smuzhiyun#	 Verify that the requested command and options will work, and
5762*4882a593Smuzhiyun#	 print the results to the terminal
5763*4882a593Smuzhiyun# Output:
5764*4882a593Smuzhiyun#	 True if the test will work, False if not
5765*4882a593Smuzhiyundef statusCheck(probecheck=False):
5766*4882a593Smuzhiyun	status = ''
5767*4882a593Smuzhiyun
5768*4882a593Smuzhiyun	pprint('Checking this system (%s)...' % platform.node())
5769*4882a593Smuzhiyun
5770*4882a593Smuzhiyun	# check we have root access
5771*4882a593Smuzhiyun	res = sysvals.colorText('NO (No features of this tool will work!)')
5772*4882a593Smuzhiyun	if(sysvals.rootCheck(False)):
5773*4882a593Smuzhiyun		res = 'YES'
5774*4882a593Smuzhiyun	pprint('    have root access: %s' % res)
5775*4882a593Smuzhiyun	if(res != 'YES'):
5776*4882a593Smuzhiyun		pprint('    Try running this script with sudo')
5777*4882a593Smuzhiyun		return 'missing root access'
5778*4882a593Smuzhiyun
5779*4882a593Smuzhiyun	# check sysfs is mounted
5780*4882a593Smuzhiyun	res = sysvals.colorText('NO (No features of this tool will work!)')
5781*4882a593Smuzhiyun	if(os.path.exists(sysvals.powerfile)):
5782*4882a593Smuzhiyun		res = 'YES'
5783*4882a593Smuzhiyun	pprint('    is sysfs mounted: %s' % res)
5784*4882a593Smuzhiyun	if(res != 'YES'):
5785*4882a593Smuzhiyun		return 'sysfs is missing'
5786*4882a593Smuzhiyun
5787*4882a593Smuzhiyun	# check target mode is a valid mode
5788*4882a593Smuzhiyun	if sysvals.suspendmode != 'command':
5789*4882a593Smuzhiyun		res = sysvals.colorText('NO')
5790*4882a593Smuzhiyun		modes = getModes()
5791*4882a593Smuzhiyun		if(sysvals.suspendmode in modes):
5792*4882a593Smuzhiyun			res = 'YES'
5793*4882a593Smuzhiyun		else:
5794*4882a593Smuzhiyun			status = '%s mode is not supported' % sysvals.suspendmode
5795*4882a593Smuzhiyun		pprint('    is "%s" a valid power mode: %s' % (sysvals.suspendmode, res))
5796*4882a593Smuzhiyun		if(res == 'NO'):
5797*4882a593Smuzhiyun			pprint('      valid power modes are: %s' % modes)
5798*4882a593Smuzhiyun			pprint('      please choose one with -m')
5799*4882a593Smuzhiyun
5800*4882a593Smuzhiyun	# check if ftrace is available
5801*4882a593Smuzhiyun	res = sysvals.colorText('NO')
5802*4882a593Smuzhiyun	ftgood = sysvals.verifyFtrace()
5803*4882a593Smuzhiyun	if(ftgood):
5804*4882a593Smuzhiyun		res = 'YES'
5805*4882a593Smuzhiyun	elif(sysvals.usecallgraph):
5806*4882a593Smuzhiyun		status = 'ftrace is not properly supported'
5807*4882a593Smuzhiyun	pprint('    is ftrace supported: %s' % res)
5808*4882a593Smuzhiyun
5809*4882a593Smuzhiyun	# check if kprobes are available
5810*4882a593Smuzhiyun	if sysvals.usekprobes:
5811*4882a593Smuzhiyun		res = sysvals.colorText('NO')
5812*4882a593Smuzhiyun		sysvals.usekprobes = sysvals.verifyKprobes()
5813*4882a593Smuzhiyun		if(sysvals.usekprobes):
5814*4882a593Smuzhiyun			res = 'YES'
5815*4882a593Smuzhiyun		else:
5816*4882a593Smuzhiyun			sysvals.usedevsrc = False
5817*4882a593Smuzhiyun		pprint('    are kprobes supported: %s' % res)
5818*4882a593Smuzhiyun
5819*4882a593Smuzhiyun	# what data source are we using
5820*4882a593Smuzhiyun	res = 'DMESG'
5821*4882a593Smuzhiyun	if(ftgood):
5822*4882a593Smuzhiyun		sysvals.usetraceevents = True
5823*4882a593Smuzhiyun		for e in sysvals.traceevents:
5824*4882a593Smuzhiyun			if not os.path.exists(sysvals.epath+e):
5825*4882a593Smuzhiyun				sysvals.usetraceevents = False
5826*4882a593Smuzhiyun		if(sysvals.usetraceevents):
5827*4882a593Smuzhiyun			res = 'FTRACE (all trace events found)'
5828*4882a593Smuzhiyun	pprint('    timeline data source: %s' % res)
5829*4882a593Smuzhiyun
5830*4882a593Smuzhiyun	# check if rtcwake
5831*4882a593Smuzhiyun	res = sysvals.colorText('NO')
5832*4882a593Smuzhiyun	if(sysvals.rtcpath != ''):
5833*4882a593Smuzhiyun		res = 'YES'
5834*4882a593Smuzhiyun	elif(sysvals.rtcwake):
5835*4882a593Smuzhiyun		status = 'rtcwake is not properly supported'
5836*4882a593Smuzhiyun	pprint('    is rtcwake supported: %s' % res)
5837*4882a593Smuzhiyun
5838*4882a593Smuzhiyun	# check info commands
5839*4882a593Smuzhiyun	pprint('    optional commands this tool may use for info:')
5840*4882a593Smuzhiyun	no = sysvals.colorText('MISSING')
5841*4882a593Smuzhiyun	yes = sysvals.colorText('FOUND', 32)
5842*4882a593Smuzhiyun	for c in ['turbostat', 'mcelog', 'lspci', 'lsusb']:
5843*4882a593Smuzhiyun		if c == 'turbostat':
5844*4882a593Smuzhiyun			res = yes if sysvals.haveTurbostat() else no
5845*4882a593Smuzhiyun		else:
5846*4882a593Smuzhiyun			res = yes if sysvals.getExec(c) else no
5847*4882a593Smuzhiyun		pprint('        %s: %s' % (c, res))
5848*4882a593Smuzhiyun
5849*4882a593Smuzhiyun	if not probecheck:
5850*4882a593Smuzhiyun		return status
5851*4882a593Smuzhiyun
5852*4882a593Smuzhiyun	# verify kprobes
5853*4882a593Smuzhiyun	if sysvals.usekprobes:
5854*4882a593Smuzhiyun		for name in sysvals.tracefuncs:
5855*4882a593Smuzhiyun			sysvals.defaultKprobe(name, sysvals.tracefuncs[name])
5856*4882a593Smuzhiyun		if sysvals.usedevsrc:
5857*4882a593Smuzhiyun			for name in sysvals.dev_tracefuncs:
5858*4882a593Smuzhiyun				sysvals.defaultKprobe(name, sysvals.dev_tracefuncs[name])
5859*4882a593Smuzhiyun		sysvals.addKprobes(True)
5860*4882a593Smuzhiyun
5861*4882a593Smuzhiyun	return status
5862*4882a593Smuzhiyun
5863*4882a593Smuzhiyun# Function: doError
5864*4882a593Smuzhiyun# Description:
5865*4882a593Smuzhiyun#	 generic error function for catastrphic failures
5866*4882a593Smuzhiyun# Arguments:
5867*4882a593Smuzhiyun#	 msg: the error message to print
5868*4882a593Smuzhiyun#	 help: True if printHelp should be called after, False otherwise
5869*4882a593Smuzhiyundef doError(msg, help=False):
5870*4882a593Smuzhiyun	if(help == True):
5871*4882a593Smuzhiyun		printHelp()
5872*4882a593Smuzhiyun	pprint('ERROR: %s\n' % msg)
5873*4882a593Smuzhiyun	sysvals.outputResult({'error':msg})
5874*4882a593Smuzhiyun	sys.exit(1)
5875*4882a593Smuzhiyun
5876*4882a593Smuzhiyun# Function: getArgInt
5877*4882a593Smuzhiyun# Description:
5878*4882a593Smuzhiyun#	 pull out an integer argument from the command line with checks
5879*4882a593Smuzhiyundef getArgInt(name, args, min, max, main=True):
5880*4882a593Smuzhiyun	if main:
5881*4882a593Smuzhiyun		try:
5882*4882a593Smuzhiyun			arg = next(args)
5883*4882a593Smuzhiyun		except:
5884*4882a593Smuzhiyun			doError(name+': no argument supplied', True)
5885*4882a593Smuzhiyun	else:
5886*4882a593Smuzhiyun		arg = args
5887*4882a593Smuzhiyun	try:
5888*4882a593Smuzhiyun		val = int(arg)
5889*4882a593Smuzhiyun	except:
5890*4882a593Smuzhiyun		doError(name+': non-integer value given', True)
5891*4882a593Smuzhiyun	if(val < min or val > max):
5892*4882a593Smuzhiyun		doError(name+': value should be between %d and %d' % (min, max), True)
5893*4882a593Smuzhiyun	return val
5894*4882a593Smuzhiyun
5895*4882a593Smuzhiyun# Function: getArgFloat
5896*4882a593Smuzhiyun# Description:
5897*4882a593Smuzhiyun#	 pull out a float argument from the command line with checks
5898*4882a593Smuzhiyundef getArgFloat(name, args, min, max, main=True):
5899*4882a593Smuzhiyun	if main:
5900*4882a593Smuzhiyun		try:
5901*4882a593Smuzhiyun			arg = next(args)
5902*4882a593Smuzhiyun		except:
5903*4882a593Smuzhiyun			doError(name+': no argument supplied', True)
5904*4882a593Smuzhiyun	else:
5905*4882a593Smuzhiyun		arg = args
5906*4882a593Smuzhiyun	try:
5907*4882a593Smuzhiyun		val = float(arg)
5908*4882a593Smuzhiyun	except:
5909*4882a593Smuzhiyun		doError(name+': non-numerical value given', True)
5910*4882a593Smuzhiyun	if(val < min or val > max):
5911*4882a593Smuzhiyun		doError(name+': value should be between %f and %f' % (min, max), True)
5912*4882a593Smuzhiyun	return val
5913*4882a593Smuzhiyun
5914*4882a593Smuzhiyundef processData(live=False, quiet=False):
5915*4882a593Smuzhiyun	if not quiet:
5916*4882a593Smuzhiyun		pprint('PROCESSING: %s' % sysvals.htmlfile)
5917*4882a593Smuzhiyun	sysvals.vprint('usetraceevents=%s, usetracemarkers=%s, usekprobes=%s' % \
5918*4882a593Smuzhiyun		(sysvals.usetraceevents, sysvals.usetracemarkers, sysvals.usekprobes))
5919*4882a593Smuzhiyun	error = ''
5920*4882a593Smuzhiyun	if(sysvals.usetraceevents):
5921*4882a593Smuzhiyun		testruns, error = parseTraceLog(live)
5922*4882a593Smuzhiyun		if sysvals.dmesgfile:
5923*4882a593Smuzhiyun			for data in testruns:
5924*4882a593Smuzhiyun				data.extractErrorInfo()
5925*4882a593Smuzhiyun	else:
5926*4882a593Smuzhiyun		testruns = loadKernelLog()
5927*4882a593Smuzhiyun		for data in testruns:
5928*4882a593Smuzhiyun			parseKernelLog(data)
5929*4882a593Smuzhiyun		if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)):
5930*4882a593Smuzhiyun			appendIncompleteTraceLog(testruns)
5931*4882a593Smuzhiyun	if not sysvals.stamp:
5932*4882a593Smuzhiyun		pprint('ERROR: data does not include the expected stamp')
5933*4882a593Smuzhiyun		return (testruns, {'error': 'timeline generation failed'})
5934*4882a593Smuzhiyun	shown = ['bios', 'biosdate', 'cpu', 'host', 'kernel', 'man', 'memfr',
5935*4882a593Smuzhiyun			'memsz', 'mode', 'numcpu', 'plat', 'time', 'wifi']
5936*4882a593Smuzhiyun	sysvals.vprint('System Info:')
5937*4882a593Smuzhiyun	for key in sorted(sysvals.stamp):
5938*4882a593Smuzhiyun		if key in shown:
5939*4882a593Smuzhiyun			sysvals.vprint('    %-8s : %s' % (key.upper(), sysvals.stamp[key]))
5940*4882a593Smuzhiyun	sysvals.vprint('Command:\n    %s' % sysvals.cmdline)
5941*4882a593Smuzhiyun	for data in testruns:
5942*4882a593Smuzhiyun		if data.turbostat:
5943*4882a593Smuzhiyun			idx, s = 0, 'Turbostat:\n    '
5944*4882a593Smuzhiyun			for val in data.turbostat.split('|'):
5945*4882a593Smuzhiyun				idx += len(val) + 1
5946*4882a593Smuzhiyun				if idx >= 80:
5947*4882a593Smuzhiyun					idx = 0
5948*4882a593Smuzhiyun					s += '\n    '
5949*4882a593Smuzhiyun				s += val + ' '
5950*4882a593Smuzhiyun			sysvals.vprint(s)
5951*4882a593Smuzhiyun		data.printDetails()
5952*4882a593Smuzhiyun	if len(sysvals.platinfo) > 0:
5953*4882a593Smuzhiyun		sysvals.vprint('\nPlatform Info:')
5954*4882a593Smuzhiyun		for info in sysvals.platinfo:
5955*4882a593Smuzhiyun			sysvals.vprint('[%s - %s]' % (info[0], info[1]))
5956*4882a593Smuzhiyun			sysvals.vprint(info[2])
5957*4882a593Smuzhiyun		sysvals.vprint('')
5958*4882a593Smuzhiyun	if sysvals.cgdump:
5959*4882a593Smuzhiyun		for data in testruns:
5960*4882a593Smuzhiyun			data.debugPrint()
5961*4882a593Smuzhiyun		sys.exit(0)
5962*4882a593Smuzhiyun	if len(testruns) < 1:
5963*4882a593Smuzhiyun		pprint('ERROR: Not enough test data to build a timeline')
5964*4882a593Smuzhiyun		return (testruns, {'error': 'timeline generation failed'})
5965*4882a593Smuzhiyun	sysvals.vprint('Creating the html timeline (%s)...' % sysvals.htmlfile)
5966*4882a593Smuzhiyun	createHTML(testruns, error)
5967*4882a593Smuzhiyun	if not quiet:
5968*4882a593Smuzhiyun		pprint('DONE:       %s' % sysvals.htmlfile)
5969*4882a593Smuzhiyun	data = testruns[0]
5970*4882a593Smuzhiyun	stamp = data.stamp
5971*4882a593Smuzhiyun	stamp['suspend'], stamp['resume'] = data.getTimeValues()
5972*4882a593Smuzhiyun	if data.fwValid:
5973*4882a593Smuzhiyun		stamp['fwsuspend'], stamp['fwresume'] = data.fwSuspend, data.fwResume
5974*4882a593Smuzhiyun	if error:
5975*4882a593Smuzhiyun		stamp['error'] = error
5976*4882a593Smuzhiyun	return (testruns, stamp)
5977*4882a593Smuzhiyun
5978*4882a593Smuzhiyun# Function: rerunTest
5979*4882a593Smuzhiyun# Description:
5980*4882a593Smuzhiyun#	 generate an output from an existing set of ftrace/dmesg logs
5981*4882a593Smuzhiyundef rerunTest(htmlfile=''):
5982*4882a593Smuzhiyun	if sysvals.ftracefile:
5983*4882a593Smuzhiyun		doesTraceLogHaveTraceEvents()
5984*4882a593Smuzhiyun	if not sysvals.dmesgfile and not sysvals.usetraceevents:
5985*4882a593Smuzhiyun		doError('recreating this html output requires a dmesg file')
5986*4882a593Smuzhiyun	if htmlfile:
5987*4882a593Smuzhiyun		sysvals.htmlfile = htmlfile
5988*4882a593Smuzhiyun	else:
5989*4882a593Smuzhiyun		sysvals.setOutputFile()
5990*4882a593Smuzhiyun	if os.path.exists(sysvals.htmlfile):
5991*4882a593Smuzhiyun		if not os.path.isfile(sysvals.htmlfile):
5992*4882a593Smuzhiyun			doError('a directory already exists with this name: %s' % sysvals.htmlfile)
5993*4882a593Smuzhiyun		elif not os.access(sysvals.htmlfile, os.W_OK):
5994*4882a593Smuzhiyun			doError('missing permission to write to %s' % sysvals.htmlfile)
5995*4882a593Smuzhiyun	testruns, stamp = processData()
5996*4882a593Smuzhiyun	sysvals.resetlog()
5997*4882a593Smuzhiyun	return stamp
5998*4882a593Smuzhiyun
5999*4882a593Smuzhiyun# Function: runTest
6000*4882a593Smuzhiyun# Description:
6001*4882a593Smuzhiyun#	 execute a suspend/resume, gather the logs, and generate the output
6002*4882a593Smuzhiyundef runTest(n=0, quiet=False):
6003*4882a593Smuzhiyun	# prepare for the test
6004*4882a593Smuzhiyun	sysvals.initFtrace(quiet)
6005*4882a593Smuzhiyun	sysvals.initTestOutput('suspend')
6006*4882a593Smuzhiyun
6007*4882a593Smuzhiyun	# execute the test
6008*4882a593Smuzhiyun	executeSuspend(quiet)
6009*4882a593Smuzhiyun	sysvals.cleanupFtrace()
6010*4882a593Smuzhiyun	if sysvals.skiphtml:
6011*4882a593Smuzhiyun		sysvals.outputResult({}, n)
6012*4882a593Smuzhiyun		sysvals.sudoUserchown(sysvals.testdir)
6013*4882a593Smuzhiyun		return
6014*4882a593Smuzhiyun	testruns, stamp = processData(True, quiet)
6015*4882a593Smuzhiyun	for data in testruns:
6016*4882a593Smuzhiyun		del data
6017*4882a593Smuzhiyun	sysvals.sudoUserchown(sysvals.testdir)
6018*4882a593Smuzhiyun	sysvals.outputResult(stamp, n)
6019*4882a593Smuzhiyun	if 'error' in stamp:
6020*4882a593Smuzhiyun		return 2
6021*4882a593Smuzhiyun	return 0
6022*4882a593Smuzhiyun
6023*4882a593Smuzhiyundef find_in_html(html, start, end, firstonly=True):
6024*4882a593Smuzhiyun	cnt, out, list = len(html), [], []
6025*4882a593Smuzhiyun	if firstonly:
6026*4882a593Smuzhiyun		m = re.search(start, html)
6027*4882a593Smuzhiyun		if m:
6028*4882a593Smuzhiyun			list.append(m)
6029*4882a593Smuzhiyun	else:
6030*4882a593Smuzhiyun		list = re.finditer(start, html)
6031*4882a593Smuzhiyun	for match in list:
6032*4882a593Smuzhiyun		s = match.end()
6033*4882a593Smuzhiyun		e = cnt if (len(out) < 1 or s + 10000 > cnt) else s + 10000
6034*4882a593Smuzhiyun		m = re.search(end, html[s:e])
6035*4882a593Smuzhiyun		if not m:
6036*4882a593Smuzhiyun			break
6037*4882a593Smuzhiyun		e = s + m.start()
6038*4882a593Smuzhiyun		str = html[s:e]
6039*4882a593Smuzhiyun		if end == 'ms':
6040*4882a593Smuzhiyun			num = re.search(r'[-+]?\d*\.\d+|\d+', str)
6041*4882a593Smuzhiyun			str = num.group() if num else 'NaN'
6042*4882a593Smuzhiyun		if firstonly:
6043*4882a593Smuzhiyun			return str
6044*4882a593Smuzhiyun		out.append(str)
6045*4882a593Smuzhiyun	if firstonly:
6046*4882a593Smuzhiyun		return ''
6047*4882a593Smuzhiyun	return out
6048*4882a593Smuzhiyun
6049*4882a593Smuzhiyundef data_from_html(file, outpath, issues, fulldetail=False):
6050*4882a593Smuzhiyun	html = open(file, 'r').read()
6051*4882a593Smuzhiyun	sysvals.htmlfile = os.path.relpath(file, outpath)
6052*4882a593Smuzhiyun	# extract general info
6053*4882a593Smuzhiyun	suspend = find_in_html(html, 'Kernel Suspend', 'ms')
6054*4882a593Smuzhiyun	resume = find_in_html(html, 'Kernel Resume', 'ms')
6055*4882a593Smuzhiyun	sysinfo = find_in_html(html, '<div class="stamp sysinfo">', '</div>')
6056*4882a593Smuzhiyun	line = find_in_html(html, '<div class="stamp">', '</div>')
6057*4882a593Smuzhiyun	stmp = line.split()
6058*4882a593Smuzhiyun	if not suspend or not resume or len(stmp) != 8:
6059*4882a593Smuzhiyun		return False
6060*4882a593Smuzhiyun	try:
6061*4882a593Smuzhiyun		dt = datetime.strptime(' '.join(stmp[3:]), '%B %d %Y, %I:%M:%S %p')
6062*4882a593Smuzhiyun	except:
6063*4882a593Smuzhiyun		return False
6064*4882a593Smuzhiyun	sysvals.hostname = stmp[0]
6065*4882a593Smuzhiyun	tstr = dt.strftime('%Y/%m/%d %H:%M:%S')
6066*4882a593Smuzhiyun	error = find_in_html(html, '<table class="testfail"><tr><td>', '</td>')
6067*4882a593Smuzhiyun	if error:
6068*4882a593Smuzhiyun		m = re.match('[a-z0-9]* failed in (?P<p>\S*).*', error)
6069*4882a593Smuzhiyun		if m:
6070*4882a593Smuzhiyun			result = 'fail in %s' % m.group('p')
6071*4882a593Smuzhiyun		else:
6072*4882a593Smuzhiyun			result = 'fail'
6073*4882a593Smuzhiyun	else:
6074*4882a593Smuzhiyun		result = 'pass'
6075*4882a593Smuzhiyun	# extract error info
6076*4882a593Smuzhiyun	tp, ilist = False, []
6077*4882a593Smuzhiyun	extra = dict()
6078*4882a593Smuzhiyun	log = find_in_html(html, '<div id="dmesglog" style="display:none;">',
6079*4882a593Smuzhiyun		'</div>').strip()
6080*4882a593Smuzhiyun	if log:
6081*4882a593Smuzhiyun		d = Data(0)
6082*4882a593Smuzhiyun		d.end = 999999999
6083*4882a593Smuzhiyun		d.dmesgtext = log.split('\n')
6084*4882a593Smuzhiyun		tp = d.extractErrorInfo()
6085*4882a593Smuzhiyun		for msg in tp.msglist:
6086*4882a593Smuzhiyun			sysvals.errorSummary(issues, msg)
6087*4882a593Smuzhiyun		if stmp[2] == 'freeze':
6088*4882a593Smuzhiyun			extra = d.turbostatInfo()
6089*4882a593Smuzhiyun		elist = dict()
6090*4882a593Smuzhiyun		for dir in d.errorinfo:
6091*4882a593Smuzhiyun			for err in d.errorinfo[dir]:
6092*4882a593Smuzhiyun				if err[0] not in elist:
6093*4882a593Smuzhiyun					elist[err[0]] = 0
6094*4882a593Smuzhiyun				elist[err[0]] += 1
6095*4882a593Smuzhiyun		for i in elist:
6096*4882a593Smuzhiyun			ilist.append('%sx%d' % (i, elist[i]) if elist[i] > 1 else i)
6097*4882a593Smuzhiyun	wifi = find_in_html(html, 'Wifi Resume: ', '</td>')
6098*4882a593Smuzhiyun	if wifi:
6099*4882a593Smuzhiyun		extra['wifi'] = wifi
6100*4882a593Smuzhiyun	low = find_in_html(html, 'freeze time: <b>', ' ms</b>')
6101*4882a593Smuzhiyun	if low and 'waking' in low:
6102*4882a593Smuzhiyun		issue = 'FREEZEWAKE'
6103*4882a593Smuzhiyun		match = [i for i in issues if i['match'] == issue]
6104*4882a593Smuzhiyun		if len(match) > 0:
6105*4882a593Smuzhiyun			match[0]['count'] += 1
6106*4882a593Smuzhiyun			if sysvals.hostname not in match[0]['urls']:
6107*4882a593Smuzhiyun				match[0]['urls'][sysvals.hostname] = [sysvals.htmlfile]
6108*4882a593Smuzhiyun			elif sysvals.htmlfile not in match[0]['urls'][sysvals.hostname]:
6109*4882a593Smuzhiyun				match[0]['urls'][sysvals.hostname].append(sysvals.htmlfile)
6110*4882a593Smuzhiyun		else:
6111*4882a593Smuzhiyun			issues.append({
6112*4882a593Smuzhiyun				'match': issue, 'count': 1, 'line': issue,
6113*4882a593Smuzhiyun				'urls': {sysvals.hostname: [sysvals.htmlfile]},
6114*4882a593Smuzhiyun			})
6115*4882a593Smuzhiyun		ilist.append(issue)
6116*4882a593Smuzhiyun	# extract device info
6117*4882a593Smuzhiyun	devices = dict()
6118*4882a593Smuzhiyun	for line in html.split('\n'):
6119*4882a593Smuzhiyun		m = re.match(' *<div id=\"[a,0-9]*\" *title=\"(?P<title>.*)\" class=\"thread.*', line)
6120*4882a593Smuzhiyun		if not m or 'thread kth' in line or 'thread sec' in line:
6121*4882a593Smuzhiyun			continue
6122*4882a593Smuzhiyun		m = re.match('(?P<n>.*) \((?P<t>[0-9,\.]*) ms\) (?P<p>.*)', m.group('title'))
6123*4882a593Smuzhiyun		if not m:
6124*4882a593Smuzhiyun			continue
6125*4882a593Smuzhiyun		name, time, phase = m.group('n'), m.group('t'), m.group('p')
6126*4882a593Smuzhiyun		if ' async' in name or ' sync' in name:
6127*4882a593Smuzhiyun			name = ' '.join(name.split(' ')[:-1])
6128*4882a593Smuzhiyun		if phase.startswith('suspend'):
6129*4882a593Smuzhiyun			d = 'suspend'
6130*4882a593Smuzhiyun		elif phase.startswith('resume'):
6131*4882a593Smuzhiyun			d = 'resume'
6132*4882a593Smuzhiyun		else:
6133*4882a593Smuzhiyun			continue
6134*4882a593Smuzhiyun		if d not in devices:
6135*4882a593Smuzhiyun			devices[d] = dict()
6136*4882a593Smuzhiyun		if name not in devices[d]:
6137*4882a593Smuzhiyun			devices[d][name] = 0.0
6138*4882a593Smuzhiyun		devices[d][name] += float(time)
6139*4882a593Smuzhiyun	# create worst device info
6140*4882a593Smuzhiyun	worst = dict()
6141*4882a593Smuzhiyun	for d in ['suspend', 'resume']:
6142*4882a593Smuzhiyun		worst[d] = {'name':'', 'time': 0.0}
6143*4882a593Smuzhiyun		dev = devices[d] if d in devices else 0
6144*4882a593Smuzhiyun		if dev and len(dev.keys()) > 0:
6145*4882a593Smuzhiyun			n = sorted(dev, key=lambda k:(dev[k], k), reverse=True)[0]
6146*4882a593Smuzhiyun			worst[d]['name'], worst[d]['time'] = n, dev[n]
6147*4882a593Smuzhiyun	data = {
6148*4882a593Smuzhiyun		'mode': stmp[2],
6149*4882a593Smuzhiyun		'host': stmp[0],
6150*4882a593Smuzhiyun		'kernel': stmp[1],
6151*4882a593Smuzhiyun		'sysinfo': sysinfo,
6152*4882a593Smuzhiyun		'time': tstr,
6153*4882a593Smuzhiyun		'result': result,
6154*4882a593Smuzhiyun		'issues': ' '.join(ilist),
6155*4882a593Smuzhiyun		'suspend': suspend,
6156*4882a593Smuzhiyun		'resume': resume,
6157*4882a593Smuzhiyun		'devlist': devices,
6158*4882a593Smuzhiyun		'sus_worst': worst['suspend']['name'],
6159*4882a593Smuzhiyun		'sus_worsttime': worst['suspend']['time'],
6160*4882a593Smuzhiyun		'res_worst': worst['resume']['name'],
6161*4882a593Smuzhiyun		'res_worsttime': worst['resume']['time'],
6162*4882a593Smuzhiyun		'url': sysvals.htmlfile,
6163*4882a593Smuzhiyun	}
6164*4882a593Smuzhiyun	for key in extra:
6165*4882a593Smuzhiyun		data[key] = extra[key]
6166*4882a593Smuzhiyun	if fulldetail:
6167*4882a593Smuzhiyun		data['funclist'] = find_in_html(html, '<div title="', '" class="traceevent"', False)
6168*4882a593Smuzhiyun	if tp:
6169*4882a593Smuzhiyun		for arg in ['-multi ', '-info ']:
6170*4882a593Smuzhiyun			if arg in tp.cmdline:
6171*4882a593Smuzhiyun				data['target'] = tp.cmdline[tp.cmdline.find(arg):].split()[1]
6172*4882a593Smuzhiyun				break
6173*4882a593Smuzhiyun	return data
6174*4882a593Smuzhiyun
6175*4882a593Smuzhiyundef genHtml(subdir, force=False):
6176*4882a593Smuzhiyun	for dirname, dirnames, filenames in os.walk(subdir):
6177*4882a593Smuzhiyun		sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = ''
6178*4882a593Smuzhiyun		for filename in filenames:
6179*4882a593Smuzhiyun			file = os.path.join(dirname, filename)
6180*4882a593Smuzhiyun			if sysvals.usable(file):
6181*4882a593Smuzhiyun				if(re.match('.*_dmesg.txt', filename)):
6182*4882a593Smuzhiyun					sysvals.dmesgfile = file
6183*4882a593Smuzhiyun				elif(re.match('.*_ftrace.txt', filename)):
6184*4882a593Smuzhiyun					sysvals.ftracefile = file
6185*4882a593Smuzhiyun		sysvals.setOutputFile()
6186*4882a593Smuzhiyun		if (sysvals.dmesgfile or sysvals.ftracefile) and sysvals.htmlfile and \
6187*4882a593Smuzhiyun			(force or not sysvals.usable(sysvals.htmlfile)):
6188*4882a593Smuzhiyun			pprint('FTRACE: %s' % sysvals.ftracefile)
6189*4882a593Smuzhiyun			if sysvals.dmesgfile:
6190*4882a593Smuzhiyun				pprint('DMESG : %s' % sysvals.dmesgfile)
6191*4882a593Smuzhiyun			rerunTest()
6192*4882a593Smuzhiyun
6193*4882a593Smuzhiyun# Function: runSummary
6194*4882a593Smuzhiyun# Description:
6195*4882a593Smuzhiyun#	 create a summary of tests in a sub-directory
6196*4882a593Smuzhiyundef runSummary(subdir, local=True, genhtml=False):
6197*4882a593Smuzhiyun	inpath = os.path.abspath(subdir)
6198*4882a593Smuzhiyun	outpath = os.path.abspath('.') if local else inpath
6199*4882a593Smuzhiyun	pprint('Generating a summary of folder:\n   %s' % inpath)
6200*4882a593Smuzhiyun	if genhtml:
6201*4882a593Smuzhiyun		genHtml(subdir)
6202*4882a593Smuzhiyun	target, issues, testruns = '', [], []
6203*4882a593Smuzhiyun	desc = {'host':[],'mode':[],'kernel':[]}
6204*4882a593Smuzhiyun	for dirname, dirnames, filenames in os.walk(subdir):
6205*4882a593Smuzhiyun		for filename in filenames:
6206*4882a593Smuzhiyun			if(not re.match('.*.html', filename)):
6207*4882a593Smuzhiyun				continue
6208*4882a593Smuzhiyun			data = data_from_html(os.path.join(dirname, filename), outpath, issues)
6209*4882a593Smuzhiyun			if(not data):
6210*4882a593Smuzhiyun				continue
6211*4882a593Smuzhiyun			if 'target' in data:
6212*4882a593Smuzhiyun				target = data['target']
6213*4882a593Smuzhiyun			testruns.append(data)
6214*4882a593Smuzhiyun			for key in desc:
6215*4882a593Smuzhiyun				if data[key] not in desc[key]:
6216*4882a593Smuzhiyun					desc[key].append(data[key])
6217*4882a593Smuzhiyun	pprint('Summary files:')
6218*4882a593Smuzhiyun	if len(desc['host']) == len(desc['mode']) == len(desc['kernel']) == 1:
6219*4882a593Smuzhiyun		title = '%s %s %s' % (desc['host'][0], desc['kernel'][0], desc['mode'][0])
6220*4882a593Smuzhiyun		if target:
6221*4882a593Smuzhiyun			title += ' %s' % target
6222*4882a593Smuzhiyun	else:
6223*4882a593Smuzhiyun		title = inpath
6224*4882a593Smuzhiyun	createHTMLSummarySimple(testruns, os.path.join(outpath, 'summary.html'), title)
6225*4882a593Smuzhiyun	pprint('   summary.html         - tabular list of test data found')
6226*4882a593Smuzhiyun	createHTMLDeviceSummary(testruns, os.path.join(outpath, 'summary-devices.html'), title)
6227*4882a593Smuzhiyun	pprint('   summary-devices.html - kernel device list sorted by total execution time')
6228*4882a593Smuzhiyun	createHTMLIssuesSummary(testruns, issues, os.path.join(outpath, 'summary-issues.html'), title)
6229*4882a593Smuzhiyun	pprint('   summary-issues.html  - kernel issues found sorted by frequency')
6230*4882a593Smuzhiyun
6231*4882a593Smuzhiyun# Function: checkArgBool
6232*4882a593Smuzhiyun# Description:
6233*4882a593Smuzhiyun#	 check if a boolean string value is true or false
6234*4882a593Smuzhiyundef checkArgBool(name, value):
6235*4882a593Smuzhiyun	if value in switchvalues:
6236*4882a593Smuzhiyun		if value in switchoff:
6237*4882a593Smuzhiyun			return False
6238*4882a593Smuzhiyun		return True
6239*4882a593Smuzhiyun	doError('invalid boolean --> (%s: %s), use "true/false" or "1/0"' % (name, value), True)
6240*4882a593Smuzhiyun	return False
6241*4882a593Smuzhiyun
6242*4882a593Smuzhiyun# Function: configFromFile
6243*4882a593Smuzhiyun# Description:
6244*4882a593Smuzhiyun#	 Configure the script via the info in a config file
6245*4882a593Smuzhiyundef configFromFile(file):
6246*4882a593Smuzhiyun	Config = configparser.ConfigParser()
6247*4882a593Smuzhiyun
6248*4882a593Smuzhiyun	Config.read(file)
6249*4882a593Smuzhiyun	sections = Config.sections()
6250*4882a593Smuzhiyun	overridekprobes = False
6251*4882a593Smuzhiyun	overridedevkprobes = False
6252*4882a593Smuzhiyun	if 'Settings' in sections:
6253*4882a593Smuzhiyun		for opt in Config.options('Settings'):
6254*4882a593Smuzhiyun			value = Config.get('Settings', opt).lower()
6255*4882a593Smuzhiyun			option = opt.lower()
6256*4882a593Smuzhiyun			if(option == 'verbose'):
6257*4882a593Smuzhiyun				sysvals.verbose = checkArgBool(option, value)
6258*4882a593Smuzhiyun			elif(option == 'addlogs'):
6259*4882a593Smuzhiyun				sysvals.dmesglog = sysvals.ftracelog = checkArgBool(option, value)
6260*4882a593Smuzhiyun			elif(option == 'dev'):
6261*4882a593Smuzhiyun				sysvals.usedevsrc = checkArgBool(option, value)
6262*4882a593Smuzhiyun			elif(option == 'proc'):
6263*4882a593Smuzhiyun				sysvals.useprocmon = checkArgBool(option, value)
6264*4882a593Smuzhiyun			elif(option == 'x2'):
6265*4882a593Smuzhiyun				if checkArgBool(option, value):
6266*4882a593Smuzhiyun					sysvals.execcount = 2
6267*4882a593Smuzhiyun			elif(option == 'callgraph'):
6268*4882a593Smuzhiyun				sysvals.usecallgraph = checkArgBool(option, value)
6269*4882a593Smuzhiyun			elif(option == 'override-timeline-functions'):
6270*4882a593Smuzhiyun				overridekprobes = checkArgBool(option, value)
6271*4882a593Smuzhiyun			elif(option == 'override-dev-timeline-functions'):
6272*4882a593Smuzhiyun				overridedevkprobes = checkArgBool(option, value)
6273*4882a593Smuzhiyun			elif(option == 'skiphtml'):
6274*4882a593Smuzhiyun				sysvals.skiphtml = checkArgBool(option, value)
6275*4882a593Smuzhiyun			elif(option == 'sync'):
6276*4882a593Smuzhiyun				sysvals.sync = checkArgBool(option, value)
6277*4882a593Smuzhiyun			elif(option == 'rs' or option == 'runtimesuspend'):
6278*4882a593Smuzhiyun				if value in switchvalues:
6279*4882a593Smuzhiyun					if value in switchoff:
6280*4882a593Smuzhiyun						sysvals.rs = -1
6281*4882a593Smuzhiyun					else:
6282*4882a593Smuzhiyun						sysvals.rs = 1
6283*4882a593Smuzhiyun				else:
6284*4882a593Smuzhiyun					doError('invalid value --> (%s: %s), use "enable/disable"' % (option, value), True)
6285*4882a593Smuzhiyun			elif(option == 'display'):
6286*4882a593Smuzhiyun				disopt = ['on', 'off', 'standby', 'suspend']
6287*4882a593Smuzhiyun				if value not in disopt:
6288*4882a593Smuzhiyun					doError('invalid value --> (%s: %s), use %s' % (option, value, disopt), True)
6289*4882a593Smuzhiyun				sysvals.display = value
6290*4882a593Smuzhiyun			elif(option == 'gzip'):
6291*4882a593Smuzhiyun				sysvals.gzip = checkArgBool(option, value)
6292*4882a593Smuzhiyun			elif(option == 'cgfilter'):
6293*4882a593Smuzhiyun				sysvals.setCallgraphFilter(value)
6294*4882a593Smuzhiyun			elif(option == 'cgskip'):
6295*4882a593Smuzhiyun				if value in switchoff:
6296*4882a593Smuzhiyun					sysvals.cgskip = ''
6297*4882a593Smuzhiyun				else:
6298*4882a593Smuzhiyun					sysvals.cgskip = sysvals.configFile(val)
6299*4882a593Smuzhiyun					if(not sysvals.cgskip):
6300*4882a593Smuzhiyun						doError('%s does not exist' % sysvals.cgskip)
6301*4882a593Smuzhiyun			elif(option == 'cgtest'):
6302*4882a593Smuzhiyun				sysvals.cgtest = getArgInt('cgtest', value, 0, 1, False)
6303*4882a593Smuzhiyun			elif(option == 'cgphase'):
6304*4882a593Smuzhiyun				d = Data(0)
6305*4882a593Smuzhiyun				if value not in d.phasedef:
6306*4882a593Smuzhiyun					doError('invalid phase --> (%s: %s), valid phases are %s'\
6307*4882a593Smuzhiyun						% (option, value, d.phasedef.keys()), True)
6308*4882a593Smuzhiyun				sysvals.cgphase = value
6309*4882a593Smuzhiyun			elif(option == 'fadd'):
6310*4882a593Smuzhiyun				file = sysvals.configFile(value)
6311*4882a593Smuzhiyun				if(not file):
6312*4882a593Smuzhiyun					doError('%s does not exist' % value)
6313*4882a593Smuzhiyun				sysvals.addFtraceFilterFunctions(file)
6314*4882a593Smuzhiyun			elif(option == 'result'):
6315*4882a593Smuzhiyun				sysvals.result = value
6316*4882a593Smuzhiyun			elif(option == 'multi'):
6317*4882a593Smuzhiyun				nums = value.split()
6318*4882a593Smuzhiyun				if len(nums) != 2:
6319*4882a593Smuzhiyun					doError('multi requires 2 integers (exec_count and delay)', True)
6320*4882a593Smuzhiyun				sysvals.multiinit(nums[0], nums[1])
6321*4882a593Smuzhiyun			elif(option == 'devicefilter'):
6322*4882a593Smuzhiyun				sysvals.setDeviceFilter(value)
6323*4882a593Smuzhiyun			elif(option == 'expandcg'):
6324*4882a593Smuzhiyun				sysvals.cgexp = checkArgBool(option, value)
6325*4882a593Smuzhiyun			elif(option == 'srgap'):
6326*4882a593Smuzhiyun				if checkArgBool(option, value):
6327*4882a593Smuzhiyun					sysvals.srgap = 5
6328*4882a593Smuzhiyun			elif(option == 'mode'):
6329*4882a593Smuzhiyun				sysvals.suspendmode = value
6330*4882a593Smuzhiyun			elif(option == 'command' or option == 'cmd'):
6331*4882a593Smuzhiyun				sysvals.testcommand = value
6332*4882a593Smuzhiyun			elif(option == 'x2delay'):
6333*4882a593Smuzhiyun				sysvals.x2delay = getArgInt('x2delay', value, 0, 60000, False)
6334*4882a593Smuzhiyun			elif(option == 'predelay'):
6335*4882a593Smuzhiyun				sysvals.predelay = getArgInt('predelay', value, 0, 60000, False)
6336*4882a593Smuzhiyun			elif(option == 'postdelay'):
6337*4882a593Smuzhiyun				sysvals.postdelay = getArgInt('postdelay', value, 0, 60000, False)
6338*4882a593Smuzhiyun			elif(option == 'maxdepth'):
6339*4882a593Smuzhiyun				sysvals.max_graph_depth = getArgInt('maxdepth', value, 0, 1000, False)
6340*4882a593Smuzhiyun			elif(option == 'rtcwake'):
6341*4882a593Smuzhiyun				if value in switchoff:
6342*4882a593Smuzhiyun					sysvals.rtcwake = False
6343*4882a593Smuzhiyun				else:
6344*4882a593Smuzhiyun					sysvals.rtcwake = True
6345*4882a593Smuzhiyun					sysvals.rtcwaketime = getArgInt('rtcwake', value, 0, 3600, False)
6346*4882a593Smuzhiyun			elif(option == 'timeprec'):
6347*4882a593Smuzhiyun				sysvals.setPrecision(getArgInt('timeprec', value, 0, 6, False))
6348*4882a593Smuzhiyun			elif(option == 'mindev'):
6349*4882a593Smuzhiyun				sysvals.mindevlen = getArgFloat('mindev', value, 0.0, 10000.0, False)
6350*4882a593Smuzhiyun			elif(option == 'callloop-maxgap'):
6351*4882a593Smuzhiyun				sysvals.callloopmaxgap = getArgFloat('callloop-maxgap', value, 0.0, 1.0, False)
6352*4882a593Smuzhiyun			elif(option == 'callloop-maxlen'):
6353*4882a593Smuzhiyun				sysvals.callloopmaxgap = getArgFloat('callloop-maxlen', value, 0.0, 1.0, False)
6354*4882a593Smuzhiyun			elif(option == 'mincg'):
6355*4882a593Smuzhiyun				sysvals.mincglen = getArgFloat('mincg', value, 0.0, 10000.0, False)
6356*4882a593Smuzhiyun			elif(option == 'bufsize'):
6357*4882a593Smuzhiyun				sysvals.bufsize = getArgInt('bufsize', value, 1, 1024*1024*8, False)
6358*4882a593Smuzhiyun			elif(option == 'output-dir'):
6359*4882a593Smuzhiyun				sysvals.outdir = sysvals.setOutputFolder(value)
6360*4882a593Smuzhiyun
6361*4882a593Smuzhiyun	if sysvals.suspendmode == 'command' and not sysvals.testcommand:
6362*4882a593Smuzhiyun		doError('No command supplied for mode "command"')
6363*4882a593Smuzhiyun
6364*4882a593Smuzhiyun	# compatibility errors
6365*4882a593Smuzhiyun	if sysvals.usedevsrc and sysvals.usecallgraph:
6366*4882a593Smuzhiyun		doError('-dev is not compatible with -f')
6367*4882a593Smuzhiyun	if sysvals.usecallgraph and sysvals.useprocmon:
6368*4882a593Smuzhiyun		doError('-proc is not compatible with -f')
6369*4882a593Smuzhiyun
6370*4882a593Smuzhiyun	if overridekprobes:
6371*4882a593Smuzhiyun		sysvals.tracefuncs = dict()
6372*4882a593Smuzhiyun	if overridedevkprobes:
6373*4882a593Smuzhiyun		sysvals.dev_tracefuncs = dict()
6374*4882a593Smuzhiyun
6375*4882a593Smuzhiyun	kprobes = dict()
6376*4882a593Smuzhiyun	kprobesec = 'dev_timeline_functions_'+platform.machine()
6377*4882a593Smuzhiyun	if kprobesec in sections:
6378*4882a593Smuzhiyun		for name in Config.options(kprobesec):
6379*4882a593Smuzhiyun			text = Config.get(kprobesec, name)
6380*4882a593Smuzhiyun			kprobes[name] = (text, True)
6381*4882a593Smuzhiyun	kprobesec = 'timeline_functions_'+platform.machine()
6382*4882a593Smuzhiyun	if kprobesec in sections:
6383*4882a593Smuzhiyun		for name in Config.options(kprobesec):
6384*4882a593Smuzhiyun			if name in kprobes:
6385*4882a593Smuzhiyun				doError('Duplicate timeline function found "%s"' % (name))
6386*4882a593Smuzhiyun			text = Config.get(kprobesec, name)
6387*4882a593Smuzhiyun			kprobes[name] = (text, False)
6388*4882a593Smuzhiyun
6389*4882a593Smuzhiyun	for name in kprobes:
6390*4882a593Smuzhiyun		function = name
6391*4882a593Smuzhiyun		format = name
6392*4882a593Smuzhiyun		color = ''
6393*4882a593Smuzhiyun		args = dict()
6394*4882a593Smuzhiyun		text, dev = kprobes[name]
6395*4882a593Smuzhiyun		data = text.split()
6396*4882a593Smuzhiyun		i = 0
6397*4882a593Smuzhiyun		for val in data:
6398*4882a593Smuzhiyun			# bracketted strings are special formatting, read them separately
6399*4882a593Smuzhiyun			if val[0] == '[' and val[-1] == ']':
6400*4882a593Smuzhiyun				for prop in val[1:-1].split(','):
6401*4882a593Smuzhiyun					p = prop.split('=')
6402*4882a593Smuzhiyun					if p[0] == 'color':
6403*4882a593Smuzhiyun						try:
6404*4882a593Smuzhiyun							color = int(p[1], 16)
6405*4882a593Smuzhiyun							color = '#'+p[1]
6406*4882a593Smuzhiyun						except:
6407*4882a593Smuzhiyun							color = p[1]
6408*4882a593Smuzhiyun				continue
6409*4882a593Smuzhiyun			# first real arg should be the format string
6410*4882a593Smuzhiyun			if i == 0:
6411*4882a593Smuzhiyun				format = val
6412*4882a593Smuzhiyun			# all other args are actual function args
6413*4882a593Smuzhiyun			else:
6414*4882a593Smuzhiyun				d = val.split('=')
6415*4882a593Smuzhiyun				args[d[0]] = d[1]
6416*4882a593Smuzhiyun			i += 1
6417*4882a593Smuzhiyun		if not function or not format:
6418*4882a593Smuzhiyun			doError('Invalid kprobe: %s' % name)
6419*4882a593Smuzhiyun		for arg in re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', format):
6420*4882a593Smuzhiyun			if arg not in args:
6421*4882a593Smuzhiyun				doError('Kprobe "%s" is missing argument "%s"' % (name, arg))
6422*4882a593Smuzhiyun		if (dev and name in sysvals.dev_tracefuncs) or (not dev and name in sysvals.tracefuncs):
6423*4882a593Smuzhiyun			doError('Duplicate timeline function found "%s"' % (name))
6424*4882a593Smuzhiyun
6425*4882a593Smuzhiyun		kp = {
6426*4882a593Smuzhiyun			'name': name,
6427*4882a593Smuzhiyun			'func': function,
6428*4882a593Smuzhiyun			'format': format,
6429*4882a593Smuzhiyun			sysvals.archargs: args
6430*4882a593Smuzhiyun		}
6431*4882a593Smuzhiyun		if color:
6432*4882a593Smuzhiyun			kp['color'] = color
6433*4882a593Smuzhiyun		if dev:
6434*4882a593Smuzhiyun			sysvals.dev_tracefuncs[name] = kp
6435*4882a593Smuzhiyun		else:
6436*4882a593Smuzhiyun			sysvals.tracefuncs[name] = kp
6437*4882a593Smuzhiyun
6438*4882a593Smuzhiyun# Function: printHelp
6439*4882a593Smuzhiyun# Description:
6440*4882a593Smuzhiyun#	 print out the help text
6441*4882a593Smuzhiyundef printHelp():
6442*4882a593Smuzhiyun	pprint('\n%s v%s\n'\
6443*4882a593Smuzhiyun	'Usage: sudo sleepgraph <options> <commands>\n'\
6444*4882a593Smuzhiyun	'\n'\
6445*4882a593Smuzhiyun	'Description:\n'\
6446*4882a593Smuzhiyun	'  This tool is designed to assist kernel and OS developers in optimizing\n'\
6447*4882a593Smuzhiyun	'  their linux stack\'s suspend/resume time. Using a kernel image built\n'\
6448*4882a593Smuzhiyun	'  with a few extra options enabled, the tool will execute a suspend and\n'\
6449*4882a593Smuzhiyun	'  capture dmesg and ftrace data until resume is complete. This data is\n'\
6450*4882a593Smuzhiyun	'  transformed into a device timeline and an optional callgraph to give\n'\
6451*4882a593Smuzhiyun	'  a detailed view of which devices/subsystems are taking the most\n'\
6452*4882a593Smuzhiyun	'  time in suspend/resume.\n'\
6453*4882a593Smuzhiyun	'\n'\
6454*4882a593Smuzhiyun	'  If no specific command is given, the default behavior is to initiate\n'\
6455*4882a593Smuzhiyun	'  a suspend/resume and capture the dmesg/ftrace output as an html timeline.\n'\
6456*4882a593Smuzhiyun	'\n'\
6457*4882a593Smuzhiyun	'  Generates output files in subdirectory: suspend-yymmdd-HHMMSS\n'\
6458*4882a593Smuzhiyun	'   HTML output:                    <hostname>_<mode>.html\n'\
6459*4882a593Smuzhiyun	'   raw dmesg output:               <hostname>_<mode>_dmesg.txt\n'\
6460*4882a593Smuzhiyun	'   raw ftrace output:              <hostname>_<mode>_ftrace.txt\n'\
6461*4882a593Smuzhiyun	'\n'\
6462*4882a593Smuzhiyun	'Options:\n'\
6463*4882a593Smuzhiyun	'   -h           Print this help text\n'\
6464*4882a593Smuzhiyun	'   -v           Print the current tool version\n'\
6465*4882a593Smuzhiyun	'   -config fn   Pull arguments and config options from file fn\n'\
6466*4882a593Smuzhiyun	'   -verbose     Print extra information during execution and analysis\n'\
6467*4882a593Smuzhiyun	'   -m mode      Mode to initiate for suspend (default: %s)\n'\
6468*4882a593Smuzhiyun	'   -o name      Overrides the output subdirectory name when running a new test\n'\
6469*4882a593Smuzhiyun	'                default: suspend-{date}-{time}\n'\
6470*4882a593Smuzhiyun	'   -rtcwake t   Wakeup t seconds after suspend, set t to "off" to disable (default: 15)\n'\
6471*4882a593Smuzhiyun	'   -addlogs     Add the dmesg and ftrace logs to the html output\n'\
6472*4882a593Smuzhiyun	'   -noturbostat Dont use turbostat in freeze mode (default: disabled)\n'\
6473*4882a593Smuzhiyun	'   -srgap       Add a visible gap in the timeline between sus/res (default: disabled)\n'\
6474*4882a593Smuzhiyun	'   -skiphtml    Run the test and capture the trace logs, but skip the timeline (default: disabled)\n'\
6475*4882a593Smuzhiyun	'   -result fn   Export a results table to a text file for parsing.\n'\
6476*4882a593Smuzhiyun	'   -wifi        If a wifi connection is available, check that it reconnects after resume.\n'\
6477*4882a593Smuzhiyun	'  [testprep]\n'\
6478*4882a593Smuzhiyun	'   -sync        Sync the filesystems before starting the test\n'\
6479*4882a593Smuzhiyun	'   -rs on/off   Enable/disable runtime suspend for all devices, restore all after test\n'\
6480*4882a593Smuzhiyun	'   -display m   Change the display mode to m for the test (on/off/standby/suspend)\n'\
6481*4882a593Smuzhiyun	'  [advanced]\n'\
6482*4882a593Smuzhiyun	'   -gzip        Gzip the trace and dmesg logs to save space\n'\
6483*4882a593Smuzhiyun	'   -cmd {s}     Run the timeline over a custom command, e.g. "sync -d"\n'\
6484*4882a593Smuzhiyun	'   -proc        Add usermode process info into the timeline (default: disabled)\n'\
6485*4882a593Smuzhiyun	'   -dev         Add kernel function calls and threads to the timeline (default: disabled)\n'\
6486*4882a593Smuzhiyun	'   -x2          Run two suspend/resumes back to back (default: disabled)\n'\
6487*4882a593Smuzhiyun	'   -x2delay t   Include t ms delay between multiple test runs (default: 0 ms)\n'\
6488*4882a593Smuzhiyun	'   -predelay t  Include t ms delay before 1st suspend (default: 0 ms)\n'\
6489*4882a593Smuzhiyun	'   -postdelay t Include t ms delay after last resume (default: 0 ms)\n'\
6490*4882a593Smuzhiyun	'   -mindev ms   Discard all device blocks shorter than ms milliseconds (e.g. 0.001 for us)\n'\
6491*4882a593Smuzhiyun	'   -multi n d   Execute <n> consecutive tests at <d> seconds intervals. If <n> is followed\n'\
6492*4882a593Smuzhiyun	'                by a "d", "h", or "m" execute for <n> days, hours, or mins instead.\n'\
6493*4882a593Smuzhiyun	'                The outputs will be created in a new subdirectory with a summary page.\n'\
6494*4882a593Smuzhiyun	'   -maxfail n   Abort a -multi run after n consecutive fails (default is 0 = never abort)\n'\
6495*4882a593Smuzhiyun	'  [debug]\n'\
6496*4882a593Smuzhiyun	'   -f           Use ftrace to create device callgraphs (default: disabled)\n'\
6497*4882a593Smuzhiyun	'   -ftop        Use ftrace on the top level call: "%s" (default: disabled)\n'\
6498*4882a593Smuzhiyun	'   -maxdepth N  limit the callgraph data to N call levels (default: 0=all)\n'\
6499*4882a593Smuzhiyun	'   -expandcg    pre-expand the callgraph data in the html output (default: disabled)\n'\
6500*4882a593Smuzhiyun	'   -fadd file   Add functions to be graphed in the timeline from a list in a text file\n'\
6501*4882a593Smuzhiyun	'   -filter "d1,d2,..." Filter out all but this comma-delimited list of device names\n'\
6502*4882a593Smuzhiyun	'   -mincg  ms   Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)\n'\
6503*4882a593Smuzhiyun	'   -cgphase P   Only show callgraph data for phase P (e.g. suspend_late)\n'\
6504*4882a593Smuzhiyun	'   -cgtest N    Only show callgraph data for test N (e.g. 0 or 1 in an x2 run)\n'\
6505*4882a593Smuzhiyun	'   -timeprec N  Number of significant digits in timestamps (0:S, [3:ms], 6:us)\n'\
6506*4882a593Smuzhiyun	'   -cgfilter S  Filter the callgraph output in the timeline\n'\
6507*4882a593Smuzhiyun	'   -cgskip file Callgraph functions to skip, off to disable (default: cgskip.txt)\n'\
6508*4882a593Smuzhiyun	'   -bufsize N   Set trace buffer size to N kilo-bytes (default: all of free memory)\n'\
6509*4882a593Smuzhiyun	'   -devdump     Print out all the raw device data for each phase\n'\
6510*4882a593Smuzhiyun	'   -cgdump      Print out all the raw callgraph data\n'\
6511*4882a593Smuzhiyun	'\n'\
6512*4882a593Smuzhiyun	'Other commands:\n'\
6513*4882a593Smuzhiyun	'   -modes       List available suspend modes\n'\
6514*4882a593Smuzhiyun	'   -status      Test to see if the system is enabled to run this tool\n'\
6515*4882a593Smuzhiyun	'   -fpdt        Print out the contents of the ACPI Firmware Performance Data Table\n'\
6516*4882a593Smuzhiyun	'   -wificheck   Print out wifi connection info\n'\
6517*4882a593Smuzhiyun	'   -x<mode>     Test xset by toggling the given mode (on/off/standby/suspend)\n'\
6518*4882a593Smuzhiyun	'   -sysinfo     Print out system info extracted from BIOS\n'\
6519*4882a593Smuzhiyun	'   -devinfo     Print out the pm settings of all devices which support runtime suspend\n'\
6520*4882a593Smuzhiyun	'   -cmdinfo     Print out all the platform info collected before and after suspend/resume\n'\
6521*4882a593Smuzhiyun	'   -flist       Print the list of functions currently being captured in ftrace\n'\
6522*4882a593Smuzhiyun	'   -flistall    Print all functions capable of being captured in ftrace\n'\
6523*4882a593Smuzhiyun	'   -summary dir Create a summary of tests in this dir [-genhtml builds missing html]\n'\
6524*4882a593Smuzhiyun	'  [redo]\n'\
6525*4882a593Smuzhiyun	'   -ftrace ftracefile  Create HTML output using ftrace input (used with -dmesg)\n'\
6526*4882a593Smuzhiyun	'   -dmesg dmesgfile    Create HTML output using dmesg (used with -ftrace)\n'\
6527*4882a593Smuzhiyun	'' % (sysvals.title, sysvals.version, sysvals.suspendmode, sysvals.ftopfunc))
6528*4882a593Smuzhiyun	return True
6529*4882a593Smuzhiyun
6530*4882a593Smuzhiyun# ----------------- MAIN --------------------
6531*4882a593Smuzhiyun# exec start (skipped if script is loaded as library)
6532*4882a593Smuzhiyunif __name__ == '__main__':
6533*4882a593Smuzhiyun	genhtml = False
6534*4882a593Smuzhiyun	cmd = ''
6535*4882a593Smuzhiyun	simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall',
6536*4882a593Smuzhiyun		'-devinfo', '-status', '-xon', '-xoff', '-xstandby', '-xsuspend',
6537*4882a593Smuzhiyun		'-xinit', '-xreset', '-xstat', '-wificheck', '-cmdinfo']
6538*4882a593Smuzhiyun	if '-f' in sys.argv:
6539*4882a593Smuzhiyun		sysvals.cgskip = sysvals.configFile('cgskip.txt')
6540*4882a593Smuzhiyun	# loop through the command line arguments
6541*4882a593Smuzhiyun	args = iter(sys.argv[1:])
6542*4882a593Smuzhiyun	for arg in args:
6543*4882a593Smuzhiyun		if(arg == '-m'):
6544*4882a593Smuzhiyun			try:
6545*4882a593Smuzhiyun				val = next(args)
6546*4882a593Smuzhiyun			except:
6547*4882a593Smuzhiyun				doError('No mode supplied', True)
6548*4882a593Smuzhiyun			if val == 'command' and not sysvals.testcommand:
6549*4882a593Smuzhiyun				doError('No command supplied for mode "command"', True)
6550*4882a593Smuzhiyun			sysvals.suspendmode = val
6551*4882a593Smuzhiyun		elif(arg in simplecmds):
6552*4882a593Smuzhiyun			cmd = arg[1:]
6553*4882a593Smuzhiyun		elif(arg == '-h'):
6554*4882a593Smuzhiyun			printHelp()
6555*4882a593Smuzhiyun			sys.exit(0)
6556*4882a593Smuzhiyun		elif(arg == '-v'):
6557*4882a593Smuzhiyun			pprint("Version %s" % sysvals.version)
6558*4882a593Smuzhiyun			sys.exit(0)
6559*4882a593Smuzhiyun		elif(arg == '-x2'):
6560*4882a593Smuzhiyun			sysvals.execcount = 2
6561*4882a593Smuzhiyun		elif(arg == '-x2delay'):
6562*4882a593Smuzhiyun			sysvals.x2delay = getArgInt('-x2delay', args, 0, 60000)
6563*4882a593Smuzhiyun		elif(arg == '-predelay'):
6564*4882a593Smuzhiyun			sysvals.predelay = getArgInt('-predelay', args, 0, 60000)
6565*4882a593Smuzhiyun		elif(arg == '-postdelay'):
6566*4882a593Smuzhiyun			sysvals.postdelay = getArgInt('-postdelay', args, 0, 60000)
6567*4882a593Smuzhiyun		elif(arg == '-f'):
6568*4882a593Smuzhiyun			sysvals.usecallgraph = True
6569*4882a593Smuzhiyun		elif(arg == '-ftop'):
6570*4882a593Smuzhiyun			sysvals.usecallgraph = True
6571*4882a593Smuzhiyun			sysvals.ftop = True
6572*4882a593Smuzhiyun			sysvals.usekprobes = False
6573*4882a593Smuzhiyun		elif(arg == '-skiphtml'):
6574*4882a593Smuzhiyun			sysvals.skiphtml = True
6575*4882a593Smuzhiyun		elif(arg == '-cgdump'):
6576*4882a593Smuzhiyun			sysvals.cgdump = True
6577*4882a593Smuzhiyun		elif(arg == '-devdump'):
6578*4882a593Smuzhiyun			sysvals.devdump = True
6579*4882a593Smuzhiyun		elif(arg == '-genhtml'):
6580*4882a593Smuzhiyun			genhtml = True
6581*4882a593Smuzhiyun		elif(arg == '-addlogs'):
6582*4882a593Smuzhiyun			sysvals.dmesglog = sysvals.ftracelog = True
6583*4882a593Smuzhiyun		elif(arg == '-nologs'):
6584*4882a593Smuzhiyun			sysvals.dmesglog = sysvals.ftracelog = False
6585*4882a593Smuzhiyun		elif(arg == '-addlogdmesg'):
6586*4882a593Smuzhiyun			sysvals.dmesglog = True
6587*4882a593Smuzhiyun		elif(arg == '-addlogftrace'):
6588*4882a593Smuzhiyun			sysvals.ftracelog = True
6589*4882a593Smuzhiyun		elif(arg == '-noturbostat'):
6590*4882a593Smuzhiyun			sysvals.tstat = False
6591*4882a593Smuzhiyun		elif(arg == '-verbose'):
6592*4882a593Smuzhiyun			sysvals.verbose = True
6593*4882a593Smuzhiyun		elif(arg == '-proc'):
6594*4882a593Smuzhiyun			sysvals.useprocmon = True
6595*4882a593Smuzhiyun		elif(arg == '-dev'):
6596*4882a593Smuzhiyun			sysvals.usedevsrc = True
6597*4882a593Smuzhiyun		elif(arg == '-sync'):
6598*4882a593Smuzhiyun			sysvals.sync = True
6599*4882a593Smuzhiyun		elif(arg == '-wifi'):
6600*4882a593Smuzhiyun			sysvals.wifi = True
6601*4882a593Smuzhiyun		elif(arg == '-gzip'):
6602*4882a593Smuzhiyun			sysvals.gzip = True
6603*4882a593Smuzhiyun		elif(arg == '-info'):
6604*4882a593Smuzhiyun			try:
6605*4882a593Smuzhiyun				val = next(args)
6606*4882a593Smuzhiyun			except:
6607*4882a593Smuzhiyun				doError('-info requires one string argument', True)
6608*4882a593Smuzhiyun		elif(arg == '-rs'):
6609*4882a593Smuzhiyun			try:
6610*4882a593Smuzhiyun				val = next(args)
6611*4882a593Smuzhiyun			except:
6612*4882a593Smuzhiyun				doError('-rs requires "enable" or "disable"', True)
6613*4882a593Smuzhiyun			if val.lower() in switchvalues:
6614*4882a593Smuzhiyun				if val.lower() in switchoff:
6615*4882a593Smuzhiyun					sysvals.rs = -1
6616*4882a593Smuzhiyun				else:
6617*4882a593Smuzhiyun					sysvals.rs = 1
6618*4882a593Smuzhiyun			else:
6619*4882a593Smuzhiyun				doError('invalid option: %s, use "enable/disable" or "on/off"' % val, True)
6620*4882a593Smuzhiyun		elif(arg == '-display'):
6621*4882a593Smuzhiyun			try:
6622*4882a593Smuzhiyun				val = next(args)
6623*4882a593Smuzhiyun			except:
6624*4882a593Smuzhiyun				doError('-display requires an mode value', True)
6625*4882a593Smuzhiyun			disopt = ['on', 'off', 'standby', 'suspend']
6626*4882a593Smuzhiyun			if val.lower() not in disopt:
6627*4882a593Smuzhiyun				doError('valid display mode values are %s' % disopt, True)
6628*4882a593Smuzhiyun			sysvals.display = val.lower()
6629*4882a593Smuzhiyun		elif(arg == '-maxdepth'):
6630*4882a593Smuzhiyun			sysvals.max_graph_depth = getArgInt('-maxdepth', args, 0, 1000)
6631*4882a593Smuzhiyun		elif(arg == '-rtcwake'):
6632*4882a593Smuzhiyun			try:
6633*4882a593Smuzhiyun				val = next(args)
6634*4882a593Smuzhiyun			except:
6635*4882a593Smuzhiyun				doError('No rtcwake time supplied', True)
6636*4882a593Smuzhiyun			if val.lower() in switchoff:
6637*4882a593Smuzhiyun				sysvals.rtcwake = False
6638*4882a593Smuzhiyun			else:
6639*4882a593Smuzhiyun				sysvals.rtcwake = True
6640*4882a593Smuzhiyun				sysvals.rtcwaketime = getArgInt('-rtcwake', val, 0, 3600, False)
6641*4882a593Smuzhiyun		elif(arg == '-timeprec'):
6642*4882a593Smuzhiyun			sysvals.setPrecision(getArgInt('-timeprec', args, 0, 6))
6643*4882a593Smuzhiyun		elif(arg == '-mindev'):
6644*4882a593Smuzhiyun			sysvals.mindevlen = getArgFloat('-mindev', args, 0.0, 10000.0)
6645*4882a593Smuzhiyun		elif(arg == '-mincg'):
6646*4882a593Smuzhiyun			sysvals.mincglen = getArgFloat('-mincg', args, 0.0, 10000.0)
6647*4882a593Smuzhiyun		elif(arg == '-bufsize'):
6648*4882a593Smuzhiyun			sysvals.bufsize = getArgInt('-bufsize', args, 1, 1024*1024*8)
6649*4882a593Smuzhiyun		elif(arg == '-cgtest'):
6650*4882a593Smuzhiyun			sysvals.cgtest = getArgInt('-cgtest', args, 0, 1)
6651*4882a593Smuzhiyun		elif(arg == '-cgphase'):
6652*4882a593Smuzhiyun			try:
6653*4882a593Smuzhiyun				val = next(args)
6654*4882a593Smuzhiyun			except:
6655*4882a593Smuzhiyun				doError('No phase name supplied', True)
6656*4882a593Smuzhiyun			d = Data(0)
6657*4882a593Smuzhiyun			if val not in d.phasedef:
6658*4882a593Smuzhiyun				doError('invalid phase --> (%s: %s), valid phases are %s'\
6659*4882a593Smuzhiyun					% (arg, val, d.phasedef.keys()), True)
6660*4882a593Smuzhiyun			sysvals.cgphase = val
6661*4882a593Smuzhiyun		elif(arg == '-cgfilter'):
6662*4882a593Smuzhiyun			try:
6663*4882a593Smuzhiyun				val = next(args)
6664*4882a593Smuzhiyun			except:
6665*4882a593Smuzhiyun				doError('No callgraph functions supplied', True)
6666*4882a593Smuzhiyun			sysvals.setCallgraphFilter(val)
6667*4882a593Smuzhiyun		elif(arg == '-skipkprobe'):
6668*4882a593Smuzhiyun			try:
6669*4882a593Smuzhiyun				val = next(args)
6670*4882a593Smuzhiyun			except:
6671*4882a593Smuzhiyun				doError('No kprobe functions supplied', True)
6672*4882a593Smuzhiyun			sysvals.skipKprobes(val)
6673*4882a593Smuzhiyun		elif(arg == '-cgskip'):
6674*4882a593Smuzhiyun			try:
6675*4882a593Smuzhiyun				val = next(args)
6676*4882a593Smuzhiyun			except:
6677*4882a593Smuzhiyun				doError('No file supplied', True)
6678*4882a593Smuzhiyun			if val.lower() in switchoff:
6679*4882a593Smuzhiyun				sysvals.cgskip = ''
6680*4882a593Smuzhiyun			else:
6681*4882a593Smuzhiyun				sysvals.cgskip = sysvals.configFile(val)
6682*4882a593Smuzhiyun				if(not sysvals.cgskip):
6683*4882a593Smuzhiyun					doError('%s does not exist' % sysvals.cgskip)
6684*4882a593Smuzhiyun		elif(arg == '-callloop-maxgap'):
6685*4882a593Smuzhiyun			sysvals.callloopmaxgap = getArgFloat('-callloop-maxgap', args, 0.0, 1.0)
6686*4882a593Smuzhiyun		elif(arg == '-callloop-maxlen'):
6687*4882a593Smuzhiyun			sysvals.callloopmaxlen = getArgFloat('-callloop-maxlen', args, 0.0, 1.0)
6688*4882a593Smuzhiyun		elif(arg == '-cmd'):
6689*4882a593Smuzhiyun			try:
6690*4882a593Smuzhiyun				val = next(args)
6691*4882a593Smuzhiyun			except:
6692*4882a593Smuzhiyun				doError('No command string supplied', True)
6693*4882a593Smuzhiyun			sysvals.testcommand = val
6694*4882a593Smuzhiyun			sysvals.suspendmode = 'command'
6695*4882a593Smuzhiyun		elif(arg == '-expandcg'):
6696*4882a593Smuzhiyun			sysvals.cgexp = True
6697*4882a593Smuzhiyun		elif(arg == '-srgap'):
6698*4882a593Smuzhiyun			sysvals.srgap = 5
6699*4882a593Smuzhiyun		elif(arg == '-maxfail'):
6700*4882a593Smuzhiyun			sysvals.maxfail = getArgInt('-maxfail', args, 0, 1000000)
6701*4882a593Smuzhiyun		elif(arg == '-multi'):
6702*4882a593Smuzhiyun			try:
6703*4882a593Smuzhiyun				c, d = next(args), next(args)
6704*4882a593Smuzhiyun			except:
6705*4882a593Smuzhiyun				doError('-multi requires two values', True)
6706*4882a593Smuzhiyun			sysvals.multiinit(c, d)
6707*4882a593Smuzhiyun		elif(arg == '-o'):
6708*4882a593Smuzhiyun			try:
6709*4882a593Smuzhiyun				val = next(args)
6710*4882a593Smuzhiyun			except:
6711*4882a593Smuzhiyun				doError('No subdirectory name supplied', True)
6712*4882a593Smuzhiyun			sysvals.outdir = sysvals.setOutputFolder(val)
6713*4882a593Smuzhiyun		elif(arg == '-config'):
6714*4882a593Smuzhiyun			try:
6715*4882a593Smuzhiyun				val = next(args)
6716*4882a593Smuzhiyun			except:
6717*4882a593Smuzhiyun				doError('No text file supplied', True)
6718*4882a593Smuzhiyun			file = sysvals.configFile(val)
6719*4882a593Smuzhiyun			if(not file):
6720*4882a593Smuzhiyun				doError('%s does not exist' % val)
6721*4882a593Smuzhiyun			configFromFile(file)
6722*4882a593Smuzhiyun		elif(arg == '-fadd'):
6723*4882a593Smuzhiyun			try:
6724*4882a593Smuzhiyun				val = next(args)
6725*4882a593Smuzhiyun			except:
6726*4882a593Smuzhiyun				doError('No text file supplied', True)
6727*4882a593Smuzhiyun			file = sysvals.configFile(val)
6728*4882a593Smuzhiyun			if(not file):
6729*4882a593Smuzhiyun				doError('%s does not exist' % val)
6730*4882a593Smuzhiyun			sysvals.addFtraceFilterFunctions(file)
6731*4882a593Smuzhiyun		elif(arg == '-dmesg'):
6732*4882a593Smuzhiyun			try:
6733*4882a593Smuzhiyun				val = next(args)
6734*4882a593Smuzhiyun			except:
6735*4882a593Smuzhiyun				doError('No dmesg file supplied', True)
6736*4882a593Smuzhiyun			sysvals.notestrun = True
6737*4882a593Smuzhiyun			sysvals.dmesgfile = val
6738*4882a593Smuzhiyun			if(os.path.exists(sysvals.dmesgfile) == False):
6739*4882a593Smuzhiyun				doError('%s does not exist' % sysvals.dmesgfile)
6740*4882a593Smuzhiyun		elif(arg == '-ftrace'):
6741*4882a593Smuzhiyun			try:
6742*4882a593Smuzhiyun				val = next(args)
6743*4882a593Smuzhiyun			except:
6744*4882a593Smuzhiyun				doError('No ftrace file supplied', True)
6745*4882a593Smuzhiyun			sysvals.notestrun = True
6746*4882a593Smuzhiyun			sysvals.ftracefile = val
6747*4882a593Smuzhiyun			if(os.path.exists(sysvals.ftracefile) == False):
6748*4882a593Smuzhiyun				doError('%s does not exist' % sysvals.ftracefile)
6749*4882a593Smuzhiyun		elif(arg == '-summary'):
6750*4882a593Smuzhiyun			try:
6751*4882a593Smuzhiyun				val = next(args)
6752*4882a593Smuzhiyun			except:
6753*4882a593Smuzhiyun				doError('No directory supplied', True)
6754*4882a593Smuzhiyun			cmd = 'summary'
6755*4882a593Smuzhiyun			sysvals.outdir = val
6756*4882a593Smuzhiyun			sysvals.notestrun = True
6757*4882a593Smuzhiyun			if(os.path.isdir(val) == False):
6758*4882a593Smuzhiyun				doError('%s is not accesible' % val)
6759*4882a593Smuzhiyun		elif(arg == '-filter'):
6760*4882a593Smuzhiyun			try:
6761*4882a593Smuzhiyun				val = next(args)
6762*4882a593Smuzhiyun			except:
6763*4882a593Smuzhiyun				doError('No devnames supplied', True)
6764*4882a593Smuzhiyun			sysvals.setDeviceFilter(val)
6765*4882a593Smuzhiyun		elif(arg == '-result'):
6766*4882a593Smuzhiyun			try:
6767*4882a593Smuzhiyun				val = next(args)
6768*4882a593Smuzhiyun			except:
6769*4882a593Smuzhiyun				doError('No result file supplied', True)
6770*4882a593Smuzhiyun			sysvals.result = val
6771*4882a593Smuzhiyun			sysvals.signalHandlerInit()
6772*4882a593Smuzhiyun		else:
6773*4882a593Smuzhiyun			doError('Invalid argument: '+arg, True)
6774*4882a593Smuzhiyun
6775*4882a593Smuzhiyun	# compatibility errors
6776*4882a593Smuzhiyun	if(sysvals.usecallgraph and sysvals.usedevsrc):
6777*4882a593Smuzhiyun		doError('-dev is not compatible with -f')
6778*4882a593Smuzhiyun	if(sysvals.usecallgraph and sysvals.useprocmon):
6779*4882a593Smuzhiyun		doError('-proc is not compatible with -f')
6780*4882a593Smuzhiyun
6781*4882a593Smuzhiyun	if sysvals.usecallgraph and sysvals.cgskip:
6782*4882a593Smuzhiyun		sysvals.vprint('Using cgskip file: %s' % sysvals.cgskip)
6783*4882a593Smuzhiyun		sysvals.setCallgraphBlacklist(sysvals.cgskip)
6784*4882a593Smuzhiyun
6785*4882a593Smuzhiyun	# callgraph size cannot exceed device size
6786*4882a593Smuzhiyun	if sysvals.mincglen < sysvals.mindevlen:
6787*4882a593Smuzhiyun		sysvals.mincglen = sysvals.mindevlen
6788*4882a593Smuzhiyun
6789*4882a593Smuzhiyun	# remove existing buffers before calculating memory
6790*4882a593Smuzhiyun	if(sysvals.usecallgraph or sysvals.usedevsrc):
6791*4882a593Smuzhiyun		sysvals.fsetVal('16', 'buffer_size_kb')
6792*4882a593Smuzhiyun	sysvals.cpuInfo()
6793*4882a593Smuzhiyun
6794*4882a593Smuzhiyun	# just run a utility command and exit
6795*4882a593Smuzhiyun	if(cmd != ''):
6796*4882a593Smuzhiyun		ret = 0
6797*4882a593Smuzhiyun		if(cmd == 'status'):
6798*4882a593Smuzhiyun			if not statusCheck(True):
6799*4882a593Smuzhiyun				ret = 1
6800*4882a593Smuzhiyun		elif(cmd == 'fpdt'):
6801*4882a593Smuzhiyun			if not getFPDT(True):
6802*4882a593Smuzhiyun				ret = 1
6803*4882a593Smuzhiyun		elif(cmd == 'sysinfo'):
6804*4882a593Smuzhiyun			sysvals.printSystemInfo(True)
6805*4882a593Smuzhiyun		elif(cmd == 'devinfo'):
6806*4882a593Smuzhiyun			deviceInfo()
6807*4882a593Smuzhiyun		elif(cmd == 'modes'):
6808*4882a593Smuzhiyun			pprint(getModes())
6809*4882a593Smuzhiyun		elif(cmd == 'flist'):
6810*4882a593Smuzhiyun			sysvals.getFtraceFilterFunctions(True)
6811*4882a593Smuzhiyun		elif(cmd == 'flistall'):
6812*4882a593Smuzhiyun			sysvals.getFtraceFilterFunctions(False)
6813*4882a593Smuzhiyun		elif(cmd == 'summary'):
6814*4882a593Smuzhiyun			runSummary(sysvals.outdir, True, genhtml)
6815*4882a593Smuzhiyun		elif(cmd in ['xon', 'xoff', 'xstandby', 'xsuspend', 'xinit', 'xreset']):
6816*4882a593Smuzhiyun			sysvals.verbose = True
6817*4882a593Smuzhiyun			ret = displayControl(cmd[1:])
6818*4882a593Smuzhiyun		elif(cmd == 'xstat'):
6819*4882a593Smuzhiyun			pprint('Display Status: %s' % displayControl('stat').upper())
6820*4882a593Smuzhiyun		elif(cmd == 'wificheck'):
6821*4882a593Smuzhiyun			dev = sysvals.checkWifi()
6822*4882a593Smuzhiyun			if dev:
6823*4882a593Smuzhiyun				print('%s is connected' % sysvals.wifiDetails(dev))
6824*4882a593Smuzhiyun			else:
6825*4882a593Smuzhiyun				print('No wifi connection found')
6826*4882a593Smuzhiyun		elif(cmd == 'cmdinfo'):
6827*4882a593Smuzhiyun			for out in sysvals.cmdinfo(False, True):
6828*4882a593Smuzhiyun				print('[%s - %s]\n%s\n' % out)
6829*4882a593Smuzhiyun		sys.exit(ret)
6830*4882a593Smuzhiyun
6831*4882a593Smuzhiyun	# if instructed, re-analyze existing data files
6832*4882a593Smuzhiyun	if(sysvals.notestrun):
6833*4882a593Smuzhiyun		stamp = rerunTest(sysvals.outdir)
6834*4882a593Smuzhiyun		sysvals.outputResult(stamp)
6835*4882a593Smuzhiyun		sys.exit(0)
6836*4882a593Smuzhiyun
6837*4882a593Smuzhiyun	# verify that we can run a test
6838*4882a593Smuzhiyun	error = statusCheck()
6839*4882a593Smuzhiyun	if(error):
6840*4882a593Smuzhiyun		doError(error)
6841*4882a593Smuzhiyun
6842*4882a593Smuzhiyun	# extract mem/disk extra modes and convert
6843*4882a593Smuzhiyun	mode = sysvals.suspendmode
6844*4882a593Smuzhiyun	if mode.startswith('mem'):
6845*4882a593Smuzhiyun		memmode = mode.split('-', 1)[-1] if '-' in mode else 'deep'
6846*4882a593Smuzhiyun		if memmode == 'shallow':
6847*4882a593Smuzhiyun			mode = 'standby'
6848*4882a593Smuzhiyun		elif memmode ==  's2idle':
6849*4882a593Smuzhiyun			mode = 'freeze'
6850*4882a593Smuzhiyun		else:
6851*4882a593Smuzhiyun			mode = 'mem'
6852*4882a593Smuzhiyun		sysvals.memmode = memmode
6853*4882a593Smuzhiyun		sysvals.suspendmode = mode
6854*4882a593Smuzhiyun	if mode.startswith('disk-'):
6855*4882a593Smuzhiyun		sysvals.diskmode = mode.split('-', 1)[-1]
6856*4882a593Smuzhiyun		sysvals.suspendmode = 'disk'
6857*4882a593Smuzhiyun
6858*4882a593Smuzhiyun	sysvals.systemInfo(dmidecode(sysvals.mempath))
6859*4882a593Smuzhiyun
6860*4882a593Smuzhiyun	setRuntimeSuspend(True)
6861*4882a593Smuzhiyun	if sysvals.display:
6862*4882a593Smuzhiyun		displayControl('init')
6863*4882a593Smuzhiyun	failcnt, ret = 0, 0
6864*4882a593Smuzhiyun	if sysvals.multitest['run']:
6865*4882a593Smuzhiyun		# run multiple tests in a separate subdirectory
6866*4882a593Smuzhiyun		if not sysvals.outdir:
6867*4882a593Smuzhiyun			if 'time' in sysvals.multitest:
6868*4882a593Smuzhiyun				s = '-%dm' % sysvals.multitest['time']
6869*4882a593Smuzhiyun			else:
6870*4882a593Smuzhiyun				s = '-x%d' % sysvals.multitest['count']
6871*4882a593Smuzhiyun			sysvals.outdir = datetime.now().strftime('suspend-%y%m%d-%H%M%S'+s)
6872*4882a593Smuzhiyun		if not os.path.isdir(sysvals.outdir):
6873*4882a593Smuzhiyun			os.makedirs(sysvals.outdir)
6874*4882a593Smuzhiyun		sysvals.sudoUserchown(sysvals.outdir)
6875*4882a593Smuzhiyun		finish = datetime.now()
6876*4882a593Smuzhiyun		if 'time' in sysvals.multitest:
6877*4882a593Smuzhiyun			finish += timedelta(minutes=sysvals.multitest['time'])
6878*4882a593Smuzhiyun		for i in range(sysvals.multitest['count']):
6879*4882a593Smuzhiyun			sysvals.multistat(True, i, finish)
6880*4882a593Smuzhiyun			if i != 0 and sysvals.multitest['delay'] > 0:
6881*4882a593Smuzhiyun				pprint('Waiting %d seconds...' % (sysvals.multitest['delay']))
6882*4882a593Smuzhiyun				time.sleep(sysvals.multitest['delay'])
6883*4882a593Smuzhiyun			fmt = 'suspend-%y%m%d-%H%M%S'
6884*4882a593Smuzhiyun			sysvals.testdir = os.path.join(sysvals.outdir, datetime.now().strftime(fmt))
6885*4882a593Smuzhiyun			ret = runTest(i+1, True)
6886*4882a593Smuzhiyun			failcnt = 0 if not ret else failcnt + 1
6887*4882a593Smuzhiyun			if sysvals.maxfail > 0 and failcnt >= sysvals.maxfail:
6888*4882a593Smuzhiyun				pprint('Maximum fail count of %d reached, aborting multitest' % (sysvals.maxfail))
6889*4882a593Smuzhiyun				break
6890*4882a593Smuzhiyun			time.sleep(5)
6891*4882a593Smuzhiyun			sysvals.resetlog()
6892*4882a593Smuzhiyun			sysvals.multistat(False, i, finish)
6893*4882a593Smuzhiyun			if 'time' in sysvals.multitest and datetime.now() >= finish:
6894*4882a593Smuzhiyun				break
6895*4882a593Smuzhiyun		if not sysvals.skiphtml:
6896*4882a593Smuzhiyun			runSummary(sysvals.outdir, False, False)
6897*4882a593Smuzhiyun		sysvals.sudoUserchown(sysvals.outdir)
6898*4882a593Smuzhiyun	else:
6899*4882a593Smuzhiyun		if sysvals.outdir:
6900*4882a593Smuzhiyun			sysvals.testdir = sysvals.outdir
6901*4882a593Smuzhiyun		# run the test in the current directory
6902*4882a593Smuzhiyun		ret = runTest()
6903*4882a593Smuzhiyun	if sysvals.display:
6904*4882a593Smuzhiyun		displayControl('reset')
6905*4882a593Smuzhiyun	setRuntimeSuspend(False)
6906*4882a593Smuzhiyun	sys.exit(ret)
6907