[comp.sources.misc] v05i066: Program to Display Memory Allocation in System V

mjy@sdti.sdti.com (Michael J. Young) (12/03/88)

Posting-number: Volume 5, Issue 66
Submitted-by: "Michael J. Young" <mjy@sdti.sdti.com>
Archive-name: mapmem

Mapmem displays a visual map of physical memory and swap device allocation
for System V Unix.  The map is updated at 1 second intervals, providing a
real-time view of system memory utilization.

I'm sure there are lots of utilities out there that do things like this,
but I don't have them here.  I hope someone else finds it useful as well.

Please send bug reports/fixes, etc., to me.


#! /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 archive 1 (of 1)."
# Contents:  Makefile README mapmem.8 mapmem.c
# Wrapped by mjy@sdti on Wed Nov 30 16:04:20 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f Makefile -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Makefile\"
else
echo shar: Extracting \"Makefile\" \(733 characters\)
sed "s/^X//" >Makefile <<'END_OF_Makefile'
X## @(#)$Id: Makefile, V1.1.1.3 88/11/30 16:03:44 $
X##
X## MAPMEM makefile
X## Version  : 1.1.1.3 - 88/11/30 16:03:44
X##
X
X## edit as appropriate...
XCFLAGS = -g -O
XLDFLAGS =
XLIBFLAGS = -lcurses
X
X## Directory in which executable should be placed:
XDESTDIR = /usr/local/bin
X
X## Directory in which man page should be placed:
XMANDIR = /usr/man/l_man/man8
X
X## MAPMEM must have read access to /dev/kmem.  Therefore, it must be
X## setgid to some group with the appropriate privilege.
XGRPID = sys
X
Xmapmem : mapmem.c
X	$(CC) $(LDFLAGS) $(CFLAGS) -o mapmem mapmem.c $(LIBFLAGS)
X
Xinstall: mapmem
X	cp mapmem $(DESTDIR)/mapmem
X	chgrp $(GRPID) $(DESTDIR)/mapmem
X	chmod g+s $(DESTDIR)/mapmem
X	cp mapmem.8 $(MANDIR)/mapmem.8
X	chmod 444 $(MANDIR)/mapmem.8
END_OF_Makefile
if test 733 -ne `wc -c <Makefile`; then
    echo shar: \"Makefile\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f README -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"README\"
else
echo shar: Extracting \"README\" \(501 characters\)
sed "s/^X//" >README <<'END_OF_README'
XMAPMEM displays a visual map of physical memory and swap device allocations
Xfor a swapping (i.e., non-paged) Unix system.  The map is updated at 1 second
Xintervals, providing a real-time indication of system memory utilization.
X
XTo install, simply edit the makefile and type 'make'.  Typing 'make install'
Xwill install the executable with the appropriate privileges.
X
XMAPMEM requires read access to /dev/kmem, and must therefore be run as
Xsetgid to some group with the appropriate access (e.g., sys).
END_OF_README
if test 501 -ne `wc -c <README`; then
    echo shar: \"README\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f mapmem.8 -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"mapmem.8\"
else
echo shar: Extracting \"mapmem.8\" \(943 characters\)
sed "s/^X//" >mapmem.8 <<'END_OF_mapmem.8'
X.\" @(#)$ mapmem.8, Version 1.1.1.1 - 88/11/28 18:16:51
X.TH MAPMEM 8
X.ad b
X.SH NAME
Xmapmem \- display map of core and swap allocation.
X.SH SYNOPSIS
X.B mapmem
X[
X.B \-d #
X] [
X.B \-vh?
X]
X.SH DESCRIPTION
X.I Mapmem
Xdisplays a visual map of physical memory and swap device
Xallocation of a swapping (i.e., non-paged) Unix system.  The map display is
Xupdated at periodic intervals, providing a real-time indication of system
Xmemory utilization.
X.I Mapmem
Xuses
X.I curses(3x)
Xfor its screen output, based on the \fBTERM\fR environment variable.
X.SH OPTIONS
X.IP \fB-d#\fR
XDelay '\fB#\fR' seconds between updates.  Defaults to 1 second.
X.IP \fB-h\fR
XDisplay a short usage message.
X.IP \fB-v\fR
XDisplay version and patch level of
X.I mapmem .
X.IP \fB-?\fR
XSame as \fB-h\fR.
X.SH BUGS
XProbably.
X.SH "SEE ALSO"
X.I curses(3x) ,
X.I terminfo(4)
X.SH AUTHOR
XMichael J. Young
X.br
Xharvard!sdti!mjy
X.br
XInternet : mjy@sdti.SDTI.COM
X.SH VERSION
X1.1 - 88/11/28 18:16:51
END_OF_mapmem.8
if test 943 -ne `wc -c <mapmem.8`; then
    echo shar: \"mapmem.8\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f mapmem.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"mapmem.c\"
