diff options
author | Todd E Brandt <todd.e.brandt@linux.intel.com> | 2018-01-30 03:17:20 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2018-02-21 17:56:22 -0500 |
commit | 700abc90f07b7f60e512c5d5b7cb33bdc207e47c (patch) | |
tree | 45c14bb495ff2b4775c75b7dfb8495cc9aa080d4 | |
parent | d83a76a8ec34b3c52e9aadafadb370ebd82cf79a (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.8 | 47 | ||||
-rwxr-xr-x | tools/power/pm-graph/sleepgraph.py | 1625 |
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 |
53 | Add the dmesg and ftrace logs to the html output. They will be viewable by | 53 | Add the dmesg and ftrace logs to the html output. They will be viewable by |
54 | clicking buttons in the timeline. | 54 | clicking buttons in the timeline. |
55 | .TP | ||
56 | \fB-result \fIfile\fR | ||
57 | Export a results table to a text file for parsing. | ||
58 | .TP | ||
59 | \fB-sync\fR | ||
60 | Sync the filesystems before starting the test. This reduces the size of | ||
61 | the sys_sync call which happens in the suspend_prepare phase. | ||
62 | .TP | ||
63 | \fB-rs \fIenable/disable\fR | ||
64 | During test, enable/disable runtime suspend for all devices. The test is delayed | ||
65 | by 5 seconds to allow runtime suspend changes to occur. The settings are restored | ||
66 | after the test is complete. | ||
67 | .TP | ||
68 | \fB-display \fIon/off\fR | ||
69 | Turn the display on or off for the test using the xset command. This helps | ||
70 | maintain the consistecy of test data for better comparison. | ||
71 | .TP | ||
72 | \fB-skiphtml\fR | ||
73 | Run the test and capture the trace logs, but skip the timeline generation. | ||
55 | 74 | ||
56 | .SS "advanced" | 75 | .SS "advanced" |
57 | .TP | 76 | .TP |
77 | \fB-gzip\fR | ||
78 | Gzip the trace and dmesg logs to save space. The tool can also read in gzipped | ||
79 | logs for processing. | ||
80 | .TP | ||
58 | \fB-cmd \fIstr\fR | 81 | \fB-cmd \fIstr\fR |
59 | Run the timeline over a custom suspend command, e.g. pm-suspend. By default | 82 | Run the timeline over a custom suspend command, e.g. pm-suspend. By default |
60 | the tool forces suspend via /sys/power/state so this allows testing over | 83 | the 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 | |||
114 | which are barely visible in the timeline. | 137 | which are barely visible in the timeline. |
115 | The value is a float: e.g. 0.001 represents 1 us. | 138 | The value is a float: e.g. 0.001 represents 1 us. |
116 | .TP | 139 | .TP |
140 | \fB-cgfilter \fI"func1,func2,..."\fR | ||
141 | Reduce callgraph output in the timeline by limiting it to a list of calls. The | ||
142 | argument can be a single function name or a comma delimited list. | ||
143 | (default: none) | ||
144 | .TP | ||
145 | \fB-cgskip \fIfile\fR | ||
146 | Reduce callgraph timeline size by skipping over uninteresting functions | ||
147 | in the trace, e.g. printk or console_unlock. The functions listed | ||
148 | in this file will show up as empty leaves in the callgraph with only the start/end | ||
149 | times displayed. cgskip.txt is used automatically if found in the path, so | ||
150 | use "off" to disable completely (default: cgskip.txt) | ||
151 | .TP | ||
117 | \fB-cgphase \fIp\fR | 152 | \fB-cgphase \fIp\fR |
118 | Only show callgraph data for phase \fIp\fR (e.g. suspend_late). | 153 | Only 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 |
124 | Number of significant digits in timestamps (0:S, [3:ms], 6:us). | 159 | Number of significant digits in timestamps (0:S, [3:ms], 6:us). |
160 | .TP | ||
161 | \fB-bufsize \fIN\fR | ||
162 | Set 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 |
145 | Print out system info extracted from BIOS. Reads /dev/mem directly instead of going through dmidecode. | 183 | Print 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 |
148 | Print out the current USB topology with power info. | 186 | Print out the pm settings of all devices which support runtime suspend. |
149 | .TP | ||
150 | \fB-usbauto\fR | ||
151 | Enable autosuspend for all connected USB devices. | ||
152 | .TP | 187 | .TP |
153 | \fB-flist\fR | 188 | \fB-flist\fR |
154 | Print the list of ftrace functions currently being captured. Functions | 189 | Print 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 |
199 | Execute a standby with a 15 second wakeup. Change the output folder name. | 234 | Execute 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 |
203 | Execute a freeze with no wakeup (require keypress). Change output folder name. | 238 | Execute 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 | |||
57 | from datetime import datetime | 57 | from datetime import datetime |
58 | import struct | 58 | import struct |
59 | import ConfigParser | 59 | import ConfigParser |
60 | import gzip | ||
60 | from threading import Thread | 61 | from threading import Thread |
61 | from subprocess import call, Popen, PIPE | 62 | from 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 |
69 | class SystemValues: | 70 | class 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 | ||
700 | sysvals = SystemValues() | 788 | sysvals = SystemValues() |
789 | switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0'] | ||
790 | switchoff = ['disable', 'off', 'false', '0'] | ||
701 | suspendmodename = { | 791 | suspendmodename = { |
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('<', '<').replace('>', '>') | 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 | ||
1810 | class DevItem: | 2020 | class 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: | |||
2067 | class TestProps: | 2277 | class 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 | ||
2194 | def 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 | ||
2204 | def doesTraceLogHaveTraceEvents(): | 2414 | def 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 |
2444 | def parseTraceLog(): | 2666 | def 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 |
2881 | def loadKernelLog(justtext=False): | 3118 | def 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+' → '+cg.name, color, dev['id']) | 3505 | name+' → '+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→</div>\n' | 3622 | html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">{2}→</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('<', '<').replace('>', '>') | 3938 | line = line.replace('<', '<').replace('>', '>') |
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 | ||
4448 | def 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 | 4575 | def 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 | ||
4316 | def 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 | ||
4334 | def 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" |
4348 | def ms2nice(val): | 4586 | def 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 | 4597 | def 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 |
4362 | def detectUSB(): | 4608 | def 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 | ||
4960 | def processData(): | 5203 | def 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(): | |||
4980 | def rerunTest(): | 5237 | def 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 |
4997 | def runTest(): | 5254 | def 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 | ||
5015 | def find_in_html(html, strs, div=False): | 5271 | def 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 |
5075 | def checkArgBool(value): | 5331 | def 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) |
5302 | if __name__ == '__main__': | 5625 | if __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) | ||