diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2019-06-13 05:06:21 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2019-06-13 05:06:21 -0400 |
commit | d382084f77b0b4c3eb594cfec893d116c21fb034 (patch) | |
tree | 02ed7b4ba4f90e458a303c483ce055e7414918a8 | |
parent | 30f234b4d6f05baa88dc46e8d16587f11990ef5f (diff) | |
parent | d5a5e4ec5b415e63dee9dd76653377455572ac09 (diff) |
Merge back earlier pm-tools material for v5.3.
-rw-r--r-- | tools/power/pm-graph/README | 552 | ||||
-rwxr-xr-x | tools/power/pm-graph/bootgraph.py | 8 | ||||
-rw-r--r-- | tools/power/pm-graph/config/example.cfg | 26 | ||||
-rw-r--r-- | tools/power/pm-graph/sleepgraph.8 | 16 | ||||
-rwxr-xr-x | tools/power/pm-graph/sleepgraph.py | 857 |
5 files changed, 1253 insertions, 206 deletions
diff --git a/tools/power/pm-graph/README b/tools/power/pm-graph/README new file mode 100644 index 000000000000..58a5591e3951 --- /dev/null +++ b/tools/power/pm-graph/README | |||
@@ -0,0 +1,552 @@ | |||
1 | p m - g r a p h | ||
2 | |||
3 | pm-graph: suspend/resume/boot timing analysis tools | ||
4 | Version: 5.4 | ||
5 | Author: Todd Brandt <todd.e.brandt@intel.com> | ||
6 | Home Page: https://01.org/pm-graph | ||
7 | |||
8 | Report bugs/issues at bugzilla.kernel.org Tools/pm-graph | ||
9 | - https://bugzilla.kernel.org/buglist.cgi?component=pm-graph&product=Tools | ||
10 | |||
11 | Full documentation available online & in man pages | ||
12 | - Getting Started: | ||
13 | https://01.org/pm-graph/documentation/getting-started | ||
14 | |||
15 | - Config File Format: | ||
16 | https://01.org/pm-graph/documentation/3-config-file-format | ||
17 | |||
18 | - upstream version in git: | ||
19 | https://github.com/intel/pm-graph/ | ||
20 | |||
21 | Table of Contents | ||
22 | - Overview | ||
23 | - Setup | ||
24 | - Usage | ||
25 | - Basic Usage | ||
26 | - Dev Mode Usage | ||
27 | - Proc Mode Usage | ||
28 | - Configuration Files | ||
29 | - Usage Examples | ||
30 | - Config File Options | ||
31 | - Custom Timeline Entries | ||
32 | - Adding/Editing Timeline Functions | ||
33 | - Adding/Editing Dev Timeline Source Functions | ||
34 | - Verifying your Custom Functions | ||
35 | - Testing on consumer linux Operating Systems | ||
36 | - Android | ||
37 | |||
38 | ------------------------------------------------------------------ | ||
39 | | OVERVIEW | | ||
40 | ------------------------------------------------------------------ | ||
41 | |||
42 | This tool suite is designed to assist kernel and OS developers in optimizing | ||
43 | their linux stack's suspend/resume & boot time. Using a kernel image built | ||
44 | with a few extra options enabled, the tools will execute a suspend or boot, | ||
45 | and will capture dmesg and ftrace data. This data is transformed into a set of | ||
46 | timelines and a callgraph to give a quick and detailed view of which devices | ||
47 | and kernel processes are taking the most time in suspend/resume & boot. | ||
48 | |||
49 | ------------------------------------------------------------------ | ||
50 | | SETUP | | ||
51 | ------------------------------------------------------------------ | ||
52 | |||
53 | These packages are required to execute the scripts | ||
54 | - python | ||
55 | - python-requests | ||
56 | |||
57 | Ubuntu: | ||
58 | sudo apt-get install python python-requests | ||
59 | |||
60 | Fedora: | ||
61 | sudo dnf install python python-requests | ||
62 | |||
63 | The tools can most easily be installed via git clone and make install | ||
64 | |||
65 | $> git clone http://github.com/intel/pm-graph.git | ||
66 | $> cd pm-graph | ||
67 | $> sudo make install | ||
68 | $> man sleepgraph ; man bootgraph | ||
69 | |||
70 | Setup involves some minor kernel configuration | ||
71 | |||
72 | The following kernel build options are required for all kernels: | ||
73 | CONFIG_DEVMEM=y | ||
74 | CONFIG_PM_DEBUG=y | ||
75 | CONFIG_PM_SLEEP_DEBUG=y | ||
76 | CONFIG_FTRACE=y | ||
77 | CONFIG_FUNCTION_TRACER=y | ||
78 | CONFIG_FUNCTION_GRAPH_TRACER=y | ||
79 | CONFIG_KPROBES=y | ||
80 | CONFIG_KPROBES_ON_FTRACE=y | ||
81 | |||
82 | In kernel 3.15.0, two patches were upstreamed which enable the | ||
83 | v3.0 behavior. These patches allow the tool to read all the | ||
84 | data from trace events instead of from dmesg. You can enable | ||
85 | this behavior on earlier kernels with these patches: | ||
86 | |||
87 | (kernel/pre-3.15/enable_trace_events_suspend_resume.patch) | ||
88 | (kernel/pre-3.15/enable_trace_events_device_pm_callback.patch) | ||
89 | |||
90 | If you're using a kernel older than 3.15.0, the following | ||
91 | additional kernel parameters are required: | ||
92 | (e.g. in file /etc/default/grub) | ||
93 | GRUB_CMDLINE_LINUX_DEFAULT="... initcall_debug log_buf_len=32M ..." | ||
94 | |||
95 | If you're using a kernel older than 3.11-rc2, the following simple | ||
96 | patch must be applied to enable ftrace data: | ||
97 | in file: kernel/power/suspend.c | ||
98 | in function: int suspend_devices_and_enter(suspend_state_t state) | ||
99 | remove call to "ftrace_stop();" | ||
100 | remove call to "ftrace_start();" | ||
101 | |||
102 | There is a patch which does this for kernel v3.8.0: | ||
103 | (kernel/pre-3.11-rc2/enable_ftrace_in_suspendresume.patch) | ||
104 | |||
105 | |||
106 | |||
107 | ------------------------------------------------------------------ | ||
108 | | USAGE | | ||
109 | ------------------------------------------------------------------ | ||
110 | |||
111 | Basic Usage | ||
112 | ___________ | ||
113 | |||
114 | 1) First configure a kernel using the instructions from the previous sections. | ||
115 | Then build, install, and boot with it. | ||
116 | 2) Open up a terminal window and execute the mode list command: | ||
117 | |||
118 | %> sudo ./sleepgraph.py -modes | ||
119 | ['freeze', 'mem', 'disk'] | ||
120 | |||
121 | Execute a test using one of the available power modes, e.g. mem (S3): | ||
122 | |||
123 | %> sudo ./sleepgraph.py -m mem -rtcwake 15 | ||
124 | |||
125 | or with a config file | ||
126 | |||
127 | %> sudo ./sleepgraph.py -config config/suspend.cfg | ||
128 | |||
129 | When the system comes back you'll see the script finishing up and | ||
130 | creating the output files in the test subdir. It generates output | ||
131 | files in subdirectory: suspend-mmddyy-HHMMSS. The ftrace file can | ||
132 | be used to regenerate the html timeline with different options | ||
133 | |||
134 | HTML output: <hostname>_<mode>.html | ||
135 | raw dmesg output: <hostname>_<mode>_dmesg.txt | ||
136 | raw ftrace output: <hostname>_<mode>_ftrace.txt | ||
137 | |||
138 | View the html in firefox or chrome. | ||
139 | |||
140 | |||
141 | Dev Mode Usage | ||
142 | ______________ | ||
143 | |||
144 | Developer mode adds information on low level source calls to the timeline. | ||
145 | The tool sets kprobes on all delay and mutex calls to see which devices | ||
146 | are waiting for something and when. It also sets a suite of kprobes on | ||
147 | subsystem dependent calls to better fill out the timeline. | ||
148 | |||
149 | The tool will also expose kernel threads that don't normally show up in the | ||
150 | timeline. This is useful in discovering dependent threads to get a better | ||
151 | idea of what each device is waiting for. For instance, the scsi_eh thread, | ||
152 | a.k.a. scsi resume error handler, is what each SATA disk device waits for | ||
153 | before it can continue resume. | ||
154 | |||
155 | The timeline will be much larger if run with dev mode, so it can be useful | ||
156 | to set the -mindev option to clip out any device blocks that are too small | ||
157 | to see easily. The following command will give a nice dev mode run: | ||
158 | |||
159 | %> sudo ./sleepgraph.py -m mem -rtcwake 15 -mindev 1 -dev | ||
160 | |||
161 | or with a config file | ||
162 | |||
163 | %> sudo ./sleepgraph.py -config config/suspend-dev.cfg | ||
164 | |||
165 | |||
166 | Proc Mode Usage | ||
167 | _______________ | ||
168 | |||
169 | Proc mode adds user process info to the timeline. This is done in a manner | ||
170 | similar to the bootchart utility, which graphs init processes and their | ||
171 | execution as the system boots. This tool option does the same thing but for | ||
172 | the period before and after suspend/resume. | ||
173 | |||
174 | In order to see any process info, there needs to be some delay before or | ||
175 | after resume since processes are frozen in suspend_prepare and thawed in | ||
176 | resume_complete. The predelay and postdelay args allow you to do this. It | ||
177 | can also be useful to run in x2 mode with an x2 delay, this way you can | ||
178 | see process activity before and after resume, and in between two | ||
179 | successive suspend/resumes. | ||
180 | |||
181 | The command can be run like this: | ||
182 | |||
183 | %> sudo ./sleepgraph.py -m mem -rtcwake 15 -x2 -x2delay 1000 -predelay 1000 -postdelay 1000 -proc | ||
184 | |||
185 | or with a config file | ||
186 | |||
187 | %> sudo ./sleepgraph.py -config config/suspend-proc.cfg | ||
188 | |||
189 | |||
190 | ------------------------------------------------------------------ | ||
191 | | CONFIGURATION FILES | | ||
192 | ------------------------------------------------------------------ | ||
193 | |||
194 | Since 4.0 we've moved to using config files in lieu of command line options. | ||
195 | The config folder contains a collection of typical use cases. | ||
196 | There are corresponding configs for other power modes: | ||
197 | |||
198 | Simple suspend/resume with basic timeline (mem/freeze/standby) | ||
199 | config/suspend.cfg | ||
200 | config/freeze.cfg | ||
201 | config/standby.cfg | ||
202 | |||
203 | Dev mode suspend/resume with dev timeline (mem/freeze/standby) | ||
204 | config/suspend-dev.cfg | ||
205 | config/freeze-dev.cfg | ||
206 | config/standby-dev.cfg | ||
207 | |||
208 | Simple suspend/resume with timeline and callgraph (mem/freeze/standby) | ||
209 | config/suspend-callgraph.cfg | ||
210 | config/freeze-callgraph.cfg | ||
211 | config/standby-callgraph.cfg | ||
212 | |||
213 | Sample proc mode x2 run using mem suspend | ||
214 | config/suspend-x2-proc.cfg | ||
215 | |||
216 | Sample for editing timeline funcs (moves internal functions into config) | ||
217 | config/custom-timeline-functions.cfg | ||
218 | |||
219 | Sample debug config for serio subsystem | ||
220 | config/debug-serio-suspend.cfg | ||
221 | |||
222 | |||
223 | Usage Examples | ||
224 | ______________ | ||
225 | |||
226 | Run a simple mem suspend: | ||
227 | %> sudo ./sleepgraph.py -config config/suspend.cfg | ||
228 | |||
229 | Run a mem suspend with callgraph data: | ||
230 | %> sudo ./sleepgraph.py -config config/suspend-callgraph.cfg | ||
231 | |||
232 | Run a mem suspend with dev mode detail: | ||
233 | %> sudo ./sleepgraph.py -config config/suspend-dev.cfg | ||
234 | |||
235 | |||
236 | Config File Options | ||
237 | ___________________ | ||
238 | |||
239 | [Settings] | ||
240 | |||
241 | # Verbosity: print verbose messages (def: false) | ||
242 | verbose: false | ||
243 | |||
244 | # Suspend Mode: e.g. standby, mem, freeze, disk (def: mem) | ||
245 | mode: mem | ||
246 | |||
247 | # Output Directory Format: {hostname}, {date}, {time} give current values | ||
248 | output-dir: suspend-{hostname}-{date}-{time} | ||
249 | |||
250 | # Automatic Wakeup: use rtcwake to wakeup after X seconds (def: infinity) | ||
251 | rtcwake: 15 | ||
252 | |||
253 | # Add Logs: add the dmesg and ftrace log to the html output (def: false) | ||
254 | addlogs: false | ||
255 | |||
256 | # Sus/Res Gap: insert a gap between sus & res in the timeline (def: false) | ||
257 | srgap: false | ||
258 | |||
259 | # Custom Command: Command to execute in lieu of suspend (def: "") | ||
260 | command: echo mem > /sys/power/state | ||
261 | |||
262 | # Proc mode: graph user processes and cpu usage in the timeline (def: false) | ||
263 | proc: false | ||
264 | |||
265 | # Dev mode: graph source functions in the timeline (def: false) | ||
266 | dev: false | ||
267 | |||
268 | # Suspend/Resume x2: run 2 suspend/resumes back to back (def: false) | ||
269 | x2: false | ||
270 | |||
271 | # x2 Suspend Delay: time delay between the two test runs in ms (def: 0 ms) | ||
272 | x2delay: 0 | ||
273 | |||
274 | # Pre Suspend Delay: nclude an N ms delay before (1st) suspend (def: 0 ms) | ||
275 | predelay: 0 | ||
276 | |||
277 | # Post Resume Delay: include an N ms delay after (last) resume (def: 0 ms) | ||
278 | postdelay: 0 | ||
279 | |||
280 | # Min Device Length: graph only dev callbacks longer than min (def: 0.001 ms) | ||
281 | mindev: 0.001 | ||
282 | |||
283 | # Callgraph: gather ftrace callgraph data on all timeline events (def: false) | ||
284 | callgraph: false | ||
285 | |||
286 | # Expand Callgraph: pre-expand the callgraph treeviews in html (def: false) | ||
287 | expandcg: false | ||
288 | |||
289 | # Min Callgraph Length: show callgraphs only if longer than min (def: 1 ms) | ||
290 | mincg: 1 | ||
291 | |||
292 | # Timestamp Precision: number of sig digits in timestamps (0:S, [3:ms], 6:us) | ||
293 | timeprec: 3 | ||
294 | |||
295 | # Device Filter: show only devs whose name/driver includes one of these strings | ||
296 | devicefilter: _cpu_up,_cpu_down,i915,usb | ||
297 | |||
298 | # Override default timeline entries: | ||
299 | # Do not use the internal default functions for timeline entries (def: false) | ||
300 | # Set this to true if you intend to only use the ones defined in the config | ||
301 | override-timeline-functions: true | ||
302 | |||
303 | # Override default dev timeline entries: | ||
304 | # Do not use the internal default functions for dev timeline entries (def: false) | ||
305 | # Set this to true if you intend to only use the ones defined in the config | ||
306 | override-dev-timeline-functions: true | ||
307 | |||
308 | # Call Loop Max Gap (dev mode only) | ||
309 | # merge loops of the same call if each is less than maxgap apart (def: 100us) | ||
310 | callloop-maxgap: 0.0001 | ||
311 | |||
312 | # Call Loop Max Length (dev mode only) | ||
313 | # merge loops of the same call if each is less than maxlen in length (def: 5ms) | ||
314 | callloop-maxlen: 0.005 | ||
315 | |||
316 | ------------------------------------------------------------------ | ||
317 | | CUSTOM TIMELINE ENTRIES | | ||
318 | ------------------------------------------------------------------ | ||
319 | |||
320 | Adding or Editing Timeline Functions | ||
321 | ____________________________________ | ||
322 | |||
323 | The tool uses an array of function names to fill out empty spaces in the | ||
324 | timeline where device callbacks don't appear. For instance, in suspend_prepare | ||
325 | the tool adds the sys_sync and freeze_processes calls as virtual device blocks | ||
326 | in the timeline to show you where the time is going. These calls should fill | ||
327 | the timeline with contiguous data so that most kernel execution is covered. | ||
328 | |||
329 | It is possible to add new function calls to the timeline by adding them to | ||
330 | the config. It's also possible to copy the internal timeline functions into | ||
331 | the config so that you can override and edit them. Place them in the | ||
332 | timeline_functions_ARCH section with the name of your architecture appended. | ||
333 | i.e. for x86_64: [timeline_functions_x86_64] | ||
334 | |||
335 | Use the override-timeline-functions option if you only want to use your | ||
336 | custom calls, or leave it false to append them to the internal ones. | ||
337 | |||
338 | This section includes a list of functions (set using kprobes) which use both | ||
339 | symbol data and function arg data. The args are pulled directly from the | ||
340 | stack using this architecture's registers and stack formatting. Each entry | ||
341 | can include up to four pieces of info: The function name, a format string, | ||
342 | an argument list, and a color. But only a function name is required. | ||
343 | |||
344 | For a full example config, see config/custom-timeline-functions.cfg. It pulls | ||
345 | all the internal timeline functions into the config and allows you to edit | ||
346 | them. | ||
347 | |||
348 | Entry format: | ||
349 | |||
350 | function: format{fn_arg1}_{fn_arg2} fn_arg1 fn_arg2 ... [color=purple] | ||
351 | |||
352 | Required Arguments: | ||
353 | |||
354 | function: The symbol name for the function you want probed, this is the | ||
355 | minimum required for an entry, it will show up as the function | ||
356 | name with no arguments. | ||
357 | |||
358 | example: _cpu_up: | ||
359 | |||
360 | Optional Arguments: | ||
361 | |||
362 | format: The format to display the data on the timeline in. Use braces to | ||
363 | enclose the arg names. | ||
364 | |||
365 | example: CPU_ON[{cpu}] | ||
366 | |||
367 | color: The color of the entry block in the timeline. The default color is | ||
368 | transparent, so the entry shares the phase color. The color is an | ||
369 | html color string, either a word, or an RGB. | ||
370 | |||
371 | example: [color=#CC00CC] | ||
372 | |||
373 | arglist: A list of arguments from registers/stack addresses. See URL: | ||
374 | https://www.kernel.org/doc/Documentation/trace/kprobetrace.txt | ||
375 | |||
376 | example: cpu=%di:s32 | ||
377 | |||
378 | Here is a full example entry. It displays cpu resume calls in the timeline | ||
379 | in orange. They will appear as CPU_ON[0], CPU_ON[1], etc. | ||
380 | |||
381 | [timeline_functions_x86_64] | ||
382 | _cpu_up: CPU_ON[{cpu}] cpu=%di:s32 [color=orange] | ||
383 | |||
384 | |||
385 | Adding or Editing Dev Mode Timeline Source Functions | ||
386 | ____________________________________________________ | ||
387 | |||
388 | In dev mode, the tool uses an array of function names to monitor source | ||
389 | execution within the timeline entries. | ||
390 | |||
391 | The function calls are displayed inside the main device/call blocks in the | ||
392 | timeline. However, if a function call is not within a main timeline event, | ||
393 | it will spawn an entirely new event named after the caller's kernel thread. | ||
394 | These asynchronous kernel threads will populate in a separate section | ||
395 | beneath the main device/call section. | ||
396 | |||
397 | The tool has a set of hard coded calls which focus on the most common use | ||
398 | cases: msleep, udelay, schedule_timeout, mutex_lock_slowpath, etc. These are | ||
399 | the functions that add a hardcoded time delay to the suspend/resume path. | ||
400 | The tool also includes some common functions native to important | ||
401 | subsystems: ata, i915, and ACPI, etc. | ||
402 | |||
403 | It is possible to add new function calls to the dev timeline by adding them | ||
404 | to the config. It's also possible to copy the internal dev timeline | ||
405 | functions into the config so that you can override and edit them. Place them | ||
406 | in the dev_timeline_functions_ARCH section with the name of your architecture | ||
407 | appended. i.e. for x86_64: [dev_timeline_functions_x86_64] | ||
408 | |||
409 | Use the override-dev-timeline-functions option if you only want to use your | ||
410 | custom calls, or leave it false to append them to the internal ones. | ||
411 | |||
412 | The format is the same as the timeline_functions_x86_64 section. It's a | ||
413 | list of functions (set using kprobes) which use both symbol data and function | ||
414 | arg data. The args are pulled directly from the stack using this | ||
415 | architecture's registers and stack formatting. Each entry can include up | ||
416 | to four pieces of info: The function name, a format string, an argument list, | ||
417 | and a color. But only the function name is required. | ||
418 | |||
419 | For a full example config, see config/custom-timeline-functions.cfg. It pulls | ||
420 | all the internal dev timeline functions into the config and allows you to edit | ||
421 | them. | ||
422 | |||
423 | Here is a full example entry. It displays the ATA port reset calls as | ||
424 | ataN_port_reset in the timeline. This is where most of the SATA disk resume | ||
425 | time goes, so it can be helpful to see the low level call. | ||
426 | |||
427 | [dev_timeline_functions_x86_64] | ||
428 | ata_eh_recover: ata{port}_port_reset port=+36(%di):s32 [color=#CC00CC] | ||
429 | |||
430 | |||
431 | Verifying your custom functions | ||
432 | _______________________________ | ||
433 | |||
434 | Once you have a set of functions (kprobes) defined, it can be useful to | ||
435 | perform a quick check to see if you formatted them correctly and if the system | ||
436 | actually supports them. To do this, run the tool with your config file | ||
437 | and the -status option. The tool will go through all the kprobes (both | ||
438 | custom and internal if you haven't overridden them) and actually attempts | ||
439 | to set them in ftrace. It will then print out success or fail for you. | ||
440 | |||
441 | Note that kprobes which don't actually exist in the kernel won't stop the | ||
442 | tool, they just wont show up. | ||
443 | |||
444 | For example: | ||
445 | |||
446 | sudo ./sleepgraph.py -config config/custom-timeline-functions.cfg -status | ||
447 | Checking this system (myhostname)... | ||
448 | have root access: YES | ||
449 | is sysfs mounted: YES | ||
450 | is "mem" a valid power mode: YES | ||
451 | is ftrace supported: YES | ||
452 | are kprobes supported: YES | ||
453 | timeline data source: FTRACE (all trace events found) | ||
454 | is rtcwake supported: YES | ||
455 | verifying timeline kprobes work: | ||
456 | _cpu_down: YES | ||
457 | _cpu_up: YES | ||
458 | acpi_pm_finish: YES | ||
459 | acpi_pm_prepare: YES | ||
460 | freeze_kernel_threads: YES | ||
461 | freeze_processes: YES | ||
462 | sys_sync: YES | ||
463 | thaw_processes: YES | ||
464 | verifying dev kprobes work: | ||
465 | __const_udelay: YES | ||
466 | __mutex_lock_slowpath: YES | ||
467 | acpi_os_stall: YES | ||
468 | acpi_ps_parse_aml: YES | ||
469 | intel_opregion_init: NO | ||
470 | intel_opregion_register: NO | ||
471 | intel_opregion_setup: NO | ||
472 | msleep: YES | ||
473 | schedule_timeout: YES | ||
474 | schedule_timeout_uninterruptible: YES | ||
475 | usleep_range: YES | ||
476 | |||
477 | |||
478 | ------------------------------------------------------------------ | ||
479 | | TESTING ON CONSUMER LINUX OPERATING SYSTEMS | | ||
480 | ------------------------------------------------------------------ | ||
481 | |||
482 | Android | ||
483 | _______ | ||
484 | |||
485 | The easiest way to execute on an android device is to run the android.sh | ||
486 | script on the device, then pull the ftrace log back to the host and run | ||
487 | sleepgraph.py on it. | ||
488 | |||
489 | Here are the steps: | ||
490 | |||
491 | [download and install the tool on the device] | ||
492 | |||
493 | host%> wget https://raw.githubusercontent.com/intel/pm-graph/master/tools/android.sh | ||
494 | host%> adb connect 192.168.1.6 | ||
495 | host%> adb root | ||
496 | # push the script to a writeable location | ||
497 | host%> adb push android.sh /sdcard/ | ||
498 | |||
499 | [check whether the tool will run on your device] | ||
500 | |||
501 | host%> adb shell | ||
502 | dev%> cd /sdcard | ||
503 | dev%> sh android.sh status | ||
504 | host : asus_t100 | ||
505 | kernel : 3.14.0-i386-dirty | ||
506 | modes : freeze mem | ||
507 | rtcwake : supported | ||
508 | ftrace : supported | ||
509 | trace events { | ||
510 | suspend_resume: found | ||
511 | device_pm_callback_end: found | ||
512 | device_pm_callback_start: found | ||
513 | } | ||
514 | # the above is what you see on a system that's properly patched | ||
515 | |||
516 | [execute the suspend] | ||
517 | |||
518 | # NOTE: The suspend will only work if the screen isn't timed out, | ||
519 | # so you have to press some keys first to wake it up b4 suspend) | ||
520 | dev%> sh android.sh suspend mem | ||
521 | ------------------------------------ | ||
522 | Suspend/Resume timing test initiated | ||
523 | ------------------------------------ | ||
524 | hostname : asus_t100 | ||
525 | kernel : 3.14.0-i386-dirty | ||
526 | mode : mem | ||
527 | ftrace out : /mnt/shell/emulated/0/ftrace.txt | ||
528 | dmesg out : /mnt/shell/emulated/0/dmesg.txt | ||
529 | log file : /mnt/shell/emulated/0/log.txt | ||
530 | ------------------------------------ | ||
531 | INITIALIZING FTRACE........DONE | ||
532 | STARTING FTRACE | ||
533 | SUSPEND START @ 21:24:02 (rtcwake in 10 seconds) | ||
534 | <adb connection will now terminate> | ||
535 | |||
536 | [retrieve the data from the device] | ||
537 | |||
538 | # I find that you have to actually kill the adb process and | ||
539 | # reconnect sometimes in order for the connection to work post-suspend | ||
540 | host%> adb connect 192.168.1.6 | ||
541 | # (required) get the ftrace data, this is the most important piece | ||
542 | host%> adb pull /sdcard/ftrace.txt | ||
543 | # (optional) get the dmesg data, this is for debugging | ||
544 | host%> adb pull /sdcard/dmesg.txt | ||
545 | # (optional) get the log, which just lists some test times for comparison | ||
546 | host%> adb pull /sdcard/log.txt | ||
547 | |||
548 | [create an output html file using sleepgraph.py] | ||
549 | |||
550 | host%> sleepgraph.py -ftrace ftrace.txt | ||
551 | |||
552 | You should now have an output.html with the android data, enjoy! | ||
diff --git a/tools/power/pm-graph/bootgraph.py b/tools/power/pm-graph/bootgraph.py index 3d899dd8147a..666bcbda648d 100755 --- a/tools/power/pm-graph/bootgraph.py +++ b/tools/power/pm-graph/bootgraph.py | |||
@@ -325,9 +325,9 @@ def parseKernelLog(): | |||
325 | if(not sysvals.stamp['kernel']): | 325 | if(not sysvals.stamp['kernel']): |
326 | sysvals.stamp['kernel'] = sysvals.kernelVersion(msg) | 326 | sysvals.stamp['kernel'] = sysvals.kernelVersion(msg) |
327 | continue | 327 | continue |
328 | m = re.match('.* setting system clock to (?P<t>.*) UTC.*', msg) | 328 | m = re.match('.* setting system clock to (?P<d>[0-9\-]*)[ A-Z](?P<t>[0-9:]*) UTC.*', msg) |
329 | if(m): | 329 | if(m): |
330 | bt = datetime.strptime(m.group('t'), '%Y-%m-%d %H:%M:%S') | 330 | bt = datetime.strptime(m.group('d')+' '+m.group('t'), '%Y-%m-%d %H:%M:%S') |
331 | bt = bt - timedelta(seconds=int(ktime)) | 331 | bt = bt - timedelta(seconds=int(ktime)) |
332 | data.boottime = bt.strftime('%Y-%m-%d_%H:%M:%S') | 332 | data.boottime = bt.strftime('%Y-%m-%d_%H:%M:%S') |
333 | sysvals.stamp['time'] = bt.strftime('%B %d %Y, %I:%M:%S %p') | 333 | sysvals.stamp['time'] = bt.strftime('%B %d %Y, %I:%M:%S %p') |
@@ -348,7 +348,7 @@ def parseKernelLog(): | |||
348 | data.newAction(phase, f, pid, start, ktime, int(r), int(t)) | 348 | data.newAction(phase, f, pid, start, ktime, int(r), int(t)) |
349 | del devtemp[f] | 349 | del devtemp[f] |
350 | continue | 350 | continue |
351 | if(re.match('^Freeing unused kernel memory.*', msg)): | 351 | if(re.match('^Freeing unused kernel .*', msg)): |
352 | data.tUserMode = ktime | 352 | data.tUserMode = ktime |
353 | data.dmesg['kernel']['end'] = ktime | 353 | data.dmesg['kernel']['end'] = ktime |
354 | data.dmesg['user']['start'] = ktime | 354 | data.dmesg['user']['start'] = ktime |
@@ -1008,7 +1008,7 @@ if __name__ == '__main__': | |||
1008 | updateKernelParams() | 1008 | updateKernelParams() |
1009 | elif cmd == 'flistall': | 1009 | elif cmd == 'flistall': |
1010 | for f in sysvals.getBootFtraceFilterFunctions(): | 1010 | for f in sysvals.getBootFtraceFilterFunctions(): |
1011 | print f | 1011 | print(f) |
1012 | elif cmd == 'checkbl': | 1012 | elif cmd == 'checkbl': |
1013 | sysvals.getBootLoader() | 1013 | sysvals.getBootLoader() |
1014 | pprint('Boot Loader: %s\n%s' % (sysvals.bootloader, sysvals.blexec)) | 1014 | pprint('Boot Loader: %s\n%s' % (sysvals.bootloader, sysvals.blexec)) |
diff --git a/tools/power/pm-graph/config/example.cfg b/tools/power/pm-graph/config/example.cfg index 05b2efb9bb54..1ef3eb9383fa 100644 --- a/tools/power/pm-graph/config/example.cfg +++ b/tools/power/pm-graph/config/example.cfg | |||
@@ -98,12 +98,34 @@ postdelay: 0 | |||
98 | # graph only devices longer than min in the timeline (default: 0.001 ms) | 98 | # graph only devices longer than min in the timeline (default: 0.001 ms) |
99 | mindev: 0.001 | 99 | mindev: 0.001 |
100 | 100 | ||
101 | # Call Loop Max Gap (dev mode only) | ||
102 | # merge loops of the same call if each is less than maxgap apart (def: 100us) | ||
103 | callloop-maxgap: 0.0001 | ||
104 | |||
105 | # Call Loop Max Length (dev mode only) | ||
106 | # merge loops of the same call if each is less than maxlen in length (def: 5ms) | ||
107 | callloop-maxlen: 0.005 | ||
108 | |||
109 | # Override default timeline entries: | ||
110 | # Do not use the internal default functions for timeline entries (def: false) | ||
111 | # Set this to true if you intend to only use the ones defined in the config | ||
112 | override-timeline-functions: true | ||
113 | |||
114 | # Override default dev timeline entries: | ||
115 | # Do not use the internal default functions for dev timeline entries (def: false) | ||
116 | # Set this to true if you intend to only use the ones defined in the config | ||
117 | override-dev-timeline-functions: true | ||
118 | |||
101 | # ---- Debug Options ---- | 119 | # ---- Debug Options ---- |
102 | 120 | ||
103 | # Callgraph | 121 | # Callgraph |
104 | # gather detailed ftrace callgraph data on all timeline events (default: false) | 122 | # gather detailed ftrace callgraph data on all timeline events (default: false) |
105 | callgraph: false | 123 | callgraph: false |
106 | 124 | ||
125 | # Max graph depth | ||
126 | # limit the callgraph trace to this depth (default: 0 = all) | ||
127 | maxdepth: 2 | ||
128 | |||
107 | # Callgraph phase filter | 129 | # Callgraph phase filter |
108 | # Only enable callgraphs for one phase, i.e. resume_noirq (default: all) | 130 | # Only enable callgraphs for one phase, i.e. resume_noirq (default: all) |
109 | cgphase: suspend | 131 | cgphase: suspend |
@@ -131,3 +153,7 @@ timeprec: 6 | |||
131 | # Add kprobe functions to the timeline | 153 | # Add kprobe functions to the timeline |
132 | # Add functions to the timeline from a text file (default: no-action) | 154 | # Add functions to the timeline from a text file (default: no-action) |
133 | # fadd: file.txt | 155 | # fadd: file.txt |
156 | |||
157 | # Ftrace buffer size | ||
158 | # Set trace buffer size to N kilo-bytes (default: all of free memory up to 3GB) | ||
159 | # bufsize: 1000 | ||
diff --git a/tools/power/pm-graph/sleepgraph.8 b/tools/power/pm-graph/sleepgraph.8 index 24a2e7d0ae63..9648be644d5f 100644 --- a/tools/power/pm-graph/sleepgraph.8 +++ b/tools/power/pm-graph/sleepgraph.8 | |||
@@ -53,6 +53,11 @@ disable rtcwake and require a user keypress to resume. | |||
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 | 55 | .TP |
56 | \fB-turbostat\fR | ||
57 | Use turbostat to execute the command in freeze mode (default: disabled). This | ||
58 | will provide turbostat output in the log which will tell you which actual | ||
59 | power modes were entered. | ||
60 | .TP | ||
56 | \fB-result \fIfile\fR | 61 | \fB-result \fIfile\fR |
57 | Export a results table to a text file for parsing. | 62 | Export a results table to a text file for parsing. |
58 | .TP | 63 | .TP |
@@ -121,6 +126,10 @@ be created in a new subdirectory with a summary page: suspend-xN-{date}-{time}. | |||
121 | Use ftrace to create device callgraphs (default: disabled). This can produce | 126 | Use ftrace to create device callgraphs (default: disabled). This can produce |
122 | very large outputs, i.e. 10MB - 100MB. | 127 | very large outputs, i.e. 10MB - 100MB. |
123 | .TP | 128 | .TP |
129 | \fB-ftop\fR | ||
130 | Use ftrace on the top level call: "suspend_devices_and_enter" only (default: disabled). | ||
131 | This option implies -f and creates a single callgraph covering all of suspend/resume. | ||
132 | .TP | ||
124 | \fB-maxdepth \fIlevel\fR | 133 | \fB-maxdepth \fIlevel\fR |
125 | limit the callgraph trace depth to \fIlevel\fR (default: 0=all). This is | 134 | limit the callgraph trace depth to \fIlevel\fR (default: 0=all). This is |
126 | the best way to limit the output size when using callgraphs via -f. | 135 | the best way to limit the output size when using callgraphs via -f. |
@@ -138,8 +147,8 @@ which are barely visible in the timeline. | |||
138 | The value is a float: e.g. 0.001 represents 1 us. | 147 | The value is a float: e.g. 0.001 represents 1 us. |
139 | .TP | 148 | .TP |
140 | \fB-cgfilter \fI"func1,func2,..."\fR | 149 | \fB-cgfilter \fI"func1,func2,..."\fR |
141 | Reduce callgraph output in the timeline by limiting it to a list of calls. The | 150 | Reduce callgraph output in the timeline by limiting it certain devices. The |
142 | argument can be a single function name or a comma delimited list. | 151 | argument can be a single device name or a comma delimited list. |
143 | (default: none) | 152 | (default: none) |
144 | .TP | 153 | .TP |
145 | \fB-cgskip \fIfile\fR | 154 | \fB-cgskip \fIfile\fR |
@@ -183,6 +192,9 @@ Print out the contents of the ACPI Firmware Performance Data Table. | |||
183 | \fB-battery\fR | 192 | \fB-battery\fR |
184 | Print out battery status and current charge. | 193 | Print out battery status and current charge. |
185 | .TP | 194 | .TP |
195 | \fB-wifi\fR | ||
196 | Print out wifi status and connection details. | ||
197 | .TP | ||
186 | \fB-xon/-xoff/-xstandby/-xsuspend\fR | 198 | \fB-xon/-xoff/-xstandby/-xsuspend\fR |
187 | Test xset by attempting to switch the display to the given mode. This | 199 | Test xset by attempting to switch the display to the given mode. This |
188 | is the same command which will be issued by \fB-display \fImode\fR. | 200 | is the same command which will be issued by \fB-display \fImode\fR. |
diff --git a/tools/power/pm-graph/sleepgraph.py b/tools/power/pm-graph/sleepgraph.py index d1a88d05e976..4f46a7a1feb6 100755 --- a/tools/power/pm-graph/sleepgraph.py +++ b/tools/power/pm-graph/sleepgraph.py | |||
@@ -9,9 +9,9 @@ | |||
9 | # | 9 | # |
10 | # Links: | 10 | # Links: |
11 | # Home Page | 11 | # Home Page |
12 | # https://01.org/suspendresume | 12 | # https://01.org/pm-graph |
13 | # Source repo | 13 | # Source repo |
14 | # git@github.com:01org/pm-graph | 14 | # git@github.com:intel/pm-graph |
15 | # | 15 | # |
16 | # Description: | 16 | # Description: |
17 | # This tool is designed to assist kernel and OS developers in optimizing | 17 | # This tool is designed to assist kernel and OS developers in optimizing |
@@ -24,6 +24,7 @@ | |||
24 | # viewed in firefox or chrome. | 24 | # viewed in firefox or chrome. |
25 | # | 25 | # |
26 | # The following kernel build options are required: | 26 | # The following kernel build options are required: |
27 | # CONFIG_DEVMEM=y | ||
27 | # CONFIG_PM_DEBUG=y | 28 | # CONFIG_PM_DEBUG=y |
28 | # CONFIG_PM_SLEEP_DEBUG=y | 29 | # CONFIG_PM_SLEEP_DEBUG=y |
29 | # CONFIG_FTRACE=y | 30 | # CONFIG_FTRACE=y |
@@ -53,6 +54,7 @@ import ConfigParser | |||
53 | import gzip | 54 | import gzip |
54 | from threading import Thread | 55 | from threading import Thread |
55 | from subprocess import call, Popen, PIPE | 56 | from subprocess import call, Popen, PIPE |
57 | import base64 | ||
56 | 58 | ||
57 | def pprint(msg): | 59 | def pprint(msg): |
58 | print(msg) | 60 | print(msg) |
@@ -66,7 +68,7 @@ def pprint(msg): | |||
66 | # store system values and test parameters | 68 | # store system values and test parameters |
67 | class SystemValues: | 69 | class SystemValues: |
68 | title = 'SleepGraph' | 70 | title = 'SleepGraph' |
69 | version = '5.2' | 71 | version = '5.4' |
70 | ansi = False | 72 | ansi = False |
71 | rs = 0 | 73 | rs = 0 |
72 | display = '' | 74 | display = '' |
@@ -74,8 +76,9 @@ class SystemValues: | |||
74 | sync = False | 76 | sync = False |
75 | verbose = False | 77 | verbose = False |
76 | testlog = True | 78 | testlog = True |
77 | dmesglog = False | 79 | dmesglog = True |
78 | ftracelog = False | 80 | ftracelog = False |
81 | tstat = False | ||
79 | mindevlen = 0.0 | 82 | mindevlen = 0.0 |
80 | mincglen = 0.0 | 83 | mincglen = 0.0 |
81 | cgphase = '' | 84 | cgphase = '' |
@@ -99,6 +102,8 @@ class SystemValues: | |||
99 | pmdpath = '/sys/power/pm_debug_messages' | 102 | pmdpath = '/sys/power/pm_debug_messages' |
100 | traceevents = [ | 103 | traceevents = [ |
101 | 'suspend_resume', | 104 | 'suspend_resume', |
105 | 'wakeup_source_activate', | ||
106 | 'wakeup_source_deactivate', | ||
102 | 'device_pm_callback_end', | 107 | 'device_pm_callback_end', |
103 | 'device_pm_callback_start' | 108 | 'device_pm_callback_start' |
104 | ] | 109 | ] |
@@ -130,6 +135,8 @@ class SystemValues: | |||
130 | x2delay = 0 | 135 | x2delay = 0 |
131 | skiphtml = False | 136 | skiphtml = False |
132 | usecallgraph = False | 137 | usecallgraph = False |
138 | ftopfunc = 'suspend_devices_and_enter' | ||
139 | ftop = False | ||
133 | usetraceevents = False | 140 | usetraceevents = False |
134 | usetracemarkers = True | 141 | usetracemarkers = True |
135 | usekprobes = True | 142 | usekprobes = True |
@@ -158,6 +165,13 @@ class SystemValues: | |||
158 | 'acpi_hibernation_leave': {}, | 165 | 'acpi_hibernation_leave': {}, |
159 | 'acpi_pm_freeze': {}, | 166 | 'acpi_pm_freeze': {}, |
160 | 'acpi_pm_thaw': {}, | 167 | 'acpi_pm_thaw': {}, |
168 | 'acpi_s2idle_end': {}, | ||
169 | 'acpi_s2idle_sync': {}, | ||
170 | 'acpi_s2idle_begin': {}, | ||
171 | 'acpi_s2idle_prepare': {}, | ||
172 | 'acpi_s2idle_wake': {}, | ||
173 | 'acpi_s2idle_wakeup': {}, | ||
174 | 'acpi_s2idle_restore': {}, | ||
161 | 'hibernate_preallocate_memory': {}, | 175 | 'hibernate_preallocate_memory': {}, |
162 | 'create_basic_memory_bitmaps': {}, | 176 | 'create_basic_memory_bitmaps': {}, |
163 | 'swsusp_write': {}, | 177 | 'swsusp_write': {}, |
@@ -191,9 +205,14 @@ class SystemValues: | |||
191 | 'usleep_range': { 'args_x86_64': {'min':'%di:s32', 'max':'%si:s32'}, 'ub': 1 }, | 205 | 'usleep_range': { 'args_x86_64': {'min':'%di:s32', 'max':'%si:s32'}, 'ub': 1 }, |
192 | 'mutex_lock_slowpath': { 'func':'__mutex_lock_slowpath', 'ub': 1 }, | 206 | 'mutex_lock_slowpath': { 'func':'__mutex_lock_slowpath', 'ub': 1 }, |
193 | 'acpi_os_stall': {'ub': 1}, | 207 | 'acpi_os_stall': {'ub': 1}, |
208 | 'rt_mutex_slowlock': {'ub': 1}, | ||
194 | # ACPI | 209 | # ACPI |
195 | 'acpi_resume_power_resources': {}, | 210 | 'acpi_resume_power_resources': {}, |
196 | 'acpi_ps_parse_aml': {}, | 211 | 'acpi_ps_execute_method': { 'args_x86_64': { |
212 | 'fullpath':'+0(+40(%di)):string', | ||
213 | }}, | ||
214 | # mei_me | ||
215 | 'mei_reset': {}, | ||
197 | # filesystem | 216 | # filesystem |
198 | 'ext4_sync_fs': {}, | 217 | 'ext4_sync_fs': {}, |
199 | # 80211 | 218 | # 80211 |
@@ -242,6 +261,7 @@ class SystemValues: | |||
242 | timeformat = '%.3f' | 261 | timeformat = '%.3f' |
243 | cmdline = '%s %s' % \ | 262 | cmdline = '%s %s' % \ |
244 | (os.path.basename(sys.argv[0]), ' '.join(sys.argv[1:])) | 263 | (os.path.basename(sys.argv[0]), ' '.join(sys.argv[1:])) |
264 | kparams = '' | ||
245 | sudouser = '' | 265 | sudouser = '' |
246 | def __init__(self): | 266 | def __init__(self): |
247 | self.archargs = 'args_'+platform.machine() | 267 | self.archargs = 'args_'+platform.machine() |
@@ -320,6 +340,7 @@ class SystemValues: | |||
320 | args['date'] = n.strftime('%y%m%d') | 340 | args['date'] = n.strftime('%y%m%d') |
321 | args['time'] = n.strftime('%H%M%S') | 341 | args['time'] = n.strftime('%H%M%S') |
322 | args['hostname'] = args['host'] = self.hostname | 342 | args['hostname'] = args['host'] = self.hostname |
343 | args['mode'] = self.suspendmode | ||
323 | return value.format(**args) | 344 | return value.format(**args) |
324 | def setOutputFile(self): | 345 | def setOutputFile(self): |
325 | if self.dmesgfile != '': | 346 | if self.dmesgfile != '': |
@@ -331,21 +352,28 @@ class SystemValues: | |||
331 | if(m): | 352 | if(m): |
332 | self.htmlfile = m.group('name')+'.html' | 353 | self.htmlfile = m.group('name')+'.html' |
333 | def systemInfo(self, info): | 354 | def systemInfo(self, info): |
334 | p = c = m = b = '' | 355 | p = m = '' |
335 | if 'baseboard-manufacturer' in info: | 356 | if 'baseboard-manufacturer' in info: |
336 | m = info['baseboard-manufacturer'] | 357 | m = info['baseboard-manufacturer'] |
337 | elif 'system-manufacturer' in info: | 358 | elif 'system-manufacturer' in info: |
338 | m = info['system-manufacturer'] | 359 | m = info['system-manufacturer'] |
339 | if 'baseboard-product-name' in info: | 360 | if 'system-product-name' in info: |
340 | p = info['baseboard-product-name'] | ||
341 | elif 'system-product-name' in info: | ||
342 | p = info['system-product-name'] | 361 | p = info['system-product-name'] |
343 | if 'processor-version' in info: | 362 | elif 'baseboard-product-name' in info: |
344 | c = info['processor-version'] | 363 | p = info['baseboard-product-name'] |
345 | if 'bios-version' in info: | 364 | if m[:5].lower() == 'intel' and 'baseboard-product-name' in info: |
346 | b = info['bios-version'] | 365 | p = info['baseboard-product-name'] |
347 | self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | numcpu:%d | memsz:%d | memfr:%d' % \ | 366 | c = info['processor-version'] if 'processor-version' in info else '' |
348 | (m, p, c, b, self.cpucount, self.memtotal, self.memfree) | 367 | b = info['bios-version'] if 'bios-version' in info else '' |
368 | r = info['bios-release-date'] if 'bios-release-date' in info else '' | ||
369 | self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | biosdate:%s | numcpu:%d | memsz:%d | memfr:%d' % \ | ||
370 | (m, p, c, b, r, self.cpucount, self.memtotal, self.memfree) | ||
371 | try: | ||
372 | kcmd = open('/proc/cmdline', 'r').read().strip() | ||
373 | except: | ||
374 | kcmd = '' | ||
375 | if kcmd: | ||
376 | self.sysstamp += '\n# kparams | %s' % kcmd | ||
349 | def printSystemInfo(self, fatal=False): | 377 | def printSystemInfo(self, fatal=False): |
350 | self.rootCheck(True) | 378 | self.rootCheck(True) |
351 | out = dmidecode(self.mempath, fatal) | 379 | out = dmidecode(self.mempath, fatal) |
@@ -353,10 +381,10 @@ class SystemValues: | |||
353 | return | 381 | return |
354 | fmt = '%-24s: %s' | 382 | fmt = '%-24s: %s' |
355 | for name in sorted(out): | 383 | for name in sorted(out): |
356 | print fmt % (name, out[name]) | 384 | print(fmt % (name, out[name])) |
357 | print fmt % ('cpucount', ('%d' % self.cpucount)) | 385 | print(fmt % ('cpucount', ('%d' % self.cpucount))) |
358 | print fmt % ('memtotal', ('%d kB' % self.memtotal)) | 386 | print(fmt % ('memtotal', ('%d kB' % self.memtotal))) |
359 | print fmt % ('memfree', ('%d kB' % self.memfree)) | 387 | print(fmt % ('memfree', ('%d kB' % self.memfree))) |
360 | def cpuInfo(self): | 388 | def cpuInfo(self): |
361 | self.cpucount = 0 | 389 | self.cpucount = 0 |
362 | fp = open('/proc/cpuinfo', 'r') | 390 | fp = open('/proc/cpuinfo', 'r') |
@@ -376,7 +404,7 @@ class SystemValues: | |||
376 | def initTestOutput(self, name): | 404 | def initTestOutput(self, name): |
377 | self.prefix = self.hostname | 405 | self.prefix = self.hostname |
378 | v = open('/proc/version', 'r').read().strip() | 406 | v = open('/proc/version', 'r').read().strip() |
379 | kver = string.split(v)[2] | 407 | kver = v.split()[2] |
380 | fmt = name+'-%m%d%y-%H%M%S' | 408 | fmt = name+'-%m%d%y-%H%M%S' |
381 | testtime = datetime.now().strftime(fmt) | 409 | testtime = datetime.now().strftime(fmt) |
382 | self.teststamp = \ | 410 | self.teststamp = \ |
@@ -391,7 +419,7 @@ class SystemValues: | |||
391 | self.htmlfile = \ | 419 | self.htmlfile = \ |
392 | self.testdir+'/'+self.prefix+'_'+self.suspendmode+'.html' | 420 | self.testdir+'/'+self.prefix+'_'+self.suspendmode+'.html' |
393 | if not os.path.isdir(self.testdir): | 421 | if not os.path.isdir(self.testdir): |
394 | os.mkdir(self.testdir) | 422 | os.makedirs(self.testdir) |
395 | def getValueList(self, value): | 423 | def getValueList(self, value): |
396 | out = [] | 424 | out = [] |
397 | for i in value.split(','): | 425 | for i in value.split(','): |
@@ -402,6 +430,12 @@ class SystemValues: | |||
402 | self.devicefilter = self.getValueList(value) | 430 | self.devicefilter = self.getValueList(value) |
403 | def setCallgraphFilter(self, value): | 431 | def setCallgraphFilter(self, value): |
404 | self.cgfilter = self.getValueList(value) | 432 | self.cgfilter = self.getValueList(value) |
433 | def skipKprobes(self, value): | ||
434 | for k in self.getValueList(value): | ||
435 | if k in self.tracefuncs: | ||
436 | del self.tracefuncs[k] | ||
437 | if k in self.dev_tracefuncs: | ||
438 | del self.dev_tracefuncs[k] | ||
405 | def setCallgraphBlacklist(self, file): | 439 | def setCallgraphBlacklist(self, file): |
406 | self.cgblacklist = self.listFromFile(file) | 440 | self.cgblacklist = self.listFromFile(file) |
407 | def rtcWakeAlarmOn(self): | 441 | def rtcWakeAlarmOn(self): |
@@ -471,9 +505,9 @@ class SystemValues: | |||
471 | if 'func' in self.tracefuncs[i]: | 505 | if 'func' in self.tracefuncs[i]: |
472 | i = self.tracefuncs[i]['func'] | 506 | i = self.tracefuncs[i]['func'] |
473 | if i in master: | 507 | if i in master: |
474 | print i | 508 | print(i) |
475 | else: | 509 | else: |
476 | print self.colorText(i) | 510 | print(self.colorText(i)) |
477 | def setFtraceFilterFunctions(self, list): | 511 | def setFtraceFilterFunctions(self, list): |
478 | master = self.listFromFile(self.tpath+'available_filter_functions') | 512 | master = self.listFromFile(self.tpath+'available_filter_functions') |
479 | flist = '' | 513 | flist = '' |
@@ -680,7 +714,8 @@ class SystemValues: | |||
680 | if self.bufsize > 0: | 714 | if self.bufsize > 0: |
681 | tgtsize = self.bufsize | 715 | tgtsize = self.bufsize |
682 | elif self.usecallgraph or self.usedevsrc: | 716 | elif self.usecallgraph or self.usedevsrc: |
683 | bmax = (1*1024*1024) if self.suspendmode == 'disk' else (3*1024*1024) | 717 | bmax = (1*1024*1024) if self.suspendmode in ['disk', 'command'] \ |
718 | else (3*1024*1024) | ||
684 | tgtsize = min(self.memfree, bmax) | 719 | tgtsize = min(self.memfree, bmax) |
685 | else: | 720 | else: |
686 | tgtsize = 65536 | 721 | tgtsize = 65536 |
@@ -715,7 +750,10 @@ class SystemValues: | |||
715 | cf.append(self.tracefuncs[fn]['func']) | 750 | cf.append(self.tracefuncs[fn]['func']) |
716 | else: | 751 | else: |
717 | cf.append(fn) | 752 | cf.append(fn) |
718 | self.setFtraceFilterFunctions(cf) | 753 | if self.ftop: |
754 | self.setFtraceFilterFunctions([self.ftopfunc]) | ||
755 | else: | ||
756 | self.setFtraceFilterFunctions(cf) | ||
719 | # initialize the kprobe trace | 757 | # initialize the kprobe trace |
720 | elif self.usekprobes: | 758 | elif self.usekprobes: |
721 | for name in self.tracefuncs: | 759 | for name in self.tracefuncs: |
@@ -768,9 +806,21 @@ class SystemValues: | |||
768 | fw = test['fw'] | 806 | fw = test['fw'] |
769 | if(fw): | 807 | if(fw): |
770 | fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1])) | 808 | fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1])) |
809 | if 'mcelog' in test: | ||
810 | fp.write('# mcelog %s\n' % test['mcelog']) | ||
811 | if 'turbo' in test: | ||
812 | fp.write('# turbostat %s\n' % test['turbo']) | ||
771 | if 'bat' in test: | 813 | if 'bat' in test: |
772 | (a1, c1), (a2, c2) = test['bat'] | 814 | (a1, c1), (a2, c2) = test['bat'] |
773 | fp.write('# battery %s %d %s %d\n' % (a1, c1, a2, c2)) | 815 | fp.write('# battery %s %d %s %d\n' % (a1, c1, a2, c2)) |
816 | if 'wifi' in test: | ||
817 | wstr = [] | ||
818 | for wifi in test['wifi']: | ||
819 | tmp = [] | ||
820 | for key in sorted(wifi): | ||
821 | tmp.append('%s:%s' % (key, wifi[key])) | ||
822 | wstr.append('|'.join(tmp)) | ||
823 | fp.write('# wifi %s\n' % (','.join(wstr))) | ||
774 | if test['error'] or len(testdata) > 1: | 824 | if test['error'] or len(testdata) > 1: |
775 | fp.write('# enter_sleep_error %s\n' % test['error']) | 825 | fp.write('# enter_sleep_error %s\n' % test['error']) |
776 | return fp | 826 | return fp |
@@ -821,6 +871,106 @@ class SystemValues: | |||
821 | if isgz: | 871 | if isgz: |
822 | return gzip.open(filename, mode+'b') | 872 | return gzip.open(filename, mode+'b') |
823 | return open(filename, mode) | 873 | return open(filename, mode) |
874 | def mcelog(self, clear=False): | ||
875 | cmd = self.getExec('mcelog') | ||
876 | if not cmd: | ||
877 | return '' | ||
878 | if clear: | ||
879 | call(cmd+' > /dev/null 2>&1', shell=True) | ||
880 | return '' | ||
881 | fp = Popen([cmd], stdout=PIPE, stderr=PIPE).stdout | ||
882 | out = fp.read().strip() | ||
883 | fp.close() | ||
884 | if not out: | ||
885 | return '' | ||
886 | return base64.b64encode(out.encode('zlib')) | ||
887 | def haveTurbostat(self): | ||
888 | if not self.tstat: | ||
889 | return False | ||
890 | cmd = self.getExec('turbostat') | ||
891 | if not cmd: | ||
892 | return False | ||
893 | fp = Popen([cmd, '-v'], stdout=PIPE, stderr=PIPE).stderr | ||
894 | out = fp.read().strip() | ||
895 | fp.close() | ||
896 | return re.match('turbostat version [0-9\.]* .*', out) | ||
897 | def turbostat(self): | ||
898 | cmd = self.getExec('turbostat') | ||
899 | if not cmd: | ||
900 | return 'missing turbostat executable' | ||
901 | text = [] | ||
902 | fullcmd = '%s -q -S echo freeze > %s' % (cmd, self.powerfile) | ||
903 | fp = Popen(['sh', '-c', fullcmd], stdout=PIPE, stderr=PIPE).stderr | ||
904 | for line in fp: | ||
905 | if re.match('[0-9.]* sec', line): | ||
906 | continue | ||
907 | text.append(line.split()) | ||
908 | fp.close() | ||
909 | if len(text) < 2: | ||
910 | return 'turbostat output format error' | ||
911 | out = [] | ||
912 | for key in text[0]: | ||
913 | values = [] | ||
914 | idx = text[0].index(key) | ||
915 | for line in text[1:]: | ||
916 | if len(line) > idx: | ||
917 | values.append(line[idx]) | ||
918 | out.append('%s=%s' % (key, ','.join(values))) | ||
919 | return '|'.join(out) | ||
920 | def checkWifi(self): | ||
921 | out = dict() | ||
922 | iwcmd, ifcmd = self.getExec('iwconfig'), self.getExec('ifconfig') | ||
923 | if not iwcmd or not ifcmd: | ||
924 | return out | ||
925 | fp = Popen(iwcmd, stdout=PIPE, stderr=PIPE).stdout | ||
926 | for line in fp: | ||
927 | m = re.match('(?P<dev>\S*) .* ESSID:(?P<ess>\S*)', line) | ||
928 | if not m: | ||
929 | continue | ||
930 | out['device'] = m.group('dev') | ||
931 | if '"' in m.group('ess'): | ||
932 | out['essid'] = m.group('ess').strip('"') | ||
933 | break | ||
934 | fp.close() | ||
935 | if 'device' in out: | ||
936 | fp = Popen([ifcmd, out['device']], stdout=PIPE, stderr=PIPE).stdout | ||
937 | for line in fp: | ||
938 | m = re.match('.* inet (?P<ip>[0-9\.]*)', line) | ||
939 | if m: | ||
940 | out['ip'] = m.group('ip') | ||
941 | break | ||
942 | fp.close() | ||
943 | return out | ||
944 | def errorSummary(self, errinfo, msg): | ||
945 | found = False | ||
946 | for entry in errinfo: | ||
947 | if re.match(entry['match'], msg): | ||
948 | entry['count'] += 1 | ||
949 | if self.hostname not in entry['urls']: | ||
950 | entry['urls'][self.hostname] = [self.htmlfile] | ||
951 | elif self.htmlfile not in entry['urls'][self.hostname]: | ||
952 | entry['urls'][self.hostname].append(self.htmlfile) | ||
953 | found = True | ||
954 | break | ||
955 | if found: | ||
956 | return | ||
957 | arr = msg.split() | ||
958 | for j in range(len(arr)): | ||
959 | if re.match('^[0-9,\-\.]*$', arr[j]): | ||
960 | arr[j] = '[0-9,\-\.]*' | ||
961 | else: | ||
962 | arr[j] = arr[j]\ | ||
963 | .replace('\\', '\\\\').replace(']', '\]').replace('[', '\[')\ | ||
964 | .replace('.', '\.').replace('+', '\+').replace('*', '\*')\ | ||
965 | .replace('(', '\(').replace(')', '\)') | ||
966 | mstr = ' '.join(arr) | ||
967 | entry = { | ||
968 | 'line': msg, | ||
969 | 'match': mstr, | ||
970 | 'count': 1, | ||
971 | 'urls': {self.hostname: [self.htmlfile]} | ||
972 | } | ||
973 | errinfo.append(entry) | ||
824 | 974 | ||
825 | sysvals = SystemValues() | 975 | sysvals = SystemValues() |
826 | switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0'] | 976 | switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0'] |
@@ -915,7 +1065,14 @@ class Data: | |||
915 | 'ERROR' : '.*ERROR.*', | 1065 | 'ERROR' : '.*ERROR.*', |
916 | 'WARNING' : '.*WARNING.*', | 1066 | 'WARNING' : '.*WARNING.*', |
917 | 'IRQ' : '.*genirq: .*', | 1067 | 'IRQ' : '.*genirq: .*', |
918 | 'TASKFAIL': '.*Freezing of tasks failed.*', | 1068 | 'TASKFAIL': '.*Freezing of tasks *.*', |
1069 | 'ACPI' : '.*ACPI *(?P<b>[A-Za-z]*) *Error[: ].*', | ||
1070 | 'DEVFAIL' : '.* failed to (?P<b>[a-z]*) async: .*', | ||
1071 | 'DISKFULL': '.*No space left on device.*', | ||
1072 | 'USBERR' : '.*usb .*device .*, error [0-9-]*', | ||
1073 | 'ATAERR' : ' *ata[0-9\.]*: .*failed.*', | ||
1074 | 'MEIERR' : ' *mei.*: .*failed.*', | ||
1075 | 'TPMERR' : '(?i) *tpm *tpm[0-9]*: .*error.*', | ||
919 | } | 1076 | } |
920 | def __init__(self, num): | 1077 | def __init__(self, num): |
921 | idchar = 'abcdefghij' | 1078 | idchar = 'abcdefghij' |
@@ -933,6 +1090,9 @@ class Data: | |||
933 | self.outfile = '' | 1090 | self.outfile = '' |
934 | self.kerror = False | 1091 | self.kerror = False |
935 | self.battery = 0 | 1092 | self.battery = 0 |
1093 | self.wifi = 0 | ||
1094 | self.turbostat = 0 | ||
1095 | self.mcelog = 0 | ||
936 | self.enterfail = '' | 1096 | self.enterfail = '' |
937 | self.currphase = '' | 1097 | self.currphase = '' |
938 | self.pstl = dict() # process timeline | 1098 | self.pstl = dict() # process timeline |
@@ -967,8 +1127,24 @@ class Data: | |||
967 | if len(plist) < 1: | 1127 | if len(plist) < 1: |
968 | return '' | 1128 | return '' |
969 | return plist[-1] | 1129 | return plist[-1] |
1130 | def turbostatInfo(self): | ||
1131 | tp = TestProps() | ||
1132 | out = {'syslpi':'N/A','pkgpc10':'N/A'} | ||
1133 | for line in self.dmesgtext: | ||
1134 | m = re.match(tp.tstatfmt, line) | ||
1135 | if not m: | ||
1136 | continue | ||
1137 | for i in m.group('t').split('|'): | ||
1138 | if 'SYS%LPI' in i: | ||
1139 | out['syslpi'] = i.split('=')[-1]+'%' | ||
1140 | elif 'pc10' in i: | ||
1141 | out['pkgpc10'] = i.split('=')[-1]+'%' | ||
1142 | break | ||
1143 | return out | ||
970 | def extractErrorInfo(self): | 1144 | def extractErrorInfo(self): |
971 | lf = sysvals.openlog(sysvals.dmesgfile, 'r') | 1145 | lf = self.dmesgtext |
1146 | if len(self.dmesgtext) < 1 and sysvals.dmesgfile: | ||
1147 | lf = sysvals.openlog(sysvals.dmesgfile, 'r') | ||
972 | i = 0 | 1148 | i = 0 |
973 | list = [] | 1149 | list = [] |
974 | for line in lf: | 1150 | for line in lf: |
@@ -983,16 +1159,19 @@ class Data: | |||
983 | msg = m.group('msg') | 1159 | msg = m.group('msg') |
984 | for err in self.errlist: | 1160 | for err in self.errlist: |
985 | if re.match(self.errlist[err], msg): | 1161 | if re.match(self.errlist[err], msg): |
986 | list.append((err, dir, t, i, i)) | 1162 | list.append((msg, err, dir, t, i, i)) |
987 | self.kerror = True | 1163 | self.kerror = True |
988 | break | 1164 | break |
989 | for e in list: | 1165 | msglist = [] |
990 | type, dir, t, idx1, idx2 = e | 1166 | for msg, type, dir, t, idx1, idx2 in list: |
1167 | msglist.append(msg) | ||
991 | sysvals.vprint('kernel %s found in %s at %f' % (type, dir, t)) | 1168 | sysvals.vprint('kernel %s found in %s at %f' % (type, dir, t)) |
992 | self.errorinfo[dir].append((type, t, idx1, idx2)) | 1169 | self.errorinfo[dir].append((type, t, idx1, idx2)) |
993 | if self.kerror: | 1170 | if self.kerror: |
994 | sysvals.dmesglog = True | 1171 | sysvals.dmesglog = True |
995 | lf.close() | 1172 | if len(self.dmesgtext) < 1 and sysvals.dmesgfile: |
1173 | lf.close() | ||
1174 | return msglist | ||
996 | def setStart(self, time): | 1175 | def setStart(self, time): |
997 | self.start = time | 1176 | self.start = time |
998 | def setEnd(self, time): | 1177 | def setEnd(self, time): |
@@ -2045,7 +2224,7 @@ class FTraceCallGraph: | |||
2045 | if(data.dmesg[p]['start'] <= self.start and | 2224 | if(data.dmesg[p]['start'] <= self.start and |
2046 | self.start <= data.dmesg[p]['end']): | 2225 | self.start <= data.dmesg[p]['end']): |
2047 | list = data.dmesg[p]['list'] | 2226 | list = data.dmesg[p]['list'] |
2048 | for devname in list: | 2227 | for devname in sorted(list, key=lambda k:list[k]['start']): |
2049 | dev = list[devname] | 2228 | dev = list[devname] |
2050 | if(pid == dev['pid'] and | 2229 | if(pid == dev['pid'] and |
2051 | self.start <= dev['start'] and | 2230 | self.start <= dev['start'] and |
@@ -2350,6 +2529,9 @@ class TestProps: | |||
2350 | '(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\ | 2529 | '(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\ |
2351 | ' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$' | 2530 | ' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$' |
2352 | batteryfmt = '^# battery (?P<a1>\w*) (?P<c1>\d*) (?P<a2>\w*) (?P<c2>\d*)' | 2531 | batteryfmt = '^# battery (?P<a1>\w*) (?P<c1>\d*) (?P<a2>\w*) (?P<c2>\d*)' |
2532 | wififmt = '^# wifi (?P<w>.*)' | ||
2533 | tstatfmt = '^# turbostat (?P<t>\S*)' | ||
2534 | mcelogfmt = '^# mcelog (?P<m>\S*)' | ||
2353 | testerrfmt = '^# enter_sleep_error (?P<e>.*)' | 2535 | testerrfmt = '^# enter_sleep_error (?P<e>.*)' |
2354 | sysinfofmt = '^# sysinfo .*' | 2536 | sysinfofmt = '^# sysinfo .*' |
2355 | cmdlinefmt = '^# command \| (?P<cmd>.*)' | 2537 | cmdlinefmt = '^# command \| (?P<cmd>.*)' |
@@ -2372,7 +2554,10 @@ class TestProps: | |||
2372 | self.cmdline = '' | 2554 | self.cmdline = '' |
2373 | self.kparams = '' | 2555 | self.kparams = '' |
2374 | self.testerror = [] | 2556 | self.testerror = [] |
2557 | self.mcelog = [] | ||
2558 | self.turbostat = [] | ||
2375 | self.battery = [] | 2559 | self.battery = [] |
2560 | self.wifi = [] | ||
2376 | self.fwdata = [] | 2561 | self.fwdata = [] |
2377 | self.ftrace_line_fmt = self.ftrace_line_fmt_nop | 2562 | self.ftrace_line_fmt = self.ftrace_line_fmt_nop |
2378 | self.cgformat = False | 2563 | self.cgformat = False |
@@ -2386,6 +2571,44 @@ class TestProps: | |||
2386 | self.ftrace_line_fmt = self.ftrace_line_fmt_nop | 2571 | self.ftrace_line_fmt = self.ftrace_line_fmt_nop |
2387 | else: | 2572 | else: |
2388 | doError('Invalid tracer format: [%s]' % tracer) | 2573 | doError('Invalid tracer format: [%s]' % tracer) |
2574 | def decode(self, data): | ||
2575 | try: | ||
2576 | out = base64.b64decode(data).decode('zlib') | ||
2577 | except: | ||
2578 | out = data | ||
2579 | return out | ||
2580 | def stampInfo(self, line): | ||
2581 | if re.match(self.stampfmt, line): | ||
2582 | self.stamp = line | ||
2583 | return True | ||
2584 | elif re.match(self.sysinfofmt, line): | ||
2585 | self.sysinfo = line | ||
2586 | return True | ||
2587 | elif re.match(self.kparamsfmt, line): | ||
2588 | self.kparams = line | ||
2589 | return True | ||
2590 | elif re.match(self.cmdlinefmt, line): | ||
2591 | self.cmdline = line | ||
2592 | return True | ||
2593 | elif re.match(self.mcelogfmt, line): | ||
2594 | self.mcelog.append(line) | ||
2595 | return True | ||
2596 | elif re.match(self.tstatfmt, line): | ||
2597 | self.turbostat.append(line) | ||
2598 | return True | ||
2599 | elif re.match(self.batteryfmt, line): | ||
2600 | self.battery.append(line) | ||
2601 | return True | ||
2602 | elif re.match(self.wififmt, line): | ||
2603 | self.wifi.append(line) | ||
2604 | return True | ||
2605 | elif re.match(self.testerrfmt, line): | ||
2606 | self.testerror.append(line) | ||
2607 | return True | ||
2608 | elif re.match(self.firmwarefmt, line): | ||
2609 | self.fwdata.append(line) | ||
2610 | return True | ||
2611 | return False | ||
2389 | def parseStamp(self, data, sv): | 2612 | def parseStamp(self, data, sv): |
2390 | # global test data | 2613 | # global test data |
2391 | m = re.match(self.stampfmt, self.stamp) | 2614 | m = re.match(self.stampfmt, self.stamp) |
@@ -2428,14 +2651,31 @@ class TestProps: | |||
2428 | sv.stamp = data.stamp | 2651 | sv.stamp = data.stamp |
2429 | # firmware data | 2652 | # firmware data |
2430 | if sv.suspendmode == 'mem' and len(self.fwdata) > data.testnumber: | 2653 | if sv.suspendmode == 'mem' and len(self.fwdata) > data.testnumber: |
2431 | data.fwSuspend, data.fwResume = self.fwdata[data.testnumber] | 2654 | m = re.match(self.firmwarefmt, self.fwdata[data.testnumber]) |
2432 | if(data.fwSuspend > 0 or data.fwResume > 0): | 2655 | if m: |
2433 | data.fwValid = True | 2656 | data.fwSuspend, data.fwResume = int(m.group('s')), int(m.group('r')) |
2657 | if(data.fwSuspend > 0 or data.fwResume > 0): | ||
2658 | data.fwValid = True | ||
2659 | # mcelog data | ||
2660 | if len(self.mcelog) > data.testnumber: | ||
2661 | m = re.match(self.mcelogfmt, self.mcelog[data.testnumber]) | ||
2662 | if m: | ||
2663 | data.mcelog = self.decode(m.group('m')) | ||
2664 | # turbostat data | ||
2665 | if len(self.turbostat) > data.testnumber: | ||
2666 | m = re.match(self.tstatfmt, self.turbostat[data.testnumber]) | ||
2667 | if m: | ||
2668 | data.turbostat = m.group('t') | ||
2434 | # battery data | 2669 | # battery data |
2435 | if len(self.battery) > data.testnumber: | 2670 | if len(self.battery) > data.testnumber: |
2436 | m = re.match(self.batteryfmt, self.battery[data.testnumber]) | 2671 | m = re.match(self.batteryfmt, self.battery[data.testnumber]) |
2437 | if m: | 2672 | if m: |
2438 | data.battery = m.groups() | 2673 | data.battery = m.groups() |
2674 | # wifi data | ||
2675 | if len(self.wifi) > data.testnumber: | ||
2676 | m = re.match(self.wififmt, self.wifi[data.testnumber]) | ||
2677 | if m: | ||
2678 | data.wifi = m.group('w') | ||
2439 | # sleep mode enter errors | 2679 | # sleep mode enter errors |
2440 | if len(self.testerror) > data.testnumber: | 2680 | if len(self.testerror) > data.testnumber: |
2441 | m = re.match(self.testerrfmt, self.testerror[data.testnumber]) | 2681 | m = re.match(self.testerrfmt, self.testerror[data.testnumber]) |
@@ -2505,9 +2745,9 @@ class ProcessMonitor: | |||
2505 | # Quickly determine if the ftrace log has all of the trace events, | 2745 | # Quickly determine if the ftrace log has all of the trace events, |
2506 | # markers, and/or kprobes required for primary parsing. | 2746 | # markers, and/or kprobes required for primary parsing. |
2507 | def doesTraceLogHaveTraceEvents(): | 2747 | def doesTraceLogHaveTraceEvents(): |
2508 | kpcheck = ['_cal: (', '_cpu_down()'] | 2748 | kpcheck = ['_cal: (', '_ret: ('] |
2509 | techeck = ['suspend_resume', 'device_pm_callback'] | 2749 | techeck = ['suspend_resume', 'device_pm_callback'] |
2510 | tmcheck = ['tracing_mark_write'] | 2750 | tmcheck = ['SUSPEND START', 'RESUME COMPLETE'] |
2511 | sysvals.usekprobes = False | 2751 | sysvals.usekprobes = False |
2512 | fp = sysvals.openlog(sysvals.ftracefile, 'r') | 2752 | fp = sysvals.openlog(sysvals.ftracefile, 'r') |
2513 | for line in fp: | 2753 | for line in fp: |
@@ -2556,21 +2796,7 @@ def appendIncompleteTraceLog(testruns): | |||
2556 | for line in tf: | 2796 | for line in tf: |
2557 | # remove any latent carriage returns | 2797 | # remove any latent carriage returns |
2558 | line = line.replace('\r\n', '') | 2798 | line = line.replace('\r\n', '') |
2559 | # grab the stamp and sysinfo | 2799 | if tp.stampInfo(line): |
2560 | if re.match(tp.stampfmt, line): | ||
2561 | tp.stamp = line | ||
2562 | continue | ||
2563 | elif re.match(tp.sysinfofmt, line): | ||
2564 | tp.sysinfo = line | ||
2565 | continue | ||
2566 | elif re.match(tp.cmdlinefmt, line): | ||
2567 | tp.cmdline = line | ||
2568 | continue | ||
2569 | elif re.match(tp.batteryfmt, line): | ||
2570 | tp.battery.append(line) | ||
2571 | continue | ||
2572 | elif re.match(tp.testerrfmt, line): | ||
2573 | tp.testerror.append(line) | ||
2574 | continue | 2800 | continue |
2575 | # determine the trace data type (required for further parsing) | 2801 | # determine the trace data type (required for further parsing) |
2576 | m = re.match(tp.tracertypefmt, line) | 2802 | m = re.match(tp.tracertypefmt, line) |
@@ -2693,26 +2919,7 @@ def parseTraceLog(live=False): | |||
2693 | for line in tf: | 2919 | for line in tf: |
2694 | # remove any latent carriage returns | 2920 | # remove any latent carriage returns |
2695 | line = line.replace('\r\n', '') | 2921 | line = line.replace('\r\n', '') |
2696 | # stamp and sysinfo lines | 2922 | if tp.stampInfo(line): |
2697 | if re.match(tp.stampfmt, line): | ||
2698 | tp.stamp = line | ||
2699 | continue | ||
2700 | elif re.match(tp.sysinfofmt, line): | ||
2701 | tp.sysinfo = line | ||
2702 | continue | ||
2703 | elif re.match(tp.cmdlinefmt, line): | ||
2704 | tp.cmdline = line | ||
2705 | continue | ||
2706 | elif re.match(tp.batteryfmt, line): | ||
2707 | tp.battery.append(line) | ||
2708 | continue | ||
2709 | elif re.match(tp.testerrfmt, line): | ||
2710 | tp.testerror.append(line) | ||
2711 | continue | ||
2712 | # firmware line: pull out any firmware data | ||
2713 | m = re.match(tp.firmwarefmt, line) | ||
2714 | if(m): | ||
2715 | tp.fwdata.append((int(m.group('s')), int(m.group('r')))) | ||
2716 | continue | 2923 | continue |
2717 | # tracer type line: determine the trace data type | 2924 | # tracer type line: determine the trace data type |
2718 | m = re.match(tp.tracertypefmt, line) | 2925 | m = re.match(tp.tracertypefmt, line) |
@@ -2925,7 +3132,7 @@ def parseTraceLog(live=False): | |||
2925 | tp.ktemp[key].append({ | 3132 | tp.ktemp[key].append({ |
2926 | 'pid': pid, | 3133 | 'pid': pid, |
2927 | 'begin': t.time, | 3134 | 'begin': t.time, |
2928 | 'end': t.time, | 3135 | 'end': -1, |
2929 | 'name': displayname, | 3136 | 'name': displayname, |
2930 | 'cdata': kprobedata, | 3137 | 'cdata': kprobedata, |
2931 | 'proc': m_proc, | 3138 | 'proc': m_proc, |
@@ -2936,12 +3143,11 @@ def parseTraceLog(live=False): | |||
2936 | elif(t.freturn): | 3143 | elif(t.freturn): |
2937 | if(key not in tp.ktemp) or len(tp.ktemp[key]) < 1: | 3144 | if(key not in tp.ktemp) or len(tp.ktemp[key]) < 1: |
2938 | continue | 3145 | continue |
2939 | e = tp.ktemp[key][-1] | 3146 | e = next((x for x in reversed(tp.ktemp[key]) if x['end'] < 0), 0) |
2940 | if e['begin'] < 0.0 or t.time - e['begin'] < 0.000001: | 3147 | if not e: |
2941 | tp.ktemp[key].pop() | 3148 | continue |
2942 | else: | 3149 | e['end'] = t.time |
2943 | e['end'] = t.time | 3150 | e['rdata'] = kprobedata |
2944 | e['rdata'] = kprobedata | ||
2945 | # end of kernel resume | 3151 | # end of kernel resume |
2946 | if(phase != 'suspend_prepare' and kprobename in krescalls): | 3152 | if(phase != 'suspend_prepare' and kprobename in krescalls): |
2947 | if phase in data.dmesg: | 3153 | if phase in data.dmesg: |
@@ -2963,8 +3169,10 @@ def parseTraceLog(live=False): | |||
2963 | if(res == -1): | 3169 | if(res == -1): |
2964 | testrun.ftemp[key][-1].addLine(t) | 3170 | testrun.ftemp[key][-1].addLine(t) |
2965 | tf.close() | 3171 | tf.close() |
3172 | if len(testdata) < 1: | ||
3173 | sysvals.vprint('WARNING: ftrace start marker is missing') | ||
2966 | if data and not data.devicegroups: | 3174 | if data and not data.devicegroups: |
2967 | sysvals.vprint('WARNING: end marker is missing') | 3175 | sysvals.vprint('WARNING: ftrace end marker is missing') |
2968 | data.handleEndMarker(t.time) | 3176 | data.handleEndMarker(t.time) |
2969 | 3177 | ||
2970 | if sysvals.suspendmode == 'command': | 3178 | if sysvals.suspendmode == 'command': |
@@ -3013,9 +3221,11 @@ def parseTraceLog(live=False): | |||
3013 | name, pid = key | 3221 | name, pid = key |
3014 | if name not in sysvals.tracefuncs: | 3222 | if name not in sysvals.tracefuncs: |
3015 | continue | 3223 | continue |
3224 | if pid not in data.devpids: | ||
3225 | data.devpids.append(pid) | ||
3016 | for e in tp.ktemp[key]: | 3226 | for e in tp.ktemp[key]: |
3017 | kb, ke = e['begin'], e['end'] | 3227 | kb, ke = e['begin'], e['end'] |
3018 | if kb == ke or tlb > kb or tle <= kb: | 3228 | if ke - kb < 0.000001 or tlb > kb or tle <= kb: |
3019 | continue | 3229 | continue |
3020 | color = sysvals.kprobeColor(name) | 3230 | color = sysvals.kprobeColor(name) |
3021 | data.newActionGlobal(e['name'], kb, ke, pid, color) | 3231 | data.newActionGlobal(e['name'], kb, ke, pid, color) |
@@ -3027,7 +3237,7 @@ def parseTraceLog(live=False): | |||
3027 | continue | 3237 | continue |
3028 | for e in tp.ktemp[key]: | 3238 | for e in tp.ktemp[key]: |
3029 | kb, ke = e['begin'], e['end'] | 3239 | kb, ke = e['begin'], e['end'] |
3030 | if kb == ke or tlb > kb or tle <= kb: | 3240 | if ke - kb < 0.000001 or tlb > kb or tle <= kb: |
3031 | continue | 3241 | continue |
3032 | data.addDeviceFunctionCall(e['name'], name, e['proc'], pid, kb, | 3242 | data.addDeviceFunctionCall(e['name'], name, e['proc'], pid, kb, |
3033 | ke, e['cdata'], e['rdata']) | 3243 | ke, e['cdata'], e['rdata']) |
@@ -3051,7 +3261,7 @@ def parseTraceLog(live=False): | |||
3051 | if not devname: | 3261 | if not devname: |
3052 | sortkey = '%f%f%d' % (cg.start, cg.end, pid) | 3262 | sortkey = '%f%f%d' % (cg.start, cg.end, pid) |
3053 | sortlist[sortkey] = cg | 3263 | sortlist[sortkey] = cg |
3054 | elif len(cg.list) > 1000000: | 3264 | elif len(cg.list) > 1000000 and cg.name != sysvals.ftopfunc: |
3055 | sysvals.vprint('WARNING: the callgraph for %s is massive (%d lines)' %\ | 3265 | sysvals.vprint('WARNING: the callgraph for %s is massive (%d lines)' %\ |
3056 | (devname, len(cg.list))) | 3266 | (devname, len(cg.list))) |
3057 | # create blocks for orphan cg data | 3267 | # create blocks for orphan cg data |
@@ -3133,25 +3343,7 @@ def loadKernelLog(): | |||
3133 | idx = line.find('[') | 3343 | idx = line.find('[') |
3134 | if idx > 1: | 3344 | if idx > 1: |
3135 | line = line[idx:] | 3345 | line = line[idx:] |
3136 | # grab the stamp and sysinfo | 3346 | if tp.stampInfo(line): |
3137 | if re.match(tp.stampfmt, line): | ||
3138 | tp.stamp = line | ||
3139 | continue | ||
3140 | elif re.match(tp.sysinfofmt, line): | ||
3141 | tp.sysinfo = line | ||
3142 | continue | ||
3143 | elif re.match(tp.cmdlinefmt, line): | ||
3144 | tp.cmdline = line | ||
3145 | continue | ||
3146 | elif re.match(tp.batteryfmt, line): | ||
3147 | tp.battery.append(line) | ||
3148 | continue | ||
3149 | elif re.match(tp.testerrfmt, line): | ||
3150 | tp.testerror.append(line) | ||
3151 | continue | ||
3152 | m = re.match(tp.firmwarefmt, line) | ||
3153 | if(m): | ||
3154 | tp.fwdata.append((int(m.group('s')), int(m.group('r')))) | ||
3155 | continue | 3347 | continue |
3156 | m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line) | 3348 | m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line) |
3157 | if(not m): | 3349 | if(not m): |
@@ -3176,7 +3368,7 @@ def loadKernelLog(): | |||
3176 | if data: | 3368 | if data: |
3177 | testruns.append(data) | 3369 | testruns.append(data) |
3178 | if len(testruns) < 1: | 3370 | if len(testruns) < 1: |
3179 | pprint('ERROR: dmesg log has no suspend/resume data: %s' \ | 3371 | doError('dmesg log has no suspend/resume data: %s' \ |
3180 | % sysvals.dmesgfile) | 3372 | % sysvals.dmesgfile) |
3181 | 3373 | ||
3182 | # fix lines with same timestamp/function with the call and return swapped | 3374 | # fix lines with same timestamp/function with the call and return swapped |
@@ -3515,6 +3707,8 @@ def addCallgraphs(sv, hf, data): | |||
3515 | name += ' '+p | 3707 | name += ' '+p |
3516 | if('ftrace' in dev): | 3708 | if('ftrace' in dev): |
3517 | cg = dev['ftrace'] | 3709 | cg = dev['ftrace'] |
3710 | if cg.name == sv.ftopfunc: | ||
3711 | name = 'top level suspend/resume call' | ||
3518 | num = callgraphHTML(sv, hf, num, cg, | 3712 | num = callgraphHTML(sv, hf, num, cg, |
3519 | name, color, dev['id']) | 3713 | name, color, dev['id']) |
3520 | if('ftraces' in dev): | 3714 | if('ftraces' in dev): |
@@ -3523,22 +3717,16 @@ def addCallgraphs(sv, hf, data): | |||
3523 | name+' → '+cg.name, color, dev['id']) | 3717 | name+' → '+cg.name, color, dev['id']) |
3524 | hf.write('\n\n </section>\n') | 3718 | hf.write('\n\n </section>\n') |
3525 | 3719 | ||
3526 | # Function: createHTMLSummarySimple | 3720 | def summaryCSS(title, center=True): |
3527 | # Description: | 3721 | tdcenter = 'text-align:center;' if center else '' |
3528 | # Create summary html file for a series of tests | 3722 | out = '<!DOCTYPE html>\n<html>\n<head>\n\ |
3529 | # Arguments: | ||
3530 | # testruns: array of Data objects from parseTraceLog | ||
3531 | def createHTMLSummarySimple(testruns, htmlfile, title): | ||
3532 | # write the html header first (html head, css code, up to body start) | ||
3533 | html = '<!DOCTYPE html>\n<html>\n<head>\n\ | ||
3534 | <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\ | 3723 | <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\ |
3535 | <title>SleepGraph Summary</title>\n\ | 3724 | <title>'+title+'</title>\n\ |
3536 | <style type=\'text/css\'>\n\ | 3725 | <style type=\'text/css\'>\n\ |
3537 | .stamp {width: 100%;text-align:center;background:#888;line-height:30px;color:white;font: 25px Arial;}\n\ | 3726 | .stamp {width: 100%;text-align:center;background:#888;line-height:30px;color:white;font: 25px Arial;}\n\ |
3538 | table {width:100%;border-collapse: collapse;}\n\ | 3727 | table {width:100%;border-collapse: collapse;border:1px solid;}\n\ |
3539 | .summary {border:1px solid;}\n\ | ||
3540 | th {border: 1px solid black;background:#222;color:white;}\n\ | 3728 | th {border: 1px solid black;background:#222;color:white;}\n\ |
3541 | td {font: 14px "Times New Roman";text-align: center;}\n\ | 3729 | td {font: 14px "Times New Roman";'+tdcenter+'}\n\ |
3542 | tr.head td {border: 1px solid black;background:#aaa;}\n\ | 3730 | tr.head td {border: 1px solid black;background:#aaa;}\n\ |
3543 | tr.alt {background-color:#ddd;}\n\ | 3731 | tr.alt {background-color:#ddd;}\n\ |
3544 | tr.notice {color:red;}\n\ | 3732 | tr.notice {color:red;}\n\ |
@@ -3547,12 +3735,23 @@ def createHTMLSummarySimple(testruns, htmlfile, title): | |||
3547 | .maxval {background-color:#FFBBBB;}\n\ | 3735 | .maxval {background-color:#FFBBBB;}\n\ |
3548 | .head a {color:#000;text-decoration: none;}\n\ | 3736 | .head a {color:#000;text-decoration: none;}\n\ |
3549 | </style>\n</head>\n<body>\n' | 3737 | </style>\n</head>\n<body>\n' |
3738 | return out | ||
3739 | |||
3740 | # Function: createHTMLSummarySimple | ||
3741 | # Description: | ||
3742 | # Create summary html file for a series of tests | ||
3743 | # Arguments: | ||
3744 | # testruns: array of Data objects from parseTraceLog | ||
3745 | def createHTMLSummarySimple(testruns, htmlfile, title): | ||
3746 | # write the html header first (html head, css code, up to body start) | ||
3747 | html = summaryCSS('Summary - SleepGraph') | ||
3550 | 3748 | ||
3551 | # extract the test data into list | 3749 | # extract the test data into list |
3552 | list = dict() | 3750 | list = dict() |
3553 | tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [[], []] | 3751 | tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()] |
3554 | iMin, iMed, iMax = [0, 0], [0, 0], [0, 0] | 3752 | iMin, iMed, iMax = [0, 0], [0, 0], [0, 0] |
3555 | num = 0 | 3753 | num = 0 |
3754 | useturbo = False | ||
3556 | lastmode = '' | 3755 | lastmode = '' |
3557 | cnt = dict() | 3756 | cnt = dict() |
3558 | for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'], v['time'])): | 3757 | for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'], v['time'])): |
@@ -3563,27 +3762,35 @@ def createHTMLSummarySimple(testruns, htmlfile, title): | |||
3563 | for i in range(2): | 3762 | for i in range(2): |
3564 | s = sorted(tMed[i]) | 3763 | s = sorted(tMed[i]) |
3565 | list[lastmode]['med'][i] = s[int(len(s)/2)] | 3764 | list[lastmode]['med'][i] = s[int(len(s)/2)] |
3566 | iMed[i] = tMed[i].index(list[lastmode]['med'][i]) | 3765 | iMed[i] = tMed[i][list[lastmode]['med'][i]] |
3567 | list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num] | 3766 | list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num] |
3568 | list[lastmode]['min'] = tMin | 3767 | list[lastmode]['min'] = tMin |
3569 | list[lastmode]['max'] = tMax | 3768 | list[lastmode]['max'] = tMax |
3570 | list[lastmode]['idx'] = (iMin, iMed, iMax) | 3769 | list[lastmode]['idx'] = (iMin, iMed, iMax) |
3571 | tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [[], []] | 3770 | tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()] |
3572 | iMin, iMed, iMax = [0, 0], [0, 0], [0, 0] | 3771 | iMin, iMed, iMax = [0, 0], [0, 0], [0, 0] |
3573 | num = 0 | 3772 | num = 0 |
3773 | pkgpc10 = syslpi = '' | ||
3774 | if 'pkgpc10' in data and 'syslpi' in data: | ||
3775 | pkgpc10 = data['pkgpc10'] | ||
3776 | syslpi = data['syslpi'] | ||
3777 | useturbo = True | ||
3778 | res = data['result'] | ||
3574 | tVal = [float(data['suspend']), float(data['resume'])] | 3779 | tVal = [float(data['suspend']), float(data['resume'])] |
3575 | list[mode]['data'].append([data['host'], data['kernel'], | 3780 | list[mode]['data'].append([data['host'], data['kernel'], |
3576 | data['time'], tVal[0], tVal[1], data['url'], data['result'], | 3781 | data['time'], tVal[0], tVal[1], data['url'], res, |
3577 | data['issues'], data['sus_worst'], data['sus_worsttime'], | 3782 | data['issues'], data['sus_worst'], data['sus_worsttime'], |
3578 | data['res_worst'], data['res_worsttime']]) | 3783 | data['res_worst'], data['res_worsttime'], pkgpc10, syslpi]) |
3579 | idx = len(list[mode]['data']) - 1 | 3784 | idx = len(list[mode]['data']) - 1 |
3580 | if data['result'] not in cnt: | 3785 | if res.startswith('fail in'): |
3581 | cnt[data['result']] = 1 | 3786 | res = 'fail' |
3787 | if res not in cnt: | ||
3788 | cnt[res] = 1 | ||
3582 | else: | 3789 | else: |
3583 | cnt[data['result']] += 1 | 3790 | cnt[res] += 1 |
3584 | if data['result'] == 'pass': | 3791 | if res == 'pass': |
3585 | for i in range(2): | 3792 | for i in range(2): |
3586 | tMed[i].append(tVal[i]) | 3793 | tMed[i][tVal[i]] = idx |
3587 | tAvg[i] += tVal[i] | 3794 | tAvg[i] += tVal[i] |
3588 | if tMin[i] == 0 or tVal[i] < tMin[i]: | 3795 | if tMin[i] == 0 or tVal[i] < tMin[i]: |
3589 | iMin[i] = idx | 3796 | iMin[i] = idx |
@@ -3597,7 +3804,7 @@ def createHTMLSummarySimple(testruns, htmlfile, title): | |||
3597 | for i in range(2): | 3804 | for i in range(2): |
3598 | s = sorted(tMed[i]) | 3805 | s = sorted(tMed[i]) |
3599 | list[lastmode]['med'][i] = s[int(len(s)/2)] | 3806 | list[lastmode]['med'][i] = s[int(len(s)/2)] |
3600 | iMed[i] = tMed[i].index(list[lastmode]['med'][i]) | 3807 | iMed[i] = tMed[i][list[lastmode]['med'][i]] |
3601 | list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num] | 3808 | list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num] |
3602 | list[lastmode]['min'] = tMin | 3809 | list[lastmode]['min'] = tMin |
3603 | list[lastmode]['max'] = tMax | 3810 | list[lastmode]['max'] = tMax |
@@ -3613,19 +3820,21 @@ def createHTMLSummarySimple(testruns, htmlfile, title): | |||
3613 | td = '\t<td>{0}</td>\n' | 3820 | td = '\t<td>{0}</td>\n' |
3614 | tdh = '\t<td{1}>{0}</td>\n' | 3821 | tdh = '\t<td{1}>{0}</td>\n' |
3615 | tdlink = '\t<td><a href="{0}">html</a></td>\n' | 3822 | tdlink = '\t<td><a href="{0}">html</a></td>\n' |
3823 | colspan = '14' if useturbo else '12' | ||
3616 | 3824 | ||
3617 | # table header | 3825 | # table header |
3618 | html += '<table class="summary">\n<tr>\n' + th.format('#') +\ | 3826 | html += '<table>\n<tr>\n' + th.format('#') +\ |
3619 | th.format('Mode') + th.format('Host') + th.format('Kernel') +\ | 3827 | th.format('Mode') + th.format('Host') + th.format('Kernel') +\ |
3620 | th.format('Test Time') + th.format('Result') + th.format('Issues') +\ | 3828 | th.format('Test Time') + th.format('Result') + th.format('Issues') +\ |
3621 | th.format('Suspend') + th.format('Resume') +\ | 3829 | th.format('Suspend') + th.format('Resume') +\ |
3622 | th.format('Worst Suspend Device') + th.format('SD Time') +\ | 3830 | th.format('Worst Suspend Device') + th.format('SD Time') +\ |
3623 | th.format('Worst Resume Device') + th.format('RD Time') +\ | 3831 | th.format('Worst Resume Device') + th.format('RD Time') |
3624 | th.format('Detail') + '</tr>\n' | 3832 | if useturbo: |
3625 | 3833 | html += th.format('PkgPC10') + th.format('SysLPI') | |
3834 | html += th.format('Detail')+'</tr>\n' | ||
3626 | # export list into html | 3835 | # export list into html |
3627 | head = '<tr class="head"><td>{0}</td><td>{1}</td>'+\ | 3836 | head = '<tr class="head"><td>{0}</td><td>{1}</td>'+\ |
3628 | '<td colspan=12 class="sus">Suspend Avg={2} '+\ | 3837 | '<td colspan='+colspan+' class="sus">Suspend Avg={2} '+\ |
3629 | '<span class=minval><a href="#s{10}min">Min={3}</a></span> '+\ | 3838 | '<span class=minval><a href="#s{10}min">Min={3}</a></span> '+\ |
3630 | '<span class=medval><a href="#s{10}med">Med={4}</a></span> '+\ | 3839 | '<span class=medval><a href="#s{10}med">Med={4}</a></span> '+\ |
3631 | '<span class=maxval><a href="#s{10}max">Max={5}</a></span> '+\ | 3840 | '<span class=maxval><a href="#s{10}max">Max={5}</a></span> '+\ |
@@ -3634,7 +3843,8 @@ def createHTMLSummarySimple(testruns, htmlfile, title): | |||
3634 | '<span class=medval><a href="#r{10}med">Med={8}</a></span> '+\ | 3843 | '<span class=medval><a href="#r{10}med">Med={8}</a></span> '+\ |
3635 | '<span class=maxval><a href="#r{10}max">Max={9}</a></span></td>'+\ | 3844 | '<span class=maxval><a href="#r{10}max">Max={9}</a></span></td>'+\ |
3636 | '</tr>\n' | 3845 | '</tr>\n' |
3637 | headnone = '<tr class="head"><td>{0}</td><td>{1}</td><td colspan=12></td></tr>\n' | 3846 | headnone = '<tr class="head"><td>{0}</td><td>{1}</td><td colspan='+\ |
3847 | colspan+'></td></tr>\n' | ||
3638 | for mode in list: | 3848 | for mode in list: |
3639 | # header line for each suspend mode | 3849 | # header line for each suspend mode |
3640 | num = 0 | 3850 | num = 0 |
@@ -3681,6 +3891,9 @@ def createHTMLSummarySimple(testruns, htmlfile, title): | |||
3681 | html += td.format('%.3f ms' % d[9]) if d[9] else td.format('') # sus_worst time | 3891 | html += td.format('%.3f ms' % d[9]) if d[9] else td.format('') # sus_worst time |
3682 | html += td.format(d[10]) # res_worst | 3892 | html += td.format(d[10]) # res_worst |
3683 | html += td.format('%.3f ms' % d[11]) if d[11] else td.format('') # res_worst time | 3893 | html += td.format('%.3f ms' % d[11]) if d[11] else td.format('') # res_worst time |
3894 | if useturbo: | ||
3895 | html += td.format(d[12]) # pkg_pc10 | ||
3896 | html += td.format(d[13]) # syslpi | ||
3684 | html += tdlink.format(d[5]) if d[5] else td.format('') # url | 3897 | html += tdlink.format(d[5]) if d[5] else td.format('') # url |
3685 | html += '</tr>\n' | 3898 | html += '</tr>\n' |
3686 | num += 1 | 3899 | num += 1 |
@@ -3690,6 +3903,115 @@ def createHTMLSummarySimple(testruns, htmlfile, title): | |||
3690 | hf.write(html+'</table>\n</body>\n</html>\n') | 3903 | hf.write(html+'</table>\n</body>\n</html>\n') |
3691 | hf.close() | 3904 | hf.close() |
3692 | 3905 | ||
3906 | def createHTMLDeviceSummary(testruns, htmlfile, title): | ||
3907 | html = summaryCSS('Device Summary - SleepGraph', False) | ||
3908 | |||
3909 | # create global device list from all tests | ||
3910 | devall = dict() | ||
3911 | for data in testruns: | ||
3912 | host, url, devlist = data['host'], data['url'], data['devlist'] | ||
3913 | for type in devlist: | ||
3914 | if type not in devall: | ||
3915 | devall[type] = dict() | ||
3916 | mdevlist, devlist = devall[type], data['devlist'][type] | ||
3917 | for name in devlist: | ||
3918 | length = devlist[name] | ||
3919 | if name not in mdevlist: | ||
3920 | mdevlist[name] = {'name': name, 'host': host, | ||
3921 | 'worst': length, 'total': length, 'count': 1, | ||
3922 | 'url': url} | ||
3923 | else: | ||
3924 | if length > mdevlist[name]['worst']: | ||
3925 | mdevlist[name]['worst'] = length | ||
3926 | mdevlist[name]['url'] = url | ||
3927 | mdevlist[name]['host'] = host | ||
3928 | mdevlist[name]['total'] += length | ||
3929 | mdevlist[name]['count'] += 1 | ||
3930 | |||
3931 | # generate the html | ||
3932 | th = '\t<th>{0}</th>\n' | ||
3933 | td = '\t<td align=center>{0}</td>\n' | ||
3934 | tdr = '\t<td align=right>{0}</td>\n' | ||
3935 | tdlink = '\t<td align=center><a href="{0}">html</a></td>\n' | ||
3936 | limit = 1 | ||
3937 | for type in sorted(devall, reverse=True): | ||
3938 | num = 0 | ||
3939 | devlist = devall[type] | ||
3940 | # table header | ||
3941 | html += '<div class="stamp">%s (%s devices > %d ms)</div><table>\n' % \ | ||
3942 | (title, type.upper(), limit) | ||
3943 | html += '<tr>\n' + '<th align=right>Device Name</th>' +\ | ||
3944 | th.format('Average Time') + th.format('Count') +\ | ||
3945 | th.format('Worst Time') + th.format('Host (worst time)') +\ | ||
3946 | th.format('Link (worst time)') + '</tr>\n' | ||
3947 | for name in sorted(devlist, key=lambda k:devlist[k]['worst'], reverse=True): | ||
3948 | data = devall[type][name] | ||
3949 | data['average'] = data['total'] / data['count'] | ||
3950 | if data['average'] < limit: | ||
3951 | continue | ||
3952 | # row classes - alternate row color | ||
3953 | rcls = ['alt'] if num % 2 == 1 else [] | ||
3954 | html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n' | ||
3955 | html += tdr.format(data['name']) # name | ||
3956 | html += td.format('%.3f ms' % data['average']) # average | ||
3957 | html += td.format(data['count']) # count | ||
3958 | html += td.format('%.3f ms' % data['worst']) # worst | ||
3959 | html += td.format(data['host']) # host | ||
3960 | html += tdlink.format(data['url']) # url | ||
3961 | html += '</tr>\n' | ||
3962 | num += 1 | ||
3963 | html += '</table>\n' | ||
3964 | |||
3965 | # flush the data to file | ||
3966 | hf = open(htmlfile, 'w') | ||
3967 | hf.write(html+'</body>\n</html>\n') | ||
3968 | hf.close() | ||
3969 | return devall | ||
3970 | |||
3971 | def createHTMLIssuesSummary(testruns, issues, htmlfile, title, extra=''): | ||
3972 | multihost = len([e for e in issues if len(e['urls']) > 1]) > 0 | ||
3973 | html = summaryCSS('Issues Summary - SleepGraph', False) | ||
3974 | total = len(testruns) | ||
3975 | |||
3976 | # generate the html | ||
3977 | th = '\t<th>{0}</th>\n' | ||
3978 | td = '\t<td align={0}>{1}</td>\n' | ||
3979 | tdlink = '<a href="{1}">{0}</a>' | ||
3980 | subtitle = '%d issues' % len(issues) if len(issues) > 0 else 'no issues' | ||
3981 | html += '<div class="stamp">%s (%s)</div><table>\n' % (title, subtitle) | ||
3982 | html += '<tr>\n' + th.format('Issue') + th.format('Count') | ||
3983 | if multihost: | ||
3984 | html += th.format('Hosts') | ||
3985 | html += th.format('Tests') + th.format('Fail Rate') +\ | ||
3986 | th.format('First Instance') + '</tr>\n' | ||
3987 | |||
3988 | num = 0 | ||
3989 | for e in sorted(issues, key=lambda v:v['count'], reverse=True): | ||
3990 | testtotal = 0 | ||
3991 | links = [] | ||
3992 | for host in sorted(e['urls']): | ||
3993 | links.append(tdlink.format(host, e['urls'][host][0])) | ||
3994 | testtotal += len(e['urls'][host]) | ||
3995 | rate = '%d/%d (%.2f%%)' % (testtotal, total, 100*float(testtotal)/float(total)) | ||
3996 | # row classes - alternate row color | ||
3997 | rcls = ['alt'] if num % 2 == 1 else [] | ||
3998 | html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n' | ||
3999 | html += td.format('left', e['line']) # issue | ||
4000 | html += td.format('center', e['count']) # count | ||
4001 | if multihost: | ||
4002 | html += td.format('center', len(e['urls'])) # hosts | ||
4003 | html += td.format('center', testtotal) # test count | ||
4004 | html += td.format('center', rate) # test rate | ||
4005 | html += td.format('center nowrap', '<br>'.join(links)) # links | ||
4006 | html += '</tr>\n' | ||
4007 | num += 1 | ||
4008 | |||
4009 | # flush the data to file | ||
4010 | hf = open(htmlfile, 'w') | ||
4011 | hf.write(html+'</table>\n'+extra+'</body>\n</html>\n') | ||
4012 | hf.close() | ||
4013 | return issues | ||
4014 | |||
3693 | def ordinal(value): | 4015 | def ordinal(value): |
3694 | suffix = 'th' | 4016 | suffix = 'th' |
3695 | if value < 10 or value > 19: | 4017 | if value < 10 or value > 19: |
@@ -3991,7 +4313,7 @@ def createHTML(testruns, testfail): | |||
3991 | for word in phase.split('_'): | 4313 | for word in phase.split('_'): |
3992 | id += word[0] | 4314 | id += word[0] |
3993 | order = '%.2f' % ((p['order'] * pdelta) + pmargin) | 4315 | order = '%.2f' % ((p['order'] * pdelta) + pmargin) |
3994 | name = string.replace(phase, '_', ' ') | 4316 | name = phase.replace('_', ' ') |
3995 | devtl.html += devtl.html_legend.format(order, p['color'], name, id) | 4317 | devtl.html += devtl.html_legend.format(order, p['color'], name, id) |
3996 | devtl.html += '</div>\n' | 4318 | devtl.html += '</div>\n' |
3997 | 4319 | ||
@@ -4580,6 +4902,7 @@ def setRuntimeSuspend(before=True): | |||
4580 | def executeSuspend(): | 4902 | def executeSuspend(): |
4581 | pm = ProcessMonitor() | 4903 | pm = ProcessMonitor() |
4582 | tp = sysvals.tpath | 4904 | tp = sysvals.tpath |
4905 | wifi = sysvals.checkWifi() | ||
4583 | testdata = [] | 4906 | testdata = [] |
4584 | battery = True if getBattery() else False | 4907 | battery = True if getBattery() else False |
4585 | # run these commands to prepare the system for suspend | 4908 | # run these commands to prepare the system for suspend |
@@ -4613,6 +4936,7 @@ def executeSuspend(): | |||
4613 | pprint('SUSPEND START') | 4936 | pprint('SUSPEND START') |
4614 | else: | 4937 | else: |
4615 | pprint('SUSPEND START (press a key to resume)') | 4938 | pprint('SUSPEND START (press a key to resume)') |
4939 | sysvals.mcelog(True) | ||
4616 | bat1 = getBattery() if battery else False | 4940 | bat1 = getBattery() if battery else False |
4617 | # set rtcwake | 4941 | # set rtcwake |
4618 | if(sysvals.rtcwake): | 4942 | if(sysvals.rtcwake): |
@@ -4644,13 +4968,23 @@ def executeSuspend(): | |||
4644 | pf = open(sysvals.diskpowerfile, 'w') | 4968 | pf = open(sysvals.diskpowerfile, 'w') |
4645 | pf.write(sysvals.diskmode) | 4969 | pf.write(sysvals.diskmode) |
4646 | pf.close() | 4970 | pf.close() |
4647 | pf = open(sysvals.powerfile, 'w') | 4971 | if mode == 'freeze' and sysvals.haveTurbostat(): |
4648 | pf.write(mode) | 4972 | # execution will pause here |
4649 | # execution will pause here | 4973 | turbo = sysvals.turbostat() |
4650 | try: | 4974 | if '|' in turbo: |
4651 | pf.close() | 4975 | tdata['turbo'] = turbo |
4652 | except Exception as e: | 4976 | else: |
4653 | tdata['error'] = str(e) | 4977 | tdata['error'] = turbo |
4978 | else: | ||
4979 | if sysvals.haveTurbostat(): | ||
4980 | sysvals.vprint('WARNING: ignoring turbostat in mode "%s"' % mode) | ||
4981 | pf = open(sysvals.powerfile, 'w') | ||
4982 | pf.write(mode) | ||
4983 | # execution will pause here | ||
4984 | try: | ||
4985 | pf.close() | ||
4986 | except Exception as e: | ||
4987 | tdata['error'] = str(e) | ||
4654 | if(sysvals.rtcwake): | 4988 | if(sysvals.rtcwake): |
4655 | sysvals.rtcWakeAlarmOff() | 4989 | sysvals.rtcWakeAlarmOff() |
4656 | # postdelay delay | 4990 | # postdelay delay |
@@ -4664,9 +4998,14 @@ def executeSuspend(): | |||
4664 | sysvals.fsetVal('RESUME COMPLETE', 'trace_marker') | 4998 | sysvals.fsetVal('RESUME COMPLETE', 'trace_marker') |
4665 | if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'): | 4999 | if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'): |
4666 | tdata['fw'] = getFPDT(False) | 5000 | tdata['fw'] = getFPDT(False) |
5001 | mcelog = sysvals.mcelog() | ||
5002 | if mcelog: | ||
5003 | tdata['mcelog'] = mcelog | ||
4667 | bat2 = getBattery() if battery else False | 5004 | bat2 = getBattery() if battery else False |
4668 | if battery and bat1 and bat2: | 5005 | if battery and bat1 and bat2: |
4669 | tdata['bat'] = (bat1, bat2) | 5006 | tdata['bat'] = (bat1, bat2) |
5007 | if 'device' in wifi and 'ip' in wifi: | ||
5008 | tdata['wifi'] = (wifi, sysvals.checkWifi()) | ||
4670 | testdata.append(tdata) | 5009 | testdata.append(tdata) |
4671 | # stop ftrace | 5010 | # stop ftrace |
4672 | if(sysvals.usecallgraph or sysvals.usetraceevents): | 5011 | if(sysvals.usecallgraph or sysvals.usetraceevents): |
@@ -4686,6 +5025,7 @@ def executeSuspend(): | |||
4686 | op.close() | 5025 | op.close() |
4687 | sysvals.fsetVal('', 'trace') | 5026 | sysvals.fsetVal('', 'trace') |
4688 | devProps() | 5027 | devProps() |
5028 | return testdata | ||
4689 | 5029 | ||
4690 | def readFile(file): | 5030 | def readFile(file): |
4691 | if os.path.islink(file): | 5031 | if os.path.islink(file): |
@@ -4772,7 +5112,7 @@ def deviceInfo(output=''): | |||
4772 | ms2nice(power['runtime_active_time']), \ | 5112 | ms2nice(power['runtime_active_time']), \ |
4773 | ms2nice(power['runtime_suspended_time'])) | 5113 | ms2nice(power['runtime_suspended_time'])) |
4774 | for i in sorted(lines): | 5114 | for i in sorted(lines): |
4775 | print lines[i] | 5115 | print(lines[i]) |
4776 | return res | 5116 | return res |
4777 | 5117 | ||
4778 | # Function: devProps | 5118 | # Function: devProps |
@@ -4905,12 +5245,12 @@ def getModes(): | |||
4905 | modes = [] | 5245 | modes = [] |
4906 | if(os.path.exists(sysvals.powerfile)): | 5246 | if(os.path.exists(sysvals.powerfile)): |
4907 | fp = open(sysvals.powerfile, 'r') | 5247 | fp = open(sysvals.powerfile, 'r') |
4908 | modes = string.split(fp.read()) | 5248 | modes = fp.read().split() |
4909 | fp.close() | 5249 | fp.close() |
4910 | if(os.path.exists(sysvals.mempowerfile)): | 5250 | if(os.path.exists(sysvals.mempowerfile)): |
4911 | deep = False | 5251 | deep = False |
4912 | fp = open(sysvals.mempowerfile, 'r') | 5252 | fp = open(sysvals.mempowerfile, 'r') |
4913 | for m in string.split(fp.read()): | 5253 | for m in fp.read().split(): |
4914 | memmode = m.strip('[]') | 5254 | memmode = m.strip('[]') |
4915 | if memmode == 'deep': | 5255 | if memmode == 'deep': |
4916 | deep = True | 5256 | deep = True |
@@ -4921,7 +5261,7 @@ def getModes(): | |||
4921 | modes.remove('mem') | 5261 | modes.remove('mem') |
4922 | if('disk' in modes and os.path.exists(sysvals.diskpowerfile)): | 5262 | if('disk' in modes and os.path.exists(sysvals.diskpowerfile)): |
4923 | fp = open(sysvals.diskpowerfile, 'r') | 5263 | fp = open(sysvals.diskpowerfile, 'r') |
4924 | for m in string.split(fp.read()): | 5264 | for m in fp.read().split(): |
4925 | modes.append('disk-%s' % m.strip('[]')) | 5265 | modes.append('disk-%s' % m.strip('[]')) |
4926 | fp.close() | 5266 | fp.close() |
4927 | return modes | 5267 | return modes |
@@ -4984,14 +5324,15 @@ def dmidecode(mempath, fatal=False): | |||
4984 | continue | 5324 | continue |
4985 | 5325 | ||
4986 | # read in the memory for scanning | 5326 | # read in the memory for scanning |
4987 | fp = open(mempath, 'rb') | ||
4988 | try: | 5327 | try: |
5328 | fp = open(mempath, 'rb') | ||
4989 | fp.seek(memaddr) | 5329 | fp.seek(memaddr) |
4990 | buf = fp.read(memsize) | 5330 | buf = fp.read(memsize) |
4991 | except: | 5331 | except: |
4992 | if(fatal): | 5332 | if(fatal): |
4993 | doError('DMI table is unreachable, sorry') | 5333 | doError('DMI table is unreachable, sorry') |
4994 | else: | 5334 | else: |
5335 | pprint('WARNING: /dev/mem is not readable, ignoring DMI data') | ||
4995 | return out | 5336 | return out |
4996 | fp.close() | 5337 | fp.close() |
4997 | 5338 | ||
@@ -5014,14 +5355,15 @@ def dmidecode(mempath, fatal=False): | |||
5014 | return out | 5355 | return out |
5015 | 5356 | ||
5016 | # read in the SM or DMI table | 5357 | # read in the SM or DMI table |
5017 | fp = open(mempath, 'rb') | ||
5018 | try: | 5358 | try: |
5359 | fp = open(mempath, 'rb') | ||
5019 | fp.seek(base) | 5360 | fp.seek(base) |
5020 | buf = fp.read(length) | 5361 | buf = fp.read(length) |
5021 | except: | 5362 | except: |
5022 | if(fatal): | 5363 | if(fatal): |
5023 | doError('DMI table is unreachable, sorry') | 5364 | doError('DMI table is unreachable, sorry') |
5024 | else: | 5365 | else: |
5366 | pprint('WARNING: /dev/mem is not readable, ignoring DMI data') | ||
5025 | return out | 5367 | return out |
5026 | fp.close() | 5368 | fp.close() |
5027 | 5369 | ||
@@ -5165,7 +5507,11 @@ def getFPDT(output): | |||
5165 | i = 0 | 5507 | i = 0 |
5166 | fwData = [0, 0] | 5508 | fwData = [0, 0] |
5167 | records = buf[36:] | 5509 | records = buf[36:] |
5168 | fp = open(sysvals.mempath, 'rb') | 5510 | try: |
5511 | fp = open(sysvals.mempath, 'rb') | ||
5512 | except: | ||
5513 | pprint('WARNING: /dev/mem is not readable, ignoring the FPDT data') | ||
5514 | return False | ||
5169 | while(i < len(records)): | 5515 | while(i < len(records)): |
5170 | header = struct.unpack('HBB', records[i:i+4]) | 5516 | header = struct.unpack('HBB', records[i:i+4]) |
5171 | if(header[0] not in rectype): | 5517 | if(header[0] not in rectype): |
@@ -5282,13 +5628,14 @@ def statusCheck(probecheck=False): | |||
5282 | pprint(' is ftrace supported: %s' % res) | 5628 | pprint(' is ftrace supported: %s' % res) |
5283 | 5629 | ||
5284 | # check if kprobes are available | 5630 | # check if kprobes are available |
5285 | res = sysvals.colorText('NO') | 5631 | if sysvals.usekprobes: |
5286 | sysvals.usekprobes = sysvals.verifyKprobes() | 5632 | res = sysvals.colorText('NO') |
5287 | if(sysvals.usekprobes): | 5633 | sysvals.usekprobes = sysvals.verifyKprobes() |
5288 | res = 'YES' | 5634 | if(sysvals.usekprobes): |
5289 | else: | 5635 | res = 'YES' |
5290 | sysvals.usedevsrc = False | 5636 | else: |
5291 | pprint(' are kprobes supported: %s' % res) | 5637 | sysvals.usedevsrc = False |
5638 | pprint(' are kprobes supported: %s' % res) | ||
5292 | 5639 | ||
5293 | # what data source are we using | 5640 | # what data source are we using |
5294 | res = 'DMESG' | 5641 | res = 'DMESG' |
@@ -5376,6 +5723,8 @@ def getArgFloat(name, args, min, max, main=True): | |||
5376 | 5723 | ||
5377 | def processData(live=False): | 5724 | def processData(live=False): |
5378 | pprint('PROCESSING DATA') | 5725 | pprint('PROCESSING DATA') |
5726 | sysvals.vprint('usetraceevents=%s, usetracemarkers=%s, usekprobes=%s' % \ | ||
5727 | (sysvals.usetraceevents, sysvals.usetracemarkers, sysvals.usekprobes)) | ||
5379 | error = '' | 5728 | error = '' |
5380 | if(sysvals.usetraceevents): | 5729 | if(sysvals.usetraceevents): |
5381 | testruns, error = parseTraceLog(live) | 5730 | testruns, error = parseTraceLog(live) |
@@ -5388,13 +5737,36 @@ def processData(live=False): | |||
5388 | parseKernelLog(data) | 5737 | parseKernelLog(data) |
5389 | if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)): | 5738 | if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)): |
5390 | appendIncompleteTraceLog(testruns) | 5739 | appendIncompleteTraceLog(testruns) |
5740 | sysvals.vprint('System Info:') | ||
5741 | for key in sorted(sysvals.stamp): | ||
5742 | sysvals.vprint(' %-8s : %s' % (key.upper(), sysvals.stamp[key])) | ||
5743 | if sysvals.kparams: | ||
5744 | sysvals.vprint('Kparams:\n %s' % sysvals.kparams) | ||
5391 | sysvals.vprint('Command:\n %s' % sysvals.cmdline) | 5745 | sysvals.vprint('Command:\n %s' % sysvals.cmdline) |
5392 | for data in testruns: | 5746 | for data in testruns: |
5747 | if data.mcelog: | ||
5748 | sysvals.vprint('MCELOG Data:') | ||
5749 | for line in data.mcelog.split('\n'): | ||
5750 | sysvals.vprint(' %s' % line) | ||
5751 | if data.turbostat: | ||
5752 | idx, s = 0, 'Turbostat:\n ' | ||
5753 | for val in data.turbostat.split('|'): | ||
5754 | idx += len(val) + 1 | ||
5755 | if idx >= 80: | ||
5756 | idx = 0 | ||
5757 | s += '\n ' | ||
5758 | s += val + ' ' | ||
5759 | sysvals.vprint(s) | ||
5393 | if data.battery: | 5760 | if data.battery: |
5394 | a1, c1, a2, c2 = data.battery | 5761 | a1, c1, a2, c2 = data.battery |
5395 | s = 'Battery:\n Before - AC: %s, Charge: %d\n After - AC: %s, Charge: %d' % \ | 5762 | s = 'Battery:\n Before - AC: %s, Charge: %d\n After - AC: %s, Charge: %d' % \ |
5396 | (a1, int(c1), a2, int(c2)) | 5763 | (a1, int(c1), a2, int(c2)) |
5397 | sysvals.vprint(s) | 5764 | sysvals.vprint(s) |
5765 | if data.wifi: | ||
5766 | w = data.wifi.replace('|', ' ').split(',') | ||
5767 | s = 'Wifi:\n Before %s\n After %s' % \ | ||
5768 | (w[0], w[1]) | ||
5769 | sysvals.vprint(s) | ||
5398 | data.printDetails() | 5770 | data.printDetails() |
5399 | if sysvals.cgdump: | 5771 | if sysvals.cgdump: |
5400 | for data in testruns: | 5772 | for data in testruns: |
@@ -5418,12 +5790,15 @@ def processData(live=False): | |||
5418 | # Function: rerunTest | 5790 | # Function: rerunTest |
5419 | # Description: | 5791 | # Description: |
5420 | # generate an output from an existing set of ftrace/dmesg logs | 5792 | # generate an output from an existing set of ftrace/dmesg logs |
5421 | def rerunTest(): | 5793 | def rerunTest(htmlfile=''): |
5422 | if sysvals.ftracefile: | 5794 | if sysvals.ftracefile: |
5423 | doesTraceLogHaveTraceEvents() | 5795 | doesTraceLogHaveTraceEvents() |
5424 | if not sysvals.dmesgfile and not sysvals.usetraceevents: | 5796 | if not sysvals.dmesgfile and not sysvals.usetraceevents: |
5425 | doError('recreating this html output requires a dmesg file') | 5797 | doError('recreating this html output requires a dmesg file') |
5426 | sysvals.setOutputFile() | 5798 | if htmlfile: |
5799 | sysvals.htmlfile = htmlfile | ||
5800 | else: | ||
5801 | sysvals.setOutputFile() | ||
5427 | if os.path.exists(sysvals.htmlfile): | 5802 | if os.path.exists(sysvals.htmlfile): |
5428 | if not os.path.isfile(sysvals.htmlfile): | 5803 | if not os.path.isfile(sysvals.htmlfile): |
5429 | doError('a directory already exists with this name: %s' % sysvals.htmlfile) | 5804 | doError('a directory already exists with this name: %s' % sysvals.htmlfile) |
@@ -5442,14 +5817,18 @@ def runTest(n=0): | |||
5442 | sysvals.initTestOutput('suspend') | 5817 | sysvals.initTestOutput('suspend') |
5443 | 5818 | ||
5444 | # execute the test | 5819 | # execute the test |
5445 | executeSuspend() | 5820 | testdata = executeSuspend() |
5446 | sysvals.cleanupFtrace() | 5821 | sysvals.cleanupFtrace() |
5447 | if sysvals.skiphtml: | 5822 | if sysvals.skiphtml: |
5448 | sysvals.sudoUserchown(sysvals.testdir) | 5823 | sysvals.sudoUserchown(sysvals.testdir) |
5449 | return | 5824 | return |
5450 | testruns, stamp = processData(True) | 5825 | if not testdata[0]['error']: |
5451 | for data in testruns: | 5826 | testruns, stamp = processData(True) |
5452 | del data | 5827 | for data in testruns: |
5828 | del data | ||
5829 | else: | ||
5830 | stamp = testdata[0] | ||
5831 | |||
5453 | sysvals.sudoUserchown(sysvals.testdir) | 5832 | sysvals.sudoUserchown(sysvals.testdir) |
5454 | sysvals.outputResult(stamp, n) | 5833 | sysvals.outputResult(stamp, n) |
5455 | if 'error' in stamp: | 5834 | if 'error' in stamp: |
@@ -5479,10 +5858,13 @@ def find_in_html(html, start, end, firstonly=True): | |||
5479 | return '' | 5858 | return '' |
5480 | return out | 5859 | return out |
5481 | 5860 | ||
5482 | def data_from_html(file, outpath, devlist=False): | 5861 | def data_from_html(file, outpath, issues, fulldetail=False): |
5483 | html = open(file, 'r').read() | 5862 | html = open(file, 'r').read() |
5863 | sysvals.htmlfile = os.path.relpath(file, outpath) | ||
5864 | # extract general info | ||
5484 | suspend = find_in_html(html, 'Kernel Suspend', 'ms') | 5865 | suspend = find_in_html(html, 'Kernel Suspend', 'ms') |
5485 | resume = find_in_html(html, 'Kernel Resume', 'ms') | 5866 | resume = find_in_html(html, 'Kernel Resume', 'ms') |
5867 | sysinfo = find_in_html(html, '<div class="stamp sysinfo">', '</div>') | ||
5486 | line = find_in_html(html, '<div class="stamp">', '</div>') | 5868 | line = find_in_html(html, '<div class="stamp">', '</div>') |
5487 | stmp = line.split() | 5869 | stmp = line.split() |
5488 | if not suspend or not resume or len(stmp) != 8: | 5870 | if not suspend or not resume or len(stmp) != 8: |
@@ -5491,6 +5873,7 @@ def data_from_html(file, outpath, devlist=False): | |||
5491 | dt = datetime.strptime(' '.join(stmp[3:]), '%B %d %Y, %I:%M:%S %p') | 5873 | dt = datetime.strptime(' '.join(stmp[3:]), '%B %d %Y, %I:%M:%S %p') |
5492 | except: | 5874 | except: |
5493 | return False | 5875 | return False |
5876 | sysvals.hostname = stmp[0] | ||
5494 | tstr = dt.strftime('%Y/%m/%d %H:%M:%S') | 5877 | tstr = dt.strftime('%Y/%m/%d %H:%M:%S') |
5495 | error = find_in_html(html, '<table class="testfail"><tr><td>', '</td>') | 5878 | error = find_in_html(html, '<table class="testfail"><tr><td>', '</td>') |
5496 | if error: | 5879 | if error: |
@@ -5501,13 +5884,45 @@ def data_from_html(file, outpath, devlist=False): | |||
5501 | result = 'fail' | 5884 | result = 'fail' |
5502 | else: | 5885 | else: |
5503 | result = 'pass' | 5886 | result = 'pass' |
5887 | # extract error info | ||
5504 | ilist = [] | 5888 | ilist = [] |
5505 | e = find_in_html(html, 'class="err"[\w=":;\.%\- ]*>', '→</div>', False) | 5889 | extra = dict() |
5506 | for i in list(set(e)): | 5890 | log = find_in_html(html, '<div id="dmesglog" style="display:none;">', |
5507 | ilist.append('%sx%d' % (i, e.count(i)) if e.count(i) > 1 else i) | 5891 | '</div>').strip() |
5892 | if log: | ||
5893 | d = Data(0) | ||
5894 | d.end = 999999999 | ||
5895 | d.dmesgtext = log.split('\n') | ||
5896 | msglist = d.extractErrorInfo() | ||
5897 | for msg in msglist: | ||
5898 | sysvals.errorSummary(issues, msg) | ||
5899 | if stmp[2] == 'freeze': | ||
5900 | extra = d.turbostatInfo() | ||
5901 | elist = dict() | ||
5902 | for dir in d.errorinfo: | ||
5903 | for err in d.errorinfo[dir]: | ||
5904 | if err[0] not in elist: | ||
5905 | elist[err[0]] = 0 | ||
5906 | elist[err[0]] += 1 | ||
5907 | for i in elist: | ||
5908 | ilist.append('%sx%d' % (i, elist[i]) if elist[i] > 1 else i) | ||
5508 | low = find_in_html(html, 'freeze time: <b>', ' ms</b>') | 5909 | low = find_in_html(html, 'freeze time: <b>', ' ms</b>') |
5509 | if low and '|' in low: | 5910 | if low and '|' in low: |
5510 | ilist.append('FREEZEx%d' % len(low.split('|'))) | 5911 | issue = 'FREEZEx%d' % len(low.split('|')) |
5912 | match = [i for i in issues if i['match'] == issue] | ||
5913 | if len(match) > 0: | ||
5914 | match[0]['count'] += 1 | ||
5915 | if sysvals.hostname not in match[0]['urls']: | ||
5916 | match[0]['urls'][sysvals.hostname] = [sysvals.htmlfile] | ||
5917 | elif sysvals.htmlfile not in match[0]['urls'][sysvals.hostname]: | ||
5918 | match[0]['urls'][sysvals.hostname].append(sysvals.htmlfile) | ||
5919 | else: | ||
5920 | issues.append({ | ||
5921 | 'match': issue, 'count': 1, 'line': issue, | ||
5922 | 'urls': {sysvals.hostname: [sysvals.htmlfile]}, | ||
5923 | }) | ||
5924 | ilist.append(issue) | ||
5925 | # extract device info | ||
5511 | devices = dict() | 5926 | devices = dict() |
5512 | for line in html.split('\n'): | 5927 | for line in html.split('\n'): |
5513 | m = re.match(' *<div id=\"[a,0-9]*\" *title=\"(?P<title>.*)\" class=\"thread.*', line) | 5928 | m = re.match(' *<div id=\"[a,0-9]*\" *title=\"(?P<title>.*)\" class=\"thread.*', line) |
@@ -5519,82 +5934,98 @@ def data_from_html(file, outpath, devlist=False): | |||
5519 | name, time, phase = m.group('n'), m.group('t'), m.group('p') | 5934 | name, time, phase = m.group('n'), m.group('t'), m.group('p') |
5520 | if ' async' in name or ' sync' in name: | 5935 | if ' async' in name or ' sync' in name: |
5521 | name = ' '.join(name.split(' ')[:-1]) | 5936 | name = ' '.join(name.split(' ')[:-1]) |
5522 | d = phase.split('_')[0] | 5937 | if phase.startswith('suspend'): |
5938 | d = 'suspend' | ||
5939 | elif phase.startswith('resume'): | ||
5940 | d = 'resume' | ||
5941 | else: | ||
5942 | continue | ||
5523 | if d not in devices: | 5943 | if d not in devices: |
5524 | devices[d] = dict() | 5944 | devices[d] = dict() |
5525 | if name not in devices[d]: | 5945 | if name not in devices[d]: |
5526 | devices[d][name] = 0.0 | 5946 | devices[d][name] = 0.0 |
5527 | devices[d][name] += float(time) | 5947 | devices[d][name] += float(time) |
5528 | worst = {'suspend': {'name':'', 'time': 0.0}, | 5948 | # create worst device info |
5529 | 'resume': {'name':'', 'time': 0.0}} | 5949 | worst = dict() |
5530 | for d in devices: | 5950 | for d in ['suspend', 'resume']: |
5531 | if d not in worst: | 5951 | worst[d] = {'name':'', 'time': 0.0} |
5532 | worst[d] = dict() | 5952 | dev = devices[d] if d in devices else 0 |
5533 | dev = devices[d] | 5953 | if dev and len(dev.keys()) > 0: |
5534 | if len(dev.keys()) > 0: | ||
5535 | n = sorted(dev, key=dev.get, reverse=True)[0] | 5954 | n = sorted(dev, key=dev.get, reverse=True)[0] |
5536 | worst[d]['name'], worst[d]['time'] = n, dev[n] | 5955 | worst[d]['name'], worst[d]['time'] = n, dev[n] |
5537 | data = { | 5956 | data = { |
5538 | 'mode': stmp[2], | 5957 | 'mode': stmp[2], |
5539 | 'host': stmp[0], | 5958 | 'host': stmp[0], |
5540 | 'kernel': stmp[1], | 5959 | 'kernel': stmp[1], |
5960 | 'sysinfo': sysinfo, | ||
5541 | 'time': tstr, | 5961 | 'time': tstr, |
5542 | 'result': result, | 5962 | 'result': result, |
5543 | 'issues': ' '.join(ilist), | 5963 | 'issues': ' '.join(ilist), |
5544 | 'suspend': suspend, | 5964 | 'suspend': suspend, |
5545 | 'resume': resume, | 5965 | 'resume': resume, |
5966 | 'devlist': devices, | ||
5546 | 'sus_worst': worst['suspend']['name'], | 5967 | 'sus_worst': worst['suspend']['name'], |
5547 | 'sus_worsttime': worst['suspend']['time'], | 5968 | 'sus_worsttime': worst['suspend']['time'], |
5548 | 'res_worst': worst['resume']['name'], | 5969 | 'res_worst': worst['resume']['name'], |
5549 | 'res_worsttime': worst['resume']['time'], | 5970 | 'res_worsttime': worst['resume']['time'], |
5550 | 'url': os.path.relpath(file, outpath), | 5971 | 'url': sysvals.htmlfile, |
5551 | } | 5972 | } |
5552 | if devlist: | 5973 | for key in extra: |
5553 | data['devlist'] = devices | 5974 | data[key] = extra[key] |
5975 | if fulldetail: | ||
5976 | data['funclist'] = find_in_html(html, '<div title="', '" class="traceevent"', False) | ||
5554 | return data | 5977 | return data |
5555 | 5978 | ||
5979 | def genHtml(subdir): | ||
5980 | for dirname, dirnames, filenames in os.walk(subdir): | ||
5981 | sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = '' | ||
5982 | for filename in filenames: | ||
5983 | if(re.match('.*_dmesg.txt', filename)): | ||
5984 | sysvals.dmesgfile = os.path.join(dirname, filename) | ||
5985 | elif(re.match('.*_ftrace.txt', filename)): | ||
5986 | sysvals.ftracefile = os.path.join(dirname, filename) | ||
5987 | sysvals.setOutputFile() | ||
5988 | if sysvals.ftracefile and sysvals.htmlfile and \ | ||
5989 | not os.path.exists(sysvals.htmlfile): | ||
5990 | pprint('FTRACE: %s' % sysvals.ftracefile) | ||
5991 | if sysvals.dmesgfile: | ||
5992 | pprint('DMESG : %s' % sysvals.dmesgfile) | ||
5993 | rerunTest() | ||
5994 | |||
5556 | # Function: runSummary | 5995 | # Function: runSummary |
5557 | # Description: | 5996 | # Description: |
5558 | # create a summary of tests in a sub-directory | 5997 | # create a summary of tests in a sub-directory |
5559 | def runSummary(subdir, local=True, genhtml=False): | 5998 | def runSummary(subdir, local=True, genhtml=False): |
5560 | inpath = os.path.abspath(subdir) | 5999 | inpath = os.path.abspath(subdir) |
5561 | outpath = os.path.abspath('.') if local else inpath | 6000 | outpath = os.path.abspath('.') if local else inpath |
5562 | pprint('Generating a summary of folder "%s"' % inpath) | 6001 | pprint('Generating a summary of folder:\n %s' % inpath) |
5563 | if genhtml: | 6002 | if genhtml: |
5564 | for dirname, dirnames, filenames in os.walk(subdir): | 6003 | genHtml(subdir) |
5565 | sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = '' | 6004 | issues = [] |
5566 | for filename in filenames: | ||
5567 | if(re.match('.*_dmesg.txt', filename)): | ||
5568 | sysvals.dmesgfile = os.path.join(dirname, filename) | ||
5569 | elif(re.match('.*_ftrace.txt', filename)): | ||
5570 | sysvals.ftracefile = os.path.join(dirname, filename) | ||
5571 | sysvals.setOutputFile() | ||
5572 | if sysvals.ftracefile and sysvals.htmlfile and \ | ||
5573 | not os.path.exists(sysvals.htmlfile): | ||
5574 | pprint('FTRACE: %s' % sysvals.ftracefile) | ||
5575 | if sysvals.dmesgfile: | ||
5576 | pprint('DMESG : %s' % sysvals.dmesgfile) | ||
5577 | rerunTest() | ||
5578 | testruns = [] | 6005 | testruns = [] |
5579 | desc = {'host':[],'mode':[],'kernel':[]} | 6006 | desc = {'host':[],'mode':[],'kernel':[]} |
5580 | for dirname, dirnames, filenames in os.walk(subdir): | 6007 | for dirname, dirnames, filenames in os.walk(subdir): |
5581 | for filename in filenames: | 6008 | for filename in filenames: |
5582 | if(not re.match('.*.html', filename)): | 6009 | if(not re.match('.*.html', filename)): |
5583 | continue | 6010 | continue |
5584 | data = data_from_html(os.path.join(dirname, filename), outpath) | 6011 | data = data_from_html(os.path.join(dirname, filename), outpath, issues) |
5585 | if(not data): | 6012 | if(not data): |
5586 | continue | 6013 | continue |
5587 | testruns.append(data) | 6014 | testruns.append(data) |
5588 | for key in desc: | 6015 | for key in desc: |
5589 | if data[key] not in desc[key]: | 6016 | if data[key] not in desc[key]: |
5590 | desc[key].append(data[key]) | 6017 | desc[key].append(data[key]) |
5591 | outfile = os.path.join(outpath, 'summary.html') | 6018 | pprint('Summary files:') |
5592 | pprint('Summary file: %s' % outfile) | ||
5593 | if len(desc['host']) == len(desc['mode']) == len(desc['kernel']) == 1: | 6019 | if len(desc['host']) == len(desc['mode']) == len(desc['kernel']) == 1: |
5594 | title = '%s %s %s' % (desc['host'][0], desc['kernel'][0], desc['mode'][0]) | 6020 | title = '%s %s %s' % (desc['host'][0], desc['kernel'][0], desc['mode'][0]) |
5595 | else: | 6021 | else: |
5596 | title = inpath | 6022 | title = inpath |
5597 | createHTMLSummarySimple(testruns, outfile, title) | 6023 | createHTMLSummarySimple(testruns, os.path.join(outpath, 'summary.html'), title) |
6024 | pprint(' summary.html - tabular list of test data found') | ||
6025 | createHTMLDeviceSummary(testruns, os.path.join(outpath, 'summary-devices.html'), title) | ||
6026 | pprint(' summary-devices.html - kernel device list sorted by total execution time') | ||
6027 | createHTMLIssuesSummary(testruns, issues, os.path.join(outpath, 'summary-issues.html'), title) | ||
6028 | pprint(' summary-issues.html - kernel issues found sorted by frequency') | ||
5598 | 6029 | ||
5599 | # Function: checkArgBool | 6030 | # Function: checkArgBool |
5600 | # Description: | 6031 | # Description: |
@@ -5839,6 +6270,7 @@ def printHelp(): | |||
5839 | ' default: suspend-{date}-{time}\n'\ | 6270 | ' default: suspend-{date}-{time}\n'\ |
5840 | ' -rtcwake t Wakeup t seconds after suspend, set t to "off" to disable (default: 15)\n'\ | 6271 | ' -rtcwake t Wakeup t seconds after suspend, set t to "off" to disable (default: 15)\n'\ |
5841 | ' -addlogs Add the dmesg and ftrace logs to the html output\n'\ | 6272 | ' -addlogs Add the dmesg and ftrace logs to the html output\n'\ |
6273 | ' -turbostat Use turbostat to execute the command in freeze mode (default: disabled)\n'\ | ||
5842 | ' -srgap Add a visible gap in the timeline between sus/res (default: disabled)\n'\ | 6274 | ' -srgap Add a visible gap in the timeline between sus/res (default: disabled)\n'\ |
5843 | ' -skiphtml Run the test and capture the trace logs, but skip the timeline (default: disabled)\n'\ | 6275 | ' -skiphtml Run the test and capture the trace logs, but skip the timeline (default: disabled)\n'\ |
5844 | ' -result fn Export a results table to a text file for parsing.\n'\ | 6276 | ' -result fn Export a results table to a text file for parsing.\n'\ |
@@ -5860,6 +6292,7 @@ def printHelp(): | |||
5860 | ' be created in a new subdirectory with a summary page.\n'\ | 6292 | ' be created in a new subdirectory with a summary page.\n'\ |
5861 | ' [debug]\n'\ | 6293 | ' [debug]\n'\ |
5862 | ' -f Use ftrace to create device callgraphs (default: disabled)\n'\ | 6294 | ' -f Use ftrace to create device callgraphs (default: disabled)\n'\ |
6295 | ' -ftop Use ftrace on the top level call: "%s" (default: disabled)\n'\ | ||
5863 | ' -maxdepth N limit the callgraph data to N call levels (default: 0=all)\n'\ | 6296 | ' -maxdepth N limit the callgraph data to N call levels (default: 0=all)\n'\ |
5864 | ' -expandcg pre-expand the callgraph data in the html output (default: disabled)\n'\ | 6297 | ' -expandcg pre-expand the callgraph data in the html output (default: disabled)\n'\ |
5865 | ' -fadd file Add functions to be graphed in the timeline from a list in a text file\n'\ | 6298 | ' -fadd file Add functions to be graphed in the timeline from a list in a text file\n'\ |
@@ -5879,6 +6312,7 @@ def printHelp(): | |||
5879 | ' -status Test to see if the system is enabled to run this tool\n'\ | 6312 | ' -status Test to see if the system is enabled to run this tool\n'\ |
5880 | ' -fpdt Print out the contents of the ACPI Firmware Performance Data Table\n'\ | 6313 | ' -fpdt Print out the contents of the ACPI Firmware Performance Data Table\n'\ |
5881 | ' -battery Print out battery info (if available)\n'\ | 6314 | ' -battery Print out battery info (if available)\n'\ |
6315 | ' -wifi Print out wifi connection info (if wireless-tools and device exists)\n'\ | ||
5882 | ' -x<mode> Test xset by toggling the given mode (on/off/standby/suspend)\n'\ | 6316 | ' -x<mode> Test xset by toggling the given mode (on/off/standby/suspend)\n'\ |
5883 | ' -sysinfo Print out system info extracted from BIOS\n'\ | 6317 | ' -sysinfo Print out system info extracted from BIOS\n'\ |
5884 | ' -devinfo Print out the pm settings of all devices which support runtime suspend\n'\ | 6318 | ' -devinfo Print out the pm settings of all devices which support runtime suspend\n'\ |
@@ -5888,7 +6322,7 @@ def printHelp(): | |||
5888 | ' [redo]\n'\ | 6322 | ' [redo]\n'\ |
5889 | ' -ftrace ftracefile Create HTML output using ftrace input (used with -dmesg)\n'\ | 6323 | ' -ftrace ftracefile Create HTML output using ftrace input (used with -dmesg)\n'\ |
5890 | ' -dmesg dmesgfile Create HTML output using dmesg (used with -ftrace)\n'\ | 6324 | ' -dmesg dmesgfile Create HTML output using dmesg (used with -ftrace)\n'\ |
5891 | '' % (sysvals.title, sysvals.version, sysvals.suspendmode)) | 6325 | '' % (sysvals.title, sysvals.version, sysvals.suspendmode, sysvals.ftopfunc)) |
5892 | return True | 6326 | return True |
5893 | 6327 | ||
5894 | # ----------------- MAIN -------------------- | 6328 | # ----------------- MAIN -------------------- |
@@ -5898,7 +6332,7 @@ if __name__ == '__main__': | |||
5898 | cmd = '' | 6332 | cmd = '' |
5899 | simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall', | 6333 | simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall', |
5900 | '-devinfo', '-status', '-battery', '-xon', '-xoff', '-xstandby', | 6334 | '-devinfo', '-status', '-battery', '-xon', '-xoff', '-xstandby', |
5901 | '-xsuspend', '-xinit', '-xreset', '-xstat'] | 6335 | '-xsuspend', '-xinit', '-xreset', '-xstat', '-wifi'] |
5902 | if '-f' in sys.argv: | 6336 | if '-f' in sys.argv: |
5903 | sysvals.cgskip = sysvals.configFile('cgskip.txt') | 6337 | sysvals.cgskip = sysvals.configFile('cgskip.txt') |
5904 | # loop through the command line arguments | 6338 | # loop through the command line arguments |
@@ -5930,6 +6364,10 @@ if __name__ == '__main__': | |||
5930 | sysvals.postdelay = getArgInt('-postdelay', args, 0, 60000) | 6364 | sysvals.postdelay = getArgInt('-postdelay', args, 0, 60000) |
5931 | elif(arg == '-f'): | 6365 | elif(arg == '-f'): |
5932 | sysvals.usecallgraph = True | 6366 | sysvals.usecallgraph = True |
6367 | elif(arg == '-ftop'): | ||
6368 | sysvals.usecallgraph = True | ||
6369 | sysvals.ftop = True | ||
6370 | sysvals.usekprobes = False | ||
5933 | elif(arg == '-skiphtml'): | 6371 | elif(arg == '-skiphtml'): |
5934 | sysvals.skiphtml = True | 6372 | sysvals.skiphtml = True |
5935 | elif(arg == '-cgdump'): | 6373 | elif(arg == '-cgdump'): |
@@ -5940,10 +6378,16 @@ if __name__ == '__main__': | |||
5940 | genhtml = True | 6378 | genhtml = True |
5941 | elif(arg == '-addlogs'): | 6379 | elif(arg == '-addlogs'): |
5942 | sysvals.dmesglog = sysvals.ftracelog = True | 6380 | sysvals.dmesglog = sysvals.ftracelog = True |
6381 | elif(arg == '-nologs'): | ||
6382 | sysvals.dmesglog = sysvals.ftracelog = False | ||
5943 | elif(arg == '-addlogdmesg'): | 6383 | elif(arg == '-addlogdmesg'): |
5944 | sysvals.dmesglog = True | 6384 | sysvals.dmesglog = True |
5945 | elif(arg == '-addlogftrace'): | 6385 | elif(arg == '-addlogftrace'): |
5946 | sysvals.ftracelog = True | 6386 | sysvals.ftracelog = True |
6387 | elif(arg == '-turbostat'): | ||
6388 | sysvals.tstat = True | ||
6389 | if not sysvals.haveTurbostat(): | ||
6390 | doError('Turbostat command not found') | ||
5947 | elif(arg == '-verbose'): | 6391 | elif(arg == '-verbose'): |
5948 | sysvals.verbose = True | 6392 | sysvals.verbose = True |
5949 | elif(arg == '-proc'): | 6393 | elif(arg == '-proc'): |
@@ -6013,6 +6457,12 @@ if __name__ == '__main__': | |||
6013 | except: | 6457 | except: |
6014 | doError('No callgraph functions supplied', True) | 6458 | doError('No callgraph functions supplied', True) |
6015 | sysvals.setCallgraphFilter(val) | 6459 | sysvals.setCallgraphFilter(val) |
6460 | elif(arg == '-skipkprobe'): | ||
6461 | try: | ||
6462 | val = args.next() | ||
6463 | except: | ||
6464 | doError('No kprobe functions supplied', True) | ||
6465 | sysvals.skipKprobes(val) | ||
6016 | elif(arg == '-cgskip'): | 6466 | elif(arg == '-cgskip'): |
6017 | try: | 6467 | try: |
6018 | val = args.next() | 6468 | val = args.next() |
@@ -6151,7 +6601,7 @@ if __name__ == '__main__': | |||
6151 | elif(cmd == 'devinfo'): | 6601 | elif(cmd == 'devinfo'): |
6152 | deviceInfo() | 6602 | deviceInfo() |
6153 | elif(cmd == 'modes'): | 6603 | elif(cmd == 'modes'): |
6154 | print getModes() | 6604 | pprint(getModes()) |
6155 | elif(cmd == 'flist'): | 6605 | elif(cmd == 'flist'): |
6156 | sysvals.getFtraceFilterFunctions(True) | 6606 | sysvals.getFtraceFilterFunctions(True) |
6157 | elif(cmd == 'flistall'): | 6607 | elif(cmd == 'flistall'): |
@@ -6163,11 +6613,18 @@ if __name__ == '__main__': | |||
6163 | ret = displayControl(cmd[1:]) | 6613 | ret = displayControl(cmd[1:]) |
6164 | elif(cmd == 'xstat'): | 6614 | elif(cmd == 'xstat'): |
6165 | pprint('Display Status: %s' % displayControl('stat').upper()) | 6615 | pprint('Display Status: %s' % displayControl('stat').upper()) |
6616 | elif(cmd == 'wifi'): | ||
6617 | out = sysvals.checkWifi() | ||
6618 | if 'device' not in out: | ||
6619 | pprint('WIFI interface not found') | ||
6620 | else: | ||
6621 | for key in sorted(out): | ||
6622 | pprint('%6s: %s' % (key.upper(), out[key])) | ||
6166 | sys.exit(ret) | 6623 | sys.exit(ret) |
6167 | 6624 | ||
6168 | # if instructed, re-analyze existing data files | 6625 | # if instructed, re-analyze existing data files |
6169 | if(sysvals.notestrun): | 6626 | if(sysvals.notestrun): |
6170 | stamp = rerunTest() | 6627 | stamp = rerunTest(sysvals.outdir) |
6171 | sysvals.outputResult(stamp) | 6628 | sysvals.outputResult(stamp) |
6172 | sys.exit(0) | 6629 | sys.exit(0) |
6173 | 6630 | ||
@@ -6204,7 +6661,7 @@ if __name__ == '__main__': | |||
6204 | s = 'suspend-x%d' % sysvals.multitest['count'] | 6661 | s = 'suspend-x%d' % sysvals.multitest['count'] |
6205 | sysvals.outdir = datetime.now().strftime(s+'-%y%m%d-%H%M%S') | 6662 | sysvals.outdir = datetime.now().strftime(s+'-%y%m%d-%H%M%S') |
6206 | if not os.path.isdir(sysvals.outdir): | 6663 | if not os.path.isdir(sysvals.outdir): |
6207 | os.mkdir(sysvals.outdir) | 6664 | os.makedirs(sysvals.outdir) |
6208 | for i in range(sysvals.multitest['count']): | 6665 | for i in range(sysvals.multitest['count']): |
6209 | if(i != 0): | 6666 | if(i != 0): |
6210 | pprint('Waiting %d seconds...' % (sysvals.multitest['delay'])) | 6667 | pprint('Waiting %d seconds...' % (sysvals.multitest['delay'])) |