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"
38 static unsigned int srvproc2info[SRVPROC2_SZ+2],
39 srvproc2info_old[SRVPROC2_SZ+2]; /* NFSv2 call counts ([0] == 18) */
40 static unsigned int cltproc2info[CLTPROC2_SZ+2],
41 cltproc2info_old[CLTPROC2_SZ+2]; /* NFSv2 call counts ([0] == 18) */
42 static unsigned int srvproc3info[SRVPROC3_SZ+2],
43 srvproc3info_old[SRVPROC3_SZ+2]; /* NFSv3 call counts ([0] == 22) */
44 static unsigned int cltproc3info[CLTPROC3_SZ+2],
45 cltproc3info_old[CLTPROC3_SZ+2]; /* NFSv3 call counts ([0] == 22) */
46 static unsigned int srvproc4info[SRVPROC4_SZ+2],
47 srvproc4info_old[SRVPROC4_SZ+2]; /* NFSv4 call counts ([0] == 2) */
48 static unsigned int cltproc4info[CLTPROC4_SZ+2],
49 cltproc4info_old[CLTPROC4_SZ+2]; /* NFSv4 call counts ([0] == 48) */
50 static unsigned int srvproc4opsinfo[SRVPROC4OPS_SZ+2],
51 srvproc4opsinfo_old[SRVPROC4OPS_SZ+2]; /* NFSv4 call counts ([0] == 59) */
52 static unsigned int srvnetinfo[5], srvnetinfo_old[5]; /* 0 # of received packets
57 static unsigned int cltnetinfo[5], cltnetinfo_old[5]; /* 0 # of received packets
63 static unsigned int srvrpcinfo[6], srvrpcinfo_old[6]; /* 0 total # of RPC calls
64 * 1 total # of bad calls
66 * 3 authentication failed
69 static unsigned int cltrpcinfo[4], cltrpcinfo_old[4]; /* 0 total # of RPC calls
70 * 1 retransmitted calls
74 static unsigned int srvrcinfo[9], srvrcinfo_old[9]; /* 0 repcache hits
77 * (for pre-2.4 kernels:)
80 * 5 noncached non-directories
81 * 6 noncached directories
85 static unsigned int srvfhinfo[7], srvfhinfo_old[7]; /* (for kernels >= 2.4.0)
89 * 3 noncached directories
90 * 4 noncached non-directories
91 * leave hole to relocate stale for order
95 static const char * nfsv2name[SRVPROC2_SZ] = {
96 "null", "getattr", "setattr", "root", "lookup", "readlink",
97 "read", "wrcache", "write", "create", "remove", "rename",
98 "link", "symlink", "mkdir", "rmdir", "readdir", "fsstat"
101 static const char * nfsv3name[SRVPROC3_SZ] = {
102 "null", "getattr", "setattr", "lookup", "access", "readlink",
103 "read", "write", "create", "mkdir", "symlink", "mknod",
104 "remove", "rmdir", "rename", "link", "readdir", "readdirplus",
105 "fsstat", "fsinfo", "pathconf", "commit"
108 static const char * nfssrvproc4name[SRVPROC4_SZ] = {
113 static const char * nfscltproc4name[CLTPROC4_SZ] = {
114 "null", "read", "write", "commit", "open", "open_conf",
115 "open_noat", "open_dgrd", "close", "setattr", "fsinfo", "renew",
116 "setclntid", "confirm", "lock",
117 "lockt", "locku", "access", "getattr", "lookup", "lookup_root",
118 "remove", "rename", "link", "symlink", "create", "pathconf",
119 "statfs", "readlink", "readdir", "server_caps", "delegreturn", "getacl",
120 "setacl", "fs_locations",
121 /* nfsv4.1 client ops */
133 /* nfsv4.1 pnfs client ops to data server only */
138 static const char * nfssrvproc4opname[SRVPROC4OPS_SZ] = {
139 "op0-unused", "op1-unused", "op2-future", "access", "close", "commit",
140 "create", "delegpurge", "delegreturn", "getattr", "getfh", "link",
141 "lock", "lockt", "locku", "lookup", "lookup_root", "nverify",
142 "open", "openattr", "open_conf", "open_dgrd", "putfh", "putpubfh",
143 "putrootfh", "read", "readdir", "readlink", "remove", "rename",
144 "renew", "restorefh", "savefh", "secinfo", "setattr", "setcltid",
145 "setcltidconf", "verify", "write", "rellockowner",
146 /* nfsv4.1 server ops */
168 #define LABEL_srvnet "Server packet stats:\n"
169 #define LABEL_srvrpc "Server rpc stats:\n"
170 #define LABEL_srvrc "Server reply cache:\n"
171 #define LABEL_srvfh "Server file handle cache:\n"
172 #define LABEL_srvproc2 "Server nfs v2:\n"
173 #define LABEL_srvproc3 "Server nfs v3:\n"
174 #define LABEL_srvproc4 "Server nfs v4:\n"
175 #define LABEL_srvproc4ops "Server nfs v4 operations:\n"
176 #define LABEL_cltnet "Client packet stats:\n"
177 #define LABEL_cltrpc "Client rpc stats:\n"
178 #define LABEL_cltproc2 "Client nfs v2:\n"
179 #define LABEL_cltproc3 "Client nfs v3:\n"
180 #define LABEL_cltproc4 "Client nfs v4:\n"
182 typedef struct statinfo {
186 unsigned int * valptr;
190 * We now build the arrays of statinfos using macros, which will make it easier
191 * to add new variables for --sleep. e.g., SRV(net) expands into the struct
192 * statinfo: { "net", "Server packet stats:\n", 5, srvnetinfo }
194 #define ARRAYSIZE(x) sizeof(x)/sizeof(*x)
195 #define STATINFO(k, t, s...) { #t, LABEL_##k##t, ARRAYSIZE(k##t##info##s), k##t##info##s }
196 #define SRV(t, s...) STATINFO(srv, t, s)
197 #define CLT(t, s...) STATINFO(clt, t, s)
198 #define DECLARE_SRV(n, s...) static statinfo n##s[] = { \
207 { NULL, NULL, 0, NULL }\
209 #define DECLARE_CLT(n, s...) static statinfo n##s[] = { \
215 { NULL, NULL, 0, NULL }\
217 DECLARE_SRV(srvinfo);
218 DECLARE_SRV(srvinfo, _old);
219 DECLARE_CLT(cltinfo);
220 DECLARE_CLT(cltinfo, _old);
222 static void print_all_stats(int, int, int);
223 static void print_server_stats(int, int);
224 static void print_client_stats(int, int);
225 static void print_stats_list(int, int, int);
226 static void print_numbers(const char *, unsigned int *,
228 static void print_callstats(const char *, const char **,
229 unsigned int *, unsigned int);
230 static void print_callstats_list(const char *, const char **,
231 unsigned int *, unsigned int);
232 static int parse_raw_statfile(const char *, struct statinfo *);
233 static int parse_pretty_statfile(const char *, struct statinfo *);
235 static statinfo *get_stat_info(const char *, struct statinfo *);
237 static int mounts(const char *);
239 static void get_stats(const char *, struct statinfo *, int *, int,
241 static int has_stats(const unsigned int *);
242 static int has_rpcstats(const unsigned int *, int);
243 static void diff_stats(struct statinfo *, struct statinfo *, int);
244 static void unpause(int);
245 static void update_old_counters(struct statinfo *, struct statinfo *);
247 static time_t starttime;
249 #define PRNT_CALLS 0x0001
250 #define PRNT_RPC 0x0002
251 #define PRNT_NET 0x0004
252 #define PRNT_FH 0x0008
253 #define PRNT_RC 0x0010
254 #define PRNT_AUTO 0x1000
255 #define PRNT_V2 0x2000
256 #define PRNT_V3 0x4000
257 #define PRNT_V4 0x8000
258 #define PRNT_ALL 0x0fff
266 void usage(char *name)
268 printf("Usage: %s [OPTION]...\n\
270 -m, --mounts Show statistics on mounted NFS filesystems\n\
271 -c, --client Show NFS client statistics\n\
272 -s, --server Show NFS server statistics\n\
273 -2 Show NFS version 2 statistics\n\
274 -3 Show NFS version 3 statistics\n\
275 -4 Show NFS version 4 statistics\n\
276 -o [facility] Show statistics on particular facilities.\n\
277 nfs NFS protocol information\n\
278 rpc General RPC information\n\
279 net Network layer statistics\n\
280 fh Usage information on the server's file handle cache\n\
281 rc Usage information on the server's request reply cache\n\
282 all Select all of the above\n\
283 -v, --verbose, --all Same as '-o all'\n\
284 -r, --rpc Show RPC statistics\n\
285 -n, --nfs Show NFS statistics\n\
286 -Z[#], --sleep[=#] Collects stats until interrupted.\n\
287 Cumulative stats are then printed\n\
288 If # is provided, stats will be output every\n\
290 -S, --since file Shows difference between current stats and those in 'file'\n\
291 -l, --list Prints stats in list format\n\
292 --version Show program version\n\
293 --help What you just did\n\
298 static struct option longopts[] =
300 { "acl", 0, 0, 'a' },
301 { "all", 0, 0, 'v' },
302 { "auto", 0, 0, '\3' },
303 { "client", 0, 0, 'c' },
304 { "mounted", 0, 0, 'm' },
305 { "nfs", 0, 0, 'n' },
306 { "rpc", 0, 0, 'r' },
307 { "server", 0, 0, 's' },
308 { "verbose", 0, 0, 'v' },
309 { "zero", 0, 0, 'z' },
310 { "help", 0, 0, '\1' },
311 { "version", 0, 0, '\2' },
312 { "sleep", 2, 0, 'Z' },
313 { "since", 1, 0, 'S' },
314 { "list", 0, 0, 'l' },
320 main(int argc, char **argv)
331 *serverfile = NFSSRVSTAT,
332 *clientfile = NFSCLTSTAT;
334 struct statinfo *serverinfo = srvinfo,
335 *serverinfo_tmp = srvinfo_old,
336 *clientinfo = cltinfo,
337 *clientinfo_tmp = cltinfo_old;
339 struct sigaction act = {
340 .sa_handler = unpause,
341 .sa_flags = SA_ONESHOT,
344 if ((progname = strrchr(argv[0], '/')))
349 while ((c = getopt_long(argc, argv, "234acmno:Z::S:vrslz\1\2", longopts, NULL)) != EOF) {
352 fprintf(stderr, "nfsstat: nfs acls are not yet supported.\n");
358 opt_prt |= PRNT_CALLS;
361 if (!strcmp(optarg, "nfs"))
362 opt_prt |= PRNT_CALLS;
363 else if (!strcmp(optarg, "rpc"))
365 else if (!strcmp(optarg, "net"))
367 else if (!strcmp(optarg, "rc"))
369 else if (!strcmp(optarg, "fh"))
371 else if (!strcmp(optarg, "all"))
372 opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH;
374 fprintf(stderr, "nfsstat: unknown category: "
382 sleep_time = atoi(optarg);
393 opt_prt |= versions[c - '2'];
399 opt_prt |= PRNT_AUTO;
411 fprintf(stderr, "nfsstat: zeroing of nfs statistics "
412 "not yet supported\n");
415 return mounts(MOUNTSFILE);
420 fprintf(stdout, "nfsstat: " VERSION "\n");
423 printf("Try `%s --help' for more information.\n", progname);
429 opt_srv = opt_clt = 1;
432 if (!(opt_srv + opt_clt))
433 opt_srv = opt_clt = 1;
434 if (!(opt_prt & 0xfff)) {
435 opt_prt |= PRNT_CALLS + PRNT_RPC;
437 if (!(opt_prt & 0xe000)) {
438 opt_prt |= PRNT_AUTO;
440 if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
442 "You requested file handle or request cache "
443 "statistics while using the -c option.\n"
444 "This information is available only for the NFS "
448 if (opt_since || opt_sleep) {
449 serverinfo = srvinfo_old;
450 serverinfo_tmp = srvinfo;
451 clientinfo = cltinfo_old;
452 clientinfo_tmp = cltinfo;
456 get_stats(serverfile, serverinfo, &opt_srv, opt_clt, 1);
458 get_stats(clientfile, clientinfo, &opt_clt, opt_srv, 0);
460 if (opt_sleep && !sleep_time) {
461 starttime = time(NULL);
462 printf("Collecting statistics; press CTRL-C to view results from interval (i.e., from pause to CTRL-C).\n");
463 if (sigaction(SIGINT, &act, NULL) != 0) {
464 fprintf(stderr, "Error: couldn't register for signal and pause.\n");
470 if (opt_since || opt_sleep) {
472 get_stats(NFSSRVSTAT, serverinfo_tmp, &opt_srv, opt_clt, 1);
473 diff_stats(serverinfo_tmp, serverinfo, 1);
476 get_stats(NFSCLTSTAT, clientinfo_tmp, &opt_clt, opt_srv, 0);
477 diff_stats(clientinfo_tmp, clientinfo, 0);
483 get_stats(NFSSRVSTAT, serverinfo_tmp , &opt_srv, opt_clt, 1);
484 diff_stats(serverinfo_tmp, serverinfo, 1);
487 get_stats(NFSCLTSTAT, clientinfo_tmp, &opt_clt, opt_srv, 0);
488 diff_stats(clientinfo_tmp, clientinfo, 0);
491 print_stats_list(opt_srv, opt_clt, opt_prt);
493 print_all_stats(opt_srv, opt_clt, opt_prt);
498 update_old_counters(serverinfo_tmp, serverinfo);
500 update_old_counters(clientinfo_tmp, clientinfo);
506 print_stats_list(opt_srv, opt_clt, opt_prt);
508 print_all_stats(opt_srv, opt_clt, opt_prt);
516 print_all_stats (int opt_srv, int opt_clt, int opt_prt)
518 print_server_stats(opt_srv, opt_prt);
519 print_client_stats(opt_clt, opt_prt);
523 print_server_stats(int opt_srv, int opt_prt)
528 if (opt_prt & PRNT_NET) {
529 if (opt_sleep && !has_rpcstats(srvnetinfo, 4)) {
531 print_numbers( LABEL_srvnet
532 "packets udp tcp tcpconn\n",
537 if (opt_prt & PRNT_RPC) {
538 if (opt_sleep && !has_rpcstats(srvrpcinfo, 5)) {
541 print_numbers(LABEL_srvrpc
542 "calls badcalls badauth badclnt xdrcall\n",
547 if (opt_prt & PRNT_RC) {
548 if (opt_sleep && !has_rpcstats(srvrcinfo, 3)) {
551 print_numbers(LABEL_srvrc
552 "hits misses nocache\n",
559 * 2.2 puts all fh-related info after the 'rc' header
560 * 2.4 puts all fh-related info after the 'fh' header, but relocates
561 * 'stale' to the start and swaps dir and nondir :-(
562 * We preseve the 2.2 order
564 if (opt_prt & PRNT_FH) {
565 if (get_stat_info("fh", srvinfo)) { /* >= 2.4 */
566 int t = srvfhinfo[3];
567 srvfhinfo[3]=srvfhinfo[4];
570 srvfhinfo[5]=srvfhinfo[0]; /* relocate 'stale' */
574 "lookup anon ncachedir ncachedir stale\n",
579 "lookup anon ncachedir ncachedir stale\n",
583 if (opt_prt & PRNT_CALLS) {
584 if ((opt_prt & PRNT_V2) ||
585 ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info))) {
586 if (opt_sleep && !has_stats(srvproc2info)) {
589 print_callstats(LABEL_srvproc2,
590 nfsv2name, srvproc2info + 1,
591 sizeof(nfsv2name)/sizeof(char *));
594 if ((opt_prt & PRNT_V3) ||
595 ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info))) {
596 if (opt_sleep && !has_stats(srvproc3info)) {
599 print_callstats(LABEL_srvproc3,
600 nfsv3name, srvproc3info + 1,
601 sizeof(nfsv3name)/sizeof(char *));
604 if ((opt_prt & PRNT_V4) ||
605 ((opt_prt & PRNT_AUTO) && has_stats(srvproc4info))) {
606 if (opt_sleep && !has_stats(srvproc4info)) {
609 print_callstats( LABEL_srvproc4,
610 nfssrvproc4name, srvproc4info + 1,
611 sizeof(nfssrvproc4name)/sizeof(char *));
612 print_callstats(LABEL_srvproc4ops,
613 nfssrvproc4opname, srvproc4opsinfo + 1,
614 sizeof(nfssrvproc4opname)/sizeof(char *));
620 print_client_stats(int opt_clt, int opt_prt)
625 if (opt_prt & PRNT_NET) {
626 if (opt_sleep && !has_rpcstats(cltnetinfo, 4)) {
629 print_numbers(LABEL_cltnet
630 "packets udp tcp tcpconn\n",
635 if (opt_prt & PRNT_RPC) {
636 if (opt_sleep && !has_rpcstats(cltrpcinfo, 3)) {
639 print_numbers(LABEL_cltrpc
640 "calls retrans authrefrsh\n",
645 if (opt_prt & PRNT_CALLS) {
646 if ((opt_prt & PRNT_V2) ||
647 ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info))) {
648 if (opt_sleep && !has_stats(cltproc2info)) {
651 print_callstats(LABEL_cltproc2,
652 nfsv2name, cltproc2info + 1,
653 sizeof(nfsv2name)/sizeof(char *));
656 if ((opt_prt & PRNT_V3) ||
657 ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info))) {
658 if (opt_sleep && !has_stats(cltproc3info)) {
661 print_callstats(LABEL_cltproc3,
662 nfsv3name, cltproc3info + 1,
663 sizeof(nfsv3name)/sizeof(char *));
666 if ((opt_prt & PRNT_V4) ||
667 ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info))) {
668 if (opt_sleep && !has_stats(cltproc4info)) {
671 print_callstats(LABEL_cltproc4,
672 nfscltproc4name, cltproc4info + 1,
673 sizeof(nfscltproc4name)/sizeof(char *));
680 print_clnt_list(int opt_prt)
682 if (opt_prt & PRNT_CALLS) {
683 if ((opt_prt & PRNT_V2) ||
684 ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info))) {
685 if (opt_sleep && !has_stats(cltproc2info)) {
688 print_callstats_list("nfs v2 client",
689 nfsv2name, cltproc2info + 1,
690 sizeof(nfsv2name)/sizeof(char *));
693 if ((opt_prt & PRNT_V3) ||
694 ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info))) {
695 if (opt_sleep && !has_stats(cltproc3info)) {
698 print_callstats_list("nfs v3 client",
699 nfsv3name, cltproc3info + 1,
700 sizeof(nfsv3name)/sizeof(char *));
703 if ((opt_prt & PRNT_V4) ||
704 ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info))) {
705 if (opt_sleep && !has_stats(cltproc4info)) {
708 print_callstats_list("nfs v4 ops",
709 nfssrvproc4opname, srvproc4opsinfo + 1,
710 sizeof(nfssrvproc4opname)/sizeof(char *));
711 print_callstats_list("nfs v4 client",
712 nfscltproc4name, cltproc4info + 1,
713 sizeof(nfscltproc4name)/sizeof(char *));
719 print_serv_list(int opt_prt)
721 if (opt_prt & PRNT_CALLS) {
722 if ((opt_prt & PRNT_V2) ||
723 ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info))) {
724 if (opt_sleep && !has_stats(srvproc2info)) {
727 print_callstats_list("nfs v2 server",
728 nfsv2name, srvproc2info + 1,
729 sizeof(nfsv2name)/sizeof(char *));
732 if ((opt_prt & PRNT_V3) ||
733 ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info))) {
734 if (opt_sleep && !has_stats(srvproc3info)) {
737 print_callstats_list("nfs v3 server",
738 nfsv3name, srvproc3info + 1,
739 sizeof(nfsv3name)/sizeof(char *));
742 if ((opt_prt & PRNT_V4) ||
743 ((opt_prt & PRNT_AUTO) && has_stats(srvproc4opsinfo))) {
744 if (opt_sleep && !has_stats(srvproc4info)) {
747 print_callstats_list("nfs v4 ops",
748 nfssrvproc4opname, srvproc4opsinfo + 1,
749 sizeof(nfssrvproc4opname)/sizeof(char *));
755 print_stats_list(int opt_srv, int opt_clt, int opt_prt)
758 print_serv_list(opt_prt);
761 print_clnt_list(opt_prt);
765 get_stat_info(const char *sp, struct statinfo *statp)
769 for (ip = statp; ip->tag; ip++) {
770 if (!strcmp(sp, ip->tag))
778 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
783 for (i = 0; i < nr; i++)
784 printf("%s%-8u", i? " " : "", info[i]);
789 print_callstats(const char *hdr, const char **names,
790 unsigned int *info, unsigned int nr)
792 unsigned long long total;
793 unsigned long long pct;
797 for (i = 0, total = 0; i < nr; i++)
801 for (i = 0; i < nr; i += 6) {
802 for (j = 0; j < 6 && i + j < nr; j++)
803 printf("%-13s", names[i+j]);
805 for (j = 0; j < 6 && i + j < nr; j++) {
806 pct = ((unsigned long long) info[i+j]*100)/total;
807 printf("%-8u%3llu%% ", info[i+j], pct);
815 print_callstats_list(const char *hdr, const char **names,
816 unsigned int *callinfo, unsigned int nr)
818 unsigned long long calltotal;
821 for (i = 0, calltotal = 0; i < nr; i++) {
822 calltotal += callinfo[i];
826 printf("%13s %13s %8llu \n", hdr, "total:", calltotal);
827 printf("------------- ------------- --------\n");
828 for (i = 0; i < nr; i++) {
830 printf("%13s %12s: %8u \n", hdr, names[i], callinfo[i]);
837 /* returns 0 on success, 1 otherwise */
839 parse_raw_statfile(const char *name, struct statinfo *statp)
841 char buffer[4096], *next;
844 /* Being unable to read e.g. the nfsd stats file shouldn't
845 * be a fatal error -- it usually means the module isn't loaded.
847 if ((fp = fopen(name, "r")) == NULL) {
848 // fprintf(stderr, "Warning: %s: %m\n", name);
852 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
854 char *sp, *line = buffer;
856 unsigned int total = 0;
858 if ((next = strchr(line, '\n')) != NULL)
860 if (!(sp = strtok(line, " \t")))
863 ip = get_stat_info(sp, statp);
869 for (i = 0; i < cnt; i++) {
870 if (!(sp = strtok(NULL, " \t")))
872 ip->valptr[i] = (unsigned int) strtoul(sp, NULL, 0);
873 total += ip->valptr[i];
875 ip->valptr[cnt - 1] = total;
882 /* returns 0 on success, 1 otherwise */
884 parse_pretty_statfile(const char *filename, struct statinfo *info)
886 int numvals, curindex, numconsumed, n, err = 1;
888 char buf[4096], *bufp, *fmt, is_proc;
892 if ((fp = fopen(filename, "r")) == NULL)
893 //err(2, "Unable to open statfile '%s'.\n", filename);
896 while (fgets(buf, sizeof(buf), fp) != NULL) {
897 for (ip = info; ip->tag; ip++) {
898 if (strcmp(buf, ip->label))
902 numvals = ip->nrvals - 1;
903 is_proc = strncmp("proc", ip->tag, 4) ? 0 : 1;
905 fmt = " %u %*u%% %n";
913 /* get (and skip) header */
914 if (fgets(buf, sizeof(buf), fp) == NULL) {
915 fprintf(stderr, "Failed to locate header after "
916 "label for '%s' in %s.\n",
920 /* no header -- done with this "tag" */
922 ip->valptr[numvals] = sum;
926 if (fgets(buf, sizeof(buf), fp) == NULL) {
927 fprintf(stderr, "Failed to locate stats after "
928 "header for '%s' in %s.\n",
933 for (; curindex < numvals; curindex++) {
934 n = sscanf(bufp, fmt, &ip->valptr[curindex],
942 sum += ip->valptr[curindex];
956 mounts(const char *name)
958 char buffer[4096], *next;
961 /* Being unable to read e.g. the nfsd stats file shouldn't
962 * be a fatal error -- it usually means the module isn't loaded.
964 if ((fp = fopen(name, "r")) == NULL) {
965 fprintf(stderr, "Warning: %s: %m\n", name);
969 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
971 char *device, *mount, *type, *flags;
973 if ((next = strchr(line, '\n')) != NULL)
976 if (!(device = strtok(line, " \t")))
979 if (!(mount = strtok(NULL, " \t")))
982 if (!(type = strtok(NULL, " \t")))
985 if (strcmp(type, "nfs") && strcmp(type,"nfs4")) {
989 if (!(flags = strtok(NULL, " \t")))
992 printf("%s from %s\n", mount, device);
993 printf(" Flags:\t%s\n", flags);
1004 get_stats(const char *file, struct statinfo *info, int *opt, int other_opt,
1010 char *label = is_srv ? "Server" : "Client";
1012 /* try to guess what type of stat file we're dealing with */
1013 if ((fp = fopen(file, "r")) == NULL)
1015 if (fgets(buf, 10, fp) == NULL)
1017 if (!strncmp(buf, "net ", 4)) {
1018 /* looks like raw client stats */
1020 fprintf(stderr, "Warning: no server info present in "
1021 "raw client stats file.\n");
1024 err = parse_raw_statfile(file, info);
1025 } else if (!strncmp(buf, "rc ", 3)) {
1026 /* looks like raw server stats */
1028 fprintf(stderr, "Warning: no client info present in "
1029 "raw server stats file.\n");
1032 err = parse_raw_statfile(file, info);
1034 /* looks like pretty client and server stats */
1035 err = parse_pretty_statfile(file, info);
1041 fprintf(stderr, "Error: No %s Stats (%s: %m). \n",
1050 * This is for proc2/3/4-type stats, where, in the /proc files, the first entry's value
1051 * denotes the number of subsequent entries. statinfo value arrays contain an additional
1052 * field at the end which contains the sum of all previous elements in the array -- so,
1053 * there are stats if the sum's greater than the entry-count.
1056 has_stats(const unsigned int *info)
1058 return (info[0] && info[info[0] + 1] > info[0]);
1061 has_rpcstats(const unsigned int *info, int size)
1065 for (i=0, cnt=0; i < size; i++)
1071 * take the difference of each individual stat value in 'new' and 'old'
1072 * and store the results back into 'new'
1075 diff_stats(struct statinfo *new, struct statinfo *old, int is_srv)
1077 int i, j, nodiff_first_index, should_diff;
1080 * Different stat types have different formats in the /proc
1081 * files: for the proc2/3/4-type stats, the first entry has
1082 * the total number of subsequent entries; one does not want
1083 * to diff that first entry. The other stat types aren't like
1084 * this. So, we diff a given entry if it's not of one of the
1085 * procX types ("i" < 2 for clt, < 4 for srv), or if it's not
1086 * the first entry ("j" > 0).
1088 nodiff_first_index = 2 + (2 * is_srv);
1090 for (i = 0; old[i].tag; i++) {
1091 for (j = 0; j < new[i].nrvals; j++) {
1092 should_diff = (i < nodiff_first_index || j > 0);
1094 new[i].valptr[j] -= old[i].valptr[j];
1098 * Make sure that the "totals" entry (last value in
1099 * each stat array) for the procX-type stats has the
1100 * "numentries" entry's (first value in procX-type
1101 * stat arrays) constant value added-back after the
1102 * diff -- i.e., it should always be included in the
1105 if (!strncmp("proc", new[i].tag, 4) && old[i].valptr[0])
1106 new[i].valptr[new[i].nrvals - 1] += new[i].valptr[0];
1114 int minutes, seconds;
1117 endtime = time(NULL);
1118 time_diff = difftime(endtime, starttime);
1119 minutes = time_diff / 60;
1120 seconds = (int)time_diff % 60;
1121 printf("Signal received; displaying (only) statistics gathered over the last %d minutes, %d seconds:\n\n", minutes, seconds);
1125 update_old_counters(struct statinfo *new, struct statinfo *old)
1128 for (z = 0; old[z].tag; z++)
1129 for (i = 0; i <= old[z].nrvals; i++)
1130 old[z].valptr[i] += new[z].valptr[i];