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}"> {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+' → '+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}→</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('_', ' ') 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('<', '<').replace('>', '>') 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→</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>←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