[Orca-dev] Orcallator.se-1.28b3 release

Blair Zajac blair at akamai.com
Tue Mar 27 15:57:04 PST 2001


Alan LeGrand found one problem with 1.28b2 where a nonexistent
member of a the p_netstat class was being used.

Attached is a fixed version and here's the patch:

11c11
< // Version 1.28b2: Feb  5, 2001       Recoded measure_disk() to access
the RAWDISK
---
> // Version 1.28b3: Mar 27, 2001       Recoded measure_disk() to access the RAWDISK
1183,1185c1183,1185
<     if (GLOBAL_net[i].up == 0) {
<       continue;
<     }
---
> //  if (GLOBAL_net[i].up == 0) {
> //    continue;
> //  }

Regards,
Blair
-------------- next part --------------
//
// Orcallator.se, a log generating performance monitor.
//
// This program logs many different system quantities to a log file
// for later processing.
//
// Author: Blair Zajac <bzajac at akamai.com>.
//
// Portions copied from percollator.se written by Adrian Cockroft.
//
// Version 1.28b3: Mar 27, 2001	Recoded measure_disk() to access the RAWDISK
//				interface to sys_kstat device information to
//				allow the activity on Sun's A1000 and Clariion
//				Raid controller drives to be seen.  Apparently
//				the pseudo drivers do not update the kstat
//				interface.  It is also inverts the fix
//				provided by version 1.23 to avoid over-counting
//				md devices.  By suppressing stats from slices
//				and metadevices and instead reporting on full
//				devices such as c0t0d0 or sd0.  Note: This may
//				have introduced an interaction with the
//				live_rules.se class monitoring of drive
//				performance.  Prevent floppy disks and tape
//				drives from RAWDISK.  Added wio% to measure
//				wait time since the idle calculation is wrong
//				without this.  Prevent filesystems mounted
//				under /snapshots from being seen.  Patch
//				contributed by Alan LeGrand
//				<alegrand at wallace.com>.
// Version 1.27: Mar 27, 2001	Print the portion of time running in idle mode
//				with some process waiting for block I/O as
//				wio% and otherwise completely idle time as
//				idle%.
// Version 1.26: Feb  5, 2001	Make sure to check the return from stat() on
//				the web server access log in case the file is
//				missing.  Use fstat() instead of stat() when a
//				file descriptor is available.
// Version 1.25: Mar 30, 2000	Fix a typo where nil was misspelled as nik.
// Version 1.24: Mar 25, 2000	When orcallator.se was running on a system
//				with an older version of SE the p_vmstat.scan
//				variable is an integer and the sprintf to
//				%8.3f fails, resulting in a perceived scan rate
//				of 0 pages per second.  Now always add 0.0 to
//				p_vmstat.scan to get a double.
// Version 1.23: Feb 25, 2000	When orcallator.se was running on a system
//				with DiskSuite, the same physical disk was
//				listed multiple times when it appeared in
//				several metadevices.  The solution to the
//				problem is not to build the c0t0d0 name but
//				use the long disk name provided by the
//				long_name string.  Patch contributed by Paul
//				Haldane <Paul.Haldane at newcastle.ac.uk>.
// Version 1.22: Jan 14, 2000	Include code to record NFS v2 and v3 server
//				statistics.  The new statistics are:
//				nfss_calls, the number of NFS calls to the NFS
//				server, nfss_bad, the number of bad NFS calls
//				per second, and v{2,3}{reads,writes}, which are
//				nfss_calls broken down into NFS version 2 and
//				NFS version 3 calls.  The sum of v{2,3}{reads,
//				writes} will be less than nfss_calls as the
//				other types of NFS calls, such as getattr and
//				lookup, are not included.  Contributed by Paul
//				Haldane <Paul.Haldane at newcastle.ac.uk>.  This
//				code is enabled by the standard -DWATCH_OS or
//				individually by -DWATCH_NFS_SERVER.  The
//				define -DWATCH_NFS has been superseded by
//				-DWATCH_NFS_CLIENT, but to keep backwards
//				compatibility, -DWATCH_NFS_CLIENT will be
//				defined if -DWATCH_NFS is defined.
// Version 1.21: Jan 12, 2000	Prevent core dumps on extremely long access
//				log lines.
// Version 1.20: Oct 20, 1999	Update my email address.
// Version 1.19: Oct 13, 1999	Prevent a division by zero in calculating the
//				mean_disk_busy if the number of disks on the
//				system is 0.
// Version 1.18: Oct 12, 1999	Rename disk_runp.c?t?d? to disk_runp_c?t?d?
//				to remove the .'s.
// Version 1.17: Oct  8, 1999	Do not record mount point statistics for
//				locally mounted /cdrom partitions.
// Version 1.16: Oct  7, 1999	To keep backwards compatibility, define
//				WATCH_WEB if WATCH_HTTPD is defined.
//				If the COMPRESSOR environmental variable
//				is defined, then when a new log file is opened
//				for a new day, the just closed log file is
//				compressed using the COMPRESSOR command in the
//				following manner:
//				system(sprintf("%s %s &", COMPRESSOR, log_file)
//				COMPRESSOR should be set to something like
//				"gzip -9", or "compress", or "bzip2 -9".
// Version 1.15: Oct  5, 1999	kvm$mpid is a int not a long.  This caused
//				problems on Solaris 7 hosts running a 64
//				bit kernel.
// Version 1.14: Oct  1, 1999	Rename disk.c?t?d? column names to
//				disk_runp.c?t?d? to better reflect the data
//				being recorded and to allow for more per disk
//				information later.
// Version 1.13: Sep 24, 1999	Fix a bug in the disk_mean calculation where
//				it was being divided by the wrong disk_count.
//				Now it should be much larger and in scale with
//				disk_peak.  When WATCH_DISK is defined, now
//				print each disk's run percent.  Add a new
//				define WATCH_MOUNTS, which reports each local
//				mount point's disk space and inode capacity,
//				usage, available for non-root users and
//				percent used.  This comes from Duncan Lawie
//				tyger at hoopoes.com. Add some smarts so that if
//				the number of interfaces, physical disks, or
//				mounted partitions changes, then a new header
//				is printed.  This will prevent column name and
//				data mixups when the system configuration
//				changes.
// Version 1.12: Sep 14, 1999	Add the page scan rate as scanrate in
//				measure_cpu.
// Version 1.11: Aug 13, 1999	Add the number of CPUs as ncpus.  Move
//				measure_disk and measure_ram sooner in the
//				list of subsystems to handle.  Increase the
//				number of characters for each network
//				interface from four to five.  Add new disk
//				reads, writes, Kbytes read, and Kbytes
//				written per second.  Add number of bytes
//				of free memory in bytes as freememK.
// Version 1.10: Jul 28, 1999	Measure the process spawn rate if WATCH_CPU
//				is defined and the user is root.
// Version 1.9:  Jun  2, 1999	If WATCH_YAHOO is defined, then process the
//				access log as a Yahoo! style access log.
//				Restructure the code to handle different
//				web server access log formats.
// Version 1.8:  Jun  1, 1999	If the environmental variable WEB_SERVER is
//				defined, use its value of the as the name
//				of the process to count for the number of
//				web servers on the system.  If WEB_SERVER
//				is not defined, then count number of httpd's.
// Version 1.7:  Mar 25, 1999	Simplify and speed up count_proc by 20%.
// Version 1.6:  Feb 23, 1999	Print pvm.user_time and system_time correctly.
// Version 1.5:  Feb 23, 1999	Always write header to a new file.
// Version 1.4:  Feb 19, 1999	Handle missing HTTP/1.x in access log.
// Version 1.3:  Feb 18, 1999	On busy machines httpops5 will be enlarged.
// Version 1.2:  Feb 18, 1999	Output data on integral multiples of interval.
// Version 1.1:	 Feb 18, 1999	Integrate Squid log processing from SE 3.1.
// Version 1.0:	 Sep  9, 1998	Initial version.
//

// The default sampling interval in seconds.
#define SAMPLE_INTERVAL		300
// The maximum number of colums of data.
#define MAX_COLUMNS		512

// Define the different parts of the system you want to examine.
#ifdef WATCH_OS
#define WATCH_CPU		1
#define WATCH_MUTEX		1
#define WATCH_NET		1
#define WATCH_TCP		1
#define WATCH_NFS_CLIENT	1
#define WATCH_NFS_SERVER	1
#define WATCH_MOUNTS		1
#define USE_RAWDISK		1
#define WATCH_DISK		1
#define WATCH_DNLC		1
#define WATCH_INODE		1
#define WATCH_RAM		1
#define WATCH_PAGES		1
#endif

// Keep backwards compatibility with WATCH_HTTPD.
#ifdef WATCH_HTTPD
#define WATCH_WEB		1
#endif

// Keep backwards compatibility with WATCH_NFS.
#ifdef WATCH_NFS
#ifndef WATCH_NFS_CLIENT
#define WATCH_NFS_CLIENT	1
#endif
#endif

#include <stdio.se>
#include <stdlib.se>
#include <unistd.se>
#include <string.se>
#include <time.se>
#include <kstat.se>
#include <utsname.se>

