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 #define LABEL_srvnet "Server packet stats:\n"
117 #define LABEL_srvrpc "Server rpc stats:\n"
118 #define LABEL_srvrc "Server reply cache:\n"
119 #define LABEL_srvfh "Server file handle cache:\n"
120 #define LABEL_srvproc2 "Server nfs v2:\n"
121 #define LABEL_srvproc3 "Server nfs v3:\n"
122 #define LABEL_srvproc4 "Server nfs v4:\n"
123 #define LABEL_srvproc4ops "Server nfs v4 operations:\n"
124 #define LABEL_cltnet "Client packet stats:\n"
125 #define LABEL_cltrpc "Client rpc stats:\n"
126 #define LABEL_cltproc2 "Client nfs v2:\n"
127 #define LABEL_cltproc3 "Client nfs v3:\n"
128 #define LABEL_cltproc4 "Client nfs v4:\n"
130 typedef struct statinfo {
134 unsigned int * valptr;
138 * We now build the arrays of statinfos using macros, which will make it easier
139 * to add new variables for --sleep. e.g., SRV(net) expands into the struct
140 * statinfo: { "net", "Server packet stats:\n", 5, srvnetinfo }
142 #define ARRAYSIZE(x) sizeof(x)/sizeof(*x)
143 #define STATINFO(k, t, s...) { #t, LABEL_##k##t, ARRAYSIZE(k##t##info##s), k##t##info##s }
144 #define SRV(t, s...) STATINFO(srv, t, s)
145 #define CLT(t, s...) STATINFO(clt, t, s)
146 #define DECLARE_SRV(n, s...) static statinfo n##s[] = { \
155 { NULL, NULL, 0, NULL }\
157 #define DECLARE_CLT(n, s...) static statinfo n##s[] = { \
163 { NULL, NULL, 0, NULL }\
165 DECLARE_SRV(srvinfo);
166 DECLARE_SRV(srvinfo, _old);
167 DECLARE_CLT(cltinfo);
168 DECLARE_CLT(cltinfo, _old);
170 static void print_numbers(const char *, unsigned int *,
172 static void print_callstats(const char *, const char **,
173 unsigned int *, unsigned int);
174 static int parse_statfile(const char *, struct statinfo *);
176 static statinfo *get_stat_info(const char *, struct statinfo *);
178 static int mounts(const char *);
180 static void get_stats(const char *, struct statinfo *, int *, int,
182 static int has_stats(const unsigned int *);
183 static void diff_stats(struct statinfo *, struct statinfo *, int);
184 static void unpause(int);
186 static time_t starttime;
188 #define PRNT_CALLS 0x0001
189 #define PRNT_RPC 0x0002
190 #define PRNT_NET 0x0004
191 #define PRNT_FH 0x0008
192 #define PRNT_RC 0x0010
193 #define PRNT_AUTO 0x1000
194 #define PRNT_V2 0x2000
195 #define PRNT_V3 0x4000
196 #define PRNT_V4 0x8000
197 #define PRNT_ALL 0x0fff
205 void usage(char *name)
207 printf("Usage: %s [OPTION]...\n\
209 -m, --mounted\t\tShow statistics on mounted NFS filesystems\n\
210 -c, --client\t\tShow NFS client statistics\n\
211 -s, --server\t\tShow NFS server statistics\n\
212 -2\t\t\tShow NFS version 2 statistics\n\
213 -3\t\t\tShow NFS version 3 statistics\n\
214 -4\t\t\tShow NFS version 4 statistics\n\
215 -o [facility]\t\tShow statistics on particular facilities.\n\
216 nfs\tNFS protocol information\n\
217 rpc\tGeneral RPC information\n\
218 net\tNetwork layer statistics\n\
219 fh\t\tUsage information on the server's file handle cache\n\
220 rc\t\tUsage information on the server's request reply cache\n\
221 all\tSelect all of the above\n\
222 -v, --verbose, --all\tSame as '-o all'\n\
223 -r, --rpc\t\tShow RPC statistics\n\
224 -n, --nfs\t\tShow NFS statistics\n\
225 -Z, --sleep\tSaves stats, pauses, diffs current and saved\n\
226 --version\t\tShow program version\n\
227 --help\t\tWhat you just did\n\
232 static struct option longopts[] =
234 { "acl", 0, 0, 'a' },
235 { "all", 0, 0, 'v' },
236 { "auto", 0, 0, '\3' },
237 { "client", 0, 0, 'c' },
238 { "mounts", 0, 0, 'm' },
239 { "nfs", 0, 0, 'n' },
240 { "rpc", 0, 0, 'r' },
241 { "server", 0, 0, 's' },
242 { "verbose", 0, 0, 'v' },
243 { "zero", 0, 0, 'z' },
244 { "help", 0, 0, '\1' },
245 { "version", 0, 0, '\2' },
246 { "sleep", 0, 0, 'Z' },
251 main(int argc, char **argv)
261 struct statinfo *serverinfo = srvinfo,
262 *serverinfo_tmp = srvinfo_old,
263 *clientinfo = cltinfo,
264 *clientinfo_tmp = cltinfo_old;
266 struct sigaction act = {
267 .sa_handler = unpause,
268 .sa_flags = SA_ONESHOT,
271 if ((progname = strrchr(argv[0], '/')))
276 while ((c = getopt_long(argc, argv, "234acmno:Zvrsz\1\2", longopts, NULL)) != EOF) {
279 fprintf(stderr, "nfsstat: nfs acls are not yet supported.\n");
285 opt_prt |= PRNT_CALLS;
288 if (!strcmp(optarg, "nfs"))
289 opt_prt |= PRNT_CALLS;
290 else if (!strcmp(optarg, "rpc"))
292 else if (!strcmp(optarg, "net"))
294 else if (!strcmp(optarg, "rc"))
296 else if (!strcmp(optarg, "fh"))
298 else if (!strcmp(optarg, "all"))
299 opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH;
301 fprintf(stderr, "nfsstat: unknown category: "
312 opt_prt |= versions[c - '2'];
318 opt_prt |= PRNT_AUTO;
327 fprintf(stderr, "nfsstat: zeroing of nfs statistics "
328 "not yet supported\n");
331 return mounts(MOUNTSFILE);
336 fprintf(stdout, "nfsstat: " VERSION "\n");
339 printf("Try `%s --help' for more information.\n", progname);
345 opt_srv = opt_clt = 1;
348 if (!(opt_srv + opt_clt))
349 opt_srv = opt_clt = 1;
350 if (!(opt_prt & 0xfff)) {
351 opt_prt |= PRNT_CALLS + PRNT_RPC;
353 if (!(opt_prt & 0xe000)) {
354 opt_prt |= PRNT_AUTO;
356 if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
358 "You requested file handle or request cache "
359 "statistics while using the -c option.\n"
360 "This information is available only for the NFS "
365 serverinfo = srvinfo_old;
366 serverinfo_tmp = srvinfo;
367 clientinfo = cltinfo_old;
368 clientinfo_tmp = cltinfo;
372 get_stats(NFSSRVSTAT, serverinfo, &opt_srv, opt_clt, 1);
374 get_stats(NFSCLTSTAT, clientinfo, &opt_clt, opt_srv, 0);
376 /* save stat snapshots; wait for signal; then diff current and saved stats */
378 starttime = time(NULL);
379 printf("Collecting statistics; press CTRL-C to view results from interval (i.e., from pause to CTRL-C).\n");
380 if (sigaction(SIGINT, &act, NULL) != 0) {
381 fprintf(stderr, "Error: couldn't register for signal and pause.\n");
386 get_stats(NFSSRVSTAT, serverinfo_tmp, &opt_srv, opt_clt, 1);
387 diff_stats(serverinfo_tmp, serverinfo, 1);
390 get_stats(NFSCLTSTAT, clientinfo_tmp, &opt_clt, opt_srv, 0);
391 diff_stats(clientinfo_tmp, clientinfo, 0);
396 if (opt_prt & PRNT_NET) {
399 "packets udp tcp tcpconn\n",
404 if (opt_prt & PRNT_RPC) {
407 "calls badcalls badauth badclnt xdrcall\n",
412 if (opt_prt & PRNT_RC) {
415 "hits misses nocache\n",
422 * 2.2 puts all fh-related info after the 'rc' header
423 * 2.4 puts all fh-related info after the 'fh' header, but relocates
424 * 'stale' to the start and swaps dir and nondir :-(
425 * We preseve the 2.2 order
427 if (opt_prt & PRNT_FH) {
428 if (get_stat_info("fh", srvinfo)) { /* >= 2.4 */
429 int t = srvfhinfo[3];
430 srvfhinfo[3]=srvfhinfo[4];
433 srvfhinfo[5]=srvfhinfo[0]; /* relocate 'stale' */
437 "lookup anon ncachedir ncachedir stale\n",
442 "lookup anon ncachedir ncachedir stale\n",
446 if (opt_prt & PRNT_CALLS) {
447 if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info)))
450 nfsv2name, srvproc2info + 1, sizeof(nfsv2name)/sizeof(char *)
452 if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info)))
455 nfsv3name, srvproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
457 if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(srvproc4info))) {
460 nfssrvproc4name, srvproc4info + 1, sizeof(nfssrvproc4name)/sizeof(char *)
464 nfssrvproc4opname, srvproc4opsinfo + 1, sizeof(nfssrvproc4opname)/sizeof(char *)
471 if (opt_prt & PRNT_NET) {
474 "packets udp tcp tcpconn\n",
479 if (opt_prt & PRNT_RPC) {
482 "calls retrans authrefrsh\n",
487 if (opt_prt & PRNT_CALLS) {
488 if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info)))
491 nfsv2name, cltproc2info + 1, sizeof(nfsv2name)/sizeof(char *)
493 if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info)))
496 nfsv3name, cltproc3info + 1, sizeof(nfsv3name)/sizeof(char *)
498 if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info)))
501 nfscltproc4name, cltproc4info + 1, sizeof(nfscltproc4name)/sizeof(char *)
510 get_stat_info(const char *sp, struct statinfo *statp)
514 for (ip = statp; ip->tag; ip++) {
515 if (!strcmp(sp, ip->tag))
523 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
528 for (i = 0; i < nr; i++)
529 printf("%s%-8d", i? " " : "", info[i]);
534 print_callstats(const char *hdr, const char **names,
535 unsigned int *info, unsigned int nr)
537 unsigned long long total;
538 unsigned long long pct;
542 for (i = 0, total = 0; i < nr; i++)
546 for (i = 0; i < nr; i += 6) {
547 for (j = 0; j < 6 && i + j < nr; j++)
548 printf("%-13s", names[i+j]);
550 for (j = 0; j < 6 && i + j < nr; j++) {
551 pct = ((unsigned long long) info[i+j]*100)/total;
552 printf("%-8d%3llu%% ", info[i+j], pct);
561 parse_statfile(const char *name, struct statinfo *statp)
563 char buffer[4096], *next;
566 /* Being unable to read e.g. the nfsd stats file shouldn't
567 * be a fatal error -- it usually means the module isn't loaded.
569 if ((fp = fopen(name, "r")) == NULL) {
570 // fprintf(stderr, "Warning: %s: %m\n", name);
574 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
576 char *sp, *line = buffer;
578 unsigned int total = 0;
580 if ((next = strchr(line, '\n')) != NULL)
582 if (!(sp = strtok(line, " \t")))
585 ip = get_stat_info(sp, statp);
591 for (i = 0; i < cnt; i++) {
592 if (!(sp = strtok(NULL, " \t")))
594 ip->valptr[i] = atoi(sp);
595 total += ip->valptr[i];
597 ip->valptr[cnt - 1] = total;
605 mounts(const char *name)
607 char buffer[4096], *next;
610 /* Being unable to read e.g. the nfsd stats file shouldn't
611 * be a fatal error -- it usually means the module isn't loaded.
613 if ((fp = fopen(name, "r")) == NULL) {
614 fprintf(stderr, "Warning: %s: %m\n", name);
618 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
620 char *device, *mount, *type, *flags;
622 if ((next = strchr(line, '\n')) != NULL)
625 if (!(device = strtok(line, " \t")))
628 if (!(mount = strtok(NULL, " \t")))
631 if (!(type = strtok(NULL, " \t")))
634 if (strcmp(type, "nfs")) {
638 if (!(flags = strtok(NULL, " \t")))
641 printf("%s from %s\n", mount, device);
642 printf(" Flags:\t%s\n", flags);
653 get_stats(const char *file, struct statinfo *info, int *opt, int other_opt,
656 const char *label = is_srv ? "Server" : "Client";
658 if (!parse_statfile(file, info)) {
660 fprintf(stderr, "Warning: No %s Stats (%s: %m). \n", label, file);
668 * This is for proc2/3/4-type stats, where, in the /proc files, the first entry's value
669 * denotes the number of subsequent entries. statinfo value arrays contain an additional
670 * field at the end which contains the sum of all previous elements in the array -- so,
671 * there are stats if the sum's greater than the entry-count.
674 has_stats(const unsigned int *info)
676 return (info[0] && info[info[0] + 1] > info[0]);
680 * take the difference of each individual stat value in 'new' and 'old'
681 * and store the results back into 'new'
684 diff_stats(struct statinfo *new, struct statinfo *old, int is_srv)
686 int i, j, nodiff_first_index, should_diff;
689 * Different stat types have different formats in the /proc
690 * files: for the proc2/3/4-type stats, the first entry has
691 * the total number of subsequent entries; one does not want
692 * to diff that first entry. The other stat types aren't like
693 * this. So, we diff a given entry if it's not of one of the
694 * procX types ("i" < 2 for clt, < 4 for srv), or if it's not
695 * the first entry ("j" > 0).
697 nodiff_first_index = 2 + (2 * is_srv);
699 for (i = 0; old[i].tag; i++) {
700 for (j = 0; j < new[i].nrvals; j++) {
701 should_diff = (i < nodiff_first_index || j > 0);
703 new[i].valptr[j] -= old[i].valptr[j];
707 * Make sure that the "totals" entry (last value in
708 * each stat array) for the procX-type stats has the
709 * "numentries" entry's (first value in procX-type
710 * stat arrays) constant value added-back after the
711 * diff -- i.e., it should always be included in the
714 if (!strncmp("proc", new[i].tag, 4))
715 new[i].valptr[new[i].nrvals - 1] += new[i].valptr[0];
723 int minutes, seconds;
726 endtime = time(NULL);
727 time_diff = difftime(endtime, starttime);
728 minutes = time_diff / 60;
729 seconds = (int)time_diff % 60;
730 printf("Signal received; displaying (only) statistics gathered over the last %d minutes, %d seconds:\n\n", minutes, seconds);