4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright (c) 2016 by Delphix. All rights reserved.
25 * Copyright 2017 Jason King
35 #include <sys/types.h>
39 #include <sys/vnode.h>
41 #include <sys/statvfs.h>
42 #include <sys/fstyp.h>
44 #include <sys/mnttab.h>
45 #include <sys/debug.h>
52 #include <libcmdutils.h>
54 #include "statcommon.h"
57 * For now, parsable output is turned off. Once we gather feedback and
58 * stablize the output format, we'll turn it back on. This prevents
59 * the situation where users build tools which depend on a specific
60 * format before we declare the output stable.
62 #define PARSABLE_OUTPUT 0
65 #define OPTIONS "FPT:afginv"
67 #define OPTIONS "FT:afginv"
70 /* Time stamp values */
71 #define NODATE 0 /* Default: No time stamp */
72 #define DDATE 1 /* Standard date format */
73 #define UDATE 2 /* Internal representation of Unix time */
75 #define RETRY_DELAY 250 /* Timeout for poll() */
76 #define HEADERLINES 12 /* Number of lines between display headers */
78 #define LBUFSZ 64 /* Generic size for local buffer */
79 CTASSERT(LBUFSZ
>= NN_NUMBUF_SZ
);
81 #define NENTITY_INIT 1 /* Initial number of entities to allocate */
84 * We need to have a mechanism for an old/previous and new/current vopstat
85 * structure. We only need two per entity and we can swap between them.
87 #define VS_SIZE 2 /* Size of vopstat array */
88 #define CUR_INDEX (vs_i)
89 #define PREV_INDEX ((vs_i == 0) ? 1 : 0) /* Opposite of CUR_INDEX */
90 #define BUMP_INDEX() vs_i = ((vs_i == 0) ? 1 : 0)
93 * An "entity" is anything we're collecting statistics on, it could
94 * be a mountpoint or an FS-type.
95 * e_name is the name of the entity (e.g. mount point or FS-type)
96 * e_ksname is the name of the associated kstat
97 * e_vs is an array of vopstats. This is used to keep track of "previous"
98 * and "current" vopstats.
100 typedef struct entity
{
101 char *e_name
; /* name of entity */
102 vopstats_t
*e_vs
; /* Array of vopstats */
103 ulong_t e_fsid
; /* fsid for ENTYPE_MNTPT only */
104 int e_type
; /* type of entity */
105 char e_ksname
[KSTAT_STRLEN
]; /* kstat name */
108 /* Types of entities (e_type) */
109 #define ENTYPE_UNKNOWN 0 /* UNKNOWN must be zero since we calloc() */
110 #define ENTYPE_FSTYPE 1
111 #define ENTYPE_MNTPT 2
113 char *cmdname
; /* name of this command */
114 int caught_cont
= 0; /* have caught a SIGCONT */
116 static uint_t timestamp_fmt
= NODATE
; /* print timestamp with stats */
118 static int vs_i
= 0; /* Index of current vs[] slot */
123 (void) fprintf(stderr
, gettext(
124 "Usage: %s [-a|f|i|n|v] [-T d|u] {-F | {fstype | fspath}...} "
125 "[interval [count]]\n"), cmdname
);
129 #define RAWVAL(ptr, member) ((ptr)->member.value.ui64)
130 #define DELTA(member) \
131 (newvsp->member.value.ui64 - (oldvsp ? oldvsp->member.value.ui64 : 0))
133 #define PRINTSTAT(isnice, nicestring, rawstring, rawval, buf) \
135 nicenum(rawval, buf, sizeof (buf)), \
136 (void) printf((nicestring), (buf)) \
138 (void) printf((rawstring), (rawval))
140 /* Values for display flag */
141 #define DISP_HEADER 0x1
145 * The policy for dealing with multiple flags is dealt with here.
146 * Currently, if we are displaying raw output, then don't allow
147 * headers to be printed.
150 dispflag_policy(int printhdr
, int dispflag
)
152 /* If we're not displaying raw output, then allow headers to print */
153 if ((dispflag
& DISP_RAW
) == 0) {
155 dispflag
|= DISP_HEADER
;
163 dflt_display(char *name
, vopstats_t
*oldvsp
, vopstats_t
*newvsp
, int dispflag
)
165 int niceflag
= ((dispflag
& DISP_RAW
) == 0);
173 longlong_t ndataread
;
174 longlong_t ndatawrite
;
175 longlong_t readthruput
;
176 longlong_t writethruput
;
179 nnewfile
= DELTA(ncreate
) + DELTA(nmkdir
) + DELTA(nsymlink
);
180 nnamerm
= DELTA(nremove
) + DELTA(nrmdir
);
181 nnamechg
= DELTA(nrename
) + DELTA(nlink
) + DELTA(nsymlink
);
182 nattrret
= DELTA(ngetattr
) + DELTA(naccess
) +
183 DELTA(ngetsecattr
) + DELTA(nfid
);
184 nattrchg
= DELTA(nsetattr
) + DELTA(nsetsecattr
) + DELTA(nspace
);
185 nlookup
= DELTA(nlookup
);
186 nreaddir
= DELTA(nreaddir
);
187 ndataread
= DELTA(nread
);
188 ndatawrite
= DELTA(nwrite
);
189 readthruput
= DELTA(read_bytes
);
190 writethruput
= DELTA(write_bytes
);
192 if (dispflag
& DISP_HEADER
) {
194 " new name name attr attr lookup rddir read read write write\n"
195 " file remov chng get set ops ops ops bytes ops bytes\n");
198 PRINTSTAT(niceflag
, "%5s ", "%lld:", nnewfile
, buf
);
199 PRINTSTAT(niceflag
, "%5s ", "%lld:", nnamerm
, buf
);
200 PRINTSTAT(niceflag
, "%5s ", "%lld:", nnamechg
, buf
);
201 PRINTSTAT(niceflag
, "%5s ", "%lld:", nattrret
, buf
);
202 PRINTSTAT(niceflag
, "%5s ", "%lld:", nattrchg
, buf
);
203 PRINTSTAT(niceflag
, " %5s ", "%lld:", nlookup
, buf
);
204 PRINTSTAT(niceflag
, "%5s ", "%lld:", nreaddir
, buf
);
205 PRINTSTAT(niceflag
, "%5s ", "%lld:", ndataread
, buf
);
206 PRINTSTAT(niceflag
, "%5s ", "%lld:", readthruput
, buf
);
207 PRINTSTAT(niceflag
, "%5s ", "%lld:", ndatawrite
, buf
);
208 PRINTSTAT(niceflag
, "%5s ", "%lld:", writethruput
, buf
);
209 (void) printf("%s\n", name
);
213 io_display(char *name
, vopstats_t
*oldvsp
, vopstats_t
*newvsp
, int dispflag
)
215 int niceflag
= ((dispflag
& DISP_RAW
) == 0);
218 if (dispflag
& DISP_HEADER
) {
220 " read read write write rddir rddir rwlock rwulock\n"
221 " ops bytes ops bytes ops bytes ops ops\n");
224 PRINTSTAT(niceflag
, "%5s ", "%lld:", DELTA(nread
), buf
);
225 PRINTSTAT(niceflag
, "%5s ", "%lld:", DELTA(read_bytes
), buf
);
227 PRINTSTAT(niceflag
, "%5s ", "%lld:", DELTA(nwrite
), buf
);
228 PRINTSTAT(niceflag
, "%5s ", "%lld:", DELTA(write_bytes
), buf
);
230 PRINTSTAT(niceflag
, "%5s ", "%lld:", DELTA(nreaddir
), buf
);
231 PRINTSTAT(niceflag
, "%5s ", "%lld:", DELTA(readdir_bytes
), buf
);
233 PRINTSTAT(niceflag
, " %5s ", "%lld:", DELTA(nrwlock
), buf
);
234 PRINTSTAT(niceflag
, "%5s ", "%lld:", DELTA(nrwunlock
), buf
);
236 (void) printf("%s\n", name
);
240 vm_display(char *name
, vopstats_t
*oldvsp
, vopstats_t
*newvsp
, int dispflag
)
242 int niceflag
= ((dispflag
& DISP_RAW
) == 0);
245 if (dispflag
& DISP_HEADER
) {
246 (void) printf(" map addmap delmap getpag putpag pagio\n");
249 PRINTSTAT(niceflag
, "%5s ", "%lld:", DELTA(nmap
), buf
);
250 PRINTSTAT(niceflag
, " %5s ", "%lld:", DELTA(naddmap
), buf
);
251 PRINTSTAT(niceflag
, " %5s ", "%lld:", DELTA(ndelmap
), buf
);
252 PRINTSTAT(niceflag
, " %5s ", "%lld:", DELTA(ngetpage
), buf
);
253 PRINTSTAT(niceflag
, " %5s ", "%lld:", DELTA(nputpage
), buf
);
254 PRINTSTAT(niceflag
, "%5s ", "%lld:", DELTA(npageio
), buf
);
255 (void) printf("%s\n", name
);
259 attr_display(char *name
, vopstats_t
*oldvsp
, vopstats_t
*newvsp
, int dispflag
)
261 int niceflag
= ((dispflag
& DISP_RAW
) == 0);
264 if (dispflag
& DISP_HEADER
) {
265 (void) printf("getattr setattr getsec setsec\n");
268 PRINTSTAT(niceflag
, " %5s ", "%lld:", DELTA(ngetattr
), buf
);
269 PRINTSTAT(niceflag
, " %5s ", "%lld:", DELTA(nsetattr
), buf
);
270 PRINTSTAT(niceflag
, " %5s ", "%lld:", DELTA(ngetsecattr
), buf
);
271 PRINTSTAT(niceflag
, " %5s ", "%lld:", DELTA(nsetsecattr
), buf
);
273 (void) printf("%s\n", name
);
277 naming_display(char *name
, vopstats_t
*oldvsp
, vopstats_t
*newvsp
, int dispflag
)
279 int niceflag
= ((dispflag
& DISP_RAW
) == 0);
282 if (dispflag
& DISP_HEADER
) {
284 "lookup creat remov link renam mkdir rmdir rddir symlnk rdlnk\n");
287 PRINTSTAT(niceflag
, "%5s ", "%lld:", DELTA(nlookup
), buf
);
288 PRINTSTAT(niceflag
, "%5s ", "%lld:", DELTA(ncreate
), buf
);
289 PRINTSTAT(niceflag
, "%5s ", "%lld:", DELTA(nremove
), buf
);
290 PRINTSTAT(niceflag
, "%5s ", "%lld:", DELTA(nlink
), buf
);
291 PRINTSTAT(niceflag
, "%5s ", "%lld:", DELTA(nrename
), buf
);
292 PRINTSTAT(niceflag
, "%5s ", "%lld:", DELTA(nmkdir
), buf
);
293 PRINTSTAT(niceflag
, "%5s ", "%lld:", DELTA(nrmdir
), buf
);
294 PRINTSTAT(niceflag
, "%5s ", "%lld:", DELTA(nreaddir
), buf
);
295 PRINTSTAT(niceflag
, " %5s ", "%lld:", DELTA(nsymlink
), buf
);
296 PRINTSTAT(niceflag
, "%5s ", "%lld:", DELTA(nreadlink
), buf
);
297 (void) printf("%s\n", name
);
301 #define PRINT_VOPSTAT_CMN(niceflag, vop) \
303 (void) printf("%10s ", #vop); \
304 PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(n##vop), buf);
306 #define PRINT_VOPSTAT(niceflag, vop) \
307 PRINT_VOPSTAT_CMN(niceflag, vop); \
311 #define PRINT_VOPSTAT_IO(niceflag, vop) \
312 PRINT_VOPSTAT_CMN(niceflag, vop); \
313 PRINTSTAT(niceflag, " %5s\n", "%lld:", \
314 DELTA(vop##_bytes), buf);
317 vop_display(char *name
, vopstats_t
*oldvsp
, vopstats_t
*newvsp
, int dispflag
)
319 int niceflag
= ((dispflag
& DISP_RAW
) == 0);
323 (void) printf("%s\n", name
);
324 (void) printf(" operation #ops bytes\n");
327 PRINT_VOPSTAT(niceflag
, open
);
328 PRINT_VOPSTAT(niceflag
, close
);
329 PRINT_VOPSTAT_IO(niceflag
, read
);
330 PRINT_VOPSTAT_IO(niceflag
, write
);
331 PRINT_VOPSTAT(niceflag
, ioctl
);
332 PRINT_VOPSTAT(niceflag
, setfl
);
333 PRINT_VOPSTAT(niceflag
, getattr
);
334 PRINT_VOPSTAT(niceflag
, setattr
);
335 PRINT_VOPSTAT(niceflag
, access
);
336 PRINT_VOPSTAT(niceflag
, lookup
);
337 PRINT_VOPSTAT(niceflag
, create
);
338 PRINT_VOPSTAT(niceflag
, remove
);
339 PRINT_VOPSTAT(niceflag
, link
);
340 PRINT_VOPSTAT(niceflag
, rename
);
341 PRINT_VOPSTAT(niceflag
, mkdir
);
342 PRINT_VOPSTAT(niceflag
, rmdir
);
343 PRINT_VOPSTAT_IO(niceflag
, readdir
);
344 PRINT_VOPSTAT(niceflag
, symlink
);
345 PRINT_VOPSTAT(niceflag
, readlink
);
346 PRINT_VOPSTAT(niceflag
, fsync
);
347 PRINT_VOPSTAT(niceflag
, inactive
);
348 PRINT_VOPSTAT(niceflag
, fid
);
349 PRINT_VOPSTAT(niceflag
, rwlock
);
350 PRINT_VOPSTAT(niceflag
, rwunlock
);
351 PRINT_VOPSTAT(niceflag
, seek
);
352 PRINT_VOPSTAT(niceflag
, cmp
);
353 PRINT_VOPSTAT(niceflag
, frlock
);
354 PRINT_VOPSTAT(niceflag
, space
);
355 PRINT_VOPSTAT(niceflag
, realvp
);
356 PRINT_VOPSTAT(niceflag
, getpage
);
357 PRINT_VOPSTAT(niceflag
, putpage
);
358 PRINT_VOPSTAT(niceflag
, map
);
359 PRINT_VOPSTAT(niceflag
, addmap
);
360 PRINT_VOPSTAT(niceflag
, delmap
);
361 PRINT_VOPSTAT(niceflag
, poll
);
362 PRINT_VOPSTAT(niceflag
, dump
);
363 PRINT_VOPSTAT(niceflag
, pathconf
);
364 PRINT_VOPSTAT(niceflag
, pageio
);
365 PRINT_VOPSTAT(niceflag
, dumpctl
);
366 PRINT_VOPSTAT(niceflag
, dispose
);
367 PRINT_VOPSTAT(niceflag
, getsecattr
);
368 PRINT_VOPSTAT(niceflag
, setsecattr
);
369 PRINT_VOPSTAT(niceflag
, shrlock
);
370 PRINT_VOPSTAT(niceflag
, vnevent
);
371 PRINT_VOPSTAT(niceflag
, reqzcbuf
);
372 PRINT_VOPSTAT(niceflag
, retzcbuf
);
375 /* Make it easier on the eyes */
378 (void) printf("%s\n", name
);
384 * Retrieve the vopstats. If kspp (pointer to kstat_t pointer) is non-NULL,
385 * then pass it back to the caller.
387 * Returns 0 on success, non-zero on failure.
390 get_vopstats(kstat_ctl_t
*kc
, char *ksname
, vopstats_t
*vsp
, kstat_t
**kspp
)
394 if (ksname
== NULL
|| *ksname
== 0)
398 /* wait for a possibly up-to-date chain */
399 while (kstat_chain_update(kc
) == -1) {
400 if (errno
== EAGAIN
) {
402 (void) poll(NULL
, 0, RETRY_DELAY
);
405 perror("kstat_chain_update");
409 if ((ksp
= kstat_lookup(kc
, NULL
, -1, ksname
)) == NULL
) {
413 if (kstat_read(kc
, ksp
, vsp
) == -1) {
424 * Given a file system type name, determine if it's part of the
425 * exception list of file systems that are not to be displayed.
428 is_exception(char *fsname
)
430 char **xlp
; /* Pointer into the exception list */
432 static char *exception_list
[] = {
443 for (xlp
= &exception_list
[0]; *xlp
!= NULL
; xlp
++) {
444 if (strcmp(fsname
, *xlp
) == 0)
452 * Plain and simple, build an array of names for fstypes
453 * Returns 0, if it encounters a problem.
456 build_fstype_list(char ***fstypep
)
460 char buf
[FSTYPSZ
+ 1];
462 if ((nfstype
= sysfs(GETNFSTYP
)) < 0) {
463 perror("sysfs(GETNFSTYP)");
467 if ((*fstypep
= calloc(nfstype
, sizeof (char *))) == NULL
) {
468 perror("calloc() fstypes");
472 for (i
= 1; i
< nfstype
; i
++) {
473 if (sysfs(GETFSTYP
, i
, buf
) < 0) {
474 perror("sysfs(GETFSTYP)");
481 /* If this is part of the exception list, move on */
482 if (is_exception(buf
))
485 if (((*fstypep
)[i
] = strdup(buf
)) == NULL
) {
486 perror("strdup() fstype name");
495 * After we're done with getopts(), process the rest of the
496 * operands. We have three cases and this is the priority:
498 * 1) [ operand... ] interval count
499 * 2) [ operand... ] interval
502 * The trick is that any of the operands might start with a number or even
503 * be made up exclusively of numbers (and we have to handle negative numbers
504 * in case a user/script gets out of line). If we find two operands at the
505 * end of the list then we claim case 1. If we find only one operand at the
506 * end made up only of number, then we claim case 2. Otherwise, case 3.
507 * BTW, argc, argv don't change.
516 entity_t
**entityp
) /* Array of stat-able entities */
518 int nentities
= 0; /* Number of entities found */
519 int out_of_range
; /* Set if 2nd-to-last operand out-of-range */
522 return (nentities
); /* None found, returns 0 */
524 * We know exactly what the maximum number of entities is going
525 * to be: argc - optind
527 if ((*entityp
= calloc((argc
- optind
), sizeof (entity_t
))) == NULL
) {
528 perror("calloc() entities");
532 for (/* void */; argc
> optind
; optind
++) {
535 /* If we have more than two operands left to process */
536 if ((argc
- optind
) > 2) {
537 (*entityp
)[nentities
++].e_name
= strdup(argv
[optind
]);
541 /* If we're here, then we only have one or two operands left */
544 *interval
= strtol(argv
[optind
], &endptr
, 10);
545 if (*endptr
&& !isdigit((int)*endptr
)) {
546 /* Operand was not a number */
547 (*entityp
)[nentities
++].e_name
= strdup(argv
[optind
]);
549 } else if (errno
== ERANGE
|| *interval
<= 0 ||
550 *interval
> MAXLONG
) {
551 /* Operand was a number, just out of range */
556 * The last operand we saw was a number. If it happened to
557 * be the last operand, then it is the interval...
559 if ((argc
- optind
) == 1) {
560 /* ...but we need to check the range. */
562 (void) fprintf(stderr
, gettext(
563 "interval must be between 1 and "
564 "%ld (inclusive)\n"), MAXLONG
);
568 * The value of the interval is valid. Set
569 * count to something really big so it goes
578 * At this point, we *might* have the interval, but if the
579 * next operand isn't a number, then we don't have either
580 * the interval nor the count. Both must be set to the
581 * defaults. In that case, both the current and the previous
582 * operands are stat-able entities.
585 *count
= strtol(argv
[optind
+ 1], &endptr
, 10);
586 if (*endptr
&& !isdigit((int)*endptr
)) {
588 * Faked out! The last operand wasn't a number so
589 * the current and previous operands should be
590 * stat-able entities. We also need to reset interval.
593 (*entityp
)[nentities
++].e_name
= strdup(argv
[optind
++]);
594 (*entityp
)[nentities
++].e_name
= strdup(argv
[optind
++]);
595 } else if (out_of_range
|| errno
== ERANGE
|| *count
<= 0) {
596 (void) fprintf(stderr
, gettext(
597 "Both interval and count must be between 1 "
598 "and %ld (inclusive)\n"), MAXLONG
);
607 * set_mntpt() looks at the entity's name (e_name) and finds its
608 * mountpoint. To do this, we need to build a list of mountpoints
609 * from /etc/mnttab. We only need to do this once and we don't do it
610 * if we don't need to look at any mountpoints.
611 * Returns 0 on success, non-zero if it couldn't find a mount-point.
614 set_mntpt(entity_t
*ep
)
619 ulong_t m_fsid
; /* From statvfs(), set only as needed */
620 } *mnt_list
= NULL
; /* Linked list of mount-points */
622 struct statvfs64 statvfsbuf
;
623 char *original_name
= ep
->e_name
;
626 if (original_name
== NULL
) /* Shouldn't happen */
629 /* We only set up mnt_list the first time this is called */
630 if (mnt_list
== NULL
) {
632 struct mnttab mnttab
;
634 if ((fp
= fopen(MNTTAB
, "r")) == NULL
) {
640 * We insert at the front of the list so that when we
641 * search entries we'll have the last mounted entries
642 * first in the list so that we can match the longest
645 while (getmntent(fp
, &mnttab
) == 0) {
646 if ((mntp
= malloc(sizeof (*mntp
))) == NULL
) {
647 perror("malloc() mount list");
650 mntp
->m_mntpt
= strdup(mnttab
.mnt_mountp
);
651 mntp
->m_next
= mnt_list
;
657 if (realpath(original_name
, path
) == NULL
) {
658 perror(original_name
);
663 * Now that we have the path, walk through the mnt_list and
664 * look for the first (best) match.
666 for (mntp
= mnt_list
; mntp
; mntp
= mntp
->m_next
) {
667 if (strncmp(path
, mntp
->m_mntpt
, strlen(mntp
->m_mntpt
)) == 0) {
668 if (mntp
->m_fsid
== 0) {
669 if (statvfs(mntp
->m_mntpt
, &statvfsbuf
)) {
670 /* Can't statvfs so no match */
673 mntp
->m_fsid
= statvfsbuf
.f_fsid
;
677 if (ep
->e_fsid
!= mntp
->m_fsid
) {
678 /* No match - Move on */
687 (void) fprintf(stderr
, gettext(
688 "Can't find mount point for %s\n"), path
);
692 ep
->e_name
= strdup(mntp
->m_mntpt
);
698 * We have an array of entities that are potentially stat-able. Using
699 * the name (e_name) of the entity, attempt to construct a ksname suitable
700 * for use by kstat_lookup(3kstat) and fill it into the e_ksname member.
702 * We check the e_name against the list of file system types. If there is
703 * no match then test to see if the path is valid. If the path is valid,
704 * then determine the mountpoint.
707 set_ksnames(entity_t
*entities
, int nentities
, char **fstypes
, int nfstypes
)
710 struct statvfs64 statvfsbuf
;
712 for (i
= 0; i
< nentities
; i
++) {
713 entity_t
*ep
= &entities
[i
];
715 /* Check the name against the list of fstypes */
716 for (j
= 1; j
< nfstypes
; j
++) {
717 if (fstypes
[j
] && ep
->e_name
&&
718 strcmp(ep
->e_name
, fstypes
[j
]) == 0) {
719 /* It's a file system type */
720 ep
->e_type
= ENTYPE_FSTYPE
;
721 (void) snprintf(ep
->e_ksname
, KSTAT_STRLEN
,
722 "%s%s", VOPSTATS_STR
, ep
->e_name
);
723 /* Now allocate the vopstats array */
724 ep
->e_vs
= calloc(VS_SIZE
, sizeof (vopstats_t
));
725 if (entities
[i
].e_vs
== NULL
) {
726 perror("calloc() fstype vopstats");
732 if (j
< nfstypes
) /* Found it! */
736 * If the entity in the exception list of fstypes, then
737 * null out the entry so it isn't displayed and move along.
739 if (is_exception(ep
->e_name
)) {
744 /* If we didn't find it, see if it's a path */
745 if (ep
->e_name
== NULL
|| statvfs(ep
->e_name
, &statvfsbuf
)) {
746 /* Error - Make sure the entry is nulled out */
750 (void) snprintf(ep
->e_ksname
, KSTAT_STRLEN
, "%s%lx",
751 VOPSTATS_STR
, statvfsbuf
.f_fsid
);
752 ep
->e_fsid
= statvfsbuf
.f_fsid
;
754 (void) fprintf(stderr
,
755 gettext("Can't determine type of \"%s\"\n"),
756 ep
->e_name
? ep
->e_name
: gettext("<NULL>"));
758 ep
->e_type
= ENTYPE_MNTPT
;
761 /* Now allocate the vopstats array */
762 ep
->e_vs
= calloc(VS_SIZE
, sizeof (vopstats_t
));
763 if (entities
[i
].e_vs
== NULL
) {
764 perror("calloc() vopstats array");
771 * The idea is that 'dspfunc' should only be modified from the default
772 * once since the display options are mutually exclusive. If 'dspfunc'
773 * only contains the default display function, then all is good and we
774 * can set it to the new display function. Otherwise, bail.
778 void (**dspfunc
)(char *, vopstats_t
*, vopstats_t
*, int),
779 void (*newfunc
)(char *, vopstats_t
*, vopstats_t
*, int))
781 if (*dspfunc
!= dflt_display
) {
782 (void) fprintf(stderr
, gettext(
783 "%s: Display options -{a|f|i|n|v} are mutually exclusive\n"),
791 main(int argc
, char *argv
[])
794 int i
, j
; /* Generic counters */
796 int linesout
= 0; /* Keeps track of lines printed */
797 int printhdr
= 0; /* Print a header? 0 = no, 1 = yes */
798 int nfstypes
; /* Number of fstypes */
799 int dispflag
= 0; /* Flags for display control */
800 long count
= 0; /* Number of iterations for display */
801 int forever
; /* Run forever */
803 boolean_t fstypes_only
= B_FALSE
; /* Display fstypes only */
804 char **fstypes
; /* Array of names of all fstypes */
805 int nentities
; /* Number of stat-able entities */
806 entity_t
*entities
; /* Array of stat-able entities */
808 void (*dfunc
)(char *, vopstats_t
*, vopstats_t
*, int) = dflt_display
;
809 hrtime_t start_n
; /* Start time */
810 hrtime_t period_n
; /* Interval in nanoseconds */
814 (void) setlocale(LC_ALL
, "");
815 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
816 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
818 (void) textdomain(TEXT_DOMAIN
);
820 /* Don't let buffering interfere with piped output. */
821 (void) setvbuf(stdout
, NULL
, _IOLBF
, 0);
824 while ((c
= getopt(argc
, argv
, OPTIONS
)) != EOF
) {
831 case 'F': /* Only display available FStypes */
832 fstypes_only
= B_TRUE
;
836 case 'P': /* Parsable output */
837 dispflag
|= DISP_RAW
;
839 #endif /* PARSABLE_OUTPUT */
841 case 'T': /* Timestamp */
843 if (strcmp(optarg
, "u") == 0) {
844 timestamp_fmt
= UDATE
;
845 } else if (strcmp(optarg
, "d") == 0) {
846 timestamp_fmt
= DDATE
;
850 /* If it was never set properly... */
851 if (timestamp_fmt
== NODATE
) {
852 (void) fprintf(stderr
, gettext("%s: -T option "
853 "requires either 'u' or 'd'\n"), cmdname
);
859 set_dispfunc(&dfunc
, attr_display
);
863 set_dispfunc(&dfunc
, vop_display
);
867 set_dispfunc(&dfunc
, io_display
);
871 set_dispfunc(&dfunc
, naming_display
);
875 set_dispfunc(&dfunc
, vm_display
);
881 if ((dispflag
& DISP_RAW
) && (timestamp_fmt
!= NODATE
)) {
882 (void) fprintf(stderr
, gettext(
883 "-P and -T options are mutually exclusive\n"));
886 #endif /* PARSABLE_OUTPUT */
888 /* Gather the list of filesystem types */
889 if ((nfstypes
= build_fstype_list(&fstypes
)) == 0) {
890 (void) fprintf(stderr
,
891 gettext("Can't build list of fstypes\n"));
895 nentities
= parse_operands(
896 argc
, argv
, optind
, &interval
, &count
, &entities
);
897 forever
= count
== MAXLONG
;
898 period_n
= (hrtime_t
)interval
* NANOSEC
;
900 if (nentities
== -1) /* Set of operands didn't parse properly */
903 if ((nentities
== 0) && (fstypes_only
== B_FALSE
)) {
904 (void) fprintf(stderr
, gettext(
905 "Must specify -F or at least one fstype or mount point\n"));
909 if ((nentities
> 0) && (fstypes_only
== B_TRUE
)) {
910 (void) fprintf(stderr
, gettext(
911 "Cannot use -F with fstypes or mount points\n"));
916 * If we had no operands (except for interval/count) and we
917 * requested FStypes only (-F), then fill in the entities[]
918 * array with all available fstypes.
920 if ((nentities
== 0) && (fstypes_only
== B_TRUE
)) {
921 if ((entities
= calloc(nfstypes
, sizeof (entity_t
))) == NULL
) {
922 perror("calloc() fstype stats");
926 for (i
= 1; i
< nfstypes
; i
++) {
928 entities
[nentities
].e_name
= strdup(fstypes
[i
]);
934 set_ksnames(entities
, nentities
, fstypes
, nfstypes
);
936 if ((kc
= kstat_open()) == NULL
) {
937 perror("kstat_open");
942 start_n
= gethrtime();
944 /* Initial timestamp */
945 if (timestamp_fmt
!= NODATE
) {
946 print_timestamp(timestamp_fmt
);
951 * The following loop walks through the entities[] list to "prime
954 for (j
= 0, printhdr
= 1; j
< nentities
; j
++) {
955 entity_t
*ent
= &entities
[j
];
956 vopstats_t
*vsp
= &ent
->e_vs
[CUR_INDEX
];
959 if (get_vopstats(kc
, ent
->e_ksname
, vsp
, &ksp
) == 0) {
960 (*dfunc
)(ent
->e_name
, NULL
, vsp
,
961 dispflag_policy(printhdr
, dispflag
));
965 * If we can't find it the first time through, then
968 entities
[j
].e_ksname
[0] = 0;
971 * If we're only displaying FStypes (-F) then don't
972 * complain about any file systems that might not
973 * be loaded. Otherwise, let the user know that
976 if (fstypes_only
== B_FALSE
) {
977 (void) fprintf(stderr
, gettext(
978 "No statistics available for %s\n"),
986 /* Set up signal handler for SIGCONT */
987 if (signal(SIGCONT
, cont_handler
) == SIG_ERR
)
988 fail(1, "signal failed");
991 BUMP_INDEX(); /* Swap the previous/current indices */
993 while (forever
|| i
++ <= count
) {
995 * No telling how many lines will be printed in any interval.
996 * There should be a minimum of HEADERLINES between any
997 * header. If we exceed that, no big deal.
999 if (linesout
> HEADERLINES
) {
1004 sleep_until(&start_n
, period_n
, forever
, &caught_cont
);
1006 if (timestamp_fmt
!= NODATE
) {
1007 print_timestamp(timestamp_fmt
);
1011 for (j
= 0, nentities_found
= 0; j
< nentities
; j
++) {
1012 entity_t
*ent
= &entities
[j
];
1015 * If this entry has been cleared, don't attempt
1018 if (ent
->e_ksname
[0] == 0) {
1022 if (get_vopstats(kc
, ent
->e_ksname
,
1023 &ent
->e_vs
[CUR_INDEX
], NULL
) == 0) {
1024 (*dfunc
)(ent
->e_name
, &ent
->e_vs
[PREV_INDEX
],
1025 &ent
->e_vs
[CUR_INDEX
],
1026 dispflag_policy(printhdr
, dispflag
));
1030 if (ent
->e_type
== ENTYPE_MNTPT
) {
1031 (void) printf(gettext(
1032 "<<mount point no longer "
1033 "available: %s>>\n"), ent
->e_name
);
1034 } else if (ent
->e_type
== ENTYPE_FSTYPE
) {
1035 (void) printf(gettext(
1036 "<<file system module no longer "
1037 "loaded: %s>>\n"), ent
->e_name
);
1039 (void) printf(gettext(
1040 "<<%s no longer available>>\n"),
1043 /* Disable this so it doesn't print again */
1044 ent
->e_ksname
[0] = 0;
1046 printhdr
= 0; /* Always shut this off */
1048 BUMP_INDEX(); /* Bump the previous/current indices */
1051 * If the entities we were observing are no longer there
1052 * (file system modules unloaded, file systems unmounted)
1055 if (nentities_found
== 0)