#include <p_iostat_class.se>
#include <p_netstat_class.se>
#include <p_vmstat_class.se>
#include <pure_rules.se>
#include <live_rules.se>
#include <mib.se>
#include <tcp_class.se>
#include <tcp_rules.se>

#ifdef WATCH_MOUNTS
#include <mnt_class.se>
#include <statvfs.se>
#endif

#if WATCH_CPU || WATCH_WEB
#include <proc.se>

#ifdef WATCH_CPU
// This is the maximum pid on Solaris hosts.
#define DEFAULT_MAXPID 30000
#include <fcntl.se>
#endif

#ifdef WATCH_WEB
#include <stat.se>

// Define this macro which returns the size index for a file of a
// particular size.  This saves the overhead of a function call.
#define WWW_SIZE_INDEX(size, size_index)			\
  if (size < 1024) {						\
    size_index=0;		/* Under 1KB   */		\
  } else {							\
    if (size < 10240) {						\
      size_index=1;		/* Under 10K   */		\
    } else {							\
      if (size < 102400) {					\
        size_index=2;		/* Under 100KB */		\
      } else {							\
        if (size < 1048576) {					\
          size_index=3;		/* Under 1MB   */		\
        } else {						\
          size_index=4;		/* Over 1MB    */		\
        }							\
      }								\
    }								\
  }								\
  dwnld_size[size_index]++;

// Handle the reply code from the server.
#define WWW_REPLY_CODE(word)					\
  if (word == "304") {						\
    httpop_condgets++;						\
  }								\
  else {							\
    first_byte = word;						\
    if (first_byte[0] == '4' || first_byte[0] == '5') {		\
      httpop_errors++;						\
    }								\
  }								\

// Handle the method of the object served.  This define only works
// with non-proxy servers.
#define WWW_METHOD1(word)					\
  switch (word) {						\
    case "get":							\
    case "GET":							\
      httpop_gets++;						\
      break;							\
    case "post":						\
    case "POST":						\
      httpop_posts++;						\
      break;							\
    case "head":						\
    case "HEAD":						\
      ishead = 1;						\
      httpop_condgets++;					\
      break;

#ifdef WATCH_SQUID
#define WWW_METHOD2						\
    case "icp_query":						\
    case "ICP_QUERY":						\
      squid_icp_queries++;					\
      break;
#else
#define WWW_METHOD2
#endif

#define WWW_METHOD_END						\
    default:							\
      break; 							\
  }
#define WWW_METHOD(word) WWW_METHOD1(word) WWW_METHOD2 WWW_METHOD_END
#endif
#endif

// Put all rules here so they can be accessed by the handle functions.
lr_cpu_t	lr_cpu$cpu;
lr_cpu_t	tmp_lrcpu;
lr_mutex_t	lr_mutex$m;
lr_mutex_t	tmp_mutex;
lr_net_t	lr_net$nr;
lr_net_t	tmp_nr;
lr_tcp_t	lr_tcp$tcp;
lr_tcp_t	tmp_lrtcp;
#ifdef WATCH_TCP
tcp		tcp$tcp;
tcp		tmp_tcp;
#endif
lr_rpcclient_t	lr_rpcclient$r;
lr_rpcclient_t	tmp_lrpcc;
lr_disk_t	lr_disk$dr;
lr_disk_t	tmp_dr;
lr_dnlc_t	lr_dnlc$dnlc;
lr_dnlc_t	tmp_lrdnlc;
lr_inode_t	lr_inode$inode;
lr_inode_t	tmp_lrinode;
lr_ram_t	lr_ram$ram;
lr_ram_t	tmp_lrram;
#ifdef WATCH_PAGES
ks_system_pages kstat$pages;
ks_system_pages tmp_kstat_pages;
#endif
lr_swapspace_t	lr_swapspace$s;
lr_swapspace_t	tmp_lrswap;
lr_kmem_t	lr_kmem$kmem;
lr_kmem_t	tmp_lrkmem;
ks_system_misc	kstat$misc;
ks_system_misc	tmp_kstat_misc;

// Put application globals here.
string		nodename;			// Name of this machine.
string		program_name;			// Name of this program.
int		hz;				// Clock tick rate.
int		page_size;			// Page size in bytes.
long		boot_time;			// Boot time of the system.
long		interval = SAMPLE_INTERVAL;	// Sampling interval.

#ifdef WATCH_CPU
int		can_read_kernel = 0;		// If the kernel can be read.
int		kvm$mpid;			// The last created PID.

// These variables store the mpid before and after the standard interval.
int		mpid_previous;
int		mpid_current;
ulonglong	mpid_then;
ulonglong	mpid_now;

// These variables store the mpid before and after 5 second intervals.
int		mpid5_previous;
int		mpid5_current;
ulonglong	mpid5_then;
ulonglong	mpid5_now;
double		mpid5_rate;
#endif

#ifdef WATCH_MOUNTS
mnttab_t	mnt$mnt;
mnttab_t	tmp_mnt;
#endif

#ifdef WATCH_NFS_SERVER
ks_nfs_server	kstat$nfs;
ks_nfs_server	tmp_nfs;
ks_rfs_proc_v2	kstat$rfsproccnt_v2;
ks_rfs_proc_v2	tmp_rfsproccnt_v2;
ks_rfs_proc_v3	kstat$rfsproccnt_v3;
ks_rfs_proc_v3	tmp_rfsproccnt_v3;
#endif

// Variables for handling the httpd access log.
#ifdef WATCH_WEB
string		www_search_url       = getenv("SEARCHURL");
string		www_server_proc_name = getenv("WEB_SERVER");
string		www_log_filename     = getenv("WEB_LOG");
string		www_gateway          = getenv("GATEWAY");
ulong		www_fd;
uint		www_gatelen;
stat_t		www_stat[1];
ulong		www_ino;
long		www_size;

double		www_interval;		// Hi-res interval time.
ulonglong	www_then;
ulonglong	www_now;

double		www5_interval;		// Actual hi-res 5 second interval.
ulonglong	www5_then;
ulonglong	www5_now;

double		httpops;
double		httpops5;
double		gateops;
double		dtmp;

long		httpop_gets;
long		httpop_condgets; // HEAD or code = 304 conditional get no data.
long		httpop_posts;
long		httpop_cgi_bins;
long		httpop_searches;
long		httpop_errors;
long		dwnld_size[5]; // [0] < 1K, [1] < 10K, [2] < 100K, [3] < 1M, [4] >= 1M
long		dwnld_totalz;  // Total size counted from log.

#if WATCH_PROXY || WATCH_SQUID || WATCH_YAHOO
// If we're watching a Yahoo log, then take the transfer time to be the
// processing time.
double		www_dwnld_time_sum;	   // Transfer time.
double		www_dwnld_time_by_size[5]; // Mean transfer time by size bin.
#endif
#if WATCH_PROXY || WATCH_SQUID
long		prxy_squid_indirect;   // # hits that go via PROXY,SOCKS,parent
long		prxy_squid_cache_hits; // # hits returned from cache.
#endif

#ifdef WATCH_PROXY
long		prxy_cache_writes; // Number of writes and updates to cache.
long		prxy_uncacheable;  // Number of explicitly uncacheable httpops.
				   // Any extra is errors or incomplete ops.
#endif

#ifdef WATCH_SQUID
long		squid_cache_misses;
long		squid_icp_requests;
long		squid_icp_queries;
long		squid_client_http;
#endif

#endif

// RAWDISK BEGIN
#ifdef USE_RAWDISK
#include <sys_kstat.se>
// This extention accesses the sys_kstat.se interface to the kstat IO
// queues to extract info on drives not available in the kstat.se
// kstat$disk interface.  Global data shared between function calls.
struct RawDisk {
  // Exposed interface (matches kstat)
  char long_name[16];
  char short_name[8];
  double reads;
  double kreads;
  double writes;
  double kwrites;
  double avg_wait;
  double avg_serv;
  double service;
  double wait_percent;
  double run_percent;

  // Hidden internal registers to track sys_kstats counters.
  int       _number;		// kstat disk #
  ulonglong _nread;		// Number of bytes read
  ulonglong _nwritten;		// Number of bytes written
  uint      _reads;		// Number of read operations
  uint      _writes;		// Number of write operations
  longlong  _wtime;		// Cumulative wait (pre-service) time
  longlong  _wlentime;		// Cumulative wait length*time product
  longlong  _wlastupdate;	// Last time wait queue changed
  longlong  _rtime;		// Cumulative run (service) time
  longlong  _rlentime;		// Cumulative run length*time product
  longlong  _rlastupdate;	// Last time run queue changed
  uint      _wcnt;		// Count of elements in wait state
  uint      _rcnt;		// Count of elements in run state
};

// Define global for tracking raw disk data.
#define MAX_RAWDISKS	512
RawDisk		RAW_disk[MAX_RAWDISKS];
int		RAW_disk_map=0;
int		RAW_disk_count=0;
double		RAW_disk_lastupdate;

