Sar(1m) for the 3b1

Mark H. Colburn mark at jhereg.Jhereg.MN.ORG
Wed May 25 13:42:10 AEST 1988


This is a public domain version of the System V sar command for the
3b1.  As far as I can tell it is a complete implementation, except for
disk acounting (see the README file) of the System V version of sar.
This code was written without use of the AT&T or Berkley sources,
other than the man pages.

For installation instructions, see the README file.

-- 
Mark H. Colburn           mark at jhereg.Jhereg.MN.ORG
                          ..!ihnp4!chinet!jhereg!mark
------------------------------- CUT HERE -------------------------------
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README Makefile sar.c sadc.c sar.h sa1.SH sa2.SH sar.1
#   sar.1m
# Wrapped by mark at jhereg on Tue May 24 22:41:29 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f README -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"README\"
else
echo shar: Extracting \"README\" \(3910 characters\)
sed "s/^X//" >README <<'END_OF_README'
X
X  This is a public domain sar(1) system activity package written for the 3b1 
X  version 3.51.  This is a complete rewrite of the sar package, reverse 
X  engineered from the man pages.  It was not derived from any software, except 
X  for the file format information in the sar.h header file which was exctracted 
X  from the sar.1m manual page.  It would most likely run on other machines and 
X  other versions of UNIX with a little hacking.  It does make use of a few 3b1 
X  only featurs, namely syslocal and ktune, but these could be hacked around.
X
X  The source is copyrighted.  Permission is given for unlimited copying and
X  modification for non-commercial use as long as the source (not binary only)
X  is distributed, and this file accompanies the distribution, and I am
X  given credit where credit is due.
X
X  I thought of writting this when answering a question about using the
X  nlist(3c) functions to access /dev/kmem.  It seemed pretty easy to do (which
X  was not true in the final analysis, but, hey...)  and since the 3b1 does not
X  have a sar, I thought that I would write it.
X
X  It seems a little silly that a sar command was not developed and/or
X  distributed with the 3b1.  After all, they give you an easy way to
X  reconfigure the kernel, you would think that they would give you some way to
X  determine if the kernel actually *needs* reconfiguring.
X
X
X
XINSTALLATION
X
X  Traditionally, sar is run by the user 'adm' (administrative user).  This
X  is how the installation is currently set up.  If you would like to have
X  sar owned by someone else, you will have to change the Makefile
X  accordingly.
X
X  The sadc command is set-GIDed to group sys in order for it to read
X  /dev/kmem.  The program does a setgid(getgid()) as soon as /dev/kmem is
X  open, so it should be secure.
X
X  To install this package edit the Makefile to specify the correct place for
X  the finished binaries and manual pages to go, then su to root and type 
X  'make install' as root.  The install procedure will create two new
X  directories and set the file ownerships and permissions as follows:
X
X       /usr/lib/sa - hiding place of shell scripts and support programs 
X       /usr/adm/sa - contains the outputs of the sar and sadc programs
X
X  Once the software has been configured and installed, you will want to
X  add the following commands to your crontab file:
X
X	   0 * * * 0,6 su adm -c "/usr/lib/sa/sa1"
X	   0 8-17 * * 1-5 su adm -c "/usr/lib/sa/sa1 1200 3"
X	   0 18-7 * * 1-5 su adm -c "/usr/lib/sa/sa1"
X	   59 23 * * *  su adm -c "/usr/lib/sa/sa2 -A"
X
X  These can be suitably editted if you are running a better version of cron
X  than the one that is distributed with the 3b1.  You may wish to change
X  the /usr/lib/sa/sa2 command line paramenters.  These parameters are
X  simply passed through to sar, so they can be anything which suits your
X  purpose.
X
X  Then add the following line to your /etc/rc:
X
X	   su adm -c "/usr/lib/sa/sadc /usr/adm/sa/sa`date +%d`"
X
X  This line should be put into the /etc/rc file verbatim so that the
X  command is run by adm.  This writes out a reboot record to the daily
X  accounting file whenever /etc/rc is run.
X
X  That's it.  You're done.
X
X
X
XLIMITATIONS
X
X  At this time, this package is a complete implementation of the System V
X  sar commend with the following limitations:
X
X     * Sar disk reporting is referenced, but does not work.  This is due to
X       the apparent lack of disk statistic recording within the kernel.  If I
X       can find the data in the kernel somewhere, I will add it.  Pointers 
X       andybody?
X
X   I will be working on this package on an ongoing basis to fix any bugs and 
X   to additional functionality that is needed.  If you have any fixes, 
X   questions, comments or suggestions please e-mail, call or write to:
X
X
X   Mark H. Colburn				mark at jhereg.mn.org
X   76 Gant Circle, #F			...!ihnp4!chinet!jhereg!mark
X   Streamwood, IL 60107
X   (312) 213-2852
END_OF_README
if test 3910 -ne `wc -c <README`; then
    echo shar: \"README\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f Makefile -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Makefile\"
