2 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
4 * U.S. Government Rights - Commercial software. Government users are subject
5 * to the Sun Microsystems, Inc. standard license agreement and applicable
6 * provisions of the FAR and its supplements.
9 * This distribution may include materials developed by third parties. Sun,
10 * Sun Microsystems, the Sun logo and Solaris are trademarks or registered
11 * trademarks of Sun Microsystems, Inc. in the U.S. and other countries.
31 #include <sys/types.h>
33 #include <sys/sysinfo.h>
36 kstat_ctl_t
*kc
; /* libkstat cookie */
37 static int ncpus
= -1;
38 typedef struct cpuinfo
{
44 static cpuinfo_t
*cpulist
= NULL
;
46 #define DELTA(i, x) (cpulist[i].cs_new.x - cpulist[i].cs_old.x)
48 static kstat_t
**cpu_stat_list
= NULL
;
49 static cpu_stat_t
*cpu_stat_data
= NULL
;
51 #define DISK_OLD 0x0001
52 #define DISK_NEW 0x0002
53 #define DISK_EXTENDED 0x0004
54 #define DISK_ERRORS 0x0008
55 #define DISK_EXTENDED_ERRORS 0x0010
56 #define DISK_NORMAL (DISK_OLD | DISK_NEW)
58 #define DISK_IO_MASK (DISK_OLD | DISK_NEW | DISK_EXTENDED)
59 #define DISK_ERROR_MASK (DISK_ERRORS | DISK_EXTENDED_ERRORS)
60 #define PRINT_VERTICAL (DISK_ERROR_MASK | DISK_EXTENDED)
65 * Name and print priority of each supported ks_class.
67 #define IO_CLASS_DISK 0
68 #define IO_CLASS_PARTITION 0
69 #define IO_CLASS_TAPE 1
70 #define IO_CLASS_NFS 2
81 int printf(const char *bar
, ...) { return 1; }
83 int putchar(int bar
){return 1;}
85 static int reentrant
= 0;
88 * I've had some strange behavior with "refreshMode = async".
90 #define REENTRANT_BEGIN() { \
95 #define REENTRANT_END() { \
99 static struct io_class io_class
[] = {
100 { "disk", IO_CLASS_DISK
},
101 { "partition", IO_CLASS_PARTITION
},
106 struct diskinfo
*next
;
108 kstat_io_t new_kios
, old_kios
;
112 kstat_t
*disk_errs
; /* pointer to the disk's error kstats */
115 #define DISK_GIGABYTE 1000000000.0
117 static void *dl
= 0; /* for device name lookup */
118 extern void *build_disk_list(void *);
119 extern char *lookup_ks_name(char *, void *);
121 #define NULLDISK (struct diskinfo *)0
122 static struct diskinfo zerodisk
;
123 static struct diskinfo
*firstdisk
= NULLDISK
;
124 static struct diskinfo
*lastdisk
= NULLDISK
;
125 static struct diskinfo
*snip
= NULLDISK
;
126 static int refreshDiskdetail
=0;
127 static int refreshDisksrv
=0;
129 static cpu_stat_t old_cpu_stat
, new_cpu_stat
;
131 #define DISK_DELTA(x) (disk->new_kios.x - disk->old_kios.x)
133 #define CPU_DELTA(x) (new_cpu_stat.x - old_cpu_stat.x)
136 #define PRINT_TTY_DATA(sys, time) \
137 (void) printf(" %3.0f %4.0f",\
138 (float)CPU_DELTA(sys.rawch) / time, \
139 (float)CPU_DELTA(sys.outch) / time);
141 #define PRINT_CPU_DATA(sys, pcnt) \
142 (void) printf(" %2.0f %2.0f %2.0f %2.0f", \
143 CPU_DELTA(sys.cpu[CPU_USER]) * pcnt, \
144 CPU_DELTA(sys.cpu[CPU_KERNEL]) * pcnt, \
145 CPU_DELTA(sys.cpu[CPU_WAIT]) * pcnt, \
146 CPU_DELTA(sys.cpu[CPU_IDLE]) * pcnt);
148 #define PRINT_CPU_HDR1 (void) printf("%12s", "cpu");
149 #define PRINT_CPU_HDR2 (void) printf(" us sy wt id");
150 #define PRINT_TTY_HDR1 (void) printf("%9s", "tty");
151 #define PRINT_TTY_HDR2 (void) printf(" tin tout");
152 #define PRINT_ERR_HDR (void) printf(" ---- errors --- ");
154 static char *cmdname
= "iostat";
156 static int do_disk
= 0;
157 static int do_cpu
= 0;
158 static int do_interval
= 0;
159 static int do_partitions
= 0; /* collect per-partition stats */
160 static int do_partitions_only
= 0; /* collect per-partition stats only */
161 /* no per-device stats for disks */
162 static int do_conversions
= 0; /* display disks as cXtYdZ */
163 static int do_megabytes
= 0; /* display data in MB/sec */
164 #define DEFAULT_LIMIT 4
165 static int limit
= 0; /* limit for drive display */
166 static int ndrives
= 0;
168 struct disk_selection
{
169 struct disk_selection
*next
;
170 char ks_name
[KSTAT_STRLEN
];
173 static struct disk_selection
*disk_selections
= (struct disk_selection
*)NULL
;
175 static void show_disk(struct diskinfo
*disk
, double *rps
, double *wps
, double *tps
, double *krps
, double *kwps
, double *kps
, double *avw
, double *avr
, double *w_pct
, double *r_pct
, double *wserv
, double *rserv
, double *serv
);
176 static void cpu_stat_init(void);
178 static int cpu_stat_load(int);
180 static void fail(int, char *, ...);
181 static void safe_zalloc(void **, int, int);
182 static void init_disks(void);
183 static void select_disks(void);
184 static int diskinfo_load(void);
185 static void init_disk_errors(void);
186 static void find_disk(kstat_t
*);
188 /* static struct diskinfo *disk; */
192 initialize_everything()
194 if ((kc
= kstat_open()) == NULL
)
196 do_disk
|= DISK_EXTENDED
/* | DISK_OLD */;
201 * Choose drives to be displayed. Priority
202 * goes to (in order) drives supplied as arguments,
203 * then any other active drives that fit.
205 struct disk_selection
**dsp
= &disk_selections
;
206 *dsp
= (struct disk_selection
*)NULL
;
211 for (disk
= firstdisk
; disk
; disk
->next
) {
215 /* disk = firstdisk; */
219 int update_kstat_chain(int cpu_save_old_flag
)
223 ret
=kstat_chain_update(kc
);
224 if (ret
== 0) { /* no change */
225 cpu_stat_load(cpu_save_old_flag
);
228 } else if (ret
> 0) { /* changed */
231 if (cpu_stat_load(cpu_save_old_flag
)) {
234 if (diskinfo_load()) {
243 /* Get Adaptive mutex summary and number of cpus for the system */
245 krgetsmtx(ulong
*smtx
, int *num_cpus
)
249 double etime
, percent
;
253 if ((first_time
) || (!kc
))
254 initialize_everything();
256 while (update_kstat_chain(1) == -1);
258 /* while (kstat_chain_update(kc) || cpu_stat_load(1)) {
259 (void) cpu_stat_init();
264 hz
= sysconf(_SC_CLK_TCK
);
265 for (c
= 0; c
< ncpus
; c
++) {
267 for (i
= 0; i
< CPU_STATES
; i
++)
268 ticks
+= DELTA(c
, cpu_sysinfo
.cpu
[i
]);
269 etime
= (double)ticks
/ hz
;
272 percent
= 100.0 / etime
/ hz
;
273 mutex
+= (int) (DELTA(c
, cpu_sysinfo
.mutex_adenters
) / etime
);
282 krgetcpudetail (cpu_stat_t
**cpus
, int *num_cpus
)
285 if ((first_time
) || (!kc
))
286 initialize_everything();
288 while (update_kstat_chain(0) == -1) {
289 /* DPRINTF("ERROR: kstat_chain_update \n");
293 *cpus
= cpu_stat_data
;
299 /* returns 0 if more disks remain, 1 if the last disk, < 0 on error */
301 krgetdiskdetail(char *name
, char *alias
, double *rps
, double *wps
, double *tps
, double *krps
, double *kwps
, double *kps
, double *avw
, double *avr
)
303 double w_pct
, r_pct
, wserv
, rserv
, serv
;
304 static struct diskinfo
*disk
= NULL
;
309 if (disk
==NULL
) disk
=firstdisk
;
310 if ((first_time
) || (!kc
)) {
311 initialize_everything();
316 if (refreshDiskdetail
) {
321 if (disk
== firstdisk
) {
322 while (update_kstat_chain(0) == -1) {
323 /* DPRINTF("ERROR: diskdetail - kstat_chain_update \n");
329 if (disk
->selected
) {
330 show_disk(disk
, rps
, wps
, tps
, krps
, kwps
, kps
, avw
, avr
, &w_pct
, &r_pct
, &wserv
, &rserv
, &serv
);
331 strcpy(name
, disk
->ks
->ks_name
);
332 if (disk
->device_name
!= NULL
)
333 strcpy(alias
, disk
->device_name
);
335 strcpy(alias
, disk
->ks
->ks_name
);
349 /* returns 0 if more disks remain, 1 if the last disk, < 0 on error */
351 krgetdisksrv(char *name
, char *alias
, double *w_pct
, double *r_pct
, double *wserv
, double *rserv
, double *serv
)
353 static struct diskinfo
*disk
= NULL
;
354 double rps
, wps
, tps
, krps
, kwps
, kps
, avw
, avr
;
358 if (disk
==NULL
) disk
=firstdisk
;
360 if (refreshDisksrv
) {
365 if (disk
->selected
) {
366 show_disk(disk
, &rps
, &wps
, &tps
, &krps
, &kwps
, &kps
, &avw
, &avr
, w_pct
, r_pct
, wserv
, rserv
, serv
);
367 strcpy(name
, disk
->ks
->ks_name
);
368 if (disk
->device_name
!= NULL
)
369 strcpy(alias
, disk
->device_name
);
371 strcpy(alias
, disk
->ks
->ks_name
);
374 /*ddlPrintf(DDL_ERROR, "krgetdisksrv - skipping %s\n", disk->device_name ? disk->device_name : disk->ks->ks_name);*/
387 show_disk(struct diskinfo
*disk
, double *Rps
, double *Wps
, double *Tps
, double *Krps
, double *Kwps
, double *Kps
, double *Avw
, double *Avr
, double *W_pct
, double *R_pct
, double *Wserv
, double *Rserv
, double *Serv
)
389 double rps
, wps
, tps
, krps
, kwps
, kps
, avw
, avr
, w_pct
, r_pct
;
390 double wserv
, rserv
, serv
;
391 double iosize
; /* kb/sec or MB/sec */
392 double etime
, hr_etime
;
394 hr_etime
= (double)DISK_DELTA(wlastupdate
);
396 hr_etime
= (double)NANOSEC
;
397 etime
= hr_etime
/ (double)NANOSEC
;
399 rps
= (double)DISK_DELTA(reads
) / etime
;
400 /* reads per second */
402 wps
= (double)DISK_DELTA(writes
) / etime
;
403 /* writes per second */
406 /* transactions per second */
409 * report throughput as either kb/sec or MB/sec
416 krps
= (double)DISK_DELTA(nread
) / etime
/ iosize
;
417 /* block reads per second */
419 kwps
= (double)DISK_DELTA(nwritten
) / etime
/ iosize
;
420 /* blocks written per second */
423 /* blocks transferred per second */
425 avw
= (double)DISK_DELTA(wlentime
) / hr_etime
;
426 /* average number of transactions waiting */
428 avr
= (double)DISK_DELTA(rlentime
) / hr_etime
;
429 /* average number of transactions running */
431 wserv
= tps
> 0 ? (avw
/ tps
) * 1000.0 : 0.0;
432 /* average wait service time in milliseconds */
434 rserv
= tps
> 0 ? (avr
/ tps
) * 1000.0 : 0.0;
435 /* average run service time in milliseconds */
437 serv
= tps
> 0 ? ((avw
+ avr
) / tps
) * 1000.0 : 0.0;
438 /* average service time in milliseconds */
440 w_pct
= (double)DISK_DELTA(wtime
) / hr_etime
* 100.0;
441 /* % of time there is a transaction waiting for service */
443 r_pct
= (double)DISK_DELTA(rtime
) / hr_etime
* 100.0;
444 /* % of time there is a transaction running */
471 * Get list of cpu_stat KIDs for subsequent cpu_stat_load operations.
483 for (ksp
= kc
->kc_chain
; ksp
; ksp
= ksp
->ks_next
) {
484 if (strncmp(ksp
->ks_name
, "cpu_stat", 8) == 0) {
486 if (kstat_read(kc
, ksp
, NULL
) == -1) {
487 /* ddlPrintf(DDL_ERROR, "cpu_stat_init - kstat_read() failed for cpu:%s\n", ksp->ks_name);*/
493 safe_zalloc((void **) &cpulist
, tmp_ncpus
* sizeof (cpuinfo_t
), 1);
494 safe_zalloc((void **)&cpu_stat_list
, tmp_ncpus
* sizeof (kstat_t
*), 1);
495 safe_zalloc((void *)&cpu_stat_data
, tmp_ncpus
* sizeof (cpu_stat_t
), 1);
499 for (ksp
= kc
->kc_chain
; ksp
; ksp
= ksp
->ks_next
) {
500 if (strncmp(ksp
->ks_name
, "cpu_stat", 8) == 0) {
501 if (kstat_read(kc
, ksp
, NULL
) != -1) {
502 cpu_stat_list
[ncpus
++] = ksp
;
504 /* ddlPrintf(DDL_ERROR, "cpu_stat_init - kstat_read() failed for cpu:%s\n", ksp->ks_name); */
508 if (strcmp(ksp
->ks_module
, "cpu_stat") != 0)
511 * insertion sort by CPU id
513 for (i
= nb_cpus
- 1; i
>= 0; i
--) {
514 if (cpulist
[i
].cs_kstat
->ks_instance
< ksp
->ks_instance
)
516 cpulist
[i
+ 1].cs_kstat
= cpulist
[i
].cs_kstat
;
518 cpulist
[i
+ 1].cs_kstat
= ksp
;
523 (void) memset(&new_cpu_stat
, 0, sizeof (cpu_stat_t
));
525 if ( ncpus
!= tmp_ncpus
) {
526 /* ddlPrintf(DDL_ERROR, "cpu_stat_init - kstat_read() for some cpu failed, Passed :ncpus=%d Total :tmp_ncpus=%d\n", ncpus, tmp_ncpus); */
532 cpu_stat_load(int save_old_flag
)
538 old_cpu_stat
= new_cpu_stat
;
539 (void) memset(&new_cpu_stat
, 0, sizeof (cpu_stat_t
));
541 /* Sum across all cpus */
542 for (i
= 0; i
< ncpus
; i
++) {
543 cpulist
[i
].cs_old
= cpulist
[i
].cs_new
;
544 if (kstat_read(kc
, cpulist
[i
].cs_kstat
,
545 (void *) &cpulist
[i
].cs_new
) == -1)
548 if (kstat_read(kc
, cpu_stat_list
[i
], (void *)&cpu_stat_data
[i
]) == -1) {
549 /* ddlPrintf(DDL_ERROR, "cpu_stat_load - kstat_read() failed for cpu:%s\n", cpu_stat_list[i]->ks_name); */
552 np
= (uint
*)&new_cpu_stat
.cpu_sysinfo
;
553 tp
= (uint
*)&(cpu_stat_data
[i
].cpu_sysinfo
);
554 for (j
= 0; j
< sizeof (cpu_sysinfo_t
); j
+= sizeof (uint_t
))
556 np
= (uint
*)&new_cpu_stat
.cpu_vminfo
;
557 tp
= (uint
*)&(cpu_stat_data
[i
].cpu_vminfo
);
558 for (j
= 0; j
< sizeof (cpu_vminfo_t
); j
+= sizeof (uint_t
))
565 fail(int do_perror
, char *message
, ...)
569 va_start(args
, message
);
570 (void) fprintf(stderr
, "%s: ", cmdname
);
571 (void) vfprintf(stderr
, message
, args
);
574 (void) fprintf(stderr
, ": %s", strerror(errno
));
575 (void) fprintf(stderr
, "\n");
580 safe_zalloc(void **ptr
, int size
, int free_first
)
582 if (free_first
&& *ptr
!= NULL
)
584 if ((*ptr
= (void *)malloc(size
)) == NULL
)
585 fail(1, "malloc failed");
586 (void) memset(*ptr
, 0, size
);
591 * Sort based on ks_class, ks_module, ks_instance, ks_name
594 kscmp(struct diskinfo
*ks1
, struct diskinfo
*ks2
)
598 cmp
= ks1
->class - ks2
->class;
602 cmp
= strcmp(ks1
->ks
->ks_module
, ks2
->ks
->ks_module
);
605 cmp
= ks1
->ks
->ks_instance
- ks2
->ks
->ks_instance
;
609 if (ks1
->device_name
&& ks2
->device_name
)
610 return (strcmp(ks1
->device_name
, ks2
->device_name
));
612 return (strcmp(ks1
->ks
->ks_name
, ks2
->ks
->ks_name
));
618 struct diskinfo
*disk
, *prevdisk
, *comp
;
620 static int first
= 1;
626 dl
= (void *)build_disk_list(dl
);
629 zerodisk
.next
= NULLDISK
;
636 * Patch the snip in the diskinfo list (see below)
639 lastdisk
->next
= snip
;
641 for (ksp
= kc
->kc_chain
; ksp
; ksp
= ksp
->ks_next
) {
644 if (ksp
->ks_type
!= KSTAT_TYPE_IO
)
647 for (i
= 0; io_class
[i
].class_name
!= NULL
; i
++) {
648 if (strcmp(ksp
->ks_class
, io_class
[i
].class_name
) == 0)
651 if (io_class
[i
].class_name
== NULL
)
654 if (do_partitions_only
&&
655 (strcmp(ksp
->ks_class
, "disk") == 0))
658 if (!do_partitions
&& !do_partitions_only
&&
659 (strcmp(ksp
->ks_class
, "partition") == 0))
661 if (!strcmp(ksp
->ks_name
, "fd0"))
668 safe_zalloc((void **)&disk
->next
,
669 sizeof (struct diskinfo
), 0);
671 disk
->next
= NULLDISK
;
674 (void) memset((void *)&disk
->new_kios
, 0, sizeof (kstat_io_t
));
675 disk
->new_kios
.wlastupdate
= disk
->ks
->ks_crtime
;
676 disk
->new_kios
.rlastupdate
= disk
->ks
->ks_crtime
;
678 if (do_conversions
&& dl
) {
679 if (!strcmp(ksp
->ks_class
, "nfs") == 0)
681 lookup_ks_name(ksp
->ks_name
, dl
);
683 disk
->device_name
= (char *)0;
686 disk
->disk_errs
= (kstat_t
*)NULL
;
687 disk
->class = io_class
[i
].class_priority
;
690 * Insertion sort on (ks_class, ks_module, ks_instance, ks_name)
693 while (kscmp(disk
, comp
->next
) > 0)
695 if (prevdisk
!= comp
) {
696 prevdisk
->next
= disk
->next
;
697 disk
->next
= comp
->next
;
703 * Put a snip in the linked list of diskinfos. The idea:
704 * If there was a state change such that now there are fewer
705 * disks, we snip the list and retain the tail, rather than
706 * freeing it. At the next state change, we clip the tail back on.
707 * This prevents a lot of malloc/free activity, and it's simpler.
711 disk
->next
= NULLDISK
;
713 firstdisk
= zerodisk
.next
;
716 if (do_disk
& DISK_ERROR_MASK
)
723 struct diskinfo
*disk
;
724 struct disk_selection
*ds
;
727 for (disk
= firstdisk
; disk
; disk
= disk
->next
) {
729 for (ds
= disk_selections
; ds
; ds
= ds
->next
) {
730 if (strcmp(disk
->ks
->ks_name
, ds
->ks_name
) == 0) {
737 for (disk
= firstdisk
; disk
; disk
= disk
->next
) {
740 if (limit
&& ndrives
>= limit
)
750 struct diskinfo
*disk
;
752 for (disk
= firstdisk
; disk
; disk
= disk
->next
) {
753 if (disk
->selected
) {
754 disk
->old_kios
= disk
->new_kios
;
755 if (kstat_read(kc
, disk
->ks
,
756 (void *)&disk
->new_kios
) == -1)
758 if (disk
->disk_errs
) {
759 if (kstat_read(kc
, disk
->disk_errs
, NULL
) == -1) {
772 for (ksp
= kc
->kc_chain
; ksp
; ksp
= ksp
->ks_next
) {
773 if ((ksp
->ks_type
== KSTAT_TYPE_NAMED
) &&
774 (strncmp(ksp
->ks_class
, "device_error", 12) == 0)) {
783 struct diskinfo
*disk
;
784 char kstat_name
[KSTAT_STRLEN
];
785 char *dname
= kstat_name
;
786 char *ename
= ksp
->ks_name
;
788 while (*ename
!= ',') {
795 for (disk
= firstdisk
; disk
; disk
= disk
->next
) {
796 if (disk
->selected
) {
797 if (strcmp(disk
->ks
->ks_name
, kstat_name
) == 0) {
798 disk
->disk_errs
= ksp
;