[net.sources] 3b2 Memory Mappers

brent@phoenix.UUCP (Brent P. Callaghan) (04/24/85)

Here it is folks!  The UNIX memory displaying software.
If you want to look at all 2 megabytes of 3b2 SVR2
memory in one glance,  then these programs are for you.
Otherwise forget it.

Memmon uses curses to display an up to date view of
memory.  You can watch programs entering and leaving
memory as it happens.

Snap gives a more detailed account of who is using memory
and where.

		>>> NOTE <<<
Since both programs read /dev/kmem, they must run with
superuser status (cf ps(1) command).
After compiling, su to root then chown root and chmod u+s.

		enjoy!

------ cut here ------ cut here ------ cut here ------ cut here ------
#!/bin/sh
# This is a shar archive.
# The rest of this file is a shell script which will extract:
# Makefile memmon.doc snap.doc memmon.c snap.c
# Archive created: Wed Apr 24 15:36:24 EST 1985
echo x - Makefile
sed 's/^X//' > Makefile << '~FUNKY STUFF~'
# Make 3b2 memory mappers

memmon : memmon.c
	cc -o memmon memmon.c -lcurses

snap : snap.c
	cc -o snap snap.c
~FUNKY STUFF~
ls -l Makefile
echo x - memmon.doc
sed 's/^X//' > memmon.doc << '~FUNKY STUFF~'
X.TH MEMMON 1 ""
X.SH NAME
memmon \- monitor memory visually.

X.SH SYNOPSIS
X.B memmon
[
X.I interval (secs)
]

X.SH DESCRIPTION
X.B Memmon
enables all 2 megabytes of 3b2 memory to be monitored
down to the click level.
Memory is allocated in 2kb clicks on the 3b2.
X.P
Using the process table, user blocks and core map,
X.B memmon
builds a memory map accounting for the use of every one of
1024 clicks (2mb) of memory.
Every click is coded by a letter:
X.sp
X.in +5
 K = Kernel
 U = User block
 S = Stack
 T = Text
 D = Data
 X = Shared Memory
 . = Available Memory
X.in
X.sp
X.B Memmon
takes a snapshot every 5 seconds and updates the memory map
using curses.
Changes to the map are highlighted for easy identification.
An alternative snapshot interval may be supplied as an argument
at invocation.
X.P
Additional information is supplied with the map:
X.sp
X.in +5
 \- Number of processes in memory
 \- Amount of available memory
 \- Names of processes entering memory since last update
 \- Names of processes leaving  memory since last update
X.in
X.sp

X.SH BUGS
Memory may change while a snapshot is being made.
Anomalies may occasionally appear.
X.P
Processes which enter and leave memory between snaphots will
not be detected.
~FUNKY STUFF~
ls -l memmon.doc
echo x - snap.doc
sed 's/^X//' > snap.doc << '~FUNKY STUFF~'
X.TH SNAP 1 ""
X.SH NAME
snap \- print a 3b2 memory map

X.SH SYNOPSIS
X.B snap
[
X.B \-i
X.I n
] [
X.B \-n
X.I n
] [
X.B \-r
] [
X.B \-m
] [
X.B \-l
]

X.SH DESCRIPTION
X.B Snap
prints snapshot maps of 3b2 memory to standard output,
detailing the ownership and use of every click (2kb).
X.P
Click usage is coded by letter:
X.sp
X.in +5
 K = Kernel
 U = User block
 S = Stack
 T = Text
 D = Data
 X = Shared Memory
 . = Available Memory
 * = Collision (memory changed during snapshot)
X.in
X.P
The invocation options are as follows:
X.sp
X.in +5
 \-\fBi\fI n\fR \- Set snaphot interval (default 5 sec).
 \-\fBn\fI n\fR \- Set number of snapshots (default 1).
 \-\fBm\fP \- Omit memory map.
 \-\fBr\fP \- Omit report.
 \-\fBl\fP \- Sort report by length of segment instead of address.
X.in
X.sp
X.P
When run without arguments, \fBsnap\fP prints the results of a
single snapshot of memory.
The printout comprises a memory map, followed by an expanded
report detailing the size, type and user of each piece of memory.

