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], srvproc2info_tmp[20]; /* NFSv2 call counts ([0] == 18) */
27 static unsigned int cltproc2info[20], cltproc2info_tmp[20]; /* NFSv2 call counts ([0] == 18) */
28 static unsigned int srvproc3info[24], srvproc3info_tmp[24]; /* NFSv3 call counts ([0] == 22) */
29 static unsigned int cltproc3info[24], cltproc3info_tmp[24]; /* NFSv3 call counts ([0] == 22) */
30 static unsigned int srvproc4info[4], srvproc4info_tmp[4]; /* NFSv4 call counts ([0] == 2) */
31 static unsigned int cltproc4info[34], cltproc4info_tmp[34]; /* NFSv4 call counts ([0] == 32) */
32 static unsigned int srvproc4opsinfo[42], srvproc4opsinfo_tmp[42]; /* NFSv4 call counts ([0] == 40) */
33 static unsigned int srvnetinfo[5], srvnetinfo_tmp[5]; /* 0 # of received packets
38 static unsigned int cltnetinfo[5], cltnetinfo_tmp[5]; /* 0 # of received packets
44 static unsigned int srvrpcinfo[6], srvrpcinfo_tmp[6]; /* 0 total # of RPC calls
45 * 1 total # of bad calls
47 * 3 authentication failed
50 static unsigned int cltrpcinfo[4], cltrpcinfo_tmp[4]; /* 0 total # of RPC calls
51 * 1 retransmitted calls
55 static unsigned int srvrcinfo[9], srvrcinfo_tmp[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], srvfhinfo_tmp[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_SRV(srvinfo, _tmp);
149 DECLARE_CLT(cltinfo);
150 DECLARE_CLT(cltinfo, _tmp);
152 static void print_numbers(const char *, unsigned int *,
154 static void print_callstats(const char *, const char **,
155 unsigned int *, unsigned int);
156 static int parse_statfile(const char *, struct statinfo *);
158 static statinfo *get_stat_info(const char *, struct statinfo *);
160 static int mounts(const char *);
162 static void get_stats(const char *, statinfo *, int *, int, const char *);
163 static int has_stats(const unsigned int *);
165 #define PRNT_CALLS 0x0001
166 #define PRNT_RPC 0x0002
167 #define PRNT_NET 0x0004
168 #define PRNT_FH 0x0008
169 #define PRNT_RC 0x0010
170 #define PRNT_AUTO 0x1000
171 #define PRNT_V2 0x2000
172 #define PRNT_V3 0x4000
173 #define PRNT_V4 0x8000
174 #define PRNT_ALL 0x0fff
182 void usage(char *name)
184 printf("Usage: %s [OPTION]...\n\
186 -m, --mounted\t\tShow statistics on mounted NFS filesystems\n\
187 -c, --client\t\tShow NFS client statistics\n\
188 -s, --server\t\tShow NFS server statistics\n\
189 -2\t\t\tShow NFS version 2 statistics\n\
190 -3\t\t\tShow NFS version 3 statistics\n\
191 -4\t\t\tShow NFS version 4 statistics\n\
192 -o [facility]\t\tShow statistics on particular facilities.\n\
193 nfs\tNFS protocol information\n\
194 rpc\tGeneral RPC information\n\
195 net\tNetwork layer statistics\n\
196 fh\t\tUsage information on the server's file handle cache\n\
197 rc\t\tUsage information on the server's request reply cache\n\
198 all\tSelect all of the above\n\
199 -v, --verbose, --all\tSame as '-o all'\n\
200 -r, --rpc\t\tShow RPC statistics\n\
201 -n, --nfs\t\tShow NFS statistics\n\
202 --version\t\tShow program version\n\
203 --help\t\tWhat you just did\n\
208 static struct option longopts[] =
210 { "acl", 0, 0, 'a' },
211 { "all", 0, 0, 'v' },
212 { "auto", 0, 0, '\3' },
213 { "client", 0, 0, 'c' },
214 { "mounts", 0, 0, 'm' },
215 { "nfs", 0, 0, 'n' },
216 { "rpc", 0, 0, 'r' },
217 { "server", 0, 0, 's' },
218 { "verbose", 0, 0, 'v' },
219 { "zero", 0, 0, 'z' },
220 { "help", 0, 0, '\1' },
221 { "version", 0, 0, '\2' },
226 main(int argc, char **argv)
235 if ((progname = strrchr(argv[0], '/')))
240 while ((c = getopt_long(argc, argv, "234acmno:vrsz\1\2", longopts, NULL)) != EOF) {
243 fprintf(stderr, "nfsstat: nfs acls are not yet supported.\n");
249 opt_prt |= PRNT_CALLS;
252 if (!strcmp(optarg, "nfs"))
253 opt_prt |= PRNT_CALLS;
254 else if (!strcmp(optarg, "rpc"))
256 else if (!strcmp(optarg, "net"))
258 else if (!strcmp(optarg, "rc"))
260 else if (!strcmp(optarg, "fh"))
262 else if (!strcmp(optarg, "all"))
263 opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH;
265 fprintf(stderr, "nfsstat: unknown category: "
273 opt_prt |= versions[c - '2'];
279 opt_prt |= PRNT_AUTO;
288 fprintf(stderr, "nfsstat: zeroing of nfs statistics "
289 "not yet supported\n");
292 return mounts(MOUNTSFILE);
297 fprintf(stdout, "nfsstat: " VERSION "\n");
300 printf("Try `%s --help' for more information.\n", progname);
306 opt_srv = opt_clt = 1;
309 if (!(opt_srv + opt_clt))
310 opt_srv = opt_clt = 1;
311 if (!(opt_prt & 0xfff)) {
312 opt_prt |= PRNT_CALLS + PRNT_RPC;
314 if (!(opt_prt & 0xe000)) {
315 opt_prt |= PRNT_AUTO;
317 if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
319 "You requested file handle or request cache "
320 "statistics while using the -c option.\n"
321 "This information is available only for the NFS "
326 get_stats(NFSSRVSTAT, srvinfo, &opt_srv, opt_clt, "Server");
328 get_stats(NFSCLTSTAT, cltinfo, &opt_clt, opt_srv, "Client");
331 if (opt_prt & PRNT_NET) {
333 "Server packet stats:\n"
334 "packets udp tcp tcpconn\n",
339 if (opt_prt & PRNT_RPC) {
341 "Server rpc stats:\n"
342 "calls badcalls badauth badclnt xdrcall\n",
347 if (opt_prt & PRNT_RC) {
349 "Server reply cache:\n"
350 "hits misses nocache\n",
357 * 2.2 puts all fh-related info after the 'rc' header
358 * 2.4 puts all fh-related info after the 'fh' header, but relocates
359 * 'stale' to the start and swaps dir and nondir :-(
360 * We preseve the 2.2 order
362 if (opt_prt & PRNT_FH) {
363 if (get_stat_info("fh", srvinfo)) { /* >= 2.4 */
364 int t = srvfhinfo[3];
365 srvfhinfo[3]=srvfhinfo[4];
368 srvfhinfo[5]=srvfhinfo[0]; /* relocate 'stale' */
371 "Server file handle cache:\n"
372 "lookup anon ncachedir ncachedir stale\n",
376 "Server file handle cache:\n"
377 "lookup anon ncachedir ncachedir stale\n",
381 if (opt_prt & PRNT_CALLS) {
382 if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info)))
385 nfsv2name, srvproc2info + 1, sizeof(nfsv2name)/sizeof(char *)
387 if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info)))
390 nfsv3name, srvproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
392 if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc4info))) {
395 nfssrvproc4name, srvproc4info + 1, sizeof(nfssrvproc4name)/sizeof(char *)
398 "Server nfs v4 operations:\n",
399 nfssrvproc4opname, srvproc4opsinfo + 1, sizeof(nfssrvproc4opname)/sizeof(char *)
406 if (opt_prt & PRNT_NET) {
408 "Client packet stats:\n"
409 "packets udp tcp tcpconn\n",
414 if (opt_prt & PRNT_RPC) {
416 "Client rpc stats:\n"
417 "calls retrans authrefrsh\n",
422 if (opt_prt & PRNT_CALLS) {
423 if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info)))
426 nfsv2name, cltproc2info + 1, sizeof(nfsv2name)/sizeof(char *)
428 if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info)))
431 nfsv3name, cltproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
433 if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info)))
436 nfscltproc4name, cltproc4info + 1, sizeof(nfscltproc4name)/sizeof(char *)
445 get_stat_info(const char *sp, struct statinfo *statp)
449 for (ip = statp; ip->tag; ip++) {
450 if (!strcmp(sp, ip->tag))
458 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
463 for (i = 0; i < nr; i++)
464 printf("%s%-8d", i? " " : "", info[i]);
469 print_callstats(const char *hdr, const char **names,
470 unsigned int *info, unsigned int nr)
472 unsigned long long total;
473 unsigned long long pct;
477 for (i = 0, total = 0; i < nr; i++)
481 for (i = 0; i < nr; i += 6) {
482 for (j = 0; j < 6 && i + j < nr; j++)
483 printf("%-13s", names[i+j]);
485 for (j = 0; j < 6 && i + j < nr; j++) {
486 pct = ((unsigned long long) info[i+j]*100)/total;
487 printf("%-8d%3llu%% ", info[i+j], pct);
496 parse_statfile(const char *name, struct statinfo *statp)
498 char buffer[4096], *next;
501 /* Being unable to read e.g. the nfsd stats file shouldn't
502 * be a fatal error -- it usually means the module isn't loaded.
504 if ((fp = fopen(name, "r")) == NULL) {
505 // fprintf(stderr, "Warning: %s: %m\n", name);
509 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
511 char *sp, *line = buffer;
513 unsigned int total = 0;
515 if ((next = strchr(line, '\n')) != NULL)
517 if (!(sp = strtok(line, " \t")))
520 ip = get_stat_info(sp, statp);
526 for (i = 0; i < cnt; i++) {
527 if (!(sp = strtok(NULL, " \t")))
529 ip->valptr[i] = atoi(sp);
530 total += ip->valptr[i];
532 ip->valptr[i] = total;
540 mounts(const char *name)
542 char buffer[4096], *next;
545 /* Being unable to read e.g. the nfsd stats file shouldn't
546 * be a fatal error -- it usually means the module isn't loaded.
548 if ((fp = fopen(name, "r")) == NULL) {
549 fprintf(stderr, "Warning: %s: %m\n", name);
553 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
555 char *device, *mount, *type, *flags;
557 if ((next = strchr(line, '\n')) != NULL)
560 if (!(device = strtok(line, " \t")))
563 if (!(mount = strtok(NULL, " \t")))
566 if (!(type = strtok(NULL, " \t")))
569 if (strcmp(type, "nfs")) {
573 if (!(flags = strtok(NULL, " \t")))
576 printf("%s from %s\n", mount, device);
577 printf(" Flags:\t%s\n", flags);
588 get_stats(const char *file, statinfo *info, int *opt, int other_opt, const char *label)
590 if (!parse_statfile(file, info)) {
592 fprintf(stderr, "Warning: No %s Stats (%s: %m). \n", label, file);
600 has_stats(const unsigned int *info)
602 return (info[0] && info[info[0] + 1] != info[0]);