Fix CPU stats percentages formatting
[dragonfly.git] / usr.sbin / iostat / iostat.c
1 /*
2  * Copyright (c) 1997, 1998  Kenneth D. Merry.
3  * 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. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD: src/usr.sbin/iostat/iostat.c,v 1.17.2.2 2001/07/19 04:15:42 kris Exp $
29  * $DragonFly: src/usr.sbin/iostat/iostat.c,v 1.3 2003/07/29 23:15:36 drhodus Exp $
30  */
31 /*
32  * Parts of this program are derived from the original FreeBSD iostat
33  * program:
34  */
35 /*-
36  * Copyright (c) 1986, 1991, 1993
37  *      The Regents of the University of California.  All rights reserved.
38  *
39  * Redistribution and use in source and binary forms, with or without
40  * modification, are permitted provided that the following conditions
41  * are met:
42  * 1. Redistributions of source code must retain the above copyright
43  *    notice, this list of conditions and the following disclaimer.
44  * 2. Redistributions in binary form must reproduce the above copyright
45  *    notice, this list of conditions and the following disclaimer in the
46  *    documentation and/or other materials provided with the distribution.
47  * 3. All advertising materials mentioning features or use of this software
48  *    must display the following acknowledgement:
49  *      This product includes software developed by the University of
50  *      California, Berkeley and its contributors.
51  * 4. Neither the name of the University nor the names of its contributors
52  *    may be used to endorse or promote products derived from this software
53  *    without specific prior written permission.
54  *
55  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65  * SUCH DAMAGE.
66  */
67 /*
68  * Ideas for the new iostat statistics output modes taken from the NetBSD
69  * version of iostat:
70  */
71 /*
72  * Copyright (c) 1996 John M. Vinopal
73  * All rights reserved.
74  *
75  * Redistribution and use in source and binary forms, with or without
76  * modification, are permitted provided that the following conditions
77  * are met:
78  * 1. Redistributions of source code must retain the above copyright
79  *    notice, this list of conditions and the following disclaimer.
80  * 2. Redistributions in binary form must reproduce the above copyright
81  *    notice, this list of conditions and the following disclaimer in the
82  *    documentation and/or other materials provided with the distribution.
83  * 3. All advertising materials mentioning features or use of this software
84  *    must display the following acknowledgement:
85  *      This product includes software developed for the NetBSD Project
86  *      by John M. Vinopal.
87  * 4. The name of the author may not be used to endorse or promote products
88  *    derived from this software without specific prior written permission.
89  *
90  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
91  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
92  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
93  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
94  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
95  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
96  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
97  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
98  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
99  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
100  * SUCH DAMAGE.
101  */
102
103
104 #include <sys/param.h>
105 #include <sys/errno.h>
106 #include <sys/dkstat.h>
107
108 #include <err.h>
109 #include <ctype.h>
110 #include <fcntl.h>
111 #include <kvm.h>
112 #include <stdio.h>
113 #include <stdlib.h>
114 #include <string.h>
115 #include <unistd.h>
116 #include <limits.h>
117 #include <devstat.h>
118
119 struct nlist namelist[] = {
120 #define X_TK_NIN        0
121         { "_tk_nin" },
122 #define X_TK_NOUT       1
123         { "_tk_nout" },
124 #define X_CP_TIME       2
125         { "_cp_time" },
126 #define X_HZ            3
127         { "_hz" },
128 #define X_STATHZ        4
129         { "_stathz" },
130 #define X_END           4
131         { NULL },
132 };
133
134 struct statinfo cur, last;
135 int num_devices;
136 struct device_selection *dev_select;
137 int maxshowdevs;
138 int dflag = 0, Iflag = 0, Cflag = 0, Tflag = 0, oflag = 0, Kflag = 0;
139
140 #define nlread(x, v) \
141         kvm_read(kd, namelist[x].n_value, &(v), sizeof(v))
142
143 /* local function declarations */
144 static void usage(void);
145 static void phdr(int signo);
146 static void devstats(int perf_select);
147 static void cpustats(void);
148
149 static void
150 usage(void)
151 {
152         /*
153          * We also support the following 'traditional' syntax:
154          * iostat [drives] [wait [count]]
155          * This isn't mentioned in the man page, or the usage statement,
156          * but it is supported.
157          */
158         fprintf(stderr, "usage: iostat [-CdhIKoT?] [-c count] [-M core]"
159                 " [-n devs] [-N system]\n"
160                 "\t      [-t type,if,pass] [-w wait] [drives]\n");
161 }
162
163 int
164 main(int argc, char **argv)
165 {
166         int c;
167         register int i;
168         int tflag = 0, hflag = 0, cflag = 0, wflag = 0, nflag = 0;
169         int count = 0, waittime = 0;
170         char *memf = NULL, *nlistf = NULL;
171         struct devstat_match *matches;
172         int num_matches = 0;
173         char errbuf[_POSIX2_LINE_MAX];
174         kvm_t    *kd;
175         int hz, stathz;
176         int headercount;
177         long generation;
178         int num_devices_specified;
179         int num_selected, num_selections;
180         long select_generation;
181         char **specified_devices;
182         devstat_select_mode select_mode;
183
184         matches = NULL;
185         maxshowdevs = 3;
186
187         while ((c = getopt(argc, argv, "c:CdhIKM:n:N:ot:Tw:?")) != -1) {
188                 switch(c) {
189                         case 'c':
190                                 cflag++;
191                                 count = atoi(optarg);
192                                 if (count < 1)
193                                         errx(1, "count %d is < 1", count);
194                                 break;
195                         case 'C':
196                                 Cflag++;
197                                 break;
198                         case 'd':
199                                 dflag++;
200                                 break;
201                         case 'h':
202                                 hflag++;
203                                 break;
204                         case 'I':
205                                 Iflag++;
206                                 break;
207                         case 'K':
208                                 Kflag++;
209                                 break;
210                         case 'M':
211                                 memf = optarg;
212                                 break;
213                         case 'n':
214                                 nflag++;
215                                 maxshowdevs = atoi(optarg);
216                                 if (maxshowdevs < 0)
217                                         errx(1, "number of devices %d is < 0",
218                                              maxshowdevs);
219                                 break;
220                         case 'N':
221                                 nlistf = optarg;
222                                 break;
223                         case 'o':
224                                 oflag++;
225                                 break;
226                         case 't':
227                                 tflag++;
228                                 if (buildmatch(optarg, &matches, 
229                                                &num_matches) != 0)
230                                         errx(1, "%s", devstat_errbuf);
231                                 break;
232                         case 'T':
233                                 Tflag++;
234                                 break;
235                         case 'w':
236                                 wflag++;
237                                 waittime = atoi(optarg);
238                                 if (waittime < 1)
239                                         errx(1, "wait time is < 1");
240                                 break;
241                         default:
242                                 usage();
243                                 exit(1);
244                                 break;
245                 }
246         }
247
248         argc -= optind;
249         argv += optind;
250
251         /*
252          * Discard setgid privileges if not the running kernel so that bad
253          * guys can't print interesting stuff from kernel memory.
254          */
255         if (nlistf != NULL || memf != NULL)
256                 setgid(getgid());
257
258         /*
259          * Make sure that the userland devstat version matches the kernel
260          * devstat version.  If not, exit and print a message informing 
261          * the user of his mistake.
262          */
263         if (checkversion() < 0)
264                 errx(1, "%s", devstat_errbuf);
265
266         /*
267          * Figure out how many devices we should display.
268          */
269         if (nflag == 0) {
270                 if (oflag > 0) {
271                         if ((dflag > 0) && (Cflag == 0) && (Tflag == 0))
272                                 maxshowdevs = 5;
273                         else if ((dflag > 0) && (Tflag > 0) && (Cflag == 0))
274                                 maxshowdevs = 5;
275                         else
276                                 maxshowdevs = 4;
277                 } else {
278                         if ((dflag > 0) && (Cflag == 0))
279                                 maxshowdevs = 4;                
280                         else
281                                 maxshowdevs = 3;
282                 }
283         }
284
285         /* find out how many devices we have */
286         if ((num_devices = getnumdevs()) < 0)
287                 err(1, "can't get number of devices");
288
289         if ((cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo))) ==
290              NULL)
291                 err(1, "devinfo malloc failed");
292         if ((last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo))) ==
293              NULL)
294                 err(1, "devinfo malloc failed");
295         bzero(cur.dinfo, sizeof(struct devinfo));
296         bzero(last.dinfo, sizeof(struct devinfo));
297
298         /*
299          * Grab all the devices.  We don't look to see if the list has
300          * changed here, since it almost certainly has.  We only look for
301          * errors.
302          */
303         if (getdevs(&cur) == -1)
304                 errx(1, "%s", devstat_errbuf);
305
306         num_devices = cur.dinfo->numdevs;
307         generation = cur.dinfo->generation;
308
309         /*
310          * If the user specified any devices on the command line, see if
311          * they are in the list of devices we have now.
312          */
313         if ((specified_devices = (char **)malloc(sizeof(char *))) == NULL)
314                 err(1, "specified_devices malloc failed");
315         for (num_devices_specified = 0; *argv; ++argv) {
316                 if (isdigit(**argv))
317                         break;
318                 num_devices_specified++;
319                 specified_devices = (char **)realloc(specified_devices,
320                                                      sizeof(char *) *
321                                                      num_devices_specified);
322                 specified_devices[num_devices_specified - 1] = *argv;
323
324         }
325         if (nflag == 0 && maxshowdevs < num_devices_specified)
326                 maxshowdevs = num_devices_specified;
327
328         dev_select = NULL;
329
330         if ((num_devices_specified == 0) && (num_matches == 0))
331                 select_mode = DS_SELECT_ADD;
332         else
333                 select_mode = DS_SELECT_ONLY;
334
335         /*
336          * At this point, selectdevs will almost surely indicate that the
337          * device list has changed, so we don't look for return values of 0
338          * or 1.  If we get back -1, though, there is an error.
339          */
340         if (selectdevs(&dev_select, &num_selected,
341                        &num_selections, &select_generation,
342                        generation, cur.dinfo->devices, num_devices,
343                        matches, num_matches,
344                        specified_devices, num_devices_specified,
345                        select_mode, maxshowdevs, hflag) == -1)
346                 errx(1, "%s", devstat_errbuf);
347
348         /*
349          * Look for the traditional wait time and count arguments.
350          */
351         if (*argv) {
352                 waittime = atoi(*argv);
353
354                 /* Let the user know he goofed, but keep going anyway */
355                 if (wflag != 0) 
356                         warnx("discarding previous wait interval, using"
357                               " %d instead", waittime);
358                 wflag++;
359
360                 if (*++argv) {
361                         count = atoi(*argv);
362                         if (cflag != 0)
363                                 warnx("discarding previous count, using %d"
364                                       " instead", count);
365                         cflag++;
366                 } else
367                         count = -1;
368         }
369
370         /*
371          * If the user specified a count, but not an interval, we default
372          * to an interval of 1 second.
373          */
374         if ((wflag == 0) && (cflag > 0))
375                 waittime = 1;
376
377         /*
378          * If the user specified a wait time, but not a count, we want to
379          * go on ad infinitum.  This can be redundant if the user uses the
380          * traditional method of specifying the wait, since in that case we
381          * already set count = -1 above.  Oh well.
382          */
383         if ((wflag > 0) && (cflag == 0))
384                 count = -1;
385
386         kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
387
388         if (kd == 0)
389                 errx(1, "kvm_openfiles: %s", errbuf);
390
391         if (kvm_nlist(kd, namelist) == -1)
392                 errx(1, "kvm_nlist: %s", kvm_geterr(kd));
393
394         (void)nlread(X_HZ, hz);
395         (void)nlread(X_STATHZ, stathz);
396         if (stathz)
397                 hz = stathz;
398
399         /*
400          * If the user stops the program (control-Z) and then resumes it,
401          * print out the header again.
402          */
403         (void)signal(SIGCONT, phdr);
404
405         for (headercount = 1;;) {
406                 struct devinfo *tmp_dinfo;
407                 long tmp;
408                 double etime;
409
410                 if (!--headercount) {
411                         phdr(0);
412                         headercount = 20;
413                 }
414                 (void)kvm_read(kd, namelist[X_TK_NIN].n_value,
415                     &cur.tk_nin, sizeof(cur.tk_nin));
416                 (void)kvm_read(kd, namelist[X_TK_NOUT].n_value,
417                     &cur.tk_nout, sizeof(cur.tk_nout));
418                 (void)kvm_read(kd, namelist[X_CP_TIME].n_value,
419                     cur.cp_time, sizeof(cur.cp_time));
420
421                 tmp_dinfo = last.dinfo;
422                 last.dinfo = cur.dinfo;
423                 cur.dinfo = tmp_dinfo;
424
425                 last.busy_time = cur.busy_time;
426
427                 /*
428                  * Here what we want to do is refresh our device stats.
429                  * getdevs() returns 1 when the device list has changed.
430                  * If the device list has changed, we want to go through
431                  * the selection process again, in case a device that we
432                  * were previously displaying has gone away.
433                  */
434                 switch (getdevs(&cur)) {
435                 case -1:
436                         errx(1, "%s", devstat_errbuf);
437                         break;
438                 case 1: {
439                         int retval;
440
441                         num_devices = cur.dinfo->numdevs;
442                         generation = cur.dinfo->generation;
443                         retval = selectdevs(&dev_select, &num_selected,
444                                             &num_selections, &select_generation,
445                                             generation, cur.dinfo->devices,
446                                             num_devices, matches, num_matches,
447                                             specified_devices,
448                                             num_devices_specified,
449                                             select_mode, maxshowdevs, hflag);
450                         switch(retval) {
451                         case -1:
452                                 errx(1, "%s", devstat_errbuf);
453                                 break;
454                         case 1:
455                                 phdr(0);
456                                 headercount = 20;
457                                 break;
458                         default:
459                                 break;
460                         }
461                         break;
462                 }
463                 default:
464                         break;
465                 }
466
467                 /*
468                  * We only want to re-select devices if we're in 'top'
469                  * mode.  This is the only mode where the devices selected
470                  * could actually change.
471                  */
472                 if (hflag > 0) {
473                         int retval;
474                         retval = selectdevs(&dev_select, &num_selected,
475                                             &num_selections, &select_generation,
476                                             generation, cur.dinfo->devices,
477                                             num_devices, matches, num_matches,
478                                             specified_devices,
479                                             num_devices_specified,
480                                             select_mode, maxshowdevs, hflag);
481                         switch(retval) {
482                         case -1:
483                                 errx(1,"%s", devstat_errbuf);
484                                 break;
485                         case 1:
486                                 phdr(0);
487                                 headercount = 20;
488                                 break;
489                         default:
490                                 break;
491                         }
492                 }
493
494                 tmp = cur.tk_nin;
495                 cur.tk_nin -= last.tk_nin;
496                 last.tk_nin = tmp;
497                 tmp = cur.tk_nout;
498                 cur.tk_nout -= last.tk_nout;
499                 last.tk_nout = tmp;
500
501                 etime = 0.0;
502
503 #define X(fld)  tmp = cur.fld[i]; cur.fld[i] -= last.fld[i]; last.fld[i] = tmp
504
505                 for (i = 0; i < CPUSTATES; i++) {
506                         X(cp_time);
507                         etime += cur.cp_time[i];
508                 }
509                 if (etime == 0.0)
510                         etime = 1.0;
511                 etime /= (float)hz;
512                 if ((dflag == 0) || (Tflag > 0))
513                         printf("%4.0f%5.0f", cur.tk_nin / etime, 
514                                 cur.tk_nout/etime);
515                 devstats(hflag);
516                 if ((dflag == 0) || (Cflag > 0))
517                         cpustats();
518                 printf("\n");
519                 fflush(stdout);
520
521                 if (count >= 0 && --count <= 0)
522                         break;
523
524                 sleep(waittime);
525         }
526
527         exit(0);
528 }
529
530 static void
531 phdr(int signo)
532 {
533         register int i;
534         int printed;
535
536         if ((dflag == 0) || (Tflag > 0))
537                 (void)printf("      tty");
538         for (i = 0, printed=0;(i < num_devices) && (printed < maxshowdevs);i++){
539                 int di;
540                 if ((dev_select[i].selected != 0)
541                  && (dev_select[i].selected <= maxshowdevs)) {
542                         di = dev_select[i].position;
543                         if (oflag > 0)
544                                 (void)printf("%12.6s%d ", 
545                                             cur.dinfo->devices[di].device_name,
546                                             cur.dinfo->devices[di].unit_number);
547                         else
548                                 printf("%15.6s%d ",
549                                             cur.dinfo->devices[di].device_name,
550                                             cur.dinfo->devices[di].unit_number);
551                         printed++;
552                 }
553         }
554         if ((dflag == 0) || (Cflag > 0))
555                 (void)printf("            cpu\n");
556         else
557                 (void)printf("\n");
558
559         if ((dflag == 0) || (Tflag > 0))
560                 (void)printf(" tin tout");
561
562         for (i=0, printed = 0;(i < num_devices) && (printed < maxshowdevs);i++){
563                 if ((dev_select[i].selected != 0)
564                  && (dev_select[i].selected <= maxshowdevs)) {
565                         if (oflag > 0) {
566                                 if (Iflag == 0)
567                                         (void)printf(" sps tps msps ");
568                                 else
569                                         (void)printf(" blk xfr msps ");
570                         } else {
571                                 if (Iflag == 0)
572                                         printf("  KB/t tps  MB/s ");
573                                 else
574                                         printf("  KB/t xfrs   MB ");
575                         }
576                         printed++;
577                 }
578         }
579         if ((dflag == 0) || (Cflag > 0))
580                 (void)printf(" us ni sy in id\n");
581         else
582                 printf("\n");
583
584 }
585
586 static void
587 devstats(int perf_select)
588 {
589         register int dn;
590         long double transfers_per_second;
591         long double kb_per_transfer, mb_per_second;
592         u_int64_t total_bytes, total_transfers, total_blocks;
593         long double busy_seconds;
594         long double total_mb;
595         long double blocks_per_second, ms_per_transaction;
596         
597         /*
598          * Calculate elapsed time up front, since it's the same for all
599          * devices.
600          */
601         busy_seconds = compute_etime(cur.busy_time, last.busy_time);
602
603         for (dn = 0; dn < num_devices; dn++) {
604                 int di;
605
606                 if (((perf_select == 0) && (dev_select[dn].selected == 0))
607                  || (dev_select[dn].selected > maxshowdevs))
608                         continue;
609
610                 di = dev_select[dn].position;
611
612                 if (compute_stats(&cur.dinfo->devices[di],
613                                   &last.dinfo->devices[di], busy_seconds,
614                                   &total_bytes, &total_transfers,
615                                   &total_blocks, &kb_per_transfer,
616                                   &transfers_per_second, &mb_per_second, 
617                                   &blocks_per_second, &ms_per_transaction)!= 0)
618                         errx(1, "%s", devstat_errbuf);
619
620                 if (perf_select != 0) {
621                         dev_select[dn].bytes = total_bytes;
622                         if ((dev_select[dn].selected == 0)
623                          || (dev_select[dn].selected > maxshowdevs))
624                                 continue;
625                 }
626
627                 if (Kflag) {
628                         int block_size = cur.dinfo->devices[di].block_size;
629                         total_blocks = total_blocks * (block_size ?
630                                                        block_size : 512) / 1024;
631                 }
632
633                 if (oflag > 0) {
634                         int msdig = (ms_per_transaction < 100.0) ? 1 : 0;
635
636                         if (Iflag == 0)
637                                 printf("%4.0Lf%4.0Lf%5.*Lf ",
638                                        blocks_per_second,
639                                        transfers_per_second,
640                                        msdig,
641                                        ms_per_transaction);
642                         else 
643                                 printf("%4.1qu%4.1qu%5.*Lf ",
644                                        total_blocks,
645                                        total_transfers,
646                                        msdig,
647                                        ms_per_transaction);
648                 } else {
649                         if (Iflag == 0)
650                                 printf(" %5.2Lf %3.0Lf %5.2Lf ", 
651                                        kb_per_transfer,
652                                        transfers_per_second,
653                                        mb_per_second);
654                         else {
655                                 total_mb = total_bytes;
656                                 total_mb /= 1024 * 1024;
657
658                                 printf(" %5.2Lf %3.1qu %5.2Lf ", 
659                                        kb_per_transfer,
660                                        total_transfers,
661                                        total_mb);
662                         }
663                 }
664         }
665 }
666
667 static void
668 cpustats(void)
669 {
670         register int state;
671         double time;
672
673         time = 0.0;
674
675         for (state = 0; state < CPUSTATES; ++state)
676                 time += cur.cp_time[state];
677         for (state = 0; state < CPUSTATES; ++state)
678                 printf(" %2.0f",
679                        100. * cur.cp_time[state] / (time ? time : 1));
680 }