a175be4414254901fc0b98900728df0873ac0497
[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.4 2004/03/23 07:45:34 cpressey 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 #include <sys/param.h>
104 #include <sys/errno.h>
105 #include <sys/dkstat.h>
106
107 #include <err.h>
108 #include <ctype.h>
109 #include <fcntl.h>
110 #include <kvm.h>
111 #include <stdio.h>
112 #include <stdlib.h>
113 #include <string.h>
114 #include <unistd.h>
115 #include <limits.h>
116 #include <devstat.h>
117
118 struct nlist namelist[] = {
119 #define X_TK_NIN        0
120         { "_tk_nin" },
121 #define X_TK_NOUT       1
122         { "_tk_nout" },
123 #define X_CP_TIME       2
124         { "_cp_time" },
125 #define X_HZ            3
126         { "_hz" },
127 #define X_STATHZ        4
128         { "_stathz" },
129 #define X_END           4
130         { NULL },
131 };
132
133 struct statinfo cur, last;
134 int num_devices;
135 struct device_selection *dev_select;
136 int maxshowdevs;
137 int dflag = 0, Iflag = 0, Cflag = 0, Tflag = 0, oflag = 0, Kflag = 0;
138
139 #define nlread(x, v) \
140         kvm_read(kd, namelist[x].n_value, &(v), sizeof(v))
141
142 /* local function declarations */
143 static void usage(void);
144 static void phdr(int signo);
145 static void devstats(int perf_select);
146 static void cpustats(void);
147
148 static void
149 usage(void)
150 {
151         /*
152          * We also support the following 'traditional' syntax:
153          * iostat [drives] [wait [count]]
154          * This isn't mentioned in the man page, or the usage statement,
155          * but it is supported.
156          */
157         fprintf(stderr, "usage: iostat [-CdhIKoT?] [-c count] [-M core]"
158                 " [-n devs] [-N system]\n"
159                 "\t      [-t type,if,pass] [-w wait] [drives]\n");
160 }
161
162 int
163 main(int argc, char **argv)
164 {
165         int c;
166         int i;
167         int tflag = 0, hflag = 0, cflag = 0, wflag = 0, nflag = 0;
168         int count = 0, waittime = 0;
169         char *memf = NULL, *nlistf = NULL;
170         struct devstat_match *matches;
171         int num_matches = 0;
172         char errbuf[_POSIX2_LINE_MAX];
173         kvm_t *kd;
174         int hz, stathz;
175         int headercount;
176         long generation;
177         int num_devices_specified;
178         int num_selected, num_selections;
179         long select_generation;
180         char **specified_devices;
181         devstat_select_mode select_mode;
182
183         matches = NULL;
184         maxshowdevs = 3;
185
186         while ((c = getopt(argc, argv, "c:CdhIKM:n:N:ot:Tw:?")) != -1) {
187                 switch(c) {
188                         case 'c':
189                                 cflag++;
190                                 count = atoi(optarg);
191                                 if (count < 1)
192                                         errx(1, "count %d is < 1", count);
193                                 break;
194                         case 'C':
195                                 Cflag++;
196                                 break;
197                         case 'd':
198                                 dflag++;
199                                 break;
200                         case 'h':
201                                 hflag++;
202                                 break;
203                         case 'I':
204                                 Iflag++;
205                                 break;
206                         case 'K':
207                                 Kflag++;
208                                 break;
209                         case 'M':
210                                 memf = optarg;
211                                 break;
212                         case 'n':
213                                 nflag++;
214                                 maxshowdevs = atoi(optarg);
215                                 if (maxshowdevs < 0)
216                                         errx(1, "number of devices %d is < 0",
217                                              maxshowdevs);
218                                 break;
219                         case 'N':
220                                 nlistf = optarg;
221                                 break;
222                         case 'o':
223                                 oflag++;
224                                 break;
225                         case 't':
226                                 tflag++;
227                                 if (buildmatch(optarg, &matches, 
228                                                &num_matches) != 0)
229                                         errx(1, "%s", devstat_errbuf);
230                                 break;
231                         case 'T':
232                                 Tflag++;
233                                 break;
234                         case 'w':
235                                 wflag++;
236                                 waittime = atoi(optarg);
237                                 if (waittime < 1)
238                                         errx(1, "wait time is < 1");
239                                 break;
240                         default:
241                                 usage();
242                                 exit(1);
243                                 break;
244                 }
245         }
246
247         argc -= optind;
248         argv += optind;
249
250         /*
251          * Discard setgid privileges if not the running kernel so that bad
252          * guys can't print interesting stuff from kernel memory.
253          */
254         if (nlistf != NULL || memf != NULL)
255                 setgid(getgid());
256
257         /*
258          * Make sure that the userland devstat version matches the kernel
259          * devstat version.  If not, exit and print a message informing 
260          * the user of his mistake.
261          */
262         if (checkversion() < 0)
263                 errx(1, "%s", devstat_errbuf);
264
265         /*
266          * Figure out how many devices we should display.
267          */
268         if (nflag == 0) {
269                 if (oflag > 0) {
270                         if ((dflag > 0) && (Cflag == 0) && (Tflag == 0))
271                                 maxshowdevs = 5;
272                         else if ((dflag > 0) && (Tflag > 0) && (Cflag == 0))
273                                 maxshowdevs = 5;
274                         else
275                                 maxshowdevs = 4;
276                 } else {
277                         if ((dflag > 0) && (Cflag == 0))
278                                 maxshowdevs = 4;                
279                         else
280                                 maxshowdevs = 3;
281                 }
282         }
283
284         /* find out how many devices we have */
285         if ((num_devices = getnumdevs()) < 0)
286                 err(1, "can't get number of devices");
287
288         if ((cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo))) ==
289              NULL)
290                 err(1, "devinfo malloc failed");
291         if ((last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo))) ==
292              NULL)
293                 err(1, "devinfo malloc failed");
294         bzero(cur.dinfo, sizeof(struct devinfo));
295         bzero(last.dinfo, sizeof(struct devinfo));
296
297         /*
298          * Grab all the devices.  We don't look to see if the list has
299          * changed here, since it almost certainly has.  We only look for
300          * errors.
301          */
302         if (getdevs(&cur) == -1)
303                 errx(1, "%s", devstat_errbuf);
304
305         num_devices = cur.dinfo->numdevs;
306         generation = cur.dinfo->generation;
307
308         /*
309          * If the user specified any devices on the command line, see if
310          * they are in the list of devices we have now.
311          */
312         if ((specified_devices = (char **)malloc(sizeof(char *))) == NULL)
313                 err(1, "specified_devices malloc failed");
314         for (num_devices_specified = 0; *argv; ++argv) {
315                 if (isdigit(**argv))
316                         break;
317                 num_devices_specified++;
318                 specified_devices = (char **)realloc(specified_devices,
319                                                      sizeof(char *) *
320                                                      num_devices_specified);
321                 specified_devices[num_devices_specified - 1] = *argv;
322
323         }
324         if (nflag == 0 && maxshowdevs < num_devices_specified)
325                 maxshowdevs = num_devices_specified;
326
327         dev_select = NULL;
328
329         if ((num_devices_specified == 0) && (num_matches == 0))
330                 select_mode = DS_SELECT_ADD;
331         else
332                 select_mode = DS_SELECT_ONLY;
333
334         /*
335          * At this point, selectdevs will almost surely indicate that the
336          * device list has changed, so we don't look for return values of 0
337          * or 1.  If we get back -1, though, there is an error.
338          */
339         if (selectdevs(&dev_select, &num_selected,
340                        &num_selections, &select_generation,
341                        generation, cur.dinfo->devices, num_devices,
342                        matches, num_matches,
343                        specified_devices, num_devices_specified,
344                        select_mode, maxshowdevs, hflag) == -1)
345                 errx(1, "%s", devstat_errbuf);
346
347         /*
348          * Look for the traditional wait time and count arguments.
349          */
350         if (*argv) {
351                 waittime = atoi(*argv);
352
353                 /* Let the user know he goofed, but keep going anyway */
354                 if (wflag != 0) 
355                         warnx("discarding previous wait interval, using"
356                               " %d instead", waittime);
357                 wflag++;
358
359                 if (*++argv) {
360                         count = atoi(*argv);
361                         if (cflag != 0)
362                                 warnx("discarding previous count, using %d"
363                                       " instead", count);
364                         cflag++;
365                 } else
366                         count = -1;
367         }
368
369         /*
370          * If the user specified a count, but not an interval, we default
371          * to an interval of 1 second.
372          */
373         if ((wflag == 0) && (cflag > 0))
374                 waittime = 1;
375
376         /*
377          * If the user specified a wait time, but not a count, we want to
378          * go on ad infinitum.  This can be redundant if the user uses the
379          * traditional method of specifying the wait, since in that case we
380          * already set count = -1 above.  Oh well.
381          */
382         if ((wflag > 0) && (cflag == 0))
383                 count = -1;
384
385         kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
386
387         if (kd == 0)
388                 errx(1, "kvm_openfiles: %s", errbuf);
389
390         if (kvm_nlist(kd, namelist) == -1)
391                 errx(1, "kvm_nlist: %s", kvm_geterr(kd));
392
393         nlread(X_HZ, hz);
394         nlread(X_STATHZ, stathz);
395         if (stathz)
396                 hz = stathz;
397
398         /*
399          * If the user stops the program (control-Z) and then resumes it,
400          * print out the header again.
401          */
402         signal(SIGCONT, phdr);
403
404         for (headercount = 1;;) {
405                 struct devinfo *tmp_dinfo;
406                 long tmp;
407                 double etime;
408
409                 if (!--headercount) {
410                         phdr(0);
411                         headercount = 20;
412                 }
413                 kvm_read(kd, namelist[X_TK_NIN].n_value,
414                     &cur.tk_nin, sizeof(cur.tk_nin));
415                 kvm_read(kd, namelist[X_TK_NOUT].n_value,
416                     &cur.tk_nout, sizeof(cur.tk_nout));
417                 kvm_read(kd, namelist[X_CP_TIME].n_value,
418                     cur.cp_time, sizeof(cur.cp_time));
419
420                 tmp_dinfo = last.dinfo;
421                 last.dinfo = cur.dinfo;
422                 cur.dinfo = tmp_dinfo;
423
424                 last.busy_time = cur.busy_time;
425
426                 /*
427                  * Here what we want to do is refresh our device stats.
428                  * getdevs() returns 1 when the device list has changed.
429                  * If the device list has changed, we want to go through
430                  * the selection process again, in case a device that we
431                  * were previously displaying has gone away.
432                  */
433                 switch (getdevs(&cur)) {
434                 case -1:
435                         errx(1, "%s", devstat_errbuf);
436                         break;
437                 case 1: {
438                         int retval;
439
440                         num_devices = cur.dinfo->numdevs;
441                         generation = cur.dinfo->generation;
442                         retval = selectdevs(&dev_select, &num_selected,
443                                             &num_selections, &select_generation,
444                                             generation, cur.dinfo->devices,
445                                             num_devices, matches, num_matches,
446                                             specified_devices,
447                                             num_devices_specified,
448                                             select_mode, maxshowdevs, hflag);
449                         switch(retval) {
450                         case -1:
451                                 errx(1, "%s", devstat_errbuf);
452                                 break;
453                         case 1:
454                                 phdr(0);
455                                 headercount = 20;
456                                 break;
457                         default:
458                                 break;
459                         }
460                         break;
461                 }
462                 default:
463                         break;
464                 }
465
466                 /*
467                  * We only want to re-select devices if we're in 'top'
468                  * mode.  This is the only mode where the devices selected
469                  * could actually change.
470                  */
471                 if (hflag > 0) {
472                         int retval;
473                         retval = selectdevs(&dev_select, &num_selected,
474                                             &num_selections, &select_generation,
475                                             generation, cur.dinfo->devices,
476                                             num_devices, matches, num_matches,
477                                             specified_devices,
478                                             num_devices_specified,
479                                             select_mode, maxshowdevs, hflag);
480                         switch(retval) {
481                         case -1:
482                                 errx(1,"%s", devstat_errbuf);
483                                 break;
484                         case 1:
485                                 phdr(0);
486                                 headercount = 20;
487                                 break;
488                         default:
489                                 break;
490                         }
491                 }
492
493                 tmp = cur.tk_nin;
494                 cur.tk_nin -= last.tk_nin;
495                 last.tk_nin = tmp;
496                 tmp = cur.tk_nout;
497                 cur.tk_nout -= last.tk_nout;
498                 last.tk_nout = tmp;
499
500                 etime = 0.0;
501
502                 for (i = 0; i < CPUSTATES; i++) {
503                         tmp = cur.cp_time[i];
504                         cur.cp_time[i] -= last.cp_time[i];
505                         last.cp_time[i] = tmp;
506                         etime += cur.cp_time[i];
507                 }
508                 if (etime == 0.0)
509                         etime = 1.0;
510                 etime /= (float)hz;
511                 if ((dflag == 0) || (Tflag > 0))
512                         printf("%4.0f%5.0f", cur.tk_nin / etime, 
513                                 cur.tk_nout/etime);
514                 devstats(hflag);
515                 if ((dflag == 0) || (Cflag > 0))
516                         cpustats();
517                 printf("\n");
518                 fflush(stdout);
519
520                 if (count >= 0 && --count <= 0)
521                         break;
522
523                 sleep(waittime);
524         }
525
526         exit(0);
527 }
528
529 static void
530 phdr(int signo)
531 {
532         int i;
533         int printed;
534
535         if ((dflag == 0) || (Tflag > 0))
536                 printf("      tty");
537         for (i = 0, printed=0;(i < num_devices) && (printed < maxshowdevs);i++){
538                 int di;
539                 if ((dev_select[i].selected != 0)
540                  && (dev_select[i].selected <= maxshowdevs)) {
541                         di = dev_select[i].position;
542                         if (oflag > 0)
543                                 printf("%12.6s%d ", 
544                                             cur.dinfo->devices[di].device_name,
545                                             cur.dinfo->devices[di].unit_number);
546                         else
547                                 printf("%15.6s%d ",
548                                             cur.dinfo->devices[di].device_name,
549                                             cur.dinfo->devices[di].unit_number);
550                         printed++;
551                 }
552         }
553         if ((dflag == 0) || (Cflag > 0))
554                 printf("            cpu\n");
555         else
556                 printf("\n");
557
558         if ((dflag == 0) || (Tflag > 0))
559                 printf(" tin tout");
560
561         for (i=0, printed = 0;(i < num_devices) && (printed < maxshowdevs);i++){
562                 if ((dev_select[i].selected != 0)
563                  && (dev_select[i].selected <= maxshowdevs)) {
564                         if (oflag > 0) {
565                                 if (Iflag == 0)
566                                         printf(" sps tps msps ");
567                                 else
568                                         printf(" blk xfr msps ");
569                         } else {
570                                 if (Iflag == 0)
571                                         printf("  KB/t tps  MB/s ");
572                                 else
573                                         printf("  KB/t xfrs   MB ");
574                         }
575                         printed++;
576                 }
577         }
578         if ((dflag == 0) || (Cflag > 0))
579                 printf(" us ni sy in id\n");
580         else
581                 printf("\n");
582
583 }
584
585 static void
586 devstats(int perf_select)
587 {
588         int dn;
589         long double transfers_per_second;
590         long double kb_per_transfer, mb_per_second;
591         u_int64_t total_bytes, total_transfers, total_blocks;
592         long double busy_seconds;
593         long double total_mb;
594         long double blocks_per_second, ms_per_transaction;
595         
596         /*
597          * Calculate elapsed time up front, since it's the same for all
598          * devices.
599          */
600         busy_seconds = compute_etime(cur.busy_time, last.busy_time);
601
602         for (dn = 0; dn < num_devices; dn++) {
603                 int di;
604
605                 if (((perf_select == 0) && (dev_select[dn].selected == 0))
606                  || (dev_select[dn].selected > maxshowdevs))
607                         continue;
608
609                 di = dev_select[dn].position;
610
611                 if (compute_stats(&cur.dinfo->devices[di],
612                                   &last.dinfo->devices[di], busy_seconds,
613                                   &total_bytes, &total_transfers,
614                                   &total_blocks, &kb_per_transfer,
615                                   &transfers_per_second, &mb_per_second, 
616                                   &blocks_per_second, &ms_per_transaction)!= 0)
617                         errx(1, "%s", devstat_errbuf);
618
619                 if (perf_select != 0) {
620                         dev_select[dn].bytes = total_bytes;
621                         if ((dev_select[dn].selected == 0)
622                          || (dev_select[dn].selected > maxshowdevs))
623                                 continue;
624                 }
625
626                 if (Kflag) {
627                         int block_size = cur.dinfo->devices[di].block_size;
628                         total_blocks = total_blocks * (block_size ?
629                                                        block_size : 512) / 1024;
630                 }
631
632                 if (oflag > 0) {
633                         int msdig = (ms_per_transaction < 100.0) ? 1 : 0;
634
635                         if (Iflag == 0)
636                                 printf("%4.0Lf%4.0Lf%5.*Lf ",
637                                        blocks_per_second,
638                                        transfers_per_second,
639                                        msdig,
640                                        ms_per_transaction);
641                         else 
642                                 printf("%4.1qu%4.1qu%5.*Lf ",
643                                        total_blocks,
644                                        total_transfers,
645                                        msdig,
646                                        ms_per_transaction);
647                 } else {
648                         if (Iflag == 0)
649                                 printf(" %5.2Lf %3.0Lf %5.2Lf ", 
650                                        kb_per_transfer,
651                                        transfers_per_second,
652                                        mb_per_second);
653                         else {
654                                 total_mb = total_bytes;
655                                 total_mb /= 1024 * 1024;
656
657                                 printf(" %5.2Lf %3.1qu %5.2Lf ", 
658                                        kb_per_transfer,
659                                        total_transfers,
660                                        total_mb);
661                         }
662                 }
663         }
664 }
665
666 static void
667 cpustats(void)
668 {
669         int state;
670         double time;
671
672         time = 0.0;
673
674         for (state = 0; state < CPUSTATES; ++state)
675                 time += cur.cp_time[state];
676         for (state = 0; state < CPUSTATES; ++state)
677                 printf(" %2.0f",
678                        100. * cur.cp_time[state] / (time ? time : 1));
679 }