Difference between revisions of "ACPI sleep power drain test script"

From ThinkWiki
Jump to: navigation, search
m (note non-POSIX date +%s)
 
(One intermediate revision by the same user not shown)
Line 1: Line 1:
The following script will suspend your notebook to ram and output some statistics about the power drain during suspend to {{path|/var/log/battery.log}}. The output will look something like this:
+
The following script will suspend your notebook to ram, and output some statistics about the power drain to {{path|/var/log/battery-test.log}}. The output will look something like this:
  
  Di Jan 11 14:01:15 CET 2005
+
  Initial energy reading (18.69) is between 10 and 100 Wh, so power_divider is probably correct.
  before: 41170 mWh
+
Sat Jan 14 19:57:25 PST 2012
  after: 41000 mWh
+
Sat Jan 14 19:57:41 PST 2012
  diff: -170 mWh
+
   
  seconds: 1671 sec
+
start second: 1326599845
  result: -366 mW
+
  end second: 1326599861
  Congratulations, your model seems NOT to be affected.
+
  time consumed: 16 seconds
 +
  === WARNING === sleep time was less than 20 minutes: results may not be reliable
 +
   
 +
start energy: 18.69 Wh
 +
  end energy: 18.64 Wh
 +
  energy consumed: .05 Wh
 +
   
 +
watts used while asleep = 11.25000000000000001125
 +
(values above 1 are high)
 +
Your computer is using a lot of power while sleeping.
 +
You may wish to refer to http://www.thinkwiki.org/wiki/How_to_reduce_power_consumption
  
{{NOTE|This Script has some limitations:
+
{{NOTE|This Script can be customized in numerous ways:
*It is not totally reliable, e.g. on some (all?) R32 the values ACPI reports for the battery are wrong by factor 10. They are actually reported in cWh instead of mWh. The script assumes they are reported in mWh as on most ThinkPads - hence the power consumption appearce to be only a tenth of its actual value.
+
*You may wish to pass some quirk mode parameters to pm-suspend, or use a different suspend method -- adjust the suspend_func function, and/or pm_params. You should really make sure pm-suspend works before using this script :)
 +
*different versions of the linux kernel combined with different hardware may report power in different units. Newer kernels use micro- rather than milli- units; and some R32 models report values in centi- rather than milli- units. Adjust the power_divider variable to compensate for these discrepancies.
 
*The script assumes you have only one battery. If you have a second battery (for example, an UltraBay Slim battery), take it out for this test or change the BATTERY variable from "BAT0" to "BAT1". If you do the latter, you will get bogus results if the second battery is run down while sleeping.
 
*The script assumes you have only one battery. If you have a second battery (for example, an UltraBay Slim battery), take it out for this test or change the BATTERY variable from "BAT0" to "BAT1". If you do the latter, you will get bogus results if the second battery is run down while sleeping.
*The usage threshold of 1000 mWh might be a bit a bit too tolerant. You can try lowering it to 500 to be sure.
+
*The usage threshold of 0.8 Watt may be too low or high for displaying a warning. You can adjust it if you like
*On at least one T23 (2647-GGU) it causes the VGA to stop refreshing after resuming from sleep. This means that you can still blindly type commands to cleanly reboot your machine, but the LCD will statically display whatever was there the moment the script was executed.}}
+
*If you are using a really old kernel that does not support /sys/class/power_supply then you can uncomment the older get_energy and get_status lines}}
  
Please save this script to a file, i.e. {{path|sleep.sh}}, make it executable ({{cmdroot|chmod +x sleep.sh}}) and execute it as root while your notebook is on battery. To get representative values you should leave the notebook suspended for at least 20 minutes.
+
Please save this script to a file, e.g. {{path|test-sleep-power-drain.sh}}, make it executable ({{cmdroot|chmod +x test-sleep-power-drain.sh}}) and execute it as root while your notebook is running on battery power. To get representative values you should leave the notebook suspended for at least 20 minutes.
  
 
  #!/bin/sh
 
  #!/bin/sh
  # sleep.sh test script for measuring power drain during suspend-to-ram with ACPI
