blob: 3d6ce60521faa62c3b59aab17d282c86f8e9f21e [file] [log] [blame]
#!/bin/bash
#
# Script to automate suspend / resume
#
# Copyright (C) 2008-2009 Canonical Ltd.
#
# Authors:
# Michael Frey <michael.frey@canonical.com>
# Andy Whitcroft <apw@canonical.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2,
# as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Script to automate suspend / resume
#
# We set a RTC alarm that wakes the system back up and then sleep
# for seconds before we go back to sleep.
#
# Changelog:
#
# Version for Linaro PM-QA:
# - this script is edited and integrated into Linaro PM-QA
# - hongbo.zhang@linaro.org, March, 2012
#
# V8:
# - add a new suspend battery drain test
# - track batteries disabling tests which require them automatically
# - disable dbus tests when we have no primary user
# - include the new power drain test in --full
# - handle AC transitions better
# - use minutes in messages where appropriate
# - report AC transition failures
# - only mention AC when we have batteries
# - report results at the bottom for easy posting
#
# V7:
# - add a --dry-run mode to simplify developement
# - add a automation mode for checkbox integration
# - add a new pm-suspend test
# - record and restore timer_delay around the variable time test.
#
# V6:
# - move an --enable/--disable interface for tests
# - add --set to allow setting of approved parameters
# - fix up prompting for interactive and non-interactive tests
# - supply a sensible default for testing on servers (apw, kirkland)
#
# V5:
# - send dbus messages as the original user
# - stop clearing the dmesg as we go
# - stop using trace generally as this affects the wakeups
# - do a single dbus test then move to pm-suspend to avoid screensaver
# - timeout waiting for a suspend to complete catching failure to go down
#
# V4:
# - update the help output
# - add --comprehensive to do AC related tests
# - add --extensive to do a range of time related tests
# - add --full to enable all harder tests
# - add fallback to pm-suspend for Kbuntu
# - collect dmesg output
# - remove hwclock update
#
# V3:
# - fix typo in fallback acpi interface
# - when recording the RTC clock do not go direct
# - pmi is now deprecated suspend using dbus
#
# V2:
# - support newer rtc sysfs wakealarm interface
# - move to using pmi action suspend
# - allow the user to specify the number of iterations
# - ensure we are running as root
# - report the iterations to the user
# - clean up the output and put it in a standard logfile
# - add a descriptive warning and allow user cancel
# - add tracing enable/disable
# - fix logfile location
# - add a failure cleanup mode
# - make time sleep time and delay time configurable
# - ensure the log directory exists
# - clock will be fixed automatically on network connect
# - default sleep before wakeup to 20s
# - do not use dates after we have corrupted the clock
# - sort out the copyright information
# - we do not have any failure cleanup currently
#
# V1:
# - add the suspend test scripts
#
P="test-suspend"
LOGDIR='/var/lib/pm-utils'
LOGFILE="$LOGDIR/stress.log"
setup_wakeup_timer ()
{
timeout="$1"
#
# Request wakeup from the RTC or ACPI alarm timers. Set the timeout
# at 'now' + $timeout seconds.
#
ctl='/sys/class/rtc/rtc0/wakealarm'
if [ -f "$ctl" ]; then
# Cancel any outstanding timers.
echo "0" >"$ctl"
# rtcN/wakealarm can use relative time in seconds
echo "+$timeout" >"$ctl"
return 0
fi
ctl='/proc/acpi/alarm'
if [ -f "$ctl" ]; then
echo `date '+%F %H:%M:%S' -d '+ '$timeout' seconds'` >"$ctl"
return 0
fi
echo "no method to awaken machine automatically" 1>&2
exit 1
}
suspend_system ()
{
if [ "$dry" -eq 1 ]; then
echo "DRY-RUN: suspend machine for $timer_sleep"
sleep 1
return
fi
setup_wakeup_timer "$timer_sleep"
dmesg >"$LOGFILE.dmesg.A"
# Send a dbus message to initiate Suspend.
if [ "$suspend_dbus" -eq 1 ]; then
sudo -u $SUDO_USER dbus-send --session --type=method_call \
--dest=org.freedesktop.PowerManagement \
/org/freedesktop/PowerManagement \
org.freedesktop.PowerManagement.Suspend \
>> "$LOGFILE" || {
ECHO "$P FAILED: dbus suspend failed" 1>&2
return 1
}
else
pm-suspend >> "$LOGFILE"
fi
# Wait on the machine coming back up -- pulling the dmesg over.
echo "v---" >>"$LOGFILE"
retry=30
while [ "$retry" -gt 0 ]; do
let "retry=$retry-1"
# Accumulate the dmesg delta.
dmesg >"$LOGFILE.dmesg.B"
diff "$LOGFILE.dmesg.A" "$LOGFILE.dmesg.B" | \
grep '^>' >"$LOGFILE.dmesg"
mv "$LOGFILE.dmesg.B" "$LOGFILE.dmesg.A"
echo "Waiting for suspend to complete $retry to go ..." \
>> "$LOGFILE"
cat "$LOGFILE.dmesg" >> "$LOGFILE"
if [ "`grep -c 'Back to C!' $LOGFILE.dmesg`" -ne 0 ]; then
break;
fi
sleep 1
done
echo "^---" >>"$LOGFILE"
rm -f "$LOGFILE.dmesg"*
if [ "$retry" -eq 0 ]; then
ECHO "$P SUSPEND FAILED, did not go to sleep" 1>&2
return 1
fi
}
delay_system ()
{
if [ "$dry" -eq 1 ]; then
echo "DRY-RUN: stay awake for $timer_delay"
sleep 1
return
fi
#
# wait for $timer_delay seconds after system resume from S3
#
ECHO "wait for $timer_delay seconds..."
sleep $timer_delay
}
ECHO ()
{
echo "$@" | tee -a "$LOGFILE"
}
enable_trace()
{
echo 1 > '/sys/power/pm_trace'
}
disable_trace()
{
echo 0 > '/sys/power/pm_trace'
}
# Battery
battery_count()
{
cat /proc/acpi/battery/*/state 2>/dev/null | \
awk '
BEGIN { total = 0 }
/present:.*yes/ { total += 1 }
END { print total }
'
}
battery_capacity()
{
cat /proc/acpi/battery/*/state 2>/dev/null | \
awk '
BEGIN { total = 0 }
/remaining capacity:/ { total += $3 }
END { print total }
'
}
# Options helpers.
chk_test ()
{
if ! declare -p "test_$1" 2>/dev/null 1>&2; then
echo "$P: $1: test unknown" 1>&2
exit 1
fi
}
handle_set ()
{
stmt=`echo "$1" | sed -e 's/\./_/g'`
test="${stmt%%_*}"
var="${stmt%%=*}"
chk_test "$test"
if ! declare -p "args_$var" 2>/dev/null 1>&2; then
echo "$P: $var: test variable unknown" 1>&2
exit 1
fi
RET="args_$stmt"
}
chk_number() {
eval "val=\"\$$1\""
let num="0+$val"
if [ "$val" != "$num" ]; then
name=`echo "$1" | sed -e 's/args_//' -e 's/_/./'`
echo "$P: $name: $val: non-numeric value" 1>&2
exit 1
fi
}
# Options handling.
dry=0
auto=0
timer_sleep=20
timer_delay=10
test_dbus=0
test_pmsuspend=0
test_ac=0
test_timed=0
test_repeat=0
args_repeat_iterations=10
test_power=0
args_power_sleep=600
chk_number "args_repeat_iterations"
chk_number "args_power_sleep"
battery_count=`battery_count`
suspend_dbus=0
# Check we are running as root as we are going to fiddle with the clock
# and use the rtc wakeups.
id=`id -u`
if [ "$id" -ne 0 ]; then
echo "ERROR: must be run as root to perform this test, use sudo:" 1>&2
echo " sudo $0 $@" 1>&2
exit 1
fi
ac_needed=-1
ac_is=-1
ac_becomes=-1
ac_required()
{
ac_check
ac_needed="$1"
ac_becomes="$1"
}
ac_transitions()
{
ac_check
ac_needed="$1"
ac_becomes="$2"
}
ac_online()
{
cat /proc/acpi/ac_adapter/*/state 2>/dev/null | \
awk '
BEGIN { online = 0; offline = 0 }
/on-line/ { online = 1 }
/off-line/ { offline = 1 }
END {
if (online) {
print "1"
} else if (offline) {
print "0"
} else {
print "-1"
}
}
'
}
ac_check()
{
typeset ac_current=`ac_online`
if [ "$ac_becomes" -ne -1 -a "$ac_current" -ne -1 -a \
"$ac_current" -ne "$ac_becomes" ]; then
ECHO "*** WARNING: AC power not in expected state" \
"($ac_becomes) after test"
fi
ac_is="$ac_becomes"
}
phase=0
phase_first=1
phase_interactive=1
phase()
{
typeset sleep
let phase="$phase+1"
if [ "$battery_count" -ne 0 -a "$ac_needed" -ne "$ac_is" ]; then
case "$ac_needed" in
0) echo "*** please ensure your AC cord is detached" ;;
1) echo "*** please ensure your AC cord is attached" ;;
esac
ac_is="$ac_needed"
fi
if [ "$timer_sleep" -gt 60 ]; then
let sleep="$timer_sleep / 60"
sleep="$sleep minutes"
else
sleep="$timer_sleep seconds"
fi
echo "*** machine will suspend for $sleep"
if [ "$auto" -eq 1 ]; then
:
elif [ "$phase_interactive" -eq 1 ]; then
echo "*** press return when ready"
read x
elif [ "$phase_first" -eq 1 ]; then
echo "*** NOTE: there will be no further user interaction from this point"
echo "*** press return when ready"
phase_first=0
read x
fi
}
# Ensure the log directory exists.
mkdir -p "$LOGDIR"