157e5da2cSKonstantin Belousov /*- 2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 357e5da2cSKonstantin Belousov * 457e5da2cSKonstantin Belousov * Copyright (c) 2021 Yoshihiro Ota <ota@j.email.ne.jp> 557e5da2cSKonstantin Belousov * 657e5da2cSKonstantin Belousov * Redistribution and use in source and binary forms, with or without 757e5da2cSKonstantin Belousov * modification, are permitted provided that the following conditions 857e5da2cSKonstantin Belousov * are met: 957e5da2cSKonstantin Belousov * 1. Redistributions of source code must retain the above copyright 1057e5da2cSKonstantin Belousov * notice, this list of conditions and the following disclaimer. 1157e5da2cSKonstantin Belousov * 2. Redistributions in binary form must reproduce the above copyright 1257e5da2cSKonstantin Belousov * notice, this list of conditions and the following disclaimer in the 1357e5da2cSKonstantin Belousov * documentation and/or other materials provided with the distribution. 1457e5da2cSKonstantin Belousov * 1557e5da2cSKonstantin Belousov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1657e5da2cSKonstantin Belousov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1757e5da2cSKonstantin Belousov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1857e5da2cSKonstantin Belousov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1957e5da2cSKonstantin Belousov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2057e5da2cSKonstantin Belousov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2157e5da2cSKonstantin Belousov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2257e5da2cSKonstantin Belousov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2357e5da2cSKonstantin Belousov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2457e5da2cSKonstantin Belousov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2557e5da2cSKonstantin Belousov * SUCH DAMAGE. 2657e5da2cSKonstantin Belousov */ 2757e5da2cSKonstantin Belousov 2857e5da2cSKonstantin Belousov #include <sys/param.h> 2957e5da2cSKonstantin Belousov #include <sys/sysctl.h> 3057e5da2cSKonstantin Belousov #include <sys/user.h> 3157e5da2cSKonstantin Belousov 3257e5da2cSKonstantin Belousov #include <curses.h> 3357e5da2cSKonstantin Belousov #include <libprocstat.h> 3457e5da2cSKonstantin Belousov #include <libutil.h> 3557e5da2cSKonstantin Belousov #include <pwd.h> 3657e5da2cSKonstantin Belousov #include <stdbool.h> 3757e5da2cSKonstantin Belousov #include <stdlib.h> 3857e5da2cSKonstantin Belousov #include <string.h> 3957e5da2cSKonstantin Belousov 4057e5da2cSKonstantin Belousov #include "systat.h" 4157e5da2cSKonstantin Belousov #include "extern.h" 4257e5da2cSKonstantin Belousov 4357e5da2cSKonstantin Belousov /* 4457e5da2cSKonstantin Belousov * vm objects of swappable types 4557e5da2cSKonstantin Belousov */ 4657e5da2cSKonstantin Belousov static struct swapvm { 4757e5da2cSKonstantin Belousov uint64_t kvo_me; 4857e5da2cSKonstantin Belousov uint32_t swapped; /* in pages */ 4957e5da2cSKonstantin Belousov uint64_t next; 5057e5da2cSKonstantin Belousov pid_t pid; /* to avoid double counting */ 5157e5da2cSKonstantin Belousov } *swobj = NULL; 5257e5da2cSKonstantin Belousov static int nswobj = 0; 5357e5da2cSKonstantin Belousov 5457e5da2cSKonstantin Belousov static struct procstat *prstat = NULL; 5557e5da2cSKonstantin Belousov /* 5657e5da2cSKonstantin Belousov *procstat_getvmmap() is an expensive call and the number of processes running 5757e5da2cSKonstantin Belousov * may also be high. So, maintain an array of pointers for ease of expanding 5857e5da2cSKonstantin Belousov * an array and also swapping pointers are faster than struct. 5957e5da2cSKonstantin Belousov */ 6057e5da2cSKonstantin Belousov static struct proc_usage { 6157e5da2cSKonstantin Belousov pid_t pid; 6257e5da2cSKonstantin Belousov uid_t uid; 6357e5da2cSKonstantin Belousov char command[COMMLEN + 1]; 6457e5da2cSKonstantin Belousov uint64_t total; 6557e5da2cSKonstantin Belousov uint32_t pages; 6657e5da2cSKonstantin Belousov } **pu = NULL; 676d88f9feSMateusz Guzik static int nproc; 6857e5da2cSKonstantin Belousov static int proc_compar(const void *, const void *); 6957e5da2cSKonstantin Belousov 7057e5da2cSKonstantin Belousov static void 7157e5da2cSKonstantin Belousov display_proc_line(int idx, int y, uint64_t totalswappages) 7257e5da2cSKonstantin Belousov { 7357e5da2cSKonstantin Belousov int offset = 0, rate; 746d88f9feSMateusz Guzik const char *uname; 7557e5da2cSKonstantin Belousov char buf[30]; 7657e5da2cSKonstantin Belousov uint64_t swapbytes; 7757e5da2cSKonstantin Belousov 7857e5da2cSKonstantin Belousov wmove(wnd, y, 0); 7957e5da2cSKonstantin Belousov wclrtoeol(wnd); 8057e5da2cSKonstantin Belousov if (idx >= nproc) 8157e5da2cSKonstantin Belousov return; 8257e5da2cSKonstantin Belousov 8357e5da2cSKonstantin Belousov uname = user_from_uid(pu[idx]->uid, 0); 8457e5da2cSKonstantin Belousov swapbytes = ptoa(pu[idx]->pages); 8557e5da2cSKonstantin Belousov 8657e5da2cSKonstantin Belousov snprintf(buf, sizeof(buf), "%6d %-10s %-10.10s", pu[idx]->pid, uname, 8757e5da2cSKonstantin Belousov pu[idx]->command); 8857e5da2cSKonstantin Belousov offset = 6 + 1 + 10 + 1 + 10 + 1; 8957e5da2cSKonstantin Belousov mvwaddstr(wnd, y, 0, buf); 9057e5da2cSKonstantin Belousov sysputuint64(wnd, y, offset, 4, swapbytes, 0); 9157e5da2cSKonstantin Belousov offset += 4; 9257e5da2cSKonstantin Belousov mvwaddstr(wnd, y, offset, " / "); 9357e5da2cSKonstantin Belousov offset += 3; 9457e5da2cSKonstantin Belousov sysputuint64(wnd, y, offset, 4, pu[idx]->total, 0); 9557e5da2cSKonstantin Belousov offset += 4; 9657e5da2cSKonstantin Belousov 9757e5da2cSKonstantin Belousov rate = pu[idx]->total > 1 ? 100 * swapbytes / pu[idx]->total : 0; 9857e5da2cSKonstantin Belousov snprintf(buf, sizeof(buf), "%3d%%", rate); 9957e5da2cSKonstantin Belousov mvwaddstr(wnd, y, offset, buf); 10057e5da2cSKonstantin Belousov if (rate > 100) /* avoid running over the screen */ 10157e5da2cSKonstantin Belousov rate = 100; 10257e5da2cSKonstantin Belousov sysputXs(wnd, y, offset + 5, rate / 10); 10357e5da2cSKonstantin Belousov 10457e5da2cSKonstantin Belousov rate = 100 * pu[idx]->pages / totalswappages; 10557e5da2cSKonstantin Belousov snprintf(buf, sizeof(buf), "%3d%%", rate); 10657e5da2cSKonstantin Belousov mvwaddstr(wnd, y, offset + 16, buf); 10757e5da2cSKonstantin Belousov if (rate > 100) /* avoid running over the screen */ 10857e5da2cSKonstantin Belousov rate = 100; 10957e5da2cSKonstantin Belousov sysputXs(wnd, y, offset + 21, rate / 10); 11057e5da2cSKonstantin Belousov } 11157e5da2cSKonstantin Belousov 11257e5da2cSKonstantin Belousov static int 11357e5da2cSKonstantin Belousov swobj_search(const void *a, const void *b) 11457e5da2cSKonstantin Belousov { 11557e5da2cSKonstantin Belousov const uint64_t *aa = a; 11657e5da2cSKonstantin Belousov const struct swapvm *bb = b; 11757e5da2cSKonstantin Belousov 11857e5da2cSKonstantin Belousov if (*aa == bb->kvo_me) 11957e5da2cSKonstantin Belousov return (0); 12057e5da2cSKonstantin Belousov return (*aa > bb->kvo_me ? -1 : 1); 12157e5da2cSKonstantin Belousov } 12257e5da2cSKonstantin Belousov 12357e5da2cSKonstantin Belousov static int 12457e5da2cSKonstantin Belousov swobj_sort(const void *a, const void *b) 12557e5da2cSKonstantin Belousov { 12657e5da2cSKonstantin Belousov 12757e5da2cSKonstantin Belousov return ((((const struct swapvm *) a)->kvo_me > 12857e5da2cSKonstantin Belousov ((const struct swapvm *) b)->kvo_me) ? -1 : 1); 12957e5da2cSKonstantin Belousov } 13057e5da2cSKonstantin Belousov 13157e5da2cSKonstantin Belousov static bool 13257e5da2cSKonstantin Belousov get_swap_vmobjects(void) 13357e5da2cSKonstantin Belousov { 13457e5da2cSKonstantin Belousov static int maxnobj; 13557e5da2cSKonstantin Belousov int cnt, i, next_i, last_nswobj; 13657e5da2cSKonstantin Belousov struct kinfo_vmobject *kvo; 13757e5da2cSKonstantin Belousov 13857e5da2cSKonstantin Belousov next_i = nswobj = 0; 13957e5da2cSKonstantin Belousov kvo = kinfo_getswapvmobject(&cnt); 14057e5da2cSKonstantin Belousov if (kvo == NULL) { 14157e5da2cSKonstantin Belousov error("kinfo_getswapvmobject()"); 14257e5da2cSKonstantin Belousov return (false); 14357e5da2cSKonstantin Belousov } 14457e5da2cSKonstantin Belousov do { 14557e5da2cSKonstantin Belousov for (i = next_i; i < cnt; i++) { 14657e5da2cSKonstantin Belousov if (kvo[i].kvo_type != KVME_TYPE_DEFAULT && 14757e5da2cSKonstantin Belousov kvo[i].kvo_type != KVME_TYPE_SWAP) 14857e5da2cSKonstantin Belousov continue; 14957e5da2cSKonstantin Belousov if (nswobj < maxnobj) { 15057e5da2cSKonstantin Belousov swobj[nswobj].kvo_me = kvo[i].kvo_me; 15157e5da2cSKonstantin Belousov swobj[nswobj].swapped = kvo[i].kvo_swapped; 15257e5da2cSKonstantin Belousov swobj[nswobj].next = kvo[i].kvo_backing_obj; 15357e5da2cSKonstantin Belousov swobj[nswobj].pid = 0; 15457e5da2cSKonstantin Belousov next_i = i + 1; 15557e5da2cSKonstantin Belousov } 15657e5da2cSKonstantin Belousov nswobj++; 15757e5da2cSKonstantin Belousov } 15857e5da2cSKonstantin Belousov if (nswobj <= maxnobj) 15957e5da2cSKonstantin Belousov break; 16057e5da2cSKonstantin Belousov /* allocate memory and fill skipped elements */ 16157e5da2cSKonstantin Belousov last_nswobj = maxnobj; 16257e5da2cSKonstantin Belousov maxnobj = nswobj; 16357e5da2cSKonstantin Belousov nswobj = last_nswobj; 16457e5da2cSKonstantin Belousov /* allocate more memory and fill missed ones */ 16557e5da2cSKonstantin Belousov if ((swobj = reallocf(swobj, maxnobj * sizeof(*swobj))) == 16657e5da2cSKonstantin Belousov NULL) { 16757e5da2cSKonstantin Belousov error("Out of memory"); 16857e5da2cSKonstantin Belousov die(0); 16957e5da2cSKonstantin Belousov } 17057e5da2cSKonstantin Belousov } while (i <= cnt); /* extra safety guard */ 17157e5da2cSKonstantin Belousov free(kvo); 17257e5da2cSKonstantin Belousov if (nswobj > 1) 17357e5da2cSKonstantin Belousov qsort(swobj, nswobj, sizeof(swobj[0]), swobj_sort); 17457e5da2cSKonstantin Belousov return (nswobj > 0); 17557e5da2cSKonstantin Belousov } 17657e5da2cSKonstantin Belousov 17757e5da2cSKonstantin Belousov /* This returns the number of swap pages a process uses. */ 17857e5da2cSKonstantin Belousov static uint32_t 17957e5da2cSKonstantin Belousov per_proc_swap_usage(struct kinfo_proc *kipp) 18057e5da2cSKonstantin Belousov { 18157e5da2cSKonstantin Belousov int i, cnt; 18257e5da2cSKonstantin Belousov uint32_t pages = 0; 18357e5da2cSKonstantin Belousov uint64_t vmobj; 18457e5da2cSKonstantin Belousov struct kinfo_vmentry *freep, *kve; 18557e5da2cSKonstantin Belousov struct swapvm *vm; 18657e5da2cSKonstantin Belousov 18757e5da2cSKonstantin Belousov freep = procstat_getvmmap(prstat, kipp, &cnt); 18857e5da2cSKonstantin Belousov if (freep == NULL) 18957e5da2cSKonstantin Belousov return (pages); 19057e5da2cSKonstantin Belousov 19157e5da2cSKonstantin Belousov for (i = 0; i < cnt; i++) { 19257e5da2cSKonstantin Belousov kve = &freep[i]; 19357e5da2cSKonstantin Belousov if (kve->kve_type == KVME_TYPE_DEFAULT || 19457e5da2cSKonstantin Belousov kve->kve_type == KVME_TYPE_SWAP) { 19557e5da2cSKonstantin Belousov vmobj = kve->kve_obj; 19657e5da2cSKonstantin Belousov do { 19757e5da2cSKonstantin Belousov vm = bsearch(&vmobj, swobj, nswobj, 19857e5da2cSKonstantin Belousov sizeof(swobj[0]), swobj_search); 19957e5da2cSKonstantin Belousov if (vm != NULL && vm->pid != kipp->ki_pid) { 20057e5da2cSKonstantin Belousov pages += vm->swapped; 20157e5da2cSKonstantin Belousov vmobj = vm->next; 20257e5da2cSKonstantin Belousov vm->pid = kipp->ki_pid; 20357e5da2cSKonstantin Belousov } else 20457e5da2cSKonstantin Belousov break; 20557e5da2cSKonstantin Belousov } while (vmobj != 0); 20657e5da2cSKonstantin Belousov } 20757e5da2cSKonstantin Belousov } 20857e5da2cSKonstantin Belousov free(freep); 20957e5da2cSKonstantin Belousov return (pages); 21057e5da2cSKonstantin Belousov } 21157e5da2cSKonstantin Belousov 21257e5da2cSKonstantin Belousov void 2136d88f9feSMateusz Guzik procshow(int lcol, int hight, uint64_t totalswappages) 21457e5da2cSKonstantin Belousov { 21557e5da2cSKonstantin Belousov int i, y; 21657e5da2cSKonstantin Belousov 2176d88f9feSMateusz Guzik for (i = 0, y = lcol + 1 /* HEADING */; i < hight; i++, y++) 21857e5da2cSKonstantin Belousov display_proc_line(i, y, totalswappages); 21957e5da2cSKonstantin Belousov } 22057e5da2cSKonstantin Belousov 22157e5da2cSKonstantin Belousov int 22257e5da2cSKonstantin Belousov procinit(void) 22357e5da2cSKonstantin Belousov { 22457e5da2cSKonstantin Belousov 22557e5da2cSKonstantin Belousov if (prstat == NULL) 22657e5da2cSKonstantin Belousov prstat = procstat_open_sysctl(); 22757e5da2cSKonstantin Belousov return (prstat != NULL); 22857e5da2cSKonstantin Belousov } 22957e5da2cSKonstantin Belousov 23057e5da2cSKonstantin Belousov void 23157e5da2cSKonstantin Belousov procgetinfo(void) 23257e5da2cSKonstantin Belousov { 2336d88f9feSMateusz Guzik static int maxnproc = 0; 23457e5da2cSKonstantin Belousov int cnt, i; 23557e5da2cSKonstantin Belousov uint32_t pages; 23657e5da2cSKonstantin Belousov struct kinfo_proc *kipp; 23757e5da2cSKonstantin Belousov 23857e5da2cSKonstantin Belousov nproc = 0; 23957e5da2cSKonstantin Belousov if ( ! get_swap_vmobjects() ) /* call failed or nothing is paged-out */ 24057e5da2cSKonstantin Belousov return; 24157e5da2cSKonstantin Belousov 24257e5da2cSKonstantin Belousov kipp = procstat_getprocs(prstat, KERN_PROC_PROC, 0, &cnt); 24357e5da2cSKonstantin Belousov if (kipp == NULL) { 24457e5da2cSKonstantin Belousov error("procstat_getprocs()"); 24557e5da2cSKonstantin Belousov return; 24657e5da2cSKonstantin Belousov } 24757e5da2cSKonstantin Belousov if (maxnproc < cnt) { 24857e5da2cSKonstantin Belousov if ((pu = realloc(pu, cnt * sizeof(*pu))) == NULL) { 24957e5da2cSKonstantin Belousov error("Out of memory"); 25057e5da2cSKonstantin Belousov die(0); 25157e5da2cSKonstantin Belousov } 25257e5da2cSKonstantin Belousov memset(&pu[maxnproc], 0, (cnt - maxnproc) * sizeof(pu[0])); 25357e5da2cSKonstantin Belousov maxnproc = cnt; 25457e5da2cSKonstantin Belousov } 25557e5da2cSKonstantin Belousov 25657e5da2cSKonstantin Belousov for (i = 0; i < cnt; i++) { 25757e5da2cSKonstantin Belousov pages = per_proc_swap_usage(&kipp[i]); 25857e5da2cSKonstantin Belousov if (pages == 0) 25957e5da2cSKonstantin Belousov continue; 26057e5da2cSKonstantin Belousov if (pu[nproc] == NULL && 26157e5da2cSKonstantin Belousov (pu[nproc] = malloc(sizeof(**pu))) == NULL) { 26257e5da2cSKonstantin Belousov error("Out of memory"); 26357e5da2cSKonstantin Belousov die(0); 26457e5da2cSKonstantin Belousov } 26557e5da2cSKonstantin Belousov strlcpy(pu[nproc]->command, kipp[i].ki_comm, 26657e5da2cSKonstantin Belousov sizeof(pu[nproc]->command)); 26757e5da2cSKonstantin Belousov pu[nproc]->pid = kipp[i].ki_pid; 26857e5da2cSKonstantin Belousov pu[nproc]->uid = kipp[i].ki_uid; 26957e5da2cSKonstantin Belousov pu[nproc]->pages = pages; 27057e5da2cSKonstantin Belousov pu[nproc]->total = kipp[i].ki_size; 27157e5da2cSKonstantin Belousov nproc++; 27257e5da2cSKonstantin Belousov } 27357e5da2cSKonstantin Belousov if (nproc > 1) 27457e5da2cSKonstantin Belousov qsort(pu, nproc, sizeof(*pu), proc_compar); 27557e5da2cSKonstantin Belousov } 27657e5da2cSKonstantin Belousov 27757e5da2cSKonstantin Belousov void 2786d88f9feSMateusz Guzik proclabel(int lcol) 27957e5da2cSKonstantin Belousov { 28057e5da2cSKonstantin Belousov 2816d88f9feSMateusz Guzik wmove(wnd, lcol, 0); 28257e5da2cSKonstantin Belousov wclrtoeol(wnd); 2836d88f9feSMateusz Guzik mvwaddstr(wnd, lcol, 0, 28457e5da2cSKonstantin Belousov "Pid Username Command Swap/Total " 28557e5da2cSKonstantin Belousov "Per-Process Per-System"); 28657e5da2cSKonstantin Belousov } 28757e5da2cSKonstantin Belousov 28857e5da2cSKonstantin Belousov int 28957e5da2cSKonstantin Belousov proc_compar(const void *a, const void *b) 29057e5da2cSKonstantin Belousov { 29157e5da2cSKonstantin Belousov const struct proc_usage *aa = *((const struct proc_usage **)a); 29257e5da2cSKonstantin Belousov const struct proc_usage *bb = *((const struct proc_usage **)b); 29357e5da2cSKonstantin Belousov 29457e5da2cSKonstantin Belousov return (aa->pages > bb->pages ? -1 : 1); 29557e5da2cSKonstantin Belousov } 296