1 /* $NetBSD: nfsstat.c,v 1.19 2004/08/26 13:29:05 yamt Exp $ */ 2 3 /* 4 * Copyright (c) 1983, 1989, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Rick Macklem at The University of Guelph. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> 36 #ifndef lint 37 __COPYRIGHT("@(#) Copyright (c) 1983, 1989, 1993\n\ 38 The Regents of the University of California. All rights reserved.\n"); 39 #endif /* not lint */ 40 41 #ifndef lint 42 #if 0 43 static char sccsid[] = "from: @(#)nfsstat.c 8.1 (Berkeley) 6/6/93"; 44 #else 45 __RCSID("$NetBSD: nfsstat.c,v 1.19 2004/08/26 13:29:05 yamt Exp $"); 46 #endif 47 #endif /* not lint */ 48 49 #include <sys/param.h> 50 #include <sys/mount.h> 51 #include <sys/sysctl.h> 52 53 #include <nfs/rpcv2.h> 54 #include <nfs/nfsproto.h> 55 #include <nfs/nfs.h> 56 57 #include <ctype.h> 58 #include <err.h> 59 #include <errno.h> 60 #include <fcntl.h> 61 #include <kvm.h> 62 #include <limits.h> 63 #include <nlist.h> 64 #include <paths.h> 65 #include <signal.h> 66 #include <stdlib.h> 67 #include <stdio.h> 68 #include <string.h> 69 #include <unistd.h> 70 71 struct nlist nl[] = { 72 #define N_NFSSTAT 0 73 { "_nfsstats" }, 74 { "" }, 75 }; 76 77 #define MASK(a) (1 << NFSPROC_##a) 78 #define ALLMASK \ 79 (MASK(GETATTR) | MASK(SETATTR) | MASK(LOOKUP) | MASK(READ) | \ 80 MASK(WRITE) | MASK(RENAME)| MASK(ACCESS) | MASK(READDIR) | \ 81 MASK(READDIRPLUS)) 82 #define OTHERMASK (((1 << NFS_NPROCS) - 1) & ~ALLMASK) 83 const struct shortprocs { 84 int mask; 85 const char *name; 86 } shortprocs[] = { 87 {MASK(GETATTR), "Getattr"}, 88 {MASK(SETATTR), "Setattr"}, 89 {MASK(LOOKUP), "Lookup"}, 90 {MASK(READ), "Read"}, 91 {MASK(WRITE), "Write"}, 92 {MASK(RENAME), "Rename"}, 93 {MASK(ACCESS), "Access"}, 94 {MASK(READDIR) | MASK(READDIRPLUS), "Readdir"}, 95 {OTHERMASK, "Others"}, 96 }; 97 98 #define NSHORTPROC (sizeof(shortprocs)/sizeof(shortprocs[0])) 99 100 void catchalarm __P((int)); 101 void getstats __P((struct nfsstats *)); 102 void intpr __P((void)); 103 int main __P((int, char **)); 104 void printhdr __P((void)); 105 void sidewaysintpr __P((u_int)); 106 void usage __P((void)); 107 108 kvm_t *kd; 109 int printall, clientinfo, serverinfo; 110 u_long nfsstataddr; 111 112 int 113 main(argc, argv) 114 int argc; 115 char **argv; 116 { 117 u_int interval; 118 int ch; 119 char *memf, *nlistf; 120 char errbuf[_POSIX2_LINE_MAX]; 121 122 interval = 0; 123 memf = nlistf = NULL; 124 printall = 1; 125 while ((ch = getopt(argc, argv, "M:N:w:cs")) != -1) 126 switch(ch) { 127 case 'M': 128 memf = optarg; 129 break; 130 case 'N': 131 nlistf = optarg; 132 break; 133 case 'w': 134 interval = atoi(optarg); 135 break; 136 case 's': 137 serverinfo = 1; 138 printall = 0; 139 break; 140 case 'c': 141 clientinfo = 1; 142 printall = 0; 143 break; 144 case '?': 145 default: 146 usage(); 147 } 148 argc -= optind; 149 argv += optind; 150 151 #define BACKWARD_COMPATIBILITY 152 #ifdef BACKWARD_COMPATIBILITY 153 if (*argv) { 154 interval = atoi(*argv); 155 if (*++argv) { 156 nlistf = *argv; 157 if (*++argv) 158 memf = *argv; 159 } 160 } 161 #endif 162 if (nlistf || memf) { 163 if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) 164 == 0) 165 errx(1, "kvm_openfiles: %s", errbuf); 166 167 if (kvm_nlist(kd, nl) != 0) 168 errx(1, "kvm_nlist: can't get names"); 169 nfsstataddr = nl[N_NFSSTAT].n_value; 170 } else { 171 kd = NULL; 172 } 173 174 if (interval) 175 sidewaysintpr(interval); 176 else 177 intpr(); 178 exit(0); 179 } 180 181 void 182 getstats(ns) 183 struct nfsstats *ns; 184 { 185 size_t size; 186 int mib[3]; 187 188 if (kd) { 189 if (kvm_read(kd, (u_long)nfsstataddr, ns, sizeof(*ns)) 190 != sizeof(*ns)) 191 errx(1, "kvm_read failed"); 192 } else { 193 mib[0] = CTL_VFS; 194 mib[1] = 2; /* XXX from CTL_VFS_NAMES in <sys/mount.h> */ 195 mib[2] = NFS_NFSSTATS; 196 197 size = sizeof(*ns); 198 if (sysctl(mib, 3, ns, &size, NULL, 0) == -1) 199 err(1, "sysctl(NFS_NFSSTATS) failed"); 200 } 201 } 202 203 /* 204 * Print a description of the nfs stats. 205 */ 206 void 207 intpr() 208 { 209 struct nfsstats nfsstats; 210 int64_t total; 211 int i; 212 213 #define PCT(x,y) ((y) == 0 ? 0 : (int)((int64_t)(x) * 100 / (y))) 214 #define NUMPCT(x,y) (x), PCT(x, (x)+(y)) 215 #define RPCSTAT(x) (x), PCT(x, total) 216 217 getstats(&nfsstats); 218 219 if (printall || clientinfo) { 220 total = 0; 221 for (i = 0; i < NFS_NPROCS; i++) 222 total += nfsstats.rpccnt[i]; 223 printf("Client Info:\n"); 224 printf("RPC Counts: (%lld call%s)\n", (long long)total, 225 total == 1 ? "" : "s"); 226 227 printf("%10s %14s %14s %14s %14s\n", 228 "null", "getattr", "setattr", "lookup", "access"); 229 printf( 230 "%10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%%\n", 231 RPCSTAT(nfsstats.rpccnt[NFSPROC_NULL]), 232 RPCSTAT(nfsstats.rpccnt[NFSPROC_GETATTR]), 233 RPCSTAT(nfsstats.rpccnt[NFSPROC_SETATTR]), 234 RPCSTAT(nfsstats.rpccnt[NFSPROC_LOOKUP]), 235 RPCSTAT(nfsstats.rpccnt[NFSPROC_ACCESS])); 236 printf("%10s %14s %14s %14s %14s\n", 237 "readlink", "read", "write", "create", "mkdir"); 238 printf( 239 "%10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%%\n", 240 RPCSTAT(nfsstats.rpccnt[NFSPROC_READLINK]), 241 RPCSTAT(nfsstats.rpccnt[NFSPROC_READ]), 242 RPCSTAT(nfsstats.rpccnt[NFSPROC_WRITE]), 243 RPCSTAT(nfsstats.rpccnt[NFSPROC_CREATE]), 244 RPCSTAT(nfsstats.rpccnt[NFSPROC_MKDIR])); 245 printf("%10s %14s %14s %14s %14s\n", 246 "symlink", "mknod", "remove", "rmdir", "rename"); 247 printf( 248 "%10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%%\n", 249 RPCSTAT(nfsstats.rpccnt[NFSPROC_SYMLINK]), 250 RPCSTAT(nfsstats.rpccnt[NFSPROC_MKNOD]), 251 RPCSTAT(nfsstats.rpccnt[NFSPROC_REMOVE]), 252 RPCSTAT(nfsstats.rpccnt[NFSPROC_RMDIR]), 253 RPCSTAT(nfsstats.rpccnt[NFSPROC_RENAME])); 254 printf("%10s %14s %14s %14s %14s\n", 255 "link", "readdir", "readdirplus", "fsstat", "fsinfo"); 256 printf( 257 "%10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%%\n", 258 RPCSTAT(nfsstats.rpccnt[NFSPROC_LINK]), 259 RPCSTAT(nfsstats.rpccnt[NFSPROC_READDIR]), 260 RPCSTAT(nfsstats.rpccnt[NFSPROC_READDIRPLUS]), 261 RPCSTAT(nfsstats.rpccnt[NFSPROC_FSSTAT]), 262 RPCSTAT(nfsstats.rpccnt[NFSPROC_FSINFO])); 263 printf("%10s %14s %14s %14s %14s\n", 264 "pathconf", "commit", "getlease", "vacated", "evicted"); 265 printf( 266 "%10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%%\n", 267 RPCSTAT(nfsstats.rpccnt[NFSPROC_PATHCONF]), 268 RPCSTAT(nfsstats.rpccnt[NFSPROC_COMMIT]), 269 RPCSTAT(nfsstats.rpccnt[NQNFSPROC_GETLEASE]), 270 RPCSTAT(nfsstats.rpccnt[NQNFSPROC_VACATED]), 271 RPCSTAT(nfsstats.rpccnt[NQNFSPROC_EVICTED])); 272 printf("%10s\n", "noop"); 273 printf("%10d %2d%%\n", 274 RPCSTAT(nfsstats.rpccnt[NFSPROC_NOOP])); 275 276 printf("RPC Info:\n"); 277 printf("%10s %14s %14s %14s %14s\n", 278 "timeout", "invalid", "unexpected", "retries", "requests"); 279 printf("%10d %14d %14d %14d %14d\n", 280 nfsstats.rpctimeouts, 281 nfsstats.rpcinvalid, 282 nfsstats.rpcunexpected, 283 nfsstats.rpcretries, 284 nfsstats.rpcrequests); 285 286 printf("Cache Info:\n"); 287 printf("%10s %14s %14s %14s %14s\n", 288 "attrcache", "lookupcache", "read", "write", "readlink"); 289 printf( 290 "%10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%%\n", 291 NUMPCT(nfsstats.attrcache_hits, 292 nfsstats.attrcache_misses), 293 NUMPCT(nfsstats.lookupcache_hits, 294 nfsstats.lookupcache_misses), 295 NUMPCT(nfsstats.biocache_reads - nfsstats.read_bios, 296 nfsstats.read_bios), 297 NUMPCT(nfsstats.biocache_writes - nfsstats.write_bios, 298 nfsstats.write_bios), 299 NUMPCT(nfsstats.biocache_readlinks - nfsstats.readlink_bios, 300 nfsstats.readlink_bios)); 301 printf("%10s %14s\n", 302 "readdir", "direofcache"); 303 printf("%10d %2d%% %10d %2d%%\n", 304 NUMPCT(nfsstats.biocache_readdirs - nfsstats.readdir_bios, 305 nfsstats.readdir_bios), 306 NUMPCT(nfsstats.direofcache_hits, 307 nfsstats.direofcache_misses)); 308 } 309 310 if (printall || (clientinfo && serverinfo)) 311 printf("\n"); 312 313 if (printall || serverinfo) { 314 total = 0; 315 for (i = 0; i < NFS_NPROCS; i++) 316 total += nfsstats.srvrpccnt[i]; 317 printf("Server Info:\n"); 318 printf("RPC Counts: (%lld call%s)\n", (long long)total, 319 total == 1 ? "" : "s"); 320 321 printf("%10s %14s %14s %14s %14s\n", 322 "null", "getattr", "setattr", "lookup", "access"); 323 printf( 324 "%10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%%\n", 325 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_NULL]), 326 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_GETATTR]), 327 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_SETATTR]), 328 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_LOOKUP]), 329 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_ACCESS])); 330 printf("%10s %14s %14s %14s %14s\n", 331 "readlink", "read", "write", "create", "mkdir"); 332 printf( 333 "%10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%%\n", 334 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_READLINK]), 335 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_READ]), 336 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_WRITE]), 337 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_CREATE]), 338 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_MKDIR])); 339 printf("%10s %14s %14s %14s %14s\n", 340 "symlink", "mknod", "remove", "rmdir", "rename"); 341 printf( 342 "%10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%%\n", 343 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_SYMLINK]), 344 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_MKNOD]), 345 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_REMOVE]), 346 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_RMDIR]), 347 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_RENAME])); 348 printf("%10s %14s %14s %14s %14s\n", 349 "link", "readdir", "readdirplus", "fsstat", "fsinfo"); 350 printf( 351 "%10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%%\n", 352 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_LINK]), 353 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_READDIR]), 354 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_READDIRPLUS]), 355 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_FSSTAT]), 356 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_FSINFO])); 357 printf("%10s %14s %14s %14s %14s\n", 358 "pathconf", "commit", "getlease", "vacated", "evicted"); 359 printf( 360 "%10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%%\n", 361 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_PATHCONF]), 362 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_COMMIT]), 363 RPCSTAT(nfsstats.srvrpccnt[NQNFSPROC_GETLEASE]), 364 RPCSTAT(nfsstats.srvrpccnt[NQNFSPROC_VACATED]), 365 RPCSTAT(nfsstats.srvrpccnt[NQNFSPROC_EVICTED])); 366 printf("%10s\n", "noop"); 367 printf("%10d %2d%%\n", 368 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_NOOP])); 369 370 printf("Server Errors:\n"); 371 printf("%10s %14s\n", 372 "RPC errors", "faults"); 373 printf("%10d %14d\n", 374 nfsstats.srvrpc_errs, 375 nfsstats.srv_errs); 376 printf("Server Cache Stats:\n"); 377 printf("%10s %14s %14s %14s\n", 378 "inprogress", "idem", "non-idem", "misses"); 379 printf("%10d %14d %14d %14d\n", 380 nfsstats.srvcache_inproghits, 381 nfsstats.srvcache_idemdonehits, 382 nfsstats.srvcache_nonidemdonehits, 383 nfsstats.srvcache_misses); 384 printf("Server Lease Stats:\n"); 385 printf("%10s %14s %14s\n", 386 "leases", "maxleases", "getleases"); 387 printf("%10d %14d %14d\n", 388 nfsstats.srvnqnfs_leases, 389 nfsstats.srvnqnfs_maxleases, 390 nfsstats.srvnqnfs_getleases); 391 printf("Server Write Gathering:\n"); 392 printf("%10s %14s %14s\n", 393 "writes", "write RPC", "OPs saved"); 394 printf("%10d %14d %14d %2d%%\n", 395 nfsstats.srvvop_writes, 396 nfsstats.srvrpccnt[NFSPROC_WRITE], 397 NUMPCT( 398 nfsstats.srvrpccnt[NFSPROC_WRITE]-nfsstats.srvvop_writes, 399 nfsstats.srvrpccnt[NFSPROC_WRITE])); 400 } 401 } 402 403 u_char signalled; /* set if alarm goes off "early" */ 404 405 /* 406 * Print a running summary of nfs statistics. 407 * Repeat display every interval seconds, showing statistics 408 * collected over that interval. Assumes that interval is non-zero. 409 * First line printed at top of screen is always cumulative. 410 */ 411 void 412 sidewaysintpr(interval) 413 u_int interval; 414 { 415 struct nfsstats nfsstats; 416 int hdrcnt, oldmask; 417 struct stats { 418 int client[NSHORTPROC]; 419 int server[NSHORTPROC]; 420 } current, last; 421 422 (void)signal(SIGALRM, catchalarm); 423 signalled = 0; 424 (void)alarm(interval); 425 memset(&last, 0, sizeof(last)); 426 427 for (hdrcnt = 1;;) { 428 int i; 429 430 if (!--hdrcnt) { 431 printhdr(); 432 hdrcnt = 20; 433 } 434 getstats(&nfsstats); 435 memset(¤t, 0, sizeof(current)); 436 for (i = 0; i < NSHORTPROC; i++) { 437 int mask = shortprocs[i].mask; 438 int idx; 439 440 while ((idx = ffs(mask)) != 0) { 441 idx--; 442 mask &= ~(1 << idx); 443 current.client[i] += nfsstats.rpccnt[idx]; 444 current.server[i] += nfsstats.srvrpccnt[idx]; 445 } 446 } 447 448 if (printall || clientinfo) { 449 printf("Client:"); 450 for (i = 0; i < NSHORTPROC; i++) 451 printf(" %7d", 452 current.client[i] - last.client[i]); 453 printf("\n"); 454 } 455 if (printall || serverinfo) { 456 printf("Server:"); 457 for (i = 0; i < NSHORTPROC; i++) 458 printf(" %7d", 459 current.server[i] - last.server[i]); 460 printf("\n"); 461 } 462 memcpy(&last, ¤t, sizeof(last)); 463 fflush(stdout); 464 oldmask = sigblock(sigmask(SIGALRM)); 465 if (!signalled) 466 sigpause(0); 467 sigsetmask(oldmask); 468 signalled = 0; 469 (void)alarm(interval); 470 } 471 /*NOTREACHED*/ 472 } 473 474 void 475 printhdr() 476 { 477 int i; 478 479 printf(" "); 480 for (i = 0; i < NSHORTPROC; i++) 481 printf("%7.7s ", shortprocs[i].name); 482 printf("\n"); 483 fflush(stdout); 484 } 485 486 /* 487 * Called if an interval expires before sidewaysintpr has completed a loop. 488 * Sets a flag to not wait for the alarm. 489 */ 490 void 491 catchalarm(dummy) 492 int dummy; 493 { 494 495 signalled = 1; 496 } 497 498 void 499 usage() 500 { 501 502 (void)fprintf(stderr, 503 "usage: nfsstat [-cs] [-M core] [-N system] [-w interval]\n"); 504 exit(1); 505 } 506