2 * nfsstat.c Output NFS statistics
4 * Copyright (C) 1995-2005 Olaf Kirch <okir@suse.de>
11 #define NFSSRVSTAT "/proc/net/rpc/nfsd"
12 #define NFSCLTSTAT "/proc/net/rpc/nfs"
14 #define MOUNTSFILE "/proc/mounts"
26 static unsigned int srvproc2info[20]; /* NFSv2 call counts ([0] == 18) */
27 static unsigned int cltproc2info[20]; /* NFSv2 call counts ([0] == 18) */
28 static unsigned int srvproc3info[24]; /* NFSv3 call counts ([0] == 22) */
29 static unsigned int cltproc3info[24]; /* NFSv3 call counts ([0] == 22) */
30 static unsigned int srvproc4info[4]; /* NFSv4 call counts ([0] == 2) */
31 static unsigned int cltproc4info[34]; /* NFSv4 call counts ([0] == 32) */
32 static unsigned int srvproc4opsinfo[42];/* NFSv4 call counts ([0] == 40) */
33 static unsigned int srvnetinfo[5]; /* 0 # of received packets
38 static unsigned int cltnetinfo[5]; /* 0 # of received packets
44 static unsigned int srvrpcinfo[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 srvrcinfo[9]; /* 0 repcache hits
58 * (for pre-2.4 kernels:)
61 * 5 noncached non-directories
62 * 6 noncached directories
66 static unsigned int srvfhinfo[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 * nfssrvproc4name[2] = {
94 static const char * nfscltproc4name[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 * nfssrvproc4opname[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;
120 * We now build the arrays of statinfos using macros, which will make it easier
121 * to add new variables for --diff-stat.
122 * e.g., SRV(net) expands into the struct statinfo: { "net", 5, srvnetinfo }
124 #define ARRAYSIZE(x) sizeof(x)/sizeof(*x)
125 #define STATINFO(k, t, s...) { #t, ARRAYSIZE(k##t##info##s), k##t##info##s }
126 #define SRV(t, s...) STATINFO(srv, t, s)
127 #define CLT(t, s...) STATINFO(clt, t, s)
128 #define DECLARE_SRV(n, s...) static statinfo n##s[] = { \
139 #define DECLARE_CLT(n, s...) static statinfo n##s[] = { \
147 DECLARE_SRV(srvinfo);
148 DECLARE_CLT(cltinfo);
150 static void print_numbers(const char *, unsigned int *,
152 static void print_callstats(const char *, const char **,
153 unsigned int *, unsigned int);
154 static int parse_statfile(const char *, struct statinfo *);
156 static statinfo *get_stat_info(const char *, struct statinfo *);
158 static int mounts(const char *);
160 static void get_stats(const char *, statinfo *, int *, int, const char *);
161 static int has_stats(const unsigned int *);
163 #define PRNT_CALLS 0x0001
164 #define PRNT_RPC 0x0002
165 #define PRNT_NET 0x0004
166 #define PRNT_FH 0x0008
167 #define PRNT_RC 0x0010
168 #define PRNT_AUTO 0x1000
169 #define PRNT_V2 0x2000
170 #define PRNT_V3 0x4000
171 #define PRNT_V4 0x8000
172 #define PRNT_ALL 0x0fff
180 void usage(char *name)
182 printf("Usage: %s [OPTION]...\n\
184 -m, --mounted\t\tShow statistics on mounted NFS filesystems\n\
185 -c, --client\t\tShow NFS client statistics\n\
186 -s, --server\t\tShow NFS server statistics\n\
187 -2\t\t\tShow NFS version 2 statistics\n\
188 -3\t\t\tShow NFS version 3 statistics\n\
189 -4\t\t\tShow NFS version 4 statistics\n\
190 -o [facility]\t\tShow statistics on particular facilities.\n\
191 nfs\tNFS protocol information\n\
192 rpc\tGeneral RPC information\n\
193 net\tNetwork layer statistics\n\
194 fh\t\tUsage information on the server's file handle cache\n\
195 rc\t\tUsage information on the server's request reply cache\n\
196 all\tSelect all of the above\n\
197 -v, --verbose, --all\tSame as '-o all'\n\
198 -r, --rpc\t\tShow RPC statistics\n\
199 -n, --nfs\t\tShow NFS statistics\n\
200 --version\t\tShow program version\n\
201 --help\t\tWhat you just did\n\
206 static struct option longopts[] =
208 { "acl", 0, 0, 'a' },
209 { "all", 0, 0, 'v' },
210 { "auto", 0, 0, '\3' },
211 { "client", 0, 0, 'c' },
212 { "mounts", 0, 0, 'm' },
213 { "nfs", 0, 0, 'n' },
214 { "rpc", 0, 0, 'r' },
215 { "server", 0, 0, 's' },
216 { "verbose", 0, 0, 'v' },
217 { "zero", 0, 0, 'z' },
218 { "help", 0, 0, '\1' },
219 { "version", 0, 0, '\2' },
224 main(int argc, char **argv)
233 if ((progname = strrchr(argv[0], '/')))
238 while ((c = getopt_long(argc, argv, "234acmno:vrsz\1\2", longopts, NULL)) != EOF) {
241 fprintf(stderr, "nfsstat: nfs acls are not yet supported.\n");
247 opt_prt |= PRNT_CALLS;
250 if (!strcmp(optarg, "nfs"))
251 opt_prt |= PRNT_CALLS;
252 else if (!strcmp(optarg, "rpc"))
254 else if (!strcmp(optarg, "net"))
256 else if (!strcmp(optarg, "rc"))
258 else if (!strcmp(optarg, "fh"))
260 else if (!strcmp(optarg, "all"))
261 opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH;
263 fprintf(stderr, "nfsstat: unknown category: "
271 opt_prt |= versions[c - '2'];
277 opt_prt |= PRNT_AUTO;
286 fprintf(stderr, "nfsstat: zeroing of nfs statistics "
287 "not yet supported\n");
290 return mounts(MOUNTSFILE);
295 fprintf(stdout, "nfsstat: " VERSION "\n");
298 printf("Try `%s --help' for more information.\n", progname);
304 opt_srv = opt_clt = 1;
307 if (!(opt_srv + opt_clt))
308 opt_srv = opt_clt = 1;
309 if (!(opt_prt & 0xfff)) {
310 opt_prt |= PRNT_CALLS + PRNT_RPC;
312 if (!(opt_prt & 0xe000)) {
313 opt_prt |= PRNT_AUTO;
315 if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
317 "You requested file handle or request cache "
318 "statistics while using the -c option.\n"
319 "This information is available only for the NFS "
324 get_stats(NFSSRVSTAT, srvinfo, &opt_srv, opt_clt, "Server");
326 get_stats(NFSCLTSTAT, cltinfo, &opt_clt, opt_srv, "Client");
329 if (opt_prt & PRNT_NET) {
331 "Server packet stats:\n"
332 "packets udp tcp tcpconn\n",
337 if (opt_prt & PRNT_RPC) {
339 "Server rpc stats:\n"
340 "calls badcalls badauth badclnt xdrcall\n",
345 if (opt_prt & PRNT_RC) {
347 "Server reply cache:\n"
348 "hits misses nocache\n",
355 * 2.2 puts all fh-related info after the 'rc' header
356 * 2.4 puts all fh-related info after the 'fh' header, but relocates
357 * 'stale' to the start and swaps dir and nondir :-(
358 * We preseve the 2.2 order
360 if (opt_prt & PRNT_FH) {
361 if (get_stat_info("fh", srvinfo)) { /* >= 2.4 */
362 int t = srvfhinfo[3];
363 srvfhinfo[3]=srvfhinfo[4];
366 srvfhinfo[5]=srvfhinfo[0]; /* relocate 'stale' */
369 "Server file handle cache:\n"
370 "lookup anon ncachedir ncachedir stale\n",
374 "Server file handle cache:\n"
375 "lookup anon ncachedir ncachedir stale\n",
379 if (opt_prt & PRNT_CALLS) {
380 if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info)))
383 nfsv2name, srvproc2info + 1, sizeof(nfsv2name)/sizeof(char *)
385 if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info)))
388 nfsv3name, srvproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
390 if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc4info))) {
393 nfssrvproc4name, srvproc4info + 1, sizeof(nfssrvproc4name)/sizeof(char *)
396 "Server nfs v4 operations:\n",
397 nfssrvproc4opname, srvproc4opsinfo + 1, sizeof(nfssrvproc4opname)/sizeof(char *)
404 if (opt_prt & PRNT_NET) {
406 "Client packet stats:\n"
407 "packets udp tcp tcpconn\n",
412 if (opt_prt & PRNT_RPC) {
414 "Client rpc stats:\n"
415 "calls retrans authrefrsh\n",
420 if (opt_prt & PRNT_CALLS) {
421 if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info)))
424 nfsv2name, cltproc2info + 1, sizeof(nfsv2name)/sizeof(char *)
426 if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info)))
429 nfsv3name, cltproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
431 if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info)))
434 nfscltproc4name, cltproc4info + 1, sizeof(nfscltproc4name)/sizeof(char *)
443 get_stat_info(const char *sp, struct statinfo *statp)
447 for (ip = statp; ip->tag; ip++) {
448 if (!strcmp(sp, ip->tag))
456 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
461 for (i = 0; i < nr; i++)
462 printf("%s%-8d", i? " " : "", info[i]);
467 print_callstats(const char *hdr, const char **names,
468 unsigned int *info, unsigned int nr)
470 unsigned long long total;
471 unsigned long long pct;
475 for (i = 0, total = 0; i < nr; i++)
479 for (i = 0; i < nr; i += 6) {
480 for (j = 0; j < 6 && i + j < nr; j++)
481 printf("%-13s", names[i+j]);
483 for (j = 0; j < 6 && i + j < nr; j++) {
484 pct = ((unsigned long long) info[i+j]*100)/total;
485 printf("%-8d%3llu%% ", info[i+j], pct);
494 parse_statfile(const char *name, struct statinfo *statp)
496 char buffer[4096], *next;
499 /* Being unable to read e.g. the nfsd stats file shouldn't
500 * be a fatal error -- it usually means the module isn't loaded.
502 if ((fp = fopen(name, "r")) == NULL) {
503 // fprintf(stderr, "Warning: %s: %m\n", name);
507 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
509 char *sp, *line = buffer;
511 unsigned int total = 0;
513 if ((next = strchr(line, '\n')) != NULL)
515 if (!(sp = strtok(line, " \t")))
518 ip = get_stat_info(sp, statp);
524 for (i = 0; i < cnt; i++) {
525 if (!(sp = strtok(NULL, " \t")))
527 ip->valptr[i] = atoi(sp);
528 total += ip->valptr[i];
530 ip->valptr[i] = total;
538 mounts(const char *name)
540 char buffer[4096], *next;
543 /* Being unable to read e.g. the nfsd stats file shouldn't
544 * be a fatal error -- it usually means the module isn't loaded.
546 if ((fp = fopen(name, "r")) == NULL) {
547 fprintf(stderr, "Warning: %s: %m\n", name);
551 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
553 char *device, *mount, *type, *flags;
555 if ((next = strchr(line, '\n')) != NULL)
558 if (!(device = strtok(line, " \t")))
561 if (!(mount = strtok(NULL, " \t")))
564 if (!(type = strtok(NULL, " \t")))
567 if (strcmp(type, "nfs")) {
571 if (!(flags = strtok(NULL, " \t")))
574 printf("%s from %s\n", mount, device);
575 printf(" Flags:\t%s\n", flags);
586 get_stats(const char *file, statinfo *info, int *opt, int other_opt, const char *label)
588 if (!parse_statfile(file, info)) {
590 fprintf(stderr, "Warning: No %s Stats (%s: %m). \n", label, file);
598 has_stats(const unsigned int *info)
600 return (info[0] && info[info[0] + 1] != info[0]);