Index: usr.bin/systat/Makefile =================================================================== --- usr.bin/systat/Makefile +++ 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 @@ WARNS?= 1 -LIBADD= ncursesw m devstat kvm util +LIBADD= ncursesw m devstat kvm util procstat .include Index: usr.bin/systat/proc.c =================================================================== --- /dev/null +++ usr.bin/systat/proc.c @@ -0,0 +1,314 @@ +/*- + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "systat.h" +#include "extern.h" + +/* + * vm objects of swappable types + */ +static struct swapvm { + uint64_t kvo_me; + uint32_t pages; + uint64_t next; +} *swobj = NULL; +static int nswobj = 0; + +static struct procstat *prstat = NULL; +/* + *procstat_getvmmap() is an expensive call and the number of processes running + * may also be high. So, maintain an array of pointers for ease of expanding + * an array and also swapping pointers are faster than struct. + */ +static struct proc_usage { + pid_t pid; + uid_t uid; + char command[COMMLEN+1]; + uint64_t total; + uint32_t pages; +} **pu = NULL; +static unsigned int nproc; +static int proc_compar(const void *, const void *); + +static void +display_proc_line(int idx, int y, uint64_t totalswappages) +{ + int offset = 0, rate; + const char *uname, *pname; + char buf[30]; + uint64_t swapbytes; + + wmove(wnd, y, 0); + wclrtoeol(wnd); + if (idx >= nproc) + return; + + uname = user_from_uid(pu[idx]->uid, 0); + swapbytes = page_to_byte(pu[idx]->pages); + + snprintf(buf, sizeof(buf), "%6d %-10s %-10.10s", pu[idx]->pid, uname, + pu[idx]->command); + offset = 6 + 1 + 10 + 1 + 10 + 1; + mvwaddstr(wnd, y, 0, buf); + sysputuint64(wnd, y, offset, 4, swapbytes, 0); + offset += 4; + mvwaddstr(wnd, y, offset, " / "); + offset += 3; + sysputuint64(wnd, y, offset, 4, pu[idx]->total, 0); + offset += 4; + + rate = pu[idx]->total > 1 ? 100 * swapbytes / pu[idx]->total : 0; + snprintf(buf, sizeof(buf), "%3d%%", rate); + mvwaddstr(wnd, y, offset, buf); + if (rate > 100) /* avoid running over the screen */ + rate = 100; + sysputXs(wnd, y, offset + 5, rate / 10); + + rate = 100 * pu[idx]->pages / totalswappages; + snprintf(buf, sizeof(buf), "%3d%%", rate); + mvwaddstr(wnd, y, offset + 16, buf); + if (rate > 100) /* avoid running over the screen */ + rate = 100; + sysputXs(wnd, y, offset + 21, rate / 10); +} + +static int +swobj_search(const void *a, const void *b) +{ + const uint64_t *aa = a; + const struct swapvm *bb = b; + + if (*aa == bb->kvo_me) + return(0); + return (*aa > bb->kvo_me) ? -1: 1; +} + +static int +swobj_sort(const void *a, const void *b) +{ + + return (((const struct swapvm *) a)->kvo_me > + ((const struct swapvm *) b)->kvo_me) ? -1: 1; +} + +static bool +get_swap_vmobjects(void) +{ + static int max; + int cnt, i, next_i, last_nswobj; + struct kinfo_vmobject *kvo; + + next_i = nswobj = 0; + kvo = kinfo_getvmobject(&cnt); + if (kvo == NULL) { + error("kinfo_getvmobject()"); + return (false); + } + do { + for (i = next_i; i < cnt; i++) { + if (kvo[i].kvo_type != KVME_TYPE_DEFAULT + && kvo[i].kvo_type != KVME_TYPE_SWAP) + continue; + if (kvo[i].kvo_swapped == 0) + continue; + if (nswobj < max) { + swobj[nswobj].kvo_me = kvo[i].kvo_me; + swobj[nswobj].pages = kvo[i].kvo_swapped; + swobj[nswobj].next = kvo[i].kvo_backing_obj; + next_i = i + 1; + } + nswobj++; + } + if (nswobj <= max) + break; + /* allocate memory and fill skipped elements */ + last_nswobj = max; + max = nswobj; + nswobj = last_nswobj; + /* allocate more memory and fill missed ones */ + if ((swobj = realloc(swobj, max * sizeof(*swobj))) == NULL) { + error("Out of memory"); + die(0); + } + } while (i <= cnt); /* extra safety guard */ + free(kvo); + if (nswobj > 1) + qsort(swobj, nswobj, sizeof (swobj[0]), swobj_sort); + return (nswobj > 0); +} + +/* This returns the number of pages a process uses. */ +static uint32_t per_proc_swap_usage(struct kinfo_proc *kipp) +{ + int i, cnt; + uint32_t pages = 0; + uint64_t vmobj; + struct kinfo_vmentry *freep, *kve; + struct swapvm *vm; + + freep = procstat_getvmmap(prstat, kipp, &cnt); + if (freep == NULL) + return (pages); + + for (i = 0; i < cnt; i++) { + kve = &freep[i]; + if (kve->kve_type == KVME_TYPE_DEFAULT || + kve->kve_type == KVME_TYPE_SWAP) { + vmobj = kve->kve_obj; + do { + vm = bsearch(&vmobj, swobj, nswobj, + sizeof(swobj[0]), swobj_search); + if (vm != NULL) { + pages += vm->pages; + vmobj = vm->next; + } else + break; + } while (vmobj != 0); + } + } + free(freep); + return pages; +} + +void +closeproc(WINDOW *w) +{ + + if (prstat != NULL) + 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, y; + + for (i = 0, y = col + 1 /* HEADING */; i < hight; i++, y++) + display_proc_line(i, y, totalswappages); +} + +int +procinit(void) +{ + + if (prstat != NULL) + return(1); + prstat = procstat_open_sysctl(); + return (prstat == NULL); +} + +void +procgetinfo(void) +{ + static unsigned int maxnproc = 0; + int cnt, i; + uint32_t pages; + struct kinfo_proc *kipp; + + nproc = 0; + if ( ! get_swap_vmobjects() ) /* call failed or nothing is paged-out */ + return; + + kipp = procstat_getprocs(prstat, KERN_PROC_PROC, 0, &cnt); + if (kipp == NULL) { + error("procstat_getprocs()"); + return; + } + if (maxnproc < cnt) { + if ((pu = realloc(pu, cnt * sizeof(*pu))) == NULL) { + error("Out of memory"); + die(0); + } + memset(&pu[maxnproc], 0, (cnt - maxnproc) * sizeof (pu[0])); + maxnproc = cnt; + } + + for (i = 0; i < cnt; i++) { + pages = per_proc_swap_usage(&kipp[i]); + if (pages == 0) + continue; + if (pu[nproc] == NULL && + ((pu[nproc] = malloc(sizeof (**pu))) == NULL)) { + error("Out of memory"); + die(0); + } + strlcpy(pu[nproc]->command, kipp[i].ki_comm, + sizeof (pu[nproc]->command)); + pu[nproc]->pid = kipp[i].ki_pid; + pu[nproc]->uid = kipp[i].ki_uid; + pu[nproc]->pages = pages; + pu[nproc]->total = kipp[i].ki_size; + nproc++; + } + if (nproc > 1) + qsort(pu, nproc, sizeof (*pu), proc_compar); +} + +void +proclabel(int col) +{ + + wmove(wnd, col, 0); + wclrtoeol(wnd); + mvwaddstr(wnd, col, 0, + "Pid Username Command Swap/Total " + "Per-Process Per-System"); +} + +int +proc_compar(const void *a, const void *b) +{ + const struct proc_usage *aa = *((const struct proc_usage **) a); + const struct proc_usage *bb = *((const struct proc_usage **) b); + + return (aa->pages > bb->pages) ? -1: 1; +} Index: usr.bin/systat/swap.c =================================================================== --- usr.bin/systat/swap.c +++ 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 @@ -103,6 +103,7 @@ } pathlen = 80 - 50 /* % */ - 5 /* Used */ - 5 /* Size */ - 3 /* space */; dsinit(12); + procinit(); once = 1; return (1); @@ -125,14 +126,13 @@ 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); @@ -146,18 +146,13 @@ 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(); @@ -167,7 +162,10 @@ 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 +176,11 @@ } 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); } Index: usr.bin/systat/sysput.c =================================================================== --- usr.bin/systat/sysput.c +++ usr.bin/systat/sysput.c @@ -40,6 +40,22 @@ #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 +120,16 @@ 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 +137,6 @@ 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); } Index: usr.bin/systat/systat.h =================================================================== --- usr.bin/systat/systat.h +++ usr.bin/systat/systat.h @@ -3,6 +3,8 @@ * * Copyright (c) 1980, 1989, 1992, 1993 * The Regents of the University of California. All rights reserved. + * Copyright (c) 1998 David E. O'Brien + * 2015, 2021 Yoshihiro Ota * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,6 +35,8 @@ */ #include +#include +#include struct cmdtab { const char *c_name; /* command name */ @@ -69,6 +73,32 @@ #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); + + +/*** reuseable components ***/ + +/** libproc stat based sub components - currently support swap usage via vm **/ +int procinit(void); +void procgetinfo(void); +void proclabel(int col); +void procshow(int col, int hight, uint64_t totalswappages); + + +/** devstat based sub components - currently support disk activities **/ + +#define DISKHIGHT 5 + +int dsinit(int); +void dsgetinfo(struct statinfo *); +int dscmd(const char *, const char *, int, struct statinfo *); + +void dslabel(int, int, int); +void dsshow(int, int, int, struct statinfo *, struct statinfo *); + +extern struct statinfo cur_dev, last_dev, run_dev;