[net.sources] display --- bar graph of top cpu using processes

wls@astrovax.UUCP (William L. Sebok) (08/10/84)

This is a program for 4.X BSD Unix that prints and continously updates a
bar graph of the top cpu using processes.  It uses curses and termcap.
  Enjoy,
  Bill Sebok	astrovax!wls
------------Cut here----------------------
: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting Makefile'
sed 's/^X//' <<'//go.sysin dd *' >Makefile
CARGS= -O # -DCANREAD
LARGS= -s # -pg

display:	display.o
	cc -o display  $(LARGS) display.o -lcurses -ltermcap -lnm

display.o:	display.c
	cc -O -c $(CARGS) display.c

clean:
	rm -f display display.o
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 Makefile
	/bin/echo -n '	'; /bin/ls -ld Makefile
fi
/bin/echo 'Extracting display.1'
sed 's/^X//' <<'//go.sysin dd *' >display.1
X.TH DISPLAY 1 local 1/26/83
X.SH NAME
display \- display bar graph of system use
X.SH SYNOPSIS
X.B display
[
-\fBT\fRterm ] [ n ]
X.SH DESCRIPTION
X.PP
X.I Display
displays a bar graph, showing  the process number,
the owner, and the percent cpu usage of all currently running processes
with cpu usage of at least 0.001%. All remaining cpu time is lumped into
an
X.I RESIDUAL
entry.
X.PP
The optional argument
X.I n
causes
X.I display
to update the bar graph every
X.I n
seconds.
Exit is by interrupt.
X.PP
The first display shows the cpu usage averaged over the past with a filter
of time constant 20 seconds.
Subsequent displays show the cpu usage over the last interval.
X.PP
The optional -\fBT\fRterm flag causes control codes to be generated for terminal
X.I term
rather than for the default terminal specified in the TERM environment variable.
X.SH FILES
X/etc/passwd /dev/kmem /vmunix
X.SH SEE ALSO
ps(1) w(1) finger(1)
X.SH AUTHOR
William L. Sebok
X.SH BUGS
X.PP
If a process dies between intervals its cpu usage will incorrectly show
up in RESIDUAL.
X.PP
Time in interrupt handlers also shows up in RESIDUAL.
X.PP
Cpu times for an interval are a funny exponentially weighed average
over the interval.
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 display.1
	/bin/echo -n '	'; /bin/ls -ld display.1
fi
/bin/echo 'Extracting display.c'
sed 's/^X//' <<'//go.sysin dd *' >display.c
X/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * **
 ** * * * * * display: show percent cpu use * * * * * * * * * * * * * * * *
 * * * * * * * * * * * * * * * * * * * * * * * * Nov 11,1982 W.Sebok * * **
 ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <sys/param.h>

#include <signal.h>
#ifdef SIGIO
#define BSD4_2
#endif

#ifdef BSD4_2
#include <sys/time.h>
#include <sys/quota.h>
#else BSD4_2
#include <sys/vtimes.h>
#endif BSD4_2
#include <sys/proc.h>
#include <curses.h>
#include <nlist.h>

struct	nlist nls[] = {
#define	X_PROC		0
	{ "_proc" },
#define	X_NPROC		1
	{ "_nproc" },
	{ 0 },
};

extern char **environ;
char term[] = "TERM=\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" ;
char *env[2] = { term , 0 } ;

#define NBAR 55
#define MXSHOWN 20
X/* exp(-1/20) */
#define	CCPU	0.95122942450071400909e0

