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_tmp[20]; /* NFSv2 call counts ([0] == 18) */
29 static unsigned int cltproc2info[20], cltproc2info_tmp[20]; /* NFSv2 call counts ([0] == 18) */
30 static unsigned int srvproc3info[24], srvproc3info_tmp[24]; /* NFSv3 call counts ([0] == 22) */
31 static unsigned int cltproc3info[24], cltproc3info_tmp[24]; /* NFSv3 call counts ([0] == 22) */
32 static unsigned int srvproc4info[4], srvproc4info_tmp[4]; /* NFSv4 call counts ([0] == 2) */
33 static unsigned int cltproc4info[34], cltproc4info_tmp[34]; /* NFSv4 call counts ([0] == 32) */
34 static unsigned int srvproc4opsinfo[42], srvproc4opsinfo_tmp[42]; /* NFSv4 call counts ([0] == 40) */
35 static unsigned int srvnetinfo[5], srvnetinfo_tmp[5]; /* 0 # of received packets
40 static unsigned int cltnetinfo[5], cltnetinfo_tmp[5]; /* 0 # of received packets
46 static unsigned int srvrpcinfo[6], srvrpcinfo_tmp[6]; /* 0 total # of RPC calls
47 * 1 total # of bad calls
49 * 3 authentication failed
52 static unsigned int cltrpcinfo[4], cltrpcinfo_tmp[4]; /* 0 total # of RPC calls
53 * 1 retransmitted calls
57 static unsigned int srvrcinfo[9], srvrcinfo_tmp[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_tmp[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[32] = {
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",
105 static const char * nfssrvproc4opname[40] = {
106 "op0-unused", "op1-unused", "op2-future", "access", "close", "commit",
107 "create", "delegpurge", "delegreturn", "getattr", "getfh", "link",
108 "lock", "lockt", "locku", "lookup", "lookup_root", "nverify",
109 "open", "openattr", "open_conf", "open_dgrd", "putfh", "putpubfh",
110 "putrootfh", "read", "readdir", "readlink", "remove", "rename",
111 "renew", "restorefh", "savefh", "secinfo", "setattr", "setcltid",
112 "setcltidconf", "verify", "write", "rellockowner"
115 typedef struct statinfo {
118 unsigned int * valptr;
122 * We now build the arrays of statinfos using macros, which will make it easier
123 * to add new variables for --diff-stat.
124 * e.g., SRV(net) expands into the struct statinfo: { "net", 5, srvnetinfo }
126 #define ARRAYSIZE(x) sizeof(x)/sizeof(*x)
127 #define STATINFO(k, t, s...) { #t, ARRAYSIZE(k##t##info##s), k##t##info##s }
128 #define SRV(t, s...) STATINFO(srv, t, s)
129 #define CLT(t, s...) STATINFO(clt, t, s)
130 #define DECLARE_SRV(n, s...) static statinfo n##s[] = { \
141 #define DECLARE_CLT(n, s...) static statinfo n##s[] = { \
149 DECLARE_SRV(srvinfo);
150 DECLARE_SRV(srvinfo, _tmp);
151 DECLARE_CLT(cltinfo);
152 DECLARE_CLT(cltinfo, _tmp);
154 static void print_numbers(const char *, unsigned int *,
156 static void print_callstats(const char *, const char **,
157 unsigned int *, unsigned int);
158 static int parse_statfile(const char *, struct statinfo *);
160 static statinfo *get_stat_info(const char *, struct statinfo *);
162 static int mounts(const char *);
164 static void get_stats(const char *, statinfo *, int *, int, const char *);
165 static int has_stats(const unsigned int *);
166 static void copy_stats(statinfo *, statinfo *);
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 -D, --diff-stat\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 { "diff-stat", 0, 0, 'D' },
235 main(int argc, char **argv)
245 struct sigaction act = {
246 .sa_handler = unpause,
247 .sa_flags = SA_ONESHOT,
250 if ((progname = strrchr(argv[0], '/')))
255 while ((c = getopt_long(argc, argv, "234acmno:Dvrsz\1\2", longopts, NULL)) != EOF) {
258 fprintf(stderr, "nfsstat: nfs acls are not yet supported.\n");
264 opt_prt |= PRNT_CALLS;
267 if (!strcmp(optarg, "nfs"))
268 opt_prt |= PRNT_CALLS;
269 else if (!strcmp(optarg, "rpc"))
271 else if (!strcmp(optarg, "net"))
273 else if (!strcmp(optarg, "rc"))
275 else if (!strcmp(optarg, "fh"))
277 else if (!strcmp(optarg, "all"))
278 opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH;
280 fprintf(stderr, "nfsstat: unknown category: "
291 opt_prt |= versions[c - '2'];
297 opt_prt |= PRNT_AUTO;
306 fprintf(stderr, "nfsstat: zeroing of nfs statistics "
307 "not yet supported\n");
310 return mounts(MOUNTSFILE);
315 fprintf(stdout, "nfsstat: " VERSION "\n");
318 printf("Try `%s --help' for more information.\n", progname);
324 opt_srv = opt_clt = 1;
327 if (!(opt_srv + opt_clt))
328 opt_srv = opt_clt = 1;
329 if (!(opt_prt & 0xfff)) {
330 opt_prt |= PRNT_CALLS + PRNT_RPC;
332 if (!(opt_prt & 0xe000)) {
333 opt_prt |= PRNT_AUTO;
335 if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
337 "You requested file handle or request cache "
338 "statistics while using the -c option.\n"
339 "This information is available only for the NFS "
344 get_stats(NFSSRVSTAT, srvinfo, &opt_srv, opt_clt, "Server");
346 get_stats(NFSCLTSTAT, cltinfo, &opt_clt, opt_srv, "Client");
348 /* save stat snapshots; wait for signal; then diff current and saved stats */
350 starttime = time(NULL);
351 printf("Collecting statistics; press CTRL-C to view results from interval (i.e., from pause to CTRL-C).\n");
353 copy_stats(srvinfo_tmp, srvinfo);
355 copy_stats(cltinfo_tmp, cltinfo);
356 if (sigaction(SIGINT, &act, NULL) != 0) {
357 fprintf(stderr, "Error: couldn't register for signal and pause.\n");
362 get_stats(NFSSRVSTAT, srvinfo, &opt_srv, opt_clt, "Server");
363 diff_stats(srvinfo, srvinfo_tmp);
366 get_stats(NFSCLTSTAT, cltinfo, &opt_clt, opt_srv, "Client");
367 diff_stats(cltinfo, cltinfo_tmp);
372 if (opt_prt & PRNT_NET) {
374 "Server packet stats:\n"
375 "packets udp tcp tcpconn\n",
380 if (opt_prt & PRNT_RPC) {
382 "Server rpc stats:\n"
383 "calls badcalls badauth badclnt xdrcall\n",
388 if (opt_prt & PRNT_RC) {
390 "Server reply cache:\n"
391 "hits misses nocache\n",
398 * 2.2 puts all fh-related info after the 'rc' header
399 * 2.4 puts all fh-related info after the 'fh' header, but relocates
400 * 'stale' to the start and swaps dir and nondir :-(
401 * We preseve the 2.2 order
403 if (opt_prt & PRNT_FH) {
404 if (get_stat_info("fh", srvinfo)) { /* >= 2.4 */
405 int t = srvfhinfo[3];
406 srvfhinfo[3]=srvfhinfo[4];
409 srvfhinfo[5]=srvfhinfo[0]; /* relocate 'stale' */
412 "Server file handle cache:\n"
413 "lookup anon ncachedir ncachedir stale\n",
417 "Server file handle cache:\n"
418 "lookup anon ncachedir ncachedir stale\n",
422 if (opt_prt & PRNT_CALLS) {
423 if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info)))
426 nfsv2name, srvproc2info + 1, sizeof(nfsv2name)/sizeof(char *)
428 if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info)))
431 nfsv3name, srvproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
433 if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc4info))) {
436 nfssrvproc4name, srvproc4info + 1, sizeof(nfssrvproc4name)/sizeof(char *)
439 "Server nfs v4 operations:\n",
440 nfssrvproc4opname, srvproc4opsinfo + 1, sizeof(nfssrvproc4opname)/sizeof(char *)
447 if (opt_prt & PRNT_NET) {
449 "Client packet stats:\n"
450 "packets udp tcp tcpconn\n",
455 if (opt_prt & PRNT_RPC) {
457 "Client rpc stats:\n"
458 "calls retrans authrefrsh\n",
463 if (opt_prt & PRNT_CALLS) {
464 if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info)))
467 nfsv2name, cltproc2info + 1, sizeof(nfsv2name)/sizeof(char *)
469 if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info)))
472 nfsv3name, cltproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
474 if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info)))
477 nfscltproc4name, cltproc4info + 1, sizeof(nfscltproc4name)/sizeof(char *)
486 get_stat_info(const char *sp, struct statinfo *statp)
490 for (ip = statp; ip->tag; ip++) {
491 if (!strcmp(sp, ip->tag))
499 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
504 for (i = 0; i < nr; i++)
505 printf("%s%-8d", i? " " : "", info[i]);
510 print_callstats(const char *hdr, const char **names,
511 unsigned int *info, unsigned int nr)
513 unsigned long long total;
514 unsigned long long pct;
518 for (i = 0, total = 0; i < nr; i++)
522 for (i = 0; i < nr; i += 6) {
523 for (j = 0; j < 6 && i + j < nr; j++)
524 printf("%-13s", names[i+j]);
526 for (j = 0; j < 6 && i + j < nr; j++) {
527 pct = ((unsigned long long) info[i+j]*100)/total;
528 printf("%-8d%3llu%% ", info[i+j], pct);
537 parse_statfile(const char *name, struct statinfo *statp)
539 char buffer[4096], *next;
542 /* Being unable to read e.g. the nfsd stats file shouldn't
543 * be a fatal error -- it usually means the module isn't loaded.
545 if ((fp = fopen(name, "r")) == NULL) {
546 // fprintf(stderr, "Warning: %s: %m\n", name);
550 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
552 char *sp, *line = buffer;
554 unsigned int total = 0;
556 if ((next = strchr(line, '\n')) != NULL)
558 if (!(sp = strtok(line, " \t")))
561 ip = get_stat_info(sp, statp);
567 for (i = 0; i < cnt; i++) {
568 if (!(sp = strtok(NULL, " \t")))
570 ip->valptr[i] = atoi(sp);
571 total += ip->valptr[i];
573 ip->valptr[i] = total;
581 mounts(const char *name)
583 char buffer[4096], *next;
586 /* Being unable to read e.g. the nfsd stats file shouldn't
587 * be a fatal error -- it usually means the module isn't loaded.
589 if ((fp = fopen(name, "r")) == NULL) {
590 fprintf(stderr, "Warning: %s: %m\n", name);
594 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
596 char *device, *mount, *type, *flags;
598 if ((next = strchr(line, '\n')) != NULL)
601 if (!(device = strtok(line, " \t")))
604 if (!(mount = strtok(NULL, " \t")))
607 if (!(type = strtok(NULL, " \t")))
610 if (strcmp(type, "nfs")) {
614 if (!(flags = strtok(NULL, " \t")))
617 printf("%s from %s\n", mount, device);
618 printf(" Flags:\t%s\n", flags);
629 get_stats(const char *file, statinfo *info, int *opt, int other_opt, const char *label)
631 if (!parse_statfile(file, info)) {
633 fprintf(stderr, "Warning: No %s Stats (%s: %m). \n", label, file);
641 has_stats(const unsigned int *info)
643 return (info[0] && info[info[0] + 1] != info[0]);
646 /* clone 'src' to 'dest' */
648 copy_stats(struct statinfo *dest, struct statinfo *src)
652 for (i = 0; src[i].tag; i++) {
653 dest[i].tag = src[i].tag;
654 dest[i].nrvals = src[i].nrvals;
655 for (j = 0; j < dest[i].nrvals; j++)
656 dest[i].valptr[j] = src[i].valptr[j];
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, should_diff;
669 is_srv = (new == srvinfo);
670 for (i = 0; old[i].tag; i++) {
671 for (j = 0; j < new[i].nrvals; j++) {
672 /* skip items in valptr that shouldn't be changed */
673 should_diff = (i < (3 + is_srv) || j > 0);
675 new[i].valptr[j] -= old[i].valptr[j];
684 int minutes, seconds;
687 endtime = time(NULL);
688 time_diff = difftime(endtime, starttime);
689 minutes = time_diff / 60;
690 seconds = (int)time_diff % 60;
691 printf("Signal received; displaying (only) statistics gathered over the last %d minutes, %d seconds:\n\n", minutes, seconds);