[Zope-Checkins] CVS: Zope/lib/python/Products/ForensicLogger/procps/linuxproc-1.0 - README.txt:1.1.2.1 linuxproc.py:1.1.2.1 pystat.py:1.1.2.1

Andreas Jung andreas@zope.com
Mon, 22 Oct 2001 15:01:07 -0400


Update of /cvs-repository/Zope/lib/python/Products/ForensicLogger/procps/linuxproc-1.0
In directory cvs.zope.org:/tmp/cvs-serv32212/ForensicLogger/procps/linuxproc-1.0

Added Files:
      Tag: ajung-forensiclogging
	README.txt linuxproc.py pystat.py 
Log Message:
added


=== Added File Zope/lib/python/Products/ForensicLogger/procps/linuxproc-1.0/README.txt ===
linuxproc -- A Python module for investigating data stored in Linux' /proc fs.

This module has been tested under Linux kernel versions 2.2.18 and 2.4.2.
For general Linux /proc filesystem info, see your Linux source tree's proc
documentation file (on my system it's /usr/src/linux/Documentation/proc.txt).
Most of the data exposed to Python is performance-related so far.

An associated module is likely available if you're reading this file named
pystat.py, which is a python vmstat clone which uses the linuxproc module.

linuxproc API:

get_kernel_version() -- returns a tuple representing the kernel version,
   e.g. ('2', '2', '18').

proc_statm(pid) -- returns a dictionary of items kept in a process'
   /proc/%(pid)/statm file.  The dict members are:

   size       total program size
   resident   size of in memory portions
   shared     number of the pages that are shared
   trs        number of pages that are 'code'
   drs        number of pages of data/stack
   lrs        number of pages of library
   dt         number of dirty pages

self_statm() -- returns proc_statm info for the running process

proc_stat() -- returns a dictionary of items kept in a process'
   /proc/%pid/stat file.  The members of the dict are below:

   'pid', 'comm', 'state', 'ppid', 'pgrp', 'session', 'tty', 'tty_pgrp',
   'flags', 'min_flt', 'cmin_flt', 'maj_flt', 'cmaj_flt', 'utime',
   'stime', 'cutime', 'cstime', 'priority', 'nice', 'NULL',
   'it_real_value', 'start_time', 'vsize', 'rss', 'rlim', 'start_code',
   'end_code', 'start_stack', 'esp', 'eip', 'signal',
   'blocked', 'sigign', 'sigcatch', 'wchan', 'nswap', 'cnswap',
   'exit_signal', 'processor'

   See the proc(5) manpage for info on these items.

self_stat() -- returns proc_stat info for the running process

loadavg() -- returns a dict representing /proc/loadavg info.  Dict members:

   '1min', '5min', '15min', 'running', 'cumulative'

   See the proc(5) manpage for info on these items.
   
uptime() -- returns a dict representing /proc/uptime info.  Dict members:

   'uptime' -- number of seconds that the system has been up 
   'idle'   -- number of seconds that the system has been idle

stat() -- returns a dict of dicts representing /proc/stat info.
   Some of these items will not be available on your kernel version.

    'cpu'       : 'user', 'nice', 'system', 'idle'
    'cpu0'      : 'user', 'nice', 'system', 'idle'
    'cpu1'      : 'user', 'nice', 'system', 'idle'
    'cpu2'      : 'user', 'nice', 'system', 'idle'
    'cpu3'      : 'user', 'nice', 'system', 'idle'
    'cpu4'      : 'user', 'nice', 'system', 'idle'
    'cpu5'      : 'user', 'nice', 'system', 'idle'
    'cpu6'      : 'user', 'nice', 'system', 'idle'
    'cpu7'      : 'user', 'nice', 'system', 'idle'
    'cpu8'      : 'user', 'nice', 'system', 'idle'
    'cpu9'      : 'user', 'nice', 'system', 'idle'
    'cpu10'     : 'user', 'nice', 'system', 'idle'
    'cpu11'     : 'user', 'nice', 'system', 'idle'
    'page'      : 'in', 'out'
    'swap'      : 'in', 'out'
    'intr'      : 'interrupts'
    'disk'      : 'unknown1', 'unknown2', 'unknown3'
    'disk_io'   : 'unknown1', 'unknown2', 'unknown3'
    'disk_rio'  : 'unknown1', 'unknown2', 'unknown3'
    'disk_wio'  : 'unknown1', 'unknown2', 'unknown3'
    'disk_rblk' : 'unknown1', 'unknown2', 'unknown3'
    'disk_wblk' : 'unknown1', 'unknown2', 'unknown3'
    'ctxt'      : 'context_switches'
    'btime'     : 'boot_time'
    'processes' : 'processes'

    See the proc(5) manpage for more info on these items.

meminfo() -- return dict representing memory information related to
   /proc/meminfo.  Dict members:

   'buffers'  : buffer mem in K
   'swapfree' : free swap space in K
   'memshared': shared memory in K
   'swaptotal': total swap space in K
   'memtotal' : total memory in K
   'memfree'  : free memory in K
   'cached'   : cached memory in K

   There may be other or different dict members depending on your
   kernel version.

   See the proc(5) manpage for more info on these items.

getrunners(getsleepers=0) -- return tuple in the form (running, swapped,
   blocked), where each of running, swapped, or blocked is a list containing
   the pids of processes which are running, swapped, or blocked.

   If getsleepers is true, tuple returned is (running, swapped,
   blocked, sleeping).

Chris McDonough
(chrism@zope.com)






=== Added File Zope/lib/python/Products/ForensicLogger/procps/linuxproc-1.0/linuxproc.py ===
##############################################################################
# 
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
# 
# Copyright (c) Digital Creations.  All rights reserved.
# 
# This license has been certified as Open Source(tm).
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# 
# 1. Redistributions in source code must retain the above copyright
#    notice, this list of conditions, and the following disclaimer.
# 
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions, and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
# 
# 3. Digital Creations requests that attribution be given to Zope
#    in any manner possible. Zope includes a "Powered by Zope"
#    button that is installed by default. While it is not a license
#    violation to remove this button, it is requested that the
#    attribution remain. A significant investment has been put
#    into Zope, and this effort will continue if the Zope community
#    continues to grow. This is one way to assure that growth.
# 
# 4. All advertising materials and documentation mentioning
#    features derived from or use of this software must display
#    the following acknowledgement:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    In the event that the product being advertised includes an
#    intact Zope distribution (with copyright and license included)
#    then this clause is waived.
# 
# 5. Names associated with Zope or Digital Creations must not be used to
#    endorse or promote products derived from this software without
#    prior written permission from Digital Creations.
# 
# 6. Modified redistributions of any form whatsoever must retain
#    the following acknowledgment:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    Intact (re-)distributions of any official Zope release do not
#    require an external acknowledgement.
# 
# 7. Modifications are encouraged but must be packaged separately as
#    patches to official Zope releases.  Distributions that do not
#    clearly separate the patches from the original work must be clearly
#    labeled as unofficial distributions.  Modifications which do not
#    carry the name Zope may be packaged in any form, as long as they
#    conform to all of the clauses above.
# 
# 
# Disclaimer
# 
#   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
#   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
#   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
#   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
#   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
#   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
#   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
#   SUCH DAMAGE.
# 
# 
# This software consists of contributions made by Digital Creations and
# many individuals on behalf of Digital Creations.  Specific
# attributions are listed in the accompanying credits file.
# 
##############################################################################
""" module for investigating the Linux /proc filesystem

"""
import os, re, string
__version__ = '1.0'

DIGITS = {}
for d  in "1234567890":
    DIGITS[d] = 1

# linux 2_2_18 /proc/self/stat format
l2_2_18_procstat = [
    'pid', 'comm', 'state', 'ppid', 'pgrp', 'session', 'tty', 'tty_pgrp',
    'flags', 'min_flt', 'cmin_flt', 'maj_flt', 'cmaj_flt', 'utime',
    'stime', 'cutime', 'cstime', 'priority', 'nice', 'NULL',
    'it_real_value', 'start_time', 'vsize', 'rss', 'rlim', 'start_code',
    'end_code', 'start_stack', 'esp', 'eip', 'signal',
    'blocked', 'sigign', 'sigcatch', 'wchan', 'nswap', 'cnswap',
    'exit_signal', 'processor'
    ]

# linux 2_2_18 /proc/loadavg format
l2_2_18_loadavg = ['1min', '5min', '15min', 'running', 'cumulative']

# linux 2_2_18 /proc/uptime format
l2_2_18_uptime = ['uptime', 'idle']

# linux 2_2_18 /proc/self/statm format
l2_2_18_procstatm = ['size', 'resident', 'shared', 'trs', 'drs', 'lrs', 'dt']

# linix 2_2_18 /proc/stat format
l2_2_18_stat = {
    'cpu'       : ['user', 'nice', 'system', 'idle'],
    'disk'      : ['unknown1', 'unknown2', 'unknown3'],
    'disk_rio'  : ['unknown1', 'unknown2', 'unknown3'],
    'disk_wio'  : ['unknown1', 'unknown2', 'unknown3'],
    'disk_rblk' : ['unknown1', 'unknown2', 'unknown3'],
    'disk_wblk' : ['unknown1', 'unknown2', 'unknown3'],
    'page'      : ['in', 'out'],
    'swap'      : ['in', 'out'],
    'intr'      : ['interrupts'],
    'ctxt'      : ['context_switches'],
    'btime'     : ['boot_time'],
    'processes' : ['processes'],
    }

# Linux 2_4_2 /proc/stat info
l2_4_2_stat = {
    'cpu'       : ['user', 'nice', 'system', 'idle'],
    'cpu0'      : ['user', 'nice', 'system', 'idle'],
    'cpu1'      : ['user', 'nice', 'system', 'idle'],
    'cpu2'      : ['user', 'nice', 'system', 'idle'],
    'cpu3'      : ['user', 'nice', 'system', 'idle'],
    'cpu4'      : ['user', 'nice', 'system', 'idle'],
    'cpu5'      : ['user', 'nice', 'system', 'idle'],
    'cpu6'      : ['user', 'nice', 'system', 'idle'],
    'cpu7'      : ['user', 'nice', 'system', 'idle'],
    'cpu8'      : ['user', 'nice', 'system', 'idle'],
    'cpu9'      : ['user', 'nice', 'system', 'idle'],
    'cpu10'     : ['user', 'nice', 'system', 'idle'],
    'cpu11'     : ['user', 'nice', 'system', 'idle'],
    'page'      : ['in', 'out'],
    'swap'      : ['in', 'out'],
    'intr'      : ['interrupts'],
    'disk_io'   : ['unknown1', 'unknown2', 'unknown3'],
    'ctxt'      : ['context_switches'],
    'btime'     : ['boot_time'],
    'processes' : ['processes'],
    }

version_map = {
    ('2','2','18'): { 'procstat' : l2_2_18_procstat,
                      'loadavg' : l2_2_18_loadavg,
                      'uptime' : l2_2_18_uptime,
                      'stat': l2_2_18_stat,
                      'procstatm' : l2_2_18_procstatm,
                      },
    ('2','4','2') : { 'procstat' : l2_2_18_procstat,
                      'loadavg' : l2_2_18_loadavg,
                      'uptime' : l2_2_18_uptime,
                      'stat': l2_4_2_stat,
                      'procstatm' : l2_2_18_procstatm,
                      },
    }

version_guess_map = {
    ('2', '2')  :  ('2', '2', '18'),
    ('2', '4')  :  ('2', '4', '2'),
    }

def get_struct(v, vmap=version_map.get, guess=version_guess_map.get):
    return vmap(v) or vmap(guess(tuple(v[:2])))

def get_kernel_version():
    version_match = re.compile(r'(\d+)\.(\d+)\.(\d+)').search
    s = open('/proc/version').readline()
    match = version_match(s)
    return (match.group(1), match.group(2), match.group(3))

VERSION = get_kernel_version()

def proc_statm(pid):
    return get_dict('procstatm', '/proc/%s/statm' % pid)
    
def self_statm():
    return get_dict('procstatm', '/proc/self/statm')

def proc_stat(pid):
    return get_dict('procstat', '/proc/%s/stat' % pid)

def self_stat():
    return get_dict('procstat', '/proc/self/stat')

def loadavg():
    return get_dict('loadavg', '/proc/loadavg')

def uptime():
    return get_dict('uptime', '/proc/uptime')

def stat():
    return get_multidict('stat', '/proc/stat')

def meminfo():
    """ very expensive on 2.2 kernels, not so on 2.4 kernels """
    lines = []; dict = {}
    for line in open('/proc/meminfo').readlines():
        if line:
            l = string.split(line)
            name, value = l[0], l[1:]
            if name in('total:', 'Mem:', 'Swap:'):
                continue
            key = string.lower(name)[:-1]
            value = value[0]
            if value and DIGITS.has_key(value[0]):
                value = maybe_number(value)
            dict[key] = value
    return dict

def getrunners(getsleepers=0, isdigit=DIGITS.has_key):
    running = []; swapped = []; blocked = []; sleeping = []
    addtorun   = running.append
    addtoswap  = swapped.append
    addtoblock = blocked.append
    addtosleep = sleeping.append
    names = os.listdir('/proc')
    for name in names:
        excp_happened = 0
        if isdigit(name[0]):
            try: stat = proc_stat(name)
            except: excp_happened = 1
            if excp_happened: continue
            state = stat['state']
            rss   = stat['rss']
            if getsleepers and state == "S":
                addtosleep(name)
            elif state == "R":
                if rss > 0: addtorun(name)
                else: addtoswap(name)
            elif state == "D":
                if rss > 0: addtoblock(name)
                else: addtoswap(name)
    if getsleepers:
        return running, blocked, swapped, sleeping
    else:
        return running, blocked, swapped

def get_dict(structname, filename, isdigit=DIGITS.has_key,
             find=string.find):
    lookup = get_struct(VERSION)[structname]
    d = {}; i = 0
    raw = string.split(open(filename).readline())
    for value in raw:
        if value and isdigit(value[0]):
            value = maybe_number(value)
        name = lookup[i]
        d[name] = value
        i = i + 1
    return d

def get_multidict(structname, filename, isdigit=DIGITS.has_key,
                  find=string.find):
    dict = {}
    lookup = get_struct(VERSION)[structname]
    for line in open(filename, 'r').readlines():
        l = string.split(line)
        category = l[0]
        if not lookup.has_key(category):
            continue
        items = l[1:]
        d = {}; i = 0
        for name in lookup[category]:
            value = items[i]
            if value and isdigit(value[0]):
                value = maybe_number(value)
            d[name] = value
            i = i + 1
        dict[category] = d
    return dict

def maybe_number(value, find=string.find, float=float, int=int):
    try:
        if find(value, '.') != -1:
            return float(value)
        else:
            return int(value)
    except ValueError:
        return value

if __name__ == '__main__':
    import time
    start = time.time()
    for x in xrange(100):
        getrunners()
        uptime()
        loadavg()
        self_statm()
        self_stat()
        stat()
        meminfo()
    end = time.time()
    print end-start
    #import sys
    #sys.exit(0)
    for key, value in self_stat().items():
         print key, value
    for key, value in loadavg().items():
          print key, value
    for key, value in uptime().items():
          print key, value
    for key, value in stat().items():
          print key, value
    for key, value in meminfo().items():
          print key, value
    for key, value in self_statm().items():
          print key, value




=== Added File Zope/lib/python/Products/ForensicLogger/procps/linuxproc-1.0/pystat.py ===
#!/bin/python
##############################################################################
# 
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
# 
# Copyright (c) Digital Creations.  All rights reserved.
# 
# This license has been certified as Open Source(tm).
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# 
# 1. Redistributions in source code must retain the above copyright
#    notice, this list of conditions, and the following disclaimer.
# 
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions, and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
# 
# 3. Digital Creations requests that attribution be given to Zope
#    in any manner possible. Zope includes a "Powered by Zope"
#    button that is installed by default. While it is not a license
#    violation to remove this button, it is requested that the
#    attribution remain. A significant investment has been put
#    into Zope, and this effort will continue if the Zope community
#    continues to grow. This is one way to assure that growth.
# 
# 4. All advertising materials and documentation mentioning
#    features derived from or use of this software must display
#    the following acknowledgement:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    In the event that the product being advertised includes an
#    intact Zope distribution (with copyright and license included)
#    then this clause is waived.
# 
# 5. Names associated with Zope or Digital Creations must not be used to
#    endorse or promote products derived from this software without
#    prior written permission from Digital Creations.
# 
# 6. Modified redistributions of any form whatsoever must retain
#    the following acknowledgment:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    Intact (re-)distributions of any official Zope release do not
#    require an external acknowledgement.
# 
# 7. Modifications are encouraged but must be packaged separately as
#    patches to official Zope releases.  Distributions that do not
#    clearly separate the patches from the original work must be clearly
#    labeled as unofficial distributions.  Modifications which do not
#    carry the name Zope may be packaged in any form, as long as they
#    conform to all of the clauses above.
# 
# 
# Disclaimer
# 
#   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
#   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
#   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
#   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
#   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
#   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
#   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
#   SUCH DAMAGE.
# 
# 
# This software consists of contributions made by Digital Creations and
# many individuals on behalf of Digital Creations.  Specific
# attributions are listed in the accompanying credits file.
# 
##############################################################################
""" python vmstat for linux, requires linuxproc.py module """
__version__ = "1.0"
import linuxproc
import sys, time
HEIGHT = 24
DIGITS = {}
for d  in "1234567890":
    DIGITS[d] = 1

def showheader(timestamp):
    head="%8s%28s%8s%12s%11s%12s" % ('procs','memory','swap','io','system',
                                     'cpu')
    thead = head +"%12s" % 'time'
    print timestamp and thead or head
    s = "%2s %2s %2s %6s %6s %6s %6s %3s %3s %5s %5s %4s %5s %3s %3s %3s"
    items = (
        'r','b','w','swpd','free','buff','cache','si','so','bi','bo','in',
        'cs','us','sy','id'
        )
    print s % items

def meminfo(mem=None):
    if not mem: mem = linuxproc.meminfo()
    swpd  = mem['swaptotal'] - mem['swapfree']
    return swpd, mem['memfree'], mem['buffers'], mem['cached']

def getcpu(stat=None):
    if not stat: stat = linuxproc.stat()
    cpu = stat['cpu']
    return cpu['user'], cpu['nice'], cpu['system'], cpu['idle']

def getswap(stat=None):
    if not stat: stat = linuxproc.stat()
    swap = stat['swap']
    return swap['in'], swap['out']

def getpage(stat=None):
    if not stat: stat = linuxproc.stat()
    page = stat['page']
    return page['in'], page['out']

def getall():
    stat = linuxproc.stat()
    d = {}
    lr, lb, ls = map(lambda x: len(x), linuxproc.getrunners())
    mswap, mfree, mbuf, mcache = meminfo()
    swapin, swapout = getswap(stat)
    pgin, pgout = getpage(stat)
    intr = stat['intr']['interrupts']
    ctxt = stat['ctxt']['context_switches']
    cuse, cnice, csys, cidl = getcpu(stat)
    loc = locals()
    for name in ('lr', 'lb', 'ls', 'mswap', 'mfree', 'mbuf', 'mcache',
                 'swapin', 'swapout', 'pgin', 'pgout', 'intr', 'ctxt', 'cuse',
                 'cnice', 'csys', 'cidl'):
        d[name] = loc[name]
    return d

def main(headers, count, delay, timestamp):
    i = 0
    HZ = 100L
    KB_PER_PAGE = 1
    showheader(timestamp)
    l = [None, None]
    stat = l[0] = getall()
    duse = stat['cuse'] + stat['cnice']
    dsys = stat['csys']
    didl = stat['cidl']
    Div = duse + dsys + didl
    divo2 = Div/2
    fswapin  = (stat['swapin']  * KB_PER_PAGE * HZ + divo2)/Div
    fswapout = (stat['swapout'] * KB_PER_PAGE * HZ + divo2)/Div
    fpgin    = (stat['pgin']                  * HZ + divo2)/Div
    fpgout   = (stat['pgout']                 * HZ + divo2)/Div
    fintr    = (stat['intr']                  * HZ + divo2)/Div
    fctxt    = (stat['ctxt']                  * HZ + divo2)/Div
    fcusr    = (100L * duse + divo2)/Div
    fcsys    = (100L * dsys + divo2)/Div
    fcidle   = (100L * didl + divo2)/Div
    flr      = stat['lr'] - 1 # minus ourselves
    flb      = stat['lb']
    fls      = stat['ls']
    fmswap   = stat['mswap']
    fmfree   = stat['mfree']
    fmbuf    = stat['mbuf']
    fmcache  = stat['mcache']
    output(
        flr, flb, fls, fmswap, fmfree, fmbuf, fmcache, fswapin, fswapout,
        fpgin, fpgout, fintr, fctxt, fcusr, fcsys, fcidle,
        timestamp and time.asctime() or ''
        )
    
    # if we're repeating, do it below
    tog = 0
    i = 0
    while 1:
        i = i + 1
        if not delay: break
        if count and i == count: break
        tog = not tog
        time.sleep(delay)
        if i % HEIGHT == 0 and headers:
            showheader(timestamp)
        stat = l[tog] = getall()
        old  = l[not tog]
        duse = (stat['cuse']-old['cuse']) + (stat['cnice']-old['cnice'])
        dsys = (stat['csys']-old['csys'])
        didl = (stat['cidl']-old['cidl'])
        # idle may run backwards (kernel "feature")
        if didl < 0:
            didl = 0
        Div = duse + dsys + didl
        divo2 = Div/2
        pero2 = delay/2
        flr      = stat['lr'] - 1 # minus ourselves
        flb      = stat['lb']
        fls      = stat['ls']
        fmswap   = stat['mswap']
        fmfree   = stat['mfree']
        fmbuf    = stat['mbuf']
        fmcache  = stat['mcache']
        fswapin  = ((stat['swapin']-old['swapin'])   *KB_PER_PAGE+pero2)/delay
        fswapout = ((stat['swapout']-old['swapout']) *KB_PER_PAGE+pero2)/delay
        fpgin    = ((stat['pgin']-old['pgin'])                   +pero2)/delay
        fpgout   = ((stat['pgout']-old['pgout'])                 +pero2)/delay
        fintr    = ((stat['intr']-old['intr'])                   +pero2)/delay
        fctxt    = ((stat['ctxt']-old['ctxt'])                   +pero2)/delay
        fcusr    = (100L * duse + divo2)/Div
        fcsys    = (100L * dsys + divo2)/Div
        fcidle   = (100L * didl + divo2)/Div
        output(
            flr, flb, fls, fmswap, fmfree, fmbuf, fmcache, fswapin, fswapout,
            fpgin, fpgout, fintr, fctxt, fcusr, fcsys, fcidle,
            timestamp and time.asctime() or ''
            )
def output(*args):
    tfmt="%2u %2u %2u %6u %6u %6u %6u %3u %3u %5u %5u %4u %5u %3u %3u %3u %s"
    fmt = tfmt[:-3]
    if len(args) == 17:
        print tfmt % args
    elif len(args) == 16:
        print fmt % args

def write_tstamp():
    print time.asctime()

def usage():
    print "usage: %s [-V] [-n] [-t] [delay [count]]" % sys.argv[0]
    print "            -V prints version."
    print "            -n causes the headers not to be reprinted regularly."
    print "            -t causes a timestamp to be printed."
    print "            delay is the delay between updates in seconds."
    print "            count is the number of updates."
    
if __name__ == '__main__':
    delay_seen = None
    headers    = 1
    timestamps = 0
    count      = 0
    delay      = 0
    for arg in sys.argv[1:]:
        if arg[0] != '-':
            for char in arg:
                if not DIGITS.has_key(char):
                    usage()
                    sys.exit(1)
            if delay_seen:
                count = int(arg)
            else:
                delay = int(arg)
                delay_seen = 1
        elif arg[1:] and arg[1] == 'V':
            print sys.argv[0], 'version %s' % __version__
            sys.exit(0)
        elif arg[1:] and arg[1] == 'n':
            headers = 0
        elif arg[1:] and arg[1] == 't':
            timestamps = 1
        else:
            usage()
            sys.exit(1)
    main(headers, count, delay, timestamps)