+
  # test-sleep-battery-usage.sh
 +
# test script for measuring power drain during suspend-to-ram with ACPI
 +
# you should carefully read through this script and make adjustments as necessary before running it!
 +
# http://www.thinkwiki.org/wiki/ACPI_sleep_power_drain_test_script
 +
 +
# written for linux kernel 2.6.38, with older kernel options that you can uncomment if needed
 +
# as far as I know, this script contains only one non-POSIX command: date +%s
 +
 +
# uncomment and/or change the following parameters if you need to pass them to pm-suspend
 +
# pm_params="--quirk-s3-bios --quirk-s3-mode"
 +
# Refer to http://www.thinkwiki.org/wiki/Problem_with_display_remaining_black_after_resume
 +
 +
# if you have two batteries, you should take one out (and adjust the battery variable) in order to get an accurate reading
 +
battery=BAT0
 +
 +
# paths and energy/status functions - these work for linux kernels 2.6.25 through 3.2 and beyond
 +
battery_dir=/sys/class/power_supply/$battery
 +
get_energy () { cat $battery_dir/energy_now; }
 +
get_status () { cat $battery_dir/status; }
 +
discharging_string="Discharging"
 +
 +
# /proc/acpi/battery was deprecated somewhere in the era of linux kernel 2.6.24
 +
# if you are using such an old kernel that does not have /sys/class/power_supply, you can uncomment these lines:
 +
# battery_dir=/proc/acpi/battery/$battery
 +
# get_energy () { grep 'remaining capacity' $battery_dir/state | awk '{print $3}'; }
 +
# get_status () { grep '^charging state:' $battery_dir/state | awk '{print $3}'; }
 +
# discharging_string="discharging"
 +
 +
# suspend command
 +
suspend_func () { pm-suspend $pm_params; }
 +
 +
# here is an older version of the suspend command; you can uncomment it if you want to use it
 +
# suspend_func () {
 +
# if [ -e /proc/acpi/sleep ]; then
 +
#  echo 3 > /proc/acpi/sleep
 +
# else
 +
#  echo -n mem > /sys/power/state
 +
# fi
 +
# }
 +
 +
# different kernels and different hardware report energy values using different units
 +
# energy values should typically be in the range of 15 - 75 Wh (Watt-hours, or Ah, Amp-hours. Whatever :-)
 +
# get an energy value from $energy_file, divide by the power_divider, and see what you get...
 +
# if yours is 2.5 or 250 rather than 25, adjust the divider
 +
power_divider=1000000 # micro watt hours / μWh
 +
# power_divider=1000 # milli watt hours / mWh
 +
# power_divider=100 # centi watt hours / cWh
 +
 +
warning_threshold=0.8 # watts above which we give a warning
 +
 +
logfile=/var/log/battery-test.log
 +
 +
# it is easier to do copy/paste script tests if failures don't exit the shell :-)
 +
do_exit () { exit $1; }
 +
 +
# the sed function strips trailing zeros off of the bc result, e.g. 14.50000000000 -> 14.5  (but 0 -> 0)
 +
calcfunc () { echo "$@" | bc -l | sed '/^0$/!s,0*$,,'; }
 +
# a creative way to compare floating point numbers...
 +
a_less_than_b () { case $(echo "$1-$2" | bc) in -*) return 0;; *) return 1;; esac; }
 
   
 
   
  # default settings, change if needed
+
  which bc >/dev/null || { echo "I need 'bc' to do floating point operations. install it, or change the functions 'calcfunc' and 'a_less_than_b'"; do_exit 1; }
LOG=/var/log/battery.log
 
BATTERY=BAT0
 
THRESHOLD=1000
 
 
   
 
   
  if ! grep -q '^charging state:.*discharging' /proc/acpi/battery/$BATTERY/state; then