else
echo shar: Extracting \"Makefile\" \(1875 characters\)
sed "s/^X//" >Makefile <<'END_OF_Makefile'
X#
X# Makefile for sar System Activity Reporting Package
X#
X# @(#)Makefile	1.7 4/24/88 Copyright Mark H. Colburn
X#
X#
X
Xinclude $(MAKEINC)/Makepre.h
X
X#
X# Please change the following parameter to match your system:
X#
X# BINDIR	- the directory where sar will eventually live.
X# SARONR	- the LOGNAME of the user who owns sar (should be adm)
X# SARGRP	- the group of the user who owns sar (should be adm)
X# KMEMGRP	- the group who has read access to /dev/kmem
X# MANDIR	- the directory where the man pages should be copied to
X# PERMS		- default permissions for binaries and directories
X#
XBINDIR  = /usr/bin
XSARONR	= adm
XSARGRP	= adm
XKMEMGRP	= sys
XMANDIR  = /usr/man/man1
XPERM	= 755
X
X
XCFLAGS   = -O 
XLDFLAGS  = -s
XPROGRAMS = sadc sar sa1 sa2
XSOURCES  = README Makefile sar.c sadc.c sar.h sa1.SH sa2.SH sar.1 sar.1m 
XMANPAGES = sar.1 sar.1m
XOBJECTS  = sar.o sadc.o
XMATHLIB  = -lm
X
Xall: $(PROGRAMS)
X
Xinstall: all
X	if [ ! -d /usr/adm/sa ]; then mkdir /usr/adm/sa; fi
X	if [ ! -d /usr/lib/sa ]; then mkdir /usr/lib/sa; fi
X	chmod $(PERM) /usr/adm/sa /usr/lib/sa
X	chown $(SARONR) /usr/adm/sa /usr/lib/sa/* /usr/lib/sa /usr/lib/sa/*
X	chgrp $(SARGRP) /usr/adm/sa /usr/lib/sa/* /usr/lib/sa /usr/lib/sa/*
X	for file in sadc sa1 sa2; do \
X		cp $$file /usr/lib/sa; \
X		chmod $(PERM) /usr/lib/sa/$$file; \
X		chown $(SARONR) $$file; \
X		chgrp $(SARONR) $$file; \
X	done
X	chgrp $(KMEMGRP) /usr/lib/sa/sadc
X	chmod g+s /usr/lib/sa/sadc
X	cp sar $(BINDIR)
X	chmod $(PERM) $(BINDIR)/sar
X	cp $(MANPAGES) $(MANDIR)
X
Xclean:
X	rm -f $(OBJECTS) $(PROGRAMS) a.out core
X
Xclobber: clean
X	rm -f $(SOURCES)
X
Xshar:
X	shar $(SOURCES) > shar 
X
Xsadc: sadc.o sar.h
X	$(LD) $(SHAREDLIB) $(LDFLAGS) sadc.o -o sadc
X
Xsar: sar.o sar.h
X	$(LD) $(SHAREDLIB) $(LDFLAGS) sar.o -o sar $(MATHLIB)
X
Xsa1: sa1.SH
X	cp sa1.SH sa1
X	chmod a+x sa1
X
Xsa2: sa2.SH
X	sed 's;%BINDIR%;$(BINDIR);g' sa2.SH > sa2
X	chmod a+x sa2
X
Xinclude $(MAKEINC)/Makepost.h
END_OF_Makefile
if test 1875 -ne `wc -c <Makefile`; then
    echo shar: \"Makefile\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f sar.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"sar.c\"
else
echo shar: Extracting \"sar.c\" \(31357 characters\)
sed "s/^X//" >sar.c <<'END_OF_sar.c'
X/*
X * sar - System Activity Report
X *
X * This routine reads the data files written by sadc(1m) and writes user
X * readable reports.
X *
X * Author: Mark H. Colburn (mark at jhereg.mn.org)
X */
X
X#include <stdio.h>
X#include <sys/utsname.h>
X#include <sys/sysinfo.h>
X#include <string.h>
X#include <fcntl.h>
X#include <ctype.h>
X#include <nlist.h>
X#include <time.h>
X#include "sar.h"
X
X#ifndef lint
Xstatic char    *SccsID = "@(#)sar.c	1.9 3/16/88 Copyright Mark H. Colburn";
X#endif
X
X/* we need to declare all of these to initialize the function array */
X
Xvoid print_cpu(), print_buffer(), print_disk(), print_tty();
Xvoid print_syscall(), print_swap(), print_file(), print_queues();
Xvoid print_status(), print_message(), print_paging();
Xvoid print_report();
X
X#define	END(v)			(sizeof(v)/sizeof(v[0]))
X#define DAYSECS			86399
X#define	PRINT_CPU		0
X#define PRINT_DISK		1
X#define	PRINT_QUEUES		2
X#define PRINT_BUFFER		3
X#define PRINT_SWAP		4
X#define PRINT_SYSCALL		5
X#define PRINT_FILE		6
X#define PRINT_TTY		7
X#define PRINT_STATUS		8
X#define PRINT_MESSAGE		9
X#define PRINT_PAGING	       10
X
X/*
X * this array of structures keeps track of which reports need to be generated,
X * the functions which need to be called to generate those reports, and the
X * order in which the reports are printed.
X */
X
Xstruct {
X    void            (*func) ();
X    int             doit;
X} reports[] = {
X    { print_cpu, 0 },
X    { print_disk, 0 },
X    { print_queues, 0 },
X    { print_buffer, 0 },
X    { print_swap, 0 },
X    { print_syscall, 0 },
X    { print_file, 0 },
X    { print_tty, 0 },
X    { print_status, 0 },
X    { print_message, 0 },
X    { print_paging, 0 }
X};
X
X
Xstruct sa       currsa;		/* current system statistics for quantum */
Xstruct sa       oldsa;		/* system statistics for last quantum */
Xchar           *myname;		/* name of this program (argv[0]) */
Xint             rptcnt = 0;	/* number of reports selected */
Xint             interval;	/* interval to display samples */
Xint             repeat = 0;	/* number of iterations to run */
Xint             delay = 0;	/* time delay between data colletcions */
Xtime_t          start_tm = 0;	/* time to start display (in seconds) */
Xtime_t          end_tm = DAYSECS;	/* time to end display (in seconds) */
X
Xextern int      optind;
Xextern char    *optarg;
Xextern int      errno;
Xvoid            exit();
Xlong            lseek();
Xtime_t          time();
Xtime_t          get_time();
Xvoid            fatal();
Xvoid            usage();
X
X
X/*
X * main - main routine, processes arguments
X */
X
Xint
Xmain(argc, argv)
X    int             argc;
X    char           *argv[];
X{
X    struct utsname  sysname;	/* name of this system */
X    struct tm      *mytime;	/* used to print time */
X    time_t          now;	/* used to fetch time */
X    char            infnm[128];	/* name of input data file */
X    char            outfn[128];	/* name of output data file (-o) */
X    int             oflag = 0;	/* used to assure command line sanity */
X    int             fflag = 0;
X    int             c;
X    int             i;
X
X    myname = argv[0];
X    strcpy(outfn, "");
X
X    /* get current system time */
X    now = time((int *) 0);
X    mytime = localtime(&now);
X
X    /* default input data file name */
X    (void) sprintf(infnm, "/usr/adm/sa/sa%.2d", mytime->tm_mday);
X
X    /* process command line arguments */
X    while ((c = getopt(argc, argv, "ubdycwaqvmpAi:e:s:o:f:")) != EOF) {
X	rptcnt++;
X	switch (c) {
X	case 'u':
X	    reports[PRINT_CPU].doit = 1;
X	    break;
X	case 'b':
X	    reports[PRINT_BUFFER].doit = 1;
X	    break;
X	case 'd':
X	    reports[PRINT_DISK].doit = 1;
X	    break;
X	case 'y':
X	    reports[PRINT_TTY].doit = 1;
X	    break;
X	case 'c':
X	    reports[PRINT_SYSCALL].doit = 1;
X	    break;
X	case 'w':
X	    reports[PRINT_SWAP].doit = 1;
X	    break;
X	case 'a':
X	    reports[PRINT_FILE].doit = 1;
X	    break;
X	case 'q':
X	    reports[PRINT_QUEUES].doit = 1;
X	    break;
X	case 'v':
X	    reports[PRINT_STATUS].doit = 1;
X	    break;
X	case 'm':
X	    reports[PRINT_MESSAGE].doit = 1;
X	    break;
X	case 'p':
X	    reports[PRINT_PAGING].doit = 1;
X	    break;
X	case 'A':
X	    for (i = PRINT_CPU; i <= PRINT_PAGING; i++) {
X		reports[i].doit = 1;
X		rptcnt++;
X	    }
X	    break;
X	case 's':
X	    rptcnt--;
X	    fflag++;
X	    start_tm = get_time(optarg);
X	    break;
X	case 'e':
X	    rptcnt--;
X	    fflag++;
X	    end_tm = get_time(optarg);
X	    break;
X	case 'i':
X	    rptcnt--;
X	    fflag++;
X	    interval = atoi(optarg);
X	    if (interval < 1 || interval > DAYSECS) {
X		fputs("interval<1 or >number of seconds in a day\n", stderr);
X		usage();
X	    }
X	    break;
X	case 'o':
X	    rptcnt--;
X	    oflag++;
X	    (void) strcpy(outfn, optarg);
X	    break;
X	case 'f':
X	    rptcnt--;
X	    fflag++;
X	    (void) strcpy(infnm, optarg);
X	    break;
X	default:
X	    usage();
X	    break;
X	}
X    }
X
X    if (oflag && fflag)
X	usage();
X    if (optind < argc) {
X	delay = atoi(argv[optind]);
X	if (++optind < argc)
X	    repeat = atoi(argv[optind]);
X	else
X	    repeat = 1;
X	if (fflag || ++optind < argc || delay < 0 || repeat < 0)
X	    usage();
X	repeat++;
X    }
X
X    /* if no reports selected, print the cpu statistics by default */
X    if (rptcnt == 0) {
X	reports[PRINT_CPU].doit = 1;
X	rptcnt++;
X    }
X
X    /* turn off disk reporting, it is not supported by the kernel.  sigh. */
X    if (reports[PRINT_DISK].doit == 1) {
X	reports[PRINT_DISK].doit = 0;
X	--rptcnt;
X    }
X    
X    /* print the report header */
X    (void) uname(&sysname);
X    (void) printf("\n%s %s %s %s %s   %.2d/%.2d/%d\n",
X	sysname.sysname, sysname.nodename, sysname.release, sysname.version,
X     sysname.machine, mytime->tm_mon + 1, mytime->tm_mday, mytime->tm_year);
X
X
X    /* print the reports the user selected */
X    print_report(infnm, outfn);
X    return (0);
X}
X
X
X/*
X * print_report - print all of the statistics that the user has requested
X *
X */
X
Xvoid
Xprint_report(inname, outname)
X    char           *inname;	/* name of file to get statistics from */
X    char	   *outname;	/* name of file to write statistics to */
X{
X    FILE           *infp;	/* input data file descriptor */
X    int             outfd;	/* output file descriptor */
X    int             err;	/* return code indication */
X    int             first;	/* 1 if first time proccessing any report */
X    char            cmdbuf[64];	/* used to hold the command to sadc */
X    register int    i;
X
X    /*
X     * check to see if we are getting our statistics from a file (count == 0)
X     * if we are getting our statistics from sadc in real-time.  Once we
X     * figure this out, we can open the correct input file. 
X     */
X
X    outfd = -1;
X    if (repeat == 0) {
X	if ((infp = fopen(inname, "r")) == NULL)
X	    fatal(stderr, "Unable to open input file");
X    } else {
X	/* -o filename was specified, open output file */
X	if (strlen(outname) &&
X	    (outfd = open(outname, O_CREAT | O_WRONLY, 0666)) == -1)
X	    fatal("unable to open output file");
X
X	/* getting data from sadc, fire off the command... */
X	sprintf(cmdbuf, "/usr/lib/sa/sadc %d %d", delay, repeat);
X	if ((infp = popen(cmdbuf, "r")) == NULL)
X	    fatal("unable to popen /usr/lib/sa/sadc");
X    }
X
X    i = 0;
X    do {
X	if (repeat || reports[i].doit) {
X
X	    putchar('\n');
X	    first = 1;
X	    while (!feof(infp)) {
X		err = (fread((char *) &currsa, sizeof(currsa), 1, infp));
X		if (ferror(infp) || feof(infp))
X		    err = 0;
X		if (err == 1 && outfd >= 0 &&
X		    write(outfd, (char *) &currsa, sizeof(currsa)) < 0)
X		    fatal("unable to write to output file");
X		if (err == 1 && currsa.ts == 0) {
X		    err = reboot(infp);
X		    if (err == 1 && outfd >= 0 &&
X			write(outfd, (char *) &oldsa, sizeof(oldsa)) < 0)
X			fatal("unable to write to output file");
X		    continue;
X		}
X		if (err == 0 || check_time(currsa.ts)) {
X		    if (repeat) {
X			/* if we are sampling, then go through all reports */
X			for (i = 0; i < END(reports); i++) {
X			    if (reports[i].doit) {
X				if (rptcnt == 1 && err == 0)
X				    putchar('\n');
X				(*reports[i].func) (err != 1);
X			    }
X			}
X			if (rptcnt > 1)
X			    putchar('\n');
X		    } else {
X			if (err == 0)
X			    putchar('\n');
X			(*reports[i].func) (err != 1);
X		    }
X		    first = 0;
X		}
X		oldsa = currsa;
X	    }
X	    if (repeat == 0)
X		(void) fseek(infp, 0L, 0);
X	}
X    } while (!repeat && ++i < END(reports));
X    if (outfd >= 0 && close(outfd) < 0)
X	fatal("unable to close output file");
X    if (repeat)
X	(void) pclose(infp);
X    else
X	(void) fclose(infp);
X}
X
X
X/*
X * reboot - handle system resets
X *
X * System reset records are marked by a system time of 0.  They are written by
X * sadc under certain conditions.  When a system reset is noted, a new 'epoch'
X * record is written to the file so that they system can use that as a
X * starting place for statistics since the reboot.
X */
X
Xint
Xreboot(fp)
X    FILE           *fp;		/* input file pointer */
X{
X    int             err;
X    struct tm      *mytime;
X
X    err = fread((char *) &oldsa, sizeof(struct sa), 1, fp);
X    if (err == 1) {
X	mytime = localtime(&oldsa.ts);
X	(void) printf("%.2d:%.2d:%.2d    ***System Reset***\n",
X		      mytime->tm_hour, mytime->tm_min, mytime->tm_sec);
X    }
X    return (err);
X}
X
X
X/*
X * fatal - print out the error message in str, along with the program name,
X *         to the user and abort
X */
X
Xvoid
Xfatal(str)
X    char           *str;	/* text of error message to print */
X{
X    char            buff[BUFSIZ];
X
X    sprintf(buff, "%s: %s", myname, str);
X    perror(buff);
X    exit(1);
X}
X
X
X/*
X * usage - print out a usage message to the user and abort
X */
X
Xvoid
Xusage()
X{
X    fprintf(stderr, "Usage: %s [-ubdycwaqvmpA] [-o file] t [n]\n",
X	    myname);
X    fprintf(stderr, "       %s [-ubdycwaqvmpA] [-s time] [-e time] [-i sec] [-f file]\n", myname);
X    fputs("\ntime takes the form hh[:mm[:ss]]\n", stderr);
X    exit(1);
X}
X
X
X/*
X * get_time - get a user supplied to and convert it to seconds
X *
X * This routine will get a time which the user typed in in the form
X * hh[:mm[:ss]] and convert it to the curresponding number of seconds
X * so that it may be used to compare times easily.
X */
X
Xtime_t
Xget_time(str)
X    char           *str;	/* time string to dissect */
X{
X    int             i;
X    time_t          hour, minute, second;
X
X    hour = 0;
X    minute = 0;
X    second = 0;
X    i = sscanf(str, "%d:%d:%d", &hour, &minute, &second);
X    if (i < 1 || hour < 0 || hour > 23 || minute < 0
X	|| minute > 59 || second < 0 || second > 59)
X	usage();
X    return (hour * 3600 + minute * 60 + second);
X}
X
X
X/*
X * check_time - check time of current item to see if it should be displayed.
X *
X * This routine checks to see if the current item falls within bounds to print
X * or not.  The bounds are determined by the start time, the stop time and the
X * interval as input by the user on the command line.
X */
X
Xint
Xcheck_time(ct)
X    time_t          ct;		/* current item time */
X{
X    struct tm      *mytime;	/* temporary time buffer */
X    time_t          tmp;
X
X    mytime = localtime(&ct);
X    tmp = mytime->tm_hour * 3600 + mytime->tm_min * 60 + mytime->tm_sec;
X    if (tmp >= start_tm && tmp <= end_tm) {
X	start_tm += interval;
X	return (1);
X    }
X    return (0);
X}
X
X
X/*
X * All of the following routines are used tp print out the statistics reports.
X * In order to make coding of these functions easier, you will note that they
X * all have the same structure, using basically the same variables although of
X * different types as needed.  Not all of the functions are as efficient as
X * possible (i.e.:  a number of fucntions use flowating point, when similar
X * results could be obtained using integer-only math, but they would not be
X * quite as accurate) due to this, but it does make it easier for these
X * functions to be developed and maintained.
X */
X
X
X/*
X * print_cpu - print out cpu usage statistics
X *
X * The following statistics are given:
X *
X *	%usr	    Amount of time system spent in user mode
X *	%sys	    Amount of time system spent in system (kernel) mode
X *	%wio	    Amount of time system spent waiting for i/o to complete
X *	%idle	    Amount of time system spent idle.
X *	average	    averages of statistics for all time quantums sampled
X *
X * All of these are given as percentages for the sample time quantum.  Idle
X * time is computed, not read from the system.  Due to rounding errors,
X * totalling all of these statistics may not always give 100.
X */
X
Xvoid
Xprint_cpu(last)
X    int             last;	/* 1 of last quantum */
X{
X    float           mu[4];	/* usage counters for each quantum */
X    static float    au[4];	/* average usage counters */
X    time_t          total;	/* total of mu[0-3] */
X    static int      count = 0;	/* number of quantums sampled */
X    struct tm      *mytime;	/* used to get quantum times */
X    int             i;
X
X    mytime = localtime(&currsa.ts);
X    if (count == 0) {
X	printf("%.2d:%.2d:%.2d    %%usr    %%sys    %%wio   %%idle\n",
X	       mytime->tm_hour, mytime->tm_min, mytime->tm_sec);
X    } else if (last) {
X	  au[CPU_USER] /= count;
X	  au[CPU_KERNAL] /= count;
X	  au[CPU_WAIT] /= count;
X	printf("Average  %7.0f %7.0f %7.0f %7.0f\n",
X	       au[CPU_USER], au[CPU_KERNAL], au[CPU_WAIT],
X		   ceil(100 - au[CPU_USER] - au[CPU_KERNAL] - au[CPU_WAIT]));
X    } else {
X	for (i = CPU_IDLE; i <= CPU_WAIT; i++)
X	    mu[i] = currsa.si.cpu[i] - oldsa.si.cpu[i];
X	total = mu[CPU_IDLE] + mu[CPU_USER] + mu[CPU_KERNAL] + mu[CPU_WAIT];
X	for (i = CPU_USER; i <= CPU_WAIT; i++) {
X	    mu[i] = (mu[i] * 100) / total;
X	    au[i] += mu[i];
X	}
X	printf("%.2d:%.2d:%.2d %7.0f %7.0f %7.0f %7.0f\n",
X	       mytime->tm_hour, mytime->tm_min, mytime->tm_sec,
X	       mu[CPU_USER], mu[CPU_KERNAL], mu[CPU_WAIT], 
X		   ceil(100 - mu[CPU_USER] - mu[CPU_KERNAL] - mu[CPU_WAIT]));
X    }
X    count++;
X}
X
X/*
X * print_disk - print disk statistics
X */
X
X/* ARGSUSED */
Xvoid
Xprint_disk(last)
X    int             last;
X{
X
X    /*
X     * it appears as if the information which is required to display the disk
X     * statistics has not been linked into this kernel.  I have looked in the
X     * include files and have found a reference to a structure which would
X     * contain these values, but it is conditionally included into one of the
X     * system headers.  Attempting to find the variable (gdstat) in the
X     * kernel namespace does not turn up anything, so I assume that it was
X     * left out. 
X     */
X}
X
X
X/*
X * print_swap - print swapping statistics
X *
X * The following statistics are given:
X *
X *	spwin/s	    number of swap-ins per second
X *	bswin/s	    number of 512 byte blocks swapped in per second
X *	swpot/s	    number of swap-outs per second
X *	bswot/s	    number of 512 byte blocks swapped out per second
X *	pswch/s	    number of process switches per second
X *	average	    averages of statistics for all time quantums sampled
X *
X * The averages are computed for all time quantums sampled.
X */
X
Xvoid
Xprint_swap(last)
X    int             last;
X{
X    float           mu[5];
X    static float    au[5];
X    static int      count = 0;
X    time_t          timer;
X    struct tm      *mytime;
X    int             i;
X
X#define SWPIN	0
X#define BSWIN	1
X#define SWPOT	2
X#define	BSWOT	3
X#define PSWCH	4
X
X    mytime = localtime(&currsa.ts);
X    if (count == 0) {
X	printf("%.2d:%.2d:%.2d spwin/s bswin/s swpot/s bswot/s pswch/s\n",
X	       mytime->tm_hour, mytime->tm_min, mytime->tm_sec);
X    } else if (last) {
X	printf("Average  %7.2f %7.1f %7.2f %7.1f %7.0f\n",
X	       au[SWPIN] / count, au[BSWIN] / (count * BUFSIZ),
X	       au[SWPOT] / count, au[BSWOT] / (count * BUFSIZ),
X	       au[PSWCH] / count);
X    } else {
X	timer = currsa.ts - oldsa.ts;
X	mu[SWPIN] = (currsa.si.swapin - oldsa.si.swapin) / timer;
X	mu[SWPOT] = (currsa.si.swapout - oldsa.si.swapout) / timer;
X	mu[BSWIN] = (currsa.si.bswapin - oldsa.si.bswapin) / timer;
X	mu[BSWOT] = (currsa.si.bswapout - oldsa.si.bswapout) / timer;
X	mu[PSWCH] = (currsa.si.pswitch - oldsa.si.pswitch) / timer;
X	for (i = SWPIN; i <= PSWCH; i++)
X	    au[i] += mu[i];
X	printf("%.2d:%.2d:%.2d %7.2f %7.1f %7.2f %7.1f %7.0f\n",
X	       mytime->tm_hour, mytime->tm_min, mytime->tm_sec,
X	       mu[SWPIN], mu[BSWIN] / BUFSIZ, mu[SWPOT],
X	       mu[BSWOT] / BUFSIZ, mu[PSWCH]);
X    }
X    count++;
X}
X
X
X/*
X * print_buffer - print buffer usage statistics
X *
X * The following statistics are given:
X *
X *	bread/s	    # of 512 byte blocks read from disk per second
X *	lread/s	    # of 512 byte blocks logically read per second
X *	%rcache	    percentage of blocks read from buffer cache
X *	bwrit/s	    # of 512 byte blocks written to disk per second
X *	lwrit/s	    # of 512 byte blocks logically written per second
X *	%wcache	    percentage of blocks written to buffer cache
X *	pread/s	    number of 512 byte blocks physically read per second
X *	pwrit/s	    number of 512 byte blocks physically written per second
X *	average	    averages of statistics for all time quantums sampled
X *
X * The lread and lwrit are logical reads or writes.  These will be done to the
X * buffer cache if the block is in the buffer cache.  If the block is not in
X * the buffer cache, then the system will go ahead and do the i/o from the
X * disk.  If the cache hits are not high enough (lread:  ~90-100%,
X * lwrite: ~70-100%) then the size of the buffer cache should be increased.
X *
X * The pread and pwrit statistics show the number of blocks that were written
X * directly to the disk (the buffer cache was not used).  These would occur if
X * a program uses non-buffered i/o (e.g.:  open, read, write, close using
X * O_NDELAY).  For optimal system performance, these should be kept to a
X * minimum.
X */
X
Xvoid
Xprint_buffer(last)
X    int             last;
X{
X    float           mu[6];
X    static float    au[6];
X    static int      count = 0;
X    time_t          timer;
X    struct tm      *mytime;
X    int             i;
X
X#define BREAD	0
X#define LREAD	1
X#define BWRIT	2
X#define	LWRIT	3
X#define PREAD	4
X#define	PWRIT	5
X
X    mytime = localtime(&currsa.ts);
X    if (count == 0) {
X	printf("%.2d:%.2d:%.2d bread/s lread/s %%rcache bwrit/s lwrit/s %%wcache pread/s pwrit/s\n",
X	       mytime->tm_hour, mytime->tm_min, mytime->tm_sec);
X    } else if (last) {
X	printf("Average  %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f\n",
X	       au[BREAD] / count, au[LREAD] / count,
X	       au[LREAD] == 0 ? 0 : (1.0 - (au[BREAD] / au[LREAD])) * 100,
X	       au[BWRIT] / count, au[LWRIT] / count,
X	       au[LWRIT] == 0 ? 0 : (1.0 - (au[BWRIT] / au[LWRIT])) * 100,
X	       au[PREAD] / count, au[PWRIT] / count);
X    } else {
X	timer = currsa.ts - oldsa.ts;
X	mu[BREAD] = (currsa.si.bread - oldsa.si.bread) / timer;
X	mu[LREAD] = (currsa.si.lread - oldsa.si.lread) / timer;
X	mu[BWRIT] = (currsa.si.bwrite - oldsa.si.bwrite) / timer;
X	mu[LWRIT] = (currsa.si.lwrite - oldsa.si.lwrite) / timer;
X	mu[PREAD] = (currsa.si.phread - oldsa.si.phread) / timer;
X	mu[PWRIT] = (currsa.si.phwrite - oldsa.si.phwrite) / timer;
X	for (i = BREAD; i <= PWRIT; i++)
X	    au[i] += mu[i];
X	printf("%.2d:%.2d:%.2d %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f\n",
X	       mytime->tm_hour, mytime->tm_min, mytime->tm_sec,
X	       mu[BREAD], mu[LREAD],
X	       mu[LREAD] == 0 ? 0 : (1.0 - (mu[BREAD] / mu[LREAD])) * 100,
X	       mu[BWRIT], mu[LWRIT],
X	       mu[LWRIT] == 0 ? 0 : (1.0 - (mu[BWRIT] / mu[LWRIT])) * 100,
X	       mu[PREAD], mu[PWRIT]);
X    }
X    count++;
X}
X
X
X/*
X * print_tty - print tty statistics
X *
X * The following statistics are given:
X *
X *	rawch/s	    # of raw characters (non-canonical) processed per second
X *	canch/s	    # of canonical characters processed per second
X *	outch/s	    # of characters output by system per second
X *	rcvin/s	    # of received character interrupts per second
X *	xmtin/s	    # of output character interrupts per second
X *	mdmin/s	    # of modem (tty) interrupts per second
X *	average	    averages of statistics for all time quantums sampled
X *
X * Generally, the mdmin/s number should be 0.  Numbers higher than one tend to
X * indicate modem trouble (if there is a modem attached to that port) or
X * terminal trouble.
X */
X
Xvoid
Xprint_tty(last)
X    int             last;
X{
X    float           mu[6];
X    static float    au[6];
X    static int      count = 0;
X    time_t          timer;
X    struct tm      *mytime;
X    int             i;
X
X#define RAWCH	0
X#define CANCH	1
X#define OUTCH	2
X#define	RCVIN	3
X#define XMTIN	4
X#define	MDMIN	5
X
X    mytime = localtime(&currsa.ts);
X    if (count == 0) {
X	printf("%.2d:%.2d:%.2d rawch/s canch/s outch/s rcvin/s xmtin/s mdmin/s\n",
X	       mytime->tm_hour, mytime->tm_min, mytime->tm_sec);
X    } else if (last) {
X	printf("Average  %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f\n",
X	       mu[RAWCH] / count, mu[CANCH] / count, mu[OUTCH] / count,
X	       mu[RCVIN] / count, mu[XMTIN] / count, mu[MDMIN] / count);
X    } else {
X	timer = currsa.ts - oldsa.ts;
X	mu[RAWCH] = (currsa.si.rawch - oldsa.si.rawch) / timer;
X	mu[CANCH] = (currsa.si.canch - oldsa.si.canch) / timer;
X	mu[OUTCH] = (currsa.si.outch - oldsa.si.outch) / timer;
X	mu[RCVIN] = (currsa.si.rcvint - oldsa.si.rcvint) / timer;
X	mu[XMTIN] = (currsa.si.xmtint - oldsa.si.xmtint) / timer;
X	mu[MDMIN] = (currsa.si.mdmint - oldsa.si.mdmint) / timer;
X	for (i = RAWCH; i <= MDMIN; i++)
X	    au[i] += mu[i];
X	printf("%.2d:%.2d:%.2d %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f\n",
X	       mytime->tm_hour, mytime->tm_min, mytime->tm_sec,
X	       mu[RAWCH], mu[CANCH], mu[OUTCH],
X	       mu[RCVIN], mu[XMTIN], mu[MDMIN]);
X    }
X    count++;
X}
X
X
X/*
X * print_syscall - print system call statistics
X *
X * The following statistics are given:
X *
X *	scall/s	    number of system calls per second
X *	sread/s	    number of system read calls per second
X *	swrit/s	    number of system write calls per second
X *	fork/s	    number of user fork calls per second
X *	exec/s	    number of user exec calls per second
X *	rchar/s	    number of characters read by system per second
X *	wchar/s	    number of characters written by system per second
X *	average	    averages of statistics for all time quantums sampled
X *
X * Only user generated fork and exec calls are logged.  For example, exec's
X * done by the shell are not included in these statistics, from what I can
X * tell.  Uucico can run up the rchar and wchar statistics in a big hurry.
X */
X
Xvoid
Xprint_syscall(last)
X    int             last;
X{
X    float           mu[7];
X    static float    au[7];
X    static int      count = 0;
X    time_t          timer;
X    struct tm      *mytime;
X    int             i;
X
X#define SCALL	0
X#define SREAD	1
X#define SWRIT	2
X#define	FORK	3
X#define EXEC	4
X#define	RCHAR	5
X#define	WCHAR	6
X
X    mytime = localtime(&currsa.ts);
X    if (count == 0) {
X	printf("%.2d:%.2d:%.2d scall/s sread/s swrit/s fork/s exec/s rchar/s wchar/s\n",
X	       mytime->tm_hour, mytime->tm_min, mytime->tm_sec);
X    } else if (last) {
X	printf("Average  %7.0f %7.0f %7.0f %6.2f %6.2f %7.0f %7.0f\n",
X	       au[SCALL] / count, au[SREAD] / count,
X	       au[SWRIT] / count, au[FORK] / count,
X	       au[EXEC] / count, au[RCHAR] / count,
X	       au[WCHAR] / count);
X    } else {
X	timer = currsa.ts - oldsa.ts;
X	mu[SCALL] = (currsa.si.syscall - oldsa.si.syscall) / timer;
X	mu[SREAD] = (currsa.si.sysread - oldsa.si.sysread) / timer;
X	mu[SWRIT] = (currsa.si.syswrite - oldsa.si.syswrite) / timer;
X	mu[FORK] = (currsa.si.sysfork - oldsa.si.sysfork) / timer;
X	mu[EXEC] = (currsa.si.sysexec - oldsa.si.sysexec) / timer;
X	mu[RCHAR] = (currsa.si.readch - oldsa.si.readch) / timer;
X	mu[WCHAR] = (currsa.si.writech - oldsa.si.writech) / timer;
X	for (i = SCALL; i <= WCHAR; i++)
X	    au[i] += mu[i];
X	printf("%.2d:%.2d:%.2d %7.0f %7.0f %7.0f %6.2f %6.2f %7.0f %7.0f\n",
X	       mytime->tm_hour, mytime->tm_min, mytime->tm_sec,
X	       mu[SCALL], mu[SREAD], mu[SWRIT], mu[FORK],
X	       mu[EXEC], mu[RCHAR], mu[WCHAR]);
X    }
X    count++;
X}
X
X
X/*
X * print_file - print file access statistics
X *
X * The following statistics are given:
X *
X *	iget/s	    number of iget calls per second
X *	namei/s	    number of namei calls per second
X *	dirbk	    number of dirbk calls per second
X *	average	    averages of statistics for all time quantums sampled
X *
X * These statistics represent the number of times the kernel routines iget(),
X * namei() and dirbk() are called.  The biggest hog of these statistics are
X * large find(1) commands.
X */
X
Xvoid
Xprint_file(last)
X    int             last;
X{
X    float           mu[3];
X    static float    au[3];
X    static int      count = 0;
X    time_t          timer;
X    struct tm      *mytime;
X    int             i;
X
X#define IGET	0
X#define NAMEI	1
X#define DIRBK	2
X
X    mytime = localtime(&currsa.ts);
X    if (count == 0) {
X	printf("%.2d:%.2d:%.2d   iget/s namei/s dirbk/s\n",
X	       mytime->tm_hour, mytime->tm_min, mytime->tm_sec);
X    } else if (last) {
X	printf("Average    %6.0f %7.0f %7.0f\n",
X	       au[IGET] / count, au[NAMEI] / count, au[DIRBK] / count);
X    } else {
X	timer = currsa.ts - oldsa.ts;
X	mu[IGET] = (currsa.si.iget - oldsa.si.iget) / timer;
X	mu[NAMEI] = (currsa.si.namei - oldsa.si.namei) / timer;
X	mu[DIRBK] = (currsa.si.dirblk - oldsa.si.dirblk) / timer;
X	for (i = IGET; i <= DIRBK; i++)
X	    au[i] += mu[i];
X	printf("%.2d:%.2d:%.2d   %6.0f %7.0f %7.0f\n",
X	       mytime->tm_hour, mytime->tm_min, mytime->tm_sec,
X	       mu[IGET], mu[NAMEI], mu[DIRBK]);
X    }
X    count++;
X}
X
X
X/*
X * print_queues - print system queue statistics
X *
X * The following statistics are given:
X *
X *	runq-sz	    run queue size (number of procs waiting to run)
X *	%runocc	    percentage of time run queue is occupied
X *	swpq-sz	    swap queue size (number of procs waiting to be swapped)
X *	%swpocc	    percentage of time swap queue is occupied
X *	average	    averages of statistics for all time quantums sampled
X *
X */
X
Xvoid
Xprint_queues(last)
X    int             last;
X{
X    float           mu[4];
X    static float    au[4];
X    static int      count = 0;
X    time_t          timer;
X    struct tm      *mytime;
X    int             i;
X
X#define RUNSZ	0
X#define RUNOCC	1
X#define SWPSZ	2
X#define SWPOCC	3
X
X    mytime = localtime(&currsa.ts);
X    if (count == 0) {
X	printf("%.2d:%.2d:%.2d runq-sz %%runocc swpq-sz %%swpocc\n",
X	       mytime->tm_hour, mytime->tm_min, mytime->tm_sec);
X    } else if (last) {
X	printf("Average  %7.1f %7.0f %7.1f %7.0f\n",
X	       au[RUNSZ] / count, au[RUNOCC] / count,
X	       au[SWPSZ] / count, au[SWPOCC] / count);
X    } else {
X	timer = currsa.ts - oldsa.ts;
X	mu[RUNSZ] = (currsa.si.runque - oldsa.si.runque) / timer;
X	mu[RUNOCC] = (currsa.si.runocc - oldsa.si.runocc) / timer;
X	mu[SWPSZ] = (currsa.si.swpque - oldsa.si.swpque) / timer;
X	mu[SWPOCC] = (currsa.si.swpocc - oldsa.si.swpocc) / timer;
X	for (i = IGET; i <= DIRBK; i++)
X	    au[i] += mu[i];
X	printf("%.2d:%.2d:%.2d %7.1f %7.0f %7.1f %7.0f\n",
X	       mytime->tm_hour, mytime->tm_min, mytime->tm_sec,
X	       mu[RUNSZ], mu[RUNOCC], mu[SWPSZ], mu[SWPOCC]);
X    }
X    count++;
X}
X
X
X/*
X * print_status - print system table status
X *
X * The following statistics are given:
X *
X *	text-sz	    number of entries in text table / max size of text table
X *	proc-sz	    number of entries in proc table / max size of proc table
X *	inod-sz	    number of entries in inode table / max size of inode table
X *	file-sz	    number of entries in file table / max size of file table
X *	text-ov	    number of times text table has overflowed this quantum
X *	proc-ov	    number of times proc table has overflowed this quantum
X *	inod-ov	    number of times inode table has overflowed this quantum
X *	file-ov	    number of times file table has overflowed this quantum
X *
X * The size of the text, proc, inode and file tables can be configured by
X * using ktune(7).  Ktune is a nifty utility, but one does not know that the
X * kernel is in need of reconfiguring unless one has the information produced
X * by sar, which does not come with the 3b1.  Hmmm.
X *
X * If there are overflows in any of the table, it may be time to up the size
X * of the table.  This should not happen at most sites: the defaults are
X * generous.
X */
X
Xvoid
Xprint_status(last)
X    int             last;
X{
X    struct tm      *mytime;
X    static int      count = 0;
X
X    mytime = localtime(&currsa.ts);
X    if (count == 0) {
X	printf("%.2d:%.2d:%.2d text-sz proc-sz inod-sz file-sz text-ov proc-ov inod-ov file-ov\n",
X	       mytime->tm_hour, mytime->tm_min, mytime->tm_sec);
X    } else if (!last) {
X	printf("%.2d:%.2d:%.2d %3d/%3d %3d/%3d %3d/%3d %3d/%3d %7d %7d %7d %7d\n",
X	       mytime->tm_hour, mytime->tm_min, mytime->tm_sec,
X	       currsa.sztext, currsa.msztext,
X	       currsa.szproc, currsa.mszproc,
X	       currsa.szinode, currsa.mszinode,
X	       currsa.szfile, currsa.mszfile,
X	       currsa.textovf - oldsa.textovf,
X	       currsa.procovf - oldsa.procovf,
X	       currsa.inodeovf - oldsa.inodeovf,
X	       currsa.fileovf - oldsa.fileovf);
X    }
X    count++;
X}
X
X
X/*
X * print_message - print interprocess communications statistics
X *
X * The following statistics are given:
X *
X *	msg/s	    number of message transactions per second
X *	sema/s	    number of semaphore transactions per second
X *	average	    averages of statistics for all time quantums sampled
X *
X * These statistics will always be zero unless you have an application using
X * the message and semaphone interprocess communications facilities.  Even
X * then, on a system as small as the 3b1, these are most likely to be zero,
X * unless heavy activity is occurring.
X */
X
Xvoid
Xprint_message(last)
X    int             last;
X{
X    float           mu[2];
X    static float    au[2];
X    static int      count = 0;
X    time_t          timer;
X    struct tm      *mytime;
X    int             i;
X
X#define MSG	0
X#define SEMA	1
X
X    mytime = localtime(&currsa.ts);
X    if (count == 0) {
X	printf("%.2d:%.2d:%.2d   msg/s sema/s\n",
X	       mytime->tm_hour, mytime->tm_min, mytime->tm_sec);
X    } else if (last) {
X	printf("Average    %5.2f %6.2f\n",
X	       au[MSG] / count, au[SEMA] / count);
X    } else {
X	timer = currsa.ts - oldsa.ts;
X	mu[MSG] = (currsa.si.msg - oldsa.si.msg) / timer;
X	mu[SEMA] = (currsa.si.sema - oldsa.si.sema) / timer;
X	for (i = MSG; i <= SEMA; i++)
X	    au[i] += mu[i];
X	printf("%.2d:%.2d:%.2d   %5.2f %6.2f\n",
X	       mytime->tm_hour, mytime->tm_min, mytime->tm_sec,
X	       mu[MSG], mu[SEMA]);
X    }
X    count++;
X}
X
X
X/*
X * print_paging - print paging statistics
X *
X * The following statistics are given:
X *
X *	pagins	    number of demand pagins occurred
X *	pagouts	    number of demand pageouts occurred
X *	average	    averages of statistics for all time quantums sampled
X * 
X * These statistics may be better given as pagins/s and pagouts/s, except that
X * the numbers are so low as to be meaningless (i.e.:  they usually came up as
X * zero).  Possibly pagins/m and pagouts/m (using minutes rather than
X * seconds).  I have never seen a sar with these reports so I am not sure
X * execatly how these are usually reported.
X */
X
Xvoid
Xprint_paging(last)
X    int             last;
X{
X    long            mu[2];
X    static long     au[2];
X    static int      count = 0;
X    struct tm      *mytime;
X    int             i;
X
X#define PGIN	0
X#define PGOUT	1
X
X    mytime = localtime(&currsa.ts);
X    if (count == 0) {
X	printf("%.2d:%.2d:%.2d   pagins pagouts\n",
X	       mytime->tm_hour, mytime->tm_min, mytime->tm_sec);
X    } else if (last) {
X	printf("Average    %6d %7d\n", au[PGIN] / count, au[PGOUT] / count);
X    } else {
X	mu[PGIN] = (currsa.si.pgin - oldsa.si.pgin);
X	mu[PGOUT] = (currsa.si.pgout - oldsa.si.pgout);
X	for (i = PGIN; i <= PGOUT; i++)
X	    au[i] += mu[i];
X	printf("%.2d:%.2d:%.2d   %6d %7d\n",
X	       mytime->tm_hour, mytime->tm_min, mytime->tm_sec,
X	       mu[PGIN], mu[PGOUT]);
X    }
X    count++;
X}
END_OF_sar.c
if test 31357 -ne `wc -c <sar.c`; then
    echo shar: \"sar.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f sadc.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"sadc.c\"
