Difference between revisions of "Script for monitoring power consumption"

From ThinkWiki
Jump to: navigation, search
(MoveToCode)
m
 
(One intermediate revision by the same user not shown)
Line 9: Line 9:
 
{{NOTE|Please feel ''very free'' to improve/fix this script.  My intent for its posting is to make its ownership as public as possible.  There's no need to try to E-mail me to validate your changes.  If you feel they are in the best interest of the public, just make the changes.  The script attempts to employ pre-conditions to intelligently apply functionality only to those laptops that appear to support it.  Hopefully, its framework will allow for extension without heavy redesign.}}
 
{{NOTE|Please feel ''very free'' to improve/fix this script.  My intent for its posting is to make its ownership as public as possible.  There's no need to try to E-mail me to validate your changes.  If you feel they are in the best interest of the public, just make the changes.  The script attempts to employ pre-conditions to intelligently apply functionality only to those laptops that appear to support it.  Hopefully, its framework will allow for extension without heavy redesign.}}
  
−
{{MoveToCode}}
+
==The script==
−
<pre>
+
{{CodeRef|tp_power_monitor.py}}
−
#!/usr/bin/python
 
  
−
# COPYRIGHT (C) 2006 Matthew Garman
 
−
# matthew (dot) garman (at) gmail (dot) com
 
−
# License: MIT <http://www.opensource.org/licenses/mit-license.php>
 
−
 
−
import sys, time, getopt, os, re
 
−
 
−
 
−
class tpPowerMonitorDataSource:
 
−
 
−
    # The name of the file to parse for needed data (data_source_file) OR
 
−
    # the name of a command whose output will be read as a file
 
−
    # (os.popen(shell_command)).
 
−
    #
 
−
    # Typically, data_source_file will be something from /proc (or
 
−
    # possibly /sys), and shell_command will be a utility+arguments such
 
−
    # as "iwconfig eth1 power".
 
−
    #
 
−
    # Note that you should set EITHER data_source_file OR shell_command,
 
−
    # but not both.
 
−
    data_source_file    = None
 
−
    shell_command      = None
 
−
 
−
    # The line number of the data file containing our data OR the string
 
−
    # that marks the beginning of the line in which we're interested
 
−
    # (startswith_string) OR a regular expression to key the data for
 
−
    # which we're looking (regexp_pattern).
 
−
    line_number        = None
 
−
    startswith_string  = None
 
−
    regexp_pattern      = None
 
−
 
−
    # The string that delimits the line containing our data.  This will be
 
−
    # used as the parameter to split().  Note that you can leave this as
 
−
    # None to split on whitespace.
 
−
    split_string        = None
 
−
 
−
    # A mapping of data names to indices in the split string.
 
−
    name_index_map      = dict()
 
−
 
−
    def __init__(self, file, cmd, line, swstr, pat, splstr, map):
 
−
        self.data_source_file    = file
 
−
        self.shell_command      = cmd
 
−
        self.line_number        = line
 
−
        self.startswith_string  = swstr
 
−
        self.regexp_pattern      = pat
 
−
        self.split_string        = splstr
 
−
        self.name_index_map      = map
 
−
 
−
    def printMembers(self):
 
−
        print "\t" + 'self.data_source_file = ' + str(self.data_source_file)
 
−
        print "\t" + 'self.shell_command = ' + str(self.shell_command)
 
−
        print "\t" + 'self.line_number = ' + str(self.line_number)
 
−
        print "\t" + 'self.startswith_string = ' + str(self.startswith_string)
 
−
        print "\t" + 'self.regexp_pattern = ' + str(self.regexp_pattern)
 
−
        print "\t" + 'self.split_string = ' + str(self.split_string)
 
−
        print "\t" + 'self.name_index_map = ' + str(self.name_index_map)
 
−
 
−
    def readData(self, d):
 
−
        # open the data source file or run the command that will produce
 
−
        # the data
 
−
        file = None
 
−
        if self.data_source_file:
 
−
            file = open(self.data_source_file, 'r')
 
−
        elif self.shell_command:
 
−
            file = os.popen(self.shell_command)
 
−
        else:
 
−
            print 'error: no data source defined'
 
−
            self.printMembers()
 
−
        # read the contents of the file into a list and close the file
 
−
        lines = None
 
−
        if file:
 
−
            lines = file.readlines()
 
−
            file.close()
 
−
        else:
 
−
            print 'error: data file not opened'
 
−
            self.printMembers()
 
−
        # now get the line in the file with our data
 
−
        line = None
 
−
        if lines:
 
−
            if None != self.line_number:
 
−
                line = lines[self.line_number]
 
−
            elif self.startswith_string:
 
−
                for l in lines:
 
−
                    if l.startswith(self.startswith_string):
 
−
                        line = l
 
−
                        break
 
−
            elif self.regexp_pattern:
 
−
                for l in lines:
 
−
                    if re.compile(self.regexp_pattern).match(l):
 
−
                        line = l
 
−
                        break
 
−
        else:
 
−
            print 'error: no lines in data file'
 
−
            self.printMembers()
 
−
        # now get the data we want from the line itself
 
−
        if line:
 
−
            fields = line.split(self.split_string)
 
−
            for key in self.name_index_map.keys():
 
−
                d[key] = fields[self.name_index_map[key]].strip()
 
−
        else:
 
−
            print 'error: could not find line with data in file'
 
−
            self.printMembers()
 
−
 
−
# END --- class tpPowerMonitorDataSource
 
−
 
−
 
−
tp_data_sources = [
 
−
 
−
    tpPowerMonitorDataSource(
 
−
        '/proc/cpuinfo',            # data file
 
−
        None,                        # shell command
 
−
        None,                        # line index
 
−
        'cpu MHz',                  # startswith() string
 
−
        None,                        # regexp pattern
 
−
        ':',                        # split string
 
−
        {'proc_cpuinfo_cpu_mhz': 1}  # name-to-index map
 
−
    ),
 
−
 
−
    # http://thinkwiki.org/wiki/Ipw2200#Power_Management
 
−
    tpPowerMonitorDataSource(
 
−
        None,                          # data file
 
−
        '/sbin/iwpriv eth1 get_power', # shell command
 
−
        0,                            # line index
 
−
        None,                          # startswith() string
 
−
        None,                          # regexp pattern
 
−
        ':',                          # split string
 
−
        {'iwpriv_get_power': 2}        # name-to-index map
 
−
    ),
 
−
 
−
    # http://forums.gentoo.org/viewtopic-t-447841.html
 
−
    # see post from ruben on Wed Mar 29, 2006 4:09 am
 
−
    tpPowerMonitorDataSource(
 
−
        None,
 
−
        '/usr/bin/sudo /sbin/hdparm -C /dev/sda',
 
−
        None,
 
−
        ' drive state is',
 
−
        None,
 
−
        ':',
 
−
        {'hard_drive_power_state': 1}
 
−
    ),
 
−
 
−
    # http://thinkwiki.org/wiki/Thermal_sensors
 
−
    tpPowerMonitorDataSource(
 
−
        '/proc/acpi/ibm/thermal',
 
−
        None,
 
−
        0,
 
−
        None,
 
−
        None,
 
−
        None,
 
−
        {'ibm_thermal_cpu':    1,
 
−
        'ibm_thermal_hdaps':  2,
 
−
        'ibm_thermal_pmcia':  3,
 
−
        'ibm_thermal_gpu':    4,
 
−
        'ibm_thermal_batt_fl': 5,
 
−
        'ibm_thermal_batt_br': 7 }
 
−
    ),
 
−
 
−
    # http://mailman.linux-thinkpad.org/pipermail/linux-thinkpad/2006-July/034738.html
 
−
    tpPowerMonitorDataSource(
 
−
        '/proc/acpi/processor/CPU/power',
 
−
        None,
 
−
        None,
 
−
        'bus master activity',
 
−
        None,
 
−
        ':',
 
−
        {'bus_master_activity': 1}
 
−
    ),
 
−
 
−
    tpPowerMonitorDataSource(
 
−
        '/proc/acpi/battery/BAT0/state',
 
−
        None,
 
−
        None,
 
−
        'present rate:',
 
−
        None,
 
−
        None,
 
−
        {'battery0_state_present_rate': 2}
 
−
    ),
 
−
 
−
    tpPowerMonitorDataSource(
 
−
        None,
 
−
        '/usr/bin/sudo /usr/sbin/radeontool dac',
 
−
        0,
 
−
        None,
 
−
        None,
 
−
        None,
 
−
        {'radeontool_dac_ext_vga': -1}
 
−
    ),
 
−
 
−
    tpPowerMonitorDataSource(
 
−
        None,
 
−
        '/usr/bin/sudo /usr/sbin/radeontool light',
 
−
        0,
 
−
        None,
 
−
        None,
 
−
        None,
 
−
        {'radeontool_light_lcd': -1}
 
−
    ),
 
−
 
−
    # http://forums.gentoo.org/viewtopic-t-343029-highlight-rovclock.html
 
−
    tpPowerMonitorDataSource(
 
−
        None,
 
−
        '/usr/bin/sudo /usr/sbin/rovclock -i',
 
−
        None,
 
−
        'Core: ',
 
−
        None,
 
−
        None,
 
−
        {'rovclock_gpu_clock': 1,
 
−
        'rovclock_mem_clock': 4 }
 
−
    ),
 
−
 
−
    tpPowerMonitorDataSource(
 
−
        None,
 
−
        '/sbin/iwconfig eth1',
 
−
        None,
 
−
        None,
 
−
        '.*Power Management.*',
 
−
        ':',
 
−
        {'wireless_power_mgmt_state': 1}
 
−
    ),
 
−
 
−
    tpPowerMonitorDataSource(
 
−
        '/proc/loadavg',
 
−
        None,
 
−
        0,
 
−
        None,
 
−
        None,
 
−
        None,
 
−
        {'proc_loadavg_1min': 0,
 
−
        'proc_loadavg_5min': 1,
 
−
        'proc_loadavg_15min': 2 }
 
−
    ),
 
−
]
 
−
log_data = list()
 
−
poll_freq_hz = 1
 
−
run_time_sec = 60*60
 
−
logfile = None
 
−
 
−
 
−
def collectPowerData():
 
−
    global run_time_sec
 
−
    global poll_freq_hz
 
−
    global log_data
 
−
    global logfile
 
−
    global tp_data_sources
 
−
 
−
    log = None
 
−
    if logfile:
 
−
        log = open(logfile, 'w')
 
−
        log.write("time, data\n")
 
−
    end_t = time.time()+run_time_sec
 
−
    while time.time() < end_t:
 
−
        datum = dict()
 
−
        for dsrc in tp_data_sources:
 
−
            dsrc.readData(datum)
 
−
        log_data.append(datum)
 
−
        if log: log.write(str(time.time()) + ', ' + str(datum) + "\n")
 
−
        time.sleep(1.0/float(poll_freq_hz))
 
−
    if log: log.close()
 
−
 
−
 
−
def createStats():
 
−
    global log_data
 
−
    global logfile
 
−
    log = None
 
−
    if logfile:
 
−
        log = open(logfile, 'a')
 
−
    if log: log.close()
 
−
 
−
 
−
def usage():
 
−
    print 'Usage: ' + sys.argv[0] + \
 
−
        '[-h] [-f freq_hz] [-t time_min] [-l logfile]'
 
−
    print "\t-h            Display this help"
 
−
    print "\t-f freq_hz    The polling frequency in Hertz, default=" \
 
−
        + str(poll_freq_hz)
 
−
    print "\t-t time_min  Total poll time in minutes, default=" \
 
−
        + str(run_time_sec/60)
 
−
    print "\t-l logfile    Write poll data to indicated log file"
 
−
 
−
 
−
def main():
 
−
    global poll_freq_hz
 
−
    global run_time_sec
 
−
    global logfile
 
−
 
−
    # parse options
 
−
    # see: http://docs.python.org/lib/module-getopt.html
 
−
    try:
 
−
        opts, args = getopt.getopt(sys.argv[1:], "hf:t:l:")
 
−
    except getopt.GetoptError:
 
−
        usage()
 
−
        sys.exit(2)
 
−
    for o, a in opts:
 
−
        if '-h' == o:
 
−
            usage()
 
−
            sys.exit()
 
−
        if '-f' == o:
 
−
            poll_freq_hz = int(a)
 
−
        if '-t' == o:
 
−
            run_time_sec = int(a)*60
 
−
        if '-l' == o:
 
−
            logfile = a
 
−
 
−
    # do stuff
 
−
    print 'running for ' + str(run_time_sec/60) + ' minutes'
 
−
    print 'poll rate:  ' + str(poll_freq_hz) + ' Hz'
 
−
    print 'logfile:    ' + str(logfile)
 
−
    collectPowerData()
 
−
    print '...done!'
 
−
    print 'Data points collected: ' + str(len(log_data))
 
−
    sum = 0
 
−
    for d in log_data:
 
−
        sum += int(d['battery0_state_present_rate'])
 
−
    avg = float(sum) / float(len(log_data))
 
−
    print 'Average power draw:    ' + str(avg)
 
−
    if logfile:    log = open(logfile, 'a')
 
−
    if log:
 
−
        log.write('Average power draw:    ' + str(avg))
 
−
        log.close()
 
−
 
−
 
−
if __name__ == '__main__':
 
−
    main()
 
−
</pre>
 
  
 
[[Category:Scripts]]
 
[[Category:Scripts]]

Latest revision as of 23:53, 19 August 2006

The intent of this script is to provide Thinkpad power consumption data. Hopefully the data will allow you to identify areas with higher-than-expected power draw. The ultimate goal of this script is to act as a tool that will ultimately allow you to maximize your battery time under Linux.

The script is written in Python.

ATTENTION!
You will probably need to modify this script to suit your needs. The original script was written using a Thinkpad T43 2668-89U. So, for example, if your particular does not have an ATI video card, then you will definately need to modify the script.
Help needed
The major deficiency with the script, in its current form, is that it does not consolidate the collected data. I would suggest that the "tpPowerMonitorDataSource" class be extended so that instances of that class know how to interpret their own data (from which nice reports can be generated).
NOTE!
Please feel very free to improve/fix this script. My intent for its posting is to make its ownership as public as possible. There's no need to try to E-mail me to validate your changes. If you feel they are in the best interest of the public, just make the changes. The script attempts to employ pre-conditions to intelligently apply functionality only to those laptops that appear to support it. Hopefully, its framework will allow for extension without heavy redesign.

The script

tp_power_monitor.py (download)