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