X.SH BUGS
Memory may change while a snapshot is being made.
Anomalies may occasionally appear.
X.P
Processes which enter and leave memory between snaphots will
not be detected.
~FUNKY STUFF~
ls -l snap.doc
echo x - memmon.c
sed 's/^X//' > memmon.c << '~FUNKY STUFF~'
/***************************************************************
 *
 *   Program  : memmon
 *   By       : Brent Callaghan
 *              AT&T Information Systems, Lincroft N.J.
 *              (201)576 3475  
 *
 *   Date     : April 1985
 *
 *                  ***  PUBLIC DOMAIN  ***
 *   This program may be freely copied or modified in any way
 *   provided it is not offered for use as, or part of, any
 *   commercial software product and the above accreditation 
 *   remains intact.
 *
 *   Function : Uses curses to display a map of 3b2 memory.
 *              Every click of memory is coded by a letter:
 *
 *                K = Kernel
 *                T = Text
 *                D = Data
 *                U = User block
 *                S = Stack
 *                . = Available memory
 *
 *              Memory changed since last snapshot is
 *              highlighted.
 *
 *              Names of processes moved into or out of
 *              memory appear at the bottom of the screen.
 *
 *              memmon takes a snapshot every 5 sec unless
 *              you give it an interval as an invocation 
 *              parameter.
 *                          e.g.  memmon 1
 *
 *              Kill memmon with a terminal interrupt
 *              (del, rubout or break)
 *
 ***************************************************************
 */
#include <stdio.h>
#include <curses.h>
#include <ctype.h>
#include <time.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/signal.h>
#include <sys/proc.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <nlist.h>
#include <sys/map.h>
#include <sys/var.h>
#include <sys/sysmacros.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/immu.h>

#define MAX(a,b)  ((a) > (b) ? (a) : (b))
#define MIN(a,b)  ((a) < (b) ? (a) : (b))
#define gp_align(x,on)  (unsigned)((uint)(x) + on-1 - ((uint)(x) + on-1) % on)

#define CMAPSIZ   70     /* size of coremap    */
#define MPROCS    60     /* size of proc table */
#define TABSZ     300    /* size of mem table  */
#define NBYTES    512    /* space for proc names */
#define MAPSZ     1024   /* size of memmap     */
#define VUSER     0xF000000

struct nlist nl[] = {
   {"proc"},        /* process table */
   {"v"},           /* system variables */
   {"coremap"},     /* available core map */
   {"shminfo"},     /* shared memory info */
   {"shmem"},       /* shared memory headers */
   {"_ksde"},       /* kernel sde table */
   {""}} ;

#define NLPROC  nl[0].n_value
#define NLV     nl[1].n_value
#define NLCORE  nl[2].n_value
#define NLSHMI  nl[3].n_value
#define NLSHM   nl[4].n_value
#define NLKSDE  nl[5].n_value
#define NLEND   6

struct PINFO {
   char
      cflg,       /* incore flag  */
      nflg,       /* new flag     */
      name[15] ;  /* name of proc */
   int
      pid ;       /* process id */
   } pinfo [MPROCS] ;

struct var v ;
struct map coremap [CMAPSIZ], *bp ; /* map of avail core */
struct proc mproc ;      /* process table entry */
struct user u     ;      /* user block          */
struct shmid_ds mds ;    /* shared memory data structure */
struct shminfo shminfo ; /* shared memory information structure */
sde_t ksde ;             /* kernel segment descriptor entry */
int
   kmem,                 /* Kernel memory descriptor */
   mem ;                 /* User memory descriptor   */

int
   highlight = 0,        /* curses highlight code */
   procs,                /* Active processes */
   avail ;               /* available memory */

char memmap [MAPSZ] ;    /* the memory map */

mpinit ()
   /* Initialize map with spaces */
   {
   register int i ;

   for (i = 0 ; i < MAPSZ ; i++) 
      memmap[i] = ' ' ;
   }

