[alt.sources] Compute load average for SYSV.

lars@myab.se (Lars Pensj|) (05/25/89)

A program (background deamon) that computes the load average on a
SYSV.3 machine. Probably easy to port to SYSV.2.

#--------CUT---------CUT---------CUT---------CUT--------#
#########################################################
#                                                       #
# This is a shell archive file.  To extract files:      #
#                                                       #
#    1)	Make a directory for the files.                 #
#    2) Write a file, such as "file.shar", containing   #
#       this archive file into the directory.           #
#    3) Type "sh file.shar".  Do not use csh.           #
#                                                       #
#########################################################
#
#
echo Extracting lload.c:
sed 's/^Z//' >lload.c <<\STUNKYFLUFF
Z/*
Z * Load Average deamon.
Z *
Z * The load average is updated every constant time interval, and the result
Z * written to a file as 3 double values.
Z *
Z * The load average is for the last 1, 5 and 15 minutes (3 values).
Z *
Z * A second argument (-v) will set the program in a verbose mode, writing
Z * the load average to the standard output and not to the file.
Z *
Z * Preferably start this process from the inittab. It needs special
Z * priviledges to read from /dev/kmem.
Z *
Z * The following processes are regarded as "runnning":
Z *   A process that has the SRUN status (simply running).
Z *   A process that is being created (SIDLE).
Z *   A process that is being swapped out (SXBRK).
Z *   A process that waits for disk I/O.
Z *
Z * A process is regarded as waiting for disk I/O if it is SSLEEP and
Z * has wchan set to a buf in the buffer pool.
Z *
Z * The sleep is implemented using poll on a stream device, not the
Z * more usual sleep() call. Why ?
Z * Because you do not want to wake up simultaneosly with other programs
Z * doing sleep(), which might give wrong load average.
Z * Of course, if you do not have the stream pipe device, use the normal
Z * sleep().
Z */
Z
Z#include <fcntl.h>
Z#include <nlist.h>
Z#include <stdio.h>
Z#include <stropts.h>
Z#include <poll.h>
Z#include <math.h>
Z#include <sys/types.h>
Z#include <sys/param.h>
Z#include <sys/buf.h>
Z#include <sys/immu.h>
Z#include <sys/region.h>
Z#include <sys/var.h>
Z#include <sys/proc.h>
Z
Z/* #define DEBUG */	/* Will append all values in a debug file */
Z
Z#define LOADAV 		"/etc/loadav"	/* Where to write the load avarge */
Z#define STREAM_PIPE	"/dev/spx"	/* Used for polling with timeout */
Z
Z/*
Z * You may or may not need the '_' in the following names.
Z */
Z
Z#define VAR_NAME "_v"
Z#define BUF_NAME "_buf"
Z#define PROC_NAME "_proc"
Z
Zstruct nlist nl[] = {
Z {VAR_NAME},
Z {BUF_NAME},
Z {PROC_NAME},
Z {0},
Z};
Z
Zint loadfile;				/* file descr to result file */
Zstruct proc *p;
Zstruct var v;
Zint kmem;
Zstruct nlist *v_p, *proc_p, *buf_p;
Zint size;
Zint first_buf, last_buf;
Zint sleeping = 1;			/* Poll frequency in seconds */
Zint verbose = 0;
Z
Zdouble av[3] = { 0.0, 0.0, 0.0 };	/* The loadaverage */
Zdouble apa[3];				/* Holding constants */
Z
Zmain(argc, argv)
Z    char **argv;
Z{
Z    int i, n, n_run, n_disk;
Z#ifdef DEBUG
Z    int debug_fd;
Z    char buff[100];
Z    debug_fd = open("/tmp/loadavdebug", O_CREAT|O_WRONLY);
Z#endif
Z
Z    if (argc == 2 && strcmp(argv[1], "-v") == 0) {
Z	verbose = 1;
Z	printf("Verbose\n");
Z    }
Z    kmem = open("/dev/kmem", 0);
Z    if (kmem == -1) {
Z	perror("/dev/kmem");
Z	exit(1);
Z    }
Z    if (!verbose) {
Z	loadfile = open(LOADAV, 1|O_CREAT,0664);
Z	if (loadfile == -1) {
Z	    fprintf(stderr, "%s:", argv[0]);
Z	    perror(LOADAV);
Z	    exit(1);
Z	}
Z    }
Z    if (nlist("/unix", nl) == -1) {
Z	perror("nlist");
Z	exit(1);
Z    }
Z    for (i=0; nl[i].n_name; i++) {
Z	if (nl[i].n_value == 0) {
Z	    fprintf(stderr, "Could not get address for %s\n", nl[i].n_name);
Z	    exit(1);
Z	}
Z	if (strcmp(nl[i].n_name, VAR_NAME) == 0)
Z	    v_p = &nl[i];
Z	if (strcmp(nl[i].n_name, PROC_NAME) == 0)
Z	    proc_p = &nl[i];
Z	if (strcmp(nl[i].n_name, BUF_NAME) == 0)
Z	    buf_p = &nl[i];
Z    }
Z    /*
Z     * Setup the constants used for computing load average.
Z     */
Z    apa[0] = exp(-sleeping/60.0);
Z    apa[1] = exp(-sleeping/300.0);
Z    apa[2] = exp(-sleeping/900.0);
Z    /*
Z     * Start looping
Z     */
Z    while(1) {
Z	/*
Z	 * Read the 'v' structure every time. It says how
Z	 * many procs are used.
Z	 */
Z	if (lseek(kmem, v_p->n_value, 0) == -1) {
Z	    perror("lseek v");
Z	    exit(1);
Z	}
Z	if (read(kmem, &v, sizeof v) == -1) {
Z	    perror("read v");
Z	    exit(1);
Z	}
Z	size = (struct proc *)v.ve_proc - (struct proc *)proc_p->n_value;
Z	first_buf = buf_p->n_value;
Z	last_buf = first_buf + v.v_buf * sizeof (struct buf);
Z	if (lseek(kmem, proc_p->n_value, 0) == -1) {
Z	    perror("lseek proc");
Z	    exit(1);
Z	}
Z	p = (struct proc *)malloc(size * sizeof (struct proc));
Z	if (p == 0) {
Z	    fprintf(stderr, "Could not malloc %d bytes\n",
Z		    size * sizeof (struct proc));
Z	    exit(1);
Z	}
Z	n = read(kmem, p, size * sizeof (struct proc));
Z	if (n != size * sizeof (struct proc)) {
Z	    if (n == -1) {
Z		perror("read procs");
Z		exit(1);
Z	    }
Z	    fprintf(stderr, "Could only read %d (%d) procs\n",
Z		    n, size);
Z	    size = n / sizeof (struct proc);
Z	}
Z	n_run = 0;
Z	n_disk = 0;
Z	for (i=0; i<size; i++) {
Z	    if (p[i].p_stat == SRUN || p[i].p_stat == SIDL ||
Z		p[i].p_stat == SXBRK)
Z		n_run++;
Z	    else if (p[i].p_stat == SSLEEP &&
Z		     (unsigned int)p[i].p_wchan >= first_buf &&
Z		     (unsigned int)p[i].p_wchan < last_buf)
Z		n_disk++;
Z	}
Z	/*
Z	 * Update the load average using a decay filter.
Z	 */
Z	for (i = 0; i < 3; i++)
Z	    av[i] = apa[i] * av[i] + (n_run + n_disk) * (1.0 - apa[i]);
Z	if (!verbose) {
Z	    if (lseek(loadfile, 0L, 0) == -1) {
Z		fprintf(stderr, "Couldn't seek in %s\n", LOADAV);
Z		exit(1);
Z	    }
Z	    if (write(loadfile, (char *)av, sizeof av) != sizeof av) {
Z		perror(argv[0]);
Z		exit(1);
Z	    }
Z	} else
Z	    printf("(%d %d) %f %f %f\n", n_run, n_disk,
Z		   av[0], av[1], av[2]);
Z#ifdef DEBUG
Z	sprintf(buff, "(%d %d) %4.2f\n", n_run, n_disk,
Z		   av[0]);
Z	write(debug_fd, buff, strlen(buff));
Z#endif
Z	nap(sleeping * 1000);
Z	free(p);
Z    }
Z}
Z
Z/*
Z * Use a stream pipe to implement a sleep.
Z * We have a stream pipe for ourselves, so we know noone will write
Z * on it.
Z */
Znap(milli) {
Z    static int fd = 0;
Z    static struct pollfd pollfd;
Z
Z    if (fd == 0) {
Z	fd = open(STREAM_PIPE, 0);
Z	if (fd == -1) {
Z	    perror(STREAM_PIPE);
Z	    exit(1);
Z	}
Z	pollfd.fd = fd;
Z	pollfd.events = POLLIN;
Z    }
Z    if (poll(&pollfd, 1, milli) == -1) {
Z	perror("nap: poll");
Z	exit(1);
Z    }
Z    if (pollfd.revents != 0) {
Z	fprintf(stderr, "nap: poll: got something\n");
Z	exit(1);
Z    }
Z}
STUNKYFLUFF
set `wc lload.c`
if test 231 != $1
then
echo lload.c: Checksum error. Is: $1, should be: 231.
fi
echo All done
exit 0
-- 
    Lars Pensj|
    lars@myab.se