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