else
echo shar: Extracting \"sadc.c\" \(8040 characters\)
sed "s/^X//" >sadc.c <<'END_OF_sadc.c'
X/*
X * sadc - System Activity Data Collector
X *
X * sadc is responsible for collecting the System Activity data from the kernel
X * and writing it out to a file.  If it has no arguments, then it writes out a
X * special reboot block.
X *
X * Returns 0 to caller if succeded, 1 if failed
X *
X * Author: Mark H. Colburn (mark at jhereg.mn.org)
X */
X
X#include <stdio.h>
X#include <time.h>
X#include <nlist.h>
X#include <fcntl.h>
X#include <ctype.h>
X#include <sys/sysinfo.h>
X#include <sys/var.h>
X#include <sys/syslocal.h>
X#include <sys/file.h>
X#include <sys/text.h>
X#include <sys/inode.h>
X#include "sar.h"
X
X#ifndef lint
Xstatic char    *SccsID = "@(#)sadc.c	1.8 3/16/88 Copyright Mark H. Colburn";
X#endif
X
X/*
X * this array will contain that addresses of the various kernel data 
X * structures that we are interested in looking at.
X */
Xstruct nlist    sysadr[] = {
X    {"sysinfo", 0, 0, 0, 0, 0},
X    {"syserr", 0, 0, 0, 0, 0},
X    {"text", 0, 0, 0, 0, 0},
X    {"file", 0, 0, 0, 0, 0},
X    {"inode", 0, 0, 0, 0, 0},
X    {"proc", 0, 0, 0, 0, 0},
X    {"ffreelist", 0, 0, 0, 0, 0},
X    {"ifreelist", 0, 0, 0, 0, 0},
X    {"", 0, 0, 0, 0, 0}
X};
X
X#define infoadr		(sysadr[0].n_value)
X#define erradr		(sysadr[1].n_value)
X#define textadr		(sysadr[2].n_value)
X#define fileadr		(sysadr[3].n_value)
X#define inodadr		(sysadr[4].n_value)
X#define procadr		(sysadr[5].n_value)
X#define ffreeadr	(sysadr[6].n_value)
X#define ifreeadr	(sysadr[7].n_value)
X
Xint             kmem;		/* file handle for /dev/kmem */
Xchar           *myname;		/* name of this program (argv[0]) */
X
Xvoid            kmemread();
Xvoid            get_sa_info();
Xvoid            usage();
Xvoid            fatal();
Xvoid            exit();
Xvoid            perror();
Xtime_t          time();
Xunsigned        sleep();
Xlong            lseek();
Xchar           *memset();
Xextern int      errno;
X
X
X/*
X * main - main processing program
X */
X
Xint
Xmain(argc, argv)
X    int             argc;
X    char           *argv[];
X{
X    int             delay;	/* seconds to delay between collections */
X    int             reps;	/* number of times to repeat collections */
X    int             outfd;	/* file descriptor to write write data to */
X    int             zerorec;	/* 1 if should write out reboot, 0 if data */
X    char           *fname;	/* filename of output file */
X    void            init();	/* initialization function */
X
X    delay = 0;
X    reps = 1;
X    zerorec = 1;
X    fname = (char *) NULL;
X
X    myname = argv[0];
X    argc--;
X    argv++;
X    if (argc >= 2) {
X	if (isdigit((*argv)[0]) && isdigit((*(argv + 1))[0])) {
X	    delay = atoi(*argv++);
X	    reps = atoi(*argv++);
X	    argc -= 2;
X	    zerorec = 0;
X	} else
X	    usage();
X    }
X    if (argc == 1) {
X	fname = *argv;
X    } else if (argc > 1) {
X	usage();
X    } else {
X	outfd = 1;		/* standard output */
X    }
X
X    init();
X    do {
X	if (fname &&
X	 (outfd = open(fname, O_WRONLY | O_CREAT | O_APPEND, 0644)) == -1) {
X	    fprintf(stderr, "Unable to open output file %s (%d)\n",
X		    fname, errno);
X	    exit(1);
X	}
X	get_sa_info(outfd, zerorec);
X	if (outfd != 1)
X	    (void) close(outfd);
X	if (reps > 1 && delay > 0)
X	    (void) sleep((unsigned) delay);
X    } while (--reps);
X    (void) close(kmem);
X    exit(0);
X}
X
X
X/*
X * init - handle miscellaneous initializations
X */
X
Xvoid
Xinit()
X{
X    /* open the kernel memory */
X    if ((kmem = open("/dev/kmem", O_RDONLY)) == -1)
X	fatal("Can't open /dev/kmem");
X
X    /* get addresses of various kernel structures from /unix */
X    if (nlist("/unix", sysadr) == -1)
X	fatal("Unable perform nlist on /unix");
X
X    /* get the "real" address for each of the tables */
X    kmemread(inodadr, (char *) &inodadr, sizeof(inodadr));
X    kmemread(ifreeadr, (char *) &ifreeadr, sizeof(ifreeadr));
X    kmemread(textadr, (char *) &textadr, sizeof(textadr));
X    kmemread(fileadr, (char *) &fileadr, sizeof(fileadr));
X    kmemread(procadr, (char *) &procadr, sizeof(procadr));
X    kmemread(ffreeadr, (char *) &ffreeadr, sizeof(ffreeadr));
X
X    /* turn of the setgid now that /dev/kmem is open */
X    setgid(getgid());
X}
X
X
X/*
X * get_sa_info - get system activity counters from kernel
X */
X
Xvoid
Xget_sa_info(outfd, zerorec)
X    int             outfd;	/* file descriptor to write output to */
X    int             zerorec;	/* not 0 if should zero record */
X{
X    long            vaddr;	/* address of kernel var structure */
X    struct var      v;		/* kernel var structure */
X    struct syserr   saerr;	/* system error counts */
X    struct sa       sadata;	/* system activity data */
X
X    /*
X     * if we are just rebooting, then write out a zero record so that our
X     * place will be marked in the file.  The next record will be taken as
X     * the zero base for the new accounting. 
X     */
X
X    (void) memset((char *) &sadata, 0, sizeof(sadata));
X
X    if (zerorec && write(outfd, (char *) &sadata, sizeof(sadata)) == -1)
X	fatal("Unable to write output file");
X
X    /*
X     * read all of the information that we need for statistics out of the
X     * kernel space.  For some of these items, it means that we will have to
X     * read the data twice, since they are only pointers to the real items. 
X     */
X
X    vaddr = (long) syslocal(SYSL_KADDR, SLA_V);
X    kmemread(vaddr, (char *) &v, (long) sizeof(v));
X    kmemread(infoadr, (char *) &sadata.si, sizeof(sadata.si));
X    kmemread(erradr, (char *) &saerr, sizeof(saerr));
X
X    /* copy the max table sizes from the system var record... */
X    sadata.mszinode = v.v_inode;
X    sadata.mszfile = v.v_file;
X    sadata.msztext = v.v_text;
X    sadata.mszproc = v.v_proc;
X
X    /* compute the current table sizes */
X    sadata.szinode = (ifreeadr - inodadr) / sizeof(struct inode);
X    sadata.szfile = (ffreeadr - fileadr) / sizeof(struct file);
X    sadata.szproc = ((long) v.ve_proc - procadr) / sizeof(struct proc);
X    sadata.sztext = cnt_txt(textadr,
X	       (long) (((long) v.ve_text - textadr) / sizeof(struct text)));
X
X    /* copy the overflow statistics from the syserr record... */
X    sadata.inodeovf = saerr.inodeovf;
X    sadata.fileovf = saerr.fileovf;
X    sadata.textovf = saerr.textovf;
X    sadata.procovf = saerr.procovf;
X
X    /* get the current time in the timestamp */
X    sadata.ts = time((int *) 0);
X
X    if (write(outfd, (char *) &sadata, sizeof(sadata)) == -1)
X	fatal("Unable to write output file\n");
X}
X
X
X/*
X * cnt_txt - count the number of active text table entries
X *
X * This is kind of a hack, but I could find no other way to get the size of
X * the text table, therefore, we will read through the text table incrementing
X * a counter each time we find an entry that does not have a size of zero.
X * This is time consuming, since we have to read /dev/kmem for each text table
X * entry.  Oh, well...
X */
X
Xint
Xcnt_txt(txtadr, txtcnt)
X    long            txtadr;	/* address of text table */
X    long	    txtcnt;	/* max number of text table entries */
X{
X    register int    i;		/* loop counter */
X    struct text     text;	/* text table entry for reads */
X    long            count;	/* count of 'used' table entries */
X
X    count = 0;
X    for (i = 0; i < txtcnt; ++i) {
X	kmemread((long) &((struct text *) txtadr)[i],
X		 (char *) &text, sizeof(text));
X	if (text.x_size != 0)
X	    count++;
X    }
X    return (count);
X}
X
X
X/*
X * usage - print out a usage message to the user and abort
X */
X
Xvoid
Xusage()
X{
X    fprintf(stderr, "Usage: %s [t n] [ofile]\n", myname);
X    exit(1);
X}
X
X
X/*
X * fatal - print out the error message in str, along with the program name,
X *         to the user and abort
X */
X
Xvoid
Xfatal(str)
X    char           *str;	/* text of error message to print */
X{
X    char            buff[BUFSIZ];
X
X    sprintf(buff, "%s: %s", myname, str);
X    perror(buff);
X    exit(1);
X}
X
X
X/*
X * kmemread - read nbytes of data from /dev/kmem into dptr
X */
X
Xvoid
Xkmemread(kmadr, dptr, nbytes)
X    long            kmadr;	/* kernel address to read data from */
X    char           *dptr;	/* pointer to place to save data */
X    unsigned        nbytes;	/* number of bytes to read */
X{
X    if (lseek(kmem, kmadr, 0) < 0L || read(kmem, dptr, nbytes) != nbytes)
X	fatal("can't read /dev/kmem");
X}
END_OF_sadc.c
if test 8040 -ne `wc -c <sadc.c`; then
    echo shar: \"sadc.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f sar.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"sar.h\"
