Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

pm-graph: AnalyzeSuspend v5.0

- add -cgskip option to reduce callgraph output size
- add -cgfilter option to focus on a list of devices
- add -result option for exporting batch test results
- removed all phoronix hooks, use -result to enable batch testing
- change -usbtopo to -devinfo, now prints all devices
- add -gzip option to read/write logs in gz format
- add -bufsize option to manually control ftrace buffer size
- add -sync option to run filesystem sync prior to test
- add -display option to enable/disable the display prior to test
- add -rs option to enable/disable runtime suspend on all devices for test
- add installed config files to search path
- add kernel error/warning links into the timeline
- fix callgraph trace to better handle interrupts
- include command string and kernel params in timeline output header

Signed-off-by: Todd Brandt <todd.e.brandt@linux.intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Todd E Brandt and committed by
Rafael J. Wysocki
700abc90 d83a76a8

+1052 -618
+41 -6
tools/power/pm-graph/sleepgraph.8
··· 52 52 \fB-addlogs\fR 53 53 Add the dmesg and ftrace logs to the html output. They will be viewable by 54 54 clicking buttons in the timeline. 55 + .TP 56 + \fB-result \fIfile\fR 57 + Export a results table to a text file for parsing. 58 + .TP 59 + \fB-sync\fR 60 + Sync the filesystems before starting the test. This reduces the size of 61 + the sys_sync call which happens in the suspend_prepare phase. 62 + .TP 63 + \fB-rs \fIenable/disable\fR 64 + During test, enable/disable runtime suspend for all devices. The test is delayed 65 + by 5 seconds to allow runtime suspend changes to occur. The settings are restored 66 + after the test is complete. 67 + .TP 68 + \fB-display \fIon/off\fR 69 + Turn the display on or off for the test using the xset command. This helps 70 + maintain the consistecy of test data for better comparison. 71 + .TP 72 + \fB-skiphtml\fR 73 + Run the test and capture the trace logs, but skip the timeline generation. 55 74 56 75 .SS "advanced" 76 + .TP 77 + \fB-gzip\fR 78 + Gzip the trace and dmesg logs to save space. The tool can also read in gzipped 79 + logs for processing. 57 80 .TP 58 81 \fB-cmd \fIstr\fR 59 82 Run the timeline over a custom suspend command, e.g. pm-suspend. By default ··· 137 114 which are barely visible in the timeline. 138 115 The value is a float: e.g. 0.001 represents 1 us. 139 116 .TP 117 + \fB-cgfilter \fI"func1,func2,..."\fR 118 + Reduce callgraph output in the timeline by limiting it to a list of calls. The 119 + argument can be a single function name or a comma delimited list. 120 + (default: none) 121 + .TP 122 + \fB-cgskip \fIfile\fR 123 + Reduce callgraph timeline size by skipping over uninteresting functions 124 + in the trace, e.g. printk or console_unlock. The functions listed 125 + in this file will show up as empty leaves in the callgraph with only the start/end 126 + times displayed. cgskip.txt is used automatically if found in the path, so 127 + use "off" to disable completely (default: cgskip.txt) 128 + .TP 140 129 \fB-cgphase \fIp\fR 141 130 Only show callgraph data for phase \fIp\fR (e.g. suspend_late). 142 131 .TP ··· 157 122 .TP 158 123 \fB-timeprec \fIn\fR 159 124 Number of significant digits in timestamps (0:S, [3:ms], 6:us). 125 + .TP 126 + \fB-bufsize \fIN\fR 127 + Set trace buffer size to N kilo-bytes (default: all of free memory up to 3GB) 160 128 161 129 .SH COMMANDS 162 130 .TP ··· 182 144 \fB-sysinfo\fR 183 145 Print out system info extracted from BIOS. Reads /dev/mem directly instead of going through dmidecode. 184 146 .TP 185 - \fB-usbtopo\fR 186 - Print out the current USB topology with power info. 187 - .TP 188 - \fB-usbauto\fR 189 - Enable autosuspend for all connected USB devices. 147 + \fB-devinfo\fR 148 + Print out the pm settings of all devices which support runtime suspend. 190 149 .TP 191 150 \fB-flist\fR 192 151 Print the list of ftrace functions currently being captured. Functions ··· 233 198 .PP 234 199 Execute a standby with a 15 second wakeup. Change the output folder name. 235 200 .IP 236 - \f(CW$ sudo sleepgraph -m standby -rtcwake 15 -o "standby-{hostname}-{date}-{time}"\fR 201 + \f(CW$ sudo sleepgraph -m standby -rtcwake 15 -o "standby-{host}-{date}-{time}"\fR 237 202 .PP 238 203 Execute a freeze with no wakeup (require keypress). Change output folder name. 239 204 .IP
+1011 -612
tools/power/pm-graph/sleepgraph.py
··· 19 19 # Home Page 20 20 # https://01.org/suspendresume 21 21 # Source repo 22 - # https://github.com/01org/pm-graph 22 + # git@github.com:01org/pm-graph 23 23 # 24 24 # Description: 25 25 # This tool is designed to assist kernel and OS developers in optimizing ··· 57 57 from datetime import datetime 58 58 import struct 59 59 import ConfigParser 60 + import gzip 60 61 from threading import Thread 61 62 from subprocess import call, Popen, PIPE 62 63 ··· 69 68 # store system values and test parameters 70 69 class SystemValues: 71 70 title = 'SleepGraph' 72 - version = '4.7' 71 + version = '5.0' 73 72 ansi = False 73 + rs = 0 74 + display = 0 75 + gzip = False 76 + sync = False 74 77 verbose = False 75 78 testlog = True 76 79 dmesglog = False ··· 83 78 mincglen = 0.0 84 79 cgphase = '' 85 80 cgtest = -1 81 + cgskip = '' 82 + multitest = {'run': False, 'count': 0, 'delay': 0} 86 83 max_graph_depth = 0 87 84 callloopmaxgap = 0.0001 88 85 callloopmaxlen = 0.005 86 + bufsize = 0 89 87 cpucount = 0 90 88 memtotal = 204800 89 + memfree = 204800 91 90 srgap = 0 92 91 cgexp = False 93 92 testdir = '' 93 + outdir = '' 94 94 tpath = '/sys/kernel/debug/tracing/' 95 95 fpdtpath = '/sys/firmware/acpi/tables/FPDT' 96 96 epath = '/sys/kernel/debug/tracing/events/power/' ··· 119 109 dmesgfile = '' 120 110 ftracefile = '' 121 111 htmlfile = 'output.html' 122 - embedded = False 112 + result = '' 123 113 rtcwake = True 124 114 rtcwaketime = 15 125 115 rtcpath = '' 126 116 devicefilter = [] 117 + cgfilter = [] 127 118 stamp = 0 128 119 execcount = 1 129 120 x2delay = 0 121 + skiphtml = False 130 122 usecallgraph = False 131 123 usetraceevents = False 132 - usetraceeventsonly = False 133 124 usetracemarkers = True 134 125 usekprobes = True 135 126 usedevsrc = False 136 127 useprocmon = False 137 128 notestrun = False 129 + cgdump = False 138 130 mixedphaseheight = True 139 131 devprops = dict() 140 132 predelay = 0 ··· 146 134 tracertypefmt = '# tracer: (?P<t>.*)' 147 135 firmwarefmt = '# fwsuspend (?P<s>[0-9]*) fwresume (?P<r>[0-9]*)$' 148 136 tracefuncs = { 149 - 'sys_sync': dict(), 150 - 'pm_prepare_console': dict(), 151 - 'pm_notifier_call_chain': dict(), 152 - 'freeze_processes': dict(), 153 - 'freeze_kernel_threads': dict(), 154 - 'pm_restrict_gfp_mask': dict(), 155 - 'acpi_suspend_begin': dict(), 156 - 'suspend_console': dict(), 157 - 'acpi_pm_prepare': dict(), 158 - 'syscore_suspend': dict(), 159 - 'arch_enable_nonboot_cpus_end': dict(), 160 - 'syscore_resume': dict(), 161 - 'acpi_pm_finish': dict(), 162 - 'resume_console': dict(), 163 - 'acpi_pm_end': dict(), 164 - 'pm_restore_gfp_mask': dict(), 165 - 'thaw_processes': dict(), 166 - 'pm_restore_console': dict(), 137 + 'sys_sync': {}, 138 + '__pm_notifier_call_chain': {}, 139 + 'pm_prepare_console': {}, 140 + 'pm_notifier_call_chain': {}, 141 + 'freeze_processes': {}, 142 + 'freeze_kernel_threads': {}, 143 + 'pm_restrict_gfp_mask': {}, 144 + 'acpi_suspend_begin': {}, 145 + 'acpi_hibernation_begin': {}, 146 + 'acpi_hibernation_enter': {}, 147 + 'acpi_hibernation_leave': {}, 148 + 'acpi_pm_freeze': {}, 149 + 'acpi_pm_thaw': {}, 150 + 'hibernate_preallocate_memory': {}, 151 + 'create_basic_memory_bitmaps': {}, 152 + 'swsusp_write': {}, 153 + 'suspend_console': {}, 154 + 'acpi_pm_prepare': {}, 155 + 'syscore_suspend': {}, 156 + 'arch_enable_nonboot_cpus_end': {}, 157 + 'syscore_resume': {}, 158 + 'acpi_pm_finish': {}, 159 + 'resume_console': {}, 160 + 'acpi_pm_end': {}, 161 + 'pm_restore_gfp_mask': {}, 162 + 'thaw_processes': {}, 163 + 'pm_restore_console': {}, 167 164 'CPU_OFF': { 168 165 'func':'_cpu_down', 169 166 'args_x86_64': {'cpu':'%di:s32'}, ··· 194 173 'mutex_lock_slowpath': { 'func':'__mutex_lock_slowpath', 'ub': 1 }, 195 174 'acpi_os_stall': {'ub': 1}, 196 175 # ACPI 197 - 'acpi_resume_power_resources': dict(), 198 - 'acpi_ps_parse_aml': dict(), 176 + 'acpi_resume_power_resources': {}, 177 + 'acpi_ps_parse_aml': {}, 199 178 # filesystem 200 - 'ext4_sync_fs': dict(), 179 + 'ext4_sync_fs': {}, 201 180 # 80211 202 - 'iwlagn_mac_start': dict(), 203 - 'iwlagn_alloc_bcast_station': dict(), 204 - 'iwl_trans_pcie_start_hw': dict(), 205 - 'iwl_trans_pcie_start_fw': dict(), 206 - 'iwl_run_init_ucode': dict(), 207 - 'iwl_load_ucode_wait_alive': dict(), 208 - 'iwl_alive_start': dict(), 209 - 'iwlagn_mac_stop': dict(), 210 - 'iwlagn_mac_suspend': dict(), 211 - 'iwlagn_mac_resume': dict(), 212 - 'iwlagn_mac_add_interface': dict(), 213 - 'iwlagn_mac_remove_interface': dict(), 214 - 'iwlagn_mac_change_interface': dict(), 215 - 'iwlagn_mac_config': dict(), 216 - 'iwlagn_configure_filter': dict(), 217 - 'iwlagn_mac_hw_scan': dict(), 218 - 'iwlagn_bss_info_changed': dict(), 219 - 'iwlagn_mac_channel_switch': dict(), 220 - 'iwlagn_mac_flush': dict(), 181 + 'iwlagn_mac_start': {}, 182 + 'iwlagn_alloc_bcast_station': {}, 183 + 'iwl_trans_pcie_start_hw': {}, 184 + 'iwl_trans_pcie_start_fw': {}, 185 + 'iwl_run_init_ucode': {}, 186 + 'iwl_load_ucode_wait_alive': {}, 187 + 'iwl_alive_start': {}, 188 + 'iwlagn_mac_stop': {}, 189 + 'iwlagn_mac_suspend': {}, 190 + 'iwlagn_mac_resume': {}, 191 + 'iwlagn_mac_add_interface': {}, 192 + 'iwlagn_mac_remove_interface': {}, 193 + 'iwlagn_mac_change_interface': {}, 194 + 'iwlagn_mac_config': {}, 195 + 'iwlagn_configure_filter': {}, 196 + 'iwlagn_mac_hw_scan': {}, 197 + 'iwlagn_bss_info_changed': {}, 198 + 'iwlagn_mac_channel_switch': {}, 199 + 'iwlagn_mac_flush': {}, 221 200 # ATA 222 201 'ata_eh_recover': { 'args_x86_64': {'port':'+36(%di):s32'} }, 223 202 # i915 224 - 'i915_gem_resume': dict(), 225 - 'i915_restore_state': dict(), 226 - 'intel_opregion_setup': dict(), 227 - 'g4x_pre_enable_dp': dict(), 228 - 'vlv_pre_enable_dp': dict(), 229 - 'chv_pre_enable_dp': dict(), 230 - 'g4x_enable_dp': dict(), 231 - 'vlv_enable_dp': dict(), 232 - 'intel_hpd_init': dict(), 233 - 'intel_opregion_register': dict(), 234 - 'intel_dp_detect': dict(), 235 - 'intel_hdmi_detect': dict(), 236 - 'intel_opregion_init': dict(), 237 - 'intel_fbdev_set_suspend': dict(), 203 + 'i915_gem_resume': {}, 204 + 'i915_restore_state': {}, 205 + 'intel_opregion_setup': {}, 206 + 'g4x_pre_enable_dp': {}, 207 + 'vlv_pre_enable_dp': {}, 208 + 'chv_pre_enable_dp': {}, 209 + 'g4x_enable_dp': {}, 210 + 'vlv_enable_dp': {}, 211 + 'intel_hpd_init': {}, 212 + 'intel_opregion_register': {}, 213 + 'intel_dp_detect': {}, 214 + 'intel_hdmi_detect': {}, 215 + 'intel_opregion_init': {}, 216 + 'intel_fbdev_set_suspend': {}, 238 217 } 218 + cgblacklist = [] 239 219 kprobes = dict() 240 220 timeformat = '%.3f' 221 + cmdline = '%s %s' % \ 222 + (os.path.basename(sys.argv[0]), string.join(sys.argv[1:], ' ')) 241 223 def __init__(self): 242 - # if this is a phoronix test run, set some default options 243 - if('LOG_FILE' in os.environ and 'TEST_RESULTS_IDENTIFIER' in os.environ): 244 - self.embedded = True 245 - self.dmesglog = self.ftracelog = True 246 - self.htmlfile = os.environ['LOG_FILE'] 247 224 self.archargs = 'args_'+platform.machine() 248 225 self.hostname = platform.node() 249 226 if(self.hostname == ''): ··· 256 237 if (hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()): 257 238 self.ansi = True 258 239 self.testdir = datetime.now().strftime('suspend-%y%m%d-%H%M%S') 240 + def vprint(self, msg): 241 + self.logmsg += msg+'\n' 242 + if(self.verbose): 243 + print(msg) 259 244 def rootCheck(self, fatal=True): 260 245 if(os.access(self.powerfile, os.W_OK)): 261 246 return True 262 247 if fatal: 263 - doError('This command requires sysfs mount and root access') 248 + msg = 'This command requires sysfs mount and root access' 249 + print('ERROR: %s\n') % msg 250 + self.outputResult({'error':msg}) 251 + sys.exit() 264 252 return False 265 253 def rootUser(self, fatal=False): 266 254 if 'USER' in os.environ and os.environ['USER'] == 'root': 267 255 return True 268 256 if fatal: 269 - doError('This command must be run as root') 257 + msg = 'This command must be run as root' 258 + print('ERROR: %s\n') % msg 259 + self.outputResult({'error':msg}) 260 + sys.exit() 270 261 return False 262 + def getExec(self, cmd): 263 + dirlist = ['/sbin', '/bin', '/usr/sbin', '/usr/bin', 264 + '/usr/local/sbin', '/usr/local/bin'] 265 + for path in dirlist: 266 + cmdfull = os.path.join(path, cmd) 267 + if os.path.exists(cmdfull): 268 + return cmdfull 269 + return '' 271 270 def setPrecision(self, num): 272 271 if num < 0 or num > 6: 273 272 return ··· 295 258 n = datetime.now() 296 259 args['date'] = n.strftime('%y%m%d') 297 260 args['time'] = n.strftime('%H%M%S') 298 - args['hostname'] = self.hostname 261 + args['hostname'] = args['host'] = self.hostname 299 262 return value.format(**args) 300 263 def setOutputFile(self): 301 264 if self.dmesgfile != '': 302 - m = re.match('(?P<name>.*)_dmesg\.txt$', self.dmesgfile) 265 + m = re.match('(?P<name>.*)_dmesg\.txt.*', self.dmesgfile) 303 266 if(m): 304 267 self.htmlfile = m.group('name')+'.html' 305 268 if self.ftracefile != '': 306 - m = re.match('(?P<name>.*)_ftrace\.txt$', self.ftracefile) 269 + m = re.match('(?P<name>.*)_ftrace\.txt.*', self.ftracefile) 307 270 if(m): 308 271 self.htmlfile = m.group('name')+'.html' 309 272 def systemInfo(self, info): ··· 320 283 c = info['processor-version'] 321 284 if 'bios-version' in info: 322 285 b = info['bios-version'] 323 - self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | numcpu:%d | memsz:%d' % \ 324 - (m, p, c, b, self.cpucount, self.memtotal) 325 - def printSystemInfo(self): 286 + self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | numcpu:%d | memsz:%d | memfr:%d' % \ 287 + (m, p, c, b, self.cpucount, self.memtotal, self.memfree) 288 + def printSystemInfo(self, fatal=False): 326 289 self.rootCheck(True) 327 - out = dmidecode(self.mempath, True) 290 + out = dmidecode(self.mempath, fatal) 291 + if len(out) < 1: 292 + return 328 293 fmt = '%-24s: %s' 329 294 for name in sorted(out): 330 295 print fmt % (name, out[name]) 331 296 print fmt % ('cpucount', ('%d' % self.cpucount)) 332 297 print fmt % ('memtotal', ('%d kB' % self.memtotal)) 298 + print fmt % ('memfree', ('%d kB' % self.memfree)) 333 299 def cpuInfo(self): 334 300 self.cpucount = 0 335 301 fp = open('/proc/cpuinfo', 'r') ··· 345 305 m = re.match('^MemTotal:[ \t]*(?P<sz>[0-9]*) *kB', line) 346 306 if m: 347 307 self.memtotal = int(m.group('sz')) 348 - break 308 + m = re.match('^MemFree:[ \t]*(?P<sz>[0-9]*) *kB', line) 309 + if m: 310 + self.memfree = int(m.group('sz')) 349 311 fp.close() 350 312 def initTestOutput(self, name): 351 313 self.prefix = self.hostname ··· 357 315 testtime = datetime.now().strftime(fmt) 358 316 self.teststamp = \ 359 317 '# '+testtime+' '+self.prefix+' '+self.suspendmode+' '+kver 360 - if(self.embedded): 361 - self.dmesgfile = \ 362 - '/tmp/'+testtime+'_'+self.suspendmode+'_dmesg.txt' 363 - self.ftracefile = \ 364 - '/tmp/'+testtime+'_'+self.suspendmode+'_ftrace.txt' 365 - return 318 + ext = '' 319 + if self.gzip: 320 + ext = '.gz' 366 321 self.dmesgfile = \ 367 - self.testdir+'/'+self.prefix+'_'+self.suspendmode+'_dmesg.txt' 322 + self.testdir+'/'+self.prefix+'_'+self.suspendmode+'_dmesg.txt'+ext 368 323 self.ftracefile = \ 369 - self.testdir+'/'+self.prefix+'_'+self.suspendmode+'_ftrace.txt' 324 + self.testdir+'/'+self.prefix+'_'+self.suspendmode+'_ftrace.txt'+ext 370 325 self.htmlfile = \ 371 326 self.testdir+'/'+self.prefix+'_'+self.suspendmode+'.html' 372 327 if not os.path.isdir(self.testdir): 373 328 os.mkdir(self.testdir) 329 + def getValueList(self, value): 330 + out = [] 331 + for i in value.split(','): 332 + if i.strip(): 333 + out.append(i.strip()) 334 + return out 374 335 def setDeviceFilter(self, value): 375 - self.devicefilter = [] 376 - if value: 377 - value = value.split(',') 378 - for i in value: 379 - self.devicefilter.append(i.strip()) 336 + self.devicefilter = self.getValueList(value) 337 + def setCallgraphFilter(self, value): 338 + self.cgfilter = self.getValueList(value) 339 + def setCallgraphBlacklist(self, file): 340 + self.cgblacklist = self.listFromFile(file) 380 341 def rtcWakeAlarmOn(self): 381 342 call('echo 0 > '+self.rtcpath+'/wakealarm', shell=True) 382 - outD = open(self.rtcpath+'/date', 'r').read().strip() 383 - outT = open(self.rtcpath+'/time', 'r').read().strip() 384 - mD = re.match('^(?P<y>[0-9]*)-(?P<m>[0-9]*)-(?P<d>[0-9]*)', outD) 385 - mT = re.match('^(?P<h>[0-9]*):(?P<m>[0-9]*):(?P<s>[0-9]*)', outT) 386 - if(mD and mT): 387 - # get the current time from hardware 388 - utcoffset = int((datetime.now() - datetime.utcnow()).total_seconds()) 389 - dt = datetime(\ 390 - int(mD.group('y')), int(mD.group('m')), int(mD.group('d')), 391 - int(mT.group('h')), int(mT.group('m')), int(mT.group('s'))) 392 - nowtime = int(dt.strftime('%s')) + utcoffset 343 + nowtime = open(self.rtcpath+'/since_epoch', 'r').read().strip() 344 + if nowtime: 345 + nowtime = int(nowtime) 393 346 else: 394 347 # if hardware time fails, use the software time 395 348 nowtime = int(datetime.now().strftime('%s')) ··· 406 369 ktime = m.group('ktime') 407 370 fp.close() 408 371 self.dmesgstart = float(ktime) 409 - def getdmesg(self): 372 + def getdmesg(self, fwdata=[]): 373 + op = self.writeDatafileHeader(sysvals.dmesgfile, fwdata) 410 374 # store all new dmesg lines since initdmesg was called 411 375 fp = Popen('dmesg', stdout=PIPE).stdout 412 - op = open(self.dmesgfile, 'a') 413 376 for line in fp: 414 377 line = line.replace('\r\n', '') 415 378 idx = line.find('[') ··· 423 386 op.write(line) 424 387 fp.close() 425 388 op.close() 426 - def addFtraceFilterFunctions(self, file): 389 + def listFromFile(self, file): 390 + list = [] 427 391 fp = open(file) 428 - list = fp.read().split('\n') 392 + for i in fp.read().split('\n'): 393 + i = i.strip() 394 + if i and i[0] != '#': 395 + list.append(i) 429 396 fp.close() 430 - for i in list: 397 + return list 398 + def addFtraceFilterFunctions(self, file): 399 + for i in self.listFromFile(file): 431 400 if len(i) < 2: 432 401 continue 433 402 self.tracefuncs[i] = dict() ··· 442 399 if not current: 443 400 call('cat '+self.tpath+'available_filter_functions', shell=True) 444 401 return 445 - fp = open(self.tpath+'available_filter_functions') 446 - master = fp.read().split('\n') 447 - fp.close() 402 + master = self.listFromFile(self.tpath+'available_filter_functions') 448 403 for i in self.tracefuncs: 449 404 if 'func' in self.tracefuncs[i]: 450 405 i = self.tracefuncs[i]['func'] ··· 451 410 else: 452 411 print self.colorText(i) 453 412 def setFtraceFilterFunctions(self, list): 454 - fp = open(self.tpath+'available_filter_functions') 455 - master = fp.read().split('\n') 456 - fp.close() 413 + master = self.listFromFile(self.tpath+'available_filter_functions') 457 414 flist = '' 458 415 for i in list: 459 416 if i not in master: ··· 540 501 rejects = [] 541 502 # sort kprobes: trace, ub-dev, custom, dev 542 503 kpl = [[], [], [], []] 504 + linesout = len(self.kprobes) 543 505 for name in sorted(self.kprobes): 544 506 res = self.colorText('YES', 32) 545 507 if not self.testKprobe(name, self.kprobes[name]): ··· 568 528 for kp in kplist: 569 529 kprobeevents += self.kprobeText(kp, self.kprobes[kp]) 570 530 self.fsetVal(kprobeevents, 'kprobe_events') 571 - # verify that the kprobes were set as ordered 572 - check = self.fgetVal('kprobe_events') 573 - linesout = len(kprobeevents.split('\n')) - 1 574 - linesack = len(check.split('\n')) - 1 575 531 if output: 576 - res = '%d/%d' % (linesack, linesout) 577 - if linesack < linesout: 578 - res = self.colorText(res, 31) 579 - else: 580 - res = self.colorText(res, 32) 581 - print(' working kprobe functions enabled: %s' % res) 532 + check = self.fgetVal('kprobe_events') 533 + linesack = (len(check.split('\n')) - 1) / 2 534 + print(' kprobe functions enabled: %d/%d' % (linesack, linesout)) 582 535 self.fsetVal('1', 'events/kprobes/enable') 583 536 def testKprobe(self, kname, kprobe): 584 537 self.fsetVal('0', 'events/kprobes/enable') ··· 588 555 if linesack < linesout: 589 556 return False 590 557 return True 591 - def fsetVal(self, val, path, mode='w'): 592 - file = self.tpath+path 558 + def setVal(self, val, file, mode='w'): 593 559 if not os.path.exists(file): 594 560 return False 595 561 try: ··· 599 567 except: 600 568 return False 601 569 return True 602 - def fgetVal(self, path): 603 - file = self.tpath+path 570 + def fsetVal(self, val, path, mode='w'): 571 + return self.setVal(val, self.tpath+path, mode) 572 + def getVal(self, file): 604 573 res = '' 605 574 if not os.path.exists(file): 606 575 return res ··· 612 579 except: 613 580 pass 614 581 return res 582 + def fgetVal(self, path): 583 + return self.getVal(self.tpath+path) 615 584 def cleanupFtrace(self): 616 - if(self.usecallgraph or self.usetraceevents): 585 + if(self.usecallgraph or self.usetraceevents or self.usedevsrc): 617 586 self.fsetVal('0', 'events/kprobes/enable') 618 587 self.fsetVal('', 'kprobe_events') 588 + self.fsetVal('1024', 'buffer_size_kb') 619 589 def setupAllKprobes(self): 620 590 for name in self.tracefuncs: 621 591 self.defaultKprobe(name, self.tracefuncs[name]) ··· 635 599 if name == f: 636 600 return True 637 601 return False 638 - def initFtrace(self, testing=False): 602 + def initFtrace(self): 603 + self.printSystemInfo(False) 639 604 print('INITIALIZING FTRACE...') 640 605 # turn trace off 641 606 self.fsetVal('0', 'tracing_on') ··· 644 607 # set the trace clock to global 645 608 self.fsetVal('global', 'trace_clock') 646 609 self.fsetVal('nop', 'current_tracer') 647 - # set trace buffer to a huge value 648 - if self.usecallgraph or self.usedevsrc: 649 - tgtsize = min(self.memtotal / 2, 2*1024*1024) 650 - maxbuf = '%d' % (tgtsize / max(1, self.cpucount)) 651 - if self.cpucount < 1 or not self.fsetVal(maxbuf, 'buffer_size_kb'): 652 - self.fsetVal('131072', 'buffer_size_kb') 610 + # set trace buffer to an appropriate value 611 + cpus = max(1, self.cpucount) 612 + if self.bufsize > 0: 613 + tgtsize = self.bufsize 614 + elif self.usecallgraph or self.usedevsrc: 615 + tgtsize = min(self.memfree, 3*1024*1024) 653 616 else: 654 - self.fsetVal('16384', 'buffer_size_kb') 655 - # go no further if this is just a status check 656 - if testing: 657 - return 617 + tgtsize = 65536 618 + while not self.fsetVal('%d' % (tgtsize / cpus), 'buffer_size_kb'): 619 + # if the size failed to set, lower it and keep trying 620 + tgtsize -= 65536 621 + if tgtsize < 65536: 622 + tgtsize = int(self.fgetVal('buffer_size_kb')) * cpus 623 + break 624 + print 'Setting trace buffers to %d kB (%d kB per cpu)' % (tgtsize, tgtsize/cpus) 658 625 # initialize the callgraph trace 659 626 if(self.usecallgraph): 660 627 # set trace type ··· 676 635 self.fsetVal('graph-time', 'trace_options') 677 636 self.fsetVal('%d' % self.max_graph_depth, 'max_graph_depth') 678 637 cf = ['dpm_run_callback'] 679 - if(self.usetraceeventsonly): 638 + if(self.usetraceevents): 680 639 cf += ['dpm_prepare', 'dpm_complete'] 681 640 for fn in self.tracefuncs: 682 641 if 'func' in self.tracefuncs[fn]: ··· 729 688 return str 730 689 return '\x1B[%d;40m%s\x1B[m' % (color, str) 731 690 def writeDatafileHeader(self, filename, fwdata=[]): 732 - fp = open(filename, 'w') 733 - fp.write(self.teststamp+'\n') 734 - fp.write(self.sysstamp+'\n') 691 + fp = self.openlog(filename, 'w') 692 + fp.write('%s\n%s\n# command | %s\n' % (self.teststamp, self.sysstamp, self.cmdline)) 735 693 if(self.suspendmode == 'mem' or self.suspendmode == 'command'): 736 694 for fw in fwdata: 737 695 if(fw): 738 696 fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1])) 697 + return fp 698 + def sudouser(self, dir): 699 + if os.path.exists(dir) and os.getuid() == 0 and \ 700 + 'SUDO_USER' in os.environ: 701 + cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1' 702 + call(cmd.format(os.environ['SUDO_USER'], dir), shell=True) 703 + def outputResult(self, testdata, num=0): 704 + if not self.result: 705 + return 706 + n = '' 707 + if num > 0: 708 + n = '%d' % num 709 + fp = open(self.result, 'a') 710 + if 'error' in testdata: 711 + fp.write('result%s: fail\n' % n) 712 + fp.write('error%s: %s\n' % (n, testdata['error'])) 713 + else: 714 + fp.write('result%s: pass\n' % n) 715 + for v in ['suspend', 'resume', 'boot', 'lastinit']: 716 + if v in testdata: 717 + fp.write('%s%s: %.3f\n' % (v, n, testdata[v])) 718 + for v in ['fwsuspend', 'fwresume']: 719 + if v in testdata: 720 + fp.write('%s%s: %.3f\n' % (v, n, testdata[v] / 1000000.0)) 721 + if 'bugurl' in testdata: 722 + fp.write('url%s: %s\n' % (n, testdata['bugurl'])) 739 723 fp.close() 724 + self.sudouser(self.result) 725 + def configFile(self, file): 726 + dir = os.path.dirname(os.path.realpath(__file__)) 727 + if os.path.exists(file): 728 + return file 729 + elif os.path.exists(dir+'/'+file): 730 + return dir+'/'+file 731 + elif os.path.exists(dir+'/config/'+file): 732 + return dir+'/config/'+file 733 + return '' 734 + def openlog(self, filename, mode): 735 + isgz = self.gzip 736 + if mode == 'r': 737 + try: 738 + with gzip.open(filename, mode+'b') as fp: 739 + test = fp.read(64) 740 + isgz = True 741 + except: 742 + isgz = False 743 + if isgz: 744 + return gzip.open(filename, mode+'b') 745 + return open(filename, mode) 740 746 741 747 sysvals = SystemValues() 748 + switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0'] 749 + switchoff = ['disable', 'off', 'false', '0'] 742 750 suspendmodename = { 743 751 'freeze': 'Freeze (S0)', 744 752 'standby': 'Standby (S1)', ··· 916 826 for phase in self.phases: 917 827 self.devicegroups.append([phase]) 918 828 self.errorinfo = {'suspend':[],'resume':[]} 919 - def extractErrorInfo(self, dmesg): 920 - error = '' 921 - tm = 0.0 922 - for i in range(len(dmesg)): 923 - if 'Call Trace:' in dmesg[i]: 924 - m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) .*', dmesg[i]) 925 - if not m: 926 - continue 927 - tm = float(m.group('ktime')) 928 - if tm < self.start or tm > self.end: 929 - continue 930 - for j in range(i-10, i+1): 931 - error += dmesg[j] 829 + def extractErrorInfo(self): 830 + lf = sysvals.openlog(sysvals.dmesgfile, 'r') 831 + i = 0 832 + list = [] 833 + # sl = start line, et = error time, el = error line 834 + type = 'ERROR' 835 + sl = et = el = -1 836 + for line in lf: 837 + i += 1 838 + m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line) 839 + if not m: 932 840 continue 933 - if error: 934 - m = re.match('[ \t]*\[ *[0-9\.]*\] \[\<[0-9a-fA-F]*\>\] .*', dmesg[i]) 935 - if m: 936 - error += dmesg[i] 937 - else: 938 - if tm < self.tSuspended: 939 - dir = 'suspend' 940 - else: 941 - dir = 'resume' 942 - error = error.replace('<', '&lt').replace('>', '&gt') 943 - vprint('kernel error found in %s at %f' % (dir, tm)) 944 - self.errorinfo[dir].append((tm, error)) 841 + t = float(m.group('ktime')) 842 + if t < self.start or t > self.end: 843 + continue 844 + if t < self.tSuspended: 845 + dir = 'suspend' 846 + else: 847 + dir = 'resume' 848 + msg = m.group('msg') 849 + if re.match('-*\[ *cut here *\]-*', msg): 850 + type = 'WARNING' 851 + sl = i 852 + elif re.match('genirq: .*', msg): 853 + type = 'IRQ' 854 + sl = i 855 + elif re.match('BUG: .*', msg) or re.match('kernel BUG .*', msg): 856 + type = 'BUG' 857 + sl = i 858 + elif re.match('-*\[ *end trace .*\]-*', msg) or \ 859 + re.match('R13: .*', msg): 860 + if et >= 0 and sl >= 0: 861 + list.append((type, dir, et, sl, i)) 945 862 self.kerror = True 946 - error = '' 863 + sl = et = el = -1 864 + type = 'ERROR' 865 + elif 'Call Trace:' in msg: 866 + if el >= 0 and et >= 0: 867 + list.append((type, dir, et, el, el)) 868 + self.kerror = True 869 + et, el = t, i 870 + if sl < 0 or type == 'BUG': 871 + slval = i 872 + if sl >= 0: 873 + slval = sl 874 + list.append((type, dir, et, slval, i)) 875 + self.kerror = True 876 + sl = et = el = -1 877 + type = 'ERROR' 878 + if el >= 0 and et >= 0: 879 + list.append((type, dir, et, el, el)) 880 + self.kerror = True 881 + for e in list: 882 + type, dir, t, idx1, idx2 = e 883 + sysvals.vprint('kernel %s found in %s at %f' % (type, dir, t)) 884 + self.errorinfo[dir].append((type, t, idx1, idx2)) 885 + if self.kerror: 886 + sysvals.dmesglog = True 887 + lf.close() 947 888 def setStart(self, time): 948 889 self.start = time 949 890 def setEnd(self, time): ··· 988 867 time < d['end']): 989 868 return False 990 869 return True 870 + def phaseCollision(self, phase, isbegin, line): 871 + key = 'end' 872 + if isbegin: 873 + key = 'start' 874 + if self.dmesg[phase][key] >= 0: 875 + sysvals.vprint('IGNORE: %s' % line.strip()) 876 + return True 877 + return False 991 878 def sourcePhase(self, start): 992 879 for phase in self.phases: 993 880 pend = self.dmesg[phase]['end'] ··· 1047 918 return self.addDeviceFunctionCall(displayname, kprobename, proc, pid, start, end, cdata, rdata) 1048 919 # this should not happen 1049 920 if not tgtdev: 1050 - vprint('[%f - %f] %s-%d %s %s %s' % \ 921 + sysvals.vprint('[%f - %f] %s-%d %s %s %s' % \ 1051 922 (start, end, proc, pid, kprobename, cdata, rdata)) 1052 923 return False 1053 924 # place the call data inside the src element of the tgtdev ··· 1183 1054 if('src' in d): 1184 1055 for e in d['src']: 1185 1056 e.time = self.trimTimeVal(e.time, t0, dT, left) 1057 + for dir in ['suspend', 'resume']: 1058 + list = [] 1059 + for e in self.errorinfo[dir]: 1060 + type, tm, idx1, idx2 = e 1061 + tm = self.trimTimeVal(tm, t0, dT, left) 1062 + list.append((type, tm, idx1, idx2)) 1063 + self.errorinfo[dir] = list 1186 1064 def normalizeTime(self, tZero): 1187 1065 # trim out any standby or freeze clock time 1188 1066 if(self.tSuspended != self.tResumed): ··· 1236 1100 if self.dmesg[p]['end'] > dev['start']: 1237 1101 dev['end'] = self.dmesg[p]['end'] 1238 1102 break 1239 - vprint('%s (%s): callback didnt return' % (devname, phase)) 1103 + sysvals.vprint('%s (%s): callback didnt return' % (devname, phase)) 1240 1104 def deviceFilter(self, devicefilter): 1241 1105 for phase in self.phases: 1242 1106 list = self.dmesg[phase]['list'] ··· 1336 1200 devlist.append(child) 1337 1201 return devlist 1338 1202 def printDetails(self): 1339 - vprint('Timeline Details:') 1340 - vprint(' test start: %f' % self.start) 1341 - vprint('kernel suspend start: %f' % self.tKernSus) 1203 + sysvals.vprint('Timeline Details:') 1204 + sysvals.vprint(' test start: %f' % self.start) 1205 + sysvals.vprint('kernel suspend start: %f' % self.tKernSus) 1342 1206 for phase in self.phases: 1343 1207 dc = len(self.dmesg[phase]['list']) 1344 - vprint(' %16s: %f - %f (%d devices)' % (phase, \ 1208 + sysvals.vprint(' %16s: %f - %f (%d devices)' % (phase, \ 1345 1209 self.dmesg[phase]['start'], self.dmesg[phase]['end'], dc)) 1346 - vprint(' kernel resume end: %f' % self.tKernRes) 1347 - vprint(' test end: %f' % self.end) 1210 + sysvals.vprint(' kernel resume end: %f' % self.tKernRes) 1211 + sysvals.vprint(' test end: %f' % self.end) 1348 1212 def deviceChildrenAllPhases(self, devname): 1349 1213 devlist = [] 1350 1214 for phase in self.phases: ··· 1494 1358 tres.append(t) 1495 1359 # process the events for suspend and resume 1496 1360 if len(proclist) > 0: 1497 - vprint('Process Execution:') 1361 + sysvals.vprint('Process Execution:') 1498 1362 for ps in proclist: 1499 1363 c = self.addProcessUsageEvent(ps, tsus) 1500 1364 if c > 0: 1501 - vprint('%25s (sus): %d' % (ps, c)) 1365 + sysvals.vprint('%25s (sus): %d' % (ps, c)) 1502 1366 c = self.addProcessUsageEvent(ps, tres) 1503 1367 if c > 0: 1504 - vprint('%25s (res): %d' % (ps, c)) 1368 + sysvals.vprint('%25s (res): %d' % (ps, c)) 1369 + def debugPrint(self): 1370 + for p in self.phases: 1371 + list = self.dmesg[p]['list'] 1372 + for devname in list: 1373 + dev = list[devname] 1374 + if 'ftrace' in dev: 1375 + dev['ftrace'].debugPrint(' [%s]' % devname) 1505 1376 1506 1377 # Class: DevFunction 1507 1378 # Description: ··· 1647 1504 # something else (possibly a trace marker) 1648 1505 else: 1649 1506 self.name = m 1507 + def isCall(self): 1508 + return self.fcall and not self.freturn 1509 + def isReturn(self): 1510 + return self.freturn and not self.fcall 1511 + def isLeaf(self): 1512 + return self.fcall and self.freturn 1650 1513 def getDepth(self, str): 1651 1514 return len(str)/2 1652 - def debugPrint(self, dev=''): 1653 - if(self.freturn and self.fcall): 1654 - print('%s -- %f (%02d): %s(); (%.3f us)' % (dev, self.time, \ 1655 - self.depth, self.name, self.length*1000000)) 1656 - elif(self.freturn): 1657 - print('%s -- %f (%02d): %s} (%.3f us)' % (dev, self.time, \ 1658 - self.depth, self.name, self.length*1000000)) 1515 + def debugPrint(self, info=''): 1516 + if self.isLeaf(): 1517 + print(' -- %12.6f (depth=%02d): %s(); (%.3f us) %s' % (self.time, \ 1518 + self.depth, self.name, self.length*1000000, info)) 1519 + elif self.freturn: 1520 + print(' -- %12.6f (depth=%02d): %s} (%.3f us) %s' % (self.time, \ 1521 + self.depth, self.name, self.length*1000000, info)) 1659 1522 else: 1660 - print('%s -- %f (%02d): %s() { (%.3f us)' % (dev, self.time, \ 1661 - self.depth, self.name, self.length*1000000)) 1523 + print(' -- %12.6f (depth=%02d): %s() { (%.3f us) %s' % (self.time, \ 1524 + self.depth, self.name, self.length*1000000, info)) 1662 1525 def startMarker(self): 1663 1526 # Is this the starting line of a suspend? 1664 1527 if not self.fevent: ··· 1707 1558 depth = 0 1708 1559 pid = 0 1709 1560 name = '' 1710 - def __init__(self, pid): 1561 + partial = False 1562 + vfname = 'missing_function_name' 1563 + ignore = False 1564 + sv = 0 1565 + def __init__(self, pid, sv): 1711 1566 self.start = -1.0 1712 1567 self.end = -1.0 1713 1568 self.list = [] 1714 1569 self.depth = 0 1715 1570 self.pid = pid 1716 - def addLine(self, line, debug=False): 1571 + self.sv = sv 1572 + def addLine(self, line): 1717 1573 # if this is already invalid, just leave 1718 1574 if(self.invalid): 1719 - return False 1720 - # invalidate on too much data or bad depth 1721 - if(len(self.list) >= 1000000 or self.depth < 0): 1575 + if(line.depth == 0 and line.freturn): 1576 + return 1 1577 + return 0 1578 + # invalidate on bad depth 1579 + if(self.depth < 0): 1722 1580 self.invalidate(line) 1723 - return False 1581 + return 0 1582 + # ignore data til we return to the current depth 1583 + if self.ignore: 1584 + if line.depth > self.depth: 1585 + return 0 1586 + else: 1587 + self.list[-1].freturn = True 1588 + self.list[-1].length = line.time - self.list[-1].time 1589 + self.ignore = False 1590 + # if this is a return at self.depth, no more work is needed 1591 + if line.depth == self.depth and line.isReturn(): 1592 + if line.depth == 0: 1593 + self.end = line.time 1594 + return 1 1595 + return 0 1724 1596 # compare current depth with this lines pre-call depth 1725 1597 prelinedep = line.depth 1726 - if(line.freturn and not line.fcall): 1598 + if line.isReturn(): 1727 1599 prelinedep += 1 1728 1600 last = 0 1729 1601 lasttime = line.time 1730 - virtualfname = 'missing_function_name' 1731 1602 if len(self.list) > 0: 1732 1603 last = self.list[-1] 1733 1604 lasttime = last.time 1605 + if last.isLeaf(): 1606 + lasttime += last.length 1734 1607 # handle low misalignments by inserting returns 1735 - if prelinedep < self.depth: 1736 - if debug and last: 1737 - print '-------- task %d --------' % self.pid 1738 - last.debugPrint() 1608 + mismatch = prelinedep - self.depth 1609 + warning = self.sv.verbose and abs(mismatch) > 1 1610 + info = [] 1611 + if mismatch < 0: 1739 1612 idx = 0 1740 1613 # add return calls to get the depth down 1741 1614 while prelinedep < self.depth: 1742 - if debug: 1743 - print 'MISALIGN LOW (add returns): C%d - eC%d' % (self.depth, prelinedep) 1744 1615 self.depth -= 1 1745 - if idx == 0 and last and last.fcall and not last.freturn: 1616 + if idx == 0 and last and last.isCall(): 1746 1617 # special case, turn last call into a leaf 1747 1618 last.depth = self.depth 1748 1619 last.freturn = True 1749 1620 last.length = line.time - last.time 1750 - if debug: 1751 - last.debugPrint() 1621 + if warning: 1622 + info.append(('[make leaf]', last)) 1752 1623 else: 1753 1624 vline = FTraceLine(lasttime) 1754 1625 vline.depth = self.depth 1755 - vline.name = virtualfname 1626 + vline.name = self.vfname 1756 1627 vline.freturn = True 1757 1628 self.list.append(vline) 1758 - if debug: 1759 - vline.debugPrint() 1629 + if warning: 1630 + if idx == 0: 1631 + info.append(('', last)) 1632 + info.append(('[add return]', vline)) 1760 1633 idx += 1 1761 - if debug: 1762 - line.debugPrint() 1763 - print '' 1634 + if warning: 1635 + info.append(('', line)) 1764 1636 # handle high misalignments by inserting calls 1765 - elif prelinedep > self.depth: 1766 - if debug and last: 1767 - print '-------- task %d --------' % self.pid 1768 - last.debugPrint() 1637 + elif mismatch > 0: 1769 1638 idx = 0 1639 + if warning: 1640 + info.append(('', last)) 1770 1641 # add calls to get the depth up 1771 1642 while prelinedep > self.depth: 1772 - if debug: 1773 - print 'MISALIGN HIGH (add calls): C%d - eC%d' % (self.depth, prelinedep) 1774 - if idx == 0 and line.freturn and not line.fcall: 1643 + if idx == 0 and line.isReturn(): 1775 1644 # special case, turn this return into a leaf 1776 1645 line.fcall = True 1777 1646 prelinedep -= 1 1647 + if warning: 1648 + info.append(('[make leaf]', line)) 1778 1649 else: 1779 1650 vline = FTraceLine(lasttime) 1780 1651 vline.depth = self.depth 1781 - vline.name = virtualfname 1652 + vline.name = self.vfname 1782 1653 vline.fcall = True 1783 - if debug: 1784 - vline.debugPrint() 1785 1654 self.list.append(vline) 1786 1655 self.depth += 1 1787 1656 if not last: 1788 1657 self.start = vline.time 1658 + if warning: 1659 + info.append(('[add call]', vline)) 1789 1660 idx += 1 1790 - if debug: 1791 - line.debugPrint() 1792 - print '' 1661 + if warning and ('[make leaf]', line) not in info: 1662 + info.append(('', line)) 1663 + if warning: 1664 + print 'WARNING: ftrace data missing, corrections made:' 1665 + for i in info: 1666 + t, obj = i 1667 + if obj: 1668 + obj.debugPrint(t) 1793 1669 # process the call and set the new depth 1794 - if(line.fcall and not line.freturn): 1795 - self.depth += 1 1796 - elif(line.freturn and not line.fcall): 1670 + skipadd = False 1671 + md = self.sv.max_graph_depth 1672 + if line.isCall(): 1673 + # ignore blacklisted/overdepth funcs 1674 + if (md and self.depth >= md - 1) or (line.name in self.sv.cgblacklist): 1675 + self.ignore = True 1676 + else: 1677 + self.depth += 1 1678 + elif line.isReturn(): 1797 1679 self.depth -= 1 1680 + # remove blacklisted/overdepth/empty funcs that slipped through 1681 + if (last and last.isCall() and last.depth == line.depth) or \ 1682 + (md and last and last.depth >= md) or \ 1683 + (line.name in self.sv.cgblacklist): 1684 + while len(self.list) > 0 and self.list[-1].depth > line.depth: 1685 + self.list.pop(-1) 1686 + if len(self.list) == 0: 1687 + self.invalid = True 1688 + return 1 1689 + self.list[-1].freturn = True 1690 + self.list[-1].length = line.time - self.list[-1].time 1691 + self.list[-1].name = line.name 1692 + skipadd = True 1798 1693 if len(self.list) < 1: 1799 1694 self.start = line.time 1800 - self.list.append(line) 1695 + # check for a mismatch that returned all the way to callgraph end 1696 + res = 1 1697 + if mismatch < 0 and self.list[-1].depth == 0 and self.list[-1].freturn: 1698 + line = self.list[-1] 1699 + skipadd = True 1700 + res = -1 1701 + if not skipadd: 1702 + self.list.append(line) 1801 1703 if(line.depth == 0 and line.freturn): 1802 1704 if(self.start < 0): 1803 1705 self.start = line.time 1804 1706 self.end = line.time 1805 1707 if line.fcall: 1806 1708 self.end += line.length 1807 - if self.list[0].name == virtualfname: 1709 + if self.list[0].name == self.vfname: 1808 1710 self.invalid = True 1809 - return True 1810 - return False 1711 + if res == -1: 1712 + self.partial = True 1713 + return res 1714 + return 0 1811 1715 def invalidate(self, line): 1812 1716 if(len(self.list) > 0): 1813 1717 first = self.list[0] ··· 1870 1668 id = 'task %s' % (self.pid) 1871 1669 window = '(%f - %f)' % (self.start, line.time) 1872 1670 if(self.depth < 0): 1873 - vprint('Too much data for '+id+\ 1671 + print('Data misalignment for '+id+\ 1874 1672 ' (buffer overflow), ignoring this callback') 1875 1673 else: 1876 - vprint('Too much data for '+id+\ 1674 + print('Too much data for '+id+\ 1877 1675 ' '+window+', ignoring this callback') 1878 - def slice(self, t0, tN): 1879 - minicg = FTraceCallGraph(0) 1880 - count = -1 1881 - firstdepth = 0 1676 + def slice(self, dev): 1677 + minicg = FTraceCallGraph(dev['pid'], self.sv) 1678 + minicg.name = self.name 1679 + mydepth = -1 1680 + good = False 1882 1681 for l in self.list: 1883 - if(l.time < t0 or l.time > tN): 1682 + if(l.time < dev['start'] or l.time > dev['end']): 1884 1683 continue 1885 - if(count < 0): 1886 - if(not l.fcall or l.name == 'dev_driver_string'): 1887 - continue 1888 - firstdepth = l.depth 1889 - count = 0 1890 - l.depth -= firstdepth 1891 - minicg.addLine(l) 1892 - if((count == 0 and l.freturn and l.fcall) or 1893 - (count > 0 and l.depth <= 0)): 1684 + if mydepth < 0: 1685 + if l.name == 'mutex_lock' and l.freturn: 1686 + mydepth = l.depth 1687 + continue 1688 + elif l.depth == mydepth and l.name == 'mutex_unlock' and l.fcall: 1689 + good = True 1894 1690 break 1895 - count += 1 1691 + l.depth -= mydepth 1692 + minicg.addLine(l) 1693 + if not good or len(minicg.list) < 1: 1694 + return 0 1896 1695 return minicg 1897 1696 def repair(self, enddepth): 1898 1697 # bring the depth back to 0 with additional returns ··· 1904 1701 t.depth = i 1905 1702 t.freturn = True 1906 1703 fixed = self.addLine(t) 1907 - if fixed: 1704 + if fixed != 0: 1908 1705 self.end = last.time 1909 1706 return True 1910 1707 return False 1911 - def postProcess(self, debug=False): 1708 + def postProcess(self): 1912 1709 if len(self.list) > 0: 1913 1710 self.name = self.list[0].name 1914 1711 stack = dict() ··· 1917 1714 for l in self.list: 1918 1715 # ftrace bug: reported duration is not reliable 1919 1716 # check each leaf and clip it at max possible length 1920 - if(last and last.freturn and last.fcall): 1717 + if last and last.isLeaf(): 1921 1718 if last.length > l.time - last.time: 1922 1719 last.length = l.time - last.time 1923 - if(l.fcall and not l.freturn): 1720 + if l.isCall(): 1924 1721 stack[l.depth] = l 1925 1722 cnt += 1 1926 - elif(l.freturn and not l.fcall): 1723 + elif l.isReturn(): 1927 1724 if(l.depth not in stack): 1928 - if debug: 1725 + if self.sv.verbose: 1929 1726 print 'Post Process Error: Depth missing' 1930 1727 l.debugPrint() 1931 1728 return False 1932 1729 # calculate call length from call/return lines 1933 - stack[l.depth].length = l.time - stack[l.depth].time 1730 + cl = stack[l.depth] 1731 + cl.length = l.time - cl.time 1732 + if cl.name == self.vfname: 1733 + cl.name = l.name 1934 1734 stack.pop(l.depth) 1935 1735 l.length = 0 1936 1736 cnt -= 1 ··· 1942 1736 # trace caught the whole call tree 1943 1737 return True 1944 1738 elif(cnt < 0): 1945 - if debug: 1739 + if self.sv.verbose: 1946 1740 print 'Post Process Error: Depth is less than 0' 1947 1741 return False 1948 1742 # trace ended before call tree finished 1949 1743 return self.repair(cnt) 1950 1744 def deviceMatch(self, pid, data): 1951 - found = False 1745 + found = '' 1952 1746 # add the callgraph data to the device hierarchy 1953 1747 borderphase = { 1954 1748 'dpm_prepare': 'suspend_prepare', ··· 1962 1756 if(pid == dev['pid'] and 1963 1757 self.start <= dev['start'] and 1964 1758 self.end >= dev['end']): 1965 - dev['ftrace'] = self.slice(dev['start'], dev['end']) 1966 - found = True 1759 + cg = self.slice(dev) 1760 + if cg: 1761 + dev['ftrace'] = cg 1762 + found = devname 1967 1763 return found 1968 1764 for p in data.phases: 1969 1765 if(data.dmesg[p]['start'] <= self.start and ··· 1977 1769 self.start <= dev['start'] and 1978 1770 self.end >= dev['end']): 1979 1771 dev['ftrace'] = self 1980 - found = True 1772 + found = devname 1981 1773 break 1982 1774 break 1983 1775 return found ··· 2001 1793 if out: 2002 1794 phase, myname = out 2003 1795 data.dmesg[phase]['list'][myname]['ftrace'] = self 2004 - def debugPrint(self): 2005 - print('[%f - %f] %s (%d)') % (self.start, self.end, self.name, self.pid) 1796 + def debugPrint(self, info=''): 1797 + print('%s pid=%d [%f - %f] %.3f us') % \ 1798 + (self.name, self.pid, self.start, self.end, 1799 + (self.end - self.start)*1000000) 2006 1800 for l in self.list: 2007 - if(l.freturn and l.fcall): 2008 - print('%f (%02d): %s(); (%.3f us)' % (l.time, \ 2009 - l.depth, l.name, l.length*1000000)) 2010 - elif(l.freturn): 2011 - print('%f (%02d): %s} (%.3f us)' % (l.time, \ 2012 - l.depth, l.name, l.length*1000000)) 1801 + if l.isLeaf(): 1802 + print('%f (%02d): %s(); (%.3f us)%s' % (l.time, \ 1803 + l.depth, l.name, l.length*1000000, info)) 1804 + elif l.freturn: 1805 + print('%f (%02d): %s} (%.3f us)%s' % (l.time, \ 1806 + l.depth, l.name, l.length*1000000, info)) 2013 1807 else: 2014 - print('%f (%02d): %s() { (%.3f us)' % (l.time, \ 2015 - l.depth, l.name, l.length*1000000)) 1808 + print('%f (%02d): %s() { (%.3f us)%s' % (l.time, \ 1809 + l.depth, l.name, l.length*1000000, info)) 2016 1810 print(' ') 2017 1811 2018 1812 class DevItem: ··· 2049 1839 self.rowH = rowheight 2050 1840 self.scaleH = scaleheight 2051 1841 self.html = '' 2052 - def createHeader(self, sv): 2053 - if(not sv.stamp['time']): 1842 + def createHeader(self, sv, stamp): 1843 + if(not stamp['time']): 2054 1844 return 2055 1845 self.html += '<div class="version"><a href="https://01.org/suspendresume">%s v%s</a></div>' \ 2056 1846 % (sv.title, sv.version) ··· 2061 1851 if sv.ftracelog: 2062 1852 self.html += '<button id="showftrace" class="logbtn btnfmt">ftrace</button>' 2063 1853 headline_stamp = '<div class="stamp">{0} {1} {2} {3}</div>\n' 2064 - self.html += headline_stamp.format(sv.stamp['host'], sv.stamp['kernel'], 2065 - sv.stamp['mode'], sv.stamp['time']) 2066 - if 'man' in sv.stamp and 'plat' in sv.stamp and 'cpu' in sv.stamp: 1854 + self.html += headline_stamp.format(stamp['host'], stamp['kernel'], 1855 + stamp['mode'], stamp['time']) 1856 + if 'man' in stamp and 'plat' in stamp and 'cpu' in stamp and \ 1857 + stamp['man'] and stamp['plat'] and stamp['cpu']: 2067 1858 headline_sysinfo = '<div class="stamp sysinfo">{0} {1} <i>with</i> {2}</div>\n' 2068 - self.html += headline_sysinfo.format(sv.stamp['man'], 2069 - sv.stamp['plat'], sv.stamp['cpu']) 1859 + self.html += headline_sysinfo.format(stamp['man'], stamp['plat'], stamp['cpu']) 2070 1860 2071 1861 # Function: getDeviceRows 2072 1862 # Description: ··· 2277 2067 class TestProps: 2278 2068 stamp = '' 2279 2069 sysinfo = '' 2070 + cmdline = '' 2071 + kparams = '' 2280 2072 S0i3 = False 2281 2073 fwdata = [] 2282 2074 stampfmt = '# [a-z]*-(?P<m>[0-9]{2})(?P<d>[0-9]{2})(?P<y>[0-9]{2})-'+\ 2283 2075 '(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\ 2284 2076 ' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$' 2285 2077 sysinfofmt = '^# sysinfo .*' 2078 + cmdlinefmt = '^# command \| (?P<cmd>.*)' 2079 + kparamsfmt = '^# kparams \| (?P<kp>.*)' 2286 2080 ftrace_line_fmt_fg = \ 2287 2081 '^ *(?P<time>[0-9\.]*) *\| *(?P<cpu>[0-9]*)\)'+\ 2288 2082 ' *(?P<proc>.*)-(?P<pid>[0-9]*) *\|'+\ ··· 2330 2116 sv.hostname = data.stamp['host'] 2331 2117 sv.suspendmode = data.stamp['mode'] 2332 2118 if sv.suspendmode == 'command' and sv.ftracefile != '': 2333 - modes = ['on', 'freeze', 'standby', 'mem'] 2334 - out = Popen(['grep', 'suspend_enter', sv.ftracefile], 2119 + modes = ['on', 'freeze', 'standby', 'mem', 'disk'] 2120 + out = Popen(['grep', 'machine_suspend', sv.ftracefile], 2335 2121 stderr=PIPE, stdout=PIPE).stdout.read() 2336 - m = re.match('.* suspend_enter\[(?P<mode>.*)\]', out) 2337 - if m and m.group('mode') in ['1', '2', '3']: 2122 + m = re.match('.* machine_suspend\[(?P<mode>.*)\]', out) 2123 + if m and m.group('mode') in ['1', '2', '3', '4']: 2338 2124 sv.suspendmode = modes[int(m.group('mode'))] 2339 2125 data.stamp['mode'] = sv.suspendmode 2126 + m = re.match(self.cmdlinefmt, self.cmdline) 2127 + if m: 2128 + sv.cmdline = m.group('cmd') 2129 + if self.kparams: 2130 + m = re.match(self.kparamsfmt, self.kparams) 2131 + if m: 2132 + sv.kparams = m.group('kp') 2340 2133 if not sv.stamp: 2341 2134 sv.stamp = data.stamp 2342 2135 ··· 2407 2186 2408 2187 # ----------------- FUNCTIONS -------------------- 2409 2188 2410 - # Function: vprint 2411 - # Description: 2412 - # verbose print (prints only with -verbose option) 2413 - # Arguments: 2414 - # msg: the debug/log message to print 2415 - def vprint(msg): 2416 - sysvals.logmsg += msg+'\n' 2417 - if(sysvals.verbose): 2418 - print(msg) 2419 - 2420 2189 # Function: doesTraceLogHaveTraceEvents 2421 2190 # Description: 2422 - # Quickly determine if the ftrace log has some or all of the trace events 2423 - # required for primary parsing. Set the usetraceevents and/or 2424 - # usetraceeventsonly flags in the global sysvals object 2191 + # Quickly determine if the ftrace log has all of the trace events, 2192 + # markers, and/or kprobes required for primary parsing. 2425 2193 def doesTraceLogHaveTraceEvents(): 2426 - # check for kprobes 2194 + kpcheck = ['_cal: (', '_cpu_down()'] 2195 + techeck = sysvals.traceevents[:] 2196 + tmcheck = ['SUSPEND START', 'RESUME COMPLETE'] 2427 2197 sysvals.usekprobes = False 2428 - out = call('grep -q "_cal: (" '+sysvals.ftracefile, shell=True) 2429 - if(out == 0): 2430 - sysvals.usekprobes = True 2431 - # check for callgraph data on trace event blocks 2432 - out = call('grep -q "_cpu_down()" '+sysvals.ftracefile, shell=True) 2433 - if(out == 0): 2434 - sysvals.usekprobes = True 2435 - out = Popen(['head', '-1', sysvals.ftracefile], 2436 - stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '') 2437 - # figure out what level of trace events are supported 2438 - sysvals.usetraceeventsonly = True 2439 - sysvals.usetraceevents = False 2440 - for e in sysvals.traceevents: 2441 - out = call('grep -q "'+e+': " '+sysvals.ftracefile, shell=True) 2442 - if(out != 0): 2443 - sysvals.usetraceeventsonly = False 2444 - if(e == 'suspend_resume' and out == 0): 2445 - sysvals.usetraceevents = True 2446 - # determine is this log is properly formatted 2447 - for e in ['SUSPEND START', 'RESUME COMPLETE']: 2448 - out = call('grep -q "'+e+'" '+sysvals.ftracefile, shell=True) 2449 - if(out != 0): 2450 - sysvals.usetracemarkers = False 2198 + fp = sysvals.openlog(sysvals.ftracefile, 'r') 2199 + for line in fp: 2200 + # check for kprobes 2201 + if not sysvals.usekprobes: 2202 + for i in kpcheck: 2203 + if i in line: 2204 + sysvals.usekprobes = True 2205 + # check for all necessary trace events 2206 + check = techeck[:] 2207 + for i in techeck: 2208 + if i in line: 2209 + check.remove(i) 2210 + techeck = check 2211 + # check for all necessary trace markers 2212 + check = tmcheck[:] 2213 + for i in tmcheck: 2214 + if i in line: 2215 + check.remove(i) 2216 + tmcheck = check 2217 + fp.close() 2218 + if len(techeck) == 0: 2219 + sysvals.usetraceevents = True 2220 + else: 2221 + sysvals.usetraceevents = False 2222 + if len(tmcheck) == 0: 2223 + sysvals.usetracemarkers = True 2224 + else: 2225 + sysvals.usetracemarkers = False 2451 2226 2452 2227 # Function: appendIncompleteTraceLog 2453 2228 # Description: ··· 2464 2247 testrun.append(TestRun(data)) 2465 2248 2466 2249 # extract the callgraph and traceevent data 2467 - vprint('Analyzing the ftrace data...') 2250 + sysvals.vprint('Analyzing the ftrace data (%s)...' % \ 2251 + os.path.basename(sysvals.ftracefile)) 2468 2252 tp = TestProps() 2469 - tf = open(sysvals.ftracefile, 'r') 2253 + tf = sysvals.openlog(sysvals.ftracefile, 'r') 2470 2254 data = 0 2471 2255 for line in tf: 2472 2256 # remove any latent carriage returns ··· 2478 2260 continue 2479 2261 elif re.match(tp.sysinfofmt, line): 2480 2262 tp.sysinfo = line 2263 + continue 2264 + elif re.match(tp.cmdlinefmt, line): 2265 + tp.cmdline = line 2481 2266 continue 2482 2267 # determine the trace data type (required for further parsing) 2483 2268 m = re.match(sysvals.tracertypefmt, line) ··· 2614 2393 # create a callgraph object for the data 2615 2394 if(pid not in testrun[testidx].ftemp): 2616 2395 testrun[testidx].ftemp[pid] = [] 2617 - testrun[testidx].ftemp[pid].append(FTraceCallGraph(pid)) 2396 + testrun[testidx].ftemp[pid].append(FTraceCallGraph(pid, sysvals)) 2618 2397 # when the call is finished, see which device matches it 2619 2398 cg = testrun[testidx].ftemp[pid][-1] 2620 - if(cg.addLine(t)): 2621 - testrun[testidx].ftemp[pid].append(FTraceCallGraph(pid)) 2399 + res = cg.addLine(t) 2400 + if(res != 0): 2401 + testrun[testidx].ftemp[pid].append(FTraceCallGraph(pid, sysvals)) 2402 + if(res == -1): 2403 + testrun[testidx].ftemp[pid][-1].addLine(t) 2622 2404 tf.close() 2623 2405 2624 2406 for test in testrun: ··· 2634 2410 # add the callgraph data to the device hierarchy 2635 2411 for pid in test.ftemp: 2636 2412 for cg in test.ftemp[pid]: 2637 - if len(cg.list) < 1 or cg.invalid: 2413 + if len(cg.list) < 1 or cg.invalid or (cg.end - cg.start == 0): 2638 2414 continue 2639 2415 if(not cg.postProcess()): 2640 2416 id = 'task %s cpu %s' % (pid, m.group('cpu')) 2641 - vprint('Sanity check failed for '+\ 2417 + sysvals.vprint('Sanity check failed for '+\ 2642 2418 id+', ignoring this callback') 2643 2419 continue 2644 2420 callstart = cg.start ··· 2655 2431 dev['ftrace'] = cg 2656 2432 break 2657 2433 2658 - test.data.printDetails() 2659 - 2660 2434 # Function: parseTraceLog 2661 2435 # Description: 2662 2436 # Analyze an ftrace log output file generated from this app during ··· 2663 2441 # The ftrace filename is taken from sysvals 2664 2442 # Output: 2665 2443 # An array of Data objects 2666 - def parseTraceLog(): 2667 - vprint('Analyzing the ftrace data...') 2444 + def parseTraceLog(live=False): 2445 + sysvals.vprint('Analyzing the ftrace data (%s)...' % \ 2446 + os.path.basename(sysvals.ftracefile)) 2668 2447 if(os.path.exists(sysvals.ftracefile) == False): 2669 2448 doError('%s does not exist' % sysvals.ftracefile) 2670 - 2671 - sysvals.setupAllKprobes() 2449 + if not live: 2450 + sysvals.setupAllKprobes() 2672 2451 tracewatch = [] 2673 2452 if sysvals.usekprobes: 2674 2453 tracewatch += ['sync_filesystems', 'freeze_processes', 'syscore_suspend', ··· 2681 2458 testdata = [] 2682 2459 testrun = 0 2683 2460 data = 0 2684 - tf = open(sysvals.ftracefile, 'r') 2461 + tf = sysvals.openlog(sysvals.ftracefile, 'r') 2685 2462 phase = 'suspend_prepare' 2686 2463 for line in tf: 2687 2464 # remove any latent carriage returns ··· 2692 2469 continue 2693 2470 elif re.match(tp.sysinfofmt, line): 2694 2471 tp.sysinfo = line 2472 + continue 2473 + elif re.match(tp.cmdlinefmt, line): 2474 + tp.cmdline = line 2695 2475 continue 2696 2476 # firmware line: pull out any firmware data 2697 2477 m = re.match(sysvals.firmwarefmt, line) ··· 2817 2591 phase = 'suspend_prepare' 2818 2592 if(not isbegin): 2819 2593 data.dmesg[phase]['end'] = t.time 2594 + if data.dmesg[phase]['start'] < 0: 2595 + data.dmesg[phase]['start'] = data.start 2820 2596 continue 2821 2597 # suspend start 2822 2598 elif(re.match('dpm_suspend\[.*', t.name)): ··· 2832 2604 continue 2833 2605 # suspend_noirq start 2834 2606 elif(re.match('dpm_suspend_noirq\[.*', t.name)): 2607 + if data.phaseCollision('suspend_noirq', isbegin, line): 2608 + continue 2835 2609 phase = 'suspend_noirq' 2836 2610 data.setPhase(phase, t.time, isbegin) 2837 2611 if(not isbegin): ··· 2866 2636 continue 2867 2637 # resume_noirq start 2868 2638 elif(re.match('dpm_resume_noirq\[.*', t.name)): 2639 + if data.phaseCollision('resume_noirq', isbegin, line): 2640 + continue 2869 2641 phase = 'resume_noirq' 2870 2642 data.setPhase(phase, t.time, isbegin) 2871 2643 if(isbegin): ··· 2974 2742 key = (m_proc, pid) 2975 2743 if(key not in testrun.ftemp): 2976 2744 testrun.ftemp[key] = [] 2977 - testrun.ftemp[key].append(FTraceCallGraph(pid)) 2745 + testrun.ftemp[key].append(FTraceCallGraph(pid, sysvals)) 2978 2746 # when the call is finished, see which device matches it 2979 2747 cg = testrun.ftemp[key][-1] 2980 - if(cg.addLine(t)): 2981 - testrun.ftemp[key].append(FTraceCallGraph(pid)) 2748 + res = cg.addLine(t) 2749 + if(res != 0): 2750 + testrun.ftemp[key].append(FTraceCallGraph(pid, sysvals)) 2751 + if(res == -1): 2752 + testrun.ftemp[key][-1].addLine(t) 2982 2753 tf.close() 2983 2754 2984 2755 if sysvals.suspendmode == 'command': ··· 3047 2812 for key in test.ftemp: 3048 2813 proc, pid = key 3049 2814 for cg in test.ftemp[key]: 3050 - if len(cg.list) < 1 or cg.invalid: 2815 + if len(cg.list) < 1 or cg.invalid or (cg.end - cg.start == 0): 3051 2816 continue 3052 2817 if(not cg.postProcess()): 3053 2818 id = 'task %s' % (pid) 3054 - vprint('Sanity check failed for '+\ 2819 + sysvals.vprint('Sanity check failed for '+\ 3055 2820 id+', ignoring this callback') 3056 2821 continue 3057 2822 # match cg data to devices 3058 - if sysvals.suspendmode == 'command' or not cg.deviceMatch(pid, data): 2823 + devname = '' 2824 + if sysvals.suspendmode != 'command': 2825 + devname = cg.deviceMatch(pid, data) 2826 + if not devname: 3059 2827 sortkey = '%f%f%d' % (cg.start, cg.end, pid) 3060 2828 sortlist[sortkey] = cg 2829 + elif len(cg.list) > 1000000: 2830 + print 'WARNING: the callgraph for %s is massive (%d lines)' %\ 2831 + (devname, len(cg.list)) 3061 2832 # create blocks for orphan cg data 3062 2833 for sortkey in sorted(sortlist): 3063 2834 cg = sortlist[sortkey] 3064 2835 name = cg.name 3065 2836 if sysvals.isCallgraphFunc(name): 3066 - vprint('Callgraph found for task %d: %.3fms, %s' % (cg.pid, (cg.end - cg.start)*1000, name)) 2837 + sysvals.vprint('Callgraph found for task %d: %.3fms, %s' % (cg.pid, (cg.end - cg.start)*1000, name)) 3067 2838 cg.newActionFromFunction(data) 3068 - 3069 2839 if sysvals.suspendmode == 'command': 3070 - for data in testdata: 3071 - data.printDetails() 3072 2840 return testdata 3073 2841 3074 2842 # fill in any missing phases ··· 3079 2841 lp = data.phases[0] 3080 2842 for p in data.phases: 3081 2843 if(data.dmesg[p]['start'] < 0 and data.dmesg[p]['end'] < 0): 3082 - vprint('WARNING: phase "%s" is missing!' % p) 2844 + sysvals.vprint('WARNING: phase "%s" is missing!' % p) 3083 2845 if(data.dmesg[p]['start'] < 0): 3084 2846 data.dmesg[p]['start'] = data.dmesg[lp]['end'] 3085 2847 if(p == 'resume_machine'): ··· 3097 2859 data.fixupInitcallsThatDidntReturn() 3098 2860 if sysvals.usedevsrc: 3099 2861 data.optimizeDevSrc() 3100 - data.printDetails() 3101 2862 3102 2863 # x2: merge any overlapping devices between test runs 3103 2864 if sysvals.usedevsrc and len(testdata) > 1: ··· 3115 2878 # The dmesg filename is taken from sysvals 3116 2879 # Output: 3117 2880 # An array of empty Data objects with only their dmesgtext attributes set 3118 - def loadKernelLog(justtext=False): 3119 - vprint('Analyzing the dmesg data...') 2881 + def loadKernelLog(): 2882 + sysvals.vprint('Analyzing the dmesg data (%s)...' % \ 2883 + os.path.basename(sysvals.dmesgfile)) 3120 2884 if(os.path.exists(sysvals.dmesgfile) == False): 3121 2885 doError('%s does not exist' % sysvals.dmesgfile) 3122 2886 3123 - if justtext: 3124 - dmesgtext = [] 3125 2887 # there can be multiple test runs in a single file 3126 2888 tp = TestProps() 3127 2889 tp.stamp = datetime.now().strftime('# suspend-%m%d%y-%H%M%S localhost mem unknown') 3128 2890 testruns = [] 3129 2891 data = 0 3130 - lf = open(sysvals.dmesgfile, 'r') 2892 + lf = sysvals.openlog(sysvals.dmesgfile, 'r') 3131 2893 for line in lf: 3132 2894 line = line.replace('\r\n', '') 3133 2895 idx = line.find('[') ··· 3139 2903 elif re.match(tp.sysinfofmt, line): 3140 2904 tp.sysinfo = line 3141 2905 continue 2906 + elif re.match(tp.cmdlinefmt, line): 2907 + tp.cmdline = line 2908 + continue 3142 2909 m = re.match(sysvals.firmwarefmt, line) 3143 2910 if(m): 3144 2911 tp.fwdata.append((int(m.group('s')), int(m.group('r')))) ··· 3150 2911 if(not m): 3151 2912 continue 3152 2913 msg = m.group("msg") 3153 - if justtext: 3154 - dmesgtext.append(line) 3155 - continue 3156 2914 if(re.match('PM: Syncing filesystems.*', msg)): 3157 2915 if(data): 3158 2916 testruns.append(data) ··· 3170 2934 data.dmesgtext.append(line) 3171 2935 lf.close() 3172 2936 3173 - if justtext: 3174 - return dmesgtext 3175 2937 if data: 3176 2938 testruns.append(data) 3177 2939 if len(testruns) < 1: ··· 3209 2975 phase = 'suspend_runtime' 3210 2976 3211 2977 if(data.fwValid): 3212 - vprint('Firmware Suspend = %u ns, Firmware Resume = %u ns' % \ 2978 + sysvals.vprint('Firmware Suspend = %u ns, Firmware Resume = %u ns' % \ 3213 2979 (data.fwSuspend, data.fwResume)) 3214 2980 3215 2981 # dmesg phase match table ··· 3435 3201 for event in actions[name]: 3436 3202 data.newActionGlobal(name, event['begin'], event['end']) 3437 3203 3438 - data.printDetails() 3439 3204 if(len(sysvals.devicefilter) > 0): 3440 3205 data.deviceFilter(sysvals.devicefilter) 3441 3206 data.fixupInitcallsThatDidntReturn() ··· 3463 3230 else: 3464 3231 fmt = '<n>(%.3f ms @ '+sv.timeformat+')</n>' 3465 3232 flen = fmt % (line.length*1000, line.time) 3466 - if(line.freturn and line.fcall): 3233 + if line.isLeaf(): 3467 3234 hf.write(html_func_leaf.format(line.name, flen)) 3468 - elif(line.freturn): 3235 + elif line.freturn: 3469 3236 hf.write(html_func_end) 3470 3237 else: 3471 3238 hf.write(html_func_start.format(num, line.name, flen)) ··· 3482 3249 continue 3483 3250 list = data.dmesg[p]['list'] 3484 3251 for devname in data.sortedDevices(p): 3485 - if len(sv.devicefilter) > 0 and devname not in sv.devicefilter: 3252 + if len(sv.cgfilter) > 0 and devname not in sv.cgfilter: 3486 3253 continue 3487 3254 dev = list[devname] 3488 3255 color = 'white' ··· 3503 3270 for cg in dev['ftraces']: 3504 3271 num = callgraphHTML(sv, hf, num, cg, 3505 3272 name+' &rarr; '+cg.name, color, dev['id']) 3506 - 3507 3273 hf.write('\n\n </section>\n') 3508 3274 3509 3275 # Function: createHTMLSummarySimple ··· 3543 3311 sTimeAvg = rTimeAvg = 0.0 3544 3312 mode = '' 3545 3313 num = 0 3546 - for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'])): 3314 + for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'], v['time'])): 3547 3315 if mode != data['mode']: 3548 3316 # test average line 3549 3317 if(num > 0): ··· 3619 3387 data.normalizeTime(testruns[-1].tSuspended) 3620 3388 3621 3389 # html function templates 3622 - html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">ERROR&rarr;</div>\n' 3390 + html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">{2}&rarr;</div>\n' 3623 3391 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' 3624 3392 html_cpuexec = '<div class="jiffie" style="left:{0}%;top:{1}px;height:{2}px;width:{3}%;background:{4};"></div>\n' 3625 3393 html_timetotal = '<table class="time1">\n<tr>'\ ··· 3648 3416 scaleH = 40 3649 3417 3650 3418 # device timeline 3651 - vprint('Creating Device Timeline...') 3652 - 3653 3419 devtl = Timeline(30, scaleH) 3654 3420 3655 3421 # write the test title and general info header 3656 - devtl.createHeader(sysvals) 3422 + devtl.createHeader(sysvals, testruns[0].stamp) 3657 3423 3658 3424 # Generate the header for this timeline 3659 3425 for data in testruns: 3660 3426 tTotal = data.end - data.start 3661 3427 sktime, rktime = data.getTimeValues() 3662 3428 if(tTotal == 0): 3663 - print('ERROR: No timeline data') 3664 - sys.exit() 3429 + doError('No timeline data') 3665 3430 if(data.tLow > 0): 3666 3431 low_time = '%.0f'%(data.tLow*1000) 3667 3432 if sysvals.suspendmode == 'command': ··· 3796 3567 data.dmesg[b]['color'], '') 3797 3568 for e in data.errorinfo[dir]: 3798 3569 # draw red lines for any kernel errors found 3799 - t, err = e 3570 + type, t, idx1, idx2 = e 3571 + id = '%d_%d' % (idx1, idx2) 3800 3572 right = '%f' % (((mMax-t)*100.0)/mTotal) 3801 - devtl.html += html_error.format(right, err) 3573 + devtl.html += html_error.format(right, id, type) 3802 3574 for b in sorted(phases[dir]): 3803 3575 # draw the devices for this phase 3804 3576 phaselist = data.dmesg[b]['list'] ··· 3893 3663 devtl.html += '</div>\n' 3894 3664 3895 3665 hf = open(sysvals.htmlfile, 'w') 3896 - 3897 - # no header or css if its embedded 3898 - if(sysvals.embedded): 3899 - hf.write('pass True tSus %.3f tRes %.3f tLow %.3f fwvalid %s tSus %.3f tRes %.3f\n' % 3900 - (data.tSuspended-data.start, data.end-data.tSuspended, data.tLow, data.fwValid, \ 3901 - data.fwSuspend/1000000, data.fwResume/1000000)) 3902 - else: 3903 - addCSS(hf, sysvals, len(testruns), kerror) 3666 + addCSS(hf, sysvals, len(testruns), kerror) 3904 3667 3905 3668 # write the device timeline 3906 3669 hf.write(devtl.html) ··· 3924 3701 data = testruns[sysvals.cgtest] 3925 3702 else: 3926 3703 data = testruns[-1] 3927 - if(sysvals.usecallgraph and not sysvals.embedded): 3704 + if sysvals.usecallgraph: 3928 3705 addCallgraphs(sysvals, hf, data) 3929 3706 3930 3707 # add the test log as a hidden div ··· 3933 3710 # add the dmesg log as a hidden div 3934 3711 if sysvals.dmesglog and sysvals.dmesgfile: 3935 3712 hf.write('<div id="dmesglog" style="display:none;">\n') 3936 - lf = open(sysvals.dmesgfile, 'r') 3713 + lf = sysvals.openlog(sysvals.dmesgfile, 'r') 3937 3714 for line in lf: 3938 3715 line = line.replace('<', '&lt').replace('>', '&gt') 3939 3716 hf.write(line) ··· 3942 3719 # add the ftrace log as a hidden div 3943 3720 if sysvals.ftracelog and sysvals.ftracefile: 3944 3721 hf.write('<div id="ftracelog" style="display:none;">\n') 3945 - lf = open(sysvals.ftracefile, 'r') 3722 + lf = sysvals.openlog(sysvals.ftracefile, 'r') 3946 3723 for line in lf: 3947 3724 hf.write(line) 3948 3725 lf.close() 3949 3726 hf.write('</div>\n') 3950 3727 3951 - if(not sysvals.embedded): 3952 - # write the footer and close 3953 - addScriptCode(hf, testruns) 3954 - hf.write('</body>\n</html>\n') 3955 - else: 3956 - # embedded out will be loaded in a page, skip the js 3957 - t0 = (testruns[0].start - testruns[-1].tSuspended) * 1000 3958 - tMax = (testruns[-1].end - testruns[-1].tSuspended) * 1000 3959 - # add js code in a div entry for later evaluation 3960 - detail = 'var bounds = [%f,%f];\n' % (t0, tMax) 3961 - detail += 'var devtable = [\n' 3962 - for data in testruns: 3963 - topo = data.deviceTopology() 3964 - detail += '\t"%s",\n' % (topo) 3965 - detail += '];\n' 3966 - hf.write('<div id=customcode style=display:none>\n'+detail+'</div>\n') 3728 + # write the footer and close 3729 + addScriptCode(hf, testruns) 3730 + hf.write('</body>\n</html>\n') 3967 3731 hf.close() 3968 3732 return True 3969 3733 ··· 4359 4149 ' win.document.write(html+dt);\n'\ 4360 4150 ' }\n'\ 4361 4151 ' function errWindow() {\n'\ 4362 - ' var text = this.id;\n'\ 4152 + ' var range = this.id.split("_");\n'\ 4153 + ' var idx1 = parseInt(range[0]);\n'\ 4154 + ' var idx2 = parseInt(range[1]);\n'\ 4363 4155 ' var win = window.open();\n'\ 4364 - ' win.document.write("<pre>"+text+"</pre>");\n'\ 4156 + ' var log = document.getElementById("dmesglog");\n'\ 4157 + ' var title = "<title>dmesg log</title>";\n'\ 4158 + ' var text = log.innerHTML.split("\\n");\n'\ 4159 + ' var html = "";\n'\ 4160 + ' for(var i = 0; i < text.length; i++) {\n'\ 4161 + ' if(i == idx1) {\n'\ 4162 + ' html += "<e id=target>"+text[i]+"</e>\\n";\n'\ 4163 + ' } else if(i > idx1 && i <= idx2) {\n'\ 4164 + ' html += "<e>"+text[i]+"</e>\\n";\n'\ 4165 + ' } else {\n'\ 4166 + ' html += text[i]+"\\n";\n'\ 4167 + ' }\n'\ 4168 + ' }\n'\ 4169 + ' win.document.write("<style>e{color:red}</style>"+title+"<pre>"+html+"</pre>");\n'\ 4170 + ' win.location.hash = "#target";\n'\ 4365 4171 ' win.document.close();\n'\ 4366 4172 ' }\n'\ 4367 4173 ' function logWindow(e) {\n'\ ··· 4445 4219 '</script>\n' 4446 4220 hf.write(script_code); 4447 4221 4222 + def setRuntimeSuspend(before=True): 4223 + global sysvals 4224 + sv = sysvals 4225 + if sv.rs == 0: 4226 + return 4227 + if before: 4228 + # runtime suspend disable or enable 4229 + if sv.rs > 0: 4230 + sv.rstgt, sv.rsval, sv.rsdir = 'on', 'auto', 'enabled' 4231 + else: 4232 + sv.rstgt, sv.rsval, sv.rsdir = 'auto', 'on', 'disabled' 4233 + print('CONFIGURING RUNTIME SUSPEND...') 4234 + sv.rslist = deviceInfo(sv.rstgt) 4235 + for i in sv.rslist: 4236 + sv.setVal(sv.rsval, i) 4237 + print('runtime suspend %s on all devices (%d changed)' % (sv.rsdir, len(sv.rslist))) 4238 + print('waiting 5 seconds...') 4239 + time.sleep(5) 4240 + else: 4241 + # runtime suspend re-enable or re-disable 4242 + for i in sv.rslist: 4243 + sv.setVal(sv.rstgt, i) 4244 + print('runtime suspend settings restored on %d devices' % len(sv.rslist)) 4245 + 4448 4246 # Function: executeSuspend 4449 4247 # Description: 4450 4248 # Execute system suspend through the sysfs interface, then copy the output ··· 4477 4227 pm = ProcessMonitor() 4478 4228 tp = sysvals.tpath 4479 4229 fwdata = [] 4230 + # run these commands to prepare the system for suspend 4231 + if sysvals.display: 4232 + if sysvals.display > 0: 4233 + print('TURN DISPLAY ON') 4234 + call('xset -d :0.0 dpms force suspend', shell=True) 4235 + call('xset -d :0.0 dpms force on', shell=True) 4236 + else: 4237 + print('TURN DISPLAY OFF') 4238 + call('xset -d :0.0 dpms force suspend', shell=True) 4239 + time.sleep(1) 4240 + if sysvals.sync: 4241 + print('SYNCING FILESYSTEMS') 4242 + call('sync', shell=True) 4480 4243 # mark the start point in the kernel ring buffer just as we start 4481 4244 sysvals.initdmesg() 4482 4245 # start ftrace ··· 4561 4298 pm.stop() 4562 4299 sysvals.fsetVal('0', 'tracing_on') 4563 4300 print('CAPTURING TRACE') 4564 - sysvals.writeDatafileHeader(sysvals.ftracefile, fwdata) 4565 - call('cat '+tp+'trace >> '+sysvals.ftracefile, shell=True) 4301 + op = sysvals.writeDatafileHeader(sysvals.ftracefile, fwdata) 4302 + fp = open(tp+'trace', 'r') 4303 + for line in fp: 4304 + op.write(line) 4305 + op.close() 4566 4306 sysvals.fsetVal('', 'trace') 4567 4307 devProps() 4568 4308 # grab a copy of the dmesg output 4569 4309 print('CAPTURING DMESG') 4570 - sysvals.writeDatafileHeader(sysvals.dmesgfile, fwdata) 4571 - sysvals.getdmesg() 4310 + sysvals.getdmesg(fwdata) 4572 4311 4573 - # Function: setUSBDevicesAuto 4574 - # Description: 4575 - # Set the autosuspend control parameter of all USB devices to auto 4576 - # This can be dangerous, so use at your own risk, most devices are set 4577 - # to always-on since the kernel cant determine if the device can 4578 - # properly autosuspend 4579 - def setUSBDevicesAuto(): 4580 - sysvals.rootCheck(True) 4581 - for dirname, dirnames, filenames in os.walk('/sys/devices'): 4582 - if(re.match('.*/usb[0-9]*.*', dirname) and 4583 - 'idVendor' in filenames and 'idProduct' in filenames): 4584 - call('echo auto > %s/power/control' % dirname, shell=True) 4585 - name = dirname.split('/')[-1] 4586 - desc = Popen(['cat', '%s/product' % dirname], 4587 - stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '') 4588 - ctrl = Popen(['cat', '%s/power/control' % dirname], 4589 - stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '') 4590 - print('control is %s for %6s: %s' % (ctrl, name, desc)) 4591 - 4592 - # Function: yesno 4593 - # Description: 4594 - # Print out an equivalent Y or N for a set of known parameter values 4595 - # Output: 4596 - # 'Y', 'N', or ' ' if the value is unknown 4597 - def yesno(val): 4598 - yesvals = ['auto', 'enabled', 'active', '1'] 4599 - novals = ['on', 'disabled', 'suspended', 'forbidden', 'unsupported'] 4600 - if val in yesvals: 4601 - return 'Y' 4602 - elif val in novals: 4603 - return 'N' 4604 - return ' ' 4312 + def readFile(file): 4313 + if os.path.islink(file): 4314 + return os.readlink(file).split('/')[-1] 4315 + else: 4316 + return sysvals.getVal(file).strip() 4605 4317 4606 4318 # Function: ms2nice 4607 4319 # Description: ··· 4584 4346 # Output: 4585 4347 # The time string, e.g. "1901m16s" 4586 4348 def ms2nice(val): 4587 - ms = 0 4588 - try: 4589 - ms = int(val) 4590 - except: 4591 - return 0.0 4592 - m = ms / 60000 4593 - s = (ms / 1000) - (m * 60) 4594 - return '%3dm%2ds' % (m, s) 4349 + val = int(val) 4350 + h = val / 3600000 4351 + m = (val / 60000) % 60 4352 + s = (val / 1000) % 60 4353 + if h > 0: 4354 + return '%d:%02d:%02d' % (h, m, s) 4355 + if m > 0: 4356 + return '%02d:%02d' % (m, s) 4357 + return '%ds' % s 4595 4358 4596 - # Function: detectUSB 4359 + def yesno(val): 4360 + list = {'enabled':'A', 'disabled':'S', 'auto':'E', 'on':'D', 4361 + 'active':'A', 'suspended':'S', 'suspending':'S'} 4362 + if val not in list: 4363 + return ' ' 4364 + return list[val] 4365 + 4366 + # Function: deviceInfo 4597 4367 # Description: 4598 4368 # Detect all the USB hosts and devices currently connected and add 4599 4369 # a list of USB device names to sysvals for better timeline readability 4600 - def detectUSB(): 4601 - field = {'idVendor':'', 'idProduct':'', 'product':'', 'speed':''} 4602 - power = {'async':'', 'autosuspend':'', 'autosuspend_delay_ms':'', 4603 - 'control':'', 'persist':'', 'runtime_enabled':'', 4604 - 'runtime_status':'', 'runtime_usage':'', 4605 - 'runtime_active_time':'', 4606 - 'runtime_suspended_time':'', 4607 - 'active_duration':'', 4608 - 'connected_duration':''} 4370 + def deviceInfo(output=''): 4371 + if not output: 4372 + print('LEGEND') 4373 + print('---------------------------------------------------------------------------------------------') 4374 + print(' A = async/sync PM queue (A/S) C = runtime active children') 4375 + print(' R = runtime suspend enabled/disabled (E/D) rACTIVE = runtime active (min/sec)') 4376 + print(' S = runtime status active/suspended (A/S) rSUSPEND = runtime suspend (min/sec)') 4377 + print(' U = runtime usage count') 4378 + print('---------------------------------------------------------------------------------------------') 4379 + print('DEVICE NAME A R S U C rACTIVE rSUSPEND') 4380 + print('---------------------------------------------------------------------------------------------') 4609 4381 4610 - print('LEGEND') 4611 - print('---------------------------------------------------------------------------------------------') 4612 - print(' A = async/sync PM queue Y/N D = autosuspend delay (seconds)') 4613 - print(' S = autosuspend Y/N rACTIVE = runtime active (min/sec)') 4614 - print(' P = persist across suspend Y/N rSUSPEN = runtime suspend (min/sec)') 4615 - print(' E = runtime suspend enabled/forbidden Y/N ACTIVE = active duration (min/sec)') 4616 - print(' R = runtime status active/suspended Y/N CONNECT = connected duration (min/sec)') 4617 - print(' U = runtime usage count') 4618 - print('---------------------------------------------------------------------------------------------') 4619 - print(' NAME ID DESCRIPTION SPEED A S P E R U D rACTIVE rSUSPEN ACTIVE CONNECT') 4620 - print('---------------------------------------------------------------------------------------------') 4621 - 4382 + res = [] 4383 + tgtval = 'runtime_status' 4384 + lines = dict() 4622 4385 for dirname, dirnames, filenames in os.walk('/sys/devices'): 4623 - if(re.match('.*/usb[0-9]*.*', dirname) and 4624 - 'idVendor' in filenames and 'idProduct' in filenames): 4625 - for i in field: 4626 - field[i] = Popen(['cat', '%s/%s' % (dirname, i)], 4627 - stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '') 4628 - name = dirname.split('/')[-1] 4629 - for i in power: 4630 - power[i] = Popen(['cat', '%s/power/%s' % (dirname, i)], 4631 - stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '') 4632 - if(re.match('usb[0-9]*', name)): 4633 - first = '%-8s' % name 4634 - else: 4635 - first = '%8s' % name 4636 - print('%s [%s:%s] %-20s %-4s %1s %1s %1s %1s %1s %1s %1s %s %s %s %s' % \ 4637 - (first, field['idVendor'], field['idProduct'], \ 4638 - field['product'][0:20], field['speed'], \ 4639 - yesno(power['async']), \ 4640 - yesno(power['control']), \ 4641 - yesno(power['persist']), \ 4642 - yesno(power['runtime_enabled']), \ 4643 - yesno(power['runtime_status']), \ 4644 - power['runtime_usage'], \ 4645 - power['autosuspend'], \ 4646 - ms2nice(power['runtime_active_time']), \ 4647 - ms2nice(power['runtime_suspended_time']), \ 4648 - ms2nice(power['active_duration']), \ 4649 - ms2nice(power['connected_duration']))) 4386 + if(not re.match('.*/power', dirname) or 4387 + 'control' not in filenames or 4388 + tgtval not in filenames): 4389 + continue 4390 + name = '' 4391 + dirname = dirname[:-6] 4392 + device = dirname.split('/')[-1] 4393 + power = dict() 4394 + power[tgtval] = readFile('%s/power/%s' % (dirname, tgtval)) 4395 + # only list devices which support runtime suspend 4396 + if power[tgtval] not in ['active', 'suspended', 'suspending']: 4397 + continue 4398 + for i in ['product', 'driver', 'subsystem']: 4399 + file = '%s/%s' % (dirname, i) 4400 + if os.path.exists(file): 4401 + name = readFile(file) 4402 + break 4403 + for i in ['async', 'control', 'runtime_status', 'runtime_usage', 4404 + 'runtime_active_kids', 'runtime_active_time', 4405 + 'runtime_suspended_time']: 4406 + if i in filenames: 4407 + power[i] = readFile('%s/power/%s' % (dirname, i)) 4408 + if output: 4409 + if power['control'] == output: 4410 + res.append('%s/power/control' % dirname) 4411 + continue 4412 + lines[dirname] = '%-26s %-26s %1s %1s %1s %1s %1s %10s %10s' % \ 4413 + (device[:26], name[:26], 4414 + yesno(power['async']), \ 4415 + yesno(power['control']), \ 4416 + yesno(power['runtime_status']), \ 4417 + power['runtime_usage'], \ 4418 + power['runtime_active_kids'], \ 4419 + ms2nice(power['runtime_active_time']), \ 4420 + ms2nice(power['runtime_suspended_time'])) 4421 + for i in sorted(lines): 4422 + print lines[i] 4423 + return res 4650 4424 4651 4425 # Function: devProps 4652 4426 # Description: ··· 4694 4444 msghead = 'Additional data added by AnalyzeSuspend' 4695 4445 alreadystamped = False 4696 4446 tp = TestProps() 4697 - tf = open(sysvals.ftracefile, 'r') 4447 + tf = sysvals.openlog(sysvals.ftracefile, 'r') 4698 4448 for line in tf: 4699 4449 if msghead in line: 4700 4450 alreadystamped = True ··· 4719 4469 if not alreadystamped and sysvals.suspendmode == 'command': 4720 4470 out = '#\n# '+msghead+'\n# Device Properties: ' 4721 4471 out += 'testcommandstring,%s,0;' % (sysvals.testcommand) 4722 - with open(sysvals.ftracefile, 'a') as fp: 4472 + with sysvals.openlog(sysvals.ftracefile, 'a') as fp: 4723 4473 fp.write(out+'\n') 4724 4474 sysvals.devprops = props 4725 4475 return ··· 4776 4526 out = '#\n# '+msghead+'\n# Device Properties: ' 4777 4527 for dev in sorted(props): 4778 4528 out += props[dev].out(dev) 4779 - with open(sysvals.ftracefile, 'a') as fp: 4529 + with sysvals.openlog(sysvals.ftracefile, 'a') as fp: 4780 4530 fp.write(out+'\n') 4781 4531 4782 4532 sysvals.devprops = props ··· 5119 4869 # what data source are we using 5120 4870 res = 'DMESG' 5121 4871 if(ftgood): 5122 - sysvals.usetraceeventsonly = True 5123 - sysvals.usetraceevents = False 4872 + sysvals.usetraceevents = True 5124 4873 for e in sysvals.traceevents: 5125 - check = False 5126 - if(os.path.exists(sysvals.epath+e)): 5127 - check = True 5128 - if(not check): 5129 - sysvals.usetraceeventsonly = False 5130 - if(e == 'suspend_resume' and check): 5131 - sysvals.usetraceevents = True 5132 - if(sysvals.usetraceevents and sysvals.usetraceeventsonly): 4874 + if not os.path.exists(sysvals.epath+e): 4875 + sysvals.usetraceevents = False 4876 + if(sysvals.usetraceevents): 5133 4877 res = 'FTRACE (all trace events found)' 5134 - elif(sysvals.usetraceevents): 5135 - res = 'DMESG and FTRACE (suspend_resume trace event found)' 5136 4878 print(' timeline data source: %s' % res) 5137 4879 5138 4880 # check if rtcwake ··· 5159 4917 if(help == True): 5160 4918 printHelp() 5161 4919 print('ERROR: %s\n') % msg 4920 + sysvals.outputResult({'error':msg}) 5162 4921 sys.exit() 5163 4922 5164 4923 # Function: getArgInt ··· 5200 4957 doError(name+': value should be between %f and %f' % (min, max), True) 5201 4958 return val 5202 4959 5203 - def processData(): 4960 + def processData(live=False): 5204 4961 print('PROCESSING DATA') 5205 - if(sysvals.usetraceeventsonly): 5206 - testruns = parseTraceLog() 4962 + if(sysvals.usetraceevents): 4963 + testruns = parseTraceLog(live) 5207 4964 if sysvals.dmesgfile: 5208 - dmesgtext = loadKernelLog(True) 5209 4965 for data in testruns: 5210 - data.extractErrorInfo(dmesgtext) 4966 + data.extractErrorInfo() 5211 4967 else: 5212 4968 testruns = loadKernelLog() 5213 4969 for data in testruns: 5214 4970 parseKernelLog(data) 5215 4971 if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)): 5216 4972 appendIncompleteTraceLog(testruns) 4973 + sysvals.vprint('Command:\n %s' % sysvals.cmdline) 4974 + for data in testruns: 4975 + data.printDetails() 4976 + if sysvals.cgdump: 4977 + for data in testruns: 4978 + data.debugPrint() 4979 + sys.exit() 4980 + 4981 + sysvals.vprint('Creating the html timeline (%s)...' % sysvals.htmlfile) 5217 4982 createHTML(testruns) 5218 - return testruns 4983 + print('DONE') 4984 + data = testruns[0] 4985 + stamp = data.stamp 4986 + stamp['suspend'], stamp['resume'] = data.getTimeValues() 4987 + if data.fwValid: 4988 + stamp['fwsuspend'], stamp['fwresume'] = data.fwSuspend, data.fwResume 4989 + return (testruns, stamp) 5219 4990 5220 4991 # Function: rerunTest 5221 4992 # Description: ··· 5237 4980 def rerunTest(): 5238 4981 if sysvals.ftracefile: 5239 4982 doesTraceLogHaveTraceEvents() 5240 - if not sysvals.dmesgfile and not sysvals.usetraceeventsonly: 4983 + if not sysvals.dmesgfile and not sysvals.usetraceevents: 5241 4984 doError('recreating this html output requires a dmesg file') 5242 4985 sysvals.setOutputFile() 5243 - vprint('Output file: %s' % sysvals.htmlfile) 5244 4986 if os.path.exists(sysvals.htmlfile): 5245 4987 if not os.path.isfile(sysvals.htmlfile): 5246 4988 doError('a directory already exists with this name: %s' % sysvals.htmlfile) 5247 4989 elif not os.access(sysvals.htmlfile, os.W_OK): 5248 4990 doError('missing permission to write to %s' % sysvals.htmlfile) 5249 - return processData() 4991 + testruns, stamp = processData(False) 4992 + return stamp 5250 4993 5251 4994 # Function: runTest 5252 4995 # Description: 5253 4996 # execute a suspend/resume, gather the logs, and generate the output 5254 - def runTest(): 4997 + def runTest(n=0): 5255 4998 # prepare for the test 5256 4999 sysvals.initFtrace() 5257 5000 sysvals.initTestOutput('suspend') 5258 - vprint('Output files:\n\t%s\n\t%s\n\t%s' % \ 5259 - (sysvals.dmesgfile, sysvals.ftracefile, sysvals.htmlfile)) 5260 5001 5261 5002 # execute the test 5262 5003 executeSuspend() 5263 5004 sysvals.cleanupFtrace() 5264 - processData() 5265 - 5266 - # if running as root, change output dir owner to sudo_user 5267 - if os.path.isdir(sysvals.testdir) and os.getuid() == 0 and \ 5268 - 'SUDO_USER' in os.environ: 5269 - cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1' 5270 - call(cmd.format(os.environ['SUDO_USER'], sysvals.testdir), shell=True) 5005 + if sysvals.skiphtml: 5006 + sysvals.sudouser(sysvals.testdir) 5007 + return 5008 + testruns, stamp = processData(True) 5009 + for data in testruns: 5010 + del data 5011 + sysvals.sudouser(sysvals.testdir) 5012 + sysvals.outputResult(stamp, n) 5271 5013 5272 5014 def find_in_html(html, strs, div=False): 5273 5015 for str in strs: ··· 5328 5072 # Function: checkArgBool 5329 5073 # Description: 5330 5074 # check if a boolean string value is true or false 5331 - def checkArgBool(value): 5332 - yes = ['1', 'true', 'yes', 'on'] 5333 - if value.lower() in yes: 5075 + def checkArgBool(name, value): 5076 + if value in switchvalues: 5077 + if value in switchoff: 5078 + return False 5334 5079 return True 5080 + doError('invalid boolean --> (%s: %s), use "true/false" or "1/0"' % (name, value), True) 5335 5081 return False 5336 5082 5337 5083 # Function: configFromFile ··· 5349 5091 if 'Settings' in sections: 5350 5092 for opt in Config.options('Settings'): 5351 5093 value = Config.get('Settings', opt).lower() 5352 - if(opt.lower() == 'verbose'): 5353 - sysvals.verbose = checkArgBool(value) 5354 - elif(opt.lower() == 'addlogs'): 5355 - sysvals.dmesglog = sysvals.ftracelog = checkArgBool(value) 5356 - elif(opt.lower() == 'dev'): 5357 - sysvals.usedevsrc = checkArgBool(value) 5358 - elif(opt.lower() == 'proc'): 5359 - sysvals.useprocmon = checkArgBool(value) 5360 - elif(opt.lower() == 'x2'): 5361 - if checkArgBool(value): 5094 + option = opt.lower() 5095 + if(option == 'verbose'): 5096 + sysvals.verbose = checkArgBool(option, value) 5097 + elif(option == 'addlogs'): 5098 + sysvals.dmesglog = sysvals.ftracelog = checkArgBool(option, value) 5099 + elif(option == 'dev'): 5100 + sysvals.usedevsrc = checkArgBool(option, value) 5101 + elif(option == 'proc'): 5102 + sysvals.useprocmon = checkArgBool(option, value) 5103 + elif(option == 'x2'): 5104 + if checkArgBool(option, value): 5362 5105 sysvals.execcount = 2 5363 - elif(opt.lower() == 'callgraph'): 5364 - sysvals.usecallgraph = checkArgBool(value) 5365 - elif(opt.lower() == 'override-timeline-functions'): 5366 - overridekprobes = checkArgBool(value) 5367 - elif(opt.lower() == 'override-dev-timeline-functions'): 5368 - overridedevkprobes = checkArgBool(value) 5369 - elif(opt.lower() == 'devicefilter'): 5106 + elif(option == 'callgraph'): 5107 + sysvals.usecallgraph = checkArgBool(option, value) 5108 + elif(option == 'override-timeline-functions'): 5109 + overridekprobes = checkArgBool(option, value) 5110 + elif(option == 'override-dev-timeline-functions'): 5111 + overridedevkprobes = checkArgBool(option, value) 5112 + elif(option == 'skiphtml'): 5113 + sysvals.skiphtml = checkArgBool(option, value) 5114 + elif(option == 'sync'): 5115 + sysvals.sync = checkArgBool(option, value) 5116 + elif(option == 'rs' or option == 'runtimesuspend'): 5117 + if value in switchvalues: 5118 + if value in switchoff: 5119 + sysvals.rs = -1 5120 + else: 5121 + sysvals.rs = 1 5122 + else: 5123 + doError('invalid value --> (%s: %s), use "enable/disable"' % (option, value), True) 5124 + elif(option == 'display'): 5125 + if value in switchvalues: 5126 + if value in switchoff: 5127 + sysvals.display = -1 5128 + else: 5129 + sysvals.display = 1 5130 + else: 5131 + doError('invalid value --> (%s: %s), use "on/off"' % (option, value), True) 5132 + elif(option == 'gzip'): 5133 + sysvals.gzip = checkArgBool(option, value) 5134 + elif(option == 'cgfilter'): 5135 + sysvals.setCallgraphFilter(value) 5136 + elif(option == 'cgskip'): 5137 + if value in switchoff: 5138 + sysvals.cgskip = '' 5139 + else: 5140 + sysvals.cgskip = sysvals.configFile(val) 5141 + if(not sysvals.cgskip): 5142 + doError('%s does not exist' % sysvals.cgskip) 5143 + elif(option == 'cgtest'): 5144 + sysvals.cgtest = getArgInt('cgtest', value, 0, 1, False) 5145 + elif(option == 'cgphase'): 5146 + d = Data(0) 5147 + if value not in d.phases: 5148 + doError('invalid phase --> (%s: %s), valid phases are %s'\ 5149 + % (option, value, d.phases), True) 5150 + sysvals.cgphase = value 5151 + elif(option == 'fadd'): 5152 + file = sysvals.configFile(value) 5153 + if(not file): 5154 + doError('%s does not exist' % value) 5155 + sysvals.addFtraceFilterFunctions(file) 5156 + elif(option == 'result'): 5157 + sysvals.result = value 5158 + elif(option == 'multi'): 5159 + nums = value.split() 5160 + if len(nums) != 2: 5161 + doError('multi requires 2 integers (exec_count and delay)', True) 5162 + sysvals.multitest['run'] = True 5163 + sysvals.multitest['count'] = getArgInt('multi: n d (exec count)', nums[0], 2, 1000000, False) 5164 + sysvals.multitest['delay'] = getArgInt('multi: n d (delay between tests)', nums[1], 0, 3600, False) 5165 + elif(option == 'devicefilter'): 5370 5166 sysvals.setDeviceFilter(value) 5371 - elif(opt.lower() == 'expandcg'): 5372 - sysvals.cgexp = checkArgBool(value) 5373 - elif(opt.lower() == 'srgap'): 5374 - if checkArgBool(value): 5167 + elif(option == 'expandcg'): 5168 + sysvals.cgexp = checkArgBool(option, value) 5169 + elif(option == 'srgap'): 5170 + if checkArgBool(option, value): 5375 5171 sysvals.srgap = 5 5376 - elif(opt.lower() == 'mode'): 5172 + elif(option == 'mode'): 5377 5173 sysvals.suspendmode = value 5378 - elif(opt.lower() == 'command'): 5174 + elif(option == 'command' or option == 'cmd'): 5379 5175 sysvals.testcommand = value 5380 - elif(opt.lower() == 'x2delay'): 5381 - sysvals.x2delay = getArgInt('-x2delay', value, 0, 60000, False) 5382 - elif(opt.lower() == 'predelay'): 5383 - sysvals.predelay = getArgInt('-predelay', value, 0, 60000, False) 5384 - elif(opt.lower() == 'postdelay'): 5385 - sysvals.postdelay = getArgInt('-postdelay', value, 0, 60000, False) 5386 - elif(opt.lower() == 'maxdepth'): 5387 - sysvals.max_graph_depth = getArgInt('-maxdepth', value, 0, 1000, False) 5388 - elif(opt.lower() == 'rtcwake'): 5389 - if value.lower() == 'off': 5176 + elif(option == 'x2delay'): 5177 + sysvals.x2delay = getArgInt('x2delay', value, 0, 60000, False) 5178 + elif(option == 'predelay'): 5179 + sysvals.predelay = getArgInt('predelay', value, 0, 60000, False) 5180 + elif(option == 'postdelay'): 5181 + sysvals.postdelay = getArgInt('postdelay', value, 0, 60000, False) 5182 + elif(option == 'maxdepth'): 5183 + sysvals.max_graph_depth = getArgInt('maxdepth', value, 0, 1000, False) 5184 + elif(option == 'rtcwake'): 5185 + if value in switchoff: 5390 5186 sysvals.rtcwake = False 5391 5187 else: 5392 5188 sysvals.rtcwake = True 5393 - sysvals.rtcwaketime = getArgInt('-rtcwake', value, 0, 3600, False) 5394 - elif(opt.lower() == 'timeprec'): 5395 - sysvals.setPrecision(getArgInt('-timeprec', value, 0, 6, False)) 5396 - elif(opt.lower() == 'mindev'): 5397 - sysvals.mindevlen = getArgFloat('-mindev', value, 0.0, 10000.0, False) 5398 - elif(opt.lower() == 'callloop-maxgap'): 5399 - sysvals.callloopmaxgap = getArgFloat('-callloop-maxgap', value, 0.0, 1.0, False) 5400 - elif(opt.lower() == 'callloop-maxlen'): 5401 - sysvals.callloopmaxgap = getArgFloat('-callloop-maxlen', value, 0.0, 1.0, False) 5402 - elif(opt.lower() == 'mincg'): 5403 - sysvals.mincglen = getArgFloat('-mincg', value, 0.0, 10000.0, False) 5404 - elif(opt.lower() == 'output-dir'): 5405 - sysvals.testdir = sysvals.setOutputFolder(value) 5189 + sysvals.rtcwaketime = getArgInt('rtcwake', value, 0, 3600, False) 5190 + elif(option == 'timeprec'): 5191 + sysvals.setPrecision(getArgInt('timeprec', value, 0, 6, False)) 5192 + elif(option == 'mindev'): 5193 + sysvals.mindevlen = getArgFloat('mindev', value, 0.0, 10000.0, False) 5194 + elif(option == 'callloop-maxgap'): 5195 + sysvals.callloopmaxgap = getArgFloat('callloop-maxgap', value, 0.0, 1.0, False) 5196 + elif(option == 'callloop-maxlen'): 5197 + sysvals.callloopmaxgap = getArgFloat('callloop-maxlen', value, 0.0, 1.0, False) 5198 + elif(option == 'mincg'): 5199 + sysvals.mincglen = getArgFloat('mincg', value, 0.0, 10000.0, False) 5200 + elif(option == 'bufsize'): 5201 + sysvals.bufsize = getArgInt('bufsize', value, 1, 1024*1024*8, False) 5202 + elif(option == 'output-dir'): 5203 + sysvals.outdir = sysvals.setOutputFolder(value) 5406 5204 5407 5205 if sysvals.suspendmode == 'command' and not sysvals.testcommand: 5408 5206 doError('No command supplied for mode "command"') ··· 5573 5259 print(' -rtcwake t Wakeup t seconds after suspend, set t to "off" to disable (default: 15)') 5574 5260 print(' -addlogs Add the dmesg and ftrace logs to the html output') 5575 5261 print(' -srgap Add a visible gap in the timeline between sus/res (default: disabled)') 5262 + print(' -skiphtml Run the test and capture the trace logs, but skip the timeline (default: disabled)') 5263 + print(' -result fn Export a results table to a text file for parsing.') 5264 + print(' [testprep]') 5265 + print(' -sync Sync the filesystems before starting the test') 5266 + print(' -rs on/off Enable/disable runtime suspend for all devices, restore all after test') 5267 + print(' -display on/off Turn the display on or off for the test') 5576 5268 print(' [advanced]') 5269 + print(' -gzip Gzip the trace and dmesg logs to save space') 5577 5270 print(' -cmd {s} Run the timeline over a custom command, e.g. "sync -d"') 5578 5271 print(' -proc Add usermode process info into the timeline (default: disabled)') 5579 5272 print(' -dev Add kernel function calls and threads to the timeline (default: disabled)') ··· 5601 5280 print(' -cgphase P Only show callgraph data for phase P (e.g. suspend_late)') 5602 5281 print(' -cgtest N Only show callgraph data for test N (e.g. 0 or 1 in an x2 run)') 5603 5282 print(' -timeprec N Number of significant digits in timestamps (0:S, [3:ms], 6:us)') 5283 + print(' -cgfilter S Filter the callgraph output in the timeline') 5284 + print(' -cgskip file Callgraph functions to skip, off to disable (default: cgskip.txt)') 5285 + print(' -bufsize N Set trace buffer size to N kilo-bytes (default: all of free memory)') 5604 5286 print('') 5605 5287 print('Other commands:') 5606 5288 print(' -modes List available suspend modes') 5607 5289 print(' -status Test to see if the system is enabled to run this tool') 5608 5290 print(' -fpdt Print out the contents of the ACPI Firmware Performance Data Table') 5609 5291 print(' -sysinfo Print out system info extracted from BIOS') 5610 - print(' -usbtopo Print out the current USB topology with power info') 5611 - print(' -usbauto Enable autosuspend for all connected USB devices') 5292 + print(' -devinfo Print out the pm settings of all devices which support runtime suspend') 5612 5293 print(' -flist Print the list of functions currently being captured in ftrace') 5613 5294 print(' -flistall Print all functions capable of being captured in ftrace') 5614 5295 print(' -summary directory Create a summary of all test in this dir') ··· 5624 5301 # exec start (skipped if script is loaded as library) 5625 5302 if __name__ == '__main__': 5626 5303 cmd = '' 5627 - outdir = '' 5628 - multitest = {'run': False, 'count': 0, 'delay': 0} 5629 - simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall', '-usbtopo', '-usbauto', '-status'] 5304 + simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall', '-devinfo', '-status'] 5305 + if '-f' in sys.argv: 5306 + sysvals.cgskip = sysvals.configFile('cgskip.txt') 5630 5307 # loop through the command line arguments 5631 5308 args = iter(sys.argv[1:]) 5632 5309 for arg in args: ··· 5656 5333 sysvals.postdelay = getArgInt('-postdelay', args, 0, 60000) 5657 5334 elif(arg == '-f'): 5658 5335 sysvals.usecallgraph = True 5336 + elif(arg == '-skiphtml'): 5337 + sysvals.skiphtml = True 5338 + elif(arg == '-cgdump'): 5339 + sysvals.cgdump = True 5659 5340 elif(arg == '-addlogs'): 5660 5341 sysvals.dmesglog = sysvals.ftracelog = True 5661 5342 elif(arg == '-verbose'): ··· 5668 5341 sysvals.useprocmon = True 5669 5342 elif(arg == '-dev'): 5670 5343 sysvals.usedevsrc = True 5344 + elif(arg == '-sync'): 5345 + sysvals.sync = True 5346 + elif(arg == '-gzip'): 5347 + sysvals.gzip = True 5348 + elif(arg == '-rs'): 5349 + try: 5350 + val = args.next() 5351 + except: 5352 + doError('-rs requires "enable" or "disable"', True) 5353 + if val.lower() in switchvalues: 5354 + if val.lower() in switchoff: 5355 + sysvals.rs = -1 5356 + else: 5357 + sysvals.rs = 1 5358 + else: 5359 + doError('invalid option: %s, use "enable/disable" or "on/off"' % val, True) 5360 + elif(arg == '-display'): 5361 + try: 5362 + val = args.next() 5363 + except: 5364 + doError('-display requires "on" or "off"', True) 5365 + if val.lower() in switchvalues: 5366 + if val.lower() in switchoff: 5367 + sysvals.display = -1 5368 + else: 5369 + sysvals.display = 1 5370 + else: 5371 + doError('invalid option: %s, use "on/off"' % val, True) 5671 5372 elif(arg == '-maxdepth'): 5672 5373 sysvals.max_graph_depth = getArgInt('-maxdepth', args, 0, 1000) 5673 5374 elif(arg == '-rtcwake'): ··· 5703 5348 val = args.next() 5704 5349 except: 5705 5350 doError('No rtcwake time supplied', True) 5706 - if val.lower() == 'off': 5351 + if val.lower() in switchoff: 5707 5352 sysvals.rtcwake = False 5708 5353 else: 5709 5354 sysvals.rtcwake = True ··· 5714 5359 sysvals.mindevlen = getArgFloat('-mindev', args, 0.0, 10000.0) 5715 5360 elif(arg == '-mincg'): 5716 5361 sysvals.mincglen = getArgFloat('-mincg', args, 0.0, 10000.0) 5362 + elif(arg == '-bufsize'): 5363 + sysvals.bufsize = getArgInt('-bufsize', args, 1, 1024*1024*8) 5717 5364 elif(arg == '-cgtest'): 5718 5365 sysvals.cgtest = getArgInt('-cgtest', args, 0, 1) 5719 5366 elif(arg == '-cgphase'): ··· 5725 5368 doError('No phase name supplied', True) 5726 5369 d = Data(0) 5727 5370 if val not in d.phases: 5728 - doError('Invalid phase, valid phaess are %s' % d.phases, True) 5371 + doError('invalid phase --> (%s: %s), valid phases are %s'\ 5372 + % (arg, val, d.phases), True) 5729 5373 sysvals.cgphase = val 5374 + elif(arg == '-cgfilter'): 5375 + try: 5376 + val = args.next() 5377 + except: 5378 + doError('No callgraph functions supplied', True) 5379 + sysvals.setCallgraphFilter(val) 5380 + elif(arg == '-cgskip'): 5381 + try: 5382 + val = args.next() 5383 + except: 5384 + doError('No file supplied', True) 5385 + if val.lower() in switchoff: 5386 + sysvals.cgskip = '' 5387 + else: 5388 + sysvals.cgskip = sysvals.configFile(val) 5389 + if(not sysvals.cgskip): 5390 + doError('%s does not exist' % sysvals.cgskip) 5730 5391 elif(arg == '-callloop-maxgap'): 5731 5392 sysvals.callloopmaxgap = getArgFloat('-callloop-maxgap', args, 0.0, 1.0) 5732 5393 elif(arg == '-callloop-maxlen'): ··· 5761 5386 elif(arg == '-srgap'): 5762 5387 sysvals.srgap = 5 5763 5388 elif(arg == '-multi'): 5764 - multitest['run'] = True 5765 - multitest['count'] = getArgInt('-multi n (exec count)', args, 2, 1000000) 5766 - multitest['delay'] = getArgInt('-multi d (delay between tests)', args, 0, 3600) 5389 + sysvals.multitest['run'] = True 5390 + sysvals.multitest['count'] = getArgInt('-multi n d (exec count)', args, 2, 1000000) 5391 + sysvals.multitest['delay'] = getArgInt('-multi n d (delay between tests)', args, 0, 3600) 5767 5392 elif(arg == '-o'): 5768 5393 try: 5769 5394 val = args.next() 5770 5395 except: 5771 5396 doError('No subdirectory name supplied', True) 5772 - outdir = sysvals.setOutputFolder(val) 5397 + sysvals.outdir = sysvals.setOutputFolder(val) 5773 5398 elif(arg == '-config'): 5774 5399 try: 5775 5400 val = args.next() 5776 5401 except: 5777 5402 doError('No text file supplied', True) 5778 - if(os.path.exists(val) == False): 5403 + file = sysvals.configFile(val) 5404 + if(not file): 5779 5405 doError('%s does not exist' % val) 5780 - configFromFile(val) 5406 + configFromFile(file) 5781 5407 elif(arg == '-fadd'): 5782 5408 try: 5783 5409 val = args.next() 5784 5410 except: 5785 5411 doError('No text file supplied', True) 5786 - if(os.path.exists(val) == False): 5412 + file = sysvals.configFile(val) 5413 + if(not file): 5787 5414 doError('%s does not exist' % val) 5788 - sysvals.addFtraceFilterFunctions(val) 5415 + sysvals.addFtraceFilterFunctions(file) 5789 5416 elif(arg == '-dmesg'): 5790 5417 try: 5791 5418 val = args.next() ··· 5812 5435 except: 5813 5436 doError('No directory supplied', True) 5814 5437 cmd = 'summary' 5815 - outdir = val 5438 + sysvals.outdir = val 5816 5439 sysvals.notestrun = True 5817 5440 if(os.path.isdir(val) == False): 5818 5441 doError('%s is not accesible' % val) ··· 5822 5445 except: 5823 5446 doError('No devnames supplied', True) 5824 5447 sysvals.setDeviceFilter(val) 5448 + elif(arg == '-result'): 5449 + try: 5450 + val = args.next() 5451 + except: 5452 + doError('No result file supplied', True) 5453 + sysvals.result = val 5825 5454 else: 5826 5455 doError('Invalid argument: '+arg, True) 5827 5456 ··· 5837 5454 if(sysvals.usecallgraph and sysvals.useprocmon): 5838 5455 doError('-proc is not compatible with -f') 5839 5456 5457 + if sysvals.usecallgraph and sysvals.cgskip: 5458 + sysvals.vprint('Using cgskip file: %s' % sysvals.cgskip) 5459 + sysvals.setCallgraphBlacklist(sysvals.cgskip) 5460 + 5840 5461 # callgraph size cannot exceed device size 5841 5462 if sysvals.mincglen < sysvals.mindevlen: 5842 5463 sysvals.mincglen = sysvals.mindevlen 5843 5464 5844 - # just run a utility command and exit 5465 + # remove existing buffers before calculating memory 5466 + if(sysvals.usecallgraph or sysvals.usedevsrc): 5467 + sysvals.fsetVal('16', 'buffer_size_kb') 5845 5468 sysvals.cpuInfo() 5469 + 5470 + # just run a utility command and exit 5846 5471 if(cmd != ''): 5847 5472 if(cmd == 'status'): 5848 5473 statusCheck(True) 5849 5474 elif(cmd == 'fpdt'): 5850 5475 getFPDT(True) 5851 5476 elif(cmd == 'sysinfo'): 5852 - sysvals.printSystemInfo() 5853 - elif(cmd == 'usbtopo'): 5854 - detectUSB() 5477 + sysvals.printSystemInfo(True) 5478 + elif(cmd == 'devinfo'): 5479 + deviceInfo() 5855 5480 elif(cmd == 'modes'): 5856 5481 print getModes() 5857 5482 elif(cmd == 'flist'): 5858 5483 sysvals.getFtraceFilterFunctions(True) 5859 5484 elif(cmd == 'flistall'): 5860 5485 sysvals.getFtraceFilterFunctions(False) 5861 - elif(cmd == 'usbauto'): 5862 - setUSBDevicesAuto() 5863 5486 elif(cmd == 'summary'): 5864 - runSummary(outdir, True) 5487 + runSummary(sysvals.outdir, True) 5865 5488 sys.exit() 5866 5489 5867 5490 # if instructed, re-analyze existing data files 5868 5491 if(sysvals.notestrun): 5869 - rerunTest() 5492 + stamp = rerunTest() 5493 + sysvals.outputResult(stamp) 5870 5494 sys.exit() 5871 5495 5872 5496 # verify that we can run a test 5873 5497 if(not statusCheck()): 5874 - print('Check FAILED, aborting the test run!') 5875 - sys.exit() 5498 + doError('Check FAILED, aborting the test run!') 5876 5499 5877 5500 # extract mem modes and convert 5878 5501 mode = sysvals.suspendmode ··· 5898 5509 5899 5510 sysvals.systemInfo(dmidecode(sysvals.mempath)) 5900 5511 5901 - if multitest['run']: 5512 + setRuntimeSuspend(True) 5513 + if sysvals.display: 5514 + call('xset -d :0.0 dpms 0 0 0', shell=True) 5515 + call('xset -d :0.0 s off', shell=True) 5516 + if sysvals.multitest['run']: 5902 5517 # run multiple tests in a separate subdirectory 5903 - if not outdir: 5904 - s = 'suspend-x%d' % multitest['count'] 5905 - outdir = datetime.now().strftime(s+'-%y%m%d-%H%M%S') 5906 - if not os.path.isdir(outdir): 5907 - os.mkdir(outdir) 5908 - for i in range(multitest['count']): 5518 + if not sysvals.outdir: 5519 + s = 'suspend-x%d' % sysvals.multitest['count'] 5520 + sysvals.outdir = datetime.now().strftime(s+'-%y%m%d-%H%M%S') 5521 + if not os.path.isdir(sysvals.outdir): 5522 + os.mkdir(sysvals.outdir) 5523 + for i in range(sysvals.multitest['count']): 5909 5524 if(i != 0): 5910 - print('Waiting %d seconds...' % (multitest['delay'])) 5911 - time.sleep(multitest['delay']) 5912 - print('TEST (%d/%d) START' % (i+1, multitest['count'])) 5525 + print('Waiting %d seconds...' % (sysvals.multitest['delay'])) 5526 + time.sleep(sysvals.multitest['delay']) 5527 + print('TEST (%d/%d) START' % (i+1, sysvals.multitest['count'])) 5913 5528 fmt = 'suspend-%y%m%d-%H%M%S' 5914 - sysvals.testdir = os.path.join(outdir, datetime.now().strftime(fmt)) 5915 - runTest() 5916 - print('TEST (%d/%d) COMPLETE' % (i+1, multitest['count'])) 5917 - runSummary(outdir, False) 5529 + sysvals.testdir = os.path.join(sysvals.outdir, datetime.now().strftime(fmt)) 5530 + runTest(i+1) 5531 + print('TEST (%d/%d) COMPLETE' % (i+1, sysvals.multitest['count'])) 5532 + sysvals.logmsg = '' 5533 + if not sysvals.skiphtml: 5534 + runSummary(sysvals.outdir, False) 5535 + sysvals.sudouser(sysvals.outdir) 5918 5536 else: 5919 - if outdir: 5920 - sysvals.testdir = outdir 5537 + if sysvals.outdir: 5538 + sysvals.testdir = sysvals.outdir 5921 5539 # run the test in the current directory 5922 5540 runTest() 5541 + if sysvals.display: 5542 + call('xset -d :0.0 s reset', shell=True) 5543 + setRuntimeSuspend(False)