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