map (type, addr, len)
   char type ; int addr, len ;
   /* Colour in map with type at addr for len clicks */
   {
   register int i ;
   register char *p ;

   for (p = &memmap[addr], i = 0 ; i < len && addr + i < MAPSZ ; p++, i++)
      *p = type | (*p != type ? highlight : 0) ;
   }

clearproc ()
   /* clear flags in my process table */
   {
   struct PINFO *pp ;

   for (pp = pinfo ; pp < pinfo + MPROCS ; pp++) {
      if (pp->cflg == 0) {
         pp->nflg = 0 ;
         pp->name[0] = 0 ;
         pp->pid = 0 ;
         }
      pp->cflg = 0 ;
      pp->nflg = 0 ;
      }
   }

int checkproc (pname, ppid)
   char *pname ; int ppid ;
   /* if process in table set a flag */
   /* otherwise add it to the table  */
   {
   struct PINFO *pp, *ppfree = NULL ;

   for (pp = pinfo ; pp < pinfo + MPROCS ; pp++) {
      if (ppid == pp->pid) {
         pp->cflg = 1 ;
         return 0 ;
         }
      if (pp->pid == 0 && ppfree == NULL)
         ppfree = pp ; /* remember empty slot */
      }
   ppfree->cflg = 1 ;
   ppfree->nflg = 1 ;
   strcpy(ppfree->name, pname) ;
   ppfree->pid = ppid ;
   return 1 ;
   }

deltaproc ()
   /* Print changes to the process table */
   {
   struct PINFO *pp ;

   printw("     Procs In : ") ; 
   for (pp = pinfo ; pp < pinfo + MPROCS ; pp++)
      if (pp->nflg)
         printw("%s ", pp->name) ;

   printw("\n     Procs Out: ") ;
   for (pp = pinfo ; pp < pinfo + MPROCS ; pp++)
      if (pp->cflg == 0 && pp->pid > 0)
         printw("%s ", pp->name) ;
   }

mapdisp ()
   /* display the memory map */
   {
   register int i, j, c ;
   static int oprocs, oavail ;
   long tod ;

   move(0,0) ;
   tod = time((long *)0) ;
   printw("     3b2 Memory Map         %s\n      ", ctime(&tod)) ;
   for (i = 0 ; i < 60 ; i+=10)
      printw("|%02d_______", i) ;
   printw("|60_\n") ;

   /* Print the map */

   for (i = 0 ; i < MAPSZ ; i += 64) {
      printw("%04d [", i) ;
      for (j=0 ; j < MIN(64, MAPSZ-i) ; j++) {
         if ((c = memmap[i+j]) & A_STANDOUT)
            memmap[i+j] &= (~A_STANDOUT) ;

         addch(c) ;
         }
      printw("]\n") ;
      }

   printw("\n     Incore Procs %2d ", procs) ;
   if (procs != oprocs && oprocs)
      printw("(%c%2d)", procs>oprocs?'+':'-', abs(procs-oprocs)) ;
   else
      printw("     ") ;
   oprocs = procs ;

   printw("    Avail Mem %3d ", avail) ;
   if (avail != oavail && oavail)
      printw("(%c%3d)\n", avail>oavail?'+':'-', abs(avail-oavail)) ;
   else
      printw("\n") ;
   oavail = avail ;

   if (highlight)
      deltaproc() ;

   clrtobot() ;
   refresh() ;
   }

proc_sdt (type, first, segs)
   int type, first, segs ;
   /* Get info about memory of type 'type' from 'segs' */
   /* sde_t's starting at 'first' in table.            */
   {
   register int j ;

   for (j = first ; j < first + segs ; j++)
      map(type,
         btoc (u.u_sdt.seg[j].wd2.address) & 0x3FFF,
         motoc(u.u_sdt.seg[j].maxoff)) ;
   }

