Subject: bin/36542: better "systat vm" output
To: None <gnats-admin@netbsd.org, netbsd-bugs@netbsd.org>
From: Greg A. Woods <woods@planix.com>
List: netbsd-bugs
Date: 06/23/2007 23:35:00
>Number:         36542
>Category:       bin
>Synopsis:       better "systat vm" output
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    bin-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Sat Jun 23 23:35:00 +0000 2007
>Originator:     Greg A. Woods
>Release:        netbsd-current 2007/06/22
>Organization:
Planix, Inc.; Toronto, Ontario; Canada
>Environment:
	
	
System: NetBSD
>Description:

	this change primarily adds some of the more useful "bufcache"
	values to the more commonly used "vm" output

	the manual page is also cleaned up significantly

	minor bug fixes are also offered

>How-To-Repeat:
	
>Fix:

cvs diff: Diffing usr.bin/systat
Index: usr.bin/systat/bufcache.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.bin/systat/bufcache.c,v
retrieving revision 1.20
diff -u -r1.20 bufcache.c
--- usr.bin/systat/bufcache.c	22 Oct 2006 16:43:24 -0000	1.20
+++ usr.bin/systat/bufcache.c	28 Mar 2007 02:02:11 -0000
@@ -128,8 +128,8 @@
 		wmove(wnd, i, 0);
 		wclrtoeol(wnd);
 	}
-	mvwaddstr(wnd, PAGEINFO_ROWS + 1, 0, "File System          Bufs used"
-	    "   %   kB in use   %  Bufsize kB   %  Util %");
+	mvwaddstr(wnd, PAGEINFO_ROWS + 1, 0, 
+"File System          Bufs used   %   kB in use   %  Bufsize kB   %  Util %");
 	wclrtoeol(wnd);
 }
 
@@ -229,6 +229,10 @@
 			nlisterr(namelist);
 			return(0);
 		}
+		if (namelist[0].n_type == 0) {
+			error("No namelist");
+			return(0);
+		}
 	}
 
 	fetchuvmexp();
Index: usr.bin/systat/cmdtab.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.bin/systat/cmdtab.c,v
retrieving revision 1.23
diff -u -r1.23 cmdtab.c
--- usr.bin/systat/cmdtab.c	18 Feb 2007 17:00:08 -0000	1.23
+++ usr.bin/systat/cmdtab.c	22 Feb 2007 23:53:21 -0000
@@ -52,6 +52,7 @@
 	{ "quit",	global_quit,		"exit systat"},
 	{ "start",	global_interval,	"restart updating display"},
 	{ "stop",	global_stop,		"stop updating display"},
+	{ "?",		global_help,		"show help"},
 	{ .c_name = NULL }
 };
 
@@ -117,7 +118,7 @@
 	{ "names",	netstat_names,	 "show names instead of addresses"},
 	{ "numbers",	netstat_numbers, "show addresses instead of names"},
 	{ "reset",	netstat_reset,	 "return to default display"},
-	{ "show",	netstat_show,	"show current display/ignore settings"},
+	{ "show",	netstat_show,	 "show current display/ignore settings"},
 	{ "tcp",	netstat_tcp,	 "show only tcp connections"},
 	{ "udp",	netstat_udp,	 "show only udp connections"},
 	{ .c_name = NULL }
Index: usr.bin/systat/main.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.bin/systat/main.c,v
retrieving revision 1.40
diff -u -r1.40 main.c
--- usr.bin/systat/main.c	22 Oct 2006 16:43:24 -0000	1.40
+++ usr.bin/systat/main.c	12 Feb 2007 17:08:11 -0000
@@ -74,7 +74,7 @@
 sig_t	sigtstpdfl;
 double avenrun[3];
 int     col;
-int	naptime = 5;
+int	naptime = 1;
 int     verbose = 1;                    /* to report kvm read errs */
 int     hz, stathz, maxslp;
 char    c;
@@ -173,10 +173,8 @@
 		(void)setegid(egid);
 
 	kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
-	if (kd == NULL) {
-		error("%s", errbuf);
-		exit(1);
-	}
+	if (kd == NULL)
+		errx(1, "%s", errbuf);
 
 	/* Get rid of privs for now. */
 	if (nlistf == NULL && memf == NULL)
@@ -201,19 +199,24 @@
 	 * routines to minimize update work by curses.
 	 */
 	if (initscr() == NULL)
