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