else
echo shar: Extracting \"sar.h\" \(1425 characters\)
sed "s/^X//" >sar.h <<'END_OF_sar.h'
X/*
X * sar.h - defintions for System Activity package
X *
X * Author: Mark H. Colburn (mark at jhereg.mn.org)
X *
X * @(#)sar.h 1.2 88/03/16 Copyright Mark H. Colburn
X */
X
X#include <sys/gdisk.h>
X
X/*
X * Used to access elements of the devio array in the sa structure
X */
X
X#define	IO_OPS	1						
X#define IO_BCNT	1					
X#define IO_ACT	2				
X#define IO_RESP	3			
X
X/*
X * the actual system account record that is written out by sadc.  This
X * record format is not compatible with other versions of sar.
X */
X
Xstruct sa {
X    struct sysinfo  si;		/* see /usr/include/sys/sysinfo.h */
X    int             szinode;	/* # of entries in inode table */
X    int             szfile;	/* # of entries in file table */
X    int             sztext;	/* # of entries in text table */
X    int             szproc;	/* # of entries in proc table */
X    int             mszinode;	/* size of inode table */
X    int             mszfile;	/* size of file table */
X    int             msztext;	/* size of text table */
X    int             mszproc;	/* size of proc table */
X    long            inodeovf;	/* number of inode table overflows */
X    long            fileovf;	/* number of file table overflows */
X    long            textovf;	/* number of text table overflows */
X    long            procovf;	/* number of proc table overflows */
X    time_t          ts;		/* time stamp, in seconds */
X    long            devio[DISKS][4];	/* device info for DISKS disks */
X};
END_OF_sar.h
if test 1425 -ne `wc -c <sar.h`; then
    echo shar: \"sar.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f sa1.SH -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"sa1.SH\"
else
echo shar: Extracting \"sa1.SH\" \(396 characters\)
sed "s/^X//" >sa1.SH <<'END_OF_sa1.SH'
X#
X# sa1 - shell script to drive sadc(1m)
X#
X# Usage: /usr/lib/sa/sa1 [t n]
X#
X# Author: Mark H. Colburn (mark at jhereg.mn.org)
X#
X# @(#)sa1.SH	1.3 3/16/88 Copyright Mark H. Colburn
X#
X
Xif [ $# = 2 ]
Xthen
X	delay=$1
X	count=$2
X	while [ $count -gt 0 ]
X	do
X		/usr/lib/sa/sadc 0 1 /usr/adm/sa/sa`date +%d`
X		sleep $delay
X		count=`expr $count - 1`
X	done
Xelse
X	/usr/lib/sa/sadc 0 1 /usr/adm/sa/sa`date +%d`
Xfi
END_OF_sa1.SH
if test 396 -ne `wc -c <sa1.SH`; then
    echo shar: \"sa1.SH\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f sa2.SH -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"sa2.SH\"
else
echo shar: Extracting \"sa2.SH\" \(270 characters\)
sed "s/^X//" >sa2.SH <<'END_OF_sa2.SH'
X#
X# sa2 - shell script to print daily sar reports
X#
X# Usage: /usr/lib/sa/sa2
X#
X# Author: Mark H. Colburn (mark at jhereg.mn.org)
X#
X# @(#)sa2.SH	1.3 4/24/88 Copyright Mark H. Colburn
X#
X%BINDIR%/sar $* > /usr/adm/sa/sar`date +%d`
Xfind /usr/adm/sa -mtime +7 -exec rm -f {} \;
END_OF_sa2.SH
if test 270 -ne `wc -c <sa2.SH`; then
    echo shar: \"sa2.SH\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f sar.1 -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"sar.1\"
else
echo shar: Extracting \"sar.1\" \(8838 characters\)
sed "s/^X//" >sar.1 <<'END_OF_sar.1'
X.TH SAR 1 
X.SH NAME
Xsar \- system activity reporting package
X.SH SYNOPSIS
X.B sar
X.RB [\-ubdycwaqvmpA]
X.RB [\-s " time" ]
X.RB [\-e " time" ]
X.RB [\-i " secs" ]
X.RB [\-f " file" ]
X.sp
X.B sar
X.RB [\-ubdycwaqvmpA]
X.RB [\-o time]
Xt [ n ]
X.SH DESCRIPTION
X.IR Sar
Xis a utility to monitor system activity on an ongoing basis.
XTwo different command forms are supported: one for monitoring the system
Xdata in real-time and the other for monitoring system activity on a
Xregularly scheduled basis.
X.PP
XWhen using the first form of the command, 
X.IR sar gets it's data from a file.
XIf no file name is specified with the
X.B \-f
Xflag, then the default the file, is 
X.BI /usr/adm/sa/sa dd\^, 
Xwhere 
X.IR dd
Xstands for the current day of the month.
XThe 
X.B \-s, 
X.B \-e
Xand
X.B \-i
Xflags provide a means to bound the data displayed by sar.
XThe
X.B \-s
Xand
X.B \-e
Xflag specifies a starting time and ending time for a report.
XThe 
X.B \-i 
Xflag specifies that only records every 
X.I sec 
Xor more seconds apart will be printed on the report.
X.PP
XWhen using the second form of the command, 
X.IR sar
Xwill sample the system usage data from the kernel
X.B n
Xtimes, pausing
X.B t
Xseconds between each sample.
XThe 
X.B \-o flag 
Xallows a means for the sampled data to be saved to a file for later
Xreview.
X.PP
XAverages are given for all statistics that can be logically averaged.
XAverages are computed based on the activities of all time quantums sampled.
XDue to round off errors, it is possible that hand calculating the averages
Xmay produce slightly different results that those which are printed by
X.B sar.
X.PP
XDifferent reports may be displayed by supplying arguments on the command line.
XIf no arguments are given, then the cpu usage report is displayed by 
Xdefault.
XThe command line options are as follows:
X.PP
X.PD 0
X.TP 5
X.B \-u
XReport
X.SM CPU
Xutilization (the default):
X.sp
X.ta \w'XXXXXXXX\ \ 'u
X.RS
X.nf
X%usr	time system spent in user mode
X%sys	time system spent in system (kernel) mode
X%wio	time system spent waiting for i/o to complete
X%idle	time system spent idle.
X.DT
X.fi
X.sp
XAll of these statistics are given as percentages for the sample time quantum.
XIdle time is computed, not read from the system.  
XDue to rounding errors, totalling all of these statistics may not always give
Xexactly 100.
X.sp
X.RE
X.TP
X.B \-b
XReport system buffer activity:
X.sp
X.ta \w'XXXXXXXX\ \ 'u
X.RS
X.nf
Xbread/s	# of 512 byte blocks read from disk
Xlread/s	# of 512 byte blocks read from buffer cache
X%rcache	percentage of blocks read from buffer cache
Xbwrit/s	# of 512 byte blocks written to disk
Xlwrit/s	# of 512 byte blocks written to buffer cache
X%wcache	percentage of blocks written to buffer cache
Xpread/s	number of 512 byte blocks physically read
Xpwrit/s	number of 512 byte blocks physically written
X.DT
X.fi
X.sp
XThe 
X.I lread 
Xand 
X.I lwrit 
Xare logical reads or writes done to or from the buffer cache. 
XIf the requested block is not in the buffer cache, then the system will go 
Xahead and do the I/O to or from the disk 
X(the 
X.I bread
Xand
X.I bwrit
Xstatistics).  
XIf the cache hits are not high enough (lread:  ~90-100%, lwrite: ~70-100%) 
Xthen the size of the buffer cache should be increased.
X.sp
XThe 
X.I pread 
Xand 
X.I pwrit 
Xstatistics show the number of blocks that were written
Xdirectly to the disk via a raw devide.
XRaw I/O does not use the buffer cache
XThese would occur if a data is transfered to or from the disk
Xusing a raw device such as /dev/rfp??? or if disk buffering is 
Xdisallowed
X(e.g.:  open, read, write, close using O_NDELAY).
XFor optimal system performance, these should be kept to a minimum.
X.sp
XTheoretically, these statistics are not just for disk, but for any block
Xformatted devices such as tape drives, etc.
XMost 3b1s do not have any block formatted devices, other than disks,
Xhowever.
X.sp
X.RE
X.TP
X.B \-d
XReport disk usage activity:
X.sp
XThis is currently not implemented on the 3b1.
X.sp
X.TP
X.B \-y
XReport TTY device activity:
X.sp
X.ta \w'XXXXXXXX\ \ 'u
X.RS
X.nf
Xrawch/s	input character rate
Xcanch/s	canonical characters processed
Xoutch/s	characters output by system
Xrcvin/s	received character interrupts
Xxmtin/s	output character interrupts
Xmdmin/s	modem (tty) interrupts
X.DT
X.fi
X.sp
XGenerally, mdmin/s should be 0.  
XNumbers higher than one tend to indicate modem or terminal trouble.
X.sp
X.RE
X.TP
X.B \-c
XReport system calls:
X.sp
X.ta \w'XXXXXXXX\ \ 'u
X.RS
X.nf
Xiget/s	number of iget calls
Xnamei/s	number of namei calls
Xdirbk	number of dirbk calls
X.DT
X.fi
X.sp
XThese statistics represent the number of times the kernel routines iget(),
Xnamei() and dirbk() are called.  
XThe biggest hog of these statistics are large find(1) commands.
X.sp
X.RE
X.TP
X.B \-w
XReport system swapping and switching activity:
X.sp
X.ta \w'XXXXXXXX\ \ 'u
X.RS
X.nf
Xspwin/s	number of swap-ins
Xbswin/s	number of 512 byte blocks swapped in
Xswpot/s	number of swap-outs
Xbswot/s	number of 512 byte blocks swapped out
Xpswch/s	number of process switches
X.DT
X.fi
X.sp
XThese statistics represent the number of blocks transfered between the
Xmain memory and the swap area (secondary memory).
X.ISwpin includes the number of blocks transfered for initially loading
Xprograms, as well as for swapping activity.
X.sp
X.RE
X.TP
X.B \-a
XReport use of file access routines:
X.sp
X.ta \w'XXXXXXXX\ \ 'u
X.RS
X.nf
Xiget/s	number of iget calls
Xnamei/s	number of namei calls
Xdirbk	number of dirbk calls
X.DT
X.fi
X.sp
XThese statistics represent the number of times the kernel routines 
X.I iget
X(get a specified inode from disk and lock it in the inode table)
X.I namei 
X(get the inode for a specified pathname) and 
X.I dirblk
Xare called.  
XThe biggest hog of these statistics are large find(1) commands.
X.sp
X.RE
X.TP
X.B \-q
XReport system queue statistics:
X.sp
X.ta \w'XXXXXXXX\ \ 'u
X.RS
X.nf
Xrunq-sz	average run queue size
X%runocc	percentage of time run queue is occupied
Xswpq-sz	averae swap queue size
X%swpocc	percentage of time swap queue is occupied
X.DT
X.fi
X.sp
XThe run queue is a queue of runnable processes which are currently in
Xmemory, awaiting scheduling.
XThe swap queue containes a list of processes which have been swapped out of
Xmain memory and are currently residing on the swap device, but are otherwise 
Xready to run.
X.sp
X.RE
X.TP
X.B \-v
XReport status of text, process, inode and file tables:
X.sp
X.ta \w'XXXXXXXX\ \ 'u
X.RS
X.nf
Xtext-sz	text table size / max size of text table
Xproc-sz	proc table size / max size of proc table
Xinod-sz	inode table size / max size of inode table
Xfile-sz	file table size / max size of file table
Xtext-ov	text table has overflow count
Xproc-ov	proc table has overflow count
Xinod-ov	inode table has overflow count
Xfile-ov	file table has overflow count
X.DT
X.fi
X.sp
XThe size of the text, proc, indoe and file tables are the only statistics
Xwhich are a 'snapshot' of the statistic at the time that 
X.IR sadc "(1m)"
Xran, rather than an average of the statistic over the time quantum.
XThe overflow counts represent the number of times the table has overflowed
Xduring the time quantum.
X.sp
XThe maximum size of the text, proc, inode and file tables can be configured 
Xby using 
X.IR ktune "(7)."  
XKtune is a nifty utility, but one does not know that the
Xkernel is in need of reconfiguring unless one has the information produced
Xby sar, which does not come with the 3b1.
X.sp
XIf there are overflows in any of the table, it may be time to up the size
Xof the table.
X.sp
X.RE
X.TP
X.B \-m
XReport interprocess communications activities:
X.sp
X.ta \w'XXXXXXXX\ \ 'u
X.RS
X.nf
Xmsg/s	number of IPC message transactions
Xsema/s	number of IPC semaphore transactions
X.DT
X.fi
X.sp
XThese statistics will always be zero unless you have an application using
Xthe message and semaphone interprocess communications facilities.  Even
Xthen, on a system as small as the 3b1, these are most likely to be zero,
Xunless heavy activity is occurring.
X.sp
X.RE
X.TP
X.B \-p
XReport pagaing statistics:
X.sp
X.ta \w'XXXXXXXX\ \ 'u
X.RS
X.nf
Xpagins	number of demand pagins occurred
Xpagouts	number of demand pageouts occurred
X.DT
X.fi
X.sp
XThese statistics may be better given as pagins/s and pagouts/s, except that
Xthe numbers are so low as to be meaningless (i.e.:  they usually came up as
Xzero).  
XPossibly pagins/m and pagouts/m (using minutes rather than seconds).  
XI have never seen a sar with these reports so I am not sureexecatly how 
Xthese are usually reported.
X.sp
X.RE
X.TP
X.B \-A
XDisplay all statistics.
XEquivalent to
X.BR \-udqbwcayvmp .
X.sp
X.SH BUGS
X.br
XCurrently disk reporting is not supported by the AT&T 3B1 kernel.
X.br
XThe 
X.B t
Xand
X.B n 
Xarguments currently must be specified as the last items on the command
Xline when using the second form of the command line.
X.SH AUTHOR
X.br
X.ta \w'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\ \ 'u
X.nf
XMark H. Colburn	mark at jhereg.mn.org
X76 Gant Circle, Apt. F	...!ihnp4!chinet!jhereg!mark
XStreamwood, IL   60107	+1 312 213 2852
X.DT
X.fi
X.sp
X.SH FILES
X.RI /usr/adm/sa/sa dd
X.sp
X.SH SEE ALSO
Xsar(1M).
END_OF_sar.1
if test 8838 -ne `wc -c <sar.1`; then
    echo shar: \"sar.1\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f sar.1m -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"sar.1m\"
else
echo shar: Extracting \"sar.1m\" \(2594 characters\)
sed "s/^X//" >sar.1m <<'END_OF_sar.1m'
X.TH SAR 1M
X.SH NAME
Xsa1, sa2, sadc \- system activity reporting package
X.SH SYNOPSIS
X.B /usr/lib/sa/sadc
X[t n]\ [ofile]
X.sp
X.B /usr/lib/sa/sa1
X[t n]
X.sp
X.B /usr/lib/sa/sa2
X.SH DESCRIPTION
XThe routines provided here allow the system activity data provided 
Xby the operating system to be automatically saved at regular intervals
Xand reviewed at the request of a user.
X.P
XA number of counters are kept internally by the operating system and are
Xincremented as various system actions occur.
XCounters are kept for 
XCPU utilization, buffer usage, TTY device activity, process switching, 
Xsystem-calls, file-access, queue activity, inter-process communications
Xand demand paging.
X.PP
X.I Sadc 
X(the system activity data collector) and the two
Xshell procedures,
X.I sa1
Xand
X.IR sa2 ,
Xare used to save the system activity counters so that they may be reviewed
Xlater with the
X.IR sar (1) 
Xcommand.
X.PP
X.IR Sadc 
Xreads the system activity information from /dev/kmem. 
XIf the optional
X.I t
Xand 
X.I n
Xcounters are provided to
X.IR sadc ,
Xthen
X.IR sadc
Xwill collect 
X.I n
Xsets of data, waiting 
X.I t
Xseconds between each data collection.
X.IR Sadc
Xwrite all its data in binary format to the file specified by
X.I ofile
Xor to standard output if no file is specified.
XIf
X.I t
Xand
X.I n
Xare omitted,
Xa special record is written to the file to indicate a system restart.
XThe
X.BR /etc/rc
Xentry:
X.RS
X.sp
X/usr/lib/sa/sadc /usr/adm/sa/sa\*`date +%d\*`
X.sp
X.RE
Xwrites a "reset record" to the daily data file whenever the machine is 
Xreset.
X.PP
XThe shell script
X.IR sa1 ,
Xcalls
X.I sadc
Xwith the appropriate parameters to save the system data
Xin the daily system activity data file
X.BI /usr/adm/sa/sa dd
Xwhere
X.I dd
Xis the current day.
XThe arguments
X.I t
Xand
X.I n
Xcauses 
X.I sa1
Xto invoke the
X.I sadc
Xprogram 
X.I n
Xtimes at an interval of
X.I t
Xseconds, or once if the
X.I t
Xand 
X.I n 
Xarguments are not supplied.
XThe entries in
X.B crontab
X(see
X.IR cron (1M)):
X.RS
X.sp
X0 \(** \(** \(** 0,6 /usr/lib/sa/sa1
X.br
X0 8\-17 \(** \(** 1\-5 /usr/lib/sa/sa1 1200 3
X.br
X0 18\-7 \(** \(** 1\-5 /usr/lib/sa/sa1
X.sp
X.RE
Xwill produce records every hour and every 20 minutes during working hours.
X.PP
XThe 
X.IR sa2 ,
Xshell script
Xwrites a daily report of all the system activity which occured
Xin the file
X.BI /usr/adm/sa/sar dd.
XThe command line arguments given to sa2 are the same as those given to 
X.B sar,
Xthey are merely passed through to 
X.B sar.
X.SH FILES
X.RI /usr/adm/sa/sa "dd	"
Xdaily data file
X.br
X.RI /usr/adm/sa/sar "dd	"
X.br
X/dev/kmem
X.br
X/unix
Xdaily report file
X.SH SEE ALSO
Xcron(1M),
Xsar(1),
Xktune(7),
X/usr/include/sys/sysinfo.h
END_OF_sar.1m
if test 2594 -ne `wc -c <sar.1m`; then
    echo shar: \"sar.1m\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0
-- 
Mark H. Colburn           mark at jhereg.Jhereg.MN.ORG
                          ..!ihnp4!chinet!jhereg!mark



More information about the Comp.sys.att mailing list