home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 September / PCWSEP07.iso / Software / Linux / Linux Mint 3.0 Light / LinuxMint-3.0-Light.iso / casper / filesystem.squashfs / usr / sbin / lm-profiler < prev    next >
Encoding:
Text File  |  2006-12-06  |  7.8 KB  |  277 lines

  1. #!/bin/bash
  2.  
  3. # This script assists you in achieving very high power savings on laptops.
  4. # It can detect programs that perform regular, non-bursty disk operations,
  5. # and network services that listen on external addresses. When started,
  6. # lm-profiler will run for 10 minutes (or a configured number of minutes),
  7. # after which it will provide a series of recommendations. 
  8. #
  9. # It will try to find init scripts for any programs that it recommends for 
  10. # stopping, and it will ask if you want to place links to those scripts in 
  11. # /etc/laptop-mode/batt-stop, so that laptop mode tools will automatically 
  12. # stop those daemons when battery mode is detected.
  13. #
  14. #
  15. # This script is a part of Laptop Mode Tools.
  16. #
  17. # Configuration options for this script can be found in
  18. # /etc/laptop-mode/lm-profiler.conf.
  19. #
  20. # Maintainer: Bart Samwel (bart@samwel.tk)
  21. # Adapted from initial version written by Jan Polacek (jerome@ucw.cz).
  22.  
  23.  
  24. #
  25. # Read configuration.
  26. #
  27.  
  28. # Defaults
  29.  
  30. PROFILE_RUN_LENGTH=600
  31. ACTIVITY_INTERVAL_MAX=150
  32. ACTIVITY_INTERVAL_MIN=5
  33. RECOMMEND_DEFAULT_SERVICES=1
  34. DEFAULT_SERVICES="anacron cron atd"
  35. DEF_IGNORE_PROGRAMS="pdflush journald XFree86 acpid apmd lm-profiler dmesg syslogd awk sed grep mc bc xfs cat diff uniq vi mv sort sleep"
  36. IGNORE_PROGRAMS="$DEF_IGNORE_PROGRAMS"
  37. RECOMMEND_NETWORK_SERVICES=1
  38. DEF_IGNORE_NETWORK_SERVICES="perl" # Some daemons run on perl, not very informative
  39. IGNORE_NETWORK_SERVICES="$DEF_IGNORE_NETWORK_SERVICES"
  40. VERBOSE_OUTPUT=0
  41. if [ -f /etc/laptop-mode/lm-profiler.conf ] ; then
  42.     . /etc/laptop-mode/lm-profiler.conf
  43. fi
  44.  
  45. #
  46. # Internal variables
  47. #
  48.  
  49. DEBUG=0
  50. ######################################################################
  51.  
  52. if [ $DEBUG -eq 1 ]; then
  53.     set -eux
  54. fi
  55.  
  56. if [ "$VERBOSE_OUTPUT" -eq 1 ] ; then
  57.     OUTPUT="/dev/stdout"
  58. else
  59.     OUTPUT="/dev/null"
  60. fi
  61.  
  62. if [ ! `id -u` -eq 0 ]; then
  63.   echo "Only root can run profiler."
  64.   exit 0
  65. fi
  66.  
  67. WORKDIR=`mktemp -d -t lm-profiler.XXXXXX`
  68.  
  69.  
  70. start_profiling(){
  71.     # Turn on disk access profilling
  72.     if [ -f /proc/sys/vm/block_dump ]; then
  73.         echo "1" > /proc/sys/vm/block_dump
  74.     else
  75.         echo "/proc/sys/vm/block_dump does not exist, exiting."
  76.         exit 1
  77.     fi
  78. }
  79.  
  80. stop_profiling(){
  81.     # Turn off disk access profilling
  82.     echo "0" > /proc/sys/vm/block_dump
  83. }
  84.  
  85. # Create a commandline for grep, checking for the presence of all
  86. # strings in a space-separated list passed as the first parameter.
  87. format_params(){
  88.     for PARAM in $1 ; do
  89.         echo -n "-e $PARAM " 
  90.     done
  91. }
  92.  
  93. # Detect all processes that have accessed the disk since the last
  94. # invocation of dmesg. The results are written to a file called
  95. # "accesses_N", where N is the first parameter of this function.
  96. process_dmesg_diff(){
  97.     LEFT="$WORKDIR/dmesg_prev"
  98.     RIGHT="$WORKDIR/dmesg_next"
  99.     dmesg > $RIGHT
  100.     if [ -s $LEFT ] && [ -s $RIGHT ]; then
  101.         # The following command is long and complicated. It does
  102.         # the following things:
  103.         # 1. Retrieve only new lines, using diff.
  104.         # 2. Drop the first line -- it is probably a truncated
  105.         #    version of an earlier line.
  106.         # 3. Parse out the name of the process.
  107.         # 4. Filter out IGNORE_PROGRAMS.
  108.         # 5. Write process name to output.
  109.         
  110.         diff -u $LEFT $RIGHT \
  111.         |grep '^+.*([0-9]*): \(WRITE\|READ\)' \
  112.         |sed '1d' \
  113.         |cut -c 2- \
  114.         |awk -v FS="(" '{print $1}' \
  115.         |grep -v `format_params "$IGNORE_PROGRAMS"` \
  116.         |sort \
  117.         |uniq \
  118.            > $WORKDIR/accesses_$1
  119.         mv $RIGHT $LEFT
  120.         ACCESSES_FOUND=0
  121.         for ACCESS in $(cat $WORKDIR/accesses_$1) ; do
  122.             if [ $ACCESSES_FOUND -eq 0 ] ; then
  123.                 echo -en "\r                                                                            \rAccesses at $1/$PROFILE_RUN_LENGTH in run:"
  124.                 ACCESSES_FOUND=1
  125.             fi
  126.             echo -n " $ACCESS"
  127.         done
  128.         if [ $ACCESSES_FOUND -ne 0 ] ; then
  129.             echo ""
  130.         fi
  131.     else
  132.         echo "No dmesg data found to profile, exiting."
  133.         exit 1
  134.     fi
  135. }
  136.  
  137. # Attempt to find an init script for ithe process given as an argument
  138. findinit(){
  139.     INITDIR=
  140.     if [ -d /etc/init.d ] ; then
  141.         INITDIR=/etc/init.d
  142.     elif [ -d /etc/rc.d/init.d ] ; then
  143.         INITDIR=/etc/rc.d/init.d
  144.     fi
  145.     if [ "$INITDIR" != "" ] ; then
  146.         INIT=`ls $INITDIR/ |grep ^$1$ |head -1`
  147.         if [ -z "$INIT" ]; then
  148.             INIT=`grep $1 $INITDIR/* |sed s/:.*// |head -1`
  149.         else
  150.             INIT="$INITDIR/$INIT"
  151.         fi
  152.         if [ ! -z "$INIT" ] && [ -x $INIT ]; then
  153.             echo "$INIT"
  154.         fi
  155.     fi
  156. }
  157.  
  158. # Look for names of running network services
  159. profilenet(){
  160.     netstat -anp |grep ^tcp.*LISTEN |grep -v "Program name" |awk -v FS="/" '{print $2}' |sort |uniq |\
  161.     tr -d ['(',')','[',']']
  162. }
  163.             
  164.  
  165. #
  166. # PROFILING RUN
  167. #
  168.  
  169. # Disable profiling if the script gets interrupted.
  170. trap "stop_profiling; echo; exit 10" EXIT HUP INT ABRT QUIT SEGV TERM
  171.  
  172. SECONDS_DONE=
  173. echo "Profiling run started."
  174. dmesg > $WORKDIR/dmesg_prev
  175. start_profiling
  176. echo > $WORKDIR/accesses_$SECONDS_DONE
  177. SECONDS_DONE=0
  178. while [ $SECONDS_DONE -le $PROFILE_RUN_LENGTH ] ; do
  179.     echo -en "\r$SECONDS_DONE seconds elapsed, $(($PROFILE_RUN_LENGTH - $SECONDS_DONE)) remaining.         \b\b\b\b\b\b\b\b\b"
  180.     sleep 1
  181.     SECONDS_DONE=$(($SECONDS_DONE + 1))
  182.     process_dmesg_diff $SECONDS_DONE
  183. done
  184. echo -en "\r                                                    \r"
  185. stop_profiling
  186. NETPROFILE=`profilenet`
  187. echo "Profiling run completed."
  188.  
  189. #
  190. # OUTPUT
  191. #
  192. ALREADY_SEEN=
  193. if [ "$RECOMMEND_DEFAULT_SERVICES" -ne 0 ] ; then
  194.     for SERVICE in $DEFAULT_SERVICES ; do
  195.         echo
  196.         echo "Program:     \"$SERVICE\""
  197.         echo "Reason:      standard recommendation (program may not be running)"
  198.         INIT=`findinit $SERVICE`
  199.         if [ "$INIT" == "" ] ; then
  200.             echo "Init script: none"
  201.             echo "If you want to disable this program, you should do so manually."
  202.         else
  203.             echo "Init script: $INIT (GUESSED)"
  204.             echo
  205.             echo -n "Do you want to disable this service in battery mode? [y/N]: "
  206.             read ANSWER
  207.             if ( echo "$ANSWER" | grep -i ^y > /dev/null ) ; then
  208.                 ln -fs $INIT /etc/laptop-mode/batt-stop/`echo $INIT | sed 's/.*\///g'`
  209.             fi
  210.         fi
  211.         ALREADY_SEEN="$ALREADY_SEEN $SERVICE"
  212.     done
  213. fi
  214.  
  215.  
  216.  
  217.  
  218. if [ "$RECOMMEND_NETWORK_SERVICES" -ne 0 ] ; then
  219.     for SERVICE in $NETPROFILE ; do
  220.         if ( echo " $IGNORE_NETWORK_SERVICES " | grep -v " $SERVICE " > /dev/null ) ; then
  221.             echo
  222.             echo "Program:     \"$SERVICE\""
  223.             echo "Reason:      listens on network, may not be needed offline."
  224.             INIT=`findinit $SERVICE`
  225.             if [ "$INIT" == "" ] ; then
  226.                 echo "Init script: none"
  227.                 echo "If you want to disable this program, you should do so manually."
  228.             else
  229.                 echo "Init script: $INIT (GUESSED)"
  230.                 echo
  231.                 echo -n "Do you want to disable this service in battery mode? [y/N]: "
  232.                 read ANSWER
  233.                 if ( echo "$ANSWER" | grep -i ^y > /dev/null ) ; then
  234.                     ln -fs $INIT /etc/laptop-mode/batt-stop/`echo $INIT | sed 's/.*\///g'`
  235.                 fi
  236.             fi
  237.             ALREADY_SEEN="$ALREADY_SEEN $SERVICE"
  238.         fi
  239.     done
  240. fi
  241.  
  242. SECONDS_LEFT=$PROFILE_RUN_LENGTH
  243. while [ $SECONDS_LEFT -gt 0 ] ; do
  244.     for SERVICE in `cat $WORKDIR/accesses_$SECONDS_LEFT` ; do
  245.         if ( echo " $ALREADY_SEEN " | grep -v " $SERVICE " > /dev/null ) ; then
  246.             CUR_COMPARE_SECONDS=$(($SECONDS_LEFT - $ACTIVITY_INTERVAL_MIN))
  247.             while [ $CUR_COMPARE_SECONDS -gt $(($SECONDS_LEFT - $ACTIVITY_INTERVAL_MAX)) -a $CUR_COMPARE_SECONDS -gt 0 ] ; do
  248.                 if ( grep "^$SERVICE$" $WORKDIR/accesses_$CUR_COMPARE_SECONDS > /dev/null ) ; then
  249.                     if ( echo " $ALREADY_SEEN " | grep -v " $SERVICE " > /dev/null ) ; then
  250.                         echo
  251.                         echo "Program:     \"$SERVICE\""
  252.                         echo "Reason:      disk access."
  253.                         INIT=`findinit $SERVICE`
  254.                         if [ "$INIT" == "" ] ; then
  255.                             echo "Init script: none"
  256.                             echo "If you want to disable this program, you should do so manually."
  257.                         else
  258.                             echo "Init script: $INIT (GUESSED)"
  259.                             echo
  260.                             echo -n "Do you want to disable this service in battery mode? [y/N]: "
  261.                         fi
  262.                         read ANSWER
  263.                         if ( echo "$ANSWER" | grep -i ^y > /dev/null ) ; then
  264.                             if [ -e $INIT ] ; then
  265.                                 ln -fs $INIT /etc/laptop-mode/batt-stop/`echo $INIT | sed 's/.*\///g'`
  266.                             fi
  267.                         fi
  268.                         ALREADY_SEEN="$ALREADY_SEEN $SERVICE"
  269.                     fi
  270.                 fi
  271.                 CUR_COMPARE_SECONDS=$(($CUR_COMPARE_SECONDS - 1))
  272.             done
  273.         fi
  274.     done
  275.     SECONDS_LEFT=$(($SECONDS_LEFT - 1))
  276. done
  277.