Split the struct vmmeter cnt structure into a global vmstats structure and
[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. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#) Copyright (c) 1980, 1986, 1991, 1993 The Regents of the University of California.  All rights reserved.
34  * @(#)vmstat.c 8.1 (Berkeley) 6/6/93
35  * $FreeBSD: src/usr.bin/vmstat/vmstat.c,v 1.38.2.4 2001/07/31 19:52:41 tmm Exp $
36  * $DragonFly: src/usr.bin/vmstat/vmstat.c,v 1.3 2003/07/03 18:34:00 dillon Exp $
37  */
38
39 #include <sys/param.h>
40 #include <sys/time.h>
41 #include <sys/proc.h>
42 #include <sys/dkstat.h>
43 #include <sys/uio.h>
44 #include <sys/namei.h>
45 #include <sys/malloc.h>
46 #include <sys/signal.h>
47 #include <sys/fcntl.h>
48 #include <sys/ioctl.h>
49 #include <sys/sysctl.h>
50 #include <sys/vmmeter.h>
51
52 #include <vm/vm_param.h>
53
54 #include <ctype.h>
55 #include <err.h>
56 #include <errno.h>
57 #include <kvm.h>
58 #include <limits.h>
59 #include <nlist.h>
60 #include <paths.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <sysexits.h>
65 #include <time.h>
66 #include <unistd.h>
67 #include <devstat.h>
68
69 struct nlist namelist[] = {
70 #define X_CPTIME        0
71         { "_cp_time" },
72 #define X_BOOTTIME      1
73         { "_boottime" },
74 #define X_HZ            2
75         { "_hz" },
76 #define X_STATHZ        3
77         { "_stathz" },
78 #define X_NCHSTATS      4
79         { "_nchstats" },
80 #define X_INTRNAMES     5
81         { "_intrnames" },
82 #define X_EINTRNAMES    6
83         { "_eintrnames" },
84 #define X_INTRCNT       8
85         { "_intrcnt" },
86 #define X_EINTRCNT      8
87         { "_eintrcnt" },
88 #define X_KMEMSTATISTICS        9
89         { "_kmemstatistics" },
90 #define X_KMEMBUCKETS   10
91         { "_bucket" },
92 #define X_ZLIST         11
93         { "_zlist" },
94 #ifdef notyet
95 #define X_DEFICIT       12
96         { "_deficit" },
97 #define X_FORKSTAT      13
98         { "_forkstat" },
99 #define X_REC           14
100         { "_rectime" },
101 #define X_PGIN          15
102         { "_pgintime" },
103 #define X_XSTATS        16
104         { "_xstats" },
105 #define X_END           17
106 #else
107 #define X_END           18
108 #endif
109         { "" },
110 };
111
112 struct statinfo cur, last;
113 int num_devices, maxshowdevs;
114 long generation;
115 struct device_selection *dev_select;
116 int num_selected;
117 struct devstat_match *matches;
118 int num_matches = 0;
119 int num_devices_specified, num_selections;
120 long select_generation;
121 char **specified_devices;
122 devstat_select_mode select_mode;
123
124 struct  vmmeter vmm, ovmm;
125 struct  vmstats vms, ovms;
126
127 int     winlines = 20;
128 int     nflag = 0;
129
130 kvm_t *kd;
131
132 #define FORKSTAT        0x01
133 #define INTRSTAT        0x02
134 #define MEMSTAT         0x04
135 #define SUMSTAT         0x08
136 #define TIMESTAT        0x10
137 #define VMSTAT          0x20
138 #define ZMEMSTAT        0x40
139
140 void    cpustats(), dointr(), domem(), dosum(), dozmem();
141 void    dovmstat(), kread(), usage();
142 #ifdef notyet
143 void    dotimes(), doforkst();
144 #endif
145 void printhdr __P((void));
146 static void devstats();
147
148 int
149 main(argc, argv)
150         register int argc;
151         register char **argv;
152 {
153         register int c, todo;
154         u_int interval;
155         int reps;
156         char *memf, *nlistf;
157         char errbuf[_POSIX2_LINE_MAX];
158
159         memf = nlistf = NULL;
160         interval = reps = todo = 0;
161         maxshowdevs = 2;
162         while ((c = getopt(argc, argv, "c:fiM:mN:n:p:stw:z")) != -1) {
163                 switch (c) {
164                 case 'c':
165                         reps = atoi(optarg);
166                         break;
167                 case 'f':
168 #ifdef notyet
169                         todo |= FORKSTAT;
170 #else
171                         errx(EX_USAGE, "sorry, -f is not (re)implemented yet");
172 #endif
173                         break;
174                 case 'i':
175                         todo |= INTRSTAT;
176                         break;
177                 case 'M':
178                         memf = optarg;
179                         break;
180                 case 'm':
181                         todo |= MEMSTAT;
182                         break;
183                 case 'N':
184                         nlistf = optarg;
185                         break;
186                 case 'n':
187                         nflag = 1;
188                         maxshowdevs = atoi(optarg);
189                         if (maxshowdevs < 0)
190                                 errx(1, "number of devices %d is < 0",
191                                      maxshowdevs);
192                         break;
193                 case 'p':
194                         if (buildmatch(optarg, &matches, &num_matches) != 0)
195                                 errx(1, "%s", devstat_errbuf);
196                         break;
197                 case 's':
198                         todo |= SUMSTAT;
199                         break;
200                 case 't':
201 #ifdef notyet
202                         todo |= TIMESTAT;
203 #else
204                         errx(EX_USAGE, "sorry, -t is not (re)implemented yet");
205 #endif
206                         break;
207                 case 'w':
208                         interval = atoi(optarg);
209                         break;
210                 case 'z':
211                         todo |= ZMEMSTAT;
212                         break;
213                 case '?':
214                 default:
215                         usage();
216                 }
217         }
218         argc -= optind;
219         argv += optind;
220
221         if (todo == 0)
222                 todo = VMSTAT;
223
224         /*
225          * Discard setgid privileges if not the running kernel so that bad
226          * guys can't print interesting stuff from kernel memory.
227          */
228         if (nlistf != NULL || memf != NULL)
229                 setgid(getgid());
230
231         kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
232         if (kd == 0) 
233                 errx(1, "kvm_openfiles: %s", errbuf);
234
235         if ((c = kvm_nlist(kd, namelist)) != 0) {
236                 if (c > 0) {
237                         warnx("undefined symbols:");
238                         for (c = 0;
239                             c < sizeof(namelist)/sizeof(namelist[0]); c++)
240                                 if (namelist[c].n_type == 0)
241                                         fprintf(stderr, " %s",
242                                             namelist[c].n_name);
243                         (void)fputc('\n', stderr);
244                 } else
245                         warnx("kvm_nlist: %s", kvm_geterr(kd));
246                 exit(1);
247         }
248
249         if (todo & VMSTAT) {
250                 char **getdrivedata();
251                 struct winsize winsize;
252
253                 /*
254                  * Make sure that the userland devstat version matches the
255                  * kernel devstat version.  If not, exit and print a
256                  * message informing the user of his mistake.
257                  */
258                 if (checkversion() < 0)
259                         errx(1, "%s", devstat_errbuf);
260
261
262                 argv = getdrivedata(argv);
263                 winsize.ws_row = 0;
264                 (void) ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&winsize);
265                 if (winsize.ws_row > 0)
266                         winlines = winsize.ws_row;
267
268         }
269
270 #define BACKWARD_COMPATIBILITY
271 #ifdef  BACKWARD_COMPATIBILITY
272         if (*argv) {
273                 interval = atoi(*argv);
274                 if (*++argv)
275                         reps = atoi(*argv);
276         }
277 #endif
278
279         if (interval) {
280                 if (!reps)
281                         reps = -1;
282         } else if (reps)
283                 interval = 1;
284
285 #ifdef notyet
286         if (todo & FORKSTAT)
287                 doforkst();
288 #endif
289         if (todo & MEMSTAT)
290                 domem();
291         if (todo & ZMEMSTAT)
292                 dozmem();
293         if (todo & SUMSTAT)
294                 dosum();
295 #ifdef notyet
296         if (todo & TIMESTAT)
297                 dotimes();
298 #endif
299         if (todo & INTRSTAT)
300                 dointr();
301         if (todo & VMSTAT)
302                 dovmstat(interval, reps);
303         exit(0);
304 }
305
306 char **
307 getdrivedata(argv)
308         char **argv;
309 {
310         if ((num_devices = getnumdevs()) < 0)
311                 errx(1, "%s", devstat_errbuf);
312
313         cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
314         last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
315         bzero(cur.dinfo, sizeof(struct devinfo));
316         bzero(last.dinfo, sizeof(struct devinfo));
317
318         if (getdevs(&cur) == -1)
319                 errx(1, "%s", devstat_errbuf);
320
321         num_devices = cur.dinfo->numdevs;
322         generation = cur.dinfo->generation;
323
324         specified_devices = (char **)malloc(sizeof(char *));
325         for (num_devices_specified = 0; *argv; ++argv) {
326                 if (isdigit(**argv))
327                         break;
328                 num_devices_specified++;
329                 specified_devices = (char **)realloc(specified_devices,
330                                                      sizeof(char *) *
331                                                      num_devices_specified);
332                 specified_devices[num_devices_specified - 1] = *argv;
333         }
334         dev_select = NULL;
335
336         if (nflag == 0 && maxshowdevs < num_devices_specified)
337                         maxshowdevs = num_devices_specified;
338
339         /*
340          * People are generally only interested in disk statistics when
341          * they're running vmstat.  So, that's what we're going to give
342          * them if they don't specify anything by default.  We'll also give
343          * them any other random devices in the system so that we get to
344          * maxshowdevs devices, if that many devices exist.  If the user
345          * specifies devices on the command line, either through a pattern
346          * match or by naming them explicitly, we will give the user only
347          * those devices.
348          */
349         if ((num_devices_specified == 0) && (num_matches == 0)) {
350                 if (buildmatch("da", &matches, &num_matches) != 0)
351                         errx(1, "%s", devstat_errbuf);
352
353                 select_mode = DS_SELECT_ADD;
354         } else
355                 select_mode = DS_SELECT_ONLY;
356
357         /*
358          * At this point, selectdevs will almost surely indicate that the
359          * device list has changed, so we don't look for return values of 0
360          * or 1.  If we get back -1, though, there is an error.
361          */
362         if (selectdevs(&dev_select, &num_selected, &num_selections,
363                        &select_generation, generation, cur.dinfo->devices,
364                        num_devices, matches, num_matches, specified_devices,
365                        num_devices_specified, select_mode,
366                        maxshowdevs, 0) == -1)
367                 errx(1, "%s", devstat_errbuf);
368
369         return(argv);
370 }
371
372 long
373 getuptime()
374 {
375         static time_t now, boottime;
376         time_t uptime;
377
378         if (boottime == 0)
379                 kread(X_BOOTTIME, &boottime, sizeof(boottime));
380         (void)time(&now);
381         uptime = now - boottime;
382         if (uptime <= 0 || uptime > 60*60*24*365*10)
383                 errx(1, "time makes no sense; namelist must be wrong");
384         return(uptime);
385 }
386
387 int     hz, hdrcnt;
388
389 void
390 dovmstat(interval, reps)
391         u_int interval;
392         int reps;
393 {
394         struct vmtotal total;
395         time_t uptime, halfuptime;
396         struct devinfo *tmp_dinfo;
397         void needhdr();
398         int mib[2];
399         size_t size;
400         int vmm_size = sizeof(vmm);
401         int vms_size = sizeof(vms);
402         int vmt_size = sizeof(total);
403
404         uptime = getuptime();
405         halfuptime = uptime / 2;
406         (void)signal(SIGCONT, needhdr);
407
408         if (namelist[X_STATHZ].n_type != 0 && namelist[X_STATHZ].n_value != 0)
409                 kread(X_STATHZ, &hz, sizeof(hz));
410         if (!hz)
411                 kread(X_HZ, &hz, sizeof(hz));
412
413         for (hdrcnt = 1;;) {
414                 if (!--hdrcnt)
415                         printhdr();
416                 kread(X_CPTIME, cur.cp_time, sizeof(cur.cp_time));
417
418                 tmp_dinfo = last.dinfo;
419                 last.dinfo = cur.dinfo;
420                 cur.dinfo = tmp_dinfo;
421                 last.busy_time = cur.busy_time;
422
423                 /*
424                  * Here what we want to do is refresh our device stats.
425                  * getdevs() returns 1 when the device list has changed.
426                  * If the device list has changed, we want to go through
427                  * the selection process again, in case a device that we
428                  * were previously displaying has gone away.
429                  */
430                 switch (getdevs(&cur)) {
431                 case -1:
432                         errx(1, "%s", devstat_errbuf);
433                         break;
434                 case 1: {
435                         int retval;
436
437                         num_devices = cur.dinfo->numdevs;
438                         generation = cur.dinfo->generation;
439
440                         retval = selectdevs(&dev_select, &num_selected,
441                                             &num_selections, &select_generation,
442                                             generation, cur.dinfo->devices,
443                                             num_devices, matches, num_matches,
444                                             specified_devices,
445                                             num_devices_specified, select_mode,
446                                             maxshowdevs, 0);
447                         switch (retval) {
448                         case -1:
449                                 errx(1, "%s", devstat_errbuf);
450                                 break;
451                         case 1:
452                                 printhdr();
453                                 break;
454                         default:
455                                 break;
456                         }
457                 }
458                 default:
459                         break;
460                 }
461
462                 if (sysctlbyname("vm.vmstats", &vms, &vms_size, NULL, 0)) {
463                         perror("sysctlbyname: vm.vmstats");
464                         exit(1);
465                 }
466                 if (sysctlbyname("vm.vmmeter", &vmm, &vmm_size, NULL, 0)) {
467                         perror("sysctlbyname: vm.vmstats");
468                         exit(1);
469                 } 
470                 if (sysctlbyname("vm.vmtotal", &total, &vmt_size, NULL, 0)) {
471                         perror("sysctlbyname: vm.vmtotal");
472                         exit(1);
473                 } 
474                 (void)printf("%2d %1d %1d",
475                     total.t_rq - 1, total.t_dw + total.t_pw, total.t_sw);
476 #define vmstat_pgtok(a) ((a) * vms.v_page_size >> 10)
477 #define rate(x) (((x) + halfuptime) / uptime)   /* round */
478                 (void)printf(" %7ld %6ld ",
479                     (long)vmstat_pgtok(total.t_avm), (long)vmstat_pgtok(total.t_free));
480                 (void)printf("%4lu ",
481                     (u_long)rate(vmm.v_vm_faults - ovmm.v_vm_faults));
482                 (void)printf("%3lu ",
483                     (u_long)rate(vmm.v_reactivated - ovmm.v_reactivated));
484                 (void)printf("%3lu ",
485                     (u_long)rate(vmm.v_swapin + vmm.v_vnodein -
486                     (ovmm.v_swapin + ovmm.v_vnodein)));
487                 (void)printf("%3lu ",
488                     (u_long)rate(vmm.v_swapout + vmm.v_vnodeout -
489                     (ovmm.v_swapout + ovmm.v_vnodeout)));
490                 (void)printf("%3lu ",
491                     (u_long)rate(vmm.v_tfree - ovmm.v_tfree));
492                 (void)printf("%3lu ",
493                     (u_long)rate(vmm.v_pdpages - ovmm.v_pdpages));
494                 devstats();
495                 (void)printf("%4lu %4lu %3lu ",
496                     (u_long)rate(vmm.v_intr - ovmm.v_intr),
497                     (u_long)rate(vmm.v_syscall - ovmm.v_syscall),
498                     (u_long)rate(vmm.v_swtch - ovmm.v_swtch));
499                 cpustats();
500                 (void)printf("\n");
501                 (void)fflush(stdout);
502                 if (reps >= 0 && --reps <= 0)
503                         break;
504                 ovmm = vmm;
505                 uptime = interval;
506                 /*
507                  * We round upward to avoid losing low-frequency events
508                  * (i.e., >= 1 per interval but < 1 per second).
509                  */
510                 if (interval != 1)
511                         halfuptime = (uptime + 1) / 2;
512                 else
513                         halfuptime = 0;
514                 (void)sleep(interval);
515         }
516 }
517
518 void
519 printhdr()
520 {
521         int i, num_shown;
522
523         num_shown = (num_selected < maxshowdevs) ? num_selected : maxshowdevs;
524         (void)printf(" procs      memory      page%*s", 19, "");
525         if (num_shown > 1)
526                 (void)printf(" disks %*s", num_shown * 4 - 7, "");
527         else if (num_shown == 1)
528                 (void)printf("disk");
529         (void)printf("   faults      cpu\n");
530         (void)printf(" r b w     avm    fre  flt  re  pi  po  fr  sr ");
531         for (i = 0; i < num_devices; i++)
532                 if ((dev_select[i].selected)
533                  && (dev_select[i].selected <= maxshowdevs))
534                         (void)printf("%c%c%d ", dev_select[i].device_name[0],
535                                      dev_select[i].device_name[1],
536                                      dev_select[i].unit_number);
537         (void)printf("  in   sy  cs us sy id\n");
538         hdrcnt = winlines - 2;
539 }
540
541 /*
542  * Force a header to be prepended to the next output.
543  */
544 void
545 needhdr()
546 {
547
548         hdrcnt = 1;
549 }
550
551 long
552 pct(top, bot)
553         long top, bot;
554 {
555         long ans;
556
557         if (bot == 0)
558                 return(0);
559         ans = (quad_t)top * 100 / bot;
560         return (ans);
561 }
562
563 #define PCT(top, bot) pct((long)(top), (long)(bot))
564
565 void
566 dosum()
567 {
568         struct nchstats nchstats;
569         long nchtotal;
570         int vms_size = sizeof(vms);
571         int vmm_size = sizeof(vmm);
572
573         if (sysctlbyname("vm.vmstats", &vms, &vms_size, NULL, 0)) {
574                 perror("sysctlbyname: vm.vmstats");
575                 exit(1);
576         }
577         if (sysctlbyname("vm.vmmeter", &vmm, &vmm_size, NULL, 0)) {
578                 perror("sysctlbyname: vm.vmstats");
579                 exit(1);
580         } 
581         (void)printf("%9u cpu context switches\n", vmm.v_swtch);
582         (void)printf("%9u device interrupts\n", vmm.v_intr);
583         (void)printf("%9u software interrupts\n", vmm.v_soft);
584         (void)printf("%9u traps\n", vmm.v_trap);
585         (void)printf("%9u system calls\n", vmm.v_syscall);
586         (void)printf("%9u kernel threads created\n", vmm.v_kthreads);
587         (void)printf("%9u  fork() calls\n", vmm.v_forks);
588         (void)printf("%9u vfork() calls\n", vmm.v_vforks);
589         (void)printf("%9u rfork() calls\n", vmm.v_rforks);
590         (void)printf("%9u swap pager pageins\n", vmm.v_swapin);
591         (void)printf("%9u swap pager pages paged in\n", vmm.v_swappgsin);
592         (void)printf("%9u swap pager pageouts\n", vmm.v_swapout);
593         (void)printf("%9u swap pager pages paged out\n", vmm.v_swappgsout);
594         (void)printf("%9u vnode pager pageins\n", vmm.v_vnodein);
595         (void)printf("%9u vnode pager pages paged in\n", vmm.v_vnodepgsin);
596         (void)printf("%9u vnode pager pageouts\n", vmm.v_vnodeout);
597         (void)printf("%9u vnode pager pages paged out\n", vmm.v_vnodepgsout);
598         (void)printf("%9u page daemon wakeups\n", vmm.v_pdwakeups);
599         (void)printf("%9u pages examined by the page daemon\n", vmm.v_pdpages);
600         (void)printf("%9u pages reactivated\n", vmm.v_reactivated);
601         (void)printf("%9u copy-on-write faults\n", vmm.v_cow_faults);
602         (void)printf("%9u copy-on-write optimized faults\n", vmm.v_cow_optim);
603         (void)printf("%9u zero fill pages zeroed\n", vmm.v_zfod);
604         (void)printf("%9u zero fill pages prezeroed\n", vmm.v_ozfod);
605         (void)printf("%9u intransit blocking page faults\n", vmm.v_intrans);
606         (void)printf("%9u total VM faults taken\n", vmm.v_vm_faults);
607         (void)printf("%9u pages affected by kernel thread creation\n", vmm.v_kthreadpages);
608         (void)printf("%9u pages affected by  fork()\n", vmm.v_forkpages);
609         (void)printf("%9u pages affected by vfork()\n", vmm.v_vforkpages);
610         (void)printf("%9u pages affected by rfork()\n", vmm.v_rforkpages);
611         (void)printf("%9u pages freed\n", vmm.v_tfree);
612         (void)printf("%9u pages freed by daemon\n", vmm.v_dfree);
613         (void)printf("%9u pages freed by exiting processes\n", vmm.v_pfree);
614         (void)printf("%9u pages active\n", vms.v_active_count);
615         (void)printf("%9u pages inactive\n", vms.v_inactive_count);
616         (void)printf("%9u pages in VM cache\n", vms.v_cache_count);
617         (void)printf("%9u pages wired down\n", vms.v_wire_count);
618         (void)printf("%9u pages free\n", vms.v_free_count);
619         (void)printf("%9u bytes per page\n", vms.v_page_size);
620         kread(X_NCHSTATS, &nchstats, sizeof(nchstats));
621         nchtotal = nchstats.ncs_goodhits + nchstats.ncs_neghits +
622             nchstats.ncs_badhits + nchstats.ncs_falsehits +
623             nchstats.ncs_miss + nchstats.ncs_long;
624         (void)printf("%9ld total name lookups\n", nchtotal);
625         (void)printf(
626             "%9s cache hits (%ld%% pos + %ld%% neg) system %ld%% per-directory\n",
627             "", PCT(nchstats.ncs_goodhits, nchtotal),
628             PCT(nchstats.ncs_neghits, nchtotal),
629             PCT(nchstats.ncs_pass2, nchtotal));
630         (void)printf("%9s deletions %ld%%, falsehits %ld%%, toolong %ld%%\n", "",
631             PCT(nchstats.ncs_badhits, nchtotal),
632             PCT(nchstats.ncs_falsehits, nchtotal),
633             PCT(nchstats.ncs_long, nchtotal));
634 }
635
636 #ifdef notyet
637 void
638 doforkst()
639 {
640         struct forkstat fks;
641
642         kread(X_FORKSTAT, &fks, sizeof(struct forkstat));
643         (void)printf("%d forks, %d pages, average %.2f\n",
644             fks.cntfork, fks.sizfork, (double)fks.sizfork / fks.cntfork);
645         (void)printf("%d vforks, %d pages, average %.2f\n",
646             fks.cntvfork, fks.sizvfork, (double)fks.sizvfork / fks.cntvfork);
647 }
648 #endif
649
650 static void
651 devstats()
652 {
653         register int dn, state;
654         long double transfers_per_second;
655         long double busy_seconds;
656         long tmp;
657         
658         for (state = 0; state < CPUSTATES; ++state) {
659                 tmp = cur.cp_time[state];
660                 cur.cp_time[state] -= last.cp_time[state];
661                 last.cp_time[state] = tmp;
662         }
663
664         busy_seconds = compute_etime(cur.busy_time, last.busy_time);
665
666         for (dn = 0; dn < num_devices; dn++) {
667                 int di;
668
669                 if ((dev_select[dn].selected == 0)
670                  || (dev_select[dn].selected > maxshowdevs))
671                         continue;
672
673                 di = dev_select[dn].position;
674
675                 if (compute_stats(&cur.dinfo->devices[di],
676                                   &last.dinfo->devices[di], busy_seconds,
677                                   NULL, NULL, NULL,
678                                   NULL, &transfers_per_second, NULL,
679                                   NULL, NULL) != 0)
680                         errx(1, "%s", devstat_errbuf);
681
682                 printf("%3.0Lf ", transfers_per_second);
683         }
684 }
685
686 void
687 cpustats()
688 {
689         register int state;
690         double pct, total;
691
692         total = 0;
693         for (state = 0; state < CPUSTATES; ++state)
694                 total += cur.cp_time[state];
695         if (total)
696                 pct = 100 / total;
697         else
698                 pct = 0;
699         (void)printf("%2.0f ", (cur.cp_time[CP_USER] +
700                                 cur.cp_time[CP_NICE]) * pct);
701         (void)printf("%2.0f ", (cur.cp_time[CP_SYS] +
702                                 cur.cp_time[CP_INTR]) * pct);
703         (void)printf("%2.0f", cur.cp_time[CP_IDLE] * pct);
704 }
705
706 void
707 dointr()
708 {
709         register u_long *intrcnt, uptime;
710         register u_int64_t inttotal;
711         register int nintr, inamlen;
712         register char *intrname;
713
714         uptime = getuptime();
715         nintr = namelist[X_EINTRCNT].n_value - namelist[X_INTRCNT].n_value;
716         inamlen =
717             namelist[X_EINTRNAMES].n_value - namelist[X_INTRNAMES].n_value;
718         intrcnt = malloc((size_t)nintr);
719         intrname = malloc((size_t)inamlen);
720         if (intrcnt == NULL || intrname == NULL)
721                 errx(1, "malloc");
722         kread(X_INTRCNT, intrcnt, (size_t)nintr);
723         kread(X_INTRNAMES, intrname, (size_t)inamlen);
724         (void)printf("interrupt                   total       rate\n");
725         inttotal = 0;
726         nintr /= sizeof(long);
727         while (--nintr >= 0) {
728                 if (*intrcnt)
729                         (void)printf("%-12s %20lu %10lu\n", intrname,
730                             *intrcnt, *intrcnt / uptime);
731                 intrname += strlen(intrname) + 1;
732                 inttotal += *intrcnt++;
733         }
734         (void)printf("Total        %20llu %10llu\n", inttotal,
735                         inttotal / (u_int64_t) uptime);
736 }
737
738 #define MAX_KMSTATS     200
739
740 void
741 domem()
742 {
743         register struct kmembuckets *kp;
744         register struct malloc_type *ks;
745         register int i, j;
746         int len, size, first, nkms;
747         long totuse = 0, totfree = 0, totreq = 0;
748         const char *name;
749         struct malloc_type kmemstats[MAX_KMSTATS], *kmsp;
750         char buf[1024];
751         struct kmembuckets buckets[MINBUCKET + 16];
752
753         kread(X_KMEMBUCKETS, buckets, sizeof(buckets));
754         kread(X_KMEMSTATISTICS, &kmsp, sizeof(kmsp));
755         for (nkms = 0; nkms < MAX_KMSTATS && kmsp != NULL; nkms++) {
756                 if (sizeof(kmemstats[0]) != kvm_read(kd, (u_long)kmsp,
757                     &kmemstats[nkms], sizeof(kmemstats[0])))
758                         err(1, "kvm_read(%p)", (void *)kmsp);
759                 if (sizeof(buf) !=  kvm_read(kd, 
760                     (u_long)kmemstats[nkms].ks_shortdesc, buf, sizeof(buf)))
761                         err(1, "kvm_read(%p)", 
762                             (void *)kmemstats[nkms].ks_shortdesc);
763                 buf[sizeof(buf) - 1] = '\0';
764                 kmemstats[nkms].ks_shortdesc = strdup(buf);
765                 kmsp = kmemstats[nkms].ks_next;
766         }
767         if (kmsp != NULL)
768                 warnx("truncated to the first %d memory types", nkms);
769         (void)printf("Memory statistics by bucket size\n");
770         (void)printf(
771             "Size   In Use   Free   Requests  HighWater  Couldfree\n");
772         for (i = MINBUCKET, kp = &buckets[i]; i < MINBUCKET + 16; i++, kp++) {
773                 if (kp->kb_calls == 0)
774                         continue;
775                 size = 1 << i;
776                 if(size < 1024)
777                         (void)printf("%4d",size);
778                 else
779                         (void)printf("%3dK",size>>10);
780                 (void)printf(" %8ld %6ld %10lld %7ld %10ld\n",
781                         kp->kb_total - kp->kb_totalfree,
782                         kp->kb_totalfree, kp->kb_calls,
783                         kp->kb_highwat, kp->kb_couldfree);
784                 totfree += size * kp->kb_totalfree;
785         }
786
787         (void)printf("\nMemory usage type by bucket size\n");
788         (void)printf("Size  Type(s)\n");
789         kp = &buckets[MINBUCKET];
790         for (j =  1 << MINBUCKET; j < 1 << (MINBUCKET + 16); j <<= 1, kp++) {
791                 if (kp->kb_calls == 0)
792                         continue;
793                 first = 1;
794                 len = 8;
795                 for (i = 0, ks = &kmemstats[0]; i < nkms; i++, ks++) {
796                         if (ks->ks_calls == 0)
797                                 continue;
798                         if ((ks->ks_size & j) == 0)
799                                 continue;
800                         name = ks->ks_shortdesc;
801                         len += 2 + strlen(name);
802                         if (first && j < 1024)
803                                 printf("%4d  %s", j, name);
804                         else if (first)
805                                 printf("%3dK  %s", j>>10, name);
806                         else
807                                 printf(",");
808                         if (len >= 79) {
809                                 printf("\n\t ");
810                                 len = 10 + strlen(name);
811                         }
812                         if (!first)
813                                 printf(" %s", name);
814                         first = 0;
815                 }
816                 printf("\n");
817         }
818
819         (void)printf(
820             "\nMemory statistics by type                          Type  Kern\n");
821         (void)printf(
822 "        Type  InUse MemUse HighUse  Limit Requests Limit Limit Size(s)\n");
823         for (i = 0, ks = &kmemstats[0]; i < nkms; i++, ks++) {
824                 if (ks->ks_calls == 0)
825                         continue;
826                 (void)printf("%13s%6ld%6ldK%7ldK%6ldK%9lld%5u%6u",
827                     ks->ks_shortdesc,
828                     ks->ks_inuse, (ks->ks_memuse + 1023) / 1024,
829                     (ks->ks_maxused + 1023) / 1024,
830                     (ks->ks_limit + 1023) / 1024, ks->ks_calls,
831                     ks->ks_limblocks, ks->ks_mapblocks);
832                 first = 1;
833                 for (j =  1 << MINBUCKET; j < 1 << (MINBUCKET + 16); j <<= 1) {
834                         if ((ks->ks_size & j) == 0)
835                                 continue;
836                         if (first)
837                                 printf("  ");
838                         else
839                                 printf(",");
840                         if(j<1024)
841                                 printf("%d",j);
842                         else
843                                 printf("%dK",j>>10);
844                         first = 0;
845                 }
846                 printf("\n");
847                 totuse += ks->ks_memuse;
848                 totreq += ks->ks_calls;
849         }
850         (void)printf("\nMemory Totals:  In Use    Free    Requests\n");
851         (void)printf("              %7ldK %6ldK    %8ld\n",
852              (totuse + 1023) / 1024, (totfree + 1023) / 1024, totreq);
853 }
854
855 void
856 dozmem()
857 {
858         char *buf;
859         size_t bufsize;
860
861         buf = NULL;
862         bufsize = 1024;
863         for (;;) {
864                 if ((buf = realloc(buf, bufsize)) == NULL)
865                         err(1, "realloc()");
866                 if (sysctlbyname("vm.zone", buf, &bufsize, 0, NULL) == 0)
867                         break;
868                 if (errno != ENOMEM)
869                         err(1, "sysctl()");
870                 bufsize *= 2;
871         }
872         buf[bufsize] = '\0'; /* play it safe */
873         (void)printf("%s\n\n", buf);
874         free(buf);
875 }
876
877 /*
878  * kread reads something from the kernel, given its nlist index.
879  */
880 void
881 kread(nlx, addr, size)
882         int nlx;
883         void *addr;
884         size_t size;
885 {
886         char *sym;
887
888         if (namelist[nlx].n_type == 0 || namelist[nlx].n_value == 0) {
889                 sym = namelist[nlx].n_name;
890                 if (*sym == '_')
891                         ++sym;
892                 errx(1, "symbol %s not defined", sym);
893         }
894         if (kvm_read(kd, namelist[nlx].n_value, addr, size) != size) {
895                 sym = namelist[nlx].n_name;
896                 if (*sym == '_')
897                         ++sym;
898                 errx(1, "%s: %s", sym, kvm_geterr(kd));
899         }
900 }
901
902 void
903 usage()
904 {
905         (void)fprintf(stderr, "%s%s",
906                 "usage: vmstat [-imsz] [-c count] [-M core] [-N system] [-w wait]\n",
907                 "              [-n devs] [disks]\n");
908         exit(1);
909 }