Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 10 Apr 2021 08:36:04 -0400
From:      Yoshihiro Ota <ota@j.email.ne.jp>
To:        tech-lists <tech-lists@zyxst.net>
Cc:        freebsd-current@freebsd.org
Subject:   Re: systat -swap to display large swap space users
Message-ID:  <20210410083604.26b852e49ea29d05095a6beb@j.email.ne.jp>
In-Reply-To: <YGmTRxRxm5dm0gRM@ceres.zyxst.net>
References:  <20210402201214.bdd588a10d430c393585e1e3@j.email.ne.jp> <YGmTRxRxm5dm0gRM@ceres.zyxst.net>

next in thread | previous in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.

--Multipart=_Sat__10_Apr_2021_08_36_04_-0400_qz=khajYNKZkDwkZ
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit

Hi,

How about these now?
These include extra improvements to other parts of systat.

These patches create new files.
If you already applies previosly, please check and clean up new files.

Regards,
Hiro

On Sun, 4 Apr 2021 10:21:59 +0000
tech-lists <tech-lists@zyxst.net> wrote:

> Hi,
> 
> On Fri, Apr 02, 2021 at 08:12:14PM -0400, Yoshihiro Ota wrote:
> >Hi,
> >
> >We do not seem to have a nice way to see current swap space usage per process.
> >I updated systat to use libprocstat to obtain such infomation and display
> >along with swap devise/file stats.
> 
> Unfotunately your patch gets rejected on recent stable/13 and main/14
> 
> -- 
> J.

--Multipart=_Sat__10_Apr_2021_08_36_04_-0400_qz=khajYNKZkDwkZ
Content-Type: text/x-diff;
 name="systat-swap-releng13.0.diff"
Content-Disposition: attachment;
 filename="systat-swap-releng13.0.diff"
Content-Transfer-Encoding: 7bit

diff --git a/usr.bin/systat/Makefile b/usr.bin/systat/Makefile
index ca3f7ed72ce..bfbe1336d29 100644
--- a/usr.bin/systat/Makefile
+++ b/usr.bin/systat/Makefile
@@ -5,7 +5,7 @@
 
 PROG=	systat
 SRCS=	cmds.c cmdtab.c devs.c fetch.c iostat.c keyboard.c main.c sysput.c \
-	netcmds.c netstat.c pigs.c swap.c icmp.c \
+	netcmds.c netstat.c pigs.c proc.c swap.c icmp.c \
 	mode.c ip.c sctp.c tcp.c zarc.c \
 	vmstat.c convtbl.c ifcmds.c ifstat.c
 
@@ -16,6 +16,6 @@ CFLAGS+= -DINET6
 
 WARNS?=	1
 
-LIBADD=	ncursesw m devstat kvm util
+LIBADD=	ncursesw m devstat kvm util procstat
 
 .include <bsd.prog.mk>
diff --git a/usr.bin/systat/devs.c b/usr.bin/systat/devs.c
index b293796c2c7..87d04f989ea 100644
--- a/usr.bin/systat/devs.c
+++ b/usr.bin/systat/devs.c
@@ -426,12 +426,6 @@ dsshow2(int diskcol, int diskrow, int dn, int lc, struct statinfo *now, struct s
 	putlongdouble(device_busy, diskrow + 4, lc, 5, 0, 0);
 }
 
-static void
-dsshow3(int diskcol, int diskrow, int dn, int lc, struct statinfo *now, struct statinfo *then)
-{
-	dsshow2(diskcol, diskrow, dn, lc, now, then);
-}
-
 void
 dsshow(int maxdrives, int diskcol, int diskrow, struct statinfo *now, struct statinfo *then)
 {
@@ -439,5 +433,5 @@ dsshow(int maxdrives, int diskcol, int diskrow, struct statinfo *now, struct sta
 
 	for (i = 0, lc = 0; i < num_devices && lc < maxdrives; i++)
 		if (dev_select[i].selected)
-			dsshow3(diskcol, diskrow, i, ++lc, now, then);
+			dsshow2(diskcol, diskrow, i, ++lc, now, then);
 }
diff --git a/usr.bin/systat/devs.h b/usr.bin/systat/devs.h
index cbedd844290..79a44a6c3f5 100644
--- a/usr.bin/systat/devs.h
+++ b/usr.bin/systat/devs.h
@@ -2,7 +2,7 @@
  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
  *
  * Copyright (c) 1998 David E. O'Brien
- *               2015 Yoshihiro Ota
+ *               2015, 2021 Yoshihiro Ota
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -34,6 +34,8 @@
 
 #include <devstat.h>
 
+#define DISKHIGHT 5
+
 int dsinit(int);
 void dsgetinfo(struct statinfo *);
 int dscmd(const char *, const char *, int, struct statinfo *);
diff --git a/usr.bin/systat/extern.h b/usr.bin/systat/extern.h
index 6e3117bece4..322065f7931 100644
--- a/usr.bin/systat/extern.h
+++ b/usr.bin/systat/extern.h
@@ -168,6 +168,7 @@ char	*sysctl_dynread(const char *, size_t *);
 void	 sysputpage(WINDOW* , int, int, int, uint64_t, int);
 void	 sysputspaces(WINDOW* , int, int, int);
 void	 sysputstrs(WINDOW* , int, int, int);
+void	 sysputXs(WINDOW* , int, int, int);
 void	 sysputuint64(WINDOW* , int, int, int, uint64_t, int);
 void	 sysputwuint64(WINDOW* , int, int, int, uint64_t, int);
 
diff --git a/usr.bin/systat/main.c b/usr.bin/systat/main.c
index b5a19d381ad..b84351379f4 100644
--- a/usr.bin/systat/main.c
+++ b/usr.bin/systat/main.c
@@ -135,6 +135,21 @@ parse_cmd_args (int argc, char **argv)
 
 }
 