+
test_energy=$(calcfunc $(get_energy)/$power_divider)
    echo 'Not running on battery power, did you forget to disconnect the charger?'
+
  if a_less_than_b $test_energy 10; then
    exit 1
+
  warn_pwr="less than 10 watt-hours"
 +
elif a_less_than_b 100 $test_energy; then
 +
  warn_pwr="more than 100 watt-hours"
 +
else
 +
  warn_pwr="ok"
 
  fi
 
  fi
 
   
 
   
  if [ "$UID" != "0" ]; then
+
  if [ $warn_pwr = "ok" ]; then
    echo 'This script can only be run by root.'
+
  echo "Initial energy reading ($test_energy) is between 10 and 100 Wh, so power_divider is probably correct." | tee -a $logfile
    exit 1
+
else
 +
  echo "WARNING: Energy reading ($test_energy) is $warn_pwr. Perhaps you need to adjust the power_divider?" | tee -a $logfile
 +
  echo -n "Continue with the test anyway? [y/N] "
 +
  read abort
 +
  case $abort in [Yy]*) ;; *) do_exit 1;; esac;
 
  fi
 
  fi
 +
 +
status=$(get_status)
 +
[ $status = $discharging_string ] || { echo "battery status is '$status' -- not '$discharging_string'. We need to run on the battery."; do_exit 1; }
 +
[ $USER = root ] || { echo "must run as root."; do_exit 1; }
 
   
 
   
 
  # remove USB for external mouse before sleeping
 
  # remove USB for external mouse before sleeping
  if lsmod | grep '^usbhid' >/dev/null ; then
+
  modprobe -r usbhid uhci_hcd ehci_hcd
    /sbin/modprobe -r -s usbhid
 
fi
 
if lsmod | grep '^uhci_hcd' >/dev/null ; then
 
    /sbin/modprobe -r -s uhci_hcd
 
fi
 
if lsmod | grep '^ehci_hcd' >/dev/null ; then
 
    /sbin/modprobe -r -s ehci_hcd
 
fi
 
 
   
 
   
  # save system time
+
  # save system time -- is this really necessary?
  hwclock --systohc
+
  # hwclock --systohc
 
   
 
   
 
  # get start values
 
  # get start values
  date >> $LOG
+
  date | tee -a $logfile
  DATE_BEFORE=`date +%s`
+
  start_second=$(date +%s)
  BAT_BEFORE=`grep 'remaining capacity' /proc/acpi/battery/$BATTERY/state | awk '{print $3}'`
+
start_energy_reading=$(get_energy)
 +
  start_watt_hours=$(calcfunc $start_energy_reading / $power_divider)
 
   
 
   
 
  # go to sleep
 
  # go to sleep
  if [ -e /proc/acpi/sleep ]; then
+
  suspend_func
    echo 3 > /proc/acpi/sleep
 
else
 
    echo -n mem > /sys/power/state
 
fi
 
 
   
 
   
 
  # get end values
 
  # get end values
  DATE_AFTER=`date +%s`
+
  end_second=$(date +%s)
  BAT_AFTER=`grep 'remaining capacity' /proc/acpi/battery/$BATTERY/state | awk '{print $3}'`
+
  end_energy_reading=$(get_energy)
 +
end_watt_hours=$(calcfunc $end_energy_reading / $power_divider)
 +
date | tee -a $logfile
 +
 +
# restore system time -- see above
 +
# hwclock --hctosys
 +
 +
#restore usb
 +
modprobe uhci_hcd ehci_hcd usbhid
 +
 +
secs_consumed=$(calcfunc $end_second-$start_second)
 +
hours_consumed=$(calcfunc $secs_consumed/3600)
 +
watt_hours_consumed=$(calcfunc $start_watt_hours-$end_watt_hours)
 +
sleep_watts=$(calcfunc $watt_hours_consumed/$hours_consumed)
 +
 +
echo | tee -a $logfile
 +