snapshot ()
   {
   register int i, j ;
   long addr ;
   int dsize, new ;

   /* get core map */

   lseek(kmem, (long)NLCORE, 0) ;
   read (kmem, coremap, sizeof(coremap)) ;

   /* find process table size */

   lseek(kmem, (long)NLV, 0) ;
   read (kmem, (char *)&v, sizeof(v)) ;

   /* locate process table */

   lseek(kmem, (long)NLPROC, 0) ;

   /* read through process table */

   clearproc() ;
   procs = 0 ;
   for (i = 0 ; i < v.v_proc ; i++) {
      read(kmem, (char *)&mproc, sizeof(mproc)) ;
      if (mproc.p_stat == 0)
         continue ;
      if (!(mproc.p_flag & SLOAD)) /* in core ? */
         continue ;
      procs++ ;

      /* get user block */

      addr = ctob((int)mproc.p_addr) & (~VUSER) ;
      lseek(mem, addr, 0) ;
      if (read(mem, (char *)&u, sizeof(u)) != sizeof(u)) {
         fprintf(stderr, "Couldn't read user block\n") ;
         exit(1) ;
         }

      if (mproc.p_pid != 0 && checkproc(u.u_comm, (int)mproc.p_pid))
         new = highlight ;
      else
         new = 0 ;

      proc_sdt('T'|new, TSEG, ctos(mproc.p_tsize)) ;

      dsize = mproc.p_size - (mproc.p_textp ? 0 : mproc.p_tsize) 
                           -  mproc.p_ssize - USIZE ;
      proc_sdt('D'|new, MAX(j,DSEG), ctos(dsize)) ;

      proc_sdt('S'|new, STKSEG, ctos(mproc.p_ssize)) ;

      proc_sdt('U'|new, USEG  , 1) ;
      }

   /* get coremap data */

   avail = 0 ;
   for (bp = mapstart(coremap) ; bp->m_size ; bp++) {
      avail += bp->m_size ;
      map('.', bp->m_addr & 0x3FFF, bp->m_size) ;
      }

   /* get shared memory info */
   
   if (NLSHMI != 0) {
      lseek(kmem, (long)NLSHMI, 0) ;
      read (kmem, &shminfo, sizeof(shminfo)) ;
      
      NLKSDE = gp_align(NLKSDE, 8) ;
      lseek(kmem,(long)NLKSDE, 0) ;
      for (i = 0 ; i < shminfo.shmmni ; i++) {
         read (kmem, &ksde, sizeof(ksde)) ;
         if (isvalid(&ksde) && ispresent(&ksde))
            map('X',
               (btoc (ksde.wd2.address) & 0x3FFF) - 1,
               motoc(ksde.maxoff)) ;
         }
      }

   /* Fill in kernel space (leading space in map) */

   if (highlight == 0)
      map('K', 0, strspn(memmap, " ")) ;

   /* Display the map */

   mapdisp() ;

   highlight = A_STANDOUT ;  /* set highlighting after 1st snapshot */
   }

quit()
   /* clean up curses stuff */
   {
   clear() ;
   refresh() ;
   endwin() ;
   exit(0) ;
   }

main (argc, argv)
   int argc ; char *argv[] ;
   {
   int interval = 5 ;  /* default 5 sec interval */

   if (argc > 1 && isdigit(*argv[1]))
      interval = atoi(argv[1]) ;

   if ((kmem = open("/dev/kmem", 0)) < 0) {
      fprintf(stderr, "Couldn't open /dev/kmem\n") ;
      exit(1) ;
      }

   if ((mem = open("/dev/mem", 0)) < 0) {
      fprintf(stderr, "Couldn't open /dev/mem\n") ;
      exit(1) ;
      }

   nlist("/unix", nl) ;
   if (NLPROC == 0) {
      fprintf(stderr, "No namelist\n") ;
      exit(1) ;
      }

   mpinit() ;  /* initialize memory map */

   signal(SIGINT , quit) ; /* set up             */
   signal(SIGQUIT, quit) ; /*   signals for      */
   signal(SIGTERM, quit) ; /*     graceful death */

   initscr() ;
   crmode() ;
   nonl() ;
   clear() ;

   for (;;) {
      snapshot() ;
      sleep(interval) ;
      }

   }
