1 /* $NetBSD: nfsstat.c,v 1.25 2014/04/24 18:40:35 christos 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.25 2014/04/24 18:40:35 christos 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 static 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 static 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 static void catchalarm(int); 101 static void getstats(struct nfsstats *); 102 static void intpr(void); 103 static void printhdr(void); 104 __dead static void sidewaysintpr(u_int); 105 __dead static void usage(void); 106 107 static kvm_t *kd; 108 static int printall, clientinfo, serverinfo; 109 static u_long nfsstataddr; 110 111 int 112 main(int argc, char **argv) 113 { 114 u_int interval; 115 int ch; 116 char *memf, *nlistf; 117 char errbuf[_POSIX2_LINE_MAX]; 118 119 interval = 0; 120 memf = nlistf = NULL; 121 printall = 1; 122 while ((ch = getopt(argc, argv, "M:N:w:cs")) != -1) 123 switch(ch) { 124 case 'M': 125 memf = optarg; 126 break; 127 case 'N': 128 nlistf = optarg; 129 break; 130 case 'w': 131 interval = atoi(optarg); 132 break; 133 case 's': 134 serverinfo = 1; 135 printall = 0; 136 break; 137 case 'c': 138 clientinfo = 1; 139 printall = 0; 140 break; 141 case '?': 142 default: 143 usage(); 144 } 145 argc -= optind; 146 argv += optind; 147 148 #define BACKWARD_COMPATIBILITY 149 #ifdef BACKWARD_COMPATIBILITY 150 if (*argv) { 151 interval = atoi(*argv); 152 if (*++argv) { 153 nlistf = *argv; 154 if (*++argv) 155 memf = *argv; 156 } 157 } 158 #endif 159 if (nlistf || memf) { 160 if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) 161 == 0) 162 errx(1, "kvm_openfiles: %s", errbuf); 163 164 if (kvm_nlist(kd, nl) != 0) 165 errx(1, "kvm_nlist: can't get names"); 166 nfsstataddr = nl[N_NFSSTAT].n_value; 167 } else { 168 kd = NULL; 169 } 170 171 if (interval) 172 sidewaysintpr(interval); 173 else 174 intpr(); 175 exit(0); 176 } 177 178 static void 179 getstats(struct nfsstats *ns) 180 { 181 size_t size; 182 int mib[3]; 183 184 if (kd) { 185 if (kvm_read(kd, (u_long)nfsstataddr, ns, sizeof(*ns)) 186 != sizeof(*ns)) 187 errx(1, "kvm_read failed"); 188 } else { 189 mib[0] = CTL_VFS; 190 mib[1] = 2; /* XXX from CTL_VFS_NAMES in <sys/mount.h> */ 191 mib[2] = NFS_NFSSTATS; 192 193 size = sizeof(*ns); 194 if (sysctl(mib, 3, ns, &size, NULL, 0) == -1) 195 err(1, "sysctl(NFS_NFSSTATS) failed"); 196 } 197 } 198 199 /* 200 * Print a description of the nfs stats. 201 */ 202 static void 203 intpr(void) 204 { 205 struct nfsstats nfsstats; 206 uint64_t total; 207 int i; 208 209 #define PCT(x,y) ((y) == 0 ? 0 : (int)((int64_t)(x) * 100 / (y))) 210 #define NUMPCT(x,y) (x), PCT(x, (x)+(y)) 211 #define RPCSTAT(x) (x), PCT(x, total) 212 213 getstats(&nfsstats); 214 215 if (printall || clientinfo) { 216 total = 0; 217 for (i = 0; i < NFS_NPROCS; i++) 218 total += nfsstats.rpccnt[i]; 219 printf("Client Info:\n"); 220 printf("RPC Counts: (%" PRIu64 " call%s)\n", total, 221 total == 1 ? "" : "s"); 222 223 printf("%10s %14s %14s %14s %14s\n", 224 "null", "getattr", "setattr", "lookup", "access"); 225 printf( 226 "%10u %2u%% %10u %2u%% %10u %2u%% %10u %2u%% %10u %2u%%\n", 227 RPCSTAT(nfsstats.rpccnt[NFSPROC_NULL]), 228 RPCSTAT(nfsstats.rpccnt[NFSPROC_GETATTR]), 229 RPCSTAT(nfsstats.rpccnt[NFSPROC_SETATTR]), 230 RPCSTAT(nfsstats.rpccnt[NFSPROC_LOOKUP]), 231 RPCSTAT(nfsstats.rpccnt[NFSPROC_ACCESS])); 232 printf("%10s %14s %14s %14s %14s\n", 233 "readlink", "read", "write", "create", "mkdir"); 234 printf( 235 "%10u %2u%% %10u %2u%% %10u %2u%% %10u %2u%% %10u %2u%%\n", 236 RPCSTAT(nfsstats.rpccnt[NFSPROC_READLINK]), 237 RPCSTAT(nfsstats.rpccnt[NFSPROC_READ]), 238 RPCSTAT(nfsstats.rpccnt[NFSPROC_WRITE]), 239 RPCSTAT(nfsstats.rpccnt[NFSPROC_CREATE]), 240 RPCSTAT(nfsstats.rpccnt[NFSPROC_MKDIR])); 241 printf("%10s %14s %14s %14s %14s\n", 242 "symlink", "mknod", "remove", "rmdir", "rename"); 243 printf( 244 "%10u %2u%% %10u %2u%% %10u %2u%% %10u %2u%% %10u %2u%%\n", 245 RPCSTAT(nfsstats.rpccnt[NFSPROC_SYMLINK]), 246 RPCSTAT(nfsstats.rpccnt[NFSPROC_MKNOD]), 247 RPCSTAT(nfsstats.rpccnt[NFSPROC_REMOVE]), 248 RPCSTAT(nfsstats.rpccnt[NFSPROC_RMDIR]), 249 RPCSTAT(nfsstats.rpccnt[NFSPROC_RENAME])); 250 printf("%10s %14s %14s %14s %14s\n", 251 "link", "readdir", "readdirplus", "fsstat", "fsinfo"); 252 printf( 253 "%10u %2u%% %10u %2u%% %10u %2u%% %10u %2u%% %10u %2u%%\n", 254 RPCSTAT(nfsstats.rpccnt[NFSPROC_LINK]), 255 RPCSTAT(nfsstats.rpccnt[NFSPROC_READDIR]), 256 RPCSTAT(nfsstats.rpccnt[NFSPROC_READDIRPLUS]), 257 RPCSTAT(nfsstats.rpccnt[NFSPROC_FSSTAT]), 258 RPCSTAT(nfsstats.rpccnt[NFSPROC_FSINFO])); 259 printf("%10s %14s\n", 260 "pathconf", "commit"); 261 printf("%10u %2u%% %10u %2u%%\n", 262 RPCSTAT(nfsstats.rpccnt[NFSPROC_PATHCONF]), 263 RPCSTAT(nfsstats.rpccnt[NFSPROC_COMMIT])); 264 265 printf("RPC Info:\n"); 266 printf("%10s %14s %14s %14s %14s\n", 267 "timeout", "invalid", "unexpected", "retries", "requests"); 268 printf("%10u %14u %14u %14u %14u\n", 269 nfsstats.rpctimeouts, 270 nfsstats.rpcinvalid, 271 nfsstats.rpcunexpected, 272 nfsstats.rpcretries, 273 nfsstats.rpcrequests); 274 275 printf("Cache Info:\n"); 276 printf("%10s %14s %14s %14s %14s\n", 277 "attrcache", "lookupcache", "read", "write", "readlink"); 278 printf( 279 "%10u %2u%% %10u %2u%% %10u %2u%% %10u %2u%% %10u %2u%%\n", 280 NUMPCT(nfsstats.attrcache_hits, 281 nfsstats.attrcache_misses), 282 NUMPCT(nfsstats.lookupcache_hits, 283 nfsstats.lookupcache_misses), 284 NUMPCT(nfsstats.biocache_reads - nfsstats.read_bios, 285 nfsstats.read_bios), 286 NUMPCT(nfsstats.biocache_writes - nfsstats.write_bios, 287 nfsstats.write_bios), 288 NUMPCT(nfsstats.biocache_readlinks - nfsstats.readlink_bios, 289 nfsstats.readlink_bios)); 290 printf("%10s %14s\n", 291 "readdir", "direofcache"); 292 printf("%10u %2u%% %10u %2u%%\n", 293 NUMPCT(nfsstats.biocache_readdirs - nfsstats.readdir_bios, 294 nfsstats.readdir_bios), 295 NUMPCT(nfsstats.direofcache_hits, 296 nfsstats.direofcache_misses)); 297 } 298 299 if (printall || (clientinfo && serverinfo)) 300 printf("\n"); 301 302 if (printall || serverinfo) { 303 total = 0; 304 for (i = 0; i < NFS_NPROCS; i++) 305 total += nfsstats.srvrpccnt[i]; 306 printf("Server Info:\n"); 307 printf("RPC Counts: (%" PRIu64 " call%s)\n", total, 308 total == 1 ? "" : "s"); 309 310 printf("%10s %14s %14s %14s %14s\n", 311 "null", "getattr", "setattr", "lookup", "access"); 312 printf( 313 "%10u %2u%% %10u %2u%% %10u %2u%% %10u %2u%% %10u %2u%%\n", 314 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_NULL]), 315 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_GETATTR]), 316 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_SETATTR]), 317 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_LOOKUP]), 318 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_ACCESS])); 319 printf("%10s %14s %14s %14s %14s\n", 320 "readlink", "read", "write", "create", "mkdir"); 321 printf( 322 "%10u %2u%% %10u %2u%% %10u %2u%% %10u %2u%% %10u %2u%%\n", 323 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_READLINK]), 324 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_READ]), 325 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_WRITE]), 326 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_CREATE]), 327 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_MKDIR])); 328 printf("%10s %14s %14s %14s %14s\n", 329 "symlink", "mknod", "remove", "rmdir", "rename"); 330 printf( 331 "%10u %2u%% %10u %2u%% %10u %2u%% %10u %2u%% %10u %2u%%\n", 332 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_SYMLINK]), 333 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_MKNOD]), 334 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_REMOVE]), 335 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_RMDIR]), 336 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_RENAME])); 337 printf("%10s %14s %14s %14s %14s\n", 338 "link", "readdir", "readdirplus", "fsstat", "fsinfo"); 339 printf( 340 "%10u %2u%% %10u %2u%% %10u %2u%% %10u %2u%% %10u %2u%%\n", 341 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_LINK]), 342 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_READDIR]), 343 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_READDIRPLUS]), 344 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_FSSTAT]), 345 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_FSINFO])); 346 printf("%10s %14s\n", 347 "pathconf", "commit"); 348 printf("%10u %2u%% %10u %2u%%\n", 349 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_PATHCONF]), 350 RPCSTAT(nfsstats.srvrpccnt[NFSPROC_COMMIT])); 351 352 printf("Server Errors:\n"); 353 printf("%10s %14s\n", 354 "RPC errors", "faults"); 355 printf("%10u %14u\n", 356 nfsstats.srvrpc_errs, 357 nfsstats.srv_errs); 358 printf("Server Cache Stats:\n"); 359 printf("%10s %14s %14s %14s\n", 360 "inprogress", "idem", "non-idem", "misses"); 361 printf("%10u %14u %14u %14u\n", 362 nfsstats.srvcache_inproghits, 363 nfsstats.srvcache_idemdonehits, 364 nfsstats.srvcache_nonidemdonehits, 365 nfsstats.srvcache_misses); 366 printf("Server Write Gathering:\n"); 367 printf("%10s %14s %14s\n", 368 "writes", "write RPC", "OPs saved"); 369 printf("%10u %14u %14u %2u%%\n", 370 nfsstats.srvvop_writes, 371 nfsstats.srvrpccnt[NFSPROC_WRITE], 372 NUMPCT( 373 nfsstats.srvrpccnt[NFSPROC_WRITE]-nfsstats.srvvop_writes, 374 nfsstats.srvrpccnt[NFSPROC_WRITE])); 375 } 376 } 377 378 static u_char signalled; /* set if alarm goes off "early" */ 379 380 /* 381 * Print a running summary of nfs statistics. 382 * Repeat display every interval seconds, showing statistics 383 * collected over that interval. Assumes that interval is non-zero. 384 * First line printed at top of screen is always cumulative. 385 */ 386 static void 387 sidewaysintpr(u_int interval) 388 { 389 struct nfsstats nfsstats; 390 int hdrcnt, oldmask; 391 struct stats { 392 int client[NSHORTPROC]; 393 int server[NSHORTPROC]; 394 } current, last; 395 396 (void)signal(SIGALRM, catchalarm); 397 signalled = 0; 398 (void)alarm(interval); 399 memset(&last, 0, sizeof(last)); 400 401 for (hdrcnt = 1;;) { 402 size_t i; 403 404 if (!--hdrcnt) { 405 printhdr(); 406 hdrcnt = 20; 407 } 408 getstats(&nfsstats); 409 memset(¤t, 0, sizeof(current)); 410 for (i = 0; i < NSHORTPROC; i++) { 411 int mask = shortprocs[i].mask; 412 int idx; 413 414 while ((idx = ffs(mask)) != 0) { 415 idx--; 416 mask &= ~(1 << idx); 417 current.client[i] += nfsstats.rpccnt[idx]; 418 current.server[i] += nfsstats.srvrpccnt[idx]; 419 } 420 } 421 422 if (printall || clientinfo) { 423 printf("Client:"); 424 for (i = 0; i < NSHORTPROC; i++) 425 printf(" %7u", 426 current.client[i] - last.client[i]); 427 printf("\n"); 428 } 429 if (printall || serverinfo) { 430 printf("Server:"); 431 for (i = 0; i < NSHORTPROC; i++) 432 printf(" %7u", 433 current.server[i] - last.server[i]); 434 printf("\n"); 435 } 436 memcpy(&last, ¤t, sizeof(last)); 437 fflush(stdout); 438 oldmask = sigblock(sigmask(SIGALRM)); 439 if (!signalled) 440 sigpause(0); 441 sigsetmask(oldmask); 442 signalled = 0; 443 (void)alarm(interval); 444 } 445 /*NOTREACHED*/ 446 } 447 448 static void 449 printhdr(void) 450 { 451 size_t i; 452 453 printf(" "); 454 for (i = 0; i < NSHORTPROC; i++) 455 printf("%7.7s ", shortprocs[i].name); 456 printf("\n"); 457 fflush(stdout); 458 } 459 460 /* 461 * Called if an interval expires before sidewaysintpr has completed a loop. 462 * Sets a flag to not wait for the alarm. 463 */ 464 static void 465 catchalarm(int dummy) 466 { 467 468 signalled = 1; 469 } 470 471 static void 472 usage(void) 473 { 474 475 (void)fprintf(stderr, 476 "Usage: %s [-cs] [-M core] [-N system] [-w interval]\n", 477 getprogname()); 478 exit(1); 479 } 480