home *** CD-ROM | disk | FTP | other *** search
- Subject: v15i004: Page accounting aide for SysVrel3.1
- Newsgroups: comp.sources.unix
- Approved: rsalz@uunet.UU.NET
-
- Submitted-by: DJ Molny <ihnp4!chinet!djmolny>
- Posting-number: Volume 15, Issue 4
- Archive-name: pages
-
-
- "Pages" notes the size and the number of pages in core for each
- paging region in the system. A typical process has three regions:
- text (executable instructions), initialized data variables, and
- uninitialized variables (called BSS). Processes may also use
- regions to connect to shared libraries, shared memory segments, etc.
-
- Notes on portability: "Pages" is highly dependent on the System V
- Release 3.1 implementation, and it has only been tested on the
- AT&T 3B2/400 computer. "Pages" will probably run on all members of
- the 3B2 family, but may run on the 3B15 as well. As far as release
- compatability goes, SVR3.0 is probably OK, SVR2.1 *may* work, and
- releases prior to 2.1 didn't have paging.
-
- #! /bin/sh
- #
- # -rw-r--r-- 1 djmolny ccom 4072 Feb 19 12:29 README
- # -r--r--r-- 1 djmolny ccom 10688 Feb 19 12:26 pages.c
- # -rw-r--r-- 1 djmolny ccom 1286 Feb 19 11:20 pages.1
- # -rw-r--r-- 1 djmolny ccom 199 Feb 19 10:32 pages.mk
- #
- echo 'x - README'
- if test -f README; then echo 'shar: not overwriting README'; else
- sed 's/^X//' << '________This_Is_The_END________' > README
- XAT&T's paging UNIX operating system provides two tools to
- Xanalyze memory usage. The sar(1) package collects statistics
- Xabout the amount of free memory in the system, and the number of
- Xpages and processes moved in and out from the disk swap area.
- XThe ps(1) command's "-l" option shows each process' size in pages.
- X(1 page = 2Kb.)
- X
- XUnfortunately, neither sar nor ps tells how many pages from each
- Xprocess are in actually core, and how many have been saved to disk.
- XTo fill this need, I wrote "pages". "Pages" works a lot like ps(1).
- XIt reads /unix to find kernel symbol addresses, then opens /dev/kmem
- Xand rummages around to find the information it wants. "Pages" saves
- Xkernel symbol data in /etc/pg_data to speed up execution. This file
- Xis updated automatically whenever /unix is newer than /etc/pg_data.
- X
- X"Pages" notes the size and the number of pages in core for each
- Xpaging region in the system. A typical process has three regions:
- Xtext (executable instructions), initialized data variables, and
- Xuninitialized variables (called BSS). Processes may also use
- Xregions to connect to shared libraries, shared memory segments, etc.
- X
- XAfter each region has been analyzed, "pages" prints a table of
- Xeach process and independent region it has encountered. The table
- Xshows the process id (if appropriate), the number of pages swapped
- Xto disk, and total number of pages, and the process' name (if appropriate.)
- X
- XA typical run of "pages" (with no arguments) may look like this:
- X
- X1: PID NSWAP/TOTL COMMAND
- X2: SHARE 1 23 sh
- X3: 11308 3 8 sh
- X4: 18486 12 104 vi
- X5: 18498 15 46 pages
- X6: 18496 3 8 sh
- X
- XLine one is column headings. The second line shows the shared text region
- Xof the Bourne shell (indicated by the word "SHARE" in column 1.) One of
- Xits 23 pages is paged out to disk (or was never loaded in the first place.)
- XThe third line is the combined data and BSS regions of a shell, with three
- Xof the 8 pages on disk. Since the data are not shared, the specific process
- Xid is shown. Line 6 gives the same information for another shell process.
- X
- XLine 4 shows that 12 of 104 pages of vi are on disk. In this case, the
- XTOTL column represents the combined text, data, and BSS sizes. If another
- Xuser was running vi, a "SHARE" line show the shared text region separately.
- XLine 5 shows "pages" itself.
- X
- XBy defaults, "pages" only shows processes attached to your current tty.
- XTo alter this behavior, the -e, -a, -t, and -u options may be used.
- XThese options work the same as on "pages" as they do on "ps", and
- Xare explained on the ps(1) man page. In addition, the -q option may
- Xbe used to get a quick look at the system's overall paging status.
- X
- X"Pages" must run as setuid bin so that it can write the /etc/pg_data file,
- Xand as setgid sys so that it can read /dev/kmem. The makefile supplied
- Xwith this package sets ownership and uid/gid bits when the "install"
- Xtarget is specified.
- X
- XNotes on portability: "Pages" is highly dependent on the System V
- XRelease 3.1 implementation, and it has only been tested on the
- XAT&T 3B2/400 computer. "Pages" will probably run on all members of
- Xthe 3B2 family, but may run on the 3B15 as well. As far as release
- Xcompatability goes, SVR3.0 is probably OK, SVR2.1 *may* work, and
- Xreleases prior to 2.1 didn't have paging.
- X
- XI had to read and compare /usr/include/sys files and wade through lots of
- Xkernel dumps to find the algorithms for this program. Anyone who wants
- Xto port "pages" to a truly foreign implementation will have to do the same.
- XI only have access to the 3B2/400, so you're on your own when it comes
- Xto porting. Sorry.
- X
- XEnhancements and bug reports (preferably with fixes) are welcome but
- XI don't promise to incorporate them immediately. Since netnews often
- Xexpires before I get around to reading it, messages should be sent
- Xvia E-mail to ihnp4!chinet!djmolny.
- X
- XThis program is placed in the public domain. It may be distributed
- Xand modified freely providing this notice is reproduced in its entirety.
- XThis program may not be sold. Author: DJ Molny, ccom consultants, inc.
- ________This_Is_The_END________
- if test `wc -l < README` -ne 79; then
- echo 'shar: README was damaged during transit (should have been 79 bytes)'
- fi
- fi ; : end of overwriting check
- echo 'x - pages.c'
- if test -f pages.c; then echo 'shar: not overwriting pages.c'; else
- sed 's/^X//' << '________This_Is_The_END________' > pages.c
- X/*
- X * pages - show process paging information
- X *
- X * Written by: DJ Molny
- X * ccom consultants, inc.
- X * 5963 Oakwood Dr.
- X * Lisle, IL 60532
- X * ihnp4!chinet!djmolny
- X *
- X * This program is placed in the public domain. It may be distributed
- X * and modified freely providing this notice is reproduced in its entirety.
- X * This program may not be sold.
- X *
- X * The "pages" program is highly dependent on AT&T's implementation
- X * of the 3B2 UNIX System V Release 3 operating system. It might be
- X * modified to other members of the AT&T 3B family, but porting to
- X * other paging implementations of UNIX is probably impossible.
- X *
- X * Refer to the accompanying manual page for usage instructions and
- X * the inevitable disclaimers.
- X *
- X */
- X
- X#include <stdio.h>
- X#include <sys/types.h>
- X#include <nlist.h>
- X#include <fcntl.h>
- X#include <pwd.h>
- X#include <sys/stat.h>
- X#include <sys/immu.h>
- X#include <sys/psw.h>
- X#include <sys/pcb.h>
- X#include <sys/signal.h>
- X#include <sys/fs/s5dir.h>
- X#include <sys/user.h>
- X#include <sys/region.h>
- X#include <sys/param.h>
- X#include <sys/proc.h>
- X#include <sys/var.h>
- X#include <sys/sysmacros.h>
- X#include <sys/sys3b.h>
- X
- Xstatic char sccsid[] = "@(#)pages.c 1.11 2/19/88";
- X
- X#define PNAME "pages"
- X#define DATAFILE "/etc/pg_data"
- X
- X#define MAXREAD (64 * 1024) /* max bytes to read in one shot */
- X
- Xstruct nlist ksymbs[] = {
- X "proc", (long) 0, (short) 0, (unsigned short) 0, (char) 0, (char) 0,
- X "v", (long) 0, (short) 0, (unsigned short) 0, (char) 0, (char) 0,
- X "freemem", (long) 0, (short) 0, (unsigned short) 0, (char) 0, (char) 0,
- X (char *) 0, (long) 0, (short) 0, (unsigned short) 0, (char) 0, (char) 0,
- X};
- X
- Xint quick = 0; /* quick mode (off by default) */
- Xint ttys; /* tty # to show (defaults to current tty) */
- Xint tflag = 0; /* tty # specified (off by default) */
- Xint aflag = 0; /* show all logged in users (off by default) */
- Xint eflag = 0; /* show all procs (off by default) */
- Xint uids = -1; /* user id # (defaults to unused) */
- X
- Xint memfd;
- Xstruct var v;
- X
- Xstatic struct regblk { /* pertinent info. for the regions we select */
- X unsigned long pid;
- X char cmd[DIRSIZ];
- X short size;
- X short incore;
- X};
- X
- Xstruct regblk *reg_tab, *reg_cur; /* ptrs to region table */
- X
- X#define Error(s) { perror(s); }
- X
- X
- Xmain(argc, argv)
- Xint argc;
- Xchar **argv;
- X{
- X extern char *optarg;
- X struct passwd *pwd, *getpwnam();
- X struct stat sbuf, sbuf2;
- X char ttynm[128];
- X char *malloc();
- X int datafd;
- X int i;
- X
- X while ((i = getopt(argc, argv, "qaeu:t:")) != -1)
- X {
- X switch(i) {
- X case 'q':
- X quick++;
- X eflag++;
- X break;
- X case 'e':
- X eflag++;
- X break;
- X case 'a':
- X aflag++;
- X break;
- X case 'u':
- X if (!optarg) usage();
- X if (*optarg >= '0' && *optarg <= '9')
- X uids = atoi(optarg);
- X else {
- X pwd = getpwnam(optarg);
- X if (!pwd)
- X {
- X fprintf(stderr,
- X "No such user: %s\n", optarg);
- X usage();
- X }
- X uids = pwd->pw_uid;
- X }
- X break;
- X case 't':
- X if (!optarg) usage();
- X tflag++;
- X strcpy(ttynm, "/dev/");
- X if (*optarg >= '0' && *optarg <= '9')
- X strcat(ttynm, "tty");
- X strcat(ttynm, optarg);
- X if (stat(ttynm, &sbuf) == -1)
- X {
- X perror(ttynm);
- X exit(4);
- X }
- X
- X ttys = sbuf.st_rdev;
- X break;
- X default:
- X usage();
- X }
- X }
- X
- X
- X /* for default case (no arguments), act as if
- X we're invoked with our own tty specified */
- X
- X if (!aflag && !eflag && !tflag && uids == -1)
- X {
- X tflag++;
- X
- X /* check stdin, stdout, stderr for a file
- X descriptor connected to a tty */
- X
- X for (i = 0; i < 3; i++)
- X if (isatty(i))
- X break;
- X
- X if (i == 3)
- X {
- X fprintf(stderr, "%s: can't find tty\n", PNAME);
- X exit(5);
- X }
- X
- X /* stat the tty file */
- X if (fstat(i, &sbuf) == -1)
- X {
- X perror(PNAME);
- X exit(6);
- X }
- X
- X /* and extract the internal device number */
- X ttys = sbuf.st_rdev;
- X }
- X
- X /* open kernel memory */
- X
- X if ((memfd = open("/dev/kmem", O_RDONLY)) == -1)
- X {
- X Error("/dev/kmem");
- X exit(7);
- X }
- X
- X /* stat /unix to see if it's changed since
- X we last wrote the data cache file */
- X
- X if (stat("/unix", &sbuf) == -1)
- X {
- X Error("/unix");
- X exit(10);
- X }
- X
- X /* if our cache file is gone, or if unix is newer than
- X the cache file, do the lengthy nlist() call and re-write
- X the cache */
- X
- X if (stat(DATAFILE, &sbuf2) == -1 || sbuf.st_mtime > sbuf2.st_mtime)
- X {
- X if (nlist("/unix", ksymbs) == -1) /* LONG library call */
- X {
- X Error("nlist");
- X exit(11);
- X }
- X
- X /* create/truncate the cache file */
- X
- X datafd = open(DATAFILE, O_CREAT | O_WRONLY | O_TRUNC, 0664);
- X
- X if (datafd == -1)
- X {
- X Error(DATAFILE);
- X exit(12);
- X }
- X
- X /* save the whole name list structure */
- X if (write(datafd, ksymbs, sizeof(ksymbs)) != sizeof(ksymbs))
- X {
- X Error(DATAFILE);
- X exit(13);
- X }
- X
- X close(datafd);
- X
- X } else { /* cache is current, so read ksymbs from there */
- X
- X datafd = open(DATAFILE, O_RDONLY);
- X
- X if (datafd == -1)
- X {
- X Error(DATAFILE);
- X exit(14);
- X }
- X
- X /* snarf in the whole array */
- X if (read(datafd, ksymbs, sizeof(ksymbs)) != sizeof(ksymbs))
- X {
- X Error(DATAFILE);
- X exit(16);
- X }
- X
- X close(datafd);
- X }
- X
- X
- X /* seek to the "var" kernel structure (contains tunables) */
- X if (lseek(memfd, ksymbs[1].n_value, 0) == -1)
- X {
- X Error("memory lseek");
- X exit(17);
- X }
- X
- X /* and read it */
- X if (read(memfd, &v, sizeof(v)) != sizeof(v))
- X {
- X Error("memory read");
- X exit(18);
- X }
- X
- X /* allocate one regblk per region */
- X reg_tab = (struct regblk *) malloc(sizeof(struct regblk) * v.v_region);
- X
- X if (reg_tab == NULL) /* make sure malloc succeeded */
- X {
- X fprintf(stderr, "can't grab %d bytes\n",
- X sizeof(struct regblk) * v.v_region);
- X exit(19);
- X }
- X
- X procloop(); /* loop through proc table */
- X showpage(); /* display the paging info */
- X
- X return(0);
- X}
- X
- X
- Xprocloop()
- X{
- X struct user u;
- X struct region r;
- X struct pregion pr;
- X struct proc p;
- X register struct proc *pp;
- X int incore, npages;
- X int i;
- X
- X pp = &p;
- X reg_cur = reg_tab; /* initialize region table ptr */
- X
- X /* for each process... */
- X for (i=0; i < v.v_proc; i++)
- X {
- X /* seek to process table */
- X if (lseek(memfd, ksymbs[0].n_value + i * sizeof(p), 0) == -1)
- X {
- X Error("memory lseek");
- X exit(21);
- X }
- X
- X if (read(memfd, &p, sizeof(p)) != sizeof(p))
- X {
- X Error("memory read");
- X exit(22);
- X }
- X
- X if (!pp->p_stat) /* if p_stat == 0, no proc in slot */
- X continue;
- X
- X /* use handy sys3b call to read the u block */
- X /* (see what I mean about non-portable!?) */
- X if (sys3b(RDUBLK, pp->p_pid, &u, sizeof(u)) == -1)
- X continue;
- X
- X /* if we're not showing just any process... */
- X if (!eflag)
- X {
- X if (aflag) /* show all procs connected to ttys */
- X {
- X if (u.u_ttyp == 0) /* if no tty, ignore */
- X continue;
- X } else { /* all procs, subject to tty # & uid */
- X
- X /* skip proc if not the right tty */
- X if (tflag && (!u.u_ttyp || ttys != u.u_ttyd))
- X continue;
- X
- X /* skip proc if not the right user id */
- X if (uids != -1 && uids != pp->p_uid)
- X continue;
- X }
- X }
- X
- X incore = npages = 0; /* init counters */
- X
- X while(1) /* zoom thru region table */
- X {
- X /* lseek may fail if conditions change during our
- X run; therefore this is a non-fatal error */
- X if (lseek(memfd, pp->p_region, 0) != (int) pp->p_region)
- X break;
- X
- X /* reads may also fail non-fatally */
- X if (read(memfd, &pr, sizeof(pr)) != sizeof(pr))
- X break;
- X
- X /* if region ptr = 0, it's part of the chain */
- X if (!pr.p_reg)
- X {
- X /* log it */
- X logr((unsigned long) pp->p_pid, npages,
- X incore, u.u_comm);
- X break;
- X }
- X
- X /* point to next spot in the chain */
- X pp->p_region++;
- X
- X /* go to the region itself */
- X if (lseek(memfd, pr.p_reg, 0) != (int) pr.p_reg)
- X break;
- X
- X /* and read it */
- X if (read(memfd, &r, sizeof(r)) != sizeof(r))
- X break;
- X
- X /* if it's PRIVATE or only used in one place,
- X count it against the current process */
- X if (r.r_type == RT_PRIVATE || r.r_refcnt == 1)
- X {
- X npages += r.r_pgsz;
- X incore += r.r_nvalid;
- X } else /* it's shared, so just log it */
- X logs((unsigned long) pr.p_reg, r.r_pgsz,
- X r.r_nvalid, u.u_comm, pr.p_type);
- X }
- X }
- X}
- X
- X
- X/* log a shared region in the table */
- X
- Xlogs(addr, size, incore, cmd, ptype)
- Xchar *addr;
- Xint size, incore;
- Xchar *cmd;
- Xint ptype;
- X{
- X register struct regblk *r;
- X char *cmdtype;
- X
- X switch(ptype) {
- X case PT_TEXT: /* for shared text, save the cmd name */
- X cmdtype = cmd;
- X break;
- X case PT_DATA:
- X cmdtype = "<DATA>";
- X break;
- X case PT_STACK:
- X cmdtype = "<STACK>";
- X break;
- X case PT_SHMEM:
- X cmdtype = "<SHMEM>";
- X break;
- X case PT_DMM:
- X cmdtype = "<DMM>";
- X break;
- X case PT_LIBTXT:
- X cmdtype = "<LIBTXT>";
- X break;
- X case PT_LIBDAT:
- X cmdtype = "<LIBDAT>";
- X break;
- X default:
- X cmdtype = "<OTHER>"; /* punt */
- X break;
- X }
- X
- X /* loop thru the region table, looking to see if this one
- X has been entered before (to avoid dups) */
- X
- X for (r = reg_tab; r < reg_cur; r++)
- X if (r->pid == (unsigned long) addr)
- X return;
- X
- X /* a new region; enter it now */
- X r->pid = (unsigned long) addr;
- X strncpy(r->cmd, cmdtype, DIRSIZ);
- X r->size = size;
- X r->incore = incore;
- X
- X reg_cur++; /* bump table ptr */
- X}
- X
- X
- X
- X/* log a region in the table */
- X
- Xlogr(pid, size, incore, cmd)
- Xunsigned long pid;
- Xint size, incore;
- Xchar *cmd;
- X{
- X register struct regblk *r = reg_cur;
- X
- X r->pid = (unsigned long) pid; /* save the process id */
- X strncpy(r->cmd, cmd, DIRSIZ); /* save the cmd name */
- X r->size = size; /* save the region size */
- X r->incore = incore; /* save the # of pages in core */
- X
- X reg_cur++; /* bump table ptr */
- X}
- X
- X
- Xshowpage()
- X{
- X if (quick)
- X show_q();
- X else
- X show_n();
- X}
- X
- X
- X/* quick mode */
- Xshow_q()
- X{
- X register struct regblk *r;
- X int swapped = 0;
- X int incore = 0;
- X int freemem;
- X
- X /* zip thru region table, adding up pages in core & pages swapped */
- X for (r = reg_tab; r < reg_cur; r++)
- X {
- X if (!r->size)
- X continue;
- X incore += r->incore;
- X swapped += r->size - r->incore;
- X }
- X
- X /* seek to freemem's address */
- X if (lseek(memfd, ksymbs[2].n_value, 0) == -1)
- X {
- X Error("memory lseek");
- X exit(23);
- X }
- X
- X /* read freemem */
- X if (read(memfd, &freemem, sizeof(freemem)) != sizeof(freemem))
- X {
- X Error("memory read");
- X exit(24);
- X }
- X
- X /* print statitistics */
- X printf("%4d pages free in core\n", freemem);
- X printf("%4d pages active in core\n", incore);
- X printf("%4d pages swapped on disk\n", swapped);
- X}
- X
- X
- X/* per-process display */
- Xshow_n()
- X{
- X register struct regblk *r;
- X
- X printf(" PID NSWAP/TOTL COMMAND\n");
- X
- X /* zip thru region table, printing processes & shared regions */
- X for (r = reg_tab; r < reg_cur; r++)
- X {
- X if (r->pid < 64*1024)
- X printf("%5d", r->pid);
- X else
- X printf("SHARE");
- X
- X printf(" %4d %4d %-.*s\n", r->size - r->incore,
- X r->size, DIRSIZ, r->cmd);
- X }
- X}
- X
- X
- X/* print canned error message */
- Xusage()
- X{
- X fprintf(stderr,
- X "usage: %s [-e] [-a] [-u user] [-t tty]\n", PNAME);
- X exit(99);
- X}
- ________This_Is_The_END________
- if test `wc -l < pages.c` -ne 522; then
- echo 'shar: pages.c was damaged during transit (should have been 522 bytes)'
- fi
- fi ; : end of overwriting check
- echo 'x - pages.1'
- if test -f pages.1; then echo 'shar: not overwriting pages.1'; else
- sed 's/^X//' << '________This_Is_The_END________' > pages.1
- X.TH PAGES 1 "2/19/88"
- X.UC 4
- X.SH NAME
- Xpages \- show 3B2 process paging status
- X.SH SYNOPSIS
- X.B pages
- X[-q]
- X[-e]
- X[-a]
- X[-u user]
- X[-t tty]
- X.SH DESCRIPTION
- X.I Pages
- Xscans kernel memory, and displays the size and number of swapped-out
- Xpages for each process.
- XRegions used by multiple processes, such as shared text, shared libraries,
- Xand conventional shared memory are also displayed.
- X.LP
- XThe -e option selects all processes and regions.
- XThe -a option selects all processes with a controlling tty.
- XThe -u and -t options may be used to select processes by
- Xuser id or controlling tty, respectively.
- XInvoking
- X.I pages
- Xwithout arguments is equivalent to using the
- X.B -t
- Xoption for your own terminal.
- X(All of these options work like their corresponding
- X.I ps
- Xoptions.)
- X.LP
- XThe -q option displays a three-line summary of pages free in core,
- Xpages in use in core, and pages swapped to disk. Individual processes
- Xare not displayed.
- X.LP
- XOn the 3B2, one page equals 2K bytes of memory.
- X.SH "SEE ALSO"
- Xps(1)
- X.SH AUTHOR
- XDJ Molny, ihnp4!chinet!djmolny
- X.SH DISCLAIMERS
- X.I Pages
- Xis highly dependent on the AT&T 3B2 System V Release 3 UNIX implementation.
- XIt has only been tested on the AT&T 3B2/400 running SVR3.1.
- X.TP
- X"UNIX" and "3B" are trademarks of AT&T.
- X.SH BUGS
- XBugs? We don't write no stinking bugs!
- ________This_Is_The_END________
- if test `wc -l < pages.1` -ne 48; then
- echo 'shar: pages.1 was damaged during transit (should have been 48 bytes)'
- fi
- fi ; : end of overwriting check
- echo 'x - pages.mk'
- if test -f pages.mk; then echo 'shar: not overwriting pages.mk'; else
- sed 's/^X//' << '________This_Is_The_END________' > pages.mk
- XBIN=/usr/local/bin
- XCFLAGS= -O
- XLDFLAGS= -s
- X
- Xpages: pages.o
- X $(CC) $(LDFLAGS) -o pages pages.o
- X
- Xinstall: pages
- X mv pages $(BIN)
- X chown bin $(BIN)/pages
- X chgrp sys $(BIN)/pages
- X chmod 6755 $(BIN)/pages
- ________This_Is_The_END________
- if test `wc -l < pages.mk` -ne 12; then
- echo 'shar: pages.mk was damaged during transit (should have been 12 bytes)'
- fi
- fi ; : end of overwriting check
- exit 0
-