~FUNKY STUFF~
ls -l memmon.c
echo x - snap.c
sed 's/^X//' > snap.c << '~FUNKY STUFF~'
/***************************************************************
 *
 *   Program  : snap
 *   By       : Brent Callaghan
 *              AT&T Information Systems, N.J.
 *              (201)576 3475
 *
 *   Date     : April 1985
 *
 *                  ***  PUBLIC DOMAIN  ***
 *   This program may be freely copied or modified in any way
 *   provided it is not offered for use as, or part of, any
 *   commercial software product and the above accreditation 
 *   remains intact.
 *
 *   Function : Displays a memory map & report, accounting
 *              for who has each click of 3b2 memory and for what.
 *              Every click of memory is coded by a letter:
 *
 *                K = Kernel
 *                T = Text
 *                D = Data
 *                U = User block
 *                S = Stack
 *                . = Available memory
 *                * = Collision (memory moved during snapshot)
 *
 *   Options  : -i n  interval of n secs between snapshots
 *              -n n  take n snapshots (default is 1)
 *              -l n  sort report by length of seg 
 *                    instead of address.
 *              -m    omit memory map
 *              -r    omit report
 *
 ***************************************************************
 */
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/signal.h>
#include <sys/proc.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <nlist.h>
#include <sys/map.h>
#include <sys/var.h>
#include <sys/sysmacros.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/immu.h>

#define MAX(a,b)  ((a) > (b) ? (a) : (b))
#define MIN(a,b)  ((a) < (b) ? (a) : (b))
#define gp_align(x,on)  (unsigned)((uint)(x) + on-1 - ((uint)(x) + on-1) % on)
#define CMAPSIZ   70     /* size of coremap    */
#define MPROCS    60     /* size of proc table */
#define TABSZ     300    /* size of mem table  */
#define NBYTES    512    /* space for proc names */
#define MAPSZ     1024   /* size of memmap     */
#define VUSER     0xF000000

struct nlist nl[] = {
   {"proc"},        /* process table */
   {"v"},           /* system variables */
   {"coremap"},     /* available core map */
   {"shminfo"},     /* shared memory info */
   {"shmem"},       /* shared memory headers */
   {"_ksde"},       /* kernel sde table */
   {""}} ;

#define NLPROC  nl[0].n_value
#define NLV     nl[1].n_value
#define NLCORE  nl[2].n_value
#define NLSHMI  nl[3].n_value
#define NLSHM   nl[4].n_value
#define NLKSDE  nl[5].n_value
#define NLEND   6

struct var v ;
struct map coremap [CMAPSIZ], *bp ; /* map of avail core */
struct proc mproc ;      /* process table entry */
struct user u     ;      /* user block          */
struct shmid_ds mds ;    /* shared memory data structure */
struct shminfo shminfo ; /* shared memory information structure */
sde_t ksde ;             /* kernel segment descriptor entry */
int
   mapflag = 1,          /* print map */
   repflag = 1,          /* print report */
   lenflag ;             /* sort report by seg length */
int
   kmem,                 /* Kernel memory descriptor */
   mem ;                 /* User   memory descriptor */

char memmap [MAPSZ] ;    /* the memory map */
struct t_entry {
   char *pname ;  /* name of process  */
   char mtype  ;  /* type of memory   */
   int  maddr  ;  /* mem addr (click) */
   int  mlen   ;  /* length (clicks)  */
   } table[TABSZ], *tablep ;

char *stralloc (bytes)
   int bytes ;
   /* Allocate some bytes for a string from an internal array */
   {
   static char strings[NBYTES], *p, *strp = strings ;

   if (bytes < 0) {    /* Init str space */
      strp = strings ;
      return(NULL) ;
      }

   if (strp+(bytes+1) > strings + NBYTES) {
      fprintf(stderr, "No space for proc names\n") ;
      exit(1) ;
      }
   p = strp ;
   strp += bytes + 1 ;
   return(p) ;
   }

mpinit ()
   /* Initialize map with spaces */
   {
   register int i ;

   for (i = 0 ; i < MAPSZ ; i++) 
      memmap[i] = ' ' ;
   }

map (type, addr, len)
   char type ; int addr, len ;
   /* Colour in map with type at addr for len clicks */
   /* Substitute a "*" if a collision occurs.        */
   {
   register int i ;
   register char *p ;

   for (p = &memmap[addr], i = 0 ; i < len && addr + i < MAPSZ ; p++, i++)
      *p = (*p==' ' || *p==type)? type : '*' ;
   }