-	{
-		warnx("couldn't initialize screen");
-		exit(0);
-	}
+		errx(1, "couldn't initialize screen");
 
 	CMDLINE = LINES - 1;
 	wnd = (*curmode->c_open)();
 	if (wnd == NULL) {
+		move(CMDLINE, 0);
+		clrtoeol();
+		refresh();
+		endwin();
 		warnx("couldn't initialize display");
 		die(0);
 	}
 	wload = newwin(1, 0, 3, 20);
 	if (wload == NULL) {
+		move(CMDLINE, 0);
+		clrtoeol();
+		refresh();
+		endwin();
 		warnx("couldn't set up load average window");
 		die(0);
 	}
@@ -385,7 +388,6 @@
 	move(CMDLINE, 0);
 	clrtoeol();
 	refresh();
-	sleep(5);
 	endwin();
 	exit(1);
 }
Index: usr.bin/systat/systat.1
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.bin/systat/systat.1,v
retrieving revision 1.37
diff -u -r1.37 systat.1
--- usr.bin/systat/systat.1	18 Feb 2007 17:29:26 -0000	1.37
+++ usr.bin/systat/systat.1	22 Feb 2007 23:53:21 -0000
@@ -410,7 +410,22 @@
 The upper left quadrant of the screen shows the number
 of users logged in and the load average over the last one, five,
 and fifteen minute intervals.