main(argc,argv)
int argc;
char *argv[];
{
	int kmem ;
	register int i, j ;
	register struct proc *pp ;
	register char *p, *op ;
	int nproc, nxs, addr ;
	double ccpu, acpu, bcpu, t ;
	int tim, ntab, ontab ;
	struct proc  *proc ;
	char *calloc(), *malloc(), *fgets(), lin[120] ;
	int nusers ;
	void done() ;
	FILE *fil ;

	struct usrs {
		double us_pctcpu ;
		double us_opctcpu ;
		short us_uid ;
		short us_pid ;
		char  us_valid ;
	} idle, *users, **tab ;

	char **names ;

	register struct usrs *us ;

	static char VMUNIX[] = "/vmunix" ;
	static char KMEM[]   = "/dev/kmem" ;
	static char USAGE[] = "Usage: display [ -Tterm ] [ time ]\n" ;
	static char NPERR[] = "/vmunix: _nproc not in namelist\n" ;
	static char PERR[]  = "/vmunix: _proc not in namelist\n" ;

	idle.us_uid = -1 ;
	idle.us_pid = -1 ;

	/*
	 * set alternate terminal option
	 */

	if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'T') {
		environ = env ;
		(void)strcat(&term[5],&argv[1][2]) ;
		argv++ ; argc-- ;
	}

	if(argc > 2) {
		(void)write(2,USAGE, sizeof(USAGE)) ;
		exit(1);
	}

	tim = (argc==2) ? atoi(*(++argv)) : 0 ;
	if (tim!=0) {
		(void)signal(2,done) ;
		initscr() ;
		for (i=0 , ccpu=1e0 ; i<tim ; i++) ccpu *= CCPU ;
		acpu = 1e0/(1e0-ccpu) ;
		bcpu = ccpu/(1e0-ccpu) ;
		ontab = 0 ;
	}

	if((kmem = open(KMEM,0)) < 0) {
		perror(KMEM);
		exit(1);
	}


	fil = fopen("/etc/passwd","r") ;
	if (fil==NULL) {
		fprintf(stderr,"Unable to Open /etc/passwd.\n") ;
		exit(1) ;
	}


X/* Scan for number of users */

	nusers = 0 ;

	while (fgets(lin,120,fil) != NULL) {
		p = lin ;
		while (*p != ':') p++ ;
		*p++ = '\0' ;
		while (*p != ':') p++ ;
		op = ++p ;
		while (*p != ':') p++ ;
		*p = '\0' ;
		i = atoi(op) ;
		nusers = (i>nusers) ? i : nusers ;	/* max */
	}

	rewind(fil) ;

	names = (char**)calloc(nusers+2,sizeof(char *));
	names[0] = "RESIDUAL" ;

X/* Get User Names */

	while (fgets(lin,120,fil) != NULL) {
		p = lin ;
		while (*p != ':') p++ ;
		*p++ = '\0' ;
		j = p - lin ;
		while (*p != ':') p++ ;
		op = ++p ;
		while (*p != ':') p++ ;
		*p = '\0' ;
		i = atoi(op) ;
		names[i+1] = (char*)calloc(j,sizeof(char)) ;
		strcpy(names[i+1], lin) ;
	}
	fclose(fil) ;

	/*
	 * Locate proc table
	 */

	nlist(VMUNIX, nls) ;

	if ((addr = nls[X_NPROC].n_value) == 0) {
		(void)write(2,NPERR,sizeof NPERR) ;
		exit(1) ;
	}
#ifdef CANREAD
	nproc = *( (int*) addr) ;
#else
	(void)lseek(kmem,(long)addr, 0);
	(void)read(kmem, (char*)&nproc, sizeof nproc);
#endif

	users = (struct usrs *) calloc((int)nproc, sizeof(struct usrs)) ;
	tab = (struct usrs **) calloc((int)nproc, sizeof(struct usrs *)) ;

#ifndef CANREAD
	proc = (struct proc *) calloc((int)nproc, sizeof(struct proc)) ;
#endif

	if ((addr = nls[X_PROC].n_value) == 0) {
		(void)write(2,PERR,sizeof PERR) ;
		exit(1) ;
	}
#ifdef CANREAD
	proc = *( (struct proc **) addr) ;
