2 * nfsstat.c Output NFS statistics
4 * Copyright (C) 1995-2005 Olaf Kirch <okir@suse.de>
11 #define NFSSVCSTAT "/proc/net/rpc/nfsd"
12 #define NFSCLTSTAT "/proc/net/rpc/nfs"
14 #define MOUNTSFILE "/proc/mounts"
26 static unsigned int svcv2info[20]; /* NFSv2 call counts ([0] == 18) */
27 static unsigned int cltv2info[20]; /* NFSv2 call counts ([0] == 18) */
28 static unsigned int svcv3info[24]; /* NFSv3 call counts ([0] == 22) */
29 static unsigned int cltv3info[24]; /* NFSv3 call counts ([0] == 22) */
30 static unsigned int svcv4info[4]; /* NFSv4 call counts ([0] == 2) */
31 static unsigned int cltv4info[34]; /* NFSv4 call counts ([0] == 32) */
32 static unsigned int svcv4opinfo[42];/* NFSv4 call counts ([0] == 40) */
33 static unsigned int svcnetinfo[5]; /* 0 # of received packets
38 static unsigned int cltnetinfo[5]; /* 0 # of received packets
44 static unsigned int svcrpcinfo[6]; /* 0 total # of RPC calls
45 * 1 total # of bad calls
47 * 3 authentication failed
50 static unsigned int cltrpcinfo[4]; /* 0 total # of RPC calls
51 * 1 retransmitted calls
55 static unsigned int svcrcinfo[9]; /* 0 repcache hits
58 * (for pre-2.4 kernels:)
61 * 5 noncached non-directories
62 * 6 noncached directories
66 static unsigned int svcfhinfo[7]; /* (for kernels >= 2.4.0)
70 * 3 noncached directories
71 * 4 noncached non-directories
72 * leave hole to relocate stale for order
76 static const char * nfsv2name[18] = {
77 "null", "getattr", "setattr", "root", "lookup", "readlink",
78 "read", "wrcache", "write", "create", "remove", "rename",
79 "link", "symlink", "mkdir", "rmdir", "readdir", "fsstat"
82 static const char * nfsv3name[22] = {
83 "null", "getattr", "setattr", "lookup", "access", "readlink",
84 "read", "write", "create", "mkdir", "symlink", "mknod",
85 "remove", "rmdir", "rename", "link", "readdir", "readdirplus",
86 "fsstat", "fsinfo", "pathconf", "commit"
89 static const char * nfssvrv4name[2] = {
94 static const char * nfscltv4name[32] = {
95 "null", "read", "write", "commit", "open", "open_conf",
96 "open_noat", "open_dgrd", "close", "setattr", "fsinfo", "renew",
97 "setclntid", "confirm", "lock",
98 "lockt", "locku", "access", "getattr", "lookup", "lookup_root",
99 "remove", "rename", "link", "symlink", "create", "pathconf",
100 "statfs", "readlink", "readdir", "server_caps", "delegreturn",
103 static const char * nfssvrv4opname[40] = {
104 "op0-unused", "op1-unused", "op2-future", "access", "close", "commit",
105 "create", "delegpurge", "delegreturn", "getattr", "getfh", "link",
106 "lock", "lockt", "locku", "lookup", "lookup_root", "nverify",
107 "open", "openattr", "open_conf", "open_dgrd", "putfh", "putpubfh",
108 "putrootfh", "read", "readdir", "readlink", "remove", "rename",
109 "renew", "restorefh", "savefh", "secinfo", "setattr", "setcltid",
110 "setcltidconf", "verify", "write", "rellockowner"
113 typedef struct statinfo {
116 unsigned int * valptr;
119 #define STRUCTSIZE(x) sizeof(x)/sizeof(*x)
121 static statinfo svcinfo[] = {
122 { "net", STRUCTSIZE(svcnetinfo), svcnetinfo },
123 { "rpc", STRUCTSIZE(svcrpcinfo), svcrpcinfo },
124 { "rc", STRUCTSIZE(svcrcinfo), svcrcinfo },
125 { "fh", STRUCTSIZE(svcfhinfo), svcfhinfo },
126 { "proc2", STRUCTSIZE(svcv2info), svcv2info },
127 { "proc3", STRUCTSIZE(svcv3info), svcv3info },
128 { "proc4", STRUCTSIZE(svcv4info), svcv4info },
129 { "proc4ops", STRUCTSIZE(svcv4opinfo),svcv4opinfo},
133 static statinfo cltinfo[] = {
134 { "net", STRUCTSIZE(cltnetinfo), cltnetinfo },
135 { "rpc", STRUCTSIZE(cltrpcinfo), cltrpcinfo },
136 { "proc2", STRUCTSIZE(cltv2info), cltv2info },
137 { "proc3", STRUCTSIZE(cltv3info), cltv3info },
138 { "proc4", STRUCTSIZE(cltv4info), cltv4info },
142 static void print_numbers(const char *, unsigned int *,
144 static void print_callstats(const char *, const char **,
145 unsigned int *, unsigned int);
146 static int parse_statfile(const char *, struct statinfo *);
148 static statinfo *get_stat_info(const char *, struct statinfo *);
150 static int mounts(const char *);
152 static int has_stats(const unsigned int *);
154 #define PRNT_CALLS 0x0001
155 #define PRNT_RPC 0x0002
156 #define PRNT_NET 0x0004
157 #define PRNT_FH 0x0008
158 #define PRNT_RC 0x0010
159 #define PRNT_AUTO 0x1000
160 #define PRNT_V2 0x2000
161 #define PRNT_V3 0x4000
162 #define PRNT_V4 0x8000
163 #define PRNT_ALL 0x0fff
171 void usage(char *name)
173 printf("Usage: %s [OPTION]...\n\
175 -m, --mounted\t\tShow statistics on mounted NFS filesystems\n\
176 -c, --client\t\tShow NFS client statistics\n\
177 -s, --server\t\tShow NFS server statistics\n\
178 -2\t\t\tShow NFS version 2 statistics\n\
179 -3\t\t\tShow NFS version 3 statistics\n\
180 -4\t\t\tShow NFS version 4 statistics\n\
181 -o [facility]\t\tShow statistics on particular facilities.\n\
182 nfs\tNFS protocol information\n\
183 rpc\tGeneral RPC information\n\
184 net\tNetwork layer statistics\n\
185 fh\t\tUsage information on the server's file handle cache\n\
186 rc\t\tUsage information on the server's request reply cache\n\
187 all\tSelect all of the above\n\
188 -v, --verbose, --all\tSame as '-o all'\n\
189 -r, --rpc\t\tShow RPC statistics\n\
190 -n, --nfs\t\tShow NFS statistics\n\
191 --version\t\tShow program version\n\
192 --help\t\tWhat you just did\n\
197 static struct option longopts[] =
199 { "acl", 0, 0, 'a' },
200 { "all", 0, 0, 'v' },
201 { "auto", 0, 0, '\3' },
202 { "client", 0, 0, 'c' },
203 { "mounts", 0, 0, 'm' },
204 { "nfs", 0, 0, 'n' },
205 { "rpc", 0, 0, 'r' },
206 { "server", 0, 0, 's' },
207 { "verbose", 0, 0, 'v' },
208 { "zero", 0, 0, 'z' },
209 { "help", 0, 0, '\1' },
210 { "version", 0, 0, '\2' },
215 main(int argc, char **argv)
226 if ((progname = strrchr(argv[0], '/')))
231 while ((c = getopt_long(argc, argv, "234acmno:vrsz\1\2", longopts, NULL)) != EOF) {
234 fprintf(stderr, "nfsstat: nfs acls are not yet supported.\n");
240 opt_prt |= PRNT_CALLS;
243 if (!strcmp(optarg, "nfs"))
244 opt_prt |= PRNT_CALLS;
245 else if (!strcmp(optarg, "rpc"))
247 else if (!strcmp(optarg, "net"))
249 else if (!strcmp(optarg, "rc"))
251 else if (!strcmp(optarg, "fh"))
253 else if (!strcmp(optarg, "all"))
254 opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH;
256 fprintf(stderr, "nfsstat: unknown category: "
264 opt_prt |= versions[c - '2'];
270 opt_prt |= PRNT_AUTO;
279 fprintf(stderr, "nfsstat: zeroing of nfs statistics "
280 "not yet supported\n");
283 return mounts(MOUNTSFILE);
288 fprintf(stdout, "nfsstat: " VERSION "\n");
291 printf("Try `%s --help' for more information.\n", progname);
297 opt_srv = opt_clt = 1;
300 if (!(opt_srv + opt_clt))
301 opt_srv = opt_clt = 1;
302 if (!(opt_prt & 0xfff)) {
303 opt_prt |= PRNT_CALLS + PRNT_RPC;
305 if (!(opt_prt & 0xe000)) {
306 opt_prt |= PRNT_AUTO;
308 if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
310 "You requested file handle or request cache "
311 "statistics while using the -c option.\n"
312 "This information is available only for the NFS "
317 srv_info = parse_statfile(NFSSVCSTAT, svcinfo);
318 if (srv_info == 0 && opt_clt == 0) {
319 fprintf(stderr, "Warning: No Server Stats (%s: %m).\n", NFSSVCSTAT);
327 clt_info = parse_statfile(NFSCLTSTAT, cltinfo);
328 if (opt_srv == 0 && clt_info == 0) {
329 fprintf(stderr, "Warning: No Client Stats (%s: %m).\n", NFSCLTSTAT);
337 if (opt_prt & PRNT_NET) {
339 "Server packet stats:\n"
340 "packets udp tcp tcpconn\n",
345 if (opt_prt & PRNT_RPC) {
347 "Server rpc stats:\n"
348 "calls badcalls badauth badclnt xdrcall\n",
353 if (opt_prt & PRNT_RC) {
355 "Server reply cache:\n"
356 "hits misses nocache\n",
363 * 2.2 puts all fh-related info after the 'rc' header
364 * 2.4 puts all fh-related info after the 'fh' header, but relocates
365 * 'stale' to the start and swaps dir and nondir :-(
366 * We preseve the 2.2 order
368 if (opt_prt & PRNT_FH) {
369 if (get_stat_info("fh", svcinfo)) { /* >= 2.4 */
370 int t = svcfhinfo[3];
371 svcfhinfo[3]=svcfhinfo[4];
374 svcfhinfo[5]=svcfhinfo[0]; /* relocate 'stale' */
377 "Server file handle cache:\n"
378 "lookup anon ncachedir ncachedir stale\n",
382 "Server file handle cache:\n"
383 "lookup anon ncachedir ncachedir stale\n",
387 if (opt_prt & PRNT_CALLS) {
388 if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(svcv2info)))
391 nfsv2name, svcv2info + 1, sizeof(nfsv2name)/sizeof(char *)
393 if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(svcv3info)))
396 nfsv3name, svcv3info + 1, sizeof(nfsv3name)/sizeof(char *)
398 if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(svcv4info))) {
401 nfssvrv4name, svcv4info + 1, sizeof(nfssvrv4name)/sizeof(char *)
404 "Server nfs v4 operations:\n",
405 nfssvrv4opname, svcv4opinfo + 1, sizeof(nfssvrv4opname)/sizeof(char *)
412 if (opt_prt & PRNT_NET) {
414 "Client packet stats:\n"
415 "packets udp tcp tcpconn\n",
420 if (opt_prt & PRNT_RPC) {
422 "Client rpc stats:\n"
423 "calls retrans authrefrsh\n",
428 if (opt_prt & PRNT_CALLS) {
429 if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(cltv2info)))
432 nfsv2name, cltv2info + 1, sizeof(nfsv2name)/sizeof(char *)
434 if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(cltv3info)))
437 nfsv3name, cltv3info + 1, sizeof(nfsv3name)/sizeof(char *)
439 if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(cltv4info)))
442 nfscltv4name, cltv4info + 1, sizeof(nfscltv4name)/sizeof(char *)
451 get_stat_info(const char *sp, struct statinfo *statp)
455 for (ip = statp; ip->tag; ip++) {
456 if (!strcmp(sp, ip->tag))
464 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
469 for (i = 0; i < nr; i++)
470 printf("%s%-8d", i? " " : "", info[i]);
475 print_callstats(const char *hdr, const char **names,
476 unsigned int *info, unsigned int nr)
478 unsigned long long total;
479 unsigned long long pct;
483 for (i = 0, total = 0; i < nr; i++)
487 for (i = 0; i < nr; i += 6) {
488 for (j = 0; j < 6 && i + j < nr; j++)
489 printf("%-13s", names[i+j]);
491 for (j = 0; j < 6 && i + j < nr; j++) {
492 pct = ((unsigned long long) info[i+j]*100)/total;
493 printf("%-8d%3llu%% ", info[i+j], pct);
502 parse_statfile(const char *name, struct statinfo *statp)
504 char buffer[4096], *next;
507 /* Being unable to read e.g. the nfsd stats file shouldn't
508 * be a fatal error -- it usually means the module isn't loaded.
510 if ((fp = fopen(name, "r")) == NULL) {
511 // fprintf(stderr, "Warning: %s: %m\n", name);
515 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
517 char *sp, *line = buffer;
519 unsigned int total = 0;
521 if ((next = strchr(line, '\n')) != NULL)
523 if (!(sp = strtok(line, " \t")))
526 ip = get_stat_info(sp, statp);
532 for (i = 0; i < cnt; i++) {
533 if (!(sp = strtok(NULL, " \t")))
535 ip->valptr[i] = atoi(sp);
536 total += ip->valptr[i];
538 ip->valptr[i] = total;
546 mounts(const char *name)
548 char buffer[4096], *next;
551 /* Being unable to read e.g. the nfsd stats file shouldn't
552 * be a fatal error -- it usually means the module isn't loaded.
554 if ((fp = fopen(name, "r")) == NULL) {
555 fprintf(stderr, "Warning: %s: %m\n", name);
559 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
561 char *device, *mount, *type, *flags;
563 if ((next = strchr(line, '\n')) != NULL)
566 if (!(device = strtok(line, " \t")))
569 if (!(mount = strtok(NULL, " \t")))
572 if (!(type = strtok(NULL, " \t")))
575 if (strcmp(type, "nfs")) {
579 if (!(flags = strtok(NULL, " \t")))
582 printf("%s from %s\n", mount, device);
583 printf(" Flags:\t%s\n", flags);
594 has_stats(const unsigned int *info)
596 return (info[0] && info[info[0] + 1] != info[0]);