[comp.bugs.sys5] RSS for System V -- does it really, really work?

bauer@loligo (Jeff Bauer) (02/09/89)

Here's a simple hack for System V running on an ETA-10 that shows
the actual working set of running processs.  It should be somewhat
adaptable to any System V I'd think.  On ETA-10s it's a necessity to know
just how much real memory a process is using (and "ps" doesn't have
bsd's nice "RSS" field!).  Incidentally, anybody know how to get the size
of the proc[] table in System V (ala NPROC)?  Also, is adding up the
"r_nvalid" pages for all the assigned pregions/regions of a process the
correct approach?

I stuck this in comp.bugs.sys5 to limit the audience to System V 
interested readers and to see if anybody can answer these questions for
their port of SysV.  Don't be offended, it's a small shar!

--- cut here ---
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	Makefile
#	ws.1
#	ws.1.cat
#	ws.c
sed 's/^X//' << 'SHAR_EOF' > Makefile
XDEFINES=
XCFLAGS=-O
X
Xws:	ws.c
X	$(CC) $@.c -o $@ $(CFLAGS) $(DEFINES)
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > ws.1
X.TH WS 1
X.SH NAME
X\fBws\fR \- display process working set
X.SH USAGE
X.B ws
X[\-a] [\-t] [\-u username] [\-p process_id]
X.SH DESCRIPTION
XThe 
X.I ws
Xcommand displays memory usage for a process by displaying the number of
Xpages assigned to the current working set.
XWith no options
X.I ws
Xwill display information for all processes that are not owned by "root".  If
X.I \-a
Xis specified,
X.I ws
Xwill display the working set information for all processes on the system.
XThe 
X.I \-u
Xoption accepts an option argument specifiying that only processes owned
Xby the specified username are to be shown.  The
X.I \-p
Xoption accepts an option argument specifying that only the working set for
Xa single process ID is to be shown.  The
X.I \-t
Xoption switches the output from showing individual process working sets
X(the default) to showing totals for all working sets of processes that
Xmatch the selection options above.
X.SH OUTPUT
X.TP
X.B PID
Xprocess ID.
X.TP
X.B USER
Xeffective owner of process.
X.TP
X.B SZ
Xprocess size (same as on ps(1) "the size (in pages or
Xclocks) of the swappable process's image in main memory").
X.TP
X.B WS
XThe number of actual pages in main memory for this process, ALWAYS
Xgiven in small page units, even if the process uses a combination of
Xsmall and large pages.  The last five columns break down WS even more,
Xwhere each category of memory region is described by a pair of numbers,
Xseparated by a comma.  The first number indicates the number of small pages
Xand the second number the number of large pages for that category.
X.TP
X.B STEXT
XThe number of small and large pages in main memory used as shared text
X(program code).
X.TP
X.B USTEXT
XThe number of small and large pages in main memory used as unshared (private)
Xtext.
X.TP
X.B DATA
XThe number of small and large pages in main memory used for data.
X.TP
X.B STACK
XThe number of small and large pages in main memory used for stack space.
X.TP
X.B OTHER
XThe number of small and large pages in main memory used for other
Xmemory types, such as shared library text, shared library
Xdata, and shared memory.
X.SH EXAMPLES
X$ ws
X.br
X PID     USER SZ  WS   STEXT  USTEXT    DATA   STACK   OTHER
X.br
X 150 operator  8   7   4,  0   0,  0   2,  0   1,  0   0,  0
X.br
X1435  jtbauer  8   7   4,  0   0,  0   2,  0   1,  0   0,  0
X.br
X1509  tjensen  8   7   4,  0   0,  0   2,  0   1,  0   0,  0
X.br
X1538  jtbauer  5   4   1,  0   0,  0   2,  0   1,  0   0,  0
X.br
X$ ws -t -a
X.br
X          SZ  WS   STEXT  USTEXT    DATA   STACK   OTHER
X.br
XTOTALS : 202 170  64,  0   0,  0  75,  0  31,  0   0,  0
X.br
XSmall page size     = 65536 bytes (8192 words)
X.br
XLarge page size     = 524288 bytes (65536 words)
X.br
XVirtual working set = 202 small pages (12 MB)
X.br
XReal working set    = 170 small pages (10 MB)
X.SH CAVEATS
XPages that are part of shared text (STEXT) and shared libraries or
Xshared memory (OTHER) may be accounted for in more than one place,
Xgiving a false impression by inflating the amount of actual real memory
Xin use.  This is only a problem, of course, for multiple occurrences of
Xthe same binary in different processes.
X.SH SEE ALSO
X.BR ps(1), pt(1).
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > ws.1.cat
XWS(1)                     USER COMMANDS                     WS(1)
X
X
X
XNAME
X     ws - display process working set
X
XUSAGE
X     ws [-a] [-t] [-u username] [-p process_id]
X
XDESCRIPTION
X     The _w_s command  displays  memory  usage  for  a  process  by
X     displaying the number of pages assigned to the current work-
X     ing set.  With no options _w_s will  display  information  for
X     all processes that are not owned by "root".  If -_a is speci-
X     fied, _w_s will display the working set  information  for  all
X     processes  on  the  system.  The -_u option accepts an option
X     argument specifiying that only processes owned by the speci-
X     fied  username  are  to  be shown.  The -_p option accepts an
X     option argument specifying that only the working set  for  a
X     single  process  ID  is to be shown.  The -_t option switches
X     the output from showing individual process working sets (the
X     default) to showing totals for all working sets of processes
X     that match the selection options above.
X
XOUTPUT
X     PID  process ID.
X
X     USER effective owner of process.
X
X     SZ   process size (same as on ps(1) "the size (in  pages  or
X          clocks)  of  the  swappable  process's  image  in  main
X          memory").
X
X     WS   The number of actual pages in main memory for this pro-
X          cess,  ALWAYS  given  in  small page units, even if the
X          process uses a combination of small  and  large  pages.
X          The  last  five  columns break down WS even more, where
X          each category of memory region is described by  a  pair
X          of  numbers,  separated  by  a comma.  The first number
X          indicates the number of  small  pages  and  the  second
X          number the number of large pages for that category.
X
X     STEXT
X          The number of small and large pages in main memory used
X          as shared text (program code).
X
X     USTEXT
X          The number of small and large pages in main memory used
X          as unshared (private) text.
X
X     DATA The number of small and large pages in main memory used
X          for data.
X
X     STACK
X          The number of small and large pages in main memory used
X          for stack space.
X
X     OTHER
X          The number of small and large pages in main memory used
X          for  other  memory  types, such as shared library text,
X          shared library data, and shared memory.
X
XEXAMPLES
X     $ ws
X      PID     USER SZ  WS   STEXT  USTEXT    DATA   STACK   OTHER
X      150 operator  8   7   4,  0   0,  0   2,  0   1,  0   0,  0
X     1435  jtbauer  8   7   4,  0   0,  0   2,  0   1,  0   0,  0
X     1509  tjensen  8   7   4,  0   0,  0   2,  0   1,  0   0,  0
X     1538  jtbauer  5   4   1,  0   0,  0   2,  0   1,  0   0,  0
X
X     $ ws -t -a
X               SZ  WS   STEXT  USTEXT    DATA   STACK   OTHER
X     TOTALS : 202 170  64,  0   0,  0  75,  0  31,  0   0,  0
X     Small page size     = 65536 bytes (8192 words)
X     Large page size     = 524288 bytes (65536 words)
X     Virtual working set = 202 small pages (12 MB)
X     Real working set    = 170 small pages (10 MB)
X
XCAVEATS
X     Pages that are  part  of  shared  text  (STEXT)  and  shared
X     libraries  or  shared memory (OTHER) may be accounted for in
X     more than one place, giving a false impression by  inflating
X     the  amount  of  actual  real memory in use.  This is only a
X     problem, of course, for multiple  occurrences  of  the  same
X     binary in different processes.
X
XSEE ALSO
X     ps(1),pt(1).
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > ws.c
X/*
X	ws.c - display working set (regions) of processes
X
X	ws [-a] [-t] [-u user] [-p pid]
X
X								*/
X#include <stdio.h>
X#include <fcntl.h>
X#include <sys/types.h>
X#include <sys/param.h>
X#include <sys/immu.h>
X#include <sys/region.h>
X#include <sys/proc.h>
X#include <nlist.h>
X#include <pwd.h>
X
X#define	PROC	0
X#define PREGPP	1
X#define MAXPRT	16
X#define TRUE 	1
X#define	FALSE	0
X
X/* Namelist for various kernel tables */
X
Xstruct nlist nl[] = {
X  {"proc", (int32) 0, (int32) 0, (uint32) 0, (char) 0, (char) 0},
X  {"pregpp", (int32) 0, (int32) 0, (uint32) 0, (char) 0, (char) 0},
X  {NULL,    (int32) 0, (int32) 0, (uint32) 0, (char) 0, (char) 0}
X};
X
Xstruct memuse {
X	uint stext;	/* shared text (pages) */
X	uint ustext;	/* unshared text */
X	uint data;	/* data pages */
X	uint stack;	/* stack pages */
X	uint other;	/* other regions : shmem, dmm, libtxt, libdat */
X};
X
X#define	readmem(mem_addr, buf_addr) \
X	if (lseek(mf, (long) mem_addr, 0) < 0) { \
X	  perror("lseek"); \
X	  exit(1); \
X	} \
X	if (read(mf, (char *) &buf_addr, sizeof(buf_addr)) < 0) { \
X	    perror("read"); \
X	  exit(1); \
X	}
X
X/*	Global variables	*/
X
Xint mf;			/* memory file - /dev/kmem */
Xchar kernel_name[] 	= { "/unix" };
Xchar mem_name[] 	= { "/dev/kmem" };
Xulong pbase;
Xstruct proc *pp;
Xstruct proc pb;
Xpreg_t preg;
Xreg_t reg, *regp;
X/* pde_t pde;
Xpde_t *ptable; */
Xint pid, pregpp;
Xstruct memuse sp;	/* small pages */
Xstruct memuse lp;	/* large pages */
Xstruct memuse totsp;
Xstruct memuse totlp;
Xstruct memuse *mymem;
Xstruct passwd *pwe;
Xint showroot, showtot;
Xchar uname[9];
Xchar user[32];
X
X/* Externally-referenced functions */
X
Xextern long lseek();
Xextern void exit();
Xextern struct passwd *getpwuid();
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	int i, j, c;
X	int ws, totws, totpsize;
X	extern char *optarg;
X
X	pid = 0;
X	user[0] = '\0';
X	showroot = FALSE;
X	showtot = FALSE;
X	while ((c = getopt(argc, argv, "atu:p:")) != -1)
X	  switch (c) {
X	  case 'a' :
X	    showroot = TRUE;
X	    break;
X	  case 't' :
X	    showtot = TRUE;
X	    break;
X	  case 'u' :
X	    strcpy(user, optarg);
X	    break;
X	  case 'p' :
X	    sscanf(optarg, "%d", &pid);
X	    break;
X	  }
X	if (strcmp(user, "root") == 0) showroot = TRUE;
X	if (showroot == TRUE) {	/* override other settings */
X	  user[0] = '\0';
X	  pid = 0;
X	}
X	  
X	/* read the kernel name list, using selected symbols */
X
X	if (nlist(kernel_name, nl)) {
X	  perror("nlist");
X	  exit(1);
X	}
X
X	if ((mf = open(mem_name, O_RDONLY)) < 0) {
X	  perror("open");
X	  exit(1);
X	}
X
X	if ((nl[PROC].n_value == 0) && (nl[PROC].n_type == 0)) {
X	  fprintf(stderr, "Symbol `proc' not found.\n");
X	  exit(1);
X	}
X	else {
X	  pbase = nl[PROC].n_value;
X	}
X	if ((nl[PREGPP].n_value == 0) && (nl[PREGPP].n_type == 0)) {
X	  fprintf(stderr, "Symbol `pregpp' not found.\n");
X	  exit(1);
X	}
X	else {
X	  readmem(nl[PREGPP].n_value, pregpp);
X	  if (pregpp > MAXPRT) {
X	    fprintf(stderr,"pregpp > MAXRT!\n");
X	    exit(1);
X	  }
X	}
X
X	totsp.stext = 0;
X	totsp.ustext = 0;
X	totsp.data = 0;
X	totsp.stack = 0;
X	totsp.other = 0;
X	totlp.stext = 0;
X	totlp.ustext = 0;
X	totlp.data = 0;
X	totlp.stack = 0;
X	totlp.other = 0;
X	totws = 0;
X	totpsize = 0;
X
X	if (showtot == FALSE)
X	  printf("   PID     USER   SZ   WS    STEXT   USTEXT     DATA    STACK    OTHER\n");
X
X	for (i = 0; i < 200; i++) {	/* yuck! where's NPROC defined? */
X	  readmem(pbase+(i*sizeof(struct proc)), pb);
X	  if (pb.p_pid != 0) {
X	    if (((pid == 0) || ((pid != 0) && (pb.p_pid == pid)))) {
X	      if ((showroot == FALSE) && (pb.p_suid == 0)) continue;
X	      
X	      /* Hunt through the pregions & regions of those pregions
X		 assigned to this process. */
X	
X	      sp.stext = 0;
X	      sp.ustext = 0;
X	      sp.data = 0;
X	      sp.stack = 0;
X	      sp.other = 0;
X	      lp.stext = 0;
X	      lp.ustext = 0;
X	      lp.data = 0;
X	      lp.stack = 0;
X	      lp.other = 0;
X	      ws = 0;
X
X	      if ((pwe = getpwuid(pb.p_suid)) == NULL)
X		strcpy(uname, "(bogus)");
X	      else
X		strcpy(uname, pwe->pw_name);
X	      if ((user[0] != '\0') && (strcmp(user,uname) != 0)) continue;
X
X	      for (j = 0; j < pregpp; j++) {
X	        readmem((pb.p_region+j), preg);
X	        if (preg.p_reg != NULL) {
X		  readmem(preg.p_reg, reg);
X		  mymem = NULL;
X		  if ((reg.r_flags & RG_SPAGE) != 0) {
X		    mymem = &sp;
X		    ws += reg.r_nvalid;
X		  }
X		  if ((reg.r_flags & RG_LPAGE) != 0) {
X		    mymem = &lp;
X		    ws += (reg.r_nvalid * NSPPLP); /* keep ws in small pages */
X		  }
X		  if (mymem == NULL) {
X		    fprintf("Error - region not of small or large pages!\n");
X		    continue;
X		  }
X	          switch (preg.p_type) {
X		    case PT_TEXT :
X		      if (reg.r_type == RT_STEXT)
X		        mymem->stext += reg.r_nvalid;
X		      else
X		        mymem->ustext += reg.r_nvalid;
X		      break;
X		    case PT_DATA :
X		      mymem->data += reg.r_nvalid;
X		      break;
X		    case PT_STACK :
X		      mymem->stack += reg.r_nvalid;
X		      break;
X		    default :
X		      mymem->other += reg.r_nvalid;
X		      break;
X		  }
X	        }
X	      }
X	      totsp.stext += sp.stext;
X	      totsp.ustext += sp.ustext;
X	      totsp.data += sp.data;
X	      totsp.stack += sp.stack;
X	      totsp.other += sp.other;
X	      totlp.stext += lp.stext;
X	      totlp.ustext += lp.ustext;
X	      totlp.data += lp.data;
X	      totlp.stack += lp.stack;
X	      totlp.other += lp.other;
X	      totws += ws;
X	      totpsize += pb.p_size;
X
X	      if (showtot == FALSE)
X	        printf("%6d %8s  %3d  %3d  %3d,%3d  %3d,%3d  %3d,%3d  %3d,%3d  %3d,%3d\n",
X		  pb.p_pid, uname, pb.p_size, ws, sp.stext, lp.stext, 
X		  sp.ustext, lp.ustext, sp.data, lp.data, sp.stack, lp.stack, 
X		  sp.other, lp.other);
X	    }
X	  }
X	}
X	if (showtot == TRUE) {
X	  printf("          SZ  WS    STEXT   USTEXT     DATA    STACK    OTHER\n");
X	  printf("TOTALS : %3d %3d  %3d,%3d  %3d,%3d  %3d,%3d  %3d,%3d  %3d,%3d\n",
X	    totpsize, totws, totsp.stext, totlp.stext, totsp.ustext, 
X	    totlp.ustext, totsp.data, totlp.data, totsp.stack, totlp.stack, 
X	    totsp.other, totlp.other);
X	  printf("Small page size     = %d bytes (%d words)\n", NBPP,
X	  (NBPP/8));
X	  printf("Large page size     = %d bytes (%d words)\n", NBPLP,
X	  (NBPLP/8));
X	  printf("Virtual working set = %d small pages (%d MB)\n",
X	    totpsize, ((totpsize*NBPP)/(1024*1024)));
X	  printf("Real working set    = %d small pages (%d MB)\n",
X	    totws, ((totws*NBPP)/(1024*1024)));
X	}
X}
SHAR_EOF
exit

pim@ctisbv.UUCP (Pim Zandbergen) (02/10/89)

In article <566@loligo.cc.fsu.edu> bauer@loligo (Jeff Bauer) writes:
>Here's a simple hack for System V running on an ETA-10 that shows
>the actual working set of running processs.  It should be somewhat
>adaptable to any System V I'd think.

On an AT&T 3B2/600 with System V release 3.1.1,
make says:

: 	cc ws.c -o ws -O -Dint32=long -Duint32=ulong -s -lc_s
: "ws.c", line 190: RG_SPAGE undefined
: "ws.c", line 194: RG_LPAGE undefined
: "ws.c", line 196: NSPPLP undefined
: "ws.c", line 250: NBPLP undefined
: *** Error code 1
: 
: Stop.

I did...

-- 
--------------------+----------------------+-----------------------------------
Pim Zandbergen      | phone: +31 70 542302 | CTI Software BV
pim@ctisbv.UUCP     | fax  : +31 70 512837 | Laan Copes van Cattenburch 70
...!uunet!mcvax!hp4nl!ctisbv!pim           | 2585 GD The Hague, The Netherlands