// Function to scan kstat and map short device names to long device names.
raw_disk_map() {
  int i;
  int j=0;
  char long_name[16];
  char short_name[16];

  // SE for 2.6 & 2.7 appears to have a bug where MAX_DISK is too
  // large when an A1000 raid controller is present.  It's not really
  // SE as iostat had to be patched for the same bug.  The bug appears
  // to be tied to a failure to update the kstat interface diskinfo
  // uses to map short names to long names.  Since raw_disk_update has
  // already identified how many physical devices are present and they
  // are listed first we limit our self to mapping the first
  // RAW_disk_count device name.
  for (i=0; i<RAW_disk_count; i++) {
    strcpy(long_name,GLOBAL_disk_info[i].long_name);
    if (long_name[0] == 'c' && strchr(long_name, 's') == nil) {
      strcpy(short_name,GLOBAL_disk_info[i].short_name);
      for (j=0; j<RAW_disk_count;j++) {
        if (strcmp(RAW_disk[j].short_name, short_name) == 0) {
          strcpy(RAW_disk[j].long_name, long_name);
          break;
        }
      }
    }
  }
  RAW_disk_map = 0;
}

raw_disk_update() {
  int         rdisk;
  ulong       ul;
  kstat_ctl_t kc[1];
  kstat_t     kp[1];
  kstat_t     nkp[1];
  kstat_io_t  kio;
  ulonglong   _nread;
  ulonglong   _nwritten;
  uint        _reads;
  uint        _writes;
  longlong    _wtime;
  longlong    _wlentime;
  longlong    _wlastupdate;
  longlong    _rtime;
  longlong    _rlentime;
  longlong    _rlastupdate;
  longlong    _wcnt;
  longlong    _rcnt;

  double      read_writes;
  double      big_etime;
  double      elapsed_etime;
  double      hz_etime;
  double      nanosecond = NANOSEC;
  double      update;
  double      delta;
  timeval_t   time_update[1];
  ulong       time_void;
  char        short_name[8];

  gettimeofday(time_update,time_void);
  update = time_update[0].tv_sec + (time_update[0].tv_usec / 1000000.0);
  delta  = update - RAW_disk_lastupdate;
  RAW_disk_lastupdate = update;

  kc[0] = kstat_open();
  // Read them.
  if (kstat_read(kc,kp,0) == -1) {
    perror("raw_disk_update:kstat_read");
    exit(1);
  }

  // Traverse the chain looking for IO events.
  for (ul=kc[0].kc_chain; ul !=0; ul=nkp[0].ks_next) {
    struct_fill(nkp[0],ul);
    if (nkp[0].ks_type == KSTAT_TYPE_IO) {
      strcpy(short_name, nkp[0].ks_name);
      if (short_name[0] != 'm' &&
          short_name[0] != 'n' &&
          strchr(short_name,',') == nil) {
        // Try to locate device.
        for (rdisk=0; rdisk<RAW_disk_count;rdisk++) {
          if (strcmp(RAW_disk[rdisk].short_name, short_name) == 0) {
            break;
          }
        }

        // It must be new. Add it!
        if (rdisk == RAW_disk_count) {
          // Must be a tape drive or something else.  Schedule device
          // name map cycle.
          RAW_disk_map = 1;
          strcpy(RAW_disk[rdisk].long_name, short_name);
          strcpy(RAW_disk[rdisk].short_name, short_name);
          RAW_disk[rdisk]._reads       = 0;
          RAW_disk[rdisk]._nread       = 0;
          RAW_disk[rdisk]._rlentime    = 0;
          RAW_disk[rdisk]._rlastupdate = boot_time;
          RAW_disk[rdisk]._rcnt        = 0;
          RAW_disk[rdisk]._writes      = 0;
          RAW_disk[rdisk]._nwritten    = 0;
          RAW_disk[rdisk]._wlentime    = 0;
          RAW_disk[rdisk]._wlastupdate = boot_time;
          RAW_disk[rdisk]._wcnt        = 0;
          RAW_disk_count++;
        }

        // Update the device registers.
        if (kstat_read(kc,nkp,0) == -1) {
          perror("raw_disk_update:kstat_read error");
          exit(1);
        } else {
          // Read sys_kstat device IO queue to find out about recent
          // activity.  We validate data that is returned.  Solaris
          // 2.6 has occasional glitches when updating certain disks
          // (c0t0d0) so we cover up the glitches by using data from
          // the previous cycle.  Eventually, we will get a good
          // update.  Fixing the data is not necessarily the best
          // choice.  Currently only kio.nread glitches.  Correcting
          // the error forces the IOs to get attributed to the next IO
          // cycle.
          struct_fill(kio,nkp[0].ks_data);
          _nread  =  kio.nread;
          if (RAW_disk[rdisk]._nread > _nread) {
            _nread = RAW_disk[rdisk]._nread;
          }
          _reads = kio.reads;
	  if (RAW_disk[rdisk]._reads > _reads) {
            _reads = RAW_disk[rdisk]._reads;
          }
          _rlentime    = kio.rlentime;
          _rtime       = kio.rtime;
          _rlastupdate = kio.wlastupdate;
          _rcnt        = kio.rcnt;
          _nwritten    = kio.nwritten;
          if (RAW_disk[rdisk]._nwritten > _nwritten) {
            _nwritten = RAW_disk[rdisk]._nwritten;
          }
          _writes = kio.writes;
          if (RAW_disk[rdisk]._writes > _writes) {
            _writes = RAW_disk[rdisk]._nwritten;
          }
          _wlentime    = kio.wlentime;
          _wtime       = kio.wtime;
          _wlastupdate = kio.wlastupdate;
          _wcnt        = kio.wcnt;

          elapsed_etime = (_wlastupdate - RAW_disk[rdisk]._wlastupdate);
          if (elapsed_etime > 0)  {
            hz_etime = elapsed_etime / nanosecond;
            big_etime = 1024.0 * hz_etime;
          } else {
            elapsed_etime = nanosecond;
            hz_etime = 1.0;
            big_etime = 1024.0;
          }
          RAW_disk[rdisk].reads  =(_reads-RAW_disk[rdisk]._reads)  /hz_etime;
          RAW_disk[rdisk].kreads =(_nread-RAW_disk[rdisk]._nread)  /big_etime;
          RAW_disk[rdisk].writes =(_writes-RAW_disk[rdisk]._writes)/hz_etime;
          RAW_disk[rdisk].kwrites=(_nwritten-RAW_disk[rdisk]._nwritten) / big_etime;

          read_writes = elapsed_etime * (RAW_disk[rdisk].reads + RAW_disk[rdisk].writes) / 1024.0;
          if (read_writes > 0) {
            RAW_disk[rdisk].avg_wait = (_wlentime - RAW_disk[rdisk]._wlentime) / read_writes;
            RAW_disk[rdisk].avg_serv = (_rlentime - RAW_disk[rdisk]._rlentime) / read_writes;
            RAW_disk[rdisk].service  = RAW_disk[rdisk].avg_wait + RAW_disk[rdisk].avg_serv;
          } else {
            RAW_disk[rdisk].avg_wait = 0.0;
            RAW_disk[rdisk].avg_serv = 0.0;
            RAW_disk[rdisk].service  = 0.0;
          }

          // Update the counters.
          RAW_disk[rdisk].run_percent  = 100.0 * (_rtime  - RAW_disk[rdisk]._rtime) / elapsed_etime;
          RAW_disk[rdisk].wait_percent = 100.0 * (_wtime - RAW_disk[rdisk]._wtime) / elapsed_etime;
          RAW_disk[rdisk]._writes      = _writes;
          RAW_disk[rdisk]._nwritten    = _nwritten;
          RAW_disk[rdisk]._wlastupdate = _wlastupdate;
          RAW_disk[rdisk]._wlentime    = _wlentime;
          RAW_disk[rdisk]._wtime       = _wtime;
          RAW_disk[rdisk]._wcnt        = _wcnt;
          RAW_disk[rdisk]._reads       = _reads;
          RAW_disk[rdisk]._nread       = _nread;
          RAW_disk[rdisk]._rlastupdate = _rlastupdate;
          RAW_disk[rdisk]._rlentime    = _rlentime;
          RAW_disk[rdisk]._rtime       = _rtime;
          RAW_disk[rdisk]._rcnt        = _rcnt;
        }
      }
    }
  }
  kstat_close(kc);

  // Map long device names for any drives that we just discovered.
  if (RAW_disk_map == 1) {
    raw_disk_map();
  }
}
#endif
// RAWDISK_END

// Variables for handling output.
string		compress = getenv("COMPRESSOR"); // How to compress logs.
ulong		ofile;				// File pointer to the logging file.
string		col_comment[MAX_COLUMNS];	// Comments for each column.
string		col_data[MAX_COLUMNS];		// Data for each column.
int		current_column = 0;		// The current column.
int		print_header = 1;		// Flag to flush header.

// Send the stored columns of information to the output.
print_columns(string data[])
{
  int i;
  for (i=0; i<current_column; i++) {
    fprintf(ofile, "%s", data[i]);
    if (i != current_column-1) {
      fputc(' ', ofile);
    }
  }
  fputc('\n', ofile);
  fflush(ofile);
}

// Add one column of comments and data to the buffers.
put_output(string comment, string data)
{
  if (current_column >= MAX_COLUMNS) {
    fprintf(stderr, "%s: too many columns (%d).  Increase MAX_COLUMNS.\n",
    	    program_name, current_column);
    exit(1);
  }

  col_comment[current_column] = comment;
  col_data[current_column]    = data;
  ++current_column;
}