else
echo shar: Extracting \"mapmem.c\" \(12856 characters\)
sed "s/^X//" >mapmem.c <<'END_OF_mapmem.c'
Xstatic char sccsid[] = "@(#)$Id: mapmem.c, V1.1.1.2 88/11/28 18:16:45 $";
X
X/*
X * map.c - display kernel allocation map information
X * Version : 1.1.1.2 - 88/11/28 18:16:45
X * Author   : Michael J. Young
X * USmail   : Software Development Technologies, Inc.
X *            375 Dutton Rd
X *            Sudbury MA 01776
X * UUCP     : harvard!sdti!mjy
X * Internet : mjy@sdti.SDTI.COM
X *
X * =========================================================================
X * Copyright (C) 1988, Michael J. Young.
X * Permission is hereby granted to copy and distribute this program for
X * non-commercial purposes, provided that this notice is not removed.
X *
X * This program is being provided "as is", with no warrantee as to safety or
X * accuracy of results.
X *
X * Please send bug reports and enhancements to me at the above address.
X * =========================================================================
X */
X
X/*
X * Modification History:
X *
X * Wed Nov 23 09:39:01 EST 1988 - M. Young (mjy@sdti.SDTI.COM),
X *    Originated.
X */
X
X#define patch_level 0
X
X#include <nlist.h>
X#include <stdio.h>
X#include <sys/map.h>
X#include <sys/param.h>
X#include <fcntl.h>
X#include <curses.h>
X#include <sys/types.h>
X#include <time.h>
X#include <varargs.h>
X#include <signal.h>
X
X/*
X * sizes of the two major allocation maps maintained by the kernel.  These
X * values are actually contained in the linkkit config.h.  If that file is
X * not available, they must be set here.
X */
X#ifdef	SMAPSIZ
X# define SMAPSIZE	SMAPSIZ
X#else
X# define SMAPSIZE  75
X#endif
X
X#ifdef	CMAPSIZ
X# define CMAPSIZE	CMAPSIZ
X#else
X# define	CMAPSIZE 150
X#endif
X
X/*
X * The following definitions specify the dimensions of the display map.
X * Change to suit your tastes.
X */
X#define	MAX_ROWS	20
X#define	MAX_COLUMNS	64
X#define MAX_ENTRIES	(MAX_ROWS*MAX_COLUMNS)
X
X/*
X * the following defines are used to index the namelist array.  Their
X * definitions must be consistent with the order of the namelist array.
X */
X#define CORE_MAP	0
X#define	SWAP_MAP 	1
X#define MAX_MEM 	2
X#define	N_SWAP 		3
X#define	PHYS_MEM	4
X
X/*
X * namelist array : contains a list of names to be extracted from the kernel
X * image using the nlist function.
X */
Xstruct nlist namelist[] = {
X	{ "coremap", 0, 0, 0, 0, 0 },
X	{ "swapmap", 0, 0, 0, 0, 0 },
X	{ "maxmem",  0, 0, 0, 0, 0 },
X	{ "nswap",   0, 0, 0, 0, 0 },
X	{ "physmem", 0, 0, 0, 0, 0 },
X	{ "",        0, 0, 0, 0, 0 }
X};
X
Xstruct map smapbf[SMAPSIZE];		/* local copy of swap allocation map */
Xstruct map cmapbf[CMAPSIZE];		/* local copy of core allocation map */
Xint kmaxmem = 0;			/* local copy of maxmem */
Xint kphysmem = 0;			/* local copy of physmem */
Xint knswap = 0;				/* local copy of nswap */
X
Xint kmem = 0;				/* file handle for kernel memory */
Xint delay = 1;				/* seconds between screen updates */
X
Xchar dmap[MAX_ENTRIES] = 0;		/* display map */
X
Xint swap_scale = 1;			/* scale factor for swap map display */
Xint core_scale = 1;			/* scale factor for core map display */
X
X/*
X * various useful definitions
X */
Xtypedef int boolean;
X
X/*#define	FALSE	0		/* defined in curses.h */
X/*#define	TRUE	1		/* defined in curses.h */
X
X/*
X * various function declarations
X */
Xextern long lseek();
Xextern int read();
Xextern time_t time();
X
X/*
X * interface to system error messages
X */
Xchar *prog_name = NULL;
X
Xextern char *sys_errlist[];
Xextern int sys_nerr;
Xextern int errno;
X
X/*
X * error : performs a function similar to perror(3), but supports variable
X * argument lists.  Prints out a formatted error string to stderr, followed if
X * possible by an appropriate system error message.  Control is then
X * returned to the system with an error status.  This function does not
X * return.
X */
X/* VARARGS0 */
Xvoid error (va_alist)
Xva_dcl                            /* varargs */
X{
X    int err;                      /* 1st arg - error number */
X    char *str;                    /* 2nd arg - error format string */
X    va_list args;
X    va_start(args);
X    err = va_arg (args, int);
X    str = va_arg (args, char *);
X    fprintf (stderr, "%s: ", prog_name);
X    vfprintf (stderr, str, args);
X    if (err != 0){
X	if (err <= sys_nerr && err > 0)
X            fprintf (stderr, " : %s\n", sys_errlist[err]);
X        else
X            fprintf (stderr, " : unknown error : %d\n", err);
X    }
X    va_end(args);
X    endwin();
X    exit(1);
X}
X
Xvoid version (){
X	printf ("\nKernel Allocation Map Display\n");
X	printf ("Version : 1.1 - 88/11/28 18:16:45, Patch Level %d\n", patch_level);
X}
X
Xvoid usage(){
X	version();
X	printf ("Usage : map [-[?h]] [-d#]\n\n");
X	printf ("\t-d#\tDelay '#' seconds between screen updates.  Defaults\n");
X	printf ("\t\tto 1 second.\n");
X	printf ("\t-h\tDisplays this message.\n");
X	printf ("\t-v\tDisplays version and patch level of program.\n");
X	printf ("\t-?\tDisplays this message.\n");
X}
X
X/*
X * get_nlist : fetches the required namelist from the kernel image
X */
Xvoid get_nlist(){
X	if (nlist ("/unix", namelist) != 0){
X		error (errno, "could not get namelist");
X	}
X}
X
X/*
X * open_kmem : opens kernel memory
X */
Xvoid open_kmem(){
X
X	/*
X	 * open kernel memory
X	 */
X	if ((kmem=open ("/dev/kmem", O_RDONLY)) == -1){
X		error (errno, "could not open /dev/kmem");
X	}
X}
X
X/*
X * init_kmem : reads kernel memory to initialize various static values
X * that are important for future processing.
X */
Xvoid init_kmem (){
X	/*
X	 * fetch maxmem (maximum free memory available to a process)
X	 */
X	if (lseek (kmem, namelist[MAX_MEM].n_value, 0) != namelist[MAX_MEM].n_value){
X		error (errno, "could not seek maxmem");
X	}
X	if (read (kmem, &kmaxmem, sizeof (int)) != sizeof (int)){
X		error (errno, "could not read maxmem");
X	}
X
X	/*
X	 * fetch physmem (maximum physical memory present in the system)
X	 */
X	if (lseek (kmem, namelist[PHYS_MEM].n_value, 0) != namelist[PHYS_MEM].n_value){
X		error (errno, "could not seek physmem");
X	}
X	if (read (kmem, &kphysmem, sizeof (int)) != sizeof (int)){
X		error (errno, "could not read physmem");
X	}
X
X	/*
X	 * fetch nswap (total blocks in the swap device)
X	 */
X	if (lseek (kmem, namelist[N_SWAP].n_value, 0) != namelist[N_SWAP].n_value){
X		error (errno, "could not seek nswap");
X	}
X	if (read (kmem, &knswap, sizeof (int)) != sizeof (int)){
X		error (errno, "could not read nswap");
X	}
X
X	/*
X	 * calculate the scale factor for the display maps.  The scale
X	 * factors should be set in such a way that a single screen can
X	 * display the entire map.  The scale factors are then massaged to
X	 * ensure that they are always even.  This is done in multiple
X	 * statements to ensure that smart compilers don't optimize the
X	 * multiplies and divides away.  I'm sure there's a more elegant
X	 * and reliable way, but this seems to work.
X	 */
X	swap_scale = (knswap + MAX_ENTRIES - 1) / MAX_ENTRIES;
X	swap_scale = (swap_scale + 1) / 2;
X	swap_scale *= 2;
X	core_scale = (kphysmem + MAX_ENTRIES - 1) / MAX_ENTRIES;
X	core_scale = (core_scale + 1) / 2;
X	core_scale *= 2;
X}
X
X/*
X * read_map : copies the specified kernel allocation map into local memory
X * for future processing.
X */
Xvoid read_map(loc, buf, size)
Xdaddr_t loc;				/* location in kernel to be read */
Xstruct map *buf;			/* buffer in which to place copy */
Xunsigned size;				/* number of map entries to read */
X{
X	if (lseek (kmem, loc, 0) != loc){
X		error (errno, "could not seek kernel map");
X	}
X	if (read (kmem, buf, size*sizeof (struct map)) != size*sizeof (struct map)){
X		error (errno, "could not read kernel map");
X	}
X}
X
X/*
X * update_map : modifies all of the appropriate display map cells according
X * that are affected by the specified kernel allocation map item.
X */
Xvoid update_map (dmap, mp, scale)
Xchar dmap[];
Xstruct map *mp;
Xint scale;
X{
X	int entry;
X	int map_index;
X	int size;
X	size = mp->m_size;
X	for (entry = mp->m_addr; size; entry++){
X 		map_index = entry / scale;
X		dmap[map_index] = 0;
X		size--;
X	}
X}
X
X/*
X * interpret_map : scans through the copy of the kernel allocation map and
X * updates the specified display map accordingly.  The total number of free
X * blocks in the map are returned.
X */
Xint interpret_map(dmap, kmp, scale)
Xchar dmap[];
Xstruct map *kmp;
Xint scale;
X{
X	int total_free = 0;
X	struct map *mp;
X	memset (dmap, 1, MAX_ENTRIES);
X	for (mp = kmp; mp->m_size; mp++){
X		total_free += mp->m_size;
X		update_map (dmap, mp, scale);
X	}
X	return total_free;
X}
X
X/*
X * print_map : scans the allocation map, displaying it in the standard
X * window.  Allocated cells are marked with an asterisk ('*'), and free
X * cells are marked with a period ('.').
X */
Xvoid print_map(dmap, scale, total_size)
Xchar dmap[];
Xint scale;
Xlong total_size;
X{
X	int i, j, k;
X	int entry;
X	move (2,0);
X	for (i = 0; i < MAX_ROWS; i++){
X		k = i*MAX_COLUMNS;
X		if (((long)k*scale) >= total_size)break;
X		printw ("%08lx:  ", (long)k * scale);
X		for (j = 0; j < MAX_COLUMNS; j++){
X			if ((((long)k+j)*scale) >= total_size)break;
X			entry = (i*MAX_COLUMNS+j);
X			if (dmap[entry] != 0)addch ('*');
X			else addch ('.');
X		}
X		addch ('\n');
X	}
X}
X
Xvoid interrupt (sig)
Xint sig;
X{
X	move (0,0);
X	clrtobot();
X	refresh();
X	endwin();
X	exit(0);
X}
X
X/*
X * init : performs system and curses initialization
X */
Xvoid init (argc, argv)
Xint argc;
Xchar *argv[];
X{
X	int i;
X	boolean done;
X	char *cp;
X
X	/*
X	 * before we go any further, disable interrupts
X	 */
X	signal (SIGINT, SIG_IGN);
X	signal (SIGQUIT, SIG_IGN);
X	signal (SIGHUP, SIG_IGN);
X	signal (SIGTERM, SIG_IGN);
X
X	/*
X	 * save program name for future reference
X	 */
X	prog_name = argv[0];
X
X	for (i = 1; i < argc; i++){
X		cp = argv[i];
X		if (*cp == '-'){
X			done = FALSE;
X			while (*++cp && !done){
X				switch (*cp){
X
X				case 'd':	/* delay */
X					if (cp[1]){
X						delay = atoi (++cp);
X						done = TRUE;
X					}
X					else {
X						delay = atoi (argv[++i]);
X					}
X					break;
X
X				case 'v':	/* display version */
X					version();
X					exit(0);
X
X				case '?':	/* usage */
X				case 'h':
X					usage();
X					exit(0);
X
X				default:
X					fprintf (stderr, "illegal option : %c; ignored.\n", *cp);
X					break;
X				}
X			}
X		}
X	}
X
X	/*
X	 * curses initialization
X	 */
X	initscr();
X	cbreak();
X	noecho();
X	nodelay (stdscr, 1);
X
X	/*
X	 * print out a simple command menu
X	 */
X	mvaddch (23, 0, '[');
X	attrset (A_BOLD);
X	addch ('m');
X	attroff (A_BOLD);
X	addch (',');
X	attrset (A_BOLD);
X	addch ('c');
X	attroff (A_BOLD);
X	addstr ("]=memory allocation map   [");
X	attrset (A_BOLD);
X	addch ('s');
X	attroff (A_BOLD);
X	addstr ("]=swap allocation map    [");
X	attrset (A_BOLD);
X	addch ('q');
X	attroff (A_BOLD);
X	addch (',');
X	attrset (A_BOLD);
X	addch ('x');
X	attroff (A_BOLD);
X	addch (',');
X	attrset (A_BOLD);
X	addch ('e');
X	attrset (A_NORMAL);
X	addstr ("]=exit");
X
X	/*
X	 * now that curses is initialized, catch the interrupt signal
X	 * so we can die gracefully
X	 */
X	signal (SIGINT, interrupt);
X	signal (SIGQUIT, interrupt);
X	signal (SIGHUP, interrupt);
X	signal (SIGTERM, interrupt);
X
X	/*
X	 * get ready to access kernel memory
X	 */
X	open_kmem();
X	get_nlist ();
X	init_kmem();
X}
X
Xint main (argc, argv)
Xint argc;
Xchar *argv[];
X{
X	struct tm *tmbuf;		/* to hold current time */
X	time_t tm;
X	boolean done = FALSE;		/* will be nonzero when ready to quit */
X	int mem_or_swap = 0;		/* 0 = display core map, 1 = swap map */
X	int free = 0;			/* free items in allocation map */
X
X	init (argc, argv);
X	while (!done){
X
X		/*
X		 * get and display current time
X		 */
X		time (&tm);
X		tmbuf = localtime (&tm);
X		move (0,0);
X		clrtoeol();
X		mvprintw (0, 67, "%02d:%02d:%02d", tmbuf->tm_hour, tmbuf->tm_min, tmbuf->tm_sec);
X
X		if (mem_or_swap == 0){
X
X			/*
X			 * update and display core map
X			 */
X			read_map (namelist[CORE_MAP].n_value, cmapbf, CMAPSIZE);
X			free = interpret_map (dmap, cmapbf, core_scale);
X			standout();
X			mvaddstr (0, 31, "Memory Allocation");
X			standend();
X			move (1,0);
X			clrtoeol();
X			mvprintw (1,  0, "Total Memory = %ld KBytes, %ld Kernel, %ld Free",
X				((long)kphysmem*NBPC)/1024,
X				((long)(kphysmem-kmaxmem)*NBPC)/1024, 
X				((long)free*NBPC)/1024);
X			mvprintw (1, 58, "Scale Factor = %2d", core_scale);
X			print_map (dmap, core_scale*NBPC, (long)kphysmem*NBPC);
X		}
X		else {
X
X			/*
X			 * update and display swap map
X			 */
X			read_map (namelist[SWAP_MAP].n_value, smapbf, SMAPSIZE);
X			free = interpret_map (dmap, smapbf, swap_scale);
X			standout();
X			mvaddstr (0, 29, "Swap Device Allocation");
X			standend();
X			move (1,0);
X			clrtoeol();
X			mvprintw (1,  0, "Total swap space = %d Blocks, %d Free", knswap, free);
X			mvprintw (1, 58, "Scale Factor = % d", swap_scale);
X			print_map (dmap, swap_scale, (long)knswap);
X		}
X
X		/*
X		 * refresh the screen and wait a while
X		 */
X		refresh();
X		sleep (delay);
X
X		/*
X		 * look for any new commands
X		 */
X		switch (getch()){
X		case 'c':	/* switch to core (memory) map */
X		case 'm':
X			mem_or_swap = 0;
X			break;
X
X		case 's':	/* switch to swap map */
X			mem_or_swap = 1;
X			break;
X
X		case 'q':	/* quit */
X		case 'e':
X		case 'x':
X			done = TRUE;
X			break;
X
X		default:	/* illegal command, ignore it */
X			break;
X		}
X			
X	}
X	move (0,0);
X	clrtobot();
X	refresh();
X	endwin();
X}
X
END_OF_mapmem.c
if test 12856 -ne `wc -c <mapmem.c`; then
    echo shar: \"mapmem.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 1 \(of 1\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 1 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Mike Young
Software Development Technologies, Inc., Sudbury MA       Tel: +1 508 443 5779
Internet: mjy@sdti.sdti.com                 UUCP: {harvard,mit-eddie}!sdti!mjy