#else
	(void)lseek(kmem,(long)addr,0) ;
	(void)read(kmem, (char*)&addr, sizeof addr) ;
#endif

	for (i=0 ; i<nproc ; i++) {
		users[i].us_pid = -1 ;
	}

	for (;;) {

		/* Read Proc Table */

#ifndef CANREAD
		(void)lseek(kmem,(long)addr,0) ;
		(void)read(kmem, (char*)proc, nproc * (sizeof *proc)) ;
#endif

		ntab = 0 ; idle.us_pctcpu = 1e0 ;

		for (pp=proc, us=users, i=0 ; i<nproc; pp++, i++, us++) {
			if ((pp->p_flag)&SLOAD) {
				tab[ntab++] = us ;
				if (us->us_pid == pp->p_pid) {
					if (us->us_valid) {
						t =   acpu*(pp->p_pctcpu)
					 	    - bcpu*(us->us_opctcpu) ;
						us->us_pctcpu = (t<0) ? 0 : t;
						us->us_opctcpu = pp->p_pctcpu;
					} else {
						us->us_valid++;
						us->us_pctcpu = us->us_opctcpu
						  = pp->p_pctcpu ;
					}
				} else {
					us->us_valid=0;
					us->us_uid = pp->p_uid ;
					us->us_pid = pp->p_pid ;
					us->us_pctcpu = pp->p_pctcpu ;
				}
				if ( (t=us->us_pctcpu) > 0)
					idle.us_pctcpu -= t ;
				if (t<1e-3)
					ntab-- ;
			}
		}

		if (idle.us_pctcpu>1e-3) tab[ntab++] = &idle ;

		/* Sort Index of Users */
		for (i=1 ; i<ntab ; i++) {
			for (j=0; j<i ; j++) {
				if (tab[j]->us_pctcpu < tab[i]->us_pctcpu) {
					us = tab[i] ;
					tab[i] = tab[j] ;
					tab[j] = us ;
				}
			}
		}

		if  (ntab>MXSHOWN)  ntab = MXSHOWN  ;

		for (i=0 ; i<ntab ; i++) {
			us = tab[i] ;
			nxs = NBAR*(us->us_pctcpu) ;
			nxs = (nxs>NBAR) ? NBAR : nxs ;
			for (j=0, p=lin ; j<nxs ; j++ ) *p++ = '#' ;
			for (; j<NBAR ; j++) *p++ = ' ' ;
			*p = '\0' ;

			if (tim>0) {
				if (us->us_pid>=0) {
					printw(
						"%8s %5d %7.3f %s\n",
						names[us->us_uid+1],
						us->us_pid,
						100*us->us_pctcpu,
						lin) ;
				} else {
					printw(
						"%8s   *** %7.3f %s\n",
						names[us->us_uid+1],
						100*us->us_pctcpu,
						lin) ;
				}
			} else {
				if (us->us_pid>=0) {
					printf("%8s %5d %7.3f %s\n",
						names[us->us_uid+1],
						us->us_pid,
						100*us->us_pctcpu,
						lin) ;
				} else {
					printf(
						"%8s   *** %7.3f %s\n",
						names[us->us_uid+1],
						100*us->us_pctcpu,
						lin) ;
				}
			}
		}

		if (tim<=0) exit(0) ;

		for (i=ntab ; i<=ontab ; i++) {	/* clear out dead lines */
			clrtoeol() ;
			printw("\n") ;
		}

		refresh() ;
		ontab = ntab ;
		sleep(tim) ;
		move(0,0) ;
	}
}

void done() {
	endwin() ;
	exit(0) ;
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 display.c
	/bin/echo -n '	'; /bin/ls -ld display.c
fi
exit
--------If this line is not here something is missing----------------
-- 
Bill Sebok			Princeton University, Astrophysics
{allegra,akgua,burl,cbosgd,decvax,ihnp4,noao,princeton,vax135}!astrovax!wls