flush_output() {
  if (print_header != 0) {
    print_columns(col_comment);
    print_header = 0;
  }
  print_columns(col_data);
  current_column = 0;
}

// Sets ofile to the output file pointer.  Creates or appends to the
// log file if OUTDIR is set, otherwise sets the file pointer to STDOUT.
// It start a new log file each day.  It compresses the previous days
// log file if the environmental variable COMPRESSOR is set.
checkoutput(tm_t now) {
  string outdir = getenv("OUTDIR");
  string outname;
  tm_t   then;
  char   tm_buf[32];

  if (outdir == nil) {
    // No output dir so use stdout.
    if (ofile == 0) {
      //  First time, so print header and set ofile.
      ofile = stdout;
      print_header = 1;
    }
    return;
  }
  // Maintain daily output logfiles in OUTDIR.
  if (now.tm_yday != then.tm_yday) {
    // First time or day has changed, start new logfile.
    if (ofile != 0) {
      // Close and optionally compress the existing output file.
      fclose(ofile);
      if (compress != nil) {
        system(sprintf(compress, outname));
      }
    }
    strftime(tm_buf, sizeof(tm_buf), "%Y-%m-%d", now);
    outname = sprintf("%s/percol-%s", outdir, tm_buf);
    // Open for append either way.
    ofile = fopen(outname, "a");
    if (ofile == 0) {
      perror("can't open output logfile");
      exit(1);
    }
    // Always write header.
    print_header = 1;
    then = now;
  }
}

int main(int argc, string argv[])
{
  utsname_t u[1];
  long      now;
  long      sleep_till;	// Time to sleep to.
  tm_t      tm_now;

  // Get the nodename of the machine.
  uname(u);
  nodename = u[0].nodename;

  program_name = argv[0];

  // Handle the command line arguments.
  switch (argc) {
    case 1:
      break;
    case 2:
      interval = atoi(argv[1]);
      break;
    default:
      fprintf(stderr, "usage: se [-Defines] %s [interval]\n", program_name);
      fprintf(stderr, "The default interval is %d seconds.\n", SAMPLE_INTERVAL);
      fprintf(stderr, "%s uses the following environmental variables:\n", program_name);
      fprintf(stderr, "   OUTDIR       directory to write log files, output to stdout if not set\n");
      fprintf(stderr, "   WEB_SERVER   string to search for number of web servers, i.e. httpd\n");
      fprintf(stderr, "   WEB_LOG      location of web server access log, i.e. /httpd/logs/access\n");
      fprintf(stderr, "   GATEWAY      special address to monitor, i.e. some.where.com\n");
      fprintf(stderr, "   SEARCHURL    match for search scripts, default is search.cgi\n");
      fprintf(stderr, "   COMPRESSOR   compress previous days logs with this command, i.e. \"gzip -9\"\n");
      fprintf(stderr, "Add these defines to enable monitoring of specific subsystems:\n");
      fprintf(stderr, "   -DWATCH_WEB        watch web server access logs\n");
      fprintf(stderr, "   -DWATCH_PROXY      use WEB_LOG as a NCSA style proxy log\n");
      fprintf(stderr, "   -DWATCH_SQUID      use WEB_LOG as a Squid log\n");
      fprintf(stderr, "   -DWATCH_OS         includes all of the below:\n");
      fprintf(stderr, "   -DWATCH_CPU        watch the cpu load, run queue, etc\n");
      fprintf(stderr, "   -DWATCH_MUTEX      watch the number of mutex spins\n");
      fprintf(stderr, "   -DWATCH_NET        watch all Ethernet interfaces\n");
      fprintf(stderr, "   -DWATCH_TCP        watch all the TCP/IP stack\n");
      fprintf(stderr, "   -DWATCH_NFS_CLIENT watch NFS client requests\n");
      fprintf(stderr, "   -DWATCH_NFS_SERVER watch NFS server requests\n");
      fprintf(stderr, "   -DWATCH_MOUNTS     watch usage of mount points\n");
      fprintf(stderr, "   -DWATCH_DISK       watch disk read/write usage\n");
      fprintf(stderr, "   -DWATCH_DNLC       watch the directory name lookup cache\n");
      fprintf(stderr, "   -DWATCH_INODE      watch the inode cache\n");
      fprintf(stderr, "   -DWATCH_RAM        watch memory usage\n");
      fprintf(stderr, "   -DWATCH_PAGES      watch where pages are allocated\n");
      exit(1);
      break;
  }

  // Initialize the various structures.
  initialize();

  // Run forever.  If WATCH_WEB is defined, then have measure_web()
  // do the sleeping while it is watching the access log file until the
  // next update time for the whole operating system.  Also, collect the
  // data from the access log file before printing any output.
  for (;;) {
    // Calculate the next time to sleep to that is an integer multiple of
    // the interval time.  Make sure that at least half of the interval
    // passes before waking up.
    now        = time(0);
    sleep_till = (now/interval)*interval;
    while (sleep_till < now + interval*0.5) {
      sleep_till += interval;
    }

#ifdef WATCH_WEB
    measure_web(sleep_till);
#else
    sleep_till_and_count_new_proceses(sleep_till);
#endif

    // Get the current time.
    now    = time(0);
    tm_now = localtime(&now);

    measure_os(now, tm_now);

#ifdef WATCH_WEB
    put_httpd();
#endif

    // Get a file descriptor to write to.  Maintains daily output files.
    checkoutput(tm_now);

    // Print the output.
    flush_output();
  }
  return 0;
}

initialize()
{
#ifdef WATCH_CPU
  int i;
#endif

  // Get the command to compress the log files.
  if (compress == nil || compress == "") {
    compress = nil;
  }
  else {
    compress = sprintf("%s %%s &", compress);
  }

#ifdef WATCH_CPU
  // Initialize the process spawning rate measurement variables.
  // Determine if the kernel can be read to measure the last pid.
  i = open("/dev/kmem", O_RDONLY);
  if (i != -1) {
    close(i);
    can_read_kernel = 1;
    mpid_previous   = kvm$mpid;
    mpid_then       = gethrtime();
    mpid_current    = mpid_previous;

    mpid5_then      = mpid_then;
    mpid5_previous  = mpid_previous;
    mpid5_current   = mpid_previous;
    mpid5_rate      = 0;
  }
#endif

#ifdef WATCH_WEB
  // Initialize those variables that were not set with environmental
  // variables.
  if (www_search_url == nil || www_search_url == "") {
    www_search_url = "search.cgi";
  }

  if (www_server_proc_name == nil || www_server_proc_name == "") {
    www_server_proc_name = "httpd";
  }

  if (www_gateway == nil || www_gateway == "" ) {
    www_gateway = "NoGatway";
    www_gatelen = 0;
  }
  else {
    www_gatelen = strlen(www_gateway);
  }

  // Initialize the web server watching variables.  Move the file pointer
  // to the end of the web access log and note the current time.
  if (www_log_filename != nil) {
    www_fd   = fopen(www_log_filename, "r");
    www_ino  = 0;
    www_size = 0;
    if (www_fd != 0) {
      if (fstat(www_fd, www_stat) == 0) {
        www_ino  = www_stat[0].st_ino;
        www_size = www_stat[0].st_size;
      }
      // Move to the end of the file.
      fseek(www_fd, 0, 2);
    }
  }

  www_then  = gethrtime();
  www5_then = www_then;
#endif

  // Sleep to give the disks a chance to update.
  sleep(DISK_UPDATE_RATE);

  // Get the clock tick rate.
  hz = sysconf(_SC_CLK_TCK);

  // Get the page size.
  page_size = sysconf(_SC_PAGESIZE);

  // Calculate the system boot time.
  boot_time = time(0) - (kstat$misc.clk_intr / hz);

  // Perform the first measurement of the system.
  _measure_os();
}

// Measure the system statistics all at once.
_measure_os()
{
  tmp_lrcpu         = lr_cpu$cpu;
  tmp_mutex         = lr_mutex$m;
  tmp_nr            = lr_net$nr;
  tmp_lrtcp         = lr_tcp$tcp;
#ifdef WATCH_TCP
  tmp_tcp           = tcp$tcp;
#endif
  tmp_lrpcc         = lr_rpcclient$r;
  tmp_dr            = lr_disk$dr;
  tmp_lrdnlc        = lr_dnlc$dnlc;
  tmp_lrinode       = lr_inode$inode;
  tmp_lrram         = lr_ram$ram;
#ifdef WATCH_PAGES
  tmp_kstat_pages   = kstat$pages;
#endif
  tmp_lrswap        = lr_swapspace$s;
  tmp_lrkmem        = lr_kmem$kmem;
  tmp_kstat_misc    = kstat$misc;
#ifdef WATCH_NFS_SERVER
  tmp_nfs           = kstat$nfs;
  tmp_rfsproccnt_v2 = kstat$rfsproccnt_v2;
  tmp_rfsproccnt_v3 = kstat$rfsproccnt_v3;
#endif

#ifdef USE_RAWDISK
   raw_disk_update();
#endif
}