mapprint ()
   {
   register int i ;

   printf("\nMemory Map\n      ") ;
   for (i = 0 ; i < 60 ; i+=10)
      printf("|%02d_______", i) ;
   printf("|60_\n") ;

   /* Print the map */

   for (i = 0 ; i < MAPSZ ; i += 64) {
      printf("%04d [", i) ;
      fwrite(&memmap[i], MIN(64, MAPSZ-i), 1, stdout) ;
      printf("]\n") ;
      }
   }

table_add (name, type, addr, len)
   char *name, type ; int addr, len ;
   {
   tablep->pname = name ;
   tablep->mtype = type ;
   tablep->maddr = addr ;
   tablep->mlen  = len ;
   if (++tablep >= table+TABSZ) {
      fprintf(stderr, "Map table overflow\n") ;
      exit(1) ;
      }
   map(type, addr, len) ;
   }

int m_compare (a,b)
   struct t_entry *a, *b ;
   {
   return(a->maddr <= b->maddr ? -1 : 1) ;
   }

int l_compare (a,b)
   struct t_entry *a, *b ;
   {
   if (a->mlen == b->mlen)  /* keep shared text together */
      return(a->maddr <= b->maddr ? -1 : 1) ;
   return(a->mlen >= b->mlen ? -1 : 1) ;
   }

table_print ()
   {
   register struct t_entry *tp ;
   register int i ;
   int l, count = 0 ;

   qsort(table, tablep-table, sizeof(struct t_entry),
         lenflag ? l_compare : m_compare) ;

   printf("\naddr size") ;
   for (tp = table ; tp < tablep ; tp++) {
      if (tp > table && tp->maddr == (tp-1)->maddr)
         count++ ;
      else {
         if (count > 1)
            printf(" (%d sharing)", count) ;
         count = 1 ;
         printf("\n%4d %4d ", tp->maddr, tp->mlen) ;
         for (l = 0 ; l < tp->mlen ; l++) {
            putchar(tp->mtype) ;
            if ((l+1) % 64 == 0)
               printf("\n          ") ;
            }
         printf("  %s", tp->pname) ;
         }
      }
   putchar('\n') ;
   }

proc_sdt (name, type, first, segs)
   char *name ; int type, first, segs ;
   /* Get info about memory of type 'type' from 'segs' */
   /* sde_t's starting at 'first' in table.            */
   {
   register int j ;
   int addr, len ;

   for (j = first ; j < first + segs ; j++) {
      addr = btoc (u.u_sdt.seg[j].wd2.address) & 0x3FFF ;
      len  = motoc(u.u_sdt.seg[j].maxoff) ;
      table_add(name, type, addr, len) ;
      }
   }

