xref: /freebsd/usr.bin/nfsstat/nfsstat.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
1 /*
2  * Copyright (c) 1983, 1989, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Rick Macklem at The University of Guelph.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #ifndef lint
38 static char copyright[] =
39 "@(#) Copyright (c) 1983, 1989, 1993\n\
40 	The Regents of the University of California.  All rights reserved.\n";
41 #endif /* not lint */
42 
43 #ifndef lint
44 #if 0
45 static char sccsid[] = "@(#)nfsstat.c	8.2 (Berkeley) 3/31/95";
46 #endif
47 static const char rcsid[] =
48   "$FreeBSD$";
49 #endif /* not lint */
50 
51 #include <sys/param.h>
52 #include <sys/mount.h>
53 #include <sys/time.h>
54 #include <sys/sysctl.h>
55 #include <nfs/rpcv2.h>
56 #include <nfs/nfsproto.h>
57 #include <nfs/nfs.h>
58 #include <signal.h>
59 #include <fcntl.h>
60 #include <ctype.h>
61 #include <errno.h>
62 #include <kvm.h>
63 #include <limits.h>
64 #include <nlist.h>
65 #include <unistd.h>
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <paths.h>
70 #include <err.h>
71 
72 struct nlist nl[] = {
73 #define	N_NFSSTAT	0
74 	{ "_nfsstats" },
75 	"",
76 };
77 kvm_t *kd;
78 
79 static int deadkernel = 0;
80 static int widemode = 0;
81 
82 void intpr __P((int, int));
83 void printhdr __P((int, int));
84 void sidewaysintpr __P((u_int, int, int));
85 void usage __P((void));
86 char *sperc1 __P((int, int));
87 char *sperc2 __P((int, int));
88 
89 #define DELTA(field)	(nfsstats.field - lastst.field)
90 
91 main(argc, argv)
92 	int argc;
93 	char **argv;
94 {
95 	u_int interval;
96 	int clientOnly = -1;
97 	int serverOnly = -1;
98 	int ch;
99 	char *memf, *nlistf;
100 	char errbuf[_POSIX2_LINE_MAX];
101 
102 	interval = 0;
103 	memf = nlistf = NULL;
104 	while ((ch = getopt(argc, argv, "csWM:N:w:")) != -1)
105 		switch(ch) {
106 		case 'M':
107 			memf = optarg;
108 			break;
109 		case 'N':
110 			nlistf = optarg;
111 			break;
112 		case 'W':
113 			widemode = 1;
114 			break;
115 		case 'w':
116 			interval = atoi(optarg);
117 			break;
118 		case 'c':
119 			clientOnly = 1;
120 			if (serverOnly < 0)
121 				serverOnly = 0;
122 			break;
123 		case 's':
124 			serverOnly = 1;
125 			if (clientOnly < 0)
126 				clientOnly = 0;
127 			break;
128 		case '?':
129 		default:
130 			usage();
131 		}
132 	argc -= optind;
133 	argv += optind;
134 
135 #define	BACKWARD_COMPATIBILITY
136 #ifdef	BACKWARD_COMPATIBILITY
137 	if (*argv) {
138 		interval = atoi(*argv);
139 		if (*++argv) {
140 			nlistf = *argv;
141 			if (*++argv)
142 				memf = *argv;
143 		}
144 	}
145 #endif
146 	if (nlistf != NULL || memf != NULL) {
147 		deadkernel = 1;
148 
149 		if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY,
150 					errbuf)) == 0) {
151 			errx(1, "kvm_openfiles: %s", errbuf);
152 		}
153 		if (kvm_nlist(kd, nl) != 0) {
154 			errx(1, "kvm_nlist: can't get names");
155 		}
156 	}
157 
158 	if (interval)
159 		sidewaysintpr(interval, clientOnly, serverOnly);
160 	else
161 		intpr(clientOnly, serverOnly);
162 	exit(0);
163 }
164 
165 /*
166  * Read the nfs stats using sysctl(3) for live kernels, or kvm_read
167  * for dead ones.
168  */
169 void
170 readstats(stp)
171 	struct nfsstats *stp;
172 {
173 	if(deadkernel) {
174 		if(kvm_read(kd, (u_long)nl[N_NFSSTAT].n_value, stp,
175 			    sizeof *stp) < 0) {
176 			err(1, "kvm_read");
177 		}
178 	} else {
179 		int name[3];
180 		size_t buflen = sizeof *stp;
181 		struct vfsconf vfc;
182 
183 		if (getvfsbyname("nfs", &vfc) < 0)
184 			err(1, "getvfsbyname: NFS not compiled into kernel");
185 		name[0] = CTL_VFS;
186 		name[1] = vfc.vfc_typenum;
187 		name[2] = NFS_NFSSTATS;
188 		if (sysctl(name, 3, stp, &buflen, (void *)0, (size_t)0) < 0) {
189 			err(1, "sysctl");
190 		}
191 	}
192 }
193 
194 /*
195  * Print a description of the nfs stats.
196  */
197 void
198 intpr(int clientOnly, int serverOnly)
199 {
200 	struct nfsstats nfsstats;
201 
202 	readstats(&nfsstats);
203 
204 	if (clientOnly) {
205 		printf("Client Info:\n");
206 		printf("Rpc Counts:\n");
207 		printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n",
208 			"Getattr", "Setattr", "Lookup", "Readlink", "Read",
209 			"Write", "Create", "Remove");
210 		printf("%9d %9d %9d %9d %9d %9d %9d %9d\n",
211 			nfsstats.rpccnt[NFSPROC_GETATTR],
212 			nfsstats.rpccnt[NFSPROC_SETATTR],
213 			nfsstats.rpccnt[NFSPROC_LOOKUP],
214 			nfsstats.rpccnt[NFSPROC_READLINK],
215 			nfsstats.rpccnt[NFSPROC_READ],
216 			nfsstats.rpccnt[NFSPROC_WRITE],
217 			nfsstats.rpccnt[NFSPROC_CREATE],
218 			nfsstats.rpccnt[NFSPROC_REMOVE]);
219 		printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n",
220 			"Rename", "Link", "Symlink", "Mkdir", "Rmdir",
221 			"Readdir", "RdirPlus", "Access");
222 		printf("%9d %9d %9d %9d %9d %9d %9d %9d\n",
223 			nfsstats.rpccnt[NFSPROC_RENAME],
224 			nfsstats.rpccnt[NFSPROC_LINK],
225 			nfsstats.rpccnt[NFSPROC_SYMLINK],
226 			nfsstats.rpccnt[NFSPROC_MKDIR],
227 			nfsstats.rpccnt[NFSPROC_RMDIR],
228 			nfsstats.rpccnt[NFSPROC_READDIR],
229 			nfsstats.rpccnt[NFSPROC_READDIRPLUS],
230 			nfsstats.rpccnt[NFSPROC_ACCESS]);
231 		printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n",
232 			"Mknod", "Fsstat", "Fsinfo", "PathConf", "Commit",
233 			"GLease", "Vacate", "Evict");
234 		printf("%9d %9d %9d %9d %9d %9d %9d %9d\n",
235 			nfsstats.rpccnt[NFSPROC_MKNOD],
236 			nfsstats.rpccnt[NFSPROC_FSSTAT],
237 			nfsstats.rpccnt[NFSPROC_FSINFO],
238 			nfsstats.rpccnt[NFSPROC_PATHCONF],
239 			nfsstats.rpccnt[NFSPROC_COMMIT],
240 			nfsstats.rpccnt[NQNFSPROC_GETLEASE],
241 			nfsstats.rpccnt[NQNFSPROC_VACATED],
242 			nfsstats.rpccnt[NQNFSPROC_EVICTED]);
243 		printf("Rpc Info:\n");
244 		printf("%9.9s %9.9s %9.9s %9.9s %9.9s\n",
245 			"TimedOut", "Invalid", "X Replies", "Retries",
246 			"Requests");
247 		printf("%9d %9d %9d %9d %9d\n",
248 			nfsstats.rpctimeouts,
249 			nfsstats.rpcinvalid,
250 			nfsstats.rpcunexpected,
251 			nfsstats.rpcretries,
252 			nfsstats.rpcrequests);
253 		printf("Cache Info:\n");
254 		printf("%9.9s %9.9s %9.9s %9.9s",
255 			"Attr Hits", "Misses", "Lkup Hits", "Misses");
256 		printf(" %9.9s %9.9s %9.9s %9.9s\n",
257 			"BioR Hits", "Misses", "BioW Hits", "Misses");
258 		printf("%9d %9d %9d %9d",
259 			nfsstats.attrcache_hits, nfsstats.attrcache_misses,
260 			nfsstats.lookupcache_hits, nfsstats.lookupcache_misses);
261 		printf(" %9d %9d %9d %9d\n",
262 			nfsstats.biocache_reads-nfsstats.read_bios,
263 			nfsstats.read_bios,
264 			nfsstats.biocache_writes-nfsstats.write_bios,
265 			nfsstats.write_bios);
266 		printf("%9.9s %9.9s %9.9s %9.9s",
267 			"BioRLHits", "Misses", "BioD Hits", "Misses");
268 		printf(" %9.9s %9.9s\n", "DirE Hits", "Misses");
269 		printf("%9d %9d %9d %9d",
270 			nfsstats.biocache_readlinks-nfsstats.readlink_bios,
271 			nfsstats.readlink_bios,
272 			nfsstats.biocache_readdirs-nfsstats.readdir_bios,
273 			nfsstats.readdir_bios);
274 		printf(" %9d %9d\n",
275 			nfsstats.direofcache_hits, nfsstats.direofcache_misses);
276 	}
277 	if (serverOnly) {
278 		printf("\nServer Info:\n");
279 		printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n",
280 			"Getattr", "Setattr", "Lookup", "Readlink", "Read",
281 			"Write", "Create", "Remove");
282 		printf("%9d %9d %9d %9d %9d %9d %9d %9d\n",
283 			nfsstats.srvrpccnt[NFSPROC_GETATTR],
284 			nfsstats.srvrpccnt[NFSPROC_SETATTR],
285 			nfsstats.srvrpccnt[NFSPROC_LOOKUP],
286 			nfsstats.srvrpccnt[NFSPROC_READLINK],
287 			nfsstats.srvrpccnt[NFSPROC_READ],
288 			nfsstats.srvrpccnt[NFSPROC_WRITE],
289 			nfsstats.srvrpccnt[NFSPROC_CREATE],
290 			nfsstats.srvrpccnt[NFSPROC_REMOVE]);
291 		printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n",
292 			"Rename", "Link", "Symlink", "Mkdir", "Rmdir",
293 			"Readdir", "RdirPlus", "Access");
294 		printf("%9d %9d %9d %9d %9d %9d %9d %9d\n",
295 			nfsstats.srvrpccnt[NFSPROC_RENAME],
296 			nfsstats.srvrpccnt[NFSPROC_LINK],
297 			nfsstats.srvrpccnt[NFSPROC_SYMLINK],
298 			nfsstats.srvrpccnt[NFSPROC_MKDIR],
299 			nfsstats.srvrpccnt[NFSPROC_RMDIR],
300 			nfsstats.srvrpccnt[NFSPROC_READDIR],
301 			nfsstats.srvrpccnt[NFSPROC_READDIRPLUS],
302 			nfsstats.srvrpccnt[NFSPROC_ACCESS]);
303 		printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n",
304 			"Mknod", "Fsstat", "Fsinfo", "PathConf", "Commit",
305 			"GLease", "Vacate", "Evict");
306 		printf("%9d %9d %9d %9d %9d %9d %9d %9d\n",
307 			nfsstats.srvrpccnt[NFSPROC_MKNOD],
308 			nfsstats.srvrpccnt[NFSPROC_FSSTAT],
309 			nfsstats.srvrpccnt[NFSPROC_FSINFO],
310 			nfsstats.srvrpccnt[NFSPROC_PATHCONF],
311 			nfsstats.srvrpccnt[NFSPROC_COMMIT],
312 			nfsstats.srvrpccnt[NQNFSPROC_GETLEASE],
313 			nfsstats.srvrpccnt[NQNFSPROC_VACATED],
314 			nfsstats.srvrpccnt[NQNFSPROC_EVICTED]);
315 		printf("Server Ret-Failed\n");
316 		printf("%17d\n", nfsstats.srvrpc_errs);
317 		printf("Server Faults\n");
318 		printf("%13d\n", nfsstats.srv_errs);
319 		printf("Server Cache Stats:\n");
320 		printf("%9.9s %9.9s %9.9s %9.9s\n",
321 			"Inprog", "Idem", "Non-idem", "Misses");
322 		printf("%9d %9d %9d %9d\n",
323 			nfsstats.srvcache_inproghits,
324 			nfsstats.srvcache_idemdonehits,
325 			nfsstats.srvcache_nonidemdonehits,
326 			nfsstats.srvcache_misses);
327 		printf("Server Lease Stats:\n");
328 		printf("%9.9s %9.9s %9.9s\n",
329 		"Leases", "PeakL", "GLeases");
330 		printf("%9d %9d %9d\n",
331 			nfsstats.srvnqnfs_leases,
332 			nfsstats.srvnqnfs_maxleases,
333 			nfsstats.srvnqnfs_getleases);
334 		printf("Server Write Gathering:\n");
335 		printf("%9.9s %9.9s %9.9s\n",
336 			"WriteOps", "WriteRPC", "Opsaved");
337 		printf("%9d %9d %9d\n",
338 			nfsstats.srvvop_writes,
339 			nfsstats.srvrpccnt[NFSPROC_WRITE],
340 			nfsstats.srvrpccnt[NFSPROC_WRITE] -
341 			    nfsstats.srvvop_writes);
342 	}
343 }
344 
345 u_char	signalled;			/* set if alarm goes off "early" */
346 
347 /*
348  * Print a running summary of nfs statistics.
349  * Repeat display every interval seconds, showing statistics
350  * collected over that interval.  Assumes that interval is non-zero.
351  * First line printed at top of screen is always cumulative.
352  */
353 void
354 sidewaysintpr(u_int interval, int clientOnly, int serverOnly)
355 {
356 	struct nfsstats nfsstats, lastst;
357 	int hdrcnt = 1;
358 
359 	readstats(&lastst);
360 	sleep(interval);
361 
362 	for (;;) {
363 		readstats(&nfsstats);
364 
365 		if (--hdrcnt == 0) {
366 			printhdr(clientOnly, serverOnly);
367 			if (clientOnly && serverOnly)
368 				hdrcnt = 10;
369 			else
370 				hdrcnt = 20;
371 		}
372 		if (clientOnly) {
373 		    printf("%s %6d %6d %6d %6d %6d %6d %6d %6d",
374 			((clientOnly && serverOnly) ? "Client:" : ""),
375 			DELTA(attrcache_hits) + DELTA(attrcache_misses),
376 			DELTA(lookupcache_hits) + DELTA(lookupcache_misses),
377 			DELTA(biocache_readlinks),
378 			DELTA(biocache_reads),
379 			DELTA(biocache_writes),
380 			nfsstats.rpccnt[NFSPROC_RENAME]-lastst.rpccnt[NFSPROC_RENAME],
381 			DELTA(accesscache_hits) + DELTA(accesscache_misses),
382 			DELTA(biocache_readdirs)
383 		    );
384 		    if (widemode) {
385 			    printf(" %s %s %s %s %s %s",
386 				sperc1(DELTA(attrcache_hits),
387 				    DELTA(attrcache_misses)),
388 				sperc1(DELTA(lookupcache_hits),
389 				    DELTA(lookupcache_misses)),
390 				sperc2(DELTA(biocache_reads),
391 				    DELTA(read_bios)),
392 				sperc2(DELTA(biocache_writes),
393 				    DELTA(write_bios)),
394 				sperc1(DELTA(accesscache_hits),
395 				    DELTA(accesscache_misses)),
396 				sperc2(DELTA(biocache_readdirs),
397 				    DELTA(readdir_bios))
398 			    );
399 		    }
400 		    printf("\n");
401 		}
402 		if (serverOnly) {
403 		    printf("%s %6d %6d %6d %6d %6d %6d %6d %6d",
404 			((clientOnly && serverOnly) ? "Server:" : ""),
405 			nfsstats.srvrpccnt[NFSPROC_GETATTR]-lastst.srvrpccnt[NFSPROC_GETATTR],
406 			nfsstats.srvrpccnt[NFSPROC_LOOKUP]-lastst.srvrpccnt[NFSPROC_LOOKUP],
407 			nfsstats.srvrpccnt[NFSPROC_READLINK]-lastst.srvrpccnt[NFSPROC_READLINK],
408 			nfsstats.srvrpccnt[NFSPROC_READ]-lastst.srvrpccnt[NFSPROC_READ],
409 			nfsstats.srvrpccnt[NFSPROC_WRITE]-lastst.srvrpccnt[NFSPROC_WRITE],
410 			nfsstats.srvrpccnt[NFSPROC_RENAME]-lastst.srvrpccnt[NFSPROC_RENAME],
411 			nfsstats.srvrpccnt[NFSPROC_ACCESS]-lastst.srvrpccnt[NFSPROC_ACCESS],
412 			(nfsstats.srvrpccnt[NFSPROC_READDIR]-lastst.srvrpccnt[NFSPROC_READDIR])
413 			+(nfsstats.srvrpccnt[NFSPROC_READDIRPLUS]-lastst.srvrpccnt[NFSPROC_READDIRPLUS]));
414 		    printf("\n");
415 		}
416 		lastst = nfsstats;
417 		fflush(stdout);
418 		sleep(interval);
419 	}
420 	/*NOTREACHED*/
421 }
422 
423 void
424 printhdr(int clientOnly, int serverOnly)
425 {
426 	printf("%s%6.6s %6.6s %6.6s %6.6s %6.6s %6.6s %6.6s %6.6s",
427 	    ((serverOnly && clientOnly) ? "        " : " "),
428 	    "GtAttr", "Lookup", "Rdlink", "Read", "Write", "Rename",
429 	    "Access", "Rddir");
430 	if (widemode && clientOnly) {
431 		printf(" Attr Lkup BioR BioW Accs BioD");
432 	}
433 	printf("\n");
434 	fflush(stdout);
435 }
436 
437 void
438 usage()
439 {
440 	(void)fprintf(stderr,
441 	    "usage: nfsstat [-csW] [-M core] [-N system] [-w interval]\n");
442 	exit(1);
443 }
444 
445 static char SPBuf[64][8];
446 static int SPIndex;
447 
448 char *
449 sperc1(int hits, int misses)
450 {
451 	char *p = SPBuf[SPIndex];
452 
453 	if (hits + misses) {
454 		sprintf(p, "%3d%%",
455 		    (int)(char)((quad_t)hits * 100 / (hits + misses)));
456 	} else {
457 		sprintf(p, "   -");
458 	}
459 	SPIndex = (SPIndex + 1) & 63;
460 	return(p);
461 }
462 
463 char *
464 sperc2(int ttl, int misses)
465 {
466 	char *p = SPBuf[SPIndex];
467 
468 	if (ttl) {
469 		sprintf(p, "%3d%%",
470 		    (int)(char)((quad_t)(ttl - misses) * 100 / ttl));
471 	} else {
472 		sprintf(p, "   -");
473 	}
474 	SPIndex = (SPIndex + 1) & 63;
475 	return(p);
476 }
477 
478