measure_os(long now, tm_t tm_now)
{
  // Measure the system now.
  _measure_os();

  // Take care of miscellaneous measurements.
  measure_misc(now, tm_now);

  // Take care of cpu.
#ifdef WATCH_CPU
  measure_cpu();
#endif

  // Take care of mutexes.
#ifdef WATCH_MUTEX
  measure_mutex();
#endif

  // Take care of mount pointes.
#ifdef WATCH_MOUNTS
  measure_mounts();
#endif

  // Take care of the disks.
#ifdef WATCH_DISK
  measure_disk();
#endif

  // Take care of ram.
#ifdef WATCH_RAM
  measure_ram();
#endif

  // Take care of the network.
#ifdef WATCH_NET
  measure_net();
#endif

  // Take care of TCP/IP.
#ifdef WATCH_TCP
  measure_tcp();
#endif

  // Take care of NFS client statistics.
#ifdef WATCH_NFS_CLIENT
  measure_nfs_client();
#endif

  // Take care of NFS server statistics.
#ifdef WATCH_NFS_SERVER
  measure_nfs_server();
#endif

  // Take care of DNLC.
#ifdef WATCH_DNLC
  measure_dnlc();
#endif

  // Take care of the inode cache.
#ifdef WATCH_INODE
  measure_inode();
#endif

  // Take care of page allocations.
#ifdef WATCH_PAGES
  measure_pages();
#endif
}

/*
 * State as a character
 */
char state_char(int state) {
  switch(state) {
    case ST_WHITE: return 'w'; /* OK states are lower case. */
    case ST_BLUE:  return 'b';
    case ST_GREEN: return 'g';
    case ST_AMBER: return 'A'; /* Bad states are upper case to stand out. */
    case ST_RED:   return 'R';
    case ST_BLACK: return 'B';
    default:       return 'I'; /* Invalid state. */
  }
}

measure_misc(long now, tm_t tm_now)
{
  long   uptime;
  char   states[12];
  char   tm_buf[16];

  uptime = now - boot_time;
  states = "wwwwwwwwwww";
  strftime(tm_buf, sizeof(tm_buf), "%T", tm_now);

  states[0] = state_char(lr_disk$dr.state);
  states[1] = state_char(lr_net$nr.state);
  states[2] = state_char(lr_rpcclient$r.state);
  states[3] = state_char(lr_swapspace$s.state);
  states[4] = state_char(lr_ram$ram.state);
  states[5] = state_char(lr_kmem$kmem.state);
  states[6] = state_char(lr_cpu$cpu.state);
  states[7] = state_char(lr_mutex$m.state);
  states[8] = state_char(lr_dnlc$dnlc.state);
  states[9] = state_char(lr_inode$inode.state);
  states[10]= state_char(lr_tcp$tcp.state);

  put_output(" timestamp",  sprintf("%10d", now));
  put_output("locltime",    tm_buf);
  put_output("DNnsrkcmdit", states);
  put_output("  uptime",    sprintf("%8d", uptime));
}

sleep_till_and_count_new_proceses(long sleep_till)
{
  long   now;
#ifdef WATCH_CPU
  long   sleep_till1;
  int    mpid5_diff;
  double mpid5_interval;
  double rate;
#endif

  now = time(0);
  while (now < sleep_till) {
#ifdef WATCH_CPU
    if (can_read_kernel != 0) {
      // Sleep at least 5 seconds to make a measurement.
      sleep_till1 = now + 5;
      while (now < sleep_till1) {
        sleep(sleep_till1 - now);
        now = time(0);
      }

      // Measure the 5 second process creation rate.
      mpid5_current  = kvm$mpid;
      mpid5_now      = gethrtime();
      mpid5_interval = (mpid5_now - mpid5_then) * 0.000000001;
      mpid5_then     = mpid5_now;
      if (mpid5_current >= mpid5_previous) {
        mpid5_diff = mpid5_current - mpid5_previous;
      }
      else {
        mpid5_diff = mpid5_current + DEFAULT_MAXPID - mpid5_previous;
      }
      rate = mpid5_diff/mpid5_interval;
      if (rate > mpid5_rate) {
        mpid5_rate = rate;
      }
      mpid5_previous = mpid5_current;

      // Now take these results to measure the long interval rate.
      // Because the mpid may flip over DEFAULT_MAXPID more than once
      // in the long interval time span, use the difference between
      // the previous and current mpid over a 5 second interval to
      // calculate the long interval difference.
      mpid_current += mpid5_diff;
      mpid_now      = mpid5_now;
    }
    else {
      sleep(sleep_till - now);
    }
#else
    sleep(sleep_till - now);
#endif
    now = time(0);
  }
}

#ifdef WATCH_CPU
measure_cpu()
{
  p_vmstat pvm;
  double   mpid_interval;
  double   mpid_rate;

  pvm = vmglobal_total();

  // In SE 3.0 user_time and system_time are int and in SE 3.1 they are
  // double, so cast everything to double using + 0.0.
  put_output(" usr%",    sprintf("%5.1f", pvm.user_time + 0.0));
  put_output(" sys%",    sprintf("%5.1f", pvm.system_time + 0.0));
  put_output(" wio%",    sprintf("%5.1f", pvm.wait_time + 0.0));
  put_output("idle%",    sprintf("%5.1f", pvm.idle_time + 0.0));
  put_output(" 1runq",   sprintf("%6.2f", tmp_kstat_misc.avenrun_1min/256.0));
  put_output(" 5runq",   sprintf("%6.2f", tmp_kstat_misc.avenrun_5min/256.0));
  put_output("15runq",   sprintf("%6.2f", tmp_kstat_misc.avenrun_15min/256.0));
  put_output("#proc",    sprintf("%5lu",  tmp_kstat_misc.nproc));
  put_output("scanrate", sprintf("%8.3f", pvm.scan + 0.0));

  // Calculate the rate of new process spawning.
  if (can_read_kernel != 0) {
    mpid_interval = (mpid_now - mpid_then) * 0.000000001;
    mpid_rate     = (mpid_current - mpid_previous) / mpid_interval;
    put_output("#proc/s",   sprintf("%7.3f", mpid_rate));
    put_output("#proc/p5s", sprintf("%9.4f", mpid5_rate));

    // Reset counters.
    mpid_then     = mpid_now;
    mpid_previous = mpid_current;
    mpid5_rate    = 0;
  }
}
#endif

#ifdef WATCH_MUTEX
measure_mutex()
{
  put_output(" smtx",    sprintf("%5d", tmp_mutex.smtx));
  put_output("smtx/cpu", sprintf("%8d", tmp_mutex.smtx/tmp_mutex.ncpus));
  put_output("ncpus",    sprintf("%5d", tmp_mutex.ncpus));
}
#endif

#ifdef WATCH_NET
measure_net()
{
  int previous_count = -1;
  int current_count;
  int i;

  current_count = 0;
  for (i=0; i<tmp_nr.net_count; i++) {
    // Skip unused interfaces.
//  if (GLOBAL_net[i].up == 0) {
//    continue;
//  }
    ++current_count;
    put_output(sprintf("%5sIpkt/s", tmp_nr.names[i]),
	       sprintf("%11.3f", GLOBAL_net[i].ipackets));
    put_output(sprintf("%5sOpkt/s", tmp_nr.names[i]),
	       sprintf("%11.3f", GLOBAL_net[i].opackets));
    put_output(sprintf("%5sInKB/s", tmp_nr.names[i]),
	       sprintf("%11.3f", GLOBAL_net[i].ioctets/1024.0));
    put_output(sprintf("%5sOuKB/s", tmp_nr.names[i]),
	       sprintf("%11.3f", GLOBAL_net[i].ooctets/1024.0));
    put_output(sprintf("%5sIErr/s", tmp_nr.names[i]),
	       sprintf("%11.3f", GLOBAL_net[i].ierrors));
    put_output(sprintf("%5sOErr/s", tmp_nr.names[i]),
	       sprintf("%11.3f", GLOBAL_net[i].oerrors));
    put_output(sprintf("%5sColl%%", tmp_nr.names[i]),
	       sprintf("%10.3f", GLOBAL_net[i].collpercent));
    put_output(sprintf("%5sNoCP/s", tmp_nr.names[i]),
	       sprintf("%11.3f", GLOBAL_net[i].nocanput));
    put_output(sprintf("%5sDefr/s", tmp_nr.names[i]),
	       sprintf("%11.3f", GLOBAL_net[i].defer));
  }

  // If the number of up interfaces changes, then print new headers.
  if (current_count != previous_count) {
    print_header = 1;
    previous_count = current_count;
  }
}
#endif

