rpacct.pl

#!/usr/local/bin/perl

##
# @file      rpacct.pl
# @author    Mitch Richling <http://www.mitchr.me/>
# @Copyright Copyright 1998 by Mitch Richling.  All rights reserved.
# @brief     How to parse System V accounting files@EOL
# @Keywords
# @Std       Perl5
#
#            This program is intended to read and parse a process
#            accounting file generated by a SVR4 system like Solaris.
#
#  The 'acct' struct contains:
#     char     ac_flag;     Accounting flag
#     char     ac_stat;     Exit status
#     uid32_t  ac_uid;      Accounting user ID
#     gid32_t  ac_gid;      Accounting group ID
#     dev32_t  ac_tty;      control typewriter
#     time32_t ac_btime;    Beginning time
#     comp_t   ac_utime;    acctng user time in clock ticks
#     comp_t   ac_stime;    acctng system time in clock ticks
#     comp_t   ac_etime;    acctng elapsed time in clock ticks
#     comp_t   ac_mem;      memory usage
#     comp_t   ac_io;       chars transferred
#     comp_t   ac_rw;       blocks read or written
#     char     ac_comm[8];  command name
#
# comp_t is a 16-bit fixed point number.  A 3-bit exponent in the
#        high order bits, and a 13-bit fraction in the low order bits.
# uid32_t, gid32_t, time32_t are all 32-bit integers.
# char is an 8-bit quantity.
#
#  Decoding the comp_t type:
#  This is a very compact expression for a floating point number. The
#  floating point number hidden with a member $foo of comp_t type may
#  be extracted with: ($foo & 8191) * (8 ** ($foo >> 13))
#
# A process accounting file is a file full of 'acct' structs.  Thus
# we can read the data in blocks that are the size of an acct struct
# and then break the blocks up into the parts we are interested in.
# From the sizes and types of the structure components we should be
# able to use a format string of ccLLLLSSSSSSA8 to unpack this data
# structure, but we have an alignment problem.  We have an extra two
# bytes stuck between the ac_stat member and the ac_uid member.
# It is ironic that such a carefully constructed data structure designed
# to conserve space has a hole in it. :)  The format string we must
# us is ccc2LLLLSSSSSSA8
#
# Meaning of the members:
#   ac_utime, ac_stime, ac_etime are all in clock ticks.  For SPARC
#     Solaris this is 100.
#   ac_btime is the time the process started.  It is expressed as
#     the standard UNIX time number stored in a 32-bit quantity.
#   ac_mem is a strange one.  Each tick, the OS updates this value
#     with (data_size + text_size) / (number_of_in-core_processes_using_text)
#     Thus ac_mem/(ac_stime+ac_utime) is a measure of the average
#     memory used.  This is in pages.  So you have to know the page
#     size of the host the process accounting file came from.
#   ac_flags is a flag quantity.  The bit at 01 means the thing forked
#     but did not exec.  The bit at 02 means that the program ran with
#     UID 0 privileges.

$TICKSPERSEC = 100.0;
$PAGESIZE = 8.0;

printf("%-10s %-10s %-10s %20s %20s %12s %12s %12s %10s %12s %15s %5s %5s\n",
       'Command', 'User', 'Group', 'Start_Time', 'End_Time', 'Run_Sec',
       'Usr_CPU', 'Sys_CPU', 'RAM', 'Blk_I/O', 'Char_I/O', 'Exit', 'Flags');
printf("%-10s %-10s %-10s %20s %20s %12s %12s %12s %10s %12s %15s %5s %5s\n",
       '', '', '', 'Local', 'Local', 'Sec',
       'Sec', 'Sec', 'Kbytes', 'Blocks', 'Bytes', 'Dec', '');
open(FP, "<$ARGV[0]") || die "ugh\n";
while($numRead = read(FP,$data,40)) {
  if($numRead = 40) {
    # Unpack the structure into variables.
    ($ac_flag, $ac_stat, $junk, $junk, $ac_uid, $ac_gid, $ac_tty,
     $ac_btime, $ac_utime, $ac_stime, $ac_etime, $ac_mem, $ac_io,
     $ac_rw, $ac_comm) = unpack("ccc2LLLLSSSSSSA8", $data);
    # Get uname
    $ac_uname = getpwuid($ac_uid);
    #Get group name
    $ac_grp = getgrgid($ac_gid);
    # Get start date
    ($st_sec, $st_min, $st_hour, $st_mday, $st_mon, $st_year, $st_wday,
     $st_yday, $st_isdst) = localtime($ac_btime);
    $st_date = sprintf("%4d-%02d-%02d_%02d:%02d:%02d", $st_year+1900,
                       $st_mon+1, $st_mday, $st_hour, $st_min, $st_sec);
    #Get real time
    $etime_real = ($ac_etime & 8191) * (8 ** ($ac_etime >> 13)) / $TICKSPERSEC;
    #get end date
    ($en_sec, $en_min, $en_hour, $en_mday, $en_mon, $en_year, $en_wday,
     $en_yday, $en_isdst) = localtime($ac_btime+$etime_real);
    $en_date = sprintf("%4d-%02d-%02d_%02d:%02d:%02d", $en_year+1900,
                       $en_mon+1, $en_mday, $en_hour, $en_min, $en_sec);
    #get block io
    $bio = ($ac_rw & 8191) * (8 ** ($ac_rw >> 13));
    #get chr io
    $cio = ($ac_io & 8191) * (8 ** ($ac_io >> 13));
    #Get User CPU time
    $ucpu_tim = ($ac_utime & 8191) * (8 ** ($ac_utime >> 13)) / $TICKSPERSEC;
    #Get System CPU time
    $scpu_tim = ($ac_stime & 8191) * (8 ** ($ac_stime >> 13)) / $TICKSPERSEC;
    #Get RAM
    $ram = ($ac_mem & 8191) * (8 ** ($ac_mem >> 13));
    #Compute mean RAM use
    if(($ac_stime+$ac_utime) > 0)
      { $ram = $ram / ($ac_stime+$ac_utime); }
    $ram = $ram * $PAGESIZE;
    $flags = ($ac_flag & 01?"F":"_") . ($ac_flag & 02?"R":"_");

    #Print the record data out.
    printf("%-10s %-10s %-10s %20s %20s %12.2f " .
           "%12.2f %12.2f %10d %12.2f %15.1f %5d %5s\n",
           $ac_comm, $ac_uname, $ac_grp, $st_date, $en_date, $etime_real,
           $ucpu_tim, $scpu_tim, $ram, $bio, $cio, int($ac_stat), $flags);
  }
}
close(FP);

Generated by GNU Enscript 1.6.5.2.