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"
28 static unsigned int srvproc2info[20], srvproc2info_old[20]; /* NFSv2 call counts ([0] == 18) */
29 static unsigned int cltproc2info[20], cltproc2info_old[20]; /* NFSv2 call counts ([0] == 18) */
30 static unsigned int srvproc3info[24], srvproc3info_old[24]; /* NFSv3 call counts ([0] == 22) */
31 static unsigned int cltproc3info[24], cltproc3info_old[24]; /* NFSv3 call counts ([0] == 22) */
32 static unsigned int srvproc4info[4], srvproc4info_old[4]; /* NFSv4 call counts ([0] == 2) */
33 static unsigned int cltproc4info[37], cltproc4info_old[37]; /* NFSv4 call counts ([0] == 35) */
34 static unsigned int srvproc4opsinfo[42], srvproc4opsinfo_old[42]; /* NFSv4 call counts ([0] == 40) */
35 static unsigned int srvnetinfo[5], srvnetinfo_old[5]; /* 0 # of received packets
40 static unsigned int cltnetinfo[5], cltnetinfo_old[5]; /* 0 # of received packets
46 static unsigned int srvrpcinfo[6], srvrpcinfo_old[6]; /* 0 total # of RPC calls
47 * 1 total # of bad calls
49 * 3 authentication failed
52 static unsigned int cltrpcinfo[4], cltrpcinfo_old[4]; /* 0 total # of RPC calls
53 * 1 retransmitted calls
57 static unsigned int srvrcinfo[9], srvrcinfo_old[9]; /* 0 repcache hits
60 * (for pre-2.4 kernels:)
63 * 5 noncached non-directories
64 * 6 noncached directories
68 static unsigned int srvfhinfo[7], srvfhinfo_old[7]; /* (for kernels >= 2.4.0)
72 * 3 noncached directories
73 * 4 noncached non-directories
74 * leave hole to relocate stale for order
78 static const char * nfsv2name[18] = {
79 "null", "getattr", "setattr", "root", "lookup", "readlink",
80 "read", "wrcache", "write", "create", "remove", "rename",
81 "link", "symlink", "mkdir", "rmdir", "readdir", "fsstat"
84 static const char * nfsv3name[22] = {
85 "null", "getattr", "setattr", "lookup", "access", "readlink",
86 "read", "write", "create", "mkdir", "symlink", "mknod",
87 "remove", "rmdir", "rename", "link", "readdir", "readdirplus",
88 "fsstat", "fsinfo", "pathconf", "commit"
91 static const char * nfssrvproc4name[2] = {
96 static const char * nfscltproc4name[35] = {
97 "null", "read", "write", "commit", "open", "open_conf",
98 "open_noat", "open_dgrd", "close", "setattr", "fsinfo", "renew",
99 "setclntid", "confirm", "lock",
100 "lockt", "locku", "access", "getattr", "lookup", "lookup_root",
101 "remove", "rename", "link", "symlink", "create", "pathconf",
102 "statfs", "readlink", "readdir", "server_caps", "delegreturn", "getacl",
103 "setacl", "fs_locations"
106 static const char * nfssrvproc4opname[40] = {
107 "op0-unused", "op1-unused", "op2-future", "access", "close", "commit",
108 "create", "delegpurge", "delegreturn", "getattr", "getfh", "link",
109 "lock", "lockt", "locku", "lookup", "lookup_root", "nverify",
110 "open", "openattr", "open_conf", "open_dgrd", "putfh", "putpubfh",
111 "putrootfh", "read", "readdir", "readlink", "remove", "rename",
112 "renew", "restorefh", "savefh", "secinfo", "setattr", "setcltid",
113 "setcltidconf", "verify", "write", "rellockowner"
116 typedef struct statinfo {
119 unsigned int * valptr;
123 * We now build the arrays of statinfos using macros, which will make it easier
124 * to add new variables for --diff-stat.
125 * e.g., SRV(net) expands into the struct statinfo: { "net", 5, srvnetinfo }
127 #define ARRAYSIZE(x) sizeof(x)/sizeof(*x)
128 #define STATINFO(k, t, s...) { #t, ARRAYSIZE(k##t##info##s), k##t##info##s }
129 #define SRV(t, s...) STATINFO(srv, t, s)
130 #define CLT(t, s...) STATINFO(clt, t, s)
131 #define DECLARE_SRV(n, s...) static statinfo n##s[] = { \
142 #define DECLARE_CLT(n, s...) static statinfo n##s[] = { \
150 DECLARE_SRV(srvinfo);
151 DECLARE_SRV(srvinfo, _old);
152 DECLARE_CLT(cltinfo);
153 DECLARE_CLT(cltinfo, _old);
155 static void print_numbers(const char *, unsigned int *,
157 static void print_callstats(const char *, const char **,
158 unsigned int *, unsigned int);
159 static int parse_statfile(const char *, struct statinfo *);
161 static statinfo *get_stat_info(const char *, struct statinfo *);
163 static int mounts(const char *);
165 static void get_stats(const char *, struct statinfo *, int *, int,
167 static int has_stats(const unsigned int *);
168 static void diff_stats(struct statinfo *, struct statinfo *, int);
169 static void unpause(int);
171 static time_t starttime;
173 #define PRNT_CALLS 0x0001
174 #define PRNT_RPC 0x0002
175 #define PRNT_NET 0x0004
176 #define PRNT_FH 0x0008
177 #define PRNT_RC 0x0010
178 #define PRNT_AUTO 0x1000
179 #define PRNT_V2 0x2000
180 #define PRNT_V3 0x4000
181 #define PRNT_V4 0x8000
182 #define PRNT_ALL 0x0fff
190 void usage(char *name)
192 printf("Usage: %s [OPTION]...\n\
194 -m, --mounted\t\tShow statistics on mounted NFS filesystems\n\
195 -c, --client\t\tShow NFS client statistics\n\
196 -s, --server\t\tShow NFS server statistics\n\
197 -2\t\t\tShow NFS version 2 statistics\n\
198 -3\t\t\tShow NFS version 3 statistics\n\
199 -4\t\t\tShow NFS version 4 statistics\n\
200 -o [facility]\t\tShow statistics on particular facilities.\n\
201 nfs\tNFS protocol information\n\
202 rpc\tGeneral RPC information\n\
203 net\tNetwork layer statistics\n\
204 fh\t\tUsage information on the server's file handle cache\n\
205 rc\t\tUsage information on the server's request reply cache\n\
206 all\tSelect all of the above\n\
207 -v, --verbose, --all\tSame as '-o all'\n\
208 -r, --rpc\t\tShow RPC statistics\n\
209 -n, --nfs\t\tShow NFS statistics\n\
210 -Z, --sleep\tSaves stats, pauses, diffs current and saved\n\
211 --version\t\tShow program version\n\
212 --help\t\tWhat you just did\n\
217 static struct option longopts[] =
219 { "acl", 0, 0, 'a' },
220 { "all", 0, 0, 'v' },
221 { "auto", 0, 0, '\3' },
222 { "client", 0, 0, 'c' },
223 { "mounts", 0, 0, 'm' },
224 { "nfs", 0, 0, 'n' },
225 { "rpc", 0, 0, 'r' },
226 { "server", 0, 0, 's' },
227 { "verbose", 0, 0, 'v' },
228 { "zero", 0, 0, 'z' },
229 { "help", 0, 0, '\1' },
230 { "version", 0, 0, '\2' },
231 { "sleep", 0, 0, 'Z' },
236 main(int argc, char **argv)
246 struct statinfo *serverinfo = srvinfo,
247 *serverinfo_tmp = srvinfo_old,
248 *clientinfo = cltinfo,
249 *clientinfo_tmp = cltinfo_old;
251 struct sigaction act = {
252 .sa_handler = unpause,
253 .sa_flags = SA_ONESHOT,
256 if ((progname = strrchr(argv[0], '/')))
261 while ((c = getopt_long(argc, argv, "234acmno:Zvrsz\1\2", longopts, NULL)) != EOF) {
264 fprintf(stderr, "nfsstat: nfs acls are not yet supported.\n");
270 opt_prt |= PRNT_CALLS;
273 if (!strcmp(optarg, "nfs"))
274 opt_prt |= PRNT_CALLS;
275 else if (!strcmp(optarg, "rpc"))
277 else if (!strcmp(optarg, "net"))
279 else if (!strcmp(optarg, "rc"))
281 else if (!strcmp(optarg, "fh"))
283 else if (!strcmp(optarg, "all"))
284 opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH;
286 fprintf(stderr, "nfsstat: unknown category: "
297 opt_prt |= versions[c - '2'];
303 opt_prt |= PRNT_AUTO;
312 fprintf(stderr, "nfsstat: zeroing of nfs statistics "
313 "not yet supported\n");
316 return mounts(MOUNTSFILE);
321 fprintf(stdout, "nfsstat: " VERSION "\n");
324 printf("Try `%s --help' for more information.\n", progname);
330 opt_srv = opt_clt = 1;
333 if (!(opt_srv + opt_clt))
334 opt_srv = opt_clt = 1;
335 if (!(opt_prt & 0xfff)) {
336 opt_prt |= PRNT_CALLS + PRNT_RPC;
338 if (!(opt_prt & 0xe000)) {
339 opt_prt |= PRNT_AUTO;
341 if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
343 "You requested file handle or request cache "
344 "statistics while using the -c option.\n"
345 "This information is available only for the NFS "
350 serverinfo = srvinfo_old;
351 serverinfo_tmp = srvinfo;
352 clientinfo = cltinfo_old;
353 clientinfo_tmp = cltinfo;
357 get_stats(NFSSRVSTAT, serverinfo, &opt_srv, opt_clt, 1);
359 get_stats(NFSCLTSTAT, clientinfo, &opt_clt, opt_srv, 0);
361 /* save stat snapshots; wait for signal; then diff current and saved stats */
363 starttime = time(NULL);
364 printf("Collecting statistics; press CTRL-C to view results from interval (i.e., from pause to CTRL-C).\n");
365 if (sigaction(SIGINT, &act, NULL) != 0) {
366 fprintf(stderr, "Error: couldn't register for signal and pause.\n");
371 get_stats(NFSSRVSTAT, serverinfo_tmp, &opt_srv, opt_clt, 1);
372 diff_stats(serverinfo_tmp, serverinfo, 1);
375 get_stats(NFSCLTSTAT, clientinfo_tmp, &opt_clt, opt_srv, 0);
376 diff_stats(clientinfo_tmp, clientinfo, 0);
381 if (opt_prt & PRNT_NET) {
383 "Server packet stats:\n"
384 "packets udp tcp tcpconn\n",
389 if (opt_prt & PRNT_RPC) {
391 "Server rpc stats:\n"
392 "calls badcalls badauth badclnt xdrcall\n",
397 if (opt_prt & PRNT_RC) {
399 "Server reply cache:\n"
400 "hits misses nocache\n",
407 * 2.2 puts all fh-related info after the 'rc' header
408 * 2.4 puts all fh-related info after the 'fh' header, but relocates
409 * 'stale' to the start and swaps dir and nondir :-(
410 * We preseve the 2.2 order
412 if (opt_prt & PRNT_FH) {
413 if (get_stat_info("fh", srvinfo)) { /* >= 2.4 */
414 int t = srvfhinfo[3];
415 srvfhinfo[3]=srvfhinfo[4];
418 srvfhinfo[5]=srvfhinfo[0]; /* relocate 'stale' */
421 "Server file handle cache:\n"
422 "lookup anon ncachedir ncachedir stale\n",
426 "Server file handle cache:\n"
427 "lookup anon ncachedir ncachedir stale\n",
431 if (opt_prt & PRNT_CALLS) {
432 if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info)))
435 nfsv2name, srvproc2info + 1, sizeof(nfsv2name)/sizeof(char *)
437 if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info)))
440 nfsv3name, srvproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
442 if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc4info))) {
445 nfssrvproc4name, srvproc4info + 1, sizeof(nfssrvproc4name)/sizeof(char *)
448 "Server nfs v4 operations:\n",
449 nfssrvproc4opname, srvproc4opsinfo + 1, sizeof(nfssrvproc4opname)/sizeof(char *)
456 if (opt_prt & PRNT_NET) {
458 "Client packet stats:\n"
459 "packets udp tcp tcpconn\n",
464 if (opt_prt & PRNT_RPC) {
466 "Client rpc stats:\n"
467 "calls retrans authrefrsh\n",
472 if (opt_prt & PRNT_CALLS) {
473 if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info)))
476 nfsv2name, cltproc2info + 1, sizeof(nfsv2name)/sizeof(char *)
478 if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info)))
481 nfsv3name, cltproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
483 if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info)))
486 nfscltproc4name, cltproc4info + 1, sizeof(nfscltproc4name)/sizeof(char *)
495 get_stat_info(const char *sp, struct statinfo *statp)
499 for (ip = statp; ip->tag; ip++) {
500 if (!strcmp(sp, ip->tag))
508 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
513 for (i = 0; i < nr; i++)
514 printf("%s%-8d", i? " " : "", info[i]);
519 print_callstats(const char *hdr, const char **names,
520 unsigned int *info, unsigned int nr)
522 unsigned long long total;
523 unsigned long long pct;
527 for (i = 0, total = 0; i < nr; i++)
531 for (i = 0; i < nr; i += 6) {
532 for (j = 0; j < 6 && i + j < nr; j++)
533 printf("%-13s", names[i+j]);
535 for (j = 0; j < 6 && i + j < nr; j++) {
536 pct = ((unsigned long long) info[i+j]*100)/total;
537 printf("%-8d%3llu%% ", info[i+j], pct);
546 parse_statfile(const char *name, struct statinfo *statp)
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 *sp, *line = buffer;
563 unsigned int total = 0;
565 if ((next = strchr(line, '\n')) != NULL)
567 if (!(sp = strtok(line, " \t")))
570 ip = get_stat_info(sp, statp);
576 for (i = 0; i < cnt; i++) {
577 if (!(sp = strtok(NULL, " \t")))
579 ip->valptr[i] = atoi(sp);
580 total += ip->valptr[i];
582 ip->valptr[cnt - 1] = total;
590 mounts(const char *name)
592 char buffer[4096], *next;
595 /* Being unable to read e.g. the nfsd stats file shouldn't
596 * be a fatal error -- it usually means the module isn't loaded.
598 if ((fp = fopen(name, "r")) == NULL) {
599 fprintf(stderr, "Warning: %s: %m\n", name);
603 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
605 char *device, *mount, *type, *flags;
607 if ((next = strchr(line, '\n')) != NULL)
610 if (!(device = strtok(line, " \t")))
613 if (!(mount = strtok(NULL, " \t")))
616 if (!(type = strtok(NULL, " \t")))
619 if (strcmp(type, "nfs")) {
623 if (!(flags = strtok(NULL, " \t")))
626 printf("%s from %s\n", mount, device);
627 printf(" Flags:\t%s\n", flags);
638 get_stats(const char *file, struct statinfo *info, int *opt, int other_opt,
641 const char *label = is_srv ? "Server" : "Client";
643 if (!parse_statfile(file, info)) {
645 fprintf(stderr, "Warning: No %s Stats (%s: %m). \n", label, file);
653 * This is for proc2/3/4-type stats, where, in the /proc files, the first entry's value
654 * denotes the number of subsequent entries. statinfo value arrays contain an additional
655 * field at the end which contains the sum of all previous elements in the array -- so,
656 * there are stats if the sum's greater than the entry-count.
659 has_stats(const unsigned int *info)
661 return (info[0] && info[info[0] + 1] > info[0]);
665 * take the difference of each individual stat value in 'new' and 'old'
666 * and store the results back into 'new'
669 diff_stats(struct statinfo *new, struct statinfo *old, int is_srv)
671 int i, j, nodiff_first_index, should_diff;
674 * Different stat types have different formats in the /proc
675 * files: for the proc2/3/4-type stats, the first entry has
676 * the total number of subsequent entries; one does not want
677 * to diff that first entry. The other stat types aren't like
678 * this. So, we diff a given entry if it's not of one of the
679 * procX types ("i" < 2 for clt, < 4 for srv), or if it's not
680 * the first entry ("j" > 0).
682 nodiff_first_index = 2 + (2 * is_srv);
684 for (i = 0; old[i].tag; i++) {
685 for (j = 0; j < new[i].nrvals; j++) {
686 should_diff = (i < nodiff_first_index || j > 0);
688 new[i].valptr[j] -= old[i].valptr[j];
692 * Make sure that the "totals" entry (last value in
693 * each stat array) for the procX-type stats has the
694 * "numentries" entry's (first value in procX-type
695 * stat arrays) constant value added-back after the
696 * diff -- i.e., it should always be included in the
699 if (!strncmp("proc", new[i].tag, 4))
700 new[i].valptr[new[i].nrvals - 1] += new[i].valptr[0];
708 int minutes, seconds;
711 endtime = time(NULL);
712 time_diff = difftime(endtime, starttime);
713 minutes = time_diff / 60;
714 seconds = (int)time_diff % 60;
715 printf("Signal received; displaying (only) statistics gathered over the last %d minutes, %d seconds:\n\n", minutes, seconds);