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