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