+static void
+resize(int signo __unused)
+{
+
+	endwin();
+	refresh();
+	clear();
+
+	CMDLINE = LINES - 1;
+	labels();
+	display();
+	status();
+}
+
+
 int
 main(int argc, char **argv)
 {
@@ -191,6 +206,7 @@ main(int argc, char **argv)
 	signal(SIGINT, die);
 	signal(SIGQUIT, die);
 	signal(SIGTERM, die);
+	signal(SIGWINCH, resize);
 
 	/*
 	 * Initialize display.  Load average appears in a one line
diff --git a/usr.bin/systat/pigs.c b/usr.bin/systat/pigs.c
index d9f3f8d4ad1..879f6548d86 100644
--- a/usr.bin/systat/pigs.c
+++ b/usr.bin/systat/pigs.c
@@ -56,13 +56,13 @@ __FBSDID("$FreeBSD$");
 #include "systat.h"
 #include "extern.h"
 
-int compar(const void *, const void *);
+static int compar(const void *, const void *);
 
 static int nproc;
 static struct p_times {
 	float pt_pctcpu;
 	struct kinfo_proc *pt_kp;
-} *pt;
+} *pt = NULL;
 
 static int    fscale;
 static double  lccpu;
@@ -90,7 +90,7 @@ showpigs(void)
 	const char *uname, *pname;
 	char pidname[30];
 
-	if (pt == NULL)
+	if (nproc == 0)
 		return;
 
 	qsort(pt, nproc, sizeof (struct p_times), compar);
@@ -146,23 +146,20 @@ fetchpigs(void)
 	float ftime;
 	float *pctp;
 	struct kinfo_proc *kpp;
-	static int lastnproc = 0;
+	static int maxnproc = 0;
 
 	if ((kpp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc)) == NULL) {
 		error("%s", kvm_geterr(kd));
-		if (pt)
-			free(pt);
+		nproc = 0;
 		return;
 	}
-	if (nproc > lastnproc) {
-		free(pt);
-		if ((pt =
-		    malloc(nproc * sizeof(struct p_times))) == NULL) {
+	if (nproc > maxnproc) {
+		if ((pt = realloc(pt, nproc * sizeof(*pt))) == NULL) {
 			error("Out of memory");
 			die(0);
 		}
+		maxnproc = nproc;
 	}
-	lastnproc = nproc;
 	/*
 	 * calculate %cpu for each proc
 	 */
diff --git a/usr.bin/systat/proc.c b/usr.bin/systat/proc.c
new file mode 100644
index 00000000000..0def660d2dc
--- /dev/null
+++ b/usr.bin/systat/proc.c
@@ -0,0 +1,192 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Yoshihiro Ota
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+
+#include <libprocstat.h>
+
+#include <curses.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "subs.h"
+#include "systat.h"
+#include "extern.h"
+
+static int compar(const void *, const void *);
+
+static unsigned int nproc;
+
+static struct procstat *prstat = NULL;
+static struct proc_usage {
+	pid_t pid;
+	uid_t uid;
+	char command[COMMLEN+1];
+	uint64_t total;
+	uint64_t swap;
+} *pu = NULL, pzero;
+
+
+void
+closeproc(WINDOW *w)
+{
+	procstat_close(prstat);
+	prstat = NULL;
+	if (w == NULL)
+		return;
+	wclear(w);
+	wrefresh(w);
+	delwin(w);
+}
+
+void
+procshow(int col, int hight, uint64_t totalswappages)
+{
+	int i, j, y, offset;
+	int rate;
+	const char *uname, *pname;
+	char buf[30];
+
+	if (nproc > 1)
+		qsort(pu, nproc, sizeof (struct proc_usage), compar);
+	y = col + 1 /* HEADING */;
+	offset = 0;
+	for (i = 0; i < hight; i++, y++) {
+		wmove(wnd, y, 0);
+		wclrtoeol(wnd);
+		if (i >= nproc)
+			continue;
+
+		uname = user_from_uid(pu[i].uid, 0);
+		snprintf(buf, sizeof(buf), "%6d %-10s %-10.10s", pu[i].pid,
+		    uname, pu[i].command);
+		offset = 6 + 1 + 10 + 1 + 10 + 1;
+		mvwaddstr(wnd, y, 0, buf);
+		sysputuint64(wnd, y, offset, 4, pu[i].swap, 0);
+		offset += 4;
+		mvwaddstr(wnd, y, offset, " / ");
+		offset += 3;
+		sysputuint64(wnd, y, offset, 4, pu[i].total, 0);
+		offset += 4;
+
+		rate = (pu[i].total > 1 ? 100 * pu[i].swap / pu[i].total : 0);
+		snprintf(buf, sizeof(buf), "%3d%%", rate);
+		mvwaddstr(wnd, y, offset, buf);
+		sysputXs(wnd, y, offset + 5, rate / 10);
+
+		rate = 100 * byte_to_page(pu[i].swap) / totalswappages;
+		snprintf(buf, sizeof(buf), "%3d%%", rate);
+		mvwaddstr(wnd, y, offset + 16, buf);
+		sysputXs(wnd, y, offset + 21, rate / 10);
+	}
+}
+
+int
+procinit(void)
+{
+
+	if (prstat != NULL)
+		return(1);
+	prstat = procstat_open_sysctl();
+	return (prstat == NULL);
+}
+
+void
+procgetinfo(void)
+{
+	static unsigned int maxnproc = 0;
+	int i, j, k;
+	unsigned int cnt;
+	struct kinfo_proc *kipp;
+	struct kinfo_vmentry *freep, *kve;
+
+	kipp = procstat_getprocs(prstat, KERN_PROC_PROC, 0, &nproc);
+	if (kipp == NULL) {
+		error("procstat_getprocs()");
+		return;
+	}
+
+	if (nproc > maxnproc) {
+		maxnproc = nproc;
+		if ((pu = realloc(pu, maxnproc * sizeof(*pu))) == NULL) {
+			error("Out of memory");
+			die(0);
+		}
+	}
+	for (i = 0, k = 0; i < nproc; i++) {
+		freep = procstat_getvmmap(prstat, &kipp[i], &cnt);
+		if (freep == NULL) {
+			continue;
+		}
+		pu[k].swap = 0;
+		for (j = 0; j < cnt; j++) {
+			kve = &freep[j];
+			if (kve->kve_type == KVME_TYPE_SWAP) {
+				pu[k].swap += kve->kve_end - kve->kve_start;
+				pu[k].swap -= page_to_byte(kve->kve_resident);
+			}
+		}
+		if (pu[k].swap != 0) {
+			strcpy(pu[k].command, kipp[i].ki_comm);
+			pu[k].pid = kipp[i].ki_pid;
+			pu[k].uid = kipp[i].ki_uid;
+			pu[k].total = kipp[i].ki_size;
+			k++;
+		}
+		free(freep);
+	}
+	procstat_freeprocs(prstat, kipp);
+	nproc = k;
+}
+
+void
+proclabel(int col)
+{
+
+	wmove(wnd, col, 0);
+	wclrtoeol(wnd);
+	mvwaddstr(wnd, col, 0,
+	    "Pid    Username   Command     Swap/Total "
+	    "Per-Process    Per-System");
+}
+
+int
+compar(const void *a, const void *b)
+{
+
+	return (((const struct proc_usage *) a)->swap >
+		((const struct proc_usage *) b)->swap) ? -1: 1;
+}
diff --git a/usr.bin/systat/subs.h b/usr.bin/systat/subs.h
new file mode 100644
index 00000000000..e4184be8dae
--- /dev/null
+++ b/usr.bin/systat/subs.h
@@ -0,0 +1,42 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Yoshihiro Ota
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef SUBS_H
+#define SUBS_H
+
+#include <sys/stdint.h>
+
+int procinit(void);
+void procgetinfo(void);
+
+void proclabel(int col);
+void procshow(int col, int hight, uint64_t totalswappages);
+
+#endif
diff --git a/usr.bin/systat/swap.c b/usr.bin/systat/swap.c
index fc44db7d7d4..44a321a5705 100644
--- a/usr.bin/systat/swap.c
+++ b/usr.bin/systat/swap.c
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1980, 1992, 1993
  *	The Regents of the University of California.  All rights reserved.
- * Copyright (c) 2017 Yoshihiro Ota
+ * Copyright (c) 2017, 2020, 2021 Yoshihiro Ota
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -57,23 +57,21 @@ static const char sccsid[] = "@(#)swap.c	8.3 (Berkeley) 4/29/95";
 #include "systat.h"
 #include "extern.h"
 #include "devs.h"
+#include "subs.h"
 
-static char *header;
-static long blocksize;
-static int dlen, odlen;
-static int hlen;
-static int ulen, oulen;
-static int pagesize;
+static int pathlen;
 
 WINDOW *
 openswap(void)
 {
-	return (subwin(stdscr, LINES-3-1, 0, MAINWIN_ROW, 0));
+
+	return (subwin(stdscr, LINES - 3 - 1, 0, MAINWIN_ROW, 0));
 }
 
 void
 closeswap(WINDOW *w)
 {
+
 	if (w == NULL)
 		return;
 	wclear(w);
@@ -92,29 +90,6 @@ closeswap(WINDOW *w)
 static struct kvm_swap kvmsw[NSWAP];
 static int kvnsw, okvnsw;
 
-static void calclens(void);
-
-#define CONVERT(v)	((int)((int64_t)(v) * pagesize / blocksize))
-
-static void
-calclens(void)
-{
-	int i, n;
-	int len;
-
-	dlen = sizeof("Disk");
-	for (i = 0; i < kvnsw; ++i) {
-		len = strlen(kvmsw[i].ksw_devname);
-		if (dlen < len)
-			dlen = len;
-	}
-
-	ulen = sizeof("Used");
-	for (n = CONVERT(kvmsw[kvnsw].ksw_used), len = 2; n /= 10; ++len);
-	if (ulen < len)
-		ulen = len;
-}
-
 int
 initswap(void)
 {
@@ -123,22 +98,14 @@ initswap(void)
 	if (once)
 		return (1);
 
-	header = getbsize(&hlen, &blocksize);
-	pagesize = getpagesize();
-
 	if ((kvnsw = kvm_getswapinfo(kd, kvmsw, NSWAP, 0)) < 0) {
 		error("systat: kvm_getswapinfo failed");
 		return (0);
 	}
-	okvnsw = kvnsw;
-
-	calclens();
-	odlen = dlen;
-	oulen = ulen;
-
-	once = 1;
-
+	pathlen = 80 - 50 /* % */ - 5 /* Used */ - 5 /* Size */ - 3 /* space */;
 	dsinit(12);
+	procinit();
+	once = 1;
 
 	return (1);
 }
@@ -146,16 +113,13 @@ initswap(void)
 void
 fetchswap(void)
 {
+
 	okvnsw = kvnsw;
 	if ((kvnsw = kvm_getswapinfo(kd, kvmsw, NSWAP, 0)) < 0) {
 		error("systat: kvm_getswapinfo failed");
 		return;
 	}
 
-	odlen = dlen;
-	oulen = ulen;
-	calclens();
-
 	struct devinfo *tmp_dinfo;
 
 	tmp_dinfo = last_dev.dinfo;
@@ -163,71 +127,61 @@ fetchswap(void)
 	cur_dev.dinfo = tmp_dinfo;
 
 	last_dev.snap_time = cur_dev.snap_time;
-	dsgetinfo( &cur_dev );
+	dsgetinfo(&cur_dev);
+	procgetinfo();
 }
 
 void
 labelswap(void)
 {
-	const char *name;
-	int i;
-
-	fetchswap();
 
 	werase(wnd);
 
-	mvwprintw(wnd, 0, 0, "%*s%*s%*s %s",
-	    -dlen, "Disk", hlen, header, ulen, "Used",
-	    "/0%  /10  /20  /30  /40  /50  /60  /70  /80  /90  /100");
-
-	for (i = 0; i <= kvnsw; ++i) {
-		if (i == kvnsw) {
-			if (kvnsw == 1)
-				break;
-			name = "Total";
-		} else
-			name = kvmsw[i].ksw_devname;
-		mvwprintw(wnd, i + 1, 0, "%*s", -dlen, name);
+	dslabel(12, 0, LINES - DISKHIGHT - 1);
+
+	if (kvnsw <= 0) {
+		mvwprintw(wnd, 0, 0, "(swap not configured)");
+		return;
 	}
-	dslabel(12, 0, 18);
+
+	mvwprintw(wnd, 0, 0, "%*s%5s %5s %s",
+	    -pathlen, "Device/Path", "Size", "Used",
+	    "|0%  /10  /20  /30  /40  / 60\\  70\\  80\\  90\\ 100|");
 }
 
 void
 showswap(void)
 {
-	int count;
-	int i;
+	const char *name;
+	int count, i;
 
-	if (kvnsw != okvnsw || dlen != odlen || ulen != oulen)
+	if (kvnsw != okvnsw)
 		labelswap();
 
-	for (i = 0; i <= kvnsw; ++i) {
-		if (i == kvnsw) {
-			if (kvnsw == 1)
-				break;
-		}
+	dsshow(12, 0, LINES - DISKHIGHT - 1, &cur_dev, &last_dev);
 
-		if (kvmsw[i].ksw_total == 0) {
-			mvwprintw(
-			    wnd,
-			    i + 1,
-			    dlen + hlen + ulen + 1,
-			    "(swap not configured)"
-			);
-			continue;
-		}
-
-		wmove(wnd, i + 1, dlen);
+	if (kvnsw <= 0)
+		return;
 
-		wprintw(wnd, "%*d", hlen, CONVERT(kvmsw[i].ksw_total));
-		wprintw(wnd, "%*d", ulen, CONVERT(kvmsw[i].ksw_used));
+	for (i = (kvnsw == 1 ? 0 : kvnsw); i >= 0; i--) {
+		name = i == kvnsw ? "Total" : kvmsw[i].ksw_devname;
+		mvwprintw(wnd, 1 + i, 0, "%-*.*s", pathlen, pathlen - 1, name);
 
-		count = 50.0 * kvmsw[i].ksw_used / kvmsw[i].ksw_total + 1;
+		sysputpage(wnd, i + 1, pathlen, 5, kvmsw[i].ksw_total, 0);
+		sysputpage(wnd, i + 1, pathlen + 5 + 1, 5, kvmsw[i].ksw_used,
+		    0);
 
-		waddch(wnd, ' ');
-		while (count--)
-			waddch(wnd, 'X');
+		if (kvmsw[i].ksw_used > 0) {
+			count = 50 * kvmsw[i].ksw_used / kvmsw[i].ksw_total;
+			sysputXs(wnd, i + 1, pathlen + 5 + 1 + 5 + 1, count);
+		}
 		wclrtoeol(wnd);
 	}
-	dsshow(12, 0, 18, &cur_dev, &last_dev);
+	if (kvnsw == 1)
+		count = 2;
+	else
+		count = 3;
+	proclabel(kvnsw + count);
+	procshow(kvnsw + count, LINES - 5 - (kvnsw + 3) - (DISKHIGHT + 1),
+	    kvmsw[kvnsw].ksw_total);
 }
diff --git a/usr.bin/systat/sysput.c b/usr.bin/systat/sysput.c
index 31c9127a83b..64a9699e45e 100644
--- a/usr.bin/systat/sysput.c
+++ b/usr.bin/systat/sysput.c
@@ -1,5 +1,7 @@
 /*-
- * Copyright (c) 2019 Yoshihiro Ota
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2019, 2020 Yoshihiro Ota
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -40,20 +42,47 @@ __FBSDID("$FreeBSD$");
 #include "systat.h"
 #include "extern.h"
 
+static int page_shift();
+
+uint64_t
+byte_to_page(uint64_t size)
+{
+
+    return (size >> page_shift());
+}
+
+uint64_t
+page_to_byte(uint64_t size)
+{
+
+    return (size << page_shift());
+}
+
 void
 sysputspaces(WINDOW *wd, int row, int col, int width)
 {
-	static char str40[] = "                                        ";
+	static char str60[] = "                    "
+	    "                                        ";
 
-	mvwaddstr(wd, row, col, str40 + sizeof(str40) - width - 1);
+	mvwaddstr(wd, row, col, str60 + sizeof(str60) - width - 1);
 }
 
 void
 sysputstrs(WINDOW *wd, int row, int col, int width)
 {
-	static char str40[] = "****************************************";
+	static char str60[] = "********************"
+	    "****************************************";
+
+	mvwaddstr(wnd, row, col, str60 + sizeof(str60) - width - 1);
+}
+
+void
+sysputXs(WINDOW *wd, int row, int col, int width)
+{
+	static char str60[] = "XXXXXXXXXXXXXXXXXXXX"
+	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
 
-	mvwaddstr(wnd, row, col, str40 + sizeof(str40) - width - 1);
+	mvwaddstr(wnd, row, col, str60 + sizeof(str60) - width - 1);
 }
 
 void
@@ -93,15 +122,16 @@ sysputwuint64(WINDOW *wd, int row, int col, int width, uint64_t val, int flags)
 		sysputuint64(wd, row, col, width, val, flags);
 }
 
-static int
-calc_page_shift()
+int
+page_shift()
 {
 	u_int page_size;
-	int shifts;
+	static int shifts = 0;
 
-	shifts = 0;
+	if (shifts != 0)
+		return (shifts);
 	GETSYSCTL("vm.stats.vm.v_page_size", page_size);
-	for(; page_size > 1; page_size >>= 1)
+	for (; page_size > 1; page_size >>= 1)
 		shifts++;
 	return shifts;
 }
@@ -109,10 +139,6 @@ calc_page_shift()
 void
 sysputpage(WINDOW *wd, int row, int col, int width, uint64_t pages, int flags)
 {
-	static int shifts = 0;
 
-	if (shifts == 0)
-		shifts = calc_page_shift();
-	pages <<= shifts;
-	sysputuint64(wd, row, col, width, pages, flags);
+	sysputuint64(wd, row, col, width, page_to_byte(pages), flags);
 }
diff --git a/usr.bin/systat/systat.1 b/usr.bin/systat/systat.1
index d2eeb285481..bc57d89cf06 100644
--- a/usr.bin/systat/systat.1
+++ b/usr.bin/systat/systat.1
@@ -297,9 +297,21 @@ not display kilobytes per transaction).
 .El
 .It Ic swap
 Show information about swap space usage on all the
-swap areas compiled into the kernel.
-The first column is the device name of the partition.
-The next column is the total space available in the partition.
+swap areas compiled into the kernel and processes that are swapped out.
+The swap areas are displayed first with their name, sizes and
+percentage usage.
+Then, processes are listed in order of higher swap vm object counts.
+Pid, username, a part of command line, the total number of swap objects
+in bytes, the size of process, and per-process swap usage percentage and
+per-system swap space percentage.
+A default vm objecte is converted to a swap vm objects when paging
+out to swap space first time.  Even if such a swap object is paged-in,
+the object remains as a swap object.
+In other words, under some circumstances, the number of swap objects
+may be higher than actual swap space usage.
+One known case is when
+.Dl $ swapoff -a
+ is run.
 The
 .Ar Used
 column indicates the total blocks used so far;
diff --git a/usr.bin/systat/systat.h b/usr.bin/systat/systat.h
index 92233e05817..661e3f2043b 100644
--- a/usr.bin/systat/systat.h
+++ b/usr.bin/systat/systat.h
@@ -33,6 +33,7 @@
  */
 
 #include <curses.h>
+#include <sys/stdint.h>
 
 struct  cmdtab {
 	const char *c_name;		/* command name */
@@ -69,6 +70,9 @@ extern int use_kvm;
 #define NPTR(indx)  (void *)NVAL((indx))
 #define NREAD(indx, buf, len) kvm_ckread(NPTR((indx)), (buf), (len))
 
+extern uint64_t byte_to_page(uint64_t size);
+extern uint64_t page_to_byte(uint64_t size);
+
 extern void putint(int, int, int, int);
 extern void putfloat(double, int, int, int, int, int);
 extern void putlongdouble(long double, int, int, int, int, int);

--Multipart=_Sat__10_Apr_2021_08_36_04_-0400_qz=khajYNKZkDwkZ
Content-Type: text/x-diff;
 name="systat-swap-stabl13.diff"
Content-Disposition: attachment;
 filename="systat-swap-stabl13.diff"
Content-Transfer-Encoding: 7bit

diff --git a/usr.bin/systat/Makefile b/usr.bin/systat/Makefile
index ca3f7ed72ce..bfbe1336d29 100644
--- a/usr.bin/systat/Makefile
+++ b/usr.bin/systat/Makefile
@@ -5,7 +5,7 @@
 
 PROG=	systat
 SRCS=	cmds.c cmdtab.c devs.c fetch.c iostat.c keyboard.c main.c sysput.c \
-	netcmds.c netstat.c pigs.c swap.c icmp.c \
+	netcmds.c netstat.c pigs.c proc.c swap.c icmp.c \
 	mode.c ip.c sctp.c tcp.c zarc.c \
 	vmstat.c convtbl.c ifcmds.c ifstat.c
 
@@ -16,6 +16,6 @@ CFLAGS+= -DINET6
 
 WARNS?=	1
 
-LIBADD=	ncursesw m devstat kvm util
+LIBADD=	ncursesw m devstat kvm util procstat
 
 .include <bsd.prog.mk>
diff --git a/usr.bin/systat/devs.c b/usr.bin/systat/devs.c
index b293796c2c7..87d04f989ea 100644
--- a/usr.bin/systat/devs.c
+++ b/usr.bin/systat/devs.c
@@ -426,12 +426,6 @@ dsshow2(int diskcol, int diskrow, int dn, int lc, struct statinfo *now, struct s
 	putlongdouble(device_busy, diskrow + 4, lc, 5, 0, 0);
 }
 
-static void
-dsshow3(int diskcol, int diskrow, int dn, int lc, struct statinfo *now, struct statinfo *then)
-{
-	dsshow2(diskcol, diskrow, dn, lc, now, then);
-}
-
 void
 dsshow(int maxdrives, int diskcol, int diskrow, struct statinfo *now, struct statinfo *then)
 {
@@ -439,5 +433,5 @@ dsshow(int maxdrives, int diskcol, int diskrow, struct statinfo *now, struct sta
 
 	for (i = 0, lc = 0; i < num_devices && lc < maxdrives; i++)
 		if (dev_select[i].selected)
-			dsshow3(diskcol, diskrow, i, ++lc, now, then);
+			dsshow2(diskcol, diskrow, i, ++lc, now, then);
 }
diff --git a/usr.bin/systat/devs.h b/usr.bin/systat/devs.h
index cbedd844290..79a44a6c3f5 100644
--- a/usr.bin/systat/devs.h
+++ b/usr.bin/systat/devs.h
@@ -2,7 +2,7 @@
  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
  *
  * Copyright (c) 1998 David E. O'Brien
- *               2015 Yoshihiro Ota
+ *               2015, 2021 Yoshihiro Ota
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -34,6 +34,8 @@
 
 #include <devstat.h>
 
+#define DISKHIGHT 5
+
 int dsinit(int);
 void dsgetinfo(struct statinfo *);
 int dscmd(const char *, const char *, int, struct statinfo *);
diff --git a/usr.bin/systat/extern.h b/usr.bin/systat/extern.h
index 6e3117bece4..322065f7931 100644
--- a/usr.bin/systat/extern.h
+++ b/usr.bin/systat/extern.h
@@ -168,6 +168,7 @@ char	*sysctl_dynread(const char *, size_t *);
 void	 sysputpage(WINDOW* , int, int, int, uint64_t, int);
 void	 sysputspaces(WINDOW* , int, int, int);
 void	 sysputstrs(WINDOW* , int, int, int);
+void	 sysputXs(WINDOW* , int, int, int);
 void	 sysputuint64(WINDOW* , int, int, int, uint64_t, int);
 void	 sysputwuint64(WINDOW* , int, int, int, uint64_t, int);
 
diff --git a/usr.bin/systat/main.c b/usr.bin/systat/main.c
index b5a19d381ad..b84351379f4 100644
--- a/usr.bin/systat/main.c
+++ b/usr.bin/systat/main.c
@@ -135,6 +135,21 @@ parse_cmd_args (int argc, char **argv)
 
 }
 
+static void
+resize(int signo __unused)
+{
+
+	endwin();
+	refresh();
+	clear();
+
+	CMDLINE = LINES - 1;
+	labels();
+	display();
+	status();
+}
+
+
 int
 main(int argc, char **argv)
 {
@@ -191,6 +206,7 @@ main(int argc, char **argv)
 	signal(SIGINT, die);
 	signal(SIGQUIT, die);
 	signal(SIGTERM, die);
+	signal(SIGWINCH, resize);
 
 	/*
 	 * Initialize display.  Load average appears in a one line
diff --git a/usr.bin/systat/pigs.c b/usr.bin/systat/pigs.c
index d9f3f8d4ad1..879f6548d86 100644
--- a/usr.bin/systat/pigs.c
+++ b/usr.bin/systat/pigs.c
@@ -56,13 +56,13 @@ __FBSDID("$FreeBSD$");
 #include "systat.h"
 #include "extern.h"
 
-int compar(const void *, const void *);
+static int compar(const void *, const void *);
 
 static int nproc;
 static struct p_times {
 	float pt_pctcpu;
 	struct kinfo_proc *pt_kp;
-} *pt;
+} *pt = NULL;
 
 static int    fscale;
 static double  lccpu;
@@ -90,7 +90,7 @@ showpigs(void)
 	const char *uname, *pname;
 	char pidname[30];
 
-	if (pt == NULL)
+	if (nproc == 0)
 		return;
 
 	qsort(pt, nproc, sizeof (struct p_times), compar);
@@ -146,23 +146,20 @@ fetchpigs(void)
 	float ftime;
 	float *pctp;
 	struct kinfo_proc *kpp;
-	static int lastnproc = 0;
+	static int maxnproc = 0;
 
 	if ((kpp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc)) == NULL) {
 		error("%s", kvm_geterr(kd));
-		if (pt)
-			free(pt);
+		nproc = 0;
 		return;
 	}
-	if (nproc > lastnproc) {
-		free(pt);
-		if ((pt =
-		    malloc(nproc * sizeof(struct p_times))) == NULL) {
+	if (nproc > maxnproc) {
+		if ((pt = realloc(pt, nproc * sizeof(*pt))) == NULL) {
 			error("Out of memory");
 			die(0);
 		}
+		maxnproc = nproc;
 	}
-	lastnproc = nproc;
 	/*
 	 * calculate %cpu for each proc
 	 */
diff --git a/usr.bin/systat/proc.c b/usr.bin/systat/proc.c
new file mode 100644
index 00000000000..0def660d2dc
--- /dev/null
+++ b/usr.bin/systat/proc.c
@@ -0,0 +1,192 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Yoshihiro Ota
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+
+#include <libprocstat.h>
+
+#include <curses.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "subs.h"
+#include "systat.h"
+#include "extern.h"
+
+static int compar(const void *, const void *);
+
+static unsigned int nproc;
+
+static struct procstat *prstat = NULL;
+static struct proc_usage {
+	pid_t pid;
+	uid_t uid;
+	char command[COMMLEN+1];
+	uint64_t total;
+	uint64_t swap;
+} *pu = NULL, pzero;
+
+
+void
+closeproc(WINDOW *w)
+{
+	procstat_close(prstat);
+	prstat = NULL;
+	if (w == NULL)
+		return;
+	wclear(w);
+	wrefresh(w);
+	delwin(w);
+}
+
+void
+procshow(int col, int hight, uint64_t totalswappages)
+{
+	int i, j, y, offset;
+	int rate;
+	const char *uname, *pname;
+	char buf[30];
+
+	if (nproc > 1)
+		qsort(pu, nproc, sizeof (struct proc_usage), compar);
+	y = col + 1 /* HEADING */;
+	offset = 0;
+	for (i = 0; i < hight; i++, y++) {
+		wmove(wnd, y, 0);
+		wclrtoeol(wnd);
+		if (i >= nproc)
+			continue;
+
+		uname = user_from_uid(pu[i].uid, 0);
+		snprintf(buf, sizeof(buf), "%6d %-10s %-10.10s", pu[i].pid,
+		    uname, pu[i].command);
+		offset = 6 + 1 + 10 + 1 + 10 + 1;
+		mvwaddstr(wnd, y, 0, buf);
+		sysputuint64(wnd, y, offset, 4, pu[i].swap, 0);
+		offset += 4;
+		mvwaddstr(wnd, y, offset, " / ");
+		offset += 3;
+		sysputuint64(wnd, y, offset, 4, pu[i].total, 0);
+		offset += 4;
+
+		rate = (pu[i].total > 1 ? 100 * pu[i].swap / pu[i].total : 0);
+		snprintf(buf, sizeof(buf), "%3d%%", rate);
+		mvwaddstr(wnd, y, offset, buf);
+		sysputXs(wnd, y, offset + 5, rate / 10);
+
+		rate = 100 * byte_to_page(pu[i].swap) / totalswappages;
+		snprintf(buf, sizeof(buf), "%3d%%", rate);
+		mvwaddstr(wnd, y, offset + 16, buf);
+		sysputXs(wnd, y, offset + 21, rate / 10);
+	}
+}
+
+int
+procinit(void)
+{
+
+	if (prstat != NULL)
+		return(1);
+	prstat = procstat_open_sysctl();
+	return (prstat == NULL);
+}
+
+void
+procgetinfo(void)
+{
+	static unsigned int maxnproc = 0;
+	int i, j, k;
+	unsigned int cnt;
+	struct kinfo_proc *kipp;
+	struct kinfo_vmentry *freep, *kve;
+
+	kipp = procstat_getprocs(prstat, KERN_PROC_PROC, 0, &nproc);
+	if (kipp == NULL) {
+		error("procstat_getprocs()");
+		return;
+	}
+
+	if (nproc > maxnproc) {
+		maxnproc = nproc;
+		if ((pu = realloc(pu, maxnproc * sizeof(*pu))) == NULL) {
+			error("Out of memory");
+			die(0);
+		}
+	}
+	for (i = 0, k = 0; i < nproc; i++) {
+		freep = procstat_getvmmap(prstat, &kipp[i], &cnt);
+		if (freep == NULL) {
+			continue;
+		}
+		pu[k].swap = 0;
+		for (j = 0; j < cnt; j++) {
+			kve = &freep[j];
+			if (kve->kve_type == KVME_TYPE_SWAP) {
+				pu[k].swap += kve->kve_end - kve->kve_start;
+				pu[k].swap -= page_to_byte(kve->kve_resident);
+			}
+		}
+		if (pu[k].swap != 0) {
+			strcpy(pu[k].command, kipp[i].ki_comm);
+			pu[k].pid = kipp[i].ki_pid;
+			pu[k].uid = kipp[i].ki_uid;
+			pu[k].total = kipp[i].ki_size;
+			k++;
+		}
+		free(freep);
+	}
+	procstat_freeprocs(prstat, kipp);
+	nproc = k;
+}
+
+void
+proclabel(int col)
+{
+
+	wmove(wnd, col, 0);
+	wclrtoeol(wnd);
+	mvwaddstr(wnd, col, 0,
+	    "Pid    Username   Command     Swap/Total "
+	    "Per-Process    Per-System");
+}
+
+int
+compar(const void *a, const void *b)
+{
+
+	return (((const struct proc_usage *) a)->swap >
+		((const struct proc_usage *) b)->swap) ? -1: 1;
+}
diff --git a/usr.bin/systat/subs.h b/usr.bin/systat/subs.h
new file mode 100644
index 00000000000..e4184be8dae
--- /dev/null
+++ b/usr.bin/systat/subs.h
@@ -0,0 +1,42 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Yoshihiro Ota
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef SUBS_H
+#define SUBS_H
+
+#include <sys/stdint.h>
+
+int procinit(void);
+void procgetinfo(void);
+
+void proclabel(int col);
+void procshow(int col, int hight, uint64_t totalswappages);
+
+#endif
diff --git a/usr.bin/systat/swap.c b/usr.bin/systat/swap.c
index fc44db7d7d4..44a321a5705 100644
--- a/usr.bin/systat/swap.c
+++ b/usr.bin/systat/swap.c
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1980, 1992, 1993
  *	The Regents of the University of California.  All rights reserved.
- * Copyright (c) 2017 Yoshihiro Ota
+ * Copyright (c) 2017, 2020, 2021 Yoshihiro Ota
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -57,23 +57,21 @@ static const char sccsid[] = "@(#)swap.c	8.3 (Berkeley) 4/29/95";
 #include "systat.h"
 #include "extern.h"
 #include "devs.h"
+#include "subs.h"
 
-static char *header;
-static long blocksize;
-static int dlen, odlen;
-static int hlen;
-static int ulen, oulen;
-static int pagesize;
+static int pathlen;
 
 WINDOW *
 openswap(void)
 {
-	return (subwin(stdscr, LINES-3-1, 0, MAINWIN_ROW, 0));
+
+	return (subwin(stdscr, LINES - 3 - 1, 0, MAINWIN_ROW, 0));
 }
 
 void
 closeswap(WINDOW *w)
 {
+
 	if (w == NULL)
 		return;
 	wclear(w);
@@ -92,29 +90,6 @@ closeswap(WINDOW *w)
 static struct kvm_swap kvmsw[NSWAP];
 static int kvnsw, okvnsw;
 
-static void calclens(void);
-
-#define CONVERT(v)	((int)((int64_t)(v) * pagesize / blocksize))
-
-static void
-calclens(void)
-{
-	int i, n;
-	int len;
-
-	dlen = sizeof("Disk");
-	for (i = 0; i < kvnsw; ++i) {
-		len = strlen(kvmsw[i].ksw_devname);
-		if (dlen < len)
-			dlen = len;
-	}
-
-	ulen = sizeof("Used");
-	for (n = CONVERT(kvmsw[kvnsw].ksw_used), len = 2; n /= 10; ++len);
-	if (ulen < len)
-		ulen = len;
-}
-
 int
 initswap(void)
 {
@@ -123,22 +98,14 @@ initswap(void)
 	if (once)
 		return (1);
 
-	header = getbsize(&hlen, &blocksize);
-	pagesize = getpagesize();
-
 	if ((kvnsw = kvm_getswapinfo(kd, kvmsw, NSWAP, 0)) < 0) {
 		error("systat: kvm_getswapinfo failed");
 		return (0);
 	}
-	okvnsw = kvnsw;
-
-	calclens();
-	odlen = dlen;
-	oulen = ulen;
-
-	once = 1;
-
+	pathlen = 80 - 50 /* % */ - 5 /* Used */ - 5 /* Size */ - 3 /* space */;
 	dsinit(12);
+	procinit();
+	once = 1;
 
 	return (1);
 }
@@ -146,16 +113,13 @@ initswap(void)
 void
 fetchswap(void)
 {
+
 	okvnsw = kvnsw;
 	if ((kvnsw = kvm_getswapinfo(kd, kvmsw, NSWAP, 0)) < 0) {
 		error("systat: kvm_getswapinfo failed");
 		return;
 	}
 
-	odlen = dlen;
-	oulen = ulen;
-	calclens();
-
 	struct devinfo *tmp_dinfo;
 
 	tmp_dinfo = last_dev.dinfo;
@@ -163,71 +127,61 @@ fetchswap(void)
 	cur_dev.dinfo = tmp_dinfo;
 
 	last_dev.snap_time = cur_dev.snap_time;
-	dsgetinfo( &cur_dev );
+	dsgetinfo(&cur_dev);
+	procgetinfo();
 }
 
 void
 labelswap(void)
 {
-	const char *name;
-	int i;
-
-	fetchswap();
 
 	werase(wnd);
 
-	mvwprintw(wnd, 0, 0, "%*s%*s%*s %s",
-	    -dlen, "Disk", hlen, header, ulen, "Used",
-	    "/0%  /10  /20  /30  /40  /50  /60  /70  /80  /90  /100");
-
-	for (i = 0; i <= kvnsw; ++i) {
-		if (i == kvnsw) {
-			if (kvnsw == 1)
-				break;
-			name = "Total";
-		} else
-			name = kvmsw[i].ksw_devname;
-		mvwprintw(wnd, i + 1, 0, "%*s", -dlen, name);
+	dslabel(12, 0, LINES - DISKHIGHT - 1);
+
+	if (kvnsw <= 0) {
+		mvwprintw(wnd, 0, 0, "(swap not configured)");
+		return;
 	}
-	dslabel(12, 0, 18);
+
+	mvwprintw(wnd, 0, 0, "%*s%5s %5s %s",
+	    -pathlen, "Device/Path", "Size", "Used",
+	    "|0%  /10  /20  /30  /40  / 60\\  70\\  80\\  90\\ 100|");
 }
 
 void
 showswap(void)
 {
-	int count;
-	int i;
+	const char *name;
+	int count, i;
 
-	if (kvnsw != okvnsw || dlen != odlen || ulen != oulen)
+	if (kvnsw != okvnsw)
 		labelswap();
 
-	for (i = 0; i <= kvnsw; ++i) {
-		if (i == kvnsw) {
-			if (kvnsw == 1)
-				break;
-		}
+	dsshow(12, 0, LINES - DISKHIGHT - 1, &cur_dev, &last_dev);
 
-		if (kvmsw[i].ksw_total == 0) {
-			mvwprintw(
-			    wnd,
-			    i + 1,
-			    dlen + hlen + ulen + 1,
-			    "(swap not configured)"
-			);
-			continue;
-		}
-
-		wmove(wnd, i + 1, dlen);
+	if (kvnsw <= 0)
+		return;
 
-		wprintw(wnd, "%*d", hlen, CONVERT(kvmsw[i].ksw_total));
-		wprintw(wnd, "%*d", ulen, CONVERT(kvmsw[i].ksw_used));
+	for (i = (kvnsw == 1 ? 0 : kvnsw); i >= 0; i--) {
+		name = i == kvnsw ? "Total" : kvmsw[i].ksw_devname;
+		mvwprintw(wnd, 1 + i, 0, "%-*.*s", pathlen, pathlen - 1, name);
 
-		count = 50.0 * kvmsw[i].ksw_used / kvmsw[i].ksw_total + 1;
+		sysputpage(wnd, i + 1, pathlen, 5, kvmsw[i].ksw_total, 0);
+		sysputpage(wnd, i + 1, pathlen + 5 + 1, 5, kvmsw[i].ksw_used,
+		    0);
 
-		waddch(wnd, ' ');
-		while (count--)
-			waddch(wnd, 'X');
+		if (kvmsw[i].ksw_used > 0) {
+			count = 50 * kvmsw[i].ksw_used / kvmsw[i].ksw_total;
+			sysputXs(wnd, i + 1, pathlen + 5 + 1 + 5 + 1, count);
+		}
 		wclrtoeol(wnd);
 	}
-	dsshow(12, 0, 18, &cur_dev, &last_dev);
+	if (kvnsw == 1)
+		count = 2;
+	else
+		count = 3;
+	proclabel(kvnsw + count);
+	procshow(kvnsw + count, LINES - 5 - (kvnsw + 3) - (DISKHIGHT + 1),
+	    kvmsw[kvnsw].ksw_total);
 }
diff --git a/usr.bin/systat/sysput.c b/usr.bin/systat/sysput.c
index 31c9127a83b..64a9699e45e 100644
--- a/usr.bin/systat/sysput.c
+++ b/usr.bin/systat/sysput.c
@@ -1,5 +1,7 @@
 /*-
- * Copyright (c) 2019 Yoshihiro Ota
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2019, 2020 Yoshihiro Ota
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -40,20 +42,47 @@ __FBSDID("$FreeBSD$");
 #include "systat.h"
 #include "extern.h"
 
+static int page_shift();
+
+uint64_t
+byte_to_page(uint64_t size)
+{
+
+    return (size >> page_shift());
+}
+
+uint64_t
+page_to_byte(uint64_t size)
+{
+
+    return (size << page_shift());
+}
+
 void
 sysputspaces(WINDOW *wd, int row, int col, int width)
 {
-	static char str40[] = "                                        ";
+	static char str60[] = "                    "
+	    "                                        ";
 
-	mvwaddstr(wd, row, col, str40 + sizeof(str40) - width - 1);
+	mvwaddstr(wd, row, col, str60 + sizeof(str60) - width - 1);
 }
 
 void
 sysputstrs(WINDOW *wd, int row, int col, int width)
 {
-	static char str40[] = "****************************************";
+	static char str60[] = "********************"
+	    "****************************************";
+
+	mvwaddstr(wnd, row, col, str60 + sizeof(str60) - width - 1);
+}
+
+void
+sysputXs(WINDOW *wd, int row, int col, int width)
+{
+	static char str60[] = "XXXXXXXXXXXXXXXXXXXX"
+	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
 
-	mvwaddstr(wnd, row, col, str40 + sizeof(str40) - width - 1);
+	mvwaddstr(wnd, row, col, str60 + sizeof(str60) - width - 1);
 }
 
 void
@@ -93,15 +122,16 @@ sysputwuint64(WINDOW *wd, int row, int col, int width, uint64_t val, int flags)
 		sysputuint64(wd, row, col, width, val, flags);
 }
 
-static int
-calc_page_shift()
+int
+page_shift()
 {
 	u_int page_size;
-	int shifts;
+	static int shifts = 0;
 
-	shifts = 0;
+	if (shifts != 0)
+		return (shifts);
 	GETSYSCTL("vm.stats.vm.v_page_size", page_size);
-	for(; page_size > 1; page_size >>= 1)
+	for (; page_size > 1; page_size >>= 1)
 		shifts++;
 	return shifts;
 }
@@ -109,10 +139,6 @@ calc_page_shift()
 void
 sysputpage(WINDOW *wd, int row, int col, int width, uint64_t pages, int flags)
 {
-	static int shifts = 0;
 
-	if (shifts == 0)
-		shifts = calc_page_shift();
-	pages <<= shifts;
-	sysputuint64(wd, row, col, width, pages, flags);
+	sysputuint64(wd, row, col, width, page_to_byte(pages), flags);
 }
diff --git a/usr.bin/systat/systat.1 b/usr.bin/systat/systat.1
index d2eeb285481..bc57d89cf06 100644
--- a/usr.bin/systat/systat.1
+++ b/usr.bin/systat/systat.1
@@ -297,9 +297,21 @@ not display kilobytes per transaction).
 .El
 .It Ic swap
 Show information about swap space usage on all the
-swap areas compiled into the kernel.
-The first column is the device name of the partition.
-The next column is the total space available in the partition.
+swap areas compiled into the kernel and processes that are swapped out.
+The swap areas are displayed first with their name, sizes and
+percentage usage.
+Then, processes are listed in order of higher swap vm object counts.
+Pid, username, a part of command line, the total number of swap objects
+in bytes, the size of process, and per-process swap usage percentage and
+per-system swap space percentage.
+A default vm objecte is converted to a swap vm objects when paging
+out to swap space first time.  Even if such a swap object is paged-in,
+the object remains as a swap object.
+In other words, under some circumstances, the number of swap objects
+may be higher than actual swap space usage.
+One known case is when
+.Dl $ swapoff -a
+ is run.
 The
 .Ar Used
 column indicates the total blocks used so far;
diff --git a/usr.bin/systat/systat.h b/usr.bin/systat/systat.h
index 92233e05817..661e3f2043b 100644
--- a/usr.bin/systat/systat.h
+++ b/usr.bin/systat/systat.h
@@ -33,6 +33,7 @@
  */
 
 #include <curses.h>
+#include <sys/stdint.h>
 
 struct  cmdtab {
 	const char *c_name;		/* command name */
@@ -69,6 +70,9 @@ extern int use_kvm;
 #define NPTR(indx)  (void *)NVAL((indx))
 #define NREAD(indx, buf, len) kvm_ckread(NPTR((indx)), (buf), (len))
 
+extern uint64_t byte_to_page(uint64_t size);
+extern uint64_t page_to_byte(uint64_t size);
+
 extern void putint(int, int, int, int);
 extern void putfloat(double, int, int, int, int, int);
 extern void putlongdouble(long double, int, int, int, int, int);

--Multipart=_Sat__10_Apr_2021_08_36_04_-0400_qz=khajYNKZkDwkZ
Content-Type: text/x-diff;
 name="systat-swap-current14.diff"
Content-Disposition: attachment;
 filename="systat-swap-current14.diff"
Content-Transfer-Encoding: 7bit

diff --git a/usr.bin/systat/Makefile b/usr.bin/systat/Makefile
index ca3f7ed72ce..bfbe1336d29 100644
--- a/usr.bin/systat/Makefile
+++ b/usr.bin/systat/Makefile
@@ -5,7 +5,7 @@
 
 PROG=	systat
 SRCS=	cmds.c cmdtab.c devs.c fetch.c iostat.c keyboard.c main.c sysput.c \
-	netcmds.c netstat.c pigs.c swap.c icmp.c \
+	netcmds.c netstat.c pigs.c proc.c swap.c icmp.c \
 	mode.c ip.c sctp.c tcp.c zarc.c \
 	vmstat.c convtbl.c ifcmds.c ifstat.c
 
@@ -16,6 +16,6 @@ CFLAGS+= -DINET6
 
 WARNS?=	1
 
-LIBADD=	ncursesw m devstat kvm util
+LIBADD=	ncursesw m devstat kvm util procstat
 
 .include <bsd.prog.mk>
diff --git a/usr.bin/systat/devs.c b/usr.bin/systat/devs.c
index b293796c2c7..87d04f989ea 100644
--- a/usr.bin/systat/devs.c
+++ b/usr.bin/systat/devs.c
@@ -426,12 +426,6 @@ dsshow2(int diskcol, int diskrow, int dn, int lc, struct statinfo *now, struct s
 	putlongdouble(device_busy, diskrow + 4, lc, 5, 0, 0);
 }
 
-static void
-dsshow3(int diskcol, int diskrow, int dn, int lc, struct statinfo *now, struct statinfo *then)
-{
-	dsshow2(diskcol, diskrow, dn, lc, now, then);
-}
-
 void
 dsshow(int maxdrives, int diskcol, int diskrow, struct statinfo *now, struct statinfo *then)
 {
@@ -439,5 +433,5 @@ dsshow(int maxdrives, int diskcol, int diskrow, struct statinfo *now, struct sta
 
 	for (i = 0, lc = 0; i < num_devices && lc < maxdrives; i++)
 		if (dev_select[i].selected)
-			dsshow3(diskcol, diskrow, i, ++lc, now, then);
+			dsshow2(diskcol, diskrow, i, ++lc, now, then);
 }
diff --git a/usr.bin/systat/devs.h b/usr.bin/systat/devs.h
index cbedd844290..79a44a6c3f5 100644
--- a/usr.bin/systat/devs.h
+++ b/usr.bin/systat/devs.h
@@ -2,7 +2,7 @@
  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
  *
  * Copyright (c) 1998 David E. O'Brien
- *               2015 Yoshihiro Ota
+ *               2015, 2021 Yoshihiro Ota
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -34,6 +34,8 @@
 
 #include <devstat.h>
 
+#define DISKHIGHT 5
+
 int dsinit(int);
 void dsgetinfo(struct statinfo *);
 int dscmd(const char *, const char *, int, struct statinfo *);
diff --git a/usr.bin/systat/main.c b/usr.bin/systat/main.c
index b5a19d381ad..b84351379f4 100644
--- a/usr.bin/systat/main.c
+++ b/usr.bin/systat/main.c
@@ -135,6 +135,21 @@ parse_cmd_args (int argc, char **argv)
 
 }
 
+static void
+resize(int signo __unused)
+{
+
+	endwin();
+	refresh();
+	clear();
+
+	CMDLINE = LINES - 1;
+	labels();
+	display();
+	status();
+}
+
+
 int
 main(int argc, char **argv)
 {
@@ -191,6 +206,7 @@ main(int argc, char **argv)
 	signal(SIGINT, die);
 	signal(SIGQUIT, die);
 	signal(SIGTERM, die);
+	signal(SIGWINCH, resize);
 
 	/*
 	 * Initialize display.  Load average appears in a one line
diff --git a/usr.bin/systat/pigs.c b/usr.bin/systat/pigs.c
index d9f3f8d4ad1..879f6548d86 100644
--- a/usr.bin/systat/pigs.c
+++ b/usr.bin/systat/pigs.c
@@ -56,13 +56,13 @@ __FBSDID("$FreeBSD$");
 #include "systat.h"
 #include "extern.h"
 
-int compar(const void *, const void *);
+static int compar(const void *, const void *);
 
 static int nproc;
 static struct p_times {
 	float pt_pctcpu;
 	struct kinfo_proc *pt_kp;
-} *pt;
+} *pt = NULL;
 
 static int    fscale;
 static double  lccpu;
@@ -90,7 +90,7 @@ showpigs(void)
 	const char *uname, *pname;
 	char pidname[30];
 
-	if (pt == NULL)
+	if (nproc == 0)
 		return;
 
 	qsort(pt, nproc, sizeof (struct p_times), compar);
@@ -146,23 +146,20 @@ fetchpigs(void)
 	float ftime;
 	float *pctp;
 	struct kinfo_proc *kpp;
-	static int lastnproc = 0;
+	static int maxnproc = 0;
 
 	if ((kpp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc)) == NULL) {
 		error("%s", kvm_geterr(kd));
-		if (pt)
-			free(pt);
+		nproc = 0;
 		return;
 	}
-	if (nproc > lastnproc) {
-		free(pt);
-		if ((pt =
-		    malloc(nproc * sizeof(struct p_times))) == NULL) {
+	if (nproc > maxnproc) {
+		if ((pt = realloc(pt, nproc * sizeof(*pt))) == NULL) {
 			error("Out of memory");
 			die(0);
 		}
+		maxnproc = nproc;
 	}
-	lastnproc = nproc;
 	/*
 	 * calculate %cpu for each proc
 	 */
diff --git a/usr.bin/systat/proc.c b/usr.bin/systat/proc.c
new file mode 100644
index 00000000000..0def660d2dc
--- /dev/null
+++ b/usr.bin/systat/proc.c
@@ -0,0 +1,192 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Yoshihiro Ota
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+
+#include <libprocstat.h>
+
+#include <curses.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "subs.h"
+#include "systat.h"
+#include "extern.h"
+
+static int compar(const void *, const void *);
+
+static unsigned int nproc;
+
+static struct procstat *prstat = NULL;
+static struct proc_usage {
+	pid_t pid;
+	uid_t uid;
+	char command[COMMLEN+1];
+	uint64_t total;
+	uint64_t swap;
+} *pu = NULL, pzero;
+
+
+void
+closeproc(WINDOW *w)
+{
+	procstat_close(prstat);
+	prstat = NULL;
+	if (w == NULL)
+		return;
+	wclear(w);
+	wrefresh(w);
+	delwin(w);
+}
+
+void
+procshow(int col, int hight, uint64_t totalswappages)
+{
+	int i, j, y, offset;
+	int rate;
+	const char *uname, *pname;
+	char buf[30];
+
+	if (nproc > 1)
+		qsort(pu, nproc, sizeof (struct proc_usage), compar);
+	y = col + 1 /* HEADING */;
+	offset = 0;
+	for (i = 0; i < hight; i++, y++) {
+		wmove(wnd, y, 0);
+		wclrtoeol(wnd);
+		if (i >= nproc)
+			continue;
+
+		uname = user_from_uid(pu[i].uid, 0);
+		snprintf(buf, sizeof(buf), "%6d %-10s %-10.10s", pu[i].pid,
+		    uname, pu[i].command);
+		offset = 6 + 1 + 10 + 1 + 10 + 1;
+		mvwaddstr(wnd, y, 0, buf);
+		sysputuint64(wnd, y, offset, 4, pu[i].swap, 0);
+		offset += 4;
+		mvwaddstr(wnd, y, offset, " / ");
+		offset += 3;
+		sysputuint64(wnd, y, offset, 4, pu[i].total, 0);
+		offset += 4;
+
+		rate = (pu[i].total > 1 ? 100 * pu[i].swap / pu[i].total : 0);
+		snprintf(buf, sizeof(buf), "%3d%%", rate);
+		mvwaddstr(wnd, y, offset, buf);
+		sysputXs(wnd, y, offset + 5, rate / 10);
+
+		rate = 100 * byte_to_page(pu[i].swap) / totalswappages;
+		snprintf(buf, sizeof(buf), "%3d%%", rate);
+		mvwaddstr(wnd, y, offset + 16, buf);
+		sysputXs(wnd, y, offset + 21, rate / 10);
+	}
+}
+
+int
+procinit(void)
+{
+
+	if (prstat != NULL)
+		return(1);
+	prstat = procstat_open_sysctl();
+	return (prstat == NULL);
+}
+
+void
+procgetinfo(void)
+{
+	static unsigned int maxnproc = 0;
+	int i, j, k;
+	unsigned int cnt;
+	struct kinfo_proc *kipp;
+	struct kinfo_vmentry *freep, *kve;
+
+	kipp = procstat_getprocs(prstat, KERN_PROC_PROC, 0, &nproc);
+	if (kipp == NULL) {
+		error("procstat_getprocs()");
+		return;
+	}
+
+	if (nproc > maxnproc) {
+		maxnproc = nproc;
+		if ((pu = realloc(pu, maxnproc * sizeof(*pu))) == NULL) {
+			error("Out of memory");
+			die(0);
+		}
+	}
+	for (i = 0, k = 0; i < nproc; i++) {
+		freep = procstat_getvmmap(prstat, &kipp[i], &cnt);
+		if (freep == NULL) {
+			continue;
+		}
+		pu[k].swap = 0;
+		for (j = 0; j < cnt; j++) {
+			kve = &freep[j];
+			if (kve->kve_type == KVME_TYPE_SWAP) {
+				pu[k].swap += kve->kve_end - kve->kve_start;
+				pu[k].swap -= page_to_byte(kve->kve_resident);
+			}
+		}
+		if (pu[k].swap != 0) {
+			strcpy(pu[k].command, kipp[i].ki_comm);
+			pu[k].pid = kipp[i].ki_pid;
+			pu[k].uid = kipp[i].ki_uid;
+			pu[k].total = kipp[i].ki_size;
+			k++;
+		}
+		free(freep);
+	}
+	procstat_freeprocs(prstat, kipp);
+	nproc = k;
+}
+
+void
+proclabel(int col)
+{
+
+	wmove(wnd, col, 0);
+	wclrtoeol(wnd);
+	mvwaddstr(wnd, col, 0,
+	    "Pid    Username   Command     Swap/Total "
+	    "Per-Process    Per-System");
+}
+
+int
+compar(const void *a, const void *b)
+{
+
+	return (((const struct proc_usage *) a)->swap >
+		((const struct proc_usage *) b)->swap) ? -1: 1;
+}
diff --git a/usr.bin/systat/subs.h b/usr.bin/systat/subs.h
new file mode 100644
index 00000000000..e4184be8dae
--- /dev/null
+++ b/usr.bin/systat/subs.h
@@ -0,0 +1,42 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Yoshihiro Ota
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef SUBS_H
+#define SUBS_H
+
+#include <sys/stdint.h>
+
+int procinit(void);
+void procgetinfo(void);
+
+void proclabel(int col);
+void procshow(int col, int hight, uint64_t totalswappages);
+
+#endif
diff --git a/usr.bin/systat/swap.c b/usr.bin/systat/swap.c
index 29b04df0157..44a321a5705 100644
--- a/usr.bin/systat/swap.c
+++ b/usr.bin/systat/swap.c
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1980, 1992, 1993
  *	The Regents of the University of California.  All rights reserved.
- * Copyright (c) 2017, 2020 Yoshihiro Ota
+ * Copyright (c) 2017, 2020, 2021 Yoshihiro Ota
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -57,6 +57,7 @@ static const char sccsid[] = "@(#)swap.c	8.3 (Berkeley) 4/29/95";
 #include "systat.h"
 #include "extern.h"
 #include "devs.h"
+#include "subs.h"
 
 static int pathlen;
 
@@ -103,6 +104,7 @@ initswap(void)
 	}
 	pathlen = 80 - 50 /* % */ - 5 /* Used */ - 5 /* Size */ - 3 /* space */;
 	dsinit(12);
+	procinit();
 	once = 1;
 
 	return (1);
@@ -125,18 +127,17 @@ fetchswap(void)
 	cur_dev.dinfo = tmp_dinfo;
 
 	last_dev.snap_time = cur_dev.snap_time;
-	dsgetinfo( &cur_dev );
+	dsgetinfo(&cur_dev);
+	procgetinfo();
 }
 
 void
 labelswap(void)
 {
-	const char *name;
-	int i;
 
 	werase(wnd);
 
-	dslabel(12, 0, 18);
+	dslabel(12, 0, LINES - DISKHIGHT - 1);
 
 	if (kvnsw <= 0) {
 		mvwprintw(wnd, 0, 0, "(swap not configured)");
@@ -146,28 +147,26 @@ labelswap(void)
 	mvwprintw(wnd, 0, 0, "%*s%5s %5s %s",
 	    -pathlen, "Device/Path", "Size", "Used",
 	    "|0%  /10  /20  /30  /40  / 60\\  70\\  80\\  90\\ 100|");
-
-	for (i = 0; i <= kvnsw; ++i) {
-		name = i == kvnsw ? "Total" : kvmsw[i].ksw_devname;
-		mvwprintw(wnd, 1 + i, 0, "%-*.*s", pathlen, pathlen - 1, name);
-	}
 }
 
 void
 showswap(void)
 {
-	int count;
-	int i;
+	const char *name;
+	int count, i;
 
 	if (kvnsw != okvnsw)
 		labelswap();
 
-	dsshow(12, 0, 18, &cur_dev, &last_dev);
+	dsshow(12, 0, LINES - DISKHIGHT - 1, &cur_dev, &last_dev);
 
 	if (kvnsw <= 0)
 		return;
 
-	for (i = 0; i <= kvnsw; ++i) {
+	for (i = (kvnsw == 1 ? 0 : kvnsw); i >= 0; i--) {
+		name = i == kvnsw ? "Total" : kvmsw[i].ksw_devname;
+		mvwprintw(wnd, 1 + i, 0, "%-*.*s", pathlen, pathlen - 1, name);
+
 		sysputpage(wnd, i + 1, pathlen, 5, kvmsw[i].ksw_total, 0);
 		sysputpage(wnd, i + 1, pathlen + 5 + 1, 5, kvmsw[i].ksw_used,
 		    0);
@@ -178,4 +177,11 @@ showswap(void)
 		}
 		wclrtoeol(wnd);
 	}
+	if (kvnsw == 1)
+		count = 2;
+	else
+		count = 3;
+	proclabel(kvnsw + count);
+	procshow(kvnsw + count, LINES - 5 - (kvnsw + 3) - (DISKHIGHT + 1),
+	    kvmsw[kvnsw].ksw_total);
 }
diff --git a/usr.bin/systat/sysput.c b/usr.bin/systat/sysput.c
index 10401cee772..64a9699e45e 100644
--- a/usr.bin/systat/sysput.c
+++ b/usr.bin/systat/sysput.c
@@ -1,4 +1,6 @@
 /*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
  * Copyright (c) 2019, 2020 Yoshihiro Ota
  *
  * Redistribution and use in source and binary forms, with or without
@@ -40,6 +42,22 @@ __FBSDID("$FreeBSD$");
 #include "systat.h"
 #include "extern.h"
 
+static int page_shift();
+
+uint64_t
+byte_to_page(uint64_t size)
+{
+
+    return (size >> page_shift());
+}
+
+uint64_t
+page_to_byte(uint64_t size)
+{
+
+    return (size << page_shift());
+}
+
 void
 sysputspaces(WINDOW *wd, int row, int col, int width)
 {
@@ -104,15 +122,16 @@ sysputwuint64(WINDOW *wd, int row, int col, int width, uint64_t val, int flags)
 		sysputuint64(wd, row, col, width, val, flags);
 }
 
-static int
-calc_page_shift()
+int
+page_shift()
 {
 	u_int page_size;
-	int shifts;
+	static int shifts = 0;
 
-	shifts = 0;
+	if (shifts != 0)
+		return (shifts);
 	GETSYSCTL("vm.stats.vm.v_page_size", page_size);
-	for(; page_size > 1; page_size >>= 1)
+	for (; page_size > 1; page_size >>= 1)
 		shifts++;
 	return shifts;
 }
@@ -120,10 +139,6 @@ calc_page_shift()
 void
 sysputpage(WINDOW *wd, int row, int col, int width, uint64_t pages, int flags)
 {
-	static int shifts = 0;
 
-	if (shifts == 0)
-		shifts = calc_page_shift();
-	pages <<= shifts;
-	sysputuint64(wd, row, col, width, pages, flags);
+	sysputuint64(wd, row, col, width, page_to_byte(pages), flags);
 }
diff --git a/usr.bin/systat/systat.1 b/usr.bin/systat/systat.1
index 5ad8078a85a..4bc8db643b4 100644
--- a/usr.bin/systat/systat.1
+++ b/usr.bin/systat/systat.1
@@ -279,9 +279,21 @@ not display kilobytes per transaction).
 .El
 .It Ic swap
 Show information about swap space usage on all the
-swap areas compiled into the kernel.
-The first column is the device name of the partition.
-The next column is the total space available in the partition.
+swap areas compiled into the kernel and processes that are swapped out.
+The swap areas are displayed first with their name, sizes and
+percentage usage.
+Then, processes are listed in order of higher swap vm object counts.
+Pid, username, a part of command line, the total number of swap objects
+in bytes, the size of process, and per-process swap usage percentage and
+per-system swap space percentage.
+A default vm objecte is converted to a swap vm objects when paging
+out to swap space first time.  Even if such a swap object is paged-in,
+the object remains as a swap object.
+In other words, under some circumstances, the number of swap objects
+may be higher than actual swap space usage.
+One known case is when
+.Dl $ swapoff -a
+ is run.
 The
 .Ar Used
 column indicates the total blocks used so far;
diff --git a/usr.bin/systat/systat.h b/usr.bin/systat/systat.h
index 92233e05817..661e3f2043b 100644
--- a/usr.bin/systat/systat.h
+++ b/usr.bin/systat/systat.h
@@ -33,6 +33,7 @@
  */
 
 #include <curses.h>
+#include <sys/stdint.h>
 
 struct  cmdtab {
 	const char *c_name;		/* command name */
@@ -69,6 +70,9 @@ extern int use_kvm;
 #define NPTR(indx)  (void *)NVAL((indx))
 #define NREAD(indx, buf, len) kvm_ckread(NPTR((indx)), (buf), (len))
 
+extern uint64_t byte_to_page(uint64_t size);
+extern uint64_t page_to_byte(uint64_t size);
+
 extern void putint(int, int, int, int);
 extern void putfloat(double, int, int, int, int, int);
 extern void putlongdouble(long double, int, int, int, int, int);

--Multipart=_Sat__10_Apr_2021_08_36_04_-0400_qz=khajYNKZkDwkZ--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20210410083604.26b852e49ea29d05095a6beb>