1 /* $NetBSD: nfsstat.c,v 1.23 2009/04/12 23:34:11 lukem 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\ 38 The Regents of the University of California. All rights reserved."); 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.23 2009/04/12 23:34:11 lukem 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", 0, 0, 0, 0 }, 74 { "", 0, 0, 0, 0 }, 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\n", 264 "pathconf", "commit"); 265 printf("%10d %2d%% %10d %2d%%\n", 266 RPCSTAT(nfsstats.rpccnt[NFSPROC_PATHCONF]), 267 RPCSTAT(nfsstats.rpccnt[NFSPROC_COMMIT])); 268 269 printf("RPC Info:\n"); 270 printf("%10s %14s %14s %14s %14s\n", 271 "timeout", "invalid", "unexpected", "retries", "requests"); 272 printf("%10d %14d %14d %14d %14d\n", 273 nfsstats.rpctimeouts, 274 nfsstats.rpcinvalid, 275 nfsstats.rpcunexpected, 276 nfsstats.rpcretries, 277 nfsstats.rpcrequests); 278 279 printf("Cache Info:\n"); 280 printf("%10s %14s %14s %14s %14s\n", 281 "attrcache", "lookupcache", "read", "write", "readlink"); 282 printf( 283 "%10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%%\n", 284 NUMPCT(nfsstats.attrcache_hits, 285 nfsstats.attrcache_misses), 286 NUMPCT(nfsstats.lookupcache_hits, 287 nfsstats.lookupcache_misses), 288 NUMPCT(nfsstats.biocache_reads - nfsstats.read_bios, 289 nfsstats.read_bios), 290 NUMPCT(nfsstats.biocache_writes - nfsstats.write_bios, 291 nfsstats.write_bios), 292 NUMPCT(nfsstats.biocache_readlinks - nfsstats.readlink_bios, 293 nfsstats.readlink_bios)); 294 printf("%10s %14s\n", 295 "readdir", "direofcache"); 296 printf("%10d %2d%% %10d %2d%%\n", 297 NUMPCT(nfsstats.biocache_readdirs - nfsstats.readdir_bios, 298 nfsstats.readdir_bios), 299 NUMPCT(nfsstats.direofcache_hits, 300 nfsstats.direofcache_misses)); 301 } 302 303 if (printall || (clientinfo && serverinfo)) 304 printf("\n"); 305 306 if (printall || serverinfo) { 307 total = 0; 308 for (i = 0; i < NFS_NPROCS; i++) 309 total += nfsstats.srvrpccnt[i]; 310 printf("Server Info:\n"); 311 printf("RPC Counts: (%lld call%s)\n", (long long)total, 312 total == 1 ? "" : "s"); 313 314 printf("%10s %14s %14s %14s %14s\n", 315 "null", "getattr", "setattr", "lookup", "access"); 316 printf( 317 "%10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%%\n", 318 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_NULL]), 319 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_GETATTR]), 320 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_SETATTR]), 321 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_LOOKUP]), 322 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_ACCESS])); 323 printf("%10s %14s %14s %14s %14s\n", 324 "readlink", "read", "write", "create", "mkdir"); 325 printf( 326 "%10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%%\n", 327 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_READLINK]), 328 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_READ]), 329 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_WRITE]), 330 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_CREATE]), 331 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_MKDIR])); 332 printf("%10s %14s %14s %14s %14s\n", 333 "symlink", "mknod", "remove", "rmdir", "rename"); 334 printf( 335 "%10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%%\n", 336 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_SYMLINK]), 337 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_MKNOD]), 338 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_REMOVE]), 339 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_RMDIR]), 340 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_RENAME])); 341 printf("%10s %14s %14s %14s %14s\n", 342 "link", "readdir", "readdirplus", "fsstat", "fsinfo"); 343 printf( 344 "%10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%% %10d %2d%%\n", 345 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_LINK]), 346 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_READDIR]), 347 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_READDIRPLUS]), 348 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_FSSTAT]), 349 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_FSINFO])); 350 printf("%10s %14s\n", 351 "pathconf", "commit"); 352 printf("%10d %2d%% %10d %2d%%\n", 353 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_PATHCONF]), 354 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_COMMIT])); 355 356 printf("Server Errors:\n"); 357 printf("%10s %14s\n", 358 "RPC errors", "faults"); 359 printf("%10d %14d\n", 360 nfsstats.srvrpc_errs, 361 nfsstats.srv_errs); 362 printf("Server Cache Stats:\n"); 363 printf("%10s %14s %14s %14s\n", 364 "inprogress", "idem", "non-idem", "misses"); 365 printf("%10d %14d %14d %14d\n", 366 nfsstats.srvcache_inproghits, 367 nfsstats.srvcache_idemdonehits, 368 nfsstats.srvcache_nonidemdonehits, 369 nfsstats.srvcache_misses); 370 printf("Server Write Gathering:\n"); 371 printf("%10s %14s %14s\n", 372 "writes", "write RPC", "OPs saved"); 373 printf("%10d %14d %14d %2d%%\n", 374 nfsstats.srvvop_writes, 375 nfsstats.srvrpccnt[NFSPROC_WRITE], 376 NUMPCT( 377 nfsstats.srvrpccnt[NFSPROC_WRITE]-nfsstats.srvvop_writes, 378 nfsstats.srvrpccnt[NFSPROC_WRITE])); 379 } 380 } 381 382 u_char signalled; /* set if alarm goes off "early" */ 383 384 /* 385 * Print a running summary of nfs statistics. 386 * Repeat display every interval seconds, showing statistics 387 * collected over that interval. Assumes that interval is non-zero. 388 * First line printed at top of screen is always cumulative. 389 */ 390 void 391 sidewaysintpr(interval) 392 u_int interval; 393 { 394 struct nfsstats nfsstats; 395 int hdrcnt, oldmask; 396 struct stats { 397 int client[NSHORTPROC]; 398 int server[NSHORTPROC]; 399 } current, last; 400 401 (void)signal(SIGALRM, catchalarm); 402 signalled = 0; 403 (void)alarm(interval); 404 memset(&last, 0, sizeof(last)); 405 406 for (hdrcnt = 1;;) { 407 size_t i; 408 409 if (!--hdrcnt) { 410 printhdr(); 411 hdrcnt = 20; 412 } 413 getstats(&nfsstats); 414 memset(¤t, 0, sizeof(current)); 415 for (i = 0; i < NSHORTPROC; i++) { 416 int mask = shortprocs[i].mask; 417 int idx; 418 419 while ((idx = ffs(mask)) != 0) { 420 idx--; 421 mask &= ~(1 << idx); 422 current.client[i] += nfsstats.rpccnt[idx]; 423 current.server[i] += nfsstats.srvrpccnt[idx]; 424 } 425 } 426 427 if (printall || clientinfo) { 428 printf("Client:"); 429 for (i = 0; i < NSHORTPROC; i++) 430 printf(" %7d", 431 current.client[i] - last.client[i]); 432 printf("\n"); 433 } 434 if (printall || serverinfo) { 435 printf("Server:"); 436 for (i = 0; i < NSHORTPROC; i++) 437 printf(" %7d", 438 current.server[i] - last.server[i]); 439 printf("\n"); 440 } 441 memcpy(&last, ¤t, sizeof(last)); 442 fflush(stdout); 443 oldmask = sigblock(sigmask(SIGALRM)); 444 if (!signalled) 445 sigpause(0); 446 sigsetmask(oldmask); 447 signalled = 0; 448 (void)alarm(interval); 449 } 450 /*NOTREACHED*/ 451 } 452 453 void 454 printhdr() 455 { 456 size_t i; 457 458 printf(" "); 459 for (i = 0; i < NSHORTPROC; i++) 460 printf("%7.7s ", shortprocs[i].name); 461 printf("\n"); 462 fflush(stdout); 463 } 464 465 /* 466 * Called if an interval expires before sidewaysintpr has completed a loop. 467 * Sets a flag to not wait for the alarm. 468 */ 469 void 470 catchalarm(dummy) 471 int dummy; 472 { 473 474 signalled = 1; 475 } 476 477 void 478 usage() 479 { 480 481 (void)fprintf(stderr, 482 "usage: nfsstat [-cs] [-M core] [-N system] [-w interval]\n"); 483 exit(1); 484 } 485