#ifdef WATCH_TCP
measure_tcp()
{
  put_output("tcp_Iseg/s", sprintf("%10.3f", tmp_tcp.InDataSegs));
  put_output("tcp_Oseg/s", sprintf("%10.3f", tmp_tcp.OutDataSegs));
  put_output("tcp_InKB/s", sprintf("%10.3f", tmp_tcp.InDataBytes/1024.0));
  put_output("tcp_OuKB/s", sprintf("%10.3f", tmp_tcp.OutDataBytes/1024.0));
  put_output("tcp_Ret%",   sprintf("%8.3f",  tmp_tcp.RetransPercent));
  put_output("tcp_Dup%",   sprintf("%8.3f",  tmp_tcp.InDupPercent));
  put_output("tcp_Icn/s",  sprintf("%9.3f",  tmp_tcp.PassiveOpens));
  put_output("tcp_Ocn/s",  sprintf("%9.3f",  tmp_tcp.ActiveOpens));
  put_output("tcp_estb",   sprintf("%8lu",   tmp_tcp.last.tcpCurrEstab));
  put_output("tcp_Rst/s",  sprintf("%9.3f",  tmp_tcp.OutRsts));
  put_output("tcp_Atf/s",  sprintf("%9.3f",  tmp_tcp.AttemptFails));
  put_output("tcp_Ldrp/s", sprintf("%10.3f", tmp_tcp.ListenDrop));
  put_output("tcp_LdQ0/s", sprintf("%10.3f", tmp_tcp.ListenDropQ0));
  put_output("tcp_HOdp/s", sprintf("%10.3f", tmp_tcp.HalfOpenDrop));
}
#endif

#ifdef WATCH_NFS_CLIENT
measure_nfs_client()
{
  put_output("nfs_call/s", sprintf("%10.3f", tmp_lrpcc.calls));
  put_output("nfs_timo/s", sprintf("%10.3f", tmp_lrpcc.timeouts));
  put_output("nfs_badx/s", sprintf("%10.3f", tmp_lrpcc.badxids));
}
#endif

#ifdef WATCH_NFS_SERVER
measure_nfs_server()
{
  ulong calls;
  ulong badcalls;
  ulong v2read;
  ulong v2write;
  ulong v3read;
  ulong v3write;

  calls    = tmp_nfs.calls;
  badcalls = tmp_nfs.badcalls;
  v2read   = tmp_rfsproccnt_v2.read;
  v2write  = tmp_rfsproccnt_v2.write;
  v3read   = tmp_rfsproccnt_v3.read;
  v3write  = tmp_rfsproccnt_v3.write;

  put_output("nfss_calls", sprintf("%10lu", calls));
  put_output("nfss_bad",   sprintf("%8lu", badcalls));
  put_output(" v2reads",   sprintf("%8lu", v2read));
  put_output("v2writes",   sprintf("%8lu", v2write));
  put_output(" v3reads",   sprintf("%8lu", v3read));
  put_output("v3writes",   sprintf("%8lu", v3write));
}
#endif

#ifdef WATCH_MOUNTS
measure_mounts()
{
  statvfs_t vfs_array[1];
  statvfs_t vfs;
  string    comment_fmt;
  string    kbytes_fmt;
  string    inode_fmt;
  string    percent_fmt;
  ulong     kbytes_used;
  ulong     inodes_used;
  double    block_factor;
  int       comment_length;
  int       previous_count = -1;
  int       current_count;

  current_count = 0;
  // Traverse the mount table to find mounted ufs/vxfs file systems.
  for (mnt$mnt.number$=0; mnt$mnt.number$ != -1; mnt$mnt.number$++) {
    tmp_mnt = mnt$mnt;
    if (tmp_mnt.mnt_fstype == "ufs" || tmp_mnt.mnt_fstype == "vxfs") {
      // Skip locally mounted /cdrom partitions.
      if (tmp_mnt.mnt_mountp =~ "^/cdrom/") {
        continue;
      }

      // Skip snapshot file systems to avoid output format changes as
      // they are mounted and umounted.
      if (tmp_mnt.mnt_mountp =~ "^/snapshots/") {
        continue;
      }

      if (statvfs(tmp_mnt.mnt_mountp, vfs_array) == -1) {
        continue;
      }
      vfs = vfs_array[0];
      ++current_count;

      // Generate the format strings for the comment and for the data.
      comment_fmt    = sprintf("mnt%%c_%s", tmp_mnt.mnt_mountp);
      comment_length = strlen(comment_fmt) - 1;
      kbytes_fmt     = sprintf("%%%d.0f",   comment_length);
      inode_fmt      = sprintf("%%%dld",    comment_length);
      percent_fmt    = sprintf("%%%d.3f",   comment_length);

      // Calculate the number of 1 kilobyte blocks on the disk.
      block_factor = vfs.f_frsize/1024;

      // Capital letters refer to the disk usage in kilobytes.  Lower case
      // letters refer to inode usage.
      // C - Capacity of the disk.
      // U - Used capacity.
      // A - Available capacity for non-root users.
      // P - Percent used.
      kbytes_used = vfs.f_blocks - vfs.f_bfree;
      inodes_used = vfs.f_files  - vfs.f_ffree;

      put_output(sprintf(comment_fmt, 'C'),
                 sprintf(kbytes_fmt, block_factor*vfs.f_blocks));
      put_output(sprintf(comment_fmt, 'U'),
                 sprintf(kbytes_fmt, block_factor*kbytes_used));
      put_output(sprintf(comment_fmt, 'A'),
                 sprintf(kbytes_fmt, block_factor*vfs.f_bavail));
      put_output(sprintf(comment_fmt, 'P'),
                 sprintf(percent_fmt,
                         100.0*kbytes_used/(vfs.f_blocks + vfs.f_bavail - vfs.f_bfree)));

      put_output(sprintf(comment_fmt, 'c'),
                 sprintf(inode_fmt, vfs.f_files));
      put_output(sprintf(comment_fmt, 'u'),
                 sprintf(inode_fmt, inodes_used));
      put_output(sprintf(comment_fmt, 'a'),
                 sprintf(inode_fmt, vfs.f_favail));
      put_output(sprintf(comment_fmt, 'p'),
                 sprintf(percent_fmt,
                         100.0*inodes_used/(vfs.f_files + vfs.f_favail - vfs.f_ffree)));

    }
  }

  // If the number of mounted filesystems changes, then print new headers.
  if (current_count != previous_count) {
    print_header = 1;
    previous_count = current_count;
  }
}
#endif

#ifdef WATCH_DISK
measure_disk()
{
  double mean_disk_busy;
  double peak_disk_busy;
  double total_reads;
  double total_writes;
  double total_readk;
  double total_writek;
  int    previous_count = -1;
  int    i;
  int    disk_count;

  mean_disk_busy = 0.0;
  peak_disk_busy = 0.0;
  total_reads    = 0.0;
  total_writes   = 0.0;
  total_readk    = 0.0;
  total_writek   = 0.0;
  disk_count     = 0;

#ifdef USE_RAWDISK
  for (i=0; i<RAW_disk_count; i++) {
    // Block the listing of tape drives for now.
    if (RAW_disk[i].short_name[1] == 't' && RAW_disk[i].short_name[0] == 's') {
      continue;
    }
    // Block the listing of floppy drives for now.
    if (RAW_disk[i].short_name[0] == 'f' && RAW_disk[i].short_name[1] == 'd') {
      continue;
    }
    disk_count++;
    put_output(sprintf("disk_runp_%s", RAW_disk[i].long_name),
               sprintf("%16.5f", RAW_disk[i].run_percent));
    total_reads    += RAW_disk[i].reads;
    total_writes   += RAW_disk[i].writes;
    total_readk    += RAW_disk[i].kreads;
    total_writek   += RAW_disk[i].kwrites;
    mean_disk_busy += RAW_disk[i].run_percent;
    if (RAW_disk[i].run_percent > peak_disk_busy) {
       peak_disk_busy = RAW_disk[i].run_percent;
    }
  }
#else
  for (i=0; i<GLOBAL_disk_count; i++) {
    disk_count++;
    put_output(sprintf("disk_runp_%s", GLOBAL_disk[i].info.long_name),
               sprintf("%16.5f", GLOBAL_disk[i].run_percent));
    total_reads    += GLOBAL_disk[i].reads;
    total_writes   += GLOBAL_disk[i].writes;
    total_readk    += GLOBAL_disk[i].kreads;
    total_writek   += GLOBAL_disk[i].kwrites;
    mean_disk_busy += GLOBAL_disk[i].run_percent;
    if (GLOBAL_disk[i].run_percent > peak_disk_busy) {
      peak_disk_busy = GLOBAL_disk[i].run_percent;
    }
  }
#endif

  if (disk_count != 0) {
    mean_disk_busy = mean_disk_busy/disk_count;
  }

  put_output("disk_peak", sprintf("%9.3f", peak_disk_busy));
  put_output("disk_mean", sprintf("%9.3f", mean_disk_busy));
  put_output("disk_rd/s", sprintf("%9.1f", total_reads));
  put_output("disk_wr/s", sprintf("%9.1f", total_writes));
  put_output("disk_rK/s", sprintf("%9.1f", total_readk));
  put_output("disk_wK/s", sprintf("%9.1f", total_writek));

  // If the number of disks has changed, say due to a add_drv, then print
  // new headers.
  if (previous_count != disk_count) {
    print_header   = 1;
    previous_count = disk_count;
  }
}
#endif

#ifdef WATCH_DNLC
measure_dnlc()
{
  put_output("dnlc_ref/s", sprintf("%10.3f", tmp_lrdnlc.refrate));
  put_output("dnlc_hit%",  sprintf("%9.3f",  tmp_lrdnlc.hitrate));
}
#endif

