[net.sources] Top, version 1.8

phil@rice.ARPA (William LeFebvre) (02/21/85)

This message contains a shar file distribution for the newest version of
"top".  "Top" is a program that will display and update information about
a Berkeley Unix (either 4.1 or 4.2) system, including the top cpu using
processes.  It would be similar to continually doing a "ps aux" except
that it doesn't consume half of the cpu to gather the information.  By
default, it will display the top ten processes and update this information
every five seconds.  I have noticed that this requires about 10% of the
cpu.  Of course, the percentage decreases as the time delay increases.

Not having access to a Bell Unix machine, I cannot say how or if this
program will work on such a system.

Just run the shar file through your favorite shell, then read the README.

Enjoy!

                                William LeFebvre
				Department of Computer Science
				Rice University
                                <phil@Rice.arpa>

------------------------- CUT HERE -------------------------
echo 'Start of distribution file top.shar:'
echo 'Extracting README...'
sed 's/^X//' > README << '/'
XThis file contains a few comments about "top".
X
X"top" is a program that will give continual reports about the state of the
Xsystem, including a list of the top cpu using processes.  It requires read
Xaccess to the memory files "/dev/kmem" and "/dev/mem" as well as the system
Ximage "/vmunix".  Some installations have these files protected from general
Xaccess.  These sites would have to install this program in the same way that
Xprograms such as "ps" are installed.
X
XThere are a few things that need to be checked before compiling the program:
X
XThe most important item is the internal hash table size.  This size is
Xdefined in the program with the preprocessor variable "Table_size".  This
Xconstant MUST be larger than the number of lines in the file /etc/passwd.
XIt is advisable that this number be about twice the number of lines, and
Xthat it be a prime number (since it dictates the size of the hash table).
XMake sure this is checked before compilation.
X
XThere are two preprocessor variables that are defined at compile time by
Xthe makefile.  These are "Default_TOPN" and "Default_DELAY".  Their values
Xare the defaults used for the top number of processes to be displayed and
Xthe number of seconds to delay between displays, respectively.  They are
Xset by the Makefile variables "TOPN" and "DELAY", respecitvely.  These
Xconstants are preset as follows:  TOPN=10, DELAY=5.  These can be overriden
Xby either changing the Makefile or by specifying the change on the make
Xcommand line (with something like "make TOPN=15").
X
XBy default, the makefile will make a "top" for a Berkeley 4.2 system.  You
Xcan change the makefile variable "CFLAGS" to make a 4.1 "top" or a Sun 4.2
X"top".  There are instructions in the makefile for doing these things.
X
XThe file "bzero.c" contains a function that will zero a block of memory on
Xa VAX.  This is only needed for Berkeley 4.1, since 4.2 has a bzero
Xdefined in the C run time library.  If you are strange enough to be
Xrunning 4.1 on smoething besides a VAX, you will have to replace this
Xroutine with one that will work on your machine.  If you don't know a
Xquick way to do it, then writing a simple loop will suffice.  "Bzero"
Xtakes two arguments:  a pointer to the buffer to zero, and the number of
Xbytes to zero.
X
XThere are also several parameters in the makefile that control
Xinstallation.  These should be altered to suit the desires and needs of
Xindividual sites.
X
XIf you make a change to "top" that you feel would be beneficial to others
Xwho use this program, or if you find and fix a bug, feel free to send me the
Xchange.
X
XEnjoy!
X
X                                William LeFebvre
X				Department of Computer Science
X				Rice University
X                                ARPANet address: <phil@Rice.arpa>
X
X				U.S. Mail address:
X				    William LeFebvre
X				    P.O. Box 1892
X				    Department of Computer Science
X				    Houston, TX  77251
/
echo 'Extracting Changes...'
sed 's/^X//' > Changes << '/'
XWed Feb 20 1985 - wnl (still 1.8)
X	Put in the ofdef FOUR_ONE statements to make top still compilable
X	on a 4.1 system.  Apparently, there are some users out there that
X	need this functionality.  Oh well.  I don't guarantee any of it,
X	since I can't test it.  Made appropriate changes to README and
X	final installation related changes to Makefile.
X
XSat Feb  2 1985 - wnl (1.8)
X	Removed all the ifdef FOUR_TWO statements and made "top" into a
X	4.2 only program.  If someone really wants to still run it on 4.1,
X	then they can do all the work.  We don't have a 4.1 machine
X	anymore, so I don't even know if the thing still works under 4.1.
X	Cleaned up the Makefile and the README.  Added installation rules
X	to the Makefile, as requested by several sites.  Fixed a very
X	obscure divide-by-zero bug.  Added a second "key" to the qsort
X	comparison function (proc_compar) so that comparisons are based on
X	cpu ticks if the percentages are equal (provided by Jonathon
X	Feiber at Sun).
X
XTue Dec 11 1984 - wnl (1.7)
X	Added the virtual and real memory status line to the header area
X	(provided by Jonathon Feiber at Sun)
X
XTue Nov 20 1984 - wnl (1.6)
X	Added an "exit" if sbrk's fail.  Added changes from Jonathon
X	Feiber at Sun:  ifdef SUN to make top work on Suns (they don't use
X	doubles in the proc structure), register declarations, check for
X	getting a user structure that has disappeared since the proc array
X	was read (it used to die, now it just shows the process as swapped).
X
XTue Nov 13 1984 - wnl (1.5)
X	If the number of displayable processes ("active_procs") was less
X	than the number of requested processes ("topn"), top would
X	segmentation fault.  This bug has been fixed.  Thanks to Prentiss
X	Riddle at ut-sally for pointing out the existence of this bug.
X
XTue Oct 23 1984 - wnl (1.4)
X	Finally fixed the hash table bug that caused processes owned by
X	root to sometimes appear with either no name or a different name
X	that had UID 0 (such as "operator").  Removed all the ifdef DEBUG
X	blocks to make top ready for distribution to the real world.
X
XSun Apr  8 1984 - wnl (still 1.3)
X	Made some slight changes to the display format.  It now looks more
X	aesthetically pleasing.  Added some preprocessor constants so that
X	the two defaults (number of processes and seconds of delay) easier
X	to change.
X
XThu Apr  5 1984 - wnl (1.3)
X	Changed the order in which things are done at initialization time.
X	This way, if an error occurs before starting the main loop, curses
X	will never get started.  Also changed other error handlers so that
X	endwin() is called before any flavor of exit.  Specifying a number
X	of processes that is more than the screen can handle is no longer
X	fatal.  It displays a warning message and pretends the user
X	specified the maximum for the screen.  Finally cured all the TSTP
X	blues (well, almost all).  I removed my TSTP handler and convinced
X	the system to always use the one that curses sets up.  Turns out
X	that "sleep" was stepping all over it during a pause.  So, I don't
X	use sleep anymore.  The only problem that remains with it now is
X	redrawing the old display before updating it after a pause.
X
XTue Apr  3 1984 - wnl (from 1.0 to 1.2)
X	I changed the format of the TIME column from just "seconds" to
X	"minutes:seconds".  I also made pausing work correctly.  Screen
X	redraws with an up to date display.  For compatability with 4.2, I
X	changed the name of the "zero" function to "bzero".  The makefile
X	has been altered to handle versions for 4.1 and 4.2, and README
X	has been updated to reflect these recent changes.
/
echo 'Extracting Makefile...'
sed 's/^X//' > Makefile << '/'
X# Makefile for "top", a top 10 process display for Unix
X
X# installation information:
X#	OWNER	- name (or uid) for the installed executable's owner
X#	GROUP	- group name (or gid) for the installed executable's group
X#	MODE	- mode for the installed executable (should start with a 0)
X#	BINDIR	- directory where the executable should live
X#	MANDIR	- directory where the manual page should live
X#	MAN	- troff macros for manual pages
X#	TROFF	- most appropriate troff command
X
XOWNER = phil
XGROUP = staff
XMODE = 755
XBINDIR = /usr/local
XMANDIR = /usr/man/manl
XMAN = man
XTROFF = troff
X
X# values for two defaults in "top"
X#	TOPN	- default number of processes to display
X#	DELAY	- default delay between updates
X
XTOPN = 10
XDELAY = 5
X
XOBJS =
XTARFILES = README Changes Makefile top.c bzero.c top.1
XCFLAGS = -O
X# To make a version for the SUN, comment out the previous line and
X# uncomment the following line:
X#CFLAGS = -DSUN -O
X# To make a version for 4.1, comment out the last uncommented line and
X# uncomment the following two lines:
X#CFLAGS = -DFOUR_ONE -O
X#OBJS = bzero.o
X
Xtop: top.o $(OBJS)
X	cc $(CFLAGS) -o top top.o $(OBJS) -lcurses -ltermcap -lm
X
Xtop.o: top.c
X	cc -c $(CFLAGS) -DDefault_TOPN=$(TOPN) -DDefault_DELAY=$(DELAY) top.c
X
Xman:
X	nroff -$(MAN) -rN$(TOPN) -rD$(DELAY) top.1 | cat -s >top.cat
X
Xtroff:
X	$(TROFF) -man -rN$(TOPN) -rD$(DELAY) top.1
X
Xtar:
X	tar cvf top.tar $(TARFILES)
X
Xclean:
X	rm -f *.o top
X
Xinstall: top
X	install -s -o $(OWNER) -m $(MODE) -g $(GROUP) top $(BINDIR)
X	install -c top.1 $(MANDIR)
/
echo 'Extracting top.c...'
sed 's/^X//' > top.c << '/'
Xchar *copyright = "Top, version 1.8, copyright (c) 1984, William LeFebvre";
X
X/*
X *  Top users display for Berkeley Unix
X *  Version 1.8
X *
X *  This program may be freely redistributed to other Unix sites, but this
X *  entire comment MUST remain intact.
X *
X *  Copyright (c) 1984, William LeFebvre, Rice University
X *
X *  This program is designed to run on either Berkeley 4.1 or 4.2 Unix.
X *  Compile with the preprocessor constant "FOUR_ONE" set to get an
X *  executable that will run on Berkeley 4.1 Unix.
X *
X *  The Sun kernel uses scaled integers instead of floating point so compile
X *  with the preprocessor variable "SUN" to get an executable that will run
X *  on Sun Unix version 1.1 or later.
X *
X *  Fixes and enhancements since version 1.5:
X *
X *  Jonathon Feiber at sun:
X *	added "#ifdef SUN" code to make top work on a Sun,
X *	fixed race bug in getkval for getting user structure,
X *	efficiency improvements:  added register variables and
X *	removed the function hashit
X *
X *	added real and virtual memory status line
X *
X *	added second "key" to the qsort comparisn function "proc_compar"
X *	which sorts by on cpu ticks if percentage is equal
X */
X
X#include <curses.h>
X#include <stdio.h>
X#include <pwd.h>
X#include <nlist.h>
X#include <signal.h>
X#ifdef FOUR_ONE
X#include <sys/pte.h>
X#else
X#include <machine/pte.h>
X#endif
X#include <sys/param.h>
X#include <sys/dir.h>
X#include <sys/user.h>
X#include <sys/proc.h>
X#include <sys/dk.h>
X#include <sys/vm.h>
X
X/* obvious file names */
X
X#define VMUNIX	"/vmunix"
X#define KMEM	"/dev/kmem"
X#define MEM	"/dev/mem"
X
X/*
X *  The number of users in /etc/passwd CANNOT be greater than Table_size!
X *  If you get the message "table overflow: too many users", then increase
X *  Table_size.  Since this is the size of a hash table, things will work
X *  best if it is a prime number that is about twice the number of users.
X */
X
X#define Table_size	421
X
X/* Number of lines of header information on the standard screen */
X# define Header_lines	6
X
X/* wish list for kernel symbols */
X
Xstruct nlist nlst[] = {
X    { "_avenrun" },
X#define X_AVENRUN	0
X    { "_ccpu" },
X#define X_CCPU		1
X    { "_cp_time" },
X#define X_CP_TIME	2
X    { "_hz" },
X#define X_HZ		3
X    { "_mpid" },
X#define X_MPID		4
X    { "_nproc" },
X#define X_NPROC		5
X    { "_proc" },
X#define X_PROC		6
X    { "_total" },
X#define X_TOTAL               7
X    { 0 },
X};
X
X/* convert kernel page to K bytes */
X#define pagetok(x) (((x) << PGSHIFT) / 1024)
X
X/* useful externals */
Xextern int errno;
Xextern char *sys_errlist[];
Xextern int LINES;
X
X/* signal handling routines */
Xint leave();
Xint onalrm();
X
X/* file descriptors for memory devices */
Xint kmem;
Xint mem;
X
Xint nproc;
Xint mpid;
X
X/* kernel "hz" variable -- clock rate */
Xlong hz;
X
X/* All this is to calculate the cpu state percentages */
X
Xlong cp_time[CPUSTATES];
Xlong cp_old[CPUSTATES];
Xlong cp_change[CPUSTATES];
Xlong total_change;
Xlong cp_time_offset;
Xlong mpid_offset;
Xlong avenrun_offset;
Xlong total_offset;
X
X#ifdef SUN
Xlong ccpu;
Xlong avenrun[3];
X#else
Xdouble ccpu;
Xdouble avenrun[3];
X#endif
Xdouble logcpu;
X
Xstruct vmtotal total;
X
Xstruct proc *proc;
Xstruct proc *pbase;
Xint bytes;
X
Xstruct user u;
X
X/* Verbose process state names */
X
Xchar *state_name[] = {
X    "", "sleeping", "ABANDONED", "running", "starting", "zombie", "stopped"
X};
X
X/* process state names for the "STATE" column of the display */
X
Xchar *state_abbrev[] = {
X    "", "sleep", "WAIT", "run", "idl", "zomb", "stop"
X};
X
X/* cpu state names for percentages */
X
Xchar *cpu_state[] = {
X    "user", "nice", "system", "idle"
X};
X
X/* routines that don't return int */
X
Xstruct passwd *getpwent();
Xchar *username();
Xchar *ctime();
Xchar *rindex();
X
Xint proc_compar();
Xdouble log();
Xdouble exp();
X
Xmain(argc, argv)
X
Xint  argc;
Xchar *argv[];
X
X{
X    register struct proc *pp;
X    register struct proc **prefp;
X    register int i;
X    register int active_procs;
X    register int change;
X    register long cputime;
X
X    struct proc **pref;
X    int total_procs;
X    int proc_brkdn[7];
X    int topn = Default_TOPN;
X    int delay = Default_DELAY;
X    long curr_time;
X    long time();
X    char do_cpu = 0;
X    char *myname = "top";
X    double pctcpu;
X
X    /* get our name */
X    if (argc > 0)
X    {
X	if ((myname = rindex(argv[0], '/')) == 0)
X	{
X	    myname = argv[0];
X	}
X	else
X	{
X	    myname++;
X	}
X    }
X
X    /* check for time delay option */
X    if (argc > 1 && argv[1][0] == '-')
X    {
X	if (argv[1][1] != 's')
X	{
X	    fprintf(stderr, "Usage: %s [-sn] [number]\n", myname);
X	    exit(1);
X	}
X	delay = atoi(&(argv[1][2]));
X	argc--;
X	argv++;
X    }
X
X    /* get count of top processes to display (if any) */
X    if (argc > 1)
X    {
X	topn = atoi(argv[1]);
X    }
X
X    /* open kmem and mem */
X    if ((kmem = open(KMEM, 0)) < 0)
X    {
X	perror(KMEM);
X	exit(1);
X    }
X    if ((mem = open(MEM, 0)) < 0)
X    {
X	perror(MEM);
X	exit(1);
X    }
X
X    /* get the list of symbols we want to access in the kernel */
X    errno = 0;
X    nlist(VMUNIX, nlst);
X    if (nlst[0].n_type == 0)
X    {
X	fprintf(stderr, "%s: can't nlist image\n", VMUNIX);
X	exit(1);
X    }
X
X    /* get the symbol values out of kmem */
X    getkval(nlst[X_PROC].n_value,  &proc,  sizeof(int), nlst[X_PROC].n_name);
X    getkval(nlst[X_NPROC].n_value, &nproc, sizeof(int), nlst[X_NPROC].n_name);
X    getkval(nlst[X_CCPU].n_value,  &ccpu,  sizeof(int), nlst[X_CCPU].n_name);
X    getkval(nlst[X_HZ].n_value,    &hz,    sizeof(int), nlst[X_HZ].n_name);
X
X    /* some calculations we use later */
X
X    cp_time_offset = nlst[X_CP_TIME].n_value;
X    mpid_offset = nlst[X_MPID].n_value;
X    avenrun_offset = nlst[X_AVENRUN].n_value;
X    total_offset = nlst[X_TOTAL].n_value;
X
X    /* this is used in calculating WCPU -- calculate it ahead of time */
X#ifdef SUN
X    logcpu = log((double)ccpu / FSCALE);
X#else
X    logcpu = log(ccpu);
X#endif
X
X    /* allocate space for proc structure array and array of pointers */
X    bytes = nproc * sizeof(struct proc);
X    pbase = (struct proc *)sbrk(bytes);
X    pref  = (struct proc **)sbrk(nproc * sizeof(struct proc *));
X
X    /* Just in case ... */
X    if (pbase == (struct proc *)NULL || pref == (struct proc **)NULL)
X    {
X	fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
X	exit(1);
X    }
X
X    /* initialize the hashing stuff */
X    init_hash();
X
X    /* initialize curses and screen (last) */
X    initscr();
X    erase();
X    clear();
X    refresh();
X
X    /* setup signal handlers */
X    signal(SIGINT, leave);
X    signal(SIGQUIT, leave);
X
X    /* can only display (LINES - Header_lines) processes */
X    if (topn > LINES - Header_lines)
X    {
X	printw("Warning: this terminal can only display %d processes...\n",
X	    LINES - Header_lines);
X	refresh();
X	sleep(2);
X	topn = LINES - Header_lines;
X	clear();
X    }
X
X    /* main loop ... */
X
X    while (1)		/* while(1)'s are forever ... */
X    {
X	/* read all the proc structures in one fell swoop */
X	getkval(proc, pbase, bytes, "proc array");
X
X	/* get the cp_time array */
X	getkval(cp_time_offset, cp_time, sizeof(cp_time), "_cp_time");
X
X	/* get load average array */
X	getkval(avenrun_offset, avenrun, sizeof(avenrun), "_avenrun");
X
X	/* get mpid -- process id of last process */
X	getkval(mpid_offset, &mpid, sizeof(mpid), "_mpid");
X
X	/* get total -- systemwide main memory usage structure */
X	getkval(total_offset, &total, sizeof(total), "_total");
X
X	/* count up process states and get pointers to interesting procs */
X	total_procs = 0;
X	active_procs = 0;
X	bzero(proc_brkdn, sizeof(proc_brkdn));
X	prefp = pref;
X	for (pp = pbase, i = 0; i < nproc; pp++, i++)
X	{
X	    /* place pointers to each valid proc structure in pref[] */
X	    /* (processes with SSYS set are system processes) */
X	    if (pp->p_pid != 0 && pp->p_stat != 0 && (pp->p_flag & SSYS) == 0)
X	    {
X		total_procs++;
X		proc_brkdn[pp->p_stat]++;
X		if (pp->p_stat != SZOMB)
X		{
X		    *prefp++ = pp;
X		    active_procs++;
X		}
X	    }
X	}
X
X	/* display the load averages */
X	printw("last pid: %d;  load averages", mpid);
X	{
X	    for (i = 0; i < 3; i++)
X	    {
X		printw("%c %4.2f",
X		    i == 0 ? ':' : ',',
X#ifdef SUN
X		    (double)avenrun[i] / FSCALE);
X#else
X		    avenrun[i]);
X#endif
X	    }
X	}
X
X	/*
X	 *  Display the current time.
X	 *  "ctime" always returns a string that looks like this:
X	 *  
X	 *	Sun Sep 16 01:03:52 1973
X	 *      012345678901234567890123
X	 *	          1         2
X	 *
X	 *  We want indices 11 thru 18 (length 8).
X	 */
X
X	curr_time = time(0);
X	move(0, 79-8);
X	printw("%-8.8s\n", &(ctime(&curr_time)[11]));
X
X	/* display process state breakdown */
X	printw("%d processes", total_procs);
X	for (i = 1; i < 7; i++)
X	{
X	    if (proc_brkdn[i] != 0)
X	    {
X		printw("%c %d %s%s",
X			i == 1 ? ':' : ',',
X			proc_brkdn[i],
X			state_name[i],
X			(i == SZOMB) && (proc_brkdn[i] > 1) ? "s" : "");
X	    }
X	}
X
X	/* calculate percentage time in each cpu state */
X	printw("\nCpu states: ");
X	if (do_cpu)	/* but not the first time */
X	{
X	    total_change = 0;
X	    for (i = 0; i < CPUSTATES; i++)
X	    {
X		/* calculate changes for each state and overall change */
X		if (cp_time[i] < cp_old[i])
X		{
X		    /* this only happens when the counter wraps */
X		    change = (int)
X			((unsigned long)cp_time[i]-(unsigned long)cp_old[i]);
X		}
X		else
X		{
X		    change = cp_time[i] - cp_old[i];
X		}
X		total_change += (cp_change[i] = change);
X	    }
X	    for (i = 0; i < CPUSTATES; i++)
X	    {
X		printw("%s%4.1f%% %s",
X			i == 0 ? "" : ", ",
X			((float)cp_change[i] / (float)total_change) * 100.0,
X			cpu_state[i]);
X		cp_old[i] = cp_time[i];
X	    }
X	}
X	else
X	{
X	    /* we'll do it next time */
X	    for (i = 0; i < CPUSTATES; i++)
X	    {
X		printw("%s      %s",
X			i == 0 ? "" : ", ",
X			cpu_state[i]);
X		cp_old[i] = 0;
X	    }
X	    do_cpu = 1;
X	}
X	printw("\n");
X
X	/* display main memory statistics */
X	printw("Memory: %4dK (%4dK) real, %4dK (%4dK) virtual, %4dK free\n",
X	pagetok(total.t_rm), pagetok(total.t_arm),
X	pagetok(total.t_vm), pagetok(total.t_avm),
X	pagetok(total.t_free));
X
X	if (topn > 0)
X	{
X	    printw("\n  PID USERNAME PRI NICE   SIZE   RES STATE   TIME   WCPU    CPU COMMAND\n");
X    
X	    /* sort by cpu percentage (pctcpu) */
X	    qsort(pref, active_procs, sizeof(struct proc *), proc_compar);
X    
X	    /* now, show the top whatever */
X	    if (active_procs > topn)
X	    {
X		/* adjust for a lack of processes */
X		active_procs = topn;
X	    }
X	    for (prefp = pref, i = 0; i < active_procs; prefp++, i++)
X	    {
X		pp = *prefp;
X		if (getu(pp) == -1)
X		{
X		    strcpy(u.u_comm, "<swapped>");
X		    cputime = 0;
X		}
X		else
X		{
X		    cputime = 
X#ifdef FOUR_ONE
X			(int)((float)(u.u_vm.vm_utime + u.u_vm.vm_stime)/hz);
X#else
X			(u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec);
X#endif
X		}
X#ifdef SUN
X		pctcpu = (double)pp->p_pctcpu / FSCALE;
X#else
X		pctcpu = pp->p_pctcpu;
X#endif
X		printw("%5d %-8.8s %3d %4d%6dK %4dK %-5s%4d:%02d %5.2f%% %5.2f%% %-14.14s\n",
X		    pp->p_pid,
X		    username(pp->p_uid),
X		    pp->p_pri - PZERO,
X		    pp->p_nice - NZERO,
X		    (pp->p_tsize + pp->p_dsize + pp->p_ssize) >> 1,
X		    pp->p_rssize >> 1,
X		    state_abbrev[pp->p_stat],
X		    cputime / 60l,
X		    cputime % 60l,
X		    pp->p_time == 0 ? 0.0 :
X			(100.0 * pctcpu / (1.0 - exp(pp->p_time * logcpu))),
X			(100.0 * pctcpu),
X		    u.u_comm);
X	    }
X	}
X	refresh();
X
X	/* wait ... */
X	signal(SIGALRM, onalrm);
X	alarm(delay);
X	pause();
X
X	/* clear for new display */
X	erase();
X    }
X}
X
X/*
X *  signal handlers
X */
X
Xleave()			/* exit under normal conditions -- INT handler */
X
X{
X    move(LINES - 1, 0);
X    refresh();
X    endwin();
X    exit(0);
X}
X
Xquit(status)		/* exit under duress */
X
Xint status;
X
X{
X    endwin();
X    exit(status);
X}
X
Xonalrm()
X
X{
X    return(0);
X}
X
X/*
X *  comparison function for "qsort"
X *  Do first order sort based on cpu percentage computed by kernel and
X *  second order sort based on number of cpu ticks the process has taken.
X */
X 
Xproc_compar(p1, p2)
X
Xregister struct proc **p1;
Xregister struct proc **p2;
X
X{
X    if ((*p1)->p_pctcpu < (*p2)->p_pctcpu)
X    {
X        return(1);
X    }   
X    else if ((*p1)->p_pctcpu == (*p2)->p_pctcpu)
X    {
X        if ((*p1)->p_cpticks < (*p2)->p_cpticks)
X        {
X            return(1);
X        }
X        else
X        {
X            return(-1);
X        }
X    }
X    else
X    {
X        return(-1);
X    }
X}
X
X/*
X *  These routines handle uid to username mapping.
X *  They use a hashing table scheme to reduce reading overhead.
X */
X
Xstruct hash_el {
X    int  uid;
X    char name[8];
X};
X
X#define    H_empty	-1
X
X/* simple minded hashing function */
X#define    hashit(i)	((i) % Table_size)
X
Xstruct hash_el hash_table[Table_size];
X
Xinit_hash()
X
X{
X    register int i;
X    register struct hash_el *h;
X
X    for (h = hash_table, i = 0; i < Table_size; h++, i++)
X    {
X	h->uid = H_empty;
X    }
X
X    setpwent();
X}
X
Xchar *username(uid)
X
Xregister int uid;
X
X{
X    register int index;
X    register int found;
X    register char *name;
X
X    /* This is incredibly naive, but it'll probably get changed anyway */
X    index = hashit(uid);
X    while ((found = hash_table[index].uid) != uid)
X    {
X	if (found == H_empty)
X	{
X	    /* not here -- get it out of passwd */
X	    index = get_user(uid);
X	    break;		/* out of while */
X	}
X	index = index++ % Table_size;
X    }
X    return(hash_table[index].name);
X}
X
Xenter_user(uid, name)
X
Xregister int  uid;
Xregister char *name;
X
X{
X    register int length;
X    register int index;
X    register int try;
X    static int uid_count = 0;
X
X    /* avoid table overflow -- insure at least one empty slot */
X    if (++uid_count >= Table_size)
X    {
X	fprintf(stderr, "table overflow: too many users\n");
X	quit(1);
X    }
X
X    index = hashit(uid);
X    while ((try = hash_table[index].uid) != H_empty)
X    {
X	if (try == uid)
X	{
X	    return(index);
X	}
X	index = index++ % Table_size;
X    }
X    hash_table[index].uid = uid;
X    strncpy(hash_table[index].name, name, 8);
X    return(index);
X}
X
Xget_user(uid)
X
Xregister int uid;
X
X{
X    struct passwd *pwd;
X    static char buff[20];
X    register int last_index;
X
X    while ((pwd = getpwent()) != NULL)
X    {
X	last_index = enter_user(pwd->pw_uid, pwd->pw_name);
X	if (pwd->pw_uid == uid)
X	{
X	    return(last_index);
X	}
X    }
X    sprintf(buff, "%d", uid);
X    return(enter_user(uid, buff));
X}
X
X/*
X *  All of this stuff gets things out of the memory files.
X */
X
X/*
X *  Get the user structure for the process who's proc structure is in p.  The
X *  user structure is returned in u.
X */
Xgetu(p)
X
Xregister struct proc *p;
X
X{
X    struct pte uptes[UPAGES];
X    register caddr_t upage;
X    register struct pte *pte;
X    register nbytes, n;
X
X    /*
X     *  Check if the process is currently loaded or swapped out.  The way we
X     *  get the u area is totally different for the two cases.  For this
X     *  application, we just don't bother if the process is swapped out.
X     */
X    if ((p->p_flag & SLOAD) == 0)
X    {
X	return(-1);
X    }
X
X    /*
X     *  Process is currently in memory, we hope!
X     */
X    if (!getkval(p->p_addr, uptes, sizeof(uptes), "p->p_addr"))
X    {
X	/* we can't seem to get to it, so pretend it's swapped out */
X	return(-1);
X    } 
X    upage = (caddr_t)&u;
X    pte = uptes;
X    for (nbytes = sizeof(u); nbytes > 0; nbytes -= NBPG)
X    {
X    	lseek(mem, pte++->pg_pfnum * NBPG, 0);
X	n = MIN(nbytes, NBPG);
X	if (read(mem, upage, n) != n)
X	{
X	    /* we can't seem to get to it, so pretend it's swapped out */
X	    return(-1);
X	}
X	upage += n;
X    }
X    return(0);
X}
X
X/*
X *  get the value of something from /dev/kmem
X */
X
Xgetkval(offset, ptr, size, refstr)
X
Xlong offset;
Xint *ptr;
Xint size;
Xchar *refstr;
X
X{
X    if (lseek(kmem, offset, 0) == -1)
X    {
X	fprintf(stderr, "%s: lseek to %s: %s\n",
X	    KMEM, refstr, sys_errlist[errno]);
X	quit(1);
X    }
X    if (read(kmem, ptr, size) == -1)
X    {
X	if (strcmp(refstr, "p->p_addr") == 0) 
X	{
X	    /* we lost the race with the kernel, process isn't in memory */
X	    return(0);
X	} 
X	else 
X	{
X	    fprintf(stderr, "%s: reading %s: %s\n",
X		KMEM, refstr, sys_errlist[errno]);
X	    quit(1);
X	}
X    }
X    return(1);
X}
X
X/*
X *  outc - real subroutine that writes a character; for termcap
X */
X
Xoutc(ch)
X
Xregister char ch;
X
X{
X    putchar(ch);
X}
/
echo 'Extracting bzero.c...'
sed 's/^X//' > bzero.c << '/'
X/*
X *  Fast, sleazy, and ugly zero function.
X *
X *  Note that this will only work on a VAX, but it is real easy to write a
X *  similar function for whatever machine you may need.  If nothing else,
X *  just a simple loop in C will suffice.
X *
X *  Dave Johnson, Rice University.
X *
X *  Enhanced by William LeFebvre of Rice University to handle zeroing more
X *  than 64K.
X */
X
X# define   K	1024
X
X/*
X *  bzero(memory, amount) - set "amount" bytes starting at "memory" to the
X *			    value 0.
X */
X
Xbzero(memory, amount)
X
Xchar *memory;
Xint  amount;
X
X{
X    while (amount >= 64*K)
X    {
X	_bzero64(memory, 64*K-1);
X	memory += 64*K-1;
X	amount -= 64*K-1;
X    }
X    _bzero64(memory, amount);
X}
X
X_bzero64(memory, amount)
X
Xchar *memory;
Xint  amount;
X
X{
X    asm("	movc5	$0, (sp), $0, 8(ap), *4(ap)");
X}
/
echo 'Extracting top.1...'
sed 's/^X//' > top.1 << '/'
X.TH TOP 1 Local
X.UC 4
X.SH NAME
Xtop \- display and update information about the top cpu processes
X.SH SYNOPSIS
X.B top
X[
X.BI \-s time
X] [
X.I number
X]
X.SH DESCRIPTION
X.\" This defines appropriate quote strings for nroff and troff
X.ds lq \&"
X.ds rq \&"
X.if t .ds lq ``
X.if t .ds rq ''
X.\" Just in case the command line didn't set these number registers...
X.if !\nN .nr N 10
X.if !\nD .nr D 5
X.I Top
Xdisplays the top \nN processes on the system and periodically
Xupdates this information.  It uses raw cpu percentage
Xto determine the top processes.  The
X.IR curses (3)
Xpackage is used to do semi-optimal screen updating.  If
X.I number
Xis given, then the top
X.I number
Xprocesses will be displayed instead of the default amount of \nN.
XThe
X.B \-s
Xoption sets the delay between screen updates to
X.I time
Xseconds.  The default delay between updates is \nD seconds.
X.PP
XThe top few lines of the display show general information
Xabout the state of the system, including
Xthe last process id assigned to a process,
Xthe three load averages,
Xthe current time,
Xthe number of existing processes,
Xthe number of processes in each state
X(sleeping, ABANDONED, running, starting, zombies, and stopped),
Xand a percentage of time spent in each of the processor states
X(user, nice, system, and idle).
XIt also includes the amount of virtual and real memory in use
X(with the amount of memory considered \*(lqactive\*(rq in parentheses) and
Xthe amount of free memory.
X.PP
XThe remainder of the screen displays information about individual
Xprocesses.  This display is similar in spirit to
X.IR ps (1)
Xbut it is not exactly the same.  PID is the process id, USERNAME is the name
Xof the process's owner, PRI is the current priority of the process, NICE is
Xthe nice amount (in the range \-20 to 20), SIZE is the total size of the
Xprocess (text, data, and stack), RES is the current amount of resident
Xmemory (both SIZE and RES are given in kilobytes), STATE is the current
Xstate (one of \*(lqsleep\*(rq, \*(lqWAIT\*(rq, \*(lqrun\*(rq,
X\*(lqidl\*(rq, \*(lqzomb\*(rq, or \*(lqstop\*(rq), TIME is
Xthe number of system and user
Xcpu seconds that the process has used, WCPU is the weighted cpu percentage
X(this is the same value that
X.IR ps (1)
Xdisplays as CPU), CPU is the raw percentage and is the field that is sorted
Xto determine the order of the processes, and COMMAND is the name of the 
Xcommand that the process is currently running (if the process is swapped
Xout, this column is marked \*(lq<swapped>\*(rq).
X.SH NOTES
XThe \*(lqABANDONED\*(rq state (known in the kernel as \*(lqSWAIT\*(rq) was
Xabandoned, thus the name.  A process should never end up in this state.
X.SH AUTHOR
XWilliam LeFebvre, Rice University graduate student
X.SH FILES
X.DT
X/dev/kmem		kernel memory
X.br
X/dev/mem		physical memory
X.br
X/vmunix 		system image
X.SH BUGS
XThe command name for swapped processes should be tracked down, but this
Xwould make the program run slower.
X.PP
XAs with
X.IR ps (1),
Xthings can change while
X.I top
Xis collecting information for an update.  The picture it gives is only a
Xclose approximation to reality.
X.SH "SEE ALSO"
Xps(1), mem(4)
/
echo 'Distribution file top.shar complete.'