-Below this is a list of the
+Below this are statistics on memory utilization.
+The first row of the table reports memory usage only among
+active processes, that is processes that have run in the previous
+twenty seconds.
+The second row reports on memory usage of all processes.
+The first column reports on the number of physical pages
+claimed by processes.
+The second column reports the number of physical pages that
+are devoted to read only text pages.
+The third and fourth columns report the same two figures for
+virtual pages, that is the number of pages that would be
+needed if all processes had all of their pages.
+Finally the last column shows the number of physical pages
+on the free list.
+.Pp
+Below the memory display is a list of the
 average number of processes (over the last refresh interval)
 that are runnable (`r'), in page wait (`p'),
 in disk wait other than paging (`d'),
@@ -422,9 +437,9 @@
 .Pp
 To the right of the process statistics is a column that
 lists the average number of context switches (`Csw'),
-traps (`Trp'; includes page faults), system calls (`Sys'), interrupts (`Int'),
-network software interrupts (`Sof'),
-page faults (`Flt').
+traps (`Traps'; includes page faults), system calls (`SysCa'), interrupts (`Intr'),
+network software interrupts (`Soft'),
+page faults (`Fault').
 .Pp
 Below this are statistics on memory utilization.
 The first row of the table reports memory usage only among
@@ -466,16 +481,75 @@
 number of pages transferred per second over the last refresh interval.
 .Pp
 Below the paging statistics is another columns of paging data.
-From top to bottom, these represent average numbers of copy on write faults
-(`cow'), object cache lookups (`objlk'), object cache hits (`objht'),
-pages zero filled on demand (`zfodw'), number zfod's created (`nzfod'),
-percentage of zfod's used (`%zfod'), number of kernel pages (`kern'),
-number of wired pages (`wire'), number of active pages (`act'), number
-of inactive pages (`inact'), number of free pages (`free'), pages freed
-by daemon (`daefr'), pages freed by exiting processes (`prcfr'), number
-of pages reactivated from freelist (`react'), scans in page out daemon
-(`scan'), revolutions of the hand (`hdrev'), and in-transit blocking page
-faults (`intrn'), per second over the refresh period.
+From top to bottom, these represent:
+.Pp
+.Bl -tag -width Fl -compact
+.It Ic Sq forks
+number of fork() calls
+.It Ic Sq fkppw
+number of fork() calls where parent waits
+.It Ic Sq fksvm
+number of fork() calls where vmspace is shared
+.It Ic Sq pwait
+number of times fault had to wait on a page
+.It Ic Sq relck
+number of times uvmfault_relock() is called
+.It Ic Sq rlkok
+number of times uvmfault_relock() is a success
+.It Ic Sq noram
+number of times fault was out of RAM
+.It Ic Sq ndcpy
+number of times fault clears ``needs copy''
+.It Ic Sq fltcp
+number of times fault promotes with copy (2b)
+.It Ic Sq zfod
+number of times fault promotes with zerofill (2b)
+.It Ic Sq cow
+number of times faulted for anonymous for Copy-On-Write (case 1b)
+.It Ic Sq fmin
+min number of free pages
+.It Ic Sq ftarg
+target number of free pages
+.It Ic Sq itarg
+target number of inactive pages
+.\".It Ic Sq objlk
+.\"object cache lookups
+.\".It Ic Sq objht
+.\"object cache hits
+.\".It Ic Sq zfodw
+.\"pages zero filled on demand
+.\".It Ic Sq nzfod
+.\"number of zfod's created
+.\".It Ic Sq %zfod
+.\"percentage of zfod's used
+.\".It Ic Sq kern
+.\"number of kernel pages
+.It Ic Sq flnan
+number of times fault was out of anonymous pages
+.\".It Ic Sq act
+.\"number of active pages
+.\".It Ic Sq inact
+.\"number of inactive pages
+.\".It Ic Sq free
+.\"number of free pages
+.\".It Ic Sq daefr
+.\"pages freed by daemon
+.\".It Ic Sq prcfr
+.\"pages freed by exiting processes
+.\".It Ic Sq react
+.\"number of pages reactivated from freelist
+.\".It Ic Sq scan
+.\"scans in page out daemon
+.\".It Ic Sq hdrev
+.\"revolutions of the hand
+.\".It Ic Sq intrn
+.\"in-transit blocking page faults per second over the refresh period.
+.It Ic Sq pdfre
+number of pages daemon freed since boot
+.It Ic Sq pdscn
+number of pages daemon scaned since boot
+.El
+.Pp
 Note that the `%zfod' percentage is usually less than 100%,
 however it may exceed 100% if a large number of requests
 are actually used long after they were set up during a
Index: usr.bin/systat/systat.h
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.bin/systat/systat.h,v
retrieving revision 1.11
diff -u -r1.11 systat.h
--- usr.bin/systat/systat.h	26 Feb 2005 22:12:34 -0000	1.11
+++ usr.bin/systat/systat.h	11 Feb 2007 22:29:35 -0000
@@ -61,4 +61,3 @@
 #define NVAL(indx)  namelist[(indx)].n_value
 #define NPTR(indx)  (void *)NVAL((indx))
 #define NREAD(indx, buf, len) kvm_ckread(NPTR((indx)), (buf), (len), # indx)
-#define LONG	(sizeof (long))
Index: usr.bin/systat/vmstat.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.bin/systat/vmstat.c,v
retrieving revision 1.69
diff -u -r1.69 vmstat.c
--- usr.bin/systat/vmstat.c	22 Oct 2006 16:43:24 -0000	1.69
+++ usr.bin/systat/vmstat.c	28 Mar 2007 17:56:32 -0000
@@ -87,6 +87,7 @@
 static	char **intrname;
 static	int nextintsrow;
 static	int disk_horiz = 1;
+static	u_int nbuf;
 
 WINDOW *
 openvmstat(void)
@@ -118,7 +119,6 @@
 	{ .n_name = "_eintrcnt" },
 #define	X_ALLEVENTS	5
 	{ .n_name = "_allevents" },
-	{ .n_name = NULL }
 };
 
 /*
@@ -126,7 +126,7 @@
  */
 #define STATROW		 0	/* uses 1 row and 68 cols */
 #define STATCOL		 2
-#define MEMROW		 9	/* uses 4 rows and 31 cols */
+#define MEMROW		 9	/* uses 5 rows and 31 cols */
 #define MEMCOL		 0
 #define PAGEROW		 2	/* uses 4 rows and 26 cols */
 #define PAGECOL		54
@@ -141,9 +141,9 @@
 #define VMSTATCOL	64
 #define GRAPHROW	 5	/* uses 3 rows and 51 cols */
 #define GRAPHCOL	 0
-#define NAMEIROW	14	/* uses 3 rows and 38 cols */
+#define NAMEIROW	15	/* uses 3 rows and 38 cols (must be MEMROW + 5 + 1) */
 #define NAMEICOL	 0
-#define DISKROW		18	/* uses 5 rows and 50 cols (for 9 drives) */
+#define DISKROW		19	/* uses 5 rows and 50 cols (for 9 drives) */
 #define DISKCOL		 0
 #define DISKCOLWIDTH	 6
 #define DISKCOLEND	INTSCOL
@@ -325,7 +325,7 @@
 
 	mvprintw(STATROW, STATCOL + 4, "users    Load");
 
-	mvprintw(GENSTATROW, GENSTATCOL, "   Csw    Trp    Sys   Int   Sof    Flt");
+	mvprintw(GENSTATROW, GENSTATCOL, "   Csw  Traps SysCal  Intr  Soft  Fault");
 
 	mvprintw(GRAPHROW, GRAPHCOL,
 		"    . %% Sy    . %% Us    . %% Ni    . %% In    . %% Id");
@@ -335,8 +335,8 @@
 
 	mvprintw(PAGEROW, PAGECOL + 8, "PAGING   SWAPPING ");
 	mvprintw(PAGEROW + 1, PAGECOL, "        in  out   in  out ");
-	mvprintw(PAGEROW + 2, PAGECOL + 2, "ops");
-	mvprintw(PAGEROW + 3, PAGECOL, "pages");
+	mvprintw(PAGEROW + 2, PAGECOL, "  ops                     ");
+	mvprintw(PAGEROW + 3, PAGECOL, "pages                     ");
 }
 
 void
@@ -350,10 +350,12 @@
 
 	/* Left hand column */
 
-	mvprintw(MEMROW, MEMCOL,     "           memory totals (in kB)");
-	mvprintw(MEMROW + 1, MEMCOL, "          real  virtual     free");
-	mvprintw(MEMROW + 2, MEMCOL, "Active");
-	mvprintw(MEMROW + 3, MEMCOL, "All");
+	mvprintw(MEMROW + 0, MEMCOL, "Anon                 %%   zero         ");
+	mvprintw(MEMROW + 1, MEMCOL, "Exec                 %%   wired        ");
+	mvprintw(MEMROW + 2, MEMCOL, "File                 %%   inact        ");
+	mvprintw(MEMROW + 3, MEMCOL, "Meta                 %%   bufs         ");
+	mvprintw(MEMROW + 4, MEMCOL, " (kB)        real   swaponly      free");
+	mvprintw(MEMROW + 5, MEMCOL, "Active                                ");
 
 	mvprintw(NAMEIROW, NAMEICOL, "Namei         Sys-cache     Proc-cache");
 	mvprintw(NAMEIROW + 1, NAMEICOL,
@@ -403,7 +405,7 @@
 	mvprintw(VMSTATROW + 11, VMSTATCOL + 10, "fmin");
 	mvprintw(VMSTATROW + 12, VMSTATCOL + 10, "ftarg");
 	mvprintw(VMSTATROW + 13, VMSTATCOL + 10, "itarg");
-	mvprintw(VMSTATROW + 14, VMSTATCOL + 10, "wired");
+	mvprintw(VMSTATROW + 14, VMSTATCOL + 10, "flnan");
 	mvprintw(VMSTATROW + 15, VMSTATCOL + 10, "pdfre");
 
 	if (LINES - 1 > VMSTATROW + 16)
@@ -455,6 +457,11 @@
 	PUTRATE(us, us1, uvmexp->softs, GENSTATROW + 1, GENSTATCOL + 27, 5);
 	PUTRATE(us, us1, uvmexp->faults, GENSTATROW + 1, GENSTATCOL + 33, 6);
 
+	/*
+	 * XXX it sure would be nice if this did what top(1) does and showed
+	 * the utilization of each CPU on a separate line, though perhaps IFF
+	 * the screen is tall enough
+	 */
 	/* Last CPU state not calculated yet. */
 	for (f2 = 0.0, psiz = 0, c = 0; c < CPUSTATES; c++) {
 		i = cpuorder[c];
@@ -486,6 +493,11 @@
 	static int relabel = 0;
 	static int last_disks = 0;
 	static char pigs[] = "pigs";
+	static u_long bufmem;
+	struct buf_sysctl *buffers;
+	int mib[6];
+	size_t size;
+	int extraslop = 0;
 
 	if (relabel) {
 		labelvmstat();
@@ -517,15 +529,74 @@
 
 	/* Memory totals */
 #define pgtokb(pg)	((pg) * (s.uvmexp.pagesize / 1024))
-	putint(pgtokb(s.uvmexp.active), MEMROW + 2, MEMCOL + 6, 8);
-	putint(pgtokb(s.uvmexp.active + s.uvmexp.swpginuse),	/* XXX */
-	    MEMROW + 2, MEMCOL + 15, 8);
-	putint(pgtokb(s.uvmexp.npages - s.uvmexp.free), MEMROW + 3, MEMCOL + 6, 8);
-	putint(pgtokb(s.uvmexp.npages - s.uvmexp.free + s.uvmexp.swpginuse),
-	    MEMROW + 3, MEMCOL + 15, 8);
-	putint(pgtokb(s.uvmexp.free), MEMROW + 2, MEMCOL + 24, 8);
-	putint(pgtokb(s.uvmexp.free + s.uvmexp.swpages - s.uvmexp.swpginuse),
-	    MEMROW + 3, MEMCOL + 24, 8);
+
+	putint(pgtokb(s.uvmexp.anonpages),				MEMROW + 0, MEMCOL + 7, 10);
+	putint((s.uvmexp.anonpages * 100 + 0.5) / s.uvmexp.npages,	MEMROW + 0, MEMCOL + 17, 4);
+
+	putint(pgtokb(s.uvmexp.zeropages),				MEMROW + 0, MEMCOL + 30, 8);
+
+	putint(pgtokb(s.uvmexp.execpages),				MEMROW + 1, MEMCOL + 7, 10);
+	putint((s.uvmexp.execpages * 100 + 0.5) / s.uvmexp.npages,	MEMROW + 1, MEMCOL + 17, 4);
+
+	putint(pgtokb(s.uvmexp.wired),					MEMROW + 1, MEMCOL + 30, 8);
+
+	putint(pgtokb(s.uvmexp.filepages),				MEMROW + 2, MEMCOL + 7, 10);
+	putint((s.uvmexp.filepages * 100 + 0.5) / s.uvmexp.npages,	MEMROW + 2, MEMCOL + 17, 4);
+
+	putint(pgtokb(s.uvmexp.inactive),				MEMROW + 2, MEMCOL + 30, 8);
+
+	/* Get total size of metadata buffers */
+	size = sizeof(bufmem);
+	if (sysctlbyname("vm.bufmem", &bufmem, &size, NULL, 0) < 0) {
+		error("can't get buffers size: %s\n", strerror(errno));
+		return;
+	}
+
+	/* Get number of metadata buffers */
+	size = 0;
+	buffers = NULL;
+	mib[0] = CTL_KERN;
+	mib[1] = KERN_BUF;
+	mib[2] = KERN_BUF_ALL;
+	mib[3] = KERN_BUF_ALL;
+	mib[4] = (int)sizeof(struct buf_sysctl);
+	mib[5] = INT_MAX; /* we want them all */
+again:
+	if (sysctl(mib, 6, NULL, &size, NULL, 0) < 0) {
+		error("can't get buffers size: %s\n", strerror(errno));
+		return;
+	}
+	if (size == 0) {
+		error("buffers size is zero: %s\n", strerror(errno));
+		return;
+	}
+	size += extraslop * sizeof(struct buf_sysctl);
+	buffers = malloc(size);
+	if (buffers == NULL) {
+		error("can't allocate buffers: %s\n", strerror(errno));
+		return;
+	}
+	if (sysctl(mib, 6, buffers, &size, NULL, 0) < 0) {
+		free(buffers);
+		if (extraslop == 0) {
+			extraslop = 100;
+			goto again;
+		}
+		error("can't get buffers: %s\n", strerror(errno));
+		return;
+	}
+	free(buffers);			/* XXX there must be a better way! */
+	nbuf = size / sizeof(struct buf_sysctl);
+
+	putint((int) (bufmem / 1024),		MEMROW + 3, MEMCOL + 5, 12);
+	putint((int) ((bufmem * 100) + 0.5) / s.uvmexp.pagesize / s.uvmexp.npages,
+						MEMROW + 3, MEMCOL + 17, 4);
+	putint(nbuf,				MEMROW + 3, MEMCOL + 30, 8);
+
+	putint(pgtokb(s.uvmexp.active),		MEMROW + 5, MEMCOL + 7, 10);
+	putint(pgtokb(s.uvmexp.swpgonly),	MEMROW + 5, MEMCOL + 18, 10);
+	putint(pgtokb(s.uvmexp.free),		MEMROW + 5, MEMCOL + 28, 10);
+
 #undef pgtokb
 
 	/* Namei cache */
@@ -641,7 +712,7 @@
 	putint(s.uvmexp.freemin, VMSTATROW + 11, VMSTATCOL, 9);
 	putint(s.uvmexp.freetarg, VMSTATROW + 12, VMSTATCOL, 9);
 	putint(s.uvmexp.inactarg, VMSTATROW + 13, VMSTATCOL, 9);
-	putint(s.uvmexp.wired, VMSTATROW + 14, VMSTATCOL, 9);
+	putint(s.uvmexp.fltnoanon, VMSTATROW + 14, VMSTATCOL, 9);
 	PUTRATE(s, s1, uvmexp.pdfreed, VMSTATROW + 15, VMSTATCOL, 9);
 	if (LINES - 1 > VMSTATROW + 16)
 		PUTRATE(s, s1, uvmexp.pdscans, VMSTATROW + 16, VMSTATCOL, 9);
@@ -784,7 +855,7 @@
 	drvreadstats();
 	NREAD(X_NCHSTATS, &stats->nchstats, sizeof stats->nchstats);
 	if (nintr)
-		NREAD(X_INTRCNT, stats->intrcnt, nintr * LONG);
+		NREAD(X_INTRCNT, stats->intrcnt, nintr * sizeof(long));
 	for (i = 0; i < nevcnt; i++)
 		KREAD(ie_head[i].ie_count, &stats->evcnt[i],
 		      sizeof stats->evcnt[i]);

>Unformatted: