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 *, statinfo *, int *, int, const char *);
166 static int has_stats(const unsigned int *);
167 static void diff_stats(statinfo *, statinfo *);
168 static void unpause(int);
170 static time_t starttime;
172 #define PRNT_CALLS 0x0001
173 #define PRNT_RPC 0x0002
174 #define PRNT_NET 0x0004
175 #define PRNT_FH 0x0008
176 #define PRNT_RC 0x0010
177 #define PRNT_AUTO 0x1000
178 #define PRNT_V2 0x2000
179 #define PRNT_V3 0x4000
180 #define PRNT_V4 0x8000
181 #define PRNT_ALL 0x0fff
189 void usage(char *name)
191 printf("Usage: %s [OPTION]...\n\
193 -m, --mounted\t\tShow statistics on mounted NFS filesystems\n\
194 -c, --client\t\tShow NFS client statistics\n\
195 -s, --server\t\tShow NFS server statistics\n\
196 -2\t\t\tShow NFS version 2 statistics\n\
197 -3\t\t\tShow NFS version 3 statistics\n\
198 -4\t\t\tShow NFS version 4 statistics\n\
199 -o [facility]\t\tShow statistics on particular facilities.\n\
200 nfs\tNFS protocol information\n\
201 rpc\tGeneral RPC information\n\
202 net\tNetwork layer statistics\n\
203 fh\t\tUsage information on the server's file handle cache\n\
204 rc\t\tUsage information on the server's request reply cache\n\
205 all\tSelect all of the above\n\
206 -v, --verbose, --all\tSame as '-o all'\n\
207 -r, --rpc\t\tShow RPC statistics\n\
208 -n, --nfs\t\tShow NFS statistics\n\
209 -Z, --sleep\tSaves stats, pauses, diffs current and saved\n\
210 --version\t\tShow program version\n\
211 --help\t\tWhat you just did\n\
216 static struct option longopts[] =
218 { "acl", 0, 0, 'a' },
219 { "all", 0, 0, 'v' },
220 { "auto", 0, 0, '\3' },
221 { "client", 0, 0, 'c' },
222 { "mounts", 0, 0, 'm' },
223 { "nfs", 0, 0, 'n' },
224 { "rpc", 0, 0, 'r' },
225 { "server", 0, 0, 's' },
226 { "verbose", 0, 0, 'v' },
227 { "zero", 0, 0, 'z' },
228 { "help", 0, 0, '\1' },
229 { "version", 0, 0, '\2' },
230 { "sleep", 0, 0, 'Z' },
235 main(int argc, char **argv)
245 struct statinfo *serverinfo = srvinfo,
246 *serverinfo_tmp = srvinfo_old,
247 *clientinfo = cltinfo,
248 *clientinfo_tmp = cltinfo_old;
250 struct sigaction act = {
251 .sa_handler = unpause,
252 .sa_flags = SA_ONESHOT,
255 if ((progname = strrchr(argv[0], '/')))
260 while ((c = getopt_long(argc, argv, "234acmno:Zvrsz\1\2", longopts, NULL)) != EOF) {
263 fprintf(stderr, "nfsstat: nfs acls are not yet supported.\n");
269 opt_prt |= PRNT_CALLS;
272 if (!strcmp(optarg, "nfs"))
273 opt_prt |= PRNT_CALLS;
274 else if (!strcmp(optarg, "rpc"))
276 else if (!strcmp(optarg, "net"))
278 else if (!strcmp(optarg, "rc"))
280 else if (!strcmp(optarg, "fh"))
282 else if (!strcmp(optarg, "all"))
283 opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH;
285 fprintf(stderr, "nfsstat: unknown category: "
296 opt_prt |= versions[c - '2'];
302 opt_prt |= PRNT_AUTO;
311 fprintf(stderr, "nfsstat: zeroing of nfs statistics "
312 "not yet supported\n");
315 return mounts(MOUNTSFILE);
320 fprintf(stdout, "nfsstat: " VERSION "\n");
323 printf("Try `%s --help' for more information.\n", progname);
329 opt_srv = opt_clt = 1;
332 if (!(opt_srv + opt_clt))
333 opt_srv = opt_clt = 1;
334 if (!(opt_prt & 0xfff)) {
335 opt_prt |= PRNT_CALLS + PRNT_RPC;
337 if (!(opt_prt & 0xe000)) {
338 opt_prt |= PRNT_AUTO;
340 if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
342 "You requested file handle or request cache "
343 "statistics while using the -c option.\n"
344 "This information is available only for the NFS "
349 serverinfo = srvinfo_old;
350 serverinfo_tmp = srvinfo;
351 clientinfo = cltinfo_old;
352 clientinfo_tmp = cltinfo;
356 get_stats(NFSSRVSTAT, serverinfo, &opt_srv, opt_clt, "Server");
358 get_stats(NFSCLTSTAT, clientinfo, &opt_clt, opt_srv, "Client");
360 /* save stat snapshots; wait for signal; then diff current and saved stats */
362 starttime = time(NULL);
363 printf("Collecting statistics; press CTRL-C to view results from interval (i.e., from pause to CTRL-C).\n");
364 if (sigaction(SIGINT, &act, NULL) != 0) {
365 fprintf(stderr, "Error: couldn't register for signal and pause.\n");
370 get_stats(NFSSRVSTAT, serverinfo_tmp, &opt_srv, opt_clt, "Server");
371 diff_stats(serverinfo_tmp, serverinfo);
374 get_stats(NFSCLTSTAT, clientinfo_tmp, &opt_clt, opt_srv, "Client");
375 diff_stats(clientinfo_tmp, clientinfo);
380 if (opt_prt & PRNT_NET) {
382 "Server packet stats:\n"
383 "packets udp tcp tcpconn\n",
388 if (opt_prt & PRNT_RPC) {
390 "Server rpc stats:\n"
391 "calls badcalls badauth badclnt xdrcall\n",
396 if (opt_prt & PRNT_RC) {
398 "Server reply cache:\n"
399 "hits misses nocache\n",
406 * 2.2 puts all fh-related info after the 'rc' header
407 * 2.4 puts all fh-related info after the 'fh' header, but relocates
408 * 'stale' to the start and swaps dir and nondir :-(
409 * We preseve the 2.2 order
411 if (opt_prt & PRNT_FH) {
412 if (get_stat_info("fh", srvinfo)) { /* >= 2.4 */
413 int t = srvfhinfo[3];
414 srvfhinfo[3]=srvfhinfo[4];
417 srvfhinfo[5]=srvfhinfo[0]; /* relocate 'stale' */
420 "Server file handle cache:\n"
421 "lookup anon ncachedir ncachedir stale\n",
425 "Server file handle cache:\n"
426 "lookup anon ncachedir ncachedir stale\n",
430 if (opt_prt & PRNT_CALLS) {
431 if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info)))
434 nfsv2name, srvproc2info + 1, sizeof(nfsv2name)/sizeof(char *)
436 if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info)))
439 nfsv3name, srvproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
441 if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc4info))) {
444 nfssrvproc4name, srvproc4info + 1, sizeof(nfssrvproc4name)/sizeof(char *)
447 "Server nfs v4 operations:\n",
448 nfssrvproc4opname, srvproc4opsinfo + 1, sizeof(nfssrvproc4opname)/sizeof(char *)
455 if (opt_prt & PRNT_NET) {
457 "Client packet stats:\n"
458 "packets udp tcp tcpconn\n",
463 if (opt_prt & PRNT_RPC) {
465 "Client rpc stats:\n"
466 "calls retrans authrefrsh\n",
471 if (opt_prt & PRNT_CALLS) {
472 if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info)))
475 nfsv2name, cltproc2info + 1, sizeof(nfsv2name)/sizeof(char *)
477 if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info)))
480 nfsv3name, cltproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
482 if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info)))
485 nfscltproc4name, cltproc4info + 1, sizeof(nfscltproc4name)/sizeof(char *)
494 get_stat_info(const char *sp, struct statinfo *statp)
498 for (ip = statp; ip->tag; ip++) {
499 if (!strcmp(sp, ip->tag))
507 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
512 for (i = 0; i < nr; i++)
513 printf("%s%-8d", i? " " : "", info[i]);
518 print_callstats(const char *hdr, const char **names,
519 unsigned int *info, unsigned int nr)
521 unsigned long long total;
522 unsigned long long pct;
526 for (i = 0, total = 0; i < nr; i++)
530 for (i = 0; i < nr; i += 6) {
531 for (j = 0; j < 6 && i + j < nr; j++)
532 printf("%-13s", names[i+j]);
534 for (j = 0; j < 6 && i + j < nr; j++) {
535 pct = ((unsigned long long) info[i+j]*100)/total;
536 printf("%-8d%3llu%% ", info[i+j], pct);
545 parse_statfile(const char *name, struct statinfo *statp)
547 char buffer[4096], *next;
550 /* Being unable to read e.g. the nfsd stats file shouldn't
551 * be a fatal error -- it usually means the module isn't loaded.
553 if ((fp = fopen(name, "r")) == NULL) {
554 // fprintf(stderr, "Warning: %s: %m\n", name);
558 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
560 char *sp, *line = buffer;
562 unsigned int total = 0;
564 if ((next = strchr(line, '\n')) != NULL)
566 if (!(sp = strtok(line, " \t")))
569 ip = get_stat_info(sp, statp);
575 for (i = 0; i < cnt; i++) {
576 if (!(sp = strtok(NULL, " \t")))
578 ip->valptr[i] = atoi(sp);
579 total += ip->valptr[i];
581 ip->valptr[cnt - 1] = total;
589 mounts(const char *name)
591 char buffer[4096], *next;
594 /* Being unable to read e.g. the nfsd stats file shouldn't
595 * be a fatal error -- it usually means the module isn't loaded.
597 if ((fp = fopen(name, "r")) == NULL) {
598 fprintf(stderr, "Warning: %s: %m\n", name);
602 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
604 char *device, *mount, *type, *flags;
606 if ((next = strchr(line, '\n')) != NULL)
609 if (!(device = strtok(line, " \t")))
612 if (!(mount = strtok(NULL, " \t")))
615 if (!(type = strtok(NULL, " \t")))
618 if (strcmp(type, "nfs")) {
622 if (!(flags = strtok(NULL, " \t")))
625 printf("%s from %s\n", mount, device);
626 printf(" Flags:\t%s\n", flags);
637 get_stats(const char *file, statinfo *info, int *opt, int other_opt, const char *label)
639 if (!parse_statfile(file, info)) {
641 fprintf(stderr, "Warning: No %s Stats (%s: %m). \n", label, file);
649 * This is for proc2/3/4-type stats, where, in the /proc files, the first entry's value
650 * denotes the number of subsequent entries. statinfo value arrays contain an additional
651 * field at the end which contains the sum of all previous elements in the array -- so,
652 * there are stats if the sum's greater than the entry-count.
655 has_stats(const unsigned int *info)
657 return (info[0] && info[info[0] + 1] > info[0]);
661 * take the difference of each individual stat value in 'new' and 'old'
662 * and store the results back into 'new'
665 diff_stats(struct statinfo *new, struct statinfo *old)
667 int i, j, is_srv, nodiff_first_index, should_diff;
670 * Different stat types have different formats in the /proc
671 * files: for the proc2/3/4-type stats, the first entry has
672 * the total number of subsequent entries; one does not want
673 * to diff that first entry. The other stat types aren't like
674 * this. So, we diff a given entry if it's not of one of the
675 * procX types ("i" < 2 for clt, < 4 for srv), or if it's not
676 * the first entry ("j" > 0).
678 is_srv = (new == srvinfo);
679 nodiff_first_index = 2 + (2 * is_srv);
681 for (i = 0; old[i].tag; i++) {
682 for (j = 0; j < new[i].nrvals; j++) {
683 should_diff = (i < nodiff_first_index || j > 0);
685 new[i].valptr[j] -= old[i].valptr[j];
689 * Make sure that the "totals" entry (last value in
690 * each stat array) for the procX-type stats has the
691 * "numentries" entry's (first value in procX-type
692 * stat arrays) constant value added-back after the
693 * diff -- i.e., it should always be included in the
696 if (!strncmp("proc", new[i].tag, 4))
697 new[i].valptr[new[i].nrvals - 1] += new[i].valptr[0];
705 int minutes, seconds;
708 endtime = time(NULL);
709 time_diff = difftime(endtime, starttime);
710 minutes = time_diff / 60;
711 seconds = (int)time_diff % 60;
712 printf("Signal received; displaying (only) statistics gathered over the last %d minutes, %d seconds:\n\n", minutes, seconds);