xref: /dragonfly/usr.bin/systat/vmstat.c (revision e89cf083729d92a54712c70909350cf83b0bd971)
1 /*-
2  * Copyright (c) 1983, 1989, 1992, 1993
3  *        The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 /*
31  * Cursed vmstat -- from Robert Elz.
32  */
33 
34 #include <sys/user.h>
35 #include <sys/param.h>
36 #include <sys/stat.h>
37 #include <sys/time.h>
38 #include <sys/uio.h>
39 #include <sys/namei.h>
40 #include <sys/sysctl.h>
41 #include <sys/vmmeter.h>
42 
43 #include <vm/vm_param.h>
44 
45 #include <ctype.h>
46 #include <err.h>
47 #include <errno.h>
48 #include <kinfo.h>
49 #include <langinfo.h>
50 #include <nlist.h>
51 #include <paths.h>
52 #include <signal.h>
53 #include <stddef.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <time.h>
57 #include <unistd.h>
58 #include "utmpentry.h"
59 #include <devstat.h>
60 #include "systat.h"
61 #include "extern.h"
62 #include "devs.h"
63 
64 #define NKVMSW      16
65 
66 static struct Info {
67           struct kinfo_cputime cp_time;
68           struct    vmmeter Vmm;
69           struct    vmtotal Total;
70           struct  vmstats Vms;
71           struct    nchstats nchstats;
72           long      nchcount;
73           long      nchpathcount;
74           long      *intrcnt;
75           long      bufspace;
76           int       maxvnodes;
77           int       cachedvnodes;
78           int       inactivevnodes;
79           int       activevnodes;
80           long      dirtybufspace;
81           long      physmem;
82           struct kvm_swap  kvmsw[NKVMSW];
83 } s, s1, s2, z;
84 
85 struct statinfo run;
86 struct kinfo_cputime cp_time;
87 static int kvnsw;
88 
89 #define   vmm s.Vmm
90 #define   vms s.Vms
91 #define oldvmm s1.Vmm
92 #define oldvms s1.Vms
93 #define   total s.Total
94 #define   nchtotal s.nchstats
95 #define   oldnchtotal s1.nchstats
96 
97 static    enum state { BOOT, TIME, RUN } state = TIME;
98 
99 static void allocinfo(struct Info *);
100 static void copyinfo(struct Info *, struct Info *);
101 static void dinfo(int, int, struct statinfo *, struct statinfo *);
102 static void getinfo(struct Info *);
103 static void put64(int64_t, int, int, int, int);
104 static void putfloat(double, int, int, int, int, int);
105 static void putlongdouble(long double, int, int, int, int, int);
106 static void putlongdoublez(long double, int, int, int, int, int);
107 static int ucount(void);
108 
109 static    int ncpu;
110 static    char buf[26];
111 static    time_t t;
112 static    double etime;
113 static    int nintr;
114 static    int  *intralias;
115 static    int  *intrsmp;
116 static    long *intrloc;
117 static    long *lacc;
118 static    char **intrname;
119 static    int nextintsrow;
120 static  int extended_vm_stats;
121 
122 
123 
124 WINDOW *
openkre(void)125 openkre(void)
126 {
127 
128           return (stdscr);
129 }
130 
131 void
closekre(WINDOW * w)132 closekre(WINDOW *w)
133 {
134 
135           if (w == NULL)
136                     return;
137           wclear(w);
138           wrefresh(w);
139 }
140 
141 
142 static struct nlist namelist[] = {
143 #define   X_BUFFERSPACE       0
144           { .n_name = "_bufspace" },
145 #define   X_NCHSTATS          1
146           { .n_name = "_nchstats" },
147 #define   X_DESIREDVNODES     2
148           { .n_name = "_maxvnodes" },
149 #define   X_CACHEDVNODES      3
150           { .n_name = "_cachedvnodes" },
151 #define   X_INACTIVEVNODES 4
152           { .n_name = "_inactivevnodes" },
153 #define   X_ACTIVEVNODES      5
154           { .n_name = "_activevnodes" },
155 #define X_NUMDIRTYBUFFERS 6
156           { .n_name = "_dirtybufspace" },
157           { .n_name = "" },
158 };
159 
160 /*
161  * These constants define where the major pieces are laid out
162  */
163 #define STATROW                0        /* uses 1 row and 68 cols */
164 #define STATCOL                2
165 #define MEMROW                 2        /* uses 4 rows and 31 cols */
166 #define MEMCOLA                0
167 #define MEMCOLB                17
168 #define PAGEROW                2        /* uses 4 rows and 26 cols */
169 #define PAGECOL               45
170 #define INTSROW                6        /* uses all rows to bottom and 17 cols */
171 #define INTSCOL               61
172 #define PROCSROW     7        /* uses 2 rows and 20 cols */
173 #define PROCSCOL     0
174 #define GENSTATROW   7        /* uses 2 rows and 30 cols */
175 #define GENSTATCOL  16
176 #define VMSTATROW    6        /* uses 17 rows and 12 cols */
177 #define VMSTATCOL   50
178 #define GRAPHROW    10        /* uses 3 rows and 51 cols */
179 #define GRAPHCOL     0
180 #define NAMEIROW    14        /* uses 3 rows and 38 cols */
181 #define NAMEICOL     0
182 #define EXECROW               14        /* uses 2 rows and 5 cols */
183 #define EXECCOL               38
184 #define DISKROW               17        /* uses 6 rows and 50 cols (for 9 drives) */
185 #define DISKCOL                0
186 
187 #define   DRIVESPACE           7        /* max # for space */
188 
189 #define   MAXDRIVES DRIVESPACE           /* max # to display */
190 
191 static
192 int
findintralias(const char * name,int limit)193 findintralias(const char *name, int limit)
194 {
195           int i;
196           size_t nlen;
197           size_t ilen;
198 
199           nlen = strlen(name);
200           for (i = 0; i < limit; ++i) {
201                     if (strcmp(name, intrname[i]) == 0)
202                               break;
203                     ilen = strlen(intrname[i]);
204                     if (nlen == ilen &&
205                         nlen > 1 &&
206                         strncmp(name, intrname[i], nlen - 1) == 0 &&
207                         strchr(name, ' ') &&
208                         isdigit(name[nlen - 1]) &&
209                         (isdigit(intrname[i][nlen - 1]) ||
210                          intrname[i][nlen - 1] == '*')) {
211                               intrname[i][nlen - 1] = '*';
212                               break;
213                     }
214           }
215           return i;
216 }
217 
218 int
initkre(void)219 initkre(void)
220 {
221           char *intrnamebuf;
222           size_t bytes;
223           size_t b;
224           size_t i;
225 
226           if (namelist[0].n_type == 0) {
227                     if (kvm_nlist(kd, namelist)) {
228                               nlisterr(namelist);
229                               return(0);
230                     }
231                     if (namelist[0].n_type == 0) {
232                               error("No namelist");
233                               return(0);
234                     }
235           }
236 
237           if ((num_devices = getnumdevs()) < 0) {
238                     warnx("%s", devstat_errbuf);
239                     return(0);
240           }
241 
242           cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
243           last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
244           run.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
245           bzero(cur.dinfo, sizeof(struct devinfo));
246           bzero(last.dinfo, sizeof(struct devinfo));
247           bzero(run.dinfo, sizeof(struct devinfo));
248 
249           if (dsinit(MAXDRIVES, &cur, &last, &run) != 1)
250                     return(0);
251 
252           if (nintr == 0) {
253                     if (sysctlbyname("hw.intrnames", NULL, &bytes, NULL, 0) == 0) {
254                               intrnamebuf = malloc(bytes);
255                               sysctlbyname("hw.intrnames", intrnamebuf, &bytes,
256                                                   NULL, 0);
257                               for (i = 0; i < bytes; ++i) {
258                                         if (intrnamebuf[i] == 0)
259                                                   ++nintr;
260                               }
261                               intrname = malloc(nintr * sizeof(char *));
262                               intrloc = malloc(nintr * sizeof(*intrloc));
263                               lacc = malloc(nintr * sizeof(*lacc));
264                               intralias = malloc(nintr * sizeof(*intralias));
265                               intrsmp = malloc(nintr * sizeof(*intrsmp));
266                               bzero(intrsmp, nintr * sizeof(*intrsmp));
267 
268                               nintr = 0;
269                               for (b = i = 0; i < bytes; ++i) {
270                                         if (intrnamebuf[i] == 0) {
271                                                   intrname[nintr] = intrnamebuf + b;
272                                                   intrloc[nintr] = 0;
273                                                   intralias[nintr] =
274                                                     findintralias(intrname[nintr], nintr);
275                                                   ++intrsmp[intralias[nintr]];
276                                                   b = i + 1;
277                                                   ++nintr;
278                                         }
279                               }
280                     }
281                     nextintsrow = INTSROW + 2;
282                     allocinfo(&s);
283                     allocinfo(&s1);
284                     allocinfo(&s2);
285                     allocinfo(&z);
286           }
287           getinfo(&s2);
288           copyinfo(&s2, &s1);
289           return(1);
290 }
291 
292 void
fetchkre(void)293 fetchkre(void)
294 {
295           time_t now;
296           struct tm *tp;
297           static int d_first = -1;
298 
299           if (d_first < 0)
300                     d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
301 
302           time(&now);
303           tp = localtime(&now);
304           (void) strftime(buf, sizeof(buf),
305                               d_first ? "%e %b %R" : "%b %e %R", tp);
306           getinfo(&s);
307 }
308 
309 void
labelkre(void)310 labelkre(void)
311 {
312           int i, j;
313 
314           clear();
315           mvprintw(STATROW, STATCOL + 4, "users    Load");
316           mvprintw(MEMROW + 0, MEMCOLA, "Active ");
317           mvprintw(MEMROW + 1, MEMCOLA, "Kernel ");
318           mvprintw(MEMROW + 2, MEMCOLA, "Free   ");
319           mvprintw(MEMROW + 3, MEMCOLA, "Total  ");
320 
321           mvprintw(MEMROW + 2, MEMCOLA + 14, "i+c+f");
322 
323           mvprintw(MEMROW + 0, MEMCOLB, "PMAP");
324           mvprintw(MEMROW + 0, MEMCOLB + 13, "VMRSS");
325           mvprintw(MEMROW + 1, MEMCOLB, "SWAP");
326           mvprintw(MEMROW + 1, MEMCOLB + 13, "SWTOT");
327 
328           mvprintw(PAGEROW, PAGECOL,     "       VNODE PAGER    SWAP PAGER ");
329           mvprintw(PAGEROW + 1, PAGECOL, "          in   out      in   out ");
330           mvprintw(PAGEROW + 2, PAGECOL, "bytes");
331           mvprintw(PAGEROW + 3, PAGECOL, "count");
332 
333           mvprintw(INTSROW, INTSCOL + 3, " Interrupts");
334           mvprintw(INTSROW + 1, INTSCOL + 9, "total");
335 
336           mvprintw(VMSTATROW + 1, VMSTATCOL + 8, "cow");
337           mvprintw(VMSTATROW + 2, VMSTATCOL + 8, "wire");
338           mvprintw(VMSTATROW + 3, VMSTATCOL + 8, "act");
339           mvprintw(VMSTATROW + 4, VMSTATCOL + 8, "inact");
340           mvprintw(VMSTATROW + 5, VMSTATCOL + 8, "cache");
341           mvprintw(VMSTATROW + 6, VMSTATCOL + 8, "free");
342           mvprintw(VMSTATROW + 7, VMSTATCOL + 8, "daefr");
343           mvprintw(VMSTATROW + 8, VMSTATCOL + 8, "prcfr");
344           mvprintw(VMSTATROW + 9, VMSTATCOL + 8, "react");
345           mvprintw(VMSTATROW + 10, VMSTATCOL + 8, "pdwake");
346           mvprintw(VMSTATROW + 11, VMSTATCOL + 8, "pdpgs");
347           mvprintw(VMSTATROW + 12, VMSTATCOL + 8, "intrn");
348           mvprintw(VMSTATROW + 13, VMSTATCOL + 8, "buf");
349           mvprintw(VMSTATROW + 14, VMSTATCOL + 8, "dirtybuf");
350 
351           mvprintw(VMSTATROW + 15, VMSTATCOL + 8, "activ-vp");
352           mvprintw(VMSTATROW + 16, VMSTATCOL + 8, "cachd-vp");
353           mvprintw(VMSTATROW + 17, VMSTATCOL + 8, "inact-vp");
354 
355           mvprintw(GENSTATROW, GENSTATCOL, "  Csw  Trp  Sys  Int  Sof  Flt");
356 
357           mvprintw(GRAPHROW, GRAPHCOL,
358                     "  . %%Sys    . %%Intr   . %%User   . %%Nice   . %%Idle");
359           mvprintw(PROCSROW, PROCSCOL, "   r   p   d   s");
360           mvprintw(GRAPHROW + 1, GRAPHCOL,
361                     "|    |    |    |    |    |    |    |    |    |    |");
362 
363           mvprintw(NAMEIROW, NAMEICOL, "Path-lookups   hits   %%    Components");
364           mvprintw(EXECROW, EXECCOL, "Execs");
365           mvprintw(DISKROW, DISKCOL, "Disks");
366           mvprintw(DISKROW + 1, DISKCOL, "KB/t");
367           mvprintw(DISKROW + 2, DISKCOL, "tpr/s");
368           mvprintw(DISKROW + 3, DISKCOL, "MBr/s");
369           mvprintw(DISKROW + 4, DISKCOL, "tpw/s");
370           mvprintw(DISKROW + 5, DISKCOL, "MBw/s");
371           mvprintw(DISKROW + 6, DISKCOL, "%% busy");
372 
373           /*
374            * For now, we don't support a fourth disk statistic.  So there's
375            * no point in providing a label for it.  If someone can think of a
376            * fourth useful disk statistic, there is room to add it.
377            */
378           j = 0;
379           for (i = 0; i < num_devices && j < MAXDRIVES; i++)
380                     if (dev_select[i].selected) {
381                               char tmpstr[80];
382                               sprintf(tmpstr, "%s%d", dev_select[i].device_name,
383                                         dev_select[i].unit_number);
384                               mvprintw(DISKROW, DISKCOL + 5 + 6 * j,
385                                         " %5.5s", tmpstr);
386                               j++;
387                     }
388 
389           if (j <= 4) {
390                     /*
391                      * room for extended VM stats
392                      */
393                     mvprintw(VMSTATROW + 11, VMSTATCOL - 6, "nzfod");
394                     mvprintw(VMSTATROW + 12, VMSTATCOL - 6, "ozfod");
395                     mvprintw(VMSTATROW + 13, VMSTATCOL - 6, "%%zslo");
396                     mvprintw(VMSTATROW + 14, VMSTATCOL - 6, "pgfre");
397                     extended_vm_stats = 1;
398           } else {
399                     extended_vm_stats = 0;
400                     mvprintw(VMSTATROW + 0, VMSTATCOL + 8, "zfod");
401           }
402 
403           for (i = 0; i < nintr; i++) {
404                     if (intrloc[i] == 0)
405                               continue;
406                     mvprintw(intrloc[i], INTSCOL + 9, "%-10.10s", intrname[i]);
407           }
408 }
409 
410 #define CP_UPDATE(fld)        do {      \
411           uint64_t lt;                  \
412           lt=s.fld;           \
413           s.fld-=s1.fld;                \
414           if(state==TIME)               \
415                     s1.fld=lt;          \
416           lt=fld;                       \
417           fld-=old_##fld;               \
418           if(state==TIME)               \
419                     old_##fld=lt;       \
420           etime += s.fld;               \
421 } while(0)
422 #define X(fld)      {t=s.fld[i]; s.fld[i]-=s1.fld[i]; if(state==TIME) s1.fld[i]=t;}
423 #define Y(fld)      {t = s.fld; s.fld -= s1.fld; if(state == TIME) s1.fld = t;}
424 #define Z(fld)      {t = s.nchstats.fld; s.nchstats.fld -= s1.nchstats.fld; \
425           if(state == TIME) s1.nchstats.fld = t;}
426 #define PUTRATE(fld, l, c, w) \
427           Y(fld); \
428           put64((int64_t)((float)s.fld/etime + 0.5), l, c, w, 'D')
429 #define PUTRATE_PGTOB(fld, l, c, w) \
430           Y(fld); \
431           put64((int64_t)((float)s.fld/etime + 0.5) * PAGE_SIZE, l, c, w, 0)
432 #define MAXFAIL 5
433 
434 #define CPUSTATES 5
435 static    const char cpuchar[5] = { '=' , '+', '>', '-', ' ' };
436 
437 static    const size_t cpuoffsets[] = {
438           offsetof(struct kinfo_cputime, cp_sys),
439           offsetof(struct kinfo_cputime, cp_intr),
440           offsetof(struct kinfo_cputime, cp_user),
441           offsetof(struct kinfo_cputime, cp_nice),
442           offsetof(struct kinfo_cputime, cp_idle)
443 };
444 
445 void
showkre(void)446 showkre(void)
447 {
448           float f1, f2;
449           int psiz;
450           int i, j, lc;
451           long inttotal;
452           long l;
453           static int failcnt = 0;
454           double total_time;
455 
456           etime = 0;
457           CP_UPDATE(cp_time.cp_user);
458           CP_UPDATE(cp_time.cp_nice);
459           CP_UPDATE(cp_time.cp_sys);
460           CP_UPDATE(cp_time.cp_intr);
461           CP_UPDATE(cp_time.cp_idle);
462 
463           total_time = etime;
464           if (total_time == 0.0)
465                     total_time = 1.0;
466 
467           if (etime < 100000.0) {       /* < 100ms ignore this trash */
468                     if (failcnt++ >= MAXFAIL) {
469                               clear();
470                               mvprintw(2, 10, "The alternate system clock has died!");
471                               mvprintw(3, 10, "Reverting to ``pigs'' display.");
472                               move(CMDLINE, 0);
473                               refresh();
474                               failcnt = 0;
475                               sleep(5);
476                               command("pigs");
477                     }
478                     return;
479           }
480           failcnt = 0;
481           etime /= 1000000.0;
482           etime /= ncpu;
483           if (etime == 0)
484                     etime = 1;
485           inttotal = 0;
486           bzero(lacc, nintr * sizeof(*lacc));
487 
488           for (i = 0; i < nintr; i++) {
489                     if (s.intrcnt[i] == 0)
490                               continue;
491                     j = intralias[i];
492                     if (intrloc[j] == 0) {
493                               if (nextintsrow == LINES)
494                                         continue;
495                               intrloc[j] = nextintsrow++;
496                               mvprintw(intrloc[j], INTSCOL + 9, "%-10.10s",
497                                         intrname[j]);
498                     }
499                     X(intrcnt);
500                     l = (long)((float)s.intrcnt[i]/etime + 0.5);
501                     lacc[j] += l;
502                     inttotal += l;
503                     put64(lacc[j], intrloc[j], INTSCOL + 3, 5, 'D');
504           }
505           put64(inttotal, INTSROW + 1, INTSCOL + 3, 5, 'D');
506           Z(ncs_goodhits); Z(ncs_badhits); Z(ncs_miss);
507           Z(ncs_longhits); Z(ncs_longmiss); Z(ncs_neghits);
508           s.nchcount = nchtotal.ncs_goodhits + nchtotal.ncs_badhits +
509               nchtotal.ncs_miss + nchtotal.ncs_neghits;
510           s.nchpathcount = nchtotal.ncs_longhits + nchtotal.ncs_longmiss;
511           if (state == TIME) {
512                     s1.nchcount = s.nchcount;
513                     s1.nchpathcount = s.nchpathcount;
514           }
515 
516 #define LOADCOLS    49        /* Don't but into the 'free' value */
517 #define LOADRANGE   (100.0 / LOADCOLS)
518 
519           psiz = 0;
520           f2 = 0.0;
521           for (lc = 0; lc < CPUSTATES; lc++) {
522                     uint64_t val = *(uint64_t *)(((uint8_t *)&s.cp_time) +
523                                      cpuoffsets[lc]);
524                     f1 = 100.0 * val / total_time;
525                     f2 += f1;
526                     l = (int)((f2 + (LOADRANGE / 2.0)) / LOADRANGE) - psiz;
527                     if (f1 > 99.9)
528                               f1 = 99.9;          /* no room to display 100.0 */
529                     putfloat(f1, GRAPHROW, GRAPHCOL + 10 * lc, 4, 1, 0);
530                     move(GRAPHROW + 2, psiz);
531                     psiz += l;
532                     while (l-- > 0)
533                               addch(cpuchar[lc]);
534           }
535 
536           put64(ucount(), STATROW, STATCOL, 3, 'D');
537           putfloat(avenrun[0], STATROW, STATCOL + 18, 6, 2, 0);
538           putfloat(avenrun[1], STATROW, STATCOL + 25, 6, 2, 0);
539           putfloat(avenrun[2], STATROW, STATCOL + 32, 6, 2, 0);
540           mvaddstr(STATROW, STATCOL + 53, buf);
541 #define pgtokb(pg) (int64_t)((intmax_t)(pg) * vms.v_page_size / 1024)
542 #define pgtomb(pg) (int64_t)((intmax_t)(pg) * vms.v_page_size / (1024 * 1024))
543 #define pgtob(pg)  (int64_t)((intmax_t)(pg) * vms.v_page_size)
544 
545           put64(pgtob(vms.v_active_count), MEMROW + 0, MEMCOLA + 7, 6, 0);
546           put64(pgtob(vms.v_wire_count), MEMROW + 1, MEMCOLA + 7, 6, 0); /*XXX*/
547           put64(pgtob(vms.v_inactive_count +
548                         vms.v_cache_count +
549                         vms.v_free_count), MEMROW + 2, MEMCOLA + 7, 6, 0);
550           put64(s.physmem, MEMROW + 3, MEMCOLA + 7, 6, 0);
551           put64(pgtob(total.t_arm),
552                               MEMROW + 0, MEMCOLB + 5, 6, 0);
553           put64(pgtob(total.t_avm + total.t_avmshr),
554                               MEMROW + 0, MEMCOLB + 19, 6, 0);
555           put64(pgtob(total.t_vm - total.t_rm),
556                               MEMROW + 1, MEMCOLB + 5, 6, 0);
557           put64(pgtob(s.kvmsw[kvnsw].ksw_total),
558                               MEMROW + 1, MEMCOLB + 19, 6, 0);
559 
560 #if 0
561           put64(pgtob(total.t_arm), MEMROW + 2, MEMCOL + 4, 6, 0);
562           put64(pgtob(total.t_armshr), MEMROW + 2, MEMCOL + 11, 6, 0);
563           put64(pgtob(total.t_avm), MEMROW + 2, MEMCOL + 19, 6, 0);
564           put64(pgtob(total.t_avmshr), MEMROW + 2, MEMCOL + 26, 6, 0);
565           put64(pgtob(total.t_rm), MEMROW + 3, MEMCOL + 4, 6, 0);
566           put64(pgtob(total.t_rmshr), MEMROW + 3, MEMCOL + 11, 6, 0);
567           put64(pgtob(total.t_vm), MEMROW + 3, MEMCOL + 19, 6, 0);
568           put64(pgtob(total.t_vmshr), MEMROW + 3, MEMCOL + 26, 6, 0);
569           put64(pgtob(total.t_free), MEMROW + 2, MEMCOL + 34, 6, 0);
570 #endif
571 
572           put64(total.t_rq - 1, PROCSROW + 1, PROCSCOL + 0, 4, 'D');
573           put64(total.t_pw, PROCSROW + 1, PROCSCOL + 4, 4, 'D');
574           put64(total.t_dw, PROCSROW + 1, PROCSCOL + 8, 4, 'D');
575           put64(total.t_sl, PROCSROW + 1, PROCSCOL + 12, 4, 'D');
576           /*put64(total.t_sw, PROCSROW + 1, PROCSCOL + 12, 3, 'D');*/
577           if (extended_vm_stats == 0) {
578                     PUTRATE_PGTOB(Vmm.v_zfod, VMSTATROW + 0, VMSTATCOL, 7);
579           }
580           PUTRATE_PGTOB(Vmm.v_cow_faults, VMSTATROW + 1, VMSTATCOL, 7);
581           put64(pgtob(vms.v_wire_count), VMSTATROW + 2, VMSTATCOL, 7, 0);
582           put64(pgtob(vms.v_active_count), VMSTATROW + 3, VMSTATCOL, 7, 0);
583           put64(pgtob(vms.v_inactive_count), VMSTATROW + 4, VMSTATCOL, 7, 0);
584           put64(pgtob(vms.v_cache_count), VMSTATROW + 5, VMSTATCOL, 7, 0);
585           put64(pgtob(vms.v_free_count), VMSTATROW + 6, VMSTATCOL, 7, 0);
586           PUTRATE(Vmm.v_dfree, VMSTATROW + 7, VMSTATCOL, 7);
587           PUTRATE(Vmm.v_pfree, VMSTATROW + 8, VMSTATCOL, 7);
588           PUTRATE(Vmm.v_reactivated, VMSTATROW + 9, VMSTATCOL, 7);
589           PUTRATE(Vmm.v_pdwakeups, VMSTATROW + 10, VMSTATCOL, 7);
590           PUTRATE(Vmm.v_pdpages, VMSTATROW + 11, VMSTATCOL, 7);
591           PUTRATE(Vmm.v_intrans, VMSTATROW + 12, VMSTATCOL, 7);
592 
593           if (extended_vm_stats) {
594                     int64_t orig_zfod = s.Vmm.v_zfod;
595                     s.Vmm.v_zfod -= s.Vmm.v_ozfod;
596                     PUTRATE_PGTOB(Vmm.v_zfod, VMSTATROW + 11, VMSTATCOL - 14, 7);
597                     PUTRATE_PGTOB(Vmm.v_ozfod, VMSTATROW + 12, VMSTATCOL - 14, 7);
598 #define nz(x)       ((x) ? (x) : 1)
599                     put64((s.Vmm.v_zfod) * 100 / nz(orig_zfod),
600                         VMSTATROW + 13, VMSTATCOL - 14, 7, 'D');
601 #undef nz
602                     PUTRATE_PGTOB(Vmm.v_tfree, VMSTATROW + 14, VMSTATCOL - 14, 7);
603           }
604 
605           put64(s.bufspace, VMSTATROW + 13, VMSTATCOL, 7, 0);
606           put64(s.dirtybufspace/1024, VMSTATROW + 14, VMSTATCOL, 7, 'K');
607           put64(s.activevnodes, VMSTATROW + 15, VMSTATCOL, 7, 'D');
608           put64(s.cachedvnodes, VMSTATROW + 16, VMSTATCOL, 7, 'D');
609           put64(s.inactivevnodes, VMSTATROW + 17, VMSTATCOL, 7, 'D');
610           PUTRATE_PGTOB(Vmm.v_vnodepgsin, PAGEROW + 2, PAGECOL + 7, 5);
611           PUTRATE_PGTOB(Vmm.v_vnodepgsout, PAGEROW + 2, PAGECOL + 13, 5);
612           PUTRATE_PGTOB(Vmm.v_swappgsin, PAGEROW + 2, PAGECOL + 21, 5);
613           PUTRATE_PGTOB(Vmm.v_swappgsout, PAGEROW + 2, PAGECOL + 27, 5);
614           PUTRATE(Vmm.v_vnodein, PAGEROW + 3, PAGECOL + 7, 5);
615           PUTRATE(Vmm.v_vnodeout, PAGEROW + 3, PAGECOL + 13, 5);
616           PUTRATE(Vmm.v_swapin, PAGEROW + 3, PAGECOL + 21, 5);
617           PUTRATE(Vmm.v_swapout, PAGEROW + 3, PAGECOL + 27, 5);
618           PUTRATE(Vmm.v_swtch, GENSTATROW + 1, GENSTATCOL + 1, 4);
619           PUTRATE(Vmm.v_trap, GENSTATROW + 1, GENSTATCOL + 6, 4);
620           PUTRATE(Vmm.v_syscall, GENSTATROW + 1, GENSTATCOL + 11, 4);
621           PUTRATE(Vmm.v_intr, GENSTATROW + 1, GENSTATCOL + 16, 4);
622           PUTRATE(Vmm.v_soft, GENSTATROW + 1, GENSTATCOL + 21, 4);
623           PUTRATE(Vmm.v_vm_faults, GENSTATROW + 1, GENSTATCOL + 26, 4);
624           mvprintw(DISKROW, DISKCOL + 5, "                              ");
625           for (i = 0, lc = 0; i < num_devices && lc < MAXDRIVES; i++)
626                     if (dev_select[i].selected) {
627                               char tmpstr[80];
628                               sprintf(tmpstr, "%s%d", dev_select[i].device_name,
629                                         dev_select[i].unit_number);
630                               mvprintw(DISKROW, DISKCOL + 5 + 6 * lc,
631                                         " %5.5s", tmpstr);
632                               switch(state) {
633                               case TIME:
634                                         dinfo(i, ++lc, &cur, &last);
635                                         break;
636                               case RUN:
637                                         dinfo(i, ++lc, &cur, &run);
638                                         break;
639                               case BOOT:
640                                         dinfo(i, ++lc, &cur, NULL);
641                                         break;
642                               }
643                     }
644 #define nz(x)       ((x) ? (x) : 1)
645           put64(s.nchpathcount, NAMEIROW + 1, NAMEICOL + 6, 6, 'D');
646           PUTRATE(Vmm.v_exec, EXECROW + 1, EXECCOL, 5);
647           put64(nchtotal.ncs_longhits, NAMEIROW + 1, NAMEICOL + 13, 6, 'D');
648           putfloat(nchtotal.ncs_longhits * 100.0 / nz(s.nchpathcount),
649               NAMEIROW + 1, NAMEICOL + 19, 4, 0, 0);
650 
651           putfloat((double)s.nchcount / nz(s.nchpathcount),
652               NAMEIROW + 1, NAMEICOL + 27, 5, 2, 1);
653 #undef nz
654 }
655 
656 int
cmdkre(const char * cmd,char * args)657 cmdkre(const char *cmd, char *args)
658 {
659           int retval;
660 
661           if (prefix(cmd, "run")) {
662                     retval = 1;
663                     copyinfo(&s2, &s1);
664                     switch (getdevs(&run)) {
665                     case -1:
666                               errx(1, "%s", devstat_errbuf);
667                               break;
668                     case 1:
669                               num_devices = run.dinfo->numdevs;
670                               generation = run.dinfo->generation;
671                               retval = dscmd("refresh", NULL, MAXDRIVES, &cur);
672                               if (retval == 2)
673                                         labelkre();
674                               break;
675                     default:
676                               break;
677                     }
678                     state = RUN;
679                     return (retval);
680           }
681           if (prefix(cmd, "boot")) {
682                     state = BOOT;
683                     copyinfo(&z, &s1);
684                     return (1);
685           }
686           if (prefix(cmd, "time")) {
687                     state = TIME;
688                     return (1);
689           }
690           if (prefix(cmd, "zero")) {
691                     retval = 1;
692                     if (state == RUN) {
693                               getinfo(&s1);
694                               switch (getdevs(&run)) {
695                               case -1:
696                                         errx(1, "%s", devstat_errbuf);
697                                         break;
698                               case 1:
699                                         num_devices = run.dinfo->numdevs;
700                                         generation = run.dinfo->generation;
701                                         retval = dscmd("refresh",NULL, MAXDRIVES, &cur);
702                                         if (retval == 2)
703                                                   labelkre();
704                                         break;
705                               default:
706                                         break;
707                               }
708                     }
709                     return (retval);
710           }
711           retval = dscmd(cmd, args, MAXDRIVES, &cur);
712 
713           if (retval == 2)
714                     labelkre();
715 
716           return(retval);
717 }
718 
719 /* calculate number of users on the system */
720 static int
ucount(void)721 ucount(void)
722 {
723           struct utmpentry *ep = NULL;  /* avoid gcc warnings */
724           int nusers = 0;
725 
726           getutentries(NULL, &ep);
727           for (; ep; ep = ep->next)
728                     nusers++;
729 
730           return (nusers);
731 }
732 
733 static void
put64(intmax_t n,int l,int lc,int w,int type)734 put64(intmax_t n, int l, int lc, int w, int type)
735 {
736           char b[128];
737           int isneg;
738           int i;
739           int64_t d;
740           int64_t u;
741 
742           move(l, lc);
743           if (n == 0) {
744                     while (w-- > 0)
745                               addch(' ');
746                     return;
747           }
748           if (type == 0 || type == 'D')
749                     snprintf(b, sizeof(b), "%*jd", w, n);
750           else
751                     snprintf(b, sizeof(b), "%*jd%c", w - 1, n, type);
752           if (strlen(b) <= (size_t)w) {
753                     addstr(b);
754                     return;
755           }
756 
757           if (type == 'D')
758                     u = 1000;
759           else
760                     u = 1024;
761           if (n < 0) {
762                     n = -n;
763                     isneg = 1;
764           } else {
765                     isneg = 0;
766           }
767 
768           for (d = 1; n / d >= 1000; d *= u) {
769                     switch(type) {
770                     case 'D':
771                     case 0:
772                               type = 'K';
773                               break;
774                     case 'K':
775                               type = 'M';
776                               break;
777                     case 'M':
778                               type = 'G';
779                               break;
780                     case 'G':
781                               type = 'T';
782                               break;
783                     case 'T':
784                               type = 'X';
785                               break;
786                     default:
787                               type = '?';
788                               break;
789                     }
790           }
791 
792           i = w - isneg;
793           if (n / d >= 100)
794                     i -= 3;
795           else if (n / d >= 10)
796                     i -= 2;
797           else
798                     i -= 1;
799           if (i > 4) {
800                     snprintf(b + 64, sizeof(b) - 64, "%jd.%03jd%c",
801                                n / d, n / (d / 1000) % 1000, type);
802           } else if (i > 3) {
803                     snprintf(b + 64, sizeof(b) - 64, "%jd.%02jd%c",
804                                n / d, n / (d / 100) % 100, type);
805           } else if (i > 2) {
806                     snprintf(b + 64, sizeof(b) - 64, "%jd.%01jd%c",
807                                n / d, n / (d / 10) % 10, type);
808           } else {
809                     snprintf(b + 64, sizeof(b) - 64, "%jd%c",
810                                n / d, type);
811           }
812           w -= strlen(b + 64);
813           i = 64;
814           if (isneg) {
815                     b[--i] = '-';
816                     --w;
817           }
818           while (w > 0) {
819                     --w;
820                     b[--i] = ' ';
821           }
822           addstr(b + i);
823 }
824 
825 static void
putfloat(double f,int l,int lc,int w,int d,int nz)826 putfloat(double f, int l, int lc, int w, int d, int nz)
827 {
828           char b[128];
829 
830           move(l, lc);
831           if (nz && f == 0.0) {
832                     while (--w >= 0)
833                               addch(' ');
834                     return;
835           }
836           snprintf(b, sizeof(b), "%*.*f", w, d, f);
837           if (strlen(b) > (size_t)w)
838                     snprintf(b, sizeof(b), "%*.0f", w, f);
839           if (strlen(b) > (size_t)w) {
840                     while (--w >= 0)
841                               addch('*');
842                     return;
843           }
844           addstr(b);
845 }
846 
847 static void
putlongdouble(long double f,int l,int lc,int w,int d,int nz)848 putlongdouble(long double f, int l, int lc, int w, int d, int nz)
849 {
850           char b[128];
851 
852           move(l, lc);
853           if (nz && f == 0.0) {
854                     while (--w >= 0)
855                               addch(' ');
856                     return;
857           }
858           sprintf(b, "%*.*Lf", w, d, f);
859           if (strlen(b) > (size_t)w)
860                     sprintf(b, "%*.0Lf", w, f);
861           if (strlen(b) > (size_t)w) {
862                     while (--w >= 0)
863                               addch('*');
864                     return;
865           }
866           addstr(b);
867 }
868 
869 static void
putlongdoublez(long double f,int l,int lc,int w,int d,int nz)870 putlongdoublez(long double f, int l, int lc, int w, int d, int nz)
871 {
872           char b[128];
873 
874           if (f == 0.0) {
875                     move(l, lc);
876                     sprintf(b, "%*.*s", w, w, "");
877                     addstr(b);
878           } else {
879                     putlongdouble(f, l, lc, w, d, nz);
880           }
881 }
882 
883 static void
getinfo(struct Info * ls)884 getinfo(struct Info *ls)
885 {
886           struct devinfo *tmp_dinfo;
887           struct nchstats *nch_tmp;
888           size_t size;
889           size_t vms_size = sizeof(ls->Vms);
890           size_t vmm_size = sizeof(ls->Vmm);
891           size_t nch_size = sizeof(ls->nchstats) * SMP_MAXCPU;
892           size_t phys_size = sizeof(ls->physmem);
893 
894         kvnsw = kvm_getswapinfo(kd, ls->kvmsw, NKVMSW, 0);
895 
896         if (sysctlbyname("vm.vmstats", &ls->Vms, &vms_size, NULL, 0)) {
897                 perror("sysctlbyname: vm.vmstats");
898                 exit(1);
899         }
900         if (sysctlbyname("vm.vmmeter", &ls->Vmm, &vmm_size, NULL, 0)) {
901                 perror("sysctlbyname: vm.vmstats");
902                 exit(1);
903         }
904         if (sysctlbyname("hw.physmem", &ls->physmem, &phys_size, NULL, 0)) {
905                 perror("sysctlbyname: hw.physmem");
906                 exit(1);
907           }
908 
909           if (kinfo_get_sched_cputime(&ls->cp_time))
910                     err(1, "kinfo_get_sched_cputime");
911           if (kinfo_get_sched_cputime(&cp_time))
912                     err(1, "kinfo_get_sched_cputime");
913           NREAD(X_BUFFERSPACE, &ls->bufspace, sizeof(ls->bufspace));
914           NREAD(X_DESIREDVNODES, &ls->maxvnodes, sizeof(ls->maxvnodes));
915           NREAD(X_CACHEDVNODES, &ls->cachedvnodes, sizeof(ls->cachedvnodes));
916           NREAD(X_INACTIVEVNODES, &ls->inactivevnodes,
917                                                             sizeof(ls->inactivevnodes));
918           NREAD(X_ACTIVEVNODES, &ls->activevnodes, sizeof(ls->activevnodes));
919           NREAD(X_NUMDIRTYBUFFERS, &ls->dirtybufspace, sizeof(ls->dirtybufspace));
920 
921           if (nintr) {
922                     size = nintr * sizeof(ls->intrcnt[0]);
923                     sysctlbyname("hw.intrcnt_all", ls->intrcnt, &size, NULL, 0);
924           }
925           size = sizeof(ls->Total);
926           if (sysctlbyname("vm.vmtotal", &ls->Total, &size, NULL, 0) < 0) {
927                     error("Can't get kernel info: %s\n", strerror(errno));
928                     bzero(&ls->Total, sizeof(ls->Total));
929           }
930 
931           if ((nch_tmp = malloc(nch_size)) == NULL) {
932                     perror("malloc");
933                     exit(1);
934           } else {
935                     if (sysctlbyname("vfs.cache.nchstats", nch_tmp, &nch_size, NULL, 0)) {
936                               perror("sysctlbyname vfs.cache.nchstats");
937                               free(nch_tmp);
938                               exit(1);
939                     } else {
940                               if ((nch_tmp = realloc(nch_tmp, nch_size)) == NULL) {
941                                         perror("realloc");
942                                         exit(1);
943                               }
944                     }
945           }
946 
947           if (kinfo_get_cpus(&ncpu))
948                     err(1, "kinfo_get_cpus");
949           kvm_nch_cpuagg(nch_tmp, &ls->nchstats, ncpu);
950           free(nch_tmp);
951 
952           tmp_dinfo = last.dinfo;
953           last.dinfo = cur.dinfo;
954           cur.dinfo = tmp_dinfo;
955 
956           last.busy_time = cur.busy_time;
957           switch (getdevs(&cur)) {
958           case -1:
959                     errx(1, "%s", devstat_errbuf);
960                     break;
961           case 1:
962                     num_devices = cur.dinfo->numdevs;
963                     generation = cur.dinfo->generation;
964                     cmdkre("refresh", NULL);
965                     break;
966           default:
967                     break;
968           }
969 }
970 
971 static void
allocinfo(struct Info * ls)972 allocinfo(struct Info *ls)
973 {
974           ls->intrcnt = (long *) calloc(nintr, sizeof(long));
975           if (ls->intrcnt == NULL)
976                     errx(2, "out of memory");
977 }
978 
979 static void
copyinfo(struct Info * from,struct Info * to)980 copyinfo(struct Info *from, struct Info *to)
981 {
982           long *intrcnt;
983 
984           /*
985            * time, wds, seek, and xfer are malloc'd so we have to
986            * save the pointers before the structure copy and then
987            * copy by hand.
988            */
989           intrcnt = to->intrcnt;
990           *to = *from;
991 
992           bcopy(from->intrcnt, to->intrcnt = intrcnt, nintr * sizeof (int));
993 }
994 
995 static void
dinfo(int dn,int lc,struct statinfo * now,struct statinfo * then)996 dinfo(int dn, int lc, struct statinfo *now, struct statinfo *then)
997 {
998           long double kb_per_transfer;
999           long double transfers_per_secondr;
1000           long double transfers_per_secondw;
1001           long double mb_per_secondr;
1002           long double mb_per_secondw;
1003           long double elapsed_time, device_busy;
1004           int di;
1005 
1006           di = dev_select[dn].position;
1007 
1008           elapsed_time = compute_etime(now->busy_time, then ?
1009                                              then->busy_time :
1010                                              now->dinfo->devices[di].dev_creation_time);
1011 
1012           device_busy =  compute_etime(now->dinfo->devices[di].busy_time, then ?
1013                                              then->dinfo->devices[di].busy_time :
1014                                              now->dinfo->devices[di].dev_creation_time);
1015 
1016           if (compute_stats(
1017                                 &now->dinfo->devices[di],
1018                                 (then ? &then->dinfo->devices[di] : NULL),
1019                                 elapsed_time,
1020                                 NULL, NULL, NULL,
1021                                 &kb_per_transfer,
1022                                 NULL,
1023                                 NULL,
1024                                 NULL, NULL) != 0)
1025                     errx(1, "%s", devstat_errbuf);
1026 
1027           if (compute_stats_read(
1028                                 &now->dinfo->devices[di],
1029                                 (then ? &then->dinfo->devices[di] : NULL),
1030                                 elapsed_time,
1031                                 NULL, NULL, NULL,
1032                                 NULL,
1033                                 &transfers_per_secondr,
1034                                 &mb_per_secondr,
1035                                 NULL, NULL) != 0)
1036                     errx(1, "%s", devstat_errbuf);
1037 
1038           if (compute_stats_write(
1039                                 &now->dinfo->devices[di],
1040                                 (then ? &then->dinfo->devices[di] : NULL),
1041                                 elapsed_time,
1042                                 NULL, NULL, NULL,
1043                                 NULL,
1044                                 &transfers_per_secondw,
1045                                 &mb_per_secondw,
1046                                 NULL, NULL) != 0)
1047                     errx(1, "%s", devstat_errbuf);
1048 
1049 #if 0
1050           /*
1051            * Remove this hack, it no longer works properly and will
1052            * report 100% busy in situations where the device is able
1053            * to respond to the requests faster than the busy counter's
1054            * granularity.
1055            */
1056           if ((device_busy == 0) &&
1057               (transfers_per_secondr > 5 || transfers_per_secondw > 5)) {
1058                     /* the device has been 100% busy, fake it because
1059                      * as long as the device is 100% busy the busy_time
1060                      * field in the devstat struct is not updated */
1061                     device_busy = elapsed_time;
1062           }
1063 #endif
1064           if (device_busy > elapsed_time) {
1065                     /* this normally happens after one or more periods
1066                      * where the device has been 100% busy, correct it */
1067                     device_busy = elapsed_time;
1068           }
1069 
1070           lc = DISKCOL + lc * 6;
1071           putlongdoublez(kb_per_transfer, DISKROW + 1, lc, 5, 2, 0);
1072           putlongdoublez(transfers_per_secondr, DISKROW + 2, lc, 5, 0, 0);
1073           putlongdoublez(mb_per_secondr, DISKROW + 3, lc, 5, 2, 0);
1074           putlongdoublez(transfers_per_secondw, DISKROW + 4, lc, 5, 0, 0);
1075           putlongdoublez(mb_per_secondw, DISKROW + 5, lc, 5, 2, 0);
1076           putlongdouble(device_busy * 100 / elapsed_time,
1077                                               DISKROW + 6, lc, 5, 0, 0);
1078 }
1079