3
# Script to automate suspend / resume
5
# Copyright (C) 2008-2009 Canonical Ltd.
8
# Michael Frey <michael.frey@canonical.com>
9
# Andy Whitcroft <apw@canonical.com>
11
# This program is free software: you can redistribute it and/or modify
12
# it under the terms of the GNU General Public License version 2,
13
# as published by the Free Software Foundation.
15
# This program is distributed in the hope that it will be useful,
16
# but WITHOUT ANY WARRANTY; without even the implied warranty of
17
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
# GNU General Public License for more details.
20
# You should have received a copy of the GNU General Public License
21
# along with this program. If not, see <http://www.gnu.org/licenses/>.
24
# Script to automate suspend / resume
26
# We set a RTC alarm that wakes the system back up and then sleep
27
# for seconds before we go back to sleep.
32
# - add a new suspend battery drain test
33
# - track batteries disabling tests which require them automatically
34
# - disable dbus tests when we have no primary user
35
# - include the new power drain test in --full
36
# - handle AC transitions better
37
# - use minutes in messages where appropriate
38
# - report AC transition failures
39
# - only mention AC when we have batteries
40
# - report results at the bottom for easy posting
43
# - add a --dry-run mode to simplify developement
44
# - add a automation mode for checkbox integration
45
# - add a new pm-suspend test
46
# - record and restore timer_delay around the variable time test.
49
# - move an --enable/--disable interface for tests
50
# - add --set to allow setting of approved parameters
51
# - fix up prompting for interactive and non-interactive tests
52
# - supply a sensible default for testing on servers (apw, kirkland)
55
# - send dbus messages as the original user
56
# - stop clearing the dmesg as we go
57
# - stop using trace generally as this affects the wakeups
58
# - do a single dbus test then move to pm-suspend to avoid screensaver
59
# - timeout waiting for a suspend to complete catching failure to go down
62
# - update the help output
63
# - add --comprehensive to do AC related tests
64
# - add --extensive to do a range of time related tests
65
# - add --full to enable all harder tests
66
# - add fallback to pm-suspend for Kbuntu
67
# - collect dmesg output
68
# - remove hwclock update
71
# - fix typo in fallback acpi interface
72
# - when recording the RTC clock do not go direct
73
# - pmi is now deprecated suspend using dbus
76
# - support newer rtc sysfs wakealarm interface
77
# - move to using pmi action suspend
78
# - allow the user to specify the number of iterations
79
# - ensure we are running as root
80
# - report the iterations to the user
81
# - clean up the output and put it in a standard logfile
82
# - add a descriptive warning and allow user cancel
83
# - add tracing enable/disable
84
# - fix logfile location
85
# - add a failure cleanup mode
86
# - make time sleep time and delay time configurable
87
# - ensure the log directory exists
88
# - clock will be fixed automatically on network connect
89
# - default sleep before wakeup to 20s
90
# - do not use dates after we have corrupted the clock
91
# - sort out the copyright information
92
# - we do not have any failure cleanup currently
95
# - add the suspend test scripts
99
LOGDIR='/var/lib/pm-utils'
100
LOGFILE="$LOGDIR/stress.log"
102
setup_wakeup_timer ()
107
# Request wakeup from the RTC or ACPI alarm timers. Set the timeout
108
# at 'now' + $timeout seconds.
110
ctl='/sys/class/rtc/rtc0/wakealarm'
111
if [ -f "$ctl" ]; then
112
time=`date '+%s' -d "+ $timeout seconds"`
113
# Cancel any outstanding timers.
115
# rtcN/wakealarm uses absolute time in seconds
119
ctl='/proc/acpi/alarm'
120
if [ -f "$ctl" ]; then
121
echo `date '+%F %H:%M:%S' -d '+ '$timeout' seconds'` >"$ctl"
125
echo "no method to awaken machine automatically" 1>&2
131
if [ "$dry" -eq 1 ]; then
132
echo "DRY-RUN: suspend machine for $timer_sleep"
137
setup_wakeup_timer "$timer_sleep"
139
dmesg >"$LOGFILE.dmesg.A"
141
# Send a dbus message to initiate Suspend.
142
if [ "$suspend_dbus" -eq 1 ]; then
143
sudo -u $SUDO_USER dbus-send --session --type=method_call \
144
--dest=org.freedesktop.PowerManagement \
145
/org/freedesktop/PowerManagement \
146
org.freedesktop.PowerManagement.Suspend \
148
ECHO "FAILED: dbus suspend failed"
152
pm-suspend >> "$LOGFILE"
155
# Wait on the machine coming back up -- pulling the dmesg over.
156
echo "v---" >>"$LOGFILE"
158
while [ "$retry" -gt 0 ]; do
161
# Accumulate the dmesg delta.
162
dmesg >"$LOGFILE.dmesg.B"
163
diff "$LOGFILE.dmesg.A" "$LOGFILE.dmesg.B" | \
164
grep '^>' >"$LOGFILE.dmesg"
165
mv "$LOGFILE.dmesg.B" "$LOGFILE.dmesg.A"
167
echo "Waiting for suspend to complete $retry to go ..." \
169
cat "$LOGFILE.dmesg" >> "$LOGFILE"
171
if [ "`grep -c 'Back to C!' $LOGFILE.dmesg`" -ne 0 ]; then
176
echo "^---" >>"$LOGFILE"
177
rm -f "$LOGFILE.dmesg"*
178
if [ "$retry" -eq 0 ]; then
179
ECHO "SUSPEND FAILED, did not go to sleep"
185
if [ "$dry" -eq 1 ]; then
186
echo "DRY-RUN: stay awake for $timer_delay"
192
# wait for $timer_delay seconds after system resume from S3
194
ECHO "wait for $timer_delay seconds"
200
echo "$@" | tee -a "$LOGFILE"
207
ECHO "Suspend Test starting on $(date '+%F %H:%M:%S') ($TOTAL cycles)"
208
while [ "$CNT" -le "$TOTAL" ]
210
ECHO "Suspend iteration $CNT of $TOTAL"
212
suspend_system "$START"
217
ECHO "Suspend Test completed"
222
echo 1 > '/sys/power/pm_trace'
227
echo 0 > '/sys/power/pm_trace'
233
cat /proc/acpi/battery/*/state 2>/dev/null | \
236
/present:.*yes/ { total += 1 }
242
cat /proc/acpi/battery/*/state 2>/dev/null | \
245
/remaining capacity:/ { total += $3 }
255
Usage: $P [<options>]
257
--sleep <seconds> - how long the machine wait before waking
258
--delay <seconds> - default delay between iterations
260
--enable <test> - enable a specific test
261
--disable <test> - disable a specific test
262
--set <test>.<var>=<val> - set a test specific variable
263
dbus - perform a suspend via dbus
264
ac - perform tests involving removing ac power
265
timed - perform a variable timing test
266
repeat - perform a longer repeat test
267
.iterations - the number of iterations in the repeat
268
power - perform a battery consumption test
269
.sleep - how long to sleep
271
--full - run a basic set of tests
272
--server - run those test appropriate for a server
276
# We need TEMP as the `eval set --' would nuke the return value of getopt.
277
TEMP=`getopt -o '' -l dry-run,auto,,sleep:,delay:,enable:,disable:,set:,full,desktop,server -n "$P" -- "$@"`
278
if [ $? != 0 ] ; then
283
# Note the quotes around `$TEMP': they are essential!
289
if ! declare -p "test_$1" 2>/dev/null 1>&2; then
290
echo "$P: $1: test unknown" 1>&2
296
stmt=`echo "$1" | sed -e 's/\./_/g'`
302
if ! declare -p "args_$var" 2>/dev/null 1>&2; then
303
echo "$P: $var: test variable unknown" 1>&2
312
if [ "$val" != "$num" ]; then
313
name=`echo "$1" | sed -e 's/args_//' -e 's/_/./'`
314
echo "$P: $name: $val: non-numeric value" 1>&2
330
args_repeat_iterations=10
332
args_power_sleep=1200
337
--dry-run) dry=1; shift 1 ;;
338
--auto) auto=1; shift 1 ;;
339
--sleep) timer_sleep="$2"; shift 2 ;;
340
--delay) timer_delay="$2"; shift 2 ;;
341
--disable) chk_test "$2"; declare "test_$1=0"; shift 2 ;;
342
--enable) chk_test "$2"; declare "test_$2=1"; shift 2 ;;
343
--set) handle_set "$2"; declare "$RET"; shift 2 ;;
344
--desktop|--full) test_dbus=1; test_ac=1; test_timed=1;
345
test_power=1; shift 1 ;;
346
--server) test_timed=1; shift 1 ;;
348
*) echo "$1: ERROR"; exit 1 ;;
352
chk_number "args_repeat_iterations"
353
chk_number "args_power_sleep"
355
tests=`set | grep ^test_ | grep -c =1`
357
if [ "$#" -gt 1 ]; then
361
if [ "$tests" -eq 0 ]; then
363
echo "$P: no tests selected" 1>&2
367
battery_count=`battery_count`
373
# Check we are running as root as we are going to fiddle with the clock
374
# and use the rtc wakeups.
376
if [ "$id" -ne 0 ]; then
377
echo "ERROR: must be run as root to perform this test, use sudo:" 1>&2
378
echo " sudo $0 $@" 1>&2
401
cat /proc/acpi/ac_adapter/*/state 2>/dev/null | \
403
BEGIN { online = 0; offline = 0 }
404
/on-line/ { online = 1 }
405
/off-line/ { offline = 1 }
409
} else if (offline) {
419
typeset ac_current=`ac_online`
421
if [ "$ac_becomes" -ne -1 -a "$ac_current" -ne -1 -a \
422
"$ac_current" -ne "$ac_becomes" ]; then
423
ECHO "*** WARNING: AC power not in expected state" \
424
"($ac_becomes) after test"
439
echo "*** TEST $phase -- $1"
445
if [ "$battery_count" -ne 0 -a "$ac_needed" -ne "$ac_is" ]; then
447
0) echo "*** please ensure your AC cord is detached" ;;
448
1) echo "*** please ensure your AC cord is attached" ;;
453
if [ "$timer_sleep" -gt 60 ]; then
454
let sleep="$timer_sleep / 60"
455
sleep="$sleep minutes"
457
sleep="$timer_sleep seconds"
459
echo "*** machine will suspend for $sleep"
461
if [ "$auto" -eq 1 ]; then
464
elif [ "$phase_interactive" -eq 1 ]; then
465
echo "*** press return when ready"
468
elif [ "$phase_first" -eq 1 ]; then
469
echo "*** NOTE: there will be no further user interaction from this point"
470
echo "*** press return when ready"
477
[ "$auto" -eq 0 ] && cat - <<EOM
478
This script will attempt to suspend and resume your computer a number of times.
479
Should the machine fail to resume, first attempt to manually resume it. If
480
that fails power your system off and on which will generate an apport bug
481
report automatically.
483
Press CTRL-C now to abort testing ...
486
# Ensure the log directory exists.
490
if [ "$test_dbus" -eq 1 -a \
491
\( "$SUDO_USER" = "" -o "$SUDO_USER" = "root" \) ]; then
492
ECHO "*** no primary user (via sudo) dbus tests skipped ..."
493
elif [ "$test_dbus" -eq 1 ]; then
496
phase "suspend triggered via dbus message"
500
if [ "$test_pmsuspend" -eq 1 ]; then
502
phase "suspend triggered via pm-suspend"
505
if [ "$test_ac" -eq 1 -a "$battery_count" -eq 0 ]; then
506
ECHO "*** no BATTERY detected ac tests skipped ..."
507
elif [ "$test_ac" -eq 1 ]; then
509
phase "suspend with AC disconnected"
513
phase "suspend with AC connected"
517
phase "loss of AC while suspended" \
518
"please remove the AC cord while the machine is suspended"
522
phase "return of AC while suspended" \
523
"please insert the AC cord while the machine is suspended"
526
if [ "$test_power" -eq 1 -a "$battery_count" -eq 0 ]; then
527
ECHO "*** no BATTERY detected power test skipped ..."
528
elif [ "$test_power" -eq 1 ]; then
529
save_timer_sleep="$timer_sleep"
530
let timer_sleep="$args_power_sleep"
533
phase "battery drain during suspend" \
534
"calculates overall power drain during a long-term suspend"
537
date_before=`date +%s`
538
bat_before=`battery_capacity`
544
date_after=`date +%s`
545
bat_after=`battery_capacity`
547
# do the calculations
548
let consumed="$bat_before - $bat_after"
549
let elapsed="$date_after - $date_before"
550
let usage="($consumed * 60*60) / $elapsed"
553
ECHO "before: $bat_before mWh"
554
ECHO "after: $bat_after mWh"
555
ECHO "consumed: $consumed mW"
556
ECHO "sleep seconds: $elapsed sec"
557
ECHO "overall usage: $usage mW"
559
report_battery="$usage mW"
561
if [ $elapsed -lt 1200 ]
563
ECHO "WARNING: the suspend was less than 20 minutes"
564
ECHO " to get reliable numbers increase the sleep time"
565
report_battery="$report_battery (unreliable)"
568
timer_sleep="$save_timer_sleep"
572
if [ "$test_timed" -eq 1 ]; then
573
save_timer_delay="$timer_delay"
577
phase "30 iteration variable delay suspend/resume stress test"
578
while [ "$timer_delay" -gt 0 ]; do
579
echo "delay $timer_delay ..."
582
let timer_delay="$timer_delay - 2"
584
timer_delay="$save_timer_delay"
586
if [ "$test_repeat" -eq 1 ]; then
588
phase "basic $args_repeat_iterations iteration suspend/resume stress test"
589
run_suspend "$args_repeat_iterations"
595
# REPORT: final report stage.
599
if [ "$2" != "" ]; then
603
if [ "$auto" -eq 0 ]; then
605
echo "*** Please report your results on the Ubuntu WIKI:"
606
echo " https://wiki.ubuntu.com/KernelTeam/SuspendResumeTesting"
608
report_this "Battery Consumption:" "$report_battery"
611
# All suceessful, clean up.