[Orca-dev] [PATCH] r528/data_gatherers/aix/orca-aix-stat.pl.in

David Michaels dragon at raytheon.com
Thu Apr 6 17:49:52 PDT 2006


This is more like a rewrite / replacement than a patch.  There have been 
enough changes that the patch is 50% longer than the original file.  
I've retooled the script quite a bit, and have distributed it personally 
to some users who've requested it on orca-users.  I suspect most AIX 
Orca users are using my version of this script.  I think even Rajesh is 
using it.

I've attached a copy of the script and, for completeness, a copy of the 
patch.

Here's a summary of some of the changes I've made since Rajesh first 
distributed the script to Orca two years ago.  The big rewrite in 2005 
was not logged.

Patched by: Dave "Dragon" Michaels <dragon at raytheon.com>

* r528/data_gatherers/aix/orca-aix-stat.pl.in
   2006-04-06 - Added ^L page markers
   2006-04-05 - Modified how physical memory cap is identified
                Now uses prtconf instead of lslpp mumbo jumbo
              - corrected memory calculation
              - added check to verify memory cap identification
                If it didn't work, use a (bogus) default
              - added conditional for uptime parsing - N days (only)
              - shifted where some warnings are printed
   2006-01-31 - Added command-line options for TSM stuff:
                  o specify TSM ID/password
                  o disable TSM collection, even if present
              - TSM id/password now specified as a global variable
              - fixed warning messages to appear prior
                to corresponding header/data variable reset
              - updated version to 1.8
              - injected patterns for gnu-configure integration
                (only OUT_ROOT cared)
   2004-08-19 - Not yet ready for ./configure integration
              - Change: Redesign of some sections to run 24x7 using kernel
                counters instead of script cycles to compute
                needed values quickly every 5 minutes
              - Change: Duration < 0 indicates infinite loop, consistent
                with other orcallators
              - Duration > 0 < 24 indicates run every 300 seconds
                for Duration hours
              - Duration = 0 or Duration >= 24 is undefined


-------------- next part --------------
#!/usr/bin/perl
#
# Version 1.20

# Description:
# Collect general perfromance statistics formatted for
# interpretaion by Orca.

# Usage: 
# The following variables may be set:
# 
# OUT_ROOT root directory for datafiles (eg /opt/log/performance)
# INTERVAL the number of seconds between checks (eg 300 = 5 min)
# DURATION numer of hours to run (eg 1 = 1 hr)
#          < 0 means run indefinitely
#          0 is undefined
#          1-24 means run for N hours
# 
# This script runs various standard system utilities to collect
# system performance statistics and writes them out to datafile named
# HOSTNAME/stats.YYYY-MM-DD-HHmm under the OUT_ROOT directory.
# 
# It runs for the the numbers specified by DURATION collecting data
# every INTERVAL number of seconds. After DURATION, the script
# closes and compresses it's datafile via /usr/bin/compress and then
# exits. If DURATION=24 and INTERVAL=300 (recommended) then the
# following cron entry would collect continuos stats for a system:
# 
# 0 0 * * * /<PATH_TO_SCRIPT>/orca-aix-stat.pl
# 
# v1.20
# 2006-04-06 - DM - Added ^L page markers
# 2006-04-05 - DM - Modified how physical memory cap is identified
#                   Now uses prtconf instead of lslpp mumbo jumbo
#                 - corrected memory calculation
#                 - added check to verify memory cap identification
#                   If it didn't work, use a (bogus) default
#                 - added conditional for uptime parsing - N days (only)
#                 - shifted where some warnings are printed 
# v1.8
# 2006-01-31 - DM - Added command-line options for TSM stuff:
#                   o specify TSM ID/password
#                   o disable TSM collection, even if present
#                 - TSM id/password now specified as a global variable
#                 - fixed warning messages to appear prior
#                   to corresponding header/data variable reset
#                 - updated version to 1.8
#                 - injected patterns for gnu-configure integration
#                   (only OUT_ROOT cared)
# 2004-08-19 - DM - Not yet ready for ./configure integration
#                 - Change: Redesign of some sections to run 24x7 using kernel
#                   counters instead of script cycles to compute
#                   needed values quickly every 5 minutes
#                 - Change: Duration < 0 indicates infinite loop, consistent
#                   with other orcallators
#                 - Duration > 0 < 24 indicates run every 300 seconds
#                   for Duration hours
#                 - Duration = 0 or Duration >= 24 is undefined
#                 DM = by Dave "Dragon" Michaels (dragon at raytheon.com)
# 2003-09-10 - RV - Modified for AIX 4.3/5.x.. by Rajesh Verma
#                       (rajeshverma at aixdude.com)
# v1.7       - RV - ignores /proc now
# 2001-04-16 - JDK - Genesis... by Jason D. Kelleher
# 2001-05-02 - JDK - Updates to make data aggregation easier.
# Added #open connections, pagestotl.
# 2001-07-06 - JDK - added command-line args & data checks
# 2001-07-09 - JDK - added signal handler, column checks, & umask
# 2001-07-10 - JDK - now autodetects interfaces via netstat -i
# v1.5
#
# $HeadURL: file:///var/www/svn/repositories-public/orcaware-public/orca/trunk/data_gatherers/aix/orca-aix-stat.pl.in $
# $LastChangedDate: 2006-04-06 17:36:41 -0700 (Thu, 06 Apr 2006) $
# $LastChangedBy: dragon at raytheon.com $
# $LastChangedRevision: 528 $
# 
# Note: Execution speed is more important than cleanliness here.

# Explicitly set PATH to prevent odd problems if run manually.

$Usage_Message = '
Usage: orca-aix-stat.pl [-r out_root] [-i interval] [-d duration]
                        [-tid ID] [-tpw PASSWORD] [-notsm] [-h]

-r out_root   - set root output directory, default: /opt/log/performance
-i interval   - number of seconds between checks, default: 300
-d duration   - number of hours to run, default: 24, -1: never terminate
-h            - this message
-tid ID       - the TSM user ID to use for dsmadmc commands
-tpw PASSWORD - the TSM password to use for dsmadmc commands
-notsm        - do not gather TSM info

';
############################
# These are the packages you need to install
# 1. perl
# 2. openssh - if using ssh to the collector server
# 3. openssl
# 4. zlib
# 5. rsync - To copy file to the collector server
# 6. gzip - to zip the files
# 7. rpm.rte - to install rpm tools
#
# This the site you can file everything
# http://www-1.ibm.com/servers/aix/products/aixos/linux/download.html
# http://www.bullfreeware.com
#
#
#  Good Luck, Rajesh Verma (rajeshverma at yahoo.com)
##############################
#
# Parse the command line arguments
while ( $#ARGV >= 0 ) {

    if ( $ARGV[0] eq "-r" ) {
        shift @ARGV;
        $OUT_ROOT = shift @ARGV;
    }
    elsif ( $ARGV[0] eq "-notsm" ) {
        shift @ARGV;
        $DOTSM = "no";
    }
    elsif ( $ARGV[0] eq "-tid" ) {
        shift @ARGV;
        $TSMID = shift @ARGV;
    }
    elsif ( $ARGV[0] eq "-tpw" ) {
        shift @ARGV;
        $TSMPW = shift @ARGV;
    }
    elsif ( $ARGV[0] eq "-i" ) {
        shift @ARGV;
        $INTERVAL = shift @ARGV;
    }
    elsif ( $ARGV[0] eq "-d" ) {
        shift @ARGV;
        $DURATION = shift @ARGV;
    }
    elsif ( $ARGV[0] eq "-h" ) {
        print $Usage_Message;
        exit 0;
    }
    elsif ( $ARGV[0] =~ /^-/ ) {
        die "Invalid flag: $ARGV[0]\n$Usage_Message";
    }
    else {
        die "Invalid argument: $ARGV[0]\n$Usage_Message";
    }
}

## BEGIN set defaults
# 
$OUT_ROOT ||= '@VAR_DIR@/AIXorcallator';    # root directory for datafiles
$DOTSM ||= "yes";
$TSMID ||= 'orca';
$TSMPW ||= 'orca';
$INTERVAL ||= 300;                       # seconds between checks
$DURATION ||= 24;                        # number of hours to run

## END set defaults

## Derived variables.
$iterations = $DURATION * 60 * 60 / $INTERVAL;    # Number of checks.
chomp( $HOST = `uname -n` );
$out_dir = "${OUT_ROOT}/${HOST}";
( $osec, $omin, $ohour, $omday, $omon, $oyear, $owday, $oyday, $oisdst ) =
  localtime(time);
( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
  localtime(time);
$stat_file =
  sprintf( "%s/percol-%.2d-%.2d-%.2d-%02d%.2d%.2d", $out_dir, $year + 1900, $mon + 1,
  $mday, $hour, $min, $sec );

$start_time = time();
$timestamp  = $start_time;

#
# find the full paths of all commands,
# and deposit them into a variable $<name of cmd>_cmd
@pathdirs = reverse grep(m!^/!, split(/:/, $ENV{'PATH'}, 999));
$compressor = "@COMPRESSOR@";
if ( $compressor =~ "@" . "COMPRESSOR" . "@" ) {
    # hmm, config must not have fixed it, so set it to a default
    $compressor = "bzip2";
}
        
@cmds = (
         $compressor,
         "netstat", 
         "ls", 
         "ctxqsession", 
         "prtconf",
         "uptime",
         "ps",
         "pstat",
         "vmstat",
         "df",
         "iostat",
         "svmon");
if ( "$DOTSM" == "yes" ) {
    @cmds = ( @cmds, "dsmadmc" );
}
foreach $cmd ( @cmds ) {
    $cmdvar = "";
    if ( $cmd !~ m:^/: ) {
        # don't have full path yet
        foreach $dir ( @pathdirs ) {
            if (-x "$dir/$cmd") {
                $cmdvar = $cmd . "_cmd";
                $$cmdvar="$dir/$cmd";
                # for "ls", that evalutes to $ls_cmd, so in later
                # code, $ls_cmd will expand to the full path, thus
                # avoiding path traversal overhead
            }
        }
    }
    printf "%20s ", $cmd;
    $$cmdvar ? print "=> $$cmdvar\n" : print "doesn't exist in PATH; Not collecting\n";

}
#
## Autodetect network interfaces
#open IN, "ifconfig -a|";
open IN, "$netstat_cmd -ni|";
while (<IN>) {

    # if ( /^(\S+):/ ) {
    if (/^(\w+).*link/) {
        push @net_interfaces, $1;
    }
}
close IN;

# Grab some base system info prior to collecting stats.
open IN, "$prtconf_cmd -m |";
while (<IN>) {
    if (/^Memory Size: (\d+) MB/) {
        $pagestotl = $1 * 1024 * 1024 / 4096;    # Grab realmem in MB and convert to pages.
        $mem_totl = $1 * 1024 * 1024;    # Grab realmem in MB and convert to Bytes

        # this gets used down in the vmstat section
    } else {
        print STDERR "ERROR: could not compute memorysize from $prtconf_cmd -m\n";
        $pagestotl = 100;
        $mem_totl = 100;
    }
}
close IN;

## Make sure we can write output.
umask 0022;    # make sure the file can be harvested
unless ( -d $out_dir ) {
    system( "mkdir", "-p", "$out_dir" );
}

# Set signal handlers to close and compress the output
# file just in case.
$SIG{HUP}  = \&exit_nicely;
$SIG{INT}  = \&exit_nicely;
$SIG{QUIT} = \&exit_nicely;
$SIG{TERM} = \&exit_nicely;

# Set gloabals used for printing (or not) headers.
$need_header     = 1;   # our file starts empty, so we need a header by default
$prev_header_cnt = 0;
$prev_info_cnt   = 0;

# Set globals used for tracking counts from interval to interval
# all counts start at zero
( $disk_t_prev,
  $disk_rK_prev,
  $disk_wK_prev,
  $citrix_sessions, 
  $citrix_disconnect ) = ( 0, 0, 0, 0, 0 );
#
# set up a data structure to hold info about each
# interface, to carry from iteration to iteration,
# so we can calculate per-second data based on delta
# of totals between iterations
#
%netstat;
foreach $if (@net_interfaces) {
    %netstat=(%netstat,
              $if => {});
    $netstat{$if}={"ipkt" => -1,
                   "ierr" => -1,
                   "opkt" => -1,
                   "oerr" => -1,
                   "coll" => -1};
}

$context_sw_prev=0;

# initialize all vmstat hash keys to -1, in a slick,
# if mystifying fashion, making it real easy to add
# more keys later, if need be
%vmstat;
#          1 2 3   4   5  6  7  8 
#      9  10 11 12 13 14 15 16 17
@vmkeys=qw(r b avm fre re pi po fr
	   sr cy in sys cs us sy id wa);
@vmstat{@vmkeys} = (-1) x @vmkeys;
$vmstat{proc_s} = -1;
$vmstat{forks} = -1;

#
# Main while loop
while ( $DURATION < 0 || $iterations-- > 0 ) {

    $timestamp = $timestamp ? time() : $start_time;
    ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
      localtime(time);
    $locltime = sprintf( "%.2d:%.2d:%.2d", $hour, $min, $sec );

    ## Get runq data
    $uptime = 0;
    open IN, "$uptime_cmd |";
    # Uptime is really really annoying.  Here are the various and sundry formats
    # you might run into over the course of a day:
    # root at npdsa01 </var/tmp><137# uname -a
    # AIX npdsa01 1 5 0025425A4C00
    #  root at npdsa01 </var/tmp><133# uptime
    #    04:40PM   up 13 days, 50 mins,  17 users,  load average: 0.05, 0.12, 0.15
    #  dmichael at npdsa01 <orca/AIXorcallator><68> rsh npddev01 uptime
    #    04:41PM   up 2 days, 20 hrs,  33 users,  load average: 0.58, 0.80, 1.06
    #  dmichael at npdsa01 <orca/AIXorcallator><70> rsh npddev01 uptime
    #    04:45PM   up 2 days,  20:04,  33 users,  load average: 0.52, 0.68, 0.95
    #  dmichael at npdsa01 <~><2> uptime
    #    03:48PM   up   5:56,  9 users,  load average: 0.00, 0.07, 0.16
    #  dmichael at npdsa01 <~><102> uptime
    #    04:16PM   up 1 day,   6:25,  8 users,  load average: 0.00, 0.01, 0.01
    # So, if you want the uptime, we have to parse all these circumstances.


    while (<IN>) {
        # first, get the easy part: load average
        if (/load average:\s+(\S+),\s+(\S+),\s+(\S+)/) {
            $load_info = join "\t", $1, $2, $3;
        }
        # now, get uptime, in various formats:
        if (/up\s+(\d+)\s+day[s]?,\s+(\d+)\s+hrs,\s+(\d+)\s+user[s]?/) {
            # up  X days, Y hrs, U users
            $up_day = $1;
            $up_hrs = $2;
            $up_min = 0;
            $nusr = $3;
        } elsif (/up\s+(\d+)\s+day[s]?,\s+(\d+):(\d+),\s+(\d+)\s+user[s]?/) {
            # up  X days, H:M, U users
            $up_day = $1;
            $up_hrs = $2;
            $up_min = $3;
            $nusr = $4;
        } elsif (/up\s+(\d+)\s+day[s]?,\s+(\d+)\s+mins,\s+(\d+)\s+user[s]?/) {
            # up X days, M mins, U users
            $up_day = $1;
            $up_hrs = 0;
            $up_min = $2;
            $nusr = $3;
        } elsif (/up\s+(\d+)\s+mins,\s+(\d+)\s+user[s]?/) {
            # up M mins, U users
            $up_day = 0;
            $up_hrs = 0;
            $up_min = $1;
            $nusr = $2;
        } elsif (/up\s+(\d+):(\d+),\s+(\d+)\s+user[s]?/) {
            # up H:M mins, U users
            $up_day = 0;
            $up_hrs = $1;
            $up_min = $2;
            $nusr = $3;
        } elsif (/up\s+(\d+)\s+day[s]?,\s+(\d+)\s+user[s]?/) {
            # up  X days, U users
            # this is pretty rare, as it only happens when
            # orca is started on an exact day boundary (it just
            # happened to me!).
            $up_day = $1;
            $up_hrs = 0;
            $up_min = 0;
            $nusr = $2;
        }
        # 
        # calculate uptime in seconds
        $uptime = (( $up_day * 24 * 60 ) + ( $up_hrs * 60 ) + ( $up_min )) * 60;
    }
    close IN;
    $load_header = "1runq\t5runq\t15runq";
    $up_header = "uptime\tnusr";
    $up_info = "$uptime\t$nusr";

    if ( scalar( split ' ', $load_header ) != scalar( split ' ', $load_info ) )
    {
        $load_header = '';
        $load_info   = '';
        $need_header = 1;
        print STDERR "WARNING: load header does not match load info.\n";
    }
    if ( scalar( split ' ', $up_header ) != scalar( split ' ', $up_info ) )
    {
        $up_header = '';
        $up_info   = '';
        $need_header = 1;
        print STDERR "WARNING: UP header does not match load info.\n";
    }

    #
    ## Get number of system processes
    $num_proc = -1;    # Don't count the header.
    open IN, "$ps_cmd -ek |";
    while (<IN>) {
        $num_proc++;
    }
    close IN;
    $proc_info   = "$num_proc";
    $proc_header = '#proc';

    if ( scalar( split ' ', $proc_header ) != scalar( split ' ', $proc_info ) )
    {
        $proc_header = '';
        $proc_info   = '';
        $need_header = 1;
        print STDERR "WARNING: #proc header does not match #proc info.\n";
    }

    #
    ## Get pstat data for pages
       $sw_used = 0;
       $sw_free = 0;
        open IN, "$pstat_cmd -s |tail -3 |";
        while (<IN>) {
         @swp = split(/ +/,);
         if (/\d/) {
         $sw_used = $swp[1];
         $sw_free = $swp[2];
         $swap_used = $sw_used * 4096;
         $swap_free = $sw_free * 4096;
         }
        }
        close IN;
        $swap_info = "$swap_used\t$swap_free";
        $swap_header = "\tswap_used\tswap_free";

    if ( scalar( split ' ', $swap_header ) !=
      scalar( split ' ', $swap_info ) )
    {
        $swap_header = '';
        $swap_info   = '';
        $need_header   = 1;
        print STDERR "WARNING: pstat header does not match pstat info.\n";
    }

    #
    ## Set up vmstat stuff
    my $inter = $INTERVAL + ( time() - $timestamp );
    my $interfact = ( $uptime - $inter ) * ( $uptime > $inter );
    my %vm_sum_prev = %vmstat;
    my $vm_sum_curr = {};

    #
    ## Get numprocs/sec
    open IN, "$vmstat_cmd -f |";
    while (<IN>) {
        chomp;
        if (/^\s+(\d+) forks/) {
            $vm_sum_curr{forks} = $1;
            if ( $vm_sum_prev{forks} > 0 ) {
                # there already exists a valid "prev", so proceed as normal
                $vm_sum_curr{proc_s} = ( $vm_sum_curr{forks} - $vm_sum_prev{forks} ) / $inter;
            } else {
                # set up first value as the average-to-date
                $vm_sum_curr{proc_s} = ( $vm_sum_curr{forks} ) / $uptime;
            }
            $vm_sum_prev{forks} = $vm_sum_curr{forks};
        }
    }
    close IN;

    #
    ## Get static/counter-type vm stats
    $inter = $INTERVAL + ( time() - $timestamp );
    $interfact = ( $uptime - $inter ) * ( $uptime > $inter );
    open IN, "$vmstat_cmd -s |";
    while (<IN>) {
        chomp;
        if (/^\s+(\d+) total address trans. faults/) {
            # total address trans. faults
            #   Incremented for each occurrence of an address translation
            #  page fault. I/O may or may not be required to resolve the
            #  page fault. Storage protection page faults (lock misses) are
            #  not included in this count.
        } elsif (/^\s+(\d+) page ins/) {
            # page ins
            #  Incremented for each page read in by the virtual memory
            #  manager. The count is incremented for page ins from page
            #  space and file space. Along with the page out statistic,
            #  this represents the total amount of real I/O initiated by
            #  the virtual memory manager.
        } elsif (/^\s+(\d+) page outs/) {
            # page outs
            #  Incremented for each page written out by the virtual memory
            #  manager. The count is incremented for page outs to page
            #  space and for page outs to file space.  Along with the page
            #  in statistic, this represents the total amount of real I/O
            #  initiated by the virtual memory manager.
        } elsif (/^\s+(\d+) paging space page ins/) {
            # paging space page ins
            #  Incremented for VMM initiated page ins from paging space only.
            $vm_sum_curr{pi} = $1;
            if ( $vm_sum_prev{pi} > 0 ) {
                # there already exists a valid "prev", so proceed as normal
                $vm_sum_curr{pi} = ( $vm_sum_curr{pi} - $vm_sum_prev{pi} ) / $inter;
            } else {
                # set up first value as the average-to-date
                $vm_sum_curr{pi} = ( $vm_sum_curr{pi} ) / $uptime;
            }
            $vm_sum_prev{pi} = $vm_sum_curr{pi};
        } elsif (/^\s+(\d+) paging space page outs/) {
            # paging space outs
            #  Incremented for VMM initiated page outs to paging space only.
            $vm_sum_curr{po} = $1;
            if ( $vm_sum_prev{po} > 0 ) {
                # there already exists a valid "prev", so proceed as normal
                $vm_sum_curr{po} = ( $vm_sum_curr{po} - $vm_sum_prev{po} ) / $inter;
            } else {
                # set up first value as the average-to-date
                $vm_sum_curr{po} = ( $vm_sum_curr{po} ) / $uptime;
            }
            $vm_sum_prev{po} = $vm_sum_curr{po};
        } elsif (/^\s+(\d+) total reclaims/) {
            # reclaims
            #  Incremented when an address translation fault can be
            #  satisfied without initiating a new I/O request. This can
            #  occur if the page has been previously requested by VMM, but
            #  the I/O has not yet completed; or if the page was
            #  pre-fetched by VMM's read-ahead algorithm, but was hidden
            #  from the faulting segment; or if the page has been put on
            #  the free list and has not yet been reused.
        } elsif (/^\s+(\d+) zero filled pages faults/) {
            # zero filled pages faults
            #  Incremented if the page fault is to working storage and can
            #  be satisfied by assigning a frame and zero-filling it.
        } elsif (/^\s+(\d+) executable filled pages faults/) {
            # executable-filled page faults
            #  Incremented for each instruction page fault.
        } elsif (/^\s+(\d+) pages examined by clock/) {
            # pages examined by the clock
            #  VMM uses a clock-algorithm to implement a pseudo least
            #  recently used (lru) page replacement scheme. Pages are aged
            #  by being examined by the clock. This count is incremented
            #  for each page examined by the clock.
        } elsif (/^\s+(\d+) revolutions of the clock hand/) {
            # revolutions of the clock hand
            #  Incremented for each VMM clock revolution (that is, after
            #  each complete scan of memory).
            $vm_sum_curr{cy} = $1;
            if ( $vm_sum_prev{cy} > 0 ) {
                # there already exists a valid "prev", so proceed as normal
                $vm_sum_curr{cy} = ( $vm_sum_curr{cy} - $vm_sum_prev{cy} ) / $inter;
            } else {
                # set up first value as the average-to-date
                $vm_sum_curr{cy} = ( $vm_sum_curr{cy} ) / $uptime;
            }
            $vm_sum_prev{cy} = $vm_sum_prev{cy};
        } elsif (/^\s+(\d+) pages freed by the clock/) {
            # pages freed by the clock
            #  Incremented for each page the clock algorithm selects to
            #  free from real memory.
            $vm_sum_curr{fr} = $1;
            if ( $vm_sum_prev{fr} > 0 ) {
                # there already exists a valid "prev", so proceed as normal
                $vm_sum_curr{fr} = ( $vm_sum_curr{fr} - $vm_sum_prev{fr} ) / $inter;
            } else {
                # set up first value as the average-to-date
                $vm_sum_curr{fr} = ( $vm_sum_curr{fr} ) / $uptime;
            }
            $vm_sum_prev{fr} = $vm_sum_prev{fr};
        } elsif (/^\s+(\d+) backtracks/) {
            # backtracks
            #  Incremented for each page fault that occurs while resolving
            #  a previous page fault. (The new page fault must be resolved
            #  first and then initial page faults can be backtracked.)
        } elsif (/^\s+(\d+) lock misses/) {
            # lock misses
            #  VMM enforces locks for concurrency by removing
            #  addressability to a page. A page fault can occur due to a
            #  lock miss, and this count is incremented for each such
            #  occurrence.
        } elsif (/^\s+(\d+) free frame waits/) {
            # free frame waits
            #  Incremented each time a process is waited by VMM while free
            #  frames are gathered.
        } elsif (/^\s+(\d+) extend XPT waits/) {
            # extend XPT waits
            #  Incremented each time a process is waited by VMM due to a
            #  commit in progress for the segment being accessed.
        } elsif (/^\s+(\d+) pending I\/O waits/) {
            # pending I/O waits
            #  Incremented each time a process is waited by VMM for a
            #  page-in I/O to complete.
        } elsif (/^\s+(\d+) start I\/Os/) {
            # start I/Os
            #  Incremented for each read or write I/O request initiated by
            #  VMM. This count should equal the sum of page-ins and
            #  page-outs.
        } elsif (/^\s+(\d+) iodones/) {
            # iodones
            #  Incremented at the completion of each VMM I/O request.
        } elsif (/^\s+(\d+) cpu context switches/) {
            # CPU context switches
            #  Incremented for each CPU context switch (dispatch of a new
            #  process).
            $vm_sum_curr{cs} = $1;
            if ( $vm_sum_prev{cs} > 0 ) {
                # there already exists a valid "prev", so proceed as normal
                $vm_sum_curr{cs} = ( $vm_sum_curr{cs} - $vm_sum_prev{cs} ) / $inter;
            } else {
                # set up first value as the average-to-date
                $vm_sum_curr{cs} = ( $vm_sum_curr{cs} ) / $uptime;
            }
        } elsif (/^\s+(\d+) device interrupts/) {
            # device interrupts
            #  Incremented on each hardware interrupt.
            $vm_sum_curr{in} = $1;
            if ( $vm_sum_prev{in} > 0 ) {
                # there already exists a valid "prev", so proceed as normal
                $vm_sum_curr{in} = ( $vm_sum_curr{in} - $vm_sum_prev{in} ) / $inter;
            } else {
                # set up first value as the average-to-date
                $vm_sum_curr{in} = ( $vm_sum_curr{in} ) / $uptime;
            }
            $vm_sum_prev{in} = $vm_sum_prev{in};
        } elsif (/^\s+(\d+) software interrupts/) {
            # software interrupts
            #  Incremented on each software interrupt. A software interrupt
            #  is a machine instruction similar to a hardware interrupt
            #  that saves some state and branches to a service
            #  routine. System calls are implemented with software
            #  interrupt instructions that branch to the system call
            #  handler routine.
        } elsif (/^\s+(\d+) traps/) {
            # traps
            #  Not maintained by the operating system.
        } elsif (/^\s+(\d+) syscalls/) {
            # syscalls
            #  Incremented for each system call.
            $vm_sum_curr{sys} = $1;
            if ( $vm_sum_prev{sys} > 0 ) {
                # there already exists a valid "prev", so proceed as normal
                $vm_sum_curr{sys} = ( $vm_sum_curr{sys} - $vm_sum_prev{sys} ) / $inter;
            } else {
                # set up first value as the average-to-date
                $vm_sum_curr{sys} = ( $vm_sum_curr{sys} ) / $uptime;
            }
            $vm_sum_prev{sys} = $vm_sum_prev{sys};
        }
    }
    close IN;
        

    #
    ## Get live vmstat data
    open IN, "$vmstat_cmd 5 2 |";
    my $inter = $INTERVAL + ( time() - $timestamp );
    # convert a given 'count' to 'estimated count before previous
    # interval', with the condition that if we haven't been up for at least
    # an interval, set this value to zero this is used to seed the initial
    # values with something more useful than 0
    while (<IN>) {
        chomp;
        # r b avm fre re pi po fr sr cy inf sysf csf us sy id wa
        # 1 r Number of kernel threads placed in run queue.
        # 2 b Number of kernel threads placed in wait queue (awaiting
        #    resource, awaiting input/output).
        #  
        #  Memory: information about the usage of virtual and real
        #  memory. Virtual pages are considered active if they have been
        #  accessed. A page is 4096 bytes.
        #  
        # 3 avm Active virtual pages.
        # 4 fre Size of the free list.
        #      Note: A large portion of real memory is utilized as a cache
        #      for file system data. It is not unusual for the size of the
        #      free list to remain small.
        #  
        #  Page: information about page faults and paging activity. These
        #  are averaged over the interval and given in units per second.
        #  
        # 5 re Pages input/output list.
        # 6 pi Pages paged in from paging space.
        # 7 po Pages paged out to paging space.
        # 8 fr Pages freed (page replacement).
        # 9 sr Pages scanned by page-replacement algorithm.
        # 10 cy Clock cycles by page-replacement algorithm.
        #  
        #  Faults: trap and interrupt rate averages per second over the
        #  sampling interval.
        #  
        # 11 in Device interrupts.
        # 12 sy System calls.
        # 13 cs Kernel thread context switches.
        #  
        #  Cpu: breakdown of percentage usage of CPU time.
        #  
        # 14 us User time.
        # 15 sy System time.
        # 16 id CPU idle time.
        # 17 wa CPU idle time during which the system had outstanding disk/NFS I/O
        #  request(s). See detailed description above.
        #  
        if (/^[\s\d]+$/) {
            # overwrite first line on 2nd pass
            my $vm_curr = {};
            my $info = "";
            # assign %curr{field} values.  @vmkeys is
            # an array of field identifiers that should align
            # with the output of the vmstat command
            @vm_curr{@vmkeys} = split;
            foreach $_ ( @vmkeys , proc_s ) {
                # if we already got the data from the summary
                # output (more reliable / accurate), then
                # use that, otherwise, use the values from the
                # vmstat interval run
                $vmstat{$_}=(
                             ($vm_sum_curr{$_} > 0 )
                             * ( $vm_sum_curr{$_} )
                             ||
                             $vm_curr{$_} );
                $info = $info . $vmstat{$_} . "\t";
            }
            $vmstat{forks}=$vm_sum_curr{forks};
            # tack on other things not in @vmkeys
            $info = $info . "\t" . $pagestotl;
            $vmstat_info = $info;
        }
    }
    close IN;
    $vmstat_header =
"runque\twaiting\tpagesactive\tfreememK\tPagesIO/s\tPagesI/s\tPagesO/s\tPagesF/s\tscanrate\tcy_cycles\tdevinter\tsyscalls\tcs\tusr%\tsys%\tidle%\twio%\t#proc/s\tpagestotl";

    if ( scalar( split ' ', $vmstat_header ) !=
      scalar( split ' ', $vmstat_info ) )
    {
        print STDERR "WARNING: vmstat header does not match vmstat info.\n";
        print STDERR "header is: $vmstat_header\n";
        print STDERR "header #fields: ", scalar(split ' ', $vmstat_header ), "\n";
        print STDERR "info is  : $vmstat_info\n";
        print STDERR "info #fields  : ", scalar(split ' ', $vmstat_info), "\n";

        $vmstat_header = '';
        $vmstat_info   = '';
        $need_header   = 1;
        print STDERR "vmkeys is : ", $#vmkeys, "\n";
    }

    #
    ## Get filesystem data
    $fs_header = '';
    $fs_info   = '';
    open IN, "$df_cmd -k -v |";
    while (<IN>) {
        chomp;

        if (m%^/dev%) {
            ( $mnt_dev, $blocks, $used, $free, $pct_used, $iused, $ifree,
              $ipct_used, $mnt ) = split;

            # Recalculate percents because df rounds.
            $fs_info .= "\t" 
                . sprintf( "%s\t%s\t%s\t%.5f\t%d\t%s\t%s\t%.5f", $blocks, $used,
              $free, ( $used / $blocks ) * 100, ( $iused + $ifree ), $iused,
              $ifree, ( $iused / ( $iused + $ifree ) ) * 100 );
            $fs_header .= "\t" . join "\t", "mntC_$mnt", "mntU_$mnt",
              "mntA_$mnt", "mntP_$mnt", "mntc_$mnt", "mntu_$mnt", "mnta_$mnt",
              "mntp_$mnt";
        }
    }
    close IN;

    if ( scalar( split ' ', $fs_header ) != scalar( split ' ', $fs_info ) ) {
        $fs_header   = '';
        $fs_info     = '';
        $need_header = 1;
        print STDERR
          "WARNING: filesystem header does not match filesystem info.\n";
    }

    #
    ## Get iostat data
    $disk_t  = 0;
    $disk_rK = 0;
    $disk_wK = 0;
    undef %disks;
    # open IN, "iostat -d 1 2|";
    # iostat totals are useful; running counts are good for
    # interactive observations, but a 1s interval observation
    # isn't representative for 'snapshots'; only total counts are.
    open IN, "$iostat_cmd -s -d|";

    # for accuracy's sake, add on number of seconds
    # since this run started
    my $inter = $INTERVAL + ( time() - $timestamp );

    while (<IN>) {
        # if (/^(\S+)\s+\S+\s+\S+\s+(\S+)\s+(\d+)\s+(\d+)/) {
        if (/^\s+(\S+)\s+(\S+)\s+(\d+)\s+(\d+)/) {
            my $kBs  = $1;
            my $tps  = $2;
            my $rK   = $3;
            my $wK   = $4;
            if ( $disk_rK_prev == 0 ) {
                # there is no previous data, so seed with average rates
                # spread over time-since-boot minus specified interval.
                # If uptime < INTERVAL, then we haven't been up long enough
                # to have a (valid) previous interval, so 
                # the previous count really is zero.
                $disk_rK_prev = ( $rK / $uptime ) * ( $uptime - $inter ) * ($uptime > $INTERVAL);
                $disk_wK_prev = ( $wK / $uptime ) * ( $uptime - $inter ) * ($uptime > $INTERVAL);

                # iostat on AIX only reports transactions per second,
                # to 3 significant figures, not total transactions.
                # So this value is useless; maybe some other AIX utility
                # can grab this, but for now, just keep reporting the
                # running average.
                $disk_t_prev  = $tps;
            }
            # calculate reads/writes per second from totals reported by iostat
            $disk_rK = ( $rK - $disk_rK_prev ) / $inter;
            $disk_wK = ( $wK - $disk_wK_prev ) / $inter;
            $disk_t = $tps;
            $disk_rK_prev = $rK;
            $disk_wK_prev = $wK;
            $disk_t_prev = $tps;
        }
    }
    close IN;
    $iostat_header = "disk_t/s\tdisk_rK/s\tdisk_wK/s";
    $iostat_info   = "${disk_t}\t${disk_rK}\t${disk_wK}";

    if ( scalar( split ' ', $iostat_header ) !=
      scalar( split ' ', $iostat_info ) )
    {
        $iostat_header = '';
        $iostat_info   = '';
        $need_header   = 1;
        print STDERR "WARNING: iostat header does not match iostat info.\n";
    }

    #
    ## Get packet data
    $packet_header = '';
    $packet_info   = '';

    foreach $interface (@net_interfaces) {
        $packet_header .=
            "\t${interface}Ipkt/s\t${interface}IErr/s\t${interface}Opkt/s\t${interface}OErr/s\t${interface}Coll/s\t";
        open IN, "$netstat_cmd -n -I $interface 1 |";
        # format looks like
        #     input   (en0)      output           input   (Total)    output
        #  packets  errs  packets  errs colls  packets  errs  packets  errs colls
        # 492126804     0 255536091     0     0 553459817     0 316872224     0     0

        # for accuracy's sake, add on number of seconds
        # since this run started
        my $interv = $INTERVAL + ( time() - $timestamp );
	# make %prev point to %netstat{$interface}, just for readability
	my $prev = %netstat->{$interface};

        while (<IN>) {
            if (/^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+/) {
                my $ipkt = $1;
                my $ierr = $2;
                my $opkt = $3;
                my $oerr = $4;
                my $coll = $5;
                if ( $prev->{"ipkt"} == -1 ) {
                    # haven't populated data yet - extrapolate
                    my $extrapolate = sub { ( @_[0] / $uptime ) * 
                            ( $uptime - $interv ) * ( $uptime > $interv ); };
                    # use this funky notation, so that packet_info
                    # syntax doesn't depend on instance status
                    # of prev (copy or pointer)
                    $prev=\%{{ "ipkt" => &$extrapolate($ipkt),
                               "ierr" => &$extrapolate($ierr),
                               "opkt" => &$extrapolate($opkt),
                               "oerr" => &$extrapolate($oerr),
                               "coll" => &$extrapolate($coll) }};
	            }
                # build packet info line with appropriate rates
                $packet_info .= "\t" . join "\t",
                   ( ( $ipkt - $prev->{"ipkt"} ) / $interv ),
                   ( ( $ierr - $prev->{"ierr"} ) / $interv ),
                   ( ( $opkt - $prev->{"opkt"} ) / $interv ),
                   ( ( $oerr - $prev->{"oerr"} ) / $interv ),
                   ( ( $coll - $prev->{"coll"} ) / $interv );
                # remember current counts for next iteration calculations
		$netstat{$interface} = { "ipkt" => $ipkt,
					 "ierr" => $ierr,
					 "opkt" => $opkt,
					 "oerr" => $oerr,
					 "coll" => $coll};
                # don't care about rest of input, just terminate read loop
                last; 
            }
        }
        close IN;
    }

    # print "packet header is $packet_header\n";
    # print "packet info is $packet_info\n";
    if ( scalar( split ' ', $packet_header ) !=
      scalar( split ' ', $packet_info ) )
    {
        $packet_header = '';
        $packet_info   = '';
        $need_header   = 1;
        print STDERR "WARNING: packet header does not match packet info.\n";
        print STDERR "packet header is: ", $packet_header, "\n";
        print STDERR "packet info is: ", $packet_info, "\n";
    }

    #
    ## Get TCP Connection data
    $tcp_estb = 0;
    open IN, "$netstat_cmd -an |";
    while (<IN>) {
        if (/^tcp.+ESTABLISHED$/) {
            $tcp_estb++;
        }
    }
    close IN;
    $tcp_info   = $tcp_estb;
    $tcp_header = 'tcp_estb';

    if ( scalar( split ' ', $tcp_estb_header ) !=
      scalar( split ' ', $tcp_estb_info ) )
    {
        $tcp_estb_header = '';
        $tcp_estb_info   = '';
        $need_header     = 1;
        print STDERR "WARNING: tcp_estb header does not match tcp_estb info.\n";
    }

    #
    ## Get TSM Database space usage
    $tsmdb = 0;
    if ( -e $dsmadmc_cmd && -x $dsmadmc_cmd ) {
        open IN, "$dsmadmc_cmd -id=$TSMID -password=$TSMPW 'query db' |tail -r -n 5 |";
        while (<IN>) {
          @fld = split(/ +/,);
          if (/\d/) {
            $tsmdb = $fld[8];
           }
        }
        close IN;
    }

    $tsm_info = $tsmdb;
    $tsm_header = "tsmdb\t";

    if ( scalar( split ' ', $tsm_header ) !=
      scalar( split ' ', $tsm_info ) )
    {
        $tsm_header = '';
        $tsm_info   = '';
        $need_header     = 1;
        print STDERR "WARNING: tsmdb header does not match tsmdb info.\n";
    }

    #
    ## Get Memory Usage breakup using SVMON
    $mem_work = 0;
    $mem_pres = 0;
    $mem_clnt = 0;
    open IN, "$svmon_cmd -G |tail -2 |";
    while (<IN>) {
     @memp = split(/ +/,);
     if (/use\s+(\d+) /) {
      $m_work = $memp[2];
      $m_pres = $memp[3];
      $m_clnt = $memp[4];
      $mem_work = $m_work * 4096;
      $mem_pres = $m_pres * 4096;
      $mem_clnt = $m_clnt * 4096;
     }
    }
    close IN;
    $mem_info = "$mem_work\t$mem_pres\t$mem_clnt\t$mem_totl";
    $mem_header = "mem_work\tmem_pres\tmem_clnt\tmem_totl";

    if ( scalar( split ' ', $mem_header ) !=
      scalar( split ' ', $mem_info ) )
    {
        $mem_header = '';
        $mem_info   = '';
        $need_header     = 1;
        print STDERR "WARNING: memory header does not match memory info.\n";
    }

    #
    ## Get TSM Tape Drive usage
    $rmt = 0;
    $rmt5 = 5;
    if ( -e $dsmadmc_cmd && -x $dsmadmc_cmd ) {
        open IN, "$dsmadmc_cmd -id=$TSMID -password=$TSMPW 'query mount' |grep matches |";
        while (<IN>) {
            @fld = split(/ +/,);
            if (/\d/) {
                $rmt = $fld[1];
            }
        }
        close IN;
    }
    $tsm_rmt_header = "rmt5\trmt\t";
    $tsm_rmt_info = "$rmt5\t$rmt";

    if ( scalar( split ' ', $tsm_rmt_header ) !=
      scalar( split ' ', $tsm_rmt_info ) )
    {
        print STDERR "WARNING: TSM RMT header does not match TSM RMT info.\n";
        $tsm_rmt_header = '';
        $tsm_rmt_info   = '';
        $need_header     = 1;
    }

    #
    ## Get TSM Recovery Log space usage
    $tsmlog = 0;
    if ( -e $dsmadmc_cmd && -x $dsmadmc_cmd ) {
            open IN, "$dsmadmc_cmd -id=$TSMID -password=$TSMPW 'query log' |tail -r -n 4 |";
            while (<IN>) {
                @fld = split(/ +/,);
                if (/\d/) {
                    $tsmlog = $fld[8];
                }
            }
            close IN;
        }
    $tsm_log_info = $tsmlog;
    $tsm_log_header = 'tsmlog';

    if ( scalar( split ' ', $tsm_log_header ) !=
      scalar( split ' ', $tsm_log_info ) )
    {
        print STDERR "WARNING: TSM Log header ($tsm_log_header) does not match TSM Log info ($tsm_log_info).\n";
        $tsm_log_header = '';
        $tsm_log_info   = '';
        $need_header     = 1;
    }

    #
    ## Get TSM Tape usage
    $tsmpvt = 0;
    $tsmscr = 0;
    $tsmvlt = 0;
    if ( -e $dsmadmc_cmd && -x $dsmadmc_cmd ) {
        open IN, "$dsmadmc_cmd -id=$TSMID -password=$TSMPW 'query libvol' | grep 'Private' | wc -l |";
        while (<IN>) {
        chomp;
        @fld = split(/ +/,);
        if (/\d/) {
          $tsmpvt = $fld[1];
         }
        }
        close IN;

        open IN, "$dsmadmc_cmd -id=$TSMID -password=$TSMPW 'query libvol' | grep 'Scratch' | wc -l |";
        while (<IN>) {
            chomp;
            @fld = split(/ +/,);
            if (/\d/) {
                $tsmscr = $fld[1];
            }
        }
        close IN;

        open IN, "$dsmadmc_cmd -id=$TSMID -password=$TSMPW 'query drmedia' | grep 'Vault' | wc -l |";
        while (<IN>) {
            chomp;
            @fld = split(/ +/,);
            if (/\d/) {
                $tsmvlt = $fld[1];
            }
        }
    }

    $tsm_tape_info = join "\t", $tsmpvt, $tsmscr, $tsmvlt;
    $tsm_tape_header = join "\t", tsmpvt, tsmscr, tsmvlt;

    if ( scalar( split ' ', $tsm_tape_header ) !=
      scalar( split ' ', $tsm_tape_info ) )
    {
        print STDERR "WARNING: TSM Tape header does not match TSM Tape info.\n";
        $tsm_tape_header = '';
        $tsm_tape_info   = '';
        $need_header     = 1;
    }

    #
    ## Get TSM Disk Storage Pool usage
    $tsmphcy = 0;
    $tsmphcn = 0;
    if ( -e $dsmadmc_cmd && -x $dsmadmc_cmd ) {
        open IN, "$dsmadmc_cmd -id=$TSMID -password=$TSMPW 'query stgpool' |";
        while (<IN>) {
        @fld = split(/ +/,);
        if (/\d/) {
          if ( $fld[0] eq "PHCYDISKPO-" ) {
            $tsmphcy = $fld[3];
           }
          elsif ( $fld[0] eq "PHCNDISKPO-" ) {
            $tsmphcn = $fld[3];
           }
         }
        }
        close IN;
    }

    $tsm_stg_info = join "\t", $tsmphcy, $tsmphcn;
    $tsm_stg_header = join "\t", tsmphcy, tsmphcn;

    if ( scalar( split ' ', $tsm_stg_header ) !=
      scalar( split ' ', $tsm_stg_info ) )
    {
        print STDERR "WARNING: TSM Storage Pool header does not match TSM Storage Pool info.\n";
        $tsm_stg_header = '';
        $tsm_stg_info   = '';
        $need_header     = 1;
    }

    #
    ## Get runq data
    if ( $ctxqsession_cmd == "" ) {
        $ctxqsession_cmd="/usr/lpp/CTXSmf/bin/ctxqsession";
    }
    $citrix_sessions=0;
    $citrix_disconnect=0;
    if ( -e $ctxqsession_cmd && -x $ctxqsession_cmd ) {
        open IN, "$ctxqsession_cmd |";
        while (<IN>) {
            if ( /tcp\#/ ) {
                $citrix_sessions++;
            }
            if ( /\sdisc\s/ ) {
                $citrix_disconnect++;
            }
        }
        close IN;
    }

    $citrix_info = join "\t", $citrix_sessions, $citrix_disconnect;
    $citrix_header = join "\t", ctx_sess, ctx_disc;

    #
    ## construct our header
    $out_header = join "\t", "timestamp", "locltime", $load_header, $up_header,
      $proc_header, $vmstat_header, $fs_header, $iostat_header, $packet_header,
      $tcp_header, $tsm_header, $swap_header, $mem_header, $tsm_rmt_header,
      $tsm_log_header, $tsm_tape_header, $tsm_stg_header, $citrix_header;
    $out_header =~ tr/ \t/\t/s;    # translate whitespace to single tabs

    ## construct a line of data
    $out_info = join "\t", $timestamp, $locltime, $load_info, $up_info, $proc_info,
      $vmstat_info, $fs_info, $iostat_info, $packet_info, $tcp_info, $tsm_info, 
      $swap_info, $mem_info, $tsm_rmt_info, $tsm_log_info, $tsm_tape_info,
      $tsm_stg_info, $citrix_info;
    $out_info =~ tr/ \t/\t/s;      # translate whitespace to single tabs

    # count the fields in header and info lines--make sure the counts match.
    $header_cnt = split ' ', $out_header;
    $info_cnt = split ' ', $out_info;
    if ( $header_cnt != $info_cnt ) {
        # oh, dear!  How did this happen?  Bad code!  No biscuit!
        # somehow we collected more (or less) data than we have columns for.
        # That's bad, Ray.
        print STDERR
          "ERROR: header columns do not equal data columns. Exiting.\n";
        &exit_nicely;
    }

    #
    if ( $need_header == 1 ) {
        # If we did things right,
        # The only way we get here is if it's the first line of the output file,
        # or if we already know of a header change, such as if TSM spewed,
        # or new filesystems were mounted, or dismounted, or .....
        # In any of these cases, we'll be creating a new output file anyway,
        # so just let things happen.
    } elsif ( $prev_header != $out_header ) {
        # the header changed.  Time for a new file.
        $need_header = 1;
    } elsif ( $header_cnt != $prev_header_cnt or $info_cnt != $prev_info_cnt ) {
        # crazy -- the previous count and the current count don't match,
        # so we must have different headers, which should have been caught
        # in the header comparison above, but apparently not.
        $need_header = 1;
    }
    $prev_header = $out_header;
    $prev_header_cnt = $header_cnt;
    $prev_info_cnt   = $info_cnt;

    #
    ## Check to see if we've crossed days,
    ## or if we need a new header.
    ## In either case, we need a new file.
    #
    ( $nsec, $nmin, $nhour, $nmday, $nmon, $nyear, $nwday, $nyday, $nisdst ) =
      localtime(time);
    if ( ($nyear*1000 + $nyday) > ($oyear*1000 + $oyday) ) {
        # hey, we crossed days.  Be nice--open a new file, to keep the
        # existing file from getting stupid-big.  Note that
        # creating a new output file automatically sets need_header to 1.
        &new_output_file;
    } elsif ( $need_header ) {
        # We didn't switch days yet, but we still need a new header for various
        # possible reasons.  So make a new output file (always need a new
        # output file whenever we need a new header--even the first time!)
        &new_output_file;
    }

    # Can't imagine how we'd get here without creating an output file.
    # But, here's the code for creating it, just in case.
    # if (! select OUT) {
    #   open OUT, ">$stat_file" or die "ERROR: Could not open $stat_file: $!";
    #   my $oldfh = select OUT;
    #   $| = 1;
    #   select $oldfh;
    # }

    ## Write output
    if ($need_header) {
        print OUT $out_header, "\n";
        $need_header = 0;
    }
    print OUT $out_info, "\n";

    # use this for testing, to speed things along.
    #sleep 5;
    sleep $INTERVAL - ( time() - $timestamp );

}

&exit_nicely;


######################################################################
#
# subroutines
#

#
##################################################
# exit_nicely ()
# This subroutine is called by the signal handler, or at the end
# of the while loop, if ever reached.
#
# It takes no arguments, and does not return - it simply exits
# the fork it's running under.
sub exit_nicely {
    if ( <OUT> ) {
        close OUT;
        @args = split(' ',$compressor);
        push @args, "$stat_file";
        print @args, "\n";
        system(@args);
    }
    exit 0;
}

#
##################################################
# new_output_file ()
# 
# Whenever a new header is needed, or if we change
# days, then do the following:
#   o close previous output file
#   o fork a process to compress it
#   o create a new output file
#
sub new_output_file {
    if ( <OUT> ) {
        close OUT;
    }
    ( $osec, $omin, $ohour, $omday, $omon,
      $oyear, $owday, $oyday, $oisdst ) = localtime(time);
    # close & compress file in background process
    # create new file & continue in current process
    if ( $pid = fork ) {
        # parent process resumes here
        $stat_file =
            sprintf( "%s/percol-%.2d-%.2d-%.2d-%.2d%.2d%.2d",
                     $out_dir, $oyear + 1900, $omon + 1,
                     $omday, $ohour, $omin, $osec );
        open OUT, ">$stat_file" or die "ERROR: Could not open $stat_file: $!";
        my $oldfh = select OUT;
        $| = 1;
        select $oldfh;
        $need_header = 1;
    } elsif (defined $pid) {
        # child process resumes here
        &exit_nicely;
    }
}

    
-------------- next part --------------
*** orca-aix-stat.pl.in	Thu Apr  6 18:02:06 2006
--- orca-aix-stat.old.pl.in	Thu Sep  8 21:51:41 2005
***************
*** 1,62 ****
! #!/usr/bin/perl
  #
! # Version 1.20
! 
! # Description:
! # Collect general perfromance statistics formatted for
! # interpretaion by Orca.
! 
! # Usage: 
  # The following variables may be set:
! # 
! # OUT_ROOT root directory for datafiles (eg /opt/log/performance)
  # INTERVAL the number of seconds between checks (eg 300 = 5 min)
! # DURATION numer of hours to run (eg 1 = 1 hr)
! #          < 0 means run indefinitely
! #          0 is undefined
! #          1-24 means run for N hours
! # 
  # This script runs various standard system utilities to collect
  # system performance statistics and writes them out to datafile named
  # HOSTNAME/stats.YYYY-MM-DD-HHmm under the OUT_ROOT directory.
! # 
! # It runs for the the numbers specified by DURATION collecting data
! # every INTERVAL number of seconds. After DURATION, the script
! # closes and compresses it's datafile via /usr/bin/compress and then
! # exits. If DURATION=24 and INTERVAL=300 (recommended) then the
! # following cron entry would collect continuos stats for a system:
! # 
  # 0 0 * * * /<PATH_TO_SCRIPT>/orca-aix-stat.pl
! # 
! # v1.20
! # 2006-04-06 - DM - Added ^L page markers
! # 2006-04-05 - DM - Modified how physical memory cap is identified
! #                   Now uses prtconf instead of lslpp mumbo jumbo
! #                 - corrected memory calculation
! #                 - added check to verify memory cap identification
! #                   If it didn't work, use a (bogus) default
! #                 - added conditional for uptime parsing - N days (only)
! #                 - shifted where some warnings are printed 
! # v1.8
! # 2006-01-31 - DM - Added command-line options for TSM stuff:
! #                   o specify TSM ID/password
! #                   o disable TSM collection, even if present
! #                 - TSM id/password now specified as a global variable
! #                 - fixed warning messages to appear prior
! #                   to corresponding header/data variable reset
! #                 - updated version to 1.8
! #                 - injected patterns for gnu-configure integration
! #                   (only OUT_ROOT cared)
! # 2004-08-19 - DM - Not yet ready for ./configure integration
! #                 - Change: Redesign of some sections to run 24x7 using kernel
! #                   counters instead of script cycles to compute
! #                   needed values quickly every 5 minutes
! #                 - Change: Duration < 0 indicates infinite loop, consistent
! #                   with other orcallators
! #                 - Duration > 0 < 24 indicates run every 300 seconds
! #                   for Duration hours
! #                 - Duration = 0 or Duration >= 24 is undefined
! #                 DM = by Dave "Dragon" Michaels (dragon at raytheon.com)
  # 2003-09-10 - RV - Modified for AIX 4.3/5.x.. by Rajesh Verma
  #                       (rajeshverma at aixdude.com)
  # v1.7       - RV - ignores /proc now
--- 1,25 ----
! # Collect general AIX performance statistics formatted for
! # interpretation by Orca.
  #
! # Usage:
  # The following variables may be set:
! #
! # OUT_ROOT root directory for data files (eg /opt/log/performance)
  # INTERVAL the number of seconds between checks (eg 300 = 5 min)
! # DURATION number of hours to run (eg 1 = 1 hr)
! #
  # This script runs various standard system utilities to collect
  # system performance statistics and writes them out to datafile named
  # HOSTNAME/stats.YYYY-MM-DD-HHmm under the OUT_ROOT directory.
! #
! # It runs for the numbers specified by DURATION collecting data every
! # INTERVAL number of seconds.  After DURATION, the script closes and
! # compresses it's datafile via /usr/bin/compress and then exits.  If
! # DURATION=24 and INTERVAL=300 (recommended) then the following cron
! # entry would collect continuous stats for a system:
! #
  # 0 0 * * * /<PATH_TO_SCRIPT>/orca-aix-stat.pl
! #
  # 2003-09-10 - RV - Modified for AIX 4.3/5.x.. by Rajesh Verma
  #                       (rajeshverma at aixdude.com)
  # v1.7       - RV - ignores /proc now
***************
*** 65,95 ****
  # Added #open connections, pagestotl.
  # 2001-07-06 - JDK - added command-line args & data checks
  # 2001-07-09 - JDK - added signal handler, column checks, & umask
! # 2001-07-10 - JDK - now autodetects interfaces via netstat -i
  # v1.5
  #
  # $HeadURL: file:///var/www/svn/repositories-public/orcaware-public/orca/trunk/data_gatherers/aix/orca-aix-stat.pl.in $
! # $LastChangedDate: 2006-04-06 17:36:41 -0700 (Thu, 06 Apr 2006) $
! # $LastChangedBy: dragon at raytheon.com $
! # $LastChangedRevision: 528 $
! # 
! # Note: Execution speed is more important than cleanliness here.
  
  # Explicitly set PATH to prevent odd problems if run manually.
  
! $Usage_Message = '
! Usage: orca-aix-stat.pl [-r out_root] [-i interval] [-d duration]
!                         [-tid ID] [-tpw PASSWORD] [-notsm] [-h]
  
! -r out_root   - set root output directory, default: /opt/log/performance
! -i interval   - number of seconds between checks, default: 300
! -d duration   - number of hours to run, default: 24, -1: never terminate
! -h            - this message
! -tid ID       - the TSM user ID to use for dsmadmc commands
! -tpw PASSWORD - the TSM password to use for dsmadmc commands
! -notsm        - do not gather TSM info
  
- ';
  ############################
  # These are the packages you need to install
  # 1. perl
--- 28,55 ----
  # Added #open connections, pagestotl.
  # 2001-07-06 - JDK - added command-line args & data checks
  # 2001-07-09 - JDK - added signal handler, column checks, & umask
! # 2001-07-10 - JDK - now auto detects interfaces via netstat -i
  # v1.5
  #
  # $HeadURL: file:///var/www/svn/repositories-public/orcaware-public/orca/trunk/data_gatherers/aix/orca-aix-stat.pl.in $
! # $LastChangedDate: 2005-09-08 20:51:41 -0700 (Thu, 08 Sep 2005) $
! # $LastChangedBy: blair at orcaware.com $
! # $LastChangedRevision: 490 $
  
+ my $COMPRESS = '@COMPRESSOR@';
+ 
  # Explicitly set PATH to prevent odd problems if run manually.
+ $ENV{PATH} = '/bin:/usr/bin:/etc:/usr/sbin:/usr/ucb:/sbin';
  
! my $Usage_Message = "
! usage: $0 [-r out_root] [-i interval] [-d duration] [-h]
  
! -r out_root set root output directory, default: /opt/log/performance
! -i interval number of seconds between checks, default: 300
! -d duration number of hours to run, default: 24
! -h this message
! ";
  
  ############################
  # These are the packages you need to install
  # 1. perl
***************
*** 104,158 ****
  # http://www-1.ibm.com/servers/aix/products/aixos/linux/download.html
  # http://www.bullfreeware.com
  #
! #
! #  Good Luck, Rajesh Verma (rajeshverma at yahoo.com)
  ##############################
- #
- # Parse the command line arguments
- while ( $#ARGV >= 0 ) {
  
!     if ( $ARGV[0] eq "-r" ) {
!         shift @ARGV;
!         $OUT_ROOT = shift @ARGV;
!     }
!     elsif ( $ARGV[0] eq "-notsm" ) {
!         shift @ARGV;
!         $DOTSM = "no";
!     }
!     elsif ( $ARGV[0] eq "-tid" ) {
!         shift @ARGV;
!         $TSMID = shift @ARGV;
!     }
!     elsif ( $ARGV[0] eq "-tpw" ) {
!         shift @ARGV;
!         $TSMPW = shift @ARGV;
!     }
!     elsif ( $ARGV[0] eq "-i" ) {
!         shift @ARGV;
!         $INTERVAL = shift @ARGV;
!     }
!     elsif ( $ARGV[0] eq "-d" ) {
!         shift @ARGV;
!         $DURATION = shift @ARGV;
!     }
!     elsif ( $ARGV[0] eq "-h" ) {
!         print $Usage_Message;
!         exit 0;
!     }
!     elsif ( $ARGV[0] =~ /^-/ ) {
!         die "Invalid flag: $ARGV[0]\n$Usage_Message";
!     }
!     else {
!         die "Invalid argument: $ARGV[0]\n$Usage_Message";
!     }
  }
  
  ## BEGIN set defaults
! # 
! $OUT_ROOT ||= '@VAR_DIR@/AIXorcallator';    # root directory for datafiles
! $DOTSM ||= "yes";
! $TSMID ||= 'orca';
! $TSMPW ||= 'orca';
  $INTERVAL ||= 300;                       # seconds between checks
  $DURATION ||= 24;                        # number of hours to run
  
--- 64,96 ----
  # http://www-1.ibm.com/servers/aix/products/aixos/linux/download.html
  # http://www.bullfreeware.com
  #
! # Good Luck, Rajesh Verma (rajeshverma at yahoo.com)
  ##############################
  
! # Parse the command line arguments
! while ($#ARGV >= 0) {
!   if ($ARGV[0] eq "-r") {
!     shift @ARGV;
!     $OUT_ROOT = shift @ARGV;
!   } elsif ($ARGV[0] eq "-i") {
!     shift @ARGV;
!     $INTERVAL = shift @ARGV;
!   } elsif ($ARGV[0] eq "-d") {
!     shift @ARGV;
!     $DURATION = shift @ARGV;
!   } elsif ($ARGV[0] eq "-h") {
!     print $Usage_Message;
!     exit 0;
!   } elsif ($ARGV[0] =~ /^-/) {
!     die "Invalid flag: $ARGV[0]\n$Usage_Message";
!   } else {
!     die "Invalid argument: $ARGV[0]\n$Usage_Message";
!   }
  }
  
  ## BEGIN set defaults
! 
! $OUT_ROOT ||= '/home/orca/orcallator';    # root directory for data files
  $INTERVAL ||= 300;                       # seconds between checks
  $DURATION ||= 24;                        # number of hours to run
  
***************
*** 160,256 ****
  
  ## Derived variables.
  $iterations = $DURATION * 60 * 60 / $INTERVAL;    # Number of checks.
! chomp( $HOST = `uname -n` );
  $out_dir = "${OUT_ROOT}/${HOST}";
! ( $osec, $omin, $ohour, $omday, $omon, $oyear, $owday, $oyday, $oisdst ) =
    localtime(time);
- ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
-   localtime(time);
  $stat_file =
!   sprintf( "%s/percol-%.2d-%.2d-%.2d-%02d%.2d%.2d", $out_dir, $year + 1900, $mon + 1,
!   $mday, $hour, $min, $sec );
  
  $start_time = time();
! $timestamp  = $start_time;
  
! #
! # find the full paths of all commands,
! # and deposit them into a variable $<name of cmd>_cmd
! @pathdirs = reverse grep(m!^/!, split(/:/, $ENV{'PATH'}, 999));
! $compressor = "@COMPRESSOR@";
! if ( $compressor =~ "@" . "COMPRESSOR" . "@" ) {
!     # hmm, config must not have fixed it, so set it to a default
!     $compressor = "bzip2";
! }
!         
! @cmds = (
!          $compressor,
!          "netstat", 
!          "ls", 
!          "ctxqsession", 
!          "prtconf",
!          "uptime",
!          "ps",
!          "pstat",
!          "vmstat",
!          "df",
!          "iostat",
!          "svmon");
! if ( "$DOTSM" == "yes" ) {
!     @cmds = ( @cmds, "dsmadmc" );
! }
! foreach $cmd ( @cmds ) {
!     $cmdvar = "";
!     if ( $cmd !~ m:^/: ) {
!         # don't have full path yet
!         foreach $dir ( @pathdirs ) {
!             if (-x "$dir/$cmd") {
!                 $cmdvar = $cmd . "_cmd";
!                 $$cmdvar="$dir/$cmd";
!                 # for "ls", that evalutes to $ls_cmd, so in later
!                 # code, $ls_cmd will expand to the full path, thus
!                 # avoiding path traversal overhead
!             }
!         }
!     }
!     printf "%20s ", $cmd;
!     $$cmdvar ? print "=> $$cmdvar\n" : print "doesn't exist in PATH; Not collecting\n";
! 
! }
! #
! ## Autodetect network interfaces
  #open IN, "ifconfig -a|";
! open IN, "$netstat_cmd -ni|";
  while (<IN>) {
! 
!     # if ( /^(\S+):/ ) {
!     if (/^(\w+).*link/) {
!         push @net_interfaces, $1;
!     }
  }
  close IN;
  
  # Grab some base system info prior to collecting stats.
! open IN, "$prtconf_cmd -m |";
  while (<IN>) {
!     if (/^Memory Size: (\d+) MB/) {
!         $pagestotl = $1 * 1024 * 1024 / 4096;    # Grab realmem in MB and convert to pages.
!         $mem_totl = $1 * 1024 * 1024;    # Grab realmem in MB and convert to Bytes
! 
!         # this gets used down in the vmstat section
!     } else {
!         print STDERR "ERROR: could not compute memorysize from $prtconf_cmd -m\n";
!         $pagestotl = 100;
!         $mem_totl = 100;
!     }
  }
  close IN;
  
  ## Make sure we can write output.
  umask 0022;    # make sure the file can be harvested
! unless ( -d $out_dir ) {
!     system( "mkdir", "-p", "$out_dir" );
  }
  
  # Set signal handlers to close and compress the output
  # file just in case.
--- 98,152 ----
  
  ## Derived variables.
  $iterations = $DURATION * 60 * 60 / $INTERVAL;    # Number of checks.
! chomp($HOST = `uname -n`);
  $out_dir = "${OUT_ROOT}/${HOST}";
! ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
    localtime(time);
  $stat_file =
!   sprintf("%s/percol-%.2d-%.2d-%.2d-%1d%.2d",
!           $out_dir,
!           $year + 1900,
!           $mon + 1,
!           $mday,
!           $hour,
!           $min);
  
+ # Base all timestamps on start time.
  $start_time = time();
! $timestamp  = 0;
  
! # Auto detect network interfaces
  #open IN, "ifconfig -a|";
! open IN, "netstat -ni|";
  while (<IN>) {
!   # if (/^(\S+):/) {
!   if (/^(\w+).*link/) {
!     push @net_interfaces, $1;
!   }
  }
  close IN;
  
  # Grab some base system info prior to collecting stats.
! open IN, "lsattr -El sys0 -a realmem |";
  while (<IN>) {
!   if (/^realmem (\d+) /) {
!     $pagestotl = $1 * 1024 / 4096; # Grab realmem in KB and convert to pages.
!     $mem_totl = $1 * 1024;         # Grab realmem in KB and convert to Bytes.
!     # this gets used down in the vmstat section
!   }
  }
  close IN;
  
  ## Make sure we can write output.
  umask 0022;    # make sure the file can be harvested
! unless (-d $out_dir) {
!   mkdir($out_dir, 0755)
!     or die "$0: cannot mkdir '$out_dir': $!\n";
  }
+ open OUT, ">$stat_file" or die "ERROR: Could not open $stat_file: $!";
+ my $oldfh = select OUT;
+ $| = 1;
+ select $oldfh;
  
  # Set signal handlers to close and compress the output
  # file just in case.
***************
*** 259,978 ****
  $SIG{QUIT} = \&exit_nicely;
  $SIG{TERM} = \&exit_nicely;
  
! # Set gloabals used for printing (or not) headers.
! $need_header     = 1;   # our file starts empty, so we need a header by default
  $prev_header_cnt = 0;
  $prev_info_cnt   = 0;
  
! # Set globals used for tracking counts from interval to interval
! # all counts start at zero
! ( $disk_t_prev,
!   $disk_rK_prev,
!   $disk_wK_prev,
!   $citrix_sessions, 
!   $citrix_disconnect ) = ( 0, 0, 0, 0, 0 );
! #
! # set up a data structure to hold info about each
! # interface, to carry from iteration to iteration,
! # so we can calculate per-second data based on delta
! # of totals between iterations
! #
! %netstat;
! foreach $if (@net_interfaces) {
!     %netstat=(%netstat,
!               $if => {});
!     $netstat{$if}={"ipkt" => -1,
!                    "ierr" => -1,
!                    "opkt" => -1,
!                    "oerr" => -1,
!                    "coll" => -1};
! }
  
! $context_sw_prev=0;
  
! # initialize all vmstat hash keys to -1, in a slick,
! # if mystifying fashion, making it real easy to add
! # more keys later, if need be
! %vmstat;
! #          1 2 3   4   5  6  7  8 
! #      9  10 11 12 13 14 15 16 17
! @vmkeys=qw(r b avm fre re pi po fr
! 	   sr cy in sys cs us sy id wa);
! @vmstat{@vmkeys} = (-1) x @vmkeys;
! $vmstat{proc_s} = -1;
! $vmstat{forks} = -1;
  
! #
! # Main while loop
! while ( $DURATION < 0 || $iterations-- > 0 ) {
  
!     $timestamp = $timestamp ? time() : $start_time;
!     ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
!       localtime(time);
!     $locltime = sprintf( "%.2d:%.2d:%.2d", $hour, $min, $sec );
  
!     ## Get runq data
!     $uptime = 0;
!     open IN, "$uptime_cmd |";
!     # Uptime is really really annoying.  Here are the various and sundry formats
!     # you might run into over the course of a day:
!     # root at npdsa01 </var/tmp><137# uname -a
!     # AIX npdsa01 1 5 0025425A4C00
!     #  root at npdsa01 </var/tmp><133# uptime
!     #    04:40PM   up 13 days, 50 mins,  17 users,  load average: 0.05, 0.12, 0.15
!     #  dmichael at npdsa01 <orca/AIXorcallator><68> rsh npddev01 uptime
!     #    04:41PM   up 2 days, 20 hrs,  33 users,  load average: 0.58, 0.80, 1.06
!     #  dmichael at npdsa01 <orca/AIXorcallator><70> rsh npddev01 uptime
!     #    04:45PM   up 2 days,  20:04,  33 users,  load average: 0.52, 0.68, 0.95
!     #  dmichael at npdsa01 <~><2> uptime
!     #    03:48PM   up   5:56,  9 users,  load average: 0.00, 0.07, 0.16
!     #  dmichael at npdsa01 <~><102> uptime
!     #    04:16PM   up 1 day,   6:25,  8 users,  load average: 0.00, 0.01, 0.01
!     # So, if you want the uptime, we have to parse all these circumstances.
  
  
!     while (<IN>) {
!         # first, get the easy part: load average
!         if (/load average:\s+(\S+),\s+(\S+),\s+(\S+)/) {
!             $load_info = join "\t", $1, $2, $3;
!         }
!         # now, get uptime, in various formats:
!         if (/up\s+(\d+)\s+day[s]?,\s+(\d+)\s+hrs,\s+(\d+)\s+user[s]?/) {
!             # up  X days, Y hrs, U users
!             $up_day = $1;
!             $up_hrs = $2;
!             $up_min = 0;
!             $nusr = $3;
!         } elsif (/up\s+(\d+)\s+day[s]?,\s+(\d+):(\d+),\s+(\d+)\s+user[s]?/) {
!             # up  X days, H:M, U users
!             $up_day = $1;
!             $up_hrs = $2;
!             $up_min = $3;
!             $nusr = $4;
!         } elsif (/up\s+(\d+)\s+day[s]?,\s+(\d+)\s+mins,\s+(\d+)\s+user[s]?/) {
!             # up X days, M mins, U users
!             $up_day = $1;
!             $up_hrs = 0;
!             $up_min = $2;
!             $nusr = $3;
!         } elsif (/up\s+(\d+)\s+mins,\s+(\d+)\s+user[s]?/) {
!             # up M mins, U users
!             $up_day = 0;
!             $up_hrs = 0;
!             $up_min = $1;
!             $nusr = $2;
!         } elsif (/up\s+(\d+):(\d+),\s+(\d+)\s+user[s]?/) {
!             # up H:M mins, U users
!             $up_day = 0;
!             $up_hrs = $1;
!             $up_min = $2;
!             $nusr = $3;
!         } elsif (/up\s+(\d+)\s+day[s]?,\s+(\d+)\s+user[s]?/) {
!             # up  X days, U users
!             # this is pretty rare, as it only happens when
!             # orca is started on an exact day boundary (it just
!             # happened to me!).
!             $up_day = $1;
!             $up_hrs = 0;
!             $up_min = 0;
!             $nusr = $2;
!         }
!         # 
!         # calculate uptime in seconds
!         $uptime = (( $up_day * 24 * 60 ) + ( $up_hrs * 60 ) + ( $up_min )) * 60;
!     }
!     close IN;
!     $load_header = "1runq\t5runq\t15runq";
!     $up_header = "uptime\tnusr";
!     $up_info = "$uptime\t$nusr";
  
!     if ( scalar( split ' ', $load_header ) != scalar( split ' ', $load_info ) )
!     {
!         $load_header = '';
!         $load_info   = '';
!         $need_header = 1;
!         print STDERR "WARNING: load header does not match load info.\n";
      }
!     if ( scalar( split ' ', $up_header ) != scalar( split ' ', $up_info ) )
!     {
!         $up_header = '';
!         $up_info   = '';
!         $need_header = 1;
!         print STDERR "WARNING: UP header does not match load info.\n";
!     }
  
!     #
!     ## Get number of system processes
!     $num_proc = -1;    # Don't count the header.
!     open IN, "$ps_cmd -ek |";
!     while (<IN>) {
!         $num_proc++;
!     }
!     close IN;
!     $proc_info   = "$num_proc";
!     $proc_header = '#proc';
  
!     if ( scalar( split ' ', $proc_header ) != scalar( split ' ', $proc_info ) )
!     {
!         $proc_header = '';
!         $proc_info   = '';
!         $need_header = 1;
!         print STDERR "WARNING: #proc header does not match #proc info.\n";
!     }
  
!     #
!     ## Get pstat data for pages
!        $sw_used = 0;
!        $sw_free = 0;
!         open IN, "$pstat_cmd -s |tail -3 |";
!         while (<IN>) {
!          @swp = split(/ +/,);
!          if (/\d/) {
!          $sw_used = $swp[1];
!          $sw_free = $swp[2];
!          $swap_used = $sw_used * 4096;
!          $swap_free = $sw_free * 4096;
!          }
!         }
!         close IN;
!         $swap_info = "$swap_used\t$swap_free";
!         $swap_header = "\tswap_used\tswap_free";
! 
!     if ( scalar( split ' ', $swap_header ) !=
!       scalar( split ' ', $swap_info ) )
!     {
!         $swap_header = '';
!         $swap_info   = '';
!         $need_header   = 1;
!         print STDERR "WARNING: pstat header does not match pstat info.\n";
      }
  
!     #
!     ## Set up vmstat stuff
!     my $inter = $INTERVAL + ( time() - $timestamp );
!     my $interfact = ( $uptime - $inter ) * ( $uptime > $inter );
!     my %vm_sum_prev = %vmstat;
!     my $vm_sum_curr = {};
  
!     #
!     ## Get numprocs/sec
!     open IN, "$vmstat_cmd -f |";
!     while (<IN>) {
!         chomp;
!         if (/^\s+(\d+) forks/) {
!             $vm_sum_curr{forks} = $1;
!             if ( $vm_sum_prev{forks} > 0 ) {
!                 # there already exists a valid "prev", so proceed as normal
!                 $vm_sum_curr{proc_s} = ( $vm_sum_curr{forks} - $vm_sum_prev{forks} ) / $inter;
!             } else {
!                 # set up first value as the average-to-date
!                 $vm_sum_curr{proc_s} = ( $vm_sum_curr{forks} ) / $uptime;
!             }
!             $vm_sum_prev{forks} = $vm_sum_curr{forks};
!         }
!     }
!     close IN;
  
!     #
!     ## Get static/counter-type vm stats
!     $inter = $INTERVAL + ( time() - $timestamp );
!     $interfact = ( $uptime - $inter ) * ( $uptime > $inter );
!     open IN, "$vmstat_cmd -s |";
!     while (<IN>) {
!         chomp;
!         if (/^\s+(\d+) total address trans. faults/) {
!             # total address trans. faults
!             #   Incremented for each occurrence of an address translation
!             #  page fault. I/O may or may not be required to resolve the
!             #  page fault. Storage protection page faults (lock misses) are
!             #  not included in this count.
!         } elsif (/^\s+(\d+) page ins/) {
!             # page ins
!             #  Incremented for each page read in by the virtual memory
!             #  manager. The count is incremented for page ins from page
!             #  space and file space. Along with the page out statistic,
!             #  this represents the total amount of real I/O initiated by
!             #  the virtual memory manager.
!         } elsif (/^\s+(\d+) page outs/) {
!             # page outs
!             #  Incremented for each page written out by the virtual memory
!             #  manager. The count is incremented for page outs to page
!             #  space and for page outs to file space.  Along with the page
!             #  in statistic, this represents the total amount of real I/O
!             #  initiated by the virtual memory manager.
!         } elsif (/^\s+(\d+) paging space page ins/) {
!             # paging space page ins
!             #  Incremented for VMM initiated page ins from paging space only.
!             $vm_sum_curr{pi} = $1;
!             if ( $vm_sum_prev{pi} > 0 ) {
!                 # there already exists a valid "prev", so proceed as normal
!                 $vm_sum_curr{pi} = ( $vm_sum_curr{pi} - $vm_sum_prev{pi} ) / $inter;
!             } else {
!                 # set up first value as the average-to-date
!                 $vm_sum_curr{pi} = ( $vm_sum_curr{pi} ) / $uptime;
!             }
!             $vm_sum_prev{pi} = $vm_sum_curr{pi};
!         } elsif (/^\s+(\d+) paging space page outs/) {
!             # paging space outs
!             #  Incremented for VMM initiated page outs to paging space only.
!             $vm_sum_curr{po} = $1;
!             if ( $vm_sum_prev{po} > 0 ) {
!                 # there already exists a valid "prev", so proceed as normal
!                 $vm_sum_curr{po} = ( $vm_sum_curr{po} - $vm_sum_prev{po} ) / $inter;
!             } else {
!                 # set up first value as the average-to-date
!                 $vm_sum_curr{po} = ( $vm_sum_curr{po} ) / $uptime;
!             }
!             $vm_sum_prev{po} = $vm_sum_curr{po};
!         } elsif (/^\s+(\d+) total reclaims/) {
!             # reclaims
!             #  Incremented when an address translation fault can be
!             #  satisfied without initiating a new I/O request. This can
!             #  occur if the page has been previously requested by VMM, but
!             #  the I/O has not yet completed; or if the page was
!             #  pre-fetched by VMM's read-ahead algorithm, but was hidden
!             #  from the faulting segment; or if the page has been put on
!             #  the free list and has not yet been reused.
!         } elsif (/^\s+(\d+) zero filled pages faults/) {
!             # zero filled pages faults
!             #  Incremented if the page fault is to working storage and can
!             #  be satisfied by assigning a frame and zero-filling it.
!         } elsif (/^\s+(\d+) executable filled pages faults/) {
!             # executable-filled page faults
!             #  Incremented for each instruction page fault.
!         } elsif (/^\s+(\d+) pages examined by clock/) {
!             # pages examined by the clock
!             #  VMM uses a clock-algorithm to implement a pseudo least
!             #  recently used (lru) page replacement scheme. Pages are aged
!             #  by being examined by the clock. This count is incremented
!             #  for each page examined by the clock.
!         } elsif (/^\s+(\d+) revolutions of the clock hand/) {
!             # revolutions of the clock hand
!             #  Incremented for each VMM clock revolution (that is, after
!             #  each complete scan of memory).
!             $vm_sum_curr{cy} = $1;
!             if ( $vm_sum_prev{cy} > 0 ) {
!                 # there already exists a valid "prev", so proceed as normal
!                 $vm_sum_curr{cy} = ( $vm_sum_curr{cy} - $vm_sum_prev{cy} ) / $inter;
!             } else {
!                 # set up first value as the average-to-date
!                 $vm_sum_curr{cy} = ( $vm_sum_curr{cy} ) / $uptime;
!             }
!             $vm_sum_prev{cy} = $vm_sum_prev{cy};
!         } elsif (/^\s+(\d+) pages freed by the clock/) {
!             # pages freed by the clock
!             #  Incremented for each page the clock algorithm selects to
!             #  free from real memory.
!             $vm_sum_curr{fr} = $1;
!             if ( $vm_sum_prev{fr} > 0 ) {
!                 # there already exists a valid "prev", so proceed as normal
!                 $vm_sum_curr{fr} = ( $vm_sum_curr{fr} - $vm_sum_prev{fr} ) / $inter;
!             } else {
!                 # set up first value as the average-to-date
!                 $vm_sum_curr{fr} = ( $vm_sum_curr{fr} ) / $uptime;
!             }
!             $vm_sum_prev{fr} = $vm_sum_prev{fr};
!         } elsif (/^\s+(\d+) backtracks/) {
!             # backtracks
!             #  Incremented for each page fault that occurs while resolving
!             #  a previous page fault. (The new page fault must be resolved
!             #  first and then initial page faults can be backtracked.)
!         } elsif (/^\s+(\d+) lock misses/) {
!             # lock misses
!             #  VMM enforces locks for concurrency by removing
!             #  addressability to a page. A page fault can occur due to a
!             #  lock miss, and this count is incremented for each such
!             #  occurrence.
!         } elsif (/^\s+(\d+) free frame waits/) {
!             # free frame waits
!             #  Incremented each time a process is waited by VMM while free
!             #  frames are gathered.
!         } elsif (/^\s+(\d+) extend XPT waits/) {
!             # extend XPT waits
!             #  Incremented each time a process is waited by VMM due to a
!             #  commit in progress for the segment being accessed.
!         } elsif (/^\s+(\d+) pending I\/O waits/) {
!             # pending I/O waits
!             #  Incremented each time a process is waited by VMM for a
!             #  page-in I/O to complete.
!         } elsif (/^\s+(\d+) start I\/Os/) {
!             # start I/Os
!             #  Incremented for each read or write I/O request initiated by
!             #  VMM. This count should equal the sum of page-ins and
!             #  page-outs.
!         } elsif (/^\s+(\d+) iodones/) {
!             # iodones
!             #  Incremented at the completion of each VMM I/O request.
!         } elsif (/^\s+(\d+) cpu context switches/) {
!             # CPU context switches
!             #  Incremented for each CPU context switch (dispatch of a new
!             #  process).
!             $vm_sum_curr{cs} = $1;
!             if ( $vm_sum_prev{cs} > 0 ) {
!                 # there already exists a valid "prev", so proceed as normal
!                 $vm_sum_curr{cs} = ( $vm_sum_curr{cs} - $vm_sum_prev{cs} ) / $inter;
!             } else {
!                 # set up first value as the average-to-date
!                 $vm_sum_curr{cs} = ( $vm_sum_curr{cs} ) / $uptime;
!             }
!         } elsif (/^\s+(\d+) device interrupts/) {
!             # device interrupts
!             #  Incremented on each hardware interrupt.
!             $vm_sum_curr{in} = $1;
!             if ( $vm_sum_prev{in} > 0 ) {
!                 # there already exists a valid "prev", so proceed as normal
!                 $vm_sum_curr{in} = ( $vm_sum_curr{in} - $vm_sum_prev{in} ) / $inter;
!             } else {
!                 # set up first value as the average-to-date
!                 $vm_sum_curr{in} = ( $vm_sum_curr{in} ) / $uptime;
!             }
!             $vm_sum_prev{in} = $vm_sum_prev{in};
!         } elsif (/^\s+(\d+) software interrupts/) {
!             # software interrupts
!             #  Incremented on each software interrupt. A software interrupt
!             #  is a machine instruction similar to a hardware interrupt
!             #  that saves some state and branches to a service
!             #  routine. System calls are implemented with software
!             #  interrupt instructions that branch to the system call
!             #  handler routine.
!         } elsif (/^\s+(\d+) traps/) {
!             # traps
!             #  Not maintained by the operating system.
!         } elsif (/^\s+(\d+) syscalls/) {
!             # syscalls
!             #  Incremented for each system call.
!             $vm_sum_curr{sys} = $1;
!             if ( $vm_sum_prev{sys} > 0 ) {
!                 # there already exists a valid "prev", so proceed as normal
!                 $vm_sum_curr{sys} = ( $vm_sum_curr{sys} - $vm_sum_prev{sys} ) / $inter;
!             } else {
!                 # set up first value as the average-to-date
!                 $vm_sum_curr{sys} = ( $vm_sum_curr{sys} ) / $uptime;
!             }
!             $vm_sum_prev{sys} = $vm_sum_prev{sys};
!         }
!     }
!     close IN;
!         
  
!     #
!     ## Get live vmstat data
!     open IN, "$vmstat_cmd 5 2 |";
!     my $inter = $INTERVAL + ( time() - $timestamp );
!     # convert a given 'count' to 'estimated count before previous
!     # interval', with the condition that if we haven't been up for at least
!     # an interval, set this value to zero this is used to seed the initial
!     # values with something more useful than 0
!     while (<IN>) {
!         chomp;
!         # r b avm fre re pi po fr sr cy inf sysf csf us sy id wa
!         # 1 r Number of kernel threads placed in run queue.
!         # 2 b Number of kernel threads placed in wait queue (awaiting
!         #    resource, awaiting input/output).
!         #  
!         #  Memory: information about the usage of virtual and real
!         #  memory. Virtual pages are considered active if they have been
!         #  accessed. A page is 4096 bytes.
!         #  
!         # 3 avm Active virtual pages.
!         # 4 fre Size of the free list.
!         #      Note: A large portion of real memory is utilized as a cache
!         #      for file system data. It is not unusual for the size of the
!         #      free list to remain small.
!         #  
!         #  Page: information about page faults and paging activity. These
!         #  are averaged over the interval and given in units per second.
!         #  
!         # 5 re Pages input/output list.
!         # 6 pi Pages paged in from paging space.
!         # 7 po Pages paged out to paging space.
!         # 8 fr Pages freed (page replacement).
!         # 9 sr Pages scanned by page-replacement algorithm.
!         # 10 cy Clock cycles by page-replacement algorithm.
!         #  
!         #  Faults: trap and interrupt rate averages per second over the
!         #  sampling interval.
!         #  
!         # 11 in Device interrupts.
!         # 12 sy System calls.
!         # 13 cs Kernel thread context switches.
!         #  
!         #  Cpu: breakdown of percentage usage of CPU time.
!         #  
!         # 14 us User time.
!         # 15 sy System time.
!         # 16 id CPU idle time.
!         # 17 wa CPU idle time during which the system had outstanding disk/NFS I/O
!         #  request(s). See detailed description above.
!         #  
!         if (/^[\s\d]+$/) {
!             # overwrite first line on 2nd pass
!             my $vm_curr = {};
!             my $info = "";
!             # assign %curr{field} values.  @vmkeys is
!             # an array of field identifiers that should align
!             # with the output of the vmstat command
!             @vm_curr{@vmkeys} = split;
!             foreach $_ ( @vmkeys , proc_s ) {
!                 # if we already got the data from the summary
!                 # output (more reliable / accurate), then
!                 # use that, otherwise, use the values from the
!                 # vmstat interval run
!                 $vmstat{$_}=(
!                              ($vm_sum_curr{$_} > 0 )
!                              * ( $vm_sum_curr{$_} )
!                              ||
!                              $vm_curr{$_} );
!                 $info = $info . $vmstat{$_} . "\t";
!             }
!             $vmstat{forks}=$vm_sum_curr{forks};
!             # tack on other things not in @vmkeys
!             $info = $info . "\t" . $pagestotl;
!             $vmstat_info = $info;
!         }
      }
!     close IN;
!     $vmstat_header =
! "runque\twaiting\tpagesactive\tfreememK\tPagesIO/s\tPagesI/s\tPagesO/s\tPagesF/s\tscanrate\tcy_cycles\tdevinter\tsyscalls\tcs\tusr%\tsys%\tidle%\twio%\t#proc/s\tpagestotl";
  
!     if ( scalar( split ' ', $vmstat_header ) !=
!       scalar( split ' ', $vmstat_info ) )
!     {
!         print STDERR "WARNING: vmstat header does not match vmstat info.\n";
!         print STDERR "header is: $vmstat_header\n";
!         print STDERR "header #fields: ", scalar(split ' ', $vmstat_header ), "\n";
!         print STDERR "info is  : $vmstat_info\n";
!         print STDERR "info #fields  : ", scalar(split ' ', $vmstat_info), "\n";
  
!         $vmstat_header = '';
!         $vmstat_info   = '';
!         $need_header   = 1;
!         print STDERR "vmkeys is : ", $#vmkeys, "\n";
!     }
  
!     #
!     ## Get filesystem data
!     $fs_header = '';
!     $fs_info   = '';
!     open IN, "$df_cmd -k -v |";
!     while (<IN>) {
!         chomp;
! 
!         if (m%^/dev%) {
!             ( $mnt_dev, $blocks, $used, $free, $pct_used, $iused, $ifree,
!               $ipct_used, $mnt ) = split;
! 
!             # Recalculate percents because df rounds.
!             $fs_info .= "\t" 
!                 . sprintf( "%s\t%s\t%s\t%.5f\t%d\t%s\t%s\t%.5f", $blocks, $used,
!               $free, ( $used / $blocks ) * 100, ( $iused + $ifree ), $iused,
!               $ifree, ( $iused / ( $iused + $ifree ) ) * 100 );
!             $fs_header .= "\t" . join "\t", "mntC_$mnt", "mntU_$mnt",
!               "mntA_$mnt", "mntP_$mnt", "mntc_$mnt", "mntu_$mnt", "mnta_$mnt",
!               "mntp_$mnt";
!         }
      }
!     close IN;
  
!     if ( scalar( split ' ', $fs_header ) != scalar( split ' ', $fs_info ) ) {
!         $fs_header   = '';
!         $fs_info     = '';
!         $need_header = 1;
!         print STDERR
!           "WARNING: filesystem header does not match filesystem info.\n";
!     }
  
!     #
!     ## Get iostat data
!     $disk_t  = 0;
!     $disk_rK = 0;
!     $disk_wK = 0;
!     undef %disks;
!     # open IN, "iostat -d 1 2|";
!     # iostat totals are useful; running counts are good for
!     # interactive observations, but a 1s interval observation
!     # isn't representative for 'snapshots'; only total counts are.
!     open IN, "$iostat_cmd -s -d|";
  
!     # for accuracy's sake, add on number of seconds
!     # since this run started
!     my $inter = $INTERVAL + ( time() - $timestamp );
  
      while (<IN>) {
!         # if (/^(\S+)\s+\S+\s+\S+\s+(\S+)\s+(\d+)\s+(\d+)/) {
!         if (/^\s+(\S+)\s+(\S+)\s+(\d+)\s+(\d+)/) {
!             my $kBs  = $1;
!             my $tps  = $2;
!             my $rK   = $3;
!             my $wK   = $4;
!             if ( $disk_rK_prev == 0 ) {
!                 # there is no previous data, so seed with average rates
!                 # spread over time-since-boot minus specified interval.
!                 # If uptime < INTERVAL, then we haven't been up long enough
!                 # to have a (valid) previous interval, so 
!                 # the previous count really is zero.
!                 $disk_rK_prev = ( $rK / $uptime ) * ( $uptime - $inter ) * ($uptime > $INTERVAL);
!                 $disk_wK_prev = ( $wK / $uptime ) * ( $uptime - $inter ) * ($uptime > $INTERVAL);
! 
!                 # iostat on AIX only reports transactions per second,
!                 # to 3 significant figures, not total transactions.
!                 # So this value is useless; maybe some other AIX utility
!                 # can grab this, but for now, just keep reporting the
!                 # running average.
!                 $disk_t_prev  = $tps;
!             }
!             # calculate reads/writes per second from totals reported by iostat
!             $disk_rK = ( $rK - $disk_rK_prev ) / $inter;
!             $disk_wK = ( $wK - $disk_wK_prev ) / $inter;
!             $disk_t = $tps;
!             $disk_rK_prev = $rK;
!             $disk_wK_prev = $wK;
!             $disk_t_prev = $tps;
!         }
      }
      close IN;
!     $iostat_header = "disk_t/s\tdisk_rK/s\tdisk_wK/s";
!     $iostat_info   = "${disk_t}\t${disk_rK}\t${disk_wK}";
  
!     if ( scalar( split ' ', $iostat_header ) !=
!       scalar( split ' ', $iostat_info ) )
!     {
!         $iostat_header = '';
!         $iostat_info   = '';
!         $need_header   = 1;
!         print STDERR "WARNING: iostat header does not match iostat info.\n";
!     }
! 
!     #
!     ## Get packet data
      $packet_header = '';
      $packet_info   = '';
  
!     foreach $interface (@net_interfaces) {
!         $packet_header .=
!             "\t${interface}Ipkt/s\t${interface}IErr/s\t${interface}Opkt/s\t${interface}OErr/s\t${interface}Coll/s\t";
!         open IN, "$netstat_cmd -n -I $interface 1 |";
!         # format looks like
!         #     input   (en0)      output           input   (Total)    output
!         #  packets  errs  packets  errs colls  packets  errs  packets  errs colls
!         # 492126804     0 255536091     0     0 553459817     0 316872224     0     0
! 
!         # for accuracy's sake, add on number of seconds
!         # since this run started
!         my $interv = $INTERVAL + ( time() - $timestamp );
! 	# make %prev point to %netstat{$interface}, just for readability
! 	my $prev = %netstat->{$interface};
! 
!         while (<IN>) {
!             if (/^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+/) {
!                 my $ipkt = $1;
!                 my $ierr = $2;
!                 my $opkt = $3;
!                 my $oerr = $4;
!                 my $coll = $5;
!                 if ( $prev->{"ipkt"} == -1 ) {
!                     # haven't populated data yet - extrapolate
!                     my $extrapolate = sub { ( @_[0] / $uptime ) * 
!                             ( $uptime - $interv ) * ( $uptime > $interv ); };
!                     # use this funky notation, so that packet_info
!                     # syntax doesn't depend on instance status
!                     # of prev (copy or pointer)
!                     $prev=\%{{ "ipkt" => &$extrapolate($ipkt),
!                                "ierr" => &$extrapolate($ierr),
!                                "opkt" => &$extrapolate($opkt),
!                                "oerr" => &$extrapolate($oerr),
!                                "coll" => &$extrapolate($coll) }};
! 	            }
!                 # build packet info line with appropriate rates
!                 $packet_info .= "\t" . join "\t",
!                    ( ( $ipkt - $prev->{"ipkt"} ) / $interv ),
!                    ( ( $ierr - $prev->{"ierr"} ) / $interv ),
!                    ( ( $opkt - $prev->{"opkt"} ) / $interv ),
!                    ( ( $oerr - $prev->{"oerr"} ) / $interv ),
!                    ( ( $coll - $prev->{"coll"} ) / $interv );
!                 # remember current counts for next iteration calculations
! 		$netstat{$interface} = { "ipkt" => $ipkt,
! 					 "ierr" => $ierr,
! 					 "opkt" => $opkt,
! 					 "oerr" => $oerr,
! 					 "coll" => $coll};
!                 # don't care about rest of input, just terminate read loop
!                 last; 
!             }
!         }
!         close IN;
      }
  
!     # print "packet header is $packet_header\n";
!     # print "packet info is $packet_info\n";
!     if ( scalar( split ' ', $packet_header ) !=
!       scalar( split ' ', $packet_info ) )
!     {
!         $packet_header = '';
!         $packet_info   = '';
!         $need_header   = 1;
!         print STDERR "WARNING: packet header does not match packet info.\n";
!         print STDERR "packet header is: ", $packet_header, "\n";
!         print STDERR "packet info is: ", $packet_info, "\n";
!     }
  
!     #
!     ## Get TCP Connection data
!     $tcp_estb = 0;
!     open IN, "$netstat_cmd -an |";
!     while (<IN>) {
!         if (/^tcp.+ESTABLISHED$/) {
!             $tcp_estb++;
!         }
!     }
!     close IN;
!     $tcp_info   = $tcp_estb;
!     $tcp_header = 'tcp_estb';
  
!     if ( scalar( split ' ', $tcp_estb_header ) !=
!       scalar( split ' ', $tcp_estb_info ) )
!     {
!         $tcp_estb_header = '';
!         $tcp_estb_info   = '';
!         $need_header     = 1;
!         print STDERR "WARNING: tcp_estb header does not match tcp_estb info.\n";
      }
  
!     #
!     ## Get TSM Database space usage
!     $tsmdb = 0;
!     if ( -e $dsmadmc_cmd && -x $dsmadmc_cmd ) {
!         open IN, "$dsmadmc_cmd -id=$TSMID -password=$TSMPW 'query db' |tail -r -n 5 |";
!         while (<IN>) {
!           @fld = split(/ +/,);
!           if (/\d/) {
!             $tsmdb = $fld[8];
!            }
!         }
!         close IN;
!     }
  
!     $tsm_info = $tsmdb;
!     $tsm_header = "tsmdb\t";
! 
!     if ( scalar( split ' ', $tsm_header ) !=
!       scalar( split ' ', $tsm_info ) )
!     {
!         $tsm_header = '';
!         $tsm_info   = '';
!         $need_header     = 1;
!         print STDERR "WARNING: tsmdb header does not match tsmdb info.\n";
!     }
! 
!     #
!     ## Get Memory Usage breakup using SVMON
!     $mem_work = 0;
!     $mem_pres = 0;
!     $mem_clnt = 0;
!     open IN, "$svmon_cmd -G |tail -2 |";
!     while (<IN>) {
!      @memp = split(/ +/,);
!      if (/use\s+(\d+) /) {
        $m_work = $memp[2];
        $m_pres = $memp[3];
        $m_clnt = $memp[4];
--- 155,450 ----
  $SIG{QUIT} = \&exit_nicely;
  $SIG{TERM} = \&exit_nicely;
  
! # Set globals used for printing (or not) headers.
! $need_header     = 1;
  $prev_header_cnt = 0;
  $prev_info_cnt   = 0;
  
! while ($iterations-- > 0) {
!   $timestamp = $timestamp ? time() : $start_time;
!   ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
!     localtime(time);
!   $locltime = sprintf("%.2d:%.2d:%.2d", $hour, $min, $sec);
  
!   ## Get runq data
!   $uptime = 0;
!   open IN, "uptime |";
!   while (<IN>) {
!     if (/load average:\s+(\S+),\s+(\S+),\s+(\S+)/) {
!       $load_info = join "\t", $1, $2, $3;
!     }
!     @upt = split(/ +/,);
!     $uptd = $upt[3];
!     $up_day = $uptd * 24 * 60 * 60;
!     $up_hrs = 0;
!     $up_min = 0;
  
!     if (/day(?:s?),\s+(\S+)\s+min(?:s?),/) {
!       $nusr = $upt[7];
!       $up_min = $1 * 60;
!     } elsif (/day(?:s?),\s+(\S+)\s+hr(?:s?),/) {
!       $nusr = $upt[7];
!       $up_hrs = $1 * 60 * 60;
!     } elsif (/day(?:s?),\s+(\S+):(\S+), /) {
!       $nusr = $upt[6];
!       $up_hrs = $1 * 60 * 60;
!       $up_min = $2 * 60;
!     } elsif ($_ !~ /day/) {
!       $up_day = 0;
!       if (/\s+(\S+):(\S+),/) {
!         $nusr = $upt[4];
!         $up_hrs = $1 * 60 * 60;
!         $up_min = $2 * 60;
!       } elsif (/\s+(\S+)\s+min(?:s?),/) {
!         $nusr = $upt[5];
!         $up_min = $1 * 60;
!       } elsif (/\s+(\S+)\s+hr(?:s?),/) {
!         $nusr = $upt[5];
!         $up_hrs = $1 * 60 * 60;
!       }
!     } else {
!       $nusr = $upt[5];
!     }
  
!     $uptime = $up_day + $up_hrs + $up_min;
!   }
!   close IN;
!   $load_header = "1runq\t5runq\t15runq";
!   $up_header = "uptime\tnusr";
!   $up_info = "$uptime\t$nusr";
  
!   unless (strings_have_same_number_words($load_header, $load_info)) {
!     $load_header = '';
!     $load_info   = '';
!     $need_header = 1;
!     warn "$0: warning: load header does not match load info.\n";
!   }
  
!   unless (strings_have_same_number_words($up_header, $up_info)) {
!     $up_header = '';
!     $up_info   = '';
!     $need_header = 1;
!     warn "$0: warning: UP header does not match load info.\n";
!   }
  
+   ## Get number of system processes
+   $num_proc = -1;    # Don't count the header.
+   open IN, "ps -ek |";
+   while (<IN>) {
+     $num_proc++;
+   }
+   close IN;
+   $proc_info   = $num_proc;
+   $proc_header = '#proc';
  
!   unless (strings_have_same_number_words($proc_header, $proc_info)) {
!     $proc_header = '';
!     $proc_info   = '';
!     $need_header = 1;
!     warn "$0: warning: #proc header does not match #proc info.\n";
!   }
  
!   ## Get pstat data for pages
!   $sw_used = 0;
!   $sw_free = 0;
!   open IN, "pstat -s |tail -3 |";
!   while (<IN>) {
!     @swp = split(/ +/,);
!     if (/\d/) {
!       $sw_used = $swp[1];
!       $sw_free = $swp[2];
!       $swap_used = $sw_used * 4096;
!       $swap_free = $sw_free * 4096;
      }
!   }
!   close IN;
!   $swap_info = "$swap_used\t$swap_free";
!   $swap_header = "\tswap_used\tswap_free";
  
!   unless (strings_have_same_number_words($swap_header, $swap_info)) {
!     warn "$0: warning: pstat header does not match pstat info.\n";
!     $swap_header = '';
!     $swap_info   = '';
!     $need_header   = 1;
!   }
  
!   ## Get vmstat data
!   open IN, "vmstat 1 2|";
!   while (<IN>) {
!     chomp;
  
!     if (/^[\s\d]+$/) {
!       # overwrite first line on 2nd pass
!       my ($vmstat_r,   $vmstat_b,  $vmstat_avm, $vmstat_fre,
!           $vmstat_re,  $vmstat_pi, $vmstat_po,  $vmstat_fr,
!           $vmstat_sr,  $vmstat_cy, $vmstat_inf, $vmstat_syf,
!           $vmstat_csf, $vmstat_us, $vmstat_sy,  $vmstat_id,
!           $vmstat_wa)
!         = split;
!       $vmstat_info = join("\t",
!                           $vmstat_r, $vmstat_b, $vmstat_avm, $vmstat_fre,
!                           $pagestotl, $vmstat_pi, $vmstat_po, $vmstat_fr,
!                           $vmstat_sr, $vmstat_us, $vmstat_sy, $vmstat_wa,
!                           $vmstat_id);
      }
+   }
+   close IN;
+   $vmstat_header = "runque\twaiting\tpagesactive\tpagesfree\tpagestotl\t" .
+                    "PagesI/s\tPagesO/s\tPagesF/s\tscanrate\tusr%\tsys%\t" .
+                    "wio%\tidle%";
  
!   unless (strings_have_same_number_words($vmstat_header, $vmstat_info)) {
!     warn "$0: warning: vmstat header does not match vmstat info.\n";
!     $vmstat_header = '';
!     $vmstat_info   = '';
!     $need_header   = 1;
!   }
  
!   ## Get filesystem data
!   $fs_header = '';
!   $fs_info   = '';
!   open IN, "df -k -v |";
!   while (<IN>) {
!     chomp;
  
!     if (m%^/dev%) {
!       my ($mnt_dev, $blocks, $used, $free, $pct_used, $iused, $ifree,
!           $ipct_used, $mnt) = split;
  
!       # Recalculate percents because df rounds.
!       $fs_info .= "\t"
!                . sprintf("%s\t%s\t%s\t%.5f\t%d\t%s\t%s\t%.5f",
!                          $blocks,
!                          $used,
!                          $free,
!                          100*($used/$blocks),
!                          ($iused + $ifree),
!                          $iused,
!                          $ifree,
!                          100*$iused/($iused + $ifree));
!       $fs_header .= "\t" . join("\t",
!                                 "mntC_$mnt", "mntU_$mnt", "mntA_$mnt",
!                                 "mntP_$mnt", "mntc_$mnt", "mntu_$mnt",
!                                 "mnta_$mnt", "mntp_$mnt");
      }
!   }
!   close IN;
  
!   unless (strings_have_same_number_words($fs_header, $fs_info)) {
!     warn "$0: warning: filesystem header does not match filesystem info.\n";
!     $fs_header   = '';
!     $fs_info     = '';
!     $need_header = 1;
!   }
  
!   ## Get iostat data
!   $disk_t  = 0;
!   $disk_rK = 0;
!   $disk_wK = 0;
!   undef %disks;
!   open IN, "iostat -d 1 2|";
  
!   while (<IN>) {
!     if (/^(\S+)\s+\S+\s+\S+\s+(\S+)\s+(\d+)\s+(\d+)/) {
!       my $disk = $1;
!       my $tps  = $2;
!       my $rK   = $3;
!       my $wK   = $4;
!       if (not $disks{$disk}) {
!         $disks{$disk}++;    # Get rK & wK from first pass.
!         $disk_rK += $rK;
!         $disk_wK += $wK;
!       } else {
!         $disk_t += $tps;    # Get trans per sec from second pass.
!       }
      }
!   }
!   close IN;
!   $iostat_header = "disk_t/s\tdisk_rK/s\tdisk_wK/s\t";
!   $iostat_info   = "${disk_t}\t${disk_rK}\t${disk_wK}";
  
!   unless (strings_have_same_number_words($iostat_header, $iostat_info)) {
!     warn "$0: warning: iostat header does not match iostat info.\n";
!     $iostat_header = '';
!     $iostat_info   = '';
!     $need_header   = 1;
!   }
  
!   ## Get packet data
!   $packet_header = '';
!   $packet_info   = '';
  
!   #foreach $interface (split(/\s+/, $NET_INTERFACES)) {
!   foreach $interface (@net_interfaces) {
!     $packet_header .= "\t${interface}Ipkt/s\t${interface}IErr/s\t" .
!                       "${interface}Opkt/s\t${interface}OErr/s\t" .
!                       "${interface}Coll/s\t";
  
+     open IN, "netstat -n -I $interface 1|";
      while (<IN>) {
!       if (/^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+/) {
!         $packet_info .= "\t" . join("\t", $1, $2, $3, $4, $5);
!         last;
!       }
      }
      close IN;
!   }
  
!   unless (strings_have_same_number_words($packet_header, $packet_info)) {
!     warn "$0: warning: packet header does not match packet info.\n";
      $packet_header = '';
      $packet_info   = '';
+     $need_header   = 1;
+   }
  
!   ## Get TCP Connection data
!   $tcp_estb = 0;
!   open IN, "netstat -an |";
!   while (<IN>) {
!     if (/^tcp.+ESTABLISHED$/) {
!       $tcp_estb++;
      }
+   }
+   close IN;
  
!   $tcp_info   = $tcp_estb;
!   $tcp_header = 'tcp_estb';
  
!   unless (strings_have_same_number_words($tcp_estb_header, $tcp_estb_info)) {
!     warn "$0: warning: tcp_estb header does not match tcp_estb info.\n";
!     $tcp_estb_header = '';
!     $tcp_estb_info   = '';
!     $need_header     = 1;
!   }
  
!   ## Get TSM Database space usage
!   $tsmdb = 0;
!   open IN, "dsmadmc -id=view -password=view 'query db' |tail -r -n 5 |";
!   while (<IN>) {
!     @fld = split(/ +/,);
!     if (/\d/) {
!       $tsmdb = $fld[8];
      }
+   }
+   close IN;
+   $tsm_info = $tsmdb;
+   $tsm_header = "tsmdb\t";
  
!   unless (strings_have_same_number_words($tsm_header, $tsm_info)) {
!     warn "$0: warning: tsmdb header does not match tsmdb info.\n";
!     $tsm_header = '';
!     $tsm_info   = '';
!     $need_header     = 1;
!   }
  
!   ## Get Memory Usage breakup using SVMON
!   $mem_work = 0;
!   $mem_pres = 0;
!   $mem_clnt = 0;
!   open IN, "svmon -G |tail -2 |";
!   while (<IN>) {
!     @memp = split(/ +/,);
!     if (/use\s+(\d+) /) {
        $m_work = $memp[2];
        $m_pres = $memp[3];
        $m_clnt = $memp[4];
***************
*** 979,1302 ****
        $mem_work = $m_work * 4096;
        $mem_pres = $m_pres * 4096;
        $mem_clnt = $m_clnt * 4096;
-      }
      }
!     close IN;
!     $mem_info = "$mem_work\t$mem_pres\t$mem_clnt\t$mem_totl";
!     $mem_header = "mem_work\tmem_pres\tmem_clnt\tmem_totl";
  
!     if ( scalar( split ' ', $mem_header ) !=
!       scalar( split ' ', $mem_info ) )
!     {
!         $mem_header = '';
!         $mem_info   = '';
!         $need_header     = 1;
!         print STDERR "WARNING: memory header does not match memory info.\n";
!     }
  
!     #
!     ## Get TSM Tape Drive usage
!     $rmt = 0;
!     $rmt5 = 5;
!     if ( -e $dsmadmc_cmd && -x $dsmadmc_cmd ) {
!         open IN, "$dsmadmc_cmd -id=$TSMID -password=$TSMPW 'query mount' |grep matches |";
!         while (<IN>) {
!             @fld = split(/ +/,);
!             if (/\d/) {
!                 $rmt = $fld[1];
!             }
!         }
!         close IN;
!     }
!     $tsm_rmt_header = "rmt5\trmt\t";
!     $tsm_rmt_info = "$rmt5\t$rmt";
  
!     if ( scalar( split ' ', $tsm_rmt_header ) !=
!       scalar( split ' ', $tsm_rmt_info ) )
!     {
!         print STDERR "WARNING: TSM RMT header does not match TSM RMT info.\n";
!         $tsm_rmt_header = '';
!         $tsm_rmt_info   = '';
!         $need_header     = 1;
      }
  
!     #
!     ## Get TSM Recovery Log space usage
!     $tsmlog = 0;
!     if ( -e $dsmadmc_cmd && -x $dsmadmc_cmd ) {
!             open IN, "$dsmadmc_cmd -id=$TSMID -password=$TSMPW 'query log' |tail -r -n 4 |";
!             while (<IN>) {
!                 @fld = split(/ +/,);
!                 if (/\d/) {
!                     $tsmlog = $fld[8];
!                 }
!             }
!             close IN;
!         }
!     $tsm_log_info = $tsmlog;
!     $tsm_log_header = 'tsmlog';
  
!     if ( scalar( split ' ', $tsm_log_header ) !=
!       scalar( split ' ', $tsm_log_info ) )
!     {
!         print STDERR "WARNING: TSM Log header ($tsm_log_header) does not match TSM Log info ($tsm_log_info).\n";
!         $tsm_log_header = '';
!         $tsm_log_info   = '';
!         $need_header     = 1;
      }
  
!     #
!     ## Get TSM Tape usage
!     $tsmpvt = 0;
!     $tsmscr = 0;
!     $tsmvlt = 0;
!     if ( -e $dsmadmc_cmd && -x $dsmadmc_cmd ) {
!         open IN, "$dsmadmc_cmd -id=$TSMID -password=$TSMPW 'query libvol' | grep 'Private' | wc -l |";
!         while (<IN>) {
!         chomp;
!         @fld = split(/ +/,);
!         if (/\d/) {
!           $tsmpvt = $fld[1];
!          }
!         }
!         close IN;
  
!         open IN, "$dsmadmc_cmd -id=$TSMID -password=$TSMPW 'query libvol' | grep 'Scratch' | wc -l |";
!         while (<IN>) {
!             chomp;
!             @fld = split(/ +/,);
!             if (/\d/) {
!                 $tsmscr = $fld[1];
!             }
!         }
!         close IN;
! 
!         open IN, "$dsmadmc_cmd -id=$TSMID -password=$TSMPW 'query drmedia' | grep 'Vault' | wc -l |";
!         while (<IN>) {
!             chomp;
!             @fld = split(/ +/,);
!             if (/\d/) {
!                 $tsmvlt = $fld[1];
!             }
!         }
      }
  
!     $tsm_tape_info = join "\t", $tsmpvt, $tsmscr, $tsmvlt;
!     $tsm_tape_header = join "\t", tsmpvt, tsmscr, tsmvlt;
! 
!     if ( scalar( split ' ', $tsm_tape_header ) !=
!       scalar( split ' ', $tsm_tape_info ) )
!     {
!         print STDERR "WARNING: TSM Tape header does not match TSM Tape info.\n";
!         $tsm_tape_header = '';
!         $tsm_tape_info   = '';
!         $need_header     = 1;
      }
  
!     #
!     ## Get TSM Disk Storage Pool usage
!     $tsmphcy = 0;
!     $tsmphcn = 0;
!     if ( -e $dsmadmc_cmd && -x $dsmadmc_cmd ) {
!         open IN, "$dsmadmc_cmd -id=$TSMID -password=$TSMPW 'query stgpool' |";
!         while (<IN>) {
!         @fld = split(/ +/,);
!         if (/\d/) {
!           if ( $fld[0] eq "PHCYDISKPO-" ) {
!             $tsmphcy = $fld[3];
!            }
!           elsif ( $fld[0] eq "PHCNDISKPO-" ) {
!             $tsmphcn = $fld[3];
!            }
!          }
!         }
!         close IN;
      }
  
!     $tsm_stg_info = join "\t", $tsmphcy, $tsmphcn;
!     $tsm_stg_header = join "\t", tsmphcy, tsmphcn;
  
!     if ( scalar( split ' ', $tsm_stg_header ) !=
!       scalar( split ' ', $tsm_stg_info ) )
!     {
!         print STDERR "WARNING: TSM Storage Pool header does not match TSM Storage Pool info.\n";
!         $tsm_stg_header = '';
!         $tsm_stg_info   = '';
!         $need_header     = 1;
!     }
  
!     #
!     ## Get runq data
!     if ( $ctxqsession_cmd == "" ) {
!         $ctxqsession_cmd="/usr/lpp/CTXSmf/bin/ctxqsession";
      }
!     $citrix_sessions=0;
!     $citrix_disconnect=0;
!     if ( -e $ctxqsession_cmd && -x $ctxqsession_cmd ) {
!         open IN, "$ctxqsession_cmd |";
!         while (<IN>) {
!             if ( /tcp\#/ ) {
!                 $citrix_sessions++;
!             }
!             if ( /\sdisc\s/ ) {
!                 $citrix_disconnect++;
!             }
!         }
!         close IN;
!     }
  
!     $citrix_info = join "\t", $citrix_sessions, $citrix_disconnect;
!     $citrix_header = join "\t", ctx_sess, ctx_disc;
  
!     #
!     ## construct our header
!     $out_header = join "\t", "timestamp", "locltime", $load_header, $up_header,
!       $proc_header, $vmstat_header, $fs_header, $iostat_header, $packet_header,
!       $tcp_header, $tsm_header, $swap_header, $mem_header, $tsm_rmt_header,
!       $tsm_log_header, $tsm_tape_header, $tsm_stg_header, $citrix_header;
!     $out_header =~ tr/ \t/\t/s;    # translate whitespace to single tabs
  
!     ## construct a line of data
!     $out_info = join "\t", $timestamp, $locltime, $load_info, $up_info, $proc_info,
!       $vmstat_info, $fs_info, $iostat_info, $packet_info, $tcp_info, $tsm_info, 
!       $swap_info, $mem_info, $tsm_rmt_info, $tsm_log_info, $tsm_tape_info,
!       $tsm_stg_info, $citrix_info;
!     $out_info =~ tr/ \t/\t/s;      # translate whitespace to single tabs
  
!     # count the fields in header and info lines--make sure the counts match.
!     $header_cnt = split ' ', $out_header;
!     $info_cnt = split ' ', $out_info;
!     if ( $header_cnt != $info_cnt ) {
!         # oh, dear!  How did this happen?  Bad code!  No biscuit!
!         # somehow we collected more (or less) data than we have columns for.
!         # That's bad, Ray.
!         print STDERR
!           "ERROR: header columns do not equal data columns. Exiting.\n";
!         &exit_nicely;
!     }
  
!     #
!     if ( $need_header == 1 ) {
!         # If we did things right,
!         # The only way we get here is if it's the first line of the output file,
!         # or if we already know of a header change, such as if TSM spewed,
!         # or new filesystems were mounted, or dismounted, or .....
!         # In any of these cases, we'll be creating a new output file anyway,
!         # so just let things happen.
!     } elsif ( $prev_header != $out_header ) {
!         # the header changed.  Time for a new file.
!         $need_header = 1;
!     } elsif ( $header_cnt != $prev_header_cnt or $info_cnt != $prev_info_cnt ) {
!         # crazy -- the previous count and the current count don't match,
!         # so we must have different headers, which should have been caught
!         # in the header comparison above, but apparently not.
!         $need_header = 1;
!     }
!     $prev_header = $out_header;
!     $prev_header_cnt = $header_cnt;
!     $prev_info_cnt   = $info_cnt;
  
!     #
!     ## Check to see if we've crossed days,
!     ## or if we need a new header.
!     ## In either case, we need a new file.
!     #
!     ( $nsec, $nmin, $nhour, $nmday, $nmon, $nyear, $nwday, $nyday, $nisdst ) =
!       localtime(time);
!     if ( ($nyear*1000 + $nyday) > ($oyear*1000 + $oyday) ) {
!         # hey, we crossed days.  Be nice--open a new file, to keep the
!         # existing file from getting stupid-big.  Note that
!         # creating a new output file automatically sets need_header to 1.
!         &new_output_file;
!     } elsif ( $need_header ) {
!         # We didn't switch days yet, but we still need a new header for various
!         # possible reasons.  So make a new output file (always need a new
!         # output file whenever we need a new header--even the first time!)
!         &new_output_file;
!     }
  
!     # Can't imagine how we'd get here without creating an output file.
!     # But, here's the code for creating it, just in case.
!     # if (! select OUT) {
!     #   open OUT, ">$stat_file" or die "ERROR: Could not open $stat_file: $!";
!     #   my $oldfh = select OUT;
!     #   $| = 1;
!     #   select $oldfh;
!     # }
  
!     ## Write output
!     if ($need_header) {
!         print OUT $out_header, "\n";
!         $need_header = 0;
!     }
!     print OUT $out_info, "\n";
  
!     # use this for testing, to speed things along.
!     #sleep 5;
!     sleep $INTERVAL - ( time() - $timestamp );
  
  }
  
! &exit_nicely;
! 
! 
! ######################################################################
! #
! # subroutines
! #
! 
! #
! ##################################################
! # exit_nicely ()
! # This subroutine is called by the signal handler, or at the end
! # of the while loop, if ever reached.
! #
! # It takes no arguments, and does not return - it simply exits
! # the fork it's running under.
  sub exit_nicely {
!     if ( <OUT> ) {
!         close OUT;
!         @args = split(' ',$compressor);
!         push @args, "$stat_file";
!         print @args, "\n";
!         system(@args);
!     }
!     exit 0;
  }
- 
- #
- ##################################################
- # new_output_file ()
- # 
- # Whenever a new header is needed, or if we change
- # days, then do the following:
- #   o close previous output file
- #   o fork a process to compress it
- #   o create a new output file
- #
- sub new_output_file {
-     if ( <OUT> ) {
-         close OUT;
-     }
-     ( $osec, $omin, $ohour, $omday, $omon,
-       $oyear, $owday, $oyday, $oisdst ) = localtime(time);
-     # close & compress file in background process
-     # create new file & continue in current process
-     if ( $pid = fork ) {
-         # parent process resumes here
-         $stat_file =
-             sprintf( "%s/percol-%.2d-%.2d-%.2d-%.2d%.2d%.2d",
-                      $out_dir, $oyear + 1900, $omon + 1,
-                      $omday, $ohour, $omin, $osec );
-         open OUT, ">$stat_file" or die "ERROR: Could not open $stat_file: $!";
-         my $oldfh = select OUT;
-         $| = 1;
-         select $oldfh;
-         $need_header = 1;
-     } elsif (defined $pid) {
-         # child process resumes here
-         &exit_nicely;
-     }
- }
- 
-     
--- 451,640 ----
        $mem_work = $m_work * 4096;
        $mem_pres = $m_pres * 4096;
        $mem_clnt = $m_clnt * 4096;
      }
!   }
!   close IN;
  
!   $mem_info = "$mem_work\t$mem_pres\t$mem_clnt\t$mem_totl";
!   $mem_header = "mem_work\tmem_pres\tmem_clnt\tmem_totl";
  
!   unless (strings_have_same_number_words($mem_header, $mem_info)) {
!     warn "$0: warning: memory header does not match memory info.\n";
!     $mem_header = '';
!     $mem_info   = '';
!     $need_header     = 1;
!   }
  
!   ## Get TSM Tape Drive usage
!   $rmt = 0;
!   $rmt5 = 5;
!   open IN, "dsmadmc -id=view -password=view 'query mount' |grep matches |";
!   while (<IN>) {
!     @fld = split(/ +/,);
!     if (/\d/) {
!       $rmt = $fld[1];
      }
+   }
+   close IN;
+   $tsm_rmt_header = "rmt5\trmt\t";
+   $tsm_rmt_info = "$rmt5\t$rmt";
  
!   unless (strings_have_same_number_words($tsm_rmt_header, $tsm_rmt_info)) {
!     warn "$0: warning: TSM RMT header does not match TSM RMT info.\n";
!     $tsm_rmt_header = '';
!     $tsm_rmt_info   = '';
!     $need_header     = 1;
!   }
  
!   ## Get TSM Recovery Log space usage
!   $tsmdb = 0;
!   open IN, "dsmadmc -id=view -password=view 'query log' |tail -r -n 4 |";
!   while (<IN>) {
!     @fld = split(/ +/,);
!     if (/\d/) {
!       $tsmlog = $fld[8];
      }
+   }
+   close IN;
+   $tsm_log_info = $tsmlog;
+   $tsm_log_header = 'tsmlog';
  
!   unless (strings_have_same_number_words($tsm_log_header, $tsm_log_info)) {
!     warn "$0: warning: TSM Log header does not match TSM Log info.\n";
!     $tsm_log_header = '';
!     $tsm_log_info   = '';
!     $need_header     = 1;
!   }
  
!   ## Get TSM Tape usage
!   $tsmpvt = 0;
!   open IN, "dsmadmc -id=view -password=view 'query libvol' | grep 'Private' | wc -l |";
!   while (<IN>) {
!     chomp;
!     @fld = split(/ +/,);
!     if (/\d/) {
!       $tsmpvt = $fld[1];
      }
+   }
+   close IN;
  
!   $tsmscr = 0;
!   open IN, "dsmadmc -id=view -password=view 'query libvol' | grep 'Scratch' | wc -l |";
!   while (<IN>) {
!     chomp;
!     @fld = split(/ +/,);
!     if (/\d/) {
!       $tsmscr = $fld[1];
      }
+   }
+   close IN;
  
!   $tsmvlt = 0;
!   open IN, "dsmadmc -id=view -password=view 'query drmedia' | grep 'Vault' | wc -l |";
!   while (<IN>) {
!     chomp;
!     @fld = split(/ +/,);
!     if (/\d/) {
!       $tsmvlt = $fld[1];
      }
+   }
  
!   $tsm_tape_header = join("\t", 'tsmpvt', 'tsmscr', 'tsmvlt');
!   $tsm_tape_info = join("\t", $tsmpvt, $tsmscr, $tsmvlt);
  
!   unless (strings_have_same_number_words($tsm_tape_header, $tsm_tape_info)) {
!     warn "$0: warning: TSM Tape header does not match TSM Tape info.\n";
!     $tsm_tape_header = '';
!     $tsm_tape_info   = '';
!     $need_header     = 1;
!   }
  
!   ## Get TSM Disk Storage Pool usage
!   $tsmphcy = 0;
!   $tsmphcn = 0;
!   open IN, "dsmadmc -id=view -password=view 'query stgpool' |";
!   while (<IN>) {
!     @fld = split(/ +/,);
!     if (/\d/) {
!       if ($fld[0] eq "PHCYDISKPO-") {
!         $tsmphcy = $fld[3];
!       } elsif ($fld[0] eq "PHCNDISKPO-") {
!         $tsmphcn = $fld[3];
!       }
      }
!   }
!   close IN;
  
!   $tsm_stg_header = join("\t", 'tsmphcy', 'tsmphcn');
!   $tsm_stg_info = join("\t", $tsmphcy, $tsmphcn);
  
!   unless (strings_have_same_number_words($tsm_stg_header, $tsm_stg_info)) {
!     warn "$0: warning: TSM Storage Pool header does not match ",
!          "TSM Storage Pool info.\n";
!     $tsm_stg_header = '';
!     $tsm_stg_info   = '';
!     $need_header     = 1;
!   }
  
!   ## Join header and info then verify column counts.
!   $out_header = join("\t",
!                      "timestamp", "locltime", $load_header, $up_header,
!                      $proc_header, $vmstat_header, $fs_header, $iostat_header,
!                      $packet_header, $tcp_header, $tsm_header, $swap_header,
!                      $mem_header, $tsm_rmt_header, $tsm_log_header,
!                      $tsm_tape_header, $tsm_stg_header);
!   $out_header =~ tr/ \t/\t/s;    # translate whitespace to single tabs
  
!   $out_info = join("\t",
!                    $timestamp, $locltime, $load_info, $up_info, $proc_info,
!                    $vmstat_info, $fs_info, $iostat_info, $packet_info,
!                    $tcp_info, $tsm_info, $swap_info, $mem_info, $tsm_rmt_info,
!                    $tsm_log_info, $tsm_tape_info, $tsm_stg_info);
!   $out_info =~ tr/ \t/\t/s;      # translate whitespace to single tabs
  
!   my @out_header = split ' ', $out_header;
!   my @out_info = split ' ', $out_info;
!   $header_cnt = @out_header;
!   $info_cnt = @out_info;
!   if ($header_cnt != $info_cnt) {
!     warn "ERROR: header columns do not equal data columns. Exiting.\n";
!     &exit_nicely;
!   } elsif ($header_cnt != $prev_header_cnt or $info_cnt != $prev_info_cnt) {
!     $need_header = 1;
!   }
!   $prev_header_cnt = $header_cnt;
!   $prev_info_cnt   = $info_cnt;
  
!   ## Write output
!   if ($need_header) {
!     print OUT $out_header, "\n";
!     $need_header = 0;
!   }
!   print OUT $out_info, "\n";
  
!   if ($iterations) {
!     sleep($INTERVAL - (time() - $timestamp));
!   }
! }
! close OUT;
  
! @args = ($COMPRESS, "-f", $stat_file);
! system(@args);
  
! exit 0;
  
+ # Subroutine to that tests if two strings have the same number of
+ # whitespace separated words.
+ sub strings_have_same_number_words {
+   my @words1 = split(' ', $_[0]);
+   my @words2 = split(' ', $_[1]);
+   scalar @words1 == scalar @words2;
  }
  
! # This subroutine is called by the signal handler.
  sub exit_nicely {
!   close OUT;
!   @args = ($COMPRESS, "-f", $stat_file);
!   system(@args);
!   exit 0;
  }


More information about the Orca-dev mailing list