snapshot ()
   {
   register int i, j ;
   long addr ;
   int dsize ;
   char *namep ;
   long tod, time() ;

   mpinit() ;        /* Init map */
   stralloc(-1) ;    /* Init string space */
   tablep = table ;


   /* get core map */

   lseek(kmem, (long)NLCORE, 0) ;
   read (kmem, coremap, sizeof(coremap)) ;

   /* find process table size */

   lseek(kmem, (long)NLV, 0) ;
   read (kmem, (char *)&v, sizeof(v)) ;

   /* locate process table */

   lseek(kmem, (long)NLPROC, 0) ;

   /* read through process table */

   for (i = 0 ; i < v.v_proc ; i++) {
      read(kmem, (char *)&mproc, sizeof(mproc)) ;
      if (mproc.p_stat == 0)
         continue ;
      if (!(mproc.p_flag & SLOAD)) /* in core ? */
         continue ;

      /* get user block */

      addr = ctob((int)mproc.p_addr) & (~VUSER) ;
      lseek(mem, addr, 0) ;
      if (read(mem, (char *)&u, sizeof(u)) != sizeof(u)) {
         fprintf(stderr, "Couldn't read user block\n") ;
         exit(1) ;
         }

      if (mproc.p_pid == 0)
         namep = "(swapper)" ;
      else
         namep = (char *)strcpy(stralloc(strlen(u.u_comm)), u.u_comm) ;

      proc_sdt(namep, 'T', TSEG, ctos(mproc.p_tsize)) ;

      dsize = mproc.p_size - (mproc.p_textp ? 0 : mproc.p_tsize) 
                           -  mproc.p_ssize - USIZE ;
      proc_sdt(namep, 'D', MAX(j,DSEG), ctos(dsize)) ;

      proc_sdt(namep, 'S', STKSEG, ctos(mproc.p_ssize)) ;

      proc_sdt(namep, 'U', USEG  , 1) ;
      }

   /* get coremap data */

   for (bp = mapstart(coremap) ; bp->m_size ; bp++)
      table_add("", '.', bp->m_addr & 0x3FFF, bp->m_size) ;

   /* get shared memory info */
   
   if (NLSHMI != 0) {
      lseek(kmem, (long)NLSHMI, 0) ;
      read (kmem, &shminfo, sizeof(shminfo)) ;
      
      NLKSDE = gp_align(NLKSDE, 8) ;
      lseek(kmem,(long)NLKSDE, 0) ;
      for (i = 0 ; i < shminfo.shmmni ; i++) {
         read (kmem, &ksde, sizeof(ksde)) ;
         if (!(isvalid(&ksde) && ispresent(&ksde)))
            continue ;
         table_add("(Shared Memory)", 'X',
            (btoc (ksde.wd2.address) & 0x3FFF) - 1,
            motoc(ksde.maxoff)) ;
         }
      }

   /* Fill in kernel space (leading space in map) */

   table_add("(Kernel)", 'K', 0, strspn(memmap, " ")) ;

   /* Print the maps */

   tod = time((long *)0) ;
   printf("%s", ctime(&tod)) ;

   if (mapflag)
      mapprint() ;

   if (repflag)
      table_print() ;
   }

main (argc, argv)
   int argc ; char *argv[] ;
   {
   int c ;
   extern int optind ;
   extern char *optarg ;
   int badflag = 0 ;

   int
      interval = 0,   /* Interval (sec) between samples */
      samples  = 1 ;  /* Number of samples */

   while ((c = getopt(argc, argv, "lmri:n:")) != EOF)
      switch(c) {
         case 'l':        /* sort report by length of memory */
            lenflag = 1 ;
            break ;

         case 'm':        /* don't print memory map */
            mapflag = 0 ;
            break ;

         case 'r':        /* don't print report */
            repflag = 0 ;
            break ;

         case 'n':        /* number of samples */
            samples = atoi(optarg) ;
            break ;

         case 'i':        /* interval between samples (sec) */
            interval = atoi(optarg) ;
            break ;

         case '?':
            badflag = 1 ;
            break ;
         }
   if (badflag) {
      fprintf(stderr, "Usage: %s [-l] [-m] [-r] [-i n] [-n n]\n", argv[0]) ;
      exit(1) ;
      }

   if (samples > 1 && interval == 0)
      interval = 5 ;

   if ((kmem = open("/dev/kmem", 0)) < 0) {
      perror("Couldn't open /dev/kmem ") ;
      exit(1) ;
      }

   if ((mem = open("/dev/mem", 0)) < 0) {
      perror("Couldn't open /dev/mem ") ;
      exit(1) ;
      }

   nlist("/unix", nl) ;
   if (NLPROC == 0) {
      fprintf(stderr, "No namelist\n") ;
      exit(1) ;
      }

   if (samples > 1)
      fprintf(stderr, "Taking %d snapshots at %d sec intervals\n", samples, interval) ;

   while (samples--) {
      snapshot() ;
      sleep(interval) ;
      }
   }
~FUNKY STUFF~
ls -l snap.c
# The following exit is to ensure that extra garbage 
# after the end of the shar file will be ignored.
exit 0
-- 
				
Made in New Zealand -->		Brent Callaghan
				AT&T Information Systems, Lincroft, NJ
				{ihnp4|ahuta|pegasus}!phoenix!brent
				(201) 576-3475