#ifdef WATCH_INODE
measure_inode()
{
  put_output("inod_ref/s", sprintf("%10.3f", tmp_lrinode.refrate));
  put_output("inod_hit%",  sprintf("%9.3f",  tmp_lrinode.hitrate));
  put_output("inod_stl/s", sprintf("%10.3f", tmp_lrinode.iprate));
}
#endif

#ifdef WATCH_RAM
measure_ram()
{
  put_output("swap_avail", sprintf("%10ld", GLOBAL_pvm[0].swap_avail));
  put_output("page_rstim", sprintf("%10d",  tmp_lrram.restime));
  put_output("  freememK", sprintf("%10d",  GLOBAL_pvm[0].freemem));
  put_output("free_pages", sprintf("%10d",  (GLOBAL_pvm[0].freemem*1024)/page_size));
}
#endif

#ifdef WATCH_PAGES
measure_pages()
{
  put_output("pp_kernel", sprintf("%9lu", tmp_kstat_pages.pp_kernel));
  put_output("pagesfree", sprintf("%9lu", tmp_kstat_pages.pagesfree));
  put_output("pageslock", sprintf("%9lu", tmp_kstat_pages.pageslocked));
  put_output("pagesio",   sprintf("%7lu", tmp_kstat_pages.pagesio));
  put_output("pagestotl", sprintf("%9lu", tmp_kstat_pages.pagestotal));
}
#endif

#ifdef WATCH_WEB
/*
 * Breakdown access log format.
 */
accesslog(string buf) {
  int     z;
  int     size_index;
  int     ishead;
  string  word;
  char    first_byte[1];

#if WATCH_PROXY || WATCH_SQUID || WATCH_YAHOO
  double  xf;
#ifdef WATCH_SQUID
  string  logtag;
  string  request;
#endif
#ifdef WATCH_YAHOO
  string  arg;
  ulong   ptr;
  ulong   tmp;
  ulong   ulong_xf;
#endif
#endif

  ishead = 0;
#ifdef WATCH_YAHOO
  /*
   * Make sure that the input line has at least 32 bytes of data plus a new
   * line, for a total length of 33.
   */
  if (strlen(buf) < 33) {
    return;
  }
  word = strtok(buf, "\05");
#else
  word = strtok(buf, " ");
#endif
  if (word == nil) {
    return;
  }

#ifdef WATCH_SQUID
  /*
   * Word contains unix time in seconds.milliseconds.
   */
  word = strtok(nil, " ");
  if (word == nil) {
    return;
  }
  xf = atof(word)/1000.0;
  www_dwnld_time_sum += xf;
#ifdef DINKY
  printf("time: %s %f total %f\n", word, xf, xfer_sum);
#endif
  word   = strtok(nil, " ");			/* Client IP address.      */
  logtag = strtok(nil, "/");			/* Log tag.                */
  if (logtag == nil) {
    return;
  }
  if (logtag =~ "TCP") {
    squid_client_http++;
  }
  if (logtag =~ "UDP") {
    squid_icp_requests++;
  }
  if (logtag =~ "HIT") {
    prxy_squid_cache_hits++;
  }
  if (logtag =~ "MISS") {
    squid_cache_misses++;
  }

  word = strtok(nil, " ");			/* Reply code.             */
  if (word == nil) {
    return;
  }
  WWW_REPLY_CODE(word)
  word = strtok(nil, " ");			/* Size sent to client.    */
  if (word == nil) {
    return;
  }
  z = atoi(word);
  WWW_SIZE_INDEX(z, size_index)
  www_dwnld_time_by_size[size_index] += xf;

  request = strtok(nil, " ");			/* Request method.         */
  if (request == nil) {
    return;
  }
  WWW_METHOD(request)
  /* Do not add the size if it is a HEAD. */
  if (ishead == 0) {
    dwnld_totalz += z;
  }

  word = strtok(nil, " ");			/* URL.                    */
  if (word == nil) {
    return;
  }
  if (word =~ "cgi-bin") {
    httpop_cgi_bins++;
  }
  if (word =~ www_search_url) {
    httpop_searches++;
  }
  strtok(nil, " ");				/* Optional user identity. */
  word = strtok(nil, "/");			/* Hierarchy.              */
  if (word == nil) {
    return;
  }
  if (word =~ "DIRECT") {
    prxy_squid_indirect++;
  }
#if 0
  word = strtok(nil, " ");			/* Hostname.               */
  if (word == nil) {
    return;
  }
  word = strtok(nil, " ");			/* Content-type.           */
  if (word == nil) {
    return;
  }
#endif

#elif WATCH_YAHOO
  /*
   * Yahoo log format.  Fields in square brackets will only appear in the
   * log file if the data actually exists (ie. you will never see a null
   * Referrer field).  Further, fields labelled here with "(CONFIG)" will
   * only appear if they are enabled via the YahooLogOptions configuration
   * directive.
   *
   *     IP Address		(8 hex digits)
   *     Timestamp		(time_t as 8 hex digits)
   *     Processing Time	(in microseconds, as 8 hex digits)
   *     Bytes Sent		(8 hex digits)
   *     URL
   *     [^Er referrer]  (CONFIG)
   *     [^Em method]    (CONFIG)
   *     [^Es status_code]
   *     ^Ed signature
   *     \n
   */

  /*
   * Ignore the IP address and timestamp.  Get the processing time, the
   * number of bytes sent and the URL.  For each portion of the line, split
   * it up into separate pieces.
   */
  if (sscanf(word, "%8lx%8lx%8x%8x", &tmp, &tmp, &ulong_xf, &z) != 4) {
    return;
  }

  xf = ulong_xf/1000000.0;
  WWW_SIZE_INDEX(z, size_index)
  www_dwnld_time_sum                 += xf;
  www_dwnld_time_by_size[size_index] += xf;

  if (word =~ "cgi-bin") {
    httpop_cgi_bins++;
  }
  if (word =~ www_search_url) {
    httpop_searches++;
  }

  for (;;) {
    word = strtok(nil, "\05");
    if (word == nil) {
      break;
    }
    first_byte = word;
    ptr        = &word + 1;
    arg        = ((string) ptr);
    ptr = 0;
    switch (first_byte[0]) {
      case 'm':
        WWW_METHOD(arg)
        ptr = 1;
        break;
      case 's':
        WWW_REPLY_CODE(arg)
        break;
      default:
        break;
    }
  }

  /* If no method was seen, then assume it was a GET. */
  if (ptr == 0) {
    httpop_gets++;
  }

  /* Do not add the size if it is a HEAD. */
  if (ishead == 0) {
    dwnld_totalz += z;
  }

#else	/* common or netscape proxy formats */
  strtok(nil, " ");		/* -.           */
  strtok(nil, " ");		/* -.           */
  strtok(nil, " [");		/* date.        */
  strtok(nil, " ");		/* zone].       */
  strtok(nil, " \"");		/* GET or POST. */
  if (word == nil) {
    return;
  }
  WWW_METHOD(word)
  word = strtok(nil, " ");	/* URL.         */
  if (word == nil) {
    return;
  }
  if (word =~ "cgi-bin") {
    httpop_cgi_bins++;
  }
  if (word =~ www_search_url) {
    httpop_searches++;
  }
  /*
   * Sometimes HTTP/1.x is not listed in the access log.  Skip it
   * if it does exist.  Load the error/success code.
   */
  word = strtok(nil, " ");
  if (word == nil) {
    return;
  }
  if (word =~ "HTTP" || word =~ "http") {
    word = strtok(nil, " ");
    if (word == nil) {
      return;
    }
  }
  WWW_REPLY_CODE(word)
  word = strtok(nil, " ");	/* Bytes transferred. */
  if (word == nil) {
    return;
  }
  z = atoi(word);
  /* Do not add the size if it is a HEAD. */
  if (ishead == 0) {
    dwnld_totalz += z;
  }
  WWW_SIZE_INDEX(z, size_index)
#ifdef WATCH_PROXY
  strtok(nil, " ");		/* Status from server.      */
  strtok(nil, " ");		/* Length from server.      */
  strtok(nil, " ");		/* Length from client POST. */
  strtok(nil, " ");		/* Length POSTed to remote. */
  strtok(nil, " ");		/* Client header request.   */
  strtok(nil, " ");		/* Proxy header response.   */
  strtok(nil, " ");		/* Proxy header request.    */
  strtok(nil, " ");		/* Server header response.  */
  strtok(nil, " ");		/* Transfer total seconds.  */
  word = strtok(nil, " ");	/* Route.                   */
  if (word == nil) {
    return;
  }

  /* - DIRECT PROXY(host.domain:port) SOCKS. */
  if (strncmp(word, "PROXY", 5) == 0 ||
      strncmp(word, "SOCKS", 5) == 0) {
    prxy_squid_indirect++;
  }
  strtok(nil, " ");		/* Client finish status.    */
  strtok(nil, " ");		/* Server finish status.    */
  word = strtok(nil, " ");	/* Cache finish status.     */
  if (word == nil) {
    return;
  }
  /*
   * ERROR HOST-NOT-AVAILABLE = error or incomplete op
   * WRITTEN REFRESHED CL-MISMATCH(content length mismatch) = cache_writes
   * NO-CHECK UP-TO-DATE = cache_hits
   * DO-NOT-CACHE NON-CACHEABLE = uncacheable
   */
  switch(word) {
    case "WRITTEN":
    case "REFRESHED":
    case "CL-MISMATCH":
      prxy_cache_writes++;
      break;
    case "NO-CHECK":
    case "UP-TO-DATE":
      prxy_squid_cache_hits++;
      break;
    case "DO-NOT-CACHE":
    case "NON-CACHEABLE":
      prxy_uncacheable++;
      break;
    default:
      break;
  }
  word = strtok(nil, " [");		/* [transfer total time x.xxx. */
  if (word == nil) {
    return;
  }
  xf = atof(word);
  www_dwnld_time_sum                 += xf;
  www_dwnld_time_by_size[size_index] += xf;
#endif
#endif
}