echo "start second: $start_second" | tee -a $logfile
 +
echo "  end second: $end_second" | tee -a $logfile
 +
echo "time consumed: $secs_consumed seconds" | tee -a $logfile
 +
[ $secs_consumed -lt 1200 ] && echo "=== WARNING === sleep time was less than 20 minutes: results may not be reliable" | tee -a $logfile
 +
echo | tee -a $logfile
 +
echo "start energy: $start_watt_hours Wh" | tee -a $logfile
 +
echo "  end energy: $end_watt_hours Wh" | tee -a $logfile
 +
echo "energy consumed: $watt_hours_consumed Wh" | tee -a $logfile
 +
echo | tee -a $logfile
 
   
 
   
  # do the calculations
+
  echo "watts used while asleep = $sleep_watts" | tee -a $logfile
DIFF=`echo "$BAT_AFTER - $BAT_BEFORE" | bc`
+
  echo "(values above 1 are high)" | tee -a $logfile
SECONDS=`echo "$DATE_AFTER - $DATE_BEFORE" | bc`
 
  USAGE=`echo "($DIFF * 60 * 60) / ($SECONDS)" | bc`
 
 
   
 
   
  # output the results
+
  if a_less_than_b $sleep_watts $warning_threshold; then
echo "before: $BAT_BEFORE mWh" >> $LOG
+
  echo "Results are good-- looks like your computer is sleeping soundly" | tee -a $logfile
echo "after: $BAT_AFTER mWh" >> $LOG
 
echo "diff: $DIFF mWh" >> $LOG
 
echo "seconds: $SECONDS sec" >> $LOG
 
echo "result: $USAGE mW" >> $LOG
 
if [ $USAGE -gt -$THRESHOLD ]
 
then
 
    echo "Congratulations, your model seems NOT to be affected." >> $LOG
 
 
  else
 
  else
    echo "Your model seems to be affected." >> $LOG
+
  echo "Your computer is using a lot of power while sleeping." | tee -a $logfile
fi
+
  echo "You may wish to refer to http://www.thinkwiki.org/wiki/How_to_reduce_power_consumption" | tee -a $logfile
if [ $SECONDS -lt 1200 ]
 
then
 
    echo "!!! The notebook was suspended less than 20 minutes." >> $LOG
 
    echo "!!! To get representative values please let the notebook sleep" >> $LOG
 
    echo "!!! for at least 20 minutes." >> $LOG
 
fi
 
echo "" >> $LOG
 
 
# restore USB support
 
if !(lsmod | grep '^ehci_hcd') >/dev/null ; then
 
    /sbin/modprobe -s ehci_hcd
 
fi
 
if !(lsmod | grep '^uhci_hcd') >/dev/null ; then
 
    /sbin/modprobe -s uhci_hcd
 
fi
 
if !(lsmod | grep '^usbhid')  >/dev/null ; then
 
    /sbin/modprobe -s usbhid
 
 
  fi
 
  fi
 
   
 
   
  # restore system time
+
  echo | tee -a $logfile
hwclock --hctosys
 
  
The script was originally written by Jan-Hendrik Benter and was modified a little for this page.
+
The script was originally written by Jan-Hendrik Benter in 2005.
 +
It was completely rewritten by David Emerson in 2012.
  
  
 
[[Category:Scripts]]
 
[[Category:Scripts]]

Latest revision as of 05:34, 15 January 2012

The following script will suspend your notebook to ram, and output some statistics about the power drain to /var/log/battery-test.log. The output will look something like this:

Initial energy reading (18.69) is between 10 and 100 Wh, so power_divider is probably correct.
Sat Jan 14 19:57:25 PST 2012
Sat Jan 14 19:57:41 PST 2012

start second: 1326599845
  end second: 1326599861
time consumed: 16 seconds
=== WARNING === sleep time was less than 20 minutes: results may not be reliable

start energy: 18.69 Wh
  end energy: 18.64 Wh
