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_all_stats(int, int, int);
171 static void print_server_stats(int, int);
172 static void print_client_stats(int, int);
173 static void print_stats_list(int, int, int);
174 static void print_numbers(const char *, unsigned int *,
176 static void print_callstats(const char *, const char **,
177 unsigned int *, unsigned int);
178 static void print_callstats_list(const char *, const char **,
179 unsigned int *, unsigned int);
180 static int parse_raw_statfile(const char *, struct statinfo *);
181 static int parse_pretty_statfile(const char *, struct statinfo *);
183 static statinfo *get_stat_info(const char *, struct statinfo *);
185 static int mounts(const char *);
187 static void get_stats(const char *, struct statinfo *, int *, int,
189 static int has_stats(const unsigned int *);
190 static int has_rpcstats(const unsigned int *, int);
191 static void diff_stats(struct statinfo *, struct statinfo *, int);
192 static void unpause(int);
193 static void update_old_counters(struct statinfo *, struct statinfo *);
195 static time_t starttime;
197 #define PRNT_CALLS 0x0001
198 #define PRNT_RPC 0x0002
199 #define PRNT_NET 0x0004
200 #define PRNT_FH 0x0008
201 #define PRNT_RC 0x0010
202 #define PRNT_AUTO 0x1000
203 #define PRNT_V2 0x2000
204 #define PRNT_V3 0x4000
205 #define PRNT_V4 0x8000
206 #define PRNT_ALL 0x0fff
214 void usage(char *name)
216 printf("Usage: %s [OPTION]...\n\
218 -m, --mounts Show statistics on mounted NFS filesystems\n\
219 -c, --client Show NFS client statistics\n\
220 -s, --server Show NFS server statistics\n\
221 -2 Show NFS version 2 statistics\n\
222 -3 Show NFS version 3 statistics\n\
223 -4 Show NFS version 4 statistics\n\
224 -o [facility] Show statistics on particular facilities.\n\
225 nfs NFS protocol information\n\
226 rpc General RPC information\n\
227 net Network layer statistics\n\
228 fh Usage information on the server's file handle cache\n\
229 rc Usage information on the server's request reply cache\n\
230 all Select all of the above\n\
231 -v, --verbose, --all Same as '-o all'\n\
232 -r, --rpc Show RPC statistics\n\
233 -n, --nfs Show NFS statistics\n\
234 -Z[#], --sleep[=#] Collects stats until interrupted.\n\
235 Cumulative stats are then printed\n\
236 If # is provided, stats will be output every\n\
238 -S, --since file Shows difference between current stats and those in 'file'\n\
239 -l, --list Prints stats in list format\n\
240 --version Show program version\n\
241 --help What you just did\n\
246 static struct option longopts[] =
248 { "acl", 0, 0, 'a' },
249 { "all", 0, 0, 'v' },
250 { "auto", 0, 0, '\3' },
251 { "client", 0, 0, 'c' },
252 { "mounted", 0, 0, 'm' },
253 { "nfs", 0, 0, 'n' },
254 { "rpc", 0, 0, 'r' },
255 { "server", 0, 0, 's' },
256 { "verbose", 0, 0, 'v' },
257 { "zero", 0, 0, 'z' },
258 { "help", 0, 0, '\1' },
259 { "version", 0, 0, '\2' },
260 { "sleep", 2, 0, 'Z' },
261 { "since", 1, 0, 'S' },
262 { "list", 0, 0, 'l' },
268 main(int argc, char **argv)
279 *serverfile = NFSSRVSTAT,
280 *clientfile = NFSCLTSTAT;
282 struct statinfo *serverinfo = srvinfo,
283 *serverinfo_tmp = srvinfo_old,
284 *clientinfo = cltinfo,
285 *clientinfo_tmp = cltinfo_old;
287 struct sigaction act = {
288 .sa_handler = unpause,
289 .sa_flags = SA_ONESHOT,
292 if ((progname = strrchr(argv[0], '/')))
297 while ((c = getopt_long(argc, argv, "234acmno:Z::S:vrslz\1\2", longopts, NULL)) != EOF) {
300 fprintf(stderr, "nfsstat: nfs acls are not yet supported.\n");
306 opt_prt |= PRNT_CALLS;
309 if (!strcmp(optarg, "nfs"))
310 opt_prt |= PRNT_CALLS;
311 else if (!strcmp(optarg, "rpc"))
313 else if (!strcmp(optarg, "net"))
315 else if (!strcmp(optarg, "rc"))
317 else if (!strcmp(optarg, "fh"))
319 else if (!strcmp(optarg, "all"))
320 opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH;
322 fprintf(stderr, "nfsstat: unknown category: "
330 sleep_time = atoi(optarg);
341 opt_prt |= versions[c - '2'];
347 opt_prt |= PRNT_AUTO;
359 fprintf(stderr, "nfsstat: zeroing of nfs statistics "
360 "not yet supported\n");
363 return mounts(MOUNTSFILE);
368 fprintf(stdout, "nfsstat: " VERSION "\n");
371 printf("Try `%s --help' for more information.\n", progname);
377 opt_srv = opt_clt = 1;
380 if (!(opt_srv + opt_clt))
381 opt_srv = opt_clt = 1;
382 if (!(opt_prt & 0xfff)) {
383 opt_prt |= PRNT_CALLS + PRNT_RPC;
385 if (!(opt_prt & 0xe000)) {
386 opt_prt |= PRNT_AUTO;
388 if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
390 "You requested file handle or request cache "
391 "statistics while using the -c option.\n"
392 "This information is available only for the NFS "
396 if (opt_since || opt_sleep) {
397 serverinfo = srvinfo_old;
398 serverinfo_tmp = srvinfo;
399 clientinfo = cltinfo_old;
400 clientinfo_tmp = cltinfo;
404 get_stats(serverfile, serverinfo, &opt_srv, opt_clt, 1);
406 get_stats(clientfile, clientinfo, &opt_clt, opt_srv, 0);
408 if (opt_sleep && !sleep_time) {
409 starttime = time(NULL);
410 printf("Collecting statistics; press CTRL-C to view results from interval (i.e., from pause to CTRL-C).\n");
411 if (sigaction(SIGINT, &act, NULL) != 0) {
412 fprintf(stderr, "Error: couldn't register for signal and pause.\n");
418 if (opt_since || opt_sleep) {
420 get_stats(NFSSRVSTAT, serverinfo_tmp, &opt_srv, opt_clt, 1);
421 diff_stats(serverinfo_tmp, serverinfo, 1);
424 get_stats(NFSCLTSTAT, clientinfo_tmp, &opt_clt, opt_srv, 0);
425 diff_stats(clientinfo_tmp, clientinfo, 0);
431 get_stats(NFSSRVSTAT, serverinfo_tmp , &opt_srv, opt_clt, 1);
432 diff_stats(serverinfo_tmp, serverinfo, 1);
435 get_stats(NFSCLTSTAT, clientinfo_tmp, &opt_clt, opt_srv, 0);
436 diff_stats(clientinfo_tmp, clientinfo, 0);
439 print_stats_list(opt_srv, opt_clt, opt_prt);
441 print_all_stats(opt_srv, opt_clt, opt_prt);
446 update_old_counters(serverinfo_tmp, serverinfo);
448 update_old_counters(clientinfo_tmp, clientinfo);
454 print_stats_list(opt_srv, opt_clt, opt_prt);
456 print_all_stats(opt_srv, opt_clt, opt_prt);
464 print_all_stats (int opt_srv, int opt_clt, int opt_prt)
466 print_server_stats(opt_srv, opt_prt);
467 print_client_stats(opt_clt, opt_prt);
471 print_server_stats(int opt_srv, int opt_prt)
476 if (opt_prt & PRNT_NET) {
477 if (opt_sleep && !has_rpcstats(srvnetinfo, 4)) {
479 print_numbers( LABEL_srvnet
480 "packets udp tcp tcpconn\n",
485 if (opt_prt & PRNT_RPC) {
486 if (opt_sleep && !has_rpcstats(srvrpcinfo, 5)) {
489 print_numbers(LABEL_srvrpc
490 "calls badcalls badauth badclnt xdrcall\n",
495 if (opt_prt & PRNT_RC) {
496 if (opt_sleep && !has_rpcstats(srvrcinfo, 3)) {
499 print_numbers(LABEL_srvrc
500 "hits misses nocache\n",
507 * 2.2 puts all fh-related info after the 'rc' header
508 * 2.4 puts all fh-related info after the 'fh' header, but relocates
509 * 'stale' to the start and swaps dir and nondir :-(
510 * We preseve the 2.2 order
512 if (opt_prt & PRNT_FH) {
513 if (get_stat_info("fh", srvinfo)) { /* >= 2.4 */
514 int t = srvfhinfo[3];
515 srvfhinfo[3]=srvfhinfo[4];
518 srvfhinfo[5]=srvfhinfo[0]; /* relocate 'stale' */
522 "lookup anon ncachedir ncachedir stale\n",
527 "lookup anon ncachedir ncachedir stale\n",
531 if (opt_prt & PRNT_CALLS) {
532 if ((opt_prt & PRNT_V2) ||
533 ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info))) {
534 if (opt_sleep && !has_stats(srvproc2info)) {
537 print_callstats(LABEL_srvproc2,
538 nfsv2name, srvproc2info + 1,
539 sizeof(nfsv2name)/sizeof(char *));
542 if ((opt_prt & PRNT_V3) ||
543 ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info))) {
544 if (opt_sleep && !has_stats(srvproc3info)) {
547 print_callstats(LABEL_srvproc3,
548 nfsv3name, srvproc3info + 1,
549 sizeof(nfsv3name)/sizeof(char *));
552 if ((opt_prt & PRNT_V4) ||
553 ((opt_prt & PRNT_AUTO) && has_stats(srvproc4info))) {
554 if (opt_sleep && !has_stats(srvproc4info)) {
557 print_callstats( LABEL_srvproc4,
558 nfssrvproc4name, srvproc4info + 1,
559 sizeof(nfssrvproc4name)/sizeof(char *));
560 print_callstats(LABEL_srvproc4ops,
561 nfssrvproc4opname, srvproc4opsinfo + 1,
562 sizeof(nfssrvproc4opname)/sizeof(char *));
568 print_client_stats(int opt_clt, int opt_prt)
573 if (opt_prt & PRNT_NET) {
574 if (opt_sleep && !has_rpcstats(cltnetinfo, 4)) {
577 print_numbers(LABEL_cltnet
578 "packets udp tcp tcpconn\n",
583 if (opt_prt & PRNT_RPC) {
584 if (opt_sleep && !has_rpcstats(cltrpcinfo, 3)) {
587 print_numbers(LABEL_cltrpc
588 "calls retrans authrefrsh\n",
593 if (opt_prt & PRNT_CALLS) {
594 if ((opt_prt & PRNT_V2) ||
595 ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info))) {
596 if (opt_sleep && !has_stats(cltproc2info)) {
599 print_callstats(LABEL_cltproc2,
600 nfsv2name, cltproc2info + 1,
601 sizeof(nfsv2name)/sizeof(char *));
604 if ((opt_prt & PRNT_V3) ||
605 ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info))) {
606 if (opt_sleep && !has_stats(cltproc3info)) {
609 print_callstats(LABEL_cltproc3,
610 nfsv3name, cltproc3info + 1,
611 sizeof(nfsv3name)/sizeof(char *));
614 if ((opt_prt & PRNT_V4) ||
615 ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info))) {
616 if (opt_sleep && !has_stats(cltproc4info)) {
619 print_callstats(LABEL_cltproc4,
620 nfscltproc4name, cltproc4info + 1,
621 sizeof(nfscltproc4name)/sizeof(char *));
628 print_clnt_list(int opt_prt)
630 if (opt_prt & PRNT_CALLS) {
631 if ((opt_prt & PRNT_V2) ||
632 ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info))) {
633 if (opt_sleep && !has_stats(cltproc2info)) {
636 print_callstats_list("nfs v2 client",
637 nfsv2name, cltproc2info + 1,
638 sizeof(nfsv2name)/sizeof(char *));
641 if ((opt_prt & PRNT_V3) ||
642 ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info))) {
643 if (opt_sleep && !has_stats(cltproc3info)) {
646 print_callstats_list("nfs v3 client",
647 nfsv3name, cltproc3info + 1,
648 sizeof(nfsv3name)/sizeof(char *));
651 if ((opt_prt & PRNT_V4) ||
652 ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info))) {
653 if (opt_sleep && !has_stats(cltproc4info)) {
656 print_callstats_list("nfs v4 ops",
657 nfssrvproc4opname, srvproc4opsinfo + 1,
658 sizeof(nfssrvproc4opname)/sizeof(char *));
659 print_callstats_list("nfs v4 client",
660 nfscltproc4name, cltproc4info + 1,
661 sizeof(nfscltproc4name)/sizeof(char *));
667 print_serv_list(int opt_prt)
669 if (opt_prt & PRNT_CALLS) {
670 if ((opt_prt & PRNT_V2) ||
671 ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info))) {
672 if (opt_sleep && !has_stats(srvproc2info)) {
675 print_callstats_list("nfs v2 server",
676 nfsv2name, srvproc2info + 1,
677 sizeof(nfsv2name)/sizeof(char *));
680 if ((opt_prt & PRNT_V3) ||
681 ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info))) {
682 if (opt_sleep && !has_stats(srvproc3info)) {
685 print_callstats_list("nfs v3 server",
686 nfsv3name, srvproc3info + 1,
687 sizeof(nfsv3name)/sizeof(char *));
690 if ((opt_prt & PRNT_V4) ||
691 ((opt_prt & PRNT_AUTO) && has_stats(srvproc4opsinfo))) {
692 if (opt_sleep && !has_stats(srvproc4info)) {
695 print_callstats_list("nfs v4 ops",
696 nfssrvproc4opname, srvproc4opsinfo + 1,
697 sizeof(nfssrvproc4opname)/sizeof(char *));
703 print_stats_list(int opt_srv, int opt_clt, int opt_prt)
706 print_serv_list(opt_prt);
709 print_clnt_list(opt_prt);
713 get_stat_info(const char *sp, struct statinfo *statp)
717 for (ip = statp; ip->tag; ip++) {
718 if (!strcmp(sp, ip->tag))
726 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
731 for (i = 0; i < nr; i++)
732 printf("%s%-8u", i? " " : "", info[i]);
737 print_callstats(const char *hdr, const char **names,
738 unsigned int *info, unsigned int nr)
740 unsigned long long total;
741 unsigned long long pct;
745 for (i = 0, total = 0; i < nr; i++)
749 for (i = 0; i < nr; i += 6) {
750 for (j = 0; j < 6 && i + j < nr; j++)
751 printf("%-13s", names[i+j]);
753 for (j = 0; j < 6 && i + j < nr; j++) {
754 pct = ((unsigned long long) info[i+j]*100)/total;
755 printf("%-8u%3llu%% ", info[i+j], pct);
763 print_callstats_list(const char *hdr, const char **names,
764 unsigned int *callinfo, unsigned int nr)
766 unsigned long long calltotal;
769 for (i = 0, calltotal = 0; i < nr; i++) {
770 calltotal += callinfo[i];
774 printf("%13s %13s %8llu \n", hdr, "total:", calltotal);
775 printf("------------- ------------- --------\n");
776 for (i = 0; i < nr; i++) {
778 printf("%13s %12s: %8u \n", hdr, names[i], callinfo[i]);
785 /* returns 0 on success, 1 otherwise */
787 parse_raw_statfile(const char *name, struct statinfo *statp)
789 char buffer[4096], *next;
792 /* Being unable to read e.g. the nfsd stats file shouldn't
793 * be a fatal error -- it usually means the module isn't loaded.
795 if ((fp = fopen(name, "r")) == NULL) {
796 // fprintf(stderr, "Warning: %s: %m\n", name);
800 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
802 char *sp, *line = buffer;
804 unsigned int total = 0;
806 if ((next = strchr(line, '\n')) != NULL)
808 if (!(sp = strtok(line, " \t")))
811 ip = get_stat_info(sp, statp);
817 for (i = 0; i < cnt; i++) {
818 if (!(sp = strtok(NULL, " \t")))
820 ip->valptr[i] = (unsigned int) strtoul(sp, NULL, 0);
821 total += ip->valptr[i];
823 ip->valptr[cnt - 1] = total;
830 /* returns 0 on success, 1 otherwise */
832 parse_pretty_statfile(const char *filename, struct statinfo *info)
834 int numvals, curindex, numconsumed, n, err = 1;
836 char buf[4096], *bufp, *fmt, is_proc;
840 if ((fp = fopen(filename, "r")) == NULL)
841 //err(2, "Unable to open statfile '%s'.\n", filename);
844 while (fgets(buf, sizeof(buf), fp) != NULL) {
845 for (ip = info; ip->tag; ip++) {
846 if (strcmp(buf, ip->label))
850 numvals = ip->nrvals - 1;
851 is_proc = strncmp("proc", ip->tag, 4) ? 0 : 1;
853 fmt = " %u %*u%% %n";
861 /* get (and skip) header */
862 if (fgets(buf, sizeof(buf), fp) == NULL) {
863 fprintf(stderr, "Failed to locate header after "
864 "label for '%s' in %s.\n",
868 /* no header -- done with this "tag" */
870 ip->valptr[numvals] = sum;
874 if (fgets(buf, sizeof(buf), fp) == NULL) {
875 fprintf(stderr, "Failed to locate stats after "
876 "header for '%s' in %s.\n",
881 for (; curindex < numvals; curindex++) {
882 n = sscanf(bufp, fmt, &ip->valptr[curindex],
890 sum += ip->valptr[curindex];
904 mounts(const char *name)
906 char buffer[4096], *next;
909 /* Being unable to read e.g. the nfsd stats file shouldn't
910 * be a fatal error -- it usually means the module isn't loaded.
912 if ((fp = fopen(name, "r")) == NULL) {
913 fprintf(stderr, "Warning: %s: %m\n", name);
917 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
919 char *device, *mount, *type, *flags;
921 if ((next = strchr(line, '\n')) != NULL)
924 if (!(device = strtok(line, " \t")))
927 if (!(mount = strtok(NULL, " \t")))
930 if (!(type = strtok(NULL, " \t")))
933 if (strcmp(type, "nfs") && strcmp(type,"nfs4")) {
937 if (!(flags = strtok(NULL, " \t")))
940 printf("%s from %s\n", mount, device);
941 printf(" Flags:\t%s\n", flags);
952 get_stats(const char *file, struct statinfo *info, int *opt, int other_opt,
958 char *label = is_srv ? "Server" : "Client";
960 /* try to guess what type of stat file we're dealing with */
961 if ((fp = fopen(file, "r")) == NULL)
963 if (fgets(buf, 10, fp) == NULL)
965 if (!strncmp(buf, "net ", 4)) {
966 /* looks like raw client stats */
968 fprintf(stderr, "Warning: no server info present in "
969 "raw client stats file.\n");
972 err = parse_raw_statfile(file, info);
973 } else if (!strncmp(buf, "rc ", 3)) {
974 /* looks like raw server stats */
976 fprintf(stderr, "Warning: no client info present in "
977 "raw server stats file.\n");
980 err = parse_raw_statfile(file, info);
982 /* looks like pretty client and server stats */
983 err = parse_pretty_statfile(file, info);
989 fprintf(stderr, "Error: No %s Stats (%s: %m). \n",
998 * This is for proc2/3/4-type stats, where, in the /proc files, the first entry's value
999 * denotes the number of subsequent entries. statinfo value arrays contain an additional
1000 * field at the end which contains the sum of all previous elements in the array -- so,
1001 * there are stats if the sum's greater than the entry-count.
1004 has_stats(const unsigned int *info)
1006 return (info[0] && info[info[0] + 1] > info[0]);
1009 has_rpcstats(const unsigned int *info, int size)
1013 for (i=0, cnt=0; i < size; i++)
1019 * take the difference of each individual stat value in 'new' and 'old'
1020 * and store the results back into 'new'
1023 diff_stats(struct statinfo *new, struct statinfo *old, int is_srv)
1025 int i, j, nodiff_first_index, should_diff;
1028 * Different stat types have different formats in the /proc
1029 * files: for the proc2/3/4-type stats, the first entry has
1030 * the total number of subsequent entries; one does not want
1031 * to diff that first entry. The other stat types aren't like
1032 * this. So, we diff a given entry if it's not of one of the
1033 * procX types ("i" < 2 for clt, < 4 for srv), or if it's not
1034 * the first entry ("j" > 0).
1036 nodiff_first_index = 2 + (2 * is_srv);
1038 for (i = 0; old[i].tag; i++) {
1039 for (j = 0; j < new[i].nrvals; j++) {
1040 should_diff = (i < nodiff_first_index || j > 0);
1042 new[i].valptr[j] -= old[i].valptr[j];
1046 * Make sure that the "totals" entry (last value in
1047 * each stat array) for the procX-type stats has the
1048 * "numentries" entry's (first value in procX-type
1049 * stat arrays) constant value added-back after the
1050 * diff -- i.e., it should always be included in the
1053 if (!strncmp("proc", new[i].tag, 4) && old[i].valptr[0])
1054 new[i].valptr[new[i].nrvals - 1] += new[i].valptr[0];
1062 int minutes, seconds;
1065 endtime = time(NULL);
1066 time_diff = difftime(endtime, starttime);
1067 minutes = time_diff / 60;
1068 seconds = (int)time_diff % 60;
1069 printf("Signal received; displaying (only) statistics gathered over the last %d minutes, %d seconds:\n\n", minutes, seconds);
1073 update_old_counters(struct statinfo *new, struct statinfo *old)
1076 for (z = 0; old[z].tag; z++)
1077 for (i = 0; i <= old[z].nrvals; i++)
1078 old[z].valptr[i] += new[z].valptr[i];