measure_web(long sleep_till)
{
  double lastops = 0.0;
  char   buf[BUFSIZ];
  int    i;
  long   now;

  httpops         = 0.0;
  httpops5        = 0.0;
  gateops         = 0.0;
  httpop_gets     = 0;
  httpop_condgets = 0;
  httpop_posts    = 0;
  httpop_cgi_bins = 0;
  httpop_errors   = 0;
  httpop_searches = 0;

  for (i=0; i<5; i++) {
    dwnld_size[i] = 0;
#if WATCH_PROXY || WATCH_SQUID || WATCH_YAHOO
    www_dwnld_time_by_size[i] = 0.0;
#endif
  }
  dwnld_totalz = 0;

#if WATCH_PROXY || WATCH_SQUID || WATCH_YAHOO
  www_dwnld_time_sum      = 0.0;
#endif
#if WATCH_PROXY || WATCH_SQUID
  prxy_squid_indirect     = 0;
  prxy_squid_cache_hits   = 0;
#ifdef WATCH_PROXY
  prxy_cache_writes       = 0;
  prxy_uncacheable        = 0;
#else
  squid_cache_misses      = 0;
  squid_icp_requests      = 0;
  squid_icp_queries       = 0;
  squid_client_http       = 0;
#endif
#endif

  if (www_log_filename != nil) {
    now = time(0);
    while (now < sleep_till) {
#ifdef WATCH_CPU
      sleep_till_and_count_new_proceses(now + 5);
#else
      sleep(5);
#endif
      now = time(0);
      if (www_fd != 0) {
        buf[BUFSIZ-1] = 127;
        while (fgets(buf, BUFSIZ, www_fd) != nil) {
          httpops += 1.0;
          if (www_gatelen > 0) {
            if (strncmp(buf, www_gateway, www_gatelen) == 0) {
              gateops += 1.0;
            }
          }
          accesslog(buf);

          /*
           * If the line is longer than the buffer, then ignore the rest
           * of the line.
           */
          while (buf[BUFSIZ-1] == 0    &&
                 buf[BUFSIZ-2] != '\n') {
            buf[BUFSIZ-1] = 127;
            if (fgets(buf, BUFSIZ, www_fd) == nil) {
              break;
            }
          }
        }
      }

      /*
       * See if the file has been switched or truncated.
       */
      if (stat(www_log_filename, www_stat) == 0) {
        if (www_ino != www_stat[0].st_ino || www_size > www_stat[0].st_size) {
          /*
           * Close the old log file.
           */
          if (www_fd != 0) {
            fclose(www_fd);
          }

          /*
           * The log file has changed, open the new one.
           */
          www_fd = fopen(www_log_filename, "r");
          if (www_fd != 0) {
            fstat(www_fd, www_stat);
            www_ino = www_stat[0].st_ino;
            buf[BUFSIZ-1] = 127;
            while(fgets(buf, BUFSIZ, www_fd) != nil) {
              httpops += 1.0;
              if (www_gatelen > 0) {
                if (strncmp(buf, www_gateway, www_gatelen) == 0) {
                  gateops += 1.0;
                }
              }
              accesslog(buf);

              /*
               * If the line is longer than the buffer, then ignore
               * the rest of the line.
               */
              while (buf[BUFSIZ-1] == 0    &&
                     buf[BUFSIZ-2] != '\n') {
                buf[BUFSIZ-1] = 127;
                if (fgets(buf, BUFSIZ, www_fd) == nil) {
                  break;
                }
              }
            }
          }
        }
        /* Remember size for next time. */
        www_size = www_stat[0].st_size;
      }

      www5_now      = gethrtime();
      www5_interval = (www5_now - www5_then) * 0.000000001;
      www5_then     = www5_now;
      dtmp          = (httpops - lastops)/www5_interval;
      if (dtmp > httpops5) {
        httpops5 = dtmp;
      }
      lastops = httpops;
    }
  }
  else {
    sleep_till_and_count_new_proceses(sleep_till);
    www5_now = gethrtime();
  }

  www_now      = www5_now;
  www_interval = (www_now - www_then) * 0.000000001;
  www_then     = www_now;

  /*
   * Use dtmp to get percentages.
   */
  if (httpops == 0.0) {
    dtmp = 0.0;
  }
  else {
    dtmp = 100.0 / httpops;
  }

#if WATCH_PROXY || WATCH_SQUID || WATCH_YAHOO
  for (i=0; i<5; i++) {
    if (dwnld_size[i] == 0) {
      www_dwnld_time_by_size[i] = 0.0;
    }
    else {
      www_dwnld_time_by_size[i] = www_dwnld_time_by_size[i]/dwnld_size[i];
    }
  }
#endif
}

int count_proc(string name)
{
  int        count;
  prpsinfo_t p;

  count = 0;
  for (p=first_proc(); p.pr_pid != -1; p=next_proc()) {
    if (p.pr_fname =~ name) {
      count++;
    }
  }
  return count;
}

put_httpd()
{
  put_output("#httpds",    sprintf("%7d",   count_proc(www_server_proc_name)));
  put_output("httpop/s",   sprintf("%8.2f", httpops/www_interval));
  put_output("http/p5s",   sprintf("%8.2f", httpops5));
  put_output("cndget/s",   sprintf("%8.2f", httpop_condgets/www_interval));
  put_output("search/s",   sprintf("%8.3f", httpop_searches/www_interval));
  put_output("   cgi/s",   sprintf("%8.3f", httpop_cgi_bins/www_interval));
  put_output(" htErr/s",   sprintf("%8.3f", httpop_errors/www_interval));
  put_output(" httpb/s",   sprintf("%8.0f", dwnld_totalz/www_interval));
  put_output("  %to1KB",   sprintf("%8.2f", dtmp*dwnld_size[0]));
  put_output(" %to10KB",   sprintf("%8.2f", dtmp*dwnld_size[1]));
  put_output("%to100KB",   sprintf("%8.2f", dtmp*dwnld_size[2]));
  put_output("  %to1MB",   sprintf("%8.2f", dtmp*dwnld_size[3]));
  put_output("%over1MB",   sprintf("%8.2f", dtmp*dwnld_size[4]));
  put_output(www_gateway,  sprintf("%8.2f", gateops/www_interval));
#if WATCH_PROXY || WATCH_SQUID
  put_output("  %indir",   sprintf("%8.2f", dtmp * prxy_squid_indirect));
  put_output("%cch_hit",   sprintf("%8.2f", dtmp * prxy_squid_cache_hits));
#ifdef WATCH_PROXY
  put_output("%cch_wrt",   sprintf("%8.2f", dtmp * prxy_cache_writes));
  put_output("%cch_unc",   sprintf("%8.2f", dtmp * prxy_uncacheable));
#else
  put_output("%cch_mis",   sprintf("%8.2f", dtmp * squid_cache_misses));
  put_output("%cch_req",   sprintf("%8.2f", dtmp * squid_icp_requests));
  put_output("%cch_qry",   sprintf("%8.2f", dtmp * squid_icp_queries));
#endif
  put_output("   xfr_t",   sprintf("%8.2f", 0.01 * dtmp * www_dwnld_time_sum));
  put_output("  xfr1_t",   sprintf("%8.2f", www_dwnld_time_by_size[0]));
  put_output(" xfr10_t",   sprintf("%8.2f", www_dwnld_time_by_size[1]));
  put_output("xfr100_t",   sprintf("%8.2f", www_dwnld_time_by_size[2]));
  put_output(" xfr1M_t",   sprintf("%8.2f", www_dwnld_time_by_size[3]));
  put_output("xfro1M_t",   sprintf("%8.2f", www_dwnld_time_by_size[4]));
#elif WATCH_YAHOO
  put_output("   wprc_t",  sprintf("%9.5f", 0.01 * dtmp * www_dwnld_time_sum));
  put_output("  wprc1_t",  sprintf("%9.5f", www_dwnld_time_by_size[0]));
  put_output(" wprc10_t",  sprintf("%9.5f", www_dwnld_time_by_size[1]));
  put_output("wprc100_t",  sprintf("%9.5f", www_dwnld_time_by_size[2]));
  put_output(" wprc1M_t",  sprintf("%9.5f", www_dwnld_time_by_size[3]));
  put_output("wprco1M_t",  sprintf("%9.5f", www_dwnld_time_by_size[4]));
#endif
}
#endif


More information about the Orca-dev mailing list