energy consumed: .05 Wh

watts used while asleep = 11.25000000000000001125
(values above 1 are high)
Your computer is using a lot of power while sleeping.
You may wish to refer to http://www.thinkwiki.org/wiki/How_to_reduce_power_consumption
NOTE!
This Script can be customized in numerous ways:
  • You may wish to pass some quirk mode parameters to pm-suspend, or use a different suspend method -- adjust the suspend_func function, and/or pm_params. You should really make sure pm-suspend works before using this script :)
  • different versions of the linux kernel combined with different hardware may report power in different units. Newer kernels use micro- rather than milli- units; and some R32 models report values in centi- rather than milli- units. Adjust the power_divider variable to compensate for these discrepancies.
  • The script assumes you have only one battery. If you have a second battery (for example, an UltraBay Slim battery), take it out for this test or change the BATTERY variable from "BAT0" to "BAT1". If you do the latter, you will get bogus results if the second battery is run down while sleeping.
  • The usage threshold of 0.8 Watt may be too low or high for displaying a warning. You can adjust it if you like
  • If you are using a really old kernel that does not support /sys/class/power_supply then you can uncomment the older get_energy and get_status lines

Please save this script to a file, e.g. test-sleep-power-drain.sh, make it executable (# chmod +x test-sleep-power-drain.sh) and execute it as root while your notebook is running on battery power. To get representative values you should leave the notebook suspended for at least 20 minutes.

#!/bin/sh
# test-sleep-battery-usage.sh
# test script for measuring power drain during suspend-to-ram with ACPI
# you should carefully read through this script and make adjustments as necessary before running it!
# http://www.thinkwiki.org/wiki/ACPI_sleep_power_drain_test_script

# written for linux kernel 2.6.38, with older kernel options that you can uncomment if needed
# as far as I know, this script contains only one non-POSIX command: date +%s

# uncomment and/or change the following parameters if you need to pass them to pm-suspend
# pm_params="--quirk-s3-bios --quirk-s3-mode"
# Refer to http://www.thinkwiki.org/wiki/Problem_with_display_remaining_black_after_resume

# if you have two batteries, you should take one out (and adjust the battery variable) in order to get an accurate reading
battery=BAT0

# paths and energy/status functions - these work for linux kernels 2.6.25 through 3.2 and beyond
battery_dir=/sys/class/power_supply/$battery
get_energy () { cat $battery_dir/energy_now; }
get_status () { cat $battery_dir/status; }
discharging_string="Discharging"

# /proc/acpi/battery was deprecated somewhere in the era of linux kernel 2.6.24
# if you are using such an old kernel that does not have /sys/class/power_supply, you can uncomment these lines:
# battery_dir=/proc/acpi/battery/$battery
# get_energy () { grep 'remaining capacity' $battery_dir/state | awk '{print $3}'; }
# get_status () { grep '^charging state:' $battery_dir/state | awk '{print $3}'; }
# discharging_string="discharging"

# suspend command
suspend_func () { pm-suspend $pm_params; }

# here is an older version of the suspend command; you can uncomment it if you want to use it
# suspend_func () {
# if [ -e /proc/acpi/sleep ]; then
#   echo 3 > /proc/acpi/sleep
# else
#   echo -n mem > /sys/power/state
# fi
# }

# different kernels and different hardware report energy values using different units
# energy values should typically be in the range of 15 - 75 Wh (Watt-hours, or Ah, Amp-hours. Whatever :-)
# get an energy value from $energy_file, divide by the power_divider, and see what you get...
# if yours is 2.5 or 250 rather than 25, adjust the divider
power_divider=1000000 # micro watt hours / μWh
# power_divider=1000 # milli watt hours / mWh
# power_divider=100 # centi watt hours / cWh

warning_threshold=0.8 # watts above which we give a warning

logfile=/var/log/battery-test.log

# it is easier to do copy/paste script tests if failures don't exit the shell :-)
do_exit () { exit $1; }

# the sed function strips trailing zeros off of the bc result, e.g. 14.50000000000 -> 14.5  (but 0 -> 0)
calcfunc () { echo "$@" | bc -l | sed '/^0$/!s,0*$,,'; }
# a creative way to compare floating point numbers...
a_less_than_b () { case $(echo "$1-$2" | bc) in -*) return 0;; *) return 1;; esac; }

which bc >/dev/null || { echo "I need 'bc' to do floating point operations. install it, or change the functions 'calcfunc' and 'a_less_than_b'"; do_exit 1; }

test_energy=$(calcfunc $(get_energy)/$power_divider)
if a_less_than_b $test_energy 10; then
  warn_pwr="less than 10 watt-hours"
elif a_less_than_b 100 $test_energy; then
  warn_pwr="more than 100 watt-hours"
else
  warn_pwr="ok"
fi

if [ $warn_pwr = "ok" ]; then
  echo "Initial energy reading ($test_energy) is between 10 and 100 Wh, so power_divider is probably correct." | tee -a $logfile
else
  echo "WARNING: Energy reading ($test_energy) is $warn_pwr. Perhaps you need to adjust the power_divider?" | tee -a $logfile
  echo -n "Continue with the test anyway? [y/N] "
  read abort
  case $abort in [Yy]*) ;; *) do_exit 1;; esac;
fi

status=$(get_status)
[ $status = $discharging_string ] || { echo "battery status is '$status' -- not '$discharging_string'. We need to run on the battery."; do_exit 1; }
[ $USER = root ] || { echo "must run as root."; do_exit 1; }

# remove USB for external mouse before sleeping
modprobe -r usbhid uhci_hcd ehci_hcd

# save system time -- is this really necessary?
# hwclock --systohc

# get start values
date | tee -a $logfile
start_second=$(date +%s)
start_energy_reading=$(get_energy)
start_watt_hours=$(calcfunc $start_energy_reading / $power_divider)

# go to sleep
suspend_func

# get end values
end_second=$(date +%s)
end_energy_reading=$(get_energy)
end_watt_hours=$(calcfunc $end_energy_reading / $power_divider)
date | tee -a $logfile

# restore system time -- see above
# hwclock --hctosys

#restore usb
modprobe uhci_hcd ehci_hcd usbhid

secs_consumed=$(calcfunc $end_second-$start_second)
hours_consumed=$(calcfunc $secs_consumed/3600)
watt_hours_consumed=$(calcfunc $start_watt_hours-$end_watt_hours)
sleep_watts=$(calcfunc $watt_hours_consumed/$hours_consumed)

echo | tee -a $logfile
echo "start second: $start_second" | tee -a $logfile
echo "  end second: $end_second" | tee -a $logfile
echo "time consumed: $secs_consumed seconds" | tee -a $logfile
[ $secs_consumed -lt 1200 ] && echo "=== WARNING === sleep time was less than 20 minutes: results may not be reliable" | tee -a $logfile
echo | tee -a $logfile
echo "start energy: $start_watt_hours Wh" | tee -a $logfile
echo "  end energy: $end_watt_hours Wh" | tee -a $logfile
echo "energy consumed: $watt_hours_consumed Wh" | tee -a $logfile
echo | tee -a $logfile

echo "watts used while asleep = $sleep_watts" | tee -a $logfile
echo "(values above 1 are high)" | tee -a $logfile

if a_less_than_b $sleep_watts $warning_threshold; then
  echo "Results are good-- looks like your computer is sleeping soundly" | tee -a $logfile
else
  echo "Your computer is using a lot of power while sleeping." | tee -a $logfile
  echo "You may wish to refer to http://www.thinkwiki.org/wiki/How_to_reduce_power_consumption" | tee -a $logfile
fi

echo | tee -a $logfile

The script was originally written by Jan-Hendrik Benter in 2005. It was completely rewritten by David Emerson in 2012.