vmstat - Increase buffer for vmstat -m
[dragonfly.git] / usr.bin / vmstat / vmstat.c
1 /*
2  * Copyright (c) 1980, 1986, 1991, 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  * @(#) Copyright (c) 1980, 1986, 1991, 1993 The Regents of the University of California.  All rights reserved.
30  * @(#)vmstat.c 8.1 (Berkeley) 6/6/93
31  * $FreeBSD: src/usr.bin/vmstat/vmstat.c,v 1.38.2.4 2001/07/31 19:52:41 tmm Exp $
32  */
33
34 #include <sys/user.h>
35 #include <sys/param.h>
36 #include <sys/time.h>
37 #include <sys/uio.h>
38 #include <sys/namei.h>
39 #include <sys/objcache.h>
40 #include <sys/signal.h>
41 #include <sys/fcntl.h>
42 #include <sys/ioctl.h>
43 #include <sys/sysctl.h>
44 #include <sys/vmmeter.h>
45 #include <sys/interrupt.h>
46
47 #include <vm/vm_param.h>
48 #include <vm/vm_zone.h>
49
50 #include <ctype.h>
51 #include <err.h>
52 #include <errno.h>
53 #include <kinfo.h>
54 #include <kvm.h>
55 #include <limits.h>
56 #include <nlist.h>
57 #include <paths.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <sysexits.h>
62 #include <time.h>
63 #include <unistd.h>
64 #include <devstat.h>
65
66 static struct nlist namelist[] = {
67 #define X_BOOTTIME      0
68         { "_boottime",  0, 0, 0, 0 },
69 #define X_NCHSTATS      1
70         { "_nchstats",  0, 0, 0, 0 },
71 #define X_KMEMSTATISTICS        2
72         { "_kmemstatistics",    0, 0, 0, 0 },
73 #define X_ZLIST         3
74         { "_zlist",     0, 0, 0, 0 },
75 #ifdef notyet
76 #define X_DEFICIT       4
77         { "_deficit",   0, 0, 0, 0 },
78 #define X_FORKSTAT      5
79         { "_forkstat",  0, 0, 0, 0 },
80 #define X_REC           6
81         { "_rectime",   0, 0, 0, 0 },
82 #define X_PGIN          7
83         { "_pgintime",  0, 0, 0, 0 },
84 #define X_XSTATS        8
85         { "_xstats",    0, 0, 0, 0 },
86 #define X_END           9
87 #else
88 #define X_END           4
89 #endif
90         { "", 0, 0, 0, 0 },
91 };
92
93 #define ONEMB   (1024L * 1024L)
94 #define ONEKB   (1024L)
95
96 LIST_HEAD(zlist, vm_zone);
97
98 struct statinfo cur, last;
99 int num_devices, maxshowdevs;
100 long generation;
101 struct device_selection *dev_select;
102 int num_selected;
103 struct devstat_match *matches;
104 int num_matches = 0;
105 int num_devices_specified, num_selections;
106 long select_generation;
107 char **specified_devices;
108 devstat_select_mode select_mode;
109
110 struct  vmmeter vmm, ovmm;
111 struct  vmstats vms, ovms;
112
113 int     winlines = 20;
114 int     nflag = 0;
115 int     verbose = 0;
116 int     unformatted_opt = 0;
117 int     brief_opt = 0;
118
119 kvm_t *kd;
120
121 struct kinfo_cputime cp_time, old_cp_time, diff_cp_time;
122
123 #define FORKSTAT        0x01
124 #define INTRSTAT        0x02
125 #define MEMSTAT         0x04
126 #define SUMSTAT         0x08
127 #define TIMESTAT        0x10
128 #define VMSTAT          0x20
129 #define ZMEMSTAT        0x40
130 #define OCSTAT          0x80
131
132 static void cpustats(void);
133 static void dointr(void);
134 static void domem(void);
135 static void dooc(void);
136 static void dosum(void);
137 static void dozmem(u_int interval, int reps);
138 static void dovmstat(u_int, int);
139 static void kread(int, void *, size_t);
140 static void usage(void);
141 static char **getdrivedata(char **);
142 static long getuptime(void);
143 static void needhdr(int);
144 static long pct(long, long);
145
146 #ifdef notyet
147 static void dotimes(void); /* Not implemented */
148 static void doforkst(void);
149 #endif
150 static void printhdr(void);
151 static const char *formatnum(intmax_t value, int width, int do10s);
152 static void devstats(int dooutput);
153
154 int
155 main(int argc, char **argv)
156 {
157         int c, todo;
158         u_int interval;         /* milliseconds */
159         int reps;
160         char *memf, *nlistf;
161         char errbuf[_POSIX2_LINE_MAX];
162
163         memf = nlistf = NULL;
164         interval = reps = todo = 0;
165         maxshowdevs = 2;
166         while ((c = getopt(argc, argv, "bc:fiM:mN:n:op:stuvw:z")) != -1) {
167                 switch (c) {
168                 case 'b':
169                         brief_opt = 1;
170                         break;
171                 case 'c':
172                         reps = atoi(optarg);
173                         break;
174                 case 'f':
175 #ifdef notyet
176                         todo |= FORKSTAT;
177 #else
178                         errx(EX_USAGE, "sorry, -f is not (re)implemented yet");
179 #endif
180                         break;
181                 case 'i':
182                         todo |= INTRSTAT;
183                         break;
184                 case 'M':
185                         memf = optarg;
186                         break;
187                 case 'm':
188                         todo |= MEMSTAT;
189                         break;
190                 case 'N':
191                         nlistf = optarg;
192                         break;
193                 case 'n':
194                         nflag = 1;
195                         maxshowdevs = atoi(optarg);
196                         if (maxshowdevs < 0)
197                                 errx(1, "number of devices %d is < 0",
198                                      maxshowdevs);
199                         break;
200                 case 'o':
201                         todo |= OCSTAT;
202                         break;
203                 case 'p':
204                         if (buildmatch(optarg, &matches, &num_matches) != 0)
205                                 errx(1, "%s", devstat_errbuf);
206                         break;
207                 case 's':
208                         todo |= SUMSTAT;
209                         break;
210                 case 't':
211 #ifdef notyet
212                         todo |= TIMESTAT;
213 #else
214                         errx(EX_USAGE, "sorry, -t is not (re)implemented yet");
215 #endif
216                         break;
217                 case 'u':
218                         unformatted_opt = 1;
219                         break;
220                 case 'v':
221                         ++verbose;
222                         break;
223                 case 'w':
224                         interval = (u_int)(strtod(optarg, NULL) * 1000.0);
225                         break;
226                 case 'z':
227                         todo |= ZMEMSTAT;
228                         break;
229                 default:
230                         usage();
231                 }
232         }
233         argc -= optind;
234         argv += optind;
235
236         if (todo == 0)
237                 todo = VMSTAT;
238
239         /*
240          * Discard setgid privileges if not the running kernel so that bad
241          * guys can't print interesting stuff from kernel memory.
242          */
243         if (nlistf != NULL || memf != NULL) {
244                 setgid(getgid());
245                 if (todo & OCSTAT) {
246                         errx(1, "objcache stats can only be gathered on "
247                             "the running system");
248                 }
249         }
250
251         kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
252         if (kd == NULL)
253                 errx(1, "kvm_openfiles: %s", errbuf);
254
255         if ((c = kvm_nlist(kd, namelist)) != 0) {
256                 if (c > 0) {
257                         warnx("undefined symbols:");
258                         for (c = 0; c < (int)NELEM(namelist); c++)
259                                 if (namelist[c].n_type == 0)
260                                         fprintf(stderr, " %s",
261                                             namelist[c].n_name);
262                         fputc('\n', stderr);
263                 } else
264                         warnx("kvm_nlist: %s", kvm_geterr(kd));
265                 exit(1);
266         }
267
268         if (todo & VMSTAT) {
269                 struct winsize winsize;
270
271                 /*
272                  * Make sure that the userland devstat version matches the
273                  * kernel devstat version.  If not, exit and print a
274                  * message informing the user of his mistake.
275                  */
276                 if (checkversion() < 0)
277                         errx(1, "%s", devstat_errbuf);
278
279
280                 argv = getdrivedata(argv);
281                 winsize.ws_row = 0;
282                 ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&winsize);
283                 if (winsize.ws_row > 0)
284                         winlines = winsize.ws_row;
285
286         }
287
288 #define BACKWARD_COMPATIBILITY
289 #ifdef  BACKWARD_COMPATIBILITY
290         if (*argv) {
291                 interval = (u_int)(strtod(*argv, NULL) * 1000.0);
292                 if (*++argv)
293                         reps = atoi(*argv);
294         }
295 #endif
296
297         if (interval) {
298                 if (!reps)
299                         reps = -1;
300         } else if (reps) {
301                 interval = 1000;
302         }
303
304 #ifdef notyet
305         if (todo & FORKSTAT)
306                 doforkst();
307 #endif
308         if (todo & MEMSTAT)
309                 domem();
310         if (todo & ZMEMSTAT)
311                 dozmem(interval, reps);
312         if (todo & SUMSTAT)
313                 dosum();
314 #ifdef notyet
315         if (todo & TIMESTAT)
316                 dotimes();
317 #endif
318         if (todo & INTRSTAT)
319                 dointr();
320         if (todo & VMSTAT)
321                 dovmstat(interval, reps);
322         if (todo & OCSTAT)
323                 dooc();
324         exit(0);
325 }
326
327 static char **
328 getdrivedata(char **argv)
329 {
330         if ((num_devices = getnumdevs()) < 0)
331                 errx(1, "%s", devstat_errbuf);
332
333         cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
334         last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
335         bzero(cur.dinfo, sizeof(struct devinfo));
336         bzero(last.dinfo, sizeof(struct devinfo));
337
338         if (getdevs(&cur) == -1)
339                 errx(1, "%s", devstat_errbuf);
340
341         num_devices = cur.dinfo->numdevs;
342         generation = cur.dinfo->generation;
343
344         specified_devices = (char **)malloc(sizeof(char *));
345         for (num_devices_specified = 0; *argv; ++argv) {
346                 if (isdigit(**argv))
347                         break;
348                 num_devices_specified++;
349                 specified_devices = (char **)realloc(specified_devices,
350                                                      sizeof(char *) *
351                                                      num_devices_specified);
352                 specified_devices[num_devices_specified - 1] = *argv;
353         }
354         dev_select = NULL;
355
356         if (nflag == 0 && maxshowdevs < num_devices_specified)
357                         maxshowdevs = num_devices_specified;
358
359         /*
360          * People are generally only interested in disk statistics when
361          * they're running vmstat.  So, that's what we're going to give
362          * them if they don't specify anything by default.  We'll also give
363          * them any other random devices in the system so that we get to
364          * maxshowdevs devices, if that many devices exist.  If the user
365          * specifies devices on the command line, either through a pattern
366          * match or by naming them explicitly, we will give the user only
367          * those devices.
368          */
369         if ((num_devices_specified == 0) && (num_matches == 0)) {
370                 if (buildmatch("da", &matches, &num_matches) != 0)
371                         errx(1, "%s", devstat_errbuf);
372
373                 select_mode = DS_SELECT_ADD;
374         } else
375                 select_mode = DS_SELECT_ONLY;
376
377         /*
378          * At this point, selectdevs will almost surely indicate that the
379          * device list has changed, so we don't look for return values of 0
380          * or 1.  If we get back -1, though, there is an error.
381          */
382         if (selectdevs(&dev_select, &num_selected, &num_selections,
383                        &select_generation, generation, cur.dinfo->devices,
384                        num_devices, matches, num_matches, specified_devices,
385                        num_devices_specified, select_mode,
386                        maxshowdevs, 0) == -1)
387                 errx(1, "%s", devstat_errbuf);
388
389         return(argv);
390 }
391
392 static long
393 getuptime(void)
394 {
395         static time_t now, boottime;
396         time_t uptime;
397
398         if (boottime == 0)
399                 kread(X_BOOTTIME, &boottime, sizeof(boottime));
400         time(&now);
401         uptime = now - boottime;
402         if (uptime <= 0 || uptime > 60*60*24*365*10)
403                 errx(1, "time makes no sense; namelist must be wrong");
404         return(uptime);
405 }
406
407 int     hdrcnt;
408
409 static void
410 dovmstat(u_int interval, int reps)
411 {
412         struct vmtotal total;
413         struct devinfo *tmp_dinfo;
414         size_t vmm_size = sizeof(vmm);
415         size_t vms_size = sizeof(vms);
416         size_t vmt_size = sizeof(total);
417         int initial = 1;
418         int dooutput = 1;
419
420         signal(SIGCONT, needhdr);
421         if (reps != 0)
422                 dooutput = 0;
423
424         for (hdrcnt = 1;;) {
425                 if (!--hdrcnt)
426                         printhdr();
427                 if (kinfo_get_sched_cputime(&cp_time))
428                         err(1, "kinfo_get_sched_cputime");
429
430                 tmp_dinfo = last.dinfo;
431                 last.dinfo = cur.dinfo;
432                 cur.dinfo = tmp_dinfo;
433                 last.busy_time = cur.busy_time;
434
435                 /*
436                  * Here what we want to do is refresh our device stats.
437                  * getdevs() returns 1 when the device list has changed.
438                  * If the device list has changed, we want to go through
439                  * the selection process again, in case a device that we
440                  * were previously displaying has gone away.
441                  */
442                 switch (getdevs(&cur)) {
443                 case -1:
444                         errx(1, "%s", devstat_errbuf);
445                         break;
446                 case 1: {
447                         int retval;
448
449                         num_devices = cur.dinfo->numdevs;
450                         generation = cur.dinfo->generation;
451
452                         retval = selectdevs(&dev_select, &num_selected,
453                                             &num_selections, &select_generation,
454                                             generation, cur.dinfo->devices,
455                                             num_devices, matches, num_matches,
456                                             specified_devices,
457                                             num_devices_specified, select_mode,
458                                             maxshowdevs, 0);
459                         switch (retval) {
460                         case -1:
461                                 errx(1, "%s", devstat_errbuf);
462                                 break;
463                         case 1:
464                                 printhdr();
465                                 break;
466                         default:
467                                 break;
468                         }
469                 }
470                 default:
471                         break;
472                 }
473
474                 if (sysctlbyname("vm.vmstats", &vms, &vms_size, NULL, 0)) {
475                         perror("sysctlbyname: vm.vmstats");
476                         exit(1);
477                 }
478                 if (sysctlbyname("vm.vmmeter", &vmm, &vmm_size, NULL, 0)) {
479                         perror("sysctlbyname: vm.vmmeter");
480                         exit(1);
481                 }
482                 if (sysctlbyname("vm.vmtotal", &total, &vmt_size, NULL, 0)) {
483                         perror("sysctlbyname: vm.vmtotal");
484                         exit(1);
485                 }
486                 if (dooutput) {
487                         printf("%3ld %2ld %2ld",
488                                total.t_rq - 1,
489                                total.t_dw + total.t_pw,
490                                total.t_sw);
491                 }
492
493 #define rate(x)         \
494         (intmax_t)(initial ? (x) : ((intmax_t)(x) * 1000 + interval / 2) \
495                                    / interval)
496
497                 if (dooutput) {
498                         printf(" %s ",
499                                formatnum((int64_t)total.t_free *
500                                          vms.v_page_size,
501                                          5, 1));
502                         printf("%s ",
503                                formatnum(rate(vmm.v_vm_faults -
504                                               ovmm.v_vm_faults),
505                                          5, 1));
506                         printf("%s ",
507                                formatnum(rate(vmm.v_reactivated -
508                                               ovmm.v_reactivated),
509                                          4, 0));
510                         printf("%s ",
511                                formatnum(rate(vmm.v_swapin + vmm.v_vnodein -
512                                               (ovmm.v_swapin +
513                                                ovmm.v_vnodein)),
514                                          4, 0));
515                         printf("%s ",
516                                formatnum(rate(vmm.v_swapout + vmm.v_vnodeout -
517                                               (ovmm.v_swapout +
518                                                ovmm.v_vnodeout)),
519                                          4, 0));
520                         printf("%s ",
521                                formatnum(rate(vmm.v_tfree - ovmm.v_tfree),
522                                          4, 0));
523                 }
524                 devstats(dooutput);
525                 if (dooutput) {
526                         printf("%s ",
527                                formatnum(rate(vmm.v_intr - ovmm.v_intr),
528                                          5, 1));
529                         printf("%s ",
530                                formatnum(rate(vmm.v_syscall -
531                                               ovmm.v_syscall),
532                                          5, 1));
533                         printf("%s ",
534                                formatnum(rate(vmm.v_swtch -
535                                               ovmm.v_swtch),
536                                          5, 1));
537                         cpustats();
538                         printf("\n");
539                         fflush(stdout);
540                 }
541                 if (reps >= 0 && --reps <= 0)
542                         break;
543                 ovmm = vmm;
544                 usleep(interval * 1000);
545                 initial = 0;
546                 dooutput = 1;
547         }
548 }
549
550 static const char *
551 formatnum(intmax_t value, int width, int do10s)
552 {
553         static char buf[16][64];
554         static int bi;
555         const char *fmt;
556         double d;
557
558         if (brief_opt)
559                 do10s = 0;
560
561         bi = (bi + 1) % 16;
562
563         if (unformatted_opt) {
564                 switch(width) {
565                 case 4:
566                         snprintf(buf[bi], sizeof(buf[bi]), "%4jd", value);
567                         break;
568                 case 5:
569                         snprintf(buf[bi], sizeof(buf[bi]), "%5jd", value);
570                         break;
571                 default:
572                         snprintf(buf[bi], sizeof(buf[bi]), "%jd", value);
573                         break;
574                 }
575                 return buf[bi];
576         }
577
578         d = (double)value;
579         fmt = "n/a";
580
581         switch(width) {
582         case 4:
583                 if (value < 1024) {
584                         fmt = "%4.0f";
585                 } else if (value < 10*1024) {
586                         fmt = "%3.1fK";
587                         d = d / 1024;
588                 } else if (value < 1000*1024) {
589                         fmt = "%3.0fK";
590                         d = d / 1024;
591                 } else if (value < 10*1024*1024) {
592                         fmt = "%3.1fM";
593                         d = d / (1024 * 1024);
594                 } else if (value < 1000*1024*1024) {
595                         fmt = "%3.0fM";
596                         d = d / (1024 * 1024);
597                 } else {
598                         fmt = "%3.1fG";
599                         d = d / (1024.0 * 1024.0 * 1024.0);
600                 }
601                 break;
602         case 5:
603                 if (value < 1024) {
604                         fmt = "%5.0f";
605                 } else if (value < 10*1024) {
606                         fmt = "%4.2fK";
607                         d = d / 1024;
608                 } else if (value < 100*1024 && do10s) {
609                         fmt = "%4.1fK";
610                         d = d / 1024;
611                 } else if (value < 1000*1024) {
612                         fmt = "%4.0fK";
613                         d = d / 1024;
614                 } else if (value < 10*1024*1024) {
615                         fmt = "%4.2fM";
616                         d = d / (1024 * 1024);
617                 } else if (value < 100*1024*1024 && do10s) {
618                         fmt = "%4.1fM";
619                         d = d / (1024 * 1024);
620                 } else if (value < 1000*1024*1024) {
621                         fmt = "%4.0fM";
622                         d = d / (1024 * 1024);
623                 } else if (value < 10LL*1024*1024*1024) {
624                         fmt = "%4.2fG";
625                         d = d / (1024.0 * 1024.0 * 1024.0);
626                 } else if (value < 100LL*1024*1024*1024 && do10s) {
627                         fmt = "%4.1fG";
628                         d = d / (1024.0 * 1024.0 * 1024.0);
629                 } else if (value < 1000LL*1024*1024*1024) {
630                         fmt = "%4.0fG";
631                         d = d / (1024.0 * 1024.0 * 1024.0);
632                 } else {
633                         fmt = "%4.2fT";
634                         d = d / (1024.0 * 1024.0 * 1024.0 * 1024.0);
635                 }
636                 break;
637         default:
638                 fprintf(stderr, "formatnum: unsupported width %d\n", width);
639                 exit(1);
640                 break;
641         }
642         snprintf(buf[bi], sizeof(buf[bi]), fmt, d);
643         return buf[bi];
644 }
645
646 static void
647 printhdr(void)
648 {
649         int i, num_shown;
650
651         num_shown = (num_selected < maxshowdevs) ? num_selected : maxshowdevs;
652         printf("--procs-- ---memory-- -------paging------ ");
653         if (num_shown > 1)
654                 printf("--disks%.*s",
655                        num_shown * 4 - 6,
656                        "---------------------------------");
657         else if (num_shown == 1)
658                 printf("disk");
659         printf(" -----faults------ ---cpu---\n");
660         printf("  r  b  w   fre   flt   re   pi   po   fr ");
661         for (i = 0; i < num_devices; i++)
662                 if ((dev_select[i].selected)
663                  && (dev_select[i].selected <= maxshowdevs))
664                         printf(" %c%c%d ", dev_select[i].device_name[0],
665                                      dev_select[i].device_name[1],
666                                      dev_select[i].unit_number);
667         printf("  int   sys   ctx us sy id\n");
668         hdrcnt = winlines - 2;
669 }
670
671 /*
672  * Force a header to be prepended to the next output.
673  */
674 static void
675 needhdr(__unused int signo)
676 {
677
678         hdrcnt = 1;
679 }
680
681 static long
682 pct(long top, long bot)
683 {
684         long ans;
685
686         if (bot == 0)
687                 return(0);
688         ans = (quad_t)top * 100 / bot;
689         return (ans);
690 }
691
692 #define PCT(top, bot) pct((long)(top), (long)(bot))
693
694 static void
695 dosum(void)
696 {
697         struct nchstats *nch_tmp, nchstats;
698         size_t vms_size = sizeof(vms);
699         size_t vmm_size = sizeof(vmm);
700         int cpucnt;
701         u_long nchtotal;
702         u_long nchpathtotal;
703         size_t nch_size = sizeof(struct nchstats) * SMP_MAXCPU;
704
705         if (sysctlbyname("vm.vmstats", &vms, &vms_size, NULL, 0)) {
706                 perror("sysctlbyname: vm.vmstats");
707                 exit(1);
708         }
709         if (sysctlbyname("vm.vmmeter", &vmm, &vmm_size, NULL, 0)) {
710                 perror("sysctlbyname: vm.vmstats");
711                 exit(1);
712         }
713         printf("%9u cpu context switches\n", vmm.v_swtch);
714         printf("%9u device interrupts\n", vmm.v_intr);
715         printf("%9u software interrupts\n", vmm.v_soft);
716         printf("%9u traps\n", vmm.v_trap);
717         printf("%9u system calls\n", vmm.v_syscall);
718         printf("%9u kernel threads created\n", vmm.v_kthreads);
719         printf("%9u  fork() calls\n", vmm.v_forks);
720         printf("%9u vfork() calls\n", vmm.v_vforks);
721         printf("%9u rfork() calls\n", vmm.v_rforks);
722         printf("%9u exec() calls\n", vmm.v_exec);
723         printf("%9u swap pager pageins\n", vmm.v_swapin);
724         printf("%9u swap pager pages paged in\n", vmm.v_swappgsin);
725         printf("%9u swap pager pageouts\n", vmm.v_swapout);
726         printf("%9u swap pager pages paged out\n", vmm.v_swappgsout);
727         printf("%9u vnode pager pageins\n", vmm.v_vnodein);
728         printf("%9u vnode pager pages paged in\n", vmm.v_vnodepgsin);
729         printf("%9u vnode pager pageouts\n", vmm.v_vnodeout);
730         printf("%9u vnode pager pages paged out\n", vmm.v_vnodepgsout);
731         printf("%9u page daemon wakeups\n", vmm.v_pdwakeups);
732         printf("%9u pages examined by the page daemon\n", vmm.v_pdpages);
733         printf("%9u pages reactivated\n", vmm.v_reactivated);
734         printf("%9u copy-on-write faults\n", vmm.v_cow_faults);
735         printf("%9u copy-on-write optimized faults\n", vmm.v_cow_optim);
736         printf("%9u zero fill pages zeroed\n", vmm.v_zfod);
737         printf("%9u zero fill pages prezeroed\n", vmm.v_ozfod);
738         printf("%9u intransit blocking page faults\n", vmm.v_intrans);
739         printf("%9u total VM faults taken\n", vmm.v_vm_faults);
740         printf("%9u pages affected by kernel thread creation\n", vmm.v_kthreadpages);
741         printf("%9u pages affected by  fork()\n", vmm.v_forkpages);
742         printf("%9u pages affected by vfork()\n", vmm.v_vforkpages);
743         printf("%9u pages affected by rfork()\n", vmm.v_rforkpages);
744         printf("%9u pages freed\n", vmm.v_tfree);
745         printf("%9u pages freed by daemon\n", vmm.v_dfree);
746         printf("%9u pages freed by exiting processes\n", vmm.v_pfree);
747         printf("%9lu pages active\n", vms.v_active_count);
748         printf("%9lu pages inactive\n", vms.v_inactive_count);
749         printf("%9lu pages in VM cache\n", vms.v_cache_count);
750         printf("%9lu pages wired down\n", vms.v_wire_count);
751         printf("%9lu pages free\n", vms.v_free_count);
752         printf("%9u bytes per page\n", vms.v_page_size);
753         printf("%9u global smp invltlbs\n", vmm.v_smpinvltlb);
754
755         if ((nch_tmp = malloc(nch_size)) == NULL) {
756                 perror("malloc");
757                 exit(1);
758         } else {
759                 if (sysctlbyname("vfs.cache.nchstats", nch_tmp, &nch_size, NULL, 0)) {
760                         perror("sysctlbyname vfs.cache.nchstats");
761                         free(nch_tmp);
762                         exit(1);
763                 } else {
764                         if ((nch_tmp = realloc(nch_tmp, nch_size)) == NULL) {
765                                 perror("realloc");
766                                 exit(1);
767                         }
768                 }
769         }
770
771         cpucnt = nch_size / sizeof(struct nchstats);
772         kvm_nch_cpuagg(nch_tmp, &nchstats, cpucnt);
773
774         nchtotal = nchstats.ncs_goodhits + nchstats.ncs_neghits +
775             nchstats.ncs_badhits + nchstats.ncs_falsehits +
776             nchstats.ncs_miss;
777         nchpathtotal = nchstats.ncs_longhits + nchstats.ncs_longmiss;
778         printf("%9ld total path lookups\n", nchpathtotal);
779         printf("%9ld total component lookups\n", nchtotal);
780         printf(
781             "%9s cache hits (%ld%% pos + %ld%% neg)\n",
782             "", PCT(nchstats.ncs_goodhits, nchtotal),
783             PCT(nchstats.ncs_neghits, nchtotal));
784         printf("%9s deletions %ld%%, falsehits %ld%%\n", "",
785             PCT(nchstats.ncs_badhits, nchtotal),
786             PCT(nchstats.ncs_falsehits, nchtotal));
787         free(nch_tmp);
788 }
789
790 #ifdef notyet
791 void
792 doforkst(void)
793 {
794         struct forkstat fks;
795
796         kread(X_FORKSTAT, &fks, sizeof(struct forkstat));
797         printf("%d forks, %d pages, average %.2f\n",
798             fks.cntfork, fks.sizfork, (double)fks.sizfork / fks.cntfork);
799         printf("%d vforks, %d pages, average %.2f\n",
800             fks.cntvfork, fks.sizvfork, (double)fks.sizvfork / fks.cntvfork);
801 }
802 #endif
803
804 static void
805 devstats(int dooutput)
806 {
807         int dn;
808         long double transfers_per_second;
809         long double busy_seconds;
810
811         diff_cp_time.cp_user = cp_time.cp_user - old_cp_time.cp_user;
812         diff_cp_time.cp_nice = cp_time.cp_nice - old_cp_time.cp_nice;
813         diff_cp_time.cp_sys = cp_time.cp_sys - old_cp_time.cp_sys;
814         diff_cp_time.cp_intr = cp_time.cp_intr - old_cp_time.cp_intr;
815         diff_cp_time.cp_idle = cp_time.cp_idle - old_cp_time.cp_idle;
816         old_cp_time = cp_time;
817
818         busy_seconds = compute_etime(cur.busy_time, last.busy_time);
819
820         for (dn = 0; dn < num_devices; dn++) {
821                 int di;
822
823                 if ((dev_select[dn].selected == 0)
824                  || (dev_select[dn].selected > maxshowdevs))
825                         continue;
826
827                 di = dev_select[dn].position;
828
829                 if (compute_stats(&cur.dinfo->devices[di],
830                                   &last.dinfo->devices[di], busy_seconds,
831                                   NULL, NULL, NULL,
832                                   NULL, &transfers_per_second, NULL,
833                                   NULL, NULL) != 0)
834                         errx(1, "%s", devstat_errbuf);
835
836                 if (dooutput)
837                         printf("%s ", formatnum(transfers_per_second, 4, 0));
838         }
839 }
840
841 static void
842 cpustats(void)
843 {
844         uint64_t total;
845         double totusage;
846
847         total = diff_cp_time.cp_user + diff_cp_time.cp_nice +
848             diff_cp_time.cp_sys + diff_cp_time.cp_intr + diff_cp_time.cp_idle;
849
850         if (total)
851                 totusage = 100.0 / total;
852         else
853                 totusage = 0;
854         printf("%2.0f ",
855                (diff_cp_time.cp_user + diff_cp_time.cp_nice) * totusage);
856         printf("%2.0f ",
857                (diff_cp_time.cp_sys + diff_cp_time.cp_intr) * totusage);
858         printf("%2.0f",
859                diff_cp_time.cp_idle * totusage);
860 }
861
862 static void
863 dointr(void)
864 {
865         u_long *intrcnt, uptime;
866         u_int64_t inttotal;
867         size_t nintr, inamlen, i, size;
868         int nwidth;
869         char *intrstr;
870         char **intrname;
871
872         uptime = getuptime();
873         if (sysctlbyname("hw.intrnames", NULL, &inamlen, NULL, 0) != 0)
874                 errx(1, "sysctlbyname");
875         intrstr = malloc(inamlen);
876         if (intrstr == NULL)
877                 err(1, "malloc");
878         sysctlbyname("hw.intrnames", intrstr, &inamlen, NULL, 0);
879         for (nintr = 0, i = 0; i < inamlen; ++i) {
880                 if (intrstr[i] == 0)
881                         nintr++;
882         }
883         intrname = malloc(nintr * sizeof(char *));
884         for (i = 0; i < nintr; ++i) {
885                 intrname[i] = intrstr;
886                 intrstr += strlen(intrstr) + 1;
887         }
888
889         size = nintr * sizeof(*intrcnt);
890         intrcnt = calloc(nintr, sizeof(*intrcnt));
891         if (intrcnt == NULL)
892                 err(1, "malloc");
893         sysctlbyname("hw.intrcnt", intrcnt, &size, NULL, 0);
894
895         nwidth = 21;
896         for (i = 0; i < nintr; ++i) {
897                 if (nwidth < (int)strlen(intrname[i]))
898                         nwidth = (int)strlen(intrname[i]);
899         }
900         if (verbose) nwidth += 12;
901
902         printf("%-*.*s %11s %10s\n",
903                 nwidth, nwidth, "interrupt", "total", "rate");
904         inttotal = 0;
905         for (i = 0; i < nintr; ++i) {
906                 int named;
907                 char *infop, irqinfo[72];
908
909                 if ((named = strncmp(intrname[i], "irq", 3)) != 0 ||
910                     intrcnt[i] > 0) {
911                         infop = intrname[i];
912                         if (verbose) {
913                                 ssize_t irq, cpu;
914
915                                 irq = i % MAX_INTS;
916                                 cpu = i / MAX_INTS;
917                                 if (named) {
918                                         snprintf(irqinfo, sizeof(irqinfo),
919                                                  "irq%-3zd %3zd: %s",
920                                                  irq, cpu, intrname[i]);
921                                 } else {
922                                         snprintf(irqinfo, sizeof(irqinfo),
923                                                  "irq%-3zd %3zd: ", irq, cpu);
924                                 }
925                                 infop = irqinfo;
926                         }
927                         printf("%-*.*s %11lu %10lu\n",
928                                 nwidth, nwidth, infop,
929                                 intrcnt[i], intrcnt[i] / uptime);
930                 }
931                 inttotal += intrcnt[i];
932         }
933         printf("%-*.*s %11llu %10llu\n",
934                 nwidth, nwidth, "Total",
935                 (long long)inttotal, (long long)(inttotal / uptime));
936 }
937
938 #define MAX_KMSTATS     16384
939
940 enum ksuse { KSINUSE, KSMEMUSE, KSCALLS };
941
942 static long
943 cpuagg(struct malloc_type *ks, enum ksuse use)
944 {
945     int i;
946     long ttl;
947
948     ttl = 0;
949
950     switch(use) {
951     case KSINUSE:
952         for (i = 0; i < SMP_MAXCPU; ++i)
953             ttl += ks->ks_use[i].inuse;
954         break;
955     case KSMEMUSE:
956         for (i = 0; i < SMP_MAXCPU; ++i)
957             ttl += ks->ks_use[i].memuse;
958         break;
959     case KSCALLS:
960         for (i = 0; i < SMP_MAXCPU; ++i)
961             ttl += ks->ks_use[i].calls;
962         break;
963     }
964     return(ttl);
965 }
966
967 static void
968 domem(void)
969 {
970         struct malloc_type *ks;
971         int i;
972         int nkms;
973         long totuse = 0, totreq = 0;
974         struct malloc_type kmemstats[MAX_KMSTATS], *kmsp;
975         char buf[1024];
976
977         kread(X_KMEMSTATISTICS, &kmsp, sizeof(kmsp));
978         for (nkms = 0; nkms < MAX_KMSTATS && kmsp != NULL; nkms++) {
979                 if (sizeof(kmemstats[0]) != kvm_read(kd, (u_long)kmsp,
980                     &kmemstats[nkms], sizeof(kmemstats[0])))
981                         err(1, "kvm_read(%p)", (void *)kmsp);
982                 if (sizeof(buf) !=  kvm_read(kd,
983                     (u_long)kmemstats[nkms].ks_shortdesc, buf, sizeof(buf)))
984                         err(1, "kvm_read(%p)",
985                             kmemstats[nkms].ks_shortdesc);
986                 buf[sizeof(buf) - 1] = '\0';
987                 kmemstats[nkms].ks_shortdesc = strdup(buf);
988                 kmsp = kmemstats[nkms].ks_next;
989         }
990         if (kmsp != NULL)
991                 warnx("truncated to the first %d memory types", nkms);
992
993         printf(
994             "\nMemory statistics by type\n");
995         printf("               Type   Count  MemUse   Limit Requests\n");
996         for (i = 0, ks = &kmemstats[0]; i < nkms; i++, ks++) {
997                 long ks_inuse;
998                 long ks_memuse;
999                 long ks_calls;
1000
1001                 ks_calls = cpuagg(ks, KSCALLS);
1002                 if (ks_calls == 0)
1003                         continue;
1004
1005                 ks_inuse = cpuagg(ks, KSINUSE);
1006                 ks_memuse = cpuagg(ks, KSMEMUSE);
1007
1008                 printf("%19s   %s   %s   %s    %s\n",
1009                         ks->ks_shortdesc,
1010                         formatnum(ks_inuse, 5, 1),
1011                         formatnum(ks_memuse, 5, 1),
1012                         formatnum(ks->ks_limit, 5, 1),
1013                         formatnum(ks_calls, 5, 1));
1014
1015                 totuse += ks_memuse;
1016                 totreq += ks_calls;
1017         }
1018         printf("\nMemory Totals:  In Use  Requests\n");
1019         printf("                 %s  %s\n",
1020                 formatnum(totuse, 5, 1),
1021                 formatnum(totreq, 5, 1));
1022 }
1023
1024 static void
1025 dooc(void)
1026 {
1027         struct objcache_stats *stat, *s;
1028         size_t len, count;
1029
1030         if (sysctlbyname("kern.objcache.stats", NULL, &len, NULL, 0) < 0)
1031                 errx(1, "objcache stats sysctl failed\n");
1032
1033         /* Add some extra space. */
1034         stat = malloc(len + (8 * sizeof(*stat)));
1035         if (sysctlbyname("kern.objcache.stats", stat, &len, NULL, 0) < 0)
1036                 errx(1, "objcache stats sysctl failed\n");
1037
1038         printf(
1039             "\nObjcache statistics by name\n");
1040         printf("                 Name    Used  Cached   Limit Requests  Allocs Fails  Exhausts\n");
1041         for (s = stat, count = 0; count < len; ++s) {
1042                 printf("%21s   %s   %s   %s    %s   %s  %s  %s\n",
1043                     s->oc_name,
1044                     formatnum(s->oc_used, 5, 1),
1045                     formatnum(s->oc_cached, 5, 1),
1046                     s->oc_limit < OBJCACHE_UNLIMITED ?
1047                     formatnum(s->oc_limit, 5, 1) : "unlim",
1048                     formatnum(s->oc_requested, 5, 1),
1049                     formatnum(s->oc_allocated, 5, 1),
1050                     formatnum(s->oc_failed, 4, 1),
1051                     formatnum(s->oc_exhausted, 4, 1));
1052
1053                 count += sizeof(*s);
1054         }
1055         free(stat);
1056 }
1057
1058 #define MAXSAVE 16
1059
1060 static void
1061 dozmem(u_int interval, int reps)
1062 {
1063         struct zlist    zlist;
1064         struct vm_zone  *kz;
1065         struct vm_zone  zone;
1066         struct vm_zone  save[MAXSAVE];
1067         long zfreecnt_prev;
1068         long znalloc_prev;
1069         long zfreecnt_next;
1070         long znalloc_next;
1071         char name[64];
1072         size_t namesz;
1073         int first = 1;
1074         int i;
1075         int n;
1076
1077         bzero(save, sizeof(save));
1078
1079 again:
1080         kread(X_ZLIST, &zlist, sizeof(zlist));
1081         kz = LIST_FIRST(&zlist);
1082         i = 0;
1083
1084         while (kz) {
1085                 if (kvm_read(kd, (intptr_t)kz, &zone, sizeof(zone)) !=
1086                     (ssize_t)sizeof(zone)) {
1087                         perror("kvm_read");
1088                         break;
1089                 }
1090                 zfreecnt_prev = save[i].zfreecnt;
1091                 znalloc_prev = save[i].znalloc;
1092                 for (n = 0; n < SMP_MAXCPU; ++n) {
1093                         zfreecnt_prev += save[i].zpcpu[n].zfreecnt;
1094                         znalloc_prev += save[i].zpcpu[n].znalloc;
1095                 }
1096
1097                 zfreecnt_next = zone.zfreecnt;
1098                 znalloc_next = zone.znalloc;
1099                 for (n = 0; n < SMP_MAXCPU; ++n) {
1100                         zfreecnt_next += zone.zpcpu[n].zfreecnt;
1101                         znalloc_next += zone.zpcpu[n].znalloc;
1102                 }
1103                 save[i] = zone;
1104
1105                 namesz = sizeof(name);
1106                 if (kvm_readstr(kd, (intptr_t)zone.zname, name, &namesz) == NULL) {
1107                         perror("kvm_read");
1108                         break;
1109                 }
1110                 if (first && interval) {
1111                         /* do nothing */
1112                 } else if (zone.zmax) {
1113                         printf("%-10s %9ld / %-9ld %5ldM used"
1114                                " %6.2f%% ",
1115                                 name,
1116                                 (long)(zone.ztotal - zfreecnt_next),
1117                                 (long)zone.zmax,
1118                                 (long)zone.zpagecount * 4096 / (1024 * 1024),
1119                                 (double)(zone.ztotal - zfreecnt_next) *
1120                                         100.0 / (double)zone.zmax);
1121                 } else {
1122                         printf("%-10s %9ld             %5ldM used"
1123                                "         ",
1124                                 name,
1125                                 (long)(zone.ztotal - zfreecnt_next),
1126                                 (long)(zone.ztotal - zfreecnt_next) *
1127                                         zone.zsize / (1024 * 1024));
1128                 }
1129                 if (first == 0) {
1130                         printf("use=%ld\n", znalloc_next - znalloc_prev);
1131                 } else if (interval == 0)
1132                         printf("\n");
1133
1134                 kz = LIST_NEXT(&zone, zlink);
1135                 ++i;
1136         }
1137         if (reps) {
1138                 first = 0;
1139                 fflush(stdout);
1140                 usleep(interval * 1000);
1141                 --reps;
1142                 printf("\n");
1143                 goto again;
1144         }
1145 }
1146
1147 /*
1148  * kread reads something from the kernel, given its nlist index.
1149  */
1150 static void
1151 kread(int nlx, void *addr, size_t size)
1152 {
1153         const char *sym;
1154
1155         if (namelist[nlx].n_type == 0 || namelist[nlx].n_value == 0) {
1156                 sym = namelist[nlx].n_name;
1157                 if (*sym == '_')
1158                         ++sym;
1159                 errx(1, "symbol %s not defined", sym);
1160         }
1161         if (kvm_read(kd, namelist[nlx].n_value, addr, size) != (ssize_t)size) {
1162                 sym = namelist[nlx].n_name;
1163                 if (*sym == '_')
1164                         ++sym;
1165                 errx(1, "%s: %s", sym, kvm_geterr(kd));
1166         }
1167 }
1168
1169 static void
1170 usage(void)
1171 {
1172         fprintf(stderr, "%s%s",
1173                 "usage: vmstat [-imsuvz] [-c count] [-M core] "
1174                 "[-N system] [-w wait]\n",
1175                 "              [-n devs] [disks]\n");
1176         exit(1);
1177 }