blob: 3d6ce60521faa62c3b59aab17d282c86f8e9f21e [file] [log] [blame]
Zhang Hongbo81a450d2012-03-23 12:01:11 +08001#!/bin/bash
2#
3# Script to automate suspend / resume
4#
5# Copyright (C) 2008-2009 Canonical Ltd.
6#
7# Authors:
8# Michael Frey <michael.frey@canonical.com>
9# Andy Whitcroft <apw@canonical.com>
10#
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.
14#
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.
19#
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/>.
22
23#
24# Script to automate suspend / resume
25#
26# We set a RTC alarm that wakes the system back up and then sleep
27# for seconds before we go back to sleep.
28#
29# Changelog:
30#
31# Version for Linaro PM-QA:
32# - this script is edited and integrated into Linaro PM-QA
33# - hongbo.zhang@linaro.org, March, 2012
34#
35# V8:
36# - add a new suspend battery drain test
37# - track batteries disabling tests which require them automatically
38# - disable dbus tests when we have no primary user
39# - include the new power drain test in --full
40# - handle AC transitions better
41# - use minutes in messages where appropriate
42# - report AC transition failures
43# - only mention AC when we have batteries
44# - report results at the bottom for easy posting
45#
46# V7:
47# - add a --dry-run mode to simplify developement
48# - add a automation mode for checkbox integration
49# - add a new pm-suspend test
50# - record and restore timer_delay around the variable time test.
51#
52# V6:
53# - move an --enable/--disable interface for tests
54# - add --set to allow setting of approved parameters
55# - fix up prompting for interactive and non-interactive tests
56# - supply a sensible default for testing on servers (apw, kirkland)
57#
58# V5:
59# - send dbus messages as the original user
60# - stop clearing the dmesg as we go
61# - stop using trace generally as this affects the wakeups
62# - do a single dbus test then move to pm-suspend to avoid screensaver
63# - timeout waiting for a suspend to complete catching failure to go down
64#
65# V4:
66# - update the help output
67# - add --comprehensive to do AC related tests
68# - add --extensive to do a range of time related tests
69# - add --full to enable all harder tests
70# - add fallback to pm-suspend for Kbuntu
71# - collect dmesg output
72# - remove hwclock update
73#
74# V3:
75# - fix typo in fallback acpi interface
76# - when recording the RTC clock do not go direct
77# - pmi is now deprecated suspend using dbus
78#
79# V2:
80# - support newer rtc sysfs wakealarm interface
81# - move to using pmi action suspend
82# - allow the user to specify the number of iterations
83# - ensure we are running as root
84# - report the iterations to the user
85# - clean up the output and put it in a standard logfile
86# - add a descriptive warning and allow user cancel
87# - add tracing enable/disable
88# - fix logfile location
89# - add a failure cleanup mode
90# - make time sleep time and delay time configurable
91# - ensure the log directory exists
92# - clock will be fixed automatically on network connect
93# - default sleep before wakeup to 20s
94# - do not use dates after we have corrupted the clock
95# - sort out the copyright information
96# - we do not have any failure cleanup currently
97#
98# V1:
99# - add the suspend test scripts
100#
101P="test-suspend"
102
103LOGDIR='/var/lib/pm-utils'
104LOGFILE="$LOGDIR/stress.log"
105
106setup_wakeup_timer ()
107{
108 timeout="$1"
109
110 #
111 # Request wakeup from the RTC or ACPI alarm timers. Set the timeout
112 # at 'now' + $timeout seconds.
113 #
114 ctl='/sys/class/rtc/rtc0/wakealarm'
115 if [ -f "$ctl" ]; then
116 # Cancel any outstanding timers.
117 echo "0" >"$ctl"
118 # rtcN/wakealarm can use relative time in seconds
119 echo "+$timeout" >"$ctl"
120 return 0
121 fi
122 ctl='/proc/acpi/alarm'
123 if [ -f "$ctl" ]; then
124 echo `date '+%F %H:%M:%S' -d '+ '$timeout' seconds'` >"$ctl"
125 return 0
126 fi
127
128 echo "no method to awaken machine automatically" 1>&2
129 exit 1
130}
131
132suspend_system ()
133{
134 if [ "$dry" -eq 1 ]; then
135 echo "DRY-RUN: suspend machine for $timer_sleep"
136 sleep 1
137 return
138 fi
139
140 setup_wakeup_timer "$timer_sleep"
141
142 dmesg >"$LOGFILE.dmesg.A"
143
144 # Send a dbus message to initiate Suspend.
145 if [ "$suspend_dbus" -eq 1 ]; then
146 sudo -u $SUDO_USER dbus-send --session --type=method_call \
147 --dest=org.freedesktop.PowerManagement \
148 /org/freedesktop/PowerManagement \
149 org.freedesktop.PowerManagement.Suspend \
150 >> "$LOGFILE" || {
151 ECHO "$P FAILED: dbus suspend failed" 1>&2
152 return 1
153 }
154 else
155 pm-suspend >> "$LOGFILE"
156 fi
157
158 # Wait on the machine coming back up -- pulling the dmesg over.
159 echo "v---" >>"$LOGFILE"
160 retry=30
161 while [ "$retry" -gt 0 ]; do
162 let "retry=$retry-1"
163
164 # Accumulate the dmesg delta.
165 dmesg >"$LOGFILE.dmesg.B"
166 diff "$LOGFILE.dmesg.A" "$LOGFILE.dmesg.B" | \
167 grep '^>' >"$LOGFILE.dmesg"
168 mv "$LOGFILE.dmesg.B" "$LOGFILE.dmesg.A"
169
170 echo "Waiting for suspend to complete $retry to go ..." \
171 >> "$LOGFILE"
172 cat "$LOGFILE.dmesg" >> "$LOGFILE"
173
174 if [ "`grep -c 'Back to C!' $LOGFILE.dmesg`" -ne 0 ]; then
175 break;
176 fi
177 sleep 1
178 done
179 echo "^---" >>"$LOGFILE"
180 rm -f "$LOGFILE.dmesg"*
181 if [ "$retry" -eq 0 ]; then
182 ECHO "$P SUSPEND FAILED, did not go to sleep" 1>&2
183 return 1
184 fi
185}
186
187delay_system ()
188{
189 if [ "$dry" -eq 1 ]; then
190 echo "DRY-RUN: stay awake for $timer_delay"
191 sleep 1
192 return
193 fi
194
195 #
196 # wait for $timer_delay seconds after system resume from S3
197 #
198 ECHO "wait for $timer_delay seconds..."
199 sleep $timer_delay
200}
201
202ECHO ()
203{
204 echo "$@" | tee -a "$LOGFILE"
205}
206
207
208enable_trace()
209{
210 echo 1 > '/sys/power/pm_trace'
211}
212
213disable_trace()
214{
215 echo 0 > '/sys/power/pm_trace'
216}
217
218# Battery
219battery_count()
220{
221 cat /proc/acpi/battery/*/state 2>/dev/null | \
222 awk '
223 BEGIN { total = 0 }
224 /present:.*yes/ { total += 1 }
225 END { print total }
226 '
227}
228battery_capacity()
229{
230 cat /proc/acpi/battery/*/state 2>/dev/null | \
231 awk '
232 BEGIN { total = 0 }
233 /remaining capacity:/ { total += $3 }
234 END { print total }
235 '
236}
237
238
239# Options helpers.
240chk_test ()
241{
242 if ! declare -p "test_$1" 2>/dev/null 1>&2; then
243 echo "$P: $1: test unknown" 1>&2
244 exit 1
245 fi
246}
247handle_set ()
248{
249 stmt=`echo "$1" | sed -e 's/\./_/g'`
250
251 test="${stmt%%_*}"
252 var="${stmt%%=*}"
253
254 chk_test "$test"
255 if ! declare -p "args_$var" 2>/dev/null 1>&2; then
256 echo "$P: $var: test variable unknown" 1>&2
257 exit 1
258 fi
259
260 RET="args_$stmt"
261}
262chk_number() {
263 eval "val=\"\$$1\""
264 let num="0+$val"
265 if [ "$val" != "$num" ]; then
266 name=`echo "$1" | sed -e 's/args_//' -e 's/_/./'`
267 echo "$P: $name: $val: non-numeric value" 1>&2
268 exit 1
269 fi
270}
271
272# Options handling.
273dry=0
274auto=0
275timer_sleep=20
276timer_delay=10
277
278test_dbus=0
279test_pmsuspend=0
280test_ac=0
281test_timed=0
282test_repeat=0
283args_repeat_iterations=10
284test_power=0
285args_power_sleep=600
286
287chk_number "args_repeat_iterations"
288chk_number "args_power_sleep"
289
290battery_count=`battery_count`
291
292suspend_dbus=0
293
294# Check we are running as root as we are going to fiddle with the clock
295# and use the rtc wakeups.
296id=`id -u`
297if [ "$id" -ne 0 ]; then
298 echo "ERROR: must be run as root to perform this test, use sudo:" 1>&2
299 echo " sudo $0 $@" 1>&2
300 exit 1
301fi
302
303ac_needed=-1
304ac_is=-1
305ac_becomes=-1
306ac_required()
307{
308 ac_check
309
310 ac_needed="$1"
311 ac_becomes="$1"
312}
313ac_transitions()
314{
315 ac_check
316
317 ac_needed="$1"
318 ac_becomes="$2"
319}
320ac_online()
321{
322 cat /proc/acpi/ac_adapter/*/state 2>/dev/null | \
323 awk '
324 BEGIN { online = 0; offline = 0 }
325 /on-line/ { online = 1 }
326 /off-line/ { offline = 1 }
327 END {
328 if (online) {
329 print "1"
330 } else if (offline) {
331 print "0"
332 } else {
333 print "-1"
334 }
335 }
336 '
337}
338ac_check()
339{
340 typeset ac_current=`ac_online`
341
342 if [ "$ac_becomes" -ne -1 -a "$ac_current" -ne -1 -a \
343 "$ac_current" -ne "$ac_becomes" ]; then
344 ECHO "*** WARNING: AC power not in expected state" \
345 "($ac_becomes) after test"
346 fi
347 ac_is="$ac_becomes"
348}
349
350phase=0
351phase_first=1
352phase_interactive=1
353phase()
354{
355 typeset sleep
356
357 let phase="$phase+1"
358
359 if [ "$battery_count" -ne 0 -a "$ac_needed" -ne "$ac_is" ]; then
360 case "$ac_needed" in
361 0) echo "*** please ensure your AC cord is detached" ;;
362 1) echo "*** please ensure your AC cord is attached" ;;
363 esac
364 ac_is="$ac_needed"
365 fi
366
367 if [ "$timer_sleep" -gt 60 ]; then
368 let sleep="$timer_sleep / 60"
369 sleep="$sleep minutes"
370 else
371 sleep="$timer_sleep seconds"
372 fi
373 echo "*** machine will suspend for $sleep"
374
375 if [ "$auto" -eq 1 ]; then
376 :
377
378 elif [ "$phase_interactive" -eq 1 ]; then
379 echo "*** press return when ready"
380 read x
381
382 elif [ "$phase_first" -eq 1 ]; then
383 echo "*** NOTE: there will be no further user interaction from this point"
384 echo "*** press return when ready"
385 phase_first=0
386 read x
387 fi
388}
389
390# Ensure the log directory exists.
391mkdir -p "$LOGDIR"
392