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 void get_stats(const char *, statinfo *, int *, int, const char *);
153 static int has_stats(const unsigned int *);
155 #define PRNT_CALLS 0x0001
156 #define PRNT_RPC 0x0002
157 #define PRNT_NET 0x0004
158 #define PRNT_FH 0x0008
159 #define PRNT_RC 0x0010
160 #define PRNT_AUTO 0x1000
161 #define PRNT_V2 0x2000
162 #define PRNT_V3 0x4000
163 #define PRNT_V4 0x8000
164 #define PRNT_ALL 0x0fff
172 void usage(char *name)
174 printf("Usage: %s [OPTION]...\n\
176 -m, --mounted\t\tShow statistics on mounted NFS filesystems\n\
177 -c, --client\t\tShow NFS client statistics\n\
178 -s, --server\t\tShow NFS server statistics\n\
179 -2\t\t\tShow NFS version 2 statistics\n\
180 -3\t\t\tShow NFS version 3 statistics\n\
181 -4\t\t\tShow NFS version 4 statistics\n\
182 -o [facility]\t\tShow statistics on particular facilities.\n\
183 nfs\tNFS protocol information\n\
184 rpc\tGeneral RPC information\n\
185 net\tNetwork layer statistics\n\
186 fh\t\tUsage information on the server's file handle cache\n\
187 rc\t\tUsage information on the server's request reply cache\n\
188 all\tSelect all of the above\n\
189 -v, --verbose, --all\tSame as '-o all'\n\
190 -r, --rpc\t\tShow RPC statistics\n\
191 -n, --nfs\t\tShow NFS statistics\n\
192 --version\t\tShow program version\n\
193 --help\t\tWhat you just did\n\
198 static struct option longopts[] =
200 { "acl", 0, 0, 'a' },
201 { "all", 0, 0, 'v' },
202 { "auto", 0, 0, '\3' },
203 { "client", 0, 0, 'c' },
204 { "mounts", 0, 0, 'm' },
205 { "nfs", 0, 0, 'n' },
206 { "rpc", 0, 0, 'r' },
207 { "server", 0, 0, 's' },
208 { "verbose", 0, 0, 'v' },
209 { "zero", 0, 0, 'z' },
210 { "help", 0, 0, '\1' },
211 { "version", 0, 0, '\2' },
216 main(int argc, char **argv)
225 if ((progname = strrchr(argv[0], '/')))
230 while ((c = getopt_long(argc, argv, "234acmno:vrsz\1\2", longopts, NULL)) != EOF) {
233 fprintf(stderr, "nfsstat: nfs acls are not yet supported.\n");
239 opt_prt |= PRNT_CALLS;
242 if (!strcmp(optarg, "nfs"))
243 opt_prt |= PRNT_CALLS;
244 else if (!strcmp(optarg, "rpc"))
246 else if (!strcmp(optarg, "net"))
248 else if (!strcmp(optarg, "rc"))
250 else if (!strcmp(optarg, "fh"))
252 else if (!strcmp(optarg, "all"))
253 opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH;
255 fprintf(stderr, "nfsstat: unknown category: "
263 opt_prt |= versions[c - '2'];
269 opt_prt |= PRNT_AUTO;
278 fprintf(stderr, "nfsstat: zeroing of nfs statistics "
279 "not yet supported\n");
282 return mounts(MOUNTSFILE);
287 fprintf(stdout, "nfsstat: " VERSION "\n");
290 printf("Try `%s --help' for more information.\n", progname);
296 opt_srv = opt_clt = 1;
299 if (!(opt_srv + opt_clt))
300 opt_srv = opt_clt = 1;
301 if (!(opt_prt & 0xfff)) {
302 opt_prt |= PRNT_CALLS + PRNT_RPC;
304 if (!(opt_prt & 0xe000)) {
305 opt_prt |= PRNT_AUTO;
307 if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
309 "You requested file handle or request cache "
310 "statistics while using the -c option.\n"
311 "This information is available only for the NFS "
316 get_stats(NFSSVCSTAT, svcinfo, &opt_srv, opt_clt, "Server");
318 get_stats(NFSCLTSTAT, cltinfo, &opt_clt, opt_srv, "Client");
321 if (opt_prt & PRNT_NET) {
323 "Server packet stats:\n"
324 "packets udp tcp tcpconn\n",
329 if (opt_prt & PRNT_RPC) {
331 "Server rpc stats:\n"
332 "calls badcalls badauth badclnt xdrcall\n",
337 if (opt_prt & PRNT_RC) {
339 "Server reply cache:\n"
340 "hits misses nocache\n",
347 * 2.2 puts all fh-related info after the 'rc' header
348 * 2.4 puts all fh-related info after the 'fh' header, but relocates
349 * 'stale' to the start and swaps dir and nondir :-(
350 * We preseve the 2.2 order
352 if (opt_prt & PRNT_FH) {
353 if (get_stat_info("fh", svcinfo)) { /* >= 2.4 */
354 int t = svcfhinfo[3];
355 svcfhinfo[3]=svcfhinfo[4];
358 svcfhinfo[5]=svcfhinfo[0]; /* relocate 'stale' */
361 "Server file handle cache:\n"
362 "lookup anon ncachedir ncachedir stale\n",
366 "Server file handle cache:\n"
367 "lookup anon ncachedir ncachedir stale\n",
371 if (opt_prt & PRNT_CALLS) {
372 if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(svcv2info)))
375 nfsv2name, svcv2info + 1, sizeof(nfsv2name)/sizeof(char *)
377 if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(svcv3info)))
380 nfsv3name, svcv3info + 1, sizeof(nfsv3name)/sizeof(char *)
382 if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(svcv4info))) {
385 nfssvrv4name, svcv4info + 1, sizeof(nfssvrv4name)/sizeof(char *)
388 "Server nfs v4 operations:\n",
389 nfssvrv4opname, svcv4opinfo + 1, sizeof(nfssvrv4opname)/sizeof(char *)
396 if (opt_prt & PRNT_NET) {
398 "Client packet stats:\n"
399 "packets udp tcp tcpconn\n",
404 if (opt_prt & PRNT_RPC) {
406 "Client rpc stats:\n"
407 "calls retrans authrefrsh\n",
412 if (opt_prt & PRNT_CALLS) {
413 if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(cltv2info)))
416 nfsv2name, cltv2info + 1, sizeof(nfsv2name)/sizeof(char *)
418 if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(cltv3info)))
421 nfsv3name, cltv3info + 1, sizeof(nfsv3name)/sizeof(char *)
423 if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(cltv4info)))
426 nfscltv4name, cltv4info + 1, sizeof(nfscltv4name)/sizeof(char *)
435 get_stat_info(const char *sp, struct statinfo *statp)
439 for (ip = statp; ip->tag; ip++) {
440 if (!strcmp(sp, ip->tag))
448 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
453 for (i = 0; i < nr; i++)
454 printf("%s%-8d", i? " " : "", info[i]);
459 print_callstats(const char *hdr, const char **names,
460 unsigned int *info, unsigned int nr)
462 unsigned long long total;
463 unsigned long long pct;
467 for (i = 0, total = 0; i < nr; i++)
471 for (i = 0; i < nr; i += 6) {
472 for (j = 0; j < 6 && i + j < nr; j++)
473 printf("%-13s", names[i+j]);
475 for (j = 0; j < 6 && i + j < nr; j++) {
476 pct = ((unsigned long long) info[i+j]*100)/total;
477 printf("%-8d%3llu%% ", info[i+j], pct);
486 parse_statfile(const char *name, struct statinfo *statp)
488 char buffer[4096], *next;
491 /* Being unable to read e.g. the nfsd stats file shouldn't
492 * be a fatal error -- it usually means the module isn't loaded.
494 if ((fp = fopen(name, "r")) == NULL) {
495 // fprintf(stderr, "Warning: %s: %m\n", name);
499 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
501 char *sp, *line = buffer;
503 unsigned int total = 0;
505 if ((next = strchr(line, '\n')) != NULL)
507 if (!(sp = strtok(line, " \t")))
510 ip = get_stat_info(sp, statp);
516 for (i = 0; i < cnt; i++) {
517 if (!(sp = strtok(NULL, " \t")))
519 ip->valptr[i] = atoi(sp);
520 total += ip->valptr[i];
522 ip->valptr[i] = total;
530 mounts(const char *name)
532 char buffer[4096], *next;
535 /* Being unable to read e.g. the nfsd stats file shouldn't
536 * be a fatal error -- it usually means the module isn't loaded.
538 if ((fp = fopen(name, "r")) == NULL) {
539 fprintf(stderr, "Warning: %s: %m\n", name);
543 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
545 char *device, *mount, *type, *flags;
547 if ((next = strchr(line, '\n')) != NULL)
550 if (!(device = strtok(line, " \t")))
553 if (!(mount = strtok(NULL, " \t")))
556 if (!(type = strtok(NULL, " \t")))
559 if (strcmp(type, "nfs")) {
563 if (!(flags = strtok(NULL, " \t")))
566 printf("%s from %s\n", mount, device);
567 printf(" Flags:\t%s\n", flags);
578 get_stats(const char *file, statinfo *info, int *opt, int other_opt, const char *label)
580 if (!parse_statfile(file, info)) {
582 fprintf(stderr, "Warning: No %s Stats (%s: %m). \n", label, file);
590 has_stats(const unsigned int *info)
592 return (info[0